ixbrl-viewer 1.4.14__py3-none-any.whl → 1.4.15__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.
Potentially problematic release.
This version of ixbrl-viewer might be problematic. Click here for more details.
- iXBRLViewerPlugin/__init__.py +2 -1
- iXBRLViewerPlugin/_version.py +2 -2
- iXBRLViewerPlugin/iXBRLViewer.py +9 -5
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
- iXBRLViewerPlugin/viewer/src/html/inspector.html +27 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +18 -1
- iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +18 -1
- 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/dimension.svg +1 -5
- iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
- iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
- iXBRLViewerPlugin/viewer/src/js/accordian.js +2 -2
- iXBRLViewerPlugin/viewer/src/js/calculation.js +202 -0
- iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
- iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +190 -0
- iXBRLViewerPlugin/viewer/src/js/concept.js +7 -1
- iXBRLViewerPlugin/viewer/src/js/fact.js +59 -5
- iXBRLViewerPlugin/viewer/src/js/factset.js +54 -5
- iXBRLViewerPlugin/viewer/src/js/factset.test.js +71 -5
- iXBRLViewerPlugin/viewer/src/js/inspector.js +85 -30
- iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
- iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
- iXBRLViewerPlugin/viewer/src/js/test-utils.js +19 -0
- iXBRLViewerPlugin/viewer/src/less/accordian.less +2 -2
- iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
- iXBRLViewerPlugin/viewer/src/less/colours.less +2 -0
- iXBRLViewerPlugin/viewer/src/less/dialog.less +8 -4
- iXBRLViewerPlugin/viewer/src/less/inspector.less +67 -23
- iXBRLViewerPlugin/viewer/src/less/validation-report.less +1 -2
- iXBRLViewerPlugin/viewer/src/less/viewer.less +1 -1
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/METADATA +2 -2
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/RECORD +39 -30
- iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/LICENSE +0 -0
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/NOTICE +0 -0
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/WHEEL +0 -0
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.14.dist-info → ixbrl_viewer-1.4.15.dist-info}/top_level.txt +0 -0
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Copyright 2021 Workiva Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import $ from 'jquery';
|
|
16
|
+
import i18next from 'i18next';
|
|
17
|
+
|
|
18
|
+
import { Dialog } from './dialog.js';
|
|
19
|
+
import { ResolvedCalc11Calculation, ResolvedLegacyCalculation } from './calculation.js';
|
|
20
|
+
|
|
21
|
+
export class CalculationInspector extends Dialog {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(".dialog.calculation-inspector");
|
|
24
|
+
this.addButton("Dismiss", true);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
duplicateFactIcons(factset) {
|
|
28
|
+
const icons = $("<td></td>").addClass("icons");
|
|
29
|
+
if (factset.size() > 1 && !factset.completeDuplicates()) {
|
|
30
|
+
if (factset.isConsistent()) {
|
|
31
|
+
icons.append(
|
|
32
|
+
$("<span></span>")
|
|
33
|
+
.addClass("duplicate-facts")
|
|
34
|
+
.attr("title", i18next.t('calculation.consistent-duplicate-facts-present', {nfacts: factset.size()}))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
icons.append(
|
|
39
|
+
$("<span></span>")
|
|
40
|
+
.addClass("duplicate-facts")
|
|
41
|
+
.attr("title", i18next.t('calculation.inconsistent-duplicate-facts-present', {nfacts: factset.size()}))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return icons;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
populateCalc11Table(resolvedCalculation, table) {
|
|
49
|
+
table.addClass("calc11");
|
|
50
|
+
const tbody = table.find("tbody");
|
|
51
|
+
let hasIcons = false;
|
|
52
|
+
for (const row of resolvedCalculation.rows) {
|
|
53
|
+
let factText = "";
|
|
54
|
+
let minText = "";
|
|
55
|
+
let maxText = "";
|
|
56
|
+
|
|
57
|
+
if (!row.facts.isEmpty()) {
|
|
58
|
+
let f = row.facts.items()[0];
|
|
59
|
+
const reportedInterval = row.facts.valueIntersection();
|
|
60
|
+
if (reportedInterval === undefined) {
|
|
61
|
+
factText = "n/a";
|
|
62
|
+
minText = "n/a";
|
|
63
|
+
maxText = "n/a";
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
factText = f.readableValue(reportedInterval.midpoint().toNumber());
|
|
67
|
+
const contributionInterval = row.contributionInterval();
|
|
68
|
+
minText = f.readableValue(contributionInterval.a.toNumber());
|
|
69
|
+
maxText = f.readableValue(contributionInterval.b.toNumber());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const icons = this.duplicateFactIcons(row.facts);
|
|
73
|
+
hasIcons = hasIcons || icons.find("span").length > 0;
|
|
74
|
+
$("<tr></tr>")
|
|
75
|
+
.append($("<td></td>").addClass("weight").text(row.weightSign))
|
|
76
|
+
.append($("<td></td>").text(row.concept.label()).addClass("line-item"))
|
|
77
|
+
.append($("<td></td>").addClass("figure").text(factText))
|
|
78
|
+
.append(icons)
|
|
79
|
+
.append($("<td></td>").addClass("figure").text(minText))
|
|
80
|
+
.append($("<td></td>").addClass("figure").text(maxText))
|
|
81
|
+
.appendTo(tbody);
|
|
82
|
+
}
|
|
83
|
+
const fact = resolvedCalculation.totalFact;
|
|
84
|
+
const cvi = resolvedCalculation.calculatedTotalInterval();
|
|
85
|
+
$("<tr></tr>").addClass("total")
|
|
86
|
+
.append($("<td></td>"))
|
|
87
|
+
.append($("<td></td>").text(`${fact.concept().label()} (${i18next.t('calculation.calculated-total')})`).addClass("line-item"))
|
|
88
|
+
.append($("<td></td>").text(cvi === undefined ? 'n/a' : fact.readableValue(cvi.midpoint().toNumber())).addClass("figure"))
|
|
89
|
+
.append($("<td></td>").addClass("icons"))
|
|
90
|
+
.append($("<td></td>").text(cvi === undefined ? 'n/a' : fact.readableValue(cvi.a.toNumber())).addClass("figure"))
|
|
91
|
+
.append($("<td></td>").text(cvi === undefined ? 'n/a' : fact.readableValue(cvi.b.toNumber())).addClass("figure"))
|
|
92
|
+
.appendTo(tbody);
|
|
93
|
+
|
|
94
|
+
const tvi = resolvedCalculation.totalFactSet.valueIntersection();
|
|
95
|
+
const icons = this.duplicateFactIcons(resolvedCalculation.totalFactSet);
|
|
96
|
+
$("<tr></tr>")
|
|
97
|
+
.append($("<td></td>"))
|
|
98
|
+
.append($("<td></td>").text(`${fact.concept().label()} (${i18next.t('calculation.reported-total')})`).addClass("line-item"))
|
|
99
|
+
.append($("<td></td>").text(tvi === undefined ? 'n/a' : fact.readableValue(tvi.midpoint().toNumber())).addClass("figure"))
|
|
100
|
+
.append(icons)
|
|
101
|
+
.append($("<td></td>").text(tvi === undefined ? 'n/a' : fact.readableValue(tvi.a.toNumber())).addClass("figure"))
|
|
102
|
+
.append($("<td></td>").text(tvi === undefined ? 'n/a' : fact.readableValue(tvi.b.toNumber())).addClass("figure"))
|
|
103
|
+
.appendTo(tbody);
|
|
104
|
+
if (hasIcons) {
|
|
105
|
+
table.addClass("has-icons");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
populateLegacyCalcTable(resolvedCalculation, table) {
|
|
110
|
+
table.removeClass("calc11");
|
|
111
|
+
const tbody = table.find("tbody");
|
|
112
|
+
let hasIcons = false;
|
|
113
|
+
for (const row of resolvedCalculation.rows) {
|
|
114
|
+
let factText = "";
|
|
115
|
+
let minText = "";
|
|
116
|
+
let maxText = "";
|
|
117
|
+
|
|
118
|
+
if (!row.facts.isEmpty()) {
|
|
119
|
+
let f = row.facts.items()[0];
|
|
120
|
+
const reportedInterval = row.facts.valueIntersection();
|
|
121
|
+
if (reportedInterval === undefined) {
|
|
122
|
+
factText = "Inconsistent duplicates";
|
|
123
|
+
}
|
|
124
|
+
else if (!row.facts.completeDuplicates()) {
|
|
125
|
+
factText = "Duplicate facts";
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
factText = f.readableValue();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const icons = this.duplicateFactIcons(row.facts);
|
|
132
|
+
hasIcons = hasIcons || icons.find("span").length > 0;
|
|
133
|
+
$("<tr></tr>")
|
|
134
|
+
.append($("<td></td>").addClass("weight").text(row.weightSign))
|
|
135
|
+
.append($("<td></td>").text(row.concept.label()).addClass("line-item"))
|
|
136
|
+
.append($("<td></td>").addClass("figure").text(factText))
|
|
137
|
+
.append(icons)
|
|
138
|
+
.appendTo(tbody);
|
|
139
|
+
}
|
|
140
|
+
const fact = resolvedCalculation.totalFact;
|
|
141
|
+
const ct = resolvedCalculation.calculatedTotal();
|
|
142
|
+
$("<tr></tr>").addClass("total")
|
|
143
|
+
.append($("<td></td>"))
|
|
144
|
+
.append($("<td></td>").text(`${fact.concept().label()} (${i18next.t('calculation.calculated-total')})`).addClass("line-item"))
|
|
145
|
+
.append($("<td></td>").text(fact.readableValue(ct)).addClass("figure"))
|
|
146
|
+
.append($("<td></td>").addClass("icons"))
|
|
147
|
+
.appendTo(tbody);
|
|
148
|
+
|
|
149
|
+
$("<tr></tr>")
|
|
150
|
+
.append($("<td></td>"))
|
|
151
|
+
.append($("<td></td>").text(`${fact.concept().label()} (${i18next.t('calculation.reported-total')})`).addClass("line-item"))
|
|
152
|
+
.append($("<td></td>").text(fact.readableValue()).addClass("figure"))
|
|
153
|
+
.append($("<td></td>").addClass("icons"))
|
|
154
|
+
.appendTo(tbody);
|
|
155
|
+
if (hasIcons) {
|
|
156
|
+
table.addClass("has-icons");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
displayCalculation(resolvedCalculation) {
|
|
161
|
+
const table = this.node.find("table.calculation-trace");
|
|
162
|
+
const tbody = table.find("tbody");
|
|
163
|
+
// We remove the padding on the icons column (used to indicate
|
|
164
|
+
// duplicate facts) if it is empty to avoid an unsightly gap
|
|
165
|
+
var hasIcons = false;
|
|
166
|
+
tbody.empty();
|
|
167
|
+
table.removeClass("has-icons");
|
|
168
|
+
if (resolvedCalculation instanceof ResolvedCalc11Calculation) {
|
|
169
|
+
this.populateCalc11Table(resolvedCalculation, table);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
this.populateLegacyCalcTable(resolvedCalculation, table);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
const messageCell = table.find("td.status");
|
|
177
|
+
messageCell.removeClass("inconsistent").removeClass("consistent").removeClass("unchecked");
|
|
178
|
+
if (resolvedCalculation.unchecked()) {
|
|
179
|
+
messageCell.addClass("unchecked").find(".message").text("Calculation does not bind");
|
|
180
|
+
}
|
|
181
|
+
else if (resolvedCalculation.isConsistent()) {
|
|
182
|
+
messageCell.addClass("consistent").find(".message").text("Calculation is consistent");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
messageCell.addClass("inconsistent").find(".message").text("Calculation is inconsistent");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
@@ -5,6 +5,8 @@ import $ from 'jquery'
|
|
|
5
5
|
export class Concept {
|
|
6
6
|
constructor(report, name) {
|
|
7
7
|
this._c = report.concepts()[name] || {};
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.report = report;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
/*
|
|
@@ -48,7 +50,11 @@ export class Concept {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
isEnumeration() {
|
|
51
|
-
return Boolean(this._c.e);
|
|
53
|
+
return Boolean(this._c && this._c.e);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
label() {
|
|
57
|
+
return this.report.getLabelOrName(this.name);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
isTextBlock() {
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import $ from 'jquery'
|
|
4
4
|
import i18next from "i18next";
|
|
5
|
+
import { isodateToHuman } from "./util.js"
|
|
6
|
+
import { QName } from "./qname.js"
|
|
5
7
|
import { Aspect } from "./aspect.js";
|
|
6
8
|
import { Period } from './period.js';
|
|
7
9
|
import { formatNumber, localId } from "./util.js";
|
|
8
10
|
import Decimal from "decimal.js";
|
|
11
|
+
import { Interval } from './interval.js';
|
|
9
12
|
|
|
10
13
|
export class Fact {
|
|
11
14
|
|
|
@@ -42,7 +45,7 @@ export class Fact {
|
|
|
42
45
|
return this.report.qname(this.f.a.c);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
period(){
|
|
48
|
+
period() {
|
|
46
49
|
return new Period(this.f.a.p);
|
|
47
50
|
}
|
|
48
51
|
|
|
@@ -63,8 +66,8 @@ export class Fact {
|
|
|
63
66
|
return this.f.v;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
readableValue() {
|
|
67
|
-
let v = this.f.v;
|
|
69
|
+
readableValue(val) {
|
|
70
|
+
let v = val === undefined ? this.f.v : val;
|
|
68
71
|
if (this.isInvalidIXValue()) {
|
|
69
72
|
v = "Invalid value";
|
|
70
73
|
}
|
|
@@ -241,9 +244,11 @@ export class Fact {
|
|
|
241
244
|
isNil() {
|
|
242
245
|
return this.f.v === null;
|
|
243
246
|
}
|
|
247
|
+
|
|
244
248
|
isNegative() {
|
|
245
249
|
return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isNegative() && !this.isZero();
|
|
246
250
|
}
|
|
251
|
+
|
|
247
252
|
isPositive() {
|
|
248
253
|
return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isPositive() && !this.isZero();
|
|
249
254
|
}
|
|
@@ -251,6 +256,7 @@ export class Fact {
|
|
|
251
256
|
isZero() {
|
|
252
257
|
return this.isNumeric() && !this.isNil() && this.value() !== undefined && new Decimal(this.value()).isZero();
|
|
253
258
|
}
|
|
259
|
+
|
|
254
260
|
isInvalidIXValue() {
|
|
255
261
|
return this.f.err == 'INVALID_IX_VALUE';
|
|
256
262
|
}
|
|
@@ -344,10 +350,58 @@ export class Fact {
|
|
|
344
350
|
return concepts;
|
|
345
351
|
}
|
|
346
352
|
|
|
347
|
-
|
|
353
|
+
/*
|
|
354
|
+
* Facts that are the source of relationships to this fact.
|
|
355
|
+
*/
|
|
348
356
|
addLinkedFact(f) {
|
|
349
357
|
this.linkedFacts.push(f);
|
|
350
358
|
}
|
|
351
|
-
}
|
|
352
359
|
|
|
360
|
+
/*
|
|
361
|
+
* Returns the fact's value, rounded according to the value of its decimals
|
|
362
|
+
* property. This is an odd thing to do, as it implies that more figures
|
|
363
|
+
* were reported than the decimals property suggest are accurate, but this
|
|
364
|
+
* is required for Calc 1.0 validation.
|
|
365
|
+
*
|
|
366
|
+
* valueInterval() is a more meaningful.
|
|
367
|
+
*/
|
|
368
|
+
roundedValue() {
|
|
369
|
+
Decimal.rounding = Decimal.ROUND_HALF_UP;
|
|
370
|
+
const v = new Decimal(this.value());
|
|
371
|
+
const d = this.decimals();
|
|
372
|
+
if (d === undefined) {
|
|
373
|
+
return v;
|
|
374
|
+
}
|
|
375
|
+
return v.mul(10 ** d).round().mul(10 ** (0-d));
|
|
376
|
+
}
|
|
353
377
|
|
|
378
|
+
isCompleteDuplicate(other) {
|
|
379
|
+
return this.value() === other.value() && this.decimals() === other.decimals();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/*
|
|
383
|
+
* Facts that are the source of relationships to this fact.
|
|
384
|
+
*/
|
|
385
|
+
addLinkedFact(f) {
|
|
386
|
+
this.linkedFacts.push(f);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/*
|
|
390
|
+
* Returns an Interval for the fact's value, as implied by its decimals
|
|
391
|
+
* property.
|
|
392
|
+
*/
|
|
393
|
+
valueInterval() {
|
|
394
|
+
return Interval.fromFact(this);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
isMorePrecise(of) {
|
|
398
|
+
// decimals of "undefined" indicates infinite precision
|
|
399
|
+
if (of.decimals() === undefined) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
if (this.decimals() === undefined) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
return this.decimals() > of.decimals();
|
|
406
|
+
}
|
|
407
|
+
}
|