diff --git a/.travis.yml b/.travis.yml index 9f3737428b9..0d8d2ff8c7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ language: python dist: trusty -group: deprecated-2017Q2 + +addons: + apt: + sources: + - google-chrome + packages: + - google-chrome-stable python: - "2.7" @@ -8,43 +14,43 @@ python: services: - mysql -before_install: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" - install: + - sudo rm /etc/apt/sources.list.d/docker.list - sudo apt-get purge -y mysql-common mysql-server mysql-client - nvm install v7.10.0 - # - wget https://raw.githubusercontent.com/frappe/bench/master/install_scripts/setup_frappe.sh - # - sudo bash setup_frappe.sh --skip-setup-bench --mysql-root-password travis --bench-branch develop - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py - sudo python install.py --develop --user travis --without-bench-setup - sudo pip install -e ~/bench - # - sudo pip install --upgrade pip - rm $TRAVIS_BUILD_DIR/.git/shallow - bash $TRAVIS_BUILD_DIR/travis/bench_init.sh - cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/ -script: +before_script: + - wget http://chromedriver.storage.googleapis.com/2.27/chromedriver_linux64.zip + - unzip chromedriver_linux64.zip + - sudo apt-get install libnss3 + - sudo apt-get --only-upgrade install google-chrome-stable + - sudo cp chromedriver /usr/local/bin/. + - sudo chmod +x /usr/local/bin/chromedriver + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + - sleep 3 + - mysql -u root -ptravis -e 'create database test_frappe' + - echo "USE mysql;\nCREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe';\nFLUSH PRIVILEGES;\n" | mysql -u root -ptravis + - echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis + - cd ~/frappe-bench - bench get-app erpnext $TRAVIS_BUILD_DIR - bench use test_site - bench reinstall --yes - bench build + - bench scheduler disable - bench start & - sleep 10 - - bench --verbose run-tests --driver Firefox -before_script: - - mysql -u root -ptravis -e 'create database test_frappe' - - echo "USE mysql;\nCREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe';\nFLUSH PRIVILEGES;\n" | mysql -u root -ptravis - - echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/92b3bea86d8c5397beef - on_success: always - on_failure: always - on_start: never +script: + - set -e + - bench --verbose run-tests + - sleep 5 + - bench --verbose run-tests --ui-tests diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a8a8055ce86..5a368384e66 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '8.3.5' +__version__ = '8.3.6' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index a6348f19daf..f99d0bb5f98 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -83,7 +83,7 @@ def validate_expense_against_budget(args): budget_records = frappe.db.sql(""" select - b.{budget_against_field}, ba.budget_amount, b.monthly_distribution, + b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution, b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded from @@ -111,15 +111,15 @@ def validate_budget_records(args, budget_records): args["month_end_date"] = get_last_day(args.posting_date) compare_expense_with_budget(args, budget_amount, - _("Accumulated Monthly"), monthly_action) + _("Accumulated Monthly"), monthly_action, budget.budget_against) if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \ and yearly_action != monthly_action: compare_expense_with_budget(args, flt(budget.budget_amount), - _("Annual"), yearly_action) + _("Annual"), yearly_action, budget.budget_against) -def compare_expense_with_budget(args, budget_amount, action_for, action): +def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against): actual_expense = get_actual_expense(args) if actual_expense > budget_amount: diff = actual_expense - budget_amount @@ -127,7 +127,7 @@ def compare_expense_with_budget(args, budget_amount, action_for, action): msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format( _(action_for), frappe.bold(args.account), args.budget_against_field, - frappe.bold(args.budget_against), + frappe.bold(budget_against), frappe.bold(fmt_money(budget_amount, currency=currency)), frappe.bold(fmt_money(diff, currency=currency))) diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index 15895dc5ca7..f6849ba433f 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -140,6 +140,33 @@ class TestBudget(unittest.TestCase): budget.load_from_db() budget.cancel() + def test_monthly_budget_against_parent_group_cost_center(self): + cost_center = "_Test Cost Center 3 - _TC" + + if not frappe.db.exists("Cost Center", cost_center): + frappe.get_doc({ + 'doctype': 'Cost Center', + 'cost_center_name': '_Test Cost Center 3', + 'parent_cost_center': "_Test Company - _TC", + 'company': '_Test Company', + 'is_group': 0 + }).insert(ignore_permissions=True) + + budget = make_budget("Cost Center", cost_center) + frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") + + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + "_Test Bank - _TC", 40000, cost_center) + + self.assertRaises(BudgetError, jv.submit) + + budget.load_from_db() + budget.cancel() + jv.cancel() + + frappe.delete_doc('Journal Entry', jv.name) + frappe.delete_doc('Cost Center', cost_center) + def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "Project": budget_against = "_Test Project" @@ -167,7 +194,8 @@ def make_budget(budget_against=None, cost_center=None): if budget_against == "Project": budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Project/_Test Fiscal Year 2013%")}) else: - budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Cost Center - _TC/_Test Fiscal Year 2013%")}) + cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/_Test Fiscal Year 2013") + budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)}) for d in budget_list: frappe.db.sql("delete from `tabBudget` where name = %(name)s", d) frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 5bfd4d9c8ac..63832fa2c90 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -291,37 +291,39 @@ frappe.ui.form.on('Payment Entry', { set_account_currency_and_balance: function(frm, account, currency_field, balance_field, callback_function) { - frappe.call({ - method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details", - args: { - "account": account, - "date": frm.doc.posting_date - }, - callback: function(r, rt) { - if(r.message) { - frm.set_value(currency_field, r.message['account_currency']); - frm.set_value(balance_field, r.message['account_balance']); + if (frm.doc.posting_date) { + frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details", + args: { + "account": account, + "date": frm.doc.posting_date + }, + callback: function(r, rt) { + if(r.message) { + frm.set_value(currency_field, r.message['account_currency']); + frm.set_value(balance_field, r.message['account_balance']); - if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") { - frm.toggle_reqd(["reference_no", "reference_date"], - (r.message['account_type'] == "Bank" ? 1 : 0)); - if(!frm.doc.received_amount && frm.doc.paid_amount) - frm.events.paid_amount(frm); - } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") { - frm.toggle_reqd(["reference_no", "reference_date"], - (r.message['account_type'] == "Bank" ? 1 : 0)); + if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") { + frm.toggle_reqd(["reference_no", "reference_date"], + (r.message['account_type'] == "Bank" ? 1 : 0)); + if(!frm.doc.received_amount && frm.doc.paid_amount) + frm.events.paid_amount(frm); + } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") { + frm.toggle_reqd(["reference_no", "reference_date"], + (r.message['account_type'] == "Bank" ? 1 : 0)); - if(!frm.doc.paid_amount && frm.doc.received_amount) - frm.events.received_amount(frm); + if(!frm.doc.paid_amount && frm.doc.received_amount) + frm.events.received_amount(frm); + } + + if(callback_function) callback_function(frm); + + frm.events.hide_unhide_fields(frm); + frm.events.set_dynamic_labels(frm); } - - if(callback_function) callback_function(frm); - - frm.events.hide_unhide_fields(frm); - frm.events.set_dynamic_labels(frm); } - } - }); + }); + } }, paid_from_account_currency: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c6559f8e9eb..e9471b7effa 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -492,9 +492,13 @@ def get_outstanding_reference_documents(args): for d in outstanding_invoices: d["exchange_rate"] = 1 - if party_account_currency != company_currency \ - and d.voucher_type in ("Sales Invoice", "Purchase Invoice"): + if party_account_currency != company_currency: + if d.voucher_type in ("Sales Invoice", "Purchase Invoice"): d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate") + elif d.voucher_type == "Journal Entry": + d["exchange_rate"] = get_exchange_rate( + party_account_currency, company_currency, d.posting_date + ) # Get all SO / PO which are not fully billed or aginst which full advance not paid orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py index bfe00ef94ac..bc9d76646b9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py @@ -11,8 +11,7 @@ def get_data(): 'Sales Invoice': 'return_against' }, 'internal_links': { - 'Sales Order': ['items', 'sales_order'], - 'Delivery Note': ['items', 'delivery_note'] + 'Sales Order': ['items', 'sales_order'] }, 'transactions': [ { diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6f1c2c973d0..800e6a91b81 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1107,6 +1107,16 @@ class TestSalesInvoice(unittest.TestCase): def test_item_wise_tax_breakup(self): si = create_sales_invoice(qty=100, rate=50, do_not_save=True) + si.append("items", { + "item_code": "_Test Item", + "warehouse": "_Test Warehouse - _TC", + "qty": 100, + "rate": 50, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC" + }) + si.append("taxes", { "charge_type": "On Net Total", "account_head": "_Test Account Service Tax - _TC", @@ -1115,8 +1125,8 @@ class TestSalesInvoice(unittest.TestCase): "rate": 10 }) si.insert() - - tax_breakup_html = '''\n
\n\t\n\t\t\n\t\t\n\t
Item NameTaxable Amount_Test Account Service Tax - _TC
_Test Item\u20b9 5,000.00(10.0%) \u20b9 500.00
\n
''' + + tax_breakup_html = '''\n
\n\t\n\t\t\n\t\t\n\t
Item NameTaxable Amount_Test Account Service Tax - _TC
_Test Item\u20b9 10,000.00(10.0%) \u20b9 1,000.00
\n
''' self.assertEqual(si.other_charges_calculation, tax_breakup_html) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 8eb83af6d57..72785da5631 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -517,9 +517,9 @@ class calculate_taxes_and_totals(object): headings = get_table_column_headings(tax_accounts) - distinct_items = self.get_distinct_items() + distinct_items, taxable_amount = self.get_distinct_items() - rows = get_table_rows(distinct_items, item_tax, tax_accounts, company_currency) + rows = get_table_rows(distinct_items, item_tax, tax_accounts, company_currency, taxable_amount) if not rows: self.doc.other_charges_calculation = "" @@ -568,13 +568,17 @@ class calculate_taxes_and_totals(object): def get_distinct_items(self): distinct_item_names = [] distinct_items = [] + taxable_amount = {} for item in self.doc.items: item_code = item.item_code or item.item_name if item_code not in distinct_item_names: distinct_item_names.append(item_code) distinct_items.append(item) + taxable_amount[item_code] = item.net_amount + else: + taxable_amount[item_code] = taxable_amount.get(item_code, 0) + item.net_amount - return distinct_items + return distinct_items, taxable_amount def get_table_column_headings(tax_accounts): headings_name = [_("Item Name"), _("Taxable Amount")] + [d[1] for d in tax_accounts] @@ -587,7 +591,7 @@ def get_table_column_headings(tax_accounts): return headings -def get_table_rows(distinct_items, item_tax, tax_accounts, company_currency): +def get_table_rows(distinct_items, item_tax, tax_accounts, company_currency, taxable_amount): rows = [] for item in distinct_items: item_tax_record = item_tax.get(item.item_code or item.item_name) @@ -601,10 +605,11 @@ def get_table_rows(distinct_items, item_tax, tax_accounts, company_currency): + item_tax_record[head[0]][1] + "") else: taxes.append("") - + + item_code = item.item_code or item.item_name rows.append("{item_name}{taxable_amount}{taxes}".format(**{ "item_name": item.item_name, - "taxable_amount": fmt_money(item.net_amount, item.precision("net_amount"), company_currency), + "taxable_amount": fmt_money(taxable_amount.get(item_code, 0), item.precision("net_amount"), company_currency), "taxes": "".join(taxes) })) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 18a97acb901..6d9b51424d1 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -56,7 +56,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Series", "length": 0, @@ -88,7 +88,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Opportunity From", "length": 0, @@ -277,8 +277,8 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, + "in_list_view": 1, + "in_standard_filter": 0, "label": "Opportunity Type", "length": 0, "no_copy": 0, @@ -310,7 +310,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 1, "label": "Status", "length": 0, @@ -1189,7 +1189,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 14:29:07.077697", + "modified": "2017-07-10 15:29:23.921967", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/docs/assets/img/setup/sms-setting2.jpg b/erpnext/docs/assets/img/setup/sms-settings2.jpg similarity index 100% rename from erpnext/docs/assets/img/setup/sms-setting2.jpg rename to erpnext/docs/assets/img/setup/sms-settings2.jpg diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ec93f626e37..0966485cfed 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -80,6 +80,13 @@ website_route_rules = [ "parents": [{"title": _("Supplier Quotation"), "name": "quotations"}] } }, + {"from_route": "/quotes", "to_route": "Quotation"}, + {"from_route": "/quotes/", "to_route": "order", + "defaults": { + "doctype": "Quotation", + "parents": [{"title": _("Quotes"), "name": "quotes"}] + } + }, {"from_route": "/shipments", "to_route": "Delivery Note"}, {"from_route": "/shipments/", "to_route": "order", "defaults": { @@ -110,6 +117,7 @@ standard_portal_menu_items = [ {"title": _("Projects"), "route": "/project", "reference_doctype": "Project"}, {"title": _("Request for Quotations"), "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier"}, {"title": _("Supplier Quotation"), "route": "/quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"}, + {"title": _("Quotes"), "route": "/quotes", "reference_doctype": "Quotation", "role":"Customer"}, {"title": _("Orders"), "route": "/orders", "reference_doctype": "Sales Order", "role":"Customer"}, {"title": _("Invoices"), "route": "/invoices", "reference_doctype": "Sales Invoice", "role":"Customer"}, {"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"}, diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py index 81455f1d175..ad9d43f5519 100644 --- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -12,32 +12,31 @@ import frappe.utils class TestDailyWorkSummary(unittest.TestCase): def test_email_trigger(self): - settings, employees, emails = self.setup_and_prepare_test() - - for d in employees: + self.setup_and_prepare_test() + for d in self.employees: # check that email is sent to this employee - self.assertTrue(d.user_id in [d.recipient for d in emails - if settings.subject in d.message]) + self.assertTrue(d.user_id in [d.recipient for d in self.emails + if self.settings.subject in d.message]) def test_email_trigger_failed(self): hour = '00' if frappe.utils.nowtime().split(':')[0]=='00': hour = '01' - settings, employees, emails = self.setup_and_prepare_test(hour) + self.setup_and_prepare_test(hour) - for d in employees: + for d in self.employees: # check that email is sent to this employee - self.assertFalse(d.user_id in [d.recipient for d in emails - if settings.subject in d.message]) + self.assertFalse(d.user_id in [d.recipient for d in self.emails + if self.settings.subject in d.message]) def test_incoming(self): - settings, employees, emails = self.setup_and_prepare_test() - # get test mail with message-id as in-reply-to + self.setup_and_prepare_test() + with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f: - test_mails = [f.read().replace('{{ sender }}', employees[-1].user_id)\ - .replace('{{ message_id }}', emails[-1].message_id)] + test_mails = [f.read().replace('{{ sender }}', self.employees[-1].user_id)\ + .replace('{{ message_id }}', self.emails[-1].message_id)] # pull the mail email_account = frappe.get_doc("Email Account", "_Test Email Account 1") @@ -52,30 +51,34 @@ class TestDailyWorkSummary(unittest.TestCase): self.assertTrue('I built Daily Work Summary!' in summary) def setup_and_prepare_test(self, hour=None): - if not hour: - hour = frappe.utils.nowtime().split(':')[0] frappe.db.sql('delete from `tabDaily Work Summary`') frappe.db.sql('delete from `tabEmail Queue`') frappe.db.sql('delete from `tabEmail Queue Recipient`') frappe.db.sql('delete from `tabCommunication`') - # setup email to trigger at this our - settings = frappe.get_doc('Daily Work Summary Settings') - settings.companies = [] - - settings.append('companies', dict(company='_Test Company', - send_emails_at=hour + ':00')) - settings.test_subject = 'this is a subject for testing summary emails' - settings.save() + self.setup_settings(hour) from erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings \ import trigger_emails trigger_emails() # check if emails are created - employees = frappe.get_all('Employee', fields = ['user_id'], - filters=dict(company='_Test Company', status='Active')) + self.employees = frappe.get_all('Employee', fields = ['user_id'], + filters=dict(company='_Test Company', status='Active', user_id=('!=', 'test@example.com'))) - emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1) + self.emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1) + + frappe.db.commit() + + def setup_settings(self, hour=None): + # setup email to trigger at this our + if not hour: + hour = frappe.utils.nowtime().split(':')[0] + self.settings = frappe.get_doc('Daily Work Summary Settings') + self.settings.companies = [] + + self.settings.append('companies', dict(company='_Test Company', + send_emails_at=hour + ':00')) + self.settings.test_subject = 'this is a subject for testing summary emails' + self.settings.save() - return settings, employees, emails \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 7aef4fcaad5..681b62f726c 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -365,9 +365,6 @@ class BOM(WebsiteGenerator): base_total_rm_cost = 0 for d in self.get('items'): - if d.bom_no: - d.rate = self.get_bom_unitcost(d.bom_no) - d.base_rate = flt(d.rate) * flt(self.conversion_rate) d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d)) d.base_amount = d.amount * flt(self.conversion_rate) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json index ee033c427d8..e56e5a1251f 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.json +++ b/erpnext/manufacturing/doctype/production_order/production_order.json @@ -1358,7 +1358,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 14:29:00.457874", + "modified": "2017-07-10 14:29:00.457874", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Order", diff --git a/erpnext/manufacturing/doctype/production_order_item/production_order_item.json b/erpnext/manufacturing/doctype/production_order_item/production_order_item.json index 06a7876d40a..00e3adf13be 100644 --- a/erpnext/manufacturing/doctype/production_order_item/production_order_item.json +++ b/erpnext/manufacturing/doctype/production_order_item/production_order_item.json @@ -353,7 +353,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-05-15 17:37:20.212361", + "modified": "2017-07-10 17:37:20.212361", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Order Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 80d64e8ae55..d83363852cc 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -406,6 +406,7 @@ erpnext.patches.v8_0.change_in_words_varchar_length erpnext.patches.v8_0.update_stock_qty_value_in_bom_item erpnext.patches.v8_0.create_domain_docs #16-05-2017 erpnext.patches.v8_0.update_sales_cost_in_project +erpnext.patches.v8_0.create_address_doc_from_address_field_in_company #10-05-2017 erpnext.patches.v8_0.save_system_settings erpnext.patches.v8_1.delete_deprecated_reports erpnext.patches.v8_1.setup_gst_india #2017-06-27 diff --git a/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py b/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py new file mode 100644 index 00000000000..cf1bc5af057 --- /dev/null +++ b/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py @@ -0,0 +1,33 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + # new field address_html is created in place of address field for the company's address in PR #8754 (without patch) + # so here is the patch for moving the address details in the address doc + company_list = [] + if 'address' in frappe.db.get_table_columns('Company'): + company_list = frappe.db.sql('''select name, address from `tabCompany` + where address is not null and address != ""''', as_dict=1) + + for company in company_list: + add_list = company.address.split(" ") + if ',' in company.address: + add_list = company.address.rpartition(',') + elif ' ' in company.address: + add_list = company.address.rpartition(' ') + else: + add_list = [company.address, None, company.address] + + doc = frappe.get_doc({ + "doctype":"Address", + "address_line1": add_list[0], + "city": add_list[2], + "links": [{ + "link_doctype": "Company", + "link_name": company.name + }] + }) + doc.save() diff --git a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py b/erpnext/patches/v8_0/merge_student_batch_and_student_group.py index 7cfdf603030..c5654eb3acc 100644 --- a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py +++ b/erpnext/patches/v8_0/merge_student_batch_and_student_group.py @@ -9,8 +9,8 @@ from frappe.model.mapper import get_mapped_doc def execute(): # for converting student batch into student group - for doctype in ["Student Group", "Student Group Student", "Student Group Instructor", "Student Attendance"]: - frappe.reload_doc("schools", "doctype", doctype) + for doctype in ["Student Group", "Student Group Student", "Student Group Instructor", "Student Attendance", "Student"]: + frappe.reload_doc("schools", "doctype", frappe.scrub(doctype)) if frappe.db.table_exists("Student Batch"): student_batches = frappe.db.sql('''select name as student_group_name, student_batch_name as batch, diff --git a/erpnext/patches/v8_1/setup_gst_india.py b/erpnext/patches/v8_1/setup_gst_india.py index ce27d371b75..566069369c5 100644 --- a/erpnext/patches/v8_1/setup_gst_india.py +++ b/erpnext/patches/v8_1/setup_gst_india.py @@ -5,6 +5,7 @@ def execute(): frappe.reload_doc('regional', 'doctype', 'gst_settings') frappe.reload_doc('regional', 'doctype', 'gst_hsn_code') frappe.reload_doc('stock', 'doctype', 'item') + frappe.reload_doc("stock", "doctype", "customs_tariff_number") for report_name in ('GST Sales Register', 'GST Purchase Register', 'GST Itemised Sales Register', 'GST Itemised Purchase Register'): diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index e917a75d6d7..e09925d0593 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -693,19 +693,26 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var distinct_item_names = []; var distinct_items = []; + var taxable_amount = {}; $.each(this.frm.doc["items"] || [], function(i, item) { - if(distinct_item_names.indexOf(item.item_code || item.item_name)===-1) { - distinct_item_names.push(item.item_code || item.item_name); + var item_code = item.item_code || item.item_name; + if(distinct_item_names.indexOf(item_code)===-1) { + distinct_item_names.push(item_code); distinct_items.push(item); + taxable_amount[item_code] = item.net_amount; + } else { + taxable_amount[item_code] = taxable_amount[item_code] + item.net_amount; } }); var rows = $.map(distinct_items, function(item) { - var item_tax_record = item_tax[item.item_code || item.item_name]; + var item_code = item.item_code || item.item_name; + var item_tax_record = item_tax[item_code]; if(!item_tax_record) { return null; } + return repl("%(item_name)s%(taxable_amount)s%(taxes)s", { item_name: item.item_name, - taxable_amount: format_currency(item.net_amount, + taxable_amount: format_currency(taxable_amount[item_code], company_currency, precision("net_amount", item)), taxes: $.map(tax_accounts, function(head) { return item_tax_record[head[0]] ? diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 43b45d91bc3..5995a039865 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -68,13 +68,13 @@ var erpnext_slides = [ slide.get_input("company_name").on("change", function () { var parts = slide.get_input("company_name").val().split(" "); var abbr = $.map(parts, function (p) { return p ? p.substr(0, 1) : null }).join(""); - slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase()); + slide.get_field("company_abbr").set_value(abbr.slice(0, 5).toUpperCase()); }).val(frappe.boot.sysdefaults.company_name || "").trigger("change"); slide.get_input("company_abbr").on("change", function () { if (slide.get_input("company_abbr").val().length > 5) { frappe.msgprint("Company Abbreviation cannot have more than 5 characters"); - slide.get_field("company_abbr").set_input(""); + slide.get_field("company_abbr").set_value(""); } }); } @@ -189,6 +189,32 @@ var erpnext_slides = [ } }, + { + // Users + name: 'users', + domains: ["all"], + title: __("Add Users"), + help: __("Add users to your organization, other than yourself"), + add_more: 1, + max_count: 3, + fields: [ + {fieldtype:"Section Break"}, + {fieldtype:"Data", fieldname:"user_fullname", + label:__("Full Name"), static: 1}, + {fieldtype:"Data", fieldname:"user_email", label:__("Email ID"), + placeholder:__("user@example.com"), options: "Email", static: 1}, + {fieldtype:"Column Break"}, + {fieldtype: "Check", fieldname: "user_sales", + label:__("Sales"), "default": 1, static: 1, + hidden: frappe.setup.domain==='Education' ? 1 : 0}, + {fieldtype: "Check", fieldname: "user_purchaser", + label:__("Purchaser"), "default": 1, static: 1, + hidden: frappe.setup.domain==='Education' ? 1 : 0}, + {fieldtype: "Check", fieldname: "user_accountant", + label:__("Accountant"), "default": 1, static: 1, + hidden: frappe.setup.domain==='Education' ? 1 : 0}, + ] + }, { // Taxes @@ -198,7 +224,8 @@ var erpnext_slides = [ title: __("Add Taxes"), help: __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."), add_more: 1, - max_count: 4, + max_count: 3, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break"}, {fieldtype:"Data", fieldname:"tax", label:__("Tax"), @@ -216,7 +243,8 @@ var erpnext_slides = [ title: __("Add Customers"), help: __("List a few of your customers. They could be organizations or individuals."), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break"}, {fieldtype:"Data", fieldname:"customer", label:__("Customer"), @@ -235,7 +263,8 @@ var erpnext_slides = [ title: __("Your Suppliers"), help: __("List a few of your suppliers. They could be organizations or individuals."), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break"}, {fieldtype:"Data", fieldname:"supplier", label:__("Supplier"), @@ -254,7 +283,8 @@ var erpnext_slides = [ title: __("Your Products or Services"), help: __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Data", fieldname:"item", label:__("Item"), @@ -262,16 +292,16 @@ var erpnext_slides = [ {fieldtype:"Select", label:__("Group"), fieldname:"item_group", options:[__("Products"), __("Services"), __("Raw Material"), __("Consumable"), __("Sub Assemblies")], - "default": __("Products")}, + "default": __("Products"), static: 1}, {fieldtype:"Select", fieldname:"item_uom", label:__("UOM"), options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"), __("Hour"), __("Minute"), __("Litre"), __("Meter"), __("Gram")], - "default": __("Unit")}, - {fieldtype: "Check", fieldname: "is_sales_item", label:__("We sell this Item"), default: 1}, - {fieldtype: "Check", fieldname: "is_purchase_item", label:__("We buy this Item")}, + "default": __("Unit"), static: 1}, + {fieldtype: "Check", fieldname: "is_sales_item", label:__("We sell this Item"), default: 1, static: 1}, + {fieldtype: "Check", fieldname: "is_purchase_item", label:__("We buy this Item"), static: 1}, {fieldtype:"Column Break"}, - {fieldtype:"Currency", fieldname:"item_price", label:__("Rate")}, - {fieldtype:"Attach Image", fieldname:"item_img", label:__("Attach Image"), is_private: 0}, + {fieldtype:"Currency", fieldname:"item_price", label:__("Rate"), static: 1}, + {fieldtype:"Attach Image", fieldname:"item_img", label:__("Attach Image"), is_private: 0, static: 1}, ], get_item_count: function() { return this.item_count; @@ -285,7 +315,8 @@ var erpnext_slides = [ title: __("Program"), help: __("Example: Masters in Computer Science"), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Data", fieldname:"program", label:__("Program"), placeholder: __("Program Name")}, @@ -299,7 +330,8 @@ var erpnext_slides = [ title: __("Course"), help: __("Example: Basic Mathematics"), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Data", fieldname:"course", label:__("Course"), placeholder: __("Course Name")}, @@ -313,7 +345,8 @@ var erpnext_slides = [ title: __("Instructor"), help: __("People who teach at your organisation"), add_more: 1, - max_count: 6, + max_count: 5, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Data", fieldname:"instructor", label:__("Instructor"), placeholder: __("Instructor Name")}, @@ -327,20 +360,21 @@ var erpnext_slides = [ title: __("Room"), help: __("Classrooms/ Laboratories etc where lectures can be scheduled."), add_more: 1, - max_count: 4, + max_count: 3, + mandatory_entry: 1, fields: [ {fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Data", fieldname:"room", label:__("Room")}, {fieldtype:"Column Break"}, - {fieldtype:"Int", fieldname:"room_capacity", label:__("Room") + " Capacity"}, + {fieldtype:"Int", fieldname:"room_capacity", label:__("Room") + " Capacity", static: 1}, ] }, { - // last slide: Bootstrap + // last slide: Sample Data name: 'bootstrap', domains: ["all"], - title: __("Bootstrap"), + title: __("Sample Data"), fields: [{fieldtype: "Section Break"}, {fieldtype: "Check", fieldname: "add_sample_data", label: __("Add a few sample records"), "default": 1}, diff --git a/erpnext/schools/doctype/program_enrollment/program_enrollment.json b/erpnext/schools/doctype/program_enrollment/program_enrollment.json index 0203b2cc254..f8a3b9ecc8a 100644 --- a/erpnext/schools/doctype/program_enrollment/program_enrollment.json +++ b/erpnext/schools/doctype/program_enrollment/program_enrollment.json @@ -143,9 +143,10 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "Today", - "fieldname": "enrollment_date", - "fieldtype": "Date", + "default": "0", + "description": "Check this if the Student is residing at the Institute's Hostel.", + "fieldname": "boarding_student", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -153,9 +154,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Enrollment Date", + "label": "Boarding Student", "length": 0, "no_copy": 0, + "options": "", "permlevel": 0, "precision": "", "print_hide": 0, @@ -163,7 +165,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -321,6 +323,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "enrollment_date", + "fieldtype": "Date", + "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": "Enrollment Date", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -370,7 +403,7 @@ "label": "Mode of Transportation", "length": 0, "no_copy": 0, - "options": "\nSchool Bus\nPublic Transport\nSelf-Driving Vehicle\nPick/Drop by Guardian", + "options": "\nWalking\nSchool Bus\nPublic Transport\nSelf-Driving Vehicle\nPick/Drop by Guardian", "permlevel": 0, "precision": "", "print_hide": 0, @@ -638,7 +671,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-06-30 08:21:49.430996", + "modified": "2017-07-10 18:16:15.810616", "modified_by": "Administrator", "module": "Schools", "name": "Program Enrollment", diff --git a/erpnext/schools/doctype/student/student.json b/erpnext/schools/doctype/student/student.json index fa6db0d140b..75cb758b069 100644 --- a/erpnext/schools/doctype/student/student.json +++ b/erpnext/schools/doctype/student/student.json @@ -589,67 +589,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_18", - "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": "Guardian Details", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "guardians", - "fieldtype": "Table", - "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": "Guardians", - "length": 0, - "no_copy": 0, - "options": "Student Guardian", - "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, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -859,6 +798,67 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_18", + "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": "Guardian Details", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "guardians", + "fieldtype": "Table", + "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": "Guardians", + "length": 0, + "no_copy": 0, + "options": "Student Guardian", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1114,7 +1114,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-06-30 08:21:50.423784", + "modified": "2017-07-07 16:30:08.930882", "modified_by": "Administrator", "module": "Schools", "name": "Student", diff --git a/erpnext/schools/doctype/student/student_dashboard.py b/erpnext/schools/doctype/student/student_dashboard.py index cd2314ef12b..b36599c2db5 100644 --- a/erpnext/schools/doctype/student/student_dashboard.py +++ b/erpnext/schools/doctype/student/student_dashboard.py @@ -7,10 +7,24 @@ def get_data(): 'fieldname': 'student', 'transactions': [ { - 'items': ['Student Log', 'Student Group', 'Program Enrollment'] + 'label': _('Admission'), + 'items': ['Program Enrollment'] }, { - 'items': ['Fees', 'Assessment Result', 'Student Attendance', 'Student Leave Application'] + 'label': _('Student Activity'), + 'items': ['Student Log', 'Student Group', ] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + }, + { + 'label': _('Attendance'), + 'items': ['Student Attendance', 'Student Leave Application'] + }, + { + 'label': _('Fee'), + 'items': ['Fees'] } ] } \ No newline at end of file diff --git a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js index 93c83f328c9..c5ff917ab57 100644 --- a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js +++ b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js @@ -135,22 +135,24 @@ schools.StudentsEditor = Class.extend({ frappe.confirm(__("Do you want to update attendance?
Present: {0}\
Absent: {1}", [students_present.length, students_absent.length]), function() { //ifyes - frappe.call({ - method: "erpnext.schools.api.mark_attendance", - freeze: true, - freeze_message: "Marking attendance", - args: { - "students_present": students_present, - "students_absent": students_absent, - "student_group": frm.doc.student_group, - "course_schedule": frm.doc.course_schedule, - "date": frm.doc.date - }, - callback: function(r) { - $(me.wrapper.find(".btn-mark-att")).attr("disabled", false); - frm.trigger("student_group"); - } - }); + if(!frappe.request.ajax_count) { + frappe.call({ + method: "erpnext.schools.api.mark_attendance", + freeze: true, + freeze_message: "Marking attendance", + args: { + "students_present": students_present, + "students_absent": students_absent, + "student_group": frm.doc.student_group, + "course_schedule": frm.doc.course_schedule, + "date": frm.doc.date + }, + callback: function(r) { + $(me.wrapper.find(".btn-mark-att")).attr("disabled", false); + frm.trigger("student_group"); + } + }); + } }, function() { //ifno $(me.wrapper.find(".btn-mark-att")).attr("disabled", false); diff --git a/erpnext/schools/doctype/student_log/student_log.json b/erpnext/schools/doctype/student_log/student_log.json index 43ac1347f4e..6c9d4b05701 100644 --- a/erpnext/schools/doctype/student_log/student_log.json +++ b/erpnext/schools/doctype/student_log/student_log.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "SLog.####", @@ -13,6 +14,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -43,64 +45,7 @@ "unique": 0 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "General\nAcademic\nMedical\nAchievement", - "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, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -131,6 +76,38 @@ "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Type", + "length": 0, + "no_copy": 0, + "options": "General\nAcademic\nMedical\nAchievement", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -160,6 +137,160 @@ "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "academic_year", + "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": "Academic Year", + "length": 0, + "no_copy": 0, + "options": "Academic Year", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "academic_term", + "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": "Academic Term", + "length": 0, + "no_copy": 0, + "options": "Academic Term", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program", + "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": "Program", + "length": 0, + "no_copy": 0, + "options": "Program", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student_batch", + "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": "Student Batch", + "length": 0, + "no_copy": 0, + "options": "Student Batch Name", + "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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -188,6 +319,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -217,17 +349,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-17 17:18:19.596892", + "modified": "2017-07-06 12:42:05.777673", "modified_by": "Administrator", "module": "Schools", "name": "Student Log", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index b102e1d699e..a4d4bf9324f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -86,6 +86,17 @@ class Quotation(SellingController): print_lst.append(lst1) return print_lst +def get_list_context(context=None): + from erpnext.controllers.website_list_for_contact import get_list_context + list_context = get_list_context(context) + list_context.update({ + 'show_sidebar': True, + 'show_search': True, + 'no_breadcrumbs': True, + 'title': _('Quotes'), + }) + + return list_context @frappe.whitelist() def make_sales_order(source_name, target_doc=None): diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index adff615cef7..2766ba067fa 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -69,7 +69,7 @@ class ItemGroup(NestedSet, WebsiteGenerator): context.update({ "items": get_product_list_for_group(product_group = self.name, start=start, limit=context.page_length + 1, search=frappe.form_dict.get("search")), - "parent_groups": get_parent_item_groups(self.name), + "parents": get_parent_item_groups(self.parent_item_group), "title": self.name, "products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list')) }) @@ -136,7 +136,8 @@ def get_group_item_count(item_group): def get_parent_item_groups(item_group_name): item_group = frappe.get_doc("Item Group", item_group_name) - return frappe.db.sql("""select name, route from `tabItem Group` + return [{"name": frappe._("Home"), "route":"/"}]+\ + frappe.db.sql("""select name, route from `tabItem Group` where lft <= %s and rgt >= %s and show_in_website=1 order by lft asc""", (item_group.lft, item_group.rgt), as_dict=True) @@ -146,6 +147,6 @@ def invalidate_cache_for(doc, item_group=None): item_group = doc.name for d in get_parent_item_groups(item_group): - d = frappe.get_doc("Item Group", d.name) - if d.route: - clear_cache(d.route) + item_group_name = frappe.db.get_value("Item Group", d.get('name')) + if item_group_name: + clear_cache(frappe.db.get_value('Item Group', item_group_name, 'route')) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 93281945e78..2c6aab52c1f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -146,10 +146,6 @@ class Item(WebsiteGenerator): return cstr(frappe.db.get_value('Item Group', self.item_group, 'route')) + '/' + self.scrub(self.item_name + '-' + random_string(5)) - def get_parents(self, context): - item_group, route = frappe.db.get_value('Item Group', self.item_group, ['name', 'route']) - context.parents = [{'name': route, 'label': item_group}] - def validate_website_image(self): """Validate if the website image is a public file""" auto_set_website_image = False @@ -242,15 +238,12 @@ class Item(WebsiteGenerator): context.show_search=True context.search_link = '/product_search' - context.parent_groups = get_parent_item_groups(self.item_group) + \ - [{"name": self.name}] + context.parents = get_parent_item_groups(self.item_group) self.set_variant_context(context) self.set_attribute_context(context) self.set_disabled_attributes(context) - self.get_parents(context) - return context def set_variant_context(self, context): diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 5f1e9440bd2..aca77a5c4cd 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -1597,7 +1597,7 @@ "collapsible": 0, "columns": 0, "default": ":Company", - "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", + "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "fieldname": "cost_center", "fieldtype": "Link", "hidden": 0, @@ -2044,7 +2044,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-06-16 17:23:01.404744", + "modified": "2017-07-10 06:31:31.457497", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 8420c9b4da1..fe158c9bbbb 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -85,6 +85,10 @@ class SerialNo(StockController): self.supplier, self.supplier_name = \ frappe.db.get_value("Purchase Receipt", purchase_sle.voucher_no, ["supplier", "supplier_name"]) + + # If sales return entry + if self.purchase_document_type == 'Delivery Note': + self.sales_invoice = None else: for fieldname in ("purchase_document_type", "purchase_document_no", "purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"): diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index ec14aabc4b0..6afb54ee2ec 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -960,7 +960,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:cint(frappe.sys_defaults.auto_accounting_for_stock)", + "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "fieldname": "expense_account", "fieldtype": "Link", "hidden": 0, @@ -1020,7 +1020,7 @@ "collapsible": 0, "columns": 0, "default": ":Company", - "depends_on": "eval:cint(frappe.sys_defaults.auto_accounting_for_stock)", + "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "fieldname": "cost_center", "fieldtype": "Link", "hidden": 0, @@ -1266,7 +1266,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-06-16 17:32:56.989049", + "modified": "2017-07-10 06:29:22.805345", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/tests/ui/test_fixtures.js b/erpnext/tests/ui/test_fixtures.js index e4ee664d0c4..2ba5db56069 100644 --- a/erpnext/tests/ui/test_fixtures.js +++ b/erpnext/tests/ui/test_fixtures.js @@ -1,48 +1,216 @@ $.extend(frappe.test_data, { - 'Customer': { - 'Test Customer 1': [ - {customer_name: 'Test Customer 1'} + "Customer": { + "Test Customer 1": [ + {customer_name: "Test Customer 1"} ], - 'Test Customer 2': [ - {customer_name: 'Test Customer 2'} + "Test Customer 2": [ + {customer_name: "Test Customer 2"} ], - 'Test Customer 3': [ - {customer_name: 'Test Customer 3'} + "Test Customer 3": [ + {customer_name: "Test Customer 3"} ], }, - 'Item': { - 'Test Product 1': [ - {item_code: 'Test Product 1'}, - {item_group: 'Products'}, + "Item": { + "Test Product 1": [ + {item_code: "Test Product 1"}, + {item_group: "Products"}, {is_stock_item: 1}, {standard_rate: 100}, {opening_stock: 100}, ], - 'Test Product 2': [ - {item_code: 'Test Product 2'}, - {item_group: 'Products'}, + "Test Product 2": [ + {item_code: "Test Product 2"}, + {item_group: "Products"}, {is_stock_item: 1}, {standard_rate: 150}, {opening_stock: 200}, ], - 'Test Product 3': [ - {item_code: 'Test Product 3'}, - {item_group: 'Products'}, + "Test Product 3": [ + {item_code: "Test Product 3"}, + {item_group: "Products"}, {is_stock_item: 1}, {standard_rate: 250}, {opening_stock: 100}, ], - 'Test Service 1': [ - {item_code: 'Test Service 1'}, - {item_group: 'Services'}, + "Test Service 1": [ + {item_code: "Test Service 1"}, + {item_group: "Services"}, {is_stock_item: 0}, {standard_rate: 200} ], - 'Test Service 2': [ - {item_code: 'Test Service 2'}, - {item_group: 'Services'}, + "Test Service 2": [ + {item_code: "Test Service 2"}, + {item_group: "Services"}, {is_stock_item: 0}, {standard_rate: 300} ] + }, + "Lead": { + "LEAD-00001": [ + {lead_name: "Test Lead 1"} + ], + "LEAD-00002": [ + {lead_name: "Test Lead 2"} + ], + "LEAD-00003": [ + {lead_name: "Test Lead 3"} + ] + }, + "Address": { + "Test1-Billing": [ + {address_title:"Test1"}, + {address_type: "Billing"}, + {address_line1: "Billing Street 1"}, + {city: "Billing City 1"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 1"} + ] + ]} + ], + "Test1-Shipping": [ + {address_title:"Test1"}, + {address_type: "Shipping"}, + {address_line1: "Shipping Street 1"}, + {city: "Shipping City 1"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 1"} + ] + ]} + ], + "Test1-Warehouse": [ + {address_title:"Test1"}, + {address_type: "Warehouse"}, + {address_line1: "Warehouse Street 1"}, + {city: "Warehouse City 1"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 1"} + ] + ]} + ], + "Test2-Billing": [ + {address_title:"Test2"}, + {address_type: "Billing"}, + {address_line1: "Billing Street 2"}, + {city: "Billing City 2"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 2"} + ] + ]} + ], + "Test2-Shipping": [ + {address_title:"Test2"}, + {address_type: "Shipping"}, + {address_line1: "Shipping Street 2"}, + {city: "Shipping City 2"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 2"} + ] + ]} + ], + "Test2-Warehouse": [ + {address_title:"Test2"}, + {address_type: "Warehouse"}, + {address_line1: "Warehouse Street 2"}, + {city: "Warehouse City 2"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 2"} + ] + ]} + ] + }, + "Contact": { + "Contact 1-Test Customer 1": [ + {first_name: "Contact 1"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 1"} + ] + ]} + ], + "Contact 2-Test Customer 1": [ + {first_name: "Contact 2"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 1"} + ] + ]} + ], + "Contact 1-Test Customer 2": [ + {first_name: "Contact 1"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 2"} + ] + ]} + ], + "Contact 2-Test Customer 2": [ + {first_name: "Contact 2"}, + {links: [ + [ + {link_doctype: "Customer"}, + {link_name: "Test Customer 2"} + ] + ]} + ], + }, + "Price List": { + "Test-Buying-USD": [ + {price_list_name: "Test-Buying-USD"}, + {currency: "USD"}, + {buying: "1"} + ], + "Test-Buying-EUR": [ + {price_list_name: "Test-Buying-EUR"}, + {currency: "EUR"}, + {buying: "1"} + ], + "Test-Selling-USD": [ + {price_list_name: "Test-Selling-USD"}, + {currency: "USD"}, + {selling: "1"} + ], + "Test-Selling-EUR": [ + {price_list_name: "Test-Selling-EUR"}, + {currency: "EUR"}, + {selling: "1"} + ], + }, + "Terms and Conditions": { + "Test Term 1": [ + {title: "Test Term 1"} + ], + "Test Term 2": [ + {title: "Test Term 2"} + ] + }, + "Sales Taxes and Charges Template": { + "TEST In State GST": [ + {title: "TEST In State GST"}, + {taxes:[ + [ + {charge_type:"On Net Total"}, + {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } + ], + [ + {charge_type:"On Net Total"}, + {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } + ] + ]} + ] } -}); \ No newline at end of file +}); diff --git a/erpnext/tests/ui/test_sellling.js b/erpnext/tests/ui/test_sellling.js index 5543a67e199..f6761525553 100644 --- a/erpnext/tests/ui/test_sellling.js +++ b/erpnext/tests/ui/test_sellling.js @@ -1,29 +1,120 @@ -QUnit.module('sales'); +QUnit.module("sales"); -QUnit.test("test quotation", function(assert) { - assert.expect(2); +QUnit.test("test: lead", function (assert) { + assert.expect(1); + let done = assert.async(); + let random = frappe.utils.get_random(10); + frappe.run_serially([ + () => frappe.tests.setup_doctype("Lead"), + () => frappe.set_route("List", "Lead"), + () => frappe.new_doc("Lead"), + () => cur_frm.set_value("lead_name", random), + () => cur_frm.save(), + () => { + assert.ok(cur_frm.doc.lead_name.includes(random)); + return done(); + } + ]); +}); + +QUnit.test("test: opportunity", function (assert) { + assert.expect(1); let done = assert.async(); frappe.run_serially([ - () => frappe.tests.setup_doctype('Customer'), - () => frappe.tests.setup_doctype('Item'), () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 5} + return frappe.tests.make("Opportunity", [{ + enquiry_from: "Lead" + }, + { + lead: "LEAD-00002" + } + ]); + }, + () => { + assert.ok(cur_frm.doc.lead === "LEAD-00002"); + return done(); + } + ]); +}); + +QUnit.test("test: quotation", function (assert) { + assert.expect(18); + let done = assert.async(); + frappe.run_serially([ + () => frappe.tests.setup_doctype("Customer"), + () => frappe.tests.setup_doctype("Item"), + () => frappe.tests.setup_doctype("Address"), + () => frappe.tests.setup_doctype("Contact"), + () => frappe.tests.setup_doctype("Price List"), + () => frappe.tests.setup_doctype("Terms and Conditions"), + () => frappe.tests.setup_doctype("Sales Taxes and Charges Template"), + () => { + return frappe.tests.make("Quotation", [{ + customer: "Test Customer 1" + }, + { + items: [ + [{ + "item_code": "Test Product 1" + }, + { + "qty": 5 + } ] - ]} + ] + } ]); }, () => { // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1'); + assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1"); // calculate_taxes_and_totals - assert.ok(cur_frm.doc.grand_total==500); + assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct"); }, + () => cur_frm.set_value("customer_address", "Test1-Billing"), + () => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"), + () => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"), + () => cur_frm.set_value("currency", "USD"), + () => frappe.timeout(0.3), + () => cur_frm.set_value("selling_price_list", "Test-Selling-USD"), + () => frappe.timeout(0.5), + () => cur_frm.doc.items[0].rate = 200, + () => frappe.timeout(0.3), + () => cur_frm.set_value("tc_name", "Test Term 1"), + () => cur_frm.set_value("taxes_and_charges", "TEST In State GST"), + () => frappe.timeout(0.3), + () => cur_frm.save(), + () => { + // Check Address and Contact Info + assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed"); + assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed"); + assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed"); + + // Check Currency + assert.ok(cur_frm.doc_currency == "USD", "Currency Changed"); + assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed"); + assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually"); + assert.ok(cur_frm.doc.total == 1000, "New Total Calculated"); + + // Check Terms and Condtions + assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked"); + + // Check Taxes + assert.ok(cur_frm.doc.taxes[0].account_head.includes("CGST")); + assert.ok(cur_frm.doc.taxes[1].account_head.includes("SGST")); + assert.ok(cur_frm.doc.grand_total == 1180, "Tax Amount Added to Total"); + assert.ok(cur_frm.doc.taxes_and_charges == "TEST In State GST", "Tax Template Selected"); + }, + () => frappe.timeout(0.3), + () => cur_frm.print_doc(), + () => frappe.timeout(1), + () => assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"), + () => assert.ok(RegExp(/QTN-\d\d\d\d\d/g).test($("#header-html small").text())), + () => assert.ok($(".important .text-right.value").text().includes("$ 1,180.00")), + () => assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected"), + () => frappe.timeout(0.3), + () => cur_frm.print_doc(), () => done() ]); -}); +}); \ No newline at end of file