1
1
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2
2
# License: MIT. See LICENSE
3
3
4
+ import itertools
4
5
5
6
import frappe
6
7
10
11
from erpnext .accounts .doctype .accounting_dimension .accounting_dimension import (
11
12
get_accounting_dimensions ,
12
13
)
13
- from erpnext .accounts .utils import get_fiscal_year
14
14
15
15
16
16
def execute ():
17
+ # clear balances, they will be recalculated
17
18
frappe .db .truncate ("Account Closing Balance" )
18
19
19
- gle_fields = get_gle_fields ()
20
+ pcv_list = get_period_closing_vouchers ()
21
+ gl_entries = get_gl_entries (pcv_list )
22
+
23
+ for _ , pcvs in itertools .groupby (pcv_list , key = lambda pcv : (pcv .company , pcv .period_start_date )):
24
+ process_grouped_pcvs (list (pcvs ), gl_entries )
25
+
26
+
27
+ def process_grouped_pcvs (pcvs , gl_entries ):
28
+ pl_account_entries = []
29
+ closing_account_entries = []
30
+ first_pcv = pcvs [0 ]
31
+
32
+ for pcv in pcvs :
33
+ pcv_entries = gl_entries .get (pcv .name ) or []
34
+ for entry in pcv_entries :
35
+ entry ["closing_date" ] = first_pcv .period_end_date
36
+ entry ["period_closing_voucher" ] = first_pcv .name
37
+ entry ["voucher_no" ] = first_pcv .name
38
+
39
+ list_to_update = (
40
+ pl_account_entries if entry .account != pcv .closing_account_head else closing_account_entries
41
+ )
42
+ list_to_update .append (entry )
43
+
44
+ # hacky!!
45
+ if to_cancel := pcvs [1 :]:
46
+ to_cancel = [pcv .name for pcv in to_cancel ]
47
+
48
+ # update voucher number
49
+ gle_to_update = [entry .name for entry in closing_account_entries + pl_account_entries ]
50
+ frappe .db .set_value (
51
+ "GL Entry" ,
52
+ {
53
+ "name" : ("in" , gle_to_update ),
54
+ "voucher_no" : ("in" , to_cancel ),
55
+ },
56
+ "voucher_no" ,
57
+ first_pcv .name ,
58
+ update_modified = False ,
59
+ )
60
+
61
+ # mark as cancelled
62
+ frappe .db .set_value (
63
+ "Period Closing Voucher" ,
64
+ {"name" : ("in" , to_cancel )},
65
+ "docstatus" ,
66
+ 2 ,
67
+ update_modified = False ,
68
+ )
69
+
70
+ pcv_doc = frappe .get_doc ("Period Closing Voucher" , first_pcv .name )
71
+ pcv_doc .pl_accounts_reverse_gle = pl_account_entries
72
+ pcv_doc .closing_account_gle = closing_account_entries
73
+ closing_entries = pcv_doc .get_account_closing_balances ()
74
+ make_closing_entries (closing_entries , pcv_doc .name , pcv_doc .company , pcv_doc .period_end_date )
75
+
76
+
77
+ def get_period_closing_vouchers ():
78
+ return frappe .db .get_all (
79
+ "Period Closing Voucher" ,
80
+ fields = ["name" , "closing_account_head" , "period_start_date" , "period_end_date" , "company" ],
81
+ filters = {"docstatus" : 1 },
82
+ order_by = "period_start_date asc, period_end_date desc" ,
83
+ )
84
+
20
85
21
- for company in frappe .get_all ("Company" , pluck = "name" ):
22
- i = 0
23
- company_wise_order = {}
24
- for pcv in get_period_closing_vouchers (company ):
25
- company_wise_order .setdefault (pcv .company , [])
26
- if pcv .period_end_date not in company_wise_order [pcv .company ]:
27
- pcv_doc = frappe .get_doc ("Period Closing Voucher" , pcv .name )
28
- pcv_doc .pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts (pcv , gle_fields )
29
- pcv_doc .closing_account_gle = get_pcv_gl_entries_for_closing_accounts (pcv , gle_fields )
30
- closing_entries = pcv_doc .get_account_closing_balances ()
31
- make_closing_entries (closing_entries , pcv .name , pcv .company , pcv .period_end_date )
86
+ def get_gl_entries (pcv_list ):
87
+ gl_entries = frappe .get_all (
88
+ "GL Entry" ,
89
+ filters = {"voucher_no" : ("in" , [pcv .name for pcv in pcv_list ]), "is_cancelled" : 0 },
90
+ fields = get_gle_fields (),
91
+ update = {"is_period_closing_voucher_entry" : 1 },
92
+ )
32
93
33
- company_wise_order [pcv .company ].append (pcv .period_end_date )
34
- i += 1
94
+ return {k : list (v ) for k , v in itertools .groupby (gl_entries , key = lambda gle : gle .voucher_no )}
35
95
36
96
37
97
def get_gle_fields ():
38
- default_diemnsion_fields = ["cost_center" , "finance_book" , "project" ]
39
- accounting_dimension_fields = get_accounting_dimensions ()
40
- gle_fields = [
98
+ return [
41
99
"name" ,
42
100
"company" ,
43
101
"posting_date" ,
@@ -47,43 +105,11 @@ def get_gle_fields():
47
105
"credit" ,
48
106
"debit_in_account_currency" ,
49
107
"credit_in_account_currency" ,
50
- * default_diemnsion_fields ,
51
- * accounting_dimension_fields ,
108
+ "voucher_no" ,
109
+ # default dimension fields
110
+ "cost_center" ,
111
+ "finance_book" ,
112
+ "project" ,
113
+ # accounting dimensions
114
+ * get_accounting_dimensions (),
52
115
]
53
-
54
- return gle_fields
55
-
56
-
57
- def get_period_closing_vouchers (company ):
58
- return frappe .db .get_all (
59
- "Period Closing Voucher" ,
60
- fields = ["name" , "closing_account_head" , "period_start_date" , "period_end_date" , "company" ],
61
- filters = {"docstatus" : 1 , "company" : company },
62
- order_by = "period_end_date" ,
63
- )
64
-
65
-
66
- def get_pcv_gl_entries_for_pl_accounts (pcv , gle_fields ):
67
- return get_gl_entries (pcv , gle_fields , {"account" : ["!=" , pcv .closing_account_head ]})
68
-
69
-
70
- def get_pcv_gl_entries_for_closing_accounts (pcv , gle_fields ):
71
- return get_gl_entries (pcv , gle_fields , {"account" : pcv .closing_account_head })
72
-
73
-
74
- def get_gl_entries (pcv , gle_fields , accounts_filter = None ):
75
- filters = {"voucher_no" : pcv .name , "is_cancelled" : 0 }
76
- if accounts_filter :
77
- filters .update (accounts_filter )
78
-
79
- gl_entries = frappe .db .get_all (
80
- "GL Entry" ,
81
- filters = filters ,
82
- fields = gle_fields ,
83
- )
84
- for entry in gl_entries :
85
- entry ["is_period_closing_voucher_entry" ] = 1
86
- entry ["closing_date" ] = pcv .period_end_date
87
- entry ["period_closing_voucher" ] = pcv .name
88
-
89
- return gl_entries
0 commit comments