Skip to content

Instantly share code, notes, and snippets.

@declann
Last active December 12, 2021 22:46
Show Gist options
  • Save declann/8ddd917912863d75641798a7bb52524e to your computer and use it in GitHub Desktop.
Save declann/8ddd917912863d75641798a7bb52524e to your computer and use it in GitHub Desktop.
saas model
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* unused harmony export subs_growth_pc */
/* unused harmony export subs_churn_pc */
/* unused harmony export subs_0_ */
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "h", function() { return year; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return CAC; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "g", function() { return subs_new_; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "f", function() { return subs_churned_; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "e", function() { return subs; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return expenses; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return revenue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return profit; });
/* harmony import */ var _rec_cul_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
const subs_growth_pc = ({
subs_growth_pc_in
}) => subs_growth_pc_in;
const subs_churn_pc = ({
subs_churn_pc_in
}) => subs_churn_pc_in;
const subs_0_ = ({
subs_0_in
}) => subs_0_in; // subs at start of year 0
const year = ({
year_in
}) => year_in;
const CAC = ({
CAC_in
}) => CAC_in;
const subs_new_ = ({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
}) => // its hard to find the year boundary needed here when I add expenses to model, when it worked without. Table and good error reporting is very important
year({
year_in
}) < 0 ? 0 : subs({
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
year_in: year({
year_in
}) - 1
}) * (subs_growth_pc({
subs_growth_pc_in
}) / 100);
const subs_churned_ = ({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
}) => (subs({
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
year_in: year({
year_in
}) - 1
}) + Object(_rec_cul_js__WEBPACK_IMPORTED_MODULE_0__["subs_new"])({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
})) * subs_churn_pc({
subs_churn_pc_in
}) / 100; // churn assumption also applies to new subs
// subs at end = prev subs at end + new subs@yr - churned subs@yr
const subs = ({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
}) => {
if (year({
year_in
}) < 0) return Object(_rec_cul_js__WEBPACK_IMPORTED_MODULE_0__["subs_0"])({
subs_0_in
});else return subs({
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
year_in: year({
year_in
}) - 1
}) + Object(_rec_cul_js__WEBPACK_IMPORTED_MODULE_0__["subs_new"])({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
}) - Object(_rec_cul_js__WEBPACK_IMPORTED_MODULE_0__["subs_churned"])({
year_in,
subs_churned_actual_to_in,
actuals_table_in,
subs_0_in,
subs_new_actual_to_in,
subs_growth_pc_in,
subs_churn_pc_in
});
}; // => can project subs given subs_0, growth and churn rates
const expenses = ({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in,
CAC_in
}) => Object(_rec_cul_js__WEBPACK_IMPORTED_MODULE_0__["subs_new"])({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
}) * CAC({
CAC_in
}); // error?
const revenue = ({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
}) => subs({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
}) * 100;
const profit = ({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
CAC_in
}) => revenue({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
}) - expenses({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in,
CAC_in
});
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "actuals_table", function() { return actuals_table; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_0", function() { return subs_0; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_new_actual", function() { return subs_new_actual; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_churned_actual", function() { return subs_churned_actual; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_new_actual_to", function() { return subs_new_actual_to; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_churned_actual_to", function() { return subs_churned_actual_to; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_new", function() { return subs_new; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_churned", function() { return subs_churned; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ea_start", function() { return ea_start; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ea_end", function() { return ea_end; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_ea", function() { return subs_ea; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_new_experience", function() { return subs_new_experience; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subs_churned_experience", function() { return subs_churned_experience; });
/* harmony import */ var _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subs", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["e"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "year", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["h"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "revenue", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["d"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expenses", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["b"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "profit", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["c"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "CAC", function() { return _expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__["a"]; });
// necessary
const actuals_table = ({
actuals_table_in
}) => actuals_table_in; // actuals
const subs_0 = ({
subs_0_in
}) => subs_0_in; // start at 100 subs (this line now redundant!)
const subs_new_actual = ({
actuals_table_in,
year_in
}) => actuals_table({
actuals_table_in
})[Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) + 1].subs_new_actual;
const subs_churned_actual = ({
actuals_table_in,
year_in
}) => actuals_table({
actuals_table_in
})[Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) + 1].subs_churned_actual; // year up to which actual data should be used, -1=use only expecteds
const subs_new_actual_to = ({
subs_new_actual_to_in
}) => subs_new_actual_to_in;
const subs_churned_actual_to = ({
subs_churned_actual_to_in
}) => subs_churned_actual_to_in; // interleave actuals into projections: this rebases expd using actuals?
const subs_new = ({
year_in,
subs_new_actual_to_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
}) => {
if (Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) <= subs_new_actual_to({
subs_new_actual_to_in
})) return subs_new_actual({
actuals_table_in,
year_in
});else return Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* subs_new_ */ "g"])({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in
}); // this is an override which uses itself: but it doesn't compile correctly, check memo-loader version?
};
const subs_churned = ({
year_in,
subs_churned_actual_to_in,
actuals_table_in,
subs_0_in,
subs_new_actual_to_in,
subs_growth_pc_in,
subs_churn_pc_in
}) => {
if (Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) <= subs_churned_actual_to({
subs_churned_actual_to_in
})) return subs_churned_actual({
actuals_table_in,
year_in
});else return Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* subs_churned_ */ "f"])({
year_in,
subs_0_in,
subs_new_actual_to_in,
actuals_table_in,
subs_growth_pc_in,
subs_churned_actual_to_in,
subs_churn_pc_in
});
}; // move away from experience.
// just look at projections
// => subs({year:x,actuals:x})
const ea_start = ({
ea_start_in
}) => ea_start_in;
const ea_end = ({
ea_end_in
}) => ea_end_in;
const subs_ea = ({
subs_0_in,
actuals_table_in,
subs_growth_pc_in,
subs_churn_pc_in,
ea_end_in,
ea_start_in
}) => [{
type: 'Expected',
function: 'subs',
value: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* subs */ "e"])({
subs_0_in,
actuals_table_in,
subs_growth_pc_in,
subs_churn_pc_in,
year_in: ea_end({
ea_end_in
}),
subs_new_actual_to_in: ea_start({
ea_start_in
}),
subs_churned_actual_to_in: ea_start({
ea_start_in
})
})
}, {
type: '-> Actual Sales',
// this impact will include expd churn on sales impact, alt. split could use 0 decrements and create a balancing 'confounding' amount
function: 'subs',
value: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* subs */ "e"])({
subs_0_in,
actuals_table_in,
subs_growth_pc_in,
subs_churn_pc_in,
year_in: ea_end({
ea_end_in
}),
subs_new_actual_to_in: ea_end({
ea_end_in
}),
subs_churned_actual_to_in: ea_start({
ea_start_in
})
})
}, {
type: '-> Actual Churn (=Actual)',
function: 'subs',
value: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* subs */ "e"])({
subs_0_in,
actuals_table_in,
subs_growth_pc_in,
subs_churn_pc_in,
year_in: ea_end({
ea_end_in
}),
subs_new_actual_to_in: ea_end({
ea_end_in
}),
subs_churned_actual_to_in: ea_end({
ea_end_in
})
})
}]; // todo use an impacts abstraction or move subtraction to VL
// reconcile actual to expected, experience=A-E
// in year
// this should be abstracted using a table for clarity
const subs_new_experience = ({
year_in,
actuals_table_in,
subs_0_in,
subs_churn_pc_in,
subs_growth_pc_in
}) => {
// now a fn on year
const subs_churned_actual_to_in = Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) - 1;
return subs_new({
year_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in,
subs_new_actual_to_in: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
})
}) - subs_new({
year_in,
actuals_table_in,
subs_0_in,
subs_churned_actual_to_in,
subs_churn_pc_in,
subs_growth_pc_in,
subs_new_actual_to_in: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) - 1
});
};
const subs_churned_experience = ({
year_in,
actuals_table_in,
subs_0_in,
subs_growth_pc_in,
subs_churn_pc_in
}) => {
// now a fn on year
const subs_new_actual_to_in = Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
});
return subs_churned({
year_in,
actuals_table_in,
subs_0_in,
subs_new_actual_to_in,
subs_growth_pc_in,
subs_churn_pc_in,
subs_churned_actual_to_in: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
})
}) - subs_churned({
year_in,
actuals_table_in,
subs_0_in,
subs_new_actual_to_in,
subs_growth_pc_in,
subs_churn_pc_in,
subs_churned_actual_to_in: Object(_expected_cul_js_cul_scope_id_1_cul_parent_scope_id_0_location_3f95bccec3f403c5faf2856865ba5649__WEBPACK_IMPORTED_MODULE_0__[/* year */ "h"])({
year_in
}) - 1
}); // todo project, constrain rec ordering somewhere
}; // re introspection: devtools can probably do this? But messy at that stage, so might not be an option
// export to excel with hacked formulae (=7+0*E8) for relationships is probably a nice short-term feature until I get the experience outside
// hacked formulae can easily be replaced by proper conversions if I convert JS to Excel in fut
// maybe this is important for explanation phase, more impt than technical/memo-loader?
/***/ })
/******/ ]);
});
//# sourceMappingURL=rec.js.map
{"version":3,"sources":["webpack:///../../../webpack/universalModuleDefinition","webpack:///../../../webpack/bootstrap","webpack:///../../../expected.cul.js","webpack:///../../../rec.cul.js"],"names":["subs_growth_pc","subs_growth_pc_in","subs_churn_pc","subs_churn_pc_in","subs_0","subs_0_in","year","year_in","CAC","CAC_in","subs_new","subs","subs_churned","expenses","revenue","profit","actuals_table","actuals_table_in","subs_new_actual","subs_churned_actual","subs_new_actual_to","subs_new_actual_to_in","subs_churned_actual_to","subs_churned_actual_to_in","subs_new_expected","subs_churned_expected","ea_start","ea_start_in","ea_end","ea_end_in","subs_ea","type","function","value","subs_new_experience","subs_churned_experience"],"mappings":"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","file":"rec.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse {\n\t\tvar a = factory();\n\t\tfor(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n\t}\n})(this, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n","export const subs_growth_pc = () => subs_growth_pc_in;\r\nexport const subs_churn_pc = () => subs_churn_pc_in;\r\nexport const subs_0 = () => subs_0_in; // subs at start of year 0\r\nexport const year = () => year_in;\r\nexport const CAC = () => CAC_in;\r\n\r\nexport const subs_new = () =>\r\n // its hard to find the year boundary needed here when I add expenses to model, when it worked without. Table and good error reporting is very important\r\n year() < 0 ? 0 : subs({ year_in: year() - 1 }) * (subs_growth_pc() / 100);\r\n\r\nexport const subs_churned = () =>\r\n ((subs({ year_in: year() - 1 }) + subs_new()) * subs_churn_pc()) / 100; // churn assumption also applies to new subs\r\n\r\n// subs at end = prev subs at end + new subs@yr - churned subs@yr\r\nexport const subs = () => {\r\n if (year() < 0) return subs_0();\r\n else return subs({ year_in: year() - 1 }) + subs_new() - subs_churned();\r\n};\r\n\r\n// => can project subs given subs_0, growth and churn rates\r\n\r\nexport const expenses = () => subs_new() * CAC(); // error?\r\nexport const revenue = () => subs() * 100;\r\nexport const profit = () => revenue() - expenses();\r\n","import {\r\n subs,\r\n subs_new as subs_new_expected,\r\n subs_churned as subs_churned_expected,\r\n year,\r\n revenue,\r\n expenses,\r\n profit,\r\n CAC,\r\n} from './expected.cul.js';\r\nexport { subs, year };\r\nexport { revenue, expenses, profit, CAC }; // necessary\r\n\r\nexport const actuals_table = () => actuals_table_in;\r\n\r\n// actuals\r\nexport const subs_0 = () => subs_0_in; // start at 100 subs (this line now redundant!)\r\nexport const subs_new_actual = () =>\r\n actuals_table()[year() + 1].subs_new_actual;\r\nexport const subs_churned_actual = () =>\r\n actuals_table()[year() + 1].subs_churned_actual;\r\n\r\n// year up to which actual data should be used, -1=use only expecteds\r\nexport const subs_new_actual_to = () => subs_new_actual_to_in;\r\nexport const subs_churned_actual_to = () => subs_churned_actual_to_in;\r\n\r\n// interleave actuals into projections: this rebases expd using actuals?\r\nexport const subs_new = () => {\r\n if (year() <= subs_new_actual_to()) return subs_new_actual();\r\n else return subs_new_expected(); // this is an override which uses itself: but it doesn't compile correctly, check memo-loader version?\r\n};\r\nexport const subs_churned = () => {\r\n if (year() <= subs_churned_actual_to()) return subs_churned_actual();\r\n else return subs_churned_expected();\r\n};\r\n\r\n// move away from experience.\r\n// just look at projections\r\n// => subs({year:x,actuals:x})\r\n\r\nexport const ea_start = () => ea_start_in;\r\nexport const ea_end = () => ea_end_in;\r\n\r\nexport const subs_ea = () => [\r\n {\r\n type: 'Expected',\r\n function: 'subs',\r\n value: subs({\r\n year_in: ea_end(),\r\n subs_new_actual_to_in: ea_start(),\r\n subs_churned_actual_to_in: ea_start(),\r\n }),\r\n },\r\n {\r\n type: '-> Actual Sales', // this impact will include expd churn on sales impact, alt. split could use 0 decrements and create a balancing 'confounding' amount\r\n function: 'subs',\r\n value: subs({\r\n year_in: ea_end(),\r\n subs_new_actual_to_in: ea_end(),\r\n subs_churned_actual_to_in: ea_start(),\r\n }),\r\n },\r\n {\r\n type: '-> Actual Churn (=Actual)',\r\n function: 'subs',\r\n value: subs({\r\n year_in: ea_end(),\r\n subs_new_actual_to_in: ea_end(),\r\n subs_churned_actual_to_in: ea_end(),\r\n }),\r\n },\r\n];\r\n// todo use an impacts abstraction or move subtraction to VL\r\n\r\n// reconcile actual to expected, experience=A-E\r\n// in year\r\n// this should be abstracted using a table for clarity\r\nexport const subs_new_experience = () => {\r\n // now a fn on year\r\n const subs_churned_actual_to_in = year() - 1;\r\n return (\r\n subs_new(/*_actual*/ { subs_new_actual_to_in: year() }) -\r\n subs_new(/*_expected*/ { subs_new_actual_to_in: year() - 1 })\r\n );\r\n};\r\n\r\nexport const subs_churned_experience = () => {\r\n // now a fn on year\r\n const subs_new_actual_to_in = year();\r\n return (\r\n subs_churned(/*_actual*/ { subs_churned_actual_to_in: year() }) -\r\n subs_churned(/*_expected*/ { subs_churned_actual_to_in: year() - 1 })\r\n ); // todo project, constrain rec ordering somewhere\r\n};\r\n\r\n// re introspection: devtools can probably do this? But messy at that stage, so might not be an option\r\n// export to excel with hacked formulae (=7+0*E8) for relationships is probably a nice short-term feature until I get the experience outside\r\n// hacked formulae can easily be replaced by proper conversions if I convert JS to Excel in fut\r\n// maybe this is important for explanation phase, more impt than technical/memo-loader?\r\n"],"sourceRoot":""}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment