1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets;
18
19 import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20 import io.github.tonywasher.joceanus.metis.data.MetisDataFieldValue;
21 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
22 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
23 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldTableItem;
24 import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
25 import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
26 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisBaseResource;
27 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisEvent;
28 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.base.MoneyWiseXAnalysisHistory;
29 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisInterfaces.MoneyWiseXAnalysisBucketForeign;
30 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisInterfaces.MoneyWiseXAnalysisCursor;
31 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountAttr;
32 import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.values.MoneyWiseXAnalysisAccountValues;
33 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetBase;
34 import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDeposit;
35 import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
36 import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
37 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
38 import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
39 import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
40 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
41 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
42 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
43 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
44 import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
45
46 import java.util.Comparator;
47 import java.util.Currency;
48 import java.util.Iterator;
49 import java.util.List;
50
51
52
53
54
55
56 public abstract class MoneyWiseXAnalysisAccountBucket<T extends MoneyWiseAssetBase>
57 implements MetisFieldTableItem, MoneyWiseXAnalysisBucketForeign {
58
59
60
61 protected static final Currency DEFAULT_CURRENCY = OceanusMoney.getDefaultCurrency();
62
63
64
65
66 @SuppressWarnings("rawtypes")
67 private static final MetisFieldSet<MoneyWiseXAnalysisAccountBucket> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisAccountBucket.class);
68
69
70
71
72 static {
73 FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisAccountBucket::getAnalysis);
74 FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_ACCOUNT, MoneyWiseXAnalysisAccountBucket::getAccount);
75 FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.BUCKET_BASEVALUES, MoneyWiseXAnalysisAccountBucket::getBaseValues);
76 FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBaseResource.BUCKET_HISTORY, MoneyWiseXAnalysisAccountBucket::getHistoryMap);
77 FIELD_DEFS.declareLocalFieldsForEnum(MoneyWiseXAnalysisAccountAttr.class, MoneyWiseXAnalysisAccountBucket::getAttributeValue);
78 }
79
80
81
82
83 private static final String NAME_TOTALS = MoneyWiseXAnalysisBucketResource.ANALYSIS_TOTALS.getValue();
84
85
86
87
88 private final MoneyWiseXAnalysisHolder theAnalysis;
89
90
91
92
93 private final T theAccount;
94
95
96
97
98 private final boolean isForeignCurrency;
99
100
101
102
103 private final MoneyWiseXAnalysisAccountValues theValues;
104
105
106
107
108 private final MoneyWiseXAnalysisAccountValues theBaseValues;
109
110
111
112
113 private final MoneyWiseXAnalysisHistory<MoneyWiseXAnalysisAccountValues, MoneyWiseXAnalysisAccountAttr> theHistory;
114
115
116
117
118
119
120
121 protected MoneyWiseXAnalysisAccountBucket(final MoneyWiseXAnalysisHolder pAnalysis,
122 final T pAccount) {
123
124 theAccount = pAccount;
125 theAnalysis = pAnalysis;
126
127
128 final MoneyWiseCurrency myReportingCurrency = pAnalysis.getCurrency();
129 final MoneyWiseCurrency myAccountCurrency = pAccount == null
130 ? myReportingCurrency
131 : pAccount.getAssetCurrency();
132
133
134 isForeignCurrency = !MetisDataDifference.isEqual(myReportingCurrency, myAccountCurrency);
135 final Currency myCurrency = deriveCurrency(myAccountCurrency);
136
137
138 final MoneyWiseXAnalysisAccountValues myValues = allocateValues(myCurrency);
139 theHistory = new MoneyWiseXAnalysisHistory<>(myValues);
140
141
142 theValues = theHistory.getValues();
143 theBaseValues = theHistory.getBaseValues();
144
145
146 if (isForeignCurrency) {
147
148 theAnalysis.getCursor().registerForXchgRateUpdates(this);
149
150
151 recordExchangeRate();
152 theBaseValues.setValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE, getValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE));
153
154
155 final OceanusMoney myReported = new OceanusMoney(theAnalysis.getCurrency().getCurrency());
156 theValues.setValue(MoneyWiseXAnalysisAccountAttr.VALUATION, myReported);
157 theBaseValues.setValue(MoneyWiseXAnalysisAccountAttr.VALUATION, myReported);
158 }
159 }
160
161
162
163
164
165
166
167 protected MoneyWiseXAnalysisAccountBucket(final MoneyWiseXAnalysisHolder pAnalysis,
168 final MoneyWiseXAnalysisAccountBucket<T> pBase) {
169
170 theAccount = pBase.getAccount();
171 theAnalysis = pAnalysis;
172 isForeignCurrency = pBase.isForeignCurrency();
173
174
175 theHistory = new MoneyWiseXAnalysisHistory<>(pBase.getHistoryMap());
176
177
178 theValues = theHistory.getValues();
179 theBaseValues = theHistory.getBaseValues();
180 }
181
182
183
184
185
186
187
188
189 protected MoneyWiseXAnalysisAccountBucket(final MoneyWiseXAnalysisHolder pAnalysis,
190 final MoneyWiseXAnalysisAccountBucket<T> pBase,
191 final OceanusDate pDate) {
192
193 theAccount = pBase.getAccount();
194 theAnalysis = pAnalysis;
195 isForeignCurrency = pBase.isForeignCurrency();
196
197
198 theHistory = new MoneyWiseXAnalysisHistory<>(pBase.getHistoryMap(), pDate);
199
200
201 theValues = theHistory.getValues();
202 theBaseValues = theHistory.getBaseValues();
203 }
204
205
206
207
208
209
210
211
212 protected MoneyWiseXAnalysisAccountBucket(final MoneyWiseXAnalysisHolder pAnalysis,
213 final MoneyWiseXAnalysisAccountBucket<T> pBase,
214 final OceanusDateRange pRange) {
215
216 theAccount = pBase.getAccount();
217 theAnalysis = pAnalysis;
218 isForeignCurrency = pBase.isForeignCurrency();
219
220
221 theHistory = new MoneyWiseXAnalysisHistory<>(pBase.getHistoryMap(), pRange);
222
223
224 theValues = theHistory.getValues();
225 theBaseValues = theHistory.getBaseValues();
226 }
227
228 @Override
229 public String formatObject(final OceanusDataFormatter pFormatter) {
230 return toString();
231 }
232
233 @Override
234 public String toString() {
235 return getName() + " " + theValues;
236 }
237
238
239
240
241
242
243
244 protected static Currency deriveCurrency(final MoneyWiseCurrency pAssetCurrency) {
245 return pAssetCurrency == null
246 ? DEFAULT_CURRENCY
247 : pAssetCurrency.getCurrency();
248 }
249
250 @Override
251 public MoneyWiseCurrency getCurrency() {
252 return theAccount == null
253 ? theAnalysis.getCurrency()
254 : theAccount.getAssetCurrency();
255 }
256
257
258
259
260
261
262
263 protected MoneyWiseXAnalysisAccountValues allocateValues(final Currency pCurrency) {
264 return new MoneyWiseXAnalysisAccountValues(pCurrency);
265 }
266
267
268
269
270
271
272 public String getName() {
273 return theAccount == null
274 ? NAME_TOTALS
275 : theAccount.getName();
276 }
277
278
279
280
281
282
283 public T getAccount() {
284 return theAccount;
285 }
286
287
288
289
290
291
292 public boolean isForeignCurrency() {
293 return isForeignCurrency;
294 }
295
296 @Override
297 public Integer getIndexedId() {
298 return theAccount.getIndexedId();
299 }
300
301
302
303
304
305
306 public boolean isIdle() {
307 return theHistory.isIdle();
308 }
309
310
311
312
313
314
315 protected MoneyWiseXAnalysisHolder getAnalysis() {
316 return theAnalysis;
317 }
318
319
320
321
322
323
324 public OceanusDateRange getDateRange() {
325 return theAnalysis.getDateRange();
326 }
327
328
329
330
331
332
333 public MoneyWiseXAnalysisAccountValues getValues() {
334 return theValues;
335 }
336
337
338
339
340
341
342 public MoneyWiseXAnalysisAccountValues getBaseValues() {
343 return theBaseValues;
344 }
345
346
347
348
349
350
351
352 public MoneyWiseXAnalysisAccountValues getValuesForEvent(final MoneyWiseXAnalysisEvent pEvent) {
353 return theHistory.getValuesForEvent(pEvent);
354 }
355
356
357
358
359
360
361
362 public MoneyWiseXAnalysisAccountValues getPreviousValuesForEvent(final MoneyWiseXAnalysisEvent pEvent) {
363 return theHistory.getPreviousValuesForEvent(pEvent);
364 }
365
366
367
368
369
370
371
372
373 public OceanusDecimal getDeltaForEvent(final MoneyWiseXAnalysisEvent pEvent,
374 final MoneyWiseXAnalysisAccountAttr pAttr) {
375
376 return theHistory.getDeltaValue(pEvent, pAttr);
377 }
378
379
380
381
382
383
384
385
386 public OceanusMoney getMoneyDeltaForEvent(final MoneyWiseXAnalysisEvent pEvent,
387 final MoneyWiseXAnalysisAccountAttr pAttr) {
388
389 return theHistory.getDeltaMoneyValue(pEvent, pAttr);
390 }
391
392
393
394
395
396
397 private MoneyWiseXAnalysisHistory<MoneyWiseXAnalysisAccountValues, MoneyWiseXAnalysisAccountAttr> getHistoryMap() {
398 return theHistory;
399 }
400
401
402
403
404
405
406
407 protected void setValue(final MoneyWiseXAnalysisAccountAttr pAttr,
408 final Object pValue) {
409
410 theValues.setValue(pAttr, pValue);
411 }
412
413
414
415
416
417
418
419 private Object getAttributeValue(final MoneyWiseXAnalysisAccountAttr pAttr) {
420
421 final Object myValue = getValue(pAttr);
422
423
424 return myValue != null
425 ? myValue
426 : MetisDataFieldValue.SKIP;
427 }
428
429
430
431
432
433
434
435 private Object getValue(final MoneyWiseXAnalysisAccountAttr pAttr) {
436
437 return theValues.getValue(pAttr);
438 }
439
440
441
442
443 public void recordOpeningBalance() {
444
445 final MoneyWiseXAnalysisAccountValues myValues = getBaseValues();
446 final OceanusMoney myBaseValue = myValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.BALANCE);
447
448
449 myBaseValue.addAmount(getAccount().getOpeningBalance());
450
451
452 if (isForeignCurrency) {
453
454 final MoneyWiseCurrency myCurrency = theAnalysis.getCurrency();
455 final OceanusRatio myRate = theValues.getRatioValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE);
456
457
458 final OceanusMoney myReport = myBaseValue.convertCurrency(myCurrency.getCurrency(), myRate);
459 theValues.setValue(MoneyWiseXAnalysisAccountAttr.VALUATION, myReport);
460 myValues.setValue(MoneyWiseXAnalysisAccountAttr.VALUATION, myReport);
461 myValues.setValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE, myRate);
462 }
463 }
464
465
466
467
468
469
470 public void addToBalance(final OceanusMoney pDelta) {
471 OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.BALANCE);
472 myValue = new OceanusMoney(myValue);
473 myValue.addAmount(pDelta);
474 setValue(MoneyWiseXAnalysisAccountAttr.BALANCE, myValue);
475 }
476
477
478
479
480
481
482 public void subtractFromBalance(final OceanusMoney pDelta) {
483 OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.BALANCE);
484 myValue = new OceanusMoney(myValue);
485 myValue.subtractAmount(pDelta);
486 setValue(MoneyWiseXAnalysisAccountAttr.BALANCE, myValue);
487 }
488
489
490
491
492 public void recordMaturity() {
493 final OceanusDate myMaturity = ((MoneyWiseDeposit) getAccount()).getMaturity();
494 if (myMaturity != null) {
495 theValues.setValue(MoneyWiseXAnalysisAccountAttr.MATURITY, ((MoneyWiseDeposit) getAccount()).getMaturity());
496 }
497 }
498
499 @Override
500 public void recordExchangeRate() {
501 final MoneyWiseXAnalysisCursor myCursor = theAnalysis.getCursor();
502 final OceanusRatio myRate = myCursor.getCurrentXchgRate(getAccount().getAssetCurrency());
503 theValues.setValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE, myRate);
504 }
505
506
507
508
509 public void recordDepositRate() {
510 final MoneyWiseXAnalysisCursor myCursor = theAnalysis.getCursor();
511 final OceanusRate myRate = myCursor.getCurrentDepositRate((MoneyWiseDeposit) getAccount());
512 theValues.setValue(MoneyWiseXAnalysisAccountAttr.DEPOSITRATE, myRate);
513 }
514
515 @Override
516 public void adjustValuation() {
517
518 OceanusMoney myBalance = theValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.BALANCE);
519 if (isForeignCurrency) {
520 final OceanusRatio myRate = theValues.getRatioValue(MoneyWiseXAnalysisAccountAttr.EXCHANGERATE);
521 myBalance = myBalance.convertCurrency(theAnalysis.getCurrency().getCurrency(), myRate);
522 }
523 theValues.setValue(MoneyWiseXAnalysisAccountAttr.VALUATION, myBalance);
524 }
525
526 @Override
527 public OceanusMoney getDeltaValuation() {
528
529 final OceanusMoney myDelta = new OceanusMoney(theValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION));
530 myDelta.subtractAmount(theHistory.getLastValues().getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION));
531 return myDelta;
532 }
533
534 @Override
535 public void registerEvent(final MoneyWiseXAnalysisEvent pEvent) {
536
537 adjustValuation();
538
539
540 theHistory.registerEvent(pEvent, theValues);
541 }
542
543
544
545
546 protected void calculateDelta() {
547
548 OceanusMoney myDelta = theValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION);
549 myDelta = new OceanusMoney(myDelta);
550
551
552 final OceanusMoney myBase = theBaseValues.getMoneyValue(MoneyWiseXAnalysisAccountAttr.VALUATION);
553 myDelta.subtractAmount(myBase);
554
555
556 setValue(MoneyWiseXAnalysisAccountAttr.VALUEDELTA, myDelta);
557
558
559 theValues.adjustToBaseValues(theBaseValues);
560 theBaseValues.resetBaseValues();
561 }
562
563
564
565
566
567
568 public boolean isActive() {
569 return theValues.isActive();
570 }
571
572
573
574
575
576
577
578 public abstract static class MoneyWiseXAnalysisAccountBucketList<B extends MoneyWiseXAnalysisAccountBucket<T>, T extends MoneyWiseAssetBase>
579 implements MetisFieldItem, MetisDataList<B> {
580
581
582
583 @SuppressWarnings("rawtypes")
584 private static final MetisFieldSet<MoneyWiseXAnalysisAccountBucketList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseXAnalysisAccountBucketList.class);
585
586
587
588
589 static {
590 FIELD_DEFS.declareLocalField(MoneyWiseXAnalysisBucketResource.ANALYSIS_NAME, MoneyWiseXAnalysisAccountBucketList::getAnalysis);
591 }
592
593
594
595
596 private final MoneyWiseXAnalysisHolder theAnalysis;
597
598
599
600
601 private final MetisListIndexed<B> theList;
602
603
604
605
606
607
608 protected MoneyWiseXAnalysisAccountBucketList(final MoneyWiseXAnalysisHolder pAnalysis) {
609
610 theAnalysis = pAnalysis;
611 theList = new MetisListIndexed<>();
612 theList.setComparator(Comparator.comparing(MoneyWiseXAnalysisAccountBucket::getAccount));
613 }
614
615 @Override
616 public List<B> getUnderlyingList() {
617 return theList.getUnderlyingList();
618 }
619
620 @Override
621 public String formatObject(final OceanusDataFormatter pFormatter) {
622 return getDataFieldSet().getName();
623 }
624
625
626
627
628
629
630 protected MoneyWiseXAnalysisHolder getAnalysis() {
631 return theAnalysis;
632 }
633
634
635
636
637
638
639
640 protected abstract B newBucket(B pBase);
641
642
643
644
645
646
647
648 protected void constructFromBase(final MoneyWiseXAnalysisAccountBucketList<B, T> pBase,
649 final OceanusDate pDate) {
650
651 final Iterator<B> myIterator = pBase.iterator();
652 while (myIterator.hasNext()) {
653 final B myCurr = myIterator.next();
654
655
656 final B myBucket = newBucket(myCurr, pDate);
657
658
659 if (myBucket.isActive() || !myBucket.isIdle()) {
660 theList.add(myBucket);
661 }
662 }
663 }
664
665
666
667
668
669
670
671
672 protected abstract B newBucket(B pBase,
673 OceanusDate pDate);
674
675
676
677
678
679
680
681 protected void constructFromBase(final MoneyWiseXAnalysisAccountBucketList<B, T> pBase,
682 final OceanusDateRange pRange) {
683
684 final Iterator<B> myIterator = pBase.iterator();
685 while (myIterator.hasNext()) {
686 final B myCurr = myIterator.next();
687
688
689 final B myBucket = newBucket(myCurr, pRange);
690
691
692 if (myBucket.isActive() || !myBucket.isIdle()) {
693
694 theList.add(myBucket);
695 }
696 }
697 }
698
699
700
701
702
703
704
705 public B findItemById(final Integer pId) {
706
707 return theList.getItemById(pId);
708 }
709
710
711
712
713
714
715
716
717 protected abstract B newBucket(B pBase,
718 OceanusDateRange pRange);
719
720
721
722
723
724
725
726 public B getBucket(final T pAccount) {
727
728 B myItem = findItemById(pAccount.getIndexedId());
729
730
731 if (myItem == null) {
732
733 myItem = newBucket(pAccount);
734
735
736 theList.add(myItem);
737 }
738
739
740 return myItem;
741 }
742
743
744
745
746
747
748
749 protected abstract B newBucket(T pAccount);
750
751
752
753
754 protected void sortBuckets() {
755 theList.sortList();
756 }
757
758
759
760
761
762
763 public void markActiveAccounts() throws OceanusException {
764
765 final Iterator<B> myIterator = iterator();
766 while (myIterator.hasNext()) {
767 final B myCurr = myIterator.next();
768 final T myAccount = myCurr.getAccount();
769
770
771 if (myCurr.isActive()) {
772
773 myAccount.setRelevant();
774 }
775
776
777 if (Boolean.TRUE.equals(myAccount.isClosed())) {
778
779 myAccount.adjustClosed();
780
781
782 if (myAccount.isRelevant()
783 && theAnalysis.getData().checkClosedAccounts()) {
784
785 throw new MoneyWiseDataException(myCurr, "Illegally closed account");
786 }
787 }
788 }
789 }
790 }
791 }