From 0db85067b26f5af3d816bd465529baae74b9f6f0 Mon Sep 17 00:00:00 2001 From: Shreya Date: Tue, 15 May 2018 11:25:46 +0530 Subject: [PATCH 1/4] Add For Buying and For Selling checkboxes in Currency Exchange --- erpnext/public/js/controllers/transaction.js | 9 +- .../currency_exchange/currency_exchange.json | 123 ++++++++++++++++-- .../currency_exchange/currency_exchange.py | 9 +- .../test_currency_exchange.js | 23 ++++ erpnext/setup/utils.py | 10 +- 5 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 erpnext/setup/doctype/currency_exchange/test_currency_exchange.js diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 2130d2b969..7c1a42a144 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -599,14 +599,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ /* manqala 19/09/2016: let the translation date be whichever of the transaction_date or posting_date is available */ var transaction_date = this.frm.doc.transaction_date || this.frm.doc.posting_date; /* end manqala */ - var me = this; this.set_dynamic_labels(); - var company_currency = this.get_company_currency(); // Added `ignore_pricing_rule` to determine if document is loading after mapping from another doc if(this.frm.doc.currency && this.frm.doc.currency !== company_currency && !this.frm.doc.ignore_pricing_rule) { + this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency, function(exchange_rate) { me.frm.set_value("conversion_rate", exchange_rate); @@ -668,13 +667,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) { + if (this.frm.doctype == "Purchase Order") { + var args = "for_buying"; + } if (!transaction_date || !from_currency || !to_currency) return; return frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", args: { transaction_date: transaction_date, from_currency: from_currency, - to_currency: to_currency + to_currency: to_currency, + args: args }, callback: function(r) { callback(flt(r.message)); diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.json b/erpnext/setup/doctype/currency_exchange/currency_exchange.json index 76e1a6b97e..89c373603b 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.json +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "beta": 0, @@ -13,6 +14,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -23,7 +25,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Date", "length": 0, "no_copy": 0, @@ -32,14 +36,17 @@ "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, "width": "5" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -50,6 +57,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "From Currency", @@ -65,10 +73,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "3" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -79,6 +89,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "To Currency", @@ -94,10 +105,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "3" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -108,6 +121,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Exchange Rate", @@ -123,22 +137,117 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "3" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_5", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "for_buying", + "fieldtype": "Check", + "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": "For Buying", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "for_selling", + "fieldtype": "Check", + "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": "For Selling", + "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 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-exchange", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-08 05:28:09.772560", + "modified": "2018-05-15 11:23:35.639039", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange", @@ -146,7 +255,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -154,7 +262,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -167,7 +274,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -175,7 +281,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -188,7 +293,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -196,7 +300,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -209,7 +312,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -217,7 +319,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -232,8 +333,10 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "name", "sort_order": "DESC", "title_field": "", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py index ab892fb41b..4effb5ab01 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py @@ -5,9 +5,9 @@ from __future__ import unicode_literals import frappe -from frappe import _ +from frappe import _, throw from frappe.model.document import Document -from frappe.utils import get_datetime_str, formatdate, nowdate +from frappe.utils import get_datetime_str, formatdate, nowdate, cint class CurrencyExchange(Document): def autoname(self): @@ -20,4 +20,7 @@ class CurrencyExchange(Document): self.validate_value("exchange_rate", ">", 0) if self.from_currency == self.to_currency: - frappe.throw(_("From Currency and To Currency cannot be same")) + throw(_("From Currency and To Currency cannot be same")) + + if not cint(self.for_buying) and not cint(self.for_selling): + throw(_("Currency Exchange must be applicable for Buying or for Selling.")) \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js new file mode 100644 index 0000000000..19fde2e148 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Currency Exchange", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Currency Exchange + () => frappe.tests.make('Currency Exchange', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 8d9bba3ff8..04c99315e4 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -55,7 +55,7 @@ def before_tests(): frappe.db.commit() @frappe.whitelist() -def get_exchange_rate(from_currency, to_currency, transaction_date=None): +def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=None): if not (from_currency and to_currency): # manqala 19/09/2016: Should this be an empty return or should it throw and exception? return @@ -74,7 +74,12 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None): ["from_currency", "=", from_currency], ["to_currency", "=", to_currency] ] - + frappe.errprint(args) + if args == "for_buying": + filters.append(["for_buying", "=", "1"]) + elif args == "for_selling": + filters.append(["for_selling", "=", 1]) + frappe.errprint(filters) if not allow_stale_rates: stale_days = currency_settings.get("stale_days") checkpoint_date = add_days(transaction_date, -stale_days) @@ -84,7 +89,6 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None): entries = frappe.get_all( "Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc", limit=1) - if entries: return flt(entries[0].exchange_rate) From b547cdd8fa847ba450aed1b5385b467550b253aa Mon Sep 17 00:00:00 2001 From: Shreya Date: Tue, 15 May 2018 16:58:45 +0530 Subject: [PATCH 2/4] Add buying or selling to filters in get_exchange_rate --- erpnext/setup/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 04c99315e4..bc17f8884f 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -59,13 +59,11 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not (from_currency and to_currency): # manqala 19/09/2016: Should this be an empty return or should it throw and exception? return - if from_currency == to_currency: return 1 if not transaction_date: transaction_date = nowdate() - currency_settings = frappe.get_doc("Accounts Settings").as_dict() allow_stale_rates = currency_settings.get("allow_stale") @@ -74,12 +72,12 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No ["from_currency", "=", from_currency], ["to_currency", "=", to_currency] ] - frappe.errprint(args) + if args == "for_buying": filters.append(["for_buying", "=", "1"]) elif args == "for_selling": - filters.append(["for_selling", "=", 1]) - frappe.errprint(filters) + filters.append(["for_selling", "=", "1"]) + if not allow_stale_rates: stale_days = currency_settings.get("stale_days") checkpoint_date = add_days(transaction_date, -stale_days) From 3f77852e93d03704b3ff76d9ded4d963396870b7 Mon Sep 17 00:00:00 2001 From: Shreya Date: Tue, 15 May 2018 16:59:20 +0530 Subject: [PATCH 3/4] Pass buying or selling as filter parameters Wherever get_exchange_rate is called, pass filter buying or selling on the basis of doctype. --- erpnext/accounts/doctype/sales_invoice/pos.py | 2 +- erpnext/controllers/accounts_controller.py | 13 +++++++++---- erpnext/crm/doctype/opportunity/opportunity.py | 2 +- erpnext/demo/user/purchase.py | 2 +- erpnext/demo/user/sales.py | 2 +- erpnext/manufacturing/doctype/bom/bom.py | 2 +- erpnext/public/js/controllers/transaction.js | 9 +++++++-- erpnext/stock/get_item_details.py | 7 ++++++- 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index be6078a871..47894647ee 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -97,7 +97,7 @@ def update_pos_profile_data(doc, pos_profile, company_data): doc.conversion_rate = 1.0 if doc.currency != company_data.default_currency: - doc.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency, doc.posting_date) + doc.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency, doc.posting_date, args="for_selling") doc.selling_price_list = pos_profile.get('selling_price_list') or \ frappe.db.get_value('Selling Settings', None, 'selling_price_list') diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 050a14332e..0c8e485f62 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -148,8 +148,13 @@ class AccountsController(TransactionBase): if self.meta.get_field("currency"): # price list part - fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ - else "buying_price_list" + if buying_or_selling.lower() == "selling": + fieldname = "selling_price_list" + args = "for_selling" + else: + fieldname = "buying_price_list" + args = "for_buying" + if self.meta.get_field(fieldname) and self.get(fieldname): self.price_list_currency = frappe.db.get_value("Price List", self.get(fieldname), "currency") @@ -159,7 +164,7 @@ class AccountsController(TransactionBase): elif not self.plc_conversion_rate: self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, - self.company_currency, transaction_date) + self.company_currency, transaction_date, args) # currency if not self.currency: @@ -169,7 +174,7 @@ class AccountsController(TransactionBase): self.conversion_rate = 1.0 elif not self.conversion_rate: self.conversion_rate = get_exchange_rate(self.currency, - self.company_currency, transaction_date) + self.company_currency, transaction_date, args) def set_missing_item_details(self, for_validate=False): """set missing item values""" diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index fc886ac69d..5c203d9e1b 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -227,7 +227,7 @@ def make_quotation(source_name, target_doc=None): exchange_rate = 1 else: exchange_rate = get_exchange_rate(quotation.currency, company_currency, - quotation.transaction_date) + quotation.transaction_date, args="for_selling") quotation.conversion_rate = exchange_rate diff --git a/erpnext/demo/user/purchase.py b/erpnext/demo/user/purchase.py index d9fc1f3c4a..327f617cc9 100644 --- a/erpnext/demo/user/purchase.py +++ b/erpnext/demo/user/purchase.py @@ -56,7 +56,7 @@ def work(): if company_currency == party_account_currency: exchange_rate = 1 else: - exchange_rate = get_exchange_rate(party_account_currency, company_currency) + exchange_rate = get_exchange_rate(party_account_currency, company_currency, args="for_buying") # make supplier quotations if random.random() < 0.2: diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py index 2fd2565aad..02e1d427a5 100644 --- a/erpnext/demo/user/sales.py +++ b/erpnext/demo/user/sales.py @@ -89,7 +89,7 @@ def make_quotation(): if company_currency == party_account_currency: exchange_rate = 1 else: - exchange_rate = get_exchange_rate(party_account_currency, company_currency) + exchange_rate = get_exchange_rate(party_account_currency, company_currency, args="for_selling") qtn = frappe.get_doc({ "creation": frappe.flags.current_date, diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 73c8ca0eb1..1fbc8068db 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -303,7 +303,7 @@ class BOM(WebsiteGenerator): if self.currency == self.company_currency(): self.conversion_rate = 1 elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0: - self.conversion_rate = get_exchange_rate(self.currency, self.company_currency()) + self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying") def validate_materials(self): """ Validate raw material entries """ diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7c1a42a144..ab7cee67bc 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -667,9 +667,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) { - if (this.frm.doctype == "Purchase Order") { - var args = "for_buying"; + var args; + if (["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"].includes(this.frm.doctype)) { + args = "for_selling"; } + else if (["Purchase Order", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doctype)) { + args = "for_buying"; + } + if (!transaction_date || !from_currency || !to_currency) return; return frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b36cd39ad7..31ee534e89 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -628,6 +628,11 @@ def get_price_list_currency_and_exchange_rate(args): if not args.price_list: return {} + if args.doctype in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']: + args.update({"exchange_rate": "for_selling"}) + 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) plc_conversion_rate = args.plc_conversion_rate @@ -637,7 +642,7 @@ def get_price_list_currency_and_exchange_rate(args): and price_list_currency != args.price_list_currency): # cksgb 19/09/2016: added args.transaction_date as posting_date argument for get_exchange_rate plc_conversion_rate = get_exchange_rate(price_list_currency, company_currency, - args.transaction_date) or plc_conversion_rate + args.transaction_date, args.exchange_rate) or plc_conversion_rate return frappe._dict({ "price_list_currency": price_list_currency, From acc4ceceb7ee794f5d7255e1531332a5b11c77a9 Mon Sep 17 00:00:00 2001 From: Shreya Date: Tue, 15 May 2018 17:50:52 +0530 Subject: [PATCH 4/4] Add Patch to update existing records --- erpnext/patches.txt | 1 + .../v11_0/check_buying_selling_in_currency_exchange.py | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cdea9e7d90..fa2251f042 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -537,3 +537,4 @@ erpnext.patches.v11_0.rename_supplier_type_to_supplier_group erpnext.patches.v11_0.create_department_records_for_each_company erpnext.patches.v11_0.make_location_from_warehouse erpnext.patches.v11_0.make_asset_finance_book_against_old_entries +erpnext.patches.v11_0.check_buying_selling_in_currency_exchange diff --git a/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py new file mode 100644 index 0000000000..ee336be3b5 --- /dev/null +++ b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py @@ -0,0 +1,5 @@ +import frappe + +def execute(): + frappe.reload_doc('setup', 'doctype', 'currency_exchange') + frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""") \ No newline at end of file