blob: e9dc5fc0ccca602617260dc7c24afcc2e72b3e3a [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)
mergify[bot]b4db5e92023-07-18 17:40:49 +053016from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
Nabin Haitb9bc7d62016-05-16 14:38:47 +053017from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
ruthra kumare8889752022-05-16 14:28:25 +053018from erpnext.accounts.utils import create_payment_ledger_entry
Chillar Anand915b3432021-09-02 16:44:59 +053019
Nabin Haitbf495c92013-01-30 12:49:08 +053020
Ankush Menat494bd9e2022-03-28 18:52:46 +053021def make_gl_entries(
22 gl_map,
23 cancel=False,
24 adv_adj=False,
25 merge_entries=True,
26 update_outstanding="Yes",
27 from_repost=False,
28):
Nabin Hait2e296fa2013-08-28 18:53:11 +053029 if gl_map:
30 if not cancel:
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053031 validate_accounting_period(gl_map)
Saqib Ansari95b059a2022-05-11 13:26:15 +053032 validate_disabled_accounts(gl_map)
Nabin Hait2e296fa2013-08-28 18:53:11 +053033 gl_map = process_gl_map(gl_map, merge_entries)
nabinhaitc3432922014-07-29 18:06:18 +053034 if gl_map and len(gl_map) > 1:
ruthra kumar7312f222022-05-29 21:33:08 +053035 create_payment_ledger_entry(
36 gl_map,
37 cancel=0,
38 adv_adj=adv_adj,
39 update_outstanding=update_outstanding,
40 from_repost=from_repost,
41 )
Nabin Haita77b8c92020-12-21 14:45:50 +053042 save_entries(gl_map, adv_adj, update_outstanding, from_repost)
Deepesh Garg4c8d15b2021-04-26 15:24:34 +053043 # Post GL Map proccess there may no be any GL Entries
44 elif gl_map:
Ankush Menat494bd9e2022-03-28 18:52:46 +053045 frappe.throw(
46 _(
47 "Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."
48 )
49 )
Nabin Hait2e296fa2013-08-28 18:53:11 +053050 else:
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +053051 make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
Anand Doshi652bc072014-04-16 15:21:46 +053052
Ankush Menat494bd9e2022-03-28 18:52:46 +053053
Saqib Ansari95b059a2022-05-11 13:26:15 +053054def validate_disabled_accounts(gl_map):
55 accounts = [d.account for d in gl_map if d.account]
56
57 Account = frappe.qb.DocType("Account")
58
59 disabled_accounts = (
60 frappe.qb.from_(Account)
61 .where(Account.name.isin(accounts) & Account.disabled == 1)
62 .select(Account.name, Account.disabled)
63 ).run(as_dict=True)
64
65 if disabled_accounts:
66 account_list = "<br>"
67 account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
68 frappe.throw(
69 _("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
70 title=_("Disabled Account Selected"),
71 )
72
73
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053074def validate_accounting_period(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +053075 accounting_periods = frappe.db.sql(
76 """ SELECT
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053077 ap.name as name
78 FROM
79 `tabAccounting Period` ap, `tabClosed Document` cd
80 WHERE
81 ap.name = cd.parent
82 AND ap.company = %(company)s
83 AND cd.closed = 1
84 AND cd.document_type = %(voucher_type)s
85 AND %(date)s between ap.start_date and ap.end_date
Ankush Menat494bd9e2022-03-28 18:52:46 +053086 """,
87 {
88 "date": gl_map[0].posting_date,
89 "company": gl_map[0].company,
90 "voucher_type": gl_map[0].voucher_type,
91 },
92 as_dict=1,
93 )
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +053094
95 if accounting_periods:
Ankush Menat494bd9e2022-03-28 18:52:46 +053096 frappe.throw(
97 _(
98 "You cannot create or cancel any accounting entries with in the closed Accounting Period {0}"
99 ).format(frappe.bold(accounting_periods[0].name)),
100 ClosedAccountingPeriod,
101 )
102
Mangesh-Khairnare5c733b2019-08-08 15:44:11 +0530103
Nabin Hait19f8fa52021-02-22 22:27:22 +0530104def process_gl_map(gl_map, merge_entries=True, precision=None):
Nabin Hait6099af52022-01-31 17:24:50 +0530105 if not gl_map:
106 return []
107
Nabin Hait004c1ed2022-01-31 13:20:18 +0530108 gl_map = distribute_gl_based_on_cost_center_allocation(gl_map, precision)
109
Nabin Haitbf495c92013-01-30 12:49:08 +0530110 if merge_entries:
Nabin Hait19f8fa52021-02-22 22:27:22 +0530111 gl_map = merge_similar_entries(gl_map, precision)
Anand Doshi602e8252015-11-16 19:05:46 +0530112
Nabin Hait004c1ed2022-01-31 13:20:18 +0530113 gl_map = toggle_debit_credit_if_negative(gl_map)
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530114
Nabin Hait27994c22013-08-26 16:53:30 +0530115 return gl_map
Anand Doshi652bc072014-04-16 15:21:46 +0530116
Ankush Menat494bd9e2022-03-28 18:52:46 +0530117
Nabin Hait004c1ed2022-01-31 13:20:18 +0530118def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530119 cost_center_allocation = get_cost_center_allocation_data(
120 gl_map[0]["company"], gl_map[0]["posting_date"]
121 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530122 if not cost_center_allocation:
123 return gl_map
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530124
Nabin Hait004c1ed2022-01-31 13:20:18 +0530125 new_gl_map = []
126 for d in gl_map:
127 cost_center = d.get("cost_center")
Deepesh Garg4e26d422022-10-30 19:33:27 +0530128
129 # Validate budget against main cost center
130 validate_expense_against_budget(
131 d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
132 )
133
Nabin Hait004c1ed2022-01-31 13:20:18 +0530134 if cost_center and cost_center_allocation.get(cost_center):
135 for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
136 gle = copy.deepcopy(d)
137 gle.cost_center = sub_cost_center
ruthra kumar71f6f782022-06-24 13:36:15 +0530138 for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
Nabin Hait004c1ed2022-01-31 13:20:18 +0530139 gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
140 new_gl_map.append(gle)
141 else:
142 new_gl_map.append(d)
143
144 return new_gl_map
145
Ankush Menat494bd9e2022-03-28 18:52:46 +0530146
Nabin Hait004c1ed2022-01-31 13:20:18 +0530147def get_cost_center_allocation_data(company, posting_date):
148 par = frappe.qb.DocType("Cost Center Allocation")
149 child = frappe.qb.DocType("Cost Center Allocation Percentage")
150
151 records = (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530152 frappe.qb.from_(par)
153 .inner_join(child)
154 .on(par.name == child.parent)
Nabin Hait004c1ed2022-01-31 13:20:18 +0530155 .select(par.main_cost_center, child.cost_center, child.percentage)
156 .where(par.docstatus == 1)
157 .where(par.company == company)
158 .where(par.valid_from <= posting_date)
159 .orderby(par.valid_from, order=frappe.qb.desc)
160 ).run(as_dict=True)
161
162 cc_allocation = frappe._dict()
163 for d in records:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530164 cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(
165 d.cost_center, d.percentage
166 )
Nabin Hait5b0ec642022-01-31 18:07:04 +0530167
Nabin Hait004c1ed2022-01-31 13:20:18 +0530168 return cc_allocation
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530169
Ankush Menat494bd9e2022-03-28 18:52:46 +0530170
Nabin Hait19f8fa52021-02-22 22:27:22 +0530171def merge_similar_entries(gl_map, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +0530172 merged_gl_map = []
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530173 accounting_dimensions = get_accounting_dimensions()
Nabin Hait914a3882022-07-19 12:32:54 +0530174
Nabin Haitbf495c92013-01-30 12:49:08 +0530175 for entry in gl_map:
Anand Doshi652bc072014-04-16 15:21:46 +0530176 # if there is already an entry in this account then just add it
Nabin Haitbf495c92013-01-30 12:49:08 +0530177 # to that entry
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530178 same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
Nabin Haitbf495c92013-01-30 12:49:08 +0530179 if same_head:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530180 same_head.debit = flt(same_head.debit) + flt(entry.debit)
181 same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
182 entry.debit_in_account_currency
183 )
Nabin Hait2e296fa2013-08-28 18:53:11 +0530184 same_head.credit = flt(same_head.credit) + flt(entry.credit)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530185 same_head.credit_in_account_currency = flt(same_head.credit_in_account_currency) + flt(
186 entry.credit_in_account_currency
187 )
Nabin Haitbf495c92013-01-30 12:49:08 +0530188 else:
189 merged_gl_map.append(entry)
Anand Doshi652bc072014-04-16 15:21:46 +0530190
thefalconx33bfc43d32019-12-13 15:09:51 +0530191 company = gl_map[0].company if gl_map else erpnext.get_default_company()
192 company_currency = erpnext.get_company_currency(company)
Nabin Hait19f8fa52021-02-22 22:27:22 +0530193
194 if not precision:
195 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
thefalconx33bfc43d32019-12-13 15:09:51 +0530196
Nabin Hait815a49e2013-08-07 17:00:01 +0530197 # filter zero debit and credit entries
Ankush Menat494bd9e2022-03-28 18:52:46 +0530198 merged_gl_map = filter(
ruthra kumar914b2302023-01-02 14:33:14 +0530199 lambda x: flt(x.debit, precision) != 0
200 or flt(x.credit, precision) != 0
201 or (
202 x.voucher_type == "Journal Entry"
203 and frappe.get_cached_value("Journal Entry", x.voucher_no, "voucher_type")
204 == "Exchange Gain Or Loss"
205 ),
206 merged_gl_map,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530207 )
Achilles Rasquinha908289d2018-03-08 13:10:51 +0530208 merged_gl_map = list(merged_gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530209
Nabin Haitbf495c92013-01-30 12:49:08 +0530210 return merged_gl_map
211
Ankush Menat494bd9e2022-03-28 18:52:46 +0530212
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530213def check_if_in_list(gle, gl_map, dimensions=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530214 account_head_fieldnames = [
215 "voucher_detail_no",
216 "party",
217 "against_voucher",
218 "cost_center",
219 "against_voucher_type",
220 "party_type",
221 "project",
222 "finance_book",
Gursheen Anand442e3f22023-06-16 13:38:47 +0530223 "voucher_no",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530224 ]
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530225
226 if dimensions:
227 account_head_fieldnames = account_head_fieldnames + dimensions
228
Nabin Haitbb777562013-08-29 18:19:37 +0530229 for e in gl_map:
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530230 same_head = True
231 if e.account != gle.account:
232 same_head = False
Ankush91527152021-08-11 11:17:50 +0530233 continue
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530234
235 for fieldname in account_head_fieldnames:
236 if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
237 same_head = False
Ankush91527152021-08-11 11:17:50 +0530238 break
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530239
240 if same_head:
241 return e
Nabin Haitbf495c92013-01-30 12:49:08 +0530242
Ankush Menat494bd9e2022-03-28 18:52:46 +0530243
Nabin Hait004c1ed2022-01-31 13:20:18 +0530244def toggle_debit_credit_if_negative(gl_map):
245 for entry in gl_map:
246 # toggle debit, credit if negative entry
247 if flt(entry.debit) < 0:
248 entry.credit = flt(entry.credit) - flt(entry.debit)
249 entry.debit = 0.0
250
251 if flt(entry.debit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530252 entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
253 entry.debit_in_account_currency
254 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530255 entry.debit_in_account_currency = 0.0
256
257 if flt(entry.credit) < 0:
258 entry.debit = flt(entry.debit) - flt(entry.credit)
259 entry.credit = 0.0
260
261 if flt(entry.credit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530262 entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
263 entry.credit_in_account_currency
264 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530265 entry.credit_in_account_currency = 0.0
266
267 update_net_values(entry)
268
269 return gl_map
270
Ankush Menat494bd9e2022-03-28 18:52:46 +0530271
Nabin Hait004c1ed2022-01-31 13:20:18 +0530272def update_net_values(entry):
273 # In some scenarios net value needs to be shown in the ledger
274 # This method updates net values as debit or credit
275 if entry.post_net_value and entry.debit and entry.credit:
276 if entry.debit > entry.credit:
277 entry.debit = entry.debit - entry.credit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530278 entry.debit_in_account_currency = (
279 entry.debit_in_account_currency - entry.credit_in_account_currency
280 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530281 entry.credit = 0
282 entry.credit_in_account_currency = 0
283 else:
284 entry.credit = entry.credit - entry.debit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530285 entry.credit_in_account_currency = (
286 entry.credit_in_account_currency - entry.debit_in_account_currency
287 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530288
289 entry.debit = 0
290 entry.debit_in_account_currency = 0
291
Ankush Menat494bd9e2022-03-28 18:52:46 +0530292
Nabin Haita77b8c92020-12-21 14:45:50 +0530293def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
294 if not from_repost:
295 validate_cwip_accounts(gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530296
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530297 process_debit_credit_difference(gl_map)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530298
299 if gl_map:
300 check_freezing_date(gl_map[0]["posting_date"], adv_adj)
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530301 is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
302 if gl_map[0]["voucher_type"] != "Period Closing Voucher":
303 validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530304
Nabin Haitbf495c92013-01-30 12:49:08 +0530305 for entry in gl_map:
Nabin Haita77b8c92020-12-21 14:45:50 +0530306 make_entry(entry, adv_adj, update_outstanding, from_repost)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530307
Nabin Hait914a3882022-07-19 12:32:54 +0530308
Nabin Haita77b8c92020-12-21 14:45:50 +0530309def make_entry(args, adv_adj, update_outstanding, from_repost=False):
Nabin Haitddc3bb22020-03-17 17:04:18 +0530310 gle = frappe.new_doc("GL Entry")
311 gle.update(args)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530312 gle.flags.ignore_permissions = 1
Nabin Haita77b8c92020-12-21 14:45:50 +0530313 gle.flags.from_repost = from_repost
Nabin Hait19f8fa52021-02-22 22:27:22 +0530314 gle.flags.adv_adj = adv_adj
Ankush Menat494bd9e2022-03-28 18:52:46 +0530315 gle.flags.update_outstanding = update_outstanding or "Yes"
Nabin Hait4caaab32022-07-18 17:59:42 +0530316 gle.flags.notify_update = False
Nabin Haitaeba24e2013-08-23 15:17:36 +0530317 gle.submit()
Anand Doshi652bc072014-04-16 15:21:46 +0530318
Nabin Hait4caaab32022-07-18 17:59:42 +0530319 if not from_repost and gle.voucher_type != "Period Closing Voucher":
Nabin Haita77b8c92020-12-21 14:45:50 +0530320 validate_expense_against_budget(args)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530321
Ankush Menat494bd9e2022-03-28 18:52:46 +0530322
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530323def validate_cwip_accounts(gl_map):
Ankush91527152021-08-11 11:17:50 +0530324 """Validate that CWIP account are not used in Journal Entry"""
325 if gl_map and gl_map[0].voucher_type != "Journal Entry":
326 return
Maricad00c5982019-11-12 19:17:43 +0530327
Ankush Menat494bd9e2022-03-28 18:52:46 +0530328 cwip_enabled = any(
329 cint(ac.enable_cwip_accounting)
330 for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")
331 )
Ankush91527152021-08-11 11:17:50 +0530332 if cwip_enabled:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530333 cwip_accounts = [
334 d[0]
335 for d in frappe.db.sql(
336 """select name from tabAccount
337 where account_type = 'Capital Work in Progress' and is_group=0"""
338 )
339 ]
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530340
Ankush91527152021-08-11 11:17:50 +0530341 for entry in gl_map:
342 if entry.account in cwip_accounts:
343 frappe.throw(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530344 _(
345 "Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry"
346 ).format(entry.account)
347 )
348
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530349
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530350def process_debit_credit_difference(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530351 precision = get_field_precision(
352 frappe.get_meta("GL Entry").get_field("debit"),
353 currency=frappe.get_cached_value("Company", gl_map[0].company, "default_currency"),
354 )
Anand Doshi602e8252015-11-16 19:05:46 +0530355
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530356 voucher_type = gl_map[0].voucher_type
357 voucher_no = gl_map[0].voucher_no
358 allowance = get_debit_credit_allowance(voucher_type, precision)
359
360 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
ruthra kumar914b2302023-01-02 14:33:14 +0530361
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530362 if abs(debit_credit_diff) > allowance:
ruthra kumar914b2302023-01-02 14:33:14 +0530363 if not (
364 voucher_type == "Journal Entry"
365 and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
366 == "Exchange Gain Or Loss"
367 ):
368 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530369
370 elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
371 make_round_off_gle(gl_map, debit_credit_diff, precision)
372
373 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
374 if abs(debit_credit_diff) > allowance:
ruthra kumar914b2302023-01-02 14:33:14 +0530375 if not (
376 voucher_type == "Journal Entry"
377 and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
378 == "Exchange Gain Or Loss"
379 ):
380 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530381
382
383def get_debit_credit_difference(gl_map, precision):
Nabin Haite2c200a2015-05-28 13:00:37 +0530384 debit_credit_diff = 0.0
385 for entry in gl_map:
386 entry.debit = flt(entry.debit, precision)
387 entry.credit = flt(entry.credit, precision)
388 debit_credit_diff += entry.debit - entry.credit
Anand Doshi602e8252015-11-16 19:05:46 +0530389
Nabin Haite2c200a2015-05-28 13:00:37 +0530390 debit_credit_diff = flt(debit_credit_diff, precision)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530391
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530392 return debit_credit_diff
393
394
395def get_debit_credit_allowance(voucher_type, precision):
396 if voucher_type in ("Journal Entry", "Payment Entry"):
Nabin Haitfb24a272016-02-18 19:18:07 +0530397 allowance = 5.0 / (10**precision)
398 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530399 allowance = 0.5
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530400
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530401 return allowance
Anand Doshi602e8252015-11-16 19:05:46 +0530402
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530403
404def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no):
405 frappe.throw(
406 _("Debit and Credit not equal for {0} #{1}. Difference is {2}.").format(
407 voucher_type, voucher_no, debit_credit_diff
408 )
409 )
Anand Doshi602e8252015-11-16 19:05:46 +0530410
Ankush Menat494bd9e2022-03-28 18:52:46 +0530411
Nabin Hait34c551d2019-07-03 10:34:31 +0530412def make_round_off_gle(gl_map, debit_credit_diff, precision):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530413 round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530414 gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
Ankush Menat494bd9e2022-03-28 18:52:46 +0530415 )
Nabin Hait80069a62015-05-28 19:19:59 +0530416 round_off_gle = frappe._dict()
Nabin Hait022d8d52022-11-21 15:16:53 +0530417 round_off_account_exists = False
Zarrar3523b772018-08-14 16:28:14 +0530418
Nabin Hait022d8d52022-11-21 15:16:53 +0530419 if gl_map[0].voucher_type != "Period Closing Voucher":
420 for d in gl_map:
421 if d.account == round_off_account:
422 round_off_gle = d
423 if d.debit:
424 debit_credit_diff -= flt(d.debit) - flt(d.credit)
425 else:
426 debit_credit_diff += flt(d.credit)
427 round_off_account_exists = True
428
429 if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
430 gl_map.remove(round_off_gle)
431 return
Nabin Hait34c551d2019-07-03 10:34:31 +0530432
Zarrar3523b772018-08-14 16:28:14 +0530433 if not round_off_gle:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530434 for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
435 round_off_gle[k] = gl_map[0][k]
Anand Doshi602e8252015-11-16 19:05:46 +0530436
Ankush Menat494bd9e2022-03-28 18:52:46 +0530437 round_off_gle.update(
438 {
439 "account": round_off_account,
440 "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
441 "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
442 "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
443 "credit": debit_credit_diff if debit_credit_diff > 0 else 0,
444 "cost_center": round_off_cost_center,
445 "party_type": None,
446 "party": None,
447 "is_opening": "No",
448 "against_voucher_type": None,
449 "against_voucher": None,
450 }
451 )
Anand Doshi602e8252015-11-16 19:05:46 +0530452
Deepesh Garg015812b2022-04-23 21:40:08 +0530453 update_accounting_dimensions(round_off_gle)
Zarrar3523b772018-08-14 16:28:14 +0530454 if not round_off_account_exists:
455 gl_map.append(round_off_gle)
Nabin Haite2c200a2015-05-28 13:00:37 +0530456
Ankush Menat494bd9e2022-03-28 18:52:46 +0530457
Deepesh Garg015812b2022-04-23 21:40:08 +0530458def update_accounting_dimensions(round_off_gle):
459 dimensions = get_accounting_dimensions()
Deepesh Gargc312cd32022-04-24 18:11:32 +0530460 meta = frappe.get_meta(round_off_gle["voucher_type"])
461 has_all_dimensions = True
Deepesh Garg015812b2022-04-23 21:40:08 +0530462
463 for dimension in dimensions:
Deepesh Gargc312cd32022-04-24 18:11:32 +0530464 if not meta.has_field(dimension):
465 has_all_dimensions = False
466
467 if dimensions and has_all_dimensions:
468 dimension_values = frappe.db.get_value(
Deepesh Garg3fa1c632022-04-25 16:29:26 +0530469 round_off_gle["voucher_type"], round_off_gle["voucher_no"], dimensions, as_dict=1
Deepesh Gargc312cd32022-04-24 18:11:32 +0530470 )
471
472 for dimension in dimensions:
473 round_off_gle[dimension] = dimension_values.get(dimension)
Deepesh Garg015812b2022-04-23 21:40:08 +0530474
475
ruthra kumarebe67872023-04-28 14:07:28 +0530476def get_round_off_account_and_cost_center(
477 company, voucher_type, voucher_no, use_company_default=False
478):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530479 round_off_account, round_off_cost_center = frappe.get_cached_value(
480 "Company", company, ["round_off_account", "round_off_cost_center"]
481 ) or [None, None]
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530482
Deepesh Gargc312cd32022-04-24 18:11:32 +0530483 meta = frappe.get_meta(voucher_type)
484
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530485 # Give first preference to parent cost center for round off GLE
ruthra kumarebe67872023-04-28 14:07:28 +0530486 if not use_company_default and meta.has_field("cost_center"):
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530487 parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
488 if parent_cost_center:
489 round_off_cost_center = parent_cost_center
490
Nabin Hait2e4de832017-09-19 14:53:16 +0530491 if not round_off_account:
492 frappe.throw(_("Please mention Round Off Account in Company"))
493
494 if not round_off_cost_center:
495 frappe.throw(_("Please mention Round Off Cost Center in Company"))
496
497 return round_off_account, round_off_cost_center
498
Ankush Menat494bd9e2022-03-28 18:52:46 +0530499
500def make_reverse_gl_entries(
Deepesh Gargda6bc1a2023-06-23 20:57:51 +0530501 gl_entries=None,
502 voucher_type=None,
503 voucher_no=None,
504 adv_adj=False,
505 update_outstanding="Yes",
506 partial_cancel=False,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530507):
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530508 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530509 Get original gl entries of the voucher
510 and make reverse gl entries by swapping debit and credit
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530511 """
Anand Doshi652bc072014-04-16 15:21:46 +0530512
Nabin Hait2e296fa2013-08-28 18:53:11 +0530513 if not gl_entries:
Deepesh Gargff574502022-02-06 22:56:12 +0530514 gl_entry = frappe.qb.DocType("GL Entry")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530515 gl_entries = (
516 frappe.qb.from_(gl_entry)
517 .select("*")
518 .where(gl_entry.voucher_type == voucher_type)
519 .where(gl_entry.voucher_no == voucher_no)
520 .where(gl_entry.is_cancelled == 0)
521 .for_update()
522 ).run(as_dict=1)
Nabin Hait56548cb2016-07-01 15:58:39 +0530523
Nabin Hait27994c22013-08-26 16:53:30 +0530524 if gl_entries:
ruthra kumar7312f222022-05-29 21:33:08 +0530525 create_payment_ledger_entry(
Deepesh Garg1e078d02023-06-29 12:18:25 +0530526 gl_entries,
527 cancel=1,
528 adv_adj=adv_adj,
529 update_outstanding=update_outstanding,
530 partial_cancel=partial_cancel,
ruthra kumar7312f222022-05-29 21:33:08 +0530531 )
Deepesh Garg069a54e2020-08-10 16:01:01 +0530532 validate_accounting_period(gl_entries)
Nabin Hait27994c22013-08-26 16:53:30 +0530533 check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530534
535 is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
536 validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
Deepesh Gargda6bc1a2023-06-23 20:57:51 +0530537 if not partial_cancel:
538 set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
Anand Doshi652bc072014-04-16 15:21:46 +0530539
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530540 for entry in gl_entries:
Deepesh Gargffec8652022-02-02 17:14:42 +0530541 new_gle = copy.deepcopy(entry)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530542 new_gle["name"] = None
543 debit = new_gle.get("debit", 0)
544 credit = new_gle.get("credit", 0)
Anand Doshi652bc072014-04-16 15:21:46 +0530545
Ankush Menat494bd9e2022-03-28 18:52:46 +0530546 debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
547 credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530548
Ankush Menat494bd9e2022-03-28 18:52:46 +0530549 new_gle["debit"] = credit
550 new_gle["credit"] = debit
551 new_gle["debit_in_account_currency"] = credit_in_account_currency
552 new_gle["credit_in_account_currency"] = debit_in_account_currency
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530553
Ankush Menat494bd9e2022-03-28 18:52:46 +0530554 new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
555 new_gle["is_cancelled"] = 1
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530556
Ankush Menat494bd9e2022-03-28 18:52:46 +0530557 if new_gle["debit"] or new_gle["credit"]:
Deepesh Gargffec8652022-02-02 17:14:42 +0530558 make_entry(new_gle, adv_adj, "Yes")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530559
560
561def check_freezing_date(posting_date, adv_adj=False):
562 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530563 Nobody can do GL Entries where posting date is before freezing date
564 except authorized person
Deepesh Garg13d2e7b2021-09-16 18:54:57 +0530565
Ankush Menat494bd9e2022-03-28 18:52:46 +0530566 Administrator has all the roles so this check will be bypassed if any role is allowed to post
567 Hence stop admin to bypass if accounts are freezed
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530568 """
569 if not adv_adj:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530570 acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530571 if acc_frozen_upto:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530572 frozen_accounts_modifier = frappe.db.get_value(
573 "Accounts Settings", None, "frozen_accounts_modifier"
574 )
575 if getdate(posting_date) <= getdate(acc_frozen_upto) and (
576 frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
577 ):
578 frappe.throw(
579 _("You are not authorized to add or update entries before {0}").format(
580 formatdate(acc_frozen_upto)
581 )
582 )
583
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530584
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530585def validate_against_pcv(is_opening, posting_date, company):
586 if is_opening and frappe.db.exists(
587 "Period Closing Voucher", {"docstatus": 1, "company": company}
588 ):
589 frappe.throw(
590 _("Opening Entry can not be created after Period Closing Voucher is created."),
591 title=_("Invalid Opening Entry"),
592 )
593
594 last_pcv_date = frappe.db.get_value(
595 "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
596 )
597
598 if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
599 message = _("Books have been closed till the period ending on {0}").format(
600 formatdate(last_pcv_date)
601 )
602 message += "</br >"
Deepesh Garg8ce1da12023-03-23 19:14:12 +0530603 message += _("You cannot create/amend any accounting entries till this date.")
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530604 frappe.throw(message, title=_("Period Closed"))
605
606
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530607def set_as_cancel(voucher_type, voucher_no):
608 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 Set is_cancelled=1 in all original gl entries for the voucher
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530610 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530611 frappe.db.sql(
612 """UPDATE `tabGL Entry` SET is_cancelled = 1,
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530613 modified=%s, modified_by=%s
614 where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530615 (now(), frappe.session.user, voucher_type, voucher_no),
616 )