ixbrl-viewer 1.4.1__py3-none-any.whl → 1.4.86__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iXBRLViewerPlugin/__init__.py +231 -127
- iXBRLViewerPlugin/_version.py +33 -3
- iXBRLViewerPlugin/constants.py +96 -2
- iXBRLViewerPlugin/featureConfig.py +8 -1
- iXBRLViewerPlugin/iXBRLViewer.py +356 -214
- iXBRLViewerPlugin/plugin.py +12 -0
- iXBRLViewerPlugin/ui.py +81 -50
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +12 -5
- iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
- iXBRLViewerPlugin/viewer/src/data/utr.json +1244 -0
- iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
- iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
- iXBRLViewerPlugin/viewer/src/html/footnote-details.html +2 -2
- iXBRLViewerPlugin/viewer/src/html/inspector.html +352 -197
- iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/scale.json +15 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/da/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/scale.json +15 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/de/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +73 -23
- iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +87 -37
- iXBRLViewerPlugin/viewer/src/i18n/fr/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/scale.json +15 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/fr/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/scale.json +15 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/nl/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/balancetypes.json +4 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/currencies.json +13 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/datatypes.json +9 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/labelroles.json +24 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/referenceparts.json +10 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/scale.json +15 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/tooltips.json +17 -0
- iXBRLViewerPlugin/viewer/src/i18n/uk/translation.json +179 -0
- iXBRLViewerPlugin/viewer/src/icons/calculator.svg +13 -0
- iXBRLViewerPlugin/viewer/src/icons/circle-cross.svg +11 -0
- iXBRLViewerPlugin/viewer/src/icons/circle-tick.svg +11 -0
- iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
- iXBRLViewerPlugin/viewer/src/icons/dimension.svg +1 -5
- iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
- iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
- iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
- iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
- iXBRLViewerPlugin/viewer/src/js/accordian.js +5 -4
- iXBRLViewerPlugin/viewer/src/js/aspect.js +29 -10
- iXBRLViewerPlugin/viewer/src/js/aspect.test.js +40 -31
- iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
- iXBRLViewerPlugin/viewer/src/js/calculation.js +213 -0
- iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
- iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +187 -0
- iXBRLViewerPlugin/viewer/src/js/chart.js +26 -24
- iXBRLViewerPlugin/viewer/src/js/chart.test.js +10 -9
- iXBRLViewerPlugin/viewer/src/js/concept.js +37 -4
- iXBRLViewerPlugin/viewer/src/js/concept.test.js +30 -6
- iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
- iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
- iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
- iXBRLViewerPlugin/viewer/src/js/docOrderIndex.js +7 -7
- iXBRLViewerPlugin/viewer/src/js/fact.js +156 -59
- iXBRLViewerPlugin/viewer/src/js/fact.test.js +160 -29
- iXBRLViewerPlugin/viewer/src/js/factset.js +64 -15
- iXBRLViewerPlugin/viewer/src/js/factset.test.js +102 -31
- iXBRLViewerPlugin/viewer/src/js/footnote.js +8 -2
- iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
- iXBRLViewerPlugin/viewer/src/js/inspector.js +747 -221
- iXBRLViewerPlugin/viewer/src/js/inspector.test.js +143 -25
- iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
- iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
- iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +391 -262
- iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +134 -20
- iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
- iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
- iXBRLViewerPlugin/viewer/src/js/number-matcher.js +7 -3
- iXBRLViewerPlugin/viewer/src/js/number-matcher.test.js +4 -0
- iXBRLViewerPlugin/viewer/src/js/outline.js +34 -13
- iXBRLViewerPlugin/viewer/src/js/outline.test.js +97 -91
- iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
- iXBRLViewerPlugin/viewer/src/js/report.js +260 -351
- iXBRLViewerPlugin/viewer/src/js/report.test.js +95 -27
- iXBRLViewerPlugin/viewer/src/js/reportset.js +264 -0
- iXBRLViewerPlugin/viewer/src/js/reportset.test.js +357 -0
- iXBRLViewerPlugin/viewer/src/js/search.js +72 -38
- iXBRLViewerPlugin/viewer/src/js/search.test.js +184 -84
- iXBRLViewerPlugin/viewer/src/js/summary.js +34 -8
- iXBRLViewerPlugin/viewer/src/js/summary.test.js +69 -25
- iXBRLViewerPlugin/viewer/src/js/tableExport.js +9 -9
- iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
- iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
- iXBRLViewerPlugin/viewer/src/js/test-utils.js +46 -0
- iXBRLViewerPlugin/viewer/src/js/theme.js +50 -0
- iXBRLViewerPlugin/viewer/src/js/unit.js +90 -32
- iXBRLViewerPlugin/viewer/src/js/unit.test.js +62 -25
- iXBRLViewerPlugin/viewer/src/js/util.js +94 -0
- iXBRLViewerPlugin/viewer/src/js/util.test.js +33 -1
- iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
- iXBRLViewerPlugin/viewer/src/js/viewer.js +205 -181
- iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
- iXBRLViewerPlugin/viewer/src/less/accordian.less +10 -6
- iXBRLViewerPlugin/viewer/src/less/block-list.less +16 -5
- iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
- iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
- iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
- iXBRLViewerPlugin/viewer/src/less/colours.less +32 -20
- iXBRLViewerPlugin/viewer/src/less/common.less +3 -3
- iXBRLViewerPlugin/viewer/src/less/components.less +6 -4
- iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
- iXBRLViewerPlugin/viewer/src/less/dialog.less +21 -14
- iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
- iXBRLViewerPlugin/viewer/src/less/inspector.less +1045 -726
- iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
- iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
- iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
- iXBRLViewerPlugin/viewer/src/less/tabs.less +9 -9
- iXBRLViewerPlugin/viewer/src/less/text-block-viewer.less +2 -0
- iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
- iXBRLViewerPlugin/viewer/src/less/validation-report.less +2 -3
- iXBRLViewerPlugin/viewer/src/less/viewer.less +105 -74
- iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
- iXBRLViewerPlugin/xhtmlserialize.py +59 -45
- {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/METADATA +181 -50
- ixbrl_viewer-1.4.86.dist-info/RECORD +217 -0
- {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/WHEEL +1 -1
- ixbrl_viewer-1.4.1.dist-info/LICENSE → ixbrl_viewer-1.4.86.dist-info/licenses/LICENSE.md +8 -14
- {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/top_level.txt +0 -1
- iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
- iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
- ixbrl_viewer-1.4.1.dist-info/RECORD +0 -155
- tests/__init__.py +0 -0
- tests/puppeteer/framework/core_elements.js +0 -117
- tests/puppeteer/framework/page_objects/doc_frame.js +0 -105
- tests/puppeteer/framework/page_objects/fact_details_panel.js +0 -80
- tests/puppeteer/framework/page_objects/search_panel.js +0 -76
- tests/puppeteer/framework/page_objects/toolbar.js +0 -18
- tests/puppeteer/framework/utils.js +0 -3
- tests/puppeteer/framework/viewer_page.js +0 -103
- tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
- tests/puppeteer/test_filings/filing_documents_smoke_test.zip +0 -0
- tests/puppeteer/test_filings/highlights.zip +0 -0
- tests/puppeteer/tests/fact_properties.test.js +0 -78
- tests/puppeteer/tests/highlight.test.js +0 -186
- tests/puppeteer/tests/search.test.js +0 -86
- tests/puppeteer/tools/generate.sh +0 -15
- tests/unit_tests/__init__.py +0 -0
- tests/unit_tests/iXBRLViewerPlugin/__init__.py +0 -0
- tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py +0 -39
- tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +0 -641
- tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py +0 -310
- {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info/licenses}/NOTICE +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import { setDefault } from './util.js';
|
|
5
|
+
import { Interval } from './interval.js';
|
|
6
|
+
import { FactSet } from './factset.js';
|
|
7
|
+
import { CALC11_ARCROLE, CALC_ARCROLE } from './util.js';
|
|
8
|
+
|
|
9
|
+
export class Calculation {
|
|
10
|
+
|
|
11
|
+
constructor(fact, calc11) {
|
|
12
|
+
this.fact = fact;
|
|
13
|
+
this.calc11 = calc11;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Resolve calculation relationships to a map of maps of arrays
|
|
17
|
+
* (ELR->conceptName->facts) */
|
|
18
|
+
calculationFacts() {
|
|
19
|
+
const fact = this.fact;
|
|
20
|
+
const report = fact.report;
|
|
21
|
+
if (!this._conceptToFact) {
|
|
22
|
+
const ctf = {};
|
|
23
|
+
for (const version of [CALC_ARCROLE, CALC11_ARCROLE]) {
|
|
24
|
+
const rels = report.getChildRelationships(fact.conceptName(), version)
|
|
25
|
+
for (const [elr, rr] of Object.entries(rels)) {
|
|
26
|
+
setDefault(ctf, version, {});
|
|
27
|
+
ctf[version][elr] = {};
|
|
28
|
+
if (rr.length > 0) {
|
|
29
|
+
const otherFacts = report.getAlignedFacts(fact, {"c": rr.map(r => r.t )});
|
|
30
|
+
otherFacts.forEach(otherFact => setDefault(ctf[version][elr], otherFact.conceptName(), new FactSet()).add(otherFact));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
this._conceptToFact = ctf;
|
|
35
|
+
}
|
|
36
|
+
return this._conceptToFact;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
hasCalculations() {
|
|
40
|
+
const ctf = this.calculationFacts();
|
|
41
|
+
return Object.keys(ctf).length > 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
resolvedCalculations() {
|
|
45
|
+
const calculations = [];
|
|
46
|
+
const ctf = this.calculationFacts();
|
|
47
|
+
for (const [version, o] of Object.entries(ctf)) {
|
|
48
|
+
for (const [elr, concepts] of Object.entries(o)) {
|
|
49
|
+
if (Object.keys(concepts).length > 0) {
|
|
50
|
+
calculations.push(this.resolvedCalculation(elr, version));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return calculations;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* Select the ELR which is the best match for a given array of facts
|
|
59
|
+
*/
|
|
60
|
+
bestELRForFactSet(facts) {
|
|
61
|
+
const ctf = this.calculationFacts();
|
|
62
|
+
let bestMatchELR = "";
|
|
63
|
+
let bestMatchCount = -1;
|
|
64
|
+
for (const [version, o] of Object.entries(ctf)) {
|
|
65
|
+
for (const [elr, rr] of Object.entries(o)) {
|
|
66
|
+
let matchCount = 0;
|
|
67
|
+
for (const [concept, calcFactSet] of Object.entries(rr)) {
|
|
68
|
+
let matched = 0;
|
|
69
|
+
for (const calcFact of calcFactSet.items()) {
|
|
70
|
+
if (facts.includes(calcFact.vuid)) {
|
|
71
|
+
matched = 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
matchCount += matched;
|
|
75
|
+
}
|
|
76
|
+
if (matchCount/Object.keys(rr).length > bestMatchCount) {
|
|
77
|
+
bestMatchCount = matchCount/Object.keys(rr).length;
|
|
78
|
+
bestMatchELR = elr;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return bestMatchELR;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/*
|
|
86
|
+
* Returns a ResolvedCalculation object for the specified ELR
|
|
87
|
+
*/
|
|
88
|
+
resolvedCalculation(elr, version) {
|
|
89
|
+
const calcFacts = this.calculationFacts()[version][elr];
|
|
90
|
+
const report = this.fact.report;
|
|
91
|
+
const rels = report.getChildRelationships(this.fact.conceptName(), version)[elr];
|
|
92
|
+
const resolvedCalcClass = this.calc11 ? ResolvedCalc11Calculation : ResolvedLegacyCalculation;
|
|
93
|
+
const resolvedCalculation = new resolvedCalcClass(elr, this.fact, version);
|
|
94
|
+
for (const r of rels) {
|
|
95
|
+
const factset = calcFacts[r.t] ?? new FactSet();
|
|
96
|
+
resolvedCalculation.addRow(new CalculationContribution(report.getConcept(r.t), r.w, factset));
|
|
97
|
+
}
|
|
98
|
+
return resolvedCalculation;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class CalculationContribution {
|
|
103
|
+
|
|
104
|
+
constructor(concept, weight, facts) {
|
|
105
|
+
this.concept = concept;
|
|
106
|
+
this.weight = weight;
|
|
107
|
+
this.facts = facts;
|
|
108
|
+
|
|
109
|
+
if (weight == 1) {
|
|
110
|
+
this.weightSign = '+';
|
|
111
|
+
}
|
|
112
|
+
else if (weight == -1) {
|
|
113
|
+
this.weightSign = '-';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.weightSign = weight.toString();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
contributionInterval() {
|
|
121
|
+
const intersection = this.facts.valueIntersection();
|
|
122
|
+
if (intersection === undefined) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return intersection.times(this.weight);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class AbstractResolvedCalculation {
|
|
130
|
+
|
|
131
|
+
constructor(elr, fact, relationshipVersion) {
|
|
132
|
+
this.totalFact = fact;
|
|
133
|
+
this.totalFactSet = new FactSet(fact.report.getAlignedFacts(fact));
|
|
134
|
+
this.elr = elr;
|
|
135
|
+
this.rows = [];
|
|
136
|
+
this.relationshipVersion = relationshipVersion;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
addRow(contribution) {
|
|
140
|
+
this.rows.push(contribution);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
binds() {
|
|
144
|
+
return this.rows.some((r) => !r.facts.isEmpty());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
unchecked() {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export class ResolvedCalc11Calculation extends AbstractResolvedCalculation {
|
|
153
|
+
|
|
154
|
+
calculatedTotalInterval() {
|
|
155
|
+
let total = new Interval(0, 0);
|
|
156
|
+
for (const item of this.rows) {
|
|
157
|
+
if (!item.facts.isEmpty()) {
|
|
158
|
+
const contribution = item.contributionInterval();
|
|
159
|
+
if (contribution === undefined) {
|
|
160
|
+
// Inconsistent duplicates
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
total = total.plus(contribution);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return total;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/*
|
|
171
|
+
* Is the calculation consistent under Calculations v1.1 rules?
|
|
172
|
+
*/
|
|
173
|
+
isConsistent() {
|
|
174
|
+
const cti = this.calculatedTotalInterval();
|
|
175
|
+
const ti = this.totalFactSet.valueIntersection();
|
|
176
|
+
return cti !== undefined && cti.intersection(ti) !== undefined;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export class ResolvedLegacyCalculation extends AbstractResolvedCalculation {
|
|
181
|
+
|
|
182
|
+
binds() {
|
|
183
|
+
return this.relationshipVersion == CALC_ARCROLE && super.binds() && this.rows.every((r) => r.facts.completeDuplicates());
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
unchecked() {
|
|
187
|
+
return super.binds() && !this.binds();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
uncheckedDueToVersionMismatch() {
|
|
191
|
+
return this.relationshipVersion !== CALC_ARCROLE;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
calculatedTotal() {
|
|
195
|
+
let total = new Decimal(0);
|
|
196
|
+
|
|
197
|
+
for (const row of this.rows) {
|
|
198
|
+
if (!row.facts.isConsistent()) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
if (!row.facts.isEmpty()) {
|
|
202
|
+
// Calculation does not bind if there are consistent (non-exact)
|
|
203
|
+
// duplicates, so we can just take the first duplicate value
|
|
204
|
+
total = total.plus(row.facts.items()[0].roundedValue().times(row.weight));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return total;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
isConsistent() {
|
|
211
|
+
return this.calculatedTotal().equals(this.totalFact.roundedValue());
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// See COPYRIGHT.md for copyright information
|
|
2
|
+
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import { FactSet } from "./factset.js";
|
|
5
|
+
import { Fact } from "./fact.js";
|
|
6
|
+
import { Interval } from "./interval.js";
|
|
7
|
+
import { NAMESPACE_ISO4217, viewerUniqueId } from "./util.js";
|
|
8
|
+
import { ReportSet } from "./reportset.js";
|
|
9
|
+
import { Calculation } from "./calculation.js";
|
|
10
|
+
import './test-utils.js';
|
|
11
|
+
|
|
12
|
+
const testReportData = {
|
|
13
|
+
"prefixes": {
|
|
14
|
+
"eg": "http://www.example.com",
|
|
15
|
+
"iso4217": NAMESPACE_ISO4217,
|
|
16
|
+
"e": "http://example.com/entity",
|
|
17
|
+
"group": "http://example.com/group",
|
|
18
|
+
},
|
|
19
|
+
"concepts": {
|
|
20
|
+
"eg:Total": {
|
|
21
|
+
"labels": {
|
|
22
|
+
"std": {
|
|
23
|
+
"en": "Total"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"eg:Item1": {
|
|
28
|
+
"labels": {
|
|
29
|
+
"std": {
|
|
30
|
+
"en": "Item 1"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"eg:Item2": {
|
|
35
|
+
"labels": {
|
|
36
|
+
"std": {
|
|
37
|
+
"en": "Item 2"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"eg:Total2": {
|
|
42
|
+
"labels": {
|
|
43
|
+
"std": {
|
|
44
|
+
"en": "Total 2"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
"facts": {
|
|
50
|
+
},
|
|
51
|
+
"rels": {
|
|
52
|
+
"calc": {
|
|
53
|
+
"group": {
|
|
54
|
+
"eg:Total": [
|
|
55
|
+
{"t": "eg:Item1", "w": 1},
|
|
56
|
+
{"t": "eg:Item2", "w": -1}
|
|
57
|
+
],
|
|
58
|
+
"eg:Total2": [
|
|
59
|
+
{"t": "eg:Item1", "w": 2},
|
|
60
|
+
{"t": "eg:Item2", "w": -2}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function testReportSet(facts) {
|
|
68
|
+
// Deep copy of standing data
|
|
69
|
+
const data = JSON.parse(JSON.stringify(testReportData));
|
|
70
|
+
data.facts = facts;
|
|
71
|
+
const reportset = new ReportSet(data);
|
|
72
|
+
reportset._initialize();
|
|
73
|
+
return reportset;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function testFact(aspectData, value, decimals) {
|
|
77
|
+
const factData = { "a": aspectData, "v": value, "d": decimals};
|
|
78
|
+
return factData;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getFact(reportSet, id) {
|
|
82
|
+
return reportSet.getItemById(viewerUniqueId(0, id));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("Simple consistent calculation", () => {
|
|
86
|
+
const reportSet = testReportSet({
|
|
87
|
+
"f1": testFact({"c": "eg:Total", "u": "iso2417:GBP"}, 10000, -3),
|
|
88
|
+
"f2": testFact({"c": "eg:Item1", "u": "iso2417:GBP"}, 12000, -3),
|
|
89
|
+
"f3": testFact({"c": "eg:Item2", "u": "iso2417:GBP"}, 2000, -3),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("Calc 1.1 total", () => {
|
|
93
|
+
const calc = new Calculation(getFact(reportSet, "f1"), true);
|
|
94
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
95
|
+
const rCalcs = calc.resolvedCalculations();
|
|
96
|
+
expect(rCalcs.length).toBe(1);
|
|
97
|
+
const rCalc = rCalcs[0];
|
|
98
|
+
expect(rCalc.elr).toBe("group");
|
|
99
|
+
expect(rCalc.calculatedTotalInterval()).toEqual(new Interval(9000, 11000));
|
|
100
|
+
expect(Interval.fromFact(rCalc.totalFact)).toEqual(new Interval(9500, 10500));
|
|
101
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
102
|
+
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("Calc contributor", () => {
|
|
106
|
+
const calc11 = new Calculation(getFact(reportSet, "f2"), true);
|
|
107
|
+
expect(calc11.hasCalculations()).toBe(false);
|
|
108
|
+
const calc10 = new Calculation(getFact(reportSet, "f2"), false);
|
|
109
|
+
expect(calc10.hasCalculations()).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("Calc 1.0 total", () => {
|
|
113
|
+
const calc = new Calculation(getFact(reportSet, "f1"), false);
|
|
114
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
115
|
+
const rCalcs = calc.resolvedCalculations();
|
|
116
|
+
expect(rCalcs.length).toBe(1);
|
|
117
|
+
const rCalc = rCalcs[0];
|
|
118
|
+
expect(rCalc.elr).toBe("group");
|
|
119
|
+
expect(rCalc.calculatedTotal()).toEqual(new Decimal(10000));
|
|
120
|
+
expect(rCalc.unchecked()).toBe(false);
|
|
121
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("Consistent only under 1.1", () => {
|
|
126
|
+
const reportSet = testReportSet({
|
|
127
|
+
"f1": testFact({"c": "eg:Total", "u": "iso2417:GBP"}, 11000, -3),
|
|
128
|
+
"f2": testFact({"c": "eg:Item1", "u": "iso2417:GBP"}, 12000, -3),
|
|
129
|
+
"f3": testFact({"c": "eg:Item2", "u": "iso2417:GBP"}, 2000, -3),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("Calc 1.1 total", () => {
|
|
133
|
+
const calc = new Calculation(getFact(reportSet, "f1"), true);
|
|
134
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
135
|
+
const rCalcs = calc.resolvedCalculations();
|
|
136
|
+
expect(rCalcs.length).toBe(1);
|
|
137
|
+
const rCalc = rCalcs[0];
|
|
138
|
+
expect(rCalc.elr).toBe("group");
|
|
139
|
+
expect(rCalc.calculatedTotalInterval()).toEqual(new Interval(9000, 11000));
|
|
140
|
+
expect(Interval.fromFact(rCalc.totalFact)).toEqual(new Interval(10500, 11500));
|
|
141
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
142
|
+
|
|
143
|
+
expect(rCalc.rows[0].concept.name).toBe("eg:Item1");
|
|
144
|
+
expect(rCalc.rows[0].weight).toBe(1);
|
|
145
|
+
expect(rCalc.rows[0].weightSign).toBe("+");
|
|
146
|
+
expect(rCalc.rows[0].facts.size()).toBe(1);
|
|
147
|
+
|
|
148
|
+
expect(rCalc.rows[1].concept.name).toBe("eg:Item2");
|
|
149
|
+
expect(rCalc.rows[1].weight).toBe(-1);
|
|
150
|
+
expect(rCalc.rows[1].weightSign).toBe("-");
|
|
151
|
+
expect(rCalc.rows[1].facts.size()).toBe(1);
|
|
152
|
+
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("Calc contributor", () => {
|
|
156
|
+
const calc11 = new Calculation(getFact(reportSet, "f2"), true);
|
|
157
|
+
expect(calc11.hasCalculations()).toBe(false);
|
|
158
|
+
const calc10 = new Calculation(getFact(reportSet, "f2"), false);
|
|
159
|
+
expect(calc10.hasCalculations()).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("Calc 1.0 total", () => {
|
|
163
|
+
const calc = new Calculation(getFact(reportSet, "f1"), false);
|
|
164
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
165
|
+
const rCalcs = calc.resolvedCalculations();
|
|
166
|
+
expect(rCalcs.length).toBe(1);
|
|
167
|
+
const rCalc = rCalcs[0];
|
|
168
|
+
expect(rCalc.elr).toBe("group");
|
|
169
|
+
expect(rCalc.calculatedTotal()).toEqual(new Decimal(10000));
|
|
170
|
+
expect(rCalc.unchecked()).toBe(false);
|
|
171
|
+
expect(rCalc.isConsistent()).toBe(false);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("Consistent duplicate contributor", () => {
|
|
177
|
+
const reportSet = testReportSet({
|
|
178
|
+
"f1": testFact({"c": "eg:Total", "u": "iso2417:GBP"}, 10000, -3),
|
|
179
|
+
"f2": testFact({"c": "eg:Item1", "u": "iso2417:GBP"}, 12000, -3),
|
|
180
|
+
"f3": testFact({"c": "eg:Item2", "u": "iso2417:GBP"}, 2000, -3),
|
|
181
|
+
"f4": testFact({"c": "eg:Item2", "u": "iso2417:GBP"}, 1990, -1),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("Calc 1.1 total", () => {
|
|
185
|
+
const calc = new Calculation(getFact(reportSet, "f1"), true);
|
|
186
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
187
|
+
const rCalcs = calc.resolvedCalculations();
|
|
188
|
+
expect(rCalcs.length).toBe(1);
|
|
189
|
+
const rCalc = rCalcs[0];
|
|
190
|
+
expect(rCalc.elr).toBe("group");
|
|
191
|
+
expect(rCalc.calculatedTotalInterval()).toEqual(new Interval(9505, 10515));
|
|
192
|
+
expect(Interval.fromFact(rCalc.totalFact)).toEqual(new Interval(9500, 10500));
|
|
193
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
194
|
+
|
|
195
|
+
expect(rCalc.rows[0].concept.name).toBe("eg:Item1");
|
|
196
|
+
expect(rCalc.rows[0].weight).toBe(1);
|
|
197
|
+
expect(rCalc.rows[0].weightSign).toBe("+");
|
|
198
|
+
expect(rCalc.rows[0].facts.size()).toBe(1);
|
|
199
|
+
|
|
200
|
+
expect(rCalc.rows[1].concept.name).toBe("eg:Item2");
|
|
201
|
+
expect(rCalc.rows[1].weight).toBe(-1);
|
|
202
|
+
expect(rCalc.rows[1].weightSign).toBe("-");
|
|
203
|
+
expect(rCalc.rows[1].facts.size()).toBe(2);
|
|
204
|
+
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("Calc contributor", () => {
|
|
208
|
+
const calc11 = new Calculation(getFact(reportSet, "f2"), true);
|
|
209
|
+
expect(calc11.hasCalculations()).toBe(false);
|
|
210
|
+
const calc10 = new Calculation(getFact(reportSet, "f2"), false);
|
|
211
|
+
expect(calc10.hasCalculations()).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("Calc 1.0 total", () => {
|
|
215
|
+
const calc = new Calculation(getFact(reportSet, "f1"), false);
|
|
216
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
217
|
+
const rCalcs = calc.resolvedCalculations();
|
|
218
|
+
expect(rCalcs.length).toBe(1);
|
|
219
|
+
const rCalc = rCalcs[0];
|
|
220
|
+
expect(rCalc.elr).toBe("group");
|
|
221
|
+
expect(rCalc.calculatedTotal()).toEqual(new Decimal(10000));
|
|
222
|
+
expect(rCalc.unchecked()).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
describe("Single contributor", () => {
|
|
229
|
+
const reportSet = testReportSet({
|
|
230
|
+
"f1": testFact({"c": "eg:Total", "u": "iso2417:GBP"}, 10000, -3),
|
|
231
|
+
"f2": testFact({"c": "eg:Item1", "u": "iso2417:GBP"}, 9990, -1),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("Calc 1.1 total", () => {
|
|
235
|
+
const calc = new Calculation(getFact(reportSet, "f1"), true);
|
|
236
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
237
|
+
const rCalcs = calc.resolvedCalculations();
|
|
238
|
+
expect(rCalcs.length).toBe(1);
|
|
239
|
+
const rCalc = rCalcs[0];
|
|
240
|
+
expect(rCalc.elr).toBe("group");
|
|
241
|
+
expect(rCalc.calculatedTotalInterval()).toEqual(new Interval(9985, 9995));
|
|
242
|
+
expect(Interval.fromFact(rCalc.totalFact)).toEqual(new Interval(9500, 10500));
|
|
243
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
244
|
+
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("Calc 1.0 total", () => {
|
|
248
|
+
const calc = new Calculation(getFact(reportSet, "f1"), false);
|
|
249
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
250
|
+
const rCalcs = calc.resolvedCalculations();
|
|
251
|
+
expect(rCalcs.length).toBe(1);
|
|
252
|
+
const rCalc = rCalcs[0];
|
|
253
|
+
expect(rCalc.elr).toBe("group");
|
|
254
|
+
expect(rCalc.calculatedTotal()).toEqual(new Decimal(9990));
|
|
255
|
+
expect(rCalc.unchecked()).toBe(false);
|
|
256
|
+
expect(rCalc.isConsistent()).toBe(false);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe("Weights", () => {
|
|
261
|
+
const reportSet = testReportSet({
|
|
262
|
+
"f1": testFact({"c": "eg:Total2", "u": "iso2417:GBP"}, 4000, -3),
|
|
263
|
+
"f2": testFact({"c": "eg:Item1", "u": "iso2417:GBP"}, 3000, -3),
|
|
264
|
+
"f3": testFact({"c": "eg:Item2", "u": "iso2417:GBP"}, 1000, -3),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("Calc 1.1 total", () => {
|
|
268
|
+
const calc = new Calculation(getFact(reportSet, "f1"), true);
|
|
269
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
270
|
+
const rCalcs = calc.resolvedCalculations();
|
|
271
|
+
expect(rCalcs.length).toBe(1);
|
|
272
|
+
const rCalc = rCalcs[0];
|
|
273
|
+
expect(rCalc.elr).toBe("group");
|
|
274
|
+
expect(rCalc.calculatedTotalInterval()).toEqual(new Interval(2000, 6000));
|
|
275
|
+
expect(Interval.fromFact(rCalc.totalFact)).toEqual(new Interval(3500, 4500));
|
|
276
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
277
|
+
|
|
278
|
+
expect(rCalc.rows[0].concept.name).toBe("eg:Item1");
|
|
279
|
+
expect(rCalc.rows[0].weight).toBe(2);
|
|
280
|
+
expect(rCalc.rows[0].weightSign).toBe("2");
|
|
281
|
+
|
|
282
|
+
expect(rCalc.rows[1].concept.name).toBe("eg:Item2");
|
|
283
|
+
expect(rCalc.rows[1].weight).toBe(-2);
|
|
284
|
+
expect(rCalc.rows[1].weightSign).toBe("-2");
|
|
285
|
+
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("Calc contributor", () => {
|
|
289
|
+
const calc11 = new Calculation(getFact(reportSet, "f2"), true);
|
|
290
|
+
expect(calc11.hasCalculations()).toBe(false);
|
|
291
|
+
const calc10 = new Calculation(getFact(reportSet, "f2"), false);
|
|
292
|
+
expect(calc10.hasCalculations()).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("Calc 1.0 total", () => {
|
|
296
|
+
const calc = new Calculation(getFact(reportSet, "f1"), false);
|
|
297
|
+
expect(calc.hasCalculations()).toBe(true);
|
|
298
|
+
const rCalcs = calc.resolvedCalculations();
|
|
299
|
+
expect(rCalcs.length).toBe(1);
|
|
300
|
+
const rCalc = rCalcs[0];
|
|
301
|
+
expect(rCalc.elr).toBe("group");
|
|
302
|
+
expect(rCalc.calculatedTotal()).toEqual(new Decimal(4000));
|
|
303
|
+
expect(rCalc.unchecked()).toBe(false);
|
|
304
|
+
expect(rCalc.isConsistent()).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
});
|