diff --git a/erpnext/__version__.py b/erpnext/__version__.py index c8b2f49e37..2c86e6c90a 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '5.1.3' +__version__ = '5.1.4' diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index ad224f1908..0c5a4bbc7e 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -77,6 +77,51 @@ "read_only": 0, "reqd": 1 }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "oldfieldname": "warehouse", + "oldfieldtype": "Link", + "options": "Warehouse", + "permlevel": 0, + "read_only": 0, + "reqd": 0 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "oldfieldname": "letter_head", + "oldfieldtype": "Select", + "options": "Letter Head", + "permlevel": 0, + "print_hide": 1, + "read_only": 0 + }, + { + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms and Conditions", + "oldfieldname": "tc_name", + "oldfieldtype": "Link", + "options": "Terms and Conditions", + "permlevel": 0, + "read_only": 0 + }, + { + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "in_filter": 0, + "label": "Print Heading", + "oldfieldname": "select_print_heading", + "oldfieldtype": "Select", + "options": "Print Heading", + "permlevel": 0, + "read_only": 0 + }, { "fieldname": "column_break0", "fieldtype": "Column Break", @@ -105,6 +150,14 @@ "read_only": 0, "reqd": 0 }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment", + "permlevel": 0, + "precision": "" + }, { "fieldname": "cash_bank_account", "fieldtype": "Link", @@ -139,17 +192,6 @@ "read_only": 0, "reqd": 0 }, - { - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "oldfieldname": "warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "permlevel": 0, - "read_only": 0, - "reqd": 0 - }, { "fieldname": "cost_center", "fieldtype": "Link", @@ -161,16 +203,6 @@ "read_only": 0, "reqd": 1 }, - { - "fieldname": "taxes_and_charges", - "fieldtype": "Link", - "label": "Taxes and Charges", - "oldfieldname": "charge", - "oldfieldtype": "Link", - "options": "Sales Taxes and Charges Template", - "permlevel": 0, - "read_only": 0 - }, { "fieldname": "write_off_account", "fieldtype": "Link", @@ -190,43 +222,19 @@ "reqd": 1 }, { - "allow_on_submit": 1, - "fieldname": "letter_head", + "fieldname": "taxes_and_charges", "fieldtype": "Link", - "label": "Letter Head", - "oldfieldname": "letter_head", - "oldfieldtype": "Select", - "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "read_only": 0 - }, - { - "fieldname": "tc_name", - "fieldtype": "Link", - "label": "Terms and Conditions", - "oldfieldname": "tc_name", + "label": "Taxes and Charges", + "oldfieldname": "charge", "oldfieldtype": "Link", - "options": "Terms and Conditions", - "permlevel": 0, - "read_only": 0 - }, - { - "allow_on_submit": 1, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "in_filter": 0, - "label": "Print Heading", - "oldfieldname": "select_print_heading", - "oldfieldtype": "Select", - "options": "Print Heading", + "options": "Sales Taxes and Charges Template", "permlevel": 0, "read_only": 0 } ], "icon": "icon-cog", "idx": 1, - "modified": "2015-05-20 05:38:44.482696", + "modified": "2015-07-07 08:56:04.381471", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 50a79ec01b..af144cbf40 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -124,20 +124,11 @@ class PurchaseInvoice(BuyingController): } }) - if cint(frappe.defaults.get_global_default('maintain_same_rate')): - super(PurchaseInvoice, self).validate_with_previous_doc({ - "Purchase Order Item": { - "ref_dn_field": "po_detail", - "compare_fields": [["rate", "="]], - "is_child_table": True, - "allow_duplicate_prev_row_id": True - }, - "Purchase Receipt Item": { - "ref_dn_field": "pr_detail", - "compare_fields": [["rate", "="]], - "is_child_table": True - } - }) + if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')): + self.validate_rate_with_reference_doc([ + ["Purchase Order", "purchase_order", "po_detail"], + ["Purchase Receipt", "purchase_receipt", "pr_detail"] + ]) def set_against_expense_account(self): auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 604370b326..f3acc74c00 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -169,6 +169,7 @@ class SalesInvoice(SellingController): if pos: if not for_validate and not self.customer: self.customer = pos.customer + self.mode_of_payment = pos.mode_of_payment # self.set_customer_defaults() for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name', @@ -263,20 +264,11 @@ class SalesInvoice(SellingController): }, }) - if cint(frappe.defaults.get_global_default('maintain_same_sales_rate')): - super(SalesInvoice, self).validate_with_previous_doc({ - "Sales Order Item": { - "ref_dn_field": "so_detail", - "compare_fields": [["rate", "="]], - "is_child_table": True, - "allow_duplicate_prev_row_id": True - }, - "Delivery Note Item": { - "ref_dn_field": "dn_detail", - "compare_fields": [["rate", "="]], - "is_child_table": True - } - }) + if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')): + self.validate_rate_with_reference_doc([ + ["Sales Order", "sales_order", "so_detail"], + ["Delivery Note", "delivery_note", "dn_detail"] + ]) def set_against_income_account(self): """Set against account for debit to account""" diff --git a/erpnext/change_log/v5/v5_1_4.md b/erpnext/change_log/v5/v5_1_4.md new file mode 100644 index 0000000000..c11af81329 --- /dev/null +++ b/erpnext/change_log/v5/v5_1_4.md @@ -0,0 +1,4 @@ +- Mode of Payment added to POS Profile +- Expired Batch is not allowed in stock entry of type manufacturing / repack +- Validate item rate against reference document with tolerance 0.009 +- Set Customer name in opportunity as per company name in lead \ No newline at end of file diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 4f35fea38a..801f6f28ed 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -229,9 +229,10 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, }, { "start": start, "page_len": page_len, "txt": ("%%%s%%" % txt) }) def get_batch_no(doctype, txt, searchfield, start, page_len, filters): - if not filters.get("posting_date"): - filters["posting_date"] = nowdate() - + cond = "" + if filters.get("posting_date"): + cond = "and (ifnull(batch.expiry_date, '')='' or batch.expiry_date >= %(posting_date)s)" + batch_nos = None args = { 'item_code': filters.get("item_code"), @@ -251,23 +252,23 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): and sle.warehouse = %(warehouse)s and sle.batch_no like %(txt)s and batch.docstatus < 2 - and (ifnull(batch.expiry_date, '')='' or batch.expiry_date >= %(posting_date)s) + {0} {match_conditions} group by batch_no having sum(sle.actual_qty) > 0 order by batch.expiry_date, sle.batch_no desc - limit %(start)s, %(page_len)s""".format(match_conditions=get_match_cond(doctype)), args) + limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) if batch_nos: return batch_nos else: - return frappe.db.sql("""select name, expiry_date from `tabBatch` + return frappe.db.sql("""select name, expiry_date from `tabBatch` batch where item = %(item_code)s and name like %(txt)s and docstatus < 2 - and (ifnull(expiry_date, '')='' or expiry_date >= %(posting_date)s) + {0} {match_conditions} order by expiry_date, name desc - limit %(start)s, %(page_len)s""".format(match_conditions=get_match_cond(doctype)), args) + limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args, debug=1) def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list = [] diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 022539817e..78729a3bcb 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -79,7 +79,8 @@ class Opportunity(TransactionBase): if self.customer: self.customer_name = frappe.db.get_value("Customer", self.customer, "customer_name") elif self.lead: - self.customer_name = frappe.db.get_value("Lead", self.lead, "lead_name") + lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"]) + self.customer_name = company_name or lead_name def get_cust_address(self,name): details = frappe.db.sql("""select customer_name, address, territory, customer_group diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a05e5aeafb..ef1afa301a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -5,7 +5,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd. and Contributors" app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations" app_icon = "icon-th" app_color = "#e74c3c" -app_version = "5.1.3" +app_version = "5.1.4" error_report_email = "support@erpnext.com" @@ -25,8 +25,13 @@ on_logout = "erpnext.shopping_cart.utils.clear_cart_count" # website update_website_context = "erpnext.shopping_cart.utils.update_website_context" my_account_context = "erpnext.shopping_cart.utils.update_my_account_context" + email_append_to = ["Job Applicant", "Opportunity", "Issue"] +calendars = ["Task", "Production Order", "Time Log", "Leave Application"] + +website_generators = ["Item Group", "Item", "Sales Partner"] + website_context = { "favicon": "/assets/erpnext/images/favicon.png", "splash_image": "/assets/erpnext/images/splash.png" @@ -52,14 +57,10 @@ dump_report_map = "erpnext.startup.report_data_map.data_map" before_tests = "erpnext.setup.utils.before_tests" -website_generators = ["Item Group", "Item", "Sales Partner"] - standard_queries = { "Customer": "erpnext.selling.doctype.customer.customer.get_customer_list" } -communication_covert_to = ["Lead", "Issue", "Job Application"] - doc_events = { "Stock Entry": { "on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", @@ -106,4 +107,3 @@ get_translated_dict = { ("page", "setup-wizard"): "frappe.geo.country_info.get_translated_dict", ("doctype", "Global Defaults"): "frappe.geo.country_info.get_translated_dict" } - diff --git a/erpnext/hr/doctype/salary_manager/salary_manager.js b/erpnext/hr/doctype/salary_manager/salary_manager.js index 240547c679..ca702265d4 100644 --- a/erpnext/hr/doctype/salary_manager/salary_manager.js +++ b/erpnext/hr/doctype/salary_manager/salary_manager.js @@ -1,30 +1,44 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -var display_activity_log = function(msg) { +cur_frm.cscript.display_activity_log = function(msg) { if(!cur_frm.ss_html) cur_frm.ss_html = $a(cur_frm.fields_dict['activity_log'].wrapper,'div'); - cur_frm.ss_html.innerHTML = - '

'+__("Activity Log:")+'

'+msg+'
'; + if(msg) { + cur_frm.ss_html.innerHTML = + '

'+__("Activity Log:")+'

'+msg+'
'; + } else { + cur_frm.ss_html.innerHTML = ""; + } } //Create salary slip //----------------------- cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) { + cur_frm.cscript.display_activity_log(""); var callback = function(r, rt){ if (r.message) - display_activity_log(r.message); + cur_frm.cscript.display_activity_log(r.message); } return $c('runserverobj', args={'method':'create_sal_slip','docs':doc},callback); } cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) { + cur_frm.cscript.display_activity_log(""); var check = confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year])); if(check){ + // clear all in locals + if(locals["Salary Slip"]) { + $.each(locals["Salary Slip"], function(name, d) { + frappe.model.remove_from_locals("Salary Slip", name); + }); + } + var callback = function(r, rt){ if (r.message) - display_activity_log(r.message); + cur_frm.cscript.display_activity_log(r.message); } + return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback); } } @@ -47,4 +61,4 @@ cur_frm.cscript.make_jv = function(doc, dt, dn) { frappe.ui.form.on("Salary Manager", "refresh", function(frm) { frm.disable_save(); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/salary_manager/salary_manager.py b/erpnext/hr/doctype/salary_manager/salary_manager.py index eeb6ac051b..3e0b53e862 100644 --- a/erpnext/hr/doctype/salary_manager/salary_manager.py +++ b/erpnext/hr/doctype/salary_manager/salary_manager.py @@ -101,7 +101,7 @@ class SalaryManager(Document): log = "

No employee for the above selected criteria OR salary slip already created

" if ss_list: log = "Salary Slip Created For\ -

%s" % '
'.join(ss_list) +

%s" % '
'.join(self.format_as_links(ss_list)) return log @@ -144,7 +144,7 @@ class SalaryManager(Document): else: all_ss = [d[0] for d in all_ss] - submitted_ss = list(set(all_ss) - set(not_submitted_ss)) + submitted_ss = self.format_as_links(list(set(all_ss) - set(not_submitted_ss))) if submitted_ss: mail_sent_msg = self.send_email and " (Mail has been sent to the employee)" or "" log = """ @@ -164,6 +164,9 @@ class SalaryManager(Document): """% ('
'.join(not_submitted_ss)) return log + def format_as_links(self, ss_list): + return ['{0}'.format(s) for s in ss_list] + def get_total_salary(self): """ diff --git a/erpnext/public/js/pos/pos.js b/erpnext/public/js/pos/pos.js index 164e88359d..70f302387b 100644 --- a/erpnext/public/js/pos/pos.js +++ b/erpnext/public/js/pos/pos.js @@ -401,7 +401,8 @@ erpnext.pos.PointOfSale = Class.extend({ this.with_modes_of_payment(function() { // prefer cash payment! - var default_mode = me.modes_of_payment.indexOf(__("Cash"))!==-1 ? __("Cash") : undefined; + var default_mode = me.frm.doc.mode_of_payment ? me.frm.doc.mode_of_payment : + me.modes_of_payment.indexOf(__("Cash"))!==-1 ? __("Cash") : undefined; // show payment wizard var dialog = new frappe.ui.Dialog({ diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 775eb4143f..cd65d1890b 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -71,7 +71,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ } else { filters = { 'item_code': item.item_code, - 'posting_date': me.frm.doc.posting_date, + 'posting_date': me.frm.doc.posting_date || nowdate(), } if(item.warehouse) filters["warehouse"] = item.warehouse diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index f52f7e51ca..90a8a6c720 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -108,11 +108,9 @@ class DeliveryNote(SellingController): if not self.installation_status: self.installation_status = 'Not Installed' def validate_with_previous_doc(self): - items = self.get("items") - for fn in (("Sales Order", "against_sales_order", "so_detail"), ("Sales Invoice", "against_sales_invoice", "si_detail")): - if filter(None, [getattr(d, fn[1], None) for d in items]): + if filter(None, [getattr(d, fn[1], None) for d in self.get("items")]): super(DeliveryNote, self).validate_with_previous_doc({ fn[0]: { "ref_dn_field": fn[1], @@ -120,15 +118,10 @@ class DeliveryNote(SellingController): ["currency", "="]], }, }) - - if cint(frappe.defaults.get_global_default('maintain_same_sales_rate')): - super(DeliveryNote, self).validate_with_previous_doc({ - fn[0] + " Item": { - "ref_dn_field": fn[2], - "compare_fields": [["rate", "="]], - "is_child_table": True - } - }) + + if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')): + self.validate_rate_with_reference_doc([["Sales Order", "sales_order", "so_detail"], + ["Sales Invoice", "sales_invoice", "si_detail"]]) def validate_proj_cust(self): """check for does customer belong to same project as entered..""" diff --git a/erpnext/stock/doctype/manage_variants/manage_variants.py b/erpnext/stock/doctype/manage_variants/manage_variants.py index b6784d3a2e..4dcfb22dac 100644 --- a/erpnext/stock/doctype/manage_variants/manage_variants.py +++ b/erpnext/stock/doctype/manage_variants/manage_variants.py @@ -36,8 +36,8 @@ class ManageVariants(Document): def get_attributes(self): attributes = {} self.set('attributes', []) - for d in frappe.db.sql("""select attribute, attribute_value from `tabVariant Attribute` as attribute, - `tabItem` as item where attribute.parent= item.name and item.variant_of = %s""", self.item_code, as_dict=1): + for d in frappe.db.sql("""select attr.attribute, attr.attribute_value from `tabVariant Attribute` as attr, + `tabItem` as item where attr.parent = item.name and item.variant_of = %s""", self.item_code, as_dict=1): attributes.setdefault(d.attribute, []).append(d.attribute_value) for d in attributes: attribute_values = set(attributes[d]) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index e56cd1e907..e78288908d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -104,15 +104,8 @@ class PurchaseReceipt(BuyingController): } }) - if cint(frappe.defaults.get_global_default('maintain_same_rate')): - super(PurchaseReceipt, self).validate_with_previous_doc({ - "Purchase Order Item": { - "ref_dn_field": "prevdoc_detail_docname", - "compare_fields": [["rate", "="]], - "is_child_table": True - } - }) - + if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')): + self.validate_rate_with_reference_doc([["Purchase Order", "prevdoc_docname", "prevdoc_detail_docname"]]) def po_required(self): if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes': diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 1135bc708f..465b6415f5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -411,14 +411,21 @@ cur_frm.fields_dict['items'].grid.get_field('batch_no').get_query = function(doc var item = locals[cdt][cdn]; if(!item.item_code) { frappe.throw(__("Please enter Item Code to get batch no")); - } else { - var filters = { - 'item_code': item.item_code, - 'posting_date': me.frm.doc.posting_date, + } + else { + if (in_list(["Material Transfer for Manufacture", "Manufacture", "Repack", "Subcontract"], doc.purpose)) { + var filters = { + 'item_code': item.item_code, + 'posting_date': me.frm.doc.posting_date || nowdate() + } + } else { + var filters = { + 'item_code': item.item_code + } } + if(item.s_warehouse) filters["warehouse"] = item.s_warehouse - return { query : "erpnext.controllers.queries.get_batch_no", filters: filters @@ -498,9 +505,9 @@ cur_frm.cscript.uom = function(doc, cdt, cdn) { } cur_frm.cscript.validate = function(doc, cdt, cdn) { - cur_frm.cscript.validate_items(doc); if($.inArray(cur_frm.doc.purpose, ["Purchase Return", "Sales Return"])!==-1) validated = cur_frm.cscript.get_doctype_docname() ? true : false; + cur_frm.cscript.validate_items(doc); } cur_frm.cscript.validate_items = function(doc) { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 813a61b7d3..93fd325d88 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -727,7 +727,7 @@ class StockEntry(StockController): frappe.MappingMismatchError) def validate_batch(self): - if self.purpose == "Material Transfer for Manufacture": + if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Subcontract"]: for item in self.get("items"): if item.batch_no: if getdate(self.posting_date) > getdate(frappe.db.get_value("Batch", item.batch_no, "expiry_date")): @@ -870,8 +870,6 @@ def make_return_jv(stock_entry): "account": r.get("account"), "party_type": r.get("party_type"), "party": r.get("party"), - "against_invoice": r.get("against_invoice"), - "against_voucher": r.get("against_voucher"), "balance": get_balance_on(r.get("account"), se.posting_date) if r.get("account") else 0 }) @@ -882,8 +880,7 @@ def make_return_jv_from_sales_invoice(se, ref): parent = { "account": ref.doc.debit_to, "party_type": "Customer", - "party": ref.doc.customer, - "against_invoice": ref.doc.name, + "party": ref.doc.customer } # income account entries @@ -957,9 +954,6 @@ def make_return_jv_from_delivery_note(se, ref): break - if len(invoices_against_delivery) == 1: - parent["against_invoice"] = invoices_against_delivery[0] - result = [parent] + [{"account": account} for account in children] return result @@ -1015,9 +1009,6 @@ def make_return_jv_from_purchase_receipt(se, ref): break - if len(invoice_against_receipt) == 1: - parent["against_voucher"] = invoice_against_receipt[0] - result = [parent] + [{"account": account} for account in children] return result diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index be74aeb920..50b0319d3f 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr, now_datetime, cint +from frappe.utils import cstr, now_datetime, cint, flt import frappe.share from erpnext.controllers.status_updater import StatusUpdater @@ -92,6 +92,17 @@ class TransactionBase(StatusUpdater): for field, condition in fields: if prevdoc_values[field] is not None: self.validate_value(field, condition, prevdoc_values[field], doc) + + + def validate_rate_with_reference_doc(self, ref_details): + for ref_dt, ref_dn_field, ref_link_field in ref_details: + for d in self.get("items"): + if d.get(ref_link_field): + ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate") + + if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= .01: + frappe.throw(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ") + .format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate)) def delete_events(ref_type, ref_name): diff --git a/setup.py b/setup.py index a0e3f14045..7154899399 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "5.1.3" +version = "5.1.4" with open("requirements.txt", "r") as f: install_requires = f.readlines()