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

fix: time out while submit / cancel the stock transactions with more than 50 Items #26081

Merged
Show file tree
Hide file tree
Changes from all 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
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