1 /*
2 * Copyright (c) 2003-2018, John Wiegley. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of New Artisans LLC nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <system.hh>
33
34 #include "balance.h"
35 #include "commodity.h"
36 #include "annotate.h"
37 #include "pool.h"
38 #include "unistring.h" // for justify()
39
40 namespace ledger {
41
balance_t(const double val)42 balance_t::balance_t(const double val)
43 {
44 amounts.insert
45 (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
46 TRACE_CTOR(balance_t, "const double");
47 }
48
balance_t(const unsigned long val)49 balance_t::balance_t(const unsigned long val)
50 {
51 amounts.insert
52 (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
53 TRACE_CTOR(balance_t, "const unsigned long");
54 }
55
balance_t(const long val)56 balance_t::balance_t(const long val)
57 {
58 amounts.insert
59 (amounts_map::value_type(commodity_pool_t::current_pool->null_commodity, val));
60 TRACE_CTOR(balance_t, "const long");
61 }
62
operator +=(const balance_t & bal)63 balance_t& balance_t::operator+=(const balance_t& bal)
64 {
65 foreach (const amounts_map::value_type& pair, bal.amounts)
66 *this += pair.second;
67 return *this;
68 }
69
operator +=(const amount_t & amt)70 balance_t& balance_t::operator+=(const amount_t& amt)
71 {
72 if (amt.is_null())
73 throw_(balance_error,
74 _("Cannot add an uninitialized amount to a balance"));
75
76 if (amt.is_realzero())
77 return *this;
78
79 amounts_map::iterator i =
80 amt.commodity().has_annotation() ?
81 find_by_name(amt.commodity()) : amounts.find(&amt.commodity());
82 if (i != amounts.end())
83 i->second += amt;
84 else
85 amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
86
87 return *this;
88 }
89
operator -=(const balance_t & bal)90 balance_t& balance_t::operator-=(const balance_t& bal)
91 {
92 foreach (const amounts_map::value_type& pair, bal.amounts)
93 *this -= pair.second;
94 return *this;
95 }
96
operator -=(const amount_t & amt)97 balance_t& balance_t::operator-=(const amount_t& amt)
98 {
99 if (amt.is_null())
100 throw_(balance_error,
101 _("Cannot subtract an uninitialized amount from a balance"));
102
103 if (amt.is_realzero())
104 return *this;
105
106 amounts_map::iterator i =
107 amt.commodity().has_annotation() ?
108 find_by_name(amt.commodity()) : amounts.find(&amt.commodity());
109 if (i != amounts.end()) {
110 i->second -= amt;
111 if (i->second.is_realzero())
112 amounts.erase(i);
113 } else {
114 amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negated()));
115 }
116 return *this;
117 }
118
operator *=(const amount_t & amt)119 balance_t& balance_t::operator*=(const amount_t& amt)
120 {
121 if (amt.is_null())
122 throw_(balance_error,
123 _("Cannot multiply a balance by an uninitialized amount"));
124
125 if (is_realzero()) {
126 ;
127 }
128 else if (amt.is_realzero()) {
129 *this = amt;
130 }
131 else if (! amt.commodity()) {
132 // Multiplying by an amount with no commodity causes all the
133 // component amounts to be increased by the same factor.
134 foreach (amounts_map::value_type& pair, amounts)
135 pair.second *= amt;
136 }
137 else if (amounts.size() == 1) {
138 // Multiplying by a commoditized amount is only valid if the sole
139 // commodity in the balance is of the same kind as the amount's
140 // commodity.
141 if (*amounts.begin()->first == amt.commodity())
142 amounts.begin()->second *= amt;
143 else
144 throw_(balance_error,
145 _("Cannot multiply a balance with annotated commodities by a commoditized amount"));
146 }
147 else {
148 assert(amounts.size() > 1);
149 throw_(balance_error,
150 _("Cannot multiply a multi-commodity balance by a commoditized amount"));
151 }
152 return *this;
153 }
154
operator /=(const amount_t & amt)155 balance_t& balance_t::operator/=(const amount_t& amt)
156 {
157 if (amt.is_null())
158 throw_(balance_error,
159 _("Cannot divide a balance by an uninitialized amount"));
160
161 if (is_realzero()) {
162 ;
163 }
164 else if (amt.is_realzero()) {
165 throw_(balance_error, _("Divide by zero"));
166 }
167 else if (! amt.commodity()) {
168 // Dividing by an amount with no commodity causes all the
169 // component amounts to be divided by the same factor.
170 foreach (amounts_map::value_type& pair, amounts)
171 pair.second /= amt;
172 }
173 else if (amounts.size() == 1) {
174 // Dividing by a commoditized amount is only valid if the sole
175 // commodity in the balance is of the same kind as the amount's
176 // commodity.
177 if (*amounts.begin()->first == amt.commodity())
178 amounts.begin()->second /= amt;
179 else
180 throw_(balance_error,
181 _("Cannot divide a balance with annotated commodities by a commoditized amount"));
182 }
183 else {
184 assert(amounts.size() > 1);
185 throw_(balance_error,
186 _("Cannot divide a multi-commodity balance by a commoditized amount"));
187 }
188 return *this;
189 }
190
191 optional<balance_t>
value(const datetime_t & moment,const commodity_t * in_terms_of) const192 balance_t::value(const datetime_t& moment,
193 const commodity_t * in_terms_of) const
194 {
195 balance_t temp;
196 bool resolved = false;
197
198 foreach (const amounts_map::value_type& pair, amounts) {
199 if (optional<amount_t> val = pair.second.value(moment, in_terms_of)) {
200 temp += *val;
201 resolved = true;
202 } else {
203 temp += pair.second;
204 }
205 }
206 return resolved ? temp : optional<balance_t>();
207 }
208
209 balance_t::amounts_map::iterator
find_by_name(const commodity_t & comm)210 balance_t::find_by_name(const commodity_t& comm)
211 {
212 for (amounts_map::iterator i = amounts.begin();
213 i != amounts.end();
214 i++) {
215 if (*(*i).first == comm)
216 return i;
217 }
218 return amounts.end();
219 }
220
221 balance_t::amounts_map::const_iterator
find_by_name(const commodity_t & comm) const222 balance_t::find_by_name(const commodity_t& comm) const
223 {
224 for (amounts_map::const_iterator i = amounts.begin();
225 i != amounts.end();
226 i++) {
227 if (*(*i).first == comm)
228 return i;
229 }
230 return amounts.end();
231 }
232
233 optional<amount_t>
commodity_amount(const optional<const commodity_t &> & commodity) const234 balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const
235 {
236 if (! commodity) {
237 if (amounts.size() == 1) {
238 return amounts.begin()->second;
239 }
240 else if (amounts.size() > 1) {
241 // Try stripping annotations before giving an error.
242 balance_t temp(strip_annotations(keep_details_t()));
243 if (temp.amounts.size() == 1)
244 return temp.commodity_amount(commodity);
245
246 throw_(amount_error,
247 _f("Requested amount of a balance with multiple commodities: %1%")
248 % temp);
249 }
250 }
251 else if (amounts.size() > 0) {
252 amounts_map::const_iterator i =
253 commodity->has_annotation() ?
254 find_by_name(*commodity) :
255 amounts.find(const_cast<commodity_t *>(&*commodity));
256 if (i != amounts.end())
257 return i->second;
258 }
259 return none;
260 }
261
262 balance_t
strip_annotations(const keep_details_t & what_to_keep) const263 balance_t::strip_annotations(const keep_details_t& what_to_keep) const
264 {
265 balance_t temp;
266
267 foreach (const amounts_map::value_type& pair, amounts)
268 temp += pair.second.strip_annotations(what_to_keep);
269
270 return temp;
271 }
272
sorted_amounts(amounts_array & sorted) const273 void balance_t::sorted_amounts(amounts_array& sorted) const
274 {
275 foreach (const amounts_map::value_type& pair, amounts)
276 sorted.push_back(&pair.second);
277 std::stable_sort(
278 sorted.begin(), sorted.end(),
279 [](const amount_t * left, const amount_t * right) {
280 return commodity_t::compare_by_commodity()(left, right) < 0;
281 });
282 }
283
map_sorted_amounts(function<void (const amount_t &)> fn) const284 void balance_t::map_sorted_amounts(function<void(const amount_t&)> fn) const
285 {
286 if (! amounts.empty()) {
287 if (amounts.size() == 1) {
288 const amount_t& amount((*amounts.begin()).second);
289 if (amount)
290 fn(amount);
291 }
292 else {
293 amounts_array sorted;
294 sorted_amounts(sorted);
295 foreach (const amount_t * amount, sorted)
296 fn(*amount);
297 }
298 }
299 }
300
301 namespace {
302 struct print_amount_from_balance
303 {
304 std::ostream& out;
305 bool& first;
306 int fwidth;
307 int lwidth;
308 uint_least8_t flags;
309
print_amount_from_balanceledger::__anonc58dea010211::print_amount_from_balance310 explicit print_amount_from_balance(std::ostream& _out,
311 bool& _first,
312 int _fwidth, int _lwidth,
313 uint_least8_t _flags)
314 : out(_out), first(_first), fwidth(_fwidth), lwidth(_lwidth),
315 flags(_flags) {
316 TRACE_CTOR(print_amount_from_balance,
317 "ostream&, int, int, uint_least8_t");
318 }
print_amount_from_balanceledger::__anonc58dea010211::print_amount_from_balance319 print_amount_from_balance(const print_amount_from_balance& other)
320 : out(other.out), first(other.first), fwidth(other.fwidth),
321 lwidth(other.lwidth), flags(other.flags) {
322 TRACE_CTOR(print_amount_from_balance, "copy");
323 }
~print_amount_from_balanceledger::__anonc58dea010211::print_amount_from_balance324 ~print_amount_from_balance() throw() {
325 TRACE_DTOR(print_amount_from_balance);
326 }
327
operator ()ledger::__anonc58dea010211::print_amount_from_balance328 void operator()(const amount_t& amount) {
329 int width;
330 if (! first) {
331 out << std::endl;
332 width = lwidth;
333 } else {
334 first = false;
335 width = fwidth;
336 }
337
338 std::ostringstream buf;
339 amount.print(buf, flags);
340
341 justify(out, buf.str(), width,
342 flags & AMOUNT_PRINT_RIGHT_JUSTIFY,
343 flags & AMOUNT_PRINT_COLORIZE && amount.sign() < 0);
344 }
345
closeledger::__anonc58dea010211::print_amount_from_balance346 void close() {
347 out.width(fwidth);
348 if (flags & AMOUNT_PRINT_RIGHT_JUSTIFY)
349 out << std::right;
350 else
351 out << std::left;
352 out << 0;
353 }
354 };
355 }
356
print(std::ostream & out,const int first_width,const int latter_width,const uint_least8_t flags) const357 void balance_t::print(std::ostream& out,
358 const int first_width,
359 const int latter_width,
360 const uint_least8_t flags) const
361 {
362 bool first = true;
363 print_amount_from_balance
364 amount_printer(out, first, first_width,
365 latter_width == 1 ? first_width : latter_width, flags);
366 map_sorted_amounts(amount_printer);
367
368 if (first)
369 amount_printer.close();
370 }
371
put_balance(property_tree::ptree & st,const balance_t & bal)372 void put_balance(property_tree::ptree& st, const balance_t& bal)
373 {
374 foreach (const balance_t::amounts_map::value_type& pair, bal.amounts)
375 put_amount(st.add("amount", ""), pair.second);
376 }
377
average_lot_prices(const balance_t & bal)378 balance_t average_lot_prices(const balance_t& bal)
379 {
380 // First, we split the balance into multiple balances by underlying
381 // commodity.
382 typedef std::map<optional<std::string>,
383 std::pair<amount_t, annotation_t> > balance_map;
384 balance_map bycomm;
385
386 foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) {
387 optional<std::string> sym(pair.first->symbol());
388 amount_t quant(pair.second.strip_annotations(keep_details_t()));
389
390 balance_map::iterator i = bycomm.find(sym);
391 if (i == bycomm.end()) {
392 bycomm.insert(
393 balance_map::value_type(sym, std::make_pair(quant, annotation_t())));
394 i = bycomm.find(sym); // must succeed now
395 } else {
396 (*i).second.first += quant;
397 }
398
399 if (pair.first->has_annotation()) {
400 annotated_commodity_t& acomm(static_cast<annotated_commodity_t&>(*pair.first));
401 annotation_t& ann((*i).second.second);
402
403 if (acomm.details.price) {
404 if (ann.price)
405 ann.price = *ann.price + (*acomm.details.price * quant);
406 else
407 ann.price = *acomm.details.price * quant;
408 }
409
410 if (acomm.details.date) {
411 if (! ann.date || *acomm.details.date < *ann.date)
412 ann.date = *acomm.details.date;
413 }
414 }
415 }
416
417 balance_t result;
418
419 foreach (balance_map::value_type& pair, bycomm) {
420 amount_t amt(pair.second.first);
421 if (! amt.is_realzero()) {
422 if (pair.second.second.price)
423 pair.second.second.price = *pair.second.second.price / amt;
424
425 commodity_t * acomm =
426 commodity_pool_t::current_pool->find_or_create(
427 amt.commodity(), pair.second.second);
428 amt.set_commodity(*acomm);
429
430 result += amt;
431 }
432 }
433
434 return result;
435 }
436
437 } // namespace ledger
438