Skip to content

Commit

Permalink
Merge pull request #26081 from rohitwaghchaure/fixed-stock-entry-subm…
Browse files Browse the repository at this point in the history
…ission-performance

fix: time out while submit / cancel the stock transactions with more than 50 Items
  • Loading branch information
rohitwaghchaure committed Jun 19, 2021
2 parents be881c7 + 8520edc commit a2bb1b8
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 33 deletions.
10 changes: 7 additions & 3 deletions erpnext/controllers/buying_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,13 @@ def update_valuation_rate(self, reset_outgoing_rate=True):
TODO: rename item_tax_amount to valuation_tax_amount
"""
stock_and_asset_items = []
stock_and_asset_items = self.get_stock_items() + self.get_asset_items()

stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
last_item_idx = 1
for d in self.get("items"):
if d.item_code and d.item_code in stock_and_asset_items:
if (d.item_code and d.item_code in stock_and_asset_items):
stock_and_asset_items_qty += flt(d.qty)
stock_and_asset_items_amount += flt(d.base_net_amount)
last_item_idx = d.idx
Expand Down Expand Up @@ -683,15 +684,18 @@ def on_submit(self):
self.process_fixed_asset()
self.update_fixed_asset(field)

update_last_purchase_rate(self, is_submit = 1)
if self.doctype in ['Purchase Order', 'Purchase Receipt']:
update_last_purchase_rate(self, is_submit = 1)

def on_cancel(self):
super(BuyingController, self).on_cancel()

if self.get('is_return'):
return

update_last_purchase_rate(self, is_submit = 0)
if self.doctype in ['Purchase Order', 'Purchase Receipt']:
update_last_purchase_rate(self, is_submit = 0)

if self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'

Expand Down
79 changes: 61 additions & 18 deletions erpnext/controllers/stock_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,6 @@ def repost_future_sle_and_gle(self):
check_if_stock_and_account_balance_synced(self.posting_date,
self.company, self.doctype, self.name)


@frappe.whitelist()
def make_quality_inspections(doctype, docname, items):
if isinstance(items, str):
Expand Down Expand Up @@ -533,21 +532,75 @@ def make_quality_inspections(doctype, docname, items):

return inspections


def is_reposting_pending():
return frappe.db.exists("Repost Item Valuation",
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})

def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no)

if validate_future_sle_not_exists(args, key, sl_entries):
return False
elif get_cached_data(args, key):
return True

if not sl_entries:
sl_entries = get_sle_entries_against_voucher(args)
if not sl_entries:
return

or_conditions = get_conditions_to_validate_future_sle(sl_entries)

data = frappe.db.sql("""
select item_code, warehouse, count(name) as total_row
from `tabStock Ledger Entry`
where
({})
and timestamp(posting_date, posting_time)
>= timestamp(%(posting_date)s, %(posting_time)s)
and voucher_no != %(voucher_no)s
and is_cancelled = 0
GROUP BY
item_code, warehouse
""".format(" or ".join(or_conditions)), args, as_dict=1)

for d in data:
frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row

return len(data)

def future_sle_exists(args):
sl_entries = frappe.get_all("Stock Ledger Entry",
def validate_future_sle_not_exists(args, key, sl_entries=None):
item_key = ''
if args.get('item_code'):
item_key = (args.get('item_code'), args.get('warehouse'))

if not sl_entries and hasattr(frappe.local, 'future_sle'):
if (not frappe.local.future_sle.get(key) or
(item_key and item_key not in frappe.local.future_sle.get(key))):
return True

def get_cached_data(args, key):
if not hasattr(frappe.local, 'future_sle'):
frappe.local.future_sle = {}

if key not in frappe.local.future_sle:
frappe.local.future_sle[key] = frappe._dict({})

if args.get('item_code'):
item_key = (args.get('item_code'), args.get('warehouse'))
count = frappe.local.future_sle[key].get(item_key)

return True if (count or count == 0) else False
else:
return frappe.local.future_sle[key]

def get_sle_entries_against_voucher(args):
return frappe.get_all("Stock Ledger Entry",
filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
fields=["item_code", "warehouse"],
order_by="creation asc")

if not sl_entries:
return

def get_conditions_to_validate_future_sle(sl_entries):
warehouse_items_map = {}
for entry in sl_entries:
if entry.warehouse not in warehouse_items_map:
Expand All @@ -561,17 +614,7 @@ def future_sle_exists(args):
f"""warehouse = {frappe.db.escape(warehouse)}
and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""")

return frappe.db.sql("""
select name
from `tabStock Ledger Entry`
where
({})
and timestamp(posting_date, posting_time)
>= timestamp(%(posting_date)s, %(posting_time)s)
and voucher_no != %(voucher_no)s
and is_cancelled = 0
limit 1
""".format(" or ".join(or_conditions)), args)
return or_conditions

def create_repost_item_valuation_entry(args):
args = frappe._dict(args)
Expand Down
19 changes: 10 additions & 9 deletions erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, date_diff
from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, cint
from frappe.model.document import Document
from datetime import date
from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
Expand Down Expand Up @@ -108,17 +108,18 @@ def validate_item(self):
self.stock_uom = item_det.stock_uom

def check_stock_frozen_date(self):
stock_frozen_upto = frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
if stock_frozen_upto:
stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role')
if getdate(self.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in frappe.get_roles():
frappe.throw(_("Stock transactions before {0} are frozen").format(formatdate(stock_frozen_upto)), StockFreezeError)
stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')

stock_frozen_upto_days = int(frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0)
if stock_settings.stock_frozen_upto:
if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
and stock_settings.stock_auth_role not in frappe.get_roles()):
frappe.throw(_("Stock transactions before {0} are frozen")
.format(formatdate(stock_settings.stock_frozen_upto)), StockFreezeError)

stock_frozen_upto_days = cint(stock_settings.stock_frozen_upto_days)
if stock_frozen_upto_days:
stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role')
older_than_x_days_ago = (add_days(getdate(self.posting_date), stock_frozen_upto_days) <= date.today())
if older_than_x_days_ago and not stock_auth_role in frappe.get_roles():
if older_than_x_days_ago and stock_settings.stock_auth_role not in frappe.get_roles():
frappe.throw(_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days), StockFreezeError)

def scrub_posting_time(self):
Expand Down
16 changes: 14 additions & 2 deletions erpnext/stock/stock_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SerialNoExistsInFutureTransaction(frappe.ValidationError):
# _exceptions = []

def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
from erpnext.controllers.stock_controller import future_sle_exists
if sl_entries:
from erpnext.stock.utils import update_bin

Expand All @@ -30,6 +31,9 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
validate_cancellation(sl_entries)
set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))

args = get_args_for_future_sle(sl_entries[0])
future_sle_exists(args, sl_entries)

for sle in sl_entries:
if sle.serial_no:
validate_serial_no(sle)
Expand All @@ -53,6 +57,14 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
args = sle_doc.as_dict()
update_bin(args, allow_negative_stock, via_landed_cost_voucher)

def get_args_for_future_sle(row):
return frappe._dict({
'voucher_type': row.get('voucher_type'),
'voucher_no': row.get('voucher_no'),
'posting_date': row.get('posting_date'),
'posting_time': row.get('posting_time')
})

def validate_serial_no(sle):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
for sn in get_serial_nos(sle.serial_no):
Expand Down Expand Up @@ -472,8 +484,8 @@ def update_rate_on_purchase_receipt(self, sle, outgoing_rate):
frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate)

# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes':
doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
doc.update_valuation_rate(reset_outgoing_rate=False)
for d in (doc.items + doc.supplied_items):
d.db_update()
Expand Down
2 changes: 1 addition & 1 deletion erpnext/stock/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def get_bin(item_code, warehouse):
return bin_obj

def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item')
is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
if is_stock_item:
bin = get_bin(args.get("item_code"), args.get("warehouse"))
bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
Expand Down

0 comments on commit a2bb1b8

Please sign in to comment.