blob: 1598d914e2d237ad2355507cecc4d3d2dec0aeaf [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
Nabin Hait004c1ed2022-01-31 13:20:18 +05305import copy
Nabin Hait5b0ec642022-01-31 18:07:04 +05306
7import frappe
Rushabh Mehta793ba6b2014-02-14 15:47:51 +05308from frappe import _
Nabin Haite2c200a2015-05-28 13:00:37 +05309from frappe.model.meta import get_field_precision
Chillar Anand915b3432021-09-02 16:44:59 +053010from frappe.utils import cint, cstr, flt, formatdate, getdate, now
11
12import erpnext
13from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
14 get_accounting_dimensions,
15)
Nabin Haitb9bc7d62016-05-16 14:38:47 +053016from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
Chillar Anand915b3432021-09-02 16:44:59 +053017
Nabin Haitbf495c92013-01-30 12:49:08 +053018
Ankush Menat494bd9e2022-03-28 18:52:46 +053019class ClosedAccountingPeriod(frappe.ValidationError):
20 pass
Nabin Haitbf495c92013-01-30 12:49:08 +053021
Ankush Menat494bd9e2022-03-28 18:52:46 +053022
23def make_gl_entries(
24 gl_map,
25 cancel=False,
26 adv_adj=False,
27 merge_entries=True,
28 update_outstanding="Yes",
29 from_repost=False,
30):
Nabin Hait2e296fa2013-08-28 18:53:11 +053031 if gl_map:
32 if not cancel:
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053033 validate_accounting_period(gl_map)
Saqib Ansari95b059a2022-05-11 13:26:15 +053034 validate_disabled_accounts(gl_map)
Nabin Hait2e296fa2013-08-28 18:53:11 +053035 gl_map = process_gl_map(gl_map, merge_entries)
nabinhaitc3432922014-07-29 18:06:18 +053036 if gl_map and len(gl_map) > 1:
Nabin Haita77b8c92020-12-21 14:45:50 +053037 save_entries(gl_map, adv_adj, update_outstanding, from_repost)
Deepesh Garg4c8d15b2021-04-26 15:24:34 +053038 # Post GL Map proccess there may no be any GL Entries
39 elif gl_map:
Ankush Menat494bd9e2022-03-28 18:52:46 +053040 frappe.throw(
41 _(
42 "Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."
43 )
44 )
Nabin Hait2e296fa2013-08-28 18:53:11 +053045 else:
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +053046 make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
Anand Doshi652bc072014-04-16 15:21:46 +053047
Ankush Menat494bd9e2022-03-28 18:52:46 +053048
Saqib Ansari95b059a2022-05-11 13:26:15 +053049def validate_disabled_accounts(gl_map):
50 accounts = [d.account for d in gl_map if d.account]
51
52 Account = frappe.qb.DocType("Account")
53
54 disabled_accounts = (
55 frappe.qb.from_(Account)
56 .where(Account.name.isin(accounts) & Account.disabled == 1)
57 .select(Account.name, Account.disabled)
58 ).run(as_dict=True)
59
60 if disabled_accounts:
61 account_list = "<br>"
62 account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
63 frappe.throw(
64 _("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
65 title=_("Disabled Account Selected"),
66 )
67
68
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053069def validate_accounting_period(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +053070 accounting_periods = frappe.db.sql(
71 """ SELECT
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053072 ap.name as name
73 FROM
74 `tabAccounting Period` ap, `tabClosed Document` cd
75 WHERE
76 ap.name = cd.parent
77 AND ap.company = %(company)s
78 AND cd.closed = 1
79 AND cd.document_type = %(voucher_type)s
80 AND %(date)s between ap.start_date and ap.end_date
Ankush Menat494bd9e2022-03-28 18:52:46 +053081 """,
82 {
83 "date": gl_map[0].posting_date,
84 "company": gl_map[0].company,
85 "voucher_type": gl_map[0].voucher_type,
86 },
87 as_dict=1,
88 )
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053089
90 if accounting_periods:
Ankush Menat494bd9e2022-03-28 18:52:46 +053091 frappe.throw(
92 _(
93 "You cannot create or cancel any accounting entries with in the closed Accounting Period {0}"
94 ).format(frappe.bold(accounting_periods[0].name)),
95 ClosedAccountingPeriod,
96 )
97
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053098
Nabin Hait19f8fa52021-02-22 22:27:22 +053099def process_gl_map(gl_map, merge_entries=True, precision=None):
Nabin Hait6099af52022-01-31 17:24:50 +0530100 if not gl_map:
101 return []
102
Nabin Hait004c1ed2022-01-31 13:20:18 +0530103 gl_map = distribute_gl_based_on_cost_center_allocation(gl_map, precision)
104
Nabin Haitbf495c92013-01-30 12:49:08 +0530105 if merge_entries:
Nabin Hait19f8fa52021-02-22 22:27:22 +0530106 gl_map = merge_similar_entries(gl_map, precision)
Anand Doshi602e8252015-11-16 19:05:46 +0530107
Nabin Hait004c1ed2022-01-31 13:20:18 +0530108 gl_map = toggle_debit_credit_if_negative(gl_map)
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530109
Nabin Hait27994c22013-08-26 16:53:30 +0530110 return gl_map
Anand Doshi652bc072014-04-16 15:21:46 +0530111
Ankush Menat494bd9e2022-03-28 18:52:46 +0530112
Nabin Hait004c1ed2022-01-31 13:20:18 +0530113def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530114 cost_center_allocation = get_cost_center_allocation_data(
115 gl_map[0]["company"], gl_map[0]["posting_date"]
116 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530117 if not cost_center_allocation:
118 return gl_map
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530119
Nabin Hait004c1ed2022-01-31 13:20:18 +0530120 new_gl_map = []
121 for d in gl_map:
122 cost_center = d.get("cost_center")
123 if cost_center and cost_center_allocation.get(cost_center):
124 for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
125 gle = copy.deepcopy(d)
126 gle.cost_center = sub_cost_center
127 for field in ("debit", "credit", "debit_in_account_currency", "credit_in_company_currency"):
128 gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
129 new_gl_map.append(gle)
130 else:
131 new_gl_map.append(d)
132
133 return new_gl_map
134
Ankush Menat494bd9e2022-03-28 18:52:46 +0530135
Nabin Hait004c1ed2022-01-31 13:20:18 +0530136def get_cost_center_allocation_data(company, posting_date):
137 par = frappe.qb.DocType("Cost Center Allocation")
138 child = frappe.qb.DocType("Cost Center Allocation Percentage")
139
140 records = (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530141 frappe.qb.from_(par)
142 .inner_join(child)
143 .on(par.name == child.parent)
Nabin Hait004c1ed2022-01-31 13:20:18 +0530144 .select(par.main_cost_center, child.cost_center, child.percentage)
145 .where(par.docstatus == 1)
146 .where(par.company == company)
147 .where(par.valid_from <= posting_date)
148 .orderby(par.valid_from, order=frappe.qb.desc)
149 ).run(as_dict=True)
150
151 cc_allocation = frappe._dict()
152 for d in records:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530153 cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(
154 d.cost_center, d.percentage
155 )
Nabin Hait5b0ec642022-01-31 18:07:04 +0530156
Nabin Hait004c1ed2022-01-31 13:20:18 +0530157 return cc_allocation
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530158
Ankush Menat494bd9e2022-03-28 18:52:46 +0530159
Nabin Hait19f8fa52021-02-22 22:27:22 +0530160def merge_similar_entries(gl_map, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +0530161 merged_gl_map = []
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530162 accounting_dimensions = get_accounting_dimensions()
Nabin Haitbf495c92013-01-30 12:49:08 +0530163 for entry in gl_map:
Anand Doshi652bc072014-04-16 15:21:46 +0530164 # if there is already an entry in this account then just add it
Nabin Haitbf495c92013-01-30 12:49:08 +0530165 # to that entry
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530166 same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
Nabin Haitbf495c92013-01-30 12:49:08 +0530167 if same_head:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530168 same_head.debit = flt(same_head.debit) + flt(entry.debit)
169 same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
170 entry.debit_in_account_currency
171 )
Nabin Hait2e296fa2013-08-28 18:53:11 +0530172 same_head.credit = flt(same_head.credit) + flt(entry.credit)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530173 same_head.credit_in_account_currency = flt(same_head.credit_in_account_currency) + flt(
174 entry.credit_in_account_currency
175 )
Nabin Haitbf495c92013-01-30 12:49:08 +0530176 else:
177 merged_gl_map.append(entry)
Anand Doshi652bc072014-04-16 15:21:46 +0530178
thefalconx33bfc43d32019-12-13 15:09:51 +0530179 company = gl_map[0].company if gl_map else erpnext.get_default_company()
180 company_currency = erpnext.get_company_currency(company)
Nabin Hait19f8fa52021-02-22 22:27:22 +0530181
182 if not precision:
183 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
thefalconx33bfc43d32019-12-13 15:09:51 +0530184
Nabin Hait815a49e2013-08-07 17:00:01 +0530185 # filter zero debit and credit entries
Ankush Menat494bd9e2022-03-28 18:52:46 +0530186 merged_gl_map = filter(
187 lambda x: flt(x.debit, precision) != 0 or flt(x.credit, precision) != 0, merged_gl_map
188 )
Achilles Rasquinha908289d2018-03-08 13:10:51 +0530189 merged_gl_map = list(merged_gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530190
Nabin Haitbf495c92013-01-30 12:49:08 +0530191 return merged_gl_map
192
Ankush Menat494bd9e2022-03-28 18:52:46 +0530193
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530194def check_if_in_list(gle, gl_map, dimensions=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530195 account_head_fieldnames = [
196 "voucher_detail_no",
197 "party",
198 "against_voucher",
199 "cost_center",
200 "against_voucher_type",
201 "party_type",
202 "project",
203 "finance_book",
204 ]
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530205
206 if dimensions:
207 account_head_fieldnames = account_head_fieldnames + dimensions
208
Nabin Haitbb777562013-08-29 18:19:37 +0530209 for e in gl_map:
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530210 same_head = True
211 if e.account != gle.account:
212 same_head = False
Ankush91527152021-08-11 11:17:50 +0530213 continue
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530214
215 for fieldname in account_head_fieldnames:
216 if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
217 same_head = False
Ankush91527152021-08-11 11:17:50 +0530218 break
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530219
220 if same_head:
221 return e
Nabin Haitbf495c92013-01-30 12:49:08 +0530222
Ankush Menat494bd9e2022-03-28 18:52:46 +0530223
Nabin Hait004c1ed2022-01-31 13:20:18 +0530224def toggle_debit_credit_if_negative(gl_map):
225 for entry in gl_map:
226 # toggle debit, credit if negative entry
227 if flt(entry.debit) < 0:
228 entry.credit = flt(entry.credit) - flt(entry.debit)
229 entry.debit = 0.0
230
231 if flt(entry.debit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530232 entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
233 entry.debit_in_account_currency
234 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530235 entry.debit_in_account_currency = 0.0
236
237 if flt(entry.credit) < 0:
238 entry.debit = flt(entry.debit) - flt(entry.credit)
239 entry.credit = 0.0
240
241 if flt(entry.credit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530242 entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
243 entry.credit_in_account_currency
244 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530245 entry.credit_in_account_currency = 0.0
246
247 update_net_values(entry)
248
249 return gl_map
250
Ankush Menat494bd9e2022-03-28 18:52:46 +0530251
Nabin Hait004c1ed2022-01-31 13:20:18 +0530252def update_net_values(entry):
253 # In some scenarios net value needs to be shown in the ledger
254 # This method updates net values as debit or credit
255 if entry.post_net_value and entry.debit and entry.credit:
256 if entry.debit > entry.credit:
257 entry.debit = entry.debit - entry.credit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530258 entry.debit_in_account_currency = (
259 entry.debit_in_account_currency - entry.credit_in_account_currency
260 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530261 entry.credit = 0
262 entry.credit_in_account_currency = 0
263 else:
264 entry.credit = entry.credit - entry.debit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530265 entry.credit_in_account_currency = (
266 entry.credit_in_account_currency - entry.debit_in_account_currency
267 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530268
269 entry.debit = 0
270 entry.debit_in_account_currency = 0
271
Ankush Menat494bd9e2022-03-28 18:52:46 +0530272
Nabin Haita77b8c92020-12-21 14:45:50 +0530273def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
274 if not from_repost:
275 validate_cwip_accounts(gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530276
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530277 process_debit_credit_difference(gl_map)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530278
279 if gl_map:
280 check_freezing_date(gl_map[0]["posting_date"], adv_adj)
281
Nabin Haitbf495c92013-01-30 12:49:08 +0530282 for entry in gl_map:
Nabin Haita77b8c92020-12-21 14:45:50 +0530283 make_entry(entry, adv_adj, update_outstanding, from_repost)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530284
Ankush Menat494bd9e2022-03-28 18:52:46 +0530285
Nabin Haita77b8c92020-12-21 14:45:50 +0530286def make_entry(args, adv_adj, update_outstanding, from_repost=False):
Nabin Haitddc3bb22020-03-17 17:04:18 +0530287 gle = frappe.new_doc("GL Entry")
288 gle.update(args)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530289 gle.flags.ignore_permissions = 1
Nabin Haita77b8c92020-12-21 14:45:50 +0530290 gle.flags.from_repost = from_repost
Nabin Hait19f8fa52021-02-22 22:27:22 +0530291 gle.flags.adv_adj = adv_adj
Ankush Menat494bd9e2022-03-28 18:52:46 +0530292 gle.flags.update_outstanding = update_outstanding or "Yes"
Nabin Haitaeba24e2013-08-23 15:17:36 +0530293 gle.submit()
Anand Doshi652bc072014-04-16 15:21:46 +0530294
Nabin Haita77b8c92020-12-21 14:45:50 +0530295 if not from_repost:
296 validate_expense_against_budget(args)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530297
Ankush Menat494bd9e2022-03-28 18:52:46 +0530298
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530299def validate_cwip_accounts(gl_map):
Ankush91527152021-08-11 11:17:50 +0530300 """Validate that CWIP account are not used in Journal Entry"""
301 if gl_map and gl_map[0].voucher_type != "Journal Entry":
302 return
Maricad00c5982019-11-12 19:17:43 +0530303
Ankush Menat494bd9e2022-03-28 18:52:46 +0530304 cwip_enabled = any(
305 cint(ac.enable_cwip_accounting)
306 for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")
307 )
Ankush91527152021-08-11 11:17:50 +0530308 if cwip_enabled:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530309 cwip_accounts = [
310 d[0]
311 for d in frappe.db.sql(
312 """select name from tabAccount
313 where account_type = 'Capital Work in Progress' and is_group=0"""
314 )
315 ]
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530316
Ankush91527152021-08-11 11:17:50 +0530317 for entry in gl_map:
318 if entry.account in cwip_accounts:
319 frappe.throw(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530320 _(
321 "Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry"
322 ).format(entry.account)
323 )
324
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530325
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530326def process_debit_credit_difference(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530327 precision = get_field_precision(
328 frappe.get_meta("GL Entry").get_field("debit"),
329 currency=frappe.get_cached_value("Company", gl_map[0].company, "default_currency"),
330 )
Anand Doshi602e8252015-11-16 19:05:46 +0530331
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530332 voucher_type = gl_map[0].voucher_type
333 voucher_no = gl_map[0].voucher_no
334 allowance = get_debit_credit_allowance(voucher_type, precision)
335
336 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
337 if abs(debit_credit_diff) > allowance:
338 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
339
340 elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
341 make_round_off_gle(gl_map, debit_credit_diff, precision)
342
343 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
344 if abs(debit_credit_diff) > allowance:
345 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
346
347
348def get_debit_credit_difference(gl_map, precision):
Nabin Haite2c200a2015-05-28 13:00:37 +0530349 debit_credit_diff = 0.0
350 for entry in gl_map:
351 entry.debit = flt(entry.debit, precision)
352 entry.credit = flt(entry.credit, precision)
353 debit_credit_diff += entry.debit - entry.credit
Anand Doshi602e8252015-11-16 19:05:46 +0530354
Nabin Haite2c200a2015-05-28 13:00:37 +0530355 debit_credit_diff = flt(debit_credit_diff, precision)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530356
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530357 return debit_credit_diff
358
359
360def get_debit_credit_allowance(voucher_type, precision):
361 if voucher_type in ("Journal Entry", "Payment Entry"):
Nabin Haitfb24a272016-02-18 19:18:07 +0530362 allowance = 5.0 / (10**precision)
363 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530364 allowance = 0.5
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530365
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530366 return allowance
Anand Doshi602e8252015-11-16 19:05:46 +0530367
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530368
369def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no):
370 frappe.throw(
371 _("Debit and Credit not equal for {0} #{1}. Difference is {2}.").format(
372 voucher_type, voucher_no, debit_credit_diff
373 )
374 )
Anand Doshi602e8252015-11-16 19:05:46 +0530375
Ankush Menat494bd9e2022-03-28 18:52:46 +0530376
Nabin Hait34c551d2019-07-03 10:34:31 +0530377def make_round_off_gle(gl_map, debit_credit_diff, precision):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530378 round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530379 gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
Ankush Menat494bd9e2022-03-28 18:52:46 +0530380 )
Zarrar3523b772018-08-14 16:28:14 +0530381 round_off_account_exists = False
Nabin Hait80069a62015-05-28 19:19:59 +0530382 round_off_gle = frappe._dict()
Zarrar3523b772018-08-14 16:28:14 +0530383 for d in gl_map:
384 if d.account == round_off_account:
385 round_off_gle = d
Saqibc6e016e2021-06-04 10:08:22 +0530386 if d.debit:
387 debit_credit_diff -= flt(d.debit)
Zarrar3523b772018-08-14 16:28:14 +0530388 else:
Saqibc6e016e2021-06-04 10:08:22 +0530389 debit_credit_diff += flt(d.credit)
Zarrar3523b772018-08-14 16:28:14 +0530390 round_off_account_exists = True
391
Saqib Ansariad2c64f2022-03-01 13:32:34 +0530392 if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
Nabin Hait34c551d2019-07-03 10:34:31 +0530393 gl_map.remove(round_off_gle)
394 return
395
Zarrar3523b772018-08-14 16:28:14 +0530396 if not round_off_gle:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530397 for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
398 round_off_gle[k] = gl_map[0][k]
Anand Doshi602e8252015-11-16 19:05:46 +0530399
Ankush Menat494bd9e2022-03-28 18:52:46 +0530400 round_off_gle.update(
401 {
402 "account": round_off_account,
403 "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
404 "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
405 "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
406 "credit": debit_credit_diff if debit_credit_diff > 0 else 0,
407 "cost_center": round_off_cost_center,
408 "party_type": None,
409 "party": None,
410 "is_opening": "No",
411 "against_voucher_type": None,
412 "against_voucher": None,
413 }
414 )
Anand Doshi602e8252015-11-16 19:05:46 +0530415
Deepesh Garg015812b2022-04-23 21:40:08 +0530416 update_accounting_dimensions(round_off_gle)
417
Zarrar3523b772018-08-14 16:28:14 +0530418 if not round_off_account_exists:
419 gl_map.append(round_off_gle)
Nabin Haite2c200a2015-05-28 13:00:37 +0530420
Ankush Menat494bd9e2022-03-28 18:52:46 +0530421
Deepesh Garg015812b2022-04-23 21:40:08 +0530422def update_accounting_dimensions(round_off_gle):
423 dimensions = get_accounting_dimensions()
Deepesh Gargc312cd32022-04-24 18:11:32 +0530424 meta = frappe.get_meta(round_off_gle["voucher_type"])
425 has_all_dimensions = True
Deepesh Garg015812b2022-04-23 21:40:08 +0530426
427 for dimension in dimensions:
Deepesh Gargc312cd32022-04-24 18:11:32 +0530428 if not meta.has_field(dimension):
429 has_all_dimensions = False
430
431 if dimensions and has_all_dimensions:
432 dimension_values = frappe.db.get_value(
Deepesh Garg3fa1c632022-04-25 16:29:26 +0530433 round_off_gle["voucher_type"], round_off_gle["voucher_no"], dimensions, as_dict=1
Deepesh Gargc312cd32022-04-24 18:11:32 +0530434 )
435
436 for dimension in dimensions:
437 round_off_gle[dimension] = dimension_values.get(dimension)
Deepesh Garg015812b2022-04-23 21:40:08 +0530438
439
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530440def get_round_off_account_and_cost_center(company, voucher_type, voucher_no):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530441 round_off_account, round_off_cost_center = frappe.get_cached_value(
442 "Company", company, ["round_off_account", "round_off_cost_center"]
443 ) or [None, None]
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530444
Deepesh Gargc312cd32022-04-24 18:11:32 +0530445 meta = frappe.get_meta(voucher_type)
446
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530447 # Give first preference to parent cost center for round off GLE
Deepesh Gargc312cd32022-04-24 18:11:32 +0530448 if meta.has_field("cost_center"):
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530449 parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
450 if parent_cost_center:
451 round_off_cost_center = parent_cost_center
452
Nabin Hait2e4de832017-09-19 14:53:16 +0530453 if not round_off_account:
454 frappe.throw(_("Please mention Round Off Account in Company"))
455
456 if not round_off_cost_center:
457 frappe.throw(_("Please mention Round Off Cost Center in Company"))
458
459 return round_off_account, round_off_cost_center
460
Ankush Menat494bd9e2022-03-28 18:52:46 +0530461
462def make_reverse_gl_entries(
463 gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
464):
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530465 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530466 Get original gl entries of the voucher
467 and make reverse gl entries by swapping debit and credit
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530468 """
Anand Doshi652bc072014-04-16 15:21:46 +0530469
Nabin Hait2e296fa2013-08-28 18:53:11 +0530470 if not gl_entries:
Deepesh Gargff574502022-02-06 22:56:12 +0530471 gl_entry = frappe.qb.DocType("GL Entry")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530472 gl_entries = (
473 frappe.qb.from_(gl_entry)
474 .select("*")
475 .where(gl_entry.voucher_type == voucher_type)
476 .where(gl_entry.voucher_no == voucher_no)
477 .where(gl_entry.is_cancelled == 0)
478 .for_update()
479 ).run(as_dict=1)
Nabin Hait56548cb2016-07-01 15:58:39 +0530480
Nabin Hait27994c22013-08-26 16:53:30 +0530481 if gl_entries:
Deepesh Garg069a54e2020-08-10 16:01:01 +0530482 validate_accounting_period(gl_entries)
Nabin Hait27994c22013-08-26 16:53:30 +0530483 check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530484 set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
Anand Doshi652bc072014-04-16 15:21:46 +0530485
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530486 for entry in gl_entries:
Deepesh Gargffec8652022-02-02 17:14:42 +0530487 new_gle = copy.deepcopy(entry)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530488 new_gle["name"] = None
489 debit = new_gle.get("debit", 0)
490 credit = new_gle.get("credit", 0)
Anand Doshi652bc072014-04-16 15:21:46 +0530491
Ankush Menat494bd9e2022-03-28 18:52:46 +0530492 debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
493 credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530494
Ankush Menat494bd9e2022-03-28 18:52:46 +0530495 new_gle["debit"] = credit
496 new_gle["credit"] = debit
497 new_gle["debit_in_account_currency"] = credit_in_account_currency
498 new_gle["credit_in_account_currency"] = debit_in_account_currency
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530499
Ankush Menat494bd9e2022-03-28 18:52:46 +0530500 new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
501 new_gle["is_cancelled"] = 1
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530502
Ankush Menat494bd9e2022-03-28 18:52:46 +0530503 if new_gle["debit"] or new_gle["credit"]:
Deepesh Gargffec8652022-02-02 17:14:42 +0530504 make_entry(new_gle, adv_adj, "Yes")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530505
506
507def check_freezing_date(posting_date, adv_adj=False):
508 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530509 Nobody can do GL Entries where posting date is before freezing date
510 except authorized person
Deepesh Garg13d2e7b2021-09-16 18:54:57 +0530511
Ankush Menat494bd9e2022-03-28 18:52:46 +0530512 Administrator has all the roles so this check will be bypassed if any role is allowed to post
513 Hence stop admin to bypass if accounts are freezed
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530514 """
515 if not adv_adj:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530516 acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530517 if acc_frozen_upto:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530518 frozen_accounts_modifier = frappe.db.get_value(
519 "Accounts Settings", None, "frozen_accounts_modifier"
520 )
521 if getdate(posting_date) <= getdate(acc_frozen_upto) and (
522 frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
523 ):
524 frappe.throw(
525 _("You are not authorized to add or update entries before {0}").format(
526 formatdate(acc_frozen_upto)
527 )
528 )
529
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530530
531def set_as_cancel(voucher_type, voucher_no):
532 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530533 Set is_cancelled=1 in all original gl entries for the voucher
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530534 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530535 frappe.db.sql(
536 """UPDATE `tabGL Entry` SET is_cancelled = 1,
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530537 modified=%s, modified_by=%s
538 where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530539 (now(), frappe.session.user, voucher_type, voucher_no),
540 )