Merge branch 'account_stock_sync_error' of https://github.com/marination/erpnext into marination-account_stock_sync_error
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 221e3a7..d6236cd 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -398,7 +398,7 @@
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"account_type": (doc.voucher_type=="Bank Entry" ?
- "Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
+ "Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
"company": doc.company
},
callback: function(r) {
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 1776275..430dce7 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -181,8 +181,9 @@
item_details.serial_no = get_serial_no(args)
return item_details
-def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
- from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules
+def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
+ from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
+ get_applied_pricing_rules, get_pricing_rule_items)
if isinstance(doc, string_types):
doc = json.loads(doc)
@@ -209,6 +210,55 @@
item_details, args.get('item_code'))
return item_details
+ update_args_for_pricing_rule(args)
+
+ pricing_rules = (get_applied_pricing_rules(args)
+ if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
+
+ if pricing_rules:
+ rules = []
+
+ for pricing_rule in pricing_rules:
+ if not pricing_rule: continue
+
+ if isinstance(pricing_rule, string_types):
+ pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
+ pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
+
+ if pricing_rule.get('suggestion'): continue
+
+ item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
+ item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount")
+
+ rules.append(get_pricing_rule_details(args, pricing_rule))
+
+ if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
+ item_details.update({
+ 'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
+ 'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
+ if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
+ })
+
+ if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
+ return item_details
+
+ if (not pricing_rule.validate_applied_rule and
+ pricing_rule.price_or_product_discount == "Price"):
+ apply_price_discount_pricing_rule(pricing_rule, item_details, args)
+
+ item_details.has_pricing_rule = 1
+
+ item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
+
+ if not doc: return item_details
+
+ elif args.get("pricing_rules"):
+ item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
+ item_details, args.get('item_code'))
+
+ return item_details
+
+def update_args_for_pricing_rule(args):
if not (args.item_group and args.brand):
try:
args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
@@ -235,52 +285,12 @@
args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group")
args.customer = args.customer_group = args.territory = None
- pricing_rules = get_pricing_rules(args, doc)
-
- if pricing_rules:
- rules = []
-
- for pricing_rule in pricing_rules:
- if not pricing_rule or pricing_rule.get('suggestion'): continue
-
- item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
-
- rules.append(get_pricing_rule_details(args, pricing_rule))
- if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
- continue
-
- if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
- return item_details
-
- if (not pricing_rule.validate_applied_rule and
- pricing_rule.price_or_product_discount == "Price"):
- apply_price_discount_pricing_rule(pricing_rule, item_details, args)
-
- item_details.has_pricing_rule = 1
-
- # if discount is applied on the rate and not on price list rate
- # if price_list_rate:
- # set_discount_amount(price_list_rate, item_details)
-
- item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
-
- if not doc: return item_details
-
- for rule in rules:
- doc.append('pricing_rules', rule)
-
- elif args.get("pricing_rules"):
- item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
- item_details, args.get('item_code'))
-
- return item_details
-
def get_pricing_rule_details(args, pricing_rule):
return frappe._dict({
'pricing_rule': pricing_rule.name,
'rate_or_discount': pricing_rule.rate_or_discount,
'margin_type': pricing_rule.margin_type,
- 'item_code': pricing_rule.item_code or args.get("item_code"),
+ 'item_code': args.get("item_code"),
'child_docname': args.get('child_docname')
})
@@ -327,10 +337,10 @@
item_details.rate = rate
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
- from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items
+ from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
for d in pricing_rules.split(','):
if not d or not frappe.db.exists("Pricing Rule", d): continue
- pricing_rule = frappe.get_doc('Pricing Rule', d)
+ pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
if pricing_rule.price_or_product_discount == 'Price':
if pricing_rule.rate_or_discount == 'Discount Percentage':
@@ -348,8 +358,9 @@
else pricing_rule.get('free_item'))
if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
- apply_on, items = get_apply_on_and_items(pricing_rule, item_details)
- item_details.apply_on = apply_on
+ items = get_pricing_rule_items(pricing_rule)
+ item_details.apply_on = (frappe.scrub(pricing_rule.apply_rule_on_other)
+ if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
item_details.applied_on_items = ','.join(items)
item_details.pricing_rules = ''
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index ef26c2e..637e503 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -8,6 +8,7 @@
from frappe import throw, _
from six import string_types
from frappe.utils import flt, cint, get_datetime
+from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
@@ -173,10 +174,11 @@
if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
- pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name)
+ pr_doc = frappe.get_cached_doc('Pricing Rule', pricing_rules[0].name)
if pricing_rules[0].mixed_conditions and doc:
- stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
+ stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
+ pricing_rules[0].apply_rule_on_other_items = items
elif pricing_rules[0].is_cumulative:
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
@@ -339,17 +341,19 @@
sum_qty += data[0]
sum_amt += data[1]
- return sum_qty, sum_amt
+ return sum_qty, sum_amt, items
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
- for d in get_pricing_rule_items(pr_doc):
- for row in doc.items:
- if d == row.get(frappe.scrub(pr_doc.apply_on)):
- pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
- row.get("amount"), pricing_rules, row)
+ items = get_pricing_rule_items(pr_doc)
- if pricing_rules and pricing_rules[0]:
- return pricing_rules
+ for row in doc.items:
+ if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items:
+ pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
+ row.get("amount"), pricing_rules, row)
+
+ if pricing_rules and pricing_rules[0]:
+ pricing_rules[0].apply_rule_on_other_items = items
+ return pricing_rules
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
sum_qty, sum_amt = [0, 0]
@@ -397,31 +401,7 @@
return [sum_qty, sum_amt]
-def validate_pricing_rules(doc):
- validate_pricing_rule_on_transactions(doc)
-
- for d in doc.items:
- validate_pricing_rule_on_items(doc, d)
-
- doc.calculate_taxes_and_totals()
-
-def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False):
- value = 0
- for pricing_rule in get_applied_pricing_rules(doc, item_row):
- pr_doc = frappe.get_doc('Pricing Rule', pricing_rule)
-
- if pr_doc.get('apply_on') == 'Transaction': continue
-
- if pr_doc.get('price_or_product_discount') == 'Product':
- apply_pricing_rule_for_free_items(doc, pr_doc)
- else:
- for field in ['discount_percentage', 'discount_amount', 'rate']:
- if not pr_doc.get(field): continue
-
- value += pr_doc.get(field)
- apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate)
-
-def validate_pricing_rule_on_transactions(doc):
+def apply_pricing_rule_on_transaction(doc):
conditions = "apply_on = 'Transaction'"
values = {}
@@ -453,7 +433,7 @@
elif d.price_or_product_discount == 'Product':
apply_pricing_rule_for_free_items(doc, d)
-def get_applied_pricing_rules(doc, item_row):
+def get_applied_pricing_rules(item_row):
return (item_row.get("pricing_rules").split(',')
if item_row.get("pricing_rules") else [])
@@ -468,70 +448,29 @@
'item_code': pricing_rule.get('free_item'),
'qty': pricing_rule.get('free_qty'),
'uom': pricing_rule.get('free_item_uom'),
- 'rate': pricing_rule.get('free_item_rate'),
+ 'rate': pricing_rule.get('free_item_rate') or 0,
'is_free_item': 1
})
doc.set_missing_values()
-def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False):
- apply_on, items = get_apply_on_and_items(pr_doc, item_row)
-
- rule_applied = {}
-
- for item in doc.get("items"):
- if item.get(apply_on) in items:
- if not item.pricing_rules:
- item.pricing_rules = item_row.pricing_rules
-
- for field in ['discount_percentage', 'discount_amount', 'rate']:
- if not pr_doc.get(field): continue
-
- key = (item.name, item.pricing_rules)
- if not pr_doc.validate_applied_rule:
- rule_applied[key] = 1
- item.set(field, value)
- elif item.get(field) < value:
- if not do_not_validate and item.idx == item_row.idx:
- rule_applied[key] = 0
- frappe.msgprint(_("Row {0}: user has not applied rule <b>{1}</b> on the item <b>{2}</b>")
- .format(item.idx, pr_doc.title, item.item_code))
-
- if rule_applied and doc.get("pricing_rules"):
- for d in doc.get("pricing_rules"):
- key = (d.child_docname, d.pricing_rule)
- if key in rule_applied:
- d.rule_applied = 1
-
-def get_apply_on_and_items(pr_doc, item_row):
- # for mixed or other items conditions
- apply_on = frappe.scrub(pr_doc.get('apply_on'))
- items = (get_pricing_rule_items(pr_doc)
- if pr_doc.mixed_conditions else [item_row.get(apply_on)])
-
- if pr_doc.apply_rule_on_other:
- apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
- items = [pr_doc.get(apply_on)]
-
- return apply_on, items
-
def get_pricing_rule_items(pr_doc):
+ apply_on_data = []
apply_on = frappe.scrub(pr_doc.get('apply_on'))
pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
- return [item.get(apply_on) for item in pr_doc.get(pricing_rule_apply_on)] or []
+ for d in pr_doc.get(pricing_rule_apply_on):
+ if apply_on == 'item_group':
+ get_child_item_groups(d.get(apply_on))
+ else:
+ apply_on_data.append(d.get(apply_on))
-@frappe.whitelist()
-def validate_pricing_rule_for_different_cond(doc):
- if isinstance(doc, string_types):
- doc = json.loads(doc)
+ if pr_doc.apply_rule_on_other:
+ apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
+ apply_on_data.append(pr_doc.get(apply_on))
- doc = frappe.get_doc(doc)
- for d in doc.get("items"):
- validate_pricing_rule_on_items(doc, d, True)
-
- return doc
+ return list(set(apply_on_data))
def validate_coupon_code(coupon_name):
from frappe.utils import today,getdate
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 5c53d26..19d54a0 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -452,6 +452,10 @@
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
+ valuation_tax_accounts = [d.account_head for d in self.get("taxes")
+ if d.category in ('Valuation', 'Total and Valuation')
+ and flt(d.base_tax_amount_after_discount_amount)]
+
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
@@ -551,10 +555,10 @@
if self.auto_accounting_for_stock and self.is_opening == "No" and \
item.item_code in stock_items and item.item_tax_amount:
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
- if item.purchase_receipt:
+ if item.purchase_receipt and valuation_tax_accounts:
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
- where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
- (item.purchase_receipt, self.expenses_included_in_valuation))
+ where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""",
+ (item.purchase_receipt, valuation_tax_accounts))
if not negative_expense_booked_in_pr:
gl_entries.append(
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 85b1166..e41ad42 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -204,7 +204,7 @@
pi.insert()
pi.submit()
- self.check_gle_for_pi_against_pr(pi.name)
+ self.check_gle_for_pi(pi.name)
def check_gle_for_pi(self, pi):
gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
@@ -225,26 +225,6 @@
self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit)
- def check_gle_for_pi_against_pr(self, pi):
- gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
- from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
- group by account""", pi, as_dict=1)
-
- self.assertTrue(gl_entries)
-
- expected_values = dict((d[0], d) for d in [
- ["Creditors - TCP1", 0, 720],
- ["Stock Received But Not Billed - TCP1", 750.0, 0],
- ["_Test Account Shipping Charges - TCP1", 100.0, 100.0],
- ["_Test Account VAT - TCP1", 120.0, 0],
- ["_Test Account Customs Duty - TCP1", 0, 150]
- ])
-
- for i, gle in enumerate(gl_entries):
- self.assertEqual(expected_values[gle.account][0], gle.account)
- self.assertEqual(expected_values[gle.account][1], gle.debit)
- self.assertEqual(expected_values[gle.account][2], gle.credit)
-
def test_purchase_invoice_change_naming_series(self):
pi = frappe.copy_doc(test_records[1])
pi.insert()
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index dc3a1be..27d8233 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -117,6 +117,7 @@
},
{
"fetch_from": "item_code.item_name",
+ "fetch_if_empty": 1,
"fieldname": "item_name",
"fieldtype": "Data",
"in_global_search": 1,
@@ -192,7 +193,6 @@
"fieldtype": "Column Break"
},
{
- "fetch_from": "item_code.stock_uom",
"fieldname": "uom",
"fieldtype": "Link",
"label": "UOM",
@@ -766,7 +766,7 @@
],
"idx": 1,
"istable": 1,
- "modified": "2019-11-03 13:43:23.782877",
+ "modified": "2019-11-21 16:27:52.043744",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index ed45b2c..a48d224 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -357,14 +357,11 @@
def get_bin_data(pos_profile):
itemwise_bin_data = {}
- cond = "1=1"
+ filters = { 'actual_qty': ['>', 0] }
if pos_profile.get('warehouse'):
- cond = "warehouse = %(warehouse)s"
+ filters.update({ 'warehouse': pos_profile.get('warehouse') })
- bin_data = frappe.db.sql(""" select item_code, warehouse, actual_qty from `tabBin`
- where actual_qty > 0 and {cond}""".format(cond=cond), {
- 'warehouse': frappe.db.escape(pos_profile.get('warehouse'))
- }, as_dict=1)
+ bin_data = frappe.db.get_all('Bin', fields = ['item_code', 'warehouse', 'actual_qty'], filters=filters)
for bins in bin_data:
if bins.item_code not in itemwise_bin_data:
@@ -550,11 +547,15 @@
def make_email_queue(email_queue):
name_list = []
+
for key, data in iteritems(email_queue):
name = frappe.db.get_value('Sales Invoice', {'offline_pos_name': key}, 'name')
+ if not name: continue
+
data = json.loads(data)
sender = frappe.session.user
print_format = "POS Invoice" if not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')) else None
+
attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)]
make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'),
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3d96d48..70a80ca 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -135,7 +135,7 @@
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
validate_loyalty_points(self, self.loyalty_points)
-
+
def validate_fixed_asset(self):
for d in self.get("items"):
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 845ff74..f62df20 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -313,7 +313,7 @@
last_purchase_details = get_last_purchase_details(item_code, name)
if last_purchase_details:
- last_purchase_rate = (last_purchase_details['base_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate
+ last_purchase_rate = (last_purchase_details['base_net_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate
return last_purchase_rate
else:
item_last_purchase_rate = frappe.get_cached_value("Item", item_code, "last_purchase_rate")
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index 8c0a1e5..b5598f8 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -24,12 +24,12 @@
last_purchase_rate = None
if last_purchase_details and \
(last_purchase_details.purchase_date > this_purchase_date):
- last_purchase_rate = last_purchase_details['base_rate']
+ last_purchase_rate = last_purchase_details['base_net_rate']
elif is_submit == 1:
# even if this transaction is the latest one, it should be submitted
# for it to be considered for latest purchase rate
if flt(d.conversion_factor):
- last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
+ last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor)
# Check if item code is present
# Conversion factor should not be mandatory for non itemized items
elif d.item_code:
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index a912ef0..1f8b663 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -5,15 +5,17 @@
import frappe, erpnext
import json
from frappe import _, throw
-from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day, nowdate
-from erpnext.stock.get_item_details import get_conversion_factor
+from frappe.utils import (today, flt, cint, fmt_money, formatdate,
+ getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
+from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.buying.utils import update_last_purchase_rate
from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
-from erpnext.accounts.doctype.pricing_rule.utils import validate_pricing_rules
+from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
+ apply_pricing_rule_for_free_items, get_applied_pricing_rules)
from erpnext.exceptions import InvalidCurrency
from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
@@ -101,7 +103,7 @@
validate_regional(self)
if self.doctype != 'Material Request':
- validate_pricing_rules(self)
+ apply_pricing_rule_on_transaction(self)
def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates()
@@ -232,7 +234,6 @@
def set_missing_item_details(self, for_validate=False):
"""set missing item values"""
- from erpnext.stock.get_item_details import get_item_details
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
if hasattr(self, "items"):
@@ -244,7 +245,6 @@
document_type = "{} Item".format(self.doctype)
parent_dict.update({"document_type": document_type})
- self.set('pricing_rules', [])
# party_name field used for customer in quotation
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
parent_dict.update({"customer": parent_dict.get("party_name")})
@@ -264,7 +264,7 @@
if self.get("is_subcontracted"):
args["is_subcontracted"] = self.is_subcontracted
- ret = get_item_details(args, self, overwrite_warehouse=False)
+ ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None:
@@ -285,24 +285,42 @@
if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'):
item.set('is_fixed_asset', ret.get('is_fixed_asset', 0))
- if ret.get("pricing_rules") and not ret.get("validate_applied_rule", 0):
- # if user changed the discount percentage then set user's discount percentage ?
- item.set("pricing_rules", ret.get("pricing_rules"))
- item.set("discount_percentage", ret.get("discount_percentage"))
- item.set("discount_amount", ret.get("discount_amount"))
- if ret.get("pricing_rule_for") == "Rate":
- item.set("price_list_rate", ret.get("price_list_rate"))
-
- if item.get("price_list_rate"):
- item.rate = flt(item.price_list_rate *
- (1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
-
- if item.get('discount_amount'):
- item.rate = item.price_list_rate - item.discount_amount
+ if ret.get("pricing_rules"):
+ self.apply_pricing_rule_on_items(item, ret)
if self.doctype == "Purchase Invoice":
self.set_expense_account(for_validate)
+ def apply_pricing_rule_on_items(self, item, pricing_rule_args):
+ if not pricing_rule_args.get("validate_applied_rule", 0):
+ # if user changed the discount percentage then set user's discount percentage ?
+ if pricing_rule_args.get("price_or_product_discount") == 'Price':
+ item.set("pricing_rules", pricing_rule_args.get("pricing_rules"))
+ item.set("discount_percentage", pricing_rule_args.get("discount_percentage"))
+ item.set("discount_amount", pricing_rule_args.get("discount_amount"))
+ if pricing_rule_args.get("pricing_rule_for") == "Rate":
+ item.set("price_list_rate", pricing_rule_args.get("price_list_rate"))
+
+ if item.get("price_list_rate"):
+ item.rate = flt(item.price_list_rate *
+ (1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
+
+ if item.get('discount_amount'):
+ item.rate = item.price_list_rate - item.discount_amount
+
+ elif pricing_rule_args.get('free_item'):
+ apply_pricing_rule_for_free_items(self, pricing_rule_args)
+
+ elif pricing_rule_args.get("validate_applied_rule"):
+ for pricing_rule in get_applied_pricing_rules(item):
+ pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
+ for field in ['discount_percentage', 'discount_amount', 'rate']:
+ if item.get(field) < pricing_rule_doc.get(field):
+ title = get_link_to_form("Pricing Rule", pricing_rule)
+
+ frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
+ .format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
+
def set_taxes(self):
if not self.meta.get_field("taxes"):
return
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 3830ca0..7b4a4c9 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -159,8 +159,12 @@
if "description" in searchfields:
searchfields.remove("description")
- columns = [field for field in searchfields if not field in ["name", "item_group", "description"]]
- columns = ", ".join(columns)
+ columns = ''
+ extra_searchfields = [field for field in searchfields
+ if not field in ["name", "item_group", "description"]]
+
+ if extra_searchfields:
+ columns = ", " + ", ".join(extra_searchfields)
searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields]
@@ -176,7 +180,7 @@
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
tabItem.item_group,
if(length(tabItem.description) > 40, \
- concat(substr(tabItem.description, 1, 40), "..."), description) as description,
+ concat(substr(tabItem.description, 1, 40), "..."), description) as description
{columns}
from tabItem
where tabItem.docstatus < 2
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 8595292..81fdbbe 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -72,7 +72,7 @@
items_returned = False
for d in doc.get("items"):
- if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0):
+ if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0):
if d.item_code not in valid_items:
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index d2db9d0..66232d7 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -552,7 +552,7 @@
if item.price_list_rate:
if item.pricing_rules and not self.doc.ignore_pricing_rule:
for d in item.pricing_rules.split(','):
- pricing_rule = frappe.get_doc('Pricing Rule', d)
+ pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
or (pricing_rule.margin_type == 'Percentage'):
diff --git a/erpnext/education/doctype/instructor/instructor.js b/erpnext/education/doctype/instructor/instructor.js
index f9c7a2a..71e044b 100644
--- a/erpnext/education/doctype/instructor/instructor.js
+++ b/erpnext/education/doctype/instructor/instructor.js
@@ -4,11 +4,11 @@
frappe.ui.form.on("Instructor", {
employee: function(frm) {
if(!frm.doc.employee) return;
- frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (company) => {
+ frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (d) => {
frm.set_query("department", function() {
return {
"filters": {
- "company": company,
+ "company": d.company,
}
};
});
@@ -16,7 +16,7 @@
frm.set_query("department", "instructor_log", function() {
return {
"filters": {
- "company": company,
+ "company": d.company,
}
};
});
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index d6b66da..df0f75a 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -20,10 +20,6 @@
department_details = {}
department_list = []
employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True)
- if employee.leave_approver:
- approver = frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])
- approvers.append(approver)
- return approvers
employee_department = filters.get("department") or employee.department
if employee_department:
@@ -34,6 +30,9 @@
and disabled=0
order by lft desc""", (department_details.lft, department_details.rgt), as_list=True)
+ if filters.get("doctype") == "Leave Application" and employee.leave_approver:
+ approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name']))
+
if filters.get("doctype") == "Leave Application":
parentfield = "leave_approvers"
else:
@@ -47,4 +46,4 @@
and approver.parentfield = %s
and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
- return approvers
+ return set(tuple(approver) for approver in approvers)
diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
index 32fcee1..16c1a32 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
@@ -6,6 +6,7 @@
import frappe
import json
from frappe.model.document import Document
+from frappe.utils import getdate
class EmployeeAttendanceTool(Document):
@@ -43,17 +44,26 @@
@frappe.whitelist()
def mark_employee_attendance(employee_list, status, date, leave_type=None, company=None):
+
employee_list = json.loads(employee_list)
for employee in employee_list:
- attendance = frappe.new_doc("Attendance")
- attendance.employee = employee['employee']
- attendance.employee_name = employee['employee_name']
- attendance.attendance_date = date
- attendance.status = status
+
if status == "On Leave" and leave_type:
- attendance.leave_type = leave_type
- if company:
- attendance.company = company
+ leave_type = leave_type
else:
- attendance.company = frappe.db.get_value("Employee", employee['employee'], "Company")
+ leave_type = None
+
+ if not company:
+ company = frappe.db.get_value("Employee", employee['employee'], "Company")
+
+ attendance=frappe.get_doc(dict(
+ doctype='Attendance',
+ employee=employee.get('employee'),
+ employee_name=employee.get('employee_name'),
+ attendance_date=getdate(date),
+ status=status,
+ leave_type=leave_type,
+ company=company
+ ))
+ attendance.insert()
attendance.submit()
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
index 260e0b8..0330e5c 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
@@ -89,7 +89,8 @@
"fieldtype": "Link",
"label": "Company",
"options": "Company",
- "reqd": 1
+ "reqd": 1,
+ "search_index": 1
},
{
"fieldname": "section_break_12",
@@ -129,7 +130,7 @@
}
],
"is_submittable": 1,
- "modified": "2019-10-16 13:38:32.302316",
+ "modified": "2019-11-18 19:37:37.151686",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Blanket Order",
diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
index 099eed4..977ad54 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
@@ -1,298 +1,78 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-05-24 07:20:04.255236",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2018-05-24 07:20:04.255236",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_code",
+ "item_name",
+ "column_break_3",
+ "qty",
+ "rate",
+ "ordered_qty",
+ "section_break_7",
+ "terms_and_conditions"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_code",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Code",
+ "options": "Item",
+ "reqd": 1,
+ "search_index": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "item_code.item_name",
- "fieldname": "item_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Item Name",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "item_code.item_name",
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "label": "Item Name"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Quantity",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Quantity"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Rate",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "ordered_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Ordered Quantity",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "ordered_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Ordered Quantity",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_7",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "terms_and_conditions",
- "fieldtype": "Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Terms and Conditions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "terms_and_conditions",
+ "fieldtype": "Text",
+ "label": "Terms and Conditions"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-06-14 07:04:14.050836",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Blanket Order Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "modified": "2019-11-18 19:37:46.245878",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Blanket Order Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9e4dc12..07b646b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -645,4 +645,5 @@
erpnext.patches.v12_0.set_payment_entry_status
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
-erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
\ No newline at end of file
+erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
+erpnext.patches.v12_0.update_price_or_product_discount
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/update_price_or_product_discount.py b/erpnext/patches/v12_0/update_price_or_product_discount.py
new file mode 100644
index 0000000..3a8cd43
--- /dev/null
+++ b/erpnext/patches/v12_0/update_price_or_product_discount.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("accounts", "doctype", "pricing_rule")
+
+ frappe.db.sql(""" UPDATE `tabPricing Rule` SET price_or_product_discount = 'Price'
+ WHERE ifnull(price_or_product_discount,'') = '' """)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ca492ba..5da9493 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -516,7 +516,7 @@
}
},
() => me.conversion_factor(doc, cdt, cdn, true),
- () => me.validate_pricing_rule(item)
+ () => me.remove_pricing_rule(item)
]);
}
}
@@ -1174,7 +1174,7 @@
callback: function(r) {
if (!r.exc && r.message) {
r.message.forEach(row_item => {
- me.validate_pricing_rule(row_item);
+ me.remove_pricing_rule(row_item);
});
me._set_values_for_item_list(r.message);
me.calculate_taxes_and_totals();
@@ -1283,6 +1283,8 @@
_set_values_for_item_list: function(children) {
var me = this;
var price_list_rate_changed = false;
+ var items_rule_dict = {};
+
for(var i=0, l=children.length; i<l; i++) {
var d = children[i];
var existing_pricing_rule = frappe.model.get_value(d.doctype, d.name, "pricing_rules");
@@ -1299,14 +1301,39 @@
// if pricing rule set as blank from an existing value, apply price_list
if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
me.apply_price_list(frappe.get_doc(d.doctype, d.name));
- } else {
- me.validate_pricing_rule(frappe.get_doc(d.doctype, d.name));
+ } else if(!d.pricing_rules) {
+ me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
+ }
+
+ if (d.apply_rule_on_other_items) {
+ items_rule_dict[d.name] = d;
}
}
+ me.apply_rule_on_other_items(items_rule_dict);
+
if(!price_list_rate_changed) me.calculate_taxes_and_totals();
},
+ apply_rule_on_other_items: function(args) {
+ const me = this;
+ const fields = ["discount_percentage", "discount_amount", "rate"];
+
+ for(var k in args) {
+ let data = args[k];
+
+ me.frm.doc.items.forEach(d => {
+ if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) {
+ for(var k in data) {
+ if (in_list(fields, k)) {
+ frappe.model.set_value(d.doctype, d.name, k, data[k]);
+ }
+ }
+ }
+ });
+ }
+ },
+
apply_price_list: function(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
@@ -1348,33 +1375,11 @@
});
},
- validate_pricing_rule: function(item) {
+ remove_pricing_rule: function(item) {
let me = this;
const fields = ["discount_percentage", "discount_amount", "pricing_rules"];
- if (item.pricing_rules) {
- frappe.call({
- method: "erpnext.accounts.doctype.pricing_rule.utils.validate_pricing_rule_for_different_cond",
- args: {
- doc: me.frm.doc
- },
- callback: function(r) {
- if (r.message) {
- r.message.items.forEach(d => {
- me.frm.doc.items.forEach(row => {
- if(d.name == row.name) {
- fields.forEach(f => {
- row[f] = d[f];
- });
- }
- });
- });
-
- me.trigger_price_list_rate();
- }
- }
- });
- } else if(item.remove_free_item) {
+ if(item.remove_free_item) {
var items = [];
me.frm.doc.items.forEach(d => {
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index d2c2d70..b213a29 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -451,7 +451,7 @@
change_pos_profile() {
return new Promise((resolve) => {
- const on_submit = ({ pos_profile, set_as_default }) => {
+ const on_submit = ({ company, pos_profile, set_as_default }) => {
if (pos_profile) {
this.pos_profile = pos_profile;
}
@@ -461,7 +461,7 @@
method: "erpnext.accounts.doctype.pos_profile.pos_profile.set_default_profile",
args: {
'pos_profile': pos_profile,
- 'company': this.frm.doc.company
+ 'company': company
}
}).then(() => {
this.on_change_pos_profile();
@@ -471,7 +471,42 @@
}
}
- frappe.prompt(this.get_prompt_fields(),
+
+ let me = this;
+
+ var dialog = frappe.prompt([{
+ fieldtype: 'Link',
+ label: __('Company'),
+ options: 'Company',
+ fieldname: 'company',
+ default: me.frm.doc.company,
+ reqd: 1,
+ onchange: function(e) {
+ me.get_default_pos_profile(this.value).then((r) => {
+ dialog.set_value('pos_profile', (r && r.name)? r.name : '');
+ });
+ }
+ },
+ {
+ fieldtype: 'Link',
+ label: __('POS Profile'),
+ options: 'POS Profile',
+ fieldname: 'pos_profile',
+ default: me.frm.doc.pos_profile,
+ reqd: 1,
+ get_query: () => {
+ return {
+ query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
+ filters: {
+ company: dialog.get_value('company')
+ }
+ };
+ }
+ }, {
+ fieldtype: 'Check',
+ label: __('Set as default'),
+ fieldname: 'set_as_default'
+ }],
on_submit,
__('Select POS Profile')
);
@@ -494,26 +529,9 @@
]);
}
- get_prompt_fields() {
- return [{
- fieldtype: 'Link',
- label: __('POS Profile'),
- options: 'POS Profile',
- fieldname: 'pos_profile',
- reqd: 1,
- get_query: () => {
- return {
- query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
- filters: {
- company: this.frm.doc.company
- }
- };
- }
- }, {
- fieldtype: 'Check',
- label: __('Set as default'),
- fieldname: 'set_as_default'
- }];
+ get_default_pos_profile(company) {
+ return frappe.xcall("erpnext.stock.get_item_details.get_pos_profile",
+ {'company': company})
}
setup_company() {
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 313de67..be736d2 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -29,7 +29,8 @@
company_name: function(frm) {
if(frm.doc.__islocal) {
- let parts = frm.doc.company_name.split();
+ // add missing " " arg in split method
+ let parts = frm.doc.company_name.split(" ");
let abbr = $.map(parts, function (p) {
return p? p.substr(0, 1) : null;
}).join("");
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 5603f17..22375ae 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -39,6 +39,7 @@
invalidate_cache_for(self)
self.validate_name_with_item()
self.validate_one_root()
+ self.delete_child_item_groups_key()
def make_route(self):
'''Make website route'''
@@ -58,6 +59,7 @@
def on_trash(self):
NestedSet.on_trash(self)
WebsiteGenerator.on_trash(self)
+ self.delete_child_item_groups_key()
def validate_name_with_item(self):
if frappe.db.exists("Item", self.name):
@@ -83,6 +85,9 @@
return context
+ def delete_child_item_groups_key(self):
+ frappe.cache().hdel("child_item_groups", self.name)
+
@frappe.whitelist(allow_guest=True)
def get_product_list_for_group(product_group=None, start=0, limit=10, search=None):
if product_group:
@@ -136,6 +141,7 @@
fields = ['name', 'route', 'description', 'image'],
filters = dict(
show_in_website = 1,
+ parent_item_group = item_group.name,
lft = ('>', item_group.lft),
rgt = ('<', item_group.rgt),
),
@@ -167,6 +173,19 @@
from `tabItem Group` where lft>=%(lft)s and rgt<=%(rgt)s
and show_in_website = 1""", {"lft": item_group.lft, "rgt": item_group.rgt})
+def get_child_item_groups(item_group_name):
+ child_item_groups = frappe.cache().hget("child_item_groups", item_group_name)
+
+ if not child_item_groups:
+ item_group = frappe.get_cached_doc("Item Group", item_group_name)
+
+ child_item_groups = [d.name for d in frappe.get_all('Item Group',
+ filters= {'lft': ('>=', item_group.lft),'rgt': ('>=', item_group.rgt)})]
+
+ frappe.cache().hset("child_item_groups", item_group_name, child_item_groups)
+
+ return child_item_groups or {}
+
def get_item_for_list_in_html(context):
# add missing absolute link in files
# user may forget it during upload
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index e17429b..04d624e 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -1,599 +1,200 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "MAT-BIN-.YYYY.-.#####",
- "beta": 0,
- "creation": "2013-01-10 16:34:25",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "autoname": "MAT-BIN-.YYYY.-.#####",
+ "creation": "2013-01-10 16:34:25",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "warehouse",
+ "item_code",
+ "reserved_qty",
+ "actual_qty",
+ "ordered_qty",
+ "indented_qty",
+ "planned_qty",
+ "projected_qty",
+ "reserved_qty_for_production",
+ "reserved_qty_for_sub_contract",
+ "ma_rate",
+ "stock_uom",
+ "fcfs_rate",
+ "valuation_rate",
+ "stock_value"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Warehouse",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Warehouse",
+ "oldfieldname": "warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "read_only": 1,
+ "search_index": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_code",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Item Code",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "item_code",
- "oldfieldtype": "Link",
- "options": "Item",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Item Code",
+ "oldfieldname": "item_code",
+ "oldfieldtype": "Link",
+ "options": "Item",
+ "read_only": 1,
+ "search_index": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0.00",
- "fieldname": "reserved_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Reserved Quantity",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "reserved_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0.00",
+ "fieldname": "reserved_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Reserved Quantity",
+ "oldfieldname": "reserved_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0.00",
- "fieldname": "actual_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Actual Quantity",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "actual_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0.00",
+ "fieldname": "actual_qty",
+ "fieldtype": "Float",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Actual Quantity",
+ "oldfieldname": "actual_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0.00",
- "fieldname": "ordered_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Ordered Quantity",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "ordered_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0.00",
+ "fieldname": "ordered_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Ordered Quantity",
+ "oldfieldname": "ordered_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0.00",
- "fieldname": "indented_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Requested Quantity",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "indented_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0.00",
+ "fieldname": "indented_qty",
+ "fieldtype": "Float",
+ "label": "Requested Quantity",
+ "oldfieldname": "indented_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "planned_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Planned Qty",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "planned_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "planned_qty",
+ "fieldtype": "Float",
+ "label": "Planned Qty",
+ "oldfieldname": "planned_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "projected_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Projected Qty",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "projected_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "projected_qty",
+ "fieldtype": "Float",
+ "label": "Projected Qty",
+ "oldfieldname": "projected_qty",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reserved_qty_for_production",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reserved Qty for Production",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "reserved_qty_for_production",
+ "fieldtype": "Float",
+ "label": "Reserved Qty for Production",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reserved_qty_for_sub_contract",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reserved Qty for sub contract",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "reserved_qty_for_sub_contract",
+ "fieldtype": "Float",
+ "label": "Reserved Qty for sub contract"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "ma_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Moving Average Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "ma_rate",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 1,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "ma_rate",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "label": "Moving Average Rate",
+ "oldfieldname": "ma_rate",
+ "oldfieldtype": "Currency",
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "stock_uom",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "UOM",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "stock_uom",
- "oldfieldtype": "Data",
- "options": "UOM",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "label": "UOM",
+ "oldfieldname": "stock_uom",
+ "oldfieldtype": "Data",
+ "options": "UOM",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "fcfs_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "FCFS Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "fcfs_rate",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 1,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "fcfs_rate",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "label": "FCFS Rate",
+ "oldfieldname": "fcfs_rate",
+ "oldfieldtype": "Currency",
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "valuation_rate",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Valuation Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "valuation_rate",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "valuation_rate",
+ "fieldtype": "Float",
+ "label": "Valuation Rate",
+ "oldfieldname": "valuation_rate",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "stock_value",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Stock Value",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "stock_value",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "stock_value",
+ "fieldtype": "Float",
+ "label": "Stock Value",
+ "oldfieldname": "stock_value",
+ "oldfieldtype": "Currency",
+ "read_only": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 1,
- "idx": 1,
- "image_view": 0,
- "in_create": 1,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-21 16:15:39.356230",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Bin",
- "owner": "Administrator",
+ ],
+ "hide_toolbar": 1,
+ "idx": 1,
+ "in_create": 1,
+ "modified": "2019-11-18 18:34:59.456882",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Bin",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Sales User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Purchase User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Stock User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Stock User"
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "search_fields": "item_code,warehouse",
- "show_name_in_global_search": 0,
- "sort_order": "ASC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "search_fields": "item_code,warehouse",
+ "sort_order": "ASC"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 164c659..7495dff 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -645,7 +645,7 @@
json.dumps(item_wise_tax_detail), update_modified=False)
def set_last_purchase_rate(self, new_name):
- last_purchase_rate = get_last_purchase_details(new_name).get("base_rate", 0)
+ last_purchase_rate = get_last_purchase_details(new_name).get("base_net_rate", 0)
frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)
def recalculate_bin_qty(self, new_name):
@@ -942,7 +942,7 @@
last_purchase_order = frappe.db.sql("""\
select po.name, po.transaction_date, po.conversion_rate,
po_item.conversion_factor, po_item.base_price_list_rate,
- po_item.discount_percentage, po_item.base_rate
+ po_item.discount_percentage, po_item.base_rate, po_item.base_net_rate
from `tabPurchase Order` po, `tabPurchase Order Item` po_item
where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and
po.name = po_item.parent
@@ -953,7 +953,7 @@
last_purchase_receipt = frappe.db.sql("""\
select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage,
- pr_item.base_rate
+ pr_item.base_rate, pr_item.base_net_rate
from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
where pr.docstatus = 1 and pr_item.item_code = %s and pr.name != %s and
pr.name = pr_item.parent
@@ -984,6 +984,7 @@
out = frappe._dict({
"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
"base_rate": flt(last_purchase.base_rate) / conversion_factor,
+ "base_net_rate": flt(last_purchase.net_rate) / conversion_factor,
"discount_percentage": flt(last_purchase.discount_percentage),
"purchase_date": purchase_date
})
@@ -992,7 +993,8 @@
out.update({
"price_list_rate": out.base_price_list_rate / conversion_rate,
"rate": out.base_rate / conversion_rate,
- "base_rate": out.base_rate
+ "base_rate": out.base_rate,
+ "base_net_rate": out.base_net_rate
})
return out
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
index 46fdc8f..0149280 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
@@ -8,11 +8,11 @@
"naming_series",
"company",
"purchase_receipts",
- "sec_break1",
- "taxes",
"purchase_receipt_items",
"get_items_from_purchase_receipts",
"items",
+ "sec_break1",
+ "taxes",
"section_break_9",
"total_taxes_and_charges",
"col_break1",
@@ -123,7 +123,7 @@
],
"icon": "icon-usd",
"is_submittable": 1,
- "modified": "2019-10-09 13:39:36.082777",
+ "modified": "2019-11-21 15:34:10.846093",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Voucher",
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index 8773b9c..33713fa 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -16,6 +16,7 @@
def on_update(self):
self.set_default_if_missing()
self.update_item_price()
+ self.delete_price_list_details_key()
def set_default_if_missing(self):
if cint(self.selling):
@@ -32,6 +33,8 @@
(self.currency, cint(self.buying), cint(self.selling), self.name))
def on_trash(self):
+ self.delete_price_list_details_key()
+
def _update_default_price_list(module):
b = frappe.get_doc(module + " Settings")
price_list_fieldname = module.lower() + "_price_list"
@@ -43,3 +46,20 @@
for module in ["Selling", "Buying"]:
_update_default_price_list(module)
+
+ def delete_price_list_details_key(self):
+ frappe.cache().hdel("price_list_details", self.name)
+
+def get_price_list_details(price_list):
+ price_list_details = frappe.cache().hget("price_list_details", price_list)
+
+ if not price_list_details:
+ price_list_details = frappe.get_cached_value("Price List", price_list,
+ ["currency", "price_not_uom_dependent", "enabled"], as_dict=1)
+
+ if not price_list_details or not price_list_details.get("enabled"):
+ throw(_("Price List {0} is disabled or does not exist").format(price_list))
+
+ frappe.cache().hset("price_list_details", price_list, price_list_details)
+
+ return price_list_details or {}
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 7c2e09e..9f47edc 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -12,6 +12,7 @@
from erpnext.stock.doctype.batch.batch import get_batch_no
from erpnext import get_company_currency
from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor
+from erpnext.stock.doctype.price_list.price_list import get_price_list_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.setup.doctype.brand.brand import get_brand_defaults
from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_manufacturer_part_no
@@ -22,7 +23,7 @@
purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
@frappe.whitelist()
-def get_item_details(args, doc=None, overwrite_warehouse=True):
+def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=True):
"""
args = {
"item_code": "",
@@ -74,7 +75,9 @@
if args.get(key) is None:
args[key] = value
- data = get_pricing_rule_for_item(args, out.price_list_rate, doc)
+ data = get_pricing_rule_for_item(args, out.price_list_rate,
+ doc, for_validate=for_validate)
+
out.update(data)
update_stock(args, out)
@@ -479,7 +482,6 @@
if meta.get_field("currency") or args.get('currency'):
pl_details = get_price_list_currency_and_exchange_rate(args)
args.update(pl_details)
- validate_price_list(args)
if meta.get_field("currency"):
validate_conversion_rate(args, meta)
@@ -634,14 +636,6 @@
return flag
-def validate_price_list(args):
- if args.get("price_list"):
- if not frappe.db.get_value("Price List",
- {"name": args.price_list, args.transaction_type: 1, "enabled": 1}):
- throw(_("Price List {0} is disabled or does not exist").format(args.price_list))
- elif args.get("customer"):
- throw(_("Price List not selected"))
-
def validate_conversion_rate(args, meta):
from erpnext.controllers.accounts_controller import validate_conversion_rate
@@ -905,27 +899,6 @@
return item_details
-def get_price_list_currency(price_list):
- if price_list:
- result = frappe.db.get_value("Price List", {"name": price_list,
- "enabled": 1}, ["name", "currency"], as_dict=True)
-
- if not result:
- throw(_("Price List {0} is disabled or does not exist").format(price_list))
-
- return result.currency
-
-def get_price_list_uom_dependant(price_list):
- if price_list:
- result = frappe.db.get_value("Price List", {"name": price_list,
- "enabled": 1}, ["name", "price_not_uom_dependent"], as_dict=True)
-
- if not result:
- throw(_("Price List {0} is disabled or does not exist").format(price_list))
-
- return not result.price_not_uom_dependent
-
-
def get_price_list_currency_and_exchange_rate(args):
if not args.price_list:
return {}
@@ -935,8 +908,11 @@
elif args.doctype in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']:
args.update({"exchange_rate": "for_buying"})
- price_list_currency = get_price_list_currency(args.price_list)
- price_list_uom_dependant = get_price_list_uom_dependant(args.price_list)
+ price_list_details = get_price_list_details(args.price_list)
+
+ price_list_currency = price_list_details.get("currency")
+ price_list_uom_dependant = price_list_details.get("price_list_uom_dependant")
+
plc_conversion_rate = args.plc_conversion_rate
company_currency = get_company_currency(args.company)