diff --git a/.travis.yml b/.travis.yml index 869fe959c00..a8a0d826145 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,11 @@ dist: trusty python: - "2.7" + - "3.6" + +env: + - TEST_TYPE="Server Side Test" + - TEST_TYPE="Patch Test" services: - mysql @@ -39,18 +44,8 @@ before_script: - bench start & - sleep 10 -jobs: - include: - - stage: test - script: - - set -e - - bench run-tests --app erpnext --coverage - after_script: - - coveralls -b apps/erpnext -d ../../sites/.coverage - env: Server Side Test - - # stage - script: - - wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz - - bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis - - bench migrate - env: Patch Testing +script: + - bash $TRAVIS_BUILD_DIR/travis/run-tests.sh + +after_script: + - coveralls -b apps/erpnext -d ../../sites/.coverage diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0fdf7031ab3..4577c3d39ce 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '11.1.38' +__version__ = '11.1.46' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index f7190b75e2c..1a366d2f7fb 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class AccountingPeriod(Document): @@ -16,7 +17,7 @@ class AccountingPeriod(Document): def autoname(self): company_abbr = frappe.get_cached_value('Company', self.company, "abbr") self.name = " - ".join([self.period_name, company_abbr]) - + def validate_overlap(self): existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period` where ( @@ -33,7 +34,7 @@ class AccountingPeriod(Document): }, as_dict=True) if len(existing_accounting_period) > 0: - frappe.throw("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name"))) + frappe.throw(_("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name")))) def get_doctypes_for_closing(self): docs_for_closing = [] diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js index 2a44cb3b52f..0acbe2009f5 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js @@ -43,8 +43,13 @@ frappe.ui.form.on('Bank Guarantee', { reference_docname: function(frm) { if (frm.doc.reference_docname && frm.doc.reference_doctype) { - let fields_to_fetch = ["project", "grand_total"]; + let fields_to_fetch = ["grand_total"]; let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier"; + + if (frm.doc.reference_doctype == "Sales Order") { + fields_to_fetch.push("project"); + } + fields_to_fetch.push(party_field); frappe.call({ method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials", diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py index f5581941ba2..7bfab873b5a 100644 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py @@ -48,7 +48,7 @@ class BankStatementTransactionEntry(Document): def get_statement_headers(self): if not self.bank_settings: - frappe.throw("Bank Data mapper doesn't exist") + frappe.throw(_("Bank Data mapper doesn't exist")) mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} return headers @@ -57,7 +57,7 @@ class BankStatementTransactionEntry(Document): if self.bank_statement is None: return filename = self.bank_statement.split("/")[-1] if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): - frappe.throw("Transactions already retreived from the statement") + frappe.throw(_("Transactions already retreived from the statement")) date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") if (date_format is None): @@ -314,7 +314,7 @@ class BankStatementTransactionEntry(Document): try: reconcile_against_document(lst) except: - frappe.throw("Exception occurred while reconciling {0}".format(payment.reference_name)) + frappe.throw(_("Exception occurred while reconciling {0}".format(payment.reference_name))) def submit_payment_entries(self): for payment in self.new_transaction_items: @@ -414,7 +414,7 @@ def get_transaction_entries(filename, headers): elif (filename.lower().endswith("xls")): rows = get_rows_from_xls_file(filename) else: - frappe.throw("Only .csv and .xlsx files are supported currently") + frappe.throw(_("Only .csv and .xlsx files are supported currently")) stmt_headers = headers.values() for row in rows: diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index f943b345810..4899e6ecf0f 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -91,7 +91,7 @@ def get_paid_amount(payment_entry): return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed") else: - frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry)) + frappe.throw(_("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))) @frappe.whitelist() def unclear_reference_payment(doctype, docname): diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index 779cd6197e9..0d5456ece6c 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', { refresh: function(frm) { if(frm.doc.docstatus==1) { - frm.add_custom_button(__('Make Journal Entry'), function() { - return frm.events.make_jv(frm); - }); + frappe.db.get_value("Journal Entry Account", { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name, + 'docstatus': 1 + }, "sum(debit) as sum", (r) =>{ + let total_amt = 0; + frm.doc.accounts.forEach(d=> { + total_amt = total_amt + d['new_balance_in_base_currency']; + }); + if(total_amt === r.sum) { + frm.add_custom_button(__("Journal Entry"), function(){ + frappe.route_options = { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name + }; + frappe.set_route("List", "Journal Entry"); + }, __("View")); + } else { + frm.add_custom_button(__('Create Journal Entry'), function() { + return frm.events.make_jv(frm); + }); + } + }, 'Journal Entry'); } }, @@ -39,8 +59,6 @@ frappe.ui.form.on('Exchange Rate Revaluation', { }); frm.events.get_total_gain_loss(frm); refresh_field("accounts"); - } else { - frappe.msgprint(__("No records found")); } } }); @@ -54,7 +72,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', { d.gain_loss = flt(d.new_balance_in_base_currency, precision("new_balance_in_base_currency", d)) - flt(d.balance_in_base_currency, precision("balance_in_base_currency", d)); total_gain_loss += flt(d.gain_loss, precision("gain_loss", d)); }); - + frm.set_value("total_gain_loss", flt(total_gain_loss, precision("total_gain_loss"))); frm.refresh_fields(); }, diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index cdfe34beadb..9594706d0f6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -22,7 +22,7 @@ class ExchangeRateRevaluation(Document): - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency")) total_gain_loss += flt(d.gain_loss, d.precision("gain_loss")) self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss")) - + def validate_mandatory(self): if not (self.company and self.posting_date): frappe.throw(_("Please select Company and Posting Date to getting entries")) @@ -33,8 +33,9 @@ class ExchangeRateRevaluation(Document): company_currency = erpnext.get_company_currency(self.company) precision = get_field_precision(frappe.get_meta("Exchange Rate Revaluation Account") .get_field("new_balance_in_base_currency"), company_currency) - for d in self.get_accounts_from_gle(): - + + account_details = self.get_accounts_from_gle() + for d in account_details: current_exchange_rate = d.balance / d.balance_in_account_currency \ if d.balance_in_account_currency else 0 new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date) @@ -52,6 +53,10 @@ class ExchangeRateRevaluation(Document): "new_exchange_rate": new_exchange_rate, "new_balance_in_base_currency": new_balance_in_base_currency }) + + if not accounts: + self.throw_invalid_response_message(account_details) + return accounts def get_accounts_from_gle(self): @@ -83,11 +88,18 @@ class ExchangeRateRevaluation(Document): return account_details + def throw_invalid_response_message(self, account_details): + if account_details: + message = _("No outstanding invoices require exchange rate revaluation") + else: + message = _("No outstanding invoices found") + frappe.msgprint(message) + def make_jv_entry(self): if self.total_gain_loss == 0: return - unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company, + unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company, "unrealized_exchange_gain_loss_account") if not unrealized_exchange_gain_loss_account: frappe.throw(_("Please set Unrealized Exchange Gain/Loss Account in Company {0}") diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json index 98ecd486ca5..30ff9ebed5b 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json +++ b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.json @@ -1,461 +1,475 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 18:30:06.110433", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-04-13 18:30:06.110433", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "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": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "account", + "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": "Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party_type", - "fieldtype": "Link", - "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": "Party Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "party_type", + "fieldtype": "Link", + "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": "Party Type", + "length": 0, + "no_copy": 0, + "options": "DocType", + "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "party", - "fieldtype": "Dynamic Link", - "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": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "party", + "fieldtype": "Dynamic Link", + "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": "Party", + "length": 0, + "no_copy": 0, + "options": "party_type", + "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account_currency", - "fieldtype": "Link", - "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": "Account Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "account_currency", + "fieldtype": "Link", + "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": "Account Currency", + "length": 0, + "no_copy": 0, + "options": "Currency", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "balance_in_account_currency", - "fieldtype": "Currency", - "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": "Balance In Account Currency", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "balance_in_account_currency", + "fieldtype": "Currency", + "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": "Balance In Account Currency", + "length": 0, + "no_copy": 0, + "options": "account_currency", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "balances", - "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, - "label": "", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "balances", + "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, + "label": "", + "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_exchange_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": "Current Exchange Rate", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "current_exchange_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": "Current Exchange Rate", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "balance_in_base_currency", - "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": "Balance In Base Currency", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "balance_in_base_currency", + "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": "Balance In Base Currency", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_9", + "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_exchange_rate", - "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": "New Exchange 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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "new_exchange_rate", + "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": "New Exchange 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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_balance_in_base_currency", - "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": "New Balance In Base Currency", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "new_balance_in_base_currency", + "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": "New Balance In Base Currency", + "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 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gain_loss", - "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": "Gain/Loss", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "gain_loss", + "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": "Gain/Loss", + "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 } - ], - "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": "2019-01-07 16:52:07.327930", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Exchange Rate Revaluation Account", - "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, + ], + "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": "2019-06-26 18:57:51.762345", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Exchange Rate Revaluation Account", + "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, "track_views": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 7f0f60ff02e..186f3fd1e60 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -350,7 +350,7 @@ def filter_pricing_rules(args, pricing_rules): if len(pricing_rules) > 1: rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules])) if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage": - pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \ + pricing_rules = list(filter(lambda x: x.for_price_list==args.price_list, pricing_rules)) \ or pricing_rules if len(pricing_rules) > 1 and not args.for_shopping_cart: @@ -373,7 +373,7 @@ def apply_internal_priority(pricing_rules, field_set, args): filtered_rules = [] for field in field_set: if args.get(field): - filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules) + filtered_rules = list(filter(lambda x: x[field]==args[field], pricing_rules)) if filtered_rules: break return filtered_rules or pricing_rules diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index e4f37c44ffc..9b1ee496753 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -18,9 +18,6 @@ class TestPricingRule(unittest.TestCase): frappe.db.sql("delete from `tabPricing Rule`") def test_pricing_rule_for_discount(self): - from erpnext.stock.get_item_details import get_item_details - from frappe import MandatoryError - test_record = { "doctype": "Pricing Rule", "title": "_Test Pricing Rule", @@ -94,9 +91,6 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(details.get("discount_percentage"), 15) def test_pricing_rule_for_margin(self): - from erpnext.stock.get_item_details import get_item_details - from frappe import MandatoryError - test_record = { "doctype": "Pricing Rule", "title": "_Test Pricing Rule", @@ -139,9 +133,6 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(details.get("margin_rate_or_amount"), 10) def test_pricing_rule_for_variants(self): - from erpnext.stock.get_item_details import get_item_details - from frappe import MandatoryError - if not frappe.db.exists("Item", "Test Variant PRT"): frappe.get_doc({ "doctype": "Item", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 2d415bb5b17..87835dcdb83 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -157,7 +157,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ can_change_release_date: function(date) { const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate()); if (diff < 0) { - frappe.throw('New release date should be in the future'); + frappe.throw(__('New release date should be in the future')); return false; } else { return true; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a33e3f760d0..7edd1eb1bc6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -484,9 +484,13 @@ class PurchaseInvoice(BuyingController): "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"])) elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): + + expense_account = (item.expense_account + if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) + gl_entries.append( self.get_gl_dict({ - "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account, + "account": expense_account, "against": self.supplier, "debit": flt(item.base_net_amount, item.precision("base_net_amount")), "debit_in_account_currency": (flt(item.base_net_amount, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 5393f5d37fb..df1dae34f35 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -174,9 +174,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice", source_doctype: "Quotation", target: me.frm, - setters: { - customer: me.frm.doc.customer || undefined, - }, + setters: [{ + fieldtype: 'Link', + label: __('Customer'), + options: 'Customer', + fieldname: 'party_name', + default: me.frm.doc.customer, + }], get_query_filters: { docstatus: 1, status: ["!=", "Lost"], diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ca94b52c532..124b6d34a36 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -487,7 +487,7 @@ class SalesInvoice(SellingController): """Set against account for debit to account""" against_acc = [] for d in self.get('items'): - if d.income_account not in against_acc: + if d.income_account and d.income_account not in against_acc: against_acc.append(d.income_account) self.against_income_account = ','.join(against_acc) @@ -770,7 +770,14 @@ class SalesInvoice(SellingController): if item.is_fixed_asset: asset = frappe.get_doc("Asset", item.asset) - fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount) + if (len(asset.finance_books) > 1 and not item.finance_book + and asset.finance_books[0].finance_book): + frappe.throw(_("Select finance book for the item {0} at row {1}") + .format(item.item_code, item.idx)) + + fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, + item.base_net_amount, item.finance_book) + for gle in fixed_asset_gl_entries: gle["against"] = self.customer gl_entries.append(self.get_gl_dict(gle)) @@ -778,10 +785,13 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) else: - account_currency = get_account_currency(item.income_account) + income_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) + + account_currency = get_account_currency(income_account) gl_entries.append( self.get_gl_dict({ - "account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account, + "account": income_account, "against": self.customer, "credit": flt(item.base_net_amount, item.precision("base_net_amount")), "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index a95f3146616..5e712d1846e 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -21,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "barcode", "fieldtype": "Data", "hidden": 0, @@ -52,6 +53,7 @@ "bold": 1, "collapsible": 0, "columns": 4, + "fetch_if_empty": 0, "fieldname": "item_code", "fieldtype": "Link", "hidden": 0, @@ -86,6 +88,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break1", "fieldtype": "Column Break", "hidden": 0, @@ -116,6 +119,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "item_name", "fieldtype": "Data", "hidden": 0, @@ -149,6 +153,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_item_code", "fieldtype": "Data", "hidden": 1, @@ -180,6 +185,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_6", "fieldtype": "Section Break", "hidden": 0, @@ -212,6 +218,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Text Editor", "hidden": 0, @@ -247,6 +254,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_8", "fieldtype": "Column Break", "hidden": 0, @@ -278,6 +286,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "image_view", "fieldtype": "Image", "hidden": 0, @@ -311,6 +320,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -343,6 +353,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "quantity_and_rate", "fieldtype": "Section Break", "hidden": 0, @@ -374,6 +385,7 @@ "bold": 1, "collapsible": 0, "columns": 2, + "fetch_if_empty": 0, "fieldname": "qty", "fieldtype": "Float", "hidden": 0, @@ -407,6 +419,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "stock_uom", "fieldtype": "Link", "hidden": 0, @@ -439,6 +452,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break2", "fieldtype": "Column Break", "hidden": 0, @@ -469,6 +483,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "uom", "fieldtype": "Link", "hidden": 0, @@ -502,6 +517,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "conversion_factor", "fieldtype": "Float", "hidden": 0, @@ -534,6 +550,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "stock_qty", "fieldtype": "Float", "hidden": 0, @@ -566,6 +583,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_17", "fieldtype": "Section Break", "hidden": 0, @@ -597,6 +615,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "price_list_rate", "fieldtype": "Currency", "hidden": 0, @@ -631,6 +650,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_price_list_rate", "fieldtype": "Currency", "hidden": 0, @@ -665,6 +685,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_and_margin", "fieldtype": "Section Break", "hidden": 0, @@ -698,6 +719,7 @@ "collapsible": 0, "columns": 0, "depends_on": "price_list_rate", + "fetch_if_empty": 0, "fieldname": "margin_type", "fieldtype": "Select", "hidden": 0, @@ -732,6 +754,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.margin_type && doc.price_list_rate", + "fetch_if_empty": 0, "fieldname": "margin_rate_or_amount", "fieldtype": "Float", "hidden": 0, @@ -765,6 +788,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount", + "fetch_if_empty": 0, "fieldname": "rate_with_margin", "fieldtype": "Currency", "hidden": 0, @@ -798,6 +822,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -830,6 +855,7 @@ "collapsible": 0, "columns": 0, "depends_on": "price_list_rate", + "fetch_if_empty": 0, "fieldname": "discount_percentage", "fieldtype": "Percent", "hidden": 0, @@ -865,6 +891,7 @@ "collapsible": 0, "columns": 0, "depends_on": "discount_percentage", + "fetch_if_empty": 0, "fieldname": "discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -899,6 +926,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount", + "fetch_if_empty": 0, "fieldname": "base_rate_with_margin", "fieldtype": "Currency", "hidden": 0, @@ -932,6 +960,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break1", "fieldtype": "Section Break", "hidden": 0, @@ -962,6 +991,7 @@ "bold": 1, "collapsible": 0, "columns": 2, + "fetch_if_empty": 0, "fieldname": "rate", "fieldtype": "Currency", "hidden": 0, @@ -996,6 +1026,7 @@ "bold": 0, "collapsible": 0, "columns": 2, + "fetch_if_empty": 0, "fieldname": "amount", "fieldtype": "Currency", "hidden": 0, @@ -1030,6 +1061,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break3", "fieldtype": "Column Break", "hidden": 0, @@ -1060,6 +1092,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rate", "fieldtype": "Currency", "hidden": 0, @@ -1094,6 +1127,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_amount", "fieldtype": "Currency", "hidden": 0, @@ -1128,6 +1162,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "pricing_rule", "fieldtype": "Link", "hidden": 0, @@ -1160,6 +1195,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_21", "fieldtype": "Section Break", "hidden": 0, @@ -1191,6 +1227,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "net_rate", "fieldtype": "Currency", "hidden": 0, @@ -1224,6 +1261,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "net_amount", "fieldtype": "Currency", "hidden": 0, @@ -1257,6 +1295,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_24", "fieldtype": "Column Break", "hidden": 0, @@ -1288,6 +1327,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_net_rate", "fieldtype": "Currency", "hidden": 0, @@ -1321,6 +1361,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_net_amount", "fieldtype": "Currency", "hidden": 0, @@ -1355,6 +1396,7 @@ "collapsible": 1, "collapsible_depends_on": "eval:doc.delivered_by_supplier==1", "columns": 0, + "fetch_if_empty": 0, "fieldname": "drop_ship", "fieldtype": "Section Break", "hidden": 0, @@ -1387,6 +1429,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "delivered_by_supplier", "fieldtype": "Check", "hidden": 0, @@ -1419,6 +1462,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "accounting", "fieldtype": "Section Break", "hidden": 0, @@ -1450,6 +1494,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "income_account", "fieldtype": "Link", "hidden": 0, @@ -1486,6 +1531,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "expense_account", "fieldtype": "Link", "hidden": 0, @@ -1519,6 +1565,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break4", "fieldtype": "Column Break", "hidden": 0, @@ -1550,6 +1597,7 @@ "collapsible": 0, "columns": 0, "default": ":Company", + "fetch_if_empty": 0, "fieldname": "cost_center", "fieldtype": "Link", "hidden": 0, @@ -1586,6 +1634,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "deferred_revenue", "fieldtype": "Section Break", "hidden": 0, @@ -1619,6 +1668,7 @@ "collapsible": 0, "columns": 0, "depends_on": "enable_deferred_revenue", + "fetch_if_empty": 0, "fieldname": "deferred_revenue_account", "fieldtype": "Link", "hidden": 0, @@ -1653,6 +1703,7 @@ "collapsible": 0, "columns": 0, "depends_on": "enable_deferred_revenue", + "fetch_if_empty": 0, "fieldname": "service_stop_date", "fieldtype": "Date", "hidden": 0, @@ -1686,6 +1737,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "enable_deferred_revenue", "fieldtype": "Check", "hidden": 0, @@ -1718,6 +1770,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_50", "fieldtype": "Column Break", "hidden": 0, @@ -1750,6 +1803,7 @@ "collapsible": 0, "columns": 0, "depends_on": "enable_deferred_revenue", + "fetch_if_empty": 0, "fieldname": "service_start_date", "fieldtype": "Date", "hidden": 0, @@ -1783,6 +1837,7 @@ "collapsible": 0, "columns": 0, "depends_on": "enable_deferred_revenue", + "fetch_if_empty": 0, "fieldname": "service_end_date", "fieldtype": "Date", "hidden": 0, @@ -1815,6 +1870,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_18", "fieldtype": "Section Break", "hidden": 0, @@ -1847,6 +1903,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "weight_per_unit", "fieldtype": "Float", "hidden": 0, @@ -1880,6 +1937,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_weight", "fieldtype": "Float", "hidden": 0, @@ -1912,6 +1970,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_21", "fieldtype": "Column Break", "hidden": 0, @@ -1943,6 +2002,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "weight_uom", "fieldtype": "Link", "hidden": 0, @@ -1978,6 +2038,7 @@ "collapsible_depends_on": "eval:doc.serial_no || doc.batch_no", "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "warehouse_and_reference", "fieldtype": "Section Break", "hidden": 0, @@ -2009,6 +2070,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "warehouse", "fieldtype": "Link", "hidden": 0, @@ -2043,6 +2105,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 1, @@ -2077,6 +2140,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:!doc.__islocal", + "fetch_if_empty": 0, "fieldname": "quality_inspection", "fieldtype": "Link", "hidden": 0, @@ -2110,6 +2174,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "batch_no", "fieldtype": "Link", "hidden": 0, @@ -2142,6 +2207,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break5", "fieldtype": "Column Break", "hidden": 0, @@ -2172,6 +2238,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "allow_zero_valuation_rate", "fieldtype": "Check", "hidden": 0, @@ -2204,6 +2271,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "serial_no", "fieldtype": "Small Text", "hidden": 0, @@ -2238,6 +2306,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "item_group", "fieldtype": "Link", "hidden": 1, @@ -2272,6 +2341,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "brand", "fieldtype": "Data", "hidden": 1, @@ -2305,6 +2375,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "item_tax_rate", "fieldtype": "Small Text", "hidden": 1, @@ -2338,6 +2409,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "actual_batch_qty", "fieldtype": "Float", "hidden": 0, @@ -2372,6 +2444,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "actual_qty", "fieldtype": "Float", "hidden": 0, @@ -2405,6 +2478,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "edit_references", "fieldtype": "Section Break", "hidden": 0, @@ -2437,6 +2511,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sales_order", "fieldtype": "Link", "hidden": 0, @@ -2471,6 +2546,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "so_detail", "fieldtype": "Data", "hidden": 1, @@ -2504,6 +2580,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_74", "fieldtype": "Column Break", "hidden": 0, @@ -2535,6 +2612,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "delivery_note", "fieldtype": "Link", "hidden": 0, @@ -2569,6 +2647,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "dn_detail", "fieldtype": "Data", "hidden": 1, @@ -2602,6 +2681,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "delivered_qty", "fieldtype": "Float", "hidden": 0, @@ -2635,6 +2715,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_fixed_asset", "fieldtype": "Check", "hidden": 1, @@ -2667,6 +2748,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "asset", "fieldtype": "Link", "hidden": 0, @@ -2700,6 +2782,42 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "asset", + "fetch_if_empty": 0, + "fieldname": "finance_book", + "fieldtype": "Link", + "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": "Finance Book", + "length": 0, + "no_copy": 0, + "options": "Finance Book", + "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_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_54", "fieldtype": "Section Break", "hidden": 0, @@ -2731,6 +2849,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "page_break", "fieldtype": "Check", "hidden": 0, @@ -2766,7 +2885,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2019-02-18 18:59:52.223628", + "modified": "2019-06-28 17:04:25.870346", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index d3fef6023ba..625979bee1f 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document from erpnext.utilities.product import get_price @@ -13,7 +14,7 @@ class SubscriptionPlan(Document): def validate_interval_count(self): if self.billing_interval_count < 1: - frappe.throw('Billing Interval Count cannot be less than 1') + frappe.throw(_('Billing Interval Count cannot be less than 1')) @frappe.whitelist() def get_plan_rate(plan, quantity=1, customer=None): @@ -26,7 +27,7 @@ def get_plan_rate(plan, quantity=1, customer=None): customer_group = frappe.db.get_value("Customer", customer, "customer_group") else: customer_group = None - + price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity) if not price: return 0 diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 25301967080..638e57ed2b9 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -22,7 +22,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices = [] # create invoices for lower than single threshold tax rate - for _ in xrange(2): + for _ in range(2): pi = create_purchase_invoice(supplier = "Test TDS Supplier") pi.submit() invoices.append(pi) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index f22f3a10091..4bc29da2c7d 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -10,4 +10,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "Check", "default": 1 }); + + frappe.query_reports["Balance Sheet"]["filters"].push({ + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" + }); }); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 391f57beac6..04221110930 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -15,4 +15,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Accumulated Values"), "fieldtype": "Check" }); + + frappe.query_reports["Cash Flow"]["filters"].push({ + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" + }); }); \ No newline at end of file diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index f048c1acda5..0f9813947a7 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -14,8 +14,8 @@ def execute(filters=None): if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')): from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom return execute_custom(filters=filters) - - period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.periodicity, filters.accumulated_values, filters.company) cash_flow_accounts = get_cash_flow_accounts() @@ -25,18 +25,18 @@ def execute(filters=None): accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True) expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters, accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True) - + net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company) data = [] company_currency = frappe.get_cached_value('Company', filters.company, "default_currency") - + for cash_flow_account in cash_flow_accounts: section_data = [] data.append({ - "account_name": cash_flow_account['section_header'], + "account_name": cash_flow_account['section_header'], "parent_account": None, - "indent": 0.0, + "indent": 0.0, "account": cash_flow_account['section_header'] }) @@ -44,18 +44,18 @@ def execute(filters=None): # add first net income in operations section if net_profit_loss: net_profit_loss.update({ - "indent": 1, + "indent": 1, "parent_account": cash_flow_accounts[0]['section_header'] }) data.append(net_profit_loss) section_data.append(net_profit_loss) for account in cash_flow_account['account_types']: - account_data = get_account_type_based_data(filters.company, - account['account_type'], period_list, filters.accumulated_values) + account_data = get_account_type_based_data(filters.company, + account['account_type'], period_list, filters.accumulated_values, filters) account_data.update({ "account_name": account['label'], - "account": account['label'], + "account": account['label'], "indent": 1, "parent_account": cash_flow_account['section_header'], "currency": company_currency @@ -63,7 +63,7 @@ def execute(filters=None): data.append(account_data) section_data.append(account_data) - add_total_row_account(data, section_data, cash_flow_account['section_footer'], + add_total_row_account(data, section_data, cash_flow_account['section_footer'], period_list, company_currency) add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency) @@ -105,13 +105,15 @@ def get_cash_flow_accounts(): # combine all cash flow accounts for iteration return [operation_accounts, investing_accounts, financing_accounts] -def get_account_type_based_data(company, account_type, period_list, accumulated_values): +def get_account_type_based_data(company, account_type, period_list, accumulated_values, filters): data = {} total = 0 for period in period_list: start_date = get_start_date(period, accumulated_values, company) - amount = get_account_type_based_gl_data(company, start_date, period['to_date'], account_type) + amount = get_account_type_based_gl_data(company, start_date, + period['to_date'], account_type, filters) + if amount and account_type == "Depreciation": amount *= -1 @@ -121,14 +123,24 @@ def get_account_type_based_data(company, account_type, period_list, accumulated_ data["total"] = total return data -def get_account_type_based_gl_data(company, start_date, end_date, account_type): +def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters): + cond = "" + + if filters.finance_book: + cond = " and finance_book = '%s'" %(frappe.db.escape(filters.finance_book)) + if filters.include_default_book_entries: + company_fb = frappe.db.get_value("Company", company, 'default_finance_book') + + cond = """ and finance_book in ('%s', '%s') + """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) + gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) from `tabGL Entry` where company=%s and posting_date >= %s and posting_date <= %s and voucher_type != 'Period Closing Voucher' - and account in ( SELECT name FROM tabAccount WHERE account_type = %s) - """, (company, start_date, end_date, account_type)) + and account in ( SELECT name FROM tabAccount WHERE account_type = %s) {cond} + """.format(cond=cond), (company, start_date, end_date, account_type)) return gl_sum[0] if gl_sum and gl_sum[0] else 0 @@ -154,7 +166,7 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated key = period if consolidated else period['key'] total_row.setdefault(key, 0.0) total_row[key] += row.get(key, 0.0) - + total_row.setdefault("total", 0.0) total_row["total"] += row["total"] diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 7b373f0d9ae..e69a993e8ce 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -55,5 +55,10 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldtype": "Check", "default": 0 }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" + } ] } diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 1bb8580f644..ed000bbac4c 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -355,7 +355,8 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g "lft": root_lft, "rgt": root_rgt, "company": d.name, - "finance_book": filters.get("finance_book") + "finance_book": filters.get("finance_book"), + "company_fb": frappe.db.get_value("Company", d.name, 'default_finance_book') }, as_dict=True) @@ -386,7 +387,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions.append("gl.posting_date >= %(from_date)s") if filters.get("finance_book"): - additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')") + if filters.get("include_default_book_entries"): + additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + else: + additional_conditions.append("finance_book in (%(finance_book)s)") return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index fe030c5f1ce..9765310fc9b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -359,7 +359,8 @@ def set_gl_entries_by_account( "to_date": to_date, "cost_center": filters.cost_center, "project": filters.project, - "finance_book": filters.get("finance_book") + "finance_book": filters.get("finance_book"), + "company_fb": frappe.db.get_value("Company", company, 'default_finance_book') }, as_dict=True) @@ -393,7 +394,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions.append("cost_center in %(cost_center)s") if filters.get("finance_book"): - additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')") + if filters.get("include_default_book_entries"): + additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + else: + additional_conditions.append("finance_book in (%(finance_book)s)") return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index e74c16af6fe..e162606b7ac 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -216,6 +216,11 @@ frappe.query_reports["General Ledger"] = { "fieldname": "show_opening_entries", "label": __("Show Opening Entries"), "fieldtype": "Check" + }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" } ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 4bea0978025..ae63ab60329 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -127,13 +127,16 @@ def get_gl_entries(filters): order_by_statement = "order by posting_date, voucher_type, voucher_no" if filters.get("group_by") == _("Group by Voucher (Consolidated)"): - group_by_statement = """group by voucher_type, voucher_no, account, - cost_center, against_voucher_type, against_voucher, posting_date""" + group_by_statement = "group by voucher_type, voucher_no, account, cost_center" select_fields = """, sum(debit) as debit, sum(credit) as credit, sum(debit_in_account_currency) as debit_in_account_currency, sum(credit_in_account_currency) as credit_in_account_currency""" + if filters.get("include_default_book_entries"): + filters['company_fb'] = frappe.db.get_value("Company", + filters.get("company"), 'default_finance_book') + gl_entries = frappe.db.sql( """ select @@ -189,7 +192,10 @@ def get_conditions(filters): conditions.append("project in %(project)s") if filters.get("finance_book"): - conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')") + if filters.get("include_default_book_entries"): + conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + else: + conditions.append("finance_book in (%(finance_book)s)") from frappe.desk.reportview import build_match_conditions match_conditions = build_match_conditions("GL Entry") diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py index 41ec9b74665..9f19c0d7591 100644 --- a/erpnext/accounts/report/non_billed_report.py +++ b/erpnext/accounts/report/non_billed_report.py @@ -12,20 +12,21 @@ def get_ordered_to_be_billed_data(args): child_tab = doctype + " Item" precision = get_field_precision(frappe.get_meta(child_tab).get_field("billed_amt"), currency=get_default_currency()) or 2 - + project_field = get_project_field(doctype, party) return frappe.db.sql(""" Select `{parent_tab}`.name, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name, {project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount, - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)), + (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)), (`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))), `{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company from `{parent_tab}`, `{child_tab}` where - `{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1 and `{parent_tab}`.status != 'Closed' + `{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1 + and `{parent_tab}`.status not in ('Closed', 'Completed') and `{child_tab}`.amount > 0 and round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) < `{child_tab}`.base_amount order by diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 250e516d7d2..7741a45feeb 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -41,6 +41,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldname": "accumulated_values", "label": __("Accumulated Values"), "fieldtype": "Check" + }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" } ); }); diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 48d7361fe0a..ac11868cabb 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): else: chart["type"] = "line" + chart["fieldtype"] = "Currency" + return chart \ No newline at end of file diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 62843e74efc..a51c4276301 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -47,8 +47,8 @@ class TestSalesPaymentSummary(unittest.TestCase): pe.submit() mop = get_mode_of_payments(filters) - self.assertTrue('Credit Card' in mop.values()[0]) - self.assertTrue('Cash' in mop.values()[0]) + self.assertTrue('Credit Card' in list(mop.values())[0]) + self.assertTrue('Cash' in list(mop.values())[0]) # Cancel all Cash payment entry and check if this mode of payment is still fetched. payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"]) @@ -57,8 +57,8 @@ class TestSalesPaymentSummary(unittest.TestCase): pe.cancel() mop = get_mode_of_payments(filters) - self.assertTrue('Credit Card' in mop.values()[0]) - self.assertTrue('Cash' not in mop.values()[0]) + self.assertTrue('Credit Card' in list(mop.values())[0]) + self.assertTrue('Cash' not in list(mop.values())[0]) def test_get_mode_of_payments_details(self): filters = get_filters() @@ -84,7 +84,7 @@ class TestSalesPaymentSummary(unittest.TestCase): mopd = get_mode_of_payment_details(filters) - mopd_values = mopd.values()[0] + mopd_values = list(mopd.values())[0] for mopd_value in mopd_values: if mopd_value[0] == "Credit Card": cc_init_amount = mopd_value[1] @@ -96,7 +96,7 @@ class TestSalesPaymentSummary(unittest.TestCase): pe.cancel() mopd = get_mode_of_payment_details(filters) - mopd_values = mopd.values()[0] + mopd_values = list(mopd.values())[0] for mopd_value in mopd_values: if mopd_value[0] == "Credit Card": cc_final_amount = mopd_value[1] diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index b5f0186d4d2..5758b0bc6b1 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -105,7 +105,7 @@ def get_rootwise_opening_balances(filters, report_type): if filters.finance_book: fb_conditions = " and finance_book = %(finance_book)s" if filters.include_default_book_entries: - fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s) or finance_book is null)" + fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s))" additional_conditions += fb_conditions diff --git a/erpnext/agriculture/doctype/disease/disease.py b/erpnext/agriculture/doctype/disease/disease.py index c7707a54652..8a0e6f33d68 100644 --- a/erpnext/agriculture/doctype/disease/disease.py +++ b/erpnext/agriculture/doctype/disease/disease.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe import _ class Disease(Document): def validate(self): diff --git a/erpnext/agriculture/doctype/soil_texture/soil_texture.py b/erpnext/agriculture/doctype/soil_texture/soil_texture.py index 8c1d7ed5ac1..2254bf87914 100644 --- a/erpnext/agriculture/doctype/soil_texture/soil_texture.py +++ b/erpnext/agriculture/doctype/soil_texture/soil_texture.py @@ -7,7 +7,6 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.utils import flt, cint -from frappe import _ class SoilTexture(Document): soil_edit_order = [2, 1, 0] @@ -35,8 +34,8 @@ class SoilTexture(Document): if sum(self.soil_edit_order) < 5: return last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order)) - # set composition of the last edited soil - self.set( self.soil_types[last_edit_index], + # set composition of the last edited soil + self.set( self.soil_types[last_edit_index], 100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index]))) # calculate soil type diff --git a/erpnext/agriculture/doctype/water_analysis/water_analysis.py b/erpnext/agriculture/doctype/water_analysis/water_analysis.py index 88f1fbd9cce..bd2cd118e1d 100644 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py +++ b/erpnext/agriculture/doctype/water_analysis/water_analysis.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe import _ class WaterAnalysis(Document): def load_contents(self): diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 72f5c627a71..45f7b30ae8a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -291,16 +291,19 @@ class Asset(AccountsController): def validate_expected_value_after_useful_life(self): for row in self.get('finance_books'): - accumulated_depreciation_after_full_schedule = max([d.accumulated_depreciation_amount - for d in self.get("schedules") if cint(d.finance_book_id) == row.idx]) + accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount + for d in self.get("schedules") if cint(d.finance_book_id) == row.idx] - asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) - - flt(accumulated_depreciation_after_full_schedule), - self.precision('gross_purchase_amount')) + if accumulated_depreciation_after_full_schedule: + accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule) - if row.expected_value_after_useful_life < asset_value_after_full_schedule: - frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}") - .format(row.idx, asset_value_after_full_schedule)) + asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) - + flt(accumulated_depreciation_after_full_schedule), + self.precision('gross_purchase_amount')) + + if row.expected_value_after_useful_life < asset_value_after_full_schedule: + frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}") + .format(row.idx, asset_value_after_full_schedule)) def validate_cancellation(self): if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 797075b8873..61108ec4a37 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -36,7 +36,7 @@ def make_depreciation_entry(asset_name, date=None): fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ get_depreciation_accounts(asset) - depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company, + depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]) depreciation_cost_center = asset.cost_center or depreciation_cost_center @@ -70,7 +70,7 @@ def make_depreciation_entry(asset_name, date=None): je.submit() d.db_set("journal_entry", je.name) - + idx = cint(d.finance_book_id) finance_books = asset.get('finance_books')[idx - 1] finance_books.value_after_depreciation -= d.depreciation_amount @@ -88,15 +88,15 @@ def get_depreciation_accounts(asset): fieldname = ['fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account'], as_dict=1) - if accounts: + if accounts: fixed_asset_account = accounts.fixed_asset_account accumulated_depreciation_account = accounts.accumulated_depreciation_account depreciation_expense_account = accounts.depreciation_expense_account - + if not accumulated_depreciation_account or not depreciation_expense_account: - accounts = frappe.get_cached_value('Company', asset.company, + accounts = frappe.get_cached_value('Company', asset.company, ["accumulated_depreciation_account", "depreciation_expense_account"]) - + if not accumulated_depreciation_account: accumulated_depreciation_account = accounts[0] if not depreciation_expense_account: @@ -135,11 +135,11 @@ def scrap_asset(asset_name): je.flags.ignore_permissions = True je.submit() - + frappe.db.set_value("Asset", asset_name, "disposal_date", today()) frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name) asset.set_status("Scrapped") - + frappe.msgprint(_("Asset scrapped via Journal Entry {0}").format(je.name)) @frappe.whitelist() @@ -147,21 +147,29 @@ def restore_asset(asset_name): asset = frappe.get_doc("Asset", asset_name) je = asset.journal_entry_for_scrap - + asset.db_set("disposal_date", None) asset.db_set("journal_entry_for_scrap", None) - + frappe.get_doc("Journal Entry", je).cancel() asset.set_status() @frappe.whitelist() -def get_gl_entries_on_asset_disposal(asset, selling_amount=0): +def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None): fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset) disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company) depreciation_cost_center = asset.cost_center or depreciation_cost_center - accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.value_after_depreciation) + idx = 1 + if finance_book: + for d in asset.finance_books: + if d.finance_book == finance_book: + idx = d.idx + break + + value_after_depreciation = asset.finance_books[idx - 1].value_after_depreciation + accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation) gl_entries = [ { @@ -176,7 +184,7 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0): } ] - profit_amount = flt(selling_amount) - flt(asset.value_after_depreciation) + profit_amount = flt(selling_amount) - flt(value_after_depreciation) if profit_amount: debit_or_credit = "debit" if profit_amount < 0 else "credit" gl_entries.append({ @@ -190,7 +198,7 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0): @frappe.whitelist() def get_disposal_account_and_cost_center(company): - disposal_account, depreciation_cost_center = frappe.get_cached_value('Company', company, + disposal_account, depreciation_cost_center = frappe.get_cached_value('Company', company, ["disposal_account", "depreciation_cost_center"]) if not disposal_account: diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index ef85ffa1cb8..fceccfbd1c9 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -366,8 +366,9 @@ class TestAsset(unittest.TestCase): self.assertTrue(asset.journal_entry_for_scrap) expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 100000.0, 0.0), - ("_Test Fixed Asset - _TC", 0.0, 100000.0) + ("_Test Accumulated Depreciations - _TC", 147.54, 0.0), + ("_Test Fixed Asset - _TC", 0.0, 100000.0), + ("_Test Gain/Loss on Asset Disposal - _TC", 99852.46, 0.0) ) gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` @@ -411,9 +412,9 @@ class TestAsset(unittest.TestCase): self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 100000.0, 0.0), + ("_Test Accumulated Depreciations - _TC", 23051.47, 0.0), ("_Test Fixed Asset - _TC", 0.0, 100000.0), - ("_Test Gain/Loss on Asset Disposal - _TC", 0, 25000.0), + ("_Test Gain/Loss on Asset Disposal - _TC", 51948.53, 0.0), ("Debtors - _TC", 25000.0, 0.0) ) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index ac3c3507027..56425a0dcb4 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -36,7 +36,7 @@ class AssetValueAdjustment(Document): fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ get_depreciation_accounts(asset) - depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company, + depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]) je = frappe.new_doc("Journal Entry") @@ -75,8 +75,8 @@ class AssetValueAdjustment(Document): rate_per_day = flt(d.value_after_depreciation) / flt(total_days) from_date = self.date else: - no_of_depreciations = len([e.name for e in asset.schedules - if (cint(s.finance_book_id) == d.idx and not e.journal_entry)]) + no_of_depreciations = len([s.name for s in asset.schedules + if (cint(s.finance_book_id) == d.idx and not s.journal_entry)]) value_after_depreciation = d.value_after_depreciation for data in asset.schedules: diff --git a/erpnext/assets/doctype/location/test_location.py b/erpnext/assets/doctype/location/test_location.py index 22d25b5e11c..c98b0b0936c 100644 --- a/erpnext/assets/doctype/location/test_location.py +++ b/erpnext/assets/doctype/location/test_location.py @@ -25,9 +25,12 @@ class TestLocation(unittest.TestCase): temp['features'][0]['properties']['feature_of'] = location formatted_locations.extend(temp['features']) - formatted_location_string = str(formatted_locations) test_location = frappe.get_doc('Location', 'Test Location Area') test_location.save() - self.assertEqual(formatted_location_string, str(json.loads(test_location.get('location'))['features'])) + test_location_features = json.loads(test_location.get('location'))['features'] + ordered_test_location_features = sorted(test_location_features, key=lambda x: x['properties']['feature_of']) + ordered_formatted_locations = sorted(formatted_locations, key=lambda x: x['properties']['feature_of']) + + self.assertEqual(ordered_formatted_locations, ordered_test_location_features) self.assertEqual(area, test_location.get('area')) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index c032df32706..7b90b2b2a6f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -104,7 +104,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(doc.docstatus == 1 && !in_list(["Closed", "Delivered"], doc.status)) { if (this.frm.has_perm("submit")) { - if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) { + if(flt(doc.per_billed, 6) < 100 || flt(doc.per_received, 6) < 100) { cur_frm.add_custom_button(__('Close'), this.close_purchase_order, __("Status")); } } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 8c586d14669..0e145009efa 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -20,6 +20,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "supplier_section", "fieldtype": "Section Break", "hidden": 0, @@ -53,6 +54,7 @@ "collapsible": 0, "columns": 0, "default": "{supplier_name}", + "fetch_if_empty": 0, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -86,6 +88,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -121,6 +124,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "supplier", "fieldtype": "Link", "hidden": 0, @@ -156,6 +160,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", + "fetch_if_empty": 0, "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", "hidden": 0, @@ -189,6 +194,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "supplier.supplier_name", + "fetch_if_empty": 0, "fieldname": "supplier_name", "fieldtype": "Data", "hidden": 0, @@ -222,6 +228,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -256,6 +263,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break1", "fieldtype": "Column Break", "hidden": 0, @@ -290,6 +298,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "transaction_date", "fieldtype": "Date", "hidden": 0, @@ -324,6 +333,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "schedule_date", "fieldtype": "Date", "hidden": 0, @@ -357,6 +367,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.docstatus===1", + "fetch_if_empty": 0, "fieldname": "order_confirmation_no", "fieldtype": "Data", "hidden": 0, @@ -390,6 +401,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.order_confirmation_no", + "fetch_if_empty": 0, "fieldname": "order_confirmation_date", "fieldtype": "Date", "hidden": 0, @@ -422,6 +434,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -457,6 +470,7 @@ "collapsible": 0, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "drop_ship", "fieldtype": "Section Break", "hidden": 0, @@ -490,6 +504,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer", "fieldtype": "Link", "hidden": 0, @@ -524,6 +539,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer_name", "fieldtype": "Data", "hidden": 0, @@ -556,6 +572,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -588,6 +605,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "customer_contact_person", "fieldtype": "Link", "hidden": 0, @@ -621,6 +639,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, @@ -653,6 +672,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, @@ -685,6 +705,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "customer_contact_email", "fieldtype": "Code", "hidden": 1, @@ -718,6 +739,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_addresses", "fieldtype": "Section Break", "hidden": 0, @@ -750,6 +772,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "supplier_address", "fieldtype": "Link", "hidden": 0, @@ -782,6 +805,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_person", "fieldtype": "Link", "hidden": 0, @@ -815,6 +839,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "address_display", "fieldtype": "Small Text", "hidden": 0, @@ -846,6 +871,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_display", "fieldtype": "Small Text", "hidden": 0, @@ -877,6 +903,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_mobile", "fieldtype": "Small Text", "hidden": 0, @@ -908,6 +935,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "contact_email", "fieldtype": "Small Text", "hidden": 0, @@ -939,6 +967,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break_address", "fieldtype": "Column Break", "hidden": 0, @@ -971,6 +1000,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "shipping_address", "fieldtype": "Link", "hidden": 0, @@ -1004,6 +1034,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_address_display", "fieldtype": "Small Text", "hidden": 0, @@ -1036,6 +1067,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "hidden": 0, @@ -1068,6 +1100,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", "hidden": 0, @@ -1103,6 +1136,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -1137,6 +1171,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "cb_price_list", "fieldtype": "Column Break", "hidden": 0, @@ -1167,6 +1202,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "buying_price_list", "fieldtype": "Link", "hidden": 0, @@ -1199,6 +1235,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "price_list_currency", "fieldtype": "Link", "hidden": 0, @@ -1231,6 +1268,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "plc_conversion_rate", "fieldtype": "Float", "hidden": 0, @@ -1263,6 +1301,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "hidden": 0, @@ -1294,6 +1333,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_warehouse", "fieldtype": "Section Break", "hidden": 0, @@ -1325,6 +1365,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "set_warehouse", "fieldtype": "Link", "hidden": 0, @@ -1358,6 +1399,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "col_break_warehouse", "fieldtype": "Column Break", "hidden": 0, @@ -1390,6 +1432,7 @@ "collapsible": 0, "columns": 0, "default": "No", + "fetch_if_empty": 0, "fieldname": "is_subcontracted", "fieldtype": "Select", "hidden": 0, @@ -1423,6 +1466,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.is_subcontracted==\"Yes\"", + "fetch_if_empty": 0, "fieldname": "supplier_warehouse", "fieldtype": "Link", "hidden": 0, @@ -1456,6 +1500,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items_section", "fieldtype": "Section Break", "hidden": 0, @@ -1489,6 +1534,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "scan_barcode", "fieldtype": "Data", "hidden": 0, @@ -1521,6 +1567,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "items", "fieldtype": "Table", "hidden": 0, @@ -1556,6 +1603,7 @@ "collapsible": 0, "collapsible_depends_on": "supplied_items", "columns": 0, + "fetch_if_empty": 0, "fieldname": "raw_material_details", "fieldtype": "Section Break", "hidden": 0, @@ -1589,6 +1637,7 @@ "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "supplied_items", "fieldtype": "Table", "hidden": 0, @@ -1623,6 +1672,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sb_last_purchase", "fieldtype": "Section Break", "hidden": 0, @@ -1653,6 +1703,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_qty", "fieldtype": "Float", "hidden": 0, @@ -1685,6 +1736,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total", "fieldtype": "Currency", "hidden": 0, @@ -1718,6 +1770,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_net_total", "fieldtype": "Currency", "hidden": 0, @@ -1752,6 +1805,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_26", "fieldtype": "Column Break", "hidden": 0, @@ -1782,6 +1836,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total", "fieldtype": "Currency", "hidden": 0, @@ -1815,6 +1870,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "net_total", "fieldtype": "Currency", "hidden": 0, @@ -1849,6 +1905,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_net_weight", "fieldtype": "Float", "hidden": 0, @@ -1881,6 +1938,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_section", "fieldtype": "Section Break", "hidden": 0, @@ -1915,6 +1973,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "taxes_and_charges", "fieldtype": "Link", "hidden": 0, @@ -1949,6 +2008,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_50", "fieldtype": "Column Break", "hidden": 0, @@ -1980,6 +2040,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "shipping_rule", "fieldtype": "Link", "hidden": 0, @@ -2013,6 +2074,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_52", "fieldtype": "Section Break", "hidden": 0, @@ -2044,6 +2106,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes", "fieldtype": "Table", "hidden": 0, @@ -2078,6 +2141,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", "hidden": 0, @@ -2110,6 +2174,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "other_charges_calculation", "fieldtype": "Text", "hidden": 0, @@ -2142,6 +2207,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "totals", "fieldtype": "Section Break", "hidden": 0, @@ -2175,6 +2241,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_taxes_and_charges_added", "fieldtype": "Currency", "hidden": 0, @@ -2209,6 +2276,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_taxes_and_charges_deducted", "fieldtype": "Currency", "hidden": 0, @@ -2243,6 +2311,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -2277,6 +2346,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_39", "fieldtype": "Column Break", "hidden": 0, @@ -2308,6 +2378,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_and_charges_added", "fieldtype": "Currency", "hidden": 0, @@ -2342,6 +2413,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "taxes_and_charges_deducted", "fieldtype": "Currency", "hidden": 0, @@ -2376,6 +2448,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "hidden": 0, @@ -2410,6 +2483,7 @@ "collapsible": 1, "collapsible_depends_on": "discount_amount", "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_section", "fieldtype": "Section Break", "hidden": 0, @@ -2443,6 +2517,7 @@ "collapsible": 0, "columns": 0, "default": "Grand Total", + "fetch_if_empty": 0, "fieldname": "apply_discount_on", "fieldtype": "Select", "hidden": 0, @@ -2476,6 +2551,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -2509,6 +2585,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_45", "fieldtype": "Column Break", "hidden": 0, @@ -2540,6 +2617,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "additional_discount_percentage", "fieldtype": "Float", "hidden": 0, @@ -2572,6 +2650,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "discount_amount", "fieldtype": "Currency", "hidden": 0, @@ -2605,6 +2684,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "totals_section", "fieldtype": "Section Break", "hidden": 0, @@ -2636,6 +2716,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2670,6 +2751,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2704,6 +2786,7 @@ "collapsible": 0, "columns": 0, "description": "In Words will be visible once you save the Purchase Order.", + "fetch_if_empty": 0, "fieldname": "base_in_words", "fieldtype": "Data", "hidden": 0, @@ -2737,6 +2820,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "base_rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2771,6 +2855,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break4", "fieldtype": "Column Break", "hidden": 0, @@ -2802,6 +2887,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "grand_total", "fieldtype": "Currency", "hidden": 0, @@ -2836,6 +2922,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounding_adjustment", "fieldtype": "Currency", "hidden": 0, @@ -2869,6 +2956,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rounded_total", "fieldtype": "Currency", "hidden": 0, @@ -2881,6 +2969,7 @@ "label": "Rounded Total", "length": 0, "no_copy": 0, + "options": "currency", "permlevel": 0, "precision": "", "print_hide": 0, @@ -2901,6 +2990,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "disable_rounded_total", "fieldtype": "Check", "hidden": 0, @@ -2933,6 +3023,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "in_words", "fieldtype": "Data", "hidden": 0, @@ -2966,6 +3057,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "advance_paid", "fieldtype": "Currency", "hidden": 0, @@ -2998,6 +3090,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hidden": 0, @@ -3030,6 +3123,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_terms_template", "fieldtype": "Link", "hidden": 0, @@ -3063,6 +3157,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_schedule", "fieldtype": "Table", "hidden": 0, @@ -3097,6 +3192,7 @@ "collapsible": 1, "collapsible_depends_on": "terms", "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms_section_break", "fieldtype": "Section Break", "hidden": 0, @@ -3130,6 +3226,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "tc_name", "fieldtype": "Link", "hidden": 0, @@ -3164,6 +3261,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "terms", "fieldtype": "Text Editor", "hidden": 0, @@ -3197,6 +3295,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "more_info", "fieldtype": "Section Break", "hidden": 0, @@ -3230,6 +3329,7 @@ "collapsible": 0, "columns": 0, "default": "Draft", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -3264,6 +3364,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "ref_sq", "fieldtype": "Data", "hidden": 1, @@ -3297,6 +3398,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "party_account_currency", "fieldtype": "Link", "hidden": 1, @@ -3330,6 +3432,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_74", "fieldtype": "Column Break", "hidden": 0, @@ -3363,6 +3466,7 @@ "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "", + "fetch_if_empty": 0, "fieldname": "per_received", "fieldtype": "Percent", "hidden": 0, @@ -3398,6 +3502,7 @@ "columns": 0, "depends_on": "eval:!doc.__islocal", "description": "", + "fetch_if_empty": 0, "fieldname": "per_billed", "fieldtype": "Percent", "hidden": 0, @@ -3431,6 +3536,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break5", "fieldtype": "Section Break", "hidden": 0, @@ -3465,6 +3571,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "letter_head", "fieldtype": "Link", "hidden": 0, @@ -3499,6 +3606,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "select_print_heading", "fieldtype": "Link", "hidden": 0, @@ -3533,6 +3641,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_86", "fieldtype": "Column Break", "hidden": 0, @@ -3565,6 +3674,7 @@ "collapsible": 0, "columns": 0, "description": "", + "fetch_if_empty": 0, "fieldname": "group_same_items", "fieldtype": "Check", "hidden": 0, @@ -3598,6 +3708,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "language", "fieldtype": "Data", "hidden": 0, @@ -3631,6 +3742,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "subscription_section", "fieldtype": "Section Break", "hidden": 0, @@ -3665,6 +3777,7 @@ "columns": 0, "depends_on": "", "description": "", + "fetch_if_empty": 0, "fieldname": "from_date", "fieldtype": "Date", "hidden": 0, @@ -3698,6 +3811,7 @@ "columns": 0, "depends_on": "", "description": "", + "fetch_if_empty": 0, "fieldname": "to_date", "fieldtype": "Date", "hidden": 0, @@ -3729,6 +3843,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_97", "fieldtype": "Column Break", "hidden": 0, @@ -3760,6 +3875,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "auto_repeat", "fieldtype": "Link", "hidden": 0, @@ -3794,6 +3910,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval: doc.auto_repeat", + "fetch_if_empty": 0, "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "hidden": 0, @@ -3831,7 +3948,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-07 16:51:56.739693", + "modified": "2019-06-24 20:55:03.466766", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 3d981c56da7..238d9545342 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -1915,7 +1915,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "1", + "default": "0", "depends_on": "eval:parent.is_subcontracted == 'Yes'", "fieldname": "include_exploded_items", "fieldtype": "Check", @@ -2344,7 +2344,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2019-01-07 16:51:57.546323", + "modified": "2019-06-23 20:03:13.818917", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", @@ -2359,4 +2359,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index e99b1d88aa5..1db5233b026 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -199,7 +199,7 @@ def get_data(): "type": "report", "is_query_report": True, "name": "Address And Contacts", - "label": "Supplier Addresses And Contacts", + "label": _("Supplier Addresses And Contacts"), "doctype": "Address", "route_options": { "party_type": "Supplier" diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 92d380421d7..b98fbf42cdd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -374,7 +374,7 @@ class AccountsController(TransactionBase): def validate_qty_is_not_zero(self): for item in self.items: if not item.qty: - frappe.throw("Item quantity can not be zero") + frappe.throw(_("Item quantity can not be zero")) def validate_account_currency(self, account, account_currency=None): valid_currency = [self.company_currency] @@ -747,7 +747,12 @@ class AccountsController(TransactionBase): count += 1 item.qty = group_item_qty[item.item_code] item.amount = group_item_amount[item.item_code] - item.rate = flt(flt(item.amount) / flt(item.qty), item.precision("rate")) + + if item.qty: + item.rate = flt(flt(item.amount) / flt(item.qty), item.precision("rate")) + else: + item.rate = 0 + item.idx = count del group_item_qty[item.item_code] else: diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 664bce4e4fa..917c901cfcb 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -428,8 +428,9 @@ class BuyingController(StockController): elif not flt(d.rejected_qty): d.rejected_qty = flt(d.received_qty) - flt(d.qty) + val = flt(d.qty) + flt(d.rejected_qty) # Check Received Qty = Accepted Qty + Rejected Qty - if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)): + if (flt(val, d.precision("received_qty")) != flt(d.received_qty, d.precision("received_qty"))): frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code)) def validate_negative_quantity(self, item_row, field_list): diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index a40f0eff43b..99fafa73056 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -206,11 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( - fcond=get_filters_cond(doctype, filters, conditions), + fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype), key=frappe.db.escape(searchfield)), { - 'txt': "%%%s%%" % frappe.db.escape(txt), + 'txt': "%"+frappe.db.escape(txt)+"%", '_txt': txt.replace("%", ""), 'start': start or 0, 'page_len': page_len or 20 @@ -297,7 +297,6 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): order by batch.expiry_date, sle.batch_no desc 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, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date) from `tabBatch` batch diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 8cf11f785be..2fddcdf24c5 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -75,7 +75,7 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): - if flt(d.qty) < 0 or d.get('received_qty') < 0: + if d.item_code and (flt(d.qty) < 0 or 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)) @@ -107,6 +107,9 @@ def validate_returned_items(doc): items_returned = True + elif d.item_name: + items_returned = True + if not items_returned: frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index a9883017cfa..f16d40c4ea8 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -55,14 +55,28 @@ class SellingController(StockController): self.set_price_list_and_item_details(for_validate=for_validate) def set_missing_lead_customer_details(self): + customer, lead = None, None if getattr(self, "customer", None): + customer = self.customer + elif self.doctype == "Opportunity" and self.party_name: + if self.opportunity_from == "Customer": + customer = self.party_name + else: + lead = self.party_name + elif self.doctype == "Quotation" and self.party_name: + if self.quotation_to == "Customer": + customer = self.party_name + else: + lead = self.party_name + + if customer: from erpnext.accounts.party import _get_party_details fetch_payment_terms_template = False if (self.get("__islocal") or self.company != frappe.db.get_value(self.doctype, self.name, 'company')): fetch_payment_terms_template = True - party_details = _get_party_details(self.customer, + party_details = _get_party_details(customer, ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company, fetch_payment_terms_template=fetch_payment_terms_template, @@ -71,10 +85,9 @@ class SellingController(StockController): party_details.pop("sales_team") self.update_if_missing(party_details) - elif getattr(self, "lead", None): + elif lead: from erpnext.crm.doctype.lead.lead import get_lead_details - self.update_if_missing(get_lead_details( - self.lead, + self.update_if_missing(get_lead_details(lead, posting_date=self.get('transaction_date') or self.get('posting_date'), company=self.company)) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2484586d041..a3018029bc0 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -27,7 +27,7 @@ status_map = { ], "Quotation": [ ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], + ["Open", "eval:self.docstatus==1"], ["Lost", "eval:self.status=='Lost'"], ["Ordered", "has_sales_order"], ["Cancelled", "eval:self.docstatus==2"], diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 14738c5ff2b..d02308d8f21 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -43,7 +43,7 @@ class TestMapper(unittest.TestCase): qtn = frappe.get_doc({ "doctype": "Quotation", "quotation_to": "Customer", - "customer": customer, + "party_name": customer, "order_type": "Sales", "transaction_date" : nowdate(), "valid_till" : add_months(nowdate(), 1) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index dad249213c7..973b3412b8b 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -320,11 +320,11 @@ def make_opportunity_from_communication(communication, ignore_communication_link if not lead: lead = make_lead_from_communication(communication, ignore_communication_links=True) - enquiry_from = "Lead" + opportunity_from = "Lead" opportunity = frappe.get_doc({ "doctype": "Opportunity", - "enquiry_from": enquiry_from, + "opportunity_from": opportunity_from, "lead": lead }).insert(ignore_permissions=True) diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py index 3809c1f0924..d5b0133f21b 100644 --- a/erpnext/demo/user/sales.py +++ b/erpnext/demo/user/sales.py @@ -99,7 +99,7 @@ def make_quotation(domain): "creation": frappe.flags.current_date, "doctype": "Quotation", "quotation_to": "Customer", - "customer": customer, + "party_name": customer, "currency": party_account_currency or company_currency, "conversion_rate": exchange_rate, "order_type": "Sales", diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py index f9979752f78..16933dcfe09 100644 --- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py +++ b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe, json +from frappe import _ from frappe.model.document import Document from erpnext.education.api import get_grade from frappe.utils.pdf import get_pdf @@ -79,7 +80,7 @@ def get_attendance_count(student, academic_year, academic_term=None): from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"]) if from_date and to_date: attendance = dict(frappe.db.sql('''select status, count(student) as no_of_days - from `tabStudent Attendance` where student = %s + from `tabStudent Attendance` where student = %s and date between %s and %s group by status''', (student, from_date, to_date))) if "Absent" not in attendance.keys(): @@ -88,4 +89,4 @@ def get_attendance_count(student, academic_year, academic_term=None): attendance["Present"] = 0 return attendance else: - frappe.throw("Provide the academic year and set the starting and ending date.") \ No newline at end of file + frappe.throw(_("Provide the academic year and set the starting and ending date.")) \ No newline at end of file diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 88078ab74f6..1d6e8917f50 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -124,7 +124,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None): else: so = frappe.get_doc("Sales Order", so) - + frappe.db.commit() return so @@ -252,6 +252,6 @@ def get_tax_account_head(tax): {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account") if not tax_account: - frappe.throw("Tax Account not specified for Shopify Tax {0}".format(tax.get("title"))) + frappe.throw(_("Tax Account not specified for Shopify Tax {0}".format(tax.get("title")))) return tax_account diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 4700202213f..0b6ea8cc7ca 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -22,7 +22,16 @@ def verify_request(): frappe.set_user(woocommerce_settings.creation_user) @frappe.whitelist(allow_guest=True) -def order(): +def order(*args, **kwargs): + try: + _order(*args, **kwargs) + except Exception: + error_message = frappe.get_traceback()+"\n\n Request Data: \n"+json.loads(frappe.request.data).__str__() + frappe.log_error(error_message, "WooCommerce Error") + raise + + +def _order(*args, **kwargs): woocommerce_settings = frappe.get_doc("Woocommerce Settings") if frappe.flags.woocomm_test_order_data: fd = frappe.flags.woocomm_test_order_data diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index 124910e35de..1c39d8818c4 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -40,7 +40,7 @@ def get_products_details(): products_response = call_mws_method(products.get_matching_product,marketplaceid=marketplace, asins=asin_list) - matching_products_list = products_response.parsed + matching_products_list = products_response.parsed for product in matching_products_list: skus = [row["sku"] for row in sku_asin if row["asin"]==product.ASIN] for sku in skus: @@ -116,7 +116,7 @@ def call_mws_method(mws_method, *args, **kwargs): mws_settings = frappe.get_doc("Amazon MWS Settings") max_retries = mws_settings.max_retry_limit - for x in xrange(0, max_retries): + for x in range(0, max_retries): try: response = mws_method(*args, **kwargs) return response diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 29a1a2b0bb8..96a533ee10c 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -7,7 +7,9 @@ import frappe from frappe import _ from frappe.model.document import Document from requests_oauthlib import OAuth2Session -import json, requests +import json +import requests +import traceback from erpnext import encode_company_abbr # QuickBooks requires a redirect URL, User will be redirect to this URL @@ -32,7 +34,6 @@ def callback(*args, **kwargs): class QuickBooksMigrator(Document): def __init__(self, *args, **kwargs): super(QuickBooksMigrator, self).__init__(*args, **kwargs) - from pprint import pprint self.oauth = OAuth2Session( client_id=self.client_id, redirect_uri=self.redirect_url, @@ -46,7 +47,9 @@ class QuickBooksMigrator(Document): if self.company: # We need a Cost Center corresponding to the selected erpnext Company self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center') - self.default_warehouse = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})[0]["name"] + company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0}) + if company_warehouses: + self.default_warehouse = company_warehouses[0].name if self.authorization_endpoint: self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0] @@ -218,7 +221,7 @@ class QuickBooksMigrator(Document): def _fetch_general_ledger(self): try: - query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint ,self.quickbooks_company_id) + query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id) response = self._get(query_uri, params={ "columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]), @@ -493,17 +496,17 @@ class QuickBooksMigrator(Document): "account_currency": customer["CurrencyRef"]["value"], "company": self.company, })[0]["name"] - except Exception as e: + except Exception: receivable_account = None erpcustomer = frappe.get_doc({ "doctype": "Customer", "quickbooks_id": customer["Id"], - "customer_name" : encode_company_abbr(customer["DisplayName"], self.company), - "customer_type" : "Individual", - "customer_group" : "Commercial", + "customer_name": encode_company_abbr(customer["DisplayName"], self.company), + "customer_type": "Individual", + "customer_group": "Commercial", "default_currency": customer["CurrencyRef"]["value"], "accounts": [{"company": self.company, "account": receivable_account}], - "territory" : "All Territories", + "territory": "All Territories", "company": self.company, }).insert() if "BillAddr" in customer: @@ -521,7 +524,7 @@ class QuickBooksMigrator(Document): item_dict = { "doctype": "Item", "quickbooks_id": item["Id"], - "item_code" : encode_company_abbr(item["Name"], self.company), + "item_code": encode_company_abbr(item["Name"], self.company), "stock_uom": "Unit", "is_stock_item": 0, "item_group": "All Item Groups", @@ -549,14 +552,14 @@ class QuickBooksMigrator(Document): erpsupplier = frappe.get_doc({ "doctype": "Supplier", "quickbooks_id": vendor["Id"], - "supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company), - "supplier_group" : "All Supplier Groups", + "supplier_name": encode_company_abbr(vendor["DisplayName"], self.company), + "supplier_group": "All Supplier Groups", "company": self.company, }).insert() if "BillAddr" in vendor: self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing") if "ShipAddr" in vendor: - self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping") + self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping") except Exception as e: self._log_error(e) @@ -829,7 +832,7 @@ class QuickBooksMigrator(Document): "currency": invoice["CurrencyRef"]["value"], "conversion_rate": invoice.get("ExchangeRate", 1), "posting_date": invoice["TxnDate"], - "due_date": invoice.get("DueDate", invoice["TxnDate"]), + "due_date": invoice.get("DueDate", invoice["TxnDate"]), "credit_to": credit_to_account, "supplier": frappe.get_all("Supplier", filters={ @@ -1200,7 +1203,7 @@ class QuickBooksMigrator(Document): def _create_address(self, entity, doctype, address, address_type): - try : + try: if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}): frappe.get_doc({ "doctype": "Address", @@ -1252,8 +1255,6 @@ class QuickBooksMigrator(Document): def _log_error(self, execption, data=""): - import json, traceback - traceback.print_exc() frappe.log_error(title="QuickBooks Migration Error", message="\n".join([ "Data", diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 90bf95770fb..141329b3db1 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -30,7 +30,7 @@ class ClinicalProcedureTemplate(Document): try: frappe.delete_doc("Item",self.item) except Exception: - frappe.throw("""Not permitted. Please disable the Procedure Template""") + frappe.throw(_("""Not permitted. Please disable the Procedure Template""")) def get_item_details(self, args=None): item = frappe.db.sql("""select stock_uom, item_name diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index c7df5b7cd94..7ea45688fd9 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -160,7 +160,7 @@ var btn_create_vital_signs = function (frm) { var btn_create_procedure = function (frm) { if(!frm.doc.patient){ - frappe.throw("Please select patient"); + frappe.throw(__("Please select patient")); } frappe.route_options = { "patient": frm.doc.patient, diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9b18e5e1f3a..a067e28747e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -42,7 +42,7 @@ update_and_get_user_progress = "erpnext.utilities.user_progress_utils.update_def on_session_creation = "erpnext.shopping_cart.utils.set_cart_count" on_logout = "erpnext.shopping_cart.utils.clear_cart_count" -treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory', 'Assessment Group'] +treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory', 'Assessment Group', 'Department'] # website update_website_context = "erpnext.shopping_cart.utils.update_website_context" diff --git a/erpnext/hr/doctype/attendance/attendance_calendar.js b/erpnext/hr/doctype/attendance/attendance_calendar.js index b21afe5eaee..104f09d69ff 100644 --- a/erpnext/hr/doctype/attendance/attendance_calendar.js +++ b/erpnext/hr/doctype/attendance/attendance_calendar.js @@ -2,8 +2,8 @@ // For license information, please see license.txt frappe.views.calendar["Attendance"] = { field_map: { - "start": "date", - "end": "date", + "start": "attendance_date", + "end": "attendance_date", "id": "name", "docstatus": 1 }, diff --git a/erpnext/hr/doctype/department/department.js b/erpnext/hr/doctype/department/department.js index 76bc932144c..963f3615cc5 100644 --- a/erpnext/hr/doctype/department/department.js +++ b/erpnext/hr/doctype/department/department.js @@ -4,7 +4,7 @@ frappe.ui.form.on('Department', { refresh: function(frm) { // read-only for root department - if(!frm.doc.parent_department) { + if(!frm.doc.parent_department && !frm.is_new()) { frm.set_read_only(); frm.set_intro(__("This is a root department and cannot be edited.")); } diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json index aed7f42faf8..3b400ce8d76 100644 --- a/erpnext/hr/doctype/department/department.json +++ b/erpnext/hr/doctype/department/department.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -19,6 +20,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "department_name", "fieldtype": "Data", "hidden": 0, @@ -52,6 +54,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "parent_department", "fieldtype": "Link", "hidden": 0, @@ -85,6 +88,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -118,6 +122,7 @@ "bold": 1, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_group", "fieldtype": "Check", "hidden": 0, @@ -150,6 +155,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "disabled", "fieldtype": "Check", "hidden": 0, @@ -182,6 +188,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_4", "fieldtype": "Section Break", "hidden": 0, @@ -214,6 +221,7 @@ "collapsible": 0, "columns": 0, "description": "Days for which Holidays are blocked for this department.", + "fetch_if_empty": 0, "fieldname": "leave_block_list", "fieldtype": "Link", "hidden": 0, @@ -246,6 +254,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leave_section", "fieldtype": "Section Break", "hidden": 0, @@ -279,6 +288,7 @@ "collapsible": 0, "columns": 0, "description": "The first Leave Approver in the list will be set as the default Leave Approver.", + "fetch_if_empty": 0, "fieldname": "leave_approvers", "fieldtype": "Table", "hidden": 0, @@ -312,6 +322,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "expense_section", "fieldtype": "Section Break", "hidden": 0, @@ -345,6 +356,7 @@ "collapsible": 0, "columns": 0, "description": "The first Expense Approver in the list will be set as the default Expense Approver.", + "fetch_if_empty": 0, "fieldname": "expense_approvers", "fieldtype": "Table", "hidden": 0, @@ -378,6 +390,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "lft", "fieldtype": "Int", "hidden": 1, @@ -410,6 +423,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "rgt", "fieldtype": "Int", "hidden": 1, @@ -442,6 +456,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "old_parent", "fieldtype": "Data", "hidden": 1, @@ -479,7 +494,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-29 06:26:12.995703", + "modified": "2019-06-25 18:43:05.550387", "modified_by": "Administrator", "module": "HR", "name": "Department", @@ -543,7 +558,7 @@ "write": 1 } ], - "quick_entry": 1, + "quick_entry": 0, "read_only": 0, "read_only_onload": 0, "show_name_in_global_search": 1, diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js index 376d0c0a5a5..22ba5ad473e 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js @@ -2,9 +2,8 @@ frappe.ui.form.on("Employee Attendance Tool", { refresh: function(frm) { frm.disable_save(); }, - + onload: function(frm) { - frm.doc.department = frm.doc.branch = frm.doc.company = "All"; frm.set_value("date", frappe.datetime.get_today()); erpnext.employee_attendance_tool.load_employees(frm); }, @@ -24,7 +23,7 @@ frappe.ui.form.on("Employee Attendance Tool", { company: function(frm) { erpnext.employee_attendance_tool.load_employees(frm); } - + }); 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 ea5f4bdecab..32fcee1abe4 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py @@ -17,12 +17,11 @@ def get_employees(date, department = None, branch = None, company = None): attendance_not_marked = [] attendance_marked = [] filters = {"status": "Active", "date_of_joining": ["<=", date]} - if department != "All": - filters["department"] = department - if branch != "All": - filters["branch"] = branch - if company != "All": - filters["company"] = company + + for field, value in {'department': department, + 'branch': branch, 'company': company}.items(): + if value: + filters[field] = value employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name") marked_employee = {} diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js index e71ce1276b5..b73dcf8ac36 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js @@ -2,20 +2,8 @@ // For license information, please see license.txt frappe.ui.form.on('Employee Benefit Application', { - setup: function(frm) { - if(!frm.doc.employee || !frm.doc.date) { - frappe.throw(__("Please select Employee and Date first")); - } else { - frm.set_query("earning_component", "employee_benefits", function() { - return { - query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", - filters: {date: frm.doc.date, employee: frm.doc.employee} - }; - }); - } - }, - employee: function(frm) { + frm.trigger('set_earning_component'); var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; @@ -35,6 +23,21 @@ frappe.ui.form.on('Employee Benefit Application', { get_max_benefits(frm, method, args); } }, + + date: function(frm) { + frm.trigger('set_earning_component'); + }, + + set_earning_component: function(frm) { + if(!frm.doc.employee && !frm.doc.date) return; + frm.set_query("earning_component", "employee_benefits", function() { + return { + query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + filters: {date: frm.doc.date, employee: frm.doc.employee} + }; + }); + }, + payroll_period: function(frm) { var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py index 3a80b303659..abb82f2cdd1 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -84,7 +84,7 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) except TypeError: # show the error in tests? - frappe.throw("Unable to find Salary Component {0}".format(sal_struct_row.salary_component)) + frappe.throw(_("Unable to find Salary Component {0}".format(sal_struct_row.salary_component))) if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: total_pro_rata_max += max_benefit_amount if total_pro_rata_max > 0: diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py deleted file mode 100644 index b6c650207f0..00000000000 --- a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe, math -from frappe import _ -from frappe.utils import flt, rounded -from frappe.model.mapper import get_mapped_doc -from frappe.model.document import Document - -from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method - -class EmployeeLoanApplication(Document): - def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) - self.validate_loan_amount() - self.get_repayment_details() - - def validate_loan_amount(self): - maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount') - if maximum_loan_limit and self.loan_amount > maximum_loan_limit: - frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit)) - - def get_repayment_details(self): - if self.repayment_method == "Repay Over Number of Periods": - self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) - - if self.repayment_method == "Repay Fixed Amount per Period": - monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) - if monthly_interest_rate: - monthly_interest_amount = self.loan_amount * monthly_interest_rate - if monthly_interest_amount >= self.repayment_amount: - frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}"). - format(self.repayment_amount, monthly_interest_amount)) - - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - - math.log(self.repayment_amount - (monthly_interest_amount))) / - (math.log(1 + monthly_interest_rate))) - else: - self.repayment_periods = self.loan_amount / self.repayment_amount - - self.calculate_payable_amount() - - def calculate_payable_amount(self): - balance_amount = self.loan_amount - self.total_payable_amount = 0 - self.total_payable_interest = 0 - - while(balance_amount > 0): - interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100)) - balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) - - self.total_payable_interest += interest_amount - - self.total_payable_amount = self.loan_amount + self.total_payable_interest - -@frappe.whitelist() -def make_employee_loan(source_name, target_doc = None): - doclist = get_mapped_doc("Employee Loan Application", source_name, { - "Employee Loan Application": { - "doctype": "Employee Loan", - "validation": { - "docstatus": ["=", 1] - } - } - }, target_doc) - - return doclist \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js index 977702e314c..05071e19743 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.js +++ b/erpnext/hr/doctype/job_applicant/job_applicant.js @@ -25,5 +25,13 @@ frappe.ui.form.on("Job Applicant", { } } + frm.set_query("job_title", function() { + return { + filters: { + 'status': 'Open' + } + }; + }); + } }); \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 88b8f77b2e0..ef8221108cf 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -400,19 +400,6 @@ def get_leave_balance_on(employee, leave_type, date, allocation_records=None, do return flt(allocation.total_leaves_allocated) - (flt(leaves_taken) + flt(leaves_encashed)) -def get_total_allocated_leaves(employee, leave_type, date): - filters= { - 'from_date': ['<=', date], - 'to_date': ['>=', date], - 'docstatus': 1, - 'leave_type': leave_type, - 'employee': employee - } - - leave_allocation_records = frappe.db.get_all('Leave Allocation', filters=filters, fields=['total_leaves_allocated']) - - return flt(leave_allocation_records[0]['total_leaves_allocated']) if leave_allocation_records else flt(0) - def get_leaves_for_period(employee, leave_type, from_date, to_date, status, docname=None): leave_applications = frappe.db.sql(""" select name, employee, leave_type, from_date, to_date, total_leave_days diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js index 3024f28207a..940e4c12daf 100644 --- a/erpnext/hr/doctype/loan/loan.js +++ b/erpnext/hr/doctype/loan/loan.js @@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', { }, refresh: function (frm) { - if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") { - frm.add_custom_button(__('Make Disbursement Entry'), function() { - frm.trigger("make_jv"); - }) - } - if (frm.doc.repayment_schedule) { - let total_amount_paid = 0; - $.each(frm.doc.repayment_schedule || [], function(i, row) { - if (row.paid) { - total_amount_paid += row.total_payment; - } - }); - frm.set_value("total_amount_paid", total_amount_paid); -; } - if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { - frm.add_custom_button(__('Make Repayment Entry'), function() { - frm.trigger("make_repayment_entry"); - }) + if (frm.doc.docstatus == 1) { + if (frm.doc.status == "Sanctioned") { + frm.add_custom_button(__('Create Disbursement Entry'), function() { + frm.trigger("make_jv"); + }).addClass("btn-primary"); + } else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { + frm.add_custom_button(__('Create Repayment Entry'), function() { + frm.trigger("make_repayment_entry"); + }).addClass("btn-primary"); + } } frm.trigger("toggle_fields"); }, - status: function (frm) { - frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed') - frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed') - }, make_jv: function (frm) { frappe.call({ diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json index 587b3010ca0..505b601edd5 100644 --- a/erpnext/hr/doctype/loan/loan.json +++ b/erpnext/hr/doctype/loan/loan.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_type", "fieldtype": "Select", "hidden": 0, @@ -53,6 +55,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant", "fieldtype": "Dynamic Link", "hidden": 0, @@ -86,6 +89,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_name", "fieldtype": "Data", "hidden": 0, @@ -118,6 +122,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_application", "fieldtype": "Link", "hidden": 0, @@ -151,6 +156,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_type", "fieldtype": "Link", "hidden": 0, @@ -184,6 +190,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -215,7 +222,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "", + "default": "Today", + "fetch_if_empty": 0, "fieldname": "posting_date", "fieldtype": "Date", "hidden": 0, @@ -248,6 +256,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -282,6 +291,7 @@ "collapsible": 0, "columns": 0, "default": "Sanctioned", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -299,7 +309,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -316,6 +326,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.applicant_type==\"Employee\"", + "fetch_if_empty": 0, "fieldname": "repay_from_salary", "fieldtype": "Check", "hidden": 0, @@ -348,6 +359,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -380,6 +392,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_amount", "fieldtype": "Currency", "hidden": 0, @@ -415,6 +428,7 @@ "columns": 0, "default": "", "fetch_from": "loan_type.rate_of_interest", + "fetch_if_empty": 0, "fieldname": "rate_of_interest", "fieldtype": "Percent", "hidden": 0, @@ -448,6 +462,8 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval:doc.status==\"Disbursed\"", + "fetch_if_empty": 0, "fieldname": "disbursement_date", "fieldtype": "Date", "hidden": 0, @@ -480,6 +496,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_start_date", "fieldtype": "Date", "hidden": 0, @@ -499,7 +516,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -512,6 +529,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_11", "fieldtype": "Column Break", "hidden": 0, @@ -544,6 +562,7 @@ "collapsible": 0, "columns": 0, "default": "Repay Over Number of Periods", + "fetch_if_empty": 0, "fieldname": "repayment_method", "fieldtype": "Select", "hidden": 0, @@ -579,6 +598,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "repayment_periods", "fieldtype": "Int", "hidden": 0, @@ -613,6 +633,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "hidden": 0, @@ -646,6 +667,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "account_info", "fieldtype": "Section Break", "hidden": 0, @@ -678,6 +700,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "mode_of_payment", "fieldtype": "Link", "hidden": 0, @@ -711,6 +734,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_account", "fieldtype": "Link", "hidden": 0, @@ -744,6 +768,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_9", "fieldtype": "Column Break", "hidden": 0, @@ -775,6 +800,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_account", "fieldtype": "Link", "hidden": 0, @@ -808,6 +834,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "interest_income_account", "fieldtype": "Link", "hidden": 0, @@ -841,6 +868,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_15", "fieldtype": "Section Break", "hidden": 0, @@ -873,6 +901,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_schedule", "fieldtype": "Table", "hidden": 0, @@ -906,6 +935,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_17", "fieldtype": "Section Break", "hidden": 0, @@ -939,6 +969,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_payment", "fieldtype": "Currency", "hidden": 0, @@ -972,6 +1003,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -1004,6 +1036,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_interest_payable", "fieldtype": "Currency", "hidden": 0, @@ -1037,6 +1070,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_amount_paid", "fieldtype": "Currency", "hidden": 0, @@ -1070,6 +1104,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -1106,7 +1141,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:53.267145", + "modified": "2019-07-10 13:04:20.953694", "modified_by": "Administrator", "module": "HR", "name": "Loan", @@ -1149,7 +1184,6 @@ "set_user_permissions": 0, "share": 0, "submit": 0, - "user_permission_doctypes": "[\"Employee\"]", "write": 0 } ], diff --git a/erpnext/hr/doctype/loan/loan.py b/erpnext/hr/doctype/loan/loan.py index 58c9b8f6676..a803863124d 100644 --- a/erpnext/hr/doctype/loan/loan.py +++ b/erpnext/hr/doctype/loan/loan.py @@ -6,29 +6,33 @@ from __future__ import unicode_literals import frappe, math, json import erpnext from frappe import _ -from frappe.utils import flt, rounded, add_months, nowdate +from frappe.utils import flt, rounded, add_months, nowdate, getdate from erpnext.controllers.accounts_controller import AccountsController class Loan(AccountsController): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + self.set_missing_fields() + self.make_repayment_schedule() + self.set_repayment_period() + self.calculate_totals() + + def set_missing_fields(self): if not self.company: self.company = erpnext.get_default_company() + if not self.posting_date: self.posting_date = nowdate() + if self.loan_type and not self.rate_of_interest: self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest") + if self.repayment_method == "Repay Over Number of Periods": self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) + if self.status == "Repaid/Closed": self.total_amount_paid = self.total_payment - if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date: - frappe.throw(_("Repayment Start Date cannot be before Disbursement Date.")) - if self.status == "Disbursed": - self.make_repayment_schedule() - self.set_repayment_period() - self.calculate_totals() def make_jv_entry(self): self.check_permission('write') @@ -105,20 +109,31 @@ def update_total_amount_paid(doc): frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid) def update_disbursement_status(doc): - disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount - from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""", - (doc.payment_account, doc.name), as_dict=1)[0] - if disbursement.disbursed_amount == doc.loan_amount: - frappe.db.set_value("Loan", doc.name , "status", "Disbursed") - if disbursement.disbursed_amount == 0: - frappe.db.set_value("Loan", doc.name , "status", "Sanctioned") - if disbursement.disbursed_amount > doc.loan_amount: - frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) - if disbursement.disbursed_amount > 0: - frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date) - frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date) + disbursement = frappe.db.sql(""" + select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount + from `tabGL Entry` + where account = %s and against_voucher_type = 'Loan' and against_voucher = %s + """, (doc.payment_account, doc.name), as_dict=1)[0] -def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): + disbursement_date = None + if not disbursement or disbursement.disbursed_amount == 0: + status = "Sanctioned" + elif disbursement.disbursed_amount == doc.loan_amount: + disbursement_date = disbursement.posting_date + status = "Disbursed" + elif disbursement.disbursed_amount > doc.loan_amount: + frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) + + if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")): + frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date")) + + frappe.db.sql(""" + update `tabLoan` + set status = %s, disbursement_date = %s + where name = %s + """, (status, disbursement_date, doc.name)) + +def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): if repayment_method == "Repay Over Number of Periods" and not repayment_periods: frappe.throw(_("Please enter Repayment Periods")) @@ -222,4 +237,4 @@ def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_a "reference_name": loan, }) journal_entry.set("accounts", account_amt_list) - return journal_entry.as_dict() \ No newline at end of file + return journal_entry.as_dict() diff --git a/erpnext/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js index febcbd88e76..a73b62a894e 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.js +++ b/erpnext/hr/doctype/loan_application/loan_application.js @@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', { }, add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { - frm.add_custom_button(__('Loan'), function() { + frm.add_custom_button(__('Create Loan'), function() { frappe.call({ - type: "GET", method: "erpnext.hr.doctype.loan_application.loan_application.make_loan", args: { "source_name": frm.doc.name @@ -37,7 +36,7 @@ frappe.ui.form.on('Loan Application', { } } }); - }) + }).addClass("btn-primary"); } } }); diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py index 706c9646c73..5dbcf15eac1 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.py +++ b/erpnext/hr/doctype/loan_application/loan_application.py @@ -9,11 +9,11 @@ from frappe.utils import flt, rounded from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document -from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method +from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method class LoanApplication(Document): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) self.validate_loan_amount() self.get_repayment_details() @@ -29,14 +29,14 @@ class LoanApplication(Document): if self.repayment_method == "Repay Fixed Amount per Period": monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) if monthly_interest_rate: - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - + self.repayment_periods = math.ceil((math.log(self.repayment_amount) - math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) / (math.log(1 + monthly_interest_rate))) else: self.repayment_periods = self.loan_amount / self.repayment_amount self.calculate_payable_amount() - + def calculate_payable_amount(self): balance_amount = self.loan_amount self.total_payable_amount = 0 @@ -47,9 +47,9 @@ class LoanApplication(Document): balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) self.total_payable_interest += interest_amount - + self.total_payable_amount = self.loan_amount + self.total_payable_interest - + @frappe.whitelist() def make_loan(source_name, target_doc = None): doclist = get_mapped_doc("Loan Application", source_name, { diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index d3b960d29eb..0ce75d1b954 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext import datetime, math -from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, getdate +from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words from frappe.model.naming import make_autoname from frappe import msgprint, _ @@ -281,7 +281,9 @@ class SalarySlip(TransactionBase): wages_row = { "salary_component": salary_component, "abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"), - "amount": self.hour_rate * self.total_working_hours + "amount": self.hour_rate * self.total_working_hours, + "default_amount": 0.0, + "additional_amount": 0.0 } doc.append('earnings', wages_row) @@ -616,7 +618,7 @@ class SalarySlip(TransactionBase): elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days): amount, additional_amount = 0, 0 elif not row.amount: - amount = row.default_amount + row.additional_amount + amount = flt(row.default_amount) + flt(row.additional_amount) # apply rounding if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"): diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 95cb30b7918..18431768528 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from erpnext.hr.doctype.leave_application.leave_application \ - import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period, get_total_allocated_leaves + import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period def execute(filters=None): @@ -35,6 +35,9 @@ def get_data(filters, leave_types): allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date) allocation_records_based_on_from_date = get_leave_allocation_records(filters.from_date) + if filters.to_date <= filters.from_date: + frappe.throw(_("From date can not be greater than than To date")) + active_employees = frappe.get_all("Employee", filters = { "status": "Active", "company": filters.company}, fields = ["name", "employee_name", "department", "user_id"]) @@ -51,7 +54,8 @@ def get_data(filters, leave_types): filters.from_date, filters.to_date) # opening balance - opening = get_total_allocated_leaves(employee.name, leave_type, filters.to_date) + opening = get_leave_balance_on(employee.name, leave_type, filters.from_date, + allocation_records_based_on_to_date.get(employee.name, frappe._dict())) # closing balance closing = get_leave_balance_on(employee.name, leave_type, filters.to_date, diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py index 0c94df31596..75f2c9323aa 100644 --- a/erpnext/hub_node/api.py +++ b/erpnext/hub_node/api.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import frappe import json +from frappe import _ from frappe import _ from frappe.frappeclient import FrappeClient @@ -120,7 +121,7 @@ def get_valid_items(search_value=''): def publish_selected_items(items_to_publish): items_to_publish = json.loads(items_to_publish) if not len(items_to_publish): - frappe.throw('No items to publish') + frappe.throw(_('No items to publish')) for item in items_to_publish: item_code = item.get('item_code') @@ -165,7 +166,7 @@ def item_sync_preprocess(intended_item_publish_count): frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1) return response else: - frappe.throw('Unable to update remote activity') + frappe.throw(_('Unable to update remote activity')) def item_sync_postprocess(): @@ -173,7 +174,7 @@ def item_sync_postprocess(): if response: frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now()) else: - frappe.throw('Unable to update remote activity') + frappe.throw(_('Unable to update remote activity')) frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0) diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index e230e598489..45a7b935d38 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -90,7 +90,7 @@ class TestBOM(unittest.TestCase): self.assertEqual(bom.base_total_cost, 486000) def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self): - frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1) + frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1) for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)): frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s", item_code) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 2f2c40ef2d4..b5abb733b01 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -18,6 +18,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty from frappe.utils.csvutils import getlink from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty from erpnext.utilities.transaction_base import validate_uom_is_integer +from six import text_type class OverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass @@ -591,10 +592,10 @@ def make_timesheet(production_order, company): @frappe.whitelist() def add_timesheet_detail(timesheet, args): - if isinstance(timesheet, unicode): + if isinstance(timesheet, text_type): timesheet = frappe.get_doc('Timesheet', timesheet) - if isinstance(args, unicode): + if isinstance(args, text_type): args = json.loads(args) timesheet.append('time_logs', args) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b6083ef44c0..9827814f07e 100755 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -533,7 +533,7 @@ 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 -erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 +erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019 erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07 erpnext.patches.v11_0.rename_overproduction_percent_field erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom @@ -586,7 +586,7 @@ erpnext.patches.v11_0.add_permissions_in_gst_settings erpnext.patches.v11_1.setup_guardian_role execute:frappe.delete_doc('DocType', 'Notification Control') erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants -erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 +erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 #25-06-2019 erpnext.patches.v11_0.make_italian_localization_fields # 26-03-2019 erpnext.patches.v11_1.make_job_card_time_logs erpnext.patches.v11_1.set_variant_based_on @@ -601,4 +601,6 @@ execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v11_1.update_bank_transaction_status erpnext.patches.v11_1.renamed_delayed_item_report -erpnext.patches.v11_1.set_missing_opportunity_from \ No newline at end of file +erpnext.patches.v11_1.set_missing_opportunity_from +erpnext.patches.v11_1.set_quotation_status +erpnext.patches.v11_1.update_default_supplier_in_item_defaults \ No newline at end of file diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py index e30e0a74c00..ec9c6c3b760 100644 --- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py +++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py @@ -8,8 +8,10 @@ import frappe def execute(): frappe.reload_doc("stock", "doctype", "item_barcode") + if frappe.get_all("Item Barcode", limit=1): return + if "barcode" not in frappe.db.get_table_columns("Item"): return - items_barcode = frappe.get_all('Item', ['name', 'barcode'], { 'barcode': ('!=', '') }) + items_barcode = frappe.db.sql("select name, barcode from tabItem where barcode is not null", as_dict=True) frappe.reload_doc("stock", "doctype", "item") diff --git a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py index 01f84a03136..c7c76355400 100644 --- a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py +++ b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py @@ -17,10 +17,8 @@ def execute(): frappe.reload_doc('stock', 'doctype', 'item_default') frappe.reload_doc('stock', 'doctype', 'item') - if frappe.db.a_row_exists('Item Default'): return - companies = frappe.get_all("Company") - if len(companies) == 1: + if len(companies) == 1 and not frappe.get_all("Item Default", limit=1): try: frappe.db.sql(''' INSERT INTO `tabItem Default` @@ -35,32 +33,64 @@ def execute(): except: pass else: - item_details = frappe.get_all("Item", fields=["name", "default_warehouse", "buying_cost_center", - "expense_account", "selling_cost_center", "income_account"], limit=100) + item_details = frappe.db.sql(""" SELECT name, default_warehouse, + buying_cost_center, expense_account, selling_cost_center, income_account + FROM tabItem + WHERE + name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0""" + , as_dict=1) - for item in item_details: - item_defaults = [] + items_default_data = {} + for item_data in item_details: + for d in [["default_warehouse", "Warehouse"], ["expense_account", "Account"], + ["income_account", "Account"], ["buying_cost_center", "Cost Center"], + ["selling_cost_center", "Cost Center"]]: + if item_data.get(d[0]): + company = frappe.get_value(d[1], item_data.get(d[0]), "company", cache=True) - def insert_into_item_defaults(doc_field_name, doc_field_value, company): - for d in item_defaults: - if d.get("company") == company: - d[doc_field_name] = doc_field_value - return - item_defaults.append({ - "company": company, - doc_field_name: doc_field_value - }) + if item_data.name not in items_default_data: + items_default_data[item_data.name] = {} - for d in [ - ["default_warehouse", "Warehouse"], ["expense_account", "Account"], ["income_account", "Account"], - ["buying_cost_center", "Cost Center"], ["selling_cost_center", "Cost Center"] - ]: - if item.get(d[0]): - company = frappe.get_value(d[1], item.get(d[0]), "company", cache=True) - insert_into_item_defaults(d[0], item.get(d[0]), company) + company_wise_data = items_default_data[item_data.name] - doc = frappe.get_doc("Item", item.name) - doc.extend("item_defaults", item_defaults) + if company not in company_wise_data: + company_wise_data[company] = {} - for child_doc in doc.item_defaults: - child_doc.db_insert() \ No newline at end of file + default_data = company_wise_data[company] + default_data[d[0]] = item_data.get(d[0]) + + to_insert_data = [] + + # items_default_data data structure will be as follow + # { + # 'item_code 1': {'company 1': {'default_warehouse': 'Test Warehouse 1'}}, + # 'item_code 2': { + # 'company 1': {'default_warehouse': 'Test Warehouse 1'}, + # 'company 2': {'default_warehouse': 'Test Warehouse 1'} + # } + # } + + for item_code, companywise_item_data in items_default_data.items(): + for company, item_default_data in companywise_item_data.items(): + to_insert_data.append(( + frappe.generate_hash("", 10), + item_code, + 'Item', + 'item_defaults', + company, + item_default_data.get('default_warehouse'), + item_default_data.get('expense_account'), + item_default_data.get('income_account'), + item_default_data.get('buying_cost_center'), + item_default_data.get('selling_cost_center'), + )) + + if to_insert_data: + frappe.db.sql(''' + INSERT INTO `tabItem Default` + ( + `name`, `parent`, `parenttype`, `parentfield`, `company`, `default_warehouse`, + `expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center` + ) + VALUES {} + '''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data)) \ No newline at end of file diff --git a/erpnext/patches/v11_1/set_missing_title_for_quotation.py b/erpnext/patches/v11_1/set_missing_title_for_quotation.py index e86b8320c31..e2ef3433d3e 100644 --- a/erpnext/patches/v11_1/set_missing_title_for_quotation.py +++ b/erpnext/patches/v11_1/set_missing_title_for_quotation.py @@ -1,6 +1,7 @@ import frappe def execute(): + frappe.reload_doctype("Quotation") # update customer_name from Customer document if quotation_to is set to Customer frappe.db.sql(''' update tabQuotation, tabCustomer diff --git a/erpnext/patches/v11_1/set_quotation_status.py b/erpnext/patches/v11_1/set_quotation_status.py new file mode 100644 index 00000000000..87643a23545 --- /dev/null +++ b/erpnext/patches/v11_1/set_quotation_status.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + + frappe.db.sql(""" UPDATE `tabQuotation` set status = 'Open' + where docstatus = 1 and status = 'Submitted' """) diff --git a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py new file mode 100644 index 00000000000..347dec1f74d --- /dev/null +++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py @@ -0,0 +1,25 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' + default supplier was not set in the item defaults for multi company instance, + this patch will set the default supplier + + ''' + if not frappe.db.has_column('Item', 'default_supplier'): + return + + frappe.reload_doc('stock', 'doctype', 'item_default') + frappe.reload_doc('stock', 'doctype', 'item') + + companies = frappe.get_all("Company") + if len(companies) > 1: + frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem` + SET `tabItem Default`.default_supplier = `tabItem`.default_supplier + WHERE + `tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null + and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """) \ No newline at end of file diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index f1179033bed..32f0428fcd8 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -103,8 +103,8 @@ class TestTimesheet(unittest.TestCase): { "billable": 1, "activity_type": "_Test Activity Type", - "from_type": now_datetime(), - "hours": 3, + "from_time": now_datetime(), + "to_time": now_datetime() + datetime.timedelta(hours=3), "company": "_Test Company" } ) @@ -113,8 +113,8 @@ class TestTimesheet(unittest.TestCase): { "billable": 1, "activity_type": "_Test Activity Type", - "from_type": now_datetime(), - "hours": 3, + "from_time": now_datetime(), + "to_time": now_datetime() + datetime.timedelta(hours=3), "company": "_Test Company" } ) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index 477781bc805..b94e9183bd8 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -41,7 +41,8 @@ $.extend(frappe.create_routes, { "Item Group": "Tree/Item Group", "Sales Person": "Tree/Sales Person", "Account": "Tree/Account", - "Cost Center": "Tree/Cost Center" + "Cost Center": "Tree/Cost Center", + "Department": "Tree/Department", }); // preferred modules for breadcrumbs diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1a9ea8288c8..79119b262ca 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1291,7 +1291,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ callback: function(r) { if(!r.exc) { me.frm.set_value("taxes", r.message); - me.calculate_taxes_and_totals(); + + if(me.frm.doc.shipping_rule) { + me.frm.script_manager.trigger("shipping_rule"); + } else { + me.calculate_taxes_and_totals(); + } } } }); diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 0202bbb872a..c6086b9962b 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -45,6 +45,13 @@ erpnext.SerialNoBatchSelector = Class.extend({ label: __(me.warehouse_details.type), default: me.warehouse_details.name, onchange: function(e) { + + if(me.has_batch) { + fields = fields.concat(me.get_batch_fields()); + } else { + fields = fields.concat(me.get_serial_no_fields()); + } + me.warehouse_details.name = this.get_value(); var batches = this.layout.fields_dict.batches; if(batches) { @@ -232,6 +239,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ get_batch_fields: function() { var me = this; + return [ { fieldtype: 'Section Break', label: __('Batches') }, { @@ -246,8 +254,11 @@ erpnext.SerialNoBatchSelector = Class.extend({ 'in_list_view': 1, get_query: function () { return { - filters: { item: me.item_code }, - query: 'erpnext.controllers.queries.get_batch_numbers' + filters: { + item_code: me.item_code, + warehouse: me.warehouse || me.warehouse_details.name + }, + query: 'erpnext.controllers.queries.get_batch_no' }; }, change: function () { diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index 730a80b547b..cb6b736320e 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -316,6 +316,9 @@ def get_company_country(company): return frappe.get_cached_value('Company', company, 'country') def get_e_invoice_attachments(invoice): + if not invoice.company_tax_id: + return [] + out = [] attachments = get_attachments(invoice.doctype, invoice.name) company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id diff --git a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py index e1b6c4db4f9..e903c9f00a4 100644 --- a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py +++ b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].py @@ -69,13 +69,13 @@ def get_gl_entries(filters): gl_entries = frappe.db.sql(""" select - gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date, + gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date, sum(gl.debit) as debit, sum(gl.credit) as credit, sum(gl.debit_in_account_currency) as debitCurr, sum(gl.credit_in_account_currency) as creditCurr, - gl.voucher_type, gl.voucher_no, gl.against_voucher_type, - gl.against_voucher, gl.account_currency, gl.against, + gl.voucher_type, gl.voucher_no, gl.against_voucher_type, + gl.against_voucher, gl.account_currency, gl.against, gl.party_type, gl.party, - inv.name as InvName, inv.title as InvTitle, inv.posting_date as InvPostDate, + inv.name as InvName, inv.title as InvTitle, inv.posting_date as InvPostDate, pur.name as PurName, pur.title as PurTitle, pur.posting_date as PurPostDate, jnl.cheque_no as JnlRef, jnl.posting_date as JnlPostDate, jnl.title as JnlTitle, pay.name as PayName, pay.posting_date as PayPostDate, pay.title as PayTitle, @@ -84,7 +84,7 @@ def get_gl_entries(filters): emp.employee_name, emp.name as empName, stu.title as student_name, stu.name as stuName, member_name, mem.name as memName - + from `tabGL Entry` gl left join `tabSales Invoice` inv on gl.voucher_no = inv.name left join `tabPurchase Invoice` pur on gl.voucher_no = pur.name @@ -124,7 +124,7 @@ def get_result_as_list(data, filters): if account_number[0] is not None: CompteNum = account_number[0] else: - frappe.throw(_("Account number for account {0} is not available.
Please setup your Chart of Accounts correctly.").format(account.name)) + frappe.throw(_("Account number for account {0} is not available.
Please setup your Chart of Accounts correctly.").format(d.get("account"))) if d.get("party_type") == "Customer": CompAuxNum = d.get("cusName") diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index b1cb7867daa..696dc73ef32 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -85,7 +85,7 @@ class Gstr1Report(object): "rate": "", "taxable_value": 0, "cess_amount": 0, - "type": 0 + "type": "" }) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) @@ -94,6 +94,7 @@ class Gstr1Report(object): row["rate"] = rate row["taxable_value"] += sum([abs(net_amount) for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items]) + row["cess_amount"] += flt(self.invoice_cess.get(inv), 2) row["type"] = "E" if ecommerce_gstin else "OE" for key, value in iteritems(b2cs_output): @@ -118,6 +119,10 @@ class Gstr1Report(object): for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items]) row += [tax_rate or 0, taxable_value] + for column in self.other_columns: + if column.get('fieldname') == 'cess_amount': + row.append(flt(self.invoice_cess.get(invoice), 2)) + return row, taxable_value def get_invoice_data(self): diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js index dcb81cb087e..dfdf9dc0958 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js @@ -29,7 +29,20 @@ frappe.query_reports["HSN-wise-summary of outward supplies"] = { "placeholder":"Company GSTIN", "options": [""], "width": "80" - } + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80" + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80" + }, + ], onload: (report) => { fetch_gstins(report); diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index e938e29c440..222dfa1eb78 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -88,7 +88,9 @@ def get_conditions(filters): for opts in (("company", " and company=%(company)s"), ("gst_hsn_code", " and gst_hsn_code=%(gst_hsn_code)s"), - ("company_gstin", " and company_gstin=%(company_gstin)s")): + ("company_gstin", " and company_gstin=%(company_gstin)s"), + ("from_date", " and posting_date >= %(from_date)s"), + ("to_date", "and posting_date <= %(to_date)s")): if filters.get(opts[0]): conditions += opts[1] diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 1a31db92cb3..02d293d3236 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -90,22 +90,29 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ if (this.frm.doc.docstatus===0) { this.frm.add_custom_button(__('Opportunity'), function() { - var setters = {}; - if(me.frm.doc.quotation_to == "Customer" && me.frm.doc.party_name) { - setters.customer = me.frm.doc.party_name || undefined; - } else if (me.frm.doc.quotation_to == "Lead" && me.frm.doc.party_name) { - setters.lead = me.frm.doc.party_name || undefined; - } erpnext.utils.map_current_doc({ method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation", source_doctype: "Opportunity", target: me.frm, - setters: setters, + setters: [ + { + label: "Party", + fieldname: "party_name", + fieldtype: "Link", + options: me.frm.doc.quotation_to, + default: me.frm.doc.party_name || undefined + }, + { + label: "Opportunity Type", + fieldname: "opportunity_type", + fieldtype: "Link", + options: "Opportunity Type", + default: me.frm.doc.order_type || undefined + } + ], get_query_filters: { status: ["not in", ["Lost", "Closed"]], - company: me.frm.doc.company, - // cannot set opportunity_type as setter, as the fieldname is order_type - opportunity_type: me.frm.doc.order_type, + company: me.frm.doc.company } }) }, __("Get items from"), "btn-default"); diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 33fc4dbeb04..65fbe52d9a4 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -3096,7 +3096,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Draft\nSubmitted\nOrdered\nLost\nCancelled\nOpen\nReplied", + "options": "Draft\nOpen\nReplied\nOrdered\nLost\nCancelled", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -3224,7 +3224,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2019-05-11 19:26:50.735628", + "modified": "2019-06-25 15:31:04.724730", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index c344d266949..8b339ba82f2 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -161,6 +161,10 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): "Sales Team": { "doctype": "Sales Team", "add_if_empty": True + }, + "Payment Schedule": { + "doctype": "Payment Schedule", + "add_if_empty": True } }, target_doc, set_missing_values, ignore_permissions=ignore_permissions) diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js index 61a8bc1cab3..5f4e2546fbc 100644 --- a/erpnext/selling/doctype/quotation/quotation_list.js +++ b/erpnext/selling/doctype/quotation/quotation_list.js @@ -13,11 +13,11 @@ frappe.listview_settings['Quotation'] = { }, get_indicator: function(doc) { - if(doc.status==="Submitted") { + if(doc.status==="Open") { if (doc.valid_till && doc.valid_till < frappe.datetime.nowdate()) { return [__("Expired"), "darkgrey", "valid_till,<," + frappe.datetime.nowdate()]; } else { - return [__("Submitted"), "blue", "status,=,Submitted"]; + return [__("Open"), "orange", "status,=,Open"]; } } else if(doc.status==="Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 8bb8c618f29..7ee4a76ca66 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, add_days, nowdate, add_months +from frappe.utils import flt, add_days, nowdate, add_months, getdate import unittest test_dependencies = ["Product Bundle"] @@ -18,7 +18,7 @@ class TestQuotation(unittest.TestCase): self.assertTrue(quotation.payment_schedule) - def test_make_sales_order_terms_not_copied(self): + def test_make_sales_order_terms_copied(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order quotation = frappe.copy_doc(test_records[0]) @@ -29,7 +29,7 @@ class TestQuotation(unittest.TestCase): sales_order = make_sales_order(quotation.name) - self.assertFalse(sales_order.get('payment_schedule')) + self.assertTrue(sales_order.get('payment_schedule')) def test_make_sales_order_with_different_currency(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order @@ -109,10 +109,10 @@ class TestQuotation(unittest.TestCase): sales_order.insert() self.assertEqual(sales_order.payment_schedule[0].payment_amount, 8906.00) - self.assertEqual(sales_order.payment_schedule[0].due_date, quotation.transaction_date) + self.assertEqual(sales_order.payment_schedule[0].due_date, getdate(quotation.transaction_date)) self.assertEqual(sales_order.payment_schedule[1].payment_amount, 8906.00) self.assertEqual( - sales_order.payment_schedule[1].due_date, add_days(quotation.transaction_date, 30) + sales_order.payment_schedule[1].due_date, getdate(add_days(quotation.transaction_date, 30)) ) def test_valid_till(self): diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 6cdb4f88840..84c1693d20c 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -133,7 +133,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if (this.frm.has_perm("submit")) { // close - if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) { + if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed, 6) < 100) { this.frm.add_custom_button(__('Close'), function() { me.close_sales_order() }, __("Status")) } @@ -219,13 +219,19 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( method: "erpnext.selling.doctype.quotation.quotation.make_sales_order", source_doctype: "Quotation", target: me.frm, - setters: { - customer: me.frm.doc.customer || undefined - }, + setters: [ + { + label: "Customer", + fieldname: "party_name", + fieldtype: "Link", + options: "Customer", + default: me.frm.doc.customer || undefined + } + ], get_query_filters: { company: me.frm.doc.company, docstatus: 1, - status: ["!=", "Lost"], + status: ["!=", "Lost"] } }) }, __("Get items from")); diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 184c6bd70d6..e1ddd2c7dac 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -486,13 +486,27 @@ def close_or_unclose_sales_orders(names, status): frappe.local.message_log = [] +def get_requested_item_qty(sales_order): + return frappe._dict(frappe.db.sql(""" + select sales_order_item, sum(stock_qty) + from `tabMaterial Request Item` + where docstatus = 1 + and sales_order = %s + group by sales_order_item + """, sales_order)) + @frappe.whitelist() def make_material_request(source_name, target_doc=None): + requested_item_qty = get_requested_item_qty(source_name) + def postprocess(source, doc): doc.material_request_type = "Purchase" def update_item(source, target, source_parent): target.project = source_parent.project + target.qty = source.stock_qty - requested_item_qty.get(source.name, 0) + target.conversion_factor = 1 + target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0) doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { @@ -517,7 +531,7 @@ def make_material_request(source_name, target_doc=None): "stock_uom": "uom", "stock_qty": "qty" }, - "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code), + "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0), "postprocess": update_item } }, target_doc, postprocess) @@ -574,8 +588,8 @@ def make_delivery_note(source_name, target_doc=None): if item: target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \ - or item.get("selling_cost_center") \ - or item_group.get("selling_cost_center") + or item.get("buying_cost_center") \ + or item_group.get("buying_cost_center") target_doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { 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 3e6ee86df00..a16a77f1340 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -737,6 +737,17 @@ class POSCart { const customer = this.frm.doc.customer; this.customer_field.set_value(customer); + + if (this.numpad) { + const disable_btns = this.disable_numpad_control() + const enable_btns = [__('Rate'), __('Disc')] + + if (disable_btns) { + enable_btns.filter(btn => !disable_btns.includes(btn)) + } + + this.numpad.enable_buttons(enable_btns); + } } get_grand_total() { @@ -1507,6 +1518,16 @@ class NumberPad { } } + enable_buttons(btns) { + btns.forEach((btn) => { + const $btn = this.get_btn(btn); + $btn.prop("disabled", false) + $btn.hover(() => { + $btn.css('cursor','pointer'); + }) + }) + } + set_class() { for (const btn in this.add_class) { const class_name = this.add_class[btn]; diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index c431df403aa..b1b64a3e4ec 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -184,7 +184,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ this.calculate_incentive(sales_person); refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name, sales_person.parentfield); - } + } }, sales_person: function(doc, cdt, cdn) { @@ -215,6 +215,9 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, callback:function(r){ if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + + if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; + me.set_batch_number(cdt, cdn); me.batch_no(doc, cdt, cdn); } @@ -358,13 +361,18 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ this._super(doc, cdt, cdn, dont_fetch_price_list_rate); if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) && in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - this.set_batch_number(cdt, cdn); - } + if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; + this.set_batch_number(cdt, cdn); + } }, qty: function(doc, cdt, cdn) { this._super(doc, cdt, cdn); - this.set_batch_number(cdt, cdn); + + if(in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; + this.set_batch_number(cdt, cdn); + } }, /* Determine appropriate batch number and set it in the form. @@ -396,7 +404,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ if (doc.auto_repeat) { frappe.call({ method:"frappe.desk.doctype.auto_repeat.auto_repeat.update_reference", - args:{ + args:{ docname: doc.auto_repeat, reference:doc.name }, diff --git a/erpnext/setup/setup_wizard/operations/sample_data.py b/erpnext/setup/setup_wizard/operations/sample_data.py index e21c9bd1089..64c669363d3 100644 --- a/erpnext/setup/setup_wizard/operations/sample_data.py +++ b/erpnext/setup/setup_wizard/operations/sample_data.py @@ -34,7 +34,7 @@ def make_opportunity(items, customer): b = frappe.get_doc({ "doctype": "Opportunity", "opportunity_from": "Customer", - "customer": customer, + "party_name": customer, "opportunity_type": _("Sales"), "with_items": 1 }) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index efc128aa0c5..3dd8729eb54 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -202,11 +202,13 @@ def _get_cart_quotation(party=None): if quotation: qdoc = frappe.get_doc("Quotation", quotation[0].name) else: + [company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"]) qdoc = frappe.get_doc({ "doctype": "Quotation", "naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-", "quotation_to": party.doctype, - "company": frappe.db.get_value("Shopping Cart Settings", None, "company"), + "company": company, + "selling_price_list": price_list, "order_type": "Shopping Cart", "status": "Draft", "docstatus": 0, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 23f7ed1889d..6cd329ef2bc 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -414,6 +414,8 @@ def get_returned_qty_map(delivery_note): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): doc = frappe.get_doc('Delivery Note', source_name) + + to_make_invoice_qty_map = {} returned_qty_map = get_returned_qty_map(source_name) invoiced_qty_map = get_invoiced_qty_map(source_name) @@ -434,8 +436,7 @@ def make_sales_invoice(source_name, target_doc=None): target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address)) def update_item(source_doc, target_doc, source_parent): - target_doc.qty, returned_qty = get_pending_qty(source_doc) - returned_qty_map[source_doc.item_code] = returned_qty + target_doc.qty = to_make_invoice_qty_map[source_doc.name] if source_doc.serial_no and source_parent.per_billed > 0: target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code, @@ -443,7 +444,12 @@ def make_sales_invoice(source_name, target_doc=None): def get_pending_qty(item_row): pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) - returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) + + returned_qty = 0 + if returned_qty_map.get(item_row.item_code, 0) > 0: + returned_qty = flt(returned_qty_map.get(item_row.item_code, 0)) + returned_qty_map[item_row.item_code] -= pending_qty + if returned_qty: if returned_qty >= pending_qty: pending_qty = 0 @@ -451,7 +457,10 @@ def make_sales_invoice(source_name, target_doc=None): else: pending_qty -= returned_qty returned_qty = 0 - return pending_qty, returned_qty + + to_make_invoice_qty_map[item_row.name] = pending_qty + + return pending_qty doc = get_mapped_doc("Delivery Note", source_name, { "Delivery Note": { @@ -471,7 +480,7 @@ def make_sales_invoice(source_name, target_doc=None): "cost_center": "cost_center" }, "postprocess": update_item, - "filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0 + "filter": lambda d: get_pending_qty(d) <= 0 if not doc.get("is_return") else get_pending_qty(d) > 0 }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 01b4734bf58..bc8c7493d5c 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -20,8 +20,7 @@ class DeliveryTrip(Document): # Google Maps returns distances in meters by default self.default_distance_uom = frappe.db.get_single_value("Global Defaults", "default_distance_unit") or "Meter" self.uom_conversion_factor = frappe.db.get_value("UOM Conversion Factor", - {"from_uom": "Meter", "to_uom": self.default_distance_uom}, - "value") + {"from_uom": "Meter", "to_uom": self.default_distance_uom}, "value") def validate(self): self.validate_stop_addresses() @@ -139,7 +138,7 @@ class DeliveryTrip(Document): # Include last leg in the final distance calculation self.uom = self.default_distance_uom total_distance = sum([leg.get("distance", {}).get("value", 0.0) - for leg in directions.get("legs")]) # in meters + for leg in directions.get("legs")]) # in meters self.total_distance = total_distance * self.uom_conversion_factor else: idx += len(route) - 1 @@ -358,8 +357,12 @@ def notify_customers(delivery_trip): email_recipients = [] for stop in delivery_trip.delivery_stops: - contact_info = frappe.db.get_value("Contact", stop.contact, - ["first_name", "last_name", "email_id", "gender"], as_dict=1) + contact_info = frappe.db.get_value("Contact", stop.contact, ["first_name", "last_name", "email_id"], as_dict=1) + + context.update({"items": []}) + if stop.delivery_note: + items = frappe.get_all("Delivery Note Item", filters={"parent": stop.delivery_note, "docstatus": 1}, fields=["*"]) + context.update({"items": items}) if contact_info and contact_info.email_id: context.update(stop.as_dict()) @@ -369,9 +372,9 @@ def notify_customers(delivery_trip): dispatch_template = frappe.get_doc("Email Template", dispatch_template_name) frappe.sendmail(recipients=contact_info.email_id, - subject=dispatch_template.subject, - message=frappe.render_template(dispatch_template.response, context), - attachments=get_attachments(stop)) + subject=dispatch_template.subject, + message=frappe.render_template(dispatch_template.response, context), + attachments=get_attachments(stop)) stop.db_set("email_sent_to", contact_info.email_id) email_recipients.append(contact_info.email_id) @@ -388,9 +391,7 @@ def get_attachments(delivery_stop): return [] dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment") - attachments = frappe.attach_print("Delivery Note", - delivery_stop.delivery_note, - file_name="Delivery Note", - print_format=dispatch_attachment) + attachments = frappe.attach_print("Delivery Note", delivery_stop.delivery_note, + file_name="Delivery Note", print_format=dispatch_attachment) return [attachments] diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 16fd811fee3..c5697eda7a8 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -67,8 +67,6 @@ class Item(WebsiteGenerator): from frappe.model.naming import set_name_by_naming_series set_name_by_naming_series(self) self.item_code = self.name - elif not self.item_code: - msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1) self.item_code = strip(self.item_code) self.name = self.item_code diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index d1822904278..30675b54b3e 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -31,13 +31,16 @@ class ItemPrice(Document): frappe.throw(_("Valid From Date must be lesser than Valid Upto Date.")) def update_price_list_details(self): - self.buying, self.selling, self.currency = \ - frappe.db.get_value("Price List", - {"name": self.price_list, "enabled": 1}, - ["buying", "selling", "currency"]) + if self.price_list: + self.buying, self.selling, self.currency = \ + frappe.db.get_value("Price List", + {"name": self.price_list, "enabled": 1}, + ["buying", "selling", "currency"]) def update_item_details(self): - self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"]) + if self.item_code: + self.item_name, self.item_description = frappe.db.get_value("Item", + self.item_code,["item_name", "description"]) def check_duplicates(self): conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" diff --git a/erpnext/stock/doctype/price_list/price_list.json b/erpnext/stock/doctype/price_list/price_list.json index 6b447ee8834..56340fb05ca 100644 --- a/erpnext/stock/doctype/price_list/price_list.json +++ b/erpnext/stock/doctype/price_list/price_list.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -22,6 +23,7 @@ "collapsible": 0, "columns": 0, "default": "1", + "fetch_if_empty": 0, "fieldname": "enabled", "fieldtype": "Check", "hidden": 0, @@ -53,6 +55,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "sb_1", "fieldtype": "Section Break", "hidden": 0, @@ -83,6 +86,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "price_list_name", "fieldtype": "Data", "hidden": 0, @@ -116,6 +120,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", "hidden": 0, @@ -148,6 +153,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "buying", "fieldtype": "Check", "hidden": 0, @@ -179,6 +185,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "selling", "fieldtype": "Check", "hidden": 0, @@ -210,7 +217,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "price_not_uom_dependant", + "fetch_if_empty": 0, + "fieldname": "price_not_uom_dependent", "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, @@ -219,7 +227,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Price Not UOM Dependant", + "label": "Price Not UOM Dependent", "length": 0, "no_copy": 0, "permlevel": 0, @@ -242,6 +250,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -272,6 +281,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "countries", "fieldtype": "Table", "hidden": 0, @@ -310,7 +320,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2018-08-29 06:35:16.546274", + "modified": "2019-06-24 17:16:28.027302", "modified_by": "Administrator", "module": "Stock", "name": "Price List", diff --git a/erpnext/stock/doctype/price_list/test_price_list_uom.js b/erpnext/stock/doctype/price_list/test_price_list_uom.js index 526d0da97d8..7fbce7d59d2 100644 --- a/erpnext/stock/doctype/price_list/test_price_list_uom.js +++ b/erpnext/stock/doctype/price_list/test_price_list_uom.js @@ -7,7 +7,7 @@ QUnit.test("test price list with uom dependancy", function(assert) { () => frappe.set_route('Form', 'Price List', 'Standard Buying'), () => { - cur_frm.set_value('price_not_uom_dependant','1'); + cur_frm.set_value('price_not_uom_dependent','1'); frappe.timeout(1); }, () => cur_frm.save(), diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 5c1f573c457..052118f67ea 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -4,11 +4,10 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, cint, nowdate +from frappe.utils import flt, cint, nowdate, getdate from frappe import throw, _ import frappe.defaults -from frappe.utils import getdate from erpnext.controllers.buying_controller import BuyingController from erpnext.accounts.utils import get_account_currency from frappe.desk.notifications import clear_doctype_notifications @@ -128,7 +127,7 @@ class PurchaseReceipt(BuyingController): self.company, self.base_grand_total) self.update_prevdoc_status() - if self.per_billed < 100: + if cint(self.per_billed) < 100: self.update_billing_status() else: self.status = "Completed" diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index c19228fea8b..dfcce5f820b 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -562,7 +562,7 @@ class TestStockEntry(unittest.TestCase): for d in stock_entry.get("items"): if d.item_code != "_Test FG Item 2": rm_cost += flt(d.amount) - fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount + fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items")))[0].amount self.assertEqual(fg_cost, flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2)) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 851088be948..0828a5ca543 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -834,12 +834,12 @@ def get_price_list_currency(price_list): 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_dependant"], as_dict=True) + "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_dependant + return not result.price_not_uom_dependent def get_price_list_currency_and_exchange_rate(args): diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index 85ea5b19be7..10ea3b7e228 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -1,7 +1,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: 'Stock Summary', + title: __('Stock Summary'), single_column: true }); page.start = 0; diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.js b/erpnext/stock/report/delayed_item_report/delayed_item_report.js index f1ead2c9fbb..40e6abefeb0 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.js +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.js @@ -55,7 +55,7 @@ frappe.query_reports["Delayed Item Report"] = { label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], - default: "Delivery Note", + default: "Sales Invoice", reqd: 1 }, ] diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py index f8aa98886c1..a3d1faf9d3f 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py @@ -46,7 +46,8 @@ class DelayedItemReport(object): self.transactions = frappe.db.sql(""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name, `tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount, `tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order, - `tab{doctype}`.customer, `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total + `tab{doctype}`.shipping_address_name, `tab{doctype}`.po_no, `tab{doctype}`.customer, + `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total FROM `tab{child_doc}`, `tab{doctype}` WHERE `tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and @@ -97,12 +98,20 @@ class DelayedItemReport(object): "fieldtype": "Link", "options": based_on, "width": 100 - },{ + }, + { "label": _("Customer"), "fieldname": "customer", "fieldtype": "Link", "options": "Customer", - "width": 100 + "width": 200 + }, + { + "label": _("Shipping Address"), + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "options": "Address", + "width": 120 }, { "label": _("Expected Delivery Date"), diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.js b/erpnext/stock/report/delayed_order_report/delayed_order_report.js index 5b02a58fafc..aab0f3d0d1f 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.js +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.js @@ -55,7 +55,7 @@ frappe.query_reports["Delayed Order Report"] = { label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], - default: "Delivery Note", + default: "Sales Invoice", reqd: 1 }, ] diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.py b/erpnext/stock/report/delayed_order_report/delayed_order_report.py index d2a1a30d9e2..79dc5d88219 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py @@ -42,7 +42,14 @@ class DelayedOrderReport(DelayedItemReport): "fieldname": "customer", "fieldtype": "Link", "options": "Customer", - "width": 100 + "width": 200 + }, + { + "label": _("Shipping Address"), + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "options": "Address", + "width": 140 }, { "label": _("Expected Delivery Date"), @@ -73,11 +80,11 @@ class DelayedOrderReport(DelayedItemReport): "fieldname": "sales_order", "fieldtype": "Link", "options": "Sales Order", - "width": 100 + "width": 150 }, { "label": _("Customer PO"), "fieldname": "po_no", "fieldtype": "Data", - "width": 100 + "width": 110 }] \ No newline at end of file diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js index b7461c485f6..264642856da 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js @@ -10,8 +10,23 @@ frappe.query_reports["Total Stock Summary"] = { "fieldtype": "Select", "width": "80", "reqd": 1, - "options": ["","Warehouse", "Company"], - "default": "Warehouse" + "options": ["", "Warehouse", "Company"], + "change": function() { + let group_by = frappe.query_report.get_filter_value("group_by") + let company_filter = frappe.query_report.get_filter("company") + if (group_by == "Company") { + company_filter.df.reqd = 0; + company_filter.df.hidden = 1; + frappe.query_report.set_filter_value("company", ""); + company_filter.refresh(); + } + else { + company_filter.df.reqd = 1; + company_filter.df.hidden = 0; + company_filter.refresh(); + frappe.query_report.refresh(); + } + } }, { "fieldname": "company", @@ -23,4 +38,4 @@ frappe.query_reports["Total Stock Summary"] = { "reqd": 1 }, ] -} +} \ No newline at end of file diff --git a/erpnext/templates/pages/search_help.py b/erpnext/templates/pages/search_help.py index 4a4b0dbd934..cd767b3099c 100644 --- a/erpnext/templates/pages/search_help.py +++ b/erpnext/templates/pages/search_help.py @@ -5,6 +5,7 @@ from jinja2 import utils from html2text import html2text from frappe.utils import sanitize_html from frappe.utils.global_search import search +from six import text_type def get_context(context): context.no_cache = 1 @@ -12,7 +13,7 @@ def get_context(context): query = str(utils.escape(sanitize_html(frappe.form_dict.q))) context.title = _('Help Results for') context.query = query - + context.route = '/search_help' d = frappe._dict() d.results_sections = get_help_results_sections(query) @@ -73,7 +74,7 @@ def prepare_api_results(api, topics_data): for topic in topics_data: route = api.base_url + '/' + (api.post_route + '/' if api.post_route else "") for key in api.post_route_key_list.split(','): - route += unicode(topic[key]) + route += text_type(topic[key]) results.append(frappe._dict({ 'title': topic[api.post_title_key], diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index 97e2a7f7235..743657de322 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -36,11 +36,11 @@ def send_message(subject="Website Query", message="", sender="", status="Open"): )) if customer: - opportunity.customer = customer[0][0] + opportunity.party_name = customer[0][0] elif lead: - opportunity.lead = lead + opportunity.party_name = lead else: - opportunity.lead = new_lead.name + opportunity.party_name = new_lead.name opportunity.insert(ignore_permissions=True) diff --git a/travis/run-tests.sh b/travis/run-tests.sh new file mode 100755 index 00000000000..7cfd64833b7 --- /dev/null +++ b/travis/run-tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +if [[ $TEST_TYPE == 'Server Side Test' ]]; then + bench run-tests --app erpnext --coverage + +elif [[ $TEST_TYPE == 'Patch Test' ]]; then + wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz + bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis + bench migrate +fi