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