Skip to content

Commit

Permalink
perf: new column posting datetime in SLE to optimize stock ledger rel…
Browse files Browse the repository at this point in the history
…ated queries
  • Loading branch information
rohitwaghchaure committed Feb 13, 2024
1 parent 1d1cb86 commit d80ca52
Show file tree
Hide file tree
Showing 19 changed files with 216 additions and 130 deletions.
2 changes: 1 addition & 1 deletion erpnext/accounts/report/gross_profit/gross_profit.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ def get_stock_ledger_entries(self, item_code, warehouse):
& (sle.is_cancelled == 0)
)
.orderby(sle.item_code)
.orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
.orderby(sle.warehouse, sle.posting_datetime, sle.creation, order=Order.desc)
.run(as_dict=True)
)

Expand Down
43 changes: 1 addition & 42 deletions erpnext/accounts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,46 +982,6 @@ def get_currency_precision():
return precision


def get_stock_rbnb_difference(posting_date, company):
stock_items = frappe.db.sql_list(
"""select distinct item_code
from `tabStock Ledger Entry` where company=%s""",
company,
)

pr_valuation_amount = frappe.db.sql(
"""
select sum(pr_item.valuation_rate * pr_item.qty * pr_item.conversion_factor)
from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
where pr.name = pr_item.parent and pr.docstatus=1 and pr.company=%s
and pr.posting_date <= %s and pr_item.item_code in (%s)"""
% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
tuple([company, posting_date] + stock_items),
)[0][0]

pi_valuation_amount = frappe.db.sql(
"""
select sum(pi_item.valuation_rate * pi_item.qty * pi_item.conversion_factor)
from `tabPurchase Invoice Item` pi_item, `tabPurchase Invoice` pi
where pi.name = pi_item.parent and pi.docstatus=1 and pi.company=%s
and pi.posting_date <= %s and pi_item.item_code in (%s)"""
% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
tuple([company, posting_date] + stock_items),
)[0][0]

# Balance should be
stock_rbnb = flt(pr_valuation_amount, 2) - flt(pi_valuation_amount, 2)

# Balance as per system
stock_rbnb_account = "Stock Received But Not Billed - " + frappe.get_cached_value(
"Company", company, "abbr"
)
sys_bal = get_balance_on(stock_rbnb_account, posting_date, in_account_currency=False)

# Amount should be credited
return flt(stock_rbnb) + flt(sys_bal)


def get_held_invoices(party_type, party):
"""
Returns a list of names Purchase Invoices for the given party that are on hold
Expand Down Expand Up @@ -1428,8 +1388,7 @@ def sort_stock_vouchers_by_posting_date(
.select(sle.voucher_type, sle.voucher_no, sle.posting_date, sle.posting_time, sle.creation)
.where((sle.is_cancelled == 0) & (sle.voucher_no.isin(voucher_nos)))
.groupby(sle.voucher_type, sle.voucher_no)
.orderby(sle.posting_date)
.orderby(sle.posting_time)
.orderby(sle.posting_datetime)
.orderby(sle.creation)
).run(as_dict=True)
sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]
Expand Down
3 changes: 1 addition & 2 deletions erpnext/manufacturing/doctype/bom/bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,8 +1071,7 @@ def get_valuation_rate(data):
frappe.qb.from_(sle)
.select(sle.valuation_rate)
.where((sle.item_code == item_code) & (sle.valuation_rate > 0) & (sle.is_cancelled == 0))
.orderby(sle.posting_date, order=frappe.qb.desc)
.orderby(sle.posting_time, order=frappe.qb.desc)
.orderby(sle.posting_datetime, order=frappe.qb.desc)
.orderby(sle.creation, order=frappe.qb.desc)
.limit(1)
).run(as_dict=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get_data(filters):
query_filters["creation"] = ("between", [filters.get("from_date"), filters.get("to_date")])

data = frappe.get_all(
"Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc", debug=1
"Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc"
)

res = []
Expand Down
1 change: 1 addition & 0 deletions erpnext/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,4 @@ erpnext.patches.v15_0.create_advance_payment_status
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
erpnext.patches.v14_0.set_maintain_stock_for_bom_item
erpnext.patches.v14_0.update_posting_datetime_and_dropped_indexes
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import frappe


def execute():
frappe.db.sql(
"""
UPDATE `tabStock Ledger Entry`
SET posting_datetime = timestamp(posting_date, posting_time)
"""
)

drop_indexes()


def drop_indexes():
if not frappe.db.has_index("tabStock Ledger Entry", "posting_sort_index"):
return

frappe.db.sql_ddl("ALTER TABLE `tabStock Ledger Entry` DROP INDEX `posting_sort_index`")
89 changes: 89 additions & 0 deletions erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2317,6 +2317,95 @@ def test_use_serial_batch_fields_for_serial_nos(self):
serial_no_status = frappe.db.get_value("Serial No", sn, "status")
self.assertTrue(serial_no_status != "Active")

def test_sle_qty_after_transaction(self):
item = make_item(
"_Test Item Qty After Transaction",
properties={"is_stock_item": 1, "valuation_method": "FIFO"},
).name

posting_date = today()
posting_time = nowtime()

# Step 1: Create Purchase Receipt
pr = make_purchase_receipt(
item_code=item,
qty=1,
rate=100,
posting_date=posting_date,
posting_time=posting_time,
do_not_save=1,
)

for i in range(9):
pr.append(
"items",
{
"item_code": item,
"qty": 1,
"rate": 100,
"warehouse": pr.items[0].warehouse,
"cost_center": pr.items[0].cost_center,
"expense_account": pr.items[0].expense_account,
"uom": pr.items[0].uom,
"stock_uom": pr.items[0].stock_uom,
"conversion_factor": pr.items[0].conversion_factor,
},
)

self.assertEqual(len(pr.items), 10)
pr.save()
pr.submit()

data = frappe.get_all(
"Stock Ledger Entry",
fields=["qty_after_transaction", "creation", "posting_datetime"],
filters={"voucher_no": pr.name, "is_cancelled": 0},
order_by="creation",
)

for index, d in enumerate(data):
self.assertEqual(d.qty_after_transaction, 1 + index)

# Step 2: Create Purchase Receipt
pr = make_purchase_receipt(
item_code=item,
qty=1,
rate=100,
posting_date=posting_date,
posting_time=posting_time,
do_not_save=1,
)

for i in range(9):
pr.append(
"items",
{
"item_code": item,
"qty": 1,
"rate": 100,
"warehouse": pr.items[0].warehouse,
"cost_center": pr.items[0].cost_center,
"expense_account": pr.items[0].expense_account,
"uom": pr.items[0].uom,
"stock_uom": pr.items[0].stock_uom,
"conversion_factor": pr.items[0].conversion_factor,
},
)

self.assertEqual(len(pr.items), 10)
pr.save()
pr.submit()

data = frappe.get_all(
"Stock Ledger Entry",
fields=["qty_after_transaction", "creation", "posting_datetime"],
filters={"voucher_no": pr.name, "is_cancelled": 0},
order_by="creation",
)

for index, d in enumerate(data):
self.assertEqual(d.qty_after_transaction, 11 + index)


def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
from frappe.utils import flt, nowtime, today

from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
Expand Down Expand Up @@ -191,6 +191,7 @@ def test_old_batch_valuation(self):
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.submit()
doc.reload()

bundle_doc = make_serial_batch_bundle(
{
Expand Down
8 changes: 6 additions & 2 deletions erpnext/stock/doctype/stock_entry/stock_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,7 @@ def add_batchwise_finished_good(self, batches, args, item):
return

id = create_serial_and_batch_bundle(
self,
row,
frappe._dict(
{
Expand Down Expand Up @@ -2169,7 +2170,7 @@ def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
"to_warehouse": "",
"qty": qty,
"item_name": item.item_name,
"serial_and_batch_bundle": create_serial_and_batch_bundle(row, item, "Outward"),
"serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward"),
"description": item.description,
"stock_uom": item.stock_uom,
"expense_account": item.expense_account,
Expand Down Expand Up @@ -2547,6 +2548,7 @@ def set_serial_no_batch_for_finished_good(self):
row = frappe._dict({"serial_nos": serial_nos[0 : cint(d.qty)]})

id = create_serial_and_batch_bundle(
self,
row,
frappe._dict(
{
Expand Down Expand Up @@ -3070,7 +3072,7 @@ def get_stock_entry_data(work_order):
return data


def create_serial_and_batch_bundle(row, child, type_of_transaction=None):
def create_serial_and_batch_bundle(parent_doc, row, child, type_of_transaction=None):
item_details = frappe.get_cached_value(
"Item", child.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
)
Expand All @@ -3088,6 +3090,8 @@ def create_serial_and_batch_bundle(row, child, type_of_transaction=None):
"item_code": child.item_code,
"warehouse": child.warehouse,
"type_of_transaction": type_of_transaction,
"posting_date": parent_doc.posting_date,
"posting_time": parent_doc.posting_time,
}
)

Expand Down
10 changes: 7 additions & 3 deletions erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"warehouse",
"posting_date",
"posting_time",
"posting_datetime",
"is_adjustment_entry",
"auto_created_serial_and_batch_bundle",
"column_break_6",
Expand Down Expand Up @@ -100,7 +101,6 @@
"oldfieldtype": "Date",
"print_width": "100px",
"read_only": 1,
"search_index": 1,
"width": "100px"
},
{
Expand Down Expand Up @@ -253,7 +253,6 @@
"options": "Company",
"print_width": "150px",
"read_only": 1,
"search_index": 1,
"width": "150px"
},
{
Expand Down Expand Up @@ -348,6 +347,11 @@
"fieldname": "auto_created_serial_and_batch_bundle",
"fieldtype": "Check",
"label": "Auto Created Serial and Batch Bundle"
},
{
"fieldname": "posting_datetime",
"fieldtype": "Datetime",
"label": "Posting Datetime"
}
],
"hide_toolbar": 1,
Expand All @@ -356,7 +360,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-14 16:47:39.791967",
"modified": "2024-02-07 09:18:13.999231",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
Expand Down
12 changes: 9 additions & 3 deletions erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class StockLedgerEntry(Document):
item_code: DF.Link | None
outgoing_rate: DF.Currency
posting_date: DF.Date | None
posting_datetime: DF.Datetime | None
posting_time: DF.Time | None
project: DF.Link | None
qty_after_transaction: DF.Float
Expand Down Expand Up @@ -92,6 +93,12 @@ def validate(self):
self.validate_with_last_transaction_posting_time()
self.validate_inventory_dimension_negative_stock()

def set_posting_datetime(self):
from erpnext.stock.utils import get_combine_datetime

self.posting_datetime = get_combine_datetime(self.posting_date, self.posting_time)
self.db_set("posting_datetime", self.posting_datetime)

def validate_inventory_dimension_negative_stock(self):
if self.is_cancelled:
return
Expand Down Expand Up @@ -162,6 +169,7 @@ def _get_inventory_dimensions(self):
return inv_dimension_dict

def on_submit(self):
self.set_posting_datetime()
self.check_stock_frozen_date()

# Added to handle few test cases where serial_and_batch_bundles are not required
Expand Down Expand Up @@ -332,9 +340,7 @@ def on_cancel(self):


def on_doctype_update():
frappe.db.add_index(
"Stock Ledger Entry", fields=["posting_date", "posting_time"], index_name="posting_sort_index"
)
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse")
frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"])
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ def ordered_qty_after_transaction():
frappe.qb.from_(sle)
.select("qty_after_transaction")
.where((sle.item_code == item) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0))
.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
.orderby(sle.posting_datetime)
.orderby(sle.creation)
).run(pluck=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from frappe import _
from frappe.model.document import Document
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt
from frappe.utils import cint, flt, nowdate, nowtime

from erpnext.stock.utils import get_or_make_bin, get_stock_balance

Expand Down Expand Up @@ -866,6 +866,8 @@ def get_ssb_bundle_for_voucher(sre: dict) -> object:
bundle = frappe.new_doc("Serial and Batch Bundle")
bundle.type_of_transaction = "Outward"
bundle.voucher_type = "Delivery Note"
bundle.posting_date = nowdate()
bundle.posting_time = nowtime()

for field in ("item_code", "warehouse", "has_serial_no", "has_batch_no"):
setattr(bundle, field, sre[field])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.query_builder import Field
from frappe.query_builder.functions import CombineDatetime, Min
from frappe.query_builder.functions import Min
from frappe.utils import add_days, getdate, today

import erpnext
Expand Down Expand Up @@ -75,7 +75,7 @@ def get_data(report_filters):
& (sle.company == report_filters.company)
& (sle.is_cancelled == 0)
)
.orderby(CombineDatetime(sle.posting_date, sle.posting_time), sle.creation)
.orderby(sle.posting_datetime, sle.creation)
).run(as_dict=True)

for d in data:
Expand Down
Loading

0 comments on commit d80ca52

Please sign in to comment.