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