blob: f1dad875fa79aacd879b78eb590e2a7a35c226bc [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")
Deepesh Garg4e26d422022-10-30 19:33:27 +0530131
132 # Validate budget against main cost center
133 validate_expense_against_budget(
134 d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
135 )
136
Nabin Hait004c1ed2022-01-31 13:20:18 +0530137 if cost_center and cost_center_allocation.get(cost_center):
138 for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
139 gle = copy.deepcopy(d)
140 gle.cost_center = sub_cost_center
ruthra kumar71f6f782022-06-24 13:36:15 +0530141 for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"):
Nabin Hait004c1ed2022-01-31 13:20:18 +0530142 gle[field] = flt(flt(d.get(field)) * percentage / 100, precision)
143 new_gl_map.append(gle)
144 else:
145 new_gl_map.append(d)
146
147 return new_gl_map
148
Ankush Menat494bd9e2022-03-28 18:52:46 +0530149
Nabin Hait004c1ed2022-01-31 13:20:18 +0530150def get_cost_center_allocation_data(company, posting_date):
151 par = frappe.qb.DocType("Cost Center Allocation")
152 child = frappe.qb.DocType("Cost Center Allocation Percentage")
153
154 records = (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530155 frappe.qb.from_(par)
156 .inner_join(child)
157 .on(par.name == child.parent)
Nabin Hait004c1ed2022-01-31 13:20:18 +0530158 .select(par.main_cost_center, child.cost_center, child.percentage)
159 .where(par.docstatus == 1)
160 .where(par.company == company)
161 .where(par.valid_from <= posting_date)
162 .orderby(par.valid_from, order=frappe.qb.desc)
163 ).run(as_dict=True)
164
165 cc_allocation = frappe._dict()
166 for d in records:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530167 cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(
168 d.cost_center, d.percentage
169 )
Nabin Hait5b0ec642022-01-31 18:07:04 +0530170
Nabin Hait004c1ed2022-01-31 13:20:18 +0530171 return cc_allocation
Deepesh Garg5ba3b282021-11-25 15:42:30 +0530172
Ankush Menat494bd9e2022-03-28 18:52:46 +0530173
Nabin Hait19f8fa52021-02-22 22:27:22 +0530174def merge_similar_entries(gl_map, precision=None):
Nabin Haitbf495c92013-01-30 12:49:08 +0530175 merged_gl_map = []
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530176 accounting_dimensions = get_accounting_dimensions()
Nabin Hait914a3882022-07-19 12:32:54 +0530177
Nabin Haitbf495c92013-01-30 12:49:08 +0530178 for entry in gl_map:
Anand Doshi652bc072014-04-16 15:21:46 +0530179 # if there is already an entry in this account then just add it
Nabin Haitbf495c92013-01-30 12:49:08 +0530180 # to that entry
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530181 same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
Nabin Haitbf495c92013-01-30 12:49:08 +0530182 if same_head:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530183 same_head.debit = flt(same_head.debit) + flt(entry.debit)
184 same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
185 entry.debit_in_account_currency
186 )
Nabin Hait2e296fa2013-08-28 18:53:11 +0530187 same_head.credit = flt(same_head.credit) + flt(entry.credit)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530188 same_head.credit_in_account_currency = flt(same_head.credit_in_account_currency) + flt(
189 entry.credit_in_account_currency
190 )
Nabin Haitbf495c92013-01-30 12:49:08 +0530191 else:
192 merged_gl_map.append(entry)
Anand Doshi652bc072014-04-16 15:21:46 +0530193
thefalconx33bfc43d32019-12-13 15:09:51 +0530194 company = gl_map[0].company if gl_map else erpnext.get_default_company()
195 company_currency = erpnext.get_company_currency(company)
Nabin Hait19f8fa52021-02-22 22:27:22 +0530196
197 if not precision:
198 precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
thefalconx33bfc43d32019-12-13 15:09:51 +0530199
Nabin Hait815a49e2013-08-07 17:00:01 +0530200 # filter zero debit and credit entries
Ankush Menat494bd9e2022-03-28 18:52:46 +0530201 merged_gl_map = filter(
ruthra kumar914b2302023-01-02 14:33:14 +0530202 lambda x: flt(x.debit, precision) != 0
203 or flt(x.credit, precision) != 0
204 or (
205 x.voucher_type == "Journal Entry"
206 and frappe.get_cached_value("Journal Entry", x.voucher_no, "voucher_type")
207 == "Exchange Gain Or Loss"
208 ),
209 merged_gl_map,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530210 )
Achilles Rasquinha908289d2018-03-08 13:10:51 +0530211 merged_gl_map = list(merged_gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530212
Nabin Haitbf495c92013-01-30 12:49:08 +0530213 return merged_gl_map
214
Ankush Menat494bd9e2022-03-28 18:52:46 +0530215
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530216def check_if_in_list(gle, gl_map, dimensions=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530217 account_head_fieldnames = [
218 "voucher_detail_no",
219 "party",
220 "against_voucher",
221 "cost_center",
222 "against_voucher_type",
223 "party_type",
224 "project",
225 "finance_book",
Gursheen Anand442e3f22023-06-16 13:38:47 +0530226 "voucher_no",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530227 ]
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530228
229 if dimensions:
230 account_head_fieldnames = account_head_fieldnames + dimensions
231
Nabin Haitbb777562013-08-29 18:19:37 +0530232 for e in gl_map:
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530233 same_head = True
234 if e.account != gle.account:
235 same_head = False
Ankush91527152021-08-11 11:17:50 +0530236 continue
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530237
238 for fieldname in account_head_fieldnames:
239 if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
240 same_head = False
Ankush91527152021-08-11 11:17:50 +0530241 break
deepeshgarg0073d11ac02019-05-19 00:02:01 +0530242
243 if same_head:
244 return e
Nabin Haitbf495c92013-01-30 12:49:08 +0530245
Ankush Menat494bd9e2022-03-28 18:52:46 +0530246
Nabin Hait004c1ed2022-01-31 13:20:18 +0530247def toggle_debit_credit_if_negative(gl_map):
248 for entry in gl_map:
249 # toggle debit, credit if negative entry
250 if flt(entry.debit) < 0:
251 entry.credit = flt(entry.credit) - flt(entry.debit)
252 entry.debit = 0.0
253
254 if flt(entry.debit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530255 entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
256 entry.debit_in_account_currency
257 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530258 entry.debit_in_account_currency = 0.0
259
260 if flt(entry.credit) < 0:
261 entry.debit = flt(entry.debit) - flt(entry.credit)
262 entry.credit = 0.0
263
264 if flt(entry.credit_in_account_currency) < 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530265 entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
266 entry.credit_in_account_currency
267 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530268 entry.credit_in_account_currency = 0.0
269
270 update_net_values(entry)
271
272 return gl_map
273
Ankush Menat494bd9e2022-03-28 18:52:46 +0530274
Nabin Hait004c1ed2022-01-31 13:20:18 +0530275def update_net_values(entry):
276 # In some scenarios net value needs to be shown in the ledger
277 # This method updates net values as debit or credit
278 if entry.post_net_value and entry.debit and entry.credit:
279 if entry.debit > entry.credit:
280 entry.debit = entry.debit - entry.credit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530281 entry.debit_in_account_currency = (
282 entry.debit_in_account_currency - entry.credit_in_account_currency
283 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530284 entry.credit = 0
285 entry.credit_in_account_currency = 0
286 else:
287 entry.credit = entry.credit - entry.debit
Ankush Menat494bd9e2022-03-28 18:52:46 +0530288 entry.credit_in_account_currency = (
289 entry.credit_in_account_currency - entry.debit_in_account_currency
290 )
Nabin Hait004c1ed2022-01-31 13:20:18 +0530291
292 entry.debit = 0
293 entry.debit_in_account_currency = 0
294
Ankush Menat494bd9e2022-03-28 18:52:46 +0530295
Nabin Haita77b8c92020-12-21 14:45:50 +0530296def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
297 if not from_repost:
298 validate_cwip_accounts(gl_map)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530299
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530300 process_debit_credit_difference(gl_map)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530301
302 if gl_map:
303 check_freezing_date(gl_map[0]["posting_date"], adv_adj)
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530304 is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
305 if gl_map[0]["voucher_type"] != "Period Closing Voucher":
306 validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530307
Nabin Haitbf495c92013-01-30 12:49:08 +0530308 for entry in gl_map:
Nabin Haita77b8c92020-12-21 14:45:50 +0530309 make_entry(entry, adv_adj, update_outstanding, from_repost)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530310
Nabin Hait914a3882022-07-19 12:32:54 +0530311
Nabin Haita77b8c92020-12-21 14:45:50 +0530312def make_entry(args, adv_adj, update_outstanding, from_repost=False):
Nabin Haitddc3bb22020-03-17 17:04:18 +0530313 gle = frappe.new_doc("GL Entry")
314 gle.update(args)
Anand Doshi6dfd4302015-02-10 14:41:27 +0530315 gle.flags.ignore_permissions = 1
Nabin Haita77b8c92020-12-21 14:45:50 +0530316 gle.flags.from_repost = from_repost
Nabin Hait19f8fa52021-02-22 22:27:22 +0530317 gle.flags.adv_adj = adv_adj
Ankush Menat494bd9e2022-03-28 18:52:46 +0530318 gle.flags.update_outstanding = update_outstanding or "Yes"
Nabin Hait4caaab32022-07-18 17:59:42 +0530319 gle.flags.notify_update = False
Nabin Haitaeba24e2013-08-23 15:17:36 +0530320 gle.submit()
Anand Doshi652bc072014-04-16 15:21:46 +0530321
Nabin Hait4caaab32022-07-18 17:59:42 +0530322 if not from_repost and gle.voucher_type != "Period Closing Voucher":
Nabin Haita77b8c92020-12-21 14:45:50 +0530323 validate_expense_against_budget(args)
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530324
Ankush Menat494bd9e2022-03-28 18:52:46 +0530325
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530326def validate_cwip_accounts(gl_map):
Ankush91527152021-08-11 11:17:50 +0530327 """Validate that CWIP account are not used in Journal Entry"""
328 if gl_map and gl_map[0].voucher_type != "Journal Entry":
329 return
Maricad00c5982019-11-12 19:17:43 +0530330
Ankush Menat494bd9e2022-03-28 18:52:46 +0530331 cwip_enabled = any(
332 cint(ac.enable_cwip_accounting)
333 for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")
334 )
Ankush91527152021-08-11 11:17:50 +0530335 if cwip_enabled:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530336 cwip_accounts = [
337 d[0]
338 for d in frappe.db.sql(
339 """select name from tabAccount
340 where account_type = 'Capital Work in Progress' and is_group=0"""
341 )
342 ]
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530343
Ankush91527152021-08-11 11:17:50 +0530344 for entry in gl_map:
345 if entry.account in cwip_accounts:
346 frappe.throw(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530347 _(
348 "Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry"
349 ).format(entry.account)
350 )
351
Anurag Mishra7e1987e2019-08-05 10:18:57 +0530352
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530353def process_debit_credit_difference(gl_map):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530354 precision = get_field_precision(
355 frappe.get_meta("GL Entry").get_field("debit"),
356 currency=frappe.get_cached_value("Company", gl_map[0].company, "default_currency"),
357 )
Anand Doshi602e8252015-11-16 19:05:46 +0530358
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530359 voucher_type = gl_map[0].voucher_type
360 voucher_no = gl_map[0].voucher_no
361 allowance = get_debit_credit_allowance(voucher_type, precision)
362
363 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
ruthra kumar914b2302023-01-02 14:33:14 +0530364
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530365 if abs(debit_credit_diff) > allowance:
ruthra kumar914b2302023-01-02 14:33:14 +0530366 if not (
367 voucher_type == "Journal Entry"
368 and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
369 == "Exchange Gain Or Loss"
370 ):
371 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530372
373 elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
374 make_round_off_gle(gl_map, debit_credit_diff, precision)
375
376 debit_credit_diff = get_debit_credit_difference(gl_map, precision)
377 if abs(debit_credit_diff) > allowance:
ruthra kumar914b2302023-01-02 14:33:14 +0530378 if not (
379 voucher_type == "Journal Entry"
380 and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
381 == "Exchange Gain Or Loss"
382 ):
383 raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530384
385
386def get_debit_credit_difference(gl_map, precision):
Nabin Haite2c200a2015-05-28 13:00:37 +0530387 debit_credit_diff = 0.0
388 for entry in gl_map:
389 entry.debit = flt(entry.debit, precision)
390 entry.credit = flt(entry.credit, precision)
391 debit_credit_diff += entry.debit - entry.credit
Anand Doshi602e8252015-11-16 19:05:46 +0530392
Nabin Haite2c200a2015-05-28 13:00:37 +0530393 debit_credit_diff = flt(debit_credit_diff, precision)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530394
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530395 return debit_credit_diff
396
397
398def get_debit_credit_allowance(voucher_type, precision):
399 if voucher_type in ("Journal Entry", "Payment Entry"):
Nabin Haitfb24a272016-02-18 19:18:07 +0530400 allowance = 5.0 / (10**precision)
401 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530402 allowance = 0.5
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530403
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530404 return allowance
Anand Doshi602e8252015-11-16 19:05:46 +0530405
Saqib Ansaribfc34e12022-04-01 11:03:16 +0530406
407def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no):
408 frappe.throw(
409 _("Debit and Credit not equal for {0} #{1}. Difference is {2}.").format(
410 voucher_type, voucher_no, debit_credit_diff
411 )
412 )
Anand Doshi602e8252015-11-16 19:05:46 +0530413
Ankush Menat494bd9e2022-03-28 18:52:46 +0530414
Nabin Hait34c551d2019-07-03 10:34:31 +0530415def make_round_off_gle(gl_map, debit_credit_diff, precision):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530416 round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530417 gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
Ankush Menat494bd9e2022-03-28 18:52:46 +0530418 )
Nabin Hait80069a62015-05-28 19:19:59 +0530419 round_off_gle = frappe._dict()
Nabin Hait022d8d52022-11-21 15:16:53 +0530420 round_off_account_exists = False
Zarrar3523b772018-08-14 16:28:14 +0530421
Nabin Hait022d8d52022-11-21 15:16:53 +0530422 if gl_map[0].voucher_type != "Period Closing Voucher":
423 for d in gl_map:
424 if d.account == round_off_account:
425 round_off_gle = d
426 if d.debit:
427 debit_credit_diff -= flt(d.debit) - flt(d.credit)
428 else:
429 debit_credit_diff += flt(d.credit)
430 round_off_account_exists = True
431
432 if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
433 gl_map.remove(round_off_gle)
434 return
Nabin Hait34c551d2019-07-03 10:34:31 +0530435
Zarrar3523b772018-08-14 16:28:14 +0530436 if not round_off_gle:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530437 for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
438 round_off_gle[k] = gl_map[0][k]
Anand Doshi602e8252015-11-16 19:05:46 +0530439
Ankush Menat494bd9e2022-03-28 18:52:46 +0530440 round_off_gle.update(
441 {
442 "account": round_off_account,
443 "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
444 "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
445 "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
446 "credit": debit_credit_diff if debit_credit_diff > 0 else 0,
447 "cost_center": round_off_cost_center,
448 "party_type": None,
449 "party": None,
450 "is_opening": "No",
451 "against_voucher_type": None,
452 "against_voucher": None,
453 }
454 )
Anand Doshi602e8252015-11-16 19:05:46 +0530455
Deepesh Garg015812b2022-04-23 21:40:08 +0530456 update_accounting_dimensions(round_off_gle)
Zarrar3523b772018-08-14 16:28:14 +0530457 if not round_off_account_exists:
458 gl_map.append(round_off_gle)
Nabin Haite2c200a2015-05-28 13:00:37 +0530459
Ankush Menat494bd9e2022-03-28 18:52:46 +0530460
Deepesh Garg015812b2022-04-23 21:40:08 +0530461def update_accounting_dimensions(round_off_gle):
462 dimensions = get_accounting_dimensions()
Deepesh Gargc312cd32022-04-24 18:11:32 +0530463 meta = frappe.get_meta(round_off_gle["voucher_type"])
464 has_all_dimensions = True
Deepesh Garg015812b2022-04-23 21:40:08 +0530465
466 for dimension in dimensions:
Deepesh Gargc312cd32022-04-24 18:11:32 +0530467 if not meta.has_field(dimension):
468 has_all_dimensions = False
469
470 if dimensions and has_all_dimensions:
471 dimension_values = frappe.db.get_value(
Deepesh Garg3fa1c632022-04-25 16:29:26 +0530472 round_off_gle["voucher_type"], round_off_gle["voucher_no"], dimensions, as_dict=1
Deepesh Gargc312cd32022-04-24 18:11:32 +0530473 )
474
475 for dimension in dimensions:
476 round_off_gle[dimension] = dimension_values.get(dimension)
Deepesh Garg015812b2022-04-23 21:40:08 +0530477
478
ruthra kumarebe67872023-04-28 14:07:28 +0530479def get_round_off_account_and_cost_center(
480 company, voucher_type, voucher_no, use_company_default=False
481):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530482 round_off_account, round_off_cost_center = frappe.get_cached_value(
483 "Company", company, ["round_off_account", "round_off_cost_center"]
484 ) or [None, None]
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530485
Deepesh Gargc312cd32022-04-24 18:11:32 +0530486 meta = frappe.get_meta(voucher_type)
487
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530488 # Give first preference to parent cost center for round off GLE
ruthra kumarebe67872023-04-28 14:07:28 +0530489 if not use_company_default and meta.has_field("cost_center"):
Deepesh Garg0ac11a52022-04-20 12:18:11 +0530490 parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
491 if parent_cost_center:
492 round_off_cost_center = parent_cost_center
493
Nabin Hait2e4de832017-09-19 14:53:16 +0530494 if not round_off_account:
495 frappe.throw(_("Please mention Round Off Account in Company"))
496
497 if not round_off_cost_center:
498 frappe.throw(_("Please mention Round Off Cost Center in Company"))
499
500 return round_off_account, round_off_cost_center
501
Ankush Menat494bd9e2022-03-28 18:52:46 +0530502
503def make_reverse_gl_entries(
Deepesh Gargda6bc1a2023-06-23 20:57:51 +0530504 gl_entries=None,
505 voucher_type=None,
506 voucher_no=None,
507 adv_adj=False,
508 update_outstanding="Yes",
509 partial_cancel=False,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530510):
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530511 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530512 Get original gl entries of the voucher
513 and make reverse gl entries by swapping debit and credit
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530514 """
Anand Doshi652bc072014-04-16 15:21:46 +0530515
Nabin Hait2e296fa2013-08-28 18:53:11 +0530516 if not gl_entries:
Deepesh Gargff574502022-02-06 22:56:12 +0530517 gl_entry = frappe.qb.DocType("GL Entry")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530518 gl_entries = (
519 frappe.qb.from_(gl_entry)
520 .select("*")
521 .where(gl_entry.voucher_type == voucher_type)
522 .where(gl_entry.voucher_no == voucher_no)
523 .where(gl_entry.is_cancelled == 0)
524 .for_update()
525 ).run(as_dict=1)
Nabin Hait56548cb2016-07-01 15:58:39 +0530526
Nabin Hait27994c22013-08-26 16:53:30 +0530527 if gl_entries:
ruthra kumar7312f222022-05-29 21:33:08 +0530528 create_payment_ledger_entry(
Deepesh Garg1e078d02023-06-29 12:18:25 +0530529 gl_entries,
530 cancel=1,
531 adv_adj=adv_adj,
532 update_outstanding=update_outstanding,
533 partial_cancel=partial_cancel,
ruthra kumar7312f222022-05-29 21:33:08 +0530534 )
Deepesh Garg069a54e2020-08-10 16:01:01 +0530535 validate_accounting_period(gl_entries)
Nabin Hait27994c22013-08-26 16:53:30 +0530536 check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530537
538 is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
539 validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
Deepesh Gargda6bc1a2023-06-23 20:57:51 +0530540 if not partial_cancel:
541 set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
Anand Doshi652bc072014-04-16 15:21:46 +0530542
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530543 for entry in gl_entries:
Deepesh Gargffec8652022-02-02 17:14:42 +0530544 new_gle = copy.deepcopy(entry)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530545 new_gle["name"] = None
546 debit = new_gle.get("debit", 0)
547 credit = new_gle.get("credit", 0)
Anand Doshi652bc072014-04-16 15:21:46 +0530548
Ankush Menat494bd9e2022-03-28 18:52:46 +0530549 debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
550 credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
Rushabh Mehta708e47a2018-08-08 16:37:31 +0530551
Ankush Menat494bd9e2022-03-28 18:52:46 +0530552 new_gle["debit"] = credit
553 new_gle["credit"] = debit
554 new_gle["debit_in_account_currency"] = credit_in_account_currency
555 new_gle["credit_in_account_currency"] = debit_in_account_currency
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530556
Ankush Menat494bd9e2022-03-28 18:52:46 +0530557 new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
558 new_gle["is_cancelled"] = 1
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530559
Ankush Menat494bd9e2022-03-28 18:52:46 +0530560 if new_gle["debit"] or new_gle["credit"]:
Deepesh Gargffec8652022-02-02 17:14:42 +0530561 make_entry(new_gle, adv_adj, "Yes")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530562
563
564def check_freezing_date(posting_date, adv_adj=False):
565 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530566 Nobody can do GL Entries where posting date is before freezing date
567 except authorized person
Deepesh Garg13d2e7b2021-09-16 18:54:57 +0530568
Ankush Menat494bd9e2022-03-28 18:52:46 +0530569 Administrator has all the roles so this check will be bypassed if any role is allowed to post
570 Hence stop admin to bypass if accounts are freezed
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530571 """
572 if not adv_adj:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530573 acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530574 if acc_frozen_upto:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530575 frozen_accounts_modifier = frappe.db.get_value(
576 "Accounts Settings", None, "frozen_accounts_modifier"
577 )
578 if getdate(posting_date) <= getdate(acc_frozen_upto) and (
579 frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
580 ):
581 frappe.throw(
582 _("You are not authorized to add or update entries before {0}").format(
583 formatdate(acc_frozen_upto)
584 )
585 )
586
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530587
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530588def validate_against_pcv(is_opening, posting_date, company):
589 if is_opening and frappe.db.exists(
590 "Period Closing Voucher", {"docstatus": 1, "company": company}
591 ):
592 frappe.throw(
593 _("Opening Entry can not be created after Period Closing Voucher is created."),
594 title=_("Invalid Opening Entry"),
595 )
596
597 last_pcv_date = frappe.db.get_value(
598 "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
599 )
600
601 if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
602 message = _("Books have been closed till the period ending on {0}").format(
603 formatdate(last_pcv_date)
604 )
605 message += "</br >"
Deepesh Garg8ce1da12023-03-23 19:14:12 +0530606 message += _("You cannot create/amend any accounting entries till this date.")
Deepesh Gargf92c63f2023-02-26 15:53:33 +0530607 frappe.throw(message, title=_("Period Closed"))
608
609
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530610def set_as_cancel(voucher_type, voucher_no):
611 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530612 Set is_cancelled=1 in all original gl entries for the voucher
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530613 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530614 frappe.db.sql(
615 """UPDATE `tabGL Entry` SET is_cancelled = 1,
Deepesh Garg2a9c5ba2020-04-30 10:38:58 +0530616 modified=%s, modified_by=%s
617 where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530618 (now(), frappe.session.user, voucher_type, voucher_no),
619 )