1 | |
|
2 | |
|
3 | |
|
4 | |
package org.homeunix.thecave.buddi.plugin.builtin.report; |
5 | |
|
6 | |
import java.util.Collections; |
7 | |
import java.util.Comparator; |
8 | |
import java.util.Date; |
9 | |
import java.util.List; |
10 | |
|
11 | |
import org.homeunix.thecave.buddi.i18n.BuddiKeys; |
12 | |
import org.homeunix.thecave.buddi.i18n.keys.PluginReportDateRangeChoices; |
13 | |
import org.homeunix.thecave.buddi.plugin.api.BuddiReportPlugin; |
14 | |
import org.homeunix.thecave.buddi.plugin.api.model.ImmutableBudgetCategory; |
15 | |
import org.homeunix.thecave.buddi.plugin.api.model.ImmutableDocument; |
16 | |
import org.homeunix.thecave.buddi.plugin.api.model.ImmutableSplit; |
17 | |
import org.homeunix.thecave.buddi.plugin.api.model.ImmutableTransaction; |
18 | |
import org.homeunix.thecave.buddi.plugin.api.model.ImmutableTransactionSplit; |
19 | |
import org.homeunix.thecave.buddi.plugin.api.util.HtmlHelper; |
20 | |
import org.homeunix.thecave.buddi.plugin.api.util.HtmlPage; |
21 | |
import org.homeunix.thecave.buddi.plugin.api.util.TextFormatter; |
22 | |
|
23 | |
import ca.digitalcave.moss.swing.MossDocumentFrame; |
24 | |
|
25 | |
|
26 | |
|
27 | |
|
28 | |
|
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | 2977 | public class IncomeExpenseReportByCategory extends BuddiReportPlugin { |
34 | |
|
35 | |
public static final long serialVersionUID = 0; |
36 | |
|
37 | |
|
38 | |
@Override |
39 | |
public HtmlPage getReport(ImmutableDocument model, MossDocumentFrame frame, Date startDate, Date endDate) { |
40 | 0 | StringBuilder sb = HtmlHelper.getHtmlHeader(getName(), null, startDate, endDate); |
41 | |
|
42 | 0 | List<ImmutableBudgetCategory> categories = model.getImmutableBudgetCategories(); |
43 | 0 | Collections.sort(categories, new Comparator<ImmutableBudgetCategory>(){ |
44 | |
public int compare(ImmutableBudgetCategory o1, ImmutableBudgetCategory o2) { |
45 | |
|
46 | 0 | if (o1.isIncome() != o2.isIncome()){ |
47 | 0 | if (o1.isIncome()){ |
48 | 0 | return -1; |
49 | |
} |
50 | |
else { |
51 | 0 | return 1; |
52 | |
} |
53 | |
} |
54 | |
|
55 | |
|
56 | 0 | return o1.toString().compareTo(o2.toString()); |
57 | |
} |
58 | |
}); |
59 | |
|
60 | 0 | sb.append("<h1>").append(TextFormatter.getTranslation(BuddiKeys.REPORT_SUMMARY)).append("</h1>\n"); |
61 | 0 | sb.append("<table class='main'>\n"); |
62 | |
|
63 | 0 | sb.append("<tr><th>"); |
64 | 0 | sb.append(TextFormatter.getTranslation(BuddiKeys.NAME)); |
65 | 0 | sb.append("</th><th>"); |
66 | 0 | sb.append(TextFormatter.getTranslation(BuddiKeys.ACTUAL)); |
67 | 0 | sb.append("</th><th>"); |
68 | 0 | sb.append(TextFormatter.getTranslation(BuddiKeys.BUDGETED)); |
69 | 0 | sb.append("</th><th>"); |
70 | 0 | sb.append(TextFormatter.getTranslation(BuddiKeys.DIFFERENCE)); |
71 | 0 | sb.append("</th></tr>\n"); |
72 | |
|
73 | 0 | long totalActual = 0, totalBudgeted = 0; |
74 | |
|
75 | 0 | for (ImmutableBudgetCategory c : categories){ |
76 | 0 | List<ImmutableTransaction> transactions = model.getImmutableTransactions(c, startDate, endDate); |
77 | 0 | long actual = 0; |
78 | 0 | for (ImmutableTransaction transaction : transactions) { |
79 | 0 | if (!transaction.isDeleted()){ |
80 | |
|
81 | |
|
82 | 0 | if (transaction.getFrom().equals(c) || transaction.getTo().equals(c)){ |
83 | 0 | actual += transaction.getAmount(); |
84 | |
} |
85 | |
|
86 | 0 | for (ImmutableTransactionSplit split : transaction.getImmutableToSplits()) { |
87 | 0 | if (split.getSource().equals(c)){ |
88 | 0 | actual += split.getAmount(); |
89 | |
} |
90 | |
} |
91 | 0 | for (ImmutableTransactionSplit split : transaction.getImmutableFromSplits()) { |
92 | 0 | if (split.getSource().equals(c)){ |
93 | 0 | actual -= split.getAmount(); |
94 | |
} |
95 | |
} |
96 | |
|
97 | |
|
98 | 0 | if (transaction.getTo() instanceof ImmutableBudgetCategory){ |
99 | 0 | totalActual -= transaction.getAmount(); |
100 | |
} |
101 | 0 | else if (transaction.getFrom() instanceof ImmutableBudgetCategory){ |
102 | 0 | totalActual += transaction.getAmount(); |
103 | |
} |
104 | |
|
105 | |
|
106 | 0 | if (transaction.getTo() instanceof ImmutableSplit){ |
107 | 0 | for (ImmutableTransactionSplit split : transaction.getImmutableToSplits()) { |
108 | 0 | if (split.getSource().equals(c)){ |
109 | 0 | totalActual -= split.getAmount(); |
110 | |
} |
111 | |
} |
112 | |
} |
113 | 0 | if (transaction.getFrom() instanceof ImmutableSplit){ |
114 | 0 | for (ImmutableTransactionSplit split : transaction.getImmutableFromSplits()) { |
115 | 0 | if (split.getSource().equals(c)){ |
116 | 0 | totalActual += split.getAmount(); |
117 | |
} |
118 | |
} |
119 | |
} |
120 | |
} |
121 | |
} |
122 | |
|
123 | 0 | long budgeted = c.getAmount(startDate, endDate); |
124 | 0 | if (c.isIncome()){ |
125 | 0 | totalBudgeted += budgeted; |
126 | |
} |
127 | |
else { |
128 | 0 | totalBudgeted -= budgeted; |
129 | |
} |
130 | |
|
131 | |
|
132 | 0 | if (budgeted != 0 || transactions.size() > 0){ |
133 | 0 | sb.append("<tr>"); |
134 | 0 | sb.append("<td>"); |
135 | 0 | sb.append(TextFormatter.getTranslation(c.toString())); |
136 | 0 | sb.append("</td><td class='right" + (TextFormatter.isRed(c, actual) ? " red'>" : "'>")); |
137 | 0 | sb.append(TextFormatter.getFormattedCurrency(actual)); |
138 | 0 | sb.append("</td><td class='right" + (TextFormatter.isRed(c, budgeted) ? " red'>" : "'>")); |
139 | 0 | sb.append(TextFormatter.getFormattedCurrency(budgeted)); |
140 | 0 | long difference = actual - budgeted; |
141 | 0 | sb.append("</td><td class='right" + (difference > 0 ^ c.isIncome() ? " red'>" : "'>")); |
142 | 0 | sb.append(TextFormatter.getFormattedCurrency(difference, false, difference < 0)); |
143 | 0 | sb.append("</td></tr>\n"); |
144 | |
} |
145 | 0 | } |
146 | |
|
147 | 0 | sb.append("<tr><th>"); |
148 | 0 | sb.append(TextFormatter.getTranslation(BuddiKeys.TOTAL)); |
149 | 0 | sb.append("</th><th class='right" + (totalActual < 0 ? " red'>" : "'>")); |
150 | 0 | sb.append(TextFormatter.getFormattedCurrency(totalActual)); |
151 | 0 | sb.append("</th><th class='right" + (totalBudgeted < 0 ? " red'>" : "'>")); |
152 | 0 | sb.append(TextFormatter.getFormattedCurrency(totalBudgeted)); |
153 | 0 | long totalDifference = totalActual - totalBudgeted; |
154 | 0 | sb.append("</th><th class='right" + (totalDifference < 0 ? " red'>" : "'>")); |
155 | 0 | sb.append(TextFormatter.getFormattedCurrency(totalDifference, false, totalDifference < 0)); |
156 | 0 | sb.append("</th></tr>\n"); |
157 | |
|
158 | 0 | sb.append("</table>\n\n"); |
159 | |
|
160 | 0 | sb.append("<hr>\n"); |
161 | |
|
162 | 0 | sb.append("<h1>").append(TextFormatter.getTranslation(BuddiKeys.REPORT_DETAILS)).append("</h1>\n"); |
163 | |
|
164 | 0 | for (ImmutableBudgetCategory c : categories){ |
165 | 0 | List<ImmutableTransaction> transactions = model.getImmutableTransactions(c, startDate, endDate); |
166 | |
|
167 | |
|
168 | 0 | if (transactions.size() > 0){ |
169 | 0 | sb.append(c.isIncome() ? "<h2>" : "<h2 class='red'>"); |
170 | 0 | sb.append(TextFormatter.getTranslation(c.toString())); |
171 | 0 | sb.append("</h2>\n"); |
172 | |
|
173 | 0 | sb.append(HtmlHelper.getHtmlTransactionHeader()); |
174 | |
|
175 | 0 | for (ImmutableTransaction t : transactions) { |
176 | 0 | if (!t.isDeleted()){ |
177 | 0 | sb.append(HtmlHelper.getHtmlTransactionRow(t, c)); |
178 | |
} |
179 | |
} |
180 | |
|
181 | 0 | sb.append(HtmlHelper.getHtmlTransactionFooter()); |
182 | |
} |
183 | 0 | } |
184 | |
|
185 | 0 | sb.append(HtmlHelper.getHtmlFooter()); |
186 | |
|
187 | 0 | return new HtmlPage(sb.toString(), null); |
188 | |
} |
189 | |
|
190 | |
public String getName() { |
191 | 0 | return TextFormatter.getTranslation(BuddiKeys.REPORT_TITLE_INCOME_AND_EXPENSES_BY_CATEGORY); |
192 | |
} |
193 | |
|
194 | |
public String getDescription() { |
195 | 3025 | return BuddiKeys.REPORT_DESCRIPTION_INCOME_EXPENSES_BY_CATEGORY.toString(); |
196 | |
} |
197 | |
@Override |
198 | |
public PluginReportDateRangeChoices getDateRangeChoice() { |
199 | 3025 | return PluginReportDateRangeChoices.INTERVAL; |
200 | |
} |
201 | |
|
202 | |
public boolean isPluginActive() { |
203 | 3025 | return true; |
204 | |
} |
205 | |
} |