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.OceanusDate;
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   * Portfolio (Market) report builder.
46   */
47  public class MoneyWiseReportPortfolioView
48          extends MetisReportBase<MoneyWiseAnalysis, MoneyWiseAnalysisFilter<?, ?>> {
49      /**
50       * Logger.
51       */
52      private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(MoneyWiseReportPortfolioView.class);
53  
54      /**
55       * The Title text.
56       */
57      private static final String TEXT_TITLE = MoneyWiseReportResource.PORTFOLIO_TITLE.getValue();
58  
59      /**
60       * The Cost text.
61       */
62      private static final String TEXT_COST = MoneyWiseAnalysisValuesResource.SECURITYATTR_RESIDUALCOST.getValue();
63  
64      /**
65       * The Adjustment text.
66       */
67      private static final String TEXT_ADJUST = MoneyWiseAnalysisValuesResource.SECURITYATTR_GROWTHADJUST.getValue();
68  
69      /**
70       * The Value text.
71       */
72      private static final String TEXT_VALUE = MoneyWiseAnalysisValuesResource.ACCOUNTATTR_VALUATION.getValue();
73  
74      /**
75       * The Gains text.
76       */
77      private static final String TEXT_GAINS = MoneyWiseAnalysisValuesResource.SECURITYATTR_REALISEDGAINS.getValue();
78  
79      /**
80       * The Dividend text.
81       */
82      private static final String TEXT_DIVIDEND = MoneyWiseAnalysisValuesResource.SECURITYATTR_DIVIDEND.getValue();
83  
84      /**
85       * HTML builder.
86       */
87      private final MetisReportHTMLBuilder theBuilder;
88  
89      /**
90       * The Formatter.
91       */
92      private final OceanusDataFormatter theFormatter;
93  
94      /**
95       * Constructor.
96       *
97       * @param pManager the Report Manager
98       */
99      protected MoneyWiseReportPortfolioView(final MetisReportManager<MoneyWiseAnalysisFilter<?, ?>> pManager) {
100         /* Access underlying utilities */
101         theBuilder = pManager.getBuilder();
102         theFormatter = theBuilder.getDataFormatter();
103     }
104 
105     @Override
106     public Document createReport(final MoneyWiseAnalysis pAnalysis) {
107         /* Access the bucket lists */
108         final MoneyWiseAnalysisPortfolioBucketList myPortfolios = pAnalysis.getPortfolios();
109 
110         /* Access the totals */
111         final MoneyWiseAnalysisPortfolioBucket myTotals = myPortfolios.getTotals();
112         final OceanusDate myDate = pAnalysis.getDateRange().getEnd();
113 
114         /* Start the report */
115         final Element myBody = theBuilder.startReport();
116         theBuilder.makeTitle(myBody, TEXT_TITLE, theFormatter.formatObject(myDate));
117 
118         /* Initialise the table */
119         final MetisHTMLTable myTable = theBuilder.startTable(myBody);
120         theBuilder.startHdrRow(myTable);
121         theBuilder.makeTitleCell(myTable);
122         theBuilder.makeTitleCell(myTable, TEXT_VALUE);
123         theBuilder.makeTitleCell(myTable, TEXT_COST);
124         theBuilder.makeTitleCell(myTable, TEXT_GAINS);
125         theBuilder.makeTitleCell(myTable, TEXT_DIVIDEND);
126         theBuilder.makeTitleCell(myTable, TEXT_ADJUST);
127         theBuilder.makeTitleCell(myTable, MoneyWiseReportBuilder.TEXT_PROFIT);
128 
129         /* Loop through the Portfolio Buckets */
130         final Iterator<MoneyWiseAnalysisPortfolioBucket> myIterator = myPortfolios.iterator();
131         while (myIterator.hasNext()) {
132             final MoneyWiseAnalysisPortfolioBucket myBucket = myIterator.next();
133 
134             /* Access bucket name */
135             final String myName = myBucket.getName();
136 
137             /* Access values */
138             final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValues();
139 
140             /* Format the Asset */
141             theBuilder.startRow(myTable);
142             theBuilder.makeDelayLinkCell(myTable, myName);
143 
144             /* Handle values bucket value */
145             theBuilder.makeValueCell(myTable, myBucket.getNonCashValue(false));
146             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST));
147             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
148             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.DIVIDEND));
149             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
150             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.PROFIT));
151             checkPortfolioProfit(myBucket);
152 
153             /* Note the delayed subTable */
154             setDelayedTable(myName, myTable, myBucket);
155         }
156 
157         /* Access values */
158         final MoneyWiseAnalysisSecurityValues myValues = myTotals.getValues();
159 
160         /* Create the total row */
161         theBuilder.startTotalRow(myTable);
162         theBuilder.makeTitleCell(myTable, MoneyWiseReportBuilder.TEXT_TOTAL);
163         theBuilder.makeTotalCell(myTable, myTotals.getNonCashValue(false));
164         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST));
165         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
166         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.DIVIDEND));
167         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
168         theBuilder.makeTotalCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.PROFIT));
169         checkPortfolioProfit(myTotals);
170 
171         /* Return the document */
172         return theBuilder.getDocument();
173     }
174 
175     @Override
176     public MetisHTMLTable createDelayedTable(final DelayedTable pTable) {
177         /* Access the source */
178         final Object mySource = pTable.getSource();
179         if (mySource instanceof MoneyWiseAnalysisPortfolioBucket mySourceBucket) {
180             return createDelayedPortfolio(pTable.getParent(), mySourceBucket);
181         }
182 
183         /* Return the null table */
184         return null;
185     }
186 
187     /**
188      * Create a delayed portfolio table.
189      *
190      * @param pParent the parent table
191      * @param pSource the source bucket
192      * @return the new document fragment
193      */
194     private MetisHTMLTable createDelayedPortfolio(final MetisHTMLTable pParent,
195                                                   final MoneyWiseAnalysisPortfolioBucket pSource) {
196         /* Access the securities and portfolio */
197         final MoneyWiseAnalysisSecurityBucketList mySecurities = pSource.getSecurities();
198 
199         /* Create a new table */
200         final MetisHTMLTable myTable = theBuilder.createEmbeddedTable(pParent);
201 
202         /* Loop through the Security Buckets */
203         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = mySecurities.iterator();
204         while (myIterator.hasNext()) {
205             final MoneyWiseAnalysisSecurityBucket myBucket = myIterator.next();
206 
207             /* Access bucket name */
208             final String myName = myBucket.getSecurityName();
209             String myFullName = myBucket.getDecoratedName();
210             myFullName = myFullName.replace(':', '-');
211 
212             /* Access values */
213             final MoneyWiseAnalysisSecurityValues myValues = myBucket.getValues();
214 
215             /* Create the detail row */
216             theBuilder.startRow(myTable);
217             theBuilder.makeFilterLinkCell(myTable, myFullName, myName);
218             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
219             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST));
220             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
221             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.DIVIDEND));
222             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
223             theBuilder.makeValueCell(myTable, myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.PROFIT));
224             checkSecurityProfit(myBucket);
225 
226             /* Record the filter */
227             setFilterForId(myFullName, myBucket);
228         }
229 
230         /* Return the table */
231         return myTable;
232     }
233 
234     /**
235      * Check portfolio profit calculation.
236      *
237      * @param pBucket the portfolio bucket
238      */
239     private static void checkPortfolioProfit(final MoneyWiseAnalysisPortfolioBucket pBucket) {
240         final MoneyWiseAnalysisSecurityValues myValues = pBucket.getValues();
241         final OceanusMoney myCalcProfit = pBucket.getNonCashValue(false);
242         myCalcProfit.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST));
243         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
244         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.DIVIDEND));
245         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
246         final OceanusMoney myProfit = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.PROFIT);
247         if (!myProfit.equals(myCalcProfit)) {
248             LOGGER.error("Incorrect profit calculation for portfolio <%s>", pBucket.getName());
249         }
250     }
251 
252     /**
253      * Check security portfolio profit calculation.
254      *
255      * @param pBucket the security bucket
256      */
257     private static void checkSecurityProfit(final MoneyWiseAnalysisSecurityBucket pBucket) {
258         final MoneyWiseAnalysisSecurityValues myValues = pBucket.getValues();
259         final OceanusMoney myCalcProfit = new OceanusMoney(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION));
260         myCalcProfit.subtractAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST));
261         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS));
262         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.DIVIDEND));
263         myCalcProfit.addAmount(myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.GROWTHADJUST));
264         final OceanusMoney myProfit = myValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.PROFIT);
265         if (!myProfit.equals(myCalcProfit)) {
266             LOGGER.error("Incorrect profit calculation for security <%s>", pBucket.getDecoratedName());
267         }
268     }
269 
270     @Override
271     public MoneyWiseAnalysisSecurityFilter processFilter(final Object pSource) {
272         /* If this is a SecurityBucket */
273         if (pSource instanceof MoneyWiseAnalysisSecurityBucket mySource) {
274             /* Create the new filter */
275             return new MoneyWiseAnalysisSecurityFilter(mySource);
276         }
277         return null;
278     }
279 }