Merge pull request #26081 from rohitwaghchaure/fixed-stock-entry-submission-performance
fix: time out while submit / cancel the stock transactions with more than 50 Items
This commit is contained in:
commit
a2bb1b8acd
@ -171,12 +171,13 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
TODO: rename item_tax_amount to valuation_tax_amount
|
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 = self.get_stock_items() + self.get_asset_items()
|
||||||
|
|
||||||
stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
|
stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
|
||||||
last_item_idx = 1
|
last_item_idx = 1
|
||||||
for d in self.get("items"):
|
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_qty += flt(d.qty)
|
||||||
stock_and_asset_items_amount += flt(d.base_net_amount)
|
stock_and_asset_items_amount += flt(d.base_net_amount)
|
||||||
last_item_idx = d.idx
|
last_item_idx = d.idx
|
||||||
@ -683,7 +684,8 @@ class BuyingController(StockController):
|
|||||||
self.process_fixed_asset()
|
self.process_fixed_asset()
|
||||||
self.update_fixed_asset(field)
|
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):
|
def on_cancel(self):
|
||||||
super(BuyingController, self).on_cancel()
|
super(BuyingController, self).on_cancel()
|
||||||
@ -691,7 +693,9 @@ class BuyingController(StockController):
|
|||||||
if self.get('is_return'):
|
if self.get('is_return'):
|
||||||
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']:
|
if self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
|
||||||
field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'
|
field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'
|
||||||
|
|
||||||
|
@ -501,7 +501,6 @@ class StockController(AccountsController):
|
|||||||
check_if_stock_and_account_balance_synced(self.posting_date,
|
check_if_stock_and_account_balance_synced(self.posting_date,
|
||||||
self.company, self.doctype, self.name)
|
self.company, self.doctype, self.name)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_quality_inspections(doctype, docname, items):
|
def make_quality_inspections(doctype, docname, items):
|
||||||
if isinstance(items, str):
|
if isinstance(items, str):
|
||||||
@ -533,21 +532,75 @@ def make_quality_inspections(doctype, docname, items):
|
|||||||
|
|
||||||
return inspections
|
return inspections
|
||||||
|
|
||||||
|
|
||||||
def is_reposting_pending():
|
def is_reposting_pending():
|
||||||
return frappe.db.exists("Repost Item Valuation",
|
return frappe.db.exists("Repost Item Valuation",
|
||||||
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
||||||
|
|
||||||
|
def future_sle_exists(args, sl_entries=None):
|
||||||
|
key = (args.voucher_type, args.voucher_no)
|
||||||
|
|
||||||
def future_sle_exists(args):
|
if validate_future_sle_not_exists(args, key, sl_entries):
|
||||||
sl_entries = frappe.get_all("Stock Ledger Entry",
|
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 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},
|
filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
|
||||||
fields=["item_code", "warehouse"],
|
fields=["item_code", "warehouse"],
|
||||||
order_by="creation asc")
|
order_by="creation asc")
|
||||||
|
|
||||||
if not sl_entries:
|
def get_conditions_to_validate_future_sle(sl_entries):
|
||||||
return
|
|
||||||
|
|
||||||
warehouse_items_map = {}
|
warehouse_items_map = {}
|
||||||
for entry in sl_entries:
|
for entry in sl_entries:
|
||||||
if entry.warehouse not in warehouse_items_map:
|
if entry.warehouse not in warehouse_items_map:
|
||||||
@ -561,17 +614,7 @@ def future_sle_exists(args):
|
|||||||
f"""warehouse = {frappe.db.escape(warehouse)}
|
f"""warehouse = {frappe.db.escape(warehouse)}
|
||||||
and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""")
|
and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""")
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return or_conditions
|
||||||
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)
|
|
||||||
|
|
||||||
def create_repost_item_valuation_entry(args):
|
def create_repost_item_valuation_entry(args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
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 frappe.model.document import Document
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
|
from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
|
||||||
@ -108,17 +108,18 @@ class StockLedgerEntry(Document):
|
|||||||
self.stock_uom = item_det.stock_uom
|
self.stock_uom = item_det.stock_uom
|
||||||
|
|
||||||
def check_stock_frozen_date(self):
|
def check_stock_frozen_date(self):
|
||||||
stock_frozen_upto = frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
|
stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')
|
||||||
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_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:
|
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())
|
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)
|
frappe.throw(_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days), StockFreezeError)
|
||||||
|
|
||||||
def scrub_posting_time(self):
|
def scrub_posting_time(self):
|
||||||
|
@ -22,6 +22,7 @@ _exceptions = frappe.local('stockledger_exceptions')
|
|||||||
# _exceptions = []
|
# _exceptions = []
|
||||||
|
|
||||||
def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
|
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:
|
if sl_entries:
|
||||||
from erpnext.stock.utils import update_bin
|
from erpnext.stock.utils import update_bin
|
||||||
|
|
||||||
@ -30,6 +31,9 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
|
|||||||
validate_cancellation(sl_entries)
|
validate_cancellation(sl_entries)
|
||||||
set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
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:
|
for sle in sl_entries:
|
||||||
if sle.serial_no:
|
if sle.serial_no:
|
||||||
validate_serial_no(sle)
|
validate_serial_no(sle)
|
||||||
@ -53,6 +57,14 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
|
|||||||
args = sle_doc.as_dict()
|
args = sle_doc.as_dict()
|
||||||
update_bin(args, allow_negative_stock, via_landed_cost_voucher)
|
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):
|
def validate_serial_no(sle):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
for sn in get_serial_nos(sle.serial_no):
|
for sn in get_serial_nos(sle.serial_no):
|
||||||
@ -472,8 +484,8 @@ class update_entries_after(object):
|
|||||||
frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", 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
|
# 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"):
|
if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes':
|
||||||
doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
|
doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
|
||||||
doc.update_valuation_rate(reset_outgoing_rate=False)
|
doc.update_valuation_rate(reset_outgoing_rate=False)
|
||||||
for d in (doc.items + doc.supplied_items):
|
for d in (doc.items + doc.supplied_items):
|
||||||
d.db_update()
|
d.db_update()
|
||||||
|
@ -177,7 +177,7 @@ def get_bin(item_code, warehouse):
|
|||||||
return bin_obj
|
return bin_obj
|
||||||
|
|
||||||
def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
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:
|
if is_stock_item:
|
||||||
bin = get_bin(args.get("item_code"), args.get("warehouse"))
|
bin = get_bin(args.get("item_code"), args.get("warehouse"))
|
||||||
bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
|
bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
|
||||||
|
Loading…
Reference in New Issue
Block a user