Merge branch 'develop' into esbuild

This commit is contained in:
Suraj Shetty 2021-05-18 14:48:28 +05:30 committed by GitHub
commit a83d2aacd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
155 changed files with 2039 additions and 1003 deletions

69
.github/workflows/patch.yml vendored Normal file
View File

@ -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

View File

@ -1,6 +1,6 @@
name: CI name: Server
on: [pull_request, workflow_dispatch, push] on: [pull_request, workflow_dispatch]
jobs: jobs:
test: test:
@ -10,15 +10,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: container: [1, 2, 3]
- 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
name: ${{ matrix.JOB_NAME }} name: Python Unit Tests
services: services:
mysql: mysql:
@ -36,7 +30,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.6 python-version: 3.7
- name: Add to Hosts - name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
@ -49,6 +43,7 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
${{ runner.os }}- ${{ runner.os }}-
- name: Cache node modules - name: Cache node modules
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
@ -60,6 +55,7 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}- ${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build- ${{ runner.os }}-build-
${{ runner.os }}- ${{ runner.os }}-
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
@ -76,33 +72,39 @@ jobs:
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
- name: Run Tests - 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: env:
TYPE: ${{ matrix.TYPE }} TYPE: server
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
- name: Coverage - Pull Request - name: Upload Coverage Data
if: matrix.TYPE == 'server' && github.event_name == 'pull_request'
run: | run: |
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
cd ${GITHUB_WORKSPACE} cd ${GITHUB_WORKSPACE}
pip install coveralls==2.2.0 pip3 install coverage==5.5
pip install coverage==4.5.4 pip3 install coveralls==3.0.1
coveralls --service=github coveralls
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} COVERALLS_FLAG_NAME: run-${{ matrix.container }}
COVERALLS_SERVICE_NAME: github COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }}
COVERALLS_PARALLEL: true
- name: Coverage - Push coveralls:
if: matrix.TYPE == 'server' && github.event_name == 'push' 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: | run: |
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
cd ${GITHUB_WORKSPACE} cd ${GITHUB_WORKSPACE}
pip install coveralls==2.2.0 pip3 install coverage==5.5
pip install coverage==4.5.4 pip3 install coveralls==3.0.1
coveralls --service=github-actions coveralls --finish
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
COVERALLS_SERVICE_NAME: github-actions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '13.2.1' __version__ = '13.3.0'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -7,7 +7,8 @@ import frappe
import unittest import unittest
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice 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.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): class TestAccountingDimension(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -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.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
test_dependencies = ['Location', 'Cost Center', 'Department']
class TestAccountingDimensionFilter(unittest.TestCase): class TestAccountingDimensionFilter(unittest.TestCase):
def setUp(self): def setUp(self):
create_dimension() create_dimension()

View File

@ -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.accounting_period.accounting_period import OverlapError
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
test_dependencies = ['Item']
class TestAccountingPeriod(unittest.TestCase): class TestAccountingPeriod(unittest.TestCase):
def test_overlap(self): def test_overlap(self):
ap1 = create_accounting_period(start_date = "2018-04-01", 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.start_date = args.start_date or nowdate()
accounting_period.end_date = args.end_date or add_months(nowdate(), 1) accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
accounting_period.company = args.company or "_Test Company" 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", { accounting_period.append("closed_documents", {
"document_type": 'Sales Invoice', "closed": 1 "document_type": 'Sales Invoice', "closed": 1
}) })

View File

@ -7,26 +7,30 @@
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"auto_accounting_for_stock", "accounts_transactions_settings_section",
"acc_frozen_upto",
"frozen_accounts_modifier",
"determine_address_tax_category_from",
"over_billing_allowance", "over_billing_allowance",
"role_allowed_to_over_bill", "role_allowed_to_over_bill",
"column_break_4",
"credit_controller",
"check_supplier_invoice_uniqueness",
"make_payment_via_journal_entry", "make_payment_via_journal_entry",
"column_break_11",
"check_supplier_invoice_uniqueness",
"unlink_payment_on_cancellation_of_invoice", "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", "automatically_fetch_payment_terms",
"delete_linked_ledger_entries", "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", "deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on", "book_deferred_entries_based_on",
"column_break_18", "column_break_18",
"automatically_process_deferred_accounting_entry",
"book_deferred_entries_via_journal_entry", "book_deferred_entries_via_journal_entry",
"submit_journal_entries", "submit_journal_entries",
"print_settings", "print_settings",
@ -40,15 +44,6 @@
"use_custom_cash_flow" "use_custom_cash_flow"
], ],
"fields": [ "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", "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", "fieldname": "acc_frozen_upto",
@ -94,6 +89,7 @@
"default": "0", "default": "0",
"fieldname": "make_payment_via_journal_entry", "fieldname": "make_payment_via_journal_entry",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1,
"label": "Make Payment via Journal Entry" "label": "Make Payment via Journal Entry"
}, },
{ {
@ -234,6 +230,29 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Role Allowed to Over Bill ", "label": "Role Allowed to Over Bill ",
"options": "Role" "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", "icon": "icon-cog",
@ -241,7 +260,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2021-03-11 18:52:05.601996", "modified": "2021-04-30 15:25:10.381008",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

@ -239,6 +239,7 @@ frappe.ui.form.on("Bank Statement Import", {
"withdrawal", "withdrawal",
"description", "description",
"reference_number", "reference_number",
"bank_account"
], ],
}, },
}); });

View File

@ -146,7 +146,7 @@
}, },
{ {
"depends_on": "eval:!doc.__islocal && !doc.import_file\n", "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", "fieldname": "google_sheets_url",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Import from Google Sheets" "label": "Import from Google Sheets"
@ -202,7 +202,7 @@
], ],
"hide_toolbar": 1, "hide_toolbar": 1,
"links": [], "links": [],
"modified": "2021-02-10 19:29:59.027325", "modified": "2021-05-12 14:17:37.777246",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Statement Import", "name": "Bank Statement Import",

View File

@ -47,6 +47,13 @@ class BankStatementImport(DataImport):
def start_import(self): 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.core.page.background_jobs.background_jobs import get_info
from frappe.utils.scheduler import is_scheduler_inactive from frappe.utils.scheduler import is_scheduler_inactive
@ -67,6 +74,7 @@ class BankStatementImport(DataImport):
data_import=self.name, data_import=self.name,
bank_account=self.bank_account, bank_account=self.bank_account,
import_file_path=self.import_file, import_file_path=self.import_file,
google_sheets_url=self.google_sheets_url,
bank=self.bank, bank=self.bank,
template_options=self.template_options, template_options=self.template_options,
now=frappe.conf.developer_mode or frappe.flags.in_test, now=frappe.conf.developer_mode or frappe.flags.in_test,
@ -90,16 +98,18 @@ def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name) data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows() 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""" """This method runs in background job"""
update_mapping_db(bank, template_options) update_mapping_db(bank, template_options)
data_import = frappe.get_doc("Bank Statement Import", data_import) 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 data = import_file.raw_data
if import_file_path:
add_bank_account(data, bank_account) add_bank_account(data, bank_account)
write_files(import_file, data) write_files(import_file, data)

View File

@ -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.budget.budget import get_actual_expense, BudgetError
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
test_dependencies = ['Monthly Distribution']
class TestBudget(unittest.TestCase): class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self): def test_monthly_budget_crossed_ignore(self):
set_total_expense_zero(nowdate(), "cost_center") set_total_expense_zero(nowdate(), "cost_center")

View File

@ -42,9 +42,9 @@ class TestDunning(unittest.TestCase):
['Sales - _TC', 0.0, 20.44] ['Sales - _TC', 0.0, 20.44]
]) ])
for gle in gl_entries: for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEquals(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEquals(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_payment_entry(self): def test_payment_entry(self):
dunning = create_dunning() dunning = create_dunning()

View File

@ -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))) 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] 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)

View File

@ -1,87 +1,39 @@
{ {
"allow_copy": 0, "actions": [],
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2014-08-29 16:02:39.740505", "creation": "2014-08-29 16:02:39.740505",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "",
"editable_grid": 1, "editable_grid": 1,
"field_order": [
"company",
"account"
],
"fields": [ "fields": [
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Company", "label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company", "options": "Company",
"permlevel": 0, "reqd": 1
"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
}, },
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "account", "fieldname": "account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Account", "label": "Account",
"length": 0, "options": "Account"
"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
} }
], ],
"hide_heading": 0, "index_web_pages_for_search": 1,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "links": [],
"modified": "2016-07-11 03:28:03.348246", "modified": "2021-04-07 18:13:08.833822",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Party Account", "name": "Party Account",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC"
"track_seen": 0
} }

View File

@ -31,10 +31,10 @@ class TestPaymentOrder(unittest.TestCase):
doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry") doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
reference_doc = doc.get("references")[0] reference_doc = doc.get("references")[0]
self.assertEquals(reference_doc.reference_name, payment_entry.name) self.assertEqual(reference_doc.reference_name, payment_entry.name)
self.assertEquals(reference_doc.reference_doctype, "Payment Entry") self.assertEqual(reference_doc.reference_doctype, "Payment Entry")
self.assertEquals(reference_doc.supplier, "_Test Supplier") self.assertEqual(reference_doc.supplier, "_Test Supplier")
self.assertEquals(reference_doc.amount, 250) self.assertEqual(reference_doc.amount, 250)
def create_payment_order_against_payment_entry(ref_doc, order_type): def create_payment_order_against_payment_entry(ref_doc, order_type):
payment_order = frappe.get_doc(dict( payment_order = frappe.get_doc(dict(

View File

@ -114,7 +114,7 @@ class PaymentReconciliation(Document):
'party_type': self.party_type, 'party_type': self.party_type,
'voucher_type': voucher_type, 'voucher_type': voucher_type,
'account': self.receivable_payable_account 'account': self.receivable_payable_account
}, as_dict=1, debug=1) }, as_dict=1)
def add_payment_entries(self, entries): def add_payment_entries(self, entries):
self.set('payments', []) self.set('payments', [])

View File

@ -461,7 +461,17 @@ def get_stock_availability(item_code, warehouse):
order by posting_date desc, posting_time desc order by posting_date desc, posting_time desc
limit 1""", (item_code, warehouse), as_dict=1) 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 from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
where p.name = p_item.parent where p.name = p_item.parent
and p.consolidated_invoice is NULL and p.consolidated_invoice is NULL
@ -471,13 +481,7 @@ def get_stock_availability(item_code, warehouse):
and p_item.warehouse = %s and p_item.warehouse = %s
""", (item_code, warehouse), as_dict=1) """, (item_code, warehouse), as_dict=1)
sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0 return reserved_qty[0].qty or 0 if reserved_qty 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
@frappe.whitelist() @frappe.whitelist()
def make_sales_return(source_name, target_doc=None): def make_sales_return(source_name, target_doc=None):

View File

@ -152,7 +152,7 @@ class PricingRule(Document):
frappe.throw(_("Valid from date must be less than valid upto date")) frappe.throw(_("Valid from date must be less than valid upto date"))
def validate_condition(self): 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")) frappe.throw(_("Invalid condition expression"))
#-------------------------------------------------------------------------------- #--------------------------------------------------------------------------------

View File

@ -99,7 +99,7 @@ class TestPricingRule(unittest.TestCase):
args.item_code = "_Test Item 2" args.item_code = "_Test Item 2"
details = get_item_details(args) 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): def test_pricing_rule_for_margin(self):
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
@ -145,8 +145,8 @@ class TestPricingRule(unittest.TestCase):
"name": None "name": None
}) })
details = get_item_details(args) details = get_item_details(args)
self.assertEquals(details.get("margin_type"), "Percentage") self.assertEqual(details.get("margin_type"), "Percentage")
self.assertEquals(details.get("margin_rate_or_amount"), 10) self.assertEqual(details.get("margin_rate_or_amount"), 10)
def test_mixed_conditions_for_item_group(self): def test_mixed_conditions_for_item_group(self):
for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]: for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]:
@ -192,7 +192,7 @@ class TestPricingRule(unittest.TestCase):
"name": None "name": None
}) })
details = get_item_details(args) 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): def test_pricing_rule_for_variants(self):
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
@ -322,11 +322,11 @@ class TestPricingRule(unittest.TestCase):
si.insert(ignore_permissions=True) si.insert(ignore_permissions=True)
item = si.items[0] item = si.items[0]
self.assertEquals(item.margin_rate_or_amount, 10) self.assertEqual(item.margin_rate_or_amount, 10)
self.assertEquals(item.rate_with_margin, 1100) self.assertEqual(item.rate_with_margin, 1100)
self.assertEqual(item.discount_percentage, 10) self.assertEqual(item.discount_percentage, 10)
self.assertEquals(item.discount_amount, 110) self.assertEqual(item.discount_amount, 110)
self.assertEquals(item.rate, 990) self.assertEqual(item.rate, 990)
def test_pricing_rule_with_margin_and_discount_amount(self): def test_pricing_rule_with_margin_and_discount_amount(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
@ -338,10 +338,10 @@ class TestPricingRule(unittest.TestCase):
si.insert(ignore_permissions=True) si.insert(ignore_permissions=True)
item = si.items[0] item = si.items[0]
self.assertEquals(item.margin_rate_or_amount, 10) self.assertEqual(item.margin_rate_or_amount, 10)
self.assertEquals(item.rate_with_margin, 1100) self.assertEqual(item.rate_with_margin, 1100)
self.assertEquals(item.discount_amount, 110) self.assertEqual(item.discount_amount, 110)
self.assertEquals(item.rate, 990) self.assertEqual(item.rate, 990)
def test_pricing_rule_for_product_discount_on_same_item(self): def test_pricing_rule_for_product_discount_on_same_item(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') 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.items[0].price_list_rate = 1000
si.submit() si.submit()
item = si.items[0] item = si.items[0]
self.assertEquals(item.rate, 100) self.assertEqual(item.rate, 100)
# Correct Customer and Incorrect is_return value # 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 = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
si.items[0].price_list_rate = 1000 si.items[0].price_list_rate = 1000
si.submit() si.submit()
item = si.items[0] item = si.items[0]
self.assertEquals(item.rate, 100) self.assertEqual(item.rate, 100)
# Correct Customer and correct is_return value # Correct Customer and correct is_return value
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0) si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
si.items[0].price_list_rate = 1000 si.items[0].price_list_rate = 1000
si.submit() si.submit()
item = si.items[0] item = si.items[0]
self.assertEquals(item.rate, 900) self.assertEqual(item.rate, 900)
def test_multiple_pricing_rules(self): def test_multiple_pricing_rules(self):
make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, 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) 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) si = create_sales_invoice(qty=5, do_not_submit=True)
self.assertEquals(len(si.items), 2) self.assertEqual(len(si.items), 2)
self.assertEquals(si.items[1].rate, 10) self.assertEqual(si.items[1].rate, 10)
si1 = create_sales_invoice(qty=2, do_not_submit=True) 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]: for doc in [si, si1]:
doc.delete() doc.delete()

View File

@ -1380,7 +1380,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-03-30 22:45:58.334107", "modified": "2021-04-30 22:45:58.334107",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -636,8 +636,8 @@ class TestPurchaseInvoice(unittest.TestCase):
def test_rejected_serial_no(self): def test_rejected_serial_no(self):
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1, 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_qty=1, rate=500, update_stock=1, rejected_warehouse = "_Test Rejected Warehouse - _TC",
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"), self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
pi.get("items")[0].warehouse) pi.get("items")[0].warehouse)
@ -994,7 +994,8 @@ def make_purchase_invoice(**args):
"project": args.project, "project": args.project,
"rejected_warehouse": args.rejected_warehouse or "", "rejected_warehouse": args.rejected_warehouse or "",
"rejected_serial_no": args.rejected_serial_no 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: if args.get_taxes_and_charges:

View File

@ -356,11 +356,11 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
} }
items_on_form_rendered() { items_on_form_rendered() {
erpnext.setup_serial_no(); erpnext.setup_serial_or_batch_no();
} }
packed_items_on_form_rendered(doc, grid_row) { packed_items_on_form_rendered(doc, grid_row) {
erpnext.setup_serial_no(); erpnext.setup_serial_or_batch_no();
} }
make_sales_return() { make_sales_return() {

View File

@ -1111,7 +1111,7 @@ class SalesInvoice(SellingController):
if not item.serial_no: if not item.serial_no:
continue 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: 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) 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)) item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
def get_delivery_note_details(internal_reference): def get_delivery_note_details(internal_reference):
so_item_map = {}
si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'], si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
filters={'parent': internal_reference}) filters={'parent': internal_reference})
for d in si_item_details: return {d.name: d.so_detail for d in si_item_details if d.so_detail}
so_item_map.setdefault(d.name, d.so_detail)
return so_item_map
def get_sales_invoice_details(internal_reference): def get_sales_invoice_details(internal_reference):
dn_item_map = {} dn_item_map = {}

View File

@ -22,6 +22,9 @@ def get_party_details(inv):
party_type = 'Supplier' party_type = 'Supplier'
party = inv.supplier party = inv.supplier
if not party:
frappe.throw(_("Please select {0} first").format(party_type))
return party_type, party return party_type, party
def get_party_tax_withholding_details(inv, tax_withholding_category=None): def get_party_tax_withholding_details(inv, tax_withholding_category=None):

View File

@ -1,5 +1,6 @@
{ {
"attach_print": 0, "attach_print": 0,
"channel": "Email",
"condition": "doc.auto_created", "condition": "doc.auto_created",
"creation": "2018-04-25 14:19:05.440361", "creation": "2018-04-25 14:19:05.440361",
"days_in_advance": 0, "days_in_advance": 0,

View File

@ -364,7 +364,7 @@ class ReceivablePayableReport(object):
payment_terms_details = frappe.db.sql(""" payment_terms_details = frappe.db.sql("""
select select
si.name, si.party_account_currency, si.currency, si.conversion_rate, 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 from `tab{0}` si, `tabPayment Schedule` ps
where where
si.name = ps.parent and si.name = ps.parent and
@ -394,7 +394,7 @@ class ReceivablePayableReport(object):
"due_date": d.due_date, "due_date": d.due_date,
"invoiced": invoiced, "invoiced": invoiced,
"invoice_grand_total": row.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, "paid": d.paid_amount + d.discounted_amount,
"credit_note": 0.0, "credit_note": 0.0,
"outstanding": invoiced - d.paid_amount - d.discounted_amount "outstanding": invoiced - d.paid_amount - d.discounted_amount

View File

@ -5,7 +5,8 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt, cint 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): def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@ -132,6 +133,10 @@ def get_report_summary(period_list, asset, liability, equity, provisional_profit
if filters.get('accumulated_values'): if filters.get('accumulated_values'):
period_list = [period_list[-1]] 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: for period in period_list:
key = period if consolidated else period.key key = period if consolidated else period.key
if asset: if asset:

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cint, cstr 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.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from six import iteritems from six import iteritems
@ -67,9 +67,9 @@ def execute(filters=None):
section_data.append(account_data) section_data.append(account_data)
add_total_row_account(data, section_data, cash_flow_account['section_footer'], add_total_row_account(data, section_data, cash_flow_account['section_footer'],
period_list, company_currency, 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) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
chart = get_chart_data(columns, data) chart = get_chart_data(columns, data)
@ -162,18 +162,26 @@ def get_start_date(period, accumulated_values, company):
return start_date 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 = { total_row = {
"account_name": "'" + _("{0}").format(label) + "'", "account_name": "'" + _("{0}").format(label) + "'",
"account": "'" + _("{0}").format(label) + "'", "account": "'" + _("{0}").format(label) + "'",
"currency": currency "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: for row in data:
if row.get("parent_account"): if row.get("parent_account"):
for period in period_list: for period in period_list:
key = period if consolidated else period['key'] key = period if consolidated else period['key']
total_row.setdefault(key, 0.0) total_row.setdefault(key, 0.0)
total_row[key] += row.get(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.setdefault("total", 0.0)
total_row["total"] += row["total"] 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(total_row)
out.append({}) out.append({})
summary_data[label] = total_row["total"]
def get_report_summary(summary_data, currency): def get_report_summary(summary_data, currency):
report_summary = [] report_summary = []

View File

@ -2,7 +2,8 @@
// For license information, please see license.txt // For license information, please see license.txt
/* eslint-disable */ /* eslint-disable */
frappe.query_reports["Consolidated Financial Statement"] = { frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Consolidated Financial Statement"] = {
"filters": [ "filters": [
{ {
"fieldname":"company", "fieldname":"company",
@ -94,6 +95,14 @@ frappe.query_reports["Consolidated Financial Statement"] = {
} }
], ],
"formatter": function(value, row, column, data, default_formatter) { "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;
}
value = default_formatter(value, row, column, data); value = default_formatter(value, row, column, data);
if (!data.parent_account) { if (!data.parent_account) {
@ -116,4 +125,5 @@ frappe.query_reports["Consolidated Financial Statement"] = {
}); });
}); });
} }
} }
});

View File

@ -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) 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 return data, None, chart, report_summary
@ -149,9 +149,9 @@ def get_cash_flow_data(fiscal_year, companies, filters):
section_data.append(account_data) section_data.append(account_data)
add_total_row_account(data, section_data, cash_flow_account['section_footer'], add_total_row_account(data, section_data, cash_flow_account['section_footer'],
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) 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 has_value = False
total = 0 total = 0
row = frappe._dict({ row = frappe._dict({
"account_name": _(d.account_name), "account_name": ('%s - %s' %(_(d.account_number), _(d.account_name))
"account": _(d.account_name), if d.account_number else _(d.account_name)),
"account": _(d.name),
"parent_account": _(d.parent_account), "parent_account": _(d.parent_account),
"indent": flt(d.indent), "indent": flt(d.indent),
"year_start_date": start_date, "year_start_date": start_date,

View File

@ -119,10 +119,10 @@ def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year):
def validate_dates(from_date, to_date): def validate_dates(from_date, to_date):
if not from_date or not 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: 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): def get_months(start_date, end_date):
diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month) diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
@ -523,3 +523,11 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None):
}) })
return columns 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

View File

@ -116,22 +116,19 @@ def validate_filters(filters):
frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method")) frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
def get_conditions(filters): def get_conditions(filters):
conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format( conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
company=filters.get("company"),
from_date=filters.get("from_date"),
to_date=filters.get("to_date"))
if filters.get("pos_profile"): 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"): if filters.get("owner"):
conditions += " AND owner = %(owner)s".format(owner=filters.get("owner")) conditions += " AND owner = %(owner)s"
if filters.get("customer"): if filters.get("customer"):
conditions += " AND customer = %(customer)s".format(customer=filters.get("customer")) conditions += " AND customer = %(customer)s"
if filters.get("is_return"): 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"): if filters.get("mode_of_payment"):
conditions += """ conditions += """

View File

@ -5,7 +5,8 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt 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): def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@ -33,13 +34,17 @@ def execute(filters=None):
chart = get_chart_data(filters, columns, income, expense, net_profit_loss) 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") 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 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 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: for period in period_list:
key = period if consolidated else period.key key = period if consolidated else period.key
if income: if income:

View File

@ -15,6 +15,7 @@
"hide_custom": 0, "hide_custom": 0,
"icon": "accounting", "icon": "accounting",
"idx": 0, "idx": 0,
"is_default": 0,
"is_standard": 1, "is_standard": 1,
"label": "Accounting", "label": "Accounting",
"links": [ "links": [
@ -625,9 +626,9 @@
"dependencies": "", "dependencies": "",
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Bank Reconciliation", "label": "Bank Reconciliation Tool",
"link_to": "bank-reconciliation", "link_to": "Bank Reconciliation Tool",
"link_type": "Page", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
@ -641,26 +642,6 @@
"onboard": 0, "onboard": 0,
"type": "Link" "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, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
@ -1071,7 +1052,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-03-04 00:38:35.349024", "modified": "2021-05-12 11:48:01.905144",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting", "name": "Accounting",

View File

@ -195,8 +195,7 @@ class Asset(AccountsController):
# If depreciation is already completed (for double declining balance) # If depreciation is already completed (for double declining balance)
if skip_row: continue if skip_row: continue
depreciation_amount = self.get_depreciation_amount(value_after_depreciation, depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
d.total_number_of_depreciations, d)
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
schedule_date = add_months(d.depreciation_start_date, schedule_date = add_months(d.depreciation_start_date,
@ -208,7 +207,7 @@ class Asset(AccountsController):
# For first row # For first row
if has_pro_rata and n==0: 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) self.available_for_use_date, d.depreciation_start_date)
# For first depr schedule date will be the 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, to_date = add_months(self.available_for_use_date,
n * cint(d.frequency_of_depreciation)) 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) depreciation_amount, schedule_date, to_date)
monthly_schedule_date = add_months(schedule_date, 1) monthly_schedule_date = add_months(schedule_date, 1)
@ -365,24 +364,6 @@ class Asset(AccountsController):
def get_value_after_depreciation(self, idx): def get_value_after_depreciation(self, idx):
return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation) 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): def validate_expected_value_after_useful_life(self):
for row in self.get('finance_books'): for row in self.get('finance_books'):
accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
@ -575,6 +556,13 @@ class Asset(AccountsController):
return 100 * (1 - flt(depreciation_rate, float_precision)) 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(): def update_maintenance_status():
assets = frappe.get_all( assets = frappe.get_all(
"Asset", filters={"docstatus": 1, "maintenance_required": 1} "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): def is_cwip_accounting_enabled(asset_category):
return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting")) 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): def get_total_days(date, frequency):
period_start_date = add_months(date, period_start_date = add_months(date,
cint(frequency) * -1) cint(frequency) * -1)
return date_diff(date, period_start_date) 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

View File

@ -78,7 +78,7 @@ class TestAsset(unittest.TestCase):
}) })
doc.set_missing_values() 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): def test_schedule_for_straight_line_method(self):
pr = make_purchase_receipt(item_code="Macbook Pro", pr = make_purchase_receipt(item_code="Macbook Pro",
@ -565,7 +565,7 @@ class TestAsset(unittest.TestCase):
doc = make_invoice(pr.name) 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): def test_asset_cwip_toggling_cases(self):
cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting") cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
@ -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.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) 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(): def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"): if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category() create_asset_category()

View File

@ -187,7 +187,7 @@ class TestPurchaseOrder(unittest.TestCase):
update_child_qty_rate('Purchase Order', trans_item, po.name) update_child_qty_rate('Purchase Order', trans_item, po.name)
po.reload() po.reload()
self.assertEquals(len(po.get('items')), 2) self.assertEqual(len(po.get('items')), 2)
self.assertEqual(po.status, 'To Receive and Bill') self.assertEqual(po.status, 'To Receive and Bill')
# ordered qty should increase on row addition # ordered qty should increase on row addition
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7) 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) update_child_qty_rate('Purchase Order', trans_item, po.name)
po.reload() po.reload()
self.assertEquals(len(po.get('items')), 1) self.assertEqual(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill') self.assertEqual(po.status, 'To Receive and Bill')
# ordered qty should decrease (back to initial) on row deletion # ordered qty should decrease (back to initial) on row deletion
@ -448,13 +448,13 @@ class TestPurchaseOrder(unittest.TestCase):
pi.load_from_db() pi.load_from_db()
self.assertEquals(pi.per_received, 100.00) self.assertEqual(pi.per_received, 100.00)
self.assertEquals(pi.items[0].qty, pi.items[0].received_qty) self.assertEqual(pi.items[0].qty, pi.items[0].received_qty)
po.load_from_db() po.load_from_db()
self.assertEquals(po.per_received, 100.00) self.assertEqual(po.per_received, 100.00)
self.assertEquals(po.per_billed, 100.00) self.assertEqual(po.per_billed, 100.00)
pr.cancel() pr.cancel()
@ -674,8 +674,8 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1) 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.assertEqual(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.projected_qty, bin1.projected_qty - 10)
# Create stock transfer # Create stock transfer
rm_item = [{"item_code":"_Test FG Item","rm_item_code":"_Test Item","item_name":"_Test Item", 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"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # close PO
po.update_status("Closed") po.update_status("Closed")
@ -698,7 +698,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Re-open PO
po.update_status("Submitted") po.update_status("Submitted")
@ -706,7 +706,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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", make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
qty=40, basic_rate=100) qty=40, basic_rate=100)
@ -723,7 +723,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Cancel PR
pr.cancel() pr.cancel()
@ -731,7 +731,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Make Purchase Invoice
pi = make_pi_from_po(po.name) pi = make_pi_from_po(po.name)
@ -743,7 +743,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Cancel PR
pi.cancel() pi.cancel()
@ -751,7 +751,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Cancel Stock Entry
se.cancel() se.cancel()
@ -759,7 +759,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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 # Cancel PO
po.reload() po.reload()
@ -768,7 +768,7 @@ class TestPurchaseOrder(unittest.TestCase):
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1) 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): def test_exploded_items_in_subcontracted(self):
item_code = "_Test Subcontracted FG Item 1" 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')]) 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]) 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, po1 = create_purchase_order(item_code=item_code, qty=1,
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0) 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]) 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')]) 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): def test_backflush_based_on_stock_entry(self):
item_code = "_Test Subcontracted FG Item 1" 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]) 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')]) issued_items = sorted([d.rm_item_code for d in pr.get('supplied_items')])
self.assertEquals(transferred_items, issued_items) self.assertEqual(transferred_items, issued_items)
self.assertEquals(pr.get('items')[0].rm_supp_cost, 2000) self.assertEqual(pr.get('items')[0].rm_supp_cost, 2000)
transferred_rm_map = frappe._dict() transferred_rm_map = frappe._dict()

View File

@ -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 from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute
import json, frappe, unittest 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') 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', target='_Test Warehouse - _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 Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
transfer_subcontracted_raw_materials(po.name) transfer_subcontracted_raw_materials(po.name)
col, data = execute(filters=frappe._dict({'supplier': po.supplier, 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)), 'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
@ -38,6 +38,7 @@ def transfer_subcontracted_raw_materials(po):
'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}] 'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}]
rm_item_string = json.dumps(rm_item) rm_item_string = json.dumps(rm_item)
se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string)) 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.to_warehouse = '_Test Warehouse 1 - _TC'
se.stock_entry_type = 'Send to Subcontractor' se.stock_entry_type = 'Send to Subcontractor'
se.save() se.save()

View File

@ -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))

View File

@ -1006,7 +1006,6 @@ class AccountsController(TransactionBase):
else: else:
grand_total -= self.get("total_advance") grand_total -= self.get("total_advance")
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) 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 \ if total != flt(grand_total, self.precision("grand_total")) or \
base_total != flt(base_grand_total, self.precision("base_grand_total")): 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")) frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))

View File

@ -838,9 +838,10 @@ class BuyingController(StockController):
if not self.get("items"): if not self.get("items"):
return return
earliest_schedule_date = min([d.schedule_date for d in self.get("items")]) if any(d.schedule_date for d in self.get("items")):
if earliest_schedule_date: # Select earliest schedule_date.
self.schedule_date = 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: if self.schedule_date:
for d in self.get('items'): for d in self.get('items'):

View File

@ -292,11 +292,14 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
cond = """(`tabProject`.customer = %s or cond = """(`tabProject`.customer = %s or
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer"))) 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` return frappe.db.sql("""select {fields} from `tabProject`
where `tabProject`.status not in ("Completed", "Cancelled") where
and {cond} `tabProject`.name like %(txt)s {match_cond} `tabProject`.status not in ("Completed", "Cancelled")
and {cond} {match_cond} {scond}
order by order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
idx desc, idx desc,
@ -304,6 +307,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
limit {start}, {page_len}""".format( limit {start}, {page_len}""".format(
fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]), fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
cond=cond, cond=cond,
scond=searchfields,
match_cond=get_match_cond(doctype), match_cond=get_match_cond(doctype),
start=start, start=start,
page_len=page_len), { page_len=page_len), {

View File

@ -100,6 +100,10 @@ status_map = {
["Queued", "eval:self.status == 'Queued'"], ["Queued", "eval:self.status == 'Queued'"],
["Failed", "eval:self.status == 'Failed'"], ["Failed", "eval:self.status == 'Failed'"],
["Cancelled", "eval:self.docstatus == 2"], ["Cancelled", "eval:self.docstatus == 2"],
],
"Transaction Deletion Record": [
["Draft", None],
["Completed", "eval:self.docstatus == 1"],
] ]
} }

View File

@ -379,8 +379,7 @@ class StockController(AccountsController):
link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection) 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) 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_doc.status != 'Accepted':
if qa_failed:
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}") frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
.format(d.idx, d.item_code), QualityInspectionRejectedError) .format(d.idx, d.item_code), QualityInspectionRejectedError)
elif qa_required : elif qa_required :

View File

@ -9,8 +9,7 @@ from frappe.utils import nowdate
from frappe.utils.make_random import get_random from frappe.utils.make_random import get_random
from erpnext.education.doctype.program.test_program import make_program_and_linked_courses 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): class TestFees(unittest.TestCase):
def test_fees(self): def test_fees(self):

View File

@ -335,13 +335,13 @@ def get_url(shopify_settings):
if not last_order_id: if not last_order_id:
if shopify_settings.sync_based_on == 'Date': 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) get_datetime(shopify_settings.from_date)), shopify_settings)
else: 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) shopify_settings.from_order_id), shopify_settings)
else: 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 return url

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, base64, hashlib, hmac, json import frappe, base64, hashlib, hmac, json
from frappe.utils import cstr
from frappe import _ from frappe import _
def verify_request(): def verify_request():
@ -146,20 +147,17 @@ def rename_address(address, customer):
def link_items(items_list, woocommerce_settings, sys_lang): def link_items(items_list, woocommerce_settings, sys_lang):
for item_data in items_list: 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}): if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, 'name'):
#Edit Item
item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id})
else:
#Create Item #Create Item
item = frappe.new_doc("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_name = item_data.get("name")
item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_woo_com_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.flags.ignore_mandatory = True
item.save() item.save()
@ -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"): for item in order.get("line_items"):
woocomm_item_id = item.get("product_id") 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") ordered_items_tax = item.get("total_tax")
new_sales_order.append("items",{ new_sales_order.append("items", {
"item_code": found_item.item_code, "item_code": found_item.name,
"item_name": found_item.item_name, "item_name": found_item.item_name,
"description": found_item.item_name, "description": found_item.item_name,
"delivery_date": new_sales_order.delivery_date, "delivery_date": new_sales_order.delivery_date,

View File

@ -19,7 +19,7 @@ class TestMpesaSettings(unittest.TestCase):
mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test") 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(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
self.assertTrue(mode_of_payment.name) 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): def test_processing_of_account_balance(self):
mpesa_doc = create_mpesa_settings(payment_gateway_name="_Account Balance") 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 # test integration request creation and successful update of the status on receiving callback response
self.assertTrue(integration_request) 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 # test formatting of account balance received as string to json with appropriate currency symbol
mpesa_doc.reload() mpesa_doc.reload()
self.assertEquals(mpesa_doc.account_balance, dumps({ self.assertEqual(mpesa_doc.account_balance, dumps({
"Working Account": { "Working Account": {
"current_balance": "Sh 481,000.00", "current_balance": "Sh 481,000.00",
"available_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() pr = pos_invoice.create_payment_request()
# test payment request creation # 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 # submitting payment request creates integration requests with random id
integration_req_ids = frappe.get_all("Integration Request", filters={ integration_req_ids = frappe.get_all("Integration Request", filters={
@ -75,12 +75,12 @@ class TestMpesaSettings(unittest.TestCase):
# test integration request creation and successful update of the status on receiving callback response # test integration request creation and successful update of the status on receiving callback response
self.assertTrue(integration_request) self.assertTrue(integration_request)
self.assertEquals(integration_request.status, "Completed") self.assertEqual(integration_request.status, "Completed")
pos_invoice.reload() pos_invoice.reload()
integration_request.reload() integration_request.reload()
self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R") self.assertEqual(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
self.assertEquals(integration_request.status, "Completed") self.assertEqual(integration_request.status, "Completed")
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "") frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
integration_request.delete() integration_request.delete()
@ -104,7 +104,7 @@ class TestMpesaSettings(unittest.TestCase):
pr = pos_invoice.create_payment_request() pr = pos_invoice.create_payment_request()
# test payment request creation # 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 # submitting payment request creates integration requests with random id
integration_req_ids = frappe.get_all("Integration Request", filters={ integration_req_ids = frappe.get_all("Integration Request", filters={
@ -126,12 +126,12 @@ class TestMpesaSettings(unittest.TestCase):
verify_transaction(**callback_response) verify_transaction(**callback_response)
# test completion of integration request # test completion of integration request
integration_request = frappe.get_doc("Integration Request", integration_req_ids[i]) 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) integration_requests.append(integration_request)
# check receipt number once all the integration requests are completed # check receipt number once all the integration requests are completed
pos_invoice.reload() 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", "") frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
[d.delete() for d in integration_requests] [d.delete() for d in integration_requests]
@ -155,7 +155,7 @@ class TestMpesaSettings(unittest.TestCase):
pr = pos_invoice.create_payment_request() pr = pos_invoice.create_payment_request()
# test payment request creation # 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 # submitting payment request creates integration requests with random id
integration_req_ids = frappe.get_all("Integration Request", filters={ integration_req_ids = frappe.get_all("Integration Request", filters={
@ -175,7 +175,7 @@ class TestMpesaSettings(unittest.TestCase):
verify_transaction(**callback_response) verify_transaction(**callback_response)
# test completion of integration request # test completion of integration request
integration_request = frappe.get_doc("Integration Request", integration_req_ids[0]) 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 # now one request is completed
# second integration request fails # second integration request fails
@ -187,7 +187,7 @@ class TestMpesaSettings(unittest.TestCase):
'name': ['not in', integration_req_ids] 'name': ['not in', integration_req_ids]
}, pluck="name") }, 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.set_value("Customer", "_Test Customer", "default_currency", "")
frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'") frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")

View File

@ -30,14 +30,14 @@ class ShopifySettings(Document):
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
# url = get_shopify_url('admin/webhooks.json', self) # url = get_shopify_url('admin/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks] 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: for method in webhooks:
session = get_request_session() session = get_request_session()
try: try:
res = session.post(url, data=json.dumps({ res = session.post(url, data=json.dumps({
"webhook": { "webhook": {
"topic": method, "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" "format": "json"
} }
}), headers=get_header(self)) }), headers=get_header(self))
@ -56,7 +56,7 @@ class ShopifySettings(Document):
deleted_webhooks = [] deleted_webhooks = []
for d in self.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: try:
res = session.delete(url, headers=get_header(self)) res = session.delete(url, headers=get_header(self))
res.raise_for_status() res.raise_for_status()

View File

@ -32,10 +32,12 @@ def create_customer(shopify_customer, shopify_settings):
raise e raise e
def create_customer_address(customer, shopify_customer): def create_customer_address(customer, shopify_customer):
if not shopify_customer.get("addresses"): addresses = shopify_customer.get("addresses", [])
return
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) address_title, address_type = get_address_title_and_type(customer.customer_name, i)
try : try :
frappe.get_doc({ frappe.get_doc({

View File

@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
shopify_variants_attr_list = ["option1", "option2", "option3"] shopify_variants_attr_list = ["option1", "option2", "option3"]
def sync_item_from_shopify(shopify_settings, item): 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() session = get_request_session()
try: try:

View File

@ -28,7 +28,7 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'):
return innerfn 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) endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method)
if exclude_uri: if exclude_uri:
@ -39,7 +39,11 @@ def get_webhook_address(connector_name, method, exclude_uri=False):
except RuntimeError: except RuntimeError:
url = "http://localhost:8000" 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 return server_url

View File

@ -17,7 +17,7 @@ class TestClinicalProcedure(unittest.TestCase):
procedure_template.disabled = 1 procedure_template.disabled = 1
procedure_template.save() 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): def test_consumables(self):
patient, medical_department, practitioner = create_healthcare_docs() patient, medical_department, practitioner = create_healthcare_docs()

View File

@ -18,7 +18,7 @@ class TestLabTest(unittest.TestCase):
lab_template.disabled = 1 lab_template.disabled = 1
lab_template.save() 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() lab_template.reload()
@ -57,7 +57,7 @@ class TestLabTest(unittest.TestCase):
# sample collection should not be created # sample collection should not be created
lab_test.reload() lab_test.reload()
self.assertEquals(lab_test.sample, None) self.assertEqual(lab_test.sample, None)
def test_create_lab_tests_from_sales_invoice(self): def test_create_lab_tests_from_sales_invoice(self):
sales_invoice = create_sales_invoice() sales_invoice = create_sales_invoice()

View File

@ -20,13 +20,13 @@ class TestPatientAppointment(unittest.TestCase):
patient, medical_department, practitioner = create_healthcare_docs() patient, medical_department, practitioner = create_healthcare_docs()
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0) frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
appointment = create_appointment(patient, practitioner, nowdate()) 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)) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
self.assertEquals(appointment.status, 'Scheduled') self.assertEqual(appointment.status, 'Scheduled')
encounter = create_encounter(appointment) 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() 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): def test_start_encounter(self):
patient, medical_department, practitioner = create_healthcare_docs() patient, medical_department, practitioner = create_healthcare_docs()

View File

@ -18,24 +18,24 @@ class TestTherapyPlan(unittest.TestCase):
def test_status(self): def test_status(self):
plan = create_therapy_plan() 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') session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
frappe.get_doc(session).submit() 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') session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
frappe.get_doc(session).submit() 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() 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 = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
session = frappe.get_doc(session) session = frappe.get_doc(session)
session.submit() 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() 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): def test_therapy_plan_from_template(self):
patient = create_patient() patient = create_patient()
@ -49,7 +49,7 @@ class TestTherapyPlan(unittest.TestCase):
si.save() si.save()
therapy_plan_template_amt = frappe.db.get_value('Therapy Plan Template', template, 'total_amount') 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): def create_therapy_plan(template=None):

View File

@ -13,7 +13,7 @@ class TestTherapyType(unittest.TestCase):
therapy_type.disabled = 1 therapy_type.disabled = 1
therapy_type.save() 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(): def create_therapy_type():
exercise = create_exercise_type() exercise = create_exercise_type()

View File

@ -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_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.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.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': { 'United Arab Emirates': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data', 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',

View File

@ -68,19 +68,19 @@ class TestCompensatoryLeaveRequest(unittest.TestCase):
filters = dict(transaction_name=compensatory_leave_request.leave_allocation) filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters) leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
self.assertEquals(len(leave_ledger_entry), 1) self.assertEqual(len(leave_ledger_entry), 1)
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
self.assertEquals(leave_ledger_entry[0].leaves, 1) self.assertEqual(leave_ledger_entry[0].leaves, 1)
# check reverse leave ledger entry on cancellation # check reverse leave ledger entry on cancellation
compensatory_leave_request.cancel() compensatory_leave_request.cancel()
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc') leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
self.assertEquals(len(leave_ledger_entry), 2) self.assertEqual(len(leave_ledger_entry), 2)
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
self.assertEquals(leave_ledger_entry[0].leaves, -1) self.assertEqual(leave_ledger_entry[0].leaves, -1)
def get_compensatory_leave_request(employee, leave_date=today()): def get_compensatory_leave_request(employee, leave_date=today()):
prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request', prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',

View File

@ -182,6 +182,10 @@
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"write": 1 "write": 1
},
{
"read": 1,
"role": "Sales User"
} }
], ],
"quick_entry": 1, "quick_entry": 1,

View File

@ -14,6 +14,7 @@
"column_break_5", "column_break_5",
"expense_approver", "expense_approver",
"approval_status", "approval_status",
"delivery_trip",
"is_paid", "is_paid",
"expense_details", "expense_details",
"expenses", "expenses",
@ -365,13 +366,20 @@
"label": "Total Taxes and Charges", "label": "Total Taxes and Charges",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
},
{
"depends_on": "eval: doc.delivery_trip",
"fieldname": "delivery_trip",
"fieldtype": "Link",
"label": "Delivery Trip",
"options": "Delivery Trip"
} }
], ],
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-09-18 17:26:09.703215", "modified": "2021-05-04 05:35:12.040199",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim", "name": "Expense Claim",

View File

@ -88,9 +88,9 @@ class TestExpenseClaim(unittest.TestCase):
]) ])
for gle in gl_entries: for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEquals(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEquals(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_rejected_expense_claim(self): def test_rejected_expense_claim(self):
payable_account = get_payable_account(company_name) payable_account = get_payable_account(company_name)
@ -104,11 +104,11 @@ class TestExpenseClaim(unittest.TestCase):
}) })
expense_claim.submit() expense_claim.submit()
self.assertEquals(expense_claim.status, 'Rejected') self.assertEqual(expense_claim.status, 'Rejected')
self.assertEquals(expense_claim.total_sanctioned_amount, 0.0) 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}) 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): def test_expense_approver_perms(self):
user = "test_approver_perm_emp@example.com" user = "test_approver_perm_emp@example.com"

View File

@ -23,7 +23,7 @@ class HolidayList(Document):
last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,]) last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,])
for i, d in enumerate(date_list): for i, d in enumerate(date_list):
ch = self.append('holidays', {}) ch = self.append('holidays', {})
ch.description = self.weekly_off ch.description = _(self.weekly_off)
ch.holiday_date = d ch.holiday_date = d
ch.weekly_off = 1 ch.weekly_off = 1
ch.idx = last_idx + i + 1 ch.idx = last_idx + i + 1

View File

@ -35,13 +35,13 @@ class TestJobOffer(unittest.TestCase):
job_offer = create_job_offer(job_applicant=job_applicant.name) job_offer = create_job_offer(job_applicant=job_applicant.name)
job_offer.submit() job_offer.submit()
job_applicant.reload() job_applicant.reload()
self.assertEquals(job_applicant.status, "Accepted") self.assertEqual(job_applicant.status, "Accepted")
# status update after rejection # status update after rejection
job_offer.status = "Rejected" job_offer.status = "Rejected"
job_offer.submit() job_offer.submit()
job_applicant.reload() job_applicant.reload()
self.assertEquals(job_applicant.status, "Rejected") self.assertEqual(job_applicant.status, "Rejected")
def create_job_offer(**args): def create_job_offer(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -96,7 +96,7 @@ class TestLeaveAllocation(unittest.TestCase):
carry_forward=1) carry_forward=1)
leave_allocation_1.submit() leave_allocation_1.submit()
self.assertEquals(leave_allocation_1.unused_leaves, 10) self.assertEqual(leave_allocation_1.unused_leaves, 10)
leave_allocation_1.cancel() leave_allocation_1.cancel()
@ -108,7 +108,7 @@ class TestLeaveAllocation(unittest.TestCase):
new_leaves_allocated=25) new_leaves_allocated=25)
leave_allocation_2.submit() 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): def test_carry_forward_leaves_expiry(self):
frappe.db.sql("delete from `tabLeave Allocation`") frappe.db.sql("delete from `tabLeave Allocation`")
@ -145,7 +145,7 @@ class TestLeaveAllocation(unittest.TestCase):
to_date=add_months(nowdate(), 12)) to_date=add_months(nowdate(), 12))
leave_allocation_1.submit() 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): def test_creation_of_leave_ledger_entry_on_submit(self):
frappe.db.sql("delete from `tabLeave Allocation`") 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)) 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.assertEqual(len(leave_ledger_entry), 1)
self.assertEquals(leave_ledger_entry[0].employee, leave_allocation.employee) self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) self.assertEqual(leave_ledger_entry[0].leave_type, leave_allocation.leave_type)
self.assertEquals(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) self.assertEqual(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated)
# check if leave ledger entry is deleted on cancellation # check if leave ledger entry is deleted on cancellation
leave_allocation.cancel() leave_allocation.cancel()

View File

@ -516,9 +516,9 @@ class TestLeaveApplication(unittest.TestCase):
leave_application.submit() leave_application.submit()
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name)) 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.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) self.assertEqual(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].leaves, leave_application.total_leave_days * -1)
# check if leave ledger entry is deleted on cancellation # check if leave ledger entry is deleted on cancellation
leave_application.cancel() 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)) leave_ledger_entry = frappe.get_all('Leave Ledger Entry', '*', filters=dict(transaction_name=leave_application.name))
self.assertEquals(len(leave_ledger_entry), 2) self.assertEqual(len(leave_ledger_entry), 2)
self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type)
self.assertEquals(leave_ledger_entry[0].leaves, -9) self.assertEqual(leave_ledger_entry[0].leaves, -9)
self.assertEquals(leave_ledger_entry[1].leaves, -2) self.assertEqual(leave_ledger_entry[1].leaves, -2)
def test_leave_application_creation_after_expiry(self): def test_leave_application_creation_after_expiry(self):
# test leave balance for carry forwarded allocation # test leave balance for carry forwarded allocation
@ -566,7 +566,7 @@ class TestLeaveApplication(unittest.TestCase):
create_carry_forwarded_allocation(employee, leave_type) 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): def test_leave_approver_perms(self):
employee = get_employee() employee = get_employee()

View File

@ -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)) 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.assertEqual(len(leave_ledger_entry), 1)
self.assertEquals(leave_ledger_entry[0].employee, leave_encashment.employee) self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee)
self.assertEquals(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) self.assertEqual(leave_ledger_entry[0].leave_type, leave_encashment.leave_type)
self.assertEquals(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) self.assertEqual(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1)
# check if leave ledger entry is deleted on cancellation # check if leave ledger entry is deleted on cancellation

View File

@ -5,11 +5,18 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
import erpnext
from frappe.utils import getdate from frappe.utils import getdate
from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.employee.test_employee import make_employee
test_dependencies = ['Holiday List']
class TestUploadAttendance(unittest.TestCase): 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): def test_date_range(self):
employee = make_employee("test_employee@company.com") employee = make_employee("test_employee@company.com")
employee_doc = frappe.get_doc("Employee", employee) employee_doc = frappe.get_doc("Employee", employee)

View File

@ -1,5 +1,6 @@
{ {
"attach_print": 0, "attach_print": 0,
"channel": "Email",
"creation": "2017-08-11 03:17:11.769210", "creation": "2017-08-11 03:17:11.769210",
"days_in_advance": 0, "days_in_advance": 0,
"docstatus": 0, "docstatus": 0,

View File

@ -55,9 +55,9 @@ class TestLoan(unittest.TestCase):
def test_loan(self): def test_loan(self):
loan = frappe.get_doc("Loan", {"applicant":self.applicant1}) loan = frappe.get_doc("Loan", {"applicant":self.applicant1})
self.assertEquals(loan.monthly_repayment_amount, 15052) self.assertEqual(loan.monthly_repayment_amount, 15052)
self.assertEquals(flt(loan.total_interest_payable, 0), 21034) self.assertEqual(flt(loan.total_interest_payable, 0), 21034)
self.assertEquals(flt(loan.total_payment, 0), 301034) self.assertEqual(flt(loan.total_payment, 0), 301034)
schedule = loan.repayment_schedule schedule = loan.repayment_schedule
@ -72,9 +72,9 @@ class TestLoan(unittest.TestCase):
loan.monthly_repayment_amount = 14000 loan.monthly_repayment_amount = 14000
loan.save() loan.save()
self.assertEquals(len(loan.repayment_schedule), 22) self.assertEqual(len(loan.repayment_schedule), 22)
self.assertEquals(flt(loan.total_interest_payable, 0), 22712) self.assertEqual(flt(loan.total_interest_payable, 0), 22712)
self.assertEquals(flt(loan.total_payment, 0), 302712) self.assertEqual(flt(loan.total_payment, 0), 302712)
def test_loan_with_security(self): 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", loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods",
12, loan_application) 12, loan_application)
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
def test_loan_disbursement(self): def test_loan_disbursement(self):
pledge = [{ pledge = [{
@ -102,7 +102,7 @@ class TestLoan(unittest.TestCase):
create_pledge(loan_application) create_pledge(loan_application)
loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, 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() loan.submit()
@ -120,8 +120,8 @@ class TestLoan(unittest.TestCase):
filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry2.name} filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry2.name}
) )
self.assertEquals(loan.status, "Disbursed") self.assertEqual(loan.status, "Disbursed")
self.assertEquals(loan.disbursed_amount, 1000000) self.assertEqual(loan.disbursed_amount, 1000000)
self.assertTrue(gl_entries1) self.assertTrue(gl_entries1)
self.assertTrue(gl_entries2) 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' last_date = '2019-10-30'
@ -156,15 +156,15 @@ class TestLoan(unittest.TestCase):
repayment_entry.submit() repayment_entry.submit()
penalty_amount = (accrued_interest_amount * 5 * 25) / 100 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']) amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount'])
loan.load_from_db() loan.load_from_db()
total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount'] 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.assertEqual(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable)
self.assertEquals(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid - self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
penalty_amount - total_interest_paid, 0)) penalty_amount - total_interest_paid, 0))
def test_loan_closure(self): 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' 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)']) 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.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
request_loan_closure(loan.name) request_loan_closure(loan.name)
loan.load_from_db() 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): def test_loan_repayment_for_term_loan(self):
pledges = [{ pledges = [{
@ -241,8 +241,8 @@ class TestLoan(unittest.TestCase):
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
'paid_principal_amount']) 'paid_principal_amount'])
self.assertEquals(amounts[0], 11250.00) self.assertEqual(amounts[0], 11250.00)
self.assertEquals(amounts[1], 78303.00) self.assertEqual(amounts[1], 78303.00)
def test_security_shortfall(self): def test_security_shortfall(self):
pledges = [{ pledges = [{
@ -268,17 +268,17 @@ class TestLoan(unittest.TestCase):
loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
self.assertTrue(loan_security_shortfall) self.assertTrue(loan_security_shortfall)
self.assertEquals(loan_security_shortfall.loan_amount, 1000000.00) self.assertEqual(loan_security_shortfall.loan_amount, 1000000.00)
self.assertEquals(loan_security_shortfall.security_value, 800000.00) self.assertEqual(loan_security_shortfall.security_value, 800000.00)
self.assertEquals(loan_security_shortfall.shortfall_amount, 600000.00) self.assertEqual(loan_security_shortfall.shortfall_amount, 600000.00)
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250 frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
where loan_security='Test Security 2'""") where loan_security='Test Security 2'""")
create_process_loan_security_shortfall() create_process_loan_security_shortfall()
loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
self.assertEquals(loan_security_shortfall.status, "Completed") self.assertEqual(loan_security_shortfall.status, "Completed")
self.assertEquals(loan_security_shortfall.shortfall_amount, 0) self.assertEqual(loan_security_shortfall.shortfall_amount, 0)
def test_loan_security_unpledge(self): def test_loan_security_unpledge(self):
pledge = [{ 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' last_date = '2019-10-30'
@ -312,7 +312,7 @@ class TestLoan(unittest.TestCase):
request_loan_closure(loan.name) request_loan_closure(loan.name)
loan.load_from_db() 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 = unpledge_security(loan=loan.name, save=1)
unpledge_request.submit() unpledge_request.submit()
@ -323,11 +323,11 @@ class TestLoan(unittest.TestCase):
pledged_qty = get_pledged_security_qty(loan.name) pledged_qty = get_pledged_security_qty(loan.name)
self.assertEqual(loan.status, 'Closed') 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)) amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5))
self.assertEqual(amounts['pending_principal_amount'], 0) 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) self.assertEqual(amounts['interest_amount'], 0)
def test_partial_loan_security_unpledge(self): 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
unpledge_map = {'Test Security 1': 4000} unpledge_map = {'Test Security 1': 4000}
unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1) 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' last_date = '2019-10-30'
@ -475,7 +475,7 @@ class TestLoan(unittest.TestCase):
request_loan_closure(loan.name) request_loan_closure(loan.name)
loan.load_from_db() 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)) amounts = calculate_amounts(loan.name, add_days(last_date, 5))
self.assertEqual(amounts['pending_principal_amount'], 0.0) 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' last_date = '2019-10-30'
@ -533,8 +533,8 @@ class TestLoan(unittest.TestCase):
calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual', calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount') {'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
self.assertEquals(calculated_penalty_amount, penalty_amount) self.assertEqual(calculated_penalty_amount, penalty_amount)
def test_penalty_repayment(self): def test_penalty_repayment(self):
loan, dummy = create_loan_scenario_for_penalty(self) loan, dummy = create_loan_scenario_for_penalty(self)
@ -547,13 +547,13 @@ class TestLoan(unittest.TestCase):
repayment_entry.submit() repayment_entry.submit()
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01') 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 = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
repayment_entry.submit() repayment_entry.submit()
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02') 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): def test_loan_write_off_limit(self):
pledge = [{ 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' 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)']) 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.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
amounts = calculate_amounts(loan.name, add_days(last_date, 5)) 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) request_loan_closure(loan.name)
loan.load_from_db() 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): def test_loan_amount_write_off(self):
pledge = [{ 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 = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' 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)']) 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.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
amounts = calculate_amounts(loan.name, add_days(last_date, 5)) 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 = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount'])
we.submit() we.submit()
amounts = calculate_amounts(loan.name, add_days(last_date, 5)) 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): def create_loan_scenario_for_penalty(doc):
pledge = [{ pledge = [{

View File

@ -87,7 +87,7 @@ class TestLoanDisbursement(unittest.TestCase):
loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01') loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit() loan.submit()
self.assertEquals(loan.loan_amount, 1000000) self.assertEqual(loan.loan_amount, 1000000)
first_date = '2019-10-01' first_date = '2019-10-01'
last_date = '2019-10-30' 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') per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30')
interest = per_day_interest * 15 interest = per_day_interest * 15
self.assertEquals(amounts['pending_principal_amount'], 1500000) self.assertEqual(amounts['pending_principal_amount'], 1500000)
self.assertEquals(amounts['interest_amount'], flt(interest + previous_interest, 2)) self.assertEqual(amounts['interest_amount'], flt(interest + previous_interest, 2))

View File

@ -52,7 +52,7 @@ class TestLoanInterestAccrual(unittest.TestCase):
process_loan_interest_accrual_for_demand_loans(posting_date=last_date) process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) 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): def test_accumulated_amounts(self):
pledge = [{ pledge = [{
@ -76,7 +76,7 @@ class TestLoanInterestAccrual(unittest.TestCase):
process_loan_interest_accrual_for_demand_loans(posting_date=last_date) process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) 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_start_date = '2019-10-31'
next_end_date = '2019-11-29' 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, loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name,
'process_loan_interest_accrual': process}) '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)

View File

@ -29,7 +29,10 @@ frappe.ui.form.on("BOM", {
frm.set_query("item", function() { frm.set_query("item", function() {
return { return {
query: "erpnext.manufacturing.doctype.bom.bom.item_query" query: "erpnext.manufacturing.doctype.bom.bom.item_query",
filters: {
"is_stock_item": 1
}
}; };
}); });

View File

@ -973,6 +973,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
if not has_variants: if not has_variants:
query_filters["has_variants"] = 0 query_filters["has_variants"] = 0
if filters and filters.get("is_stock_item"):
query_filters["is_stock_item"] = 1
return frappe.get_all("Item", return frappe.get_all("Item",
fields = fields, filters=query_filters, fields = fields, filters=query_filters,
or_filters = or_cond_filters, order_by=order_by, or_filters = or_cond_filters, order_by=order_by,

View File

@ -223,7 +223,7 @@ class TestBOM(unittest.TestCase):
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") 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]) 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]) 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"): 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}) return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@ -45,16 +45,16 @@ class TestBOMUpdateTool(unittest.TestCase):
else: else:
doc = frappe.get_doc("BOM", bom_no) 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) frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 200)
update_cost() update_cost()
doc.load_from_db() 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) frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 100)
update_cost() update_cost()
doc.load_from_db() doc.load_from_db()
self.assertEquals(doc.total_cost, 200) self.assertEqual(doc.total_cost, 200)

View File

@ -473,7 +473,7 @@ class TestWorkOrder(unittest.TestCase):
def test_cost_center_for_manufacture(self): def test_cost_center_for_manufacture(self):
wo_order = make_wo_order_test_record() wo_order = make_wo_order_test_record()
ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty) 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): def test_operation_time_with_batch_size(self):
fg_item = "Test Batch Size Item For BOM" fg_item = "Test Batch Size Item For BOM"
@ -539,11 +539,11 @@ class TestWorkOrder(unittest.TestCase):
ste_cancel_list.append(ste1) ste_cancel_list.append(ste1)
ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2)) 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} expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4}
for row in ste3.items: 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() ste_cancel_list.reverse()
for ste_doc in ste_cancel_list: for ste_doc in ste_cancel_list:
ste_doc.cancel() ste_doc.cancel()
@ -577,7 +577,7 @@ class TestWorkOrder(unittest.TestCase):
ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2)) ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
for ste_row in ste3.items: for ste_row in ste3.items:
if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: 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() ste3.submit()
ste_cancel_list.append(ste3) 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)) ste2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
for ste_row in ste2.items: for ste_row in ste2.items:
if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse: 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() ste_cancel_list.reverse()
for ste_doc in ste_cancel_list: for ste_doc in ste_cancel_list:
ste_doc.cancel() ste_doc.cancel()

View File

@ -39,7 +39,7 @@ class TestDonation(unittest.TestCase):
donation.on_payment_authorized() donation.on_payment_authorized()
donation.reload() donation.reload()
self.assertEquals(donation.paid, 1) self.assertEqual(donation.paid, 1)
self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name})) self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name}))

View File

@ -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.make_non_standard_user_type #13-04-2021
erpnext.patches.v13_0.update_shipment_status erpnext.patches.v13_0.update_shipment_status
erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting 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 erpnext.patches.v13_0.set_pos_closing_as_failed

View File

@ -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)

View File

@ -44,9 +44,11 @@ def execute():
# make current item's tax map # make current item's tax map
item_tax_map = {} item_tax_map = {}
for d in old_item_taxes[item_code]: for d in old_item_taxes[item_code]:
if d.tax_type not in item_tax_map:
item_tax_map[d.tax_type] = d.tax_rate 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 # update the item tax table
frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code) 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): and item_tax_template is NULL""".format(dt), as_dict=1):
item_tax_map = json.loads(d.item_tax_rate) item_tax_map = json.loads(d.item_tax_rate)
item_tax_template_name = get_item_tax_template(item_tax_templates, 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.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
frappe.db.auto_commit_on_many_writes = False frappe.db.auto_commit_on_many_writes = False
@ -78,7 +80,7 @@ def execute():
settings.determine_address_tax_category_from = "Billing Address" settings.determine_address_tax_category_from = "Billing Address"
settings.save() 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 # search for previously created item tax template by comparing tax maps
for template, item_tax_template_map in iteritems(item_tax_templates): for template, item_tax_template_map in iteritems(item_tax_templates):
if item_tax_map == item_tax_template_map: 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") 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'): if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
if tax_type not in tax_types:
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) 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.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate item_tax_templates[item_tax_template.title][tax_type] = tax_rate
if item_tax_template.get("taxes"): if item_tax_template.get("taxes"):

View File

@ -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', '')

View File

@ -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()

View File

@ -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.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 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): 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): def setUp(self):
for dt in ["Salary Slip", "Salary Component", "Salary Component Account", for dt in ["Salary Slip", "Salary Component", "Salary Component Account",
"Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]: "Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]:

View File

@ -1,5 +1,6 @@
{ {
"attach_print": 0, "attach_print": 0,
"channel": "Email",
"condition": "doc.docstatus==1", "condition": "doc.docstatus==1",
"creation": "2018-05-15 18:52:36.362838", "creation": "2018-05-15 18:52:36.362838",
"date_changed": "bonus_payment_date", "date_changed": "bonus_payment_date",

View File

@ -13,7 +13,7 @@ class TestHomepage(unittest.TestCase):
set_request(method='GET', path='home') set_request(method='GET', path='home')
response = render() response = render()
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
html = frappe.safe_decode(response.get_data()) html = frappe.safe_decode(response.get_data())
self.assertTrue('<section class="hero-section' in html) self.assertTrue('<section class="hero-section' in html)

View File

@ -28,7 +28,7 @@ class TestHomepageSection(unittest.TestCase):
set_request(method='GET', path='home') set_request(method='GET', path='home')
response = render() response = render()
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
html = frappe.safe_decode(response.get_data()) html = frappe.safe_decode(response.get_data())
@ -61,7 +61,7 @@ class TestHomepageSection(unittest.TestCase):
set_request(method='GET', path='home') set_request(method='GET', path='home')
response = render() response = render()
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
html = frappe.safe_decode(response.get_data()) html = frappe.safe_decode(response.get_data())

View File

@ -458,7 +458,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"max_attachments": 4, "max_attachments": 4,
"modified": "2020-09-02 11:54:01.223620", "modified": "2021-04-28 16:36:11.654632",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Project", "name": "Project",
@ -495,7 +495,7 @@
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"search_fields": "customer, status, priority, is_active", "search_fields": "project_name,customer, status, priority, is_active",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",

View File

@ -209,7 +209,7 @@ def get_projectwise_timesheet_data(project, parent=None, from_time=None, to_time
if parent: if parent:
condition = "AND parent = %(parent)s" condition = "AND parent = %(parent)s"
if from_time and to_time: if from_time and to_time:
condition += "AND from_time BETWEEN %(from_time)s AND %(to_time)s" condition += "AND CAST(from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1 from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt
def execute(filters=None): def execute(filters=None):
columns, data = [], [] columns, data = [], []
@ -52,8 +53,8 @@ def get_rows(filters):
def calculate_cost_and_profit(data): def calculate_cost_and_profit(data):
for row in data: for row in data:
row.fractional_cost = row.base_gross_pay * row.utilization row.fractional_cost = flt(row.base_gross_pay) * flt(row.utilization)
row.profit = row.base_grand_total - row.base_gross_pay * row.utilization row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization)
return data return data
def get_conditions(filters): def get_conditions(filters):

View File

@ -1329,7 +1329,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
this.toggle_item_grid_columns(company_currency); this.toggle_item_grid_columns(company_currency);
if(this.frm.fields_dict["operations"]) { if (this.frm.doc.operations && this.frm.doc.operations.length > 0) {
this.frm.set_currency_labels(["operating_cost", "hour_rate"], this.frm.doc.currency, "operations"); this.frm.set_currency_labels(["operating_cost", "hour_rate"], this.frm.doc.currency, "operations");
this.frm.set_currency_labels(["base_operating_cost", "base_hour_rate"], company_currency, "operations"); this.frm.set_currency_labels(["base_operating_cost", "base_hour_rate"], company_currency, "operations");
@ -1340,7 +1340,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}); });
} }
if(this.frm.fields_dict["scrap_items"]) { if (this.frm.doc.scrap_items && this.frm.doc.scrap_items.length > 0) {
this.frm.set_currency_labels(["rate", "amount"], this.frm.doc.currency, "scrap_items"); this.frm.set_currency_labels(["rate", "amount"], this.frm.doc.currency, "scrap_items");
this.frm.set_currency_labels(["base_rate", "base_amount"], company_currency, "scrap_items"); this.frm.set_currency_labels(["base_rate", "base_amount"], company_currency, "scrap_items");
@ -1351,13 +1351,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}); });
} }
if(this.frm.fields_dict["taxes"]) { if (this.frm.doc.taxes && this.frm.doc.taxes.length > 0) {
this.frm.set_currency_labels(["tax_amount", "total", "tax_amount_after_discount"], this.frm.doc.currency, "taxes"); this.frm.set_currency_labels(["tax_amount", "total", "tax_amount_after_discount"], this.frm.doc.currency, "taxes");
this.frm.set_currency_labels(["base_tax_amount", "base_total", "base_tax_amount_after_discount"], company_currency, "taxes"); this.frm.set_currency_labels(["base_tax_amount", "base_total", "base_tax_amount_after_discount"], company_currency, "taxes");
} }
if(this.frm.fields_dict["advances"]) { if (this.frm.doc.advances && this.frm.doc.advances.length > 0) {
this.frm.set_currency_labels(["advance_amount", "allocated_amount"], this.frm.set_currency_labels(["advance_amount", "allocated_amount"],
this.frm.doc.party_account_currency, "advances"); this.frm.doc.party_account_currency, "advances");
} }

View File

@ -118,7 +118,7 @@ class Quiz {
quiz_response: this.get_selected(), quiz_response: this.get_selected(),
course: this.course, course: this.course,
program: this.program, program: this.program,
time_taken: this.is_time_bound ? this.time_taken : "" time_taken: this.is_time_bound ? this.time_taken : 0
}).then(res => { }).then(res => {
this.submit_btn.remove() this.submit_btn.remove()
if (!res.message) { if (!res.message) {

View File

@ -48,31 +48,24 @@ $.extend(erpnext, {
return cint(frappe.boot.sysdefaults.allow_stale); return cint(frappe.boot.sysdefaults.allow_stale);
}, },
setup_serial_no: function() { setup_serial_or_batch_no: function() {
var grid_row = cur_frm.open_grid_row(); let grid_row = cur_frm.open_grid_row();
if(!grid_row || !grid_row.grid_form.fields_dict.serial_no || if (!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
grid_row.grid_form.fields_dict.serial_no.get_status()!=="Write") return; grid_row.grid_form.fields_dict.serial_no.get_status() !== "Write") return;
var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>') frappe.model.get_value('Item', {'name': grid_row.doc.item_code},
.appendTo($("<div>") ['has_serial_no', 'has_batch_no'], ({has_serial_no, has_batch_no}) => {
.css({"margin-bottom": "10px", "margin-top": "10px"}) Object.assign(grid_row.doc, {has_serial_no, has_batch_no});
.appendTo(grid_row.grid_form.fields_dict.serial_no.$wrapper));
var me = this; if (has_serial_no) {
$btn.on("click", function() { attach_selector_button(__("Add Serial No"),
let callback = ''; grid_row.grid_form.fields_dict.serial_no.$wrapper, this, grid_row);
let on_close = ''; } else if (has_batch_no) {
attach_selector_button(__("Pick Batch No"),
frappe.model.get_value('Item', {'name':grid_row.doc.item_code}, 'has_serial_no', grid_row.grid_form.fields_dict.batch_no.$wrapper, this, grid_row);
(data) => {
if(data) {
grid_row.doc.has_serial_no = data.has_serial_no;
me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
callback, on_close, true);
} }
} }
); );
});
}, },
route_to_adjustment_jv: (args) => { route_to_adjustment_jv: (args) => {
@ -743,3 +736,14 @@ $(document).on('app_ready', function() {
}); });
} }
}); });
function attach_selector_button(inner_text, append_loction, context, grid_row) {
let $btn_div = $("<div>").css({"margin-bottom": "10px", "margin-top": "10px"})
.appendTo(append_loction);
let $btn = $(`<button class="btn btn-sm btn-default">${inner_text}</button>`)
.appendTo($btn_div);
$btn.on("click", function() {
context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
});
}

View File

@ -129,11 +129,20 @@
@extend .pointer-no-select; @extend .pointer-no-select;
border-radius: var(--border-radius-md); border-radius: var(--border-radius-md);
box-shadow: var(--shadow-base); box-shadow: var(--shadow-base);
position: relative;
&:hover { &:hover {
transform: scale(1.02, 1.02); transform: scale(1.02, 1.02);
} }
.item-qty-pill {
position: absolute;
display: flex;
margin: var(--margin-sm);
justify-content: flex-end;
right: 0px;
}
.item-display { .item-display {
display: flex; display: flex;
align-items: center; align-items: center;
@ -766,9 +775,10 @@
> .payment-modes { > .payment-modes {
display: flex; display: flex;
padding-bottom: var(--padding-sm); padding-bottom: var(--padding-sm);
margin-bottom: var(--margin-xs); margin-bottom: var(--margin-sm);
overflow-x: scroll; overflow-x: scroll;
overflow-y: hidden; overflow-y: hidden;
flex-shrink: 0;
> .payment-mode-wrapper { > .payment-mode-wrapper {
min-width: 40%; min-width: 40%;
@ -825,9 +835,24 @@
> .fields-numpad-container { > .fields-numpad-container {
display: flex; display: flex;
flex: 1; flex: 1;
height: 100%;
position: relative;
justify-content: flex-end;
> .fields-section { > .fields-section {
flex: 1; flex: 1;
position: absolute;
display: flex;
flex-direction: column;
width: 50%;
height: 100%;
top: 0;
left: 0;
padding-bottom: var(--margin-md);
.invoice-fields {
overflow-y: scroll;
}
} }
> .number-pad { > .number-pad {
@ -835,6 +860,7 @@
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: flex-end; align-items: flex-end;
max-width: 50%;
.numpad-container { .numpad-container {
display: grid; display: grid;
@ -861,6 +887,7 @@
margin-bottom: var(--margin-sm); margin-bottom: var(--margin-sm);
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
flex-shrink: 0;
> .totals { > .totals {
display: flex; display: flex;

View File

@ -49,11 +49,11 @@ class TestTaxExemption80GCertificate(unittest.TestCase):
certificate.insert() certificate.insert()
# check company details # check company details
self.assertEquals(certificate.company_pan_number, 'BBBTI3374C') self.assertEqual(certificate.company_pan_number, 'BBBTI3374C')
self.assertEquals(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087') self.assertEqual(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087')
# check donation details # check donation details
self.assertEquals(certificate.amount, donation.amount) self.assertEqual(certificate.amount, donation.amount)
duplicate_certificate = create_80g_certificate(args) duplicate_certificate = create_80g_certificate(args)
# duplicate validation # duplicate validation
@ -83,9 +83,9 @@ class TestTaxExemption80GCertificate(unittest.TestCase):
certificate.get_payments() certificate.get_payments()
certificate.insert() certificate.insert()
self.assertEquals(len(certificate.payments), 1) self.assertEqual(len(certificate.payments), 1)
self.assertEquals(certificate.payments[0].amount, membership.amount) self.assertEqual(certificate.payments[0].amount, membership.amount)
self.assertEquals(certificate.payments[0].invoice_id, invoice.name) self.assertEqual(certificate.payments[0].invoice_id, invoice.name)
def create_80g_certificate(args): def create_80g_certificate(args):

View File

@ -1,11 +1,24 @@
import os import os
import frappe import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def setup(company=None, patch=True): def setup(company=None, patch=True):
make_custom_fields()
add_custom_roles_for_reports() add_custom_roles_for_reports()
def make_custom_fields():
custom_fields = {
'Party Account': [
dict(fieldname='debtor_creditor_number', label='Debtor/Creditor Number',
fieldtype='Data', insert_after='account', translatable=0)
]
}
create_custom_fields(custom_fields)
def add_custom_roles_for_reports(): def add_custom_roles_for_reports():
"""Add Access Control to UAE VAT 201.""" """Add Access Control to UAE VAT 201."""
if not frappe.db.get_value('Custom Role', dict(report='DATEV')): if not frappe.db.get_value('Custom Role', dict(report='DATEV')):

View File

@ -56,10 +56,10 @@ def get_datev_csv(data, filters, csv_class):
) )
if not six.PY2: if not six.PY2:
data = data.encode('latin_1') data = data.encode('latin_1', errors='replace')
header = get_header(filters, csv_class) header = get_header(filters, csv_class)
header = ';'.join(header).encode('latin_1') header = ';'.join(header).encode('latin_1', errors='replace')
# 1st Row: Header with meta data # 1st Row: Header with meta data
# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here. # 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.

View File

@ -71,13 +71,14 @@ def validate_einvoice_fields(doc):
def raise_document_name_too_long_error(): def raise_document_name_too_long_error():
title = _('Document ID Too Long') title = _('Document ID Too Long')
msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice, ') msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice')
msg += _('document id {} exceed 16 letters. ').format(bold(_('should not'))) msg += ', '
msg += _('document id {} exceed 16 letters.').format(bold(_('should not')))
msg += '<br><br>' msg += '<br><br>'
msg += _('You must {} your {} in order to have document id of {} length 16. ').format( msg += _('You must {} your {} in order to have document id of {} length 16.').format(
bold(_('modify')), bold(_('naming series')), bold(_('maximum')) bold(_('modify')), bold(_('naming series')), bold(_('maximum'))
) )
msg += _('Please account for ammended documents too. ') msg += _('Please account for ammended documents too.')
frappe.throw(msg, title=title) frappe.throw(msg, title=title)
def read_json(name): def read_json(name):
@ -847,6 +848,7 @@ class GSPConnector():
res = self.make_request('post', self.generate_ewaybill_url, headers, data) res = self.make_request('post', self.generate_ewaybill_url, headers, data)
if res.get('success'): if res.get('success'):
self.invoice.ewaybill = res.get('result').get('EwbNo') self.invoice.ewaybill = res.get('result').get('EwbNo')
self.invoice.eway_bill_validity = res.get('result').get('EwbValidTill')
self.invoice.eway_bill_cancelled = 0 self.invoice.eway_bill_cancelled = 0
self.invoice.update(args) self.invoice.update(args)
self.invoice.flags.updater_reference = { self.invoice.flags.updater_reference = {
@ -944,6 +946,7 @@ class GSPConnector():
self.invoice.irn = res.get('Irn') self.invoice.irn = res.get('Irn')
self.invoice.ewaybill = res.get('EwbNo') self.invoice.ewaybill = res.get('EwbNo')
self.invoice.eway_bill_validity = res.get('EwbValidTill')
self.invoice.ack_no = res.get('AckNo') self.invoice.ack_no = res.get('AckNo')
self.invoice.ack_date = res.get('AckDt') self.invoice.ack_date = res.get('AckDt')
self.invoice.signed_einvoice = dec_signed_invoice self.invoice.signed_einvoice = dec_signed_invoice
@ -960,6 +963,7 @@ class GSPConnector():
'label': _('IRN Generated') 'label': _('IRN Generated')
} }
self.update_invoice() self.update_invoice()
def attach_qrcode_image(self): def attach_qrcode_image(self):
qrcode = self.invoice.signed_qr_code qrcode = self.invoice.signed_qr_code
doctype = self.invoice.doctype doctype = self.invoice.doctype

View File

@ -422,6 +422,9 @@ def make_custom_fields(update=True):
dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
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'),
dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1, dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),

Some files were not shown because too many files have changed in this diff Show More