-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38118 from frappe/mergify/bp/version-15-hotfix/pr…
…-38038 refactor: supercharge Bulk actions (backport #38038)
- Loading branch information
Showing
8 changed files
with
274 additions
and
241 deletions.
There are no files selected for viewing
45 changes: 18 additions & 27 deletions
45
erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,21 @@ | ||
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors | ||
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors | ||
// For license information, please see license.txt | ||
|
||
frappe.ui.form.on('Bulk Transaction Log', { | ||
|
||
refresh: function(frm) { | ||
frm.disable_save(); | ||
frm.add_custom_button(__('Retry Failed Transactions'), ()=>{ | ||
frappe.confirm(__("Retry Failing Transactions ?"), ()=>{ | ||
query(frm, 1); | ||
} | ||
); | ||
}); | ||
} | ||
}); | ||
|
||
function query(frm) { | ||
frappe.call({ | ||
method: "erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction", | ||
args: { | ||
log_date: frm.doc.log_date | ||
frappe.ui.form.on("Bulk Transaction Log", { | ||
refresh(frm) { | ||
frm.add_custom_button(__('Succeeded Entries'), function() { | ||
frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Success"}); | ||
}, __("View")); | ||
frm.add_custom_button(__('Failed Entries'), function() { | ||
frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Failed"}); | ||
}, __("View")); | ||
if (frm.doc.failed) { | ||
frm.add_custom_button(__('Retry Failed Transactions'), function() { | ||
frappe.call({ | ||
method: "erpnext.utilities.bulk_transaction.retry", | ||
args: {date: frm.doc.date} | ||
}); | ||
}); | ||
} | ||
}).then((r) => { | ||
if (r.message === "No Failed Records") { | ||
frappe.show_alert(__(r.message), 5); | ||
} else { | ||
frappe.show_alert(__("Retrying Failed Transactions"), 5); | ||
} | ||
}); | ||
} | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 103 additions & 58 deletions
161
erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,112 @@ | ||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors | ||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors | ||
# For license information, please see license.txt | ||
|
||
from datetime import date | ||
|
||
import frappe | ||
from frappe import qb | ||
from frappe.model.document import Document | ||
|
||
from erpnext.utilities.bulk_transaction import task, update_logger | ||
from frappe.query_builder.functions import Count | ||
from frappe.utils import cint | ||
from pypika import Order | ||
|
||
|
||
class BulkTransactionLog(Document): | ||
pass | ||
|
||
|
||
@frappe.whitelist() | ||
def retry_failing_transaction(log_date=None): | ||
if not log_date: | ||
log_date = str(date.today()) | ||
btp = frappe.qb.DocType("Bulk Transaction Log Detail") | ||
data = ( | ||
frappe.qb.from_(btp) | ||
.select(btp.transaction_name, btp.from_doctype, btp.to_doctype) | ||
.distinct() | ||
.where(btp.retried != 1) | ||
.where(btp.transaction_status == "Failed") | ||
.where(btp.date == log_date) | ||
).run(as_dict=True) | ||
|
||
if data: | ||
if len(data) > 10: | ||
frappe.enqueue(job, queue="long", job_name="bulk_retry", data=data, log_date=log_date) | ||
else: | ||
job(data, log_date) | ||
else: | ||
return "No Failed Records" | ||
|
||
|
||
def job(data, log_date): | ||
for d in data: | ||
failed = [] | ||
try: | ||
frappe.db.savepoint("before_creation_of_record") | ||
task(d.transaction_name, d.from_doctype, d.to_doctype) | ||
except Exception as e: | ||
frappe.db.rollback(save_point="before_creation_of_record") | ||
failed.append(e) | ||
update_logger( | ||
d.transaction_name, | ||
e, | ||
d.from_doctype, | ||
d.to_doctype, | ||
status="Failed", | ||
log_date=log_date, | ||
restarted=1, | ||
) | ||
def db_insert(self, *args, **kwargs): | ||
pass | ||
|
||
def load_from_db(self): | ||
log_detail = qb.DocType("Bulk Transaction Log Detail") | ||
|
||
has_records = frappe.db.sql( | ||
f"select exists (select * from `tabBulk Transaction Log Detail` where date = '{self.name}');" | ||
)[0][0] | ||
if not has_records: | ||
raise frappe.DoesNotExistError | ||
|
||
if not failed: | ||
update_logger( | ||
d.transaction_name, | ||
None, | ||
d.from_doctype, | ||
d.to_doctype, | ||
status="Success", | ||
log_date=log_date, | ||
restarted=1, | ||
succeeded_logs = ( | ||
qb.from_(log_detail) | ||
.select(Count(log_detail.date).as_("count")) | ||
.where((log_detail.date == self.name) & (log_detail.transaction_status == "Success")) | ||
.run() | ||
)[0][0] or 0 | ||
failed_logs = ( | ||
qb.from_(log_detail) | ||
.select(Count(log_detail.date).as_("count")) | ||
.where((log_detail.date == self.name) & (log_detail.transaction_status == "Failed")) | ||
.run() | ||
)[0][0] or 0 | ||
total_logs = succeeded_logs + failed_logs | ||
transaction_log = frappe._dict( | ||
{ | ||
"date": self.name, | ||
"count": total_logs, | ||
"succeeded": succeeded_logs, | ||
"failed": failed_logs, | ||
} | ||
) | ||
super(Document, self).__init__(serialize_transaction_log(transaction_log)) | ||
|
||
@staticmethod | ||
def get_list(args): | ||
filter_date = parse_list_filters(args) | ||
limit = cint(args.get("page_length")) or 20 | ||
log_detail = qb.DocType("Bulk Transaction Log Detail") | ||
|
||
dates_query = ( | ||
qb.from_(log_detail) | ||
.select(log_detail.date) | ||
.distinct() | ||
.orderby(log_detail.date, order=Order.desc) | ||
.limit(limit) | ||
) | ||
if filter_date: | ||
dates_query = dates_query.where(log_detail.date == filter_date) | ||
dates = dates_query.run() | ||
|
||
transaction_logs = [] | ||
if dates: | ||
transaction_logs_query = ( | ||
qb.from_(log_detail) | ||
.select(log_detail.date.as_("date"), Count(log_detail.date).as_("count")) | ||
.where(log_detail.date.isin(dates)) | ||
.orderby(log_detail.date, order=Order.desc) | ||
.groupby(log_detail.date) | ||
.limit(limit) | ||
) | ||
transaction_logs = transaction_logs_query.run(as_dict=True) | ||
|
||
return [serialize_transaction_log(x) for x in transaction_logs] | ||
|
||
@staticmethod | ||
def get_count(args): | ||
pass | ||
|
||
@staticmethod | ||
def get_stats(args): | ||
pass | ||
|
||
def db_update(self, *args, **kwargs): | ||
pass | ||
|
||
def delete(self): | ||
pass | ||
|
||
|
||
def serialize_transaction_log(data): | ||
return frappe._dict( | ||
name=data.date, | ||
date=data.date, | ||
log_entries=data.count, | ||
succeeded=data.succeeded, | ||
failed=data.failed, | ||
) | ||
|
||
|
||
def parse_list_filters(args): | ||
# parse date filter | ||
filter_date = None | ||
for fil in args.get("filters"): | ||
if isinstance(fil, list): | ||
for elem in fil: | ||
if elem == "date": | ||
filter_date = fil[3] | ||
return filter_date |
80 changes: 5 additions & 75 deletions
80
erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,9 @@ | ||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors | ||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors | ||
# See license.txt | ||
|
||
import unittest | ||
from datetime import date | ||
# import frappe | ||
from frappe.tests.utils import FrappeTestCase | ||
|
||
import frappe | ||
|
||
from erpnext.utilities.bulk_transaction import transaction_processing | ||
|
||
|
||
class TestBulkTransactionLog(unittest.TestCase): | ||
def setUp(self): | ||
create_company() | ||
create_customer() | ||
create_item() | ||
|
||
def test_entry_in_log(self): | ||
so_name = create_so() | ||
transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice") | ||
doc = frappe.get_doc("Bulk Transaction Log", str(date.today())) | ||
for d in doc.get("logger_data"): | ||
if d.transaction_name == so_name: | ||
self.assertEqual(d.transaction_name, so_name) | ||
self.assertEqual(d.transaction_status, "Success") | ||
self.assertEqual(d.from_doctype, "Sales Order") | ||
self.assertEqual(d.to_doctype, "Sales Invoice") | ||
self.assertEqual(d.retried, 0) | ||
|
||
|
||
def create_company(): | ||
if not frappe.db.exists("Company", "_Test Company"): | ||
frappe.get_doc( | ||
{ | ||
"doctype": "Company", | ||
"company_name": "_Test Company", | ||
"country": "India", | ||
"default_currency": "INR", | ||
} | ||
).insert() | ||
|
||
|
||
def create_customer(): | ||
if not frappe.db.exists("Customer", "Bulk Customer"): | ||
frappe.get_doc({"doctype": "Customer", "customer_name": "Bulk Customer"}).insert() | ||
|
||
|
||
def create_item(): | ||
if not frappe.db.exists("Item", "MK"): | ||
frappe.get_doc( | ||
{ | ||
"doctype": "Item", | ||
"item_code": "MK", | ||
"item_name": "Milk", | ||
"description": "Milk", | ||
"item_group": "Products", | ||
} | ||
).insert() | ||
|
||
|
||
def create_so(intent=None): | ||
so = frappe.new_doc("Sales Order") | ||
so.customer = "Bulk Customer" | ||
so.company = "_Test Company" | ||
so.transaction_date = date.today() | ||
|
||
so.set_warehouse = "Finished Goods - _TC" | ||
so.append( | ||
"items", | ||
{ | ||
"item_code": "MK", | ||
"delivery_date": date.today(), | ||
"qty": 10, | ||
"rate": 80, | ||
}, | ||
) | ||
so.insert() | ||
so.submit() | ||
return so.name | ||
class TestBulkTransactionLog(FrappeTestCase): | ||
pass |
Oops, something went wrong.