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.tax.uk;
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.decimal.OceanusMoney;
22  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
23  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
25  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceKey;
26  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceManager;
27  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticResource;
29  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTaxClass;
30  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseTaxAnalysis;
31  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseTaxBandSet;
32  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseTaxDueBucket;
33  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseTaxResource;
34  import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseTaxSource;
35  import io.github.tonywasher.joceanus.moneywise.tax.uk.MoneyWiseUKChargeableGainsScheme.MoneyWiseUKSlicedTaxDueBucket;
36  
37  import java.time.Month;
38  import java.util.ArrayList;
39  import java.util.Iterator;
40  import java.util.List;
41  
42  /**
43   * UK Tax Analysis.
44   */
45  public class MoneyWiseUKTaxAnalysis
46          implements MetisFieldItem, MoneyWiseTaxAnalysis {
47      /**
48       * Local Report fields.
49       */
50      private static final MetisFieldSet<MoneyWiseUKTaxAnalysis> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseUKTaxAnalysis.class);
51  
52      /*
53       * Declare Fields.
54       */
55      static {
56          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXYEAR_NAME, MoneyWiseUKTaxAnalysis::getTaxYear);
57          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXCONFIG_NAME, MoneyWiseUKTaxAnalysis::getTaxConfig);
58          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXANALYSIS_TAXBUCKETS, MoneyWiseUKTaxAnalysis::getTaxBuckets);
59          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXBANDS_INCOME, MoneyWiseUKTaxAnalysis::getTaxableIncome);
60          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXBANDS_TAXDUE, MoneyWiseUKTaxAnalysis::getTaxDue);
61          FIELD_DEFS.declareLocalField(MoneyWiseStaticResource.TAXBASIS_TAXPAID, MoneyWiseUKTaxAnalysis::getTaxPaid);
62          FIELD_DEFS.declareLocalField(MoneyWiseTaxResource.TAXANALYSIS_TAXPROFIT, MoneyWiseUKTaxAnalysis::getTaxProfit);
63      }
64  
65      /**
66       * The TaxYear.
67       */
68      private final MoneyWiseUKTaxYear theTaxYear;
69  
70      /**
71       * The TaxSource.
72       */
73      private final MoneyWiseTaxSource theTaxSource;
74  
75      /**
76       * The TaxConfig.
77       */
78      private final MoneyWiseUKTaxConfig theTaxConfig;
79  
80      /**
81       * The TaxDueBuckets.
82       */
83      private final List<MoneyWiseTaxDueBucket> theTaxBuckets;
84  
85      /**
86       * The Total TaxableIncome.
87       */
88      private final OceanusMoney theTaxableIncome;
89  
90      /**
91       * The Total TaxDue.
92       */
93      private final OceanusMoney theTaxDue;
94  
95      /**
96       * The Total Tax Paid.
97       */
98      private final OceanusMoney theTaxPaid;
99  
100     /**
101      * The TaxProfit/Loss.
102      */
103     private final OceanusMoney theTaxProfit;
104 
105     /**
106      * TaxPreferenceKeys.
107      */
108     public enum MoneyWiseUKTaxPreferenceKey implements MetisPreferenceKey {
109         /**
110          * Birth Date.
111          */
112         BIRTHDATE("BirthDate", MoneyWiseTaxResource.TAXPREF_BIRTH);
113 
114         /**
115          * The name of the Preference.
116          */
117         private final String theName;
118 
119         /**
120          * The display string.
121          */
122         private final String theDisplay;
123 
124         /**
125          * Constructor.
126          *
127          * @param pName    the name
128          * @param pDisplay the display string;
129          */
130         MoneyWiseUKTaxPreferenceKey(final String pName,
131                                     final MoneyWiseTaxResource pDisplay) {
132             theName = pName;
133             theDisplay = pDisplay.getValue();
134         }
135 
136         @Override
137         public String getName() {
138             return theName;
139         }
140 
141         @Override
142         public String getDisplay() {
143             return theDisplay;
144         }
145     }
146 
147     /**
148      * Taxation Preferences.
149      */
150     public static class MoneyWiseUKTaxPreferences
151             extends MetisPreferenceSet {
152         /**
153          * Default year.
154          */
155         private static final int YEAR = 1970;
156 
157         /**
158          * Constructor.
159          *
160          * @param pManager the preference manager
161          * @throws OceanusException on error
162          */
163         public MoneyWiseUKTaxPreferences(final MetisPreferenceManager pManager) throws OceanusException {
164             super(pManager, MoneyWiseTaxResource.TAXPREF_NAME);
165         }
166 
167         @Override
168         protected void definePreferences() {
169             defineDatePreference(MoneyWiseUKTaxPreferenceKey.BIRTHDATE);
170         }
171 
172         @Override
173         public void autoCorrectPreferences() {
174             /* Make sure that the birthDate is specified */
175             final MetisDatePreference myPref = getDatePreference(MoneyWiseUKTaxPreferenceKey.BIRTHDATE);
176             if (!myPref.isAvailable()) {
177                 myPref.setValue(new OceanusDate(YEAR, Month.JANUARY, 1));
178             }
179         }
180     }
181 
182     /**
183      * Constructor.
184      *
185      * @param pTaxSource the taxSource
186      * @param pPrefMgr   the preference manager
187      * @param pTaxYear   the tax year
188      */
189     protected MoneyWiseUKTaxAnalysis(final MoneyWiseTaxSource pTaxSource,
190                                      final MetisPreferenceManager pPrefMgr,
191                                      final MoneyWiseUKTaxYear pTaxYear) {
192         /* Store the parameters */
193         theTaxYear = pTaxYear;
194         theTaxSource = pTaxSource;
195 
196         /* Create the TaxDue Buckets */
197         theTaxBuckets = new ArrayList<>();
198 
199         /* Determine the client birthday */
200         final MoneyWiseUKTaxPreferences myPreferences = pPrefMgr.getPreferenceSet(MoneyWiseUKTaxPreferences.class);
201         final OceanusDate myBirthday = myPreferences.getDateValue(MoneyWiseUKTaxPreferenceKey.BIRTHDATE);
202         theTaxConfig = new MoneyWiseUKTaxConfig(theTaxYear, theTaxSource, myBirthday);
203 
204         /* Create the totals */
205         theTaxPaid = pTaxSource.getAmountForTaxBasis(MoneyWiseTaxClass.TAXPAID);
206         theTaxableIncome = new OceanusMoney(theTaxPaid);
207         theTaxableIncome.setZero();
208         theTaxDue = new OceanusMoney(theTaxPaid);
209         theTaxDue.setZero();
210         theTaxProfit = new OceanusMoney(theTaxPaid);
211         theTaxProfit.setZero();
212     }
213 
214     @Override
215     public MoneyWiseUKTaxYear getTaxYear() {
216         return theTaxYear;
217     }
218 
219     /**
220      * Obtain the taxConfig.
221      *
222      * @return the taxConfig
223      */
224     public MoneyWiseUKTaxConfig getTaxConfig() {
225         return theTaxConfig;
226     }
227 
228     @Override
229     public Iterator<MoneyWiseTaxDueBucket> taxDueIterator() {
230         return theTaxBuckets.iterator();
231     }
232 
233     /**
234      * Obtain the taxBuckets.
235      *
236      * @return the taxBuckets
237      */
238     private List<MoneyWiseTaxDueBucket> getTaxBuckets() {
239         return theTaxBuckets;
240     }
241 
242     @Override
243     public OceanusMoney getTaxableIncome() {
244         return theTaxableIncome;
245     }
246 
247     @Override
248     public OceanusMoney getTaxDue() {
249         return theTaxDue;
250     }
251 
252     @Override
253     public OceanusMoney getTaxPaid() {
254         return theTaxPaid;
255     }
256 
257     @Override
258     public OceanusMoney getTaxProfit() {
259         return theTaxProfit;
260     }
261 
262     /**
263      * Calculate the taxDue.
264      */
265     protected void calculateTaxDue() {
266         /* Reset the tax Due */
267         theTaxableIncome.setZero();
268         theTaxDue.setZero();
269 
270         /* Loop through the tax bands */
271         final Iterator<MoneyWiseTaxDueBucket> myIterator = theTaxBuckets.iterator();
272         while (myIterator.hasNext()) {
273             final MoneyWiseTaxDueBucket myBucket = myIterator.next();
274 
275             /* Add the values */
276             theTaxableIncome.addAmount(myBucket.getTaxableIncome());
277             theTaxDue.addAmount(myBucket.getTaxDue());
278 
279             /* If this is a sliced tax bucket */
280             if (myBucket instanceof MoneyWiseUKSlicedTaxDueBucket mySliced) {
281                 /* Apply Tax Relief */
282                 theTaxDue.subtractAmount(mySliced.getTaxRelief());
283             }
284         }
285     }
286 
287     /**
288      * Calculate the taxProfit.
289      */
290     protected void calculateTaxProfit() {
291         /* Calculate the profit */
292         theTaxProfit.setZero();
293         theTaxProfit.addAmount(theTaxDue);
294         theTaxProfit.addAmount(theTaxPaid);
295     }
296 
297     /**
298      * Process the item.
299      *
300      * @param pBasis  the tax basis
301      * @param pScheme the income scheme
302      */
303     protected void processItem(final MoneyWiseTaxClass pBasis,
304                                final MoneyWiseUKIncomeScheme pScheme) {
305         /* Obtain the amount */
306         final OceanusMoney myAmount = theTaxSource.getAmountForTaxBasis(pBasis);
307 
308         /* Ignore zero or negative amounts */
309         if (myAmount.isZero()
310                 || !myAmount.isPositive()) {
311             return;
312         }
313 
314         /* Take a clone of the taxConfig */
315         final MoneyWiseUKTaxConfig myConfig = theTaxConfig.cloneIt();
316 
317         /* Allocate the amount to the various taxBands */
318         final MoneyWiseTaxBandSet myBands = pScheme.allocateToTaxBands(theTaxConfig, pBasis, myAmount);
319 
320         /* Create the TaxDueBucket */
321         MoneyWiseTaxDueBucket myBucket = new MoneyWiseTaxDueBucket(pBasis, myBands, myConfig);
322 
323         /* If this is ChargeableGains, and we have multiple Bands */
324         if (MoneyWiseTaxClass.CHARGEABLEGAINS.equals(pBasis)
325                 && myBands.multipleBands()) {
326             /* Analyse the Slices */
327             myBucket = new MoneyWiseUKSlicedTaxDueBucket(myBucket, theTaxSource);
328         }
329 
330         /* Add the bucket to the list */
331         theTaxBuckets.add(myBucket);
332     }
333 
334     @Override
335     public MetisFieldSet<MoneyWiseUKTaxAnalysis> getDataFieldSet() {
336         return FIELD_DEFS;
337     }
338 
339     @Override
340     public String formatObject(final OceanusDataFormatter pFormatter) {
341         return FIELD_DEFS.getName();
342     }
343 }