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