diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml new file mode 100644 index 00000000000..7c9e0272c95 --- /dev/null +++ b/.github/workflows/patch.yml @@ -0,0 +1,69 @@ +name: Patch + +on: [pull_request, workflow_dispatch] + +jobs: + test: + runs-on: ubuntu-18.04 + + name: Patch Test + + services: + mysql: + image: mariadb:10.3 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: YES + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.6 + + - name: Add to Hosts + run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + + - name: Run Patch Tests + run: cd ~/frappe-bench/ && wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz && bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz && bench --site test_site migrate diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/server-tests.yml similarity index 59% rename from .github/workflows/ci-tests.yml rename to .github/workflows/server-tests.yml index 84ecfb14571..92685e2177d 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/server-tests.yml @@ -1,6 +1,6 @@ -name: CI +name: Server -on: [pull_request, workflow_dispatch, push] +on: [pull_request, workflow_dispatch] jobs: test: @@ -10,15 +10,9 @@ jobs: fail-fast: false matrix: - include: - - TYPE: "server" - JOB_NAME: "Server" - RUN_COMMAND: cd ~/frappe-bench/ && bench --site test_site run-tests --app erpnext --coverage - - TYPE: "patch" - JOB_NAME: "Patch" - RUN_COMMAND: cd ~/frappe-bench/ && wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz && bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz && bench --site test_site migrate + container: [1, 2, 3] - name: ${{ matrix.JOB_NAME }} + name: Python Unit Tests services: mysql: @@ -36,7 +30,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Add to Hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts @@ -49,6 +43,7 @@ jobs: restore-keys: | ${{ runner.os }}-pip- ${{ runner.os }}- + - name: Cache node modules uses: actions/cache@v2 env: @@ -60,6 +55,7 @@ jobs: ${{ runner.os }}-build-${{ env.cache-name }}- ${{ runner.os }}-build- ${{ runner.os }}- + - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" @@ -76,33 +72,39 @@ jobs: run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh - name: Run Tests - run: ${{ matrix.RUN_COMMAND }} + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage env: - TYPE: ${{ matrix.TYPE }} + TYPE: server + CI_BUILD_ID: ${{ github.run_id }} + ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - name: Coverage - Pull Request - if: matrix.TYPE == 'server' && github.event_name == 'pull_request' + - name: Upload Coverage Data run: | cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} cd ${GITHUB_WORKSPACE} - pip install coveralls==2.2.0 - pip install coverage==4.5.4 - coveralls --service=github + pip3 install coverage==5.5 + pip3 install coveralls==3.0.1 + coveralls env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - COVERALLS_SERVICE_NAME: github - - - name: Coverage - Push - if: matrix.TYPE == 'server' && github.event_name == 'push' - run: | - cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} - cd ${GITHUB_WORKSPACE} - pip install coveralls==2.2.0 - pip install coverage==4.5.4 - coveralls --service=github-actions - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - COVERALLS_SERVICE_NAME: github-actions + COVERALLS_FLAG_NAME: run-${{ matrix.container }} + COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }} + COVERALLS_PARALLEL: true + coveralls: + name: Coverage Wrap Up + needs: test + container: python:3-slim + runs-on: ubuntu-18.04 + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Coveralls Finished + run: | + cd ${GITHUB_WORKSPACE} + pip3 install coverage==5.5 + pip3 install coveralls==3.0.1 + coveralls --finish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6775398532b..ad971e2976c 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__ = '13.2.1' +__version__ = '13.3.0' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index fc1d7e344af..e657a9ae34b 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -7,7 +7,8 @@ import frappe import unittest from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import delete_accounting_dimension + +test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department'] class TestAccountingDimension(unittest.TestCase): def setUp(self): diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index 7877abd0263..7f6254f99f5 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -9,6 +9,8 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError +test_dependencies = ['Location', 'Cost Center', 'Department'] + class TestAccountingDimensionFilter(unittest.TestCase): def setUp(self): create_dimension() diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py index 10cd9398942..dc472c7695d 100644 --- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py @@ -10,6 +10,8 @@ from erpnext.accounts.general_ledger import ClosedAccountingPeriod from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +test_dependencies = ['Item'] + class TestAccountingPeriod(unittest.TestCase): def test_overlap(self): ap1 = create_accounting_period(start_date = "2018-04-01", @@ -38,7 +40,7 @@ def create_accounting_period(**args): accounting_period.start_date = args.start_date or nowdate() accounting_period.end_date = args.end_date or add_months(nowdate(), 1) accounting_period.company = args.company or "_Test Company" - accounting_period.period_name =args.period_name or "_Test_Period_Name_1" + accounting_period.period_name = args.period_name or "_Test_Period_Name_1" accounting_period.append("closed_documents", { "document_type": 'Sales Invoice', "closed": 1 }) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index e1276e7da3d..781f94e203a 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -7,26 +7,30 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "auto_accounting_for_stock", - "acc_frozen_upto", - "frozen_accounts_modifier", - "determine_address_tax_category_from", + "accounts_transactions_settings_section", "over_billing_allowance", "role_allowed_to_over_bill", - "column_break_4", - "credit_controller", - "check_supplier_invoice_uniqueness", "make_payment_via_journal_entry", + "column_break_11", + "check_supplier_invoice_uniqueness", "unlink_payment_on_cancellation_of_invoice", - "unlink_advance_payment_on_cancelation_of_order", - "book_asset_depreciation_entry_automatically", - "add_taxes_from_item_tax_template", "automatically_fetch_payment_terms", "delete_linked_ledger_entries", + "book_asset_depreciation_entry_automatically", + "unlink_advance_payment_on_cancelation_of_order", + "tax_settings_section", + "determine_address_tax_category_from", + "column_break_19", + "add_taxes_from_item_tax_template", + "period_closing_settings_section", + "acc_frozen_upto", + "frozen_accounts_modifier", + "column_break_4", + "credit_controller", "deferred_accounting_settings_section", - "automatically_process_deferred_accounting_entry", "book_deferred_entries_based_on", "column_break_18", + "automatically_process_deferred_accounting_entry", "book_deferred_entries_via_journal_entry", "submit_journal_entries", "print_settings", @@ -40,15 +44,6 @@ "use_custom_cash_flow" ], "fields": [ - { - "default": "1", - "description": "If enabled, the system will post accounting entries for inventory automatically", - "fieldname": "auto_accounting_for_stock", - "fieldtype": "Check", - "hidden": 1, - "in_list_view": 1, - "label": "Make Accounting Entry For Every Stock Movement" - }, { "description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below", "fieldname": "acc_frozen_upto", @@ -94,6 +89,7 @@ "default": "0", "fieldname": "make_payment_via_journal_entry", "fieldtype": "Check", + "hidden": 1, "label": "Make Payment via Journal Entry" }, { @@ -234,6 +230,29 @@ "fieldtype": "Link", "label": "Role Allowed to Over Bill ", "options": "Role" + }, + { + "fieldname": "period_closing_settings_section", + "fieldtype": "Section Break", + "label": "Period Closing Settings" + }, + { + "fieldname": "accounts_transactions_settings_section", + "fieldtype": "Section Break", + "label": "Transactions Settings" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "tax_settings_section", + "fieldtype": "Section Break", + "label": "Tax Settings" + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" } ], "icon": "icon-cog", @@ -241,7 +260,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-03-11 18:52:05.601996", + "modified": "2021-04-30 15:25:10.381008", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index 0d4e56ff14a..0a2e0bc9ba3 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -239,6 +239,7 @@ frappe.ui.form.on("Bank Statement Import", { "withdrawal", "description", "reference_number", + "bank_account" ], }, }); diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json index 5e913cc2aac..7ffff02850c 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json @@ -146,7 +146,7 @@ }, { "depends_on": "eval:!doc.__islocal && !doc.import_file\n", - "description": "Must be a publicly accessible Google Sheets URL", + "description": "Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets", "fieldname": "google_sheets_url", "fieldtype": "Data", "label": "Import from Google Sheets" @@ -202,7 +202,7 @@ ], "hide_toolbar": 1, "links": [], - "modified": "2021-02-10 19:29:59.027325", + "modified": "2021-05-12 14:17:37.777246", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Statement Import", @@ -224,4 +224,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py index 9f41b13f4b6..5f110e2727c 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -47,6 +47,13 @@ class BankStatementImport(DataImport): def start_import(self): + preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template( + self.import_file, self.google_sheets_url + ) + + if 'Bank Account' not in json.dumps(preview): + frappe.throw(_("Please add the Bank Account column")) + from frappe.core.page.background_jobs.background_jobs import get_info from frappe.utils.scheduler import is_scheduler_inactive @@ -67,6 +74,7 @@ class BankStatementImport(DataImport): data_import=self.name, bank_account=self.bank_account, import_file_path=self.import_file, + google_sheets_url=self.google_sheets_url, bank=self.bank, template_options=self.template_options, now=frappe.conf.developer_mode or frappe.flags.in_test, @@ -90,18 +98,20 @@ def download_errored_template(data_import_name): data_import = frappe.get_doc("Bank Statement Import", data_import_name) data_import.export_errored_rows() -def start_import(data_import, bank_account, import_file_path, bank, template_options): +def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options): """This method runs in background job""" update_mapping_db(bank, template_options) data_import = frappe.get_doc("Bank Statement Import", data_import) + file = import_file_path if import_file_path else google_sheets_url - import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records") + import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records") data = import_file.raw_data - add_bank_account(data, bank_account) - write_files(import_file, data) + if import_file_path: + add_bank_account(data, bank_account) + write_files(import_file, data) try: i = Importer(data_import.reference_doctype, data_import=data_import) diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index c5ec23c8295..603e21ea248 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -11,6 +11,8 @@ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_pur from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry +test_dependencies = ['Monthly Distribution'] + class TestBudget(unittest.TestCase): def test_monthly_budget_crossed_ignore(self): set_total_expense_zero(nowdate(), "cost_center") diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py index cb18309e3c9..e2d4d82e418 100644 --- a/erpnext/accounts/doctype/dunning/test_dunning.py +++ b/erpnext/accounts/doctype/dunning/test_dunning.py @@ -29,7 +29,7 @@ class TestDunning(unittest.TestCase): self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44) self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44) self.assertEqual(round(amounts.get('grand_total'), 2), 120.44) - + def test_gl_entries(self): dunning = create_dunning() dunning.submit() @@ -42,9 +42,9 @@ class TestDunning(unittest.TestCase): ['Sales - _TC', 0.0, 20.44] ]) for gle in gl_entries: - self.assertEquals(expected_values[gle.account][0], gle.account) - self.assertEquals(expected_values[gle.account][1], gle.debit) - self.assertEquals(expected_values[gle.account][2], gle.credit) + self.assertEqual(expected_values[gle.account][0], gle.account) + self.assertEqual(expected_values[gle.account][1], gle.debit) + self.assertEqual(expected_values[gle.account][2], gle.credit) def test_payment_entry(self): dunning = create_dunning() diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py index b4a547b21ba..4167ca70df2 100644 --- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py @@ -54,4 +54,4 @@ class TestGLEntry(unittest.TestCase): self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries))) new_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0] - self.assertEquals(old_naming_series_current_value + 2, new_naming_series_current_value) + self.assertEqual(old_naming_series_current_value + 2, new_naming_series_current_value) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index aa32d953738..c9f15a6a470 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -1,87 +1,39 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-08-29 16:02:39.740505", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2014-08-29 16:02:39.740505", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "company", + "account" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account" } - ], - "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": "2016-07-11 03:28:03.348246", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Party Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-07 18:13:08.833822", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Party Account", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py index 1c23e2a0ec2..5fdde07faa4 100644 --- a/erpnext/accounts/doctype/payment_order/test_payment_order.py +++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py @@ -31,10 +31,10 @@ class TestPaymentOrder(unittest.TestCase): doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry") reference_doc = doc.get("references")[0] - self.assertEquals(reference_doc.reference_name, payment_entry.name) - self.assertEquals(reference_doc.reference_doctype, "Payment Entry") - self.assertEquals(reference_doc.supplier, "_Test Supplier") - self.assertEquals(reference_doc.amount, 250) + self.assertEqual(reference_doc.reference_name, payment_entry.name) + self.assertEqual(reference_doc.reference_doctype, "Payment Entry") + self.assertEqual(reference_doc.supplier, "_Test Supplier") + self.assertEqual(reference_doc.amount, 250) def create_payment_order_against_payment_entry(ref_doc, order_type): payment_order = frappe.get_doc(dict( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index cf6ec18f3b8..6635128f9ef 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -114,7 +114,7 @@ class PaymentReconciliation(Document): 'party_type': self.party_type, 'voucher_type': voucher_type, 'account': self.receivable_payable_account - }, as_dict=1, debug=1) + }, as_dict=1) def add_payment_entries(self, entries): self.set('payments', []) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 1e6a3d1b3be..473db565fa5 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -461,7 +461,17 @@ def get_stock_availability(item_code, warehouse): order by posting_date desc, posting_time desc limit 1""", (item_code, warehouse), as_dict=1) - pos_sales_qty = frappe.db.sql("""select sum(p_item.qty) as qty + pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) + + sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0 + + if sle_qty and pos_sales_qty: + return sle_qty - pos_sales_qty + else: + return sle_qty + +def get_pos_reserved_qty(item_code, warehouse): + reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item where p.name = p_item.parent and p.consolidated_invoice is NULL @@ -470,14 +480,8 @@ def get_stock_availability(item_code, warehouse): and p_item.item_code = %s and p_item.warehouse = %s """, (item_code, warehouse), as_dict=1) - - sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0 - pos_sales_qty = pos_sales_qty[0].qty or 0 if pos_sales_qty else 0 - - if sle_qty and pos_sales_qty: - return sle_qty - pos_sales_qty - else: - return sle_qty + + return reserved_qty[0].qty or 0 if reserved_qty else 0 @frappe.whitelist() def make_sales_return(source_name, target_doc=None): diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index aedf1c6f1a6..556f49d34c0 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -152,7 +152,7 @@ class PricingRule(Document): frappe.throw(_("Valid from date must be less than valid upto date")) def validate_condition(self): - if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition): + if self.condition and ("=" in self.condition) and re.match(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+', self.condition): frappe.throw(_("Invalid condition expression")) #-------------------------------------------------------------------------------- diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index ef9aad562df..ffe8be1162f 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -99,7 +99,7 @@ class TestPricingRule(unittest.TestCase): args.item_code = "_Test Item 2" details = get_item_details(args) - self.assertEquals(details.get("discount_percentage"), 15) + self.assertEqual(details.get("discount_percentage"), 15) def test_pricing_rule_for_margin(self): from erpnext.stock.get_item_details import get_item_details @@ -145,8 +145,8 @@ class TestPricingRule(unittest.TestCase): "name": None }) details = get_item_details(args) - self.assertEquals(details.get("margin_type"), "Percentage") - self.assertEquals(details.get("margin_rate_or_amount"), 10) + self.assertEqual(details.get("margin_type"), "Percentage") + self.assertEqual(details.get("margin_rate_or_amount"), 10) def test_mixed_conditions_for_item_group(self): for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]: @@ -192,7 +192,7 @@ class TestPricingRule(unittest.TestCase): "name": None }) details = get_item_details(args) - self.assertEquals(details.get("discount_percentage"), 10) + self.assertEqual(details.get("discount_percentage"), 10) def test_pricing_rule_for_variants(self): from erpnext.stock.get_item_details import get_item_details @@ -322,11 +322,11 @@ class TestPricingRule(unittest.TestCase): si.insert(ignore_permissions=True) item = si.items[0] - self.assertEquals(item.margin_rate_or_amount, 10) - self.assertEquals(item.rate_with_margin, 1100) + self.assertEqual(item.margin_rate_or_amount, 10) + self.assertEqual(item.rate_with_margin, 1100) self.assertEqual(item.discount_percentage, 10) - self.assertEquals(item.discount_amount, 110) - self.assertEquals(item.rate, 990) + self.assertEqual(item.discount_amount, 110) + self.assertEqual(item.rate, 990) def test_pricing_rule_with_margin_and_discount_amount(self): frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') @@ -338,10 +338,10 @@ class TestPricingRule(unittest.TestCase): si.insert(ignore_permissions=True) item = si.items[0] - self.assertEquals(item.margin_rate_or_amount, 10) - self.assertEquals(item.rate_with_margin, 1100) - self.assertEquals(item.discount_amount, 110) - self.assertEquals(item.rate, 990) + self.assertEqual(item.margin_rate_or_amount, 10) + self.assertEqual(item.rate_with_margin, 1100) + self.assertEqual(item.discount_amount, 110) + self.assertEqual(item.rate, 990) def test_pricing_rule_for_product_discount_on_same_item(self): frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') @@ -458,21 +458,21 @@ class TestPricingRule(unittest.TestCase): si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] - self.assertEquals(item.rate, 100) + self.assertEqual(item.rate, 100) # Correct Customer and Incorrect is_return value si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1) si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] - self.assertEquals(item.rate, 100) + self.assertEqual(item.rate, 100) # Correct Customer and correct is_return value si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0) si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] - self.assertEquals(item.rate, 900) + self.assertEqual(item.rate, 900) def test_multiple_pricing_rules(self): make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, @@ -545,11 +545,11 @@ class TestPricingRule(unittest.TestCase): apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10) si = create_sales_invoice(qty=5, do_not_submit=True) - self.assertEquals(len(si.items), 2) - self.assertEquals(si.items[1].rate, 10) + self.assertEqual(len(si.items), 2) + self.assertEqual(si.items[1].rate, 10) si1 = create_sales_invoice(qty=2, do_not_submit=True) - self.assertEquals(len(si1.items), 1) + self.assertEqual(len(si1.items), 1) for doc in [si, si1]: doc.delete() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 24e67febca5..d3d3ffa17fa 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1380,7 +1380,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-03-30 22:45:58.334107", + "modified": "2021-04-30 22:45:58.334107", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 66be11ff231..53db689c84a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -636,8 +636,8 @@ class TestPurchaseInvoice(unittest.TestCase): def test_rejected_serial_no(self): pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1, - rejected_qty=1, rate=500, update_stock=1, - rejected_warehouse = "_Test Rejected Warehouse - _TC") + rejected_qty=1, rate=500, update_stock=1, rejected_warehouse = "_Test Rejected Warehouse - _TC", + allow_zero_valuation_rate=1) self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"), pi.get("items")[0].warehouse) @@ -994,7 +994,8 @@ def make_purchase_invoice(**args): "project": args.project, "rejected_warehouse": args.rejected_warehouse or "", "rejected_serial_no": args.rejected_serial_no or "", - "asset_location": args.location or "" + "asset_location": args.location or "", + "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0 }) if args.get_taxes_and_charges: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 95e570b4d9d..b48db246ac6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -356,11 +356,11 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e } items_on_form_rendered() { - erpnext.setup_serial_no(); + erpnext.setup_serial_or_batch_no(); } packed_items_on_form_rendered(doc, grid_row) { - erpnext.setup_serial_no(); + erpnext.setup_serial_or_batch_no(); } make_sales_return() { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4461f29fe37..bb74a02606f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1111,7 +1111,7 @@ class SalesInvoice(SellingController): if not item.serial_no: continue - for serial_no in item.serial_no.split("\n"): + for serial_no in get_serial_nos(item.serial_no): if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code: frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice) @@ -1755,15 +1755,10 @@ def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, wa item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item)) def get_delivery_note_details(internal_reference): - so_item_map = {} - si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'], filters={'parent': internal_reference}) - for d in si_item_details: - so_item_map.setdefault(d.name, d.so_detail) - - return so_item_map + return {d.name: d.so_detail for d in si_item_details if d.so_detail} def get_sales_invoice_details(internal_reference): dn_item_map = {} diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 09db7fee2b1..5c1cbaa4aaa 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -21,7 +21,10 @@ def get_party_details(inv): else: party_type = 'Supplier' party = inv.supplier - + + if not party: + frappe.throw(_("Please select {0} first").format(party_type)) + return party_type, party def get_party_tax_withholding_details(inv, tax_withholding_category=None): @@ -324,7 +327,7 @@ def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, post net_total, ldc.certificate_limit ): tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details) - + return tds_amount def get_debit_note_amount(suppliers, fiscal_year_details, company=None): diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json index bd7a1265170..4c7faf4f65b 100644 --- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json +++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json @@ -1,5 +1,6 @@ { "attach_print": 0, + "channel": "Email", "condition": "doc.auto_created", "creation": "2018-04-25 14:19:05.440361", "days_in_advance": 0, diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 444b40ed798..db605f7285a 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -364,7 +364,7 @@ class ReceivablePayableReport(object): payment_terms_details = frappe.db.sql(""" select si.name, si.party_account_currency, si.currency, si.conversion_rate, - ps.due_date, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount + ps.due_date, ps.payment_term, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount from `tab{0}` si, `tabPayment Schedule` ps where si.name = ps.parent and @@ -394,7 +394,7 @@ class ReceivablePayableReport(object): "due_date": d.due_date, "invoiced": invoiced, "invoice_grand_total": row.invoiced, - "payment_term": d.description, + "payment_term": d.description or d.payment_term, "paid": d.paid_amount + d.discounted_amount, "credit_note": 0.0, "outstanding": invoiced - d.paid_amount - d.discounted_amount diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 1729abce9ef..26bb44f4f7b 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt, cint -from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, + get_filtered_list_for_consolidated_report) def execute(filters=None): period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, @@ -132,6 +133,10 @@ def get_report_summary(period_list, asset, liability, equity, provisional_profit if filters.get('accumulated_values'): period_list = [period_list[-1]] + # from consolidated financial statement + if filters.get('accumulated_in_group_company'): + period_list = get_filtered_list_for_consolidated_report(filters, period_list) + for period in period_list: key = period if consolidated else period.key if asset: diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index cf0946beaba..3577457c980 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint, cstr -from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, get_filtered_list_for_consolidated_report) from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss from erpnext.accounts.utils import get_fiscal_year from six import iteritems @@ -67,9 +67,9 @@ def execute(filters=None): section_data.append(account_data) add_total_row_account(data, section_data, cash_flow_account['section_footer'], - period_list, company_currency, summary_data) + period_list, company_currency, summary_data, filters) - add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data) + add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) chart = get_chart_data(columns, data) @@ -162,18 +162,26 @@ def get_start_date(period, accumulated_values, company): return start_date -def add_total_row_account(out, data, label, period_list, currency, summary_data, consolidated = False): +def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False): total_row = { "account_name": "'" + _("{0}").format(label) + "'", "account": "'" + _("{0}").format(label) + "'", "currency": currency } + + summary_data[label] = 0 + + # from consolidated financial statement + if filters.get('accumulated_in_group_company'): + period_list = get_filtered_list_for_consolidated_report(filters, period_list) + for row in data: if row.get("parent_account"): for period in period_list: key = period if consolidated else period['key'] total_row.setdefault(key, 0.0) total_row[key] += row.get(key, 0.0) + summary_data[label] += row.get(key) total_row.setdefault("total", 0.0) total_row["total"] += row["total"] @@ -181,7 +189,6 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data, out.append(total_row) out.append({}) - summary_data[label] = total_row["total"] def get_report_summary(summary_data, currency): report_summary = [] 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 09479221fbb..1363b53746a 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -2,118 +2,128 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports["Consolidated Financial Statement"] = { - "filters": [ - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - }, - { - "fieldname":"filter_based_on", - "label": __("Filter Based On"), - "fieldtype": "Select", - "options": ["Fiscal Year", "Date Range"], - "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); +frappe.require("assets/erpnext/js/financial_statements.js", function() { + frappe.query_reports["Consolidated Financial Statement"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname":"filter_based_on", + "label": __("Filter Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.refresh(); + frappe.query_report.refresh(); + } + }, + { + "fieldname":"period_start_date", + "label": __("Start Date"), + "fieldtype": "Date", + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"period_end_date", + "label": __("End Date"), + "fieldtype": "Date", + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"from_fiscal_year", + "label": __("Start Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"to_fiscal_year", + "label": __("End Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"finance_book", + "label": __("Finance Book"), + "fieldtype": "Link", + "options": "Finance Book" + }, + { + "fieldname":"report", + "label": __("Report"), + "fieldtype": "Select", + "options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"], + "default": "Balance Sheet", + "reqd": 1 + }, + { + "fieldname": "presentation_currency", + "label": __("Currency"), + "fieldtype": "Select", + "options": erpnext.get_presentation_currency_list(), + "default": frappe.defaults.get_user_default("Currency") + }, + { + "fieldname":"accumulated_in_group_company", + "label": __("Accumulated Values in Group Company"), + "fieldtype": "Check", + "default": 0 + }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check", + "default": 1 + } + ], + "formatter": function(value, row, column, data, default_formatter) { + if (data && column.fieldname=="account") { + value = data.account_name || value; + + column.link_onclick = + "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; + column.is_tree = true; } - }, - { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 - }, - { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 - }, - { - "fieldname":"from_fiscal_year", - "label": __("Start Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), - "reqd": 1 - }, - { - "fieldname":"to_fiscal_year", - "label": __("End Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), - "reqd": 1 - }, - { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" - }, - { - "fieldname":"report", - "label": __("Report"), - "fieldtype": "Select", - "options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"], - "default": "Balance Sheet", - "reqd": 1 - }, - { - "fieldname": "presentation_currency", - "label": __("Currency"), - "fieldtype": "Select", - "options": erpnext.get_presentation_currency_list(), - "default": frappe.defaults.get_user_default("Currency") - }, - { - "fieldname":"accumulated_in_group_company", - "label": __("Accumulated Values in Group Company"), - "fieldtype": "Check", - "default": 0 - }, - { - "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), - "fieldtype": "Check", - "default": 1 - } - ], - "formatter": function(value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - if (!data.parent_account) { - value = $(`${value}`); + value = default_formatter(value, row, column, data); - var $value = $(value).css("font-weight", "bold"); + if (!data.parent_account) { + value = $(`${value}`); - value = $value.wrap("
").parent().html(); - } - return value; - }, - onload: function() { - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + var $value = $(value).css("font-weight", "bold"); - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { - var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); - frappe.query_report.set_filter_value({ - period_start_date: fy.year_start_date, - period_end_date: fy.year_end_date + value = $value.wrap("").parent().html(); + } + return value; + }, + onload: function() { + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); }); - }); + } } -} +}); \ No newline at end of file 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 0c4a4224407..7793af737f9 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -94,7 +94,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters): chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss) - report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True) + report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, filters, True) return data, None, chart, report_summary @@ -149,9 +149,9 @@ def get_cash_flow_data(fiscal_year, companies, filters): section_data.append(account_data) add_total_row_account(data, section_data, cash_flow_account['section_footer'], - companies, company_currency, summary_data, True) + companies, company_currency, summary_data, filters, True) - add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, True) + add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True) report_summary = get_cash_flow_summary(summary_data, company_currency) @@ -329,8 +329,9 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com has_value = False total = 0 row = frappe._dict({ - "account_name": _(d.account_name), - "account": _(d.account_name), + "account_name": ('%s - %s' %(_(d.account_number), _(d.account_name)) + if d.account_number else _(d.account_name)), + "account": _(d.name), "parent_account": _(d.parent_account), "indent": flt(d.indent), "year_start_date": start_date, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 14efa1f8fc7..d20ddbde5c6 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -119,10 +119,10 @@ def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year): def validate_dates(from_date, to_date): if not from_date or not to_date: - frappe.throw("From Date and To Date are mandatory") + frappe.throw(_("From Date and To Date are mandatory")) if to_date < from_date: - frappe.throw("To Date cannot be less than From Date") + frappe.throw(_("To Date cannot be less than From Date")) def get_months(start_date, end_date): diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month) @@ -522,4 +522,12 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None): "width": 150 }) - return columns \ No newline at end of file + return columns + +def get_filtered_list_for_consolidated_report(filters, period_list): + filtered_summary_list = [] + for period in period_list: + if period == filters.get('company'): + filtered_summary_list.append(period) + + return filtered_summary_list diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py index 52f7fe238e8..cfbd7fd0c8b 100644 --- a/erpnext/accounts/report/pos_register/pos_register.py +++ b/erpnext/accounts/report/pos_register/pos_register.py @@ -116,22 +116,19 @@ def validate_filters(filters): frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method")) def get_conditions(filters): - conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format( - company=filters.get("company"), - from_date=filters.get("from_date"), - to_date=filters.get("to_date")) + conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s" if filters.get("pos_profile"): - conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile")) + conditions += " AND pos_profile = %(pos_profile)s" if filters.get("owner"): - conditions += " AND owner = %(owner)s".format(owner=filters.get("owner")) + conditions += " AND owner = %(owner)s" if filters.get("customer"): - conditions += " AND customer = %(customer)s".format(customer=filters.get("customer")) + conditions += " AND customer = %(customer)s" if filters.get("is_return"): - conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return")) + conditions += " AND is_return = %(is_return)s" if filters.get("mode_of_payment"): conditions += """ 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 fe261b30b45..5d04824b571 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 @@ -5,7 +5,8 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) +from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, + get_filtered_list_for_consolidated_report) def execute(filters=None): period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, @@ -33,13 +34,17 @@ def execute(filters=None): chart = get_chart_data(filters, columns, income, expense, net_profit_loss) currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency") - report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency) + report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters) return columns, data, None, chart, report_summary -def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, consolidated=False): +def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False): net_income, net_expense, net_profit = 0.0, 0.0, 0.0 + # from consolidated financial statement + if filters.get('accumulated_in_group_company'): + period_list = get_filtered_list_for_consolidated_report(filters, period_list) + for period in period_list: key = period if consolidated else period.key if income: diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index 9ffa481c1cb..df68318052f 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -15,6 +15,7 @@ "hide_custom": 0, "icon": "accounting", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Accounting", "links": [ @@ -625,9 +626,9 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Bank Reconciliation", - "link_to": "bank-reconciliation", - "link_type": "Page", + "label": "Bank Reconciliation Tool", + "link_to": "Bank Reconciliation Tool", + "link_type": "DocType", "onboard": 0, "type": "Link" }, @@ -641,26 +642,6 @@ "onboard": 0, "type": "Link" }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Bank Statement Transaction Entry", - "link_to": "Bank Statement Transaction Entry", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Bank Statement Settings", - "link_to": "Bank Statement Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -1071,7 +1052,7 @@ "type": "Link" } ], - "modified": "2021-03-04 00:38:35.349024", + "modified": "2021-05-12 11:48:01.905144", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9aff1440d6a..8799275fc4e 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -195,8 +195,7 @@ class Asset(AccountsController): # If depreciation is already completed (for double declining balance) if skip_row: continue - depreciation_amount = self.get_depreciation_amount(value_after_depreciation, - d.total_number_of_depreciations, d) + depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d) if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: schedule_date = add_months(d.depreciation_start_date, @@ -208,7 +207,7 @@ class Asset(AccountsController): # For first row if has_pro_rata and n==0: - depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount, + depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, self.available_for_use_date, d.depreciation_start_date) # For first depr schedule date will be the start date @@ -220,7 +219,7 @@ class Asset(AccountsController): to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) - depreciation_amount, days, months = get_pro_rata_amt(d, + depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, schedule_date, to_date) monthly_schedule_date = add_months(schedule_date, 1) @@ -365,24 +364,6 @@ class Asset(AccountsController): def get_value_after_depreciation(self, idx): return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation) - def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row): - precision = self.precision("gross_purchase_amount") - - if row.depreciation_method in ("Straight Line", "Manual"): - depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) - - if not depreciation_left: - frappe.msgprint(_("All the depreciations has been booked")) - depreciation_amount = flt(row.expected_value_after_useful_life) - return depreciation_amount - - depreciation_amount = (flt(row.value_after_depreciation) - - flt(row.expected_value_after_useful_life)) / depreciation_left - else: - depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision) - - return depreciation_amount - def validate_expected_value_after_useful_life(self): for row in self.get('finance_books'): accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount @@ -575,6 +556,13 @@ class Asset(AccountsController): return 100 * (1 - flt(depreciation_rate, float_precision)) + def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date): + days = date_diff(to_date, from_date) + months = month_diff(to_date, from_date) + total_days = get_total_days(to_date, row.frequency_of_depreciation) + + return (depreciation_amount * flt(days)) / flt(total_days), days, months + def update_maintenance_status(): assets = frappe.get_all( "Asset", filters={"docstatus": 1, "maintenance_required": 1} @@ -758,15 +746,20 @@ def make_asset_movement(assets, purpose=None): def is_cwip_accounting_enabled(asset_category): return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting")) -def get_pro_rata_amt(row, depreciation_amount, from_date, to_date): - days = date_diff(to_date, from_date) - months = month_diff(to_date, from_date) - total_days = get_total_days(to_date, row.frequency_of_depreciation) - - return (depreciation_amount * flt(days)) / flt(total_days), days, months - def get_total_days(date, frequency): period_start_date = add_months(date, cint(frequency) * -1) return date_diff(date, period_start_date) + +@erpnext.allow_regional +def get_depreciation_amount(asset, depreciable_value, row): + depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) + + if row.depreciation_method in ("Straight Line", "Manual"): + depreciation_amount = (flt(row.value_after_depreciation) - + flt(row.expected_value_after_useful_life)) / depreciation_left + else: + depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) + + return depreciation_amount \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a0d76031fc4..3cd4b802c11 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -78,7 +78,7 @@ class TestAsset(unittest.TestCase): }) doc.set_missing_values() - self.assertEquals(doc.items[0].is_fixed_asset, 1) + self.assertEqual(doc.items[0].is_fixed_asset, 1) def test_schedule_for_straight_line_method(self): pr = make_purchase_receipt(item_code="Macbook Pro", @@ -565,8 +565,8 @@ class TestAsset(unittest.TestCase): doc = make_invoice(pr.name) - self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account) - + self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account) + def test_asset_cwip_toggling_cases(self): cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting") name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"]) @@ -635,6 +635,45 @@ class TestAsset(unittest.TestCase): frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc) frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc) + def test_discounted_wdv_depreciation_rate_for_indian_region(self): + # set indian company + company_flag = frappe.flags.company + frappe.flags.company = "_Test Company" + + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=8000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2030-06-12' + asset.purchase_date = '2030-01-01' + asset.append("finance_books", { + "expected_value_after_useful_life": 1000, + "depreciation_method": "Written Down Value", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + asset.save(ignore_permissions=True) + + self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) + + expected_schedules = [ + ["2030-12-31", 1106.85, 1106.85], + ["2031-12-31", 3446.58, 4553.43], + ["2032-12-31", 1723.29, 6276.72], + ["2033-06-12", 723.28, 7000.00] + ] + + schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + # reset indian company + frappe.flags.company = company_flag + def create_asset_data(): if not frappe.db.exists("Asset Category", "Computers"): create_asset_category() diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 42f4472f29e..aaa98f2f1f4 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -187,7 +187,7 @@ class TestPurchaseOrder(unittest.TestCase): update_child_qty_rate('Purchase Order', trans_item, po.name) po.reload() - self.assertEquals(len(po.get('items')), 2) + self.assertEqual(len(po.get('items')), 2) self.assertEqual(po.status, 'To Receive and Bill') # ordered qty should increase on row addition self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7) @@ -234,7 +234,7 @@ class TestPurchaseOrder(unittest.TestCase): update_child_qty_rate('Purchase Order', trans_item, po.name) po.reload() - self.assertEquals(len(po.get('items')), 1) + self.assertEqual(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') # ordered qty should decrease (back to initial) on row deletion @@ -448,13 +448,13 @@ class TestPurchaseOrder(unittest.TestCase): pi.load_from_db() - self.assertEquals(pi.per_received, 100.00) - self.assertEquals(pi.items[0].qty, pi.items[0].received_qty) + self.assertEqual(pi.per_received, 100.00) + self.assertEqual(pi.items[0].qty, pi.items[0].received_qty) po.load_from_db() - self.assertEquals(po.per_received, 100.00) - self.assertEquals(po.per_billed, 100.00) + self.assertEqual(po.per_received, 100.00) + self.assertEqual(po.per_billed, 100.00) pr.cancel() @@ -674,8 +674,8 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1) - self.assertEquals(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) - self.assertEquals(bin2.projected_qty, bin1.projected_qty - 10) + self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) + self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10) # Create stock transfer rm_item = [{"item_code":"_Test FG Item","rm_item_code":"_Test Item","item_name":"_Test Item", @@ -690,7 +690,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) + self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) # close PO po.update_status("Closed") @@ -698,7 +698,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) + self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) # Re-open PO po.update_status("Submitted") @@ -706,7 +706,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) + self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100) @@ -723,7 +723,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) + self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) # Cancel PR pr.cancel() @@ -731,7 +731,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) + self.assertEqual(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) # Make Purchase Invoice pi = make_pi_from_po(po.name) @@ -743,7 +743,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) + self.assertEqual(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) # Cancel PR pi.cancel() @@ -751,7 +751,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) + self.assertEqual(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) # Cancel Stock Entry se.cancel() @@ -759,7 +759,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) + self.assertEqual(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) # Cancel PO po.reload() @@ -768,7 +768,7 @@ class TestPurchaseOrder(unittest.TestCase): filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1) - self.assertEquals(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) + self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) def test_exploded_items_in_subcontracted(self): item_code = "_Test Subcontracted FG Item 1" @@ -782,7 +782,7 @@ class TestPurchaseOrder(unittest.TestCase): exploded_items = sorted([d.item_code for d in bom.exploded_items if not d.get('sourced_by_supplier')]) supplied_items = sorted([d.rm_item_code for d in po.supplied_items]) - self.assertEquals(exploded_items, supplied_items) + self.assertEqual(exploded_items, supplied_items) po1 = create_purchase_order(item_code=item_code, qty=1, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0) @@ -790,7 +790,7 @@ class TestPurchaseOrder(unittest.TestCase): supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items]) bom_items = sorted([d.item_code for d in bom.items if not d.get('sourced_by_supplier')]) - self.assertEquals(supplied_items1, bom_items) + self.assertEqual(supplied_items1, bom_items) def test_backflush_based_on_stock_entry(self): item_code = "_Test Subcontracted FG Item 1" @@ -840,8 +840,8 @@ class TestPurchaseOrder(unittest.TestCase): transferred_items = sorted([d.item_code for d in se.get('items') if se.purchase_order == po.name]) issued_items = sorted([d.rm_item_code for d in pr.get('supplied_items')]) - self.assertEquals(transferred_items, issued_items) - self.assertEquals(pr.get('items')[0].rm_supp_cost, 2000) + self.assertEqual(transferred_items, issued_items) + self.assertEqual(pr.get('items')[0].rm_supp_cost, 2000) transferred_rm_map = frappe._dict() diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py index 6900938236f..c1fc6fb82ff 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py @@ -9,12 +9,12 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute import json, frappe, unittest -class TestSubcontractedItemToBeReceived(unittest.TestCase): +class TestSubcontractedItemToBeTransferred(unittest.TestCase): - def test_pending_and_received_qty(self): + def test_pending_and_transferred_qty(self): po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes') - make_stock_entry(item_code='_Test Item', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100) - make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100) + make_stock_entry(item_code='_Test Item', target='_Test Warehouse - _TC', qty=100, basic_rate=100) + make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100) transfer_subcontracted_raw_materials(po.name) col, data = execute(filters=frappe._dict({'supplier': po.supplier, 'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)), @@ -38,7 +38,8 @@ def transfer_subcontracted_raw_materials(po): 'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}] rm_item_string = json.dumps(rm_item) se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string)) + se.from_warehouse = '_Test Warehouse 1 - _TC' se.to_warehouse = '_Test Warehouse 1 - _TC' se.stock_entry_type = 'Send to Subcontractor' se.save() - se.submit() \ No newline at end of file + se.submit() diff --git a/erpnext/change_log/v13/v13.0.2.md b/erpnext/change_log/v13/v13_0_2.md similarity index 100% rename from erpnext/change_log/v13/v13.0.2.md rename to erpnext/change_log/v13/v13_0_2.md diff --git a/erpnext/change_log/v13/v13_3_0.md b/erpnext/change_log/v13/v13_3_0.md new file mode 100644 index 00000000000..016dbb01f4d --- /dev/null +++ b/erpnext/change_log/v13/v13_3_0.md @@ -0,0 +1,73 @@ +# Version 13.3.0 Release Notes + +### Features & Enhancements + +- Purchase receipt creation from purchase invoice ([#25126](https://github.com/frappe/erpnext/pull/25126)) +- New Document Transaction Deletion ([#25354](https://github.com/frappe/erpnext/pull/25354)) +- Employee Referral ([#24997](https://github.com/frappe/erpnext/pull/24997)) +- Add Create Expense Claim button in Delivery Trip ([#25526](https://github.com/frappe/erpnext/pull/25526)) +- Reduced rate of asset depreciation as per IT Act ([#25648](https://github.com/frappe/erpnext/pull/25648)) +- Improve DATEV export ([#25238](https://github.com/frappe/erpnext/pull/25238)) +- Add pick batch button ([#25413](https://github.com/frappe/erpnext/pull/25413)) +- Enable custom field search on POS ([#25421](https://github.com/frappe/erpnext/pull/25421)) +- New check field in subscriptions for (not) submitting invoices ([#25394](https://github.com/frappe/erpnext/pull/25394)) +- Show POS reserved stock in stock projected qty report ([#25593](https://github.com/frappe/erpnext/pull/25593)) +- e-way bill validity field ([#25555](https://github.com/frappe/erpnext/pull/25555)) +- Significant reduction in time taken to save sales documents ([#25475](https://github.com/frappe/erpnext/pull/25475)) + +### Fixes + +- Bank statement import via google sheet ([#25677](https://github.com/frappe/erpnext/pull/25677)) +- Invoices not getting fetched during payment reconciliation ([#25598](https://github.com/frappe/erpnext/pull/25598)) +- Error on applying TDS without party ([#25632](https://github.com/frappe/erpnext/pull/25632)) +- Allow to cancel loan with cancelled repayment entry ([#25507](https://github.com/frappe/erpnext/pull/25507)) +- Can't open general ledger from consolidated financial report ([#25542](https://github.com/frappe/erpnext/pull/25542)) +- Add 'Partially Received' to Status drop-down list in Material Request ([#24857](https://github.com/frappe/erpnext/pull/24857)) +- Updated item filters for material request ([#25531](https://github.com/frappe/erpnext/pull/25531)) +- Added validation in stock entry to check duplicate serial nos ([#25611](https://github.com/frappe/erpnext/pull/25611)) +- Update shopify api version ([#25600](https://github.com/frappe/erpnext/pull/25600)) +- Dialog variable assignment after definition in POS ([#25680](https://github.com/frappe/erpnext/pull/25680)) +- Added tax_types list ([#25587](https://github.com/frappe/erpnext/pull/25587)) +- Include search fields in Project Link field query ([#25505](https://github.com/frappe/erpnext/pull/25505)) +- Item stock levels displaying inconsistently ([#25506](https://github.com/frappe/erpnext/pull/25506)) +- Change today to now to get data for reposting ([#25703](https://github.com/frappe/erpnext/pull/25703)) +- Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet ([#25700](https://github.com/frappe/erpnext/pull/25700)) +- Minor fixes in loan ([#25546](https://github.com/frappe/erpnext/pull/25546)) +- Fieldname when updating docfield property ([#25516](https://github.com/frappe/erpnext/pull/25516)) +- Use get_serial_nos for splitting ([#25590](https://github.com/frappe/erpnext/pull/25590)) +- Show item's full name on hover over item in POS ([#25554](https://github.com/frappe/erpnext/pull/25554)) +- Stock ledger entry created against draft stock entry ([#25540](https://github.com/frappe/erpnext/pull/25540)) +- Incorrect expense account set in pos invoice ([#25543](https://github.com/frappe/erpnext/pull/25543)) +- Stock balance and batch-wise balance history report showing different closing stock ([#25575](https://github.com/frappe/erpnext/pull/25575)) +- Make strings translatable ([#25521](https://github.com/frappe/erpnext/pull/25521)) +- Serial no changed after saving stock reconciliation ([#25541](https://github.com/frappe/erpnext/pull/25541)) +- Ignore fraction difference while making round off gl entry ([#25438](https://github.com/frappe/erpnext/pull/25438)) +- Sync shopify customer addresses ([#25481](https://github.com/frappe/erpnext/pull/25481)) +- Total stock summary report not working ([#25551](https://github.com/frappe/erpnext/pull/25551)) +- Rename field has not updated value of deposit and withdrawal fields ([#25545](https://github.com/frappe/erpnext/pull/25545)) +- Unexpected keyword argument 'merge_logs' ([#25489](https://github.com/frappe/erpnext/pull/25489)) +- Validation message of quality inspection in purchase receipt ([#25667](https://github.com/frappe/erpnext/pull/25667)) +- Added is_stock_item filter ([#25530](https://github.com/frappe/erpnext/pull/25530)) +- Fetch total stock at company in PO ([#25532](https://github.com/frappe/erpnext/pull/25532)) +- Updated filters for process statement of accounts ([#25384](https://github.com/frappe/erpnext/pull/25384)) +- Incorrect expense account set in pos invoice ([#25571](https://github.com/frappe/erpnext/pull/25571)) +- Client script breaking while settings tax labels ([#25653](https://github.com/frappe/erpnext/pull/25653)) +- Empty payment term column in accounts receivable report ([#25556](https://github.com/frappe/erpnext/pull/25556)) +- Designation insufficient permission on lead doctype. ([#25331](https://github.com/frappe/erpnext/pull/25331)) +- Force https for shopify webhook registration ([#25630](https://github.com/frappe/erpnext/pull/25630)) +- Patch regional fields for old companies ([#25673](https://github.com/frappe/erpnext/pull/25673)) +- Woocommerce order sync issue ([#25692](https://github.com/frappe/erpnext/pull/25692)) +- Allow to receive same serial numbers multiple times ([#25471](https://github.com/frappe/erpnext/pull/25471)) +- Update Allocated amount after Paid Amount is changed in PE ([#25515](https://github.com/frappe/erpnext/pull/25515)) +- Updating Standard Notification's channel field ([#25564](https://github.com/frappe/erpnext/pull/25564)) +- Report summary showing inflated values when values are accumulated in Group Company ([#25577](https://github.com/frappe/erpnext/pull/25577)) +- UI fixes related to overflowing payment section ([#25652](https://github.com/frappe/erpnext/pull/25652)) +- List invoices in Payment Reconciliation Payment ([#25524](https://github.com/frappe/erpnext/pull/25524)) +- Ageing errors in PSOA ([#25490](https://github.com/frappe/erpnext/pull/25490)) +- Prevent spurious defaults for items when making prec from dnote ([#25559](https://github.com/frappe/erpnext/pull/25559)) +- Stock reconciliation getting time out error during submission ([#25557](https://github.com/frappe/erpnext/pull/25557)) +- Timesheet filter date exclusive issue ([#25626](https://github.com/frappe/erpnext/pull/25626)) +- Update cost center in the item table fetched from POS Profile ([#25609](https://github.com/frappe/erpnext/pull/25609)) +- Updated modified time in purchase invoice to pull new fields ([#25678](https://github.com/frappe/erpnext/pull/25678)) +- Stock and Accounts Settings form refactor ([#25534](https://github.com/frappe/erpnext/pull/25534)) +- Payment amount showing in foreign currency ([#25292](https://github.com/frappe/erpnext/pull/25292)) \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c409850734c..913458fa355 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1006,7 +1006,6 @@ class AccountsController(TransactionBase): else: grand_total -= self.get("total_advance") base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) - print(grand_total, base_grand_total) if total != flt(grand_total, self.precision("grand_total")) or \ base_total != flt(base_grand_total, self.precision("base_grand_total")): frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total")) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index b686dc026c6..3f2d3390c05 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -838,9 +838,10 @@ class BuyingController(StockController): if not self.get("items"): return - earliest_schedule_date = min([d.schedule_date for d in self.get("items")]) - if earliest_schedule_date: - self.schedule_date = earliest_schedule_date + if any(d.schedule_date for d in self.get("items")): + # Select earliest schedule_date. + self.schedule_date = min(d.schedule_date for d in self.get("items") + if d.schedule_date is not None) if self.schedule_date: for d in self.get('items'): diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index bc1ac5ea069..b31724fa487 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -292,11 +292,14 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): cond = """(`tabProject`.customer = %s or ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer"))) - fields = get_fields("Project", ["name"]) + fields = get_fields("Project", ["name", "project_name"]) + searchfields = frappe.get_meta("Project").get_search_fields() + searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) return frappe.db.sql("""select {fields} from `tabProject` - where `tabProject`.status not in ("Completed", "Cancelled") - and {cond} `tabProject`.name like %(txt)s {match_cond} + where + `tabProject`.status not in ("Completed", "Cancelled") + and {cond} {match_cond} {scond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, @@ -304,6 +307,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): limit {start}, {page_len}""".format( fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]), cond=cond, + scond=searchfields, match_cond=get_match_cond(doctype), start=start, page_len=page_len), { diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 4bb6138e5d7..ed3aee5c1a1 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -100,6 +100,10 @@ status_map = { ["Queued", "eval:self.status == 'Queued'"], ["Failed", "eval:self.status == 'Failed'"], ["Cancelled", "eval:self.docstatus == 2"], + ], + "Transaction Deletion Record": [ + ["Draft", None], + ["Completed", "eval:self.docstatus == 1"], ] } diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index b14c2745159..41ca404d9b8 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -379,8 +379,7 @@ class StockController(AccountsController): link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection) frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError) - qa_failed = any([r.status=="Rejected" for r in qa_doc.readings]) - if qa_failed: + if qa_doc.status != 'Accepted': frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}") .format(d.idx, d.item_code), QualityInspectionRejectedError) elif qa_required : diff --git a/erpnext/education/doctype/fees/test_fees.py b/erpnext/education/doctype/fees/test_fees.py index eedc2ae7301..c6bb704b412 100644 --- a/erpnext/education/doctype/fees/test_fees.py +++ b/erpnext/education/doctype/fees/test_fees.py @@ -9,8 +9,7 @@ from frappe.utils import nowdate from frappe.utils.make_random import get_random from erpnext.education.doctype.program.test_program import make_program_and_linked_courses -# test_records = frappe.get_test_records('Fees') - +test_dependencies = ['Company'] class TestFees(unittest.TestCase): def test_fees(self): diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index f0a05ed192f..5d5b2e19ce3 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -335,13 +335,13 @@ def get_url(shopify_settings): if not last_order_id: if shopify_settings.sync_based_on == 'Date': - url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format( + url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format( get_datetime(shopify_settings.from_date)), shopify_settings) else: - url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format( + url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format( shopify_settings.from_order_id), shopify_settings) else: - url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings) + url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings) return url diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 6dedaa8c530..a505ee09d28 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import frappe, base64, hashlib, hmac, json +from frappe.utils import cstr from frappe import _ def verify_request(): @@ -146,22 +147,19 @@ def rename_address(address, customer): def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: - item_woo_com_id = item_data.get("product_id") + item_woo_com_id = cstr(item_data.get("product_id")) - if frappe.get_value("Item", {"woocommerce_id": item_woo_com_id}): - #Edit Item - item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id}) - else: + if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, 'name'): #Create Item item = frappe.new_doc("Item") + item.item_code = _("woocommerce - {0}", sys_lang).format(item_woo_com_id) + item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang) + item.item_group = _("WooCommerce Products", sys_lang) - item.item_name = item_data.get("name") - item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) - item.woocommerce_id = item_data.get("product_id") - item.item_group = _("WooCommerce Products", sys_lang) - item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang) - item.flags.ignore_mandatory = True - item.save() + item.item_name = item_data.get("name") + item.woocommerce_id = item_woo_com_id + item.flags.ignore_mandatory = True + item.save() def create_sales_order(order, woocommerce_settings, customer_name, sys_lang): new_sales_order = frappe.new_doc("Sales Order") @@ -194,12 +192,12 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l for item in order.get("line_items"): woocomm_item_id = item.get("product_id") - found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id}) + found_item = frappe.get_doc("Item", {"woocommerce_id": cstr(woocomm_item_id)}) ordered_items_tax = item.get("total_tax") - new_sales_order.append("items",{ - "item_code": found_item.item_code, + new_sales_order.append("items", { + "item_code": found_item.name, "item_name": found_item.item_name, "description": found_item.item_name, "delivery_date": new_sales_order.delivery_date, @@ -207,7 +205,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l "qty": item.get("quantity"), "rate": item.get("price"), "warehouse": woocommerce_settings.warehouse or default_warehouse - }) + }) add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account) diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py index 29487962f69..3c2e59ab821 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py @@ -19,7 +19,7 @@ class TestMpesaSettings(unittest.TestCase): mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test") self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"})) self.assertTrue(mode_of_payment.name) - self.assertEquals(mode_of_payment.type, "Phone") + self.assertEqual(mode_of_payment.type, "Phone") def test_processing_of_account_balance(self): mpesa_doc = create_mpesa_settings(payment_gateway_name="_Account Balance") @@ -31,11 +31,11 @@ class TestMpesaSettings(unittest.TestCase): # test integration request creation and successful update of the status on receiving callback response self.assertTrue(integration_request) - self.assertEquals(integration_request.status, "Completed") + self.assertEqual(integration_request.status, "Completed") # test formatting of account balance received as string to json with appropriate currency symbol mpesa_doc.reload() - self.assertEquals(mpesa_doc.account_balance, dumps({ + self.assertEqual(mpesa_doc.account_balance, dumps({ "Working Account": { "current_balance": "Sh 481,000.00", "available_balance": "Sh 481,000.00", @@ -60,7 +60,7 @@ class TestMpesaSettings(unittest.TestCase): pr = pos_invoice.create_payment_request() # test payment request creation - self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + self.assertEqual(pr.payment_gateway, "Mpesa-Payment") # submitting payment request creates integration requests with random id integration_req_ids = frappe.get_all("Integration Request", filters={ @@ -75,13 +75,13 @@ class TestMpesaSettings(unittest.TestCase): # test integration request creation and successful update of the status on receiving callback response self.assertTrue(integration_request) - self.assertEquals(integration_request.status, "Completed") + self.assertEqual(integration_request.status, "Completed") pos_invoice.reload() integration_request.reload() - self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R") - self.assertEquals(integration_request.status, "Completed") - + self.assertEqual(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R") + self.assertEqual(integration_request.status, "Completed") + frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") integration_request.delete() pr.reload() @@ -104,7 +104,7 @@ class TestMpesaSettings(unittest.TestCase): pr = pos_invoice.create_payment_request() # test payment request creation - self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + self.assertEqual(pr.payment_gateway, "Mpesa-Payment") # submitting payment request creates integration requests with random id integration_req_ids = frappe.get_all("Integration Request", filters={ @@ -126,12 +126,12 @@ class TestMpesaSettings(unittest.TestCase): verify_transaction(**callback_response) # test completion of integration request integration_request = frappe.get_doc("Integration Request", integration_req_ids[i]) - self.assertEquals(integration_request.status, "Completed") + self.assertEqual(integration_request.status, "Completed") integration_requests.append(integration_request) # check receipt number once all the integration requests are completed pos_invoice.reload() - self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers)) + self.assertEqual(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers)) frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") [d.delete() for d in integration_requests] @@ -139,7 +139,7 @@ class TestMpesaSettings(unittest.TestCase): pr.cancel() pr.delete() pos_invoice.delete() - + def test_processing_of_only_one_succes_callback_payload(self): create_mpesa_settings(payment_gateway_name="Payment") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") @@ -155,7 +155,7 @@ class TestMpesaSettings(unittest.TestCase): pr = pos_invoice.create_payment_request() # test payment request creation - self.assertEquals(pr.payment_gateway, "Mpesa-Payment") + self.assertEqual(pr.payment_gateway, "Mpesa-Payment") # submitting payment request creates integration requests with random id integration_req_ids = frappe.get_all("Integration Request", filters={ @@ -175,7 +175,7 @@ class TestMpesaSettings(unittest.TestCase): verify_transaction(**callback_response) # test completion of integration request integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) - self.assertEquals(integration_request.status, "Completed") + self.assertEqual(integration_request.status, "Completed") # now one request is completed # second integration request fails @@ -187,7 +187,7 @@ class TestMpesaSettings(unittest.TestCase): 'name': ['not in', integration_req_ids] }, pluck="name") - self.assertEquals(len(new_integration_req_ids), 1) + self.assertEqual(len(new_integration_req_ids), 1) frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'") diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index cbdf90681d3..381c5e5dec4 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -30,14 +30,14 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2020-04/webhooks.json', self) + url = get_shopify_url('admin/api/2021-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: res = session.post(url, data=json.dumps({ "webhook": { "topic": method, - "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), + "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True), "format": "json" } }), headers=get_header(self)) @@ -56,7 +56,7 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py index 7866fdea31a..2af57f4c891 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py @@ -32,10 +32,12 @@ def create_customer(shopify_customer, shopify_settings): raise e def create_customer_address(customer, shopify_customer): - if not shopify_customer.get("addresses"): - return + addresses = shopify_customer.get("addresses", []) - for i, address in enumerate(shopify_customer.get("addresses")): + if not addresses and "default_address" in shopify_customer: + addresses.append(shopify_customer["default_address"]) + + for i, address in enumerate(addresses): address_title, address_type = get_address_title_and_type(customer.customer_name, i) try : frappe.get_doc({ diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index f9f0bb3cecc..16efb6caee1 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py index 362f6cf88ee..3840e781b4c 100644 --- a/erpnext/erpnext_integrations/utils.py +++ b/erpnext/erpnext_integrations/utils.py @@ -28,7 +28,7 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'): return innerfn -def get_webhook_address(connector_name, method, exclude_uri=False): +def get_webhook_address(connector_name, method, exclude_uri=False, force_https=False): endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method) if exclude_uri: @@ -39,7 +39,11 @@ def get_webhook_address(connector_name, method, exclude_uri=False): except RuntimeError: url = "http://localhost:8000" - server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint) + url_data = urlparse(url) + scheme = "https" if force_https else url_data.scheme + netloc = url_data.netloc + + server_url = f"{scheme}://{netloc}/api/method/{endpoint}" return server_url diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index fb72073a07f..03e96a4b3be 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -17,7 +17,7 @@ class TestClinicalProcedure(unittest.TestCase): procedure_template.disabled = 1 procedure_template.save() - self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1) + self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1) def test_consumables(self): patient, medical_department, practitioner = create_healthcare_docs() diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py index 79ab8a4d7f2..c9f0029ed80 100644 --- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py @@ -18,7 +18,7 @@ class TestLabTest(unittest.TestCase): lab_template.disabled = 1 lab_template.save() - self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1) + self.assertEqual(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1) lab_template.reload() @@ -57,7 +57,7 @@ class TestLabTest(unittest.TestCase): # sample collection should not be created lab_test.reload() - self.assertEquals(lab_test.sample, None) + self.assertEqual(lab_test.sample, None) def test_create_lab_tests_from_sales_invoice(self): sales_invoice = create_sales_invoice() diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 2bb8a53c454..5f2dc480a1b 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -20,13 +20,13 @@ class TestPatientAppointment(unittest.TestCase): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) appointment = create_appointment(patient, practitioner, nowdate()) - self.assertEquals(appointment.status, 'Open') + self.assertEqual(appointment.status, 'Open') appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2)) - self.assertEquals(appointment.status, 'Scheduled') + self.assertEqual(appointment.status, 'Scheduled') encounter = create_encounter(appointment) - self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') encounter.cancel() - self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open') + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open') def test_start_encounter(self): patient, medical_department, practitioner = create_healthcare_docs() diff --git a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py index 7fb159d6b50..113fa513f98 100644 --- a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py +++ b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py @@ -18,24 +18,24 @@ class TestTherapyPlan(unittest.TestCase): def test_status(self): plan = create_therapy_plan() - self.assertEquals(plan.status, 'Not Started') + self.assertEqual(plan.status, 'Not Started') session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company') frappe.get_doc(session).submit() - self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress') + self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress') session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company') frappe.get_doc(session).submit() - self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed') + self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed') patient, medical_department, practitioner = create_healthcare_docs() - appointment = create_appointment(patient, practitioner, nowdate()) + appointment = create_appointment(patient, practitioner, nowdate()) session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name) session = frappe.get_doc(session) session.submit() - self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') session.cancel() - self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open') + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open') def test_therapy_plan_from_template(self): patient = create_patient() @@ -49,7 +49,7 @@ class TestTherapyPlan(unittest.TestCase): si.save() therapy_plan_template_amt = frappe.db.get_value('Therapy Plan Template', template, 'total_amount') - self.assertEquals(si.items[0].amount, therapy_plan_template_amt) + self.assertEqual(si.items[0].amount, therapy_plan_template_amt) def create_therapy_plan(template=None): diff --git a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py index 03a1be8a4e7..21f63699753 100644 --- a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py +++ b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py @@ -13,7 +13,7 @@ class TestTherapyType(unittest.TestCase): therapy_type.disabled = 1 therapy_type.save() - self.assertEquals(frappe.db.get_value('Item', therapy_type.item, 'disabled'), 1) + self.assertEqual(frappe.db.get_value('Item', therapy_type.item, 'disabled'), 1) def create_therapy_type(): exercise = create_exercise_type() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5fa419f412e..3024819d764 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -427,7 +427,8 @@ regional_overrides = { 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries', - 'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields' + 'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields', + 'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount' }, 'United Arab Emirates': { 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data', diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py index 74ce30108fd..3b99c57051a 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py @@ -68,19 +68,19 @@ class TestCompensatoryLeaveRequest(unittest.TestCase): filters = dict(transaction_name=compensatory_leave_request.leave_allocation) leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters) - self.assertEquals(len(leave_ledger_entry), 1) - self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, 1) + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, 1) # check reverse leave ledger entry on cancellation compensatory_leave_request.cancel() leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc') - self.assertEquals(len(leave_ledger_entry), 2) - self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, -1) + self.assertEqual(len(leave_ledger_entry), 2) + self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, -1) def get_compensatory_leave_request(employee, leave_date=today()): prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request', diff --git a/erpnext/hr/doctype/designation/designation.json b/erpnext/hr/doctype/designation/designation.json index 4c3888be4aa..bab6b90d1aa 100644 --- a/erpnext/hr/doctype/designation/designation.json +++ b/erpnext/hr/doctype/designation/designation.json @@ -182,6 +182,10 @@ "share": 1, "submit": 0, "write": 1 + }, + { + "read": 1, + "role": "Sales User" } ], "quick_entry": 1, @@ -191,4 +195,4 @@ "track_changes": 0, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index e3e6e80616a..a268c15c70b 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -14,6 +14,7 @@ "column_break_5", "expense_approver", "approval_status", + "delivery_trip", "is_paid", "expense_details", "expenses", @@ -365,13 +366,20 @@ "label": "Total Taxes and Charges", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "depends_on": "eval: doc.delivery_trip", + "fieldname": "delivery_trip", + "fieldtype": "Link", + "label": "Delivery Trip", + "options": "Delivery Trip" } ], "icon": "fa fa-money", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-09-18 17:26:09.703215", + "modified": "2021-05-04 05:35:12.040199", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 3f22ca21412..578eccf787d 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -88,9 +88,9 @@ class TestExpenseClaim(unittest.TestCase): ]) for gle in gl_entries: - self.assertEquals(expected_values[gle.account][0], gle.account) - self.assertEquals(expected_values[gle.account][1], gle.debit) - self.assertEquals(expected_values[gle.account][2], gle.credit) + self.assertEqual(expected_values[gle.account][0], gle.account) + self.assertEqual(expected_values[gle.account][1], gle.debit) + self.assertEqual(expected_values[gle.account][2], gle.credit) def test_rejected_expense_claim(self): payable_account = get_payable_account(company_name) @@ -104,11 +104,11 @@ class TestExpenseClaim(unittest.TestCase): }) expense_claim.submit() - self.assertEquals(expense_claim.status, 'Rejected') - self.assertEquals(expense_claim.total_sanctioned_amount, 0.0) + self.assertEqual(expense_claim.status, 'Rejected') + self.assertEqual(expense_claim.total_sanctioned_amount, 0.0) gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name}) - self.assertEquals(len(gl_entry), 0) + self.assertEqual(len(gl_entry), 0) def test_expense_approver_perms(self): user = "test_approver_perm_emp@example.com" diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index 8af8cea605d..f65e6e12074 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -23,7 +23,7 @@ class HolidayList(Document): last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,]) for i, d in enumerate(date_list): ch = self.append('holidays', {}) - ch.description = self.weekly_off + ch.description = _(self.weekly_off) ch.holiday_date = d ch.weekly_off = 1 ch.idx = last_idx + i + 1 diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py index 690a692ddca..b3e1dc8d87b 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ b/erpnext/hr/doctype/job_offer/test_job_offer.py @@ -35,13 +35,13 @@ class TestJobOffer(unittest.TestCase): job_offer = create_job_offer(job_applicant=job_applicant.name) job_offer.submit() job_applicant.reload() - self.assertEquals(job_applicant.status, "Accepted") + self.assertEqual(job_applicant.status, "Accepted") # status update after rejection job_offer.status = "Rejected" job_offer.submit() job_applicant.reload() - self.assertEquals(job_applicant.status, "Rejected") + self.assertEqual(job_applicant.status, "Rejected") def create_job_offer(**args): args = frappe._dict(args) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 0b71036c860..6e7ae87d08c 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -96,7 +96,7 @@ class TestLeaveAllocation(unittest.TestCase): carry_forward=1) leave_allocation_1.submit() - self.assertEquals(leave_allocation_1.unused_leaves, 10) + self.assertEqual(leave_allocation_1.unused_leaves, 10) leave_allocation_1.cancel() @@ -108,7 +108,7 @@ class TestLeaveAllocation(unittest.TestCase): new_leaves_allocated=25) leave_allocation_2.submit() - self.assertEquals(leave_allocation_2.unused_leaves, 5) + self.assertEqual(leave_allocation_2.unused_leaves, 5) def test_carry_forward_leaves_expiry(self): frappe.db.sql("delete from `tabLeave Allocation`") @@ -145,7 +145,7 @@ class TestLeaveAllocation(unittest.TestCase): to_date=add_months(nowdate(), 12)) leave_allocation_1.submit() - self.assertEquals(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) + self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) def test_creation_of_leave_ledger_entry_on_submit(self): frappe.db.sql("delete from `tabLeave Allocation`") @@ -155,10 +155,10 @@ class TestLeaveAllocation(unittest.TestCase): leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name)) - self.assertEquals(len(leave_ledger_entry), 1) - self.assertEquals(leave_ledger_entry[0].employee, leave_allocation.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) # check if leave ledger entry is deleted on cancellation leave_allocation.cancel() diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index b54c9712c89..a4a96b813ee 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -16,36 +16,36 @@ from erpnext.hr.doctype.employee.test_employee import make_employee test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"] _test_records = [ - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00001", - "from_date": "2013-05-01", - "description": "_Test Reason", - "leave_type": "_Test Leave Type", - "posting_date": "2013-01-02", - "to_date": "2013-05-05" - }, - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00002", - "from_date": "2013-05-01", - "description": "_Test Reason", - "leave_type": "_Test Leave Type", - "posting_date": "2013-01-02", - "to_date": "2013-05-05" - }, - { - "company": "_Test Company", - "doctype": "Leave Application", - "employee": "_T-Employee-00001", - "from_date": "2013-01-15", - "description": "_Test Reason", - "leave_type": "_Test Leave Type LWP", - "posting_date": "2013-01-02", - "to_date": "2013-01-15" - } + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00001", + "from_date": "2013-05-01", + "description": "_Test Reason", + "leave_type": "_Test Leave Type", + "posting_date": "2013-01-02", + "to_date": "2013-05-05" + }, + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00002", + "from_date": "2013-05-01", + "description": "_Test Reason", + "leave_type": "_Test Leave Type", + "posting_date": "2013-01-02", + "to_date": "2013-05-05" + }, + { + "company": "_Test Company", + "doctype": "Leave Application", + "employee": "_T-Employee-00001", + "from_date": "2013-01-15", + "description": "_Test Reason", + "leave_type": "_Test Leave Type LWP", + "posting_date": "2013-01-02", + "to_date": "2013-01-15" + } ] @@ -516,9 +516,9 @@ class TestLeaveApplication(unittest.TestCase): leave_application.submit() leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name)) - self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1) + self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1) # check if leave ledger entry is deleted on cancellation leave_application.cancel() @@ -549,11 +549,11 @@ class TestLeaveApplication(unittest.TestCase): leave_ledger_entry = frappe.get_all('Leave Ledger Entry', '*', filters=dict(transaction_name=leave_application.name)) - self.assertEquals(len(leave_ledger_entry), 2) - self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, -9) - self.assertEquals(leave_ledger_entry[1].leaves, -2) + self.assertEqual(len(leave_ledger_entry), 2) + self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, -9) + self.assertEqual(leave_ledger_entry[1].leaves, -2) def test_leave_application_creation_after_expiry(self): # test leave balance for carry forwarded allocation @@ -566,7 +566,7 @@ class TestLeaveApplication(unittest.TestCase): create_carry_forwarded_allocation(employee, leave_type) - self.assertEquals(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0) + self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0) def test_leave_approver_perms(self): employee = get_employee() diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index aafc9642d46..e0ffa5dd41a 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -88,10 +88,10 @@ class TestLeaveEncashment(unittest.TestCase): leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_encashment.name)) - self.assertEquals(len(leave_ledger_entry), 1) - self.assertEquals(leave_ledger_entry[0].employee, leave_encashment.employee) - self.assertEquals(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) + self.assertEqual(len(leave_ledger_entry), 1) + self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee) + self.assertEqual(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) + self.assertEqual(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) # check if leave ledger entry is deleted on cancellation diff --git a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py index 6e151d0e3c5..03b0cf3da21 100644 --- a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py @@ -5,11 +5,18 @@ from __future__ import unicode_literals import frappe import unittest +import erpnext from frappe.utils import getdate from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data from erpnext.hr.doctype.employee.test_employee import make_employee +test_dependencies = ['Holiday List'] + class TestUploadAttendance(unittest.TestCase): + @classmethod + def setUpClass(cls): + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", '_Test Holiday List') + def test_date_range(self): employee = make_employee("test_employee@company.com") employee_doc = frappe.get_doc("Employee", employee) diff --git a/erpnext/hr/notification/training_feedback/training_feedback.json b/erpnext/hr/notification/training_feedback/training_feedback.json index 2cc064f34a5..92b68a98a90 100644 --- a/erpnext/hr/notification/training_feedback/training_feedback.json +++ b/erpnext/hr/notification/training_feedback/training_feedback.json @@ -1,5 +1,6 @@ { "attach_print": 0, + "channel": "Email", "creation": "2017-08-11 03:17:11.769210", "days_in_advance": 0, "docstatus": 0, diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index fae6f860b6d..fa4707ce2b4 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -55,9 +55,9 @@ class TestLoan(unittest.TestCase): def test_loan(self): loan = frappe.get_doc("Loan", {"applicant":self.applicant1}) - self.assertEquals(loan.monthly_repayment_amount, 15052) - self.assertEquals(flt(loan.total_interest_payable, 0), 21034) - self.assertEquals(flt(loan.total_payment, 0), 301034) + self.assertEqual(loan.monthly_repayment_amount, 15052) + self.assertEqual(flt(loan.total_interest_payable, 0), 21034) + self.assertEqual(flt(loan.total_payment, 0), 301034) schedule = loan.repayment_schedule @@ -72,9 +72,9 @@ class TestLoan(unittest.TestCase): loan.monthly_repayment_amount = 14000 loan.save() - self.assertEquals(len(loan.repayment_schedule), 22) - self.assertEquals(flt(loan.total_interest_payable, 0), 22712) - self.assertEquals(flt(loan.total_payment, 0), 302712) + self.assertEqual(len(loan.repayment_schedule), 22) + self.assertEqual(flt(loan.total_interest_payable, 0), 22712) + self.assertEqual(flt(loan.total_payment, 0), 302712) def test_loan_with_security(self): @@ -89,7 +89,7 @@ class TestLoan(unittest.TestCase): loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) def test_loan_disbursement(self): pledge = [{ @@ -102,7 +102,7 @@ class TestLoan(unittest.TestCase): create_pledge(loan_application) loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) loan.submit() @@ -120,8 +120,8 @@ class TestLoan(unittest.TestCase): filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry2.name} ) - self.assertEquals(loan.status, "Disbursed") - self.assertEquals(loan.disbursed_amount, 1000000) + self.assertEqual(loan.status, "Disbursed") + self.assertEqual(loan.disbursed_amount, 1000000) self.assertTrue(gl_entries1) self.assertTrue(gl_entries2) @@ -137,7 +137,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -156,15 +156,15 @@ class TestLoan(unittest.TestCase): repayment_entry.submit() penalty_amount = (accrued_interest_amount * 5 * 25) / 100 - self.assertEquals(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0)) + self.assertEqual(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0)) amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount']) loan.load_from_db() total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount'] - self.assertEquals(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable) - self.assertEquals(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid - + self.assertEqual(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable) + self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid - penalty_amount - total_interest_paid, 0)) def test_loan_closure(self): @@ -179,7 +179,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -204,12 +204,12 @@ class TestLoan(unittest.TestCase): amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)']) - self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0)) - self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) + self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0)) + self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0) request_loan_closure(loan.name) loan.load_from_db() - self.assertEquals(loan.status, "Loan Closure Requested") + self.assertEqual(loan.status, "Loan Closure Requested") def test_loan_repayment_for_term_loan(self): pledges = [{ @@ -241,8 +241,8 @@ class TestLoan(unittest.TestCase): amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', 'paid_principal_amount']) - self.assertEquals(amounts[0], 11250.00) - self.assertEquals(amounts[1], 78303.00) + self.assertEqual(amounts[0], 11250.00) + self.assertEqual(amounts[1], 78303.00) def test_security_shortfall(self): pledges = [{ @@ -268,17 +268,17 @@ class TestLoan(unittest.TestCase): loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) self.assertTrue(loan_security_shortfall) - self.assertEquals(loan_security_shortfall.loan_amount, 1000000.00) - self.assertEquals(loan_security_shortfall.security_value, 800000.00) - self.assertEquals(loan_security_shortfall.shortfall_amount, 600000.00) + self.assertEqual(loan_security_shortfall.loan_amount, 1000000.00) + self.assertEqual(loan_security_shortfall.security_value, 800000.00) + self.assertEqual(loan_security_shortfall.shortfall_amount, 600000.00) frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250 where loan_security='Test Security 2'""") create_process_loan_security_shortfall() loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) - self.assertEquals(loan_security_shortfall.status, "Completed") - self.assertEquals(loan_security_shortfall.shortfall_amount, 0) + self.assertEqual(loan_security_shortfall.status, "Completed") + self.assertEqual(loan_security_shortfall.shortfall_amount, 0) def test_loan_security_unpledge(self): pledge = [{ @@ -292,7 +292,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -312,7 +312,7 @@ class TestLoan(unittest.TestCase): request_loan_closure(loan.name) loan.load_from_db() - self.assertEquals(loan.status, "Loan Closure Requested") + self.assertEqual(loan.status, "Loan Closure Requested") unpledge_request = unpledge_security(loan=loan.name, save=1) unpledge_request.submit() @@ -323,11 +323,11 @@ class TestLoan(unittest.TestCase): pledged_qty = get_pledged_security_qty(loan.name) self.assertEqual(loan.status, 'Closed') - self.assertEquals(sum(pledged_qty.values()), 0) + self.assertEqual(sum(pledged_qty.values()), 0) amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(amounts['pending_principal_amount'], 0) - self.assertEquals(amounts['payable_principal_amount'], 0.0) + self.assertEqual(amounts['payable_principal_amount'], 0.0) self.assertEqual(amounts['interest_amount'], 0) def test_partial_loan_security_unpledge(self): @@ -346,7 +346,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -379,7 +379,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) unpledge_map = {'Test Security 1': 4000} unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1) @@ -450,7 +450,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -475,7 +475,7 @@ class TestLoan(unittest.TestCase): request_loan_closure(loan.name) loan.load_from_db() - self.assertEquals(loan.status, "Loan Closure Requested") + self.assertEqual(loan.status, "Loan Closure Requested") amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(amounts['pending_principal_amount'], 0.0) @@ -492,7 +492,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -533,8 +533,8 @@ class TestLoan(unittest.TestCase): calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount') - self.assertEquals(loan.loan_amount, 1000000) - self.assertEquals(calculated_penalty_amount, penalty_amount) + self.assertEqual(loan.loan_amount, 1000000) + self.assertEqual(calculated_penalty_amount, penalty_amount) def test_penalty_repayment(self): loan, dummy = create_loan_scenario_for_penalty(self) @@ -547,13 +547,13 @@ class TestLoan(unittest.TestCase): repayment_entry.submit() amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01') - self.assertEquals(amounts['penalty_amount'], second_penalty) + self.assertEqual(amounts['penalty_amount'], second_penalty) repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty) repayment_entry.submit() amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02') - self.assertEquals(amounts['penalty_amount'], 0) + self.assertEqual(amounts['penalty_amount'], 0) def test_loan_write_off_limit(self): pledge = [{ @@ -567,7 +567,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -589,15 +589,15 @@ class TestLoan(unittest.TestCase): amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)']) - self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0)) - self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) + self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0)) + self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0) amounts = calculate_amounts(loan.name, add_days(last_date, 5)) - self.assertEquals(flt(amounts['pending_principal_amount'], 0), 50) + self.assertEqual(flt(amounts['pending_principal_amount'], 0), 50) request_loan_closure(loan.name) loan.load_from_db() - self.assertEquals(loan.status, "Loan Closure Requested") + self.assertEqual(loan.status, "Loan Closure Requested") def test_loan_amount_write_off(self): pledge = [{ @@ -611,7 +611,7 @@ class TestLoan(unittest.TestCase): loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -633,17 +633,17 @@ class TestLoan(unittest.TestCase): amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)']) - self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0)) - self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) + self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0)) + self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0) amounts = calculate_amounts(loan.name, add_days(last_date, 5)) - self.assertEquals(flt(amounts['pending_principal_amount'], 0), 100) + self.assertEqual(flt(amounts['pending_principal_amount'], 0), 100) we = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount']) we.submit() amounts = calculate_amounts(loan.name, add_days(last_date, 5)) - self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0) + self.assertEqual(flt(amounts['pending_principal_amount'], 0), 0) def create_loan_scenario_for_penalty(doc): pledge = [{ diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index a8753877a6a..da56710c679 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -87,7 +87,7 @@ class TestLoanDisbursement(unittest.TestCase): loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01') loan.submit() - self.assertEquals(loan.loan_amount, 1000000) + self.assertEqual(loan.loan_amount, 1000000) first_date = '2019-10-01' last_date = '2019-10-30' @@ -114,5 +114,5 @@ class TestLoanDisbursement(unittest.TestCase): per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30') interest = per_day_interest * 15 - self.assertEquals(amounts['pending_principal_amount'], 1500000) - self.assertEquals(amounts['interest_amount'], flt(interest + previous_interest, 2)) + self.assertEqual(amounts['pending_principal_amount'], 1500000) + self.assertEqual(amounts['interest_amount'], flt(interest + previous_interest, 2)) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index 85e008ac293..eb626f3eee0 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -52,7 +52,7 @@ class TestLoanInterestAccrual(unittest.TestCase): process_loan_interest_accrual_for_demand_loans(posting_date=last_date) loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) - self.assertEquals(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0)) + self.assertEqual(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0)) def test_accumulated_amounts(self): pledge = [{ @@ -76,7 +76,7 @@ class TestLoanInterestAccrual(unittest.TestCase): process_loan_interest_accrual_for_demand_loans(posting_date=last_date) loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) - self.assertEquals(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0)) + self.assertEqual(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0)) next_start_date = '2019-10-31' next_end_date = '2019-11-29' @@ -90,4 +90,4 @@ class TestLoanInterestAccrual(unittest.TestCase): loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name, 'process_loan_interest_accrual': process}) - self.assertEquals(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount) + self.assertEqual(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 2b329448fba..b32b96af13b 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -29,7 +29,10 @@ frappe.ui.form.on("BOM", { frm.set_query("item", function() { return { - query: "erpnext.manufacturing.doctype.bom.bom.item_query" + query: "erpnext.manufacturing.doctype.bom.bom.item_query", + filters: { + "is_stock_item": 1 + } }; }); diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 979f7ca3128..d1f63854c71 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -973,6 +973,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): if not has_variants: query_filters["has_variants"] = 0 + if filters and filters.get("is_stock_item"): + query_filters["is_stock_item"] = 1 + return frappe.get_all("Item", fields = fields, filters=query_filters, or_filters = or_cond_filters, order_by=order_by, diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 7108338dab4..e1cca9e3ef4 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -223,7 +223,7 @@ class TestBOM(unittest.TestCase): is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") bom_items = sorted([d.item_code for d in bom.items if d.sourced_by_supplier != 1]) supplied_items = sorted([d.rm_item_code for d in po.supplied_items]) - self.assertEquals(bom_items, supplied_items) + self.assertEqual(bom_items, supplied_items) def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py index ac9a409bcbe..80d1cdfc8f2 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py @@ -45,16 +45,16 @@ class TestBOMUpdateTool(unittest.TestCase): else: doc = frappe.get_doc("BOM", bom_no) - self.assertEquals(doc.total_cost, 200) + self.assertEqual(doc.total_cost, 200) frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 200) update_cost() doc.load_from_db() - self.assertEquals(doc.total_cost, 300) + self.assertEqual(doc.total_cost, 300) frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 100) update_cost() doc.load_from_db() - self.assertEquals(doc.total_cost, 200) + self.assertEqual(doc.total_cost, 200) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 6b1fafe5f4c..cb1ee92196f 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -473,7 +473,7 @@ class TestWorkOrder(unittest.TestCase): def test_cost_center_for_manufacture(self): wo_order = make_wo_order_test_record() ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty) - self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC") + self.assertEqual(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC") def test_operation_time_with_batch_size(self): fg_item = "Test Batch Size Item For BOM" @@ -539,11 +539,11 @@ class TestWorkOrder(unittest.TestCase): ste_cancel_list.append(ste1) ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2)) - self.assertEquals(ste3.fg_completed_qty, 2) + self.assertEqual(ste3.fg_completed_qty, 2) expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4} for row in ste3.items: - self.assertEquals(row.qty, expected_qty.get(row.item_code)) + self.assertEqual(row.qty, expected_qty.get(row.item_code)) ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() @@ -577,7 +577,7 @@ class TestWorkOrder(unittest.TestCase): ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2)) for ste_row in ste3.items: if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: - self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) + self.assertEqual(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) ste3.submit() ste_cancel_list.append(ste3) @@ -585,7 +585,7 @@ class TestWorkOrder(unittest.TestCase): ste2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2)) for ste_row in ste2.items: if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: - self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) + self.assertEqual(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2) ste_cancel_list.reverse() for ste_doc in ste_cancel_list: ste_doc.cancel() diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py index c6a534dac34..bbe9bf5228d 100644 --- a/erpnext/non_profit/doctype/donation/test_donation.py +++ b/erpnext/non_profit/doctype/donation/test_donation.py @@ -39,7 +39,7 @@ class TestDonation(unittest.TestCase): donation.on_payment_authorized() donation.reload() - self.assertEquals(donation.paid, 1) + self.assertEqual(donation.paid, 1) self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name})) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9ef949c2c73..82d223cada0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -774,4 +774,7 @@ erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 erpnext.patches.v13_0.update_shipment_status erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting +erpnext.patches.v12_0.add_ewaybill_validity_field +erpnext.patches.v13_0.germany_make_custom_fields +erpnext.patches.v13_0.germany_fill_debtor_creditor_number erpnext.patches.v13_0.set_pos_closing_as_failed diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py new file mode 100644 index 00000000000..87d98f1a563 --- /dev/null +++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py @@ -0,0 +1,16 @@ +from __future__ import unicode_literals +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + custom_fields = { + 'Sales Invoice': [ + dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1, + depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill') + ] + } + create_custom_fields(custom_fields, update=True) \ No newline at end of file diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index 06331d7ff78..a6471eb53cd 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -44,9 +44,11 @@ def execute(): # make current item's tax map item_tax_map = {} for d in old_item_taxes[item_code]: - item_tax_map[d.tax_type] = d.tax_rate + if d.tax_type not in item_tax_map: + item_tax_map[d.tax_type] = d.tax_rate - item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code) + tax_types = [] + item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code, tax_types=tax_types) # update the item tax table frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code) @@ -68,7 +70,7 @@ def execute(): and item_tax_template is NULL""".format(dt), as_dict=1): item_tax_map = json.loads(d.item_tax_rate) item_tax_template_name = get_item_tax_template(item_tax_templates, - item_tax_map, d.item_code, d.parenttype, d.parent) + item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types) frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name) frappe.db.auto_commit_on_many_writes = False @@ -78,7 +80,7 @@ def execute(): settings.determine_address_tax_category_from = "Billing Address" settings.save() -def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None): +def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None): # search for previously created item tax template by comparing tax maps for template, item_tax_template_map in iteritems(item_tax_templates): if item_tax_map == item_tax_template_map: @@ -126,7 +128,9 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp account_type = frappe.get_cached_value("Account", tax_type, "account_type") if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'): - item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) + if tax_type not in tax_types: + item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) + tax_types.append(tax_type) item_tax_templates.setdefault(item_tax_template.title, {}) item_tax_templates[item_tax_template.title][tax_type] = tax_rate if item_tax_template.get("taxes"): diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py new file mode 100644 index 00000000000..11e1e9b3b94 --- /dev/null +++ b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py @@ -0,0 +1,31 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + + +def execute(): + """Move account number into the new custom field debtor_creditor_number. + + German companies used to use a dedicated payable/receivable account for + every party to mimick party accounts in the external accounting software + "DATEV". This is no longer necessary. The reference ID for DATEV will be + stored in a new custom field "debtor_creditor_number". + """ + company_list = frappe.get_all('Company', filters={'country': 'Germany'}) + + for company in company_list: + party_account_list = frappe.get_all('Party Account', filters={'company': company.name}, fields=['name', 'account', 'debtor_creditor_number']) + for party_account in party_account_list: + if (not party_account.account) or party_account.debtor_creditor_number: + # account empty or debtor_creditor_number already filled + continue + + account_number = frappe.db.get_value('Account', party_account.account, 'account_number') + if not account_number: + continue + + frappe.db.set_value('Party Account', party_account.name, 'debtor_creditor_number', account_number) + frappe.db.set_value('Party Account', party_account.name, 'account', '') diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py new file mode 100644 index 00000000000..41ab945eb12 --- /dev/null +++ b/erpnext/patches/v13_0/germany_make_custom_fields.py @@ -0,0 +1,20 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from erpnext.regional.germany.setup import make_custom_fields + + +def execute(): + """Execute the make_custom_fields method for german companies. + + It is usually run once at setup of a new company. Since it's new, run it + once for existing companies as well. + """ + company_list = frappe.get_all('Company', filters = {'country': 'Germany'}) + if not company_list: + return + + make_custom_fields() diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index 7528bf7a7f8..b80b32061f3 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -15,7 +15,13 @@ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_ from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry, create_loan_type, create_loan_accounts from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans +test_dependencies = ['Holiday List'] + class TestPayrollEntry(unittest.TestCase): + @classmethod + def setUpClass(cls): + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", '_Test Holiday List') + def setUp(self): for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]: diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json index 50db0338c4a..37381fa9428 100644 --- a/erpnext/payroll/notification/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json @@ -1,5 +1,6 @@ { "attach_print": 0, + "channel": "Email", "condition": "doc.docstatus==1", "creation": "2018-05-15 18:52:36.362838", "date_changed": "bonus_payment_date", diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py index bf5c4025a0b..b717491a821 100644 --- a/erpnext/portal/doctype/homepage/test_homepage.py +++ b/erpnext/portal/doctype/homepage/test_homepage.py @@ -13,7 +13,7 @@ class TestHomepage(unittest.TestCase): set_request(method='GET', path='home') response = render() - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) self.assertTrue('