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