blob: 1836db6477f84de266ab188d5d432568d5011ef4 [file] [log] [blame]
Anand Doshi885e0742015-03-03 14:55:30 +05301# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
Rushabh Mehtae67d1fb2013-08-05 14:59:54 +05302# License: GNU General Public License v3. See license.txt
Nabin Haitbf495c92013-01-30 12:49:08 +05303
Chillar Anand915b3432021-09-02 16:44:59 +05304
5import frappe
Rushabh Mehta793ba6b2014-02-14 15:47:51 +05306from frappe import _
Nabin Haite2c200a2015-05-28 13:00:37 +05307from frappe.model.meta import get_field_precision
Chillar Anand915b3432021-09-02 16:44:59 +05308from frappe.utils import cint, cstr, flt, formatdate, getdate, now
9
10import erpnext
11from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
12 get_accounting_dimensions,
13)
Nabin Haitb9bc7d62016-05-16 14:38:47 +053014from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
Chillar Anand915b3432021-09-02 16:44:59 +053015
Nabin Haitbf495c92013-01-30 12:49:08 +053016
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053017class ClosedAccountingPeriod(frappe.ValidationError): pass
Nabin Haitbf495c92013-01-30 12:49:08 +053018
Nabin Haita77b8c92020-12-21 14:45:50 +053019def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
Nabin Hait2e296fa2013-08-28 18:53:11 +053020 if gl_map:
21 if not cancel:
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053022 validate_accounting_period(gl_map)
Nabin Hait2e296fa2013-08-28 18:53:11 +053023 gl_map = process_gl_map(gl_map, merge_entries)
nabinhaitc3432922014-07-29 18:06:18 +053024 if gl_map and len(gl_map) > 1:
Nabin Haita77b8c92020-12-21 14:45:50 +053025 save_entries(gl_map, adv_adj, update_outstanding, from_repost)
Deepesh Garg4c8d15b2021-04-26 15:24:34 +053026 # Post GL Map proccess there may no be any GL Entries
27 elif gl_map:
nabinhait4859b482014-07-30 14:28:24 +053028 frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
Nabin Hait2e296fa2013-08-28 18:53:11 +053029 else:
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +053030 make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
Anand Doshi652bc072014-04-16 15:21:46 +053031
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053032def validate_accounting_period(gl_map):
33 accounting_periods = frappe.db.sql(""" SELECT
34 ap.name as name
35 FROM
36 `tabAccounting Period` ap, `tabClosed Document` cd
37 WHERE
38 ap.name = cd.parent
39 AND ap.company = %(company)s
40 AND cd.closed = 1
41 AND cd.document_type = %(voucher_type)s
42 AND %(date)s between ap.start_date and ap.end_date
43 """, {
44 'date': gl_map[0].posting_date,
45 'company': gl_map[0].company,
46 'voucher_type': gl_map[0].voucher_type
47 }, as_dict=1)
48
49 if accounting_periods:
Deepesh Garg069a54e2020-08-10 16:01:01 +053050 frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
51 .format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053052
Nabin Hait19f8fa52021-02-22 22:27:22 +053053def process_gl_map(gl_map, merge_entries=True, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +053054 if merge_entries:
Nabin Hait19f8fa52021-02-22 22:27:22 +053055 gl_map = merge_similar_entries(gl_map, precision)
Nabin Hait27994c22013-08-26 16:53:30 +053056 for entry in gl_map:
Anand Doshi602e8252015-11-16 19:05:46 +053057 # toggle debit, credit if negative entry
Nabin Hait88f3cd52013-10-23 16:29:19 +053058 if flt(entry.debit) < 0:
59 entry.credit = flt(entry.credit) - flt(entry.debit)
60 entry.debit = 0.0
Anand Doshi602e8252015-11-16 19:05:46 +053061
Nabin Haitc561a492015-08-19 19:22:34 +053062 if flt(entry.debit_in_account_currency) < 0:
63 entry.credit_in_account_currency = \
64 flt(entry.credit_in_account_currency) - flt(entry.debit_in_account_currency)
65 entry.debit_in_account_currency = 0.0
Anand Doshi602e8252015-11-16 19:05:46 +053066
Nabin Hait88f3cd52013-10-23 16:29:19 +053067 if flt(entry.credit) < 0:
68 entry.debit = flt(entry.debit) - flt(entry.credit)
69 entry.credit = 0.0
Anand Doshi602e8252015-11-16 19:05:46 +053070
Nabin Haitc561a492015-08-19 19:22:34 +053071 if flt(entry.credit_in_account_currency) < 0:
72 entry.debit_in_account_currency = \
73 flt(entry.debit_in_account_currency) - flt(entry.credit_in_account_currency)
74 entry.credit_in_account_currency = 0.0
Anand Doshi602e8252015-11-16 19:05:46 +053075
Deepesh Garg5ba3b282021-11-25 15:42:30 +053076 update_net_values(entry)
77
Nabin Hait27994c22013-08-26 16:53:30 +053078 return gl_map
Anand Doshi652bc072014-04-16 15:21:46 +053079
Deepesh Garg5ba3b282021-11-25 15:42:30 +053080def update_net_values(entry):
81 # In some scenarios net value needs to be shown in the ledger
82 # This method updates net values as debit or credit
83 if entry.post_net_value and entry.debit and entry.credit:
84 if entry.debit > entry.credit:
85 entry.debit = entry.debit - entry.credit
86 entry.debit_in_account_currency = entry.debit_in_account_currency \
87 - entry.credit_in_account_currency
88 entry.credit = 0
89 entry.credit_in_account_currency = 0
90 else:
91 entry.credit = entry.credit - entry.debit
92 entry.credit_in_account_currency = entry.credit_in_account_currency \
93 - entry.debit_in_account_currency
94
95 entry.debit = 0
96 entry.debit_in_account_currency = 0
97
Nabin Hait19f8fa52021-02-22 22:27:22 +053098def merge_similar_entries(gl_map, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +053099 merged_gl_map = []
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530100 accounting_dimensions = get_accounting_dimensions()
Nabin Haitbf495c92013-01-30 12:49:08 +0530101 for entry in gl_map:
Anand Doshi652bc072014-04-16 15:21:46 +0530102 # if there is already an entry in this account then just add it
Nabin Haitbf495c92013-01-30 12:49:08 +0530103 # to that entry
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530104 same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
Nabin Haitbf495c92013-01-30 12:49:08 +0530105 if same_head:
Nabin Hait2e296fa2013-08-28 18:53:11 +0530106 same_head.debit = flt(same_head.debit) + flt(entry.debit)
Nabin Haitc561a492015-08-19 19:22:34 +0530107 same_head.debit_in_account_currency = \
108 flt(same_head.debit_in_account_currency) + flt(entry.debit_in_account_currency)
Nabin Hait2e296fa2013-08-28 18:53:11 +0530109 same_head.credit = flt(same_head.credit) + flt(entry.credit)
Nabin Haitc561a492015-08-19 19:22:34 +0530110 same_head.credit_in_account_currency = \
111 flt(same_head.credit_in_account_currency) + flt(entry.credit_in_account_currency)
Nabin Haitbf495c92013-01-30 12:49:08 +0530112 else:
113 merged_gl_map.append(entry)
Anand Doshi652bc072014-04-16 15:21:46 +0530114
thefalconx33bfc43d32019-12-13 15:09:51 +0530115 company = gl_map[0].company if gl_map else erpnext.get_default_company()
116 company_currency = erpnext.get_company_currency(company)
Nabin Hait19f8fa52021-02-22 22:27:22 +0530117
118 if not precision:
119 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
thefalconx33bfc43d32019-12-13 15:09:51 +0530120
Nabin Hait815a49e2013-08-07 17:00:01 +0530121 # filter zero debit and credit entries
thefalconx33bfc43d32019-12-13 15:09:51 +0530122 merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
Achilles Rasquinha908289d2018-03-08 13:10:51 +0530123 merged_gl_map = list(merged_gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530124
Nabin Haitbf495c92013-01-30 12:49:08 +0530125 return merged_gl_map
126
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530127def check_if_in_list(gle, gl_map, dimensions=None):
Ankush91527152021-08-11 11:17:50 +0530128 account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher',
Saqib9198caa2021-08-24 17:26:23 +0530129 'cost_center', 'against_voucher_type', 'party_type', 'project', 'finance_book']
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530130
131 if dimensions:
132 account_head_fieldnames = account_head_fieldnames + dimensions
133
Nabin Haitbb777562013-08-29 18:19:37 +0530134 for e in gl_map:
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530135 same_head = True
136 if e.account != gle.account:
137 same_head = False
Ankush91527152021-08-11 11:17:50 +0530138 continue
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530139
140 for fieldname in account_head_fieldnames:
141 if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
142 same_head = False
Ankush91527152021-08-11 11:17:50 +0530143 break
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530144
145 if same_head:
146 return e
Nabin Haitbf495c92013-01-30 12:49:08 +0530147
Nabin Haita77b8c92020-12-21 14:45:50 +0530148def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
149 if not from_repost:
150 validate_cwip_accounts(gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530151
Nabin Haite2c200a2015-05-28 13:00:37 +0530152 round_off_debit_credit(gl_map)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530153
154 if gl_map:
155 check_freezing_date(gl_map[0]["posting_date"], adv_adj)
156
Nabin Haitbf495c92013-01-30 12:49:08 +0530157 for entry in gl_map:
Nabin Haita77b8c92020-12-21 14:45:50 +0530158 make_entry(entry, adv_adj, update_outstanding, from_repost)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530159
Nabin Haita77b8c92020-12-21 14:45:50 +0530160def make_entry(args, adv_adj, update_outstanding, from_repost=False):
Nabin Haitddc3bb22020-03-17 17:04:18 +0530161 gle = frappe.new_doc("GL Entry")
162 gle.update(args)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530163 gle.flags.ignore_permissions = 1
Nabin Haita77b8c92020-12-21 14:45:50 +0530164 gle.flags.from_repost = from_repost
Nabin Hait19f8fa52021-02-22 22:27:22 +0530165 gle.flags.adv_adj = adv_adj
166 gle.flags.update_outstanding = update_outstanding or 'Yes'
Nabin Haitaeba24e2013-08-23 15:17:36 +0530167 gle.submit()
Anand Doshi652bc072014-04-16 15:21:46 +0530168
Nabin Haita77b8c92020-12-21 14:45:50 +0530169 if not from_repost:
170 validate_expense_against_budget(args)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530171
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530172def validate_cwip_accounts(gl_map):
Ankush91527152021-08-11 11:17:50 +0530173 """Validate that CWIP account are not used in Journal Entry"""
174 if gl_map and gl_map[0].voucher_type != "Journal Entry":
175 return
Maricad00c5982019-11-12 19:17:43 +0530176
Ankush91527152021-08-11 11:17:50 +0530177 cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting"))
178 if cwip_enabled:
179 cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
180 where account_type = 'Capital Work in Progress' and is_group=0""")]
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530181
Ankush91527152021-08-11 11:17:50 +0530182 for entry in gl_map:
183 if entry.account in cwip_accounts:
184 frappe.throw(
185 _("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530186
Nabin Haite2c200a2015-05-28 13:00:37 +0530187def round_off_debit_credit(gl_map):
188 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530189 currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
Anand Doshi602e8252015-11-16 19:05:46 +0530190
Nabin Haite2c200a2015-05-28 13:00:37 +0530191 debit_credit_diff = 0.0
192 for entry in gl_map:
193 entry.debit = flt(entry.debit, precision)
194 entry.credit = flt(entry.credit, precision)
195 debit_credit_diff += entry.debit - entry.credit
Anand Doshi602e8252015-11-16 19:05:46 +0530196
Nabin Haite2c200a2015-05-28 13:00:37 +0530197 debit_credit_diff = flt(debit_credit_diff, precision)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530198
Nabin Haitd2a966e2017-05-05 10:41:16 +0530199 if gl_map[0]["voucher_type"] in ("Journal Entry", "Payment Entry"):
Nabin Haitfb24a272016-02-18 19:18:07 +0530200 allowance = 5.0 / (10**precision)
201 else:
Nabin Haitd2a966e2017-05-05 10:41:16 +0530202 allowance = .5
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530203
Saqib Ansarib0e160f2021-04-22 13:23:50 +0530204 if abs(debit_credit_diff) > allowance:
Nabin Haite2c200a2015-05-28 13:00:37 +0530205 frappe.throw(_("Debit and Credit not equal for {0} #{1}. Difference is {2}.")
206 .format(gl_map[0].voucher_type, gl_map[0].voucher_no, debit_credit_diff))
Anand Doshi602e8252015-11-16 19:05:46 +0530207
Saqib Ansariaa4f7502021-04-28 14:42:49 +0530208 elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
Nabin Hait34c551d2019-07-03 10:34:31 +0530209 make_round_off_gle(gl_map, debit_credit_diff, precision)
Anand Doshi602e8252015-11-16 19:05:46 +0530210
Nabin Hait34c551d2019-07-03 10:34:31 +0530211def make_round_off_gle(gl_map, debit_credit_diff, precision):
Nabin Hait2e4de832017-09-19 14:53:16 +0530212 round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company)
Zarrar3523b772018-08-14 16:28:14 +0530213 round_off_account_exists = False
Nabin Hait80069a62015-05-28 19:19:59 +0530214 round_off_gle = frappe._dict()
Zarrar3523b772018-08-14 16:28:14 +0530215 for d in gl_map:
216 if d.account == round_off_account:
217 round_off_gle = d
Saqibc6e016e2021-06-04 10:08:22 +0530218 if d.debit:
219 debit_credit_diff -= flt(d.debit)
Zarrar3523b772018-08-14 16:28:14 +0530220 else:
Saqibc6e016e2021-06-04 10:08:22 +0530221 debit_credit_diff += flt(d.credit)
Zarrar3523b772018-08-14 16:28:14 +0530222 round_off_account_exists = True
223
Nabin Hait34c551d2019-07-03 10:34:31 +0530224 if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
225 gl_map.remove(round_off_gle)
226 return
227
Zarrar3523b772018-08-14 16:28:14 +0530228 if not round_off_gle:
229 for k in ["voucher_type", "voucher_no", "company",
Ankush Menat23c30e42021-02-24 11:03:24 +0530230 "posting_date", "remarks"]:
Zarrar3523b772018-08-14 16:28:14 +0530231 round_off_gle[k] = gl_map[0][k]
Anand Doshi602e8252015-11-16 19:05:46 +0530232
Nabin Haite2c200a2015-05-28 13:00:37 +0530233 round_off_gle.update({
234 "account": round_off_account,
Nabin Haitd4507ac2016-01-20 16:14:27 +0530235 "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
236 "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
Nabin Haite2c200a2015-05-28 13:00:37 +0530237 "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
238 "credit": debit_credit_diff if debit_credit_diff > 0 else 0,
Nabin Hait3c671462015-05-28 13:19:01 +0530239 "cost_center": round_off_cost_center,
240 "party_type": None,
241 "party": None,
Ankush Menat23c30e42021-02-24 11:03:24 +0530242 "is_opening": "No",
Nabin Hait3c671462015-05-28 13:19:01 +0530243 "against_voucher_type": None,
244 "against_voucher": None
Nabin Haite2c200a2015-05-28 13:00:37 +0530245 })
Anand Doshi602e8252015-11-16 19:05:46 +0530246
Zarrar3523b772018-08-14 16:28:14 +0530247 if not round_off_account_exists:
248 gl_map.append(round_off_gle)
Nabin Haite2c200a2015-05-28 13:00:37 +0530249
Nabin Hait2e4de832017-09-19 14:53:16 +0530250def get_round_off_account_and_cost_center(company):
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530251 round_off_account, round_off_cost_center = frappe.get_cached_value('Company', company,
Nabin Hait2e4de832017-09-19 14:53:16 +0530252 ["round_off_account", "round_off_cost_center"]) or [None, None]
253 if not round_off_account:
254 frappe.throw(_("Please mention Round Off Account in Company"))
255
256 if not round_off_cost_center:
257 frappe.throw(_("Please mention Round Off Cost Center in Company"))
258
259 return round_off_account, round_off_cost_center
260
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530261def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
262 adv_adj=False, update_outstanding="Yes"):
263 """
264 Get original gl entries of the voucher
265 and make reverse gl entries by swapping debit and credit
266 """
Anand Doshi652bc072014-04-16 15:21:46 +0530267
Nabin Hait2e296fa2013-08-28 18:53:11 +0530268 if not gl_entries:
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530269 gl_entries = frappe.get_all("GL Entry",
270 fields = ["*"],
271 filters = {
272 "voucher_type": voucher_type,
Deepesh Gargb821f222020-05-06 18:12:23 +0530273 "voucher_no": voucher_no,
274 "is_cancelled": 0
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530275 })
Nabin Hait56548cb2016-07-01 15:58:39 +0530276
Nabin Hait27994c22013-08-26 16:53:30 +0530277 if gl_entries:
Deepesh Garg069a54e2020-08-10 16:01:01 +0530278 validate_accounting_period(gl_entries)
Nabin Hait27994c22013-08-26 16:53:30 +0530279 check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
Deepesh Garg069a54e2020-08-10 16:01:01 +0530280 set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
Anand Doshi652bc072014-04-16 15:21:46 +0530281
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530282 for entry in gl_entries:
283 entry['name'] = None
284 debit = entry.get('debit', 0)
285 credit = entry.get('credit', 0)
Anand Doshi652bc072014-04-16 15:21:46 +0530286
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530287 debit_in_account_currency = entry.get('debit_in_account_currency', 0)
288 credit_in_account_currency = entry.get('credit_in_account_currency', 0)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530289
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530290 entry['debit'] = credit
291 entry['credit'] = debit
292 entry['debit_in_account_currency'] = credit_in_account_currency
293 entry['credit_in_account_currency'] = debit_in_account_currency
294
295 entry['remarks'] = "On cancellation of " + entry['voucher_no']
296 entry['is_cancelled'] = 1
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530297
298 if entry['debit'] or entry['credit']:
299 make_entry(entry, adv_adj, "Yes")
300
301
302def check_freezing_date(posting_date, adv_adj=False):
303 """
304 Nobody can do GL Entries where posting date is before freezing date
305 except authorized person
Deepesh Garg13d2e7b2021-09-16 18:54:57 +0530306
307 Administrator has all the roles so this check will be bypassed if any role is allowed to post
308 Hence stop admin to bypass if accounts are freezed
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530309 """
310 if not adv_adj:
311 acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
312 if acc_frozen_upto:
313 frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
314 if getdate(posting_date) <= getdate(acc_frozen_upto) \
Noah Jacob2bb383b2021-10-13 16:20:08 +0530315 and (frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == 'Administrator'):
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530316 frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
317
318def set_as_cancel(voucher_type, voucher_no):
319 """
320 Set is_cancelled=1 in all original gl entries for the voucher
321 """
Deepesh Garg069a54e2020-08-10 16:01:01 +0530322 frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530323 modified=%s, modified_by=%s
324 where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
325 (now(), frappe.session.user, voucher_type, voucher_no))