blob: eed87174ac27924550b2e5824e6490e59079b7f6 [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
ruthra kumare8889752022-05-16 14:28:25 +053017from erpnext.accounts.utils import create_payment_ledger_entry
Chillar Anand915b3432021-09-02 16:44:59 +053018
Nabin Haitbf495c92013-01-30 12:49:08 +053019
Ankush Menat494bd9e2022-03-28 18:52:46 +053020class ClosedAccountingPeriod(frappe.ValidationError):
21 pass
Nabin Haitbf495c92013-01-30 12:49:08 +053022
Ankush Menat494bd9e2022-03-28 18:52:46 +053023
24def make_gl_entries(
25 gl_map,
26 cancel=False,
27 adv_adj=False,
28 merge_entries=True,
29 update_outstanding="Yes",
30 from_repost=False,
31):
Nabin Hait2e296fa2013-08-28 18:53:11 +053032 if gl_map:
33 if not cancel:
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053034 validate_accounting_period(gl_map)
Saqib Ansari95b059a2022-05-11 13:26:15 +053035 validate_disabled_accounts(gl_map)
Nabin Hait2e296fa2013-08-28 18:53:11 +053036 gl_map = process_gl_map(gl_map, merge_entries)
nabinhaitc3432922014-07-29 18:06:18 +053037 if gl_map and len(gl_map) > 1:
ruthra kumar7312f222022-05-29 21:33:08 +053038 create_payment_ledger_entry(
39 gl_map,
40 cancel=0,
41 adv_adj=adv_adj,
42 update_outstanding=update_outstanding,
43 from_repost=from_repost,
44 )
Nabin Haita77b8c92020-12-21 14:45:50 +053045 save_entries(gl_map, adv_adj, update_outstanding, from_repost)
Deepesh Garg4c8d15b2021-04-26 15:24:34 +053046 # Post GL Map proccess there may no be any GL Entries
47 elif gl_map:
Ankush Menat494bd9e2022-03-28 18:52:46 +053048 frappe.throw(
49 _(
50 "Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."
51 )
52 )
Nabin Hait2e296fa2013-08-28 18:53:11 +053053 else:
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +053054 make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
Anand Doshi652bc072014-04-16 15:21:46 +053055
Ankush Menat494bd9e2022-03-28 18:52:46 +053056
Saqib Ansari95b059a2022-05-11 13:26:15 +053057def validate_disabled_accounts(gl_map):
58 accounts = [d.account for d in gl_map if d.account]
59
60 Account = frappe.qb.DocType("Account")
61
62 disabled_accounts = (
63 frappe.qb.from_(Account)
64 .where(Account.name.isin(accounts) & Account.disabled == 1)
65 .select(Account.name, Account.disabled)
66 ).run(as_dict=True)
67
68 if disabled_accounts:
69 account_list = "<br>"
70 account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
71 frappe.throw(
72 _("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
73 title=_("Disabled Account Selected"),
74 )
75
76
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053077def validate_accounting_period(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +053078 accounting_periods = frappe.db.sql(
79 """ SELECT
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053080 ap.name as name
81 FROM
82 `tabAccounting Period` ap, `tabClosed Document` cd
83 WHERE
84 ap.name = cd.parent
85 AND ap.company = %(company)s
86 AND cd.closed = 1
87 AND cd.document_type = %(voucher_type)s
88 AND %(date)s between ap.start_date and ap.end_date
Ankush Menat494bd9e2022-03-28 18:52:46 +053089 """,
90 {
91 "date": gl_map[0].posting_date,
92 "company": gl_map[0].company,
93 "voucher_type": gl_map[0].voucher_type,
94 },
95 as_dict=1,
96 )
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053097
98 if accounting_periods:
Ankush Menat494bd9e2022-03-28 18:52:46 +053099 frappe.throw(
100 _(
101 "You cannot create or cancel any accounting entries with in the closed Accounting Period {0}"
102 ).format(frappe.bold(accounting_periods[0].name)),
103 ClosedAccountingPeriod,
104 )
105
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +0530106
Nabin Hait19f8fa52021-02-22 22:27:22 +0530107def process_gl_map(gl_map, merge_entries=True, precision=None):
Nabin Hait6099af52022-01-31 17:24:50 +0530108 if not gl_map:
109 return []
110
Nabin Hait004c1ed2022-01-31 13:20:18 +0530111 gl_map = distribute_gl_based_on_cost_center_allocation(gl_map, precision)
112
Nabin Haitbf495c92013-01-30 12:49:08 +0530113 if merge_entries:
Nabin Hait19f8fa52021-02-22 22:27:22 +0530114 gl_map = merge_similar_entries(gl_map, precision)
Anand Doshi602e8252015-11-16 19:05:46 +0530115
Nabin Hait004c1ed2022-01-31 13:20:18 +0530116 gl_map = toggle_debit_credit_if_negative(gl_map)
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530117
Nabin Hait27994c22013-08-26 16:53:30 +0530118 return gl_map
Anand Doshi652bc072014-04-16 15:21:46 +0530119
Ankush Menat494bd9e2022-03-28 18:52:46 +0530120
Nabin Hait004c1ed2022-01-31 13:20:18 +0530121def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530122 cost_center_allocation = get_cost_center_allocation_data(
123 gl_map[0]["company"], gl_map[0]["posting_date"]
124 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530125 if not cost_center_allocation:
126 return gl_map
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530127
Nabin Hait004c1ed2022-01-31 13:20:18 +0530128 new_gl_map = []
129 for d in gl_map:
130 cost_center = d.get("cost_center")
131 if cost_center and cost_center_allocation.get(cost_center):
132 for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
133 gle = copy.deepcopy(d)
134 gle.cost_center = sub_cost_center
ruthra kumar71f6f782022-06-24 13:36:15 +0530135 for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
Nabin Hait004c1ed2022-01-31 13:20:18 +0530136 gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
137 new_gl_map.append(gle)
138 else:
139 new_gl_map.append(d)
140
141 return new_gl_map
142
Ankush Menat494bd9e2022-03-28 18:52:46 +0530143
Nabin Hait004c1ed2022-01-31 13:20:18 +0530144def get_cost_center_allocation_data(company, posting_date):
145 par = frappe.qb.DocType("Cost Center Allocation")
146 child = frappe.qb.DocType("Cost Center Allocation Percentage")
147
148 records = (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530149 frappe.qb.from_(par)
150 .inner_join(child)
151 .on(par.name == child.parent)
Nabin Hait004c1ed2022-01-31 13:20:18 +0530152 .select(par.main_cost_center, child.cost_center, child.percentage)
153 .where(par.docstatus == 1)
154 .where(par.company == company)
155 .where(par.valid_from <= posting_date)
156 .orderby(par.valid_from, order=frappe.qb.desc)
157 ).run(as_dict=True)
158
159 cc_allocation = frappe._dict()
160 for d in records:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530161 cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(
162 d.cost_center, d.percentage
163 )
Nabin Hait5b0ec642022-01-31 18:07:04 +0530164
Nabin Hait004c1ed2022-01-31 13:20:18 +0530165 return cc_allocation
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530166
Ankush Menat494bd9e2022-03-28 18:52:46 +0530167
Nabin Hait19f8fa52021-02-22 22:27:22 +0530168def merge_similar_entries(gl_map, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +0530169 merged_gl_map = []
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530170 accounting_dimensions = get_accounting_dimensions()
Nabin Hait4caaab32022-07-18 17:59:42 +0530171
Nabin Haitbf495c92013-01-30 12:49:08 +0530172 for entry in gl_map:
Anand Doshi652bc072014-04-16 15:21:46 +0530173 # if there is already an entry in this account then just add it
Nabin Haitbf495c92013-01-30 12:49:08 +0530174 # to that entry
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530175 same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
Nabin Haitbf495c92013-01-30 12:49:08 +0530176 if same_head:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530177 same_head.debit = flt(same_head.debit) + flt(entry.debit)
178 same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
179 entry.debit_in_account_currency
180 )
Nabin Hait2e296fa2013-08-28 18:53:11 +0530181 same_head.credit = flt(same_head.credit) + flt(entry.credit)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530182 same_head.credit_in_account_currency = flt(same_head.credit_in_account_currency) + flt(
183 entry.credit_in_account_currency
184 )
Nabin Haitbf495c92013-01-30 12:49:08 +0530185 else:
186 merged_gl_map.append(entry)
Anand Doshi652bc072014-04-16 15:21:46 +0530187
thefalconx33bfc43d32019-12-13 15:09:51 +0530188 company = gl_map[0].company if gl_map else erpnext.get_default_company()
189 company_currency = erpnext.get_company_currency(company)
Nabin Hait19f8fa52021-02-22 22:27:22 +0530190
191 if not precision:
192 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
thefalconx33bfc43d32019-12-13 15:09:51 +0530193
Nabin Hait815a49e2013-08-07 17:00:01 +0530194 # filter zero debit and credit entries
Ankush Menat494bd9e2022-03-28 18:52:46 +0530195 merged_gl_map = filter(
196 lambda x: flt(x.debit, precision) != 0 or flt(x.credit, precision) != 0, merged_gl_map
197 )
Achilles Rasquinha908289d2018-03-08 13:10:51 +0530198 merged_gl_map = list(merged_gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530199
Nabin Haitbf495c92013-01-30 12:49:08 +0530200 return merged_gl_map
201
Ankush Menat494bd9e2022-03-28 18:52:46 +0530202
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530203def check_if_in_list(gle, gl_map, dimensions=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530204 account_head_fieldnames = [
205 "voucher_detail_no",
206 "party",
207 "against_voucher",
208 "cost_center",
209 "against_voucher_type",
210 "party_type",
211 "project",
212 "finance_book",
213 ]
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530214
215 if dimensions:
216 account_head_fieldnames = account_head_fieldnames + dimensions
217
Nabin Haitbb777562013-08-29 18:19:37 +0530218 for e in gl_map:
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530219 same_head = True
220 if e.account != gle.account:
221 same_head = False
Ankush91527152021-08-11 11:17:50 +0530222 continue
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530223
224 for fieldname in account_head_fieldnames:
225 if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
226 same_head = False
Ankush91527152021-08-11 11:17:50 +0530227 break
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530228
229 if same_head:
230 return e
Nabin Haitbf495c92013-01-30 12:49:08 +0530231
Ankush Menat494bd9e2022-03-28 18:52:46 +0530232
Nabin Hait004c1ed2022-01-31 13:20:18 +0530233def toggle_debit_credit_if_negative(gl_map):
234 for entry in gl_map:
235 # toggle debit, credit if negative entry
236 if flt(entry.debit) < 0:
237 entry.credit = flt(entry.credit) - flt(entry.debit)
238 entry.debit = 0.0
239
240 if flt(entry.debit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530241 entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
242 entry.debit_in_account_currency
243 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530244 entry.debit_in_account_currency = 0.0
245
246 if flt(entry.credit) < 0:
247 entry.debit = flt(entry.debit) - flt(entry.credit)
248 entry.credit = 0.0
249
250 if flt(entry.credit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530251 entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
252 entry.credit_in_account_currency
253 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530254 entry.credit_in_account_currency = 0.0
255
256 update_net_values(entry)
257
258 return gl_map
259
Ankush Menat494bd9e2022-03-28 18:52:46 +0530260
Nabin Hait004c1ed2022-01-31 13:20:18 +0530261def update_net_values(entry):
262 # In some scenarios net value needs to be shown in the ledger
263 # This method updates net values as debit or credit
264 if entry.post_net_value and entry.debit and entry.credit:
265 if entry.debit > entry.credit:
266 entry.debit = entry.debit - entry.credit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530267 entry.debit_in_account_currency = (
268 entry.debit_in_account_currency - entry.credit_in_account_currency
269 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530270 entry.credit = 0
271 entry.credit_in_account_currency = 0
272 else:
273 entry.credit = entry.credit - entry.debit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530274 entry.credit_in_account_currency = (
275 entry.credit_in_account_currency - entry.debit_in_account_currency
276 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530277
278 entry.debit = 0
279 entry.debit_in_account_currency = 0
280
Ankush Menat494bd9e2022-03-28 18:52:46 +0530281
Nabin Haita77b8c92020-12-21 14:45:50 +0530282def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
283 if not from_repost:
284 validate_cwip_accounts(gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530285
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530286 process_debit_credit_difference(gl_map)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530287
288 if gl_map:
289 check_freezing_date(gl_map[0]["posting_date"], adv_adj)
290
Nabin Haitbf495c92013-01-30 12:49:08 +0530291 for entry in gl_map:
Nabin Haita77b8c92020-12-21 14:45:50 +0530292 make_entry(entry, adv_adj, update_outstanding, from_repost)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530293
Nabin Haita77b8c92020-12-21 14:45:50 +0530294def make_entry(args, adv_adj, update_outstanding, from_repost=False):
Nabin Haitddc3bb22020-03-17 17:04:18 +0530295 gle = frappe.new_doc("GL Entry")
296 gle.update(args)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530297 gle.flags.ignore_permissions = 1
Nabin Haita77b8c92020-12-21 14:45:50 +0530298 gle.flags.from_repost = from_repost
Nabin Hait19f8fa52021-02-22 22:27:22 +0530299 gle.flags.adv_adj = adv_adj
Ankush Menat494bd9e2022-03-28 18:52:46 +0530300 gle.flags.update_outstanding = update_outstanding or "Yes"
Nabin Hait4caaab32022-07-18 17:59:42 +0530301 gle.flags.notify_update = False
Nabin Haitaeba24e2013-08-23 15:17:36 +0530302 gle.submit()
Anand Doshi652bc072014-04-16 15:21:46 +0530303
Nabin Hait4caaab32022-07-18 17:59:42 +0530304 if not from_repost and gle.voucher_type != "Period Closing Voucher":
Nabin Haita77b8c92020-12-21 14:45:50 +0530305 validate_expense_against_budget(args)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530306
Ankush Menat494bd9e2022-03-28 18:52:46 +0530307
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530308def validate_cwip_accounts(gl_map):
Ankush91527152021-08-11 11:17:50 +0530309 """Validate that CWIP account are not used in Journal Entry"""
310 if gl_map and gl_map[0].voucher_type != "Journal Entry":
311 return
Maricad00c5982019-11-12 19:17:43 +0530312
Ankush Menat494bd9e2022-03-28 18:52:46 +0530313 cwip_enabled = any(
314 cint(ac.enable_cwip_accounting)
315 for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")
316 )
Ankush91527152021-08-11 11:17:50 +0530317 if cwip_enabled:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530318 cwip_accounts = [
319 d[0]
320 for d in frappe.db.sql(
321 """select name from tabAccount
322 where account_type = 'Capital Work in Progress' and is_group=0"""
323 )
324 ]
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530325
Ankush91527152021-08-11 11:17:50 +0530326 for entry in gl_map:
327 if entry.account in cwip_accounts:
328 frappe.throw(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530329 _(
330 "Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry"
331 ).format(entry.account)
332 )
333
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530334
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530335def process_debit_credit_difference(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530336 precision = get_field_precision(
337 frappe.get_meta("GL Entry").get_field("debit"),
338 currency=frappe.get_cached_value("Company", gl_map[0].company, "default_currency"),
339 )
Anand Doshi602e8252015-11-16 19:05:46 +0530340
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530341 voucher_type = gl_map[0].voucher_type
342 voucher_no = gl_map[0].voucher_no
343 allowance = get_debit_credit_allowance(voucher_type, precision)
344
345 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
346 if abs(debit_credit_diff) > allowance:
347 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
348
349 elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
350 make_round_off_gle(gl_map, debit_credit_diff, precision)
351
352 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
353 if abs(debit_credit_diff) > allowance:
354 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
355
356
357def get_debit_credit_difference(gl_map, precision):
Nabin Haite2c200a2015-05-28 13:00:37 +0530358 debit_credit_diff = 0.0
359 for entry in gl_map:
360 entry.debit = flt(entry.debit, precision)
361 entry.credit = flt(entry.credit, precision)
362 debit_credit_diff += entry.debit - entry.credit
Anand Doshi602e8252015-11-16 19:05:46 +0530363
Nabin Haite2c200a2015-05-28 13:00:37 +0530364 debit_credit_diff = flt(debit_credit_diff, precision)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530365
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530366 return debit_credit_diff
367
368
369def get_debit_credit_allowance(voucher_type, precision):
370 if voucher_type in ("Journal Entry", "Payment Entry"):
Nabin Haitfb24a272016-02-18 19:18:07 +0530371 allowance = 5.0 / (10**precision)
372 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530373 allowance = 0.5
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530374
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530375 return allowance
Anand Doshi602e8252015-11-16 19:05:46 +0530376
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530377
378def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no):
379 frappe.throw(
380 _("Debit and Credit not equal for {0} #{1}. Difference is {2}.").format(
381 voucher_type, voucher_no, debit_credit_diff
382 )
383 )
Anand Doshi602e8252015-11-16 19:05:46 +0530384
Ankush Menat494bd9e2022-03-28 18:52:46 +0530385
Nabin Hait34c551d2019-07-03 10:34:31 +0530386def make_round_off_gle(gl_map, debit_credit_diff, precision):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530387 round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530388 gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
Ankush Menat494bd9e2022-03-28 18:52:46 +0530389 )
Zarrar3523b772018-08-14 16:28:14 +0530390 round_off_account_exists = False
Nabin Hait80069a62015-05-28 19:19:59 +0530391 round_off_gle = frappe._dict()
Zarrar3523b772018-08-14 16:28:14 +0530392 for d in gl_map:
393 if d.account == round_off_account:
394 round_off_gle = d
Saqibc6e016e2021-06-04 10:08:22 +0530395 if d.debit:
396 debit_credit_diff -= flt(d.debit)
Zarrar3523b772018-08-14 16:28:14 +0530397 else:
Saqibc6e016e2021-06-04 10:08:22 +0530398 debit_credit_diff += flt(d.credit)
Zarrar3523b772018-08-14 16:28:14 +0530399 round_off_account_exists = True
400
Saqib Ansariad2c64f2022-03-01 13:32:34 +0530401 if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
Nabin Hait34c551d2019-07-03 10:34:31 +0530402 gl_map.remove(round_off_gle)
403 return
404
Zarrar3523b772018-08-14 16:28:14 +0530405 if not round_off_gle:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530406 for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
407 round_off_gle[k] = gl_map[0][k]
Anand Doshi602e8252015-11-16 19:05:46 +0530408
Ankush Menat494bd9e2022-03-28 18:52:46 +0530409 round_off_gle.update(
410 {
411 "account": round_off_account,
412 "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
413 "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
414 "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
415 "credit": debit_credit_diff if debit_credit_diff > 0 else 0,
416 "cost_center": round_off_cost_center,
417 "party_type": None,
418 "party": None,
419 "is_opening": "No",
420 "against_voucher_type": None,
421 "against_voucher": None,
422 }
423 )
Anand Doshi602e8252015-11-16 19:05:46 +0530424
Deepesh Garg015812b2022-04-23 21:40:08 +0530425 update_accounting_dimensions(round_off_gle)
426
Zarrar3523b772018-08-14 16:28:14 +0530427 if not round_off_account_exists:
428 gl_map.append(round_off_gle)
Nabin Haite2c200a2015-05-28 13:00:37 +0530429
Ankush Menat494bd9e2022-03-28 18:52:46 +0530430
Deepesh Garg015812b2022-04-23 21:40:08 +0530431def update_accounting_dimensions(round_off_gle):
432 dimensions = get_accounting_dimensions()
Deepesh Gargc312cd32022-04-24 18:11:32 +0530433 meta = frappe.get_meta(round_off_gle["voucher_type"])
434 has_all_dimensions = True
Deepesh Garg015812b2022-04-23 21:40:08 +0530435
436 for dimension in dimensions:
Deepesh Gargc312cd32022-04-24 18:11:32 +0530437 if not meta.has_field(dimension):
438 has_all_dimensions = False
439
440 if dimensions and has_all_dimensions:
441 dimension_values = frappe.db.get_value(
Deepesh Garg3fa1c632022-04-25 16:29:26 +0530442 round_off_gle["voucher_type"], round_off_gle["voucher_no"], dimensions, as_dict=1
Deepesh Gargc312cd32022-04-24 18:11:32 +0530443 )
444
445 for dimension in dimensions:
446 round_off_gle[dimension] = dimension_values.get(dimension)
Deepesh Garg015812b2022-04-23 21:40:08 +0530447
448
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530449def get_round_off_account_and_cost_center(company, voucher_type, voucher_no):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530450 round_off_account, round_off_cost_center = frappe.get_cached_value(
451 "Company", company, ["round_off_account", "round_off_cost_center"]
452 ) or [None, None]
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530453
Deepesh Gargc312cd32022-04-24 18:11:32 +0530454 meta = frappe.get_meta(voucher_type)
455
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530456 # Give first preference to parent cost center for round off GLE
Deepesh Gargc312cd32022-04-24 18:11:32 +0530457 if meta.has_field("cost_center"):
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530458 parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
459 if parent_cost_center:
460 round_off_cost_center = parent_cost_center
461
Nabin Hait2e4de832017-09-19 14:53:16 +0530462 if not round_off_account:
463 frappe.throw(_("Please mention Round Off Account in Company"))
464
465 if not round_off_cost_center:
466 frappe.throw(_("Please mention Round Off Cost Center in Company"))
467
468 return round_off_account, round_off_cost_center
469
Ankush Menat494bd9e2022-03-28 18:52:46 +0530470
471def make_reverse_gl_entries(
472 gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
473):
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530474 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530475 Get original gl entries of the voucher
476 and make reverse gl entries by swapping debit and credit
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530477 """
Anand Doshi652bc072014-04-16 15:21:46 +0530478
Nabin Hait2e296fa2013-08-28 18:53:11 +0530479 if not gl_entries:
Deepesh Gargff574502022-02-06 22:56:12 +0530480 gl_entry = frappe.qb.DocType("GL Entry")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530481 gl_entries = (
482 frappe.qb.from_(gl_entry)
483 .select("*")
484 .where(gl_entry.voucher_type == voucher_type)
485 .where(gl_entry.voucher_no == voucher_no)
486 .where(gl_entry.is_cancelled == 0)
487 .for_update()
488 ).run(as_dict=1)
Nabin Hait56548cb2016-07-01 15:58:39 +0530489
Nabin Hait27994c22013-08-26 16:53:30 +0530490 if gl_entries:
ruthra kumare8889752022-05-16 14:28:25 +0530491 create_payment_ledger_entry(gl_entries, cancel=1)
ruthra kumar7312f222022-05-29 21:33:08 +0530492 create_payment_ledger_entry(
493 gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
494 )
Deepesh Garg069a54e2020-08-10 16:01:01 +0530495 validate_accounting_period(gl_entries)
Nabin Hait27994c22013-08-26 16:53:30 +0530496 check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530497 set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
Anand Doshi652bc072014-04-16 15:21:46 +0530498
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530499 for entry in gl_entries:
Deepesh Gargffec8652022-02-02 17:14:42 +0530500 new_gle = copy.deepcopy(entry)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530501 new_gle["name"] = None
502 debit = new_gle.get("debit", 0)
503 credit = new_gle.get("credit", 0)
Anand Doshi652bc072014-04-16 15:21:46 +0530504
Ankush Menat494bd9e2022-03-28 18:52:46 +0530505 debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
506 credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530507
Ankush Menat494bd9e2022-03-28 18:52:46 +0530508 new_gle["debit"] = credit
509 new_gle["credit"] = debit
510 new_gle["debit_in_account_currency"] = credit_in_account_currency
511 new_gle["credit_in_account_currency"] = debit_in_account_currency
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530512
Ankush Menat494bd9e2022-03-28 18:52:46 +0530513 new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
514 new_gle["is_cancelled"] = 1
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530515
Ankush Menat494bd9e2022-03-28 18:52:46 +0530516 if new_gle["debit"] or new_gle["credit"]:
Deepesh Gargffec8652022-02-02 17:14:42 +0530517 make_entry(new_gle, adv_adj, "Yes")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530518
519
520def check_freezing_date(posting_date, adv_adj=False):
521 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530522 Nobody can do GL Entries where posting date is before freezing date
523 except authorized person
Deepesh Garg13d2e7b2021-09-16 18:54:57 +0530524
Ankush Menat494bd9e2022-03-28 18:52:46 +0530525 Administrator has all the roles so this check will be bypassed if any role is allowed to post
526 Hence stop admin to bypass if accounts are freezed
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530527 """
528 if not adv_adj:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530529 acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530530 if acc_frozen_upto:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530531 frozen_accounts_modifier = frappe.db.get_value(
532 "Accounts Settings", None, "frozen_accounts_modifier"
533 )
534 if getdate(posting_date) <= getdate(acc_frozen_upto) and (
535 frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
536 ):
537 frappe.throw(
538 _("You are not authorized to add or update entries before {0}").format(
539 formatdate(acc_frozen_upto)
540 )
541 )
542
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530543
544def set_as_cancel(voucher_type, voucher_no):
545 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530546 Set is_cancelled=1 in all original gl entries for the voucher
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530547 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530548 frappe.db.sql(
549 """UPDATE `tabGL Entry` SET is_cancelled = 1,
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530550 modified=%s, modified_by=%s
551 where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530552 (now(), frappe.session.user, voucher_type, voucher_no),
553 )