fix: change frappe.db.sql to frappe.qb

This commit is contained in:
Rohit Waghchaure 2022-07-15 15:43:38 +05:30
parent e576f7f07e
commit 289e6cd4ce
7 changed files with 253 additions and 185 deletions

View File

@ -372,7 +372,8 @@ class StockController(AccountsController):
return sl_dict return sl_dict
def update_inventory_dimensions(self, row, sl_dict) -> None: def update_inventory_dimensions(self, row, sl_dict) -> None:
dimension = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self) dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self)
for dimension in dimensions:
if dimension and row.get(dimension.source_fieldname): if dimension and row.get(dimension.source_fieldname):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname) sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)

View File

@ -85,7 +85,7 @@
"default": "0", "default": "0",
"fieldname": "apply_to_all_doctypes", "fieldname": "apply_to_all_doctypes",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Apply to All Document Types" "label": "Apply to All Inventory Document Types"
}, },
{ {
"default": "0", "default": "0",
@ -144,7 +144,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2022-07-05 15:33:37.270373", "modified": "2022-07-19 21:06:11.824976",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Inventory Dimension", "name": "Inventory Dimension",

View File

@ -11,6 +11,14 @@ class DoNotChangeError(frappe.ValidationError):
pass pass
class CanNotBeChildDoc(frappe.ValidationError):
pass
class CanNotBeDefaultDimension(frappe.ValidationError):
pass
class InventoryDimension(Document): class InventoryDimension(Document):
def onload(self): def onload(self):
if not self.is_new() and frappe.db.has_column("Stock Ledger Entry", self.target_fieldname): if not self.is_new() and frappe.db.has_column("Stock Ledger Entry", self.target_fieldname):
@ -51,11 +59,11 @@ class InventoryDimension(Document):
def validate_reference_document(self): def validate_reference_document(self):
if frappe.get_cached_value("DocType", self.reference_document, "istable") == 1: if frappe.get_cached_value("DocType", self.reference_document, "istable") == 1:
msg = f"The reference document {self.reference_document} can not be child table." msg = f"The reference document {self.reference_document} can not be child table."
frappe.throw(_(msg)) frappe.throw(_(msg), CanNotBeChildDoc)
if self.reference_document in ["Batch", "Serial No", "Warehouse", "Item"]: if self.reference_document in ["Batch", "Serial No", "Warehouse", "Item"]:
msg = f"The reference document {self.reference_document} can not be an Inventory Dimension." msg = f"The reference document {self.reference_document} can not be an Inventory Dimension."
frappe.throw(_(msg)) frappe.throw(_(msg), CanNotBeDefaultDimension)
def set_source_and_target_fieldname(self) -> None: def set_source_and_target_fieldname(self) -> None:
if not self.source_fieldname: if not self.source_fieldname:
@ -122,8 +130,9 @@ def get_inventory_documents(
) )
def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None) -> dict: def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None):
dimensions = get_document_wise_inventory_dimensions(doc.doctype) dimensions = get_document_wise_inventory_dimensions(doc.doctype)
filter_dimensions = []
for row in dimensions: for row in dimensions:
if ( if (
row.type_of_transaction == "Inward" row.type_of_transaction == "Inward"
@ -142,8 +151,12 @@ def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None) -> dict:
if parent_doc: if parent_doc:
evals["parent"] = parent_doc evals["parent"] = parent_doc
if frappe.safe_eval(row.condition, evals): if row.condition and frappe.safe_eval(row.condition, evals):
return row filter_dimensions.append(row)
else:
filter_dimensions.append(row)
return filter_dimensions
def get_document_wise_inventory_dimensions(doctype) -> dict: def get_document_wise_inventory_dimensions(doctype) -> dict:

View File

@ -4,6 +4,11 @@
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
CanNotBeChildDoc,
CanNotBeDefaultDimension,
DoNotChangeError,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@ -12,11 +17,36 @@ class TestInventoryDimension(FrappeTestCase):
def setUp(self): def setUp(self):
prepare_test_data() prepare_test_data()
def test_validate_inventory_dimension(self):
# Can not be child doc
inv_dim1 = create_inventory_dimension(
reference_document="Stock Entry Detail",
type_of_transaction="Outward",
dimension_name="Stock Entry",
apply_to_all_doctypes=0,
istable=0,
document_type="Stock Entry",
do_not_save=True,
)
self.assertRaises(CanNotBeChildDoc, inv_dim1.insert)
inv_dim1 = create_inventory_dimension(
reference_document="Batch",
type_of_transaction="Outward",
dimension_name="Batch",
apply_to_all_doctypes=0,
document_type="Stock Entry Detail",
do_not_save=True,
)
self.assertRaises(CanNotBeDefaultDimension, inv_dim1.insert)
def test_inventory_dimension(self): def test_inventory_dimension(self):
warehouse = "Shelf Warehouse - _TC" warehouse = "Shelf Warehouse - _TC"
item_code = "_Test Item" item_code = "_Test Item"
create_inventory_dimension( inv_dim1 = create_inventory_dimension(
reference_document="Shelf", reference_document="Shelf",
type_of_transaction="Outward", type_of_transaction="Outward",
dimension_name="Shelf", dimension_name="Shelf",
@ -47,7 +77,6 @@ class TestInventoryDimension(FrappeTestCase):
inward.save() inward.save()
inward.submit() inward.submit()
inward.load_from_db() inward.load_from_db()
print(inward.name)
sle_data = frappe.db.get_value( sle_data = frappe.db.get_value(
"Stock Ledger Entry", {"voucher_no": inward.name}, ["shelf", "warehouse"], as_dict=1 "Stock Ledger Entry", {"voucher_no": inward.name}, ["shelf", "warehouse"], as_dict=1
@ -74,6 +103,12 @@ class TestInventoryDimension(FrappeTestCase):
sle_shelf = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": outward.name}, "shelf") sle_shelf = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": outward.name}, "shelf")
self.assertEqual(sle_shelf, "Shelf 1") self.assertEqual(sle_shelf, "Shelf 1")
inv_dim1.load_from_db()
inv_dim1.apply_to_all_doctypes = 1
self.assertTrue(inv_dim1.has_stock_ledger())
self.assertRaises(DoNotChangeError, inv_dim1.save)
def prepare_test_data(): def prepare_test_data():
if not frappe.db.exists("DocType", "Shelf"): if not frappe.db.exists("DocType", "Shelf"):
@ -107,6 +142,8 @@ def create_inventory_dimension(**args):
doc = frappe.new_doc("Inventory Dimension") doc = frappe.new_doc("Inventory Dimension")
doc.update(args) doc.update(args)
if not args.do_not_save:
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True)
return doc return doc

View File

@ -478,10 +478,10 @@ class StockEntry(StockController):
if not d.s_warehouse: if not d.s_warehouse:
frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx)) frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx))
if ( if cstr(d.s_warehouse) == cstr(d.t_warehouse) and self.purpose not in [
cstr(d.s_warehouse) == cstr(d.t_warehouse) "Material Transfer for Manufacture",
and not self.purpose == "Material Transfer for Manufacture" "Material Transfer",
): ]:
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx)) frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
if not (d.s_warehouse or d.t_warehouse): if not (d.s_warehouse or d.t_warehouse):

View File

@ -329,7 +329,7 @@ def get_stock_ledger_entries(filters: StockBalanceFilter, items: List[str]) -> L
query = query.where(sle.item_code.isin(items)) query = query.where(sle.item_code.isin(items))
query = apply_conditions(query, filters) query = apply_conditions(query, filters)
return query.run(as_dict=True, debug=1) return query.run(as_dict=True)
def get_inventory_dimension_fields(): def get_inventory_dimension_fields():

View File

@ -4,7 +4,9 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.query_builder.functions import CombineDatetime
from frappe.utils import cint, flt from frappe.utils import cint, flt
from pypika.terms import ExistsCriterion
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@ -70,7 +72,7 @@ def update_available_serial_nos(available_serial_nos, sle):
key = (sle.item_code, sle.warehouse) key = (sle.item_code, sle.warehouse)
if key not in available_serial_nos: if key not in available_serial_nos:
stock_balance = get_stock_balance_for( stock_balance = get_stock_balance_for(
sle.item_code, sle.warehouse, sle.date.split(" ")[0], sle.date.split(" ")[1] sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time
) )
serials = get_serial_nos(stock_balance["serial_nos"]) if stock_balance["serial_nos"] else [] serials = get_serial_nos(stock_balance["serial_nos"]) if stock_balance["serial_nos"] else []
available_serial_nos.setdefault(key, serials) available_serial_nos.setdefault(key, serials)
@ -109,6 +111,21 @@ def get_columns(filters):
"options": "UOM", "options": "UOM",
"width": 90, "width": 90,
}, },
]
for dimension in get_inventory_dimensions():
columns.append(
{
"label": _(dimension.doctype),
"fieldname": dimension.fieldname,
"fieldtype": "Link",
"options": dimension.doctype,
"width": 110,
}
)
columns.extend(
[
{ {
"label": _("In Qty"), "label": _("In Qty"),
"fieldname": "in_qty", "fieldname": "in_qty",
@ -219,85 +236,85 @@ def get_columns(filters):
"options": "Project", "options": "Project",
"width": 100, "width": 100,
}, },
]
for dimension in get_inventory_dimensions():
columns.append(
{
"label": _(dimension.doctype),
"fieldname": dimension.fieldname,
"fieldtype": "Link",
"options": dimension.doctype,
"width": 110,
}
)
columns.append(
{ {
"label": _("Company"), "label": _("Company"),
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"options": "Company", "options": "Company",
"width": 110, "width": 110,
} },
]
) )
return columns return columns
def get_stock_ledger_entries(filters, items): def get_stock_ledger_entries(filters, items):
item_conditions_sql = "" sle = frappe.qb.DocType("Stock Ledger Entry")
query = (
frappe.qb.from_(sle)
.select(
sle.item_code,
CombineDatetime(sle.posting_date, sle.posting_time).as_("date"),
sle.warehouse,
sle.posting_date,
sle.posting_time,
sle.actual_qty,
sle.incoming_rate,
sle.valuation_rate,
sle.company,
sle.voucher_type,
sle.qty_after_transaction,
sle.stock_value_difference,
sle.voucher_no,
sle.stock_value,
sle.batch_no,
sle.serial_no,
sle.project,
)
.where(
(sle.docstatus < 2)
& (sle.is_cancelled == 0)
& (sle.posting_date[filters.from_date : filters.to_date])
)
.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
.orderby(sle.creation)
)
inventory_dimension_fields = get_inventory_dimension_fields()
if inventory_dimension_fields:
for fieldname in inventory_dimension_fields:
query = query.select(fieldname)
if fieldname in filters and filters.get(fieldname):
query = query.where(sle[fieldname].isin(filters.get(fieldname)))
if items: if items:
item_conditions_sql = "and sle.item_code in ({})".format( query = query.where(sle.item_code.isin(items))
", ".join(frappe.db.escape(i) for i in items)
for field in ["voucher_no", "batch_no", "project", "company"]:
if filters.get(field):
query = query.where(sle[field] == filters.get(field))
if warehouse := filters.get("warehouse"):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
warehouse_table = frappe.qb.DocType("Warehouse")
chilren_subquery = (
frappe.qb.from_(warehouse_table)
.select(warehouse_table.name)
.where(
(warehouse_table.lft >= lft)
& (warehouse_table.rgt <= rgt)
& (warehouse_table.name == sle.warehouse)
) )
sl_entries = frappe.db.sql(
"""
SELECT
concat_ws(' ', posting_date, posting_time) AS date,
item_code,
warehouse,
actual_qty,
qty_after_transaction,
incoming_rate,
valuation_rate,
stock_value,
voucher_type,
voucher_no,
batch_no,
serial_no,
company,
project,
stock_value_difference {get_dimension_fields}
FROM
`tabStock Ledger Entry` sle
WHERE
company = %(company)s
AND is_cancelled = 0 AND posting_date BETWEEN %(from_date)s AND %(to_date)s
{sle_conditions}
{item_conditions_sql}
ORDER BY
posting_date asc, posting_time asc, creation asc
""".format(
sle_conditions=get_sle_conditions(filters),
item_conditions_sql=item_conditions_sql,
get_dimension_fields=get_dimension_fields(),
),
filters,
as_dict=1,
) )
query = query.where(ExistsCriterion(chilren_subquery))
return sl_entries return query.run(as_dict=True)
def get_dimension_fields() -> str: def get_inventory_dimension_fields():
fields = "" return [dimension.fieldname for dimension in get_inventory_dimensions()]
for dimension in get_inventory_dimensions():
fields += f", {dimension.fieldname}"
return fields
def get_items(filters): def get_items(filters):
@ -395,7 +412,7 @@ def get_opening_balance(filters, columns, sl_entries):
for sle in sl_entries: for sle in sl_entries:
if ( if (
sle.get("voucher_type") == "Stock Reconciliation" sle.get("voucher_type") == "Stock Reconciliation"
and sle.get("date").split()[0] == filters.from_date and sle.posting_date == filters.from_date
and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock" and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock"
): ):
last_entry = sle last_entry = sle