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
7b7211ac79c248a79ba8a999ff34e734d874c0ae
d827ed21adc7b36047e247cbb0dc6388d048a7f9

View File

@@ -533,7 +533,11 @@ class POSInvoice(SalesInvoice):
def set_pos_fields(self, for_validate=False):
"""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:
pos_profile = get_pos_profile(self.company) or {}
@@ -603,7 +607,7 @@ class POSInvoice(SalesInvoice):
for item in self.get("items"):
if item.get("item_code"):
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():
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"
)
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:
pos_profile = get_pos_profile(self.company) or {}
@@ -835,7 +839,7 @@ class SalesInvoice(SellingController):
for item in self.get("items"):
if item.get("item_code"):
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():
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.doctype.item.item import get_item_defaults
from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_default_cost_center,
get_default_expense_account,
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)
brand_defaults = get_brand_defaults(item.name, company)
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_group_defaults,
brand_defaults,
@@ -785,45 +786,42 @@ def get_target_asset_details(asset=None, company=None):
@frappe.whitelist()
def get_consumed_stock_item_details(args_):
if isinstance(args_, str):
args_ = json.loads(args_)
args = frappe._dict(args_)
@erpnext.normalize_ctx_input(ItemDetailsCtx)
def get_consumed_stock_item_details(ctx: ItemDetailsCtx):
out = frappe._dict()
item = frappe._dict()
if args.item_code:
item = frappe.get_cached_doc("Item", args.item_code)
if ctx.item_code:
item = frappe.get_cached_doc("Item", ctx.item_code)
out.item_name = item.item_name
out.batch_no = None
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.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
item_defaults = get_item_defaults(item.name, args.company)
item_group_defaults = get_item_group_defaults(item.name, args.company)
brand_defaults = get_brand_defaults(item.name, args.company)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, ctx.company)
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(
{
"item_code": args.item_code,
"item_code": ctx.item_code,
"warehouse": out.warehouse,
"posting_date": args.posting_date,
"posting_time": args.posting_time,
"posting_date": ctx.posting_date,
"posting_time": ctx.posting_time,
"qty": -1 * flt(out.stock_qty),
"voucher_type": args.doctype,
"voucher_no": args.name,
"company": args.company,
"serial_no": args.serial_no,
"batch_no": args.batch_no,
"voucher_type": ctx.doctype,
"voucher_no": ctx.name,
"company": ctx.company,
"serial_no": ctx.serial_no,
"batch_no": ctx.batch_no,
}
)
out.update(get_warehouse_details(incoming_rate_args))
@@ -851,31 +849,28 @@ def get_warehouse_details(args):
@frappe.whitelist()
def get_consumed_asset_details(args):
if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
@erpnext.normalize_ctx_input(ItemDetailsCtx)
def get_consumed_asset_details(ctx):
out = frappe._dict()
asset_details = frappe._dict()
if args.asset:
if ctx.asset:
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:
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.asset_name = asset_details.asset_name
out.item_name = asset_details.item_name
if args.asset:
if ctx.asset:
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(
args.asset, args.posting_date, finance_book=args.finance_book
ctx.asset, ctx.posting_date, finance_book=ctx.finance_book
)
else:
out.current_asset_value = 0
@@ -884,7 +879,7 @@ def get_consumed_asset_details(args):
# Account
if asset_details.item_code:
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:
out.fixed_asset_account = None
@@ -892,37 +887,32 @@ def get_consumed_asset_details(args):
# Cost Center
if asset_details.item_code:
item = frappe.get_cached_doc("Item", asset_details.item_code)
item_defaults = get_item_defaults(item.name, args.company)
item_group_defaults = get_item_group_defaults(item.name, args.company)
brand_defaults = get_brand_defaults(item.name, args.company)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, ctx.company)
out.cost_center = get_default_cost_center(ctx, item_defaults, item_group_defaults, brand_defaults)
return out
@frappe.whitelist()
def get_service_item_details(args):
if isinstance(args, str):
args = json.loads(args)
args = frappe._dict(args)
@erpnext.normalize_ctx_input(ItemDetailsCtx)
def get_service_item_details(ctx):
out = frappe._dict()
item = frappe._dict()
if args.item_code:
item = frappe.get_cached_doc("Item", args.item_code)
if ctx.item_code:
item = frappe.get_cached_doc("Item", ctx.item_code)
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
item_defaults = get_item_defaults(item.name, args.company)
item_group_defaults = get_item_group_defaults(item.name, args.company)
brand_defaults = get_brand_defaults(item.name, args.company)
item_defaults = get_item_defaults(item.name, ctx.company)
item_group_defaults = get_item_group_defaults(item.name, ctx.company)
brand_defaults = get_brand_defaults(item.name, ctx.company)
out.expense_account = get_default_expense_account(
args, item_defaults, item_group_defaults, brand_defaults
)
out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
out.expense_account = get_default_expense_account(ctx, item_defaults, item_group_defaults, brand_defaults)
out.cost_center = get_default_cost_center(ctx, item_defaults, item_group_defaults, brand_defaults)
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.packed_item.packed_item import make_packing_list
from erpnext.stock.get_item_details import (
ItemDetailsCtx,
_get_item_tax_template,
get_conversion_factor,
get_item_details,
@@ -752,24 +753,28 @@ class AccountsController(TransactionBase):
for item in self.get("items"):
if item.get("item_code"):
args = parent_dict.copy()
args.update(item.as_dict())
ctx: ItemDetailsCtx = ItemDetailsCtx(parent_dict.copy())
ctx.update(item.as_dict())
args["doctype"] = self.doctype
args["name"] = self.name
args["child_doctype"] = item.doctype
args["child_docname"] = item.name
args["ignore_pricing_rule"] = (
self.ignore_pricing_rule if hasattr(self, "ignore_pricing_rule") else 0
ctx.update(
{
"doctype": self.doctype,
"name": self.name,
"child_doctype": item.doctype,
"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"):
args["transaction_date"] = args.get("posting_date")
if not ctx.transaction_date:
ctx.transaction_date = ctx.posting_date
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():
if item.meta.get_field(fieldname) and value is not None:
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):
args = {
"item_code": item.item_code,
"posting_date": parent_doc.transaction_date,
"tax_category": parent_doc.get("tax_category"),
"company": parent_doc.get("company"),
}
ctx = ItemDetailsCtx(
{
"item_code": item.item_code,
"posting_date": parent_doc.transaction_date,
"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"):
child_item.item_tax_rate = get_item_tax_map(
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
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
@@ -813,14 +813,16 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
valid_from = filters.get("valid_from")
valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from
args = {
"item_code": filters.get("item_code"),
"posting_date": valid_from,
"tax_category": filters.get("tax_category"),
"company": company,
}
ctx = ItemDetailsCtx(
{
"item_code": filters.get("item_code"),
"posting_date": valid_from,
"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)]

View File

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

View File

@@ -16,7 +16,7 @@ from frappe.website.website_generator import WebsiteGenerator
import erpnext
from erpnext.setup.utils import get_exchange_rate
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"}
@@ -1052,7 +1052,7 @@ def get_bom_item_rate(args, bom_doc):
elif bom_doc.rm_cost_as_per == "Price List":
if not bom_doc.buying_price_list:
frappe.throw(_("Please select Price List"))
bom_args = frappe._dict(
ctx = ItemDetailsCtx(
{
"doctype": "BOM",
"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"))
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
return flt(rate)

View File

@@ -54,7 +54,7 @@ class TestQuotation(IntegrationTestCase):
def test_gross_profit(self):
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.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_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
frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 1)
insert_item_price(
frappe._dict(
ItemDetailsCtx(
{
"item_code": item_code,
"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,
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
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
).get("actual_qty", 0)
args = target.as_dict().copy()
args.update(
ctx = ItemDetailsCtx(target.as_dict().copy())
ctx.update(
{
"company": source_parent.get("company"),
"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(
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"
)
)

View File

@@ -20,7 +20,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle
get_batch_from_bundle,
)
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
@@ -436,7 +436,7 @@ class TestBatch(IntegrationTestCase):
company = "_Test Company with perpetual inventory"
currency = frappe.get_cached_value("Company", company, "default_currency")
args = frappe._dict(
ctx = ItemDetailsCtx(
{
"item_code": "_Test Batch Price Item",
"company": company,
@@ -452,18 +452,18 @@ class TestBatch(IntegrationTestCase):
)
# test price for batch1
args.update({"batch_no": batch1})
details = get_item_details(args)
ctx.update({"batch_no": batch1})
details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 200)
# test price for batch2
args.update({"batch_no": batch2})
details = get_item_details(args)
ctx.update({"batch_no": batch2})
details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 300)
# test price for batch3
args.update({"batch_no": batch3})
details = get_item_details(args)
ctx.update({"batch_no": batch3})
details = get_item_details(ctx)
self.assertEqual(details.get("price_list_rate"), 400)
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,
)
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"]
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")
details = get_item_details(
{
"item_code": "_Test Item",
"company": company,
"price_list": "_Test Price List",
"currency": currency,
"doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": currency,
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"conversion_factor": 1,
"price_list_uom_dependant": 1,
"ignore_pricing_rule": 1,
}
ItemDetailsCtx(
{
"item_code": "_Test Item",
"company": company,
"price_list": "_Test Price List",
"currency": currency,
"doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": currency,
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"conversion_factor": 1,
"price_list_uom_dependant": 1,
"ignore_pricing_rule": 1,
}
)
)
for key, value in to_check.items():
@@ -172,23 +174,27 @@ class TestItem(IntegrationTestCase):
create_fixed_asset_item()
details = get_item_details(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
ItemDetailsCtx(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
)
)
self.assertEqual(details.get("expense_account"), "_Test Fixed Asset - _TC")
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", "1")
details = get_item_details(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
ItemDetailsCtx(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
)
)
self.assertEqual(details.get("expense_account"), "CWIP Account - _TC")
@@ -271,22 +277,24 @@ class TestItem(IntegrationTestCase):
for data in expected_item_tax_template:
details = get_item_details(
{
"item_code": data["item_code"],
"tax_category": data["tax_category"],
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"conversion_factor": 1,
"price_list_uom_dependant": 1,
"ignore_pricing_rule": 1,
}
ItemDetailsCtx(
{
"item_code": data["item_code"],
"tax_category": data["tax_category"],
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
"conversion_factor": 1,
"price_list_uom_dependant": 1,
"ignore_pricing_rule": 1,
}
)
)
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
}
sales_item_details = get_item_details(
{
"item_code": "Test Item With Defaults",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"customer": "_Test Customer",
}
ItemDetailsCtx(
{
"item_code": "Test Item With Defaults",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"customer": "_Test Customer",
}
)
)
for key, value in sales_item_check.items():
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
}
purchase_item_details = get_item_details(
{
"item_code": "Test Item With Defaults",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Purchase Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"supplier": "_Test Supplier",
}
ItemDetailsCtx(
{
"item_code": "Test Item With Defaults",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Purchase Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"supplier": "_Test Supplier",
}
)
)
for key, value in purchase_item_check.items():
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 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):
@@ -80,85 +80,97 @@ class TestItemPrice(IntegrationTestCase):
# Check correct price at this quantity
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = {
"price_list": doc.price_list,
"customer": doc.customer,
"uom": "_Test UOM",
"transaction_date": "2017-04-18",
"qty": 10,
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"customer": doc.customer,
"uom": "_Test UOM",
"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)
def test_price_with_no_qty(self):
# Check correct price when no quantity
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = {
"price_list": doc.price_list,
"customer": doc.customer,
"uom": "_Test UOM",
"transaction_date": "2017-04-18",
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"customer": doc.customer,
"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)
def test_prices_at_date(self):
# Check correct price at first date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][2])
args = {
"price_list": doc.price_list,
"customer": "_Test Customer",
"uom": "_Test UOM",
"transaction_date": "2017-04-18",
"qty": 7,
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"customer": "_Test Customer",
"uom": "_Test UOM",
"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)
def test_prices_at_invalid_date(self):
# Check correct price at invalid date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][3])
args = {
"price_list": doc.price_list,
"qty": 7,
"uom": "_Test UOM",
"transaction_date": "01-15-2019",
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"qty": 7,
"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)
def test_prices_outside_of_date(self):
# Check correct price when outside of the date
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][4])
args = {
"price_list": doc.price_list,
"customer": "_Test Customer",
"uom": "_Test UOM",
"transaction_date": "2017-04-25",
"qty": 7,
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"customer": "_Test Customer",
"uom": "_Test UOM",
"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)
def test_lowest_price_when_no_date_provided(self):
# Check lowest price when no date provided
doc = frappe.copy_doc(self.globalTestRecords["Item Price"][1])
args = {
"price_list": doc.price_list,
"uom": "_Test UOM",
"qty": 7,
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"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)
def test_invalid_item(self):
@@ -182,14 +194,16 @@ class TestItemPrice(IntegrationTestCase):
doc.price_list_rate = 21
doc.insert()
args = {
"price_list": doc.price_list,
"uom": "_Test UOM",
"transaction_date": "2017-04-18",
"qty": 7,
}
ctx = ItemDetailsCtx(
{
"price_list": doc.price_list,
"uom": "_Test UOM",
"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()
self.assertEqual(price, 21)

View File

@@ -10,7 +10,7 @@ import frappe
from frappe.model.document import Document
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):
@@ -243,8 +243,8 @@ def update_packed_item_price_data(pi_row, item_data, doc):
return
item_doc = frappe.get_cached_doc("Item", pi_row.item_code)
row_data = pi_row.as_dict().copy()
row_data.update(
ctx = ItemDetailsCtx(pi_row.as_dict().copy())
ctx.update(
{
"company": doc.get("company"),
"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"),
}
)
if not row_data.get("transaction_date"):
row_data.update({"transaction_date": doc.get("transaction_date")})
if not ctx.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
@@ -323,7 +323,7 @@ def on_doctype_update():
@frappe.whitelist()
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"])
for item in bundled_items:

View File

@@ -1618,7 +1618,7 @@ class TestPurchaseReceipt(IntegrationTestCase):
self.assertTrue(return_pi.docstatus == 1)
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(
"_Test Disable Last Purchase Rate",
@@ -1633,8 +1633,8 @@ class TestPurchaseReceipt(IntegrationTestCase):
item_code=item.name,
)
args = pr.items[0].as_dict()
args.update(
ctx = ItemDetailsCtx(pr.items[0].as_dict())
ctx.update(
{
"supplier": pr.supplier,
"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)
frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 0)
@@ -1657,7 +1657,7 @@ class TestPurchaseReceipt(IntegrationTestCase):
item_code=item.name,
)
res = get_item_details(args)
res = get_item_details(ctx)
self.assertEqual(res.get("last_purchase_rate"), 100)
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,
)
from erpnext.stock.get_item_details import (
ItemDetailsCtx,
get_barcode_data,
get_bin_details,
get_conversion_factor,
@@ -1636,7 +1637,7 @@ class StockEntry(StockController):
pro_doc.set_actual_dates()
@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(
"""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,