View Javadoc
1   /*
2    * MoneyWise: Finance Application
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.moneywise.lethe.reports;
18  
19  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
20  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
21  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
22  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
23  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
24  import io.github.tonywasher.joceanus.metis.report.MetisReportBase;
25  import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder;
26  import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder.MetisHTMLTable;
27  import io.github.tonywasher.joceanus.metis.report.MetisReportManager;
28  import io.github.tonywasher.joceanus.metis.report.MetisReportReferenceManager.DelayedTable;
29  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
30  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
31  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
32  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
33  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket.MoneyWiseAnalysisSecurityBucketList;
34  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityAttr;
35  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityValues;
36  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisValuesResource;
37  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
38  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter.MoneyWiseAnalysisSecurityFilter;
39  import org.w3c.dom.Document;
40  import org.w3c.dom.Element;
41  
42  import java.util.Iterator;
43  
44  /**
45   * MarketGrowth report builder.
46   */
47  public class MoneyWiseReportMarketGrowth
48          extends MetisReportBase<MoneyWiseAnalysis, MoneyWiseAnalysisFilter<?, ?>> {
49      /**
50       * Logger.
51       */
52      private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(MoneyWiseReportMarketGrowth.class);
53  
54      /**
55       * The Title text.
56       */
57      private static final String TEXT_TITLE = MoneyWiseReportResource.MARKETGROWTH_TITLE.getValue();
58  
59      /**
60       * The Value text.
61       */
62      private static final String TEXT_VALUE = MoneyWiseAnalysisValuesResource.ACCOUNTATTR_VALUATION.getValue();
63  
64      /**
65       * The Invested text.
66       */
67      private static final String TEXT_INVEST = MoneyWiseAnalysisValuesResource.SECURITYATTR_INVESTED.getValue();
68  
69      /**
70       * The Adjustment text.
71       */
72      private static final String TEXT_ADJUST = MoneyWiseAnalysisValuesResource.SECURITYATTR_GROWTHADJUST.getValue();
73  
74      /**
75       * The Adjustment text.
76       */
77      private static final String TEXT_GAINS = MoneyWiseAnalysisValuesResource.SECURITYATTR_REALISEDGAINS.getValue();
78  
79      /**
80       * The Base text.
81       */
82      private static final String TEXT_BASE = MoneyWiseReportResource.MARKETGROWTH_BASE.getValue();
83  
84      /**
85       * The Growth text.
86       */
87      private static final String TEXT_GROWTH = MoneyWiseAnalysisValuesResource.SECURITYATTR_MARKETGROWTH.getValue();
88  
89      /**
90       * The CurrenctFluctuation text.
91       */
92      private static final String TEXT_CURRENCY = MoneyWiseAnalysisValuesResource.ACCOUNTATTR_CURRENCYFLUCT.getValue();
93  
94      /**
95       * The Profit text.
96       */
97      private static final String TEXT_PROFIT = MoneyWiseAnalysisValuesResource.SECURITYATTR_PROFIT.getValue();
98  
99      /**
100      * HTML builder.
101      */
102     private final MetisReportHTMLBuilder theBuilder;
103 
104     /**
105      * The Formatter.
106      */
107     private final OceanusDataFormatter theFormatter;
108 
109     /**
110      * Do we have foreign assets?
111      */
112     private boolean hasForeign;
113 
114     /**
115      * Constructor.
116      *
117      * @param pManager the Report Manager
118      */
119     protected MoneyWiseReportMarketGrowth(final MetisReportManager<MoneyWiseAnalysisFilter<?, ?>> pManager) {
120         /* Access underlying utilities */
121         theBuilder = pManager.getBuilder();
122         theFormatter = theBuilder.getDataFormatter();
123     }
124 
125     @Override
126     public Document createReport(final MoneyWiseAnalysis pAnalysis) {
127         /* Access the bucket lists */
128         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = pAnalysis.getPortfolios();
129         hasForeign = myPortfolios.haveForeignCurrency();
130 
131         /* Access the totals */
132         final MoneyWiseAnalysisPortfolioBucket myTotals = myPortfolios.getTotals();
133         final OceanusDateRange myRange = pAnalysis.getDateRange();
134 
135         /* Start the report */
136         final Element myBody = theBuilder.startReport();
137         theBuilder.makeTitle(myBody, TEXT_TITLE, theFormatter.formatObject(myRange));
138 
139         /* Initialise the table */
140         final MetisHTMLTable myTable = theBuilder.startTable(myBody);
141         theBuilder.startHdrRow(myTable);
142         theBuilder.makeTitleCell(myTable);
143         theBuilder.makeTitleCell(myTable, TEXT_VALUE);
144         theBuilder.makeTitleCell(myTable, TEXT_BASE);
145         theBuilder.makeTitleCell(myTable, TEXT_INVEST);
146         theBuilder.makeTitleCell(myTable, TEXT_ADJUST);
147         theBuilder.makeTitleCell(myTable, TEXT_GAINS);
148         theBuilder.makeTitleCell(myTable, TEXT_GROWTH);
149         if (hasForeign) {
150             theBuilder.makeTitleCell(myTable, TEXT_CURRENCY);
151         }
152         theBuilder.makeTitleCell(myTable, TEXT_PROFIT);
153 
154         /* Loop through the Portfolio Buckets */
155         final Iterator<MoneyWiseAnalysisPortfolioBucket> myIterator = myPortfolios.iterator();
156         while (myIterator.hasNext()) {
157             final MoneyWiseAnalysisPortfolioBucket myBucket = myIterator.next();
158 
159             /* Access bucket name */
160             final String myName = myBucket.getName();
161 
162             /* Access values */
163             final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValues();
164 
165             /* Only declare the entry if we have securities */
166             if (myBucket.securityIterator().hasNext()) {
167                 /* Format the Asset */
168                 theBuilder.startRow(myTable);
169                 theBuilder.makeDelayLinkCell(myTable, myName);
170                 theBuilder.makeTotalCell(myTable, myBucket.getNonCashValue(false));
171                 theBuilder.makeTotalCell(myTable, myBucket.getNonCashValue(true));
172                 theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED));
173                 theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
174                 theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
175                 theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETGROWTH));
176                 if (hasForeign) {
177                     theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CURRENCYFLUCT));
178                 }
179                 theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETPROFIT));
180                 checkPortfolioGrowth(myBucket);
181 
182                 /* Note the delayed subTable */
183                 setDelayedTable(myName, myTable, myBucket);
184             }
185         }
186 
187         /* Access values */
188         final MoneyWiseAnalysisSecurityValues myValues = myTotals.getValues();
189 
190         /* Create the total row */
191         theBuilder.startTotalRow(myTable);
192         theBuilder.makeTitleCell(myTable, MoneyWiseReportBuilder.TEXT_TOTAL);
193         theBuilder.makeTotalCell(myTable, myTotals.getNonCashValue(false));
194         theBuilder.makeTotalCell(myTable, myTotals.getNonCashValue(true));
195         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED));
196         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
197         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
198         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETGROWTH));
199         if (hasForeign) {
200             theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CURRENCYFLUCT));
201         }
202         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETPROFIT));
203         checkPortfolioGrowth(myTotals);
204 
205         /* Return the document */
206         return theBuilder.getDocument();
207     }
208 
209     @Override
210     public MetisHTMLTable createDelayedTable(final DelayedTable pTable) {
211         /* Access the source */
212         final Object mySource = pTable.getSource();
213         if (mySource instanceof MoneyWiseAnalysisPortfolioBucket mySourceBucket) {
214             return createDelayedPortfolio(pTable.getParent(), mySourceBucket);
215         }
216 
217         /* Return the null table */
218         return null;
219     }
220 
221     /**
222      * Create a delayed portfolio table.
223      *
224      * @param pParent the parent table
225      * @param pSource the source bucket
226      * @return the new document fragment
227      */
228     private MetisHTMLTable createDelayedPortfolio(final MetisHTMLTable pParent,
229                                                   final MoneyWiseAnalysisPortfolioBucket pSource) {
230         /* Access the securities */
231         final MoneyWiseAnalysisSecurityBucketList mySecurities = pSource.getSecurities();
232 
233         /* Create a new table */
234         final MetisHTMLTable myTable = theBuilder.createEmbeddedTable(pParent);
235 
236         /* Loop through the Security Buckets */
237         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = mySecurities.iterator();
238         while (myIterator.hasNext()) {
239             final MoneyWiseAnalysisSecurityBucket myBucket = myIterator.next();
240 
241             /* Access bucket name */
242             final String myName = myBucket.getSecurityName();
243             String myFullName = myBucket.getDecoratedName();
244             myFullName = myFullName.replace(':', '-');
245 
246             /* Access values */
247             final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValues();
248             final MoneyWiseAnalysisSecurityValues myBaseValues = myBucket.getBaseValues();
249 
250             /* Create the detail row */
251             theBuilder.startRow(myTable);
252             theBuilder.makeFilterLinkCell(myTable, myFullName, myName);
253             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
254             theBuilder.makeValueCell(myTable, myBaseValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
255             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED));
256             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
257             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
258             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETGROWTH));
259             if (hasForeign) {
260                 theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CURRENCYFLUCT));
261             }
262             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETPROFIT));
263             checkSecurityGrowth(myBucket);
264 
265             /* Record the filter */
266             setFilterForId(myFullName, myBucket);
267         }
268 
269         /* Return the table */
270         return myTable;
271     }
272 
273     /**
274      * Check portfolio growth calculation.
275      *
276      * @param pBucket the portfolio bucket
277      */
278     private static void checkPortfolioGrowth(final MoneyWiseAnalysisPortfolioBucket pBucket) {
279         /* Check market profit */
280         final MoneyWiseAnalysisSecurityValues myValues = pBucket.getValues();
281         final OceanusMoney myAdjust = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST);
282         final OceanusMoney myCalcGrowth = pBucket.getNonCashValue(false);
283         myCalcGrowth.subtractAmount(pBucket.getNonCashValue(true));
284         myCalcGrowth.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED));
285         myCalcGrowth.addAmount(myAdjust);
286         final OceanusMoney myProfit = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETPROFIT);
287         if (!myProfit.equals(myCalcGrowth)) {
288             LOGGER.error("Incorrect profit calculation for security <%s> of <%s>", pBucket.getName(), myCalcGrowth);
289         }
290 
291         /* Check market growth */
292         myCalcGrowth.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
293         myCalcGrowth.subtractAmount(myAdjust);
294         myCalcGrowth.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CURRENCYFLUCT));
295         final OceanusMoney myGrowth = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETGROWTH);
296         if (!myGrowth.equals(myCalcGrowth)) {
297             LOGGER.error("Incorrect growth calculation for portfolio <%s> of <%s>", pBucket.getName(), myCalcGrowth);
298         }
299     }
300 
301     /**
302      * Check security portfolio profit calculation.
303      *
304      * @param pBucket the security bucket
305      */
306     private static void checkSecurityGrowth(final MoneyWiseAnalysisSecurityBucket pBucket) {
307         /* Check market profit */
308         final MoneyWiseAnalysisSecurityValues myValues = pBucket.getValues();
309         final MoneyWiseAnalysisSecurityValues myBaseValues = pBucket.getBaseValues();
310         final OceanusMoney myAdjust = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST);
311         final OceanusMoney myCalcGrowth = new OceanusMoney(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
312         myCalcGrowth.subtractAmount(myBaseValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
313         myCalcGrowth.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.INVESTED));
314         myCalcGrowth.addAmount(myAdjust);
315         final OceanusMoney myProfit = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETPROFIT);
316         if (!myProfit.equals(myCalcGrowth)) {
317             LOGGER.error("Incorrect profit calculation for security <%s> of <%s>", pBucket.getDecoratedName(), myCalcGrowth);
318         }
319 
320         /* Check market growth */
321         myCalcGrowth.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
322         myCalcGrowth.subtractAmount(myAdjust);
323         final OceanusMoney myFluct = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CURRENCYFLUCT);
324         if (myFluct != null) {
325             myCalcGrowth.subtractAmount(myFluct);
326         }
327         final OceanusMoney myGrowth = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.MARKETGROWTH);
328         if (!myGrowth.equals(myCalcGrowth)) {
329             LOGGER.error("Incorrect growth calculation for security <%s> of <%s>", pBucket.getDecoratedName(), myCalcGrowth);
330         }
331     }
332 
333     @Override
334     public MoneyWiseAnalysisSecurityFilter processFilter(final Object pSource) {
335         /* If this is a SecurityBucket */
336         if (pSource instanceof MoneyWiseAnalysisSecurityBucket mySource) {
337             /* Create the new filter */
338             return new MoneyWiseAnalysisSecurityFilter(mySource);
339         }
340         return null;
341     }
342 }