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 "convert.h"
35 #include "csv.h"
36 #include "scope.h"
37 #include "iterators.h"
38 #include "report.h"
39 #include "xact.h"
40 #include "print.h"
41 #include "lookup.h"
42 
43 namespace ledger {
44 
convert_command(call_scope_t & args)45 value_t convert_command(call_scope_t& args)
46 {
47   report_t&  report(args.context<report_t>());
48   journal_t& journal(*report.session.journal.get());
49 
50   string bucket_name;
51   if (report.HANDLED(account_))
52     bucket_name = report.HANDLER(account_).str();
53   else
54     bucket_name = _("Equity:Unknown");
55 
56   account_t * bucket  = journal.master->find_account(bucket_name);
57   account_t * unknown = journal.master->find_account(_("Expenses:Unknown"));
58 
59   // Create a flat list
60   xacts_list current_xacts(journal.xacts_begin(), journal.xacts_end());
61 
62   // Read in the series of transactions from the CSV file
63 
64   print_xacts formatter(report);
65   path        csv_file_path(args.get<string>(0));
66 
67   report.session.parsing_context.push(csv_file_path);
68   parse_context_t& context(report.session.parsing_context.get_current());
69   context.journal = &journal;
70   context.master  = bucket;
71 
72   csv_reader reader(context);
73 
74   try {
75     while (xact_t * xact = reader.read_xact(report.HANDLED(rich_data))) {
76       if (report.HANDLED(invert)) {
77         foreach (post_t * post, xact->posts)
78           post->amount.in_place_negate();
79       }
80 
81       string ref = (xact->has_tag(_("UUID")) ?
82                     xact->get_tag(_("UUID"))->to_string() :
83                     sha1sum(reader.get_last_line()));
84 
85       checksum_map_t::const_iterator entry = journal.checksum_map.find(ref);
86       if (entry != journal.checksum_map.end()) {
87         INFO(file_context(reader.get_pathname(),
88                           reader.get_linenum())
89              << " " << "Ignoring known UUID " << ref);
90         checked_delete(xact);     // ignore it
91         continue;
92       }
93 
94       if (report.HANDLED(rich_data) && ! xact->has_tag(_("UUID")))
95         xact->set_tag(_("UUID"), string_value(ref));
96 
97       if (xact->posts.front()->account == NULL) {
98         if (account_t * acct =
99             (report.HANDLED(auto_match) ?
100              lookup_probable_account(xact->payee, current_xacts.rbegin(),
101                                      current_xacts.rend(), bucket).second :
102              NULL))
103           xact->posts.front()->account = acct;
104         else
105           xact->posts.front()->account = unknown;
106       }
107 
108       if (! journal.add_xact(xact)) {
109         checked_delete(xact);
110         throw_(std::runtime_error,
111                _("Failed to finalize derived transaction (check commodities)"));
112       }
113       else {
114         xact_posts_iterator xact_iter(*xact);
115         while (post_t * post = *xact_iter++)
116           formatter(*post);
117       }
118     }
119     formatter.flush();
120   }
121   catch (const std::exception&) {
122     add_error_context(_f("While parsing file %1%")
123                       % file_context(reader.get_pathname(),
124                                       reader.get_linenum()));
125     add_error_context(_("While parsing CSV line:"));
126     add_error_context(line_context(reader.get_last_line()));
127     throw;
128   }
129 
130   // If not, transform the payee according to regexps
131 
132   // Set the account to a default vaule, then transform the account according
133   // to the payee
134 
135   // Print out the final form of the transaction
136 
137   return true;
138 }
139 
140 } // namespace ledger
141