From 5a31e228923d3ec2fe387e3e83eca3920cee6da4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 6 Nov 2019 17:03:12 +0530 Subject: [PATCH 01/25] chore: frappe.rename_doc update --- .../clinical_procedure_template.py | 3 ++- .../healthcare_service_unit_type.py | 3 ++- .../doctype/lab_test_template/lab_test_template.py | 5 +++-- erpnext/patches/v9_2/rename_translated_domains_in_en.py | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 141329b3db..a8a9037e4a 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, json from frappe import _ from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe.utils import nowdate class ClinicalProcedureTemplate(Document): @@ -115,7 +116,7 @@ def change_item_code_from_template(item_code, doc): "item_code": item_code})): frappe.throw(_("Code {0} already exist").format(item_code)) else: - frappe.rename_doc("Item", doc.item_code, item_code, ignore_permissions = True) + rename_doc("Item", doc.item_code, item_code, ignore_permissions=True) frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code) return diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index 650499454b..43f01c86b9 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc class HealthcareServiceUnitType(Document): def validate(self): @@ -107,7 +108,7 @@ def change_item_code(item, item_code, doc_name): if(item_exist): frappe.throw(_("Code {0} already exist").format(item_code)) else: - frappe.rename_doc("Item", item, item_code, ignore_permissions = True) + rename_doc("Item", item, item_code, ignore_permissions=True) frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code) @frappe.whitelist() diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index 101e143c12..91488e3533 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe, json from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe import _ class LabTestTemplate(Document): @@ -112,9 +113,9 @@ def change_test_code_from_template(lab_test_code, doc): if(item_exist): frappe.throw(_("Code {0} already exist").format(lab_test_code)) else: - frappe.rename_doc("Item", doc.name, lab_test_code, ignore_permissions = True) + rename_doc("Item", doc.name, lab_test_code, ignore_permissions=True) frappe.db.set_value("Lab Test Template",doc.name,"lab_test_code",lab_test_code) - frappe.rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions = True) + rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions=True) return lab_test_code @frappe.whitelist() diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py index aec5d438ec..e5a9e2461f 100644 --- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py +++ b/erpnext/patches/v9_2/rename_translated_domains_in_en.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.model.rename_doc import rename_doc def execute(): frappe.reload_doc('stock', 'doctype', 'item') @@ -20,11 +21,11 @@ def execute(): if frappe.db.exists("Domain", domain): merge=True - frappe.rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) + rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) domain_settings = frappe.get_single("Domain Settings") active_domains = [d.domain for d in domain_settings.active_domains] - + try: for domain in active_domains: domain = frappe.get_doc("Domain", domain) From 1e4ea466d3a58c2433006cdccf919a7acd0763fa Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 11 Dec 2019 09:36:17 +0530 Subject: [PATCH 02/25] style: update/standardize rename_doc usage --- .../v11_0/merge_land_unit_with_location.py | 5 ++-- .../v11_0/rename_asset_adjustment_doctype.py | 4 ++-- .../patches/v11_0/rename_health_insurance.py | 3 +-- .../rename_healthcare_doctype_and_fields.py | 3 +-- .../rename_production_order_to_work_order.py | 9 ++++--- .../rename_supplier_type_to_supplier_group.py | 3 +-- .../rename_pricing_rule_child_doctypes.py | 3 +-- erpnext/stock/doctype/item/test_item.py | 3 +-- .../stock/doctype/warehouse/test_warehouse.py | 24 +++++++++++-------- 9 files changed, 27 insertions(+), 30 deletions(-) diff --git a/erpnext/patches/v11_0/merge_land_unit_with_location.py b/erpnext/patches/v11_0/merge_land_unit_with_location.py index 1ea486dfd5..7845da255a 100644 --- a/erpnext/patches/v11_0/merge_land_unit_with_location.py +++ b/erpnext/patches/v11_0/merge_land_unit_with_location.py @@ -4,19 +4,18 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field def execute(): # Rename and reload the Land Unit and Linked Land Unit doctypes if frappe.db.table_exists('Land Unit') and not frappe.db.table_exists('Location'): - rename_doc('DocType', 'Land Unit', 'Location', force=True) + frappe.rename_doc('DocType', 'Land Unit', 'Location', force=True) frappe.reload_doc('assets', 'doctype', 'location') if frappe.db.table_exists('Linked Land Unit') and not frappe.db.table_exists('Linked Location'): - rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True) + frappe.rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True) frappe.reload_doc('assets', 'doctype', 'linked_location') diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py index c03ab0b711..fad0cf7a45 100644 --- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py +++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py @@ -3,9 +3,9 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc + def execute(): if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists("Asset Value Adjustment"): - rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True) + frappe.rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True) frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment') \ No newline at end of file diff --git a/erpnext/patches/v11_0/rename_health_insurance.py b/erpnext/patches/v11_0/rename_health_insurance.py index 24d1ddf031..e605071a29 100644 --- a/erpnext/patches/v11_0/rename_health_insurance.py +++ b/erpnext/patches/v11_0/rename_health_insurance.py @@ -2,9 +2,8 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from frappe.model.rename_doc import rename_doc import frappe def execute(): - rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True) + frappe.rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True) frappe.reload_doc('hr', 'doctype', 'employee_health_insurance') \ No newline at end of file diff --git a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py index 8fdac07658..9705681b33 100644 --- a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py +++ b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field from frappe.modules import scrub, get_doctype_module @@ -37,7 +36,7 @@ doc_rename_map = { def execute(): for dt in doc_rename_map: if frappe.db.exists('DocType', dt): - rename_doc('DocType', dt, doc_rename_map[dt], force=True) + frappe.rename_doc('DocType', dt, doc_rename_map[dt], force=True) for dn in field_rename_map: if frappe.db.exists('DocType', dn): diff --git a/erpnext/patches/v11_0/rename_production_order_to_work_order.py b/erpnext/patches/v11_0/rename_production_order_to_work_order.py index 2c27fbbc9d..2f620f413b 100644 --- a/erpnext/patches/v11_0/rename_production_order_to_work_order.py +++ b/erpnext/patches/v11_0/rename_production_order_to_work_order.py @@ -2,18 +2,17 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from frappe.model.rename_doc import rename_doc -from frappe.model.utils.rename_field import rename_field import frappe +from frappe.model.utils.rename_field import rename_field def execute(): - rename_doc('DocType', 'Production Order', 'Work Order', force=True) + frappe.rename_doc('DocType', 'Production Order', 'Work Order', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order') - rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True) + frappe.rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order_item') - rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True) + frappe.rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order_operation') frappe.reload_doc('projects', 'doctype', 'timesheet') diff --git a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py index 52d4621c7b..c4b3838c71 100644 --- a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py +++ b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field from frappe import _ from frappe.utils.nestedset import rebuild_tree @@ -9,7 +8,7 @@ def execute(): if frappe.db.table_exists("Supplier Group"): frappe.reload_doc('setup', 'doctype', 'supplier_group') elif frappe.db.table_exists("Supplier Type"): - rename_doc("DocType", "Supplier Type", "Supplier Group", force=True) + frappe.rename_doc("DocType", "Supplier Type", "Supplier Group", force=True) frappe.reload_doc('setup', 'doctype', 'supplier_group') frappe.reload_doc("accounts", "doctype", "pricing_rule") frappe.reload_doc("accounts", "doctype", "tax_rule") diff --git a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py index 41ac8cff2b..b9ad622b0e 100644 --- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py +++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc doctypes = { 'Price Discount Slab': 'Promotional Scheme Price Discount', @@ -16,6 +15,6 @@ doctypes = { def execute(): for old_doc, new_doc in doctypes.items(): if not frappe.db.table_exists(new_doc) and frappe.db.table_exists(old_doc): - rename_doc('DocType', old_doc, new_doc) + frappe.rename_doc('DocType', old_doc, new_doc) frappe.reload_doc("accounts", "doctype", frappe.scrub(new_doc)) frappe.delete_doc("DocType", old_doc) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index da53d8d6b1..cbd5e33b14 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -11,7 +11,6 @@ from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsE InvalidItemAttributeValueError, get_variant) from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode from erpnext.stock.doctype.item.item import get_uom_conv_factor -from frappe.model.rename_doc import rename_doc from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.get_item_details import get_item_details @@ -348,7 +347,7 @@ class TestItem(unittest.TestCase): make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC", qty=1, rate=100) - rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True) + frappe.rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True) self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1")) diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py index dc39e101ce..e0483d3ab6 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.py +++ b/erpnext/stock/doctype/warehouse/test_warehouse.py @@ -1,18 +1,22 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from frappe.model.rename_doc import rename_doc -from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + +import unittest + +import frappe from frappe.utils import cint -from erpnext import set_perpetual_inventory from frappe.test_runner import make_test_records -from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account import erpnext -import frappe -import unittest +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext import set_perpetual_inventory +from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account + + test_records = frappe.get_test_records('Warehouse') + class TestWarehouse(unittest.TestCase): def setUp(self): if not frappe.get_value('Item', '_Test Item'): @@ -41,7 +45,7 @@ class TestWarehouse(unittest.TestCase): # Rename with abbr if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"): frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC") - rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC") self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": "Test Warehouse for Renaming 1 - _TC"})) @@ -50,7 +54,7 @@ class TestWarehouse(unittest.TestCase): if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"): frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC") - rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3") self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": "Test Warehouse for Renaming 1 - _TC"})) @@ -58,7 +62,7 @@ class TestWarehouse(unittest.TestCase): # Another rename with multiple dashes if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"): frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC") - rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company") + frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company") def test_warehouse_merging(self): set_perpetual_inventory(1) @@ -78,7 +82,7 @@ class TestWarehouse(unittest.TestCase): {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")) ) - rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC", + frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC", "Test Warehouse for Merging 2 - _TC", merge=True) self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC")) From 328c4f9b92e5ff5fa0e50ae8eaf4038531f92148 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 2 Jan 2020 19:00:32 +0530 Subject: [PATCH 03/25] fix: Get outgoing rate of serial no from SLE if serial no already transferred to another company --- erpnext/stock/stock_ledger.py | 37 +++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 69a4b94b4e..b100f45327 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -212,7 +212,7 @@ class update_entries_after(object): def get_serialized_values(self, sle): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) - serial_no = cstr(sle.serial_no).split("\n") + serial_nos = cstr(sle.serial_no).split("\n") if incoming_rate < 0: # wrong incoming rate @@ -224,9 +224,8 @@ class update_entries_after(object): elif actual_qty < 0: # In case of delivery/stock issue, get average purchase rate # of serial nos of current entry - stock_value_change = -1 * flt(frappe.get_all("Serial No", - fields=["sum(purchase_rate)"], - filters = {'name': ('in', serial_no)}, as_list=1)[0][0]) + outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos) + stock_value_change = -1 * outgoing_value new_stock_qty = self.qty_after_transaction + actual_qty @@ -244,6 +243,36 @@ class update_entries_after(object): sle.voucher_type, sle.voucher_no, self.allow_zero_rate, currency=erpnext.get_company_currency(sle.company)) + def get_incoming_value_for_serial_nos(self, sle, serial_nos): + # get rate from serial nos within same company + all_serial_nos = frappe.get_all("Serial No", + fields=["purchase_rate", "name", "company"], + filters = {'name': ('in', serial_nos)}) + + incoming_values = sum([flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company]) + + # Get rate for serial nos which has been transferred to other company + invalid_serial_nos = [d.name for d in all_serial_nos if d.company!=sle.company] + for serial_no in invalid_serial_nos: + incoming_rate = frappe.db.sql(""" + select incoming_rate + from `tabStock Ledger Entry` + where + company = %s + and actual_qty > 0 + and (serial_no = %s + or serial_no like %s + or serial_no like %s + or serial_no like %s + ) + order by posting_date desc + limit 1 + """, (sle.company, serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%')) + + incoming_values += flt(incoming_rate[0][0]) if incoming_rate else 0 + + return incoming_values + def get_moving_average_values(self, sle): actual_qty = flt(sle.actual_qty) new_stock_qty = flt(self.qty_after_transaction) + actual_qty From 03bf1a875f76ff75b50c8c778a0ef7d4daff424a Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Sat, 4 Jan 2020 13:26:42 +0530 Subject: [PATCH 04/25] feat: Allow write-off for Fees doctype Signed-off-by: Syed Mujeer Hashmi --- .../doctype/journal_entry_account/journal_entry_account.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ab811d81b2..c3f95fa42e 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -201,7 +201,7 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", - "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting" + "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees" }, { "fieldname": "reference_name", @@ -281,4 +281,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From 9496b8453f234fdc14794a30db3135603dc5343a Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 6 Jan 2020 00:17:16 +0530 Subject: [PATCH 05/25] fix(plaid): change json structure (#20189) --- .../plaid_settings/plaid_settings.json | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json index 9903048d0b..df77ad8bc9 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2018-10-25 10:02:48.656165", "doctype": "DocType", "editable_grid": 1, @@ -23,48 +24,41 @@ }, { "default": "0", - "depends_on": "eval:doc.enabled==1", + "depends_on": "enabled", "fieldname": "automatic_sync", "fieldtype": "Check", "label": "Synchronize all accounts every hour" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_client_id", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Client ID", - "reqd": 1 + "label": "Plaid Client ID" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_secret", "fieldtype": "Password", "in_list_view": 1, - "label": "Plaid Secret", - "reqd": 1 + "label": "Plaid Secret" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_public_key", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Public Key", - "reqd": 1 + "label": "Plaid Public Key" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_env", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Environment", - "reqd": 1 + "label": "Plaid Environment" }, { "fieldname": "column_break_2", "fieldtype": "Column Break" }, { + "depends_on": "enabled", "fieldname": "section_break_4", "fieldtype": "Section Break" }, @@ -74,7 +68,8 @@ } ], "issingle": 1, - "modified": "2019-08-13 17:00:06.939422", + "links": [], + "modified": "2020-01-05 10:00:22.137832", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Plaid Settings", From fe2afc643e37c0502599e1690e52851589ae4fc9 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 6 Jan 2020 11:51:43 +0530 Subject: [PATCH 06/25] fix: incorrect valuation rate for finished good entry (#20165) --- .../stock/doctype/stock_entry/stock_entry.py | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 01d54deadf..1c9d4c0cdf 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -103,9 +103,8 @@ class StockEntry(StockController): if self.work_order and self.purpose == "Material Consumption for Manufacture": self.validate_work_order_status() - else: - self.update_work_order() + self.update_work_order() self.update_stock_ledger() self.make_gl_entries_on_cancel() self.update_cost_in_project() @@ -479,10 +478,16 @@ class StockEntry(StockController): def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost): if self.purpose in ["Manufacture", "Repack"]: for d in self.get("items"): - if d.transfer_qty and (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse): + if (d.transfer_qty and (d.bom_no or d.t_warehouse) and raw_material_cost + and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)): d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) + if (not d.basic_rate and self.work_order and + frappe.db.get_single_value("Manufacturing Settings", "material_consumption")): + d.basic_rate = get_valuation_rate_for_finished_good_entry(self.work_order) or 0 + d.basic_amount = d.basic_rate * d.qty + def distribute_additional_costs(self): if self.purpose == "Material Issue": self.additional_costs = [] @@ -833,7 +838,6 @@ class StockEntry(StockController): (self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture") and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")== 1): self.get_unconsumed_raw_materials() - else: if not self.fg_completed_qty: frappe.throw(_("Manufacturing Quantity is mandatory")) @@ -1152,20 +1156,17 @@ class StockEntry(StockController): se_child.s_warehouse = item_dict[d].get("from_warehouse") se_child.t_warehouse = item_dict[d].get("to_warehouse") se_child.item_code = item_dict[d].get('item_code') or cstr(d) - se_child.item_name = item_dict[d]["item_name"] - se_child.description = item_dict[d]["description"] se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom se_child.stock_uom = stock_uom se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty")) - se_child.expense_account = item_dict[d].get("expense_account") se_child.cost_center = item_dict[d].get("cost_center") or cost_center se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) se_child.subcontracted_item = item_dict[d].get("main_item_code") - se_child.original_item = item_dict[d].get("original_item") - se_child.po_detail = item_dict[d].get("po_detail") - if item_dict[d].get("idx"): - se_child.idx = item_dict[d].get("idx") + for field in ["idx", "po_detail", "original_item", + "expense_account", "description", "item_name"]: + if item_dict[d].get(field): + se_child.set(field, item_dict[d].get(field)) if se_child.s_warehouse==None: se_child.s_warehouse = self.from_warehouse @@ -1470,6 +1471,24 @@ def get_used_alternative_items(purchase_order=None, work_order=None): return used_alternative_items +def get_valuation_rate_for_finished_good_entry(work_order): + work_order_qty = flt(frappe.get_cached_value("Work Order", + work_order, 'material_transferred_for_manufacturing')) + + field = "(SUM(total_outgoing_value) / %s) as valuation_rate" % (work_order_qty) + + stock_data = frappe.get_all("Stock Entry", + fields = field, + filters = { + "docstatus": 1, + "purpose": "Material Transfer for Manufacture", + "work_order": work_order + } + ) + + if stock_data: + return stock_data[0].valuation_rate + @frappe.whitelist() def get_uom_details(item_code, uom, qty): """Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}` From 8641f9ce77f4b37da733d4c4ad5737ed318f9c25 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 6 Jan 2020 15:07:06 +0530 Subject: [PATCH 07/25] fix: party type in payment request --- .../doctype/payment_request/payment_request.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e2510f675f..e1e43140c0 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -2,6 +2,16 @@ cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway") cur_frm.add_fetch("payment_gateway_account", "message", "message") +frappe.ui.form.on("Payment Request", { + setup: function(frm) { + frm.set_query("party_type", function() { + return { + query: "erpnext.setup.doctype.party_type.party_type.get_party_type", + }; + }); + } +}) + frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ if (frm.doc.reference_doctype) { frappe.call({ From ef0d26c16185112913d1ccf374c4296836f6e007 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 6 Jan 2020 15:34:15 +0530 Subject: [PATCH 08/25] feat: Validity for Item taxes (#20135) * feat: Validity for Item taxes * fix: Trigger for gst hsn code * fix: Sort taxes based on validity * fix: Validation for item tax template and filters based on validity * fix: Add missing semicolon * fix: Validate tax template only if item code available * fix: Do not validate or filter item tax template if no item taxes applied * fix: Consider item group for validating taxes * fix: Test cases for item tax validation * fix: Item tax template filtering fixes * fix: Add missing semicolon * fix: Remove unnecessary query --- .../sales_invoice/test_sales_invoice.py | 22 ++- erpnext/controllers/queries.py | 32 +++- erpnext/controllers/taxes_and_totals.py | 34 ++++ erpnext/public/js/controllers/buying.js | 6 + erpnext/public/js/controllers/transaction.js | 23 +++ .../doctype/gst_hsn_code/gst_hsn_code.py | 6 +- erpnext/selling/sales_common.js | 7 + erpnext/stock/doctype/item/item.js | 10 +- erpnext/stock/doctype/item_tax/item_tax.json | 145 ++++++------------ erpnext/stock/get_item_details.py | 41 ++++- 10 files changed, 212 insertions(+), 114 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 530bd893c0..a2a47b3a19 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest, copy, time -from frappe.utils import nowdate, flt, getdate, cint +from frappe.utils import nowdate, flt, getdate, cint, add_days from frappe.model.dynamic_links import get_dynamic_link_map from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice @@ -1847,6 +1847,26 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234') self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000) + def test_item_tax_validity(self): + item = frappe.get_doc("Item", "_Test Item 2") + + if item.taxes: + item.taxes = [] + item.save() + + item.append("taxes", { + "item_tax_template": "_Test Item Tax Template 1", + "valid_from": add_days(nowdate(), 1) + }) + + item.save() + + sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1) + sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1" + self.assertRaises(frappe.ValidationError, sales_invoice.save) + + item.taxes = [] + item.save() def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5c319008a8..d18f8e54d8 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -4,9 +4,9 @@ from __future__ import unicode_literals import frappe from frappe.desk.reportview import get_match_cond, get_filters_cond -from frappe.utils import nowdate +from frappe.utils import nowdate, getdate from collections import defaultdict - +from erpnext.stock.get_item_details import _get_item_tax_template # searches for active employees def employee_query(doctype, txt, searchfield, start, page_len, filters): @@ -486,7 +486,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters) @frappe.whitelist() def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): query = """ - select pr.name + select pr.name from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem where pr.docstatus = 1 and pritem.parent = pr.name and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt))) @@ -499,7 +499,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): query = """ - select pi.name + select pi.name from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem where pi.docstatus = 1 and piitem.parent = pi.name and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt))) @@ -508,3 +508,27 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code'))) return frappe.db.sql(query, filters) + +@frappe.whitelist() +def get_tax_template(doctype, txt, searchfield, start, page_len, filters): + + item_doc = frappe.get_cached_doc('Item', filters.get('item_code')) + item_group = filters.get('item_group') + taxes = item_doc.taxes or [] + + while item_group: + item_group_doc = frappe.get_cached_doc('Item Group', item_group) + taxes += item_group_doc.taxes or [] + item_group = item_group_doc.parent_item_group + + if not taxes: + return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """) + else: + args = { + 'item_code': filters.get('item_code'), + 'posting_date': filters.get('valid_from'), + 'tax_category': filters.get('tax_category') + } + + taxes = _get_item_tax_template(args, taxes, for_validate=True) + return [(d,) for d in set(taxes)] diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 049a837eaf..b52a07dbdf 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -8,6 +8,7 @@ from frappe import _, scrub from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction from erpnext.controllers.accounts_controller import validate_conversion_rate, \ validate_taxes_and_charges, validate_inclusive_tax +from erpnext.stock.get_item_details import _get_item_tax_template class calculate_taxes_and_totals(object): def __init__(self, doc): @@ -34,6 +35,7 @@ class calculate_taxes_and_totals(object): def _calculate(self): self.validate_conversion_rate() self.calculate_item_values() + self.validate_item_tax_template() self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() @@ -43,6 +45,38 @@ class calculate_taxes_and_totals(object): self._cleanup() self.calculate_total_net_weight() + def validate_item_tax_template(self): + for item in self.doc.get('items'): + if item.item_code and item.get('item_tax_template'): + item_doc = frappe.get_cached_doc("Item", item.item_code) + args = { + 'tax_category': self.doc.get('tax_category'), + 'posting_date': self.doc.get('posting_date'), + 'bill_date': self.doc.get('bill_date'), + 'transaction_date': self.doc.get('transaction_date') + } + + item_group = item_doc.item_group + item_group_taxes = [] + + while item_group: + item_group_doc = frappe.get_cached_doc('Item Group', item_group) + item_group_taxes += item_group_doc.taxes or [] + item_group = item_group_doc.parent_item_group + + item_taxes = item_doc.taxes or [] + + if not item_group_taxes and (not item_taxes): + # No validation if no taxes in item or item group + continue + + taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True) + + if item.item_tax_template not in taxes: + frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format( + item.idx, frappe.bold(item.item_code) + )) + def validate_conversion_rate(self): # validate conversion rate company_currency = erpnext.get_company_currency(self.doc.company) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 926227b24c..3d4c4a6459 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -107,6 +107,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ filters:{ 'item_code': row.item_code } } }); + + if(this.frm.fields_dict["items"].grid.get_field('item_code')) { + this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) { + return me.set_query_for_item_tax_template(doc, cdt, cdn) + }); + } }, refresh: function(doc) { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 748e623730..51ab48a3ab 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1700,6 +1700,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + set_query_for_item_tax_template: function(doc, cdt, cdn) { + + var item = frappe.get_doc(cdt, cdn); + if(!item.item_code) { + frappe.throw(__("Please enter Item Code to get item taxes")); + } else { + + let filters = { + 'item_code': item.item_code, + 'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date, + 'item_group': item.item_group, + } + + if (doc.tax_category) + filters['tax_category'] = doc.tax_category; + + return { + query: "erpnext.controllers.queries.get_tax_template", + filters: filters + } + } + }, + payment_terms_template: function() { var me = this; const doc = this.frm.doc; diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py index fa2cb1299a..86cd4d1545 100644 --- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py +++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py @@ -25,5 +25,9 @@ def update_item_document(items, taxes): item_to_be_updated.taxes = [] for tax in taxes: tax = frappe._dict(tax) - item_to_be_updated.append("taxes", {'item_tax_template': tax.item_tax_template, 'tax_category': tax.tax_category}) + item_to_be_updated.append("taxes", { + 'item_tax_template': tax.item_tax_template, + 'tax_category': tax.tax_category, + 'valid_from': tax.valid_from + }) item_to_be_updated.save() \ No newline at end of file diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 1c9b30b828..8278745a80 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -84,6 +84,13 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ return me.set_query_for_batch(doc, cdt, cdn) }); } + + if(this.frm.fields_dict["items"].grid.get_field('item_code')) { + this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) { + return me.set_query_for_item_tax_template(doc, cdt, cdn) + }); + } + }, refresh: function() { diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index e3d356f93b..253390ac50 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -49,7 +49,7 @@ frappe.ui.form.on("Item", { if (!frm.doc.is_fixed_asset) { erpnext.item.make_dashboard(frm); } - + if (frm.doc.is_fixed_asset) { frm.trigger('is_fixed_asset'); frm.trigger('auto_create_assets'); @@ -136,14 +136,14 @@ frappe.ui.form.on("Item", { frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0); }, - gst_hsn_code: function(frm){ - if(!frm.doc.taxes){ - frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc=>{ - frm.doc.taxes = []; + gst_hsn_code: function(frm) { + if(!frm.doc.taxes.length) { + frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => { $.each(hsn_doc.taxes || [], function(i, tax) { let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes'); a.item_tax_template = tax.item_tax_template; a.tax_category = tax.tax_category; + a.valid_from = tax.valid_from; frm.refresh_field('taxes'); }); }); diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json index 37daa2938d..a93e4636ad 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.json +++ b/erpnext/stock/doctype/item_tax/item_tax.json @@ -1,107 +1,50 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:28:01", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "actions": [], + "creation": "2013-02-22 01:28:01", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_tax_template", + "tax_category", + "valid_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_tax_template", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Tax Template", - "length": 0, - "no_copy": 0, - "oldfieldname": "tax_type", - "oldfieldtype": "Link", - "options": "Item Tax Template", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_tax_template", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Tax Template", + "oldfieldname": "tax_type", + "oldfieldtype": "Link", + "options": "Item Tax Template", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "", - "fieldname": "tax_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Tax Category", - "length": 0, - "no_copy": 0, - "oldfieldname": "tax_rate", - "oldfieldtype": "Currency", - "options": "Tax Category", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "tax_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Tax Category", + "oldfieldname": "tax_rate", + "oldfieldtype": "Currency", + "options": "Tax Category" + }, + { + "fieldname": "valid_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Valid From" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-12-21 23:52:40.798944", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Tax", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2019-12-28 21:54:40.807849", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Tax", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 76644ed846..4e5b933a3f 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _, throw -from frappe.utils import flt, cint, add_days, cstr, add_months +from frappe.utils import flt, cint, add_days, cstr, add_months, getdate import json, copy from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type from erpnext.setup.utils import get_exchange_rate @@ -52,6 +52,16 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru out = get_basic_details(args, item, overwrite_warehouse) + if isinstance(doc, string_types): + doc = json.loads(doc) + + if doc and doc.get('doctype') == 'Purchase Invoice': + args['bill_date'] = doc.get('bill_date') + + if doc: + args['posting_date'] = doc.get('posting_date') + args['transaction_date'] = doc.get('transaction_date') + get_item_tax_template(args, item, out) out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \ else out.get("item_tax_template"), as_json=True) @@ -395,7 +405,34 @@ def get_item_tax_template(args, item, out): item_tax_template = _get_item_tax_template(args, item_group_doc.taxes, out) item_group = item_group_doc.parent_item_group -def _get_item_tax_template(args, taxes, out): +def _get_item_tax_template(args, taxes, out={}, for_validate=False): + taxes_with_validity = [] + taxes_with_no_validity = [] + + for tax in taxes: + if tax.valid_from: + # In purchase Invoice first preference will be given to supplier invoice date + # if supplier date is not present then posting date + validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') + + if getdate(tax.valid_from) <= getdate(validation_date): + taxes_with_validity.append(tax) + else: + taxes_with_no_validity.append(tax) + + if taxes_with_validity: + taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True) + else: + taxes = taxes_with_no_validity + + if for_validate: + return [tax.item_tax_template for tax in taxes if (cstr(tax.tax_category) == cstr(args.get('tax_category')) \ + and (tax.item_tax_template not in taxes))] + + # all templates have validity and no template is valid + if not taxes_with_validity and (not taxes_with_no_validity): + return None + for tax in taxes: if cstr(tax.tax_category) == cstr(args.get("tax_category")): out["item_tax_template"] = tax.item_tax_template From 7c8aaba44926e1430669fce78e09f89f0c555a1e Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 7 Jan 2020 11:23:53 +0530 Subject: [PATCH 09/25] fix: integer UOM check in transactions (#20176) --- erpnext/utilities/transaction_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index f845cef9b1..2099810846 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -164,8 +164,8 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None): qty_fields = [qty_fields] distinct_uoms = list(set([d.get(uom_field) for d in doc.get_all_children()])) - integer_uoms = filter(lambda uom: frappe.db.get_value("UOM", uom, - "must_be_whole_number", cache=True) or None, distinct_uoms) + integer_uoms = list(filter(lambda uom: frappe.db.get_value("UOM", uom, + "must_be_whole_number", cache=True) or None, distinct_uoms)) if not integer_uoms: return From 971458043da357a004c8ec298d8131c1f669a717 Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Tue, 7 Jan 2020 05:59:35 +0000 Subject: [PATCH 10/25] fix: add new delivery note button in Sales Order (#20199) * fix: add new delivery note button in Sales Order * fix: removed debugger Co-authored-by: Nabin Hait --- erpnext/selling/doctype/sales_order/sales_order.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index cb016a1a74..fa765dfaad 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -6,7 +6,7 @@ frappe.ui.form.on("Sales Order", { setup: function(frm) { frm.custom_make_buttons = { - 'Delivery Note': 'Delivery', + 'Delivery Note': 'Delivery Note', 'Pick List': 'Pick List', 'Sales Invoice': 'Invoice', 'Material Request': 'Material Request', @@ -135,7 +135,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { - allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)) && !this.frm.doc.skip_delivery_note @@ -697,4 +696,4 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }); } }); -$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm})); \ No newline at end of file +$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm})); From 88737e38f2fab95215c7a2f6ec4626d4541c6808 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Tue, 7 Jan 2020 11:30:22 +0530 Subject: [PATCH 11/25] fix(item_tax_template): fetch parent account if not set (#20198) --- .../v12_0/move_item_tax_to_item_tax_template.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index e47344bd92..5a12795000 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -60,10 +60,10 @@ def execute(): 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice' ] - + for dt in doctypes: for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item` - where ifnull(item_tax_rate, '') not in ('', '{{}}') + where ifnull(item_tax_rate, '') not in ('', '{{}}') and item_tax_template is NULL""".format(dt), as_dict=1): item_tax_map = json.loads(d.item_tax_rate) item_tax_template_name = get_item_tax_template(item_tax_templates, @@ -98,12 +98,15 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp company = get_company(parts[-1], parenttype, parent) parent_account = frappe.db.get_value("Account", filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") + if not parent_account: + parent_account = frappe.db.get_value("Account", + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company}) filters = { "account_name": account_name, - "company": company, - "account_type": "Tax", - "parent_account": parent_account - } + "company": company, + "account_type": "Tax", + "parent_account": parent_account + } tax_type = frappe.db.get_value("Account", filters) if not tax_type: account = frappe.new_doc("Account") From aab96b7fbf69708f00ad91b48c555720663af3c3 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Tue, 7 Jan 2020 07:07:08 +0100 Subject: [PATCH 12/25] Update de.csv (#20205) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changed translation of "leave/-s" to "Urlaub/Urlaubstage" instead of "Blätter" (Blätter is leaves as in tree or sheet as in paper) --- erpnext/translations/de.csv | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index cdff3ff422..954f6db5b4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -304,7 +304,7 @@ apps/erpnext/erpnext/selling/report/sales_partner_target_variance_based_on_item_ DocType: BOM,Total Cost,Gesamtkosten apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.js,Allocation Expired!,Zuteilung abgelaufen! DocType: Soil Analysis,Ca/K,Ca / K -DocType: Leave Type,Maximum Carry Forwarded Leaves,Maximale Anzahl weitergeleiteter Blätter +DocType: Leave Type,Maximum Carry Forwarded Leaves,Obergrenze für übertragbaren Urlaub erreicht DocType: Salary Slip,Employee Loan,MItarbeiterdarlehen DocType: Additional Salary,HR-ADS-.YY.-.MM.-,HR-ADS-.YY .-. MM.- DocType: Fee Schedule,Send Payment Request Email,Zahlungaufforderung per E-Mail versenden @@ -893,7 +893,7 @@ DocType: Supplier Scorecard Standing,Notify Other,Andere benachrichtigen DocType: Vital Signs,Blood Pressure (systolic),Blutdruck (systolisch) apps/erpnext/erpnext/controllers/buying_controller.py,{0} {1} is {2},{0} {1} ist {2} DocType: Item Price,Valid Upto,Gültig bis -DocType: Leave Type,Expire Carry Forwarded Leaves (Days),Verfallsdatum für weitergeleitete Blätter (Tage) +DocType: Leave Type,Expire Carry Forwarded Leaves (Days),Verfallsdatum für übertragenen Urlaub (Tage) DocType: Training Event,Workshop,Werkstatt DocType: Supplier Scorecard Scoring Standing,Warn Purchase Orders,Warnung Bestellungen apps/erpnext/erpnext/utilities/user_progress.py,List a few of your customers. They could be organizations or individuals.,Bitte ein paar Kunden angeben. Dies können Firmen oder Einzelpersonen sein. @@ -1103,7 +1103,7 @@ apps/erpnext/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py, DocType: Payroll Entry,Select Payment Account to make Bank Entry,Wählen Sie ein Zahlungskonto für die Buchung apps/erpnext/erpnext/config/accounting.py,Opening and Closing,Öffnen und Schließen DocType: Hotel Settings,Default Invoice Naming Series,Standard-Rechnungsnummernkreis -apps/erpnext/erpnext/utilities/activation.py,"Create Employee records to manage leaves, expense claims and payroll","Erstellen Sie Mitarbeiterdaten Blätter, Spesenabrechnung und Gehaltsabrechnung zu verwalten" +apps/erpnext/erpnext/utilities/activation.py,"Create Employee records to manage leaves, expense claims and payroll","Erstellen Sie Mitarbeiterdaten für Urlaubs, Spesenabrechnung und Gehaltsabrechnung zu verwalten" apps/erpnext/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js,An error occurred during the update process,Während des Aktualisierungsprozesses ist ein Fehler aufgetreten DocType: Restaurant Reservation,Restaurant Reservation,Restaurant Reservierung apps/erpnext/erpnext/public/js/hub/Sidebar.vue,Your Items,Ihre Artikel @@ -1548,7 +1548,7 @@ DocType: Share Transfer,To Shareholder,An den Aktionär apps/erpnext/erpnext/accounts/doctype/journal_entry/journal_entry.py,{0} against Bill {1} dated {2},{0} zu Rechnung {1} vom {2} apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py,From State,Aus dem Staat apps/erpnext/erpnext/utilities/user_progress.py,Setup Institution,Einrichtung Einrichtung -apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.py,Allocating leaves...,Blätter zuordnen... +apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.py,Allocating leaves...,Urlaub zuordnen... DocType: Program Enrollment,Vehicle/Bus Number,Fahrzeug / Bus Nummer apps/erpnext/erpnext/public/js/call_popup/call_popup.js,Create New Contact,Neuen Kontakt erstellen apps/erpnext/erpnext/education/doctype/course/course.js,Course Schedule,Kurstermine @@ -1806,7 +1806,7 @@ apps/erpnext/erpnext/setup/setup_wizard/data/industry_type.py,Chemical,Chemische DocType: Salary Component Account,Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.,"Standard Bank / Geldkonto wird automatisch in Gehalts Journal Entry aktualisiert werden, wenn dieser Modus ausgewählt ist." DocType: Quiz,Latest Attempt,Letzter Versuch DocType: Quiz Result,Quiz Result,Quiz-Ergebnis -apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total leaves allocated is mandatory for Leave Type {0},Die Gesamtzahl der zugewiesenen Blätter ist für Abwesenheitsart {0} erforderlich. +apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total leaves allocated is mandatory for Leave Type {0},Die Gesamtzahl der zugewiesenen Urlaube ist für Abwesenheitsart {0} erforderlich. apps/erpnext/erpnext/controllers/sales_and_purchase_return.py,Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}" apps/erpnext/erpnext/utilities/user_progress.py,Meter,Meter DocType: Workstation,Electricity Cost,Stromkosten @@ -1916,7 +1916,7 @@ apps/erpnext/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html,Go to apps/erpnext/erpnext/templates/pages/order.js,Pay Remaining,Verbleibende Bezahlung DocType: Purchase Invoice Item,Manufacturer,Hersteller DocType: Landed Cost Item,Purchase Receipt Item,Kaufbeleg-Artikel -DocType: Leave Allocation,Total Leaves Encashed,Insgesamt Blätter umkränzt +DocType: Leave Allocation,Total Leaves Encashed,Summe ausbezahlter Urlaubstage DocType: POS Profile,Sales Invoice Payment,Ausgangsrechnung-Zahlungen DocType: Quality Inspection Template,Quality Inspection Template Name,Name der Qualitätsinspektionsvorlage DocType: Project,First Email,Erste E-Mail @@ -2540,7 +2540,7 @@ Used for Taxes and Charges",Die Tabelle Steuerdetails wird aus dem Artikelstamm apps/erpnext/erpnext/hr/doctype/employee/employee.py,Employee cannot report to himself.,Mitarbeiter können nicht an sich selbst Bericht erstatten apps/erpnext/erpnext/templates/pages/order.html,Rate:,Bewertung: DocType: Bank Account,Change this date manually to setup the next synchronization start date,"Ändern Sie dieses Datum manuell, um das nächste Startdatum für die Synchronisierung festzulegen" -DocType: Leave Type,Max Leaves Allowed,Max Blätter erlaubt +DocType: Leave Type,Max Leaves Allowed,Höchstzahl erlaubter Urlaubstage DocType: Account,"If the account is frozen, entries are allowed to restricted users.","Wenn das Konto gesperrt ist, sind einem eingeschränkten Benutzerkreis Buchungen erlaubt." DocType: Email Digest,Bank Balance,Kontostand apps/erpnext/erpnext/controllers/accounts_controller.py,Accounting Entry for {0}: {1} can only be made in currency: {2},Eine Buchung für {0}: {1} kann nur in der Währung: {2} vorgenommen werden @@ -2990,7 +2990,7 @@ DocType: Student Group Creation Tool,Separate course based Group for every Batch apps/erpnext/erpnext/config/support.py,Single unit of an Item.,Einzelnes Element eines Artikels DocType: Fee Category,Fee Category,Gebührenkategorie DocType: Agriculture Task,Next Business Day,Nächster Arbeitstag -apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Allocated Leaves,Zugewiesene Blätter +apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Allocated Leaves,Genehmigter Urlaub DocType: Drug Prescription,Dosage by time interval,Dosierung nach Zeitintervall apps/erpnext/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html,Total Taxable Value,Steuerpflichtiger Gesamtwert DocType: Cash Flow Mapper,Section Header,Abschnitt Kopfzeile @@ -3774,10 +3774,10 @@ apps/erpnext/erpnext/public/js/templates/address_list.html,New Address,Neue Adre DocType: Quality Inspection,Sample Size,Stichprobenumfang apps/erpnext/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py,Please enter Receipt Document,Bitte geben Sie Eingangsbeleg apps/erpnext/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py,All items have already been invoiced,Alle Artikel sind bereits abgerechnet -apps/erpnext/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py,Leaves Taken,Blätter genommen +apps/erpnext/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py,Leaves Taken,Urlaubstage genommen apps/erpnext/erpnext/stock/doctype/packing_slip/packing_slip.py,Please specify a valid 'From Case No.',"Bitte eine eine gültige ""Von Fall Nr."" angeben" apps/erpnext/erpnext/accounts/doctype/cost_center/cost_center_tree.js,Further cost centers can be made under Groups but entries can be made against non-Groups,"Weitere Kostenstellen können unter Gruppen angelegt werden, aber Buchungen können zu nicht-Gruppen erstellt werden" -apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Die insgesamt zugewiesenen Blätter sind mehr Tage als die maximale Zuweisung von {0} Abwesenheitsart für den Mitarbeiter {1} in der Periode +apps/erpnext/erpnext/hr/doctype/leave_allocation/leave_allocation.py,Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Die insgesamt zugewiesenen Urlaubstage sind mehr Tage als die maximale Zuweisung von {0} Abwesenheitsart für den Mitarbeiter {1} in der Periode DocType: Branch,Branch,Betrieb apps/erpnext/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html,"Other outward supplies(Nil rated,Exempted)","Sonstige Auslandslieferungen (ohne Rating, ausgenommen)" DocType: Soil Analysis,Ca/(K+Ca+Mg),Ca / (K + Ca + Mg) @@ -4097,7 +4097,7 @@ DocType: Work Order,Actual End Date,Tatsächliches Enddatum DocType: Cash Flow Mapping,Is Finance Cost Adjustment,Ist Finanzkostenanpassung DocType: BOM,Operating Cost (Company Currency),Betriebskosten (Gesellschaft Währung) DocType: Authorization Rule,Applicable To (Role),Anwenden auf (Rolle) -apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Pending Leaves,Ausstehende Blätter +apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Pending Leaves,Schwebende Urlaubstage DocType: BOM Update Tool,Replace BOM,Erstelle Stückliste apps/erpnext/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py,Code {0} already exist,Code {0} existiert bereits DocType: Patient Encounter,Procedures,Verfahren @@ -4342,7 +4342,7 @@ apps/erpnext/erpnext/accounts/report/asset_depreciations_and_balances/asset_depr DocType: Sales Invoice,Is Return (Credit Note),ist Rücklieferung (Gutschrift) apps/erpnext/erpnext/manufacturing/doctype/job_card/job_card.js,Start Job,Job starten apps/erpnext/erpnext/assets/doctype/asset_movement/asset_movement.py,Serial no is required for the asset {0},Für Vermögenswert {0} ist eine Seriennr. Erforderlich. -DocType: Leave Control Panel,Allocate Leaves,Blätter zuweisen +DocType: Leave Control Panel,Allocate Leaves,Urlaubstage zuweisen apps/erpnext/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py,Disabled template must not be default template,Deaktivierte Vorlage darf nicht Standardvorlage sein DocType: Pricing Rule,Price or Product Discount,Preis- oder Produktrabatt apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.py,For row {0}: Enter planned qty,Für Zeile {0}: Geben Sie die geplante Menge ein @@ -4440,7 +4440,7 @@ DocType: Loan,Loan Application,Kreditantrag DocType: Crop,Scientific Name,Wissenschaftlicher Name DocType: Healthcare Service Unit,Service Unit Type,Serviceeinheitstyp DocType: Bank Account,Branch Code,Bankleitzahl / BIC -apps/erpnext/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py,Total Leaves,insgesamt Blätter +apps/erpnext/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py,Total Leaves,summe der Urlaubstage DocType: Customer,"Reselect, if the chosen contact is edited after save","Wählen Sie erneut, wenn der ausgewählte Kontakt nach dem Speichern bearbeitet wird" DocType: Quality Procedure,Parent Procedure,Übergeordnetes Verfahren DocType: Patient Encounter,In print,in Druckbuchstaben @@ -5658,7 +5658,7 @@ apps/erpnext/erpnext/education/report/course_wise_assessment_report/course_wise_ DocType: Depreciation Schedule,Finance Book Id,Finanzbuch-ID DocType: Item,Safety Stock,Sicherheitsbestand DocType: Healthcare Settings,Healthcare Settings,Gesundheitswesen -apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Total Allocated Leaves,Insgesamt zugeteilte Blätter +apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Total Allocated Leaves,Insgesamt zugeteilte Urlaubstage apps/erpnext/erpnext/projects/doctype/task/task.py,Progress % for a task cannot be more than 100.,Fortschritt-% eines Vorgangs darf nicht größer 100 sein. DocType: Stock Reconciliation Item,Before reconciliation,Vor Ausgleich apps/erpnext/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py,To {0},An {0} @@ -5769,7 +5769,7 @@ apps/erpnext/erpnext/accounts/doctype/bank_account/test_bank_account.py,BankAcco DocType: Normal Test Items,Normal Test Items,Normale Testartikel DocType: QuickBooks Migrator,Company Settings,Unternehmenseinstellungen DocType: Additional Salary,Overwrite Salary Structure Amount,Gehaltsstruktur überschreiben -DocType: Leave Ledger Entry,Leaves,Blätter +DocType: Leave Ledger Entry,Leaves,Urlaubstage DocType: Student Language,Student Language,Student Sprache DocType: Cash Flow Mapping,Is Working Capital,Ist Arbeitskapital apps/erpnext/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js,Submit Proof,Nachweis einreichen @@ -6362,7 +6362,7 @@ apps/erpnext/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax DocType: Sales Partner,Contact Desc,Kontakt-Beschr. DocType: Email Digest,Send regular summary reports via Email.,Regelmäßig zusammenfassende Berichte per E-Mail senden. apps/erpnext/erpnext/hr/doctype/expense_claim/expense_claim.py,Please set default account in Expense Claim Type {0},Bitte setzen Sie Standardkonto in Kostenabrechnung Typ {0} -apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Available Leaves,Verfügbare Blätter +apps/erpnext/erpnext/hr/doctype/leave_application/leave_application_dashboard.html,Available Leaves,Verfügbare Urlaubstage DocType: Assessment Result,Student Name,Name des Studenten DocType: Hub Tracked Item,Item Manager,Artikel-Manager apps/erpnext/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py,Payroll Payable,Payroll Kreditoren @@ -7508,7 +7508,7 @@ DocType: Pricing Rule,Percentage,Prozentsatz apps/erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py,Item {0} must be a stock Item,Artikel {0} muss ein Lagerartikel sein DocType: Manufacturing Settings,Default Work In Progress Warehouse,Standard-Fertigungslager apps/erpnext/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js,"Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Schedules für {0} Überlappungen, möchten Sie nach Überlappung überlappender Slots fortfahren?" -apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.js,Grant Leaves,Grant Blätter +apps/erpnext/erpnext/hr/doctype/leave_period/leave_period.js,Grant Leaves,Urlaubstage gewähren DocType: Restaurant,Default Tax Template,Standardsteuervorlage apps/erpnext/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py,{0} Students have been enrolled,{0} Studenten wurden angemeldet DocType: Fees,Student Details,Studenten Details From 6b316cd7cb112168cfaff1a262320c6ca1792953 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 7 Jan 2020 11:48:38 +0530 Subject: [PATCH 13/25] feat: Backdated leave application (#20201) * fix: only HR Managers can make backdated leave applications * fix (leave application): error message changed and user check modified * fix: Move hardcoded logic to HR Settings * fix: added role as mandetory field on check restict field * fix: minor changes --- erpnext/hr/doctype/hr_settings/hr_settings.js | 3 ++- erpnext/hr/doctype/hr_settings/hr_settings.json | 17 ++++++++++++++++- .../leave_application/leave_application.py | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index 4004c1cc2d..2e8c99f061 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -19,5 +19,6 @@ frappe.ui.form.on('HR Settings', { } frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); } + frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } -}); +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 6cb0e211f9..90f49886f8 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -23,10 +23,12 @@ "leave_settings", "leave_approval_notification_template", "leave_status_notification_template", + "role_allowed_to_create_backdated_leave_application", "column_break_18", "leave_approver_mandatory_in_leave_application", "show_leaves_of_all_department_members_in_calendar", "auto_leave_encashment", + "restrict_backdated_leave_application", "hiring_settings", "check_vacancies" ], @@ -169,13 +171,26 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "default": "0", + "fieldname": "restrict_backdated_leave_application", + "fieldtype": "Check", + "label": "Restrict Backdated Leave Application" + }, + { + "depends_on": "eval:doc.restrict_backdated_leave_application == 1", + "fieldname": "role_allowed_to_create_backdated_leave_application", + "fieldtype": "Link", + "label": "Role Allowed to Create Backdated Leave Application", + "options": "Role" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-31 14:28:32.004121", + "modified": "2020-01-06 18:46:30.189815", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 5222712ea2..7594cb72ae 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -79,6 +79,12 @@ class LeaveApplication(Document): frappe.throw(_("{0} applicable after {1} working days").format(self.leave_type, leave_type.applicable_after)) def validate_dates(self): + if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"): + if self.from_date and self.from_date < frappe.utils.today(): + allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application") + if allowed_role not in frappe.get_roles(): + frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role)) + if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): frappe.throw(_("To date cannot be before from date")) From 7ae11005c406dea062dd83455c2c38f71e7585f7 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 7 Jan 2020 13:02:13 +0530 Subject: [PATCH 14/25] fix: UX improvement. move trigger on fieldname restrict_backdated_leave_application (#20211) --- erpnext/hr/doctype/hr_settings/hr_settings.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index 2e8c99f061..b629b42f9b 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -2,10 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('HR Settings', { - refresh: function(frm) { - - }, - encrypt_salary_slips_in_emails: function(frm) { let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; frm.set_df_property('password_policy', 'reqd', encrypt_state); @@ -19,6 +15,9 @@ frappe.ui.form.on('HR Settings', { } frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); } + }, + + restrict_backdated_leave_application: function(frm) { frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } }); \ No newline at end of file From c72de6edaa3492a44128248ae2568167239528d9 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 7 Jan 2020 13:06:08 +0530 Subject: [PATCH 15/25] fix: Added description and title to supplier selection popup in Material Request. (#20179) --- erpnext/stock/doctype/material_request/material_request.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 99195c300d..935d61310e 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -230,7 +230,8 @@ frappe.ui.form.on('Material Request', { make_purchase_order: function(frm) { frappe.prompt( - {fieldname:'default_supplier', label: __('For Default Supplier (optional)'), fieldtype: 'Link', options: 'Supplier'}, + {fieldname:'default_supplier', label: __('For Default Supplier (optional)'), description: __('Selected Supplier\ + must be the Default Supplier of one of the items below.'), fieldtype: 'Link', options: 'Supplier'}, (values) => { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order", @@ -238,7 +239,8 @@ frappe.ui.form.on('Material Request', { args: { default_supplier: values.default_supplier }, run_link_triggers: true }); - } + }, + __('Enter Supplier') ) }, From 229e5398b55fc9eaecd34222567e1bc9af314567 Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Thu, 9 Jan 2020 06:51:26 +0000 Subject: [PATCH 16/25] fix: escape % in customer name (#20202) --- .../doctype/authorization_control/authorization_control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py index 7db703fadf..0c52b834de 100644 --- a/erpnext/setup/doctype/authorization_control/authorization_control.py +++ b/erpnext/setup/doctype/authorization_control/authorization_control.py @@ -76,7 +76,7 @@ class AuthorizationControl(TransactionBase): add_cond = '' auth_value = av_dis - if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\\'")+"'" + if val == 1: add_cond += " and system_user = '"+ frappe.db.escape(session['user'])+"'" elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')") else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''" @@ -85,7 +85,7 @@ class AuthorizationControl(TransactionBase): if doc_obj: if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer else: customer = doc_obj.customer_name - add_cond = " and master_name = '"+cstr(customer).replace("'", "\\'")+"'" + add_cond = " and master_name = '"+ frappe.db.escape(customer) +"'" if based_on == 'Itemwise Discount': if doc_obj: for t in doc_obj.get("items"): From ccdf19314d317eb1e7b4eda9f2040aed989ba554 Mon Sep 17 00:00:00 2001 From: Don-Leopardo <46027152+Don-Leopardo@users.noreply.github.com> Date: Thu, 9 Jan 2020 03:54:43 -0300 Subject: [PATCH 17/25] fix: min_qty and valid_from for the price selection (#20217) --- erpnext/public/js/controllers/transaction.js | 2 +- erpnext/stock/get_item_details.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 51ab48a3ab..8cfde8cf97 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -968,7 +968,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ qty: function(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); - this.conversion_factor(doc, cdt, cdn, true); + this.conversion_factor(doc, cdt, cdn, false); this.apply_pricing_rule(item, true); }, diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4e5b933a3f..b80f99d551 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -610,7 +610,7 @@ def get_item_price(args, item_code, ignore_party=False): return frappe.db.sql(""" select name, price_list_rate, uom from `tabItem Price` {conditions} - order by uom desc, min_qty desc """.format(conditions=conditions), args) + order by uom desc, min_qty desc, valid_from desc """.format(conditions=conditions), args) def get_price_list_rate_for(args, item_code): """ @@ -643,10 +643,15 @@ def get_price_list_rate_for(args, item_code): if desired_qty and check_packing_list(price_list_rate[0][0], desired_qty, item_code): item_price_data = price_list_rate else: - for field in ["customer", "supplier", "min_qty"]: + for field in ["customer", "supplier"]: del item_price_args[field] general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) + + if not general_price_list_rate: + del item_price_args["min_qty"] + general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) + if not general_price_list_rate and args.get("uom") != args.get("stock_uom"): item_price_args["uom"] = args.get("stock_uom") general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party")) From 1612432f45416a069993b180be59c1258d3f6914 Mon Sep 17 00:00:00 2001 From: Pranav Nachnekar Date: Thu, 9 Jan 2020 08:02:15 +0000 Subject: [PATCH 18/25] fix: parent item should not appear in raw material request dialog (#20216) --- erpnext/selling/doctype/sales_order/sales_order.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 346200262d..e7cbf405e1 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -376,6 +376,9 @@ class SalesOrder(SellingController): def get_work_order_items(self, for_raw_material_request=0): '''Returns items with BOM that already do not have a linked work order''' items = [] + item_codes = [i.item_code for i in self.items] + product_bundle_parents = [pb.new_item_code for pb in frappe.get_all("Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"])] + for table in [self.items, self.packed_items]: for i in table: bom = get_default_bom_item(i.item_code) @@ -387,7 +390,7 @@ class SalesOrder(SellingController): else: pending_qty = stock_qty - if pending_qty: + if pending_qty and i.item_code not in product_bundle_parents: if bom: items.append(dict( name= i.name, From 287f491f7bd07e166b60b55f5b4ecd6d41c3f993 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 9 Jan 2020 13:35:06 +0530 Subject: [PATCH 19/25] fix: Employee name in Report trial balance for party (#20223) * fix: Employee name in Report trial balance for party * fix: Add account filter in trial balance for party report Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- .../trial_balance_for_party.js | 15 +++++++++++++++ .../trial_balance_for_party.py | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 873c65ebf1..0e93035a35 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -65,6 +65,21 @@ frappe.query_reports["Trial Balance for Party"] = { return party_type; } }, + { + "fieldname": "account", + "label": __("Account"), + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Account", + "filters": { + "company": company, + } + } + } + }, { "fieldname": "show_zero_values", "label": __("Show zero values"), diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py index 3e47906a98..78c7e439d3 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py @@ -20,7 +20,7 @@ def execute(filters=None): def get_data(filters, show_party_name): if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'): party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type'))) - if filters.get('party_type') == 'Student': + elif filters.get('party_type') == 'Student': party_name_field = 'first_name' elif filters.get('party_type') == 'Shareholder': party_name_field = 'title' @@ -96,13 +96,19 @@ def get_data(filters, show_party_name): return data def get_opening_balances(filters): + + account_filter = '' + if filters.get('account'): + account_filter = "and account = %s" % (frappe.db.escape(filters.get('account'))) + gle = frappe.db.sql(""" select party, sum(debit) as opening_debit, sum(credit) as opening_credit from `tabGL Entry` where company=%(company)s and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != '' and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes') - group by party""", { + {account_filter} + group by party""".format(account_filter=account_filter), { "company": filters.company, "from_date": filters.from_date, "party_type": filters.party_type @@ -116,6 +122,11 @@ def get_opening_balances(filters): return opening def get_balances_within_period(filters): + + account_filter = '' + if filters.get('account'): + account_filter = "and account = %s" % (frappe.db.escape(filters.get('account'))) + gle = frappe.db.sql(""" select party, sum(debit) as debit, sum(credit) as credit from `tabGL Entry` @@ -123,7 +134,8 @@ def get_balances_within_period(filters): and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != '' and posting_date >= %(from_date)s and posting_date <= %(to_date)s and ifnull(is_opening, 'No') = 'No' - group by party""", { + {account_filter} + group by party""".format(account_filter=account_filter), { "company": filters.company, "from_date": filters.from_date, "to_date": filters.to_date, From d4d7d211ead84d26a029fbed9e6e6d06011a4bf3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Jan 2020 15:05:05 +0530 Subject: [PATCH 20/25] feat: report to show difference between stock and account value (#20186) --- erpnext/accounts/utils.py | 6 + erpnext/config/stock.py | 6 + erpnext/stock/doctype/warehouse/warehouse.py | 23 ++- .../__init__.py | 0 .../stock_and_account_value_comparison.js | 37 +++++ .../stock_and_account_value_comparison.json | 28 ++++ .../stock_and_account_value_comparison.py | 131 ++++++++++++++++++ .../report/stock_balance/stock_balance.py | 2 +- 8 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 erpnext/stock/report/stock_and_account_value_comparison/__init__.py create mode 100644 erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js create mode 100644 erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json create mode 100644 erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 89c8467da2..e01d6d5dc7 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -891,3 +891,9 @@ def get_allow_cost_center_in_entry_of_bs_account(): def generator(): return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account')) return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True) + +def get_stock_accounts(company): + return frappe.get_all("Account", filters = { + "account_type": "Stock", + "company": company + }) \ No newline at end of file diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 03e26b2b86..dd35f5ab36 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -348,6 +348,12 @@ def get_data(): "is_query_report": True, "name": "Subcontracted Item To Be Received", "doctype": "Purchase Order" + }, + { + "type": "report", + "is_query_report": True, + "name": "Stock and Account Value Comparison", + "doctype": "Stock Ledger Entry" } ] }, diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 6cdb56be7f..6ed6044f32 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -177,7 +177,26 @@ def convert_to_group_or_ledger(): return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger() def get_child_warehouses(warehouse): - p_warehouse = frappe.get_doc("Warehouse", warehouse) + lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt]) return frappe.db.sql_list("""select name from `tabWarehouse` - where lft >= %s and rgt =< %s""", (p_warehouse.lft, p_warehouse.rgt)) + where lft >= %s and rgt =< %s""", (lft, rgt)) + +def get_warehouses_based_on_account(account, company=None): + warehouses = [] + for d in frappe.get_all("Warehouse", fields = ["name", "is_group"], + filters = {"account": account}): + if d.is_group: + warehouses.extend(get_child_warehouses(d.name)) + else: + warehouses.append(d.name) + + if (not warehouses and company and + frappe.get_cached_value("Company", company, "default_inventory_account") == account): + warehouses = [d.name for d in frappe.get_all("Warehouse", filters={'is_group': 0})] + + if not warehouses: + frappe.throw(_("Warehouse not found against the account {0}") + .format(account)) + + return warehouses \ No newline at end of file diff --git a/erpnext/stock/report/stock_and_account_value_comparison/__init__.py b/erpnext/stock/report/stock_and_account_value_comparison/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js new file mode 100644 index 0000000000..7a170beec3 --- /dev/null +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js @@ -0,0 +1,37 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Stock and Account Value Comparison"] = { + "filters": [ + { + "label": __("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "label": __("Account"), + "fieldname": "account", + "fieldtype": "Link", + "options": "Account", + get_query: function() { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + "account_type": "Stock", + "company": company + } + } + } + }, + { + "label": __("As On Date"), + "fieldname": "as_on_date", + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + }, + ] +}; diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json new file mode 100644 index 0000000000..021159a89d --- /dev/null +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.json @@ -0,0 +1,28 @@ +{ + "add_total_row": 1, + "creation": "2020-01-09 14:42:45.254751", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-01-09 14:42:45.254751", + "modified_by": "Administrator", + "module": "Stock", + "name": "Stock and Account Value Comparison", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Stock Ledger Entry", + "report_name": "Stock and Account Value Comparison", + "report_type": "Script Report", + "roles": [ + { + "role": "Stock User" + }, + { + "role": "Accounts Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py new file mode 100644 index 0000000000..eef121e542 --- /dev/null +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -0,0 +1,131 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from erpnext.accounts.utils import get_stock_accounts +from erpnext.accounts.utils import get_currency_precision +from erpnext.stock.doctype.warehouse.warehouse import get_warehouses_based_on_account + +def execute(filters=None): + if not erpnext.is_perpetual_inventory_enabled(filters.company): + frappe.throw(_("Perpetual inventory required for the company {0} to view this report.") + .format(filters.company)) + + data = get_data(filters) + columns = get_columns(filters) + + return columns, data + +def get_data(report_filters): + data = [] + + filters = { + "company": report_filters.company, + "posting_date": ("<=", report_filters.as_on_date) + } + + currency_precision = get_currency_precision() or 2 + stock_ledger_entries = get_stock_ledger_data(report_filters, filters) + voucher_wise_gl_data = get_gl_data(report_filters, filters) + + for d in stock_ledger_entries: + key = (d.voucher_type, d.voucher_no) + gl_data = voucher_wise_gl_data.get(key) or {} + d.account_value = gl_data.get("account_value", 0) + d.difference_value = (d.stock_value - d.account_value) + if abs(d.difference_value) > 1.0/10 ** currency_precision: + data.append(d) + + return data + +def get_stock_ledger_data(report_filters, filters): + if report_filters.account: + warehouses = get_warehouses_based_on_account(report_filters.account, + report_filters.warehouse) + + filters["warehouse"] = ("in", warehouses) + + return frappe.get_all("Stock Ledger Entry", filters=filters, + fields = ["name", "voucher_type", "voucher_no", + "sum(stock_value_difference) as stock_value", "posting_date", "posting_time"], + group_by = "voucher_type, voucher_no", + order_by = "posting_date ASC, posting_time ASC") + +def get_gl_data(report_filters, filters): + if report_filters.account: + stock_accounts = [report_filters.account] + else: + stock_accounts = [k.name + for k in get_stock_accounts(report_filters.company)] + + filters.update({ + "account": ("in", stock_accounts) + }) + + if filters.get("warehouse"): + del filters["warehouse"] + + gl_entries = frappe.get_all("GL Entry", filters=filters, + fields = ["name", "voucher_type", "voucher_no", + "sum(debit_in_account_currency) - sum(credit_in_account_currency) as account_value"], + group_by = "voucher_type, voucher_no") + + voucher_wise_gl_data = {} + for d in gl_entries: + key = (d.voucher_type, d.voucher_no) + voucher_wise_gl_data[key] = d + + return voucher_wise_gl_data + +def get_columns(filters): + return [ + { + "label": _("Stock Ledger ID"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Stock Ledger Entry", + "width": "80" + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date" + }, + { + "label": _("Posting Time"), + "fieldname": "posting_time", + "fieldtype": "Time" + }, + { + "label": _("Voucher Type"), + "fieldname": "voucher_type", + "width": "110" + }, + { + "label": _("Voucher No"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": "110" + }, + { + "label": _("Stock Value"), + "fieldname": "stock_value", + "fieldtype": "Currency", + "width": "120" + }, + { + "label": _("Account Value"), + "fieldname": "account_value", + "fieldtype": "Currency", + "width": "120" + }, + { + "label": _("Difference Value"), + "fieldname": "difference_value", + "fieldtype": "Currency", + "width": "120" + } + ] \ No newline at end of file diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index ccba8b0e23..ff03381389 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -264,7 +264,7 @@ def get_item_details(items, sle, filters): `tabItem` item %s where - item.name in (%s) and ifnull(item.disabled, 0) = 0 + item.name in (%s) """ % (cf_field, cf_join, ','.join(['%s'] *len(items))), items, as_dict=1) for item in res: From 00746306a300b81cea3c63f5ec422578de6f029d Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Fri, 10 Jan 2020 15:01:50 +0530 Subject: [PATCH 21/25] fix: Item tax template fetching from HSN Code --- erpnext/stock/doctype/item/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 253390ac50..dd3248baf2 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -137,7 +137,7 @@ frappe.ui.form.on("Item", { }, gst_hsn_code: function(frm) { - if(!frm.doc.taxes.length) { + if(!frm.doc.taxes || !frm.doc.taxes.length) { frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => { $.each(hsn_doc.taxes || [], function(i, tax) { let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes'); From 1e7a127dfaf8ce340d0339d4e549e7743832f748 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Sat, 11 Jan 2020 14:19:02 +0530 Subject: [PATCH 22/25] fix: auto cancel if movement exists --- erpnext/assets/doctype/asset/asset.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3e7f6833a0..86b5a11a1d 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -132,9 +132,10 @@ class Asset(AccountsController): if len(movements) > 1: frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \ cancelled manually to cancel this asset.')) - movement = frappe.get_doc('Asset Movement', movements[0].get('name')) - movement.flags.ignore_validate = True - movement.cancel() + if movements: + movement = frappe.get_doc('Asset Movement', movements[0].get('name')) + movement.flags.ignore_validate = True + movement.cancel() def make_asset_movement(self): reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' From f04d054faabea0ae8da4e9a62479a80c9b795ab6 Mon Sep 17 00:00:00 2001 From: thefalconx33 Date: Mon, 13 Jan 2020 12:43:49 +0530 Subject: [PATCH 23/25] fix: remove default customer as party type --- .../doctype/journal_entry_account/journal_entry_account.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ab811d81b2..dbf4e5c4b4 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -90,7 +90,6 @@ "fieldtype": "Column Break" }, { - "default": "Customer", "fieldname": "party_type", "fieldtype": "Link", "in_list_view": 1, @@ -272,7 +271,7 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-02 12:23:21.693443", + "modified": "2020-01-13 12:41:33.968025", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From 2d151a7b85acc2bd530793ff3586a58f117ee270 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 13 Jan 2020 15:09:27 +0530 Subject: [PATCH 24/25] fix: Remove patch to set automatic tax fetching from item tax template (#20234) --- erpnext/patches.txt | 1 - .../set_default_for_add_taxes_from_item_tax_template.py | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cea4662569..1989f4d814 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -645,7 +645,6 @@ erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings erpnext.patches.v12_0.set_payment_entry_status erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields -erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger erpnext.patches.v12_0.update_price_or_product_discount diff --git a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py b/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py deleted file mode 100644 index 06ee798198..0000000000 --- a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py +++ /dev/null @@ -1,5 +0,0 @@ -import frappe - -def execute(): - frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 1) - frappe.db.set_default("add_taxes_from_item_tax_template", 1) \ No newline at end of file From b8bcad5850eeb6a73d01e23365cf5ddde002419e Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 13 Jan 2020 18:17:05 +0530 Subject: [PATCH 25/25] fix: gl not generated on manual asset creation (#20265) --- erpnext/assets/doctype/asset/asset.js | 51 ++++++++++++--------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f6a7fa20d0..a53ff88177 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -338,25 +338,12 @@ frappe.ui.form.on('Asset', { }) }, - purchase_receipt: function(frm) { + purchase_receipt: (frm) => { frm.trigger('toggle_reference_doc'); - if (frm.doc.purchase_receipt) { if (frm.doc.item_code) { frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => { - frm.set_value('company', pr_doc.company); - frm.set_value('purchase_date', pr_doc.posting_date); - const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code); - if (!item) { - frm.set_value('purchase_receipt', ''); - frappe.msgprint({ - title: __('Invalid Purchase Receipt'), - message: __("The selected Purchase Receipt doesn't contains selected Asset Item."), - indicator: 'red' - }); - } - frm.set_value('gross_purchase_amount', item.base_net_rate); - frm.set_value('location', item.asset_location); + frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc) }); } else { frm.set_value('purchase_receipt', ''); @@ -368,24 +355,12 @@ frappe.ui.form.on('Asset', { } }, - purchase_invoice: function(frm) { + purchase_invoice: (frm) => { frm.trigger('toggle_reference_doc'); if (frm.doc.purchase_invoice) { if (frm.doc.item_code) { frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => { - frm.set_value('company', pi_doc.company); - frm.set_value('purchase_date', pi_doc.posting_date); - const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code); - if (!item) { - frm.set_value('purchase_invoice', ''); - frappe.msgprint({ - title: __('Invalid Purchase Invoice'), - message: __("The selected Purchase Invoice doesn't contains selected Asset Item."), - indicator: 'red' - }); - } - frm.set_value('gross_purchase_amount', item.base_net_rate); - frm.set_value('location', item.asset_location); + frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc) }); } else { frm.set_value('purchase_invoice', ''); @@ -397,6 +372,24 @@ frappe.ui.form.on('Asset', { } }, + set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { + frm.set_value('company', purchase_doc.company); + frm.set_value('purchase_date', purchase_doc.posting_date); + const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); + if (!item) { + doctype_field = frappe.scrub(doctype) + frm.set_value(doctype_field, ''); + frappe.msgprint({ + title: __(`Invalid ${doctype}`), + message: __(`The selected ${doctype} doesn't contains selected Asset Item.`), + indicator: 'red' + }); + } + frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('location', item.asset_location); + }, + set_depreciation_rate: function(frm, row) { if (row.total_number_of_depreciations && row.frequency_of_depreciation && row.expected_value_after_useful_life) {