blob: 1bd9b2a962f559722837b275d31f6a25667fd1b5 [file] [log] [blame]
Nabin Hait2df4d542013-01-29 11:34:39 +05301# ERPNext - web based ERP (http://erpnext.com)
2# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17from __future__ import unicode_literals
18import webnotes
19from webnotes.utils import flt, cstr
20from webnotes.model.doc import Document
21
22from webnotes.model.controller import DocListController
23
24class AccountsController(DocListController):
25 def make_gl_entries(self, cancel=False, adv_adj=False, mapper=None, merge_entries=True,
26 update_outstanding='Yes', gl_map=None):
27 self.entries = []
28 self.merged_entries = []
29 self.total_debit = self.total_credit = 0.0
30
31 if gl_map:
32 self.entries = gl_map
33 else:
34 self.make_gl_map(mapper)
35
36 self.merge_similar_entries(merge_entries)
37
38 self.check_budget(cancel)
39 self.save_entries(cancel, adv_adj, update_outstanding)
40
41 if cancel:
42 self.set_as_cancel()
43 else:
44 self.validate_total_debit_credit()
45
46
47 def make_gl_map(self, mapper):
48 def _gl_map(parent, d, entry_map):
49 if self.get_val(entry_map['account'], d, parent) \
50 and (self.get_val(entry_map['debit'], d, parent)
51 or self.get_val(entry_map['credit'], d, parent)):
52 gl_dict = {}
53 for k in entry_map:
54 gl_dict[k] = self.get_val(entry_map[k], d, parent)
55 self.entries.append(gl_dict)
56
57 # get entries
58 gl_fields = ", ".join([d.fieldname for d in \
59 webnotes.model.doctype.get("GL Mapper Detail").get({
60 "doctype": "DocField", "parent": "GL Mapper Detail"})])
61 entry_map_list = webnotes.conn.sql("""select %s from `tabGL Mapper Detail`
62 where parent = %s""" % (gl_fields, '%s'), mapper or self.doc.doctype, as_dict=1)
63
64 for entry_map in entry_map_list:
65 table_field = entry_map.get("table_field")
66
67 # table_field does not exist in gl entry table
68 entry_map.pop("table_field")
69
70 if table_field:
71 for d in self.doclist.get({"parentfield": table_field}):
72 # taxes_and_charges is the table of other charges in purchase cycle
73 if table_field == "taxes_and_charges" and \
74 d.fields.get("category") == "Valuation":
75 # don't create gl entry for only valuation type charges
76 continue
77 _gl_map(self.doc, d, entry_map)
78 else:
79 _gl_map(None, self.doc, entry_map)
80
81
82 def get_val(self, src, d, parent=None):
83 """Get field values from the voucher"""
84 if not src:
85 return None
86 if src.startswith('parent:'):
87 return parent.fields[src.split(':')[1]]
88 elif src.startswith('value:'):
89 return eval(src.split(':')[1])
90 elif src:
91 return d.fields.get(src)
92
93
94 def merge_similar_entries(self, merge_entries):
95 if merge_entries:
96 for entry in self.entries:
97 # if there is already an entry in this account then just add it
98 # to that entry
99 same_head = self.check_if_in_list(entry)
100 if same_head:
101 same_head['debit'] = flt(same_head['debit']) + flt(entry['debit'])
102 same_head['credit'] = flt(same_head['credit']) + flt(entry['credit'])
103 else:
104 self.merged_entries.append(entry)
105 else:
106 self.merged_entries = self.entries
107
108
109 def save_entries(self, cancel, adv_adj, update_outstanding):
110 def _swap(gle):
111 gle.debit, gle.credit = abs(flt(gle.credit)), abs(flt(gle.debit))
112
113 for entry in self.merged_entries:
114 gle = Document('GL Entry', fielddata=entry)
115
116 # toggle debit, credit if negative entry
117 if flt(gle.debit) < 0 or flt(gle.credit) < 0:
118 _swap(gle)
119
120 # toggled debit/credit in two separate condition because
121 # both should be executed at the
122 # time of cancellation when there is negative amount (tax discount)
123 if cancel:
124 _swap(gle)
125
126 gle_obj = webnotes.get_obj(doc=gle)
127 # validate except on_cancel
128 if not cancel:
129 gle_obj.validate()
130
131 # save
132 gle.save(1)
133 gle_obj.on_update(adv_adj, cancel, update_outstanding)
134
135 # update total debit / credit
136 self.total_debit += flt(gle.debit)
137 self.total_credit += flt(gle.credit)
138
139 def check_if_in_list(self, gle):
140 for e in self.merged_entries:
141 if e['account'] == gle['account'] and \
142 cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \
143 and cstr(e.get('against_voucher_type')) == \
144 cstr(gle.get('against_voucher_type')) \
145 and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')):
146 return e
147
148 def validate_total_debit_credit(self):
149 if abs(self.total_debit - self.total_credit) > 0.005:
150 webnotes.msgprint("""Debit and Credit not equal for
151 this voucher: Diff (Debit) is %s""" %
152 (self.total_debit - self.total_credit), raise_exception=1)
153
154 def set_as_cancel(self):
155 webnotes.conn.sql("""update `tabGL Entry` set is_cancelled='Yes'
156 where voucher_type=%s and voucher_no=%s""", (self.doc.doctype, self.doc.name))
157
158 def check_budget(self, cancel):
159 for gle in self.merged_entries:
160 if gle.get('cost_center'):
161 #check budget only if account is expense account
162 acc_details = webnotes.conn.get_value("Account", gle['account'],
163 ['is_pl_account', 'debit_or_credit'])
164
165 if acc_details[0]=="Yes" and acc_details[1]=="Debit":
166 webnotes.get_obj('Budget Control').check_budget(gle, cancel)
167
168 def get_gl_dict(self, args, cancel):
169 """this method populates the common properties of a gl entry record"""
170 gl_dict = {
171 'company': self.doc.company,
172 'posting_date': self.doc.posting_date,
173 'voucher_type': self.doc.doctype,
174 'voucher_no': self.doc.name,
175 'aging_date': self.doc.fields.get("aging_date") or self.doc.posting_date,
176 'remarks': self.doc.remarks,
177 'is_cancelled': cancel and "Yes" or "No",
178 'fiscal_year': self.doc.fiscal_year,
179 'debit': 0,
180 'credit': 0,
181 'is_opening': self.doc.fields.get("is_opening") or "No",
182 }
183 gl_dict.update(args)
184 return gl_dict
185
186 def get_company_abbr(self):
187 return webnotes.conn.get_value("Company", self.doc.company, "abbr")
188
189 def get_stock_in_hand_account(self):
190 stock_in_hand = webnotes.conn.get_value("Company", self.doc.company, "stock_in_hand")
191
192 if not stock_in_hand:
193 webnotes.msgprint("""Please specify "Stock In Hand" account
194 for company: %s""" % (self.doc.company,), raise_exception=1)
195
196 return stock_in_hand