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.
Files changed (190) hide show
  1. iXBRLViewerPlugin/__init__.py +231 -127
  2. iXBRLViewerPlugin/_version.py +33 -3
  3. iXBRLViewerPlugin/constants.py +96 -2
  4. iXBRLViewerPlugin/featureConfig.py +8 -1
  5. iXBRLViewerPlugin/iXBRLViewer.py +356 -214
  6. iXBRLViewerPlugin/plugin.py +12 -0
  7. iXBRLViewerPlugin/ui.py +81 -50
  8. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
  9. iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt +12 -5
  10. iXBRLViewerPlugin/viewer/i18next-parser.config.js +1 -1
  11. iXBRLViewerPlugin/viewer/src/data/utr.json +1244 -0
  12. iXBRLViewerPlugin/viewer/src/html/fact-details.html +69 -38
  13. iXBRLViewerPlugin/viewer/src/html/footer-logo.html +4 -0
  14. iXBRLViewerPlugin/viewer/src/html/footnote-details.html +2 -2
  15. iXBRLViewerPlugin/viewer/src/html/inspector.html +352 -197
  16. iXBRLViewerPlugin/viewer/src/i18n/cy/balancetypes.json +1 -0
  17. iXBRLViewerPlugin/viewer/src/i18n/cy/currencies.json +13 -0
  18. iXBRLViewerPlugin/viewer/src/i18n/cy/datatypes.json +9 -0
  19. iXBRLViewerPlugin/viewer/src/i18n/cy/labelroles.json +24 -0
  20. iXBRLViewerPlugin/viewer/src/i18n/cy/referenceparts.json +10 -0
  21. iXBRLViewerPlugin/viewer/src/i18n/cy/scale.json +16 -0
  22. iXBRLViewerPlugin/viewer/src/i18n/cy/tooltips.json +17 -0
  23. iXBRLViewerPlugin/viewer/src/i18n/cy/translation.json +179 -0
  24. iXBRLViewerPlugin/viewer/src/i18n/da/balancetypes.json +4 -0
  25. iXBRLViewerPlugin/viewer/src/i18n/da/currencies.json +13 -0
  26. iXBRLViewerPlugin/viewer/src/i18n/da/datatypes.json +9 -0
  27. iXBRLViewerPlugin/viewer/src/i18n/da/labelroles.json +24 -0
  28. iXBRLViewerPlugin/viewer/src/i18n/da/referenceparts.json +10 -0
  29. iXBRLViewerPlugin/viewer/src/i18n/da/scale.json +15 -0
  30. iXBRLViewerPlugin/viewer/src/i18n/da/tooltips.json +17 -0
  31. iXBRLViewerPlugin/viewer/src/i18n/da/translation.json +179 -0
  32. iXBRLViewerPlugin/viewer/src/i18n/de/balancetypes.json +4 -0
  33. iXBRLViewerPlugin/viewer/src/i18n/de/currencies.json +13 -0
  34. iXBRLViewerPlugin/viewer/src/i18n/de/datatypes.json +9 -0
  35. iXBRLViewerPlugin/viewer/src/i18n/de/labelroles.json +24 -0
  36. iXBRLViewerPlugin/viewer/src/i18n/de/referenceparts.json +10 -0
  37. iXBRLViewerPlugin/viewer/src/i18n/de/scale.json +15 -0
  38. iXBRLViewerPlugin/viewer/src/i18n/de/tooltips.json +17 -0
  39. iXBRLViewerPlugin/viewer/src/i18n/de/translation.json +179 -0
  40. iXBRLViewerPlugin/viewer/src/i18n/en/balancetypes.json +4 -0
  41. iXBRLViewerPlugin/viewer/src/i18n/en/datatypes.json +10 -0
  42. iXBRLViewerPlugin/viewer/src/i18n/en/labelroles.json +4 -0
  43. iXBRLViewerPlugin/viewer/src/i18n/en/scale.json +16 -0
  44. iXBRLViewerPlugin/viewer/src/i18n/en/tooltips.json +17 -0
  45. iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +73 -23
  46. iXBRLViewerPlugin/viewer/src/i18n/es/balancetypes.json +4 -0
  47. iXBRLViewerPlugin/viewer/src/i18n/es/datatypes.json +10 -0
  48. iXBRLViewerPlugin/viewer/src/i18n/es/labelroles.json +24 -0
  49. iXBRLViewerPlugin/viewer/src/i18n/es/scale.json +16 -0
  50. iXBRLViewerPlugin/viewer/src/i18n/es/tooltips.json +17 -0
  51. iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +87 -37
  52. iXBRLViewerPlugin/viewer/src/i18n/fr/balancetypes.json +4 -0
  53. iXBRLViewerPlugin/viewer/src/i18n/fr/currencies.json +13 -0
  54. iXBRLViewerPlugin/viewer/src/i18n/fr/datatypes.json +9 -0
  55. iXBRLViewerPlugin/viewer/src/i18n/fr/labelroles.json +24 -0
  56. iXBRLViewerPlugin/viewer/src/i18n/fr/referenceparts.json +10 -0
  57. iXBRLViewerPlugin/viewer/src/i18n/fr/scale.json +15 -0
  58. iXBRLViewerPlugin/viewer/src/i18n/fr/tooltips.json +17 -0
  59. iXBRLViewerPlugin/viewer/src/i18n/fr/translation.json +179 -0
  60. iXBRLViewerPlugin/viewer/src/i18n/nl/balancetypes.json +4 -0
  61. iXBRLViewerPlugin/viewer/src/i18n/nl/currencies.json +13 -0
  62. iXBRLViewerPlugin/viewer/src/i18n/nl/datatypes.json +9 -0
  63. iXBRLViewerPlugin/viewer/src/i18n/nl/labelroles.json +24 -0
  64. iXBRLViewerPlugin/viewer/src/i18n/nl/referenceparts.json +10 -0
  65. iXBRLViewerPlugin/viewer/src/i18n/nl/scale.json +15 -0
  66. iXBRLViewerPlugin/viewer/src/i18n/nl/tooltips.json +17 -0
  67. iXBRLViewerPlugin/viewer/src/i18n/nl/translation.json +179 -0
  68. iXBRLViewerPlugin/viewer/src/i18n/uk/balancetypes.json +4 -0
  69. iXBRLViewerPlugin/viewer/src/i18n/uk/currencies.json +13 -0
  70. iXBRLViewerPlugin/viewer/src/i18n/uk/datatypes.json +9 -0
  71. iXBRLViewerPlugin/viewer/src/i18n/uk/labelroles.json +24 -0
  72. iXBRLViewerPlugin/viewer/src/i18n/uk/referenceparts.json +10 -0
  73. iXBRLViewerPlugin/viewer/src/i18n/uk/scale.json +15 -0
  74. iXBRLViewerPlugin/viewer/src/i18n/uk/tooltips.json +17 -0
  75. iXBRLViewerPlugin/viewer/src/i18n/uk/translation.json +179 -0
  76. iXBRLViewerPlugin/viewer/src/icons/calculator.svg +13 -0
  77. iXBRLViewerPlugin/viewer/src/icons/circle-cross.svg +11 -0
  78. iXBRLViewerPlugin/viewer/src/icons/circle-tick.svg +11 -0
  79. iXBRLViewerPlugin/viewer/src/icons/dark-mode.svg +4 -0
  80. iXBRLViewerPlugin/viewer/src/icons/dimension.svg +1 -5
  81. iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
  82. iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
  83. iXBRLViewerPlugin/viewer/src/img/arelle-dark.svg +179 -0
  84. iXBRLViewerPlugin/viewer/src/img/inline-viewer-dark.svg +59 -0
  85. iXBRLViewerPlugin/viewer/src/js/accordian.js +5 -4
  86. iXBRLViewerPlugin/viewer/src/js/aspect.js +29 -10
  87. iXBRLViewerPlugin/viewer/src/js/aspect.test.js +40 -31
  88. iXBRLViewerPlugin/viewer/src/js/balance.js +14 -0
  89. iXBRLViewerPlugin/viewer/src/js/calculation.js +213 -0
  90. iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
  91. iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +187 -0
  92. iXBRLViewerPlugin/viewer/src/js/chart.js +26 -24
  93. iXBRLViewerPlugin/viewer/src/js/chart.test.js +10 -9
  94. iXBRLViewerPlugin/viewer/src/js/concept.js +37 -4
  95. iXBRLViewerPlugin/viewer/src/js/concept.test.js +30 -6
  96. iXBRLViewerPlugin/viewer/src/js/datatype.js +20 -0
  97. iXBRLViewerPlugin/viewer/src/js/datatype.test.js +62 -0
  98. iXBRLViewerPlugin/viewer/src/js/dialog.js +6 -4
  99. iXBRLViewerPlugin/viewer/src/js/docOrderIndex.js +7 -7
  100. iXBRLViewerPlugin/viewer/src/js/fact.js +156 -59
  101. iXBRLViewerPlugin/viewer/src/js/fact.test.js +160 -29
  102. iXBRLViewerPlugin/viewer/src/js/factset.js +64 -15
  103. iXBRLViewerPlugin/viewer/src/js/factset.test.js +102 -31
  104. iXBRLViewerPlugin/viewer/src/js/footnote.js +8 -2
  105. iXBRLViewerPlugin/viewer/src/js/index.js +11 -3
  106. iXBRLViewerPlugin/viewer/src/js/inspector.js +747 -221
  107. iXBRLViewerPlugin/viewer/src/js/inspector.test.js +143 -25
  108. iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
  109. iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
  110. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.js +391 -262
  111. iXBRLViewerPlugin/viewer/src/js/ixbrlviewer.test.js +134 -20
  112. iXBRLViewerPlugin/viewer/src/js/ixnode.js +1 -1
  113. iXBRLViewerPlugin/viewer/src/js/menu.js +25 -7
  114. iXBRLViewerPlugin/viewer/src/js/number-matcher.js +7 -3
  115. iXBRLViewerPlugin/viewer/src/js/number-matcher.test.js +4 -0
  116. iXBRLViewerPlugin/viewer/src/js/outline.js +34 -13
  117. iXBRLViewerPlugin/viewer/src/js/outline.test.js +97 -91
  118. iXBRLViewerPlugin/viewer/src/js/period.js +0 -1
  119. iXBRLViewerPlugin/viewer/src/js/report.js +260 -351
  120. iXBRLViewerPlugin/viewer/src/js/report.test.js +95 -27
  121. iXBRLViewerPlugin/viewer/src/js/reportset.js +264 -0
  122. iXBRLViewerPlugin/viewer/src/js/reportset.test.js +357 -0
  123. iXBRLViewerPlugin/viewer/src/js/search.js +72 -38
  124. iXBRLViewerPlugin/viewer/src/js/search.test.js +184 -84
  125. iXBRLViewerPlugin/viewer/src/js/summary.js +34 -8
  126. iXBRLViewerPlugin/viewer/src/js/summary.test.js +69 -25
  127. iXBRLViewerPlugin/viewer/src/js/tableExport.js +9 -9
  128. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.js +34 -0
  129. iXBRLViewerPlugin/viewer/src/js/taxonomynamer.test.js +32 -0
  130. iXBRLViewerPlugin/viewer/src/js/test-utils.js +46 -0
  131. iXBRLViewerPlugin/viewer/src/js/theme.js +50 -0
  132. iXBRLViewerPlugin/viewer/src/js/unit.js +90 -32
  133. iXBRLViewerPlugin/viewer/src/js/unit.test.js +62 -25
  134. iXBRLViewerPlugin/viewer/src/js/util.js +94 -0
  135. iXBRLViewerPlugin/viewer/src/js/util.test.js +33 -1
  136. iXBRLViewerPlugin/viewer/src/js/utr.js +27 -0
  137. iXBRLViewerPlugin/viewer/src/js/viewer.js +205 -181
  138. iXBRLViewerPlugin/viewer/src/js/viewerOptions.js +0 -2
  139. iXBRLViewerPlugin/viewer/src/less/accordian.less +10 -6
  140. iXBRLViewerPlugin/viewer/src/less/block-list.less +16 -5
  141. iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
  142. iXBRLViewerPlugin/viewer/src/less/chart.less +8 -5
  143. iXBRLViewerPlugin/viewer/src/less/colours-dark-mode.less +40 -0
  144. iXBRLViewerPlugin/viewer/src/less/colours.less +32 -20
  145. iXBRLViewerPlugin/viewer/src/less/common.less +3 -3
  146. iXBRLViewerPlugin/viewer/src/less/components.less +6 -4
  147. iXBRLViewerPlugin/viewer/src/less/core.less +2 -0
  148. iXBRLViewerPlugin/viewer/src/less/dialog.less +21 -14
  149. iXBRLViewerPlugin/viewer/src/less/form-controls.less +33 -11
  150. iXBRLViewerPlugin/viewer/src/less/inspector.less +1045 -726
  151. iXBRLViewerPlugin/viewer/src/less/loader.less +2 -2
  152. iXBRLViewerPlugin/viewer/src/less/menu.less +33 -15
  153. iXBRLViewerPlugin/viewer/src/less/summary.less +16 -6
  154. iXBRLViewerPlugin/viewer/src/less/tabs.less +9 -9
  155. iXBRLViewerPlugin/viewer/src/less/text-block-viewer.less +2 -0
  156. iXBRLViewerPlugin/viewer/src/less/text-mixins.less +2 -1
  157. iXBRLViewerPlugin/viewer/src/less/validation-report.less +2 -3
  158. iXBRLViewerPlugin/viewer/src/less/viewer.less +105 -74
  159. iXBRLViewerPlugin/viewer/webpack.common.js +19 -9
  160. iXBRLViewerPlugin/xhtmlserialize.py +59 -45
  161. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/METADATA +181 -50
  162. ixbrl_viewer-1.4.86.dist-info/RECORD +217 -0
  163. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/WHEEL +1 -1
  164. ixbrl_viewer-1.4.1.dist-info/LICENSE → ixbrl_viewer-1.4.86.dist-info/licenses/LICENSE.md +8 -14
  165. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/top_level.txt +0 -1
  166. iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
  167. iXBRLViewerPlugin/viewer/src/js/interact.min.js +0 -6
  168. ixbrl_viewer-1.4.1.dist-info/RECORD +0 -155
  169. tests/__init__.py +0 -0
  170. tests/puppeteer/framework/core_elements.js +0 -117
  171. tests/puppeteer/framework/page_objects/doc_frame.js +0 -105
  172. tests/puppeteer/framework/page_objects/fact_details_panel.js +0 -80
  173. tests/puppeteer/framework/page_objects/search_panel.js +0 -76
  174. tests/puppeteer/framework/page_objects/toolbar.js +0 -18
  175. tests/puppeteer/framework/utils.js +0 -3
  176. tests/puppeteer/framework/viewer_page.js +0 -103
  177. tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
  178. tests/puppeteer/test_filings/filing_documents_smoke_test.zip +0 -0
  179. tests/puppeteer/test_filings/highlights.zip +0 -0
  180. tests/puppeteer/tests/fact_properties.test.js +0 -78
  181. tests/puppeteer/tests/highlight.test.js +0 -186
  182. tests/puppeteer/tests/search.test.js +0 -86
  183. tests/puppeteer/tools/generate.sh +0 -15
  184. tests/unit_tests/__init__.py +0 -0
  185. tests/unit_tests/iXBRLViewerPlugin/__init__.py +0 -0
  186. tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py +0 -39
  187. tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +0 -641
  188. tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py +0 -310
  189. {ixbrl_viewer-1.4.1.dist-info → ixbrl_viewer-1.4.86.dist-info}/entry_points.txt +0 -0
  190. {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
+ });