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 /**
33  * @addtogroup data
34  */
35 
36 /**
37  * @file   post.h
38  * @author John Wiegley
39  *
40  * @ingroup data
41  */
42 #ifndef _POST_H
43 #define _POST_H
44 
45 #include "item.h"
46 
47 namespace ledger {
48 
49 class xact_t;
50 class account_t;
51 
52 class post_t : public item_t
53 {
54 public:
55 #define POST_VIRTUAL         0x0010 // the account was specified with (parens)
56 #define POST_MUST_BALANCE    0x0020 // posting must balance in the transaction
57 #define POST_CALCULATED      0x0040 // posting's amount was calculated
58 #define POST_COST_CALCULATED 0x0080 // posting's cost was calculated
59 #define POST_COST_IN_FULL    0x0100 // cost specified using @@
60 #define POST_COST_FIXATED    0x0200 // cost is fixed using = indicator
61 #define POST_COST_VIRTUAL    0x0400 // cost is virtualized: (@)
62 #define POST_ANONYMIZED      0x0800 // a temporary, anonymous posting
63 #define POST_DEFERRED        0x1000 // the account was specified with <angles>
64 
65   xact_t *    xact;             // only set for posts of regular xacts
66   account_t * account;
67 
68   amount_t             amount;  // can be null until finalization
69   optional<expr_t>     amount_expr;
70   optional<amount_t>   cost;
71   optional<amount_t>   given_cost;
72   optional<amount_t>   assigned_amount;
73   optional<datetime_t> checkin;
74   optional<datetime_t> checkout;
75 
76   post_t(account_t * _account = NULL,
77          flags_t     _flags   = ITEM_NORMAL)
item_t(_flags)78     : item_t(_flags), xact(NULL), account(_account)
79   {
80     TRACE_CTOR(post_t, "account_t *, flags_t");
81   }
82   post_t(account_t *             _account,
83          const amount_t&         _amount,
84          flags_t                 _flags = ITEM_NORMAL,
85          const optional<string>& _note = none)
item_t(_flags,_note)86     : item_t(_flags, _note), xact(NULL), account(_account), amount(_amount)
87   {
88     TRACE_CTOR(post_t, "account_t *, amount_t, flags_t, optional<string>");
89   }
post_t(const post_t & post)90   post_t(const post_t& post)
91     : item_t(post),
92       xact(post.xact),
93       account(post.account),
94       amount(post.amount),
95       cost(post.cost),
96       assigned_amount(post.assigned_amount),
97       checkin(post.checkin),
98       checkout(post.checkout),
99       xdata_(post.xdata_)
100   {
101     copy_details(post);
102     TRACE_CTOR(post_t, "copy");
103   }
~post_t()104   virtual ~post_t() {
105     TRACE_DTOR(post_t);
106   }
107 
description()108   virtual string description() {
109     if (pos) {
110       std::ostringstream buf;
111       buf << _f("posting at line %1%") % pos->beg_line;
112       return buf.str();
113     } else {
114       return string(_("generated posting"));
115     }
116   }
117 
118   virtual bool has_tag(const string& tag,
119                        bool          inherit = true) const;
120   virtual bool has_tag(const mask_t&           tag_mask,
121                        const optional<mask_t>& value_mask = none,
122                        bool                    inherit    = true) const;
123 
124   virtual optional<value_t> get_tag(const string& tag,
125                                     bool          inherit = true) const;
126   virtual optional<value_t> get_tag(const mask_t&           tag_mask,
127                                     const optional<mask_t>& value_mask = none,
128                                     bool                    inherit    = true) const;
129 
130   virtual date_t value_date() const;
131   virtual date_t date() const;
132   virtual date_t primary_date() const;
133   virtual optional<date_t> aux_date() const;
134 
135   string payee() const;
136 
must_balance()137   bool must_balance() const {
138     return ! has_flags(POST_VIRTUAL) || has_flags(POST_MUST_BALANCE);
139   }
140 
141   virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
142                                   const string& name);
143 
144   amount_t resolve_expr(scope_t& scope, expr_t& expr);
145 
146   std::size_t xact_id() const;
147   std::size_t account_id() const;
148 
copy_details(const item_t & item)149   virtual void copy_details(const item_t& item) {
150     const post_t& post(dynamic_cast<const post_t&>(item));
151     xdata_ = post.xdata_;
152     item_t::copy_details(item);
153   }
154 
155   bool valid() const;
156 
157   struct xdata_t : public supports_flags<uint_least16_t>
158   {
159 #define POST_EXT_RECEIVED   0x0001
160 #define POST_EXT_HANDLED    0x0002
161 #define POST_EXT_DISPLAYED  0x0004
162 #define POST_EXT_DIRECT_AMT 0x0008
163 #define POST_EXT_SORT_CALC  0x0010
164 #define POST_EXT_COMPOUND   0x0020
165 #define POST_EXT_VISITED    0x0040
166 #define POST_EXT_MATCHES    0x0080
167 #define POST_EXT_CONSIDERED 0x0100
168 
169     value_t     visited_value;
170     value_t     compound_value;
171     value_t     total;
172     std::size_t count;
173     date_t      date;
174     date_t      value_date;
175     datetime_t  datetime;
176     account_t * account;
177 
178     std::list<sort_value_t> sort_values;
179 
xdata_txdata_t180     xdata_t()
181       : supports_flags<uint_least16_t>(), count(0), account(NULL) {
182       TRACE_CTOR(post_t::xdata_t, "");
183     }
xdata_txdata_t184     xdata_t(const xdata_t& other)
185       : supports_flags<uint_least16_t>(other.flags()),
186         visited_value(other.visited_value),
187         compound_value(other.compound_value),
188         total(other.total),
189         count(other.count),
190         date(other.date),
191         account(other.account),
192         sort_values(other.sort_values)
193     {
194       TRACE_CTOR(post_t::xdata_t, "copy");
195     }
throwxdata_t196     ~xdata_t() throw() {
197       TRACE_DTOR(post_t::xdata_t);
198     }
199   };
200 
201   // This variable holds optional "extended data" which is usually produced
202   // only during reporting, and only for the posting set being reported.
203   // It's a memory-saving measure to delay allocation until the last possible
204   // moment.
205   mutable optional<xdata_t> xdata_;
206 
has_xdata()207   bool has_xdata() const {
208     return static_cast<bool>(xdata_);
209   }
clear_xdata()210   void clear_xdata() {
211     xdata_ = none;
212   }
xdata()213   xdata_t& xdata() {
214     if (! xdata_)
215       xdata_ = xdata_t();
216     return *xdata_;
217   }
xdata()218   const xdata_t& xdata() const {
219     return const_cast<post_t *>(this)->xdata();
220   }
221 
222   void add_to_value(value_t& value,
223                     const optional<expr_t&>& expr = none) const;
224 
225   void set_reported_account(account_t * account);
226 
reported_account()227   account_t * reported_account() {
228     if (xdata_)
229       if (account_t * acct = xdata_->account)
230         return acct;
231     assert(account);
232     return account;
233   }
234 
reported_account()235   const account_t * reported_account() const {
236     return const_cast<post_t *>(this)->reported_account();
237   }
238 
239   friend class xact_t;
240 
241   struct compare_by_date_and_sequence
242   {
operatorcompare_by_date_and_sequence243     bool operator()(const post_t * left, const post_t * right) const {
244       gregorian::date_duration duration =
245         left->primary_date() - right->primary_date();
246       if (duration.days() == 0) {
247         return ((left->pos ? left->pos->sequence : 0) <
248                 (right->pos ? right->pos->sequence : 0));
249       } else {
250         return duration.days() < 0;
251       }
252     }
253   };
254 };
255 
256 class journal_t;
257 void extend_post(post_t& post, journal_t& journal);
258 void put_post(property_tree::ptree& pt, const post_t& post);
259 
260 } // namespace ledger
261 
262 #endif // _POST_H
263