refactor: pass typed arguments to get_item_details methods (#44230)

* refactor: pass proper types to get_item_details methods

* chore: excempt previous commit from git blame
This commit is contained in:
David Arnold
2024-11-20 02:31:03 +01:00
committed by GitHub
parent 4ec23b5525
commit 9673bf85ec
16 changed files with 286 additions and 244 deletions

View File

@@ -41,3 +41,4 @@ a308792ee7fda18a681e9181f4fd00b36385bc23
# noisy typing refactoring of get_item_details # noisy typing refactoring of get_item_details
7b7211ac79c248a79ba8a999ff34e734d874c0ae 7b7211ac79c248a79ba8a999ff34e734d874c0ae
d827ed21adc7b36047e247cbb0dc6388d048a7f9

View File

@@ -533,7 +533,11 @@ class POSInvoice(SalesInvoice):
def set_pos_fields(self, for_validate=False): def set_pos_fields(self, for_validate=False):
"""Set retail related fields from POS Profiles""" """Set retail related fields from POS Profiles"""
from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details_ from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_pos_profile,
get_pos_profile_item_details_,
)
if not self.pos_profile: if not self.pos_profile:
pos_profile = get_pos_profile(self.company) or {} pos_profile = get_pos_profile(self.company) or {}
@@ -603,7 +607,7 @@ class POSInvoice(SalesInvoice):
for item in self.get("items"): for item in self.get("items"):
if item.get("item_code"): if item.get("item_code"):
profile_details = get_pos_profile_item_details_( profile_details = get_pos_profile_item_details_(
frappe._dict(item.as_dict()), profile.get("company"), profile ItemDetailsCtx(item.as_dict()), profile.get("company"), profile
) )
for fname, val in profile_details.items(): for fname, val in profile_details.items():
if (not for_validate) or (for_validate and not item.get(fname)): if (not for_validate) or (for_validate and not item.get(fname)):

View File

@@ -766,7 +766,11 @@ class SalesInvoice(SellingController):
"Company", self.company, "default_cash_account" "Company", self.company, "default_cash_account"
) )
from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details_ from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_pos_profile,
get_pos_profile_item_details_,
)
if not self.pos_profile and not self.flags.ignore_pos_profile: if not self.pos_profile and not self.flags.ignore_pos_profile:
pos_profile = get_pos_profile(self.company) or {} pos_profile = get_pos_profile(self.company) or {}
@@ -835,7 +839,7 @@ class SalesInvoice(SellingController):
for item in self.get("items"): for item in self.get("items"):
if item.get("item_code"): if item.get("item_code"):
profile_details = get_pos_profile_item_details_( profile_details = get_pos_profile_item_details_(
frappe._dict(item.as_dict()), pos, pos, update_data=True ItemDetailsCtx(item.as_dict()), pos, pos, update_data=True
) )
for fname, val in profile_details.items(): for fname, val in profile_details.items():
if (not for_validate) or (for_validate and not item.get(fname)): if (not for_validate) or (for_validate and not item.get(fname)):

View File

@@ -26,6 +26,7 @@ from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.stock import get_warehouse_account_map from erpnext.stock import get_warehouse_account_map
from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.stock.get_item_details import ( from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_default_cost_center, get_default_cost_center,
get_default_expense_account, get_default_expense_account,
get_item_warehouse_, get_item_warehouse_,
@@ -748,7 +749,7 @@ def get_target_item_details(item_code=None, company=None):
item_group_defaults = get_item_group_defaults(item.name, company) item_group_defaults = get_item_group_defaults(item.name, company)
brand_defaults = get_brand_defaults(item.name, company) brand_defaults = get_brand_defaults(item.name, company)
out.cost_center = get_default_cost_center( out.cost_center = get_default_cost_center(
frappe._dict({"item_code": item.name, "company": company}), ItemDetailsCtx({"item_code": item.name, "company": company}),
item_defaults, item_defaults,
item_group_defaults, item_group_defaults,
brand_defaults, brand_defaults,
@@ -785,45 +786,42 @@ def get_target_asset_details(asset=None, company=None):
@frappe.whitelist() @frappe.whitelist()
def get_consumed_stock_item_details(args_): @erpnext.normalize_ctx_input(ItemDetailsCtx)
if isinstance(args_, str): def get_consumed_stock_item_details(ctx: ItemDetailsCtx):
args_ = json.loads(args_)
args = frappe._dict(args_)
out = frappe._dict() out = frappe._dict()
item = frappe._dict() item = frappe._dict()
if args.item_code: if ctx.item_code:
item = frappe.get_cached_doc("Item", args.item_code) item = frappe.get_cached_doc("Item", ctx.item_code)
out.item_name = item.item_name out.item_name = item.item_name
out.batch_no = None out.batch_no = None
out.serial_no = "" out.serial_no = ""
out.stock_qty = flt(args.stock_qty) or 1 out.stock_qty = flt(ctx.stock_qty) or 1
out.stock_uom = item.stock_uom out.stock_uom = item.stock_uom
out.warehouse = get_item_warehouse_(args, item, overwrite_warehouse=True) if item else None out.warehouse = get_item_warehouse_(ctx, item, overwrite_warehouse=True) if item else None
# Cost Center # Cost Center
item_defaults = get_item_defaults(item.name, args.company) item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, ctx.company)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults) out.cost_center = get_default_cost_center(ctx, item_defaults, item_group_defaults, brand_defaults)
if args.item_code and out.warehouse: if ctx.item_code and out.warehouse:
incoming_rate_args = frappe._dict( incoming_rate_args = frappe._dict(
{ {
"item_code": args.item_code, "item_code": ctx.item_code,
"warehouse": out.warehouse, "warehouse": out.warehouse,
"posting_date": args.posting_date, "posting_date": ctx.posting_date,
"posting_time": args.posting_time, "posting_time": ctx.posting_time,
"qty": -1 * flt(out.stock_qty), "qty": -1 * flt(out.stock_qty),
"voucher_type": args.doctype, "voucher_type": ctx.doctype,
"voucher_no": args.name, "voucher_no": ctx.name,
"company": args.company, "company": ctx.company,
"serial_no": args.serial_no, "serial_no": ctx.serial_no,
"batch_no": args.batch_no, "batch_no": ctx.batch_no,
} }
) )
out.update(get_warehouse_details(incoming_rate_args)) out.update(get_warehouse_details(incoming_rate_args))
@@ -851,31 +849,28 @@ def get_warehouse_details(args):
@frappe.whitelist() @frappe.whitelist()
def get_consumed_asset_details(args): @erpnext.normalize_ctx_input(ItemDetailsCtx)
if isinstance(args, str): def get_consumed_asset_details(ctx):
args = json.loads(args)
args = frappe._dict(args)
out = frappe._dict() out = frappe._dict()
asset_details = frappe._dict() asset_details = frappe._dict()
if args.asset: if ctx.asset:
asset_details = frappe.db.get_value( asset_details = frappe.db.get_value(
"Asset", args.asset, ["asset_name", "item_code", "item_name"], as_dict=1 "Asset", ctx.asset, ["asset_name", "item_code", "item_name"], as_dict=1
) )
if not asset_details: if not asset_details:
frappe.throw(_("Asset {0} does not exist").format(args.asset)) frappe.throw(_("Asset {0} does not exist").format(ctx.asset))
out.item_code = asset_details.item_code out.item_code = asset_details.item_code
out.asset_name = asset_details.asset_name out.asset_name = asset_details.asset_name
out.item_name = asset_details.item_name out.item_name = asset_details.item_name
if args.asset: if ctx.asset:
out.current_asset_value = flt( out.current_asset_value = flt(
get_asset_value_after_depreciation(args.asset, finance_book=args.finance_book) get_asset_value_after_depreciation(ctx.asset, finance_book=ctx.finance_book)
) )
out.asset_value = get_value_after_depreciation_on_disposal_date( out.asset_value = get_value_after_depreciation_on_disposal_date(
args.asset, args.posting_date, finance_book=args.finance_book ctx.asset, ctx.posting_date, finance_book=ctx.finance_book
) )
else: else:
out.current_asset_value = 0 out.current_asset_value = 0
@@ -884,7 +879,7 @@ def get_consumed_asset_details(args):
# Account # Account
if asset_details.item_code: if asset_details.item_code:
out.fixed_asset_account = get_asset_category_account( out.fixed_asset_account = get_asset_category_account(
"fixed_asset_account", item=asset_details.item_code, company=args.company "fixed_asset_account", item=asset_details.item_code, company=ctx.company
) )
else: else:
out.fixed_asset_account = None out.fixed_asset_account = None
@@ -892,37 +887,32 @@ def get_consumed_asset_details(args):
# Cost Center # Cost Center
if asset_details.item_code: if asset_details.item_code:
item = frappe.get_cached_doc("Item", asset_details.item_code) item = frappe.get_cached_doc("Item", asset_details.item_code)
item_defaults = get_item_defaults(item.name, args.company) item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, ctx.company)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults) out.cost_center = get_default_cost_center(ctx, item_defaults, item_group_defaults, brand_defaults)
return out return out
@frappe.whitelist() @frappe.whitelist()
def get_service_item_details(args): @erpnext.normalize_ctx_input(ItemDetailsCtx)
if isinstance(args, str): def get_service_item_details(ctx):
args = json.loads(args)
args = frappe._dict(args)
out = frappe._dict() out = frappe._dict()
item = frappe._dict() item = frappe._dict()
if args.item_code: if ctx.item_code:
item = frappe.get_cached_doc("Item", args.item_code) item = frappe.get_cached_doc("Item", ctx.item_code)
out.item_name = item.item_name out.item_name = item.item_name
out.qty = flt(args.qty) or 1 out.qty = flt(ctx.qty) or 1
out.uom = item.purchase_uom or item.stock_uom out.uom = item.purchase_uom or item.stock_uom
item_defaults = get_item_defaults(item.name, args.company) item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, ctx.company)
out.expense_account = get_default_expense_account( out.expense_account = get_default_expense_account(ctx, item_defaults, item_group_defaults, brand_defaults)
args, item_defaults, item_group_defaults, brand_defaults out.cost_center = get_default_cost_center(ctx, item_defaults, item_group_defaults, brand_defaults)
)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
return out return out

View File

@@ -62,6 +62,7 @@ from erpnext.setup.utils import get_exchange_rate
from erpnext.stock.doctype.item.item import get_uom_conv_factor from erpnext.stock.doctype.item.item import get_uom_conv_factor
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
from erpnext.stock.get_item_details import ( from erpnext.stock.get_item_details import (
ItemDetailsCtx,
_get_item_tax_template, _get_item_tax_template,
get_conversion_factor, get_conversion_factor,
get_item_details, get_item_details,
@@ -752,24 +753,28 @@ class AccountsController(TransactionBase):
for item in self.get("items"): for item in self.get("items"):
if item.get("item_code"): if item.get("item_code"):
args = parent_dict.copy() ctx: ItemDetailsCtx = ItemDetailsCtx(parent_dict.copy())
args.update(item.as_dict()) ctx.update(item.as_dict())
args["doctype"] = self.doctype ctx.update(
args["name"] = self.name {
args["child_doctype"] = item.doctype "doctype": self.doctype,
args["child_docname"] = item.name "name": self.name,
args["ignore_pricing_rule"] = ( "child_doctype": item.doctype,
self.ignore_pricing_rule if hasattr(self, "ignore_pricing_rule") else 0 "child_docname": item.name,
"ignore_pricing_rule": (
self.ignore_pricing_rule if hasattr(self, "ignore_pricing_rule") else 0
),
}
) )
if not args.get("transaction_date"): if not ctx.transaction_date:
args["transaction_date"] = args.get("posting_date") ctx.transaction_date = ctx.posting_date
if self.get("is_subcontracted"): if self.get("is_subcontracted"):
args["is_subcontracted"] = self.is_subcontracted ctx.is_subcontracted = self.is_subcontracted
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False) ret = get_item_details(ctx, self, for_validate=for_validate, overwrite_warehouse=False)
for fieldname, value in ret.items(): for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None: if item.meta.get_field(fieldname) and value is not None:
if item.get(fieldname) is None or fieldname in force_item_fields: if item.get(fieldname) is None or fieldname in force_item_fields:
@@ -3161,14 +3166,16 @@ def get_supplier_block_status(party_name):
def set_child_tax_template_and_map(item, child_item, parent_doc): def set_child_tax_template_and_map(item, child_item, parent_doc):
args = { ctx = ItemDetailsCtx(
"item_code": item.item_code, {
"posting_date": parent_doc.transaction_date, "item_code": item.item_code,
"tax_category": parent_doc.get("tax_category"), "posting_date": parent_doc.transaction_date,
"company": parent_doc.get("company"), "tax_category": parent_doc.get("tax_category"),
} "company": parent_doc.get("company"),
}
)
child_item.item_tax_template = _get_item_tax_template(args, item.taxes) child_item.item_tax_template = _get_item_tax_template(ctx, item.taxes)
if child_item.get("item_tax_template"): if child_item.get("item_tax_template"):
child_item.item_tax_rate = get_item_tax_map( child_item.item_tax_rate = get_item_tax_map(
parent_doc.get("company"), child_item.item_tax_template, as_json=True parent_doc.get("company"), child_item.item_tax_template, as_json=True

View File

@@ -14,7 +14,7 @@ from frappe.utils import nowdate, today, unique
from pypika import Order from pypika import Order
import erpnext import erpnext
from erpnext.stock.get_item_details import _get_item_tax_template from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template
# searches for active employees # searches for active employees
@@ -813,14 +813,16 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
valid_from = filters.get("valid_from") valid_from = filters.get("valid_from")
valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from
args = { ctx = ItemDetailsCtx(
"item_code": filters.get("item_code"), {
"posting_date": valid_from, "item_code": filters.get("item_code"),
"tax_category": filters.get("tax_category"), "posting_date": valid_from,
"company": company, "tax_category": filters.get("tax_category"),
} "company": company,
}
)
taxes = _get_item_tax_template(args, taxes, for_validate=True) taxes = _get_item_tax_template(ctx, taxes, for_validate=True)
return [(d,) for d in set(taxes)] return [(d,) for d in set(taxes)]

View File

@@ -18,7 +18,7 @@ from erpnext.controllers.accounts_controller import (
validate_inclusive_tax, validate_inclusive_tax,
validate_taxes_and_charges, validate_taxes_and_charges,
) )
from erpnext.stock.get_item_details import _get_item_tax_template from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template
from erpnext.utilities.regional import temporary_flag from erpnext.utilities.regional import temporary_flag
logger = frappe.logger(__name__) logger = frappe.logger(__name__)
@@ -103,15 +103,17 @@ class calculate_taxes_and_totals:
for item in self.doc.items: for item in self.doc.items:
if item.item_code and item.get("item_tax_template"): if item.item_code and item.get("item_tax_template"):
item_doc = frappe.get_cached_doc("Item", item.item_code) item_doc = frappe.get_cached_doc("Item", item.item_code)
args = { ctx = ItemDetailsCtx(
"net_rate": item.net_rate or item.rate, {
"base_net_rate": item.base_net_rate or item.base_rate, "net_rate": item.net_rate or item.rate,
"tax_category": self.doc.get("tax_category"), "base_net_rate": item.base_net_rate or item.base_rate,
"posting_date": self.doc.get("posting_date"), "tax_category": self.doc.get("tax_category"),
"bill_date": self.doc.get("bill_date"), "posting_date": self.doc.get("posting_date"),
"transaction_date": self.doc.get("transaction_date"), "bill_date": self.doc.get("bill_date"),
"company": self.doc.get("company"), "transaction_date": self.doc.get("transaction_date"),
} "company": self.doc.get("company"),
}
)
item_group = item_doc.item_group item_group = item_doc.item_group
item_group_taxes = [] item_group_taxes = []
@@ -127,7 +129,7 @@ class calculate_taxes_and_totals:
# No validation if no taxes in item or item group # No validation if no taxes in item or item group
continue continue
taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True) taxes = _get_item_tax_template(ctx, item_taxes + item_group_taxes, for_validate=True)
if taxes: if taxes:
if item.item_tax_template not in taxes: if item.item_tax_template not in taxes:

View File

@@ -16,7 +16,7 @@ from frappe.website.website_generator import WebsiteGenerator
import erpnext import erpnext
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
from erpnext.stock.doctype.item.item import get_item_details from erpnext.stock.doctype.item.item import get_item_details
from erpnext.stock.get_item_details import get_conversion_factor, get_price_list_rate from erpnext.stock.get_item_details import ItemDetailsCtx, get_conversion_factor, get_price_list_rate
form_grid_templates = {"items": "templates/form_grid/item_grid.html"} form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@@ -1052,7 +1052,7 @@ def get_bom_item_rate(args, bom_doc):
elif bom_doc.rm_cost_as_per == "Price List": elif bom_doc.rm_cost_as_per == "Price List":
if not bom_doc.buying_price_list: if not bom_doc.buying_price_list:
frappe.throw(_("Please select Price List")) frappe.throw(_("Please select Price List"))
bom_args = frappe._dict( ctx = ItemDetailsCtx(
{ {
"doctype": "BOM", "doctype": "BOM",
"price_list": bom_doc.buying_price_list, "price_list": bom_doc.buying_price_list,
@@ -1070,7 +1070,7 @@ def get_bom_item_rate(args, bom_doc):
} }
) )
item_doc = frappe.get_cached_doc("Item", args.get("item_code")) item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
price_list_data = get_price_list_rate(bom_args, item_doc) price_list_data = get_price_list_rate(ctx, item_doc)
rate = price_list_data.price_list_rate rate = price_list_data.price_list_rate
return flt(rate) return flt(rate)

View File

@@ -54,7 +54,7 @@ class TestQuotation(IntegrationTestCase):
def test_gross_profit(self): def test_gross_profit(self):
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
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.get_item_details import insert_item_price from erpnext.stock.get_item_details import ItemDetailsCtx, insert_item_price
item_doc = make_item("_Test Item for Gross Profit", {"is_stock_item": 1}) item_doc = make_item("_Test Item for Gross Profit", {"is_stock_item": 1})
item_code = item_doc.name item_code = item_doc.name
@@ -63,7 +63,7 @@ class TestQuotation(IntegrationTestCase):
selling_price_list = frappe.get_all("Price List", filters={"selling": 1}, limit=1)[0].name selling_price_list = frappe.get_all("Price List", filters={"selling": 1}, limit=1)[0].name
frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 1) frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 1)
insert_item_price( insert_item_price(
frappe._dict( ItemDetailsCtx(
{ {
"item_code": item_code, "item_code": item_code,
"price_list": selling_price_list, "price_list": selling_price_list,

View File

@@ -35,7 +35,12 @@ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry impor
get_sre_reserved_qty_details_for_voucher, get_sre_reserved_qty_details_for_voucher,
has_reserved_stock, has_reserved_stock,
) )
from erpnext.stock.get_item_details import get_bin_details, get_default_bom, get_price_list_rate from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_bin_details,
get_default_bom,
get_price_list_rate,
)
from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
form_grid_templates = {"items": "templates/form_grid/item_grid.html"} form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@@ -849,8 +854,8 @@ def make_material_request(source_name, target_doc=None):
target.item_code, target.warehouse, source_parent.company, True target.item_code, target.warehouse, source_parent.company, True
).get("actual_qty", 0) ).get("actual_qty", 0)
args = target.as_dict().copy() ctx = ItemDetailsCtx(target.as_dict().copy())
args.update( ctx.update(
{ {
"company": source_parent.get("company"), "company": source_parent.get("company"),
"price_list": frappe.db.get_single_value("Buying Settings", "buying_price_list"), "price_list": frappe.db.get_single_value("Buying Settings", "buying_price_list"),
@@ -860,7 +865,7 @@ def make_material_request(source_name, target_doc=None):
) )
target.rate = flt( target.rate = flt(
get_price_list_rate(args, item_doc=frappe.get_cached_doc("Item", target.item_code)).get( get_price_list_rate(ctx, item_doc=frappe.get_cached_doc("Item", target.item_code)).get(
"price_list_rate" "price_list_rate"
) )
) )

View File

@@ -20,7 +20,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle
get_batch_from_bundle, get_batch_from_bundle,
) )
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.get_item_details import get_item_details from erpnext.stock.get_item_details import ItemDetailsCtx, get_item_details
from erpnext.stock.serial_batch_bundle import SerialBatchCreation from erpnext.stock.serial_batch_bundle import SerialBatchCreation
@@ -436,7 +436,7 @@ class TestBatch(IntegrationTestCase):
company = "_Test Company with perpetual inventory" company = "_Test Company with perpetual inventory"
currency = frappe.get_cached_value("Company", company, "default_currency") currency = frappe.get_cached_value("Company", company, "default_currency")
args = frappe._dict( ctx = ItemDetailsCtx(
{ {
"item_code": "_Test Batch Price Item", "item_code": "_Test Batch Price Item",
"company": company, "company": company,
@@ -452,18 +452,18 @@ class TestBatch(IntegrationTestCase):
) )
# test price for batch1 # test price for batch1
args.update({"batch_no": batch1}) ctx.update({"batch_no": batch1})
details = get_item_details(args) details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 200) self.assertEqual(details.get("price_list_rate"), 200)
# test price for batch2 # test price for batch2
args.update({"batch_no": batch2}) ctx.update({"batch_no": batch2})
details = get_item_details(args) details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 300) self.assertEqual(details.get("price_list_rate"), 300)
# test price for batch3 # test price for batch3
args.update({"batch_no": batch3}) ctx.update({"batch_no": batch3})
details = get_item_details(args) details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 400) self.assertEqual(details.get("price_list_rate"), 400)
def test_basic_batch_wise_valuation(self, batch_qty=100): def test_basic_batch_wise_valuation(self, batch_qty=100):

View File

@@ -26,7 +26,7 @@ from erpnext.stock.doctype.item.item import (
validate_is_stock_item, validate_is_stock_item,
) )
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.get_item_details import get_item_details from erpnext.stock.get_item_details import ItemDetailsCtx, get_item_details
IGNORE_TEST_RECORD_DEPENDENCIES = ["BOM"] IGNORE_TEST_RECORD_DEPENDENCIES = ["BOM"]
EXTRA_TEST_RECORD_DEPENDENCIES = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] EXTRA_TEST_RECORD_DEPENDENCIES = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -145,21 +145,23 @@ class TestItem(IntegrationTestCase):
currency = frappe.get_cached_value("Company", company, "default_currency") currency = frappe.get_cached_value("Company", company, "default_currency")
details = get_item_details( details = get_item_details(
{ ItemDetailsCtx(
"item_code": "_Test Item", {
"company": company, "item_code": "_Test Item",
"price_list": "_Test Price List", "company": company,
"currency": currency, "price_list": "_Test Price List",
"doctype": "Sales Order", "currency": currency,
"conversion_rate": 1, "doctype": "Sales Order",
"price_list_currency": currency, "conversion_rate": 1,
"plc_conversion_rate": 1, "price_list_currency": currency,
"order_type": "Sales", "plc_conversion_rate": 1,
"customer": "_Test Customer", "order_type": "Sales",
"conversion_factor": 1, "customer": "_Test Customer",
"price_list_uom_dependant": 1, "conversion_factor": 1,
"ignore_pricing_rule": 1, "price_list_uom_dependant": 1,
} "ignore_pricing_rule": 1,
}
)
) )
for key, value in to_check.items(): for key, value in to_check.items():
@@ -172,23 +174,27 @@ class TestItem(IntegrationTestCase):
create_fixed_asset_item() create_fixed_asset_item()
details = get_item_details( details = get_item_details(
{ ItemDetailsCtx(
"item_code": "Macbook Pro", {
"company": "_Test Company", "item_code": "Macbook Pro",
"currency": "INR", "company": "_Test Company",
"doctype": "Purchase Receipt", "currency": "INR",
} "doctype": "Purchase Receipt",
}
)
) )
self.assertEqual(details.get("expense_account"), "_Test Fixed Asset - _TC") self.assertEqual(details.get("expense_account"), "_Test Fixed Asset - _TC")
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", "1") frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", "1")
details = get_item_details( details = get_item_details(
{ ItemDetailsCtx(
"item_code": "Macbook Pro", {
"company": "_Test Company", "item_code": "Macbook Pro",
"currency": "INR", "company": "_Test Company",
"doctype": "Purchase Receipt", "currency": "INR",
} "doctype": "Purchase Receipt",
}
)
) )
self.assertEqual(details.get("expense_account"), "CWIP Account - _TC") self.assertEqual(details.get("expense_account"), "CWIP Account - _TC")
@@ -271,22 +277,24 @@ class TestItem(IntegrationTestCase):
for data in expected_item_tax_template: for data in expected_item_tax_template:
details = get_item_details( details = get_item_details(
{ ItemDetailsCtx(
"item_code": data["item_code"], {
"tax_category": data["tax_category"], "item_code": data["item_code"],
"company": "_Test Company", "tax_category": data["tax_category"],
"price_list": "_Test Price List", "company": "_Test Company",
"currency": "_Test Currency", "price_list": "_Test Price List",
"doctype": "Sales Order", "currency": "_Test Currency",
"conversion_rate": 1, "doctype": "Sales Order",
"price_list_currency": "_Test Currency", "conversion_rate": 1,
"plc_conversion_rate": 1, "price_list_currency": "_Test Currency",
"order_type": "Sales", "plc_conversion_rate": 1,
"customer": "_Test Customer", "order_type": "Sales",
"conversion_factor": 1, "customer": "_Test Customer",
"price_list_uom_dependant": 1, "conversion_factor": 1,
"ignore_pricing_rule": 1, "price_list_uom_dependant": 1,
} "ignore_pricing_rule": 1,
}
)
) )
self.assertEqual(details.item_tax_template, data["item_tax_template"]) self.assertEqual(details.item_tax_template, data["item_tax_template"])
@@ -320,17 +328,19 @@ class TestItem(IntegrationTestCase):
"cost_center": "_Test Cost Center 2 - _TC", # from item group "cost_center": "_Test Cost Center 2 - _TC", # from item group
} }
sales_item_details = get_item_details( sales_item_details = get_item_details(
{ ItemDetailsCtx(
"item_code": "Test Item With Defaults", {
"company": "_Test Company", "item_code": "Test Item With Defaults",
"price_list": "_Test Price List", "company": "_Test Company",
"currency": "_Test Currency", "price_list": "_Test Price List",
"doctype": "Sales Invoice", "currency": "_Test Currency",
"conversion_rate": 1, "doctype": "Sales Invoice",
"price_list_currency": "_Test Currency", "conversion_rate": 1,
"plc_conversion_rate": 1, "price_list_currency": "_Test Currency",
"customer": "_Test Customer", "plc_conversion_rate": 1,
} "customer": "_Test Customer",
}
)
) )
for key, value in sales_item_check.items(): for key, value in sales_item_check.items():
self.assertEqual(value, sales_item_details.get(key)) self.assertEqual(value, sales_item_details.get(key))
@@ -343,17 +353,19 @@ class TestItem(IntegrationTestCase):
"cost_center": "_Test Write Off Cost Center - _TC", # from item "cost_center": "_Test Write Off Cost Center - _TC", # from item
} }
purchase_item_details = get_item_details( purchase_item_details = get_item_details(
{ ItemDetailsCtx(
"item_code": "Test Item With Defaults", {
"company": "_Test Company", "item_code": "Test Item With Defaults",
"price_list": "_Test Price List", "company": "_Test Company",
"currency": "_Test Currency", "price_list": "_Test Price List",
"doctype": "Purchase Invoice", "currency": "_Test Currency",
"conversion_rate": 1, "doctype": "Purchase Invoice",
"price_list_currency": "_Test Currency", "conversion_rate": 1,
"plc_conversion_rate": 1, "price_list_currency": "_Test Currency",
"supplier": "_Test Supplier", "plc_conversion_rate": 1,
} "supplier": "_Test Supplier",
}
)
) )
for key, value in purchase_item_check.items(): for key, value in purchase_item_check.items():
self.assertEqual(value, purchase_item_details.get(key)) self.assertEqual(value, purchase_item_details.get(key))

View File

@@ -7,7 +7,7 @@ from frappe.tests import IntegrationTestCase, UnitTestCase
from frappe.tests.utils import make_test_records_for_doctype from frappe.tests.utils import make_test_records_for_doctype
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
from erpnext.stock.get_item_details import get_price_list_rate_for from erpnext.stock.get_item_details import ItemDetailsCtx, get_price_list_rate_for
class UnitTestItemPrice(UnitTestCase): class UnitTestItemPrice(UnitTestCase):
@@ -80,85 +80,97 @@ class TestItemPrice(IntegrationTestCase):
# Check correct price at this quantity # Check correct price at this quantity
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"customer": doc.customer, "price_list": doc.price_list,
"uom": "_Test UOM", "customer": doc.customer,
"transaction_date": "2017-04-18", "uom": "_Test UOM",
"qty": 10, "transaction_date": "2017-04-18",
} "qty": 10,
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, 20.0) self.assertEqual(price, 20.0)
def test_price_with_no_qty(self): def test_price_with_no_qty(self):
# Check correct price when no quantity # Check correct price when no quantity
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"customer": doc.customer, "price_list": doc.price_list,
"uom": "_Test UOM", "customer": doc.customer,
"transaction_date": "2017-04-18", "uom": "_Test UOM",
} "transaction_date": "2017-04-18",
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, None) self.assertEqual(price, None)
def test_prices_at_date(self): def test_prices_at_date(self):
# Check correct price at first date # Check correct price at first date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"customer": "_Test Customer", "price_list": doc.price_list,
"uom": "_Test UOM", "customer": "_Test Customer",
"transaction_date": "2017-04-18", "uom": "_Test UOM",
"qty": 7, "transaction_date": "2017-04-18",
} "qty": 7,
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, 20) self.assertEqual(price, 20)
def test_prices_at_invalid_date(self): def test_prices_at_invalid_date(self):
# Check correct price at invalid date # Check correct price at invalid date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][3]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][3])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"qty": 7, "price_list": doc.price_list,
"uom": "_Test UOM", "qty": 7,
"transaction_date": "01-15-2019", "uom": "_Test UOM",
} "transaction_date": "01-15-2019",
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, None) self.assertEqual(price, None)
def test_prices_outside_of_date(self): def test_prices_outside_of_date(self):
# Check correct price when outside of the date # Check correct price when outside of the date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][4]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][4])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"customer": "_Test Customer", "price_list": doc.price_list,
"uom": "_Test UOM", "customer": "_Test Customer",
"transaction_date": "2017-04-25", "uom": "_Test UOM",
"qty": 7, "transaction_date": "2017-04-25",
} "qty": 7,
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, None) self.assertEqual(price, None)
def test_lowest_price_when_no_date_provided(self): def test_lowest_price_when_no_date_provided(self):
# Check lowest price when no date provided # Check lowest price when no date provided
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][1]) doc = frappe.copy_doc(self.globalTestRecords["Item Price"][1])
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"uom": "_Test UOM", "price_list": doc.price_list,
"qty": 7, "uom": "_Test UOM",
} "qty": 7,
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
self.assertEqual(price, 10) self.assertEqual(price, 10)
def test_invalid_item(self): def test_invalid_item(self):
@@ -182,14 +194,16 @@ class TestItemPrice(IntegrationTestCase):
doc.price_list_rate = 21 doc.price_list_rate = 21
doc.insert() doc.insert()
args = { ctx = ItemDetailsCtx(
"price_list": doc.price_list, {
"uom": "_Test UOM", "price_list": doc.price_list,
"transaction_date": "2017-04-18", "uom": "_Test UOM",
"qty": 7, "transaction_date": "2017-04-18",
} "qty": 7,
}
)
price = get_price_list_rate_for(args, doc.item_code) price = get_price_list_rate_for(ctx, doc.item_code)
frappe.db.rollback() frappe.db.rollback()
self.assertEqual(price, 21) self.assertEqual(price, 21)

View File

@@ -10,7 +10,7 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import flt from frappe.utils import flt
from erpnext.stock.get_item_details import get_item_details, get_price_list_rate from erpnext.stock.get_item_details import ItemDetailsCtx, get_item_details, get_price_list_rate
class PackedItem(Document): class PackedItem(Document):
@@ -243,8 +243,8 @@ def update_packed_item_price_data(pi_row, item_data, doc):
return return
item_doc = frappe.get_cached_doc("Item", pi_row.item_code) item_doc = frappe.get_cached_doc("Item", pi_row.item_code)
row_data = pi_row.as_dict().copy() ctx = ItemDetailsCtx(pi_row.as_dict().copy())
row_data.update( ctx.update(
{ {
"company": doc.get("company"), "company": doc.get("company"),
"price_list": doc.get("selling_price_list"), "price_list": doc.get("selling_price_list"),
@@ -252,10 +252,10 @@ def update_packed_item_price_data(pi_row, item_data, doc):
"conversion_rate": doc.get("conversion_rate"), "conversion_rate": doc.get("conversion_rate"),
} }
) )
if not row_data.get("transaction_date"): if not ctx.transaction_date:
row_data.update({"transaction_date": doc.get("transaction_date")}) ctx.update({"transaction_date": doc.get("transaction_date")})
rate = get_price_list_rate(row_data, item_doc).get("price_list_rate") rate = get_price_list_rate(ctx, item_doc).get("price_list_rate")
pi_row.rate = rate or item_data.get("valuation_rate") or 0.0 pi_row.rate = rate or item_data.get("valuation_rate") or 0.0
@@ -323,7 +323,7 @@ def on_doctype_update():
@frappe.whitelist() @frappe.whitelist()
def get_items_from_product_bundle(row): def get_items_from_product_bundle(row):
row, items = json.loads(row), [] row, items = ItemDetailsCtx(json.loads(row)), []
bundled_items = get_product_bundle_items(row["item_code"]) bundled_items = get_product_bundle_items(row["item_code"])
for item in bundled_items: for item in bundled_items:

View File

@@ -1618,7 +1618,7 @@ class TestPurchaseReceipt(IntegrationTestCase):
self.assertTrue(return_pi.docstatus == 1) self.assertTrue(return_pi.docstatus == 1)
def test_disable_last_purchase_rate(self): def test_disable_last_purchase_rate(self):
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import ItemDetailsCtx, get_item_details
item = make_item( item = make_item(
"_Test Disable Last Purchase Rate", "_Test Disable Last Purchase Rate",
@@ -1633,8 +1633,8 @@ class TestPurchaseReceipt(IntegrationTestCase):
item_code=item.name, item_code=item.name,
) )
args = pr.items[0].as_dict() ctx = ItemDetailsCtx(pr.items[0].as_dict())
args.update( ctx.update(
{ {
"supplier": pr.supplier, "supplier": pr.supplier,
"doctype": pr.doctype, "doctype": pr.doctype,
@@ -1646,7 +1646,7 @@ class TestPurchaseReceipt(IntegrationTestCase):
} }
) )
res = get_item_details(args) res = get_item_details(ctx)
self.assertEqual(res.get("last_purchase_rate"), 0) self.assertEqual(res.get("last_purchase_rate"), 0)
frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 0) frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 0)
@@ -1657,7 +1657,7 @@ class TestPurchaseReceipt(IntegrationTestCase):
item_code=item.name, item_code=item.name,
) )
res = get_item_details(args) res = get_item_details(ctx)
self.assertEqual(res.get("last_purchase_rate"), 100) self.assertEqual(res.get("last_purchase_rate"), 100)
def test_validate_received_qty_for_internal_pr(self): def test_validate_received_qty_for_internal_pr(self):

View File

@@ -40,6 +40,7 @@ from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
OpeningEntryAccountError, OpeningEntryAccountError,
) )
from erpnext.stock.get_item_details import ( from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_barcode_data, get_barcode_data,
get_bin_details, get_bin_details,
get_conversion_factor, get_conversion_factor,
@@ -1636,7 +1637,7 @@ class StockEntry(StockController):
pro_doc.set_actual_dates() pro_doc.set_actual_dates()
@frappe.whitelist() @frappe.whitelist()
def get_item_details(self, args=None, for_update=False): def get_item_details(self, args: ItemDetailsCtx = None, for_update=False):
item = frappe.db.sql( item = frappe.db.sql(
"""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, """select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item, i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,