Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: supercharge Bulk actions #38038

Merged
merged 16 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(()=> { });
});
ruthra-kumar marked this conversation as resolved.
Show resolved Hide resolved
}
}).then((r) => {
if (r.message === "No Failed Records") {
frappe.show_alert(__(r.message), 5);
} else {
frappe.show_alert(__("Retrying Failed Transactions"), 5);
}
});
}
},
});
Original file line number Diff line number Diff line change
@@ -1,31 +1,64 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2021-11-30 13:41:16.343827",
"allow_copy": 1,
"creation": "2023-11-09 20:14:45.139593",
"default_view": "List",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"log_date",
"logger_data"
"date",
"column_break_bsan",
"log_entries",
"section_break_mdmv",
"succeeded",
"column_break_qryp",
"failed"
],
"fields": [
{
"fieldname": "log_date",
"fieldname": "date",
"fieldtype": "Date",
"label": "Log Date",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Date",
"read_only": 1
},
{
"fieldname": "logger_data",
"fieldtype": "Table",
"label": "Logger Data",
"options": "Bulk Transaction Log Detail"
"fieldname": "log_entries",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Log Entries",
"read_only": 1
},
{
"fieldname": "column_break_bsan",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_mdmv",
"fieldtype": "Section Break"
},
{
"fieldname": "succeeded",
"fieldtype": "Int",
"label": "Succeeded",
"read_only": 1
},
{
"fieldname": "column_break_qryp",
"fieldtype": "Column Break"
},
{
"fieldname": "failed",
"fieldtype": "Int",
"label": "Failed",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"in_create": 1,
"is_virtual": 1,
"links": [],
"modified": "2022-02-03 17:23:02.935325",
"modified": "2023-11-11 04:52:49.347376",
"modified_by": "Administrator",
"module": "Bulk Transaction",
"name": "Bulk Transaction Log",
Expand All @@ -47,5 +80,5 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
"title_field": "date"
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,105 @@
# 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")
succeeded_logs = (
qb.from_(log_detail)
ruthra-kumar marked this conversation as resolved.
Show resolved Hide resolved
.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))

if not failed:
update_logger(
d.transaction_name,
None,
d.from_doctype,
d.to_doctype,
status="Success",
log_date=log_date,
restarted=1,
@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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

// frappe.ui.form.on("Bulk Transaction Log Detail", {
// refresh(frm) {

// },
// });
Loading
Loading