diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 06ba5dfe086..c98e77fdd24 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -35,6 +35,14 @@ frappe.ui.form.on("Journal Entry", { multi_currency: function(frm) { erpnext.journal_entry.toggle_fields_based_on_currency(frm); + }, + + posting_date: function(frm) { + if(!frm.doc.multi_currency) return; + + $.each(frm.doc.accounts || [], function(i, row) { + erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name); + }) } }) @@ -345,7 +353,7 @@ frappe.ui.form.on("Journal Entry Account", { }); } }, - + debit_in_account_currency: function(frm, cdt, cdn) { erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn); }, @@ -420,6 +428,7 @@ $.extend(erpnext.journal_entry, { frappe.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate", args: { + posting_date: frm.doc.posting_date, account: row.account, account_currency: row.account_currency, company: frm.doc.company, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 2f3b101f33a..cd793635029 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -326,8 +326,9 @@ class JournalEntry(AccountsController): if d.account_currency == self.company_currency: d.exchange_rate = 1 elif not d.exchange_rate or d.exchange_rate == 1 or \ - (d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name): - d.exchange_rate = get_exchange_rate(d.account, d.account_currency, self.company, + (d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name and d.posting_date): + # Modified to include the posting date for which to retreive the exchange rate + d.exchange_rate = get_exchange_rate(self.posting_date, d.account, d.account_currency, self.company, d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate) if not d.exchange_rate: @@ -648,7 +649,9 @@ def get_payment_entry(ref_doc, args): cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center") exchange_rate = 1 if args.get("party_account"): - exchange_rate = get_exchange_rate(args.get("party_account"), args.get("party_account_currency"), + # Modified to include the posting date for which the exchange rate is required. + # Assumed to be the posting date in the reference document + exchange_rate = get_exchange_rate(ref_doc.posting_date, args.get("party_account"), args.get("party_account_currency"), ref_doc.company, ref_doc.doctype, ref_doc.name) je = frappe.new_doc("Journal Entry") @@ -681,7 +684,9 @@ def get_payment_entry(ref_doc, args): bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account")) if bank_account: bank_row.update(bank_account) - bank_row.exchange_rate = get_exchange_rate(bank_account["account"], + # Modified to include the posting date for which the exchange rate is required. + # Assumed to be the posting date of the reference date + bank_row.exchange_rate = get_exchange_rate(ref_doc.posting_date, bank_account["account"], bank_account["account_currency"], ref_doc.company) bank_row.cost_center = cost_center @@ -805,7 +810,10 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi "party_type": party_type, "account_type": account_details.account_type, "account_currency": account_details.account_currency or company_currency, - "exchange_rate": get_exchange_rate(account, account_details.account_currency, + + # The date used to retreive the exchange rate here is the date passed in + # as an argument to this function. It is assumed to be the date on which the balance is sought + "exchange_rate": get_exchange_rate(date, account, account_details.account_currency, company, debit=debit, credit=credit, exchange_rate=exchange_rate) } @@ -815,8 +823,9 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi return grid_values +# Added posting_date as one of the parameters of get_exchange_rate @frappe.whitelist() -def get_exchange_rate(account, account_currency=None, company=None, +def get_exchange_rate(posting_date, account, account_currency=None, company=None, reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None): from erpnext.setup.utils import get_exchange_rate account_details = frappe.db.get_value("Account", account, @@ -842,9 +851,10 @@ def get_exchange_rate(account, account_currency=None, company=None, (account_details.root_type == "Liability" and debit)): exchange_rate = get_average_exchange_rate(account) - if not exchange_rate and account_currency: - exchange_rate = get_exchange_rate(account_currency, company_currency) - + # The date used to retreive the exchange rate here is the date passed + # in as an argument to this function. + if not exchange_rate and account_currency and posting_date: + exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date) else: exchange_rate = 1 diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1f6199e0523..d3dbd314cc7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -327,6 +327,7 @@ frappe.ui.form.on('Payment Entry', { frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", args: { + transaction_date: frm.doc.posting_date, from_currency: from_currency, to_currency: to_currency }, @@ -335,6 +336,10 @@ frappe.ui.form.on('Payment Entry', { } }) }, + + posting_date: function(frm) { + frm.events.paid_from_account_currency(frm); + }, source_exchange_rate: function(frm) { if (frm.doc.paid_amount) { @@ -425,6 +430,7 @@ frappe.ui.form.on('Payment Entry', { method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents', args: { args: { + "posting_date": frm.doc.posting_date, "company": frm.doc.company, "party_type": frm.doc.party_type, "payment_type": frm.doc.payment_type, diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d4f8edbb6f7..3e10b515498 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -153,11 +153,11 @@ class PaymentEntry(AccountsController): self.source_exchange_rate = get_average_exchange_rate(self.paid_from) else: self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, - self.company_currency) + self.company_currency, self.posting_date) if self.paid_to and not self.target_exchange_rate: self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, - self.company_currency) + self.company_currency, self.posting_date) def validate_mandatory(self): for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"): @@ -482,12 +482,12 @@ def get_outstanding_reference_documents(args): d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate") # Get all SO / PO which are not fully billed or aginst which full advance not paid - orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"), + orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), party_account_currency, company_currency) return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed -def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency): +def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency): voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order' ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" @@ -517,7 +517,9 @@ def get_orders_to_be_billed(party_type, party, party_account_currency, company_c order_list = [] for d in orders: d["voucher_type"] = voucher_type - d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency) + # This assumes that the exchange rate required is the one in the SO + d["exchange_rate"] = get_exchange_rate(party_account_currency, + company_currency, posting_date) order_list.append(d) return order_list @@ -592,14 +594,19 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre exchange_rate = 1 else: total_amount = ref_doc.grand_total + + # Get the exchange rate from the original ref doc + # or get it based on the posting date of the ref doc exchange_rate = ref_doc.get("conversion_rate") or \ - get_exchange_rate(party_account_currency, ref_doc.company_currency) + get_exchange_rate(party_account_currency, ref_doc.company_currency, ref_doc.posting_date) outstanding_amount = ref_doc.get("outstanding_amount") \ if reference_doctype in ("Sales Invoice", "Purchase Invoice") \ else flt(total_amount) - flt(ref_doc.advance_paid) else: - exchange_rate = get_exchange_rate(party_account_currency, ref_doc.company_currency) + # Get the exchange rate based on the posting date of the ref doc + exchange_rate = get_exchange_rate(party_account_currency, + ref_doc.company_currency, ref_doc.posting_date) return frappe._dict({ "due_date": ref_doc.get("due_date"), diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 73c412f2473..bf3e24fe7af 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -9,7 +9,6 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.setup.utils import get_exchange_rate -# test_records = frappe.get_test_records('Payment Request') test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"] diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 4e4ad780ed6..9fb11f2b7c2 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import frappe, json -from frappe import _ from frappe.utils import nowdate from erpnext.setup.utils import get_exchange_rate from erpnext.stock.get_item_details import get_pos_profile @@ -63,8 +62,10 @@ def update_pos_profile_data(doc, pos_profile, company_data): doc.currency = pos_profile.get('currency') or company_data.default_currency 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.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency, doc.posting_date) + doc.selling_price_list = pos_profile.get('selling_price_list') or \ frappe.db.get_value('Selling Settings', None, 'selling_price_list') doc.naming_series = pos_profile.get('naming_series') or 'SINV-' diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py index 1793fc31405..03f7a340792 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py @@ -39,7 +39,7 @@ def get_quote_list(item, qty_list): #Add a row for each supplier for root in set(suppliers): supplier_currency = frappe.db.get_value("Supplier",root,"default_currency") - exg = get_exchange_rate(supplier_currency,company_currency) + exg = get_exchange_rate(supplier_currency, company_currency) row = frappe._dict({ "supplier_name": root diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5796a4da102..554529c6f6c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -127,6 +127,11 @@ class AccountsController(TransactionBase): validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company) def set_price_list_currency(self, buying_or_selling): + if self.meta.get_field("posting_date"): + transaction_date = self.posting_date + else: + transaction_date = self.transaction_date + if self.meta.get_field("currency"): # price list part fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ @@ -139,8 +144,8 @@ class AccountsController(TransactionBase): self.plc_conversion_rate = 1.0 elif not self.plc_conversion_rate: - self.plc_conversion_rate = get_exchange_rate( - self.price_list_currency, self.company_currency) + self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, + self.company_currency, transaction_date) # currency if not self.currency: @@ -150,7 +155,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) + self.company_currency, transaction_date) def set_missing_item_details(self, for_validate=False): """set missing item values""" @@ -602,7 +607,6 @@ class AccountsController(TransactionBase): for item in duplicate_list: self.remove(item) - @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 6be0768bf41..6ee90036080 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -205,7 +205,8 @@ def make_quotation(source_name, target_doc=None): if company_currency == quotation.currency: exchange_rate = 1 else: - exchange_rate = get_exchange_rate(quotation.currency, company_currency) + exchange_rate = get_exchange_rate(quotation.currency, company_currency, + quotation.transaction_date) quotation.conversion_rate = exchange_rate diff --git a/erpnext/patches.txt b/erpnext/patches.txt index c4b66e7a12f..94212ec62ef 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -354,4 +354,5 @@ erpnext.patches.v7_1.rename_quality_inspection_field erpnext.patches.v7_0.update_autoname_field erpnext.patches.v7_1.update_bom_base_currency erpnext.patches.v7_0.update_status_of_po_so -erpnext.patches.v7_1.set_budget_against_as_cost_center \ No newline at end of file +erpnext.patches.v7_1.set_budget_against_as_cost_center +erpnext.patches.v7_1.set_currency_exchange_date \ No newline at end of file diff --git a/erpnext/patches/v7_1/set_currency_exchange_date.py b/erpnext/patches/v7_1/set_currency_exchange_date.py new file mode 100644 index 00000000000..7d8e4f0415d --- /dev/null +++ b/erpnext/patches/v7_1/set_currency_exchange_date.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + frappe.reload_doctype("Currency Exchange") + frappe.db.sql(""" + update `tabCurrency Exchange` + set `date` = '2010-01-01' + where date is null or date = '' or date = '0000-00-00' + """) \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 954b03b1f68..c31b0c84a82 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -413,6 +413,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ transaction_date: function() { if (this.frm.doc.transaction_date) { this.frm.transaction_date = this.frm.doc.transaction_date; + frappe.ui.form.trigger(this.frm.doc.doctype, "currency"); } }, @@ -434,9 +435,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ callback: function(r, rt) { if(r.message) { me.frm.set_value("due_date", r.message); + frappe.ui.form.trigger(me.frm.doc.doctype, "currency"); } } }) + } else { + frappe.ui.form.trigger(me.frm.doc.doctype, "currency"); } } }, @@ -450,6 +454,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, currency: function() { + /* 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(); @@ -457,7 +465,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ // 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(this.frm.doc.currency, company_currency, + this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency, function(exchange_rate) { me.frm.set_value("conversion_rate", exchange_rate); }); @@ -485,10 +493,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, - get_exchange_rate: function(from_currency, to_currency, callback) { + get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) { return frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", args: { + transaction_date: transaction_date, from_currency: from_currency, to_currency: to_currency }, @@ -505,7 +514,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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.price_list_currency !== company_currency && !this.frm.doc.ignore_pricing_rule) { - this.get_exchange_rate(this.frm.doc.price_list_currency, company_currency, + this.get_exchange_rate(this.frm.doc.posting_date, this.frm.doc.price_list_currency, company_currency, function(exchange_rate) { me.frm.set_value("plc_conversion_rate", exchange_rate); }); diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.json b/erpnext/setup/doctype/currency_exchange/currency_exchange.json index 44cea2076dd..76e1a6b97ee 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.json +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.json @@ -12,6 +12,33 @@ "editable_grid": 0, "engine": "InnoDB", "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "5" + }, { "allow_on_submit": 0, "bold": 0, @@ -38,7 +65,8 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "unique": 0, + "width": "3" }, { "allow_on_submit": 0, @@ -66,7 +94,8 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "unique": 0, + "width": "3" }, { "allow_on_submit": 0, @@ -94,7 +123,8 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "unique": 0, + "width": "3" } ], "hide_heading": 0, @@ -108,7 +138,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:28:09.772560", + "modified": "2016-11-08 05:28:09.772560", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange", @@ -202,5 +232,8 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "sort_field": "name", + "sort_order": "DESC", + "title_field": "", "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 60228129751..7f1a43c0eef 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py @@ -7,13 +7,15 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document +from frappe.utils import get_datetime, get_datetime_str, formatdate class CurrencyExchange(Document): - def autoname(self): - self.name = self.from_currency + "-" + self.to_currency + def autoname(self): + self.name = formatdate(get_datetime_str(self.date),"yyyy-MM-dd") + "-" + self.from_currency + "-" + self.to_currency + #self.name = self.date + "-" + self.from_currency + "-" + self.to_currency - def validate(self): - self.validate_value("exchange_rate", ">", 0) + def validate(self): + self.validate_value("exchange_rate", ">", 0) - if self.from_currency == self.to_currency: - frappe.throw(_("From Currency and To Currency cannot be same")) + if self.from_currency == self.to_currency: + frappe.throw(_("From Currency and To Currency cannot be same")) diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index 00776307de0..181f07257b3 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -3,5 +3,18 @@ from __future__ import unicode_literals -import frappe -test_records = frappe.get_test_records('Currency Exchange') \ No newline at end of file +import frappe, unittest +test_records = frappe.get_test_records('Currency Exchange') + +class TestCurrencyExchange(unittest.TestCase): + def test_exchnage_rate(self): + from erpnext.setup.utils import get_exchange_rate + + # Exchange rate as on 15th Jan, 2016, should be fetched from Currency Exchange record + exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15") + self.assertEqual(exchange_rate, 60.0) + + # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io + exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15") + self.assertFalse(exchange_rate==60) + \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange/test_records.json b/erpnext/setup/doctype/currency_exchange/test_records.json index 784bf262c03..23edd8a7994 100644 --- a/erpnext/setup/doctype/currency_exchange/test_records.json +++ b/erpnext/setup/doctype/currency_exchange/test_records.json @@ -1,18 +1,21 @@ [ { "doctype": "Currency Exchange", + "date": "2016-01-01", "exchange_rate": 60.0, "from_currency": "USD", "to_currency": "INR" }, { "doctype": "Currency Exchange", + "date": "2016-01-01", "exchange_rate": 0.773, "from_currency": "USD", "to_currency": "EUR" }, { "doctype": "Currency Exchange", + "date": "2016-01-01", "exchange_rate": 0.0167, "from_currency": "INR", "to_currency": "USD" diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index eda2042fd5e..0c214e4c2a8 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals import frappe from frappe import _, throw from frappe.utils import flt - +from frappe.utils import get_datetime_str, nowdate + def get_company_currency(company): currency = frappe.db.get_value("Company", company, "default_currency", cache=True) if not currency: @@ -64,36 +65,44 @@ def before_tests(): frappe.db.commit() @frappe.whitelist() -def get_exchange_rate(from_currency, to_currency): +def get_exchange_rate(from_currency, to_currency, transaction_date=None): + if not transaction_date: + transaction_date = nowdate() 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 - exchange = "%s-%s" % (from_currency, to_currency) - value = flt(frappe.db.get_value("Currency Exchange", exchange, "exchange_rate")) + # cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency. + entries = frappe.get_all("Currency Exchange", fields = ["exchange_rate"], + filters=[ + ["date", "<=", get_datetime_str(transaction_date)], + ["from_currency", "=", from_currency], + ["to_currency", "=", to_currency] + ], order_by="date desc", limit=1) + + if entries: + return flt(entries[0].exchange_rate) - if not value: - try: - cache = frappe.cache() - key = "currency_exchange_rate:{0}:{1}".format(from_currency, to_currency) - value = cache.get(key) + try: + cache = frappe.cache() + key = "currency_exchange_rate:{0}:{1}".format(from_currency, to_currency) + value = cache.get(key) - if not value: - import requests - response = requests.get("http://api.fixer.io/latest", params={ - "base": from_currency, - "symbols": to_currency - }) - # expire in 6 hours - response.raise_for_status() - value = response.json()["rates"][to_currency] - cache.setex(key, value, 6 * 60 * 60) + if not value: + import requests + response = requests.get("http://api.fixer.io/latest", params={ + "base": from_currency, + "symbols": to_currency + }) + # expire in 6 hours + response.raise_for_status() + value = response.json()["rates"][to_currency] + cache.setex(key, value, 6 * 60 * 60) - return flt(value) - except: - frappe.msgprint(_("Unable to find exchange rate for {0} to {1}").format(from_currency, to_currency)) - return 0.0 - else: - return value + return flt(value) + except: + frappe.msgprint(_("Unable to find exchange rate for {0} to {1} for key date {2}").format(from_currency, to_currency, transaction_date)) + return 0.0 \ No newline at end of file diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index d8d00efa967..6915ef54a15 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -8,6 +8,7 @@ import frappe from frappe import _, msgprint from frappe.utils import comma_and from frappe.model.document import Document +from frappe.utils import get_datetime, get_datetime_str, now_datetime class ShoppingCartSetupError(frappe.ValidationError): pass @@ -38,11 +39,18 @@ class ShoppingCartSettings(Document): expected_to_exist = [currency + "-" + company_currency for currency in price_list_currency_map.values() if currency != company_currency] + + # manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange + from_currency = [currency for currency in price_list_currency_map.values() if currency != company_currency] + to_currency = company_currency + # manqala end if expected_to_exist: - exists = frappe.db.sql_list("""select name from `tabCurrency Exchange` - where name in (%s)""" % (", ".join(["%s"]*len(expected_to_exist)),), - tuple(expected_to_exist)) + # manqala 20/09/2016: modify query so that it uses date in the selection from Currency Exchange. + # exchange rates defined with date less than the date on which this document is being saved will be selected + exists = frappe.db.sql_list("""select CONCAT(from_currency,'-',to_currency) from `tabCurrency Exchange` + where from_currency in (%s) and to_currency = "%s" and date <= curdate()""" % (", ".join(["%s"]*len(from_currency)), to_currency), tuple(from_currency)) + # manqala end missing = list(set(expected_to_exist).difference(exists)) diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index f7ca7d99152..b1d6ec0cb6e 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -23,6 +23,7 @@ "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 1, + "in_standard_filter": 0, "label": "Item Code", "length": 0, "no_copy": 0, @@ -34,6 +35,7 @@ "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 1, @@ -53,12 +55,14 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, "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, @@ -77,6 +81,7 @@ "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 0, + "in_standard_filter": 0, "label": "Item Name", "length": 0, "no_copy": 0, @@ -87,6 +92,7 @@ "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 1, @@ -106,6 +112,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Description", "length": 0, "no_copy": 0, @@ -114,6 +121,7 @@ "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, @@ -132,6 +140,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Description", "length": 0, "no_copy": 0, @@ -142,6 +151,7 @@ "print_hide_if_no_value": 0, "print_width": "250px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -161,6 +171,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, @@ -168,6 +179,7 @@ "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, @@ -186,6 +198,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Image", "length": 0, "no_copy": 0, @@ -194,6 +207,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -212,6 +226,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Quantity and Warehouse", "length": 0, "no_copy": 0, @@ -219,6 +234,7 @@ "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, @@ -237,6 +253,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Quantity", "length": 0, "no_copy": 0, @@ -247,6 +264,7 @@ "print_hide_if_no_value": 0, "print_width": "80px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -266,6 +284,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Stock UOM", "length": 0, "no_copy": 0, @@ -277,6 +296,7 @@ "print_hide_if_no_value": 0, "print_width": "70px", "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -296,6 +316,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "For Warehouse", "length": 0, "no_copy": 0, @@ -307,6 +328,7 @@ "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -326,12 +348,14 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, "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, @@ -351,6 +375,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Required Date", "length": 0, "no_copy": 0, @@ -361,6 +386,7 @@ "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -380,6 +406,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "More Information", "length": 0, "no_copy": 0, @@ -387,6 +414,7 @@ "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, @@ -406,6 +434,7 @@ "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 0, + "in_standard_filter": 0, "label": "Item Group", "length": 0, "no_copy": 0, @@ -416,6 +445,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 1, @@ -434,6 +464,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Brand", "length": 0, "no_copy": 0, @@ -445,6 +476,7 @@ "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -464,6 +496,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Lead Time Date", "length": 0, "no_copy": 1, @@ -473,6 +506,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -491,6 +525,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Sales Order", "length": 0, "no_copy": 0, @@ -499,6 +534,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -517,6 +553,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Project", "length": 0, "no_copy": 0, @@ -526,6 +563,7 @@ "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, @@ -544,12 +582,14 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, "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, @@ -568,6 +608,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Min Order Qty", "length": 0, "no_copy": 1, @@ -578,6 +619,7 @@ "print_hide_if_no_value": 0, "print_width": "70px", "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -597,6 +639,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Projected Qty", "length": 0, "no_copy": 1, @@ -607,6 +650,7 @@ "print_hide_if_no_value": 0, "print_width": "70px", "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -626,6 +670,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Actual Qty", "length": 0, "no_copy": 1, @@ -634,6 +679,7 @@ "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, @@ -652,6 +698,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Completed Qty", "length": 0, "no_copy": 1, @@ -661,6 +708,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -679,6 +727,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Page Break", "length": 0, "no_copy": 1, @@ -688,6 +737,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -705,7 +755,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-10-17 04:58:33.317145", + "modified": "2016-12-08 14:49:48.397015", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 0caf1cdf7ac..16ea58e47ab 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -481,7 +481,9 @@ def get_price_list_currency_and_exchange_rate(args): if (not plc_conversion_rate) or (price_list_currency and args.price_list_currency \ and price_list_currency != args.price_list_currency): - plc_conversion_rate = get_exchange_rate(price_list_currency, args.currency) or plc_conversion_rate + # 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, args.currency, + args.transaction_date) or plc_conversion_rate return frappe._dict({ "price_list_currency": price_list_currency,