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.ui.dialog;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
21  import io.github.tonywasher.joceanus.metis.ui.MetisAction;
22  import io.github.tonywasher.joceanus.metis.ui.MetisErrorPanel;
23  import io.github.tonywasher.joceanus.metis.ui.MetisIcon;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
28  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice;
29  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityPrice.MoneyWiseSecurityPriceList;
30  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
31  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
32  import io.github.tonywasher.joceanus.moneywise.ui.base.MoneyWiseUIDialogTable;
33  import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
34  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
35  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
36  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateConfig;
37  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
38  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
39  import io.github.tonywasher.joceanus.prometheus.data.PrometheusData.PrometheusDataSetCtl;
40  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataResource;
41  import io.github.tonywasher.joceanus.prometheus.ui.fieldset.PrometheusFieldSetTableTab.PrometheusFieldSetTable;
42  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
43  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControl.TethysUIIconMapSet;
44  import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableColumn;
45  import io.github.tonywasher.joceanus.tethys.api.table.TethysUITableManager;
46  
47  import java.util.ArrayList;
48  import java.util.Iterator;
49  import java.util.List;
50  
51  /**
52   * Security Price Table.
53   */
54  public class MoneyWiseUISecurityPriceTable
55          extends MoneyWiseUIDialogTable<MoneyWiseSecurityPrice>
56          implements PrometheusFieldSetTable<MoneyWiseSecurity> {
57      /**
58       * Security.
59       */
60      private MoneyWiseSecurity theSecurity;
61  
62      /**
63       * The edit list.
64       */
65      private MoneyWiseSecurityPriceList thePrices;
66  
67      /**
68       * The active column.
69       */
70      private final TethysUITableColumn<MetisAction, MetisDataFieldId, MoneyWiseSecurityPrice> theActiveColumn;
71  
72      /**
73       * Constructor.
74       *
75       * @param pView    the view
76       * @param pEditSet the editSet
77       * @param pError   the error panel
78       */
79      public MoneyWiseUISecurityPriceTable(final MoneyWiseView pView,
80                                           final PrometheusEditSet pEditSet,
81                                           final MetisErrorPanel pError) {
82          /* Store parameters */
83          super(pView, pEditSet, pError, MoneyWiseBasicDataType.SECURITYPRICE);
84  
85          /* Access Gui factory */
86          final TethysUITableManager<MetisDataFieldId, MoneyWiseSecurityPrice> myTable = getTable();
87  
88          /* Set table configuration */
89          myTable.setDisabled(MoneyWiseSecurityPrice::isDisabled)
90                  .setComparator(MoneyWiseSecurityPrice::compareTo);
91  
92          /* Create the date column */
93          myTable.declareDateColumn(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE)
94                  .setDateConfigurator(this::handleDateConfig)
95                  .setCellValueFactory(MoneyWiseSecurityPrice::getDate)
96                  .setEditable(true)
97                  .setColumnWidth(WIDTH_DATE)
98                  .setOnCommit((r, v) -> updateField(MoneyWiseSecurityPrice::setDate, r, v));
99  
100         /* Create the price column */
101         myTable.declarePriceColumn(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE)
102                 .setCellValueFactory(MoneyWiseSecurityPrice::getPrice)
103                 .setEditable(true)
104                 .setColumnWidth(WIDTH_PRICE)
105                 .setOnCommit((r, v) -> updateField(MoneyWiseSecurityPrice::setPrice, r, v));
106 
107         /* Create the Active column */
108         final TethysUIIconMapSet<MetisAction> myActionMapSet = MetisIcon.configureStatusIconButton(pView.getGuiFactory());
109         theActiveColumn = myTable.declareIconColumn(PrometheusDataResource.DATAITEM_TOUCH, MetisAction.class)
110                 .setIconMapSet(r -> myActionMapSet)
111                 .setCellValueFactory(r -> r.isActive() ? MetisAction.ACTIVE : MetisAction.DELETE)
112                 .setName(MoneyWiseUIResource.STATICDATA_ACTIVE.getValue())
113                 .setEditable(true)
114                 .setCellEditable(r -> !r.isActive())
115                 .setColumnWidth(WIDTH_ICON)
116                 .setOnCommit((r, v) -> updateField(this::deleteRow, r, v));
117     }
118 
119     @Override
120     public void refreshData() {
121         /* Access the prices list */
122         thePrices = getEditSet().getDataList(MoneyWiseBasicDataType.SECURITYPRICE, MoneyWiseSecurityPriceList.class);
123         getTable().setItems(thePrices.getUnderlyingList());
124     }
125 
126     @Override
127     public void setItem(final MoneyWiseSecurity pSecurity) {
128         /* Store the security */
129         if (!MetisDataDifference.isEqual(pSecurity, theSecurity)) {
130             theSecurity = pSecurity;
131             updateTableData();
132         }
133     }
134 
135     @Override
136     protected void addNewItem() {
137         /* Protect against Exceptions */
138         try {
139             /* Add a new price */
140             final MoneyWiseSecurityPrice myPrice = addNewPrice(theSecurity);
141 
142             /* Shift display to line */
143             updateTableData();
144             selectItem(myPrice);
145             getEditSet().incrementVersion();
146             notifyChanges();
147 
148             /* Handle Exceptions */
149         } catch (OceanusException e) {
150             /* Build the error */
151             final OceanusException myError = new MoneyWiseDataException("Failed to create new price", e);
152 
153             /* Show the error */
154             setError(myError);
155         }
156     }
157 
158     /**
159      * Add a new price for a security.
160      *
161      * @param pSecurity the security
162      * @return the price
163      * @throws OceanusException on error
164      */
165     public MoneyWiseSecurityPrice addNewPrice(final MoneyWiseSecurity pSecurity) throws OceanusException {
166         /* Create the new price */
167         final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(thePrices);
168 
169         /* Set the item value */
170         myPrice.setSecurity(pSecurity);
171         myPrice.setPrice(OceanusPrice.getWholeUnits(1, pSecurity.getCurrency()));
172 
173         /* Access iterator */
174         final Iterator<MoneyWiseSecurityPrice> myIterator = getTable().viewIterator();
175 
176         /* Assume that we can use today's date */
177         OceanusDate myDate = new OceanusDate();
178 
179         /* Access the last price */
180         final MoneyWiseSecurityPrice myLast = myIterator.hasNext()
181                 ? myIterator.next()
182                 : null;
183 
184         /* If we have a most recent price */
185         if (myLast != null) {
186             /* Obtain the date that is one after the latest date used */
187             final OceanusDate myNew = new OceanusDate(myLast.getDate());
188             myNew.adjustDay(1);
189 
190             /* Use the latest of the two dates */
191             if (myDate.compareTo(myNew) < 0) {
192                 myDate = myNew;
193             }
194         }
195 
196         /* Add to the list */
197         myPrice.setDate(myDate);
198         myPrice.setNewVersion();
199         thePrices.add(myPrice);
200 
201         /*
202          * Don't validate the price yet. We need to take care such that we can only add a new price
203          * when there is a slot available, and that we validate the entire list after an update
204          */
205         return myPrice;
206     }
207 
208     @Override
209     public boolean isVisible() {
210         return isViewActive();
211     }
212 
213     @Override
214     public void setEditable(final boolean pEditable) {
215         /* Show/Hide columns/panels */
216         super.setEditable(pEditable);
217         theActiveColumn.setVisible(pEditable);
218     }
219 
220     @Override
221     protected boolean isFiltered(final MoneyWiseSecurityPrice pRow) {
222         return super.isFiltered(pRow)
223                 && theSecurity != null
224                 && theSecurity.equals(pRow.getSecurity());
225     }
226 
227     /**
228      * Handle dateConfig.
229      *
230      * @param pRow    the row
231      * @param pConfig the dateConfig
232      */
233     private void handleDateConfig(final MoneyWiseSecurityPrice pRow,
234                                   final OceanusDateConfig pConfig) {
235         /* Build a list of used dates */
236         final List<OceanusDate> myDates = new ArrayList<>();
237         final Iterator<MoneyWiseSecurityPrice> myIterator = getTable().viewIterator();
238         while (myIterator.hasNext()) {
239             final MoneyWiseSecurityPrice myPrice = myIterator.next();
240             myDates.add(myPrice.getDate());
241         }
242 
243         /* Set filter constraint */
244         final OceanusDate myCurrent = pRow.getDate();
245         pConfig.setAllowed(d -> myCurrent.equals(d) || !myDates.contains(d));
246 
247         /* Set range constraint */
248         final PrometheusDataSetCtl myDataSet = pRow.getDataSet();
249         final OceanusDateRange myRange = ((MoneyWiseDataSet) myDataSet).getDateRange();
250         pConfig.setEarliestDate(myRange.getStart());
251         pConfig.setLatestDate(myRange.getEnd());
252     }
253 }