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