This commit is contained in:
2025-07-20 10:31:19 +02:00
parent 8e47487a23
commit cbde29a8b4
12 changed files with 414 additions and 387 deletions

View File

@@ -10,7 +10,7 @@ Next will define an OEM product in month 10, 1 Million EUR, ... cogs is a percen
!!bizmodel.revenue_define bizname:'test' name:'oem1'
descr:'OEM Deals'
revenue:'10:1000000EUR,15:3333,20:1200000'
cogs_percent: '1:20%,20:10%'
cogs_percent: '1:20%,20:15%' cogs_delay:1
This time we have the cogs defined in fixed manner, the default currency is USD doesn't have to be mentioned.
@@ -25,4 +25,4 @@ bizmodel.play(heroscript:heroscript)!
mut bm:=bizmodel.get("test")!
bm.sheet.pprint()!
bm.sheet.pprint(nr_columns:40)!

23
examples/biztools/bizmodel2.vsh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.biz.bizmodel
import os
heroscript:="
!!bizmodel.revenue_define bizname:'test' name:'nodes'
descr:'Node Sales'
nr_sold: '0:10,20:10'
revenue_item_setup:'0:1000,20:1200' revenue_item_setup_delay:1
revenue_item_monthly:'0:5' revenue_item_monthly_delay:1
cogs_item_monthly_rev_perc: '40%'
cogs_item_delay:1
cogs_item_setup_rev_perc: '80%'
//revenue_item_monthly_perc:'3%'
"
bizmodel.play(heroscript:heroscript)!
mut bm:=bizmodel.get("test")!
bm.sheet.pprint(nr_columns:30)!

View File

@@ -1,28 +0,0 @@
import freeflowuniverse.herolib.biz.bizmodel
import os
fn main() ! {
heroscript:="
Next will define an OEM product in month 10, 1 Million EUR, ... cogs is a percent which is 20% at start but goes to 10% after 20 months.
!!bizmodel.revenue_define bizname:'test' name:'oem1'
descr:'OEM Deals'
revenue:'10:1000000EUR,15:3333,20:1200000'
cogs_percent: '1:20%,20:10%'
This time we have the cogs defined in fixed manner, the default currency is USD doesn't have to be mentioned.
!!bizmodel.revenue_define bizname:'test' name:'oem2'
descr:'OEM Deals'
revenue:'10:1000000EUR,15:3333,20:1200000'
cogs: '10:100000,15:1000,20:120000'
"
bizmodel.play(heroscript:heroscript)!
mut bm:=bizmodel.get("test")!
bm.sheet.pprint()!
}

View File

@@ -16,6 +16,8 @@ pub fn (mut m BizModel) act(action Action) !Action {
}
'revenue_define' {
m.revenue_action(action)!
m.revenue_item_action(action)!
m.revenue_name_total(action)!
}
'costcenter_define' {
m.costcenter_define_action(action)!

View File

@@ -9,34 +9,20 @@
- bizname, is the name of the biz model we are populating
- name, name of product, project
- descr, description of the revenue line item
- nr_months_recurring: e.g. 60 is 5 years
## descrete revenue/cogs (not per item)
## discrete revenue/cogs (not per item)
cogs stands for cost of goods
- revenue: one of revenue, is not extrapolated, a deal at certain time
- revenue_growth: is a revenue stream which is being extrapolated
- revenue: one of revenue, can be extrapolated if specified
- cogs: cost of goods, this is the cost of the revenue, can be extrapolated if specified
- cogs_percent: percent of revenue
- cogs_delay: delay in months between cogs and revenue
## grouped per items sold
if you want to extrapolate cogs or revenue do extrapolate:1
- nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
- nr_months_recurring: e.g. 60 is 5 years
- revenue_item_setup, revenue for 1 item '1000usd'
- revenue_item_setup_delay, delay between sell and recognition of sale in months
- revenue_item_monthly, revenue per month for 1 item
- revenue_item_monthly_delay, how many months before monthly revenue starts
- revenue_item_maintenance_perc, how much percent of revenue_item_setup will come back over months
- cogs_item_setup, cost of good for 1 item at setup
- cogs_item_setup_delay, how many months before setup cogs starts, after sales
- cogs_item_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
- cogs_item_monthly, cost of goods for the monthly per 1 item
- cogs_item_monthly_delay, how many months before monthly cogs starts, after sales
- cogs_item_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
## results in
### results in
follow rows in sheets
@@ -44,6 +30,33 @@ follow rows in sheets
- {name}_revenue_total
- {name}_cogs_total
## grouped per items sold
- nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
- revenue_item_setup, revenue for 1 item '1000usd'
- revenue_item_setup_delay, delay between sell and recognition of sale in months e.g. 1
- revenue_item_monthly, revenue per month for 1 item
- revenue_item_monthly_delay, how many months before monthly revenue starts
- revenue_item_monthly_perc, how much percent of revenue_item_setup will come back over months e.g. 20%
- cogs_item_setup, cost of good for 1 item at setup
- cogs_item_setup_rev_perc: what is percentage of the revenue which is cogs, e.g. 2%
- cogs_item_monthly, cost of goods for the monthly per 1 item
- cogs_item_monthly_rev_perc: what is percentage of the monthly revenue which is cogs, e.g. 10%
- cogs_item_delay, how many months before cogs starts after sales
### results in
follow rows in sheets
- {name}_ + all the arg names as mentioned above...
- {name}_revenue_item_setup_total
- {name}_revenue_item_monthly_total
- {name}_revenue_item_total
- {name}_cogs_item_total
## to use
### basic example

View File

@@ -5,354 +5,63 @@ import freeflowuniverse.herolib.core.texttools
// see lib/biz/bizmodel/docs/revenue.md
fn (mut m BizModel) revenue_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
descr = action.params.get_default('description', '')!
}
if name.len == 0 {
// make name ourselves
name = texttools.name_fix(descr)
}
name = texttools.name_fix(name)
if name.len == 0 {
return error('name and description is empty for ${action}')
}
name2 := name.replace('_', ' ').replace('-', ' ')
descr = descr.replace('_', ' ').replace('-', ' ')
mut r := get_action_descr(action)!
mut product := Product{
name: name
title: action.params.get_default('title', name)!
description: descr
name: r.name
title: action.params.get_default('title', r.name)!
description: r.description
}
m.products[name] = &product
m.products[r.name] = &product
mut nr_months_recurring := action.params.get_int_default('nr_months_recurring', 60)!
product.nr_months_recurring = nr_months_recurring
mut revenue := m.sheet.row_new(
name: '${name}_revenue_total'
growth: '0:0'
tags: 'rev name:${name}'
descr: 'Revenue for ${name2}'
extrapolate: false
)!
// Handle revenue_items parameter (non-recurring revenue items)
mut revenue_items := m.sheet.row_new(
name: '${name}_revenue'
name: '${r.name}_revenue'
growth: action.params.get_default('revenue', '0:0')!
tags: 'rev name:${name}'
descr: 'Revenue items for ${name2}'
extrapolate: false
tags: 'rev rev:${r.name} name:${r.name}'
descr: 'Revenue items for ${r.name}'
extrapolate: action.params.get_default_false('extrapolate')
)!
// Handle revenue_growth parameter
mut revenue_growth := m.sheet.row_new(
name: '${name}_revenue_growth'
growth: action.params.get_default('revenue_growth', '0:0')!
tags: 'rev name:${name}'
descr: 'Revenue growth for ${name2}'
extrapolate: true
)!
// Handle revenue_nr parameter (number of revenue items)
mut revenue_nr := m.sheet.row_new(
name: '${name}_nr_sold'
growth: action.params.get_default('nr_sold', '0:0')!
tags: 'rev name:${name}'
descr: 'Items sold per month for ${name2}'
extrapolate: false
)!
mut nr_sold := m.sheet.row_new(
name: '${name}_nr_sold'
growth: action.params.get_default('nr_sold', '0')!
tags: 'rev name:${name}'
descr: 'nr of items sold/month for ${name2}'
aggregatetype: .avg
)!
if nr_sold.max() > 0 {
product.has_items = true
}
// Handle revenue_item parameter (singular item)
mut revenue_item := m.sheet.row_new(
name: '${name}_revenue_item_setup'
growth: action.params.get_default('revenue_item_setup', '0:0')!
tags: 'rev name:${name}'
descr: 'Item Revenue for ${name2}'
extrapolate: false
)!
mut revenue_setup := m.sheet.row_new(
name: '${name}_revenue_setup'
growth: action.params.get_default('revenue_setup', '0:0')!
tags: 'rev name:${name}'
descr: 'Setup Sales price for ${name2}'
aggregatetype: .avg
)!
mut revenue_setup_delay := action.params.get_int_default('revenue_setup_delay', 0)!
mut revenue_monthly := m.sheet.row_new(
name: '${name}_revenue_monthly'
growth: action.params.get_default('revenue_monthly', '0:0')!
tags: 'rev name:${name}'
descr: 'Monthly Sales price for ${name2}'
aggregatetype: .avg
)!
mut revenue_monthly_delay := action.params.get_int_default('revenue_monthly_delay',
1)!
mut cogs := m.sheet.row_new(
name: '${name}_cogs_total'
mut cogs_param := m.sheet.row_new(
name: '${r.name}_cogs_param'
growth: action.params.get_default('cogs', '0:0')!
tags: 'cogs name:${name}'
descr: 'COGS for ${name2}'
extrapolate: false
tags: 'name:${r.name}'
descr: 'COGS for ${r.name}'
extrapolate: action.params.get_default_false('extrapolate')
)!
if revenue.max() > 0 || cogs.max() > 0 {
product.has_oneoffs = true
}
_ := m.sheet.row_new(
name: '${name}_cogs_perc'
growth: action.params.get_default('cogs_perc', '0')!
tags: 'rev name:${name}'
descr: 'COGS as percent of revenue for ${name2}'
mut cogs_perc := m.sheet.row_new(
name: '${r.name}_cogs_perc'
growth: action.params.get_default('cogs_percent', '0')!
tags: 'name:${r.name}'
descr: 'COGS as percent of revenue for ${r.name}'
aggregatetype: .avg
)!
mut cogs_setup := m.sheet.row_new(
name: '${name}_cogs_setup'
growth: action.params.get_default('cogs_setup', '0:0')!
tags: 'rev name:${name}'
descr: 'COGS for ${name2} Setup'
aggregatetype: .avg
)!
mut cogs_setup_delay := action.params.get_int_default('cogs_setup_delay', 1)!
mut cogs_setup_perc := m.sheet.row_new(
name: '${name}_cogs_setup_perc'
growth: action.params.get_default('cogs_setup_perc', '0')!
tags: 'rev name:${name}'
descr: 'COGS as percent of revenue for ${name2} Setup'
aggregatetype: .avg
)!
mut cogs_monthly := m.sheet.row_new(
name: '${name}_cogs_monthly'
growth: action.params.get_default('cogs_monthly', '0:0')!
tags: 'rev name:${name}'
descr: 'Cost of Goods (COGS) for ${name2} Monthly'
aggregatetype: .avg
)!
mut cogs_monthly_delay := action.params.get_int_default('cogs_monthly_delay', 1)!
mut cogs_monthly_perc := m.sheet.row_new(
name: '${name}_cogs_monthly_perc'
growth: action.params.get_default('cogs_monthly_perc', '0')!
tags: 'rev name:${name}'
descr: 'COGS as percent of revenue for ${name2} Monthly'
aggregatetype: .avg
)!
// CALCULATE THE TOTAL (multiply with nr sold)
mut revenue_setup_total := revenue_setup.action(
name: '${name}_revenue_setup_total'
descr: 'Setup sales for ${name2} total'
action: .multiply
rows: [nr_sold]
delaymonths: revenue_setup_delay
)!
mut revenue_monthly_total := revenue_monthly.action(
name: '${name}_revenue_monthly_total'
descr: 'Monthly sales for ${name2} total'
action: .multiply
rows: [nr_sold]
delaymonths: revenue_monthly_delay
)!
mut cogs_setup_total := cogs_setup.action(
name: '${name}_cogs_setup_total'
descr: 'Setup COGS for ${name2} total'
action: .multiply
rows: [nr_sold]
delaymonths: cogs_setup_delay
)!
mut cogs_monthly_total := cogs_monthly.action(
name: '${name}_cogs_monthly_total'
descr: 'Monthly COGS for ${name2} total'
action: .multiply
rows: [nr_sold]
delaymonths: cogs_monthly_delay
)!
// DEAL WITH RECURRING
if nr_months_recurring > 0 {
revenue_monthly_total = revenue_monthly_total.recurring(
name: '${name}_revenue_monthly_recurring'
descr: 'Revenue monthly recurring for ${name2}'
nrmonths: nr_months_recurring
)!
cogs_monthly_total = cogs_monthly_total.recurring(
name: '${name}_cogs_monthly_recurring'
descr: 'COGS recurring for ${name2}'
nrmonths: nr_months_recurring
)!
_ := nr_sold.recurring(
name: '${name}_nr_sold_recurring'
descr: 'Nr products active because of recurring for ${name2}'
nrmonths: nr_months_recurring
aggregatetype: .max
)!
}
// cogs as percentage of revenue
mut cogs_setup_from_perc := cogs_setup_perc.action(
mut cogs_percent_temp := cogs_perc.action(
action: .multiply
rows: [revenue_setup_total]
name: '${name}_cogs_setup_from_perc'
)!
mut cogs_monthly_from_perc := cogs_monthly_perc.action(
action: .multiply
rows: [revenue_monthly_total]
name: '${name}_cogs_monthly_from_perc'
rows: [revenue]
name: '${r.name}_cogs_percent_temp'
)!
cogs_percent_temp.delay(action.params.get_int_default('cogs_delay', 0)!)!
// mut cogs_from_perc:=cogs_perc.action(action:.multiply,rows:[revenue],name:"cogs_from_perc")!
cogs_percent_temp.delete()
// DEAL WITH MAINTENANCE
// make sum of all past revenue (all one off revenue, needed to calculate maintenance)
mut temp_past := revenue.recurring(
nrmonths: nr_months_recurring
name: 'temp_past'
// delaymonths:4
)!
mut maintenance_month_perc := action.params.get_percentage_default('maintenance_month_perc',
'0%')!
mut maintenance_month := m.sheet.row_new(
name: '${name}_maintenance_month'
growth: '0:${maintenance_month_perc:.2f}'
tags: 'rev name:${name}'
descr: 'maintenance fee for ${name2}'
)!
maintenance_month.action(action: .multiply, rows: [temp_past])!
// temp_past.delete()
// Process revenue_item and revenue_nr if they are provided
mut revenue_item_total := m.sheet.row_new(
name: '${name}_revenue_item_total'
growth: '0:0'
tags: 'rev name:${name}'
descr: 'Revenue item total for ${name2}'
)!
// If revenue_item and revenue_nr are provided, multiply them
if revenue_item.max() > 0 && revenue_nr.max() > 0 {
revenue_item_total = revenue_item.action(
name: '${name}_revenue_item_total'
descr: 'Revenue item total for ${name2}'
action: .multiply
rows: [revenue_nr]
)!
}
// TOTALS
mut revenue_total := m.sheet.row_new(
name: '${name}_revenue_total'
growth: '0:0'
tags: 'rev revtotal name:${name}'
descr: 'Revenue total for ${name2}.'
)!
mut cogs_total := m.sheet.row_new(
name: '${name}_cogs_total'
growth: '0:0'
tags: 'rev cogstotal name:${name}'
descr: 'COGS total for ${name2}.'
)!
if revenue_total.max() > 0.0 || cogs_total.max() > 0.0 {
product.has_revenue
}
revenue_total = revenue_total.action(
mut cogs := cogs_param.action(
action: .add
rows: [revenue, revenue_items, revenue_growth, revenue_item_total, revenue_monthly_total,
revenue_setup_total, maintenance_month]
rows: [cogs_percent_temp]
name: '${r.name}_cogs'
tags: 'cogs cogs:${r.name} name:${r.name}'
)!
if revenue_total.max() > 0 {
if revenue.max() > 0 {
product.has_revenue = true
}
cogs_total = cogs_total.action(
action: .add
rows: [cogs, cogs_monthly_total, cogs_setup_total, cogs_setup_from_perc,
cogs_monthly_from_perc]
)!
// if true{
// //println(m.sheet)
// println(revenue_total)
// println(cogs_total)
// println(cogs)
// println(cogs_monthly_total)
// println(cogs_setup_total)
// println(cogs_setup_from_perc)
// println(cogs_monthly_from_perc)
// panic("sdsd")
// }
return action
}
// revenue_total calculates and aggregates the total revenue and cost of goods sold (COGS) for the business model
fn (mut sim BizModel) revenue_total() ! {
// Create a new row in the sheet to represent the total revenue across all products
sim.sheet.group2row(
name: 'revenue_total'
tags: ''
descr: 'total revenue.'
)!
// Create a new row in the sheet to represent the total COGS across all products
sim.sheet.group2row(
name: 'cogs_total'
tags: ''
descr: 'total cogs.'
)!
// Note: The following commented-out code block seems to be for debugging or future implementation
// It demonstrates how to create a smaller version of the sheet with specific filters
// if true{
// // name string
// // namefilter []string // only include the exact names as specified for the rows
// // includefilter []string // matches for the tags
// // excludefilter []string // matches for the tags
// // period_months int = 12
// mut r:=sim.sheet.tosmaller(name:"tmp",includefilter:["cogstotal"],period_months:12)!
// println(r)
// panic("sdsd")
// }
}
}

View File

@@ -0,0 +1,203 @@
module bizmodel
import freeflowuniverse.herolib.core.playbook { Action }
import freeflowuniverse.herolib.core.texttools
// see lib/biz/bizmodel/docs/revenue.md
fn (mut m BizModel) revenue_item_action(action Action) !Action {
mut r := get_action_descr(action)!
mut product := m.products[r.name]
mut nr_sold := m.sheet.row_new(
name: '${r.name}_nr_sold'
growth: action.params.get_default('nr_sold', '0')!
tags: 'name:${r.name}'
descr: 'nr of items sold/month for ${r.name}'
aggregatetype: .avg
extrapolate: true
)!
if nr_sold.max() > 0 {
product.has_items = true
}
mut revenue_item_setup_param := m.sheet.row_new(
name: '${r.name}_revenue_item_setup'
growth: action.params.get_default('revenue_item_setup', '0:0')!
tags: 'name:${r.name}'
descr: 'Item Revenue setup for ${r.name} Param'
extrapolate: true
)!
mut revenue_item_monthly_param := m.sheet.row_new(
name: '${r.name}_revenue_item_monthly_param'
growth: action.params.get_default('revenue_item_monthly', '0:0')!
tags: 'name:${r.name}'
descr: 'Item Revenue monthly for ${r.name} Param'
extrapolate: true
)!
mut revenue_item_monthly_perc_temp := revenue_item_setup_param.action(
name: '${r.name}_revenue_item_monthly_perc_temp'
descr: 'Monthly sales as percentage from Setup Revenue for ${r.name}'
action: .multiply
val: action.params.get_float_default('revenue_item_monthly_perc', 0.0)!
tags: 'name:${r.name}'
)!
mut revenue_item_monthly := revenue_item_monthly_param.action(
name: '${r.name}_revenue_item_monthly'
descr: 'Item Revenue monthly for ${r.name}'
action: .add
rows: [revenue_item_monthly_perc_temp]
tags: 'name:${r.name}'
)!
revenue_item_monthly_perc_temp.delete()
mut cogs_item_setup_param := m.sheet.row_new(
name: '${r.name}_cogs_item_setup_param'
growth: action.params.get_default('cogs_item_setup', '0:0')!
tags: 'name:${r.name}'
descr: 'Item COGS setup for ${r.name} parameter'
extrapolate: true
)!
mut cogs_item_monthly_param := m.sheet.row_new(
name: '${r.name}_cogs_item_monthly_param'
growth: action.params.get_default('cogs_item_monthly', '0:0')!
tags: 'name:${r.name}'
descr: 'Item COGS monthly for ${r.name} parameter'
extrapolate: true
)!
mut cogs_item_setup_rev_perc_temp := revenue_item_setup_param.action(
name: '${r.name}_cogs_item_setup_rev_perc_temp'
descr: 'Setup cogs as percentage from Setup for ${r.name}'
action: .multiply
val: action.params.get_float_default('cogs_item_setup_rev_perc', 0.0)!
tags: 'name:${r.name}'
)!
mut cogs_item_monthly_rev_perc_temp := revenue_item_monthly_param.action(
name: '${r.name}_cogs_item_monthly_rev_perc_temp'
descr: 'Monthly cogs as percentage from Monthly for ${r.name}'
action: .multiply
val: action.params.get_float_default('cogs_item_monthly_rev_perc', 0.0)!
tags: 'name:${r.name}'
)!
mut cogs_item_setup1 := cogs_item_setup_param.action(
name: '${r.name}_cogs_item_setup1'
descr: 'Item COGS setup for ${r.name}'
action: .add
rows: [cogs_item_setup_rev_perc_temp]
tags: 'name:${r.name}'
)!
mut cogs_item_monthly := cogs_item_monthly_param.action(
name: '${r.name}_cogs_item_monthly'
descr: 'Item COGS monthly for ${r.name}'
action: .add
rows: [cogs_item_monthly_rev_perc_temp]
tags: 'name:${r.name}'
)!
cogs_item_setup_rev_perc_temp.delete()
cogs_item_monthly_rev_perc_temp.delete()
////////////////////////////////////////////////////////////////
// CALCULATE THE TOTAL (multiply with nr sold)
mut revenue_setup := revenue_item_setup_param.action(
name: '${r.name}_revenue_setup'
descr: 'Setup sales for ${r.name} total'
action: .multiply
rows: [nr_sold]
tags: 'name:${r.name}'
delaymonths: action.params.get_int_default('revenue_item_setup_delay', 0)!
)!
mut revenue_monthly_total := revenue_item_monthly.action(
name: '${r.name}_revenue_monthly_total'
descr: 'Monthly sales for ${r.name} total'
action: .multiply
rows: [nr_sold]
tags: 'name:${r.name}'
delaymonths: action.params.get_int_default('revenue_item_monthly_delay', 0)!
)!
mut cogs_setup := cogs_item_setup1.action(
name: '${r.name}_cogs_setup'
descr: 'Setup COGS for ${r.name} total'
action: .multiply
rows: [nr_sold]
tags: 'name:${r.name}'
delaymonths: action.params.get_int_default('cogs_item_delay', 0)!
)!
mut cogs_monthly_total := cogs_item_monthly.action(
name: '${r.name}_cogs_monthly_total'
descr: 'Monthly COGS for ${r.name} total'
action: .multiply
rows: [nr_sold]
tags: 'name:${r.name}'
delaymonths: action.params.get_int_default('cogs_item_delay', 0)!
)!
// DEAL WITH RECURRING
mut revenue_monthly_recurring := revenue_monthly_total.recurring(
name: '${r.name}_revenue_monthly'
descr: 'Revenue monthly recurring for ${r.name}'
nrmonths: product.nr_months_recurring
)!
revenue_monthly_total.delete()
mut cogs_monthly_recurring := cogs_monthly_total.recurring(
name: '${r.name}_cogs_monthly'
descr: 'COGS monthly recurring for ${r.name}'
nrmonths: product.nr_months_recurring
)!
cogs_monthly_total.delete()
_ := nr_sold.recurring(
name: '${r.name}_nr_active'
descr: 'Nr products active because of recurring for ${r.name}'
nrmonths: product.nr_months_recurring
aggregatetype: .max
delaymonths: action.params.get_int_default('revenue_item_monthly_delay', 0)!
)!
//DEAL WITH MARGIN
mut margin_setup_total := revenue_setup.action(
name: '${r.name}_margin_setup'
descr: 'Setup margin for ${r.name}'
action: .substract
rows: [cogs_setup]
tags: 'name:${r.name}'
)!
mut margin_monthly_total := revenue_monthly_recurring.action(
name: '${r.name}_margin_monthly'
descr: 'Monthly margin for ${r.name}'
action: .substract
rows: [cogs_monthly_recurring]
tags: 'name:${r.name}'
)!
return action
}

View File

@@ -0,0 +1,28 @@
module bizmodel
import freeflowuniverse.herolib.core.playbook { Action }
// revenue_total calculates and aggregates the total revenue and cost of goods sold (COGS) for the business model
fn (mut sim BizModel) revenue_total() ! {
mut sheet:= sim.sheet
mut revenue_total := sheet.group2row(name:"revenue_total", include:['rev'], tags:"total", descr:'total revenue.')!
mut cogs_total := sheet.group2row(name:"cogs_total", include:['cogs'], tags:"total", descr:'total cogs.')!
}
fn (mut sim BizModel) revenue_name_total(action Action) !Action {
mut r := get_action_descr(action)!
mut product := sim.products[r.name]
mut sheet:= sim.sheet
mut revenue_total := sheet.group2row(name:"${r.name}_revenue_total", include:['rev:${r.name}'], tags:"name:${r.name}", descr:'total revenue for ${r.name}.')!
mut cogs_total := sheet.group2row(name:"${r.name}_cogs_total", include:['cogs:${r.name}'], tags:"name:${r.name}", descr:'total cogs for ${r.name}.')!
return action
}

View File

@@ -0,0 +1,37 @@
module bizmodel
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.playbook { Action }
pub struct RowDescrFields{
pub mut:
name string
title string
description string
}
fn get_action_descr(action Action) !RowDescrFields {
mut r:=RowDescrFields{}
r.name= action.params.get_default('name', '')!
r.description= action.params.get_default('descr', '')!
if r.description.len == 0 {
r.description = action.params.get_default('description', '')!
}
if r.name.len == 0 {
// make name ourselves
r.name = texttools.name_fix(r.description)
}
r.name = texttools.name_fix(r.name)
if r.name.len == 0 {
return error('name and description is empty for ${action}')
}
r.name = r.name.replace('_', ' ').replace('-', ' ')
r.description = r.description.replace('_', ' ').replace('-', ' ')
return r
}

View File

@@ -19,7 +19,7 @@ pub struct RowActionArgs {
pub mut:
name string
action RowAction
val f64
val ?f64
rows []&Row
tags string
descr string
@@ -98,15 +98,16 @@ pub fn (mut r Row) action(args_ RowActionArgs) !&Row {
}
}
}
if args.val > 0.0 {
val := args.val or { 999999999 }
if val != 999999999 {
if args.action == .add {
row_result.cells[x].val = row_result.cells[x].val + args.val
row_result.cells[x].val = row_result.cells[x].val + val
} else if args.action == .substract {
row_result.cells[x].val = row_result.cells[x].val - args.val
row_result.cells[x].val = row_result.cells[x].val - val
} else if args.action == .multiply {
row_result.cells[x].val = row_result.cells[x].val * args.val
row_result.cells[x].val = row_result.cells[x].val * val
} else if args.action == .divide {
row_result.cells[x].val = row_result.cells[x].val / args.val
row_result.cells[x].val = row_result.cells[x].val / val
} else if args.action == .aggregate {
row_result.cells[x].val = row_result.cells[x].val + prevval
prevval = row_result.cells[x].val
@@ -115,12 +116,12 @@ pub fn (mut r Row) action(args_ RowActionArgs) !&Row {
} else if args.action == .roundint {
row_result.cells[x].val = int(row_result.cells[x].val)
} else if args.action == .max {
if args.val > row_result.cells[x].val {
row_result.cells[x].val = args.val
if val > row_result.cells[x].val {
row_result.cells[x].val = val
}
} else if args.action == .min {
if args.val < row_result.cells[x].val {
row_result.cells[x].val = args.val
if val < row_result.cells[x].val {
row_result.cells[x].val = val
}
} else {
return error('Action wrongly specified for ${r} with\nargs:${args}')
@@ -141,9 +142,8 @@ pub fn (mut r Row) action(args_ RowActionArgs) !&Row {
return row_result
}
// pub fn (mut r Row) add(name string, r2 Row) !&Row {
// return r.action(name:name, rows:[]r2, tags:r.tags)
// }
pub fn (mut r Row) delay(monthdelay int) ! {
mut todelay := []f64{}
for x in 0 .. r.sheet.nrcol {

View File

@@ -23,7 +23,7 @@ pub mut:
nr_columns int = 0 //number of columns to show in the table, 0 is all
description bool //show description in the table
aggrtype bool = true //show aggregate type in the table
tags bool //show tags in the table
tags bool = true //show tags in the table
subgroup bool //show subgroup in the table
}
// calculate_column_widths calculates the maximum width for each column
@@ -31,8 +31,14 @@ fn calculate_column_widths(rows [][]string) []int {
if rows.len == 0 {
return []int{}
}
mut max_nr_cols := 0
for row in rows {
if row.len > max_nr_cols {
max_nr_cols = row.len
}
}
mut widths := []int{len: rows[0].len, init: 0}
mut widths := []int{len: max_nr_cols, init: 0}
for _, row in rows {
for i, cell in row {
if cell.len > widths[i] {
@@ -124,6 +130,32 @@ pub fn (mut s Sheet) pprint(args PPrintArgs) ! {
}
}
if args.nr_columns > 0 {
mut data_start_index := 1 // for row.name
if args.description {
data_start_index++
}
if args.aggrtype {
data_start_index++
}
if args.tags {
data_start_index++
}
if args.subgroup {
data_start_index++
}
max_cols := data_start_index + args.nr_columns
mut new_all_rows := [][]string{}
for i, row in all_rows {
if row.len > max_cols {
new_all_rows << row[0..max_cols]
} else {
new_all_rows << row
}
}
all_rows = new_all_rows.clone()
}
// Calculate column widths
widths := calculate_column_widths(all_rows)

View File

@@ -50,7 +50,15 @@ pub fn (params &Params) get_int(key string) !int {
}
pub fn (params &Params) get_float(key string) !f64 {
valuestr := params.get(key)!
mut valuestr := params.get(key)!
if valuestr.contains("%"){
valuestr = valuestr.replace('%', '')
mut vs:=strconv.atof64(valuestr) or {
return error('Parameter ${key} = ${valuestr} is not a valid 64-bit float')
}
vs=vs/100
return vs
}
return strconv.atof64(valuestr) or {
return error('Parameter ${key} = ${valuestr} is not a valid 64-bit float')
}