Skip to content

Commit 54b9392

Browse files
authored
Merge pull request #54584 from frappe/version-15-hotfix
2 parents 071a28f + 51e7c66 commit 54b9392

34 files changed

Lines changed: 810 additions & 140 deletions

File tree

erpnext/accounts/doctype/payment_entry/payment_entry.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2306,22 +2306,20 @@ def get_outstanding_reference_documents(args, validate=False):
23062306
# Get positive outstanding sales /purchase invoices
23072307
condition = ""
23082308
if args.get("voucher_type") and args.get("voucher_no"):
2309-
condition = " and voucher_type={} and voucher_no={}".format(
2310-
frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
2311-
)
2309+
condition = f" and voucher_type={frappe.db.escape(args['voucher_type'])} and voucher_no={frappe.db.escape(args['voucher_no'])}"
23122310
common_filter.append(ple.voucher_type == args["voucher_type"])
23132311
common_filter.append(ple.voucher_no == args["voucher_no"])
23142312

23152313
# Add cost center condition
23162314
if args.get("cost_center"):
2317-
condition += " and cost_center='%s'" % args.get("cost_center")
2315+
condition += f" and cost_center={frappe.db.escape(args.get('cost_center'))}"
23182316
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
23192317

23202318
# dynamic dimension filters
23212319
active_dimensions = get_dimensions()[0]
23222320
for dim in active_dimensions:
23232321
if args.get(dim.fieldname):
2324-
condition += f" and {dim.fieldname}='{args.get(dim.fieldname)}'"
2322+
condition += f" and {dim.fieldname}={frappe.db.escape(args.get(dim.fieldname))}"
23252323
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
23262324

23272325
date_fields_dict = {
@@ -2331,17 +2329,15 @@ def get_outstanding_reference_documents(args, validate=False):
23312329

23322330
for fieldname, date_fields in date_fields_dict.items():
23332331
if args.get(date_fields[0]) and args.get(date_fields[1]):
2334-
condition += " and {} between '{}' and '{}'".format(
2335-
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
2336-
)
2332+
condition += f" and {fieldname} between {frappe.db.escape(args.get(date_fields[0]))} and {frappe.db.escape(args.get(date_fields[1]))}"
23372333
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
23382334
elif args.get(date_fields[0]):
23392335
# if only from date is supplied
2340-
condition += f" and {fieldname} >= '{args.get(date_fields[0])}'"
2336+
condition += f" and {fieldname} >= {frappe.db.escape(args.get(date_fields[0]))}"
23412337
posting_and_due_date.append(ple[fieldname].gte(args.get(date_fields[0])))
23422338
elif args.get(date_fields[1]):
23432339
# if only to date is supplied
2344-
condition += f" and {fieldname} <= '{args.get(date_fields[1])}'"
2340+
condition += f" and {fieldname} <= {frappe.db.escape(args.get(date_fields[1]))}"
23452341
posting_and_due_date.append(ple[fieldname].lte(args.get(date_fields[1])))
23462342

23472343
if args.get("company"):
@@ -2561,7 +2557,7 @@ def get_orders_to_be_billed(
25612557
active_dimensions = get_dimensions(True)[0]
25622558
for dim in active_dimensions:
25632559
if filters.get(dim.fieldname):
2564-
condition += f" and {dim.fieldname}='{filters.get(dim.fieldname)}'"
2560+
condition += f" and {dim.fieldname}={frappe.db.escape(filters.get(dim.fieldname))}"
25652561

25662562
if party_account_currency == company_currency:
25672563
grand_total_field = "base_grand_total"

erpnext/accounts/doctype/payment_entry/test_payment_entry.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,30 @@ def test_payment_entry_against_si_usd_to_usd(self):
200200
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
201201
self.assertEqual(outstanding_amount, 100)
202202

203+
def test_reference_outstanding_amount_on_advance_pull(self):
204+
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
205+
206+
so = make_sales_order(qty=1, rate=1000)
207+
pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
208+
pe.paid_amount = pe.received_amount = 500
209+
pe.references[0].allocated_amount = 500
210+
pe.insert()
211+
pe.submit()
212+
213+
so.reload()
214+
self.assertEqual(so.advance_paid, 500)
215+
216+
si = make_sales_invoice(so.name)
217+
si.allocate_advances_automatically = 1
218+
si.save()
219+
self.assertEqual(si.get("advances")[0].allocated_amount, 500)
220+
self.assertEqual(si.get("advances")[0].reference_name, pe.name)
221+
si.submit()
222+
223+
pe.load_from_db()
224+
self.assertEqual(pe.references[0].reference_name, si.name)
225+
self.assertEqual(pe.references[0].outstanding_amount, si.outstanding_amount)
226+
203227
def test_payment_entry_against_pi(self):
204228
pi = make_purchase_invoice(
205229
supplier="_Test Supplier USD",
@@ -1937,6 +1961,37 @@ def test_delete_linked_exchange_gain_loss_journal(self):
19371961
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name)
19381962
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0])
19391963

1964+
def test_project_name_in_exchange_gain_loss_entry(self):
1965+
si = create_sales_invoice(
1966+
customer="_Test Customer USD",
1967+
debit_to="_Test Receivable USD - _TC",
1968+
currency="USD",
1969+
conversion_rate=50,
1970+
do_not_submit=True,
1971+
)
1972+
from erpnext.projects.doctype.project.test_project import make_project
1973+
1974+
si.project = make_project({"project_name": "_Test Project for Exchange Gain Loss Entry"}).name
1975+
1976+
si.submit()
1977+
1978+
pe = get_payment_entry("Sales Invoice", si.name)
1979+
1980+
pe.source_exchange_rate = 100
1981+
1982+
pe.insert()
1983+
pe.submit()
1984+
1985+
rows = frappe.get_all(
1986+
"Journal Entry Account",
1987+
or_filters=[{"reference_name": pe.name}, {"reference_name": si.name}],
1988+
fields=["project"],
1989+
)
1990+
self.assertEqual(len(rows), 2)
1991+
1992+
self.assertEqual(rows[0].project, si.project)
1993+
self.assertEqual(rows[1].project, si.project)
1994+
19401995

19411996
def create_payment_entry(**args):
19421997
payment_entry = frappe.new_doc("Payment Entry")

erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ frappe.ui.form.on("Period Closing Voucher", {
4646
function () {
4747
frappe.route_options = {
4848
voucher_no: frm.doc.name,
49-
from_date: frm.doc.posting_date,
50-
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
49+
from_date: frm.doc.period_start_date,
50+
to_date: frm.doc.period_end_date,
5151
company: frm.doc.company,
5252
categorize_by: "",
5353
show_cancelled_entries: frm.doc.docstatus === 2,

erpnext/accounts/report/accounts_payable/accounts_payable.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ frappe.query_reports["Accounts Payable"] = {
3434
},
3535
options: "Cost Center",
3636
},
37+
{
38+
fieldname: "project",
39+
label: __("Project"),
40+
fieldtype: "MultiSelectList",
41+
options: "Project",
42+
get_data: function (txt) {
43+
return frappe.db.get_link_options("Project", txt, {
44+
company: frappe.query_report.get_filter_value("company"),
45+
});
46+
},
47+
},
3748
{
3849
fieldname: "party_account",
3950
label: __("Payable Account"),

erpnext/accounts/report/accounts_payable/test_accounts_payable.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,49 @@ def test_payment_terms_template_filters(self):
120120

121121
self.assertEqual(len(report[1]), 2)
122122
self.assertEqual([pi.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term])
123+
124+
def test_project_filter(self):
125+
project = frappe.get_doc(
126+
{"doctype": "Project", "project_name": "_Test AP Project", "company": self.company}
127+
).insert()
128+
129+
pi = self.create_purchase_invoice(do_not_submit=True)
130+
pi.project = project.name
131+
pi.save().submit()
132+
133+
filters = {
134+
"company": self.company,
135+
"report_date": today(),
136+
"range": "30, 60, 90, 120",
137+
"project": [project.name],
138+
}
139+
140+
report = execute(filters)[1]
141+
self.assertEqual(len(report), 1)
142+
row = report[0]
143+
self.assertEqual(row.project, project.name)
144+
self.assertEqual(row.invoiced, 300.0)
145+
146+
def test_project_on_report_output(self):
147+
"""
148+
Report row must carry the invoice's project.
149+
"""
150+
filters = {
151+
"company": self.company,
152+
"report_date": today(),
153+
"range": "30, 60, 90, 120",
154+
}
155+
156+
project = frappe.get_doc(
157+
{"doctype": "Project", "project_name": "_Test AP Project Output", "company": self.company}
158+
).insert()
159+
160+
pi = self.create_purchase_invoice(do_not_submit=True)
161+
pi.project = project.name
162+
pi.save().submit()
163+
164+
report = execute(filters)
165+
166+
self.assertEqual(len(report[1]), 1)
167+
row = report[1][0]
168+
self.assertEqual([pi.name, project.name, 300], [row.voucher_no, row.project, row.outstanding])

erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ frappe.query_reports["Accounts Payable Summary"] = {
5353
},
5454
options: "Cost Center",
5555
},
56+
{
57+
fieldname: "project",
58+
label: __("Project"),
59+
fieldtype: "MultiSelectList",
60+
options: "Project",
61+
get_data: function (txt) {
62+
return frappe.db.get_link_options("Project", txt, {
63+
company: frappe.query_report.get_filter_value("company"),
64+
});
65+
},
66+
},
5667
{
5768
fieldname: "party_type",
5869
label: __("Party Type"),

erpnext/accounts/report/accounts_receivable/accounts_receivable.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ frappe.query_reports["Accounts Receivable"] = {
3636
},
3737
options: "Cost Center",
3838
},
39+
{
40+
fieldname: "project",
41+
label: __("Project"),
42+
fieldtype: "MultiSelectList",
43+
options: "Project",
44+
get_data: function (txt) {
45+
return frappe.db.get_link_options("Project", txt, {
46+
company: frappe.query_report.get_filter_value("company"),
47+
});
48+
},
49+
},
3950
{
4051
fieldname: "party_type",
4152
label: __("Party Type"),

erpnext/accounts/report/accounts_receivable/accounts_receivable.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ def init_voucher_balance(self, ple):
194194
and ple.against_voucher_type in self.advance_payment_doctypes
195195
):
196196
self.voucher_balance[key].cost_center = ple.cost_center
197+
self.voucher_balance[key].project = ple.project
197198

198199
self.get_invoices(ple)
199200

@@ -360,6 +361,7 @@ def fetch_ple_in_sql_procedures(self):
360361
posting_date,
361362
account_currency,
362363
cost_center,
364+
project,
363365
sum(invoiced) `invoiced`,
364366
sum(paid) `paid`,
365367
sum(credit_note) `credit_note`,
@@ -388,6 +390,7 @@ def fetch_ple_in_sql_procedures(self):
388390
"credit_note_in_account_currency",
389391
"outstanding_in_account_currency",
390392
"cost_center",
393+
"project",
391394
]:
392395
_d[field] = x.get(field)
393396

@@ -925,6 +928,7 @@ def prepare_ple_query(self):
925928
ple.against_voucher_no,
926929
ple.party_type,
927930
ple.cost_center,
931+
ple.project,
928932
ple.party,
929933
ple.posting_date,
930934
ple.due_date,
@@ -992,6 +996,9 @@ def prepare_conditions(self):
992996
if self.filters.cost_center:
993997
self.get_cost_center_conditions()
994998

999+
if self.filters.project:
1000+
self.qb_selection_filter.append(self.ple.project.isin(self.filters.project))
1001+
9951002
self.add_accounting_dimensions_filters()
9961003

9971004
def get_cost_center_conditions(self):
@@ -1231,6 +1238,7 @@ def get_columns(self):
12311238
)
12321239

12331240
self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data")
1241+
self.add_column(label=_("Project"), fieldname="project", fieldtype="Link", options="Project")
12341242
self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data")
12351243
self.add_column(
12361244
label=_("Voucher No"),
@@ -1403,6 +1411,7 @@ class InitSQLProceduresForAR:
14031411
posting_date date,
14041412
account_currency {_varchar_type},
14051413
cost_center {_varchar_type},
1414+
project {_varchar_type},
14061415
invoiced {_currency_type},
14071416
paid {_currency_type},
14081417
credit_note {_currency_type},
@@ -1422,6 +1431,7 @@ class InitSQLProceduresForAR:
14221431
against_voucher_no {_varchar_type},
14231432
party_type {_varchar_type},
14241433
cost_center {_varchar_type},
1434+
project {_varchar_type},
14251435
party {_varchar_type},
14261436
posting_date date,
14271437
due_date date,
@@ -1450,7 +1460,7 @@ class InitSQLProceduresForAR:
14501460
begin
14511461
if not exists (select name from `{_voucher_balance_name}` where name = `{genkey_function_name}`(ple, false))
14521462
then
1453-
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, false), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0);
1463+
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, false), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, ple.project, 0, 0, 0, 0, 0, 0);
14541464
end if;
14551465
end;
14561466
"""
@@ -1492,7 +1502,7 @@ class InitSQLProceduresForAR:
14921502
14931503
end if;
14941504
1495-
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, true), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0);
1505+
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, true), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', '', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0);
14961506
end;
14971507
"""
14981508

erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,3 +1204,52 @@ def test_payment_terms_template_filters(self):
12041204

12051205
self.assertEqual(len(report[1]), 2)
12061206
self.assertEqual([si.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term])
1207+
1208+
def test_project_filter(self):
1209+
project = frappe.get_doc(
1210+
{"doctype": "Project", "project_name": "_Test AR Project", "company": self.company}
1211+
).insert()
1212+
1213+
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
1214+
si.project = project.name
1215+
si.save().submit()
1216+
1217+
filters = {
1218+
"company": self.company,
1219+
"report_date": today(),
1220+
"range": "30, 60, 90, 120",
1221+
"project": [project.name],
1222+
}
1223+
1224+
report = execute(filters)[1]
1225+
self.assertEqual(len(report), 1)
1226+
row = report[0]
1227+
self.assertEqual(row.project, project.name)
1228+
self.assertEqual(row.invoiced, 100.0)
1229+
1230+
def test_project_on_report_output(self):
1231+
"""
1232+
Report row must carry the invoice's project even when the payment entry
1233+
has no project set.
1234+
"""
1235+
filters = {
1236+
"company": self.company,
1237+
"report_date": today(),
1238+
"range": "30, 60, 90, 120",
1239+
}
1240+
1241+
project = frappe.get_doc(
1242+
{"doctype": "Project", "project_name": "_Test AR Project Output", "company": self.company}
1243+
).insert()
1244+
1245+
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
1246+
si.project = project.name
1247+
si.save().submit()
1248+
1249+
# payment has no project — report row must still show the invoice's project
1250+
self.create_payment_entry(si.name)
1251+
report = execute(filters)
1252+
1253+
self.assertEqual(len(report[1]), 1)
1254+
row = report[1][0]
1255+
self.assertEqual([si.name, project.name, 60], [row.voucher_no, row.project, row.outstanding])

erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ frappe.query_reports["Accounts Receivable Summary"] = {
5353
},
5454
options: "Cost Center",
5555
},
56+
{
57+
fieldname: "project",
58+
label: __("Project"),
59+
fieldtype: "MultiSelectList",
60+
options: "Project",
61+
get_data: function (txt) {
62+
return frappe.db.get_link_options("Project", txt, {
63+
company: frappe.query_report.get_filter_value("company"),
64+
});
65+
},
66+
},
5667
{
5768
fieldname: "party_type",
5869
label: __("Party Type"),

0 commit comments

Comments
 (0)