fix: pricing rule for non stock UOM and conversions
* fix: pricing rule for non stock UOM and conversions
This commit is contained in:
parent
f08c42e920
commit
96b4211ea1
@ -268,6 +268,18 @@ def get_serial_no_for_item(args):
|
|||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
|
|
||||||
|
def update_pricing_rule_uom(pricing_rule, args):
|
||||||
|
child_doc = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}.get(
|
||||||
|
pricing_rule.apply_on
|
||||||
|
)
|
||||||
|
|
||||||
|
apply_on_field = frappe.scrub(pricing_rule.apply_on)
|
||||||
|
|
||||||
|
for row in pricing_rule.get(child_doc):
|
||||||
|
if row.get(apply_on_field) == args.get(apply_on_field):
|
||||||
|
pricing_rule.uom = row.uom
|
||||||
|
|
||||||
|
|
||||||
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (
|
from erpnext.accounts.doctype.pricing_rule.utils import (
|
||||||
get_applied_pricing_rules,
|
get_applied_pricing_rules,
|
||||||
@ -324,6 +336,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
|
|
||||||
if isinstance(pricing_rule, str):
|
if isinstance(pricing_rule, str):
|
||||||
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
||||||
|
update_pricing_rule_uom(pricing_rule, args)
|
||||||
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
|
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
|
||||||
|
|
||||||
if pricing_rule.get("suggestion"):
|
if pricing_rule.get("suggestion"):
|
||||||
@ -440,12 +453,15 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
|||||||
if pricing_rule.currency == args.currency:
|
if pricing_rule.currency == args.currency:
|
||||||
pricing_rule_rate = pricing_rule.rate
|
pricing_rule_rate = pricing_rule.rate
|
||||||
|
|
||||||
|
# TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way.
|
||||||
if pricing_rule_rate:
|
if pricing_rule_rate:
|
||||||
|
is_blank_uom = pricing_rule.get("uom") != args.get("uom")
|
||||||
# Override already set price list rate (from item price)
|
# Override already set price list rate (from item price)
|
||||||
# if pricing_rule_rate > 0
|
# if pricing_rule_rate > 0
|
||||||
item_details.update(
|
item_details.update(
|
||||||
{
|
{
|
||||||
"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
|
"price_list_rate": pricing_rule_rate
|
||||||
|
* (args.get("conversion_factor", 1) if is_blank_uom else 1),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
item_details.update({"discount_percentage": 0.0})
|
item_details.update({"discount_percentage": 0.0})
|
||||||
|
|||||||
@ -595,6 +595,121 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
|
def test_item_price_with_blank_uom_pricing_rule(self):
|
||||||
|
properties = {
|
||||||
|
"item_code": "Item Blank UOM",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"sales_uom": "Box",
|
||||||
|
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||||
|
}
|
||||||
|
item = make_item(properties=properties)
|
||||||
|
|
||||||
|
make_item_price("Item Blank UOM", "_Test Price List", 100)
|
||||||
|
|
||||||
|
pricing_rule_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Item Blank UOM Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "Item Blank UOM",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selling": 1,
|
||||||
|
"currency": "INR",
|
||||||
|
"rate_or_discount": "Rate",
|
||||||
|
"rate": 101,
|
||||||
|
"company": "_Test Company",
|
||||||
|
}
|
||||||
|
rule = frappe.get_doc(pricing_rule_record)
|
||||||
|
rule.insert()
|
||||||
|
|
||||||
|
si = create_sales_invoice(
|
||||||
|
do_not_save=True, item_code="Item Blank UOM", uom="Box", conversion_factor=10
|
||||||
|
)
|
||||||
|
si.selling_price_list = "_Test Price List"
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
# If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM.
|
||||||
|
# rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010
|
||||||
|
self.assertEqual(si.items[0].price_list_rate, 1010)
|
||||||
|
self.assertEqual(si.items[0].rate, 1010)
|
||||||
|
|
||||||
|
si.delete()
|
||||||
|
|
||||||
|
si = create_sales_invoice(do_not_save=True, item_code="Item Blank UOM", uom="Nos")
|
||||||
|
si.selling_price_list = "_Test Price List"
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
# UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM.
|
||||||
|
# rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101
|
||||||
|
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||||
|
self.assertEqual(si.items[0].rate, 101)
|
||||||
|
|
||||||
|
si.delete()
|
||||||
|
rule.delete()
|
||||||
|
frappe.get_doc("Item Price", {"item_code": "Item Blank UOM"}).delete()
|
||||||
|
|
||||||
|
item.delete()
|
||||||
|
|
||||||
|
def test_item_price_with_selling_uom_pricing_rule(self):
|
||||||
|
properties = {
|
||||||
|
"item_code": "Item UOM other than Stock",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"sales_uom": "Box",
|
||||||
|
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||||
|
}
|
||||||
|
item = make_item(properties=properties)
|
||||||
|
|
||||||
|
make_item_price("Item UOM other than Stock", "_Test Price List", 100)
|
||||||
|
|
||||||
|
pricing_rule_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Item UOM other than Stock Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "Item UOM other than Stock",
|
||||||
|
"uom": "Box",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selling": 1,
|
||||||
|
"currency": "INR",
|
||||||
|
"rate_or_discount": "Rate",
|
||||||
|
"rate": 101,
|
||||||
|
"company": "_Test Company",
|
||||||
|
}
|
||||||
|
rule = frappe.get_doc(pricing_rule_record)
|
||||||
|
rule.insert()
|
||||||
|
|
||||||
|
si = create_sales_invoice(
|
||||||
|
do_not_save=True, item_code="Item UOM other than Stock", uom="Box", conversion_factor=10
|
||||||
|
)
|
||||||
|
si.selling_price_list = "_Test Price List"
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
# UOM is Box so apply pricing_rule only on Box UOM.
|
||||||
|
# Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor.
|
||||||
|
self.assertEqual(si.items[0].price_list_rate, 101)
|
||||||
|
self.assertEqual(si.items[0].rate, 101)
|
||||||
|
|
||||||
|
si.delete()
|
||||||
|
|
||||||
|
si = create_sales_invoice(do_not_save=True, item_code="Item UOM other than Stock", uom="Nos")
|
||||||
|
si.selling_price_list = "_Test Price List"
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
# UOM is Box so pricing_rule won't apply as selling_uom is Nos.
|
||||||
|
# As Pricing Rule is not applied price of 100 will be fetched from Item Price List.
|
||||||
|
self.assertEqual(si.items[0].price_list_rate, 100)
|
||||||
|
self.assertEqual(si.items[0].rate, 100)
|
||||||
|
|
||||||
|
si.delete()
|
||||||
|
rule.delete()
|
||||||
|
frappe.get_doc("Item Price", {"item_code": "Item UOM other than Stock"}).delete()
|
||||||
|
|
||||||
|
item.delete()
|
||||||
|
|
||||||
def test_pricing_rule_for_different_currency(self):
|
def test_pricing_rule_for_different_currency(self):
|
||||||
make_item("Test Sanitizer Item")
|
make_item("Test Sanitizer Item")
|
||||||
|
|
||||||
|
|||||||
@ -111,6 +111,12 @@ def _get_pricing_rules(apply_on, args, values):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if apply_on_field == "item_code":
|
if apply_on_field == "item_code":
|
||||||
|
if args.get("uom", None):
|
||||||
|
item_conditions += (
|
||||||
|
" and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
|
||||||
|
child_doc=child_doc, item_uom=args.get("uom")
|
||||||
|
)
|
||||||
|
)
|
||||||
if "variant_of" not in args:
|
if "variant_of" not in args:
|
||||||
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
|
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
|
||||||
|
|
||||||
|
|||||||
@ -3320,7 +3320,7 @@ def create_sales_invoice(**args):
|
|||||||
"asset": args.asset or None,
|
"asset": args.asset or None,
|
||||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||||
"serial_no": args.serial_no,
|
"serial_no": args.serial_no,
|
||||||
"conversion_factor": 1,
|
"conversion_factor": args.get("conversion_factor", 1),
|
||||||
"incoming_rate": args.incoming_rate or 0,
|
"incoming_rate": args.incoming_rate or 0,
|
||||||
"batch_no": args.batch_no or None,
|
"batch_no": args.batch_no or None,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -426,6 +426,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
if(!this.validate_company_and_party()) {
|
if(!this.validate_company_and_party()) {
|
||||||
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
|
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
|
||||||
} else {
|
} else {
|
||||||
|
item.pricing_rules = ''
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
method: "erpnext.stock.get_item_details.get_item_details",
|
method: "erpnext.stock.get_item_details.get_item_details",
|
||||||
child: item,
|
child: item,
|
||||||
@ -1045,6 +1046,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
uom(doc, cdt, cdn) {
|
uom(doc, cdt, cdn) {
|
||||||
var me = this;
|
var me = this;
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
item.pricing_rules = ''
|
||||||
if(item.item_code && item.uom) {
|
if(item.item_code && item.uom) {
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
method: "erpnext.stock.get_item_details.get_conversion_factor",
|
method: "erpnext.stock.get_item_details.get_conversion_factor",
|
||||||
@ -1121,6 +1123,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
|
|
||||||
qty(doc, cdt, cdn) {
|
qty(doc, cdt, cdn) {
|
||||||
let item = frappe.get_doc(cdt, cdn);
|
let item = frappe.get_doc(cdt, cdn);
|
||||||
|
item.pricing_rules = ''
|
||||||
this.conversion_factor(doc, cdt, cdn, true);
|
this.conversion_factor(doc, cdt, cdn, true);
|
||||||
this.calculate_stock_uom_rate(doc, cdt, cdn);
|
this.calculate_stock_uom_rate(doc, cdt, cdn);
|
||||||
this.apply_pricing_rule(item, true);
|
this.apply_pricing_rule(item, true);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user