mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-19 04:59:18 +00:00
Merge branch 'develop' into asset_bill_date_new
This commit is contained in:
@@ -107,7 +107,7 @@ class Budget(Document):
|
|||||||
self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###"
|
self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###"
|
||||||
|
|
||||||
|
|
||||||
def validate_expense_against_budget(args):
|
def validate_expense_against_budget(args, expense_amount=0):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
if args.get("company") and not args.fiscal_year:
|
if args.get("company") and not args.fiscal_year:
|
||||||
@@ -175,13 +175,13 @@ def validate_expense_against_budget(args):
|
|||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
if budget_records:
|
if budget_records:
|
||||||
validate_budget_records(args, budget_records)
|
validate_budget_records(args, budget_records, expense_amount)
|
||||||
|
|
||||||
|
|
||||||
def validate_budget_records(args, budget_records):
|
def validate_budget_records(args, budget_records, expense_amount):
|
||||||
for budget in budget_records:
|
for budget in budget_records:
|
||||||
if flt(budget.budget_amount):
|
if flt(budget.budget_amount):
|
||||||
amount = get_amount(args, budget)
|
amount = expense_amount or get_amount(args, budget)
|
||||||
yearly_action, monthly_action = get_actions(args, budget)
|
yearly_action, monthly_action = get_actions(args, budget)
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
|
|||||||
@@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
jv.cancel()
|
jv.cancel()
|
||||||
|
|
||||||
|
def test_monthly_budget_against_main_cost_center(self):
|
||||||
|
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||||
|
from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import (
|
||||||
|
create_cost_center_allocation,
|
||||||
|
)
|
||||||
|
|
||||||
|
cost_centers = [
|
||||||
|
"Main Budget Cost Center 1",
|
||||||
|
"Sub Budget Cost Center 1",
|
||||||
|
"Sub Budget Cost Center 2",
|
||||||
|
]
|
||||||
|
|
||||||
|
for cc in cost_centers:
|
||||||
|
create_cost_center(cost_center_name=cc, company="_Test Company")
|
||||||
|
|
||||||
|
create_cost_center_allocation(
|
||||||
|
"_Test Company",
|
||||||
|
"Main Budget Cost Center 1 - _TC",
|
||||||
|
{"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40},
|
||||||
|
)
|
||||||
|
|
||||||
|
make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC")
|
||||||
|
|
||||||
|
jv = make_journal_entry(
|
||||||
|
"_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC",
|
||||||
|
400000,
|
||||||
|
"Main Budget Cost Center 1 - _TC",
|
||||||
|
posting_date=nowdate(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv.submit)
|
||||||
|
|
||||||
|
|
||||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||||
if budget_against_field == "project":
|
if budget_against_field == "project":
|
||||||
|
|||||||
@@ -52,7 +52,10 @@
|
|||||||
"free_item_rate",
|
"free_item_rate",
|
||||||
"column_break_42",
|
"column_break_42",
|
||||||
"free_item_uom",
|
"free_item_uom",
|
||||||
|
"round_free_qty",
|
||||||
"is_recursive",
|
"is_recursive",
|
||||||
|
"recurse_for",
|
||||||
|
"apply_recursion_over",
|
||||||
"section_break_23",
|
"section_break_23",
|
||||||
"valid_from",
|
"valid_from",
|
||||||
"valid_upto",
|
"valid_upto",
|
||||||
@@ -578,12 +581,34 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Naming Series",
|
"label": "Naming Series",
|
||||||
"options": "PRLE-.####"
|
"options": "PRLE-.####"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "round_free_qty",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Round Free Qty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "is_recursive",
|
||||||
|
"description": "Give free item for every N quantity",
|
||||||
|
"fieldname": "recurse_for",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Recurse Every (As Per Transaction UOM)",
|
||||||
|
"mandatory_depends_on": "is_recursive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "is_recursive",
|
||||||
|
"description": "Qty for which recursion isn't applicable.",
|
||||||
|
"fieldname": "apply_recursion_over",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Apply Recursion Over (As Per Transaction UOM)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-09-16 16:00:38.356266",
|
"modified": "2022-10-13 19:05:35.056304",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class PricingRule(Document):
|
|||||||
self.validate_applicable_for_selling_or_buying()
|
self.validate_applicable_for_selling_or_buying()
|
||||||
self.validate_min_max_amt()
|
self.validate_min_max_amt()
|
||||||
self.validate_min_max_qty()
|
self.validate_min_max_qty()
|
||||||
|
self.validate_recursion()
|
||||||
self.cleanup_fields_value()
|
self.cleanup_fields_value()
|
||||||
self.validate_rate_or_discount()
|
self.validate_rate_or_discount()
|
||||||
self.validate_max_discount()
|
self.validate_max_discount()
|
||||||
@@ -109,6 +110,18 @@ class PricingRule(Document):
|
|||||||
if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt):
|
if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt):
|
||||||
throw(_("Min Amt can not be greater than Max Amt"))
|
throw(_("Min Amt can not be greater than Max Amt"))
|
||||||
|
|
||||||
|
def validate_recursion(self):
|
||||||
|
if self.price_or_product_discount != "Product":
|
||||||
|
return
|
||||||
|
if self.free_item or self.same_item:
|
||||||
|
if flt(self.recurse_for) <= 0:
|
||||||
|
self.recurse_for = 1
|
||||||
|
if self.is_recursive:
|
||||||
|
if flt(self.apply_recursion_over) > flt(self.min_qty):
|
||||||
|
throw(_("Min Qty should be greater than Recurse Over Qty"))
|
||||||
|
if flt(self.apply_recursion_over) < 0:
|
||||||
|
throw(_("Recurse Over Qty cannot be less than 0"))
|
||||||
|
|
||||||
def cleanup_fields_value(self):
|
def cleanup_fields_value(self):
|
||||||
for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]:
|
for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]:
|
||||||
fieldname = frappe.scrub(self.get(logic_field) or "")
|
fieldname = frappe.scrub(self.get(logic_field) or "")
|
||||||
|
|||||||
@@ -710,6 +710,132 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
|
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
|
def test_item_group_price_with_blank_uom_pricing_rule(self):
|
||||||
|
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group")
|
||||||
|
group.save()
|
||||||
|
properties = {
|
||||||
|
"item_code": "Item with Group Blank UOM",
|
||||||
|
"item_group": "_Test Pricing Rule Item Group",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"sales_uom": "Box",
|
||||||
|
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||||
|
}
|
||||||
|
item = make_item(properties=properties)
|
||||||
|
|
||||||
|
make_item_price("Item with Group Blank UOM", "_Test Price List", 100)
|
||||||
|
|
||||||
|
pricing_rule_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Item with Group Blank UOM Rule",
|
||||||
|
"apply_on": "Item Group",
|
||||||
|
"item_groups": [
|
||||||
|
{
|
||||||
|
"item_group": "_Test Pricing Rule Item Group",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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 with Group 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 with Group 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 with Group Blank UOM"}).delete()
|
||||||
|
item.delete()
|
||||||
|
group.delete()
|
||||||
|
|
||||||
|
def test_item_group_price_with_selling_uom_pricing_rule(self):
|
||||||
|
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group UOM")
|
||||||
|
group.save()
|
||||||
|
properties = {
|
||||||
|
"item_code": "Item with Group UOM other than Stock",
|
||||||
|
"item_group": "_Test Pricing Rule Item Group UOM",
|
||||||
|
"stock_uom": "Nos",
|
||||||
|
"sales_uom": "Box",
|
||||||
|
"uoms": [dict(uom="Box", conversion_factor=10)],
|
||||||
|
}
|
||||||
|
item = make_item(properties=properties)
|
||||||
|
|
||||||
|
make_item_price("Item with Group UOM other than Stock", "_Test Price List", 100)
|
||||||
|
|
||||||
|
pricing_rule_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Item with Group UOM other than Stock Rule",
|
||||||
|
"apply_on": "Item Group",
|
||||||
|
"item_groups": [
|
||||||
|
{
|
||||||
|
"item_group": "_Test Pricing Rule Item Group UOM",
|
||||||
|
"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 with Group 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 with Group 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 with Group UOM other than Stock"}).delete()
|
||||||
|
item.delete()
|
||||||
|
group.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")
|
||||||
|
|
||||||
@@ -943,6 +1069,45 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
si.delete()
|
si.delete()
|
||||||
rule.delete()
|
rule.delete()
|
||||||
|
|
||||||
|
def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
test_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Pricing Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"currency": "USD",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selling": 1,
|
||||||
|
"rate": 0,
|
||||||
|
"min_qty": 3,
|
||||||
|
"max_qty": 7,
|
||||||
|
"price_or_product_discount": "Product",
|
||||||
|
"same_item": 1,
|
||||||
|
"free_qty": 1,
|
||||||
|
"round_free_qty": 1,
|
||||||
|
"is_recursive": 1,
|
||||||
|
"recurse_for": 2,
|
||||||
|
"company": "_Test Company",
|
||||||
|
}
|
||||||
|
frappe.get_doc(test_record.copy()).insert()
|
||||||
|
|
||||||
|
# With pricing rule
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=5)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||||
|
self.assertEqual(so.items[1].qty, 2)
|
||||||
|
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=7)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||||
|
self.assertEqual(so.items[1].qty, 4)
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Campaign"]
|
test_dependencies = ["Campaign"]
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,12 @@ def _get_pricing_rules(apply_on, args, values):
|
|||||||
values["variant_of"] = args.variant_of
|
values["variant_of"] = args.variant_of
|
||||||
elif apply_on_field == "item_group":
|
elif apply_on_field == "item_group":
|
||||||
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
|
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
conditions += get_other_conditions(conditions, values, args)
|
conditions += get_other_conditions(conditions, values, args)
|
||||||
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
|
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
|
||||||
@@ -627,9 +633,13 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
|||||||
|
|
||||||
qty = pricing_rule.free_qty or 1
|
qty = pricing_rule.free_qty or 1
|
||||||
if pricing_rule.is_recursive:
|
if pricing_rule.is_recursive:
|
||||||
transaction_qty = args.get("qty") if args else doc.total_qty
|
transaction_qty = (
|
||||||
|
args.get("qty") if args else doc.total_qty
|
||||||
|
) - pricing_rule.apply_recursion_over
|
||||||
if transaction_qty:
|
if transaction_qty:
|
||||||
qty = flt(transaction_qty) * qty
|
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
|
||||||
|
if pricing_rule.round_free_qty:
|
||||||
|
qty = round(qty)
|
||||||
|
|
||||||
free_item_data_args = {
|
free_item_data_args = {
|
||||||
"item_code": free_item,
|
"item_code": free_item,
|
||||||
|
|||||||
@@ -128,6 +128,12 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
|
|||||||
new_gl_map = []
|
new_gl_map = []
|
||||||
for d in gl_map:
|
for d in gl_map:
|
||||||
cost_center = d.get("cost_center")
|
cost_center = d.get("cost_center")
|
||||||
|
|
||||||
|
# Validate budget against main cost center
|
||||||
|
validate_expense_against_budget(
|
||||||
|
d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
|
||||||
|
)
|
||||||
|
|
||||||
if cost_center and cost_center_allocation.get(cost_center):
|
if cost_center and cost_center_allocation.get(cost_center):
|
||||||
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
|
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
|
||||||
gle = copy.deepcopy(d)
|
gle = copy.deepcopy(d)
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ class TestAsset(AssetSetup):
|
|||||||
asset.precision("gross_purchase_amount"),
|
asset.precision("gross_purchase_amount"),
|
||||||
)
|
)
|
||||||
pro_rata_amount, _, _ = asset.get_pro_rata_amt(
|
pro_rata_amount, _, _ = asset.get_pro_rata_amt(
|
||||||
asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
|
asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date
|
||||||
)
|
)
|
||||||
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
|
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
|
||||||
self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount)
|
self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount)
|
||||||
@@ -283,7 +283,7 @@ class TestAsset(AssetSetup):
|
|||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
|
||||||
|
|
||||||
pro_rata_amount, _, _ = asset.get_pro_rata_amt(
|
pro_rata_amount, _, _ = asset.get_pro_rata_amt(
|
||||||
asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
|
asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date
|
||||||
)
|
)
|
||||||
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
|
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ frappe.ui.form.on("Purchase Order", {
|
|||||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
return erpnext.queries.warehouse(frm.doc);
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// On cancel and amending a purchase order with advance payment, reset advance paid amount
|
||||||
|
if (frm.is_new()) {
|
||||||
|
frm.set_value("advance_paid", 0)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
apply_tds: function(frm) {
|
apply_tds: function(frm) {
|
||||||
|
|||||||
@@ -1404,7 +1404,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
if (!r.exc && r.message) {
|
if (!r.exc && r.message) {
|
||||||
me._set_values_for_item_list(r.message);
|
me._set_values_for_item_list(r.message);
|
||||||
if(item) me.set_gross_profit(item);
|
if(item) me.set_gross_profit(item);
|
||||||
if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on")
|
if (me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1577,6 +1577,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
for (let key in pr_row) {
|
for (let key in pr_row) {
|
||||||
row_to_modify[key] = pr_row[key];
|
row_to_modify[key] = pr_row[key];
|
||||||
}
|
}
|
||||||
|
this.frm.script_manager.copy_from_first_row("items", row_to_modify, ["expense_account", "income_account"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// free_item_data is a temporary variable
|
// free_item_data is a temporary variable
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
return query;
|
return query;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// On cancel and amending a sales order with advance payment, reset advance paid amount
|
||||||
|
if (frm.is_new()) {
|
||||||
|
frm.set_value("advance_paid", 0)
|
||||||
|
}
|
||||||
|
|
||||||
frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
|
frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -121,18 +121,24 @@ class InventoryDimension(Document):
|
|||||||
|
|
||||||
if self.apply_to_all_doctypes:
|
if self.apply_to_all_doctypes:
|
||||||
for doctype in get_inventory_documents():
|
for doctype in get_inventory_documents():
|
||||||
custom_fields.setdefault(doctype[0], dimension_fields)
|
if not field_exists(doctype[0], self.source_fieldname):
|
||||||
else:
|
custom_fields.setdefault(doctype[0], dimension_fields)
|
||||||
|
elif not field_exists(self.document_type, self.source_fieldname):
|
||||||
custom_fields.setdefault(self.document_type, dimension_fields)
|
custom_fields.setdefault(self.document_type, dimension_fields)
|
||||||
|
|
||||||
if not frappe.db.get_value(
|
if not frappe.db.get_value(
|
||||||
"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
|
"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
|
||||||
):
|
) and not field_exists("Stock Ledger Entry", self.target_fieldname):
|
||||||
dimension_field = dimension_fields[1]
|
dimension_field = dimension_fields[1]
|
||||||
dimension_field["fieldname"] = self.target_fieldname
|
dimension_field["fieldname"] = self.target_fieldname
|
||||||
custom_fields["Stock Ledger Entry"] = dimension_field
|
custom_fields["Stock Ledger Entry"] = dimension_field
|
||||||
|
|
||||||
create_custom_fields(custom_fields)
|
if custom_fields:
|
||||||
|
create_custom_fields(custom_fields)
|
||||||
|
|
||||||
|
|
||||||
|
def field_exists(doctype, fieldname) -> str or None:
|
||||||
|
return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name")
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -191,6 +191,21 @@ class TestInventoryDimension(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(sle_rack, "Rack 1")
|
self.assertEqual(sle_rack, "Rack 1")
|
||||||
|
|
||||||
|
def test_check_standard_dimensions(self):
|
||||||
|
create_inventory_dimension(
|
||||||
|
reference_document="Project",
|
||||||
|
type_of_transaction="Outward",
|
||||||
|
dimension_name="Project",
|
||||||
|
apply_to_all_doctypes=0,
|
||||||
|
document_type="Stock Ledger Entry",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertFalse(
|
||||||
|
frappe.db.get_value(
|
||||||
|
"Custom Field", {"fieldname": "project", "dt": "Stock Ledger Entry"}, "name"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def prepare_test_data():
|
def prepare_test_data():
|
||||||
if not frappe.db.exists("DocType", "Shelf"):
|
if not frappe.db.exists("DocType", "Shelf"):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user