From 5c73bafeaa34e325a8c555123cd4bd063ff1f568 Mon Sep 17 00:00:00 2001 From: Meatechsupport Date: Sun, 5 Jul 2015 11:26:55 +0400 Subject: [PATCH 01/43] Modifying the number of leave days calculation part. we don't need to exclude the Holiday list (that comes in between) from the total number of leaves applied. Add option in the leave type added a field in the leave type to include and exclude the holidays from the tolal leave applied days Added the field Added a field Include Holiday to leave Type doctype changed the lable changed the lable from "Include Holiday" to "Include holidays within leaves as leaves" Rearranged the function moved holidays = leave_app.get_holidays() under if Corrected 'total_leave_days' : flt(tot_days)-flt(holidays) Adding test case added the test case Added test case Added test case to test_leave_application.py adding default value added default value and corrected the syntax. IndentationError removed extra tabs after --- .../leave_application/leave_application.py | 7 ++++- .../test_leave_application.py | 27 +++++++++++++++++++ erpnext/hr/doctype/leave_type/leave_type.json | 6 +++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index c75c2bd2f2..5704775c23 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -238,10 +238,15 @@ def get_total_leave_days(leave_app): ret = {'total_leave_days' : 0.5} if not leave_app.half_day: tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1 + if frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): holidays = leave_app.get_holidays() ret = { 'total_leave_days' : flt(tot_days)-flt(holidays) - } + } + else: + ret = { + 'total_leave_days' : flt(tot_days) + } return ret @frappe.whitelist() diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 8cf0c80661..d9c0846908 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -247,3 +247,30 @@ class TestLeaveApplication(unittest.TestCase): "_T-Employee-0001") frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department) + + def test_exclude_holiday_in_leave(self): + frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 0) + application = frappe.copy_doc(_test_records[2]) + application.from_date = "2015-07-01" + application.to_date = "2015-07-05" + application.get_holidays = "2015-07-03" + application.insert() + + self.assertEquals(application.tot_days, 5) + self.assertEquals(application.holidays, 1) + self.assertEquals(application.ret, 4) + + def test_include_holiday_in_leave(self): + frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 1) + application = frappe.copy_doc(_test_records[2]) + application.from_date = "2015-07-01" + application.to_date = "2015-07-05" + application.get_holidays = "2015-07-03" + application.insert() + + self.assertEquals(application.tot_days, 5) + self.assertEquals(application.holidays, 1) + self.assertEquals(application.ret, 5) + + def tearDown(self): + frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 0) \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index f644c696d1..4493af504c 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -59,6 +59,12 @@ "fieldtype": "Check", "label": "Allow Negative Balance", "permlevel": 0 + }, + { + "fieldname": "include_holiday", + "fieldtype": "Check", + "label": "Include holidays within leaves as leaves", + "permlevel": 0 } ], "icon": "icon-flag", From e319598c51057c2e5036ddc4b719a28b21ac44f6 Mon Sep 17 00:00:00 2001 From: Meatechsupport Date: Mon, 27 Jul 2015 16:42:22 +0400 Subject: [PATCH 02/43] IndentationError alligned code --- .../leave_application/leave_application.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 5704775c23..2580bb1aaf 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -239,14 +239,14 @@ def get_total_leave_days(leave_app): if not leave_app.half_day: tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1 if frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): - holidays = leave_app.get_holidays() - ret = { - 'total_leave_days' : flt(tot_days)-flt(holidays) - } - else: - ret = { - 'total_leave_days' : flt(tot_days) - } + holidays = leave_app.get_holidays() + ret = { + 'total_leave_days' : flt(tot_days)-flt(holidays) + } + else: + ret = { + 'total_leave_days' : flt(tot_days) + } return ret @frappe.whitelist() From 8b96fdac12f574bdcfd6280f2881d49e4e569beb Mon Sep 17 00:00:00 2001 From: Meatechsupport Date: Mon, 27 Jul 2015 16:55:54 +0400 Subject: [PATCH 03/43] Indentation Error realligned --- .../doctype/leave_application/leave_application.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 2580bb1aaf..68daa31dc5 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -240,13 +240,13 @@ def get_total_leave_days(leave_app): tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1 if frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): holidays = leave_app.get_holidays() - ret = { - 'total_leave_days' : flt(tot_days)-flt(holidays) - } - else: - ret = { - 'total_leave_days' : flt(tot_days) - } + ret = { + 'total_leave_days' : flt(tot_days)-flt(holidays) + } + else: + ret = { + 'total_leave_days' : flt(tot_days) + } return ret @frappe.whitelist() From 08a60653baff294324aaeb0963e00dbb60ace86f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 28 Jul 2015 09:22:36 +0530 Subject: [PATCH 04/43] [fix] [setup wizard] ignore if user is the primary user --- erpnext/setup/page/setup_wizard/setup_wizard.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 9ddd2dc03e..02b7e44422 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -542,13 +542,15 @@ def create_users(args): user.append_roles("Accounts Manager", "Accounts User") user.flags.delay_emails = True - user.insert(ignore_permissions=True) + + if not frappe.db.get_value("User", email): + user.insert(ignore_permissions=True) # create employee emp = frappe.get_doc({ "doctype": "Employee", "full_name": fullname, - "user_id": user.name, + "user_id": email, "status": "Active", "company": args.get("company_name") }) From aa5fb5e50e16a47e034a1156fd97f7695fdf0609 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 28 Jul 2015 10:31:09 +0530 Subject: [PATCH 05/43] [fix] [setup wizard] ignore if user is the primary user --- .../setup/page/setup_wizard/setup_wizard.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 02b7e44422..cfc8d768c9 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -546,16 +546,16 @@ def create_users(args): if not frappe.db.get_value("User", email): user.insert(ignore_permissions=True) - # create employee - emp = frappe.get_doc({ - "doctype": "Employee", - "full_name": fullname, - "user_id": email, - "status": "Active", - "company": args.get("company_name") - }) - emp.flags.ignore_mandatory = True - emp.insert(ignore_permissions = True) + # create employee + emp = frappe.get_doc({ + "doctype": "Employee", + "full_name": fullname, + "user_id": email, + "status": "Active", + "company": args.get("company_name") + }) + emp.flags.ignore_mandatory = True + emp.insert(ignore_permissions = True) @frappe.whitelist() def load_messages(language): From 424f0c7b844868ef2bf93fbec0ef9f88ea5e3727 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 28 Jul 2015 10:36:33 +0530 Subject: [PATCH 06/43] [fix] [setup wizard] ignore fiscal year --- erpnext/setup/page/setup_wizard/setup_wizard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index cfc8d768c9..812a9f12b7 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -14,6 +14,7 @@ from frappe.utils.nestedset import get_root_of from .default_website import website_maker import install_fixtures from .sample_data import make_sample_data +from erpnext.accounts.utils import FiscalYearError @frappe.whitelist() def setup_account(args=None): @@ -85,7 +86,10 @@ def setup_account(args=None): frappe.clear_cache() - make_sample_data() + try: + make_sample_data() + except FiscalYearError: + pass except: if args: traceback = frappe.get_traceback() From 8e8e9c61caed47dd5ac72920124b15eb3f8636d5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 28 Jul 2015 10:45:59 +0530 Subject: [PATCH 07/43] [minor] Show description in Sales Order --- .../sales_order_item/sales_order_item.json | 834 +++++++++--------- 1 file changed, 417 insertions(+), 417 deletions(-) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 8012bba7af..23e4137b78 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -1,509 +1,509 @@ { - "autoname": "hash", - "creation": "2013-03-07 11:42:58", - "docstatus": 0, - "doctype": "DocType", + "autoname": "hash", + "creation": "2013-03-07 11:42:58", + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "fieldname": "item_code", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Item Code", - "oldfieldname": "item_code", - "oldfieldtype": "Link", - "options": "Item", - "permlevel": 0, - "print_width": "150px", - "read_only": 0, - "reqd": 1, - "search_index": 1, + "fieldname": "item_code", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Item Code", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "permlevel": 0, + "print_width": "150px", + "read_only": 0, + "reqd": 1, + "search_index": 1, "width": "150px" - }, + }, { - "fieldname": "customer_item_code", - "fieldtype": "Data", - "hidden": 1, - "label": "Customer's Item Code", - "permlevel": 0, - "print_hide": 1, + "fieldname": "customer_item_code", + "fieldtype": "Data", + "hidden": 1, + "label": "Customer's Item Code", + "permlevel": 0, + "print_hide": 1, "read_only": 1 - }, + }, { - "fieldname": "col_break1", - "fieldtype": "Column Break", + "fieldname": "col_break1", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "item_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Item Name", - "oldfieldname": "item_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_width": "150", - "read_only": 0, - "reqd": 1, + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "oldfieldname": "item_name", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 1, + "print_width": "150", + "read_only": 0, + "reqd": 1, "width": "150" - }, + }, { - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "permlevel": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "description", - "fieldtype": "Small Text", - "in_filter": 1, - "in_list_view": 0, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_width": "300px", - "read_only": 0, - "reqd": 1, - "search_index": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "in_filter": 1, + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_width": "300px", + "read_only": 0, + "reqd": 1, + "search_index": 0, "width": "300px" - }, + }, { - "fieldname": "column_break_7", - "fieldtype": "Column Break", - "permlevel": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "image", - "fieldtype": "Attach", - "hidden": 1, - "label": "Image", - "permlevel": 0, - "precision": "", + "fieldname": "image", + "fieldtype": "Attach", + "hidden": 1, + "label": "Image", + "permlevel": 0, + "precision": "", "print_hide": 0 - }, + }, { - "fieldname": "image_view", - "fieldtype": "Image", - "label": "Image View", - "options": "image", - "permlevel": 0, - "precision": "", + "fieldname": "image_view", + "fieldtype": "Image", + "label": "Image View", + "options": "image", + "permlevel": 0, + "precision": "", "print_hide": 1 - }, + }, { - "fieldname": "quantity_and_rate", - "fieldtype": "Section Break", - "label": "Quantity and Rate", + "fieldname": "quantity_and_rate", + "fieldtype": "Section Break", + "label": "Quantity and Rate", "permlevel": 0 - }, + }, { - "fieldname": "qty", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Quantity", - "oldfieldname": "qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_width": "100px", - "read_only": 0, - "reqd": 1, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "oldfieldname": "qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_width": "100px", + "read_only": 0, + "reqd": 1, "width": "100px" - }, + }, { - "fieldname": "price_list_rate", - "fieldtype": "Currency", - "label": "Price List Rate", - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "70px", - "read_only": 1, - "reqd": 0, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "label": "Price List Rate", + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "70px", + "read_only": 1, + "reqd": 0, "width": "70px" - }, + }, { - "depends_on": "price_list_rate", - "fieldname": "discount_percentage", - "fieldtype": "Percent", - "in_list_view": 1, - "label": "Discount on Price List Rate (%)", - "oldfieldname": "adj_rate", - "oldfieldtype": "Float", - "permlevel": 0, - "print_hide": 1, - "print_width": "70px", - "read_only": 0, + "depends_on": "price_list_rate", + "fieldname": "discount_percentage", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Discount on Price List Rate (%)", + "oldfieldname": "adj_rate", + "oldfieldtype": "Float", + "permlevel": 0, + "print_hide": 1, + "print_width": "70px", + "read_only": 0, "width": "70px" - }, + }, { - "fieldname": "col_break2", - "fieldtype": "Column Break", + "fieldname": "col_break2", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "in_list_view": 1, - "label": "UOM", - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_width": "70px", - "read_only": 1, - "reqd": 0, + "fieldname": "stock_uom", + "fieldtype": "Link", + "hidden": 0, + "in_list_view": 1, + "label": "UOM", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "permlevel": 0, + "print_width": "70px", + "read_only": 1, + "reqd": 0, "width": "70px" - }, + }, { - "fieldname": "base_price_list_rate", - "fieldtype": "Currency", - "label": "Price List Rate (Company Currency)", - "oldfieldname": "base_ref_rate", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "100px", - "read_only": 1, + "fieldname": "base_price_list_rate", + "fieldtype": "Currency", + "label": "Price List Rate (Company Currency)", + "oldfieldname": "base_ref_rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "100px", + "read_only": 1, "width": "100px" - }, + }, { - "fieldname": "section_break_simple1", - "fieldtype": "Section Break", + "fieldname": "section_break_simple1", + "fieldtype": "Section Break", "permlevel": 0 - }, + }, { - "fieldname": "rate", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Rate", - "oldfieldname": "export_rate", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_width": "100px", - "read_only": 0, - "reqd": 0, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "export_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_width": "100px", + "read_only": 0, + "reqd": 0, "width": "100px" - }, + }, { - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "no_copy": 0, - "oldfieldname": "export_amount", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_width": "100px", - "read_only": 1, - "reqd": 0, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "no_copy": 0, + "oldfieldname": "export_amount", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_width": "100px", + "read_only": 1, + "reqd": 0, "width": "100px" - }, + }, { - "fieldname": "col_break3", - "fieldtype": "Column Break", + "fieldname": "col_break3", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "base_rate", - "fieldtype": "Currency", - "label": "Basic Rate (Company Currency)", - "oldfieldname": "basic_rate", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "100px", - "read_only": 1, - "reqd": 0, + "fieldname": "base_rate", + "fieldtype": "Currency", + "label": "Basic Rate (Company Currency)", + "oldfieldname": "basic_rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "100px", + "read_only": 1, + "reqd": 0, "width": "100px" - }, + }, { - "fieldname": "base_amount", - "fieldtype": "Currency", - "label": "Amount (Company Currency)", - "no_copy": 0, - "oldfieldname": "amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "100px", - "read_only": 1, - "reqd": 0, + "fieldname": "base_amount", + "fieldtype": "Currency", + "label": "Amount (Company Currency)", + "no_copy": 0, + "oldfieldname": "amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "100px", + "read_only": 1, + "reqd": 0, "width": "100px" - }, + }, { - "fieldname": "pricing_rule", - "fieldtype": "Link", - "label": "Pricing Rule", - "options": "Pricing Rule", - "permlevel": 0, + "fieldname": "pricing_rule", + "fieldtype": "Link", + "label": "Pricing Rule", + "options": "Pricing Rule", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "section_break_24", - "fieldtype": "Section Break", - "permlevel": 0, + "fieldname": "section_break_24", + "fieldtype": "Section Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "net_rate", - "fieldtype": "Currency", - "label": "Net Rate", - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, + "fieldname": "net_rate", + "fieldtype": "Currency", + "label": "Net Rate", + "options": "currency", + "permlevel": 0, + "precision": "", + "print_hide": 1, "read_only": 1 - }, + }, { - "fieldname": "net_amount", - "fieldtype": "Currency", - "label": "Net Amount", - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, + "fieldname": "net_amount", + "fieldtype": "Currency", + "label": "Net Amount", + "options": "currency", + "permlevel": 0, + "precision": "", + "print_hide": 1, "read_only": 1 - }, + }, { - "fieldname": "column_break_27", - "fieldtype": "Column Break", - "permlevel": 0, + "fieldname": "column_break_27", + "fieldtype": "Column Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "base_net_rate", - "fieldtype": "Currency", - "label": "Net Rate (Company Currency)", - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, + "fieldname": "base_net_rate", + "fieldtype": "Currency", + "label": "Net Rate (Company Currency)", + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 1, "read_only": 1 - }, + }, { - "fieldname": "base_net_amount", - "fieldtype": "Currency", - "label": "Net Amount (Company Currency)", - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, + "fieldname": "base_net_amount", + "fieldtype": "Currency", + "label": "Net Amount (Company Currency)", + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 1, "read_only": 1 - }, + }, { - "fieldname": "warehouse_and_reference", - "fieldtype": "Section Break", - "label": "Warehouse and Reference", + "fieldname": "warehouse_and_reference", + "fieldtype": "Section Break", + "label": "Warehouse and Reference", "permlevel": 0 - }, + }, { - "fieldname": "warehouse", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Delivery Warehouse", - "no_copy": 0, - "oldfieldname": "reserved_warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "permlevel": 0, - "print_hide": 1, - "print_width": "150px", - "read_only": 0, - "reqd": 0, + "fieldname": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Delivery Warehouse", + "no_copy": 0, + "oldfieldname": "reserved_warehouse", + "oldfieldtype": "Link", + "options": "Warehouse", + "permlevel": 0, + "print_hide": 1, + "print_width": "150px", + "read_only": 0, + "reqd": 0, "width": "150px" - }, + }, { - "fieldname": "prevdoc_docname", - "fieldtype": "Link", - "hidden": 0, - "in_filter": 1, - "label": "Quotation", - "no_copy": 1, - "oldfieldname": "prevdoc_docname", - "oldfieldtype": "Link", - "options": "Quotation", - "permlevel": 0, - "print_hide": 1, - "read_only": 1, + "fieldname": "prevdoc_docname", + "fieldtype": "Link", + "hidden": 0, + "in_filter": 1, + "label": "Quotation", + "no_copy": 1, + "oldfieldname": "prevdoc_docname", + "oldfieldtype": "Link", + "options": "Quotation", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, "search_index": 1 - }, + }, { - "fieldname": "brand", - "fieldtype": "Link", - "hidden": 1, - "in_filter": 1, - "label": "Brand Name", - "oldfieldname": "brand", - "oldfieldtype": "Link", - "options": "Brand", - "permlevel": 0, - "print_hide": 1, - "read_only": 1, + "fieldname": "brand", + "fieldtype": "Link", + "hidden": 1, + "in_filter": 1, + "label": "Brand Name", + "oldfieldname": "brand", + "oldfieldtype": "Link", + "options": "Brand", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, "search_index": 1 - }, + }, { - "description": "", - "fieldname": "item_group", - "fieldtype": "Link", - "hidden": 1, - "in_filter": 1, - "label": "Item Group", - "oldfieldname": "item_group", - "oldfieldtype": "Link", - "options": "Item Group", - "permlevel": 0, - "print_hide": 1, - "read_only": 1, + "description": "", + "fieldname": "item_group", + "fieldtype": "Link", + "hidden": 1, + "in_filter": 1, + "label": "Item Group", + "oldfieldname": "item_group", + "oldfieldtype": "Link", + "options": "Item Group", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, "search_index": 1 - }, + }, { - "allow_on_submit": 1, - "fieldname": "page_break", - "fieldtype": "Check", - "label": "Page Break", - "oldfieldname": "page_break", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 1, - "read_only": 0, + "allow_on_submit": 1, + "fieldname": "page_break", + "fieldtype": "Check", + "label": "Page Break", + "oldfieldname": "page_break", + "oldfieldtype": "Check", + "permlevel": 0, + "print_hide": 1, + "read_only": 0, "report_hide": 1 - }, + }, { - "fieldname": "col_break4", - "fieldtype": "Column Break", + "fieldname": "col_break4", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "allow_on_submit": 1, - "fieldname": "projected_qty", - "fieldtype": "Float", - "hidden": 0, - "label": "Projected Qty", - "no_copy": 1, - "oldfieldname": "projected_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "70px", - "read_only": 1, + "allow_on_submit": 1, + "fieldname": "projected_qty", + "fieldtype": "Float", + "hidden": 0, + "label": "Projected Qty", + "no_copy": 1, + "oldfieldname": "projected_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "70px", + "read_only": 1, "width": "70px" - }, + }, { - "allow_on_submit": 1, - "fieldname": "actual_qty", - "fieldtype": "Float", - "label": "Actual Qty", - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_width": "70px", - "read_only": 1, + "allow_on_submit": 1, + "fieldname": "actual_qty", + "fieldtype": "Float", + "label": "Actual Qty", + "no_copy": 1, + "permlevel": 0, + "print_hide": 1, + "print_width": "70px", + "read_only": 1, "width": "70px" - }, + }, { - "fieldname": "delivered_qty", - "fieldtype": "Float", - "hidden": 0, - "in_filter": 0, - "label": "Delivered Qty", - "no_copy": 1, - "oldfieldname": "delivered_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "100px", - "read_only": 1, - "search_index": 0, + "fieldname": "delivered_qty", + "fieldtype": "Float", + "hidden": 0, + "in_filter": 0, + "label": "Delivered Qty", + "no_copy": 1, + "oldfieldname": "delivered_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "100px", + "read_only": 1, + "search_index": 0, "width": "100px" - }, + }, { - "fieldname": "billed_amt", - "fieldtype": "Currency", - "label": "Billed Amt", - "no_copy": 1, - "options": "currency", - "permlevel": 0, - "print_hide": 1, + "fieldname": "billed_amt", + "fieldtype": "Currency", + "label": "Billed Amt", + "no_copy": 1, + "options": "currency", + "permlevel": 0, + "print_hide": 1, "read_only": 1 - }, + }, { - "description": "For Production", - "fieldname": "planned_qty", - "fieldtype": "Float", - "hidden": 1, - "label": "Planned Quantity", - "no_copy": 1, - "oldfieldname": "planned_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "50px", - "read_only": 1, - "report_hide": 1, + "description": "For Production", + "fieldname": "planned_qty", + "fieldtype": "Float", + "hidden": 1, + "label": "Planned Quantity", + "no_copy": 1, + "oldfieldname": "planned_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "50px", + "read_only": 1, + "report_hide": 1, "width": "50px" - }, + }, { - "description": "For Production", - "fieldname": "produced_qty", - "fieldtype": "Float", - "hidden": 1, - "label": "Produced Quantity", - "oldfieldname": "produced_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_width": "50px", - "read_only": 1, - "report_hide": 1, + "description": "For Production", + "fieldname": "produced_qty", + "fieldtype": "Float", + "hidden": 1, + "label": "Produced Quantity", + "oldfieldname": "produced_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 1, + "print_width": "50px", + "read_only": 1, + "report_hide": 1, "width": "50px" - }, + }, { - "fieldname": "item_tax_rate", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Item Tax Rate", - "oldfieldname": "item_tax_rate", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 1, - "read_only": 1, + "fieldname": "item_tax_rate", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Item Tax Rate", + "oldfieldname": "item_tax_rate", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, "report_hide": 1 - }, + }, { - "description": "Used for Production Plan", - "fieldname": "transaction_date", - "fieldtype": "Date", - "hidden": 1, - "in_filter": 0, - "label": "Sales Order Date", - "oldfieldname": "transaction_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "read_only": 1, - "report_hide": 1, + "description": "Used for Production Plan", + "fieldname": "transaction_date", + "fieldtype": "Date", + "hidden": 1, + "in_filter": 0, + "label": "Sales Order Date", + "oldfieldname": "transaction_date", + "oldfieldtype": "Date", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, + "report_hide": 1, "search_index": 0 } - ], - "idx": 1, - "istable": 1, - "modified": "2015-07-02 05:37:29.289574", - "modified_by": "Administrator", - "module": "Selling", - "name": "Sales Order Item", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", + ], + "idx": 1, + "istable": 1, + "modified": "2015-07-28 05:37:29.289574", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Order Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From c80059e10e72f849f82acf1c2f95bd5d48e5a1a0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jul 2015 11:14:01 +0530 Subject: [PATCH 08/43] [fix] Ignore making SLE for opening stock reco with zero qty --- .../doctype/stock_reconciliation/stock_reconciliation.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index efa6a8a25d..bed8dd7e56 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -163,16 +163,17 @@ class StockReconciliation(StockController): }) if previous_sle: if row.qty in ("", None): - row.qty = previous_sle.get("qty_after_transaction") + row.qty = previous_sle.get("qty_after_transaction", 0) if row.valuation_rate in ("", None): - row.valuation_rate = previous_sle.get("valuation_rate") + row.valuation_rate = previous_sle.get("valuation_rate", 0) if row.qty and not row.valuation_rate: frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code)) - if previous_sle and row.qty == previous_sle.get("qty_after_transaction") \ - and row.valuation_rate == previous_sle.get("valuation_rate"): + if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction") + and row.valuation_rate == previous_sle.get("valuation_rate")) + or (not previous_sle and not row.qty)): continue self.insert_entries(row) From 9088432f1054c1a76be2c3ebf109f0afbc66b367 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jul 2015 11:16:14 +0530 Subject: [PATCH 09/43] Fetch default expense account and cost center from item if company matches, otherwise fetch from company master --- erpnext/manufacturing/doctype/bom/bom.py | 13 ++++++++++--- erpnext/manufacturing/doctype/bom/test_bom.py | 6 +++--- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index fe67ed8057..5a3bbcdaeb 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -364,7 +364,7 @@ class BOM(Document): if self.with_operations and not self.get('operations'): frappe.throw(_("Operations cannot be left blank.")) -def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1): +def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1): item_dict = {} # Did not use qty_consumed_per_unit in the query, as it leads to rounding loss @@ -405,11 +405,18 @@ def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1): else: item_dict[item.item_code] = item + for item, item_details in item_dict.items(): + for d in [["Account", "expense_account", "default_expense_account"], + ["Cost Center", "cost_center", "cost_center"], ["Warehouse", "default_warehouse", ""]]: + company_in_record = frappe.db.get_value(d[0], item_details.get(d[1]), "company") + if not item_details.get(d[1]) or (company_in_record and company != company_in_record): + item_dict[item][d[1]] = frappe.db.get_value("Company", company, d[2]) if d[2] else None + return item_dict @frappe.whitelist() -def get_bom_items(bom, qty=1, fetch_exploded=1): - items = get_bom_items_as_dict(bom, qty, fetch_exploded).values() +def get_bom_items(bom, company, qty=1, fetch_exploded=1): + items = get_bom_items_as_dict(bom, company, qty, fetch_exploded).values() items.sort(lambda a, b: a.item_code > b.item_code and 1 or -1) return items diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 528c62663b..4e520ef233 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -12,14 +12,14 @@ test_records = frappe.get_test_records('BOM') class TestBOM(unittest.TestCase): def test_get_items(self): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict - items_dict = get_bom_items_as_dict(bom=get_default_bom(), qty=1, fetch_exploded=0) + items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=0) self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict) self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict) self.assertEquals(len(items_dict.values()), 2) def test_get_items_exploded(self): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict - items_dict = get_bom_items_as_dict(bom=get_default_bom(), qty=1, fetch_exploded=1) + items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=1) self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict) self.assertFalse(test_records[2]["items"][1]["item_code"] in items_dict) self.assertTrue(test_records[0]["items"][0]["item_code"] in items_dict) @@ -28,7 +28,7 @@ class TestBOM(unittest.TestCase): def test_get_items_list(self): from erpnext.manufacturing.doctype.bom.bom import get_bom_items - self.assertEquals(len(get_bom_items(bom=get_default_bom())), 3) + self.assertEquals(len(get_bom_items(bom=get_default_bom(), company="_Test Company")), 3) def test_default_bom(self): def _get_default_bom_in_item(): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 1b01f3aa68..31484553ef 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -553,7 +553,7 @@ class StockEntry(StockController): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict # item dict = { item_code: {qty, description, stock_uom} } - item_dict = get_bom_items_as_dict(self.bom_no, qty=qty, fetch_exploded = self.use_multi_level_bom) + item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom) for item in item_dict.values(): item.from_warehouse = self.from_warehouse or item.default_warehouse From a2f18ba794a52d12d1a47aa126e422d08ac3edb8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jul 2015 12:00:45 +0530 Subject: [PATCH 10/43] [fix] Contact section visibility in Warranty Claim --- erpnext/support/doctype/warranty_claim/warranty_claim.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json index cabbdae33e..fd36d47771 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.json +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json @@ -222,6 +222,7 @@ "permlevel": 0 }, { + "depends_on": "customer", "fieldname": "contact_info", "fieldtype": "Section Break", "label": "Contact Info", @@ -396,7 +397,7 @@ "icon": "icon-bug", "idx": 1, "is_submittable": 0, - "modified": "2015-02-21 04:11:40.653543", + "modified": "2015-07-28 11:59:38.762393", "modified_by": "Administrator", "module": "Support", "name": "Warranty Claim", From 3fbbb71afcd67eee67c05bbcb9d3061600ae55a9 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 17 Jul 2015 12:10:12 +0530 Subject: [PATCH 11/43] Added ability to freeze Customer/Supplier --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 6 ++++++ erpnext/buying/doctype/supplier/supplier.json | 9 ++++++++- erpnext/controllers/accounts_controller.py | 17 +++++++++++++++++ erpnext/selling/doctype/customer/customer.json | 9 ++++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index edee1226eb..de5b312287 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -17,6 +17,7 @@ class GLEntry(Document): self.validate_posting_date() self.check_pl_account() self.validate_cost_center() + self.validate_party() def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'): self.validate_account_details(adv_adj) @@ -88,6 +89,11 @@ class GLEntry(Document): if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) + + def validate_party(self): + if self.meta.get_field("party_type"): + if frappe.db.get_value(self.party_type, self.party, "is_frozen"): + frappe.throw("Accounts for {0} {1} is frozen".format(self.party_type, self.party)) def validate_balance_type(account, adv_adj=False): if not adv_adj and account: diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index a1a38d5ccd..0ea49a0819 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -54,6 +54,13 @@ "permlevel": 0, "reqd": 1 }, + { + "fieldname": "is_frozen", + "fieldtype": "Check", + "label": "Is Frozen ?", + "permlevel": 0, + "precision": "" + }, { "depends_on": "eval:!doc.__islocal", "fieldname": "address_contacts", @@ -172,7 +179,7 @@ ], "icon": "icon-user", "idx": 1, - "modified": "2015-07-13 05:28:29.121285", + "modified": "2015-07-17 02:01:36.727437", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7610042b5f..c893a0a09d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -16,6 +16,7 @@ class AccountsController(TransactionBase): if self.get("_action") and self._action != "update_after_submit": self.set_missing_values(for_validate=True) self.validate_date_with_fiscal_year() + if self.meta.get_field("currency"): self.calculate_taxes_and_totals() if not self.meta.get_field("is_return") or not self.is_return: @@ -32,6 +33,8 @@ class AccountsController(TransactionBase): if self.meta.get_field("taxes_and_charges"): self.validate_enabled_taxes_and_charges() + + self.validate_party() def on_submit(self): if self.meta.get_field("is_recurring"): @@ -340,6 +343,20 @@ class AccountsController(TransactionBase): return self._abbr + def validate_party(self): + party = None + if self.meta.get_field("customer"): + party_type = 'customer' + party = self.customer + + elif self.meta.get_field("suppier"): + party_type = 'supplier' + party = self.supplier + + if party: + if frappe.db.get_value(party_type, party, "is_frozen"): + frappe.throw("Accounts for {0} {1} is frozen".format(party_type, party)) + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, "tax_rate") diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index cced319cf5..cebfc29ced 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -101,6 +101,13 @@ "print_hide": 1, "reqd": 1 }, + { + "fieldname": "is_frozen", + "fieldtype": "Check", + "label": "Is Frozen ?", + "permlevel": 0, + "precision": "" + }, { "depends_on": "eval:!doc.__islocal", "fieldname": "address_contacts", @@ -278,7 +285,7 @@ ], "icon": "icon-user", "idx": 1, - "modified": "2015-07-13 05:28:25.753684", + "modified": "2015-07-16 09:07:11.565841", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 3698b84d7c1112c7b41392b1843a811c3fc95733 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 17 Jul 2015 19:10:11 +0530 Subject: [PATCH 12/43] Typo fixes in Supplier and Customer Document --- erpnext/buying/doctype/supplier/supplier.json | 4 ++-- erpnext/selling/doctype/customer/customer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 0ea49a0819..c3128a5187 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -57,7 +57,7 @@ { "fieldname": "is_frozen", "fieldtype": "Check", - "label": "Is Frozen ?", + "label": "Is Frozen", "permlevel": 0, "precision": "" }, @@ -179,7 +179,7 @@ ], "icon": "icon-user", "idx": 1, - "modified": "2015-07-17 02:01:36.727437", + "modified": "2015-07-17 09:39:05.318826", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index cebfc29ced..a3bcc8acb2 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -104,7 +104,7 @@ { "fieldname": "is_frozen", "fieldtype": "Check", - "label": "Is Frozen ?", + "label": "Is Frozen", "permlevel": 0, "precision": "" }, @@ -285,7 +285,7 @@ ], "icon": "icon-user", "idx": 1, - "modified": "2015-07-16 09:07:11.565841", + "modified": "2015-07-17 09:38:50.086978", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 79bf2337341dd8aae21fe8e7e07ae30b55616ee1 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 20 Jul 2015 13:03:48 +0530 Subject: [PATCH 13/43] Test Cases added to check if customer is frozen --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 6 ++++-- erpnext/controllers/accounts_controller.py | 17 +++++++++-------- .../selling/doctype/customer/test_customer.py | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index de5b312287..d3246d7554 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -9,6 +9,8 @@ from frappe import _ from frappe.model.document import Document +class CustomerFrozen(frappe.ValidationError): pass + class GLEntry(Document): def validate(self): self.flags.ignore_submit_comment = True @@ -91,9 +93,9 @@ class GLEntry(Document): frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) def validate_party(self): - if self.meta.get_field("party_type"): + if self.party_type and self.party: if frappe.db.get_value(self.party_type, self.party, "is_frozen"): - frappe.throw("Accounts for {0} {1} is frozen".format(self.party_type, self.party)) + frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) def validate_balance_type(account, adv_adj=False): if not adv_adj and account: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c893a0a09d..cbd8a6fb82 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -11,6 +11,8 @@ from erpnext.utilities.transaction_base import TransactionBase from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.sales_and_purchase_return import validate_return +class CustomerFrozen(frappe.ValidationError): pass + class AccountsController(TransactionBase): def validate(self): if self.get("_action") and self._action != "update_after_submit": @@ -344,18 +346,17 @@ class AccountsController(TransactionBase): return self._abbr def validate_party(self): - party = None + party_type = None if self.meta.get_field("customer"): - party_type = 'customer' - party = self.customer + party_type = 'Customer' - elif self.meta.get_field("suppier"): - party_type = 'supplier' - party = self.supplier + elif self.meta.get_field("supplier"): + party_type = 'Supplier' - if party: + if party_type: + party = self.get(party_type.lower()) if frappe.db.get_value(party_type, party, "is_frozen"): - frappe.throw("Accounts for {0} {1} is frozen".format(party_type, party)) + frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen) @frappe.whitelist() def get_tax_rate(account_head): diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 1db6c6a24a..2f9c4e1270 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -7,6 +7,7 @@ import frappe import unittest from frappe.test_runner import make_test_records +from erpnext.controllers.accounts_controller import CustomerFrozen test_ignore = ["Price List"] @@ -65,5 +66,19 @@ class TestCustomer(unittest.TestCase): {"comment_doctype": "Customer", "comment_docname": "_Test Customer 1 Renamed"}), comment.name) frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1") - - + + def test_freezed_customer(self): + cust = frappe.get_doc("Customer", "_Test Customer") + cust.is_frozen = 1 + cust.save() + + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + so = make_sales_order(do_not_save= True) + so.customer = "_Test Customer" + self.assertRaises(CustomerFrozen, so.save) + + cust.is_frozen = 0 + cust.save() + + so.save() \ No newline at end of file From cc7cb2a70add9f9548493b1a8f1d599358e3e7d4 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 24 Jul 2015 13:10:50 +0530 Subject: [PATCH 14/43] Allowed frozen_accounts_modifier to create records against frozen customer/supplier --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 ++- erpnext/controllers/accounts_controller.py | 4 ++++ erpnext/selling/doctype/customer/test_customer.py | 8 ++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index d3246d7554..83955ecdeb 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -93,7 +93,8 @@ class GLEntry(Document): frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) def validate_party(self): - if self.party_type and self.party: + frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier') + if self.party_type and self.party and not frozen_accounts_modifier in frappe.get_roles(): if frappe.db.get_value(self.party_type, self.party, "is_frozen"): frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cbd8a6fb82..55e9e3d000 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -346,6 +346,10 @@ class AccountsController(TransactionBase): return self._abbr def validate_party(self): + frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier') + if frozen_accounts_modifier in frappe.get_roles(): + return + party_type = None if self.meta.get_field("customer"): party_type = 'Customer' diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 2f9c4e1270..dca4bb76fb 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -68,17 +68,13 @@ class TestCustomer(unittest.TestCase): frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1") def test_freezed_customer(self): - cust = frappe.get_doc("Customer", "_Test Customer") - cust.is_frozen = 1 - cust.save() + frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 1) from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order so = make_sales_order(do_not_save= True) - so.customer = "_Test Customer" self.assertRaises(CustomerFrozen, so.save) - cust.is_frozen = 0 - cust.save() + frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0) so.save() \ No newline at end of file From ff3b220c79907aa346fe2149693168cc5926439f Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Tue, 28 Jul 2015 12:23:14 +0530 Subject: [PATCH 15/43] Fixed gl_entry Validation --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 83955ecdeb..43c421352c 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, fmt_money, getdate, formatdate, cstr, cint +from frappe.utils import flt, fmt_money, getdate, formatdate, cstr from frappe import _ from frappe.model.document import Document @@ -93,10 +93,11 @@ class GLEntry(Document): frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) def validate_party(self): - frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier') - if self.party_type and self.party and not frozen_accounts_modifier in frappe.get_roles(): - if frappe.db.get_value(self.party_type, self.party, "is_frozen"): - frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) + if self.party_type and self.party: + frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier') + if not frozen_accounts_modifier in frappe.get_roles(): + if frappe.db.get_value(self.party_type, self.party, "is_frozen"): + frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen) def validate_balance_type(account, adv_adj=False): if not adv_adj and account: From 49a59c075f76ada8e62aea029bfc885a55cea1b9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 28 Jul 2015 12:23:20 +0530 Subject: [PATCH 16/43] [minor] [setup-wizard] add check for sample data --- erpnext/setup/page/setup_wizard/setup_wizard.js | 10 +++++++--- erpnext/setup/page/setup_wizard/setup_wizard.py | 9 +++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.js b/erpnext/setup/page/setup_wizard/setup_wizard.js index b38bd1c373..ac720c97d5 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.js +++ b/erpnext/setup/page/setup_wizard/setup_wizard.js @@ -29,7 +29,7 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) { erpnext.wiz.taxes.slide, erpnext.wiz.customers.slide, erpnext.wiz.suppliers.slide, - erpnext.wiz.items.slide, + erpnext.wiz.items.slide ] } @@ -406,7 +406,6 @@ $.extend(erpnext.wiz, { description: __('Your financial year begins on'), reqd:1}, {fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date', description: __('Your financial year ends on'), reqd:1}, - ], help: __('The name of your company for which you are setting up this system.'), @@ -510,7 +509,7 @@ $.extend(erpnext.wiz, { slide: { icon: "icon-money", "title": __("Add Users"), - "help": __("Add users to your organization"), + "help": __("Add users to your organization, other than yourself"), "fields": [], before_load: function(slide) { slide.fields = []; @@ -635,6 +634,11 @@ $.extend(erpnext.wiz, { ]) } slide.fields[1].reqd = 1; + + // dummy data + slide.fields.push({fieldtype: "Section Break"}); + slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data", + label: __("Add a few sample records"), "default": 1}); }, css_class: "two-column" }, diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 812a9f12b7..e8c769974b 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -86,10 +86,11 @@ def setup_account(args=None): frappe.clear_cache() - try: - make_sample_data() - except FiscalYearError: - pass + if args.get("add_sample_data"): + try: + make_sample_data() + except FiscalYearError: + pass except: if args: traceback = frappe.get_traceback() From 26052df76e5eafcd94697888950c19328d569f72 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jul 2015 15:10:17 +0530 Subject: [PATCH 17/43] POS Profile: removed income account, cost center, territory and bank-cash account from mandatory --- erpnext/accounts/doctype/pos_profile/pos_profile.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 0c5a4bbc7e..9e79590116 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -27,7 +27,7 @@ "options": "Territory", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "naming_series", @@ -167,7 +167,7 @@ "options": "Account", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "income_account", @@ -178,7 +178,7 @@ "options": "Account", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", @@ -201,7 +201,7 @@ "options": "Cost Center", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "write_off_account", @@ -234,7 +234,7 @@ ], "icon": "icon-cog", "idx": 1, - "modified": "2015-07-07 08:56:04.381471", + "modified": "2015-07-28 15:07:14.417200", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", From 145227e4ecb6ba8d69bf33e15dcc361bb91cd9f8 Mon Sep 17 00:00:00 2001 From: Meatechsupport Date: Tue, 28 Jul 2015 18:39:04 +0400 Subject: [PATCH 18/43] Edited the Test case --- .../test_leave_application.py | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index d9c0846908..97c6f2f655 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -19,7 +19,8 @@ _test_records = [ "from_date": "2013-05-01", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", - "to_date": "2013-05-05" + "to_date": "2013-05-05", + "get_holidays": "2013-05-03" }, { "company": "_Test Company", @@ -29,7 +30,8 @@ _test_records = [ "from_date": "2013-05-01", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", - "to_date": "2013-05-05" + "to_date": "2013-05-05", + "get_holidays": "2013-05-03" }, { "company": "_Test Company", @@ -39,7 +41,8 @@ _test_records = [ "from_date": "2013-01-15", "leave_type": "_Test Leave Type LWP", "posting_date": "2013-01-02", - "to_date": "2013-01-15" + "to_date": "2013-01-15", + "get_holidays": "2013-05-03" } ] @@ -250,27 +253,18 @@ class TestLeaveApplication(unittest.TestCase): def test_exclude_holiday_in_leave(self): frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 0) - application = frappe.copy_doc(_test_records[2]) - application.from_date = "2015-07-01" - application.to_date = "2015-07-05" - application.get_holidays = "2015-07-03" + application = frappe.copy_doc(_test_records[0]) application.insert() - + self.assertEquals(application.tot_days, 5) self.assertEquals(application.holidays, 1) self.assertEquals(application.ret, 4) def test_include_holiday_in_leave(self): frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 1) - application = frappe.copy_doc(_test_records[2]) - application.from_date = "2015-07-01" - application.to_date = "2015-07-05" - application.get_holidays = "2015-07-03" + application = frappe.copy_doc(_test_records[0]) application.insert() - + self.assertEquals(application.tot_days, 5) self.assertEquals(application.holidays, 1) - self.assertEquals(application.ret, 5) - - def tearDown(self): - frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 0) \ No newline at end of file + self.assertEquals(application.ret, 5) \ No newline at end of file From e102332f088401cc632c2e47c994ed94c7f3efa5 Mon Sep 17 00:00:00 2001 From: Meatechsupport Date: Tue, 28 Jul 2015 19:03:30 +0400 Subject: [PATCH 19/43] removed testcase --- .../test_leave_application.py | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 97c6f2f655..9e2170855a 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -19,8 +19,7 @@ _test_records = [ "from_date": "2013-05-01", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", - "to_date": "2013-05-05", - "get_holidays": "2013-05-03" + "to_date": "2013-05-05" }, { "company": "_Test Company", @@ -30,8 +29,7 @@ _test_records = [ "from_date": "2013-05-01", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", - "to_date": "2013-05-05", - "get_holidays": "2013-05-03" + "to_date": "2013-05-05" }, { "company": "_Test Company", @@ -41,8 +39,7 @@ _test_records = [ "from_date": "2013-01-15", "leave_type": "_Test Leave Type LWP", "posting_date": "2013-01-02", - "to_date": "2013-01-15", - "get_holidays": "2013-05-03" + "to_date": "2013-01-15" } ] @@ -249,22 +246,4 @@ class TestLeaveApplication(unittest.TestCase): frappe.db.sql("""delete from `tabEmployee Leave Approver` where parent=%s""", "_T-Employee-0001") - frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department) - - def test_exclude_holiday_in_leave(self): - frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 0) - application = frappe.copy_doc(_test_records[0]) - application.insert() - - self.assertEquals(application.tot_days, 5) - self.assertEquals(application.holidays, 1) - self.assertEquals(application.ret, 4) - - def test_include_holiday_in_leave(self): - frappe.db.set_value("Leave Type", self.leave_type, "include_holiday", 1) - application = frappe.copy_doc(_test_records[0]) - application.insert() - - self.assertEquals(application.tot_days, 5) - self.assertEquals(application.holidays, 1) - self.assertEquals(application.ret, 5) \ No newline at end of file + frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department) \ No newline at end of file From 0e6f2474e8dde3bb42a53c2c58a4a62da9c56ef0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 29 Jul 2015 11:27:02 +0530 Subject: [PATCH 20/43] [minor] removed trailing space in offer letter status --- .../hr/doctype/offer_letter/offer_letter.json | 318 +++++++++--------- 1 file changed, 159 insertions(+), 159 deletions(-) diff --git a/erpnext/hr/doctype/offer_letter/offer_letter.json b/erpnext/hr/doctype/offer_letter/offer_letter.json index ec93bb004b..6f60a333ca 100644 --- a/erpnext/hr/doctype/offer_letter/offer_letter.json +++ b/erpnext/hr/doctype/offer_letter/offer_letter.json @@ -1,193 +1,193 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "Offer-.#####", - "creation": "2015-03-04 14:20:17.662207", - "custom": 0, - "default_print_format": "Offer Letter", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Transaction", + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "Offer-.#####", + "creation": "2015-03-04 14:20:17.662207", + "custom": 0, + "default_print_format": "Offer Letter", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", "fields": [ { - "allow_on_submit": 0, - "fieldname": "job_applicant", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Job Applicant", - "no_copy": 0, - "options": "Job Applicant", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, + "allow_on_submit": 0, + "fieldname": "job_applicant", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Job Applicant", + "no_copy": 0, + "options": "Job Applicant", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, "set_only_once": 0 - }, + }, { - "fieldname": "applicant_name", - "fieldtype": "Data", - "label": "Applicant Name", - "options": "job_applicant.applicant_name", - "permlevel": 0, - "precision": "", - "read_only": 1, + "fieldname": "applicant_name", + "fieldtype": "Data", + "label": "Applicant Name", + "options": "job_applicant.applicant_name", + "permlevel": 0, + "precision": "", + "read_only": 1, "reqd": 1 - }, + }, { - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "permlevel": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "permlevel": 0, "precision": "" - }, + }, { - "allow_on_submit": 1, - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "options": "Awaiting Response\nAccepted\nRejected ", - "permlevel": 0, - "precision": "", + "allow_on_submit": 1, + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Awaiting Response\nAccepted\nRejected", + "permlevel": 0, + "precision": "", "print_hide": 1 - }, + }, { - "default": "", - "fieldname": "offer_date", - "fieldtype": "Date", - "label": "Offer Date", - "permlevel": 0, + "default": "", + "fieldname": "offer_date", + "fieldtype": "Date", + "label": "Offer Date", + "permlevel": 0, "precision": "" - }, + }, { - "allow_on_submit": 0, - "fieldname": "designation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Designation", - "no_copy": 0, - "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, + "allow_on_submit": 0, + "fieldname": "designation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Designation", + "no_copy": 0, + "options": "Designation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, "set_only_once": 0 - }, + }, { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 1, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 1, "reqd": 1 - }, + }, { - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "permlevel": 0, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "offer_terms", - "fieldtype": "Table", - "label": "Offer Letter Terms", - "options": "Offer Letter Term", - "permlevel": 0, + "fieldname": "offer_terms", + "fieldtype": "Table", + "label": "Offer Letter Terms", + "options": "Offer Letter Term", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "permlevel": 0, + "fieldname": "section_break_14", + "fieldtype": "Section Break", + "permlevel": 0, "precision": "" - }, + }, { - "allow_on_submit": 0, - "fieldname": "select_terms", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Select Terms and Conditions", - "no_copy": 0, - "options": "Terms and Conditions", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, + "allow_on_submit": 0, + "fieldname": "select_terms", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Select Terms and Conditions", + "no_copy": 0, + "options": "Terms and Conditions", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, "set_only_once": 0 - }, + }, { - "fieldname": "terms", - "fieldtype": "Text Editor", - "label": "Terms and Conditions", - "options": "", - "permlevel": 0, + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions", + "options": "", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Offer Letter", - "permlevel": 0, - "print_hide": 1, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Offer Letter", + "permlevel": 0, + "print_hide": 1, "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "in_create": 0, - "in_dialog": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "modified": "2015-04-01 05:51:39.841591", - "modified_by": "Administrator", - "module": "HR", - "name": "Offer Letter", - "name_case": "", - "owner": "Administrator", + ], + "hide_heading": 0, + "hide_toolbar": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "modified": "2015-07-29 05:51:39.841591", + "modified_by": "Administrator", + "module": "HR", + "name": "Offer Letter", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 } - ], - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", "title_field": "applicant_name" -} \ No newline at end of file +} From 1e8025b3275c004f50ccffb0db6b21c7abfc62d1 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 24 Jul 2015 15:16:25 +0530 Subject: [PATCH 21/43] [cleanup] yes/no selects changed to checks in Item --- .../purchase_invoice/purchase_invoice.js | 5 +- .../purchase_invoice/purchase_invoice.py | 2 +- erpnext/accounts/doctype/sales_invoice/pos.py | 4 +- .../doctype/sales_invoice/sales_invoice.py | 32 +- .../report/gross_profit/gross_profit.py | 4 +- .../purchase_common/purchase_common.js | 4 +- .../purchase_common/purchase_common.py | 11 +- .../doctype/purchase_order/purchase_order.py | 2 +- erpnext/change_log/current/item_cleanup.md | 2 + erpnext/controllers/accounts_controller.py | 2 +- erpnext/controllers/buying_controller.py | 6 +- erpnext/controllers/selling_controller.py | 2 +- erpnext/controllers/stock_controller.py | 2 +- .../crm/doctype/opportunity/opportunity.js | 2 +- .../doctype/hub_settings/hub_settings.py | 2 +- erpnext/manufacturing/doctype/bom/bom.py | 8 +- .../production_order/production_order.js | 11 +- .../production_order/production_order.py | 10 +- .../production_order/test_production_order.py | 16 +- .../production_planning_tool.js | 2 +- .../production_planning_tool.py | 30 +- erpnext/patches.txt | 5 +- erpnext/patches/v4_2/set_item_has_batch.py | 56 +- erpnext/patches/v5_2/__init__.py | 1 + .../v5_2/change_item_selects_to_checks.py | 21 + .../installation_note/installation_note.py | 8 +- .../doctype/product_bundle/product_bundle.py | 4 +- .../selling/doctype/quotation/quotation.py | 10 +- .../doctype/sales_order/sales_order.py | 12 +- erpnext/selling/sales_common.js | 4 +- .../setup/page/setup_wizard/sample_data.py | 8 +- .../setup/page/setup_wizard/setup_wizard.py | 6 +- erpnext/stock/doctype/batch/batch.js | 6 +- erpnext/stock/doctype/batch/batch.py | 4 +- .../doctype/delivery_note/delivery_note.py | 12 +- erpnext/stock/doctype/item/item.js | 8 +- erpnext/stock/doctype/item/item.json | 690 +++++++++--------- erpnext/stock/doctype/item/item.py | 25 +- erpnext/stock/doctype/item/test_item.py | 8 +- erpnext/stock/doctype/item/test_records.json | 238 +++--- .../landed_cost_voucher.py | 4 +- .../material_request/material_request.py | 2 +- .../purchase_receipt/purchase_receipt.py | 5 +- erpnext/stock/doctype/serial_no/serial_no.js | 2 +- erpnext/stock/doctype/serial_no/serial_no.py | 6 +- .../stock/doctype/stock_entry/stock_entry.js | 8 +- .../stock/doctype/stock_entry/stock_entry.py | 10 +- .../stock_ledger_entry/stock_ledger_entry.py | 6 +- .../stock_reconciliation.py | 6 +- .../stock_uom_replace_utility.js | 2 +- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- erpnext/stock/get_item_details.py | 13 +- erpnext/stock/reorder_item.py | 2 +- .../items_to_be_requested.json | 30 +- .../supplier_wise_sales_analytics.py | 28 +- erpnext/stock/utils.py | 3 +- .../maintenance_schedule.js | 2 +- .../maintenance_visit/maintenance_visit.js | 2 +- erpnext/utilities/repost_stock.py | 2 +- 59 files changed, 731 insertions(+), 689 deletions(-) create mode 100644 erpnext/change_log/current/item_cleanup.md create mode 100644 erpnext/patches/v5_2/__init__.py create mode 100644 erpnext/patches/v5_2/change_item_selects_to_checks.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 6a02706532..3e4522ca0d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -69,7 +69,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } }, - + supplier: function() { var me = this; if(this.frm.updating_party_details) @@ -152,7 +152,7 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do return { query: "erpnext.controllers.queries.item_query", filters:{ - 'is_purchase_item': 'Yes' + 'is_purchase_item': 1 } } } @@ -232,4 +232,3 @@ cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){ else cur_frm.pformat.print_heading = __("Purchase Invoice"); } - diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 006470f860..cc77394bf8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -80,7 +80,7 @@ class PurchaseInvoice(BuyingController): def check_active_purchase_items(self): for d in self.get('items'): if d.item_code: # extra condn coz item_code is not mandatory in PV - if frappe.db.get_value("Item", d.item_code, "is_purchase_item") != 'Yes': + if frappe.db.get_value("Item", d.item_code, "is_purchase_item") != 1: msgprint(_("Item {0} is not Purchase Item").format(d.item_code), raise_exception=True) def check_conversion_rate(self): diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 2c5bb12fdb..e81eec9319 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -11,9 +11,9 @@ def get_items(price_list, sales_or_purchase, item=None): args = {"price_list": price_list} if sales_or_purchase == "Sales": - condition = "i.is_sales_item='Yes'" + condition = "i.is_sales_item=1" else: - condition = "i.is_purchase_item='Yes'" + condition = "i.is_purchase_item=1" if item: # search serial no diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5a9ccea1d2..12e199f74c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -85,7 +85,7 @@ class SalesInvoice(SellingController): self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Sales Order") self.check_credit_limit() - + # this sequence because outstanding may get -ve self.make_gl_entries() @@ -102,15 +102,15 @@ class SalesInvoice(SellingController): self.update_stock_ledger() self.check_stop_sales_order("sales_order") - + from erpnext.accounts.utils import remove_against_link_from_jv remove_against_link_from_jv(self.doctype, self.name, "against_invoice") - + if not self.is_return: self.update_status_updater_args() self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Sales Order") - + self.validate_c_form_on_cancel() self.make_gl_entries_on_cancel() @@ -248,12 +248,10 @@ class SalesInvoice(SellingController): def validate_fixed_asset_account(self): """Validate Fixed Asset and whether Income Account Entered Exists""" for d in self.get('items'): - item = frappe.db.sql("""select name,is_asset_item,is_sales_item from `tabItem` - where name = %s""", d.item_code) - acc = frappe.db.sql("""select account_type from `tabAccount` - where name = %s and docstatus != 2""", d.income_account) - if item and item[0][1] == 'Yes' and acc and acc[0][0] != 'Fixed Asset': - msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(acc[0][0], d.item_code), raise_exception=True) + is_asset_item = frappe.db.get_value("Item", d.item_code, "is_asset_item") + account_type = frappe.db.get_value("Account", d.income_account, "account_type") + if is_asset_item == 1 and account_type != 'Fixed Asset': + msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(d.income_account, d.item_code), raise_exception=True) def validate_with_previous_doc(self): super(SalesInvoice, self).validate_with_previous_doc({ @@ -271,7 +269,7 @@ class SalesInvoice(SellingController): if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')): self.validate_rate_with_reference_doc([ - ["Sales Order", "sales_order", "so_detail"], + ["Sales Order", "sales_order", "so_detail"], ["Delivery Note", "delivery_note", "dn_detail"] ]) @@ -296,7 +294,7 @@ class SalesInvoice(SellingController): for i in dic: if frappe.db.get_value('Selling Settings', None, dic[i]) == 'Yes': for d in self.get('items'): - if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 'Yes' \ + if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 1 \ and not d.get(i.lower().replace(' ','_')): msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1) @@ -426,11 +424,13 @@ class SalesInvoice(SellingController): def update_stock_ledger(self): sl_entries = [] for d in self.get_item_list(): - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse: + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \ + and d.warehouse: incoming_rate = 0 if cint(self.is_return) and self.return_against and self.docstatus==1: - incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against) - + incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, + self.return_against) + sl_entries.append(self.get_sl_entries(d, { "actual_qty": -1*flt(d.qty), "stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom"), @@ -664,4 +664,4 @@ def make_delivery_note(source_name, target_doc=None): @frappe.whitelist() def make_sales_return(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc - return make_return_doc("Sales Invoice", source_name, target_doc) \ No newline at end of file + return make_return_doc("Sales Invoice", source_name, target_doc) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 75c353d170..36d1676047 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -215,7 +215,7 @@ class GrossProfitGenerator(object): if self.filters.to_date: conditions += " and posting_date <= %(to_date)s" - self.si_list = frappe.db.sql("""select item.parenttype, item.parent, + self.si_list = frappe.db.sql("""select item.parenttype, item.parent, si.posting_date, si.posting_time, si.project_name, si.update_stock, si.customer, si.customer_group, si.territory, item.item_code, item.item_name, item.description, item.warehouse, @@ -257,4 +257,4 @@ class GrossProfitGenerator(object): def load_non_stock_items(self): self.non_stock_items = frappe.db.sql_list("""select name from tabItem - where ifnull(is_stock_item, 'No')='No'""") + where is_stock_item=0""") diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.js b/erpnext/buying/doctype/purchase_common/purchase_common.js index 19ad9ab651..7693e08094 100644 --- a/erpnext/buying/doctype/purchase_common/purchase_common.js +++ b/erpnext/buying/doctype/purchase_common/purchase_common.js @@ -44,12 +44,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ if(me.frm.doc.is_subcontracted == "Yes") { return{ query: "erpnext.controllers.queries.item_query", - filters:{ 'is_sub_contracted_item': 'Yes' } + filters:{ 'is_sub_contracted_item': 1 } } } else { return{ query: "erpnext.controllers.queries.item_query", - filters: { 'is_purchase_item': 'Yes' } + filters: { 'is_purchase_item': 1 } } } }); diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.py b/erpnext/buying/doctype/purchase_common/purchase_common.py index 1bf6f8fe67..7c55d59350 100644 --- a/erpnext/buying/doctype/purchase_common/purchase_common.py +++ b/erpnext/buying/doctype/purchase_common/purchase_common.py @@ -56,24 +56,25 @@ class PurchaseCommon(BuyingController): d.set(x, f_lst[x]) item = frappe.db.sql("""select is_stock_item, is_purchase_item, - is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", d.item_code) + is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", + d.item_code, as_dict=1)[0] from erpnext.stock.doctype.item.item import validate_end_of_life - validate_end_of_life(d.item_code, item[0][3]) + validate_end_of_life(d.item_code, item.end_of_life) # validate stock item - if item[0][0]=='Yes' and d.qty and not d.warehouse: + if item.is_stock_item==1 and d.qty and not d.warehouse: frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx)) # validate purchase item if not (obj.doctype=="Material Request" and getattr(obj, "material_request_type", None)=="Material Transfer"): - if item[0][1] != 'Yes' and item[0][2] != 'Yes': + if item.is_purchase_item != 1 and item.is_sub_contracted_item != 1: frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx)) items.append(cstr(d.item_code)) if items and len(items) != len(set(items)): frappe.msgprint(_("Warning: Same item has been entered multiple times.")) - + def check_for_stopped_status(self, doctype, docname): stopped = frappe.db.sql("""select name from `tab%s` where name = %s and diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 220dff203f..6756e47d0e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -153,7 +153,7 @@ class PurchaseOrder(BuyingController): item_wh_list = [] for d in self.get("items"): if (not po_item_rows or d.name in po_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \ - and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse: + and frappe.db.get_value("Item", d.item_code, "is_stock_item") and d.warehouse: item_wh_list.append([d.item_code, d.warehouse]) for item_code, warehouse in item_wh_list: diff --git a/erpnext/change_log/current/item_cleanup.md b/erpnext/change_log/current/item_cleanup.md new file mode 100644 index 0000000000..c2f1e911e7 --- /dev/null +++ b/erpnext/change_log/current/item_cleanup.md @@ -0,0 +1,2 @@ +- **Item** form cleanups: "Yes" / "No" type fields changed to checkboxes.
+ **Warning**: This could break your 3rd party integrations with Item, if any. diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 55e9e3d000..1623f3f7e4 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -307,7 +307,7 @@ class AccountsController(TransactionBase): item_codes = list(set(item.item_code for item in self.get("items"))) if item_codes: stock_items = [r[0] for r in frappe.db.sql("""select name - from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \ + from `tabItem` where name in (%s) and is_stock_item=1""" % \ (", ".join((["%s"]*len(item_codes))),), item_codes)] return stock_items diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 0b60473b8b..7fd8febd4f 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -238,8 +238,8 @@ class BuyingController(StockController): t2.rate, t2.stock_uom, t2.name, t2.description from `tabBOM` t1, `tabBOM Item` t2, tabItem t3 where t2.parent = t1.name and t1.item = %s - and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s - and t2.item_code = t3.name and ifnull(t3.is_stock_item, 'No') = 'Yes'""", (item_code, bom), as_dict=1) + and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s + and t2.item_code = t3.name and t3.is_stock_item = 1""", (item_code, bom), as_dict=1) if not bom_items: msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) @@ -254,7 +254,7 @@ class BuyingController(StockController): self.get("items"))) if item_codes: self._sub_contracted_items = [r[0] for r in frappe.db.sql("""select name - from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \ + from `tabItem` where name in (%s) and is_sub_contracted_item=1""" % \ (", ".join((["%s"]*len(item_codes))),), item_codes)] return self._sub_contracted_items diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 5ad0a25af3..9a0aedf176 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -248,7 +248,7 @@ def check_active_sales_items(obj): item = frappe.db.sql("""select docstatus, is_sales_item, is_service_item, income_account from tabItem where name = %s""", d.item_code, as_dict=True)[0] - if item.is_sales_item == 'No' and item.is_service_item == 'No': + if item.is_sales_item == 0 and item.is_service_item == 0: frappe.throw(_("Item {0} must be Sales or Service Item in {1}").format(d.item_code, d.idx)) if getattr(d, "income_account", None) and not item.income_account: frappe.db.set_value("Item", d.item_code, "income_account", diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 19440e24a7..a47314bfd6 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -212,7 +212,7 @@ class StockController(AccountsController): item_codes = list(set([d.item_code for d in self.get("items")])) if item_codes: serialized_items = frappe.db.sql_list("""select name from `tabItem` - where has_serial_no='Yes' and name in ({})""".format(", ".join(["%s"]*len(item_codes))), + where has_serial_no=1 and name in ({})""".format(", ".join(["%s"]*len(item_codes))), tuple(item_codes)) return serialized_items diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 6fc258902f..6156dcf567 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -48,7 +48,7 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ return { query: "erpnext.controllers.queries.item_query", filters: me.frm.doc.enquiry_type === "Maintenance" ? - {"is_service_item": "Yes"} : {"is_sales_item": "Yes"} + {"is_service_item": 1} : {"is_sales_item":1} }; }); diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 4201e674fd..cf48381fc5 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -24,7 +24,7 @@ class HubSettings(Document): def publish_selling_items(self): """Set `publish_in_hub`=1 for all Sales Items""" for item in frappe.get_all("Item", fields=["name"], - filters={"is_sales_item": "Yes", "publish_in_hub": "0"}): + filters={"is_sales_item": 1, "publish_in_hub": "0"}): frappe.db.set_value("Item", item.name, "publish_in_hub", 1) def register(self): diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index fe67ed8057..ccbba15f2e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -107,7 +107,7 @@ class BOM(Document): rate = 0 if arg['bom_no']: rate = self.get_bom_unitcost(arg['bom_no']) - elif arg and (arg['is_purchase_item'] == 'Yes' or arg['is_sub_contracted_item'] == 'Yes'): + elif arg and (arg['is_purchase_item'] == 1 or arg['is_sub_contracted_item'] == 1): if self.rm_cost_as_per == 'Valuation Rate': rate = self.get_valuation_rate(arg) elif self.rm_cost_as_per == 'Last Purchase Rate': @@ -385,14 +385,14 @@ def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1): and bom_item.docstatus < 2 and bom_item.parent = %(bom)s and item.name = bom_item.item_code - and ifnull(item.is_stock_item, 'No') = 'Yes' + and is_stock_item = 1 {conditions} group by item_code, stock_uom""" if fetch_exploded: query = query.format(table="BOM Explosion Item", - conditions="""and ifnull(item.is_pro_applicable, 'No') = 'No' - and ifnull(item.is_sub_contracted_item, 'No') = 'No' """) + conditions="""and item.is_pro_applicable = 0 + and item.is_sub_contracted_item = 0 """) items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True) else: query = query.format(table="BOM Item", conditions="") diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js index d36637784a..60b4918f9a 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.js +++ b/erpnext/manufacturing/doctype/production_order/production_order.js @@ -191,11 +191,10 @@ $.extend(cur_frm.cscript, { method: "set_production_order_operations" }); }, - + qty: function() { frappe.ui.form.trigger("Production Order", 'bom_no') }, - show_time_logs: function(doc, cdt, cdn) { var child = locals[cdt][cdn] frappe.route_options = {"operation_id": child.name}; @@ -250,8 +249,8 @@ cur_frm.cscript['Update Finished Goods'] = function() { cur_frm.fields_dict['production_item'].get_query = function(doc) { return { filters:[ - ['Item', 'is_pro_applicable', '=', 'Yes'], - ['Item', 'has_variants', '=', 'No'], + ['Item', 'is_pro_applicable', '=', 1], + ['Item', 'has_variants', '=', 0] ['Item', 'end_of_life', '>=', frappe.datetime.nowdate()] ] } @@ -264,7 +263,3 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) { ] } } - - - - diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 13cc52374d..b79f8b624a 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -94,7 +94,7 @@ class ProductionOrder(Document): (self.sales_order, self.production_item))[0][0] # total qty in SO so_qty = flt(so_item_qty) + flt(dnpi_qty) - + allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "over_production_allowance_percentage")) if total_qty > so_qty + (allowance_percentage/100 * so_qty): frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}").format(self.production_item, @@ -320,14 +320,14 @@ class ProductionOrder(Document): def delete_time_logs(self): for time_log in frappe.get_all("Time Log", ["name"], {"production_order": self.name}): frappe.delete_doc("Time Log", time_log.name) - + def validate_production_item(self): - if frappe.db.get_value("Item", self.production_item, "is_pro_applicable")=='No': + if not frappe.db.get_value("Item", self.production_item, "is_pro_applicable"): frappe.throw(_("Item is not allowed to have Production Order."), ProductionNotApplicableError) - + if frappe.db.get_value("Item", self.production_item, "has_variants"): frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError) - + validate_end_of_life(self.production_item) @frappe.whitelist() diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index b91b2e12c8..ddcc8c7404 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -126,7 +126,7 @@ class TestProductionOrder(unittest.TestCase): "docstatus": 0 }) self.assertRaises(OverProductionLoggedError, time_log2.save) - + def test_planned_operating_cost(self): prod_order = make_prod_order_test_record(item="_Test FG Item 2", planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True) @@ -135,20 +135,20 @@ class TestProductionOrder(unittest.TestCase): prod_order.qty = 2 prod_order.set_production_order_operations() self.assertEqual(prod_order.planned_operating_cost, cost*2) - + def test_production_item(self): - frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", "No") + frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", 0) prod_order = make_prod_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) self.assertRaises(ProductionNotApplicableError, prod_order.save) - - frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", "Yes") + + frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", 1) frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1") - + self.assertRaises(frappe.ValidationError, prod_order.save) - + frappe.db.set_value("Item", "_Test FG Item", "end_of_life", None) - + prod_order = make_prod_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True) self.assertRaises(ItemHasVariantError, prod_order.save) diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js index f3b918d139..212acaac61 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js @@ -52,7 +52,7 @@ cur_frm.fields_dict['sales_orders'].grid.get_field('sales_order').get_query = fu cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) { return erpnext.queries.item({ - 'is_pro_applicable': 'Yes' + 'is_pro_applicable': 1 }); } diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 271abac10e..c4cebb80bb 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -61,13 +61,13 @@ class ProductionPlanningTool(Document): and so.company = %s and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s and (exists (select name from `tabItem` item where item.name=so_item.item_code - and (ifnull(item.is_pro_applicable, 'No') = 'Yes' - or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s) + and (item.is_pro_applicable = 1 + or item.is_sub_contracted_item = 1 %s) or exists (select name from `tabPacked Item` pi where pi.parent = so.name and pi.parent_item = so_item.item_code and exists (select name from `tabItem` item where item.name=pi.item_code - and (ifnull(item.is_pro_applicable, 'No') = 'Yes' - or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s))) + and (item.is_pro_applicable = 1 + or item.is_sub_contracted_item = 1) %s))) """ % ('%s', so_filter, item_filter, item_filter), self.company, as_dict=1) self.add_so_in_table(open_so) @@ -108,8 +108,8 @@ class ProductionPlanningTool(Document): from `tabSales Order Item` so_item where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0) and exists (select * from `tabItem` item where item.name=so_item.item_code - and (ifnull(item.is_pro_applicable, 'No') = 'Yes' - or ifnull(item.is_sub_contracted_item, 'No') = 'Yes')) %s""" % \ + and (item.is_pro_applicable = 1 + or item.is_sub_contracted_item = 1)) %s""" % \ (", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1) if self.fg_item: @@ -123,8 +123,8 @@ class ProductionPlanningTool(Document): and pi.parent_item = so_item.item_code and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) and exists (select * from `tabItem` item where item.name=pi.item_code - and (ifnull(item.is_pro_applicable, 'No') = 'Yes' - or ifnull(item.is_sub_contracted_item, 'No') = 'Yes')) %s""" % \ + and (item.is_pro_applicable = 1 + or item.is_sub_contracted_item = 1)) %s""" % \ (", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1) return items + packed_items @@ -178,7 +178,7 @@ class ProductionPlanningTool(Document): for d in self.get("items"): if d.bom_no: bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)]) - if frappe.db.get_value("Item", d.item_code, "is_pro_applicable") == "Yes": + if frappe.db.get_value("Item", d.item_code, "is_pro_applicable"): item_dict[(d.item_code, d.sales_order, d.warehouse)] = { "production_item" : d.item_code, "sales_order" : d.sales_order, @@ -239,10 +239,10 @@ class ProductionPlanningTool(Document): ifnull(sum(ifnull(fb.qty, 0)/ifnull(bom.quantity, 1)), 0) as qty, fb.description, fb.stock_uom, it.min_order_qty from `tabBOM Explosion Item` fb, `tabBOM` bom, `tabItem` it - where bom.name = fb.parent and it.name = fb.item_code - and ifnull(it.is_pro_applicable, 'No') = 'No' - and ifnull(it.is_sub_contracted_item, 'No') = 'No' - and ifnull(it.is_stock_item, 'No') = 'Yes' + where bom.name = fb.parent and it.name = fb.item_code + and is_pro_applicable = 0 + and is_sub_contracted_item = 0 + and is_stock_item = 1 and fb.docstatus<2 and bom.name=%s group by item_code, stock_uom""", bom, as_dict=1): bom_wise_item_details.setdefault(d.item_code, d) @@ -255,7 +255,7 @@ class ProductionPlanningTool(Document): from `tabBOM Item` bom_item, `tabBOM` bom, tabItem item where bom.name = bom_item.parent and bom.name = %s and bom_item.docstatus < 2 and bom_item.item_code = item.name - and ifnull(item.is_stock_item, 'No') = 'Yes' + and item.is_stock_item = 1 group by item_code""", bom, as_dict=1): bom_wise_item_details.setdefault(d.item_code, d) @@ -350,7 +350,7 @@ class ProductionPlanningTool(Document): def insert_purchase_request(self): items_to_be_requested = self.get_requested_items() - + purchase_request_list = [] if items_to_be_requested: for item in items_to_be_requested: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 762912372b..648d5b7c83 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -24,6 +24,9 @@ erpnext.patches.v4_0.map_charge_to_taxes_and_charges execute:frappe.reload_doc('support', 'doctype', 'newsletter') # 2014-01-31 execute:frappe.reload_doc('hr', 'doctype', 'employee') # 2014-02-03 execute:frappe.db.sql("update tabPage set module='Core' where name='Setup'") + +# inserted this patch here since Item types are being changed +erpnext.patches.v5_2.change_item_selects_to_checks erpnext.patches.v4_0.fields_to_be_renamed erpnext.patches.v4_0.rename_sitemap_to_route erpnext.patches.v4_0.fix_contact_address @@ -176,4 +179,4 @@ erpnext.patches.v5_1.fix_credit_days_based_on execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True) erpnext.patches.v5_1.rename_roles erpnext.patches.v5_1.default_bom -execute:frappe.delete_doc("DocType", "Party Type") \ No newline at end of file +execute:frappe.delete_doc("DocType", "Party Type") diff --git a/erpnext/patches/v4_2/set_item_has_batch.py b/erpnext/patches/v4_2/set_item_has_batch.py index 740efc8548..7e52d2def0 100644 --- a/erpnext/patches/v4_2/set_item_has_batch.py +++ b/erpnext/patches/v4_2/set_item_has_batch.py @@ -5,61 +5,61 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.db.sql("update tabItem set has_batch_no = 'No' where ifnull(has_batch_no, '') = ''") - frappe.db.sql("update tabItem set has_serial_no = 'No' where ifnull(has_serial_no, '') = ''") - - item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem - where ifnull(is_stock_item, 'No') = 'Yes'""", as_dict=1) - + frappe.db.sql("update tabItem set has_batch_no = 0 where ifnull(has_batch_no, '') = ''") + frappe.db.sql("update tabItem set has_serial_no = 0 where ifnull(has_serial_no, '') = ''") + + item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem + where is_stock_item = 1""", as_dict=1) + sle_count = get_sle_count() sle_with_batch = get_sle_with_batch() sle_with_serial = get_sle_with_serial() - + batch_items = get_items_with_batch() serialized_items = get_items_with_serial() - - for d in item_list: - if d.has_batch_no == 'Yes': + + for d in item_list: + if d.has_batch_no == 1: if d.name not in batch_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_batch.get(d.name): - frappe.db.set_value("Item", d.name, "has_batch_no", "No") + frappe.db.set_value("Item", d.name, "has_batch_no", 0) else: if d.name in batch_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_batch.get(d.name)): - frappe.db.set_value("Item", d.name, "has_batch_no", "Yes") - - if d.has_serial_no == 'Yes': + frappe.db.set_value("Item", d.name, "has_batch_no", 1) + + if d.has_serial_no == 1: if d.name not in serialized_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_serial.get(d.name): - frappe.db.set_value("Item", d.name, "has_serial_no", "No") + frappe.db.set_value("Item", d.name, "has_serial_no", 0) else: if d.name in serialized_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_serial.get(d.name)): - frappe.db.set_value("Item", d.name, "has_serial_no", "Yes") - - + frappe.db.set_value("Item", d.name, "has_serial_no", 1) + + def get_sle_count(): sle_count = {} for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` group by item_code""", as_dict=1): sle_count.setdefault(d.item_code, d.cnt) - + return sle_count - + def get_sle_with_batch(): sle_with_batch = {} - for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` + for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` where ifnull(batch_no, '') != '' group by item_code""", as_dict=1): sle_with_batch.setdefault(d.item_code, d.cnt) - + return sle_with_batch - + def get_sle_with_serial(): sle_with_serial = {} - for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` + for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` where ifnull(serial_no, '') != '' group by item_code""", as_dict=1): sle_with_serial.setdefault(d.item_code, d.cnt) - + return sle_with_serial - + def get_items_with_batch(): return frappe.db.sql_list("select item from tabBatch") - + def get_items_with_serial(): - return frappe.db.sql_list("select item_code from `tabSerial No`") \ No newline at end of file + return frappe.db.sql_list("select item_code from `tabSerial No`") diff --git a/erpnext/patches/v5_2/__init__.py b/erpnext/patches/v5_2/__init__.py new file mode 100644 index 0000000000..baffc48825 --- /dev/null +++ b/erpnext/patches/v5_2/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/patches/v5_2/change_item_selects_to_checks.py b/erpnext/patches/v5_2/change_item_selects_to_checks.py new file mode 100644 index 0000000000..25d596b3eb --- /dev/null +++ b/erpnext/patches/v5_2/change_item_selects_to_checks.py @@ -0,0 +1,21 @@ +from __future__ import unicode_literals + +import frappe + +def execute(): + fields = ("is_stock_item", "is_asset_item", "has_batch_no", "has_serial_no", + "is_purchase_item", "is_sales_item", "is_service_item", "inspection_required", + "is_pro_applicable", "is_sub_contracted_item") + + + # convert to 1 or 0 + update_str = ", ".join(["`{0}`=if(`{0}`='Yes',1,0)".format(f) for f in fields]) + frappe.db.sql("update tabItem set {0}".format(update_str)) + + frappe.db.commit() + + # alter fields to int + for f in fields: + frappe.db.sql("alter table tabItem change {0} {0} int(1) default '0'".format(f, f)) + + frappe.reload_doctype("Item") diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py index 662a8f7ea9..e2e7ee6ef1 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.py +++ b/erpnext/selling/doctype/installation_note/installation_note.py @@ -39,10 +39,10 @@ class InstallationNote(TransactionBase): check_active_sales_items(self) def is_serial_no_added(self, item_code, serial_no): - ar_required = frappe.db.get_value("Item", item_code, "has_serial_no") - if ar_required == 'Yes' and not serial_no: + has_serial_no = frappe.db.get_value("Item", item_code, "has_serial_no") + if has_serial_no == 1 and not serial_no: frappe.throw(_("Serial No is mandatory for Item {0}").format(item_code)) - elif ar_required != 'Yes' and cstr(serial_no).strip(): + elif has_serial_no != 1 and cstr(serial_no).strip(): frappe.throw(_("Item {0} is not a serialized Item").format(item_code)) def is_serial_no_exist(self, item_code, serial_no): @@ -69,7 +69,7 @@ class InstallationNote(TransactionBase): frappe.throw(_("Serial No {0} does not belong to Delivery Note {1}").format(sr, prevdoc_docname)) def validate_serial_no(self): - cur_s_no, prevdoc_s_no, sr_list = [], [], [] + prevdoc_s_no, sr_list = [], [] for d in self.get('items'): self.is_serial_no_added(d.item_code, d.serial_no) if d.serial_no: diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index 796b4b43ac..8c95a45bf3 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -21,7 +21,7 @@ class ProductBundle(Document): def validate_main_item(self): """main item must have Is Stock Item as No and Is Sales Item as Yes""" if not frappe.db.sql("""select name from tabItem where name=%s and - ifnull(is_stock_item,'')='No' and ifnull(is_sales_item,'')='Yes'""", self.new_item_code): + is_stock_item = 0 and is_sales_item = 1""", self.new_item_code): frappe.throw(_("Parent Item {0} must be not Stock Item and must be a Sales Item").format(self.new_item_code)) def get_item_details(self, name): @@ -36,7 +36,7 @@ def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond return frappe.db.sql("""select name, item_name, description from tabItem - where is_stock_item="No" and is_sales_item="Yes" + where is_stock_item=0 and is_sales_item=1 and name not in (select name from `tabProduct Bundle`) and %s like %s %s limit %s, %s""" % (searchfield, "%s", get_match_cond(doctype),"%s", "%s"), diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 6ce34e0ff8..5a98e084b9 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -28,17 +28,11 @@ class Quotation(SellingController): if self.order_type in ['Maintenance', 'Service']: for d in self.get('items'): - is_service_item = frappe.db.sql("select is_service_item from `tabItem` where name=%s", d.item_code) - is_service_item = is_service_item and is_service_item[0][0] or 'No' - - if is_service_item == 'No': + if not frappe.db.get_value("Item", d.item_code, "is_service_item"): frappe.throw(_("Item {0} must be Service Item").format(d.item_code)) else: for d in self.get('items'): - is_sales_item = frappe.db.sql("select is_sales_item from `tabItem` where name=%s", d.item_code) - is_sales_item = is_sales_item and is_sales_item[0][0] or 'No' - - if is_sales_item == 'No': + if not frappe.db.get_value("Item", d.item_code, "is_sales_item"): frappe.throw(_("Item {0} must be Sales Item").format(d.item_code)) def validate_quotation_to(self): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d45fbba486..b92e934fa3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -39,7 +39,7 @@ class SalesOrder(SellingController): for d in self.get('items'): check_list.append(cstr(d.item_code)) - if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or + if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or self.has_product_bundle(d.item_code)) and not d.warehouse: frappe.throw(_("Reserved warehouse required for stock item {0}").format(d.item_code)) @@ -217,7 +217,7 @@ class SalesOrder(SellingController): def update_stock_ledger(self, update_stock): from erpnext.stock.utils import update_bin for d in self.get_item_list(): - if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == "Yes": + if frappe.db.get_value("Item", d['item_code'], "is_stock_item")==1: args = { "item_code": d['item_code'], "warehouse": d['reserved_warehouse'], @@ -258,14 +258,14 @@ def stop_or_unstop_sales_orders(names, status): def before_recurring(self): super(SalesOrder, self).before_recurring() - + for field in ("delivery_status", "per_delivered", "billing_status", "per_billed"): self.set(field, None) for d in self.get("items"): for field in ("delivered_qty", "billed_amt", "planned_qty", "prevdoc_docname"): d.set(field, None) - + @frappe.whitelist() def make_material_request(source_name, target_doc=None): @@ -273,9 +273,9 @@ def make_material_request(source_name, target_doc=None): doc.material_request_type = "Purchase" so = frappe.get_doc("Sales Order", source_name) - + item_table = "Packed Item" if so.packed_items else "Sales Order Item" - + doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { "doctype": "Material Request", diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index e8d8fd5f7a..5404947e1d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -57,8 +57,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ return { query: "erpnext.controllers.queries.item_query", filters: (me.frm.doc.order_type === "Maintenance" ? - {'is_service_item': 'Yes'}: - {'is_sales_item': 'Yes' }) + {'is_service_item': 1}: + {'is_sales_item': 1 }) } }); } diff --git a/erpnext/setup/page/setup_wizard/sample_data.py b/erpnext/setup/page/setup_wizard/sample_data.py index d9f8343722..b90cceb589 100644 --- a/erpnext/setup/page/setup_wizard/sample_data.py +++ b/erpnext/setup/page/setup_wizard/sample_data.py @@ -11,8 +11,8 @@ def make_sample_data(): """Create a few opportunities, quotes, material requests, issues, todos, projects to help the user get started""" - selling_items = frappe.get_all("Item", filters = {"is_sales_item": "Yes"}) - buying_items = frappe.get_all("Item", filters = {"is_sales_item": "No"}) + selling_items = frappe.get_all("Item", filters = {"is_sales_item": 1}) + buying_items = frappe.get_all("Item", filters = {"is_sales_item": 0}) if selling_items: for i in range(3): @@ -37,7 +37,7 @@ def make_opportunity(selling_items): add_random_children(b, "items", rows=len(selling_items), randomize = { "qty": (1, 5), - "item_code": ("Item", {"is_sales_item": "Yes"}) + "item_code": ("Item", {"is_sales_item": 1}) }, unique="item_code") b.insert(ignore_permissions=True) @@ -54,7 +54,7 @@ def make_quote(selling_items): add_random_children(qtn, "items", rows=len(selling_items), randomize = { "qty": (1, 5), - "item_code": ("Item", {"is_sales_item": "Yes"}) + "item_code": ("Item", {"is_sales_item": 1}) }, unique="item_code") qtn.insert(ignore_permissions=True) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index e8c769974b..2734172978 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -368,10 +368,10 @@ def create_items(args): "item_code": item, "item_name": item, "description": item, - "is_sales_item": "Yes" if is_sales_item else "No", - "is_purchase_item": "Yes" if is_purchase_item else "No", + "is_sales_item": 1 if is_sales_item else 0, + "is_purchase_item": 1 if is_purchase_item else 0, "show_in_website": 1, - "is_stock_item": is_stock_item and "Yes" or "No", + "is_stock_item": is_stock_item and 1 or 0, "item_group": item_group, "stock_uom": args.get("item_uom_" + str(i)), "default_warehouse": default_warehouse diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index d0d532e63e..2cfdb6386d 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -5,8 +5,8 @@ cur_frm.fields_dict['item'].get_query = function(doc, cdt, cdn) { return { query: "erpnext.controllers.queries.item_query", filters:{ - 'is_stock_item': 'Yes', - 'has_batch_no': 'Yes' + 'is_stock_item': 1, + 'has_batch_no': 1 } - } + } } diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index abc6fb37fb..157b0944fc 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -7,10 +7,10 @@ from frappe import _ from frappe.model.document import Document class Batch(Document): - + def validate(self): self.item_has_batch_enabled() def item_has_batch_enabled(self): - if frappe.db.get_value("Item",self.item,"has_batch_no") =='No': + if frappe.db.get_value("Item",self.item,"has_batch_no") == 0: frappe.throw(_("The selected item cannot have Batch")) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index e3058822f6..bf7505baa3 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -108,7 +108,7 @@ class DeliveryNote(SellingController): if not self.installation_status: self.installation_status = 'Not Installed' def validate_with_previous_doc(self): - for fn in (("Sales Order", "against_sales_order", "so_detail"), + for fn in (("Sales Order", "against_sales_order", "so_detail"), ("Sales Invoice", "against_sales_invoice", "si_detail")): if filter(None, [getattr(d, fn[1], None) for d in self.get("items")]): super(DeliveryNote, self).validate_with_previous_doc({ @@ -118,9 +118,9 @@ class DeliveryNote(SellingController): ["currency", "="]], }, }) - + if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')): - self.validate_rate_with_reference_doc([["Sales Order", "sales_order", "so_detail"], + self.validate_rate_with_reference_doc([["Sales Order", "sales_order", "so_detail"], ["Sales Invoice", "sales_invoice", "si_detail"]]) def validate_proj_cust(self): @@ -138,7 +138,7 @@ class DeliveryNote(SellingController): e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes': + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: if e in check_list: msgprint(_("Note: Item {0} entered multiple times").format(d.item_code)) else: @@ -151,7 +151,7 @@ class DeliveryNote(SellingController): def validate_warehouse(self): for d in self.get_item_list(): - if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == "Yes": + if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1: if not d['warehouse']: frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"])) @@ -247,7 +247,7 @@ class DeliveryNote(SellingController): def update_stock_ledger(self): sl_entries = [] for d in self.get_item_list(): - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \ + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \ and d.warehouse and flt(d['qty']): self.update_reserved_qty(d) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 3bd5657d59..35455b4fee 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -43,7 +43,7 @@ frappe.ui.form.on("Item", { erpnext.item.edit_prices_button(frm); - if (!frm.doc.__islocal && frm.doc.is_stock_item == 'Yes') { + if (!frm.doc.__islocal && frm.doc.is_stock_item) { frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'], (frm.doc.__onload && frm.doc.__onload.sle_exists=="exists") ? false : true); } @@ -80,11 +80,11 @@ frappe.ui.form.on("Item", { method: "copy_specification_from_item_group" }); }, - + is_stock_item: function(frm) { erpnext.item.toggle_reqd(frm); }, - + manage_variants: function(frm) { if (cur_frm.doc.__unsaved==1) { frappe.throw(__("You have unsaved changes. Please save.")) @@ -168,7 +168,7 @@ $.extend(erpnext.item, { }, toggle_reqd: function(frm) { - frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item==="Yes"); + frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item); }, make_dashboard: function(frm) { diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 1012cdab89..b0bc85a7c4 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -28,7 +28,7 @@ "read_only": 0 }, { - "description": "Item will be saved by this name in the data base.", + "description": "", "fieldname": "item_code", "fieldtype": "Data", "in_filter": 0, @@ -80,7 +80,7 @@ "reqd": 1 }, { - "description": "Unit of measurement of this item (e.g. Kg, Unit, No, Pair).", + "description": "", "fieldname": "stock_uom", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -113,17 +113,6 @@ "permlevel": 0, "read_only": 0 }, - { - "default": "2099-12-31", - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "end_of_life", - "fieldtype": "Date", - "label": "End of Life", - "oldfieldname": "end_of_life", - "oldfieldtype": "Date", - "permlevel": 0, - "read_only": 0 - }, { "fieldname": "column_break0", "fieldtype": "Column Break", @@ -166,11 +155,233 @@ "reqd": 1, "search_index": 0 }, + { + "fieldname": "inventory", + "fieldtype": "Section Break", + "label": "Inventory", + "oldfieldtype": "Section Break", + "options": "icon-truck", + "permlevel": 0, + "read_only": 0 + }, + { + "default": "1", + "description": "", + "fieldname": "is_stock_item", + "fieldtype": "Check", + "label": "Maintain Stock", + "oldfieldname": "is_stock_item", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "default": "", + "depends_on": "eval:doc.is_stock_item", + "fieldname": "has_batch_no", + "fieldtype": "Check", + "label": "Has Batch No", + "oldfieldname": "has_batch_no", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "default": "", + "depends_on": "eval:doc.is_stock_item", + "description": "Selecting \"Yes\" will give a unique identity to each entity of this item which can be viewed in the Serial No master.", + "fieldname": "has_serial_no", + "fieldtype": "Check", + "in_filter": 1, + "label": "Has Serial No", + "oldfieldname": "has_serial_no", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "depends_on": "has_serial_no", + "description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.", + "fieldname": "serial_no_series", + "fieldtype": "Data", + "label": "Serial Number Series", + "no_copy": 0, + "permlevel": 0 + }, + { + "default": "", + "depends_on": "eval:doc.is_stock_item", + "description": "", + "fieldname": "is_asset_item", + "fieldtype": "Check", + "label": "Is Fixed Asset Item", + "oldfieldname": "is_asset_item", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "depends_on": "is_stock_item", + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "permlevel": 0, + "read_only": 0, + "width": "50%" + }, + { + "depends_on": "", + "description": "", + "fieldname": "default_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Warehouse", + "oldfieldname": "default_warehouse", + "oldfieldtype": "Link", + "options": "Warehouse", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "is_stock_item", + "description": "", + "fieldname": "tolerance", + "fieldtype": "Float", + "label": "Allow over delivery or receipt upto this percent", + "oldfieldname": "tolerance", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "is_stock_item", + "fieldname": "valuation_method", + "fieldtype": "Select", + "label": "Valuation Method", + "options": "\nFIFO\nMoving Average", + "permlevel": 0, + "read_only": 0 + }, + { + "default": "0.00", + "depends_on": "is_stock_item", + "description": "", + "fieldname": "min_order_qty", + "fieldtype": "Float", + "hidden": 0, + "label": "Minimum Order Qty", + "oldfieldname": "min_order_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:doc.is_stock_item", + "fieldname": "warranty_period", + "fieldtype": "Data", + "label": "Warranty Period (in days)", + "oldfieldname": "warranty_period", + "oldfieldtype": "Data", + "permlevel": 0, + "read_only": 0 + }, + { + "default": "2099-12-31", + "depends_on": "is_stock_item", + "fieldname": "end_of_life", + "fieldtype": "Date", + "label": "End of Life", + "oldfieldname": "end_of_life", + "oldfieldtype": "Date", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "is_stock_item", + "description": "", + "fieldname": "net_weight", + "fieldtype": "Float", + "label": "Net Weight", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:doc.is_stock_item", + "fieldname": "weight_uom", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Weight UOM", + "options": "UOM", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:doc.is_stock_item", + "description": "", + "fieldname": "reorder_section", + "fieldtype": "Section Break", + "label": "Auto re-order", + "options": "icon-rss", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)", + "description": "Automatically create Material Request if quantity falls below this level", + "fieldname": "re_order_level", + "fieldtype": "Float", + "label": "Re-order Level", + "oldfieldname": "re_order_level", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)", + "fieldname": "re_order_qty", + "fieldtype": "Float", + "label": "Re-order Qty", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "is_stock_item", + "fieldname": "apply_warehouse_wise_reorder_level", + "fieldtype": "Check", + "label": "Apply Warehouse-wise Reorder Level", + "permlevel": 0, + "precision": "" + }, + { + "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)", + "fieldname": "section_break_31", + "fieldtype": "Section Break", + "permlevel": 0, + "read_only": 0 + }, + { + "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)", + "description": "Will also apply for variants unless overrridden", + "fieldname": "reorder_levels", + "fieldtype": "Table", + "label": "Warehouse-wise Reorder Levels", + "options": "Item Reorder", + "permlevel": 0, + "read_only": 0 + }, { "depends_on": "eval:!doc.variant_of", "fieldname": "variants_section", "fieldtype": "Section Break", - "label": "Variant", + "label": "Variants", "permlevel": 0, "precision": "" }, @@ -219,253 +430,34 @@ "precision": "", "read_only": 1 }, - { - "fieldname": "inventory", - "fieldtype": "Section Break", - "label": "Inventory", - "oldfieldtype": "Section Break", - "options": "icon-truck", - "permlevel": 0, - "read_only": 0 - }, - { - "default": "Yes", - "description": "Select \"Yes\" if you are maintaining stock of this item in your Inventory.", - "fieldname": "is_stock_item", - "fieldtype": "Select", - "label": "Is Stock Item", - "oldfieldname": "is_stock_item", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, - { - "depends_on": "", - "description": "Mandatory if Stock Item is \"Yes\". Also the default warehouse where reserved quantity is set from Sales Order.", - "fieldname": "default_warehouse", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Warehouse", - "oldfieldname": "default_warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Percentage variation in quantity to be allowed while receiving or delivering this item.", - "fieldname": "tolerance", - "fieldtype": "Float", - "label": "Allowance Percent", - "oldfieldname": "tolerance", - "oldfieldtype": "Currency", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "valuation_method", - "fieldtype": "Select", - "label": "Valuation Method", - "options": "\nFIFO\nMoving Average", - "permlevel": 0, - "read_only": 0 - }, - { - "default": "0.00", - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "You can enter the minimum quantity of this item to be ordered.", - "fieldname": "min_order_qty", - "fieldtype": "Float", - "hidden": 0, - "label": "Minimum Order Qty", - "oldfieldname": "min_order_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "permlevel": 0, - "read_only": 0, - "width": "50%" - }, - { - "default": "No", - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Select \"Yes\" if this item is used for some internal purpose in your company.", - "fieldname": "is_asset_item", - "fieldtype": "Select", - "label": "Is Fixed Asset Item", - "oldfieldname": "is_asset_item", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, - { - "default": "No", - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "has_batch_no", - "fieldtype": "Select", - "label": "Has Batch No", - "oldfieldname": "has_batch_no", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, - { - "default": "No", - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Selecting \"Yes\" will give a unique identity to each entity of this item which can be viewed in the Serial No master.", - "fieldname": "has_serial_no", - "fieldtype": "Select", - "in_filter": 1, - "label": "Has Serial No", - "oldfieldname": "has_serial_no", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, - { - "depends_on": "eval: doc.has_serial_no===\"Yes\"", - "description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.", - "fieldname": "serial_no_series", - "fieldtype": "Data", - "label": "Serial Number Series", - "no_copy": 0, - "permlevel": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "warranty_period", - "fieldtype": "Data", - "label": "Warranty Period (in days)", - "oldfieldname": "warranty_period", - "oldfieldtype": "Data", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Net Weight of each Item", - "fieldname": "net_weight", - "fieldtype": "Float", - "label": "Net Weight", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "weight_uom", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Weight UOM", - "options": "UOM", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "description": "Auto-raise Material Request if quantity goes below re-order level in default warehouse", - "fieldname": "reorder_section", - "fieldtype": "Section Break", - "label": "Re-order", - "options": "icon-rss", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:(doc.is_stock_item==\"Yes\" && !doc.apply_warehouse_wise_reorder_level)", - "fieldname": "re_order_level", - "fieldtype": "Float", - "label": "Re-Order Level", - "oldfieldname": "re_order_level", - "oldfieldtype": "Currency", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:(doc.is_stock_item==\"Yes\" && !doc.apply_warehouse_wise_reorder_level)", - "fieldname": "re_order_qty", - "fieldtype": "Float", - "label": "Re-Order Qty", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_stock_item==\"Yes\"", - "fieldname": "apply_warehouse_wise_reorder_level", - "fieldtype": "Check", - "label": "Apply Warehouse-wise Reorder Level", - "permlevel": 0, - "precision": "" - }, - { - "depends_on": "eval:(doc.is_stock_item==\"Yes\" && doc.apply_warehouse_wise_reorder_level)", - "fieldname": "section_break_31", - "fieldtype": "Section Break", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:(doc.is_stock_item==\"Yes\" && doc.apply_warehouse_wise_reorder_level)", - "description": "Will also apply for variants unless overrridden", - "fieldname": "reorder_levels", - "fieldtype": "Table", - "label": "Warehouse-wise Reorder Levels", - "options": "Item Reorder", - "permlevel": 0, - "read_only": 0 - }, { "fieldname": "purchase_details", "fieldtype": "Section Break", - "label": "", + "label": "Purchase Details", "oldfieldtype": "Section Break", "options": "icon-shopping-cart", "permlevel": 0, "read_only": 0 }, { - "default": "Yes", - "description": "Selecting \"Yes\" will allow this item to appear in Purchase Order , Purchase Receipt.", + "default": "1", + "description": "", "fieldname": "is_purchase_item", - "fieldtype": "Select", + "fieldtype": "Check", "label": "Is Purchase Item", "oldfieldname": "is_purchase_item", "oldfieldtype": "Select", - "options": "Yes\nNo", + "options": "", "permlevel": 0, "read_only": 0, "reqd": 1 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", - "fieldname": "default_supplier", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Supplier", - "options": "Supplier", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", - "description": "Lead Time days is number of days by which this item is expected in your warehouse. This days is fetched in Material Request when you select this item.", + "depends_on": "is_purchase_item", + "description": "Average time taken by the supplier to deliver", "fieldname": "lead_time_days", "fieldtype": "Int", - "label": "Lead Time Days", + "label": "Lead Time in days", "no_copy": 0, "oldfieldname": "lead_time_days", "oldfieldtype": "Int", @@ -473,20 +465,7 @@ "read_only": 0 }, { - "depends_on": "", - "description": "Default Purchase Account in which cost of the item will be debited.", - "fieldname": "expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Expense Account", - "oldfieldname": "purchase_account", - "oldfieldtype": "Link", - "options": "Account", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "", + "depends_on": "is_purchase_item", "description": "", "fieldname": "buying_cost_center", "fieldtype": "Link", @@ -499,27 +478,28 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", - "fieldname": "last_purchase_rate", - "fieldtype": "Float", - "label": "Last Purchase Rate", - "no_copy": 1, - "oldfieldname": "last_purchase_rate", - "oldfieldtype": "Currency", + "depends_on": "is_purchase_item", + "description": "", + "fieldname": "expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Expense Account", + "oldfieldname": "purchase_account", + "oldfieldtype": "Link", + "options": "Account", "permlevel": 0, - "read_only": 1 + "read_only": 0 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", - "fieldname": "column_break2", + "depends_on": "is_purchase_item", + "fieldname": "unit_of_measure_conversion", "fieldtype": "Column Break", - "oldfieldtype": "Column Break", + "label": "Unit of Measure Conversion", "permlevel": 0, - "read_only": 0, - "width": "50%" + "precision": "" }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", + "depends_on": "is_purchase_item", "description": "Will also apply for variants", "fieldname": "uoms", "fieldtype": "Table", @@ -532,7 +512,35 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", + "depends_on": "is_purchase_item", + "fieldname": "last_purchase_rate", + "fieldtype": "Float", + "label": "Last Purchase Rate", + "no_copy": 1, + "oldfieldname": "last_purchase_rate", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 1 + }, + { + "depends_on": "is_purchase_item", + "fieldname": "supplier_details", + "fieldtype": "Section Break", + "label": "Supplier Details", + "permlevel": 0, + "precision": "" + }, + { + "depends_on": "eval:doc.is_purchase_item", + "fieldname": "default_supplier", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Supplier", + "options": "Supplier", + "permlevel": 0 + }, + { + "depends_on": "eval:doc.is_purchase_item", "fieldname": "manufacturer", "fieldtype": "Data", "label": "Manufacturer", @@ -540,7 +548,7 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", + "depends_on": "eval:doc.is_purchase_item", "fieldname": "manufacturer_part_no", "fieldtype": "Data", "label": "Manufacturer Part Number", @@ -548,7 +556,17 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_purchase_item==\"Yes\"", + "depends_on": "is_purchase_item", + "fieldname": "column_break2", + "fieldtype": "Column Break", + "label": "Item Code for Suppliers", + "oldfieldtype": "Column Break", + "permlevel": 0, + "read_only": 0, + "width": "50%" + }, + { + "depends_on": "is_purchase_item", "fieldname": "supplier_items", "fieldtype": "Table", "label": "Supplier Items", @@ -566,30 +584,30 @@ "read_only": 0 }, { - "default": "Yes", - "description": "Selecting \"Yes\" will allow this item to figure in Sales Order, Delivery Note", + "default": "1", + "description": "", "fieldname": "is_sales_item", - "fieldtype": "Select", + "fieldtype": "Check", "in_filter": 1, "label": "Is Sales Item", "oldfieldname": "is_sales_item", "oldfieldtype": "Select", - "options": "Yes\nNo", + "options": "", "permlevel": 0, "read_only": 0, "reqd": 1 }, { - "default": "No", - "depends_on": "eval:doc.is_sales_item==\"Yes\"", - "description": "Select \"Yes\" if this item represents some work like training, designing, consulting etc.", + "default": "", + "depends_on": "eval:doc.is_sales_item", + "description": "Allow in Sales Order of type \"Service\"", "fieldname": "is_service_item", - "fieldtype": "Select", + "fieldtype": "Check", "in_filter": 1, "label": "Is Service Item", "oldfieldname": "is_service_item", "oldfieldtype": "Select", - "options": "Yes\nNo", + "options": "", "permlevel": 0, "read_only": 0, "reqd": 1 @@ -614,17 +632,7 @@ "precision": "" }, { - "depends_on": "eval:doc.is_sales_item==\"Yes\"", - "fieldname": "max_discount", - "fieldtype": "Float", - "label": "Max Discount (%)", - "oldfieldname": "max_discount", - "oldfieldtype": "Currency", - "permlevel": 0, - "read_only": 0 - }, - { - "depends_on": "eval:doc.is_sales_item==\"Yes\"", + "depends_on": "is_sales_item", "fieldname": "income_account", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -634,7 +642,7 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_sales_item==\"Yes\"", + "depends_on": "is_sales_item", "fieldname": "selling_cost_center", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -644,17 +652,18 @@ "read_only": 0 }, { - "depends_on": "eval:doc.is_sales_item==\"Yes\"", + "depends_on": "is_sales_item", "fieldname": "column_break3", "fieldtype": "Column Break", + "label": "Customer Item Codes", "oldfieldtype": "Column Break", "permlevel": 0, "read_only": 0, "width": "50%" }, { - "depends_on": "eval:doc.is_sales_item==\"Yes\"", - "description": "For the convenience of customers, these codes can be used in print formats like Invoices and Delivery Notes", + "depends_on": "is_sales_item", + "description": "", "fieldname": "customer_items", "fieldtype": "Table", "label": "Customer Items", @@ -662,6 +671,16 @@ "permlevel": 0, "read_only": 0 }, + { + "depends_on": "eval:doc.is_sales_item", + "fieldname": "max_discount", + "fieldtype": "Float", + "label": "Max Discount (%)", + "oldfieldname": "max_discount", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 0 + }, { "fieldname": "item_tax_section_break", "fieldtype": "Section Break", @@ -692,20 +711,20 @@ "read_only": 0 }, { - "default": "No", + "default": "", "fieldname": "inspection_required", - "fieldtype": "Select", + "fieldtype": "Check", "label": "Inspection Required", "no_copy": 0, "oldfieldname": "inspection_required", "oldfieldtype": "Select", - "options": "\nYes\nNo", + "options": "", "permlevel": 0, "read_only": 0, "reqd": 1 }, { - "depends_on": "eval:doc.inspection_required==\"Yes\"", + "depends_on": "inspection_required", "description": "Will also apply to variants", "fieldname": "quality_parameters", "fieldtype": "Table", @@ -725,6 +744,39 @@ "permlevel": 0, "read_only": 0 }, + { + "default": "", + "depends_on": "", + "description": "", + "fieldname": "is_pro_applicable", + "fieldtype": "Check", + "label": "Allow Production Order", + "oldfieldname": "is_pro_applicable", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "default": "", + "description": "If subcontracted to a vendor", + "fieldname": "is_sub_contracted_item", + "fieldtype": "Check", + "label": "Supply Raw Materials for Purchase", + "oldfieldname": "is_sub_contracted_item", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "read_only": 0, + "reqd": 1 + }, + { + "fieldname": "column_break_74", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, { "depends_on": "", "fieldname": "default_bom", @@ -738,33 +790,6 @@ "permlevel": 0, "read_only": 1 }, - { - "default": "No", - "depends_on": "", - "description": "Selecting \"Yes\" will allow you to make a Production Order for this item.", - "fieldname": "is_pro_applicable", - "fieldtype": "Select", - "label": "Allow Production Order", - "oldfieldname": "is_pro_applicable", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, - { - "default": "No", - "description": "", - "fieldname": "is_sub_contracted_item", - "fieldtype": "Select", - "label": "Supply Raw Materials for Purchase", - "oldfieldname": "is_sub_contracted_item", - "oldfieldtype": "Select", - "options": "Yes\nNo", - "permlevel": 0, - "read_only": 0, - "reqd": 1 - }, { "fieldname": "customer_code", "fieldtype": "Data", @@ -803,7 +828,7 @@ }, { "depends_on": "show_in_website", - "description": "Products will be sorted by weight-age in default searches. More the weight-age, higher the product will appear in the list.", + "description": "Items with higher weightage will be shown higher", "fieldname": "weightage", "fieldtype": "Int", "label": "Weightage", @@ -865,6 +890,13 @@ "permlevel": 0, "read_only": 0 }, + { + "fieldname": "website_specifications_cb", + "fieldtype": "Column Break", + "label": "Website Specifications", + "permlevel": 0, + "precision": "" + }, { "depends_on": "show_in_website", "fieldname": "copy_from_item_group", @@ -903,7 +935,7 @@ "icon": "icon-tag", "idx": 1, "max_attachments": 1, - "modified": "2015-07-13 05:28:28.698107", + "modified": "2015-07-24 06:04:28.448050", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d3d8e9c6a1..608d01a67a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -87,7 +87,7 @@ class Item(WebsiteGenerator): return context def check_warehouse_is_set_for_stock_item(self): - if self.is_stock_item=="Yes" and not self.default_warehouse and frappe.get_all("Warehouse"): + if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"): frappe.msgprint(_("Default Warehouse is mandatory for stock Item."), raise_exception=WarehouseNotSet) @@ -158,13 +158,13 @@ class Item(WebsiteGenerator): frappe.throw(_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx)) def validate_item_type(self): - if self.is_pro_applicable == 'Yes' and self.is_stock_item == 'No': + if self.is_pro_applicable == 1 and self.is_stock_item==0: frappe.throw(_("As Production Order can be made for this item, it must be a stock item.")) - if self.has_serial_no == 'Yes' and self.is_stock_item == 'No': + if self.has_serial_no == 1 and self.is_stock_item == 0: msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1) - if self.has_serial_no == "No" and self.serial_no_series: + if self.has_serial_no == 0 and self.serial_no_series: self.serial_no_series = None @@ -208,7 +208,7 @@ class Item(WebsiteGenerator): vals = frappe.db.get_value("Item", self.name, ["has_serial_no", "is_stock_item", "valuation_method", "has_batch_no"], as_dict=True) - if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or + if vals and ((self.is_stock_item == 0 and vals.is_stock_item == 1) or vals.has_serial_no != self.has_serial_no or vals.has_batch_no != self.has_batch_no or cstr(vals.valuation_method) != cstr(self.valuation_method)): @@ -313,11 +313,11 @@ class Item(WebsiteGenerator): def update_item_desc(self): if frappe.db.get_value('BOM',self.name, 'description') != self.description: frappe.db.sql("""update `tabBOM` set description = %s where item = %s and docstatus < 2""",(self.description, self.name)) - frappe.db.sql("""update `tabBOM Item` set description = %s where + frappe.db.sql("""update `tabBOM Item` set description = %s where item_code = %s and docstatus < 2""",(self.description, self.name)) - frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where + frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where item_code = %s and docstatus < 2""",(self.description, self.name)) - + def update_variants(self): if self.has_variants: updated = [] @@ -327,7 +327,7 @@ class Item(WebsiteGenerator): updated.append(d.item_code) if updated: frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated))) - + def validate_has_variants(self): if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"): if frappe.db.exists("Item", {"variant_of": self.name}): @@ -336,11 +336,11 @@ class Item(WebsiteGenerator): def validate_stock_for_template_must_be_zero(self): if self.has_variants: stock_in = frappe.db.sql_list("""select warehouse from tabBin - where item_code=%s and (ifnull(actual_qty, 0) > 0 or ifnull(ordered_qty, 0) > 0 + where item_code=%s and (ifnull(actual_qty, 0) > 0 or ifnull(ordered_qty, 0) > 0 or ifnull(reserved_qty, 0) > 0 or ifnull(indented_qty, 0) > 0 or ifnull(planned_qty, 0) > 0)""", self.name) if stock_in: frappe.throw(_("Item Template cannot have stock or Open Sales/Purchase/Production Orders."), ItemTemplateCannotHaveStock) - + def validate_end_of_life(item_code, end_of_life=None, verbose=1): if not end_of_life: end_of_life = frappe.db.get_value("Item", item_code, "end_of_life") @@ -353,7 +353,7 @@ def validate_is_stock_item(item_code, is_stock_item=None, verbose=1): if not is_stock_item: is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item") - if is_stock_item != "Yes": + if is_stock_item != 1: msg = _("Item {0} is not a stock Item").format(item_code) _msgprint(msg, verbose) @@ -445,4 +445,3 @@ def invalidate_cache_for_item(doc): if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group: invalidate_cache_for(doc, doc.old_item_group) - diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 9cf3c077b4..f299c4a75c 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -21,16 +21,16 @@ class TestItem(unittest.TestCase): else: item = frappe.get_doc("Item", item_code) return item - + def test_template_cannot_have_stock(self): item = self.get_item(10) - se = make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, incoming_rate=1) + make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, incoming_rate=1) item.has_variants = 1 self.assertRaises(ItemTemplateCannotHaveStock, item.save) - + def test_default_warehouse(self): item = frappe.copy_doc(test_records[0]) - item.is_stock_item = "Yes" + item.is_stock_item = 1 item.default_warehouse = None self.assertRaises(WarehouseNotSet, item.insert) diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index 86d581d899..5dd63e38de 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -5,17 +5,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Item", "item_group": "_Test Item Group", "item_name": "_Test Item", @@ -38,17 +38,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Item 2", "item_group": "_Test Item Group", "item_name": "_Test Item 2", @@ -62,17 +62,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Item Home Desktop 100", "item_group": "_Test Item Group Desktops", "item_name": "_Test Item Home Desktop 100", @@ -92,17 +92,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Item Home Desktop 200", "item_group": "_Test Item Group Desktops", "item_name": "_Test Item Home Desktop 200", @@ -113,17 +113,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "No", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 0, + "is_sub_contracted_item": 0, "item_code": "_Test Product Bundle Item", "item_group": "_Test Item Group Desktops", "item_name": "_Test Product Bundle Item", @@ -135,17 +135,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "Yes", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "Yes", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 1, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 1, "item_code": "_Test FG Item", "item_group": "_Test Item Group Desktops", "item_name": "_Test FG Item", @@ -154,16 +154,16 @@ { "description": "_Test Non Stock Item 7", "doctype": "Item", - "has_batch_no": "No", - "has_serial_no": "No", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "No", - "is_sub_contracted_item": "No", + "has_batch_no": 0, + "has_serial_no": 0, + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 0, + "is_sub_contracted_item": 0, "item_code": "_Test Non Stock Item", "item_group": "_Test Item Group Desktops", "item_name": "_Test Non Stock Item", @@ -173,16 +173,16 @@ "default_warehouse": "_Test Warehouse - _TC", "description": "_Test Serialized Item 8", "doctype": "Item", - "has_batch_no": "No", - "has_serial_no": "Yes", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "has_batch_no": 0, + "has_serial_no": 1, + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Serialized Item", "item_group": "_Test Item Group Desktops", "item_name": "_Test Serialized Item", @@ -192,16 +192,16 @@ "default_warehouse": "_Test Warehouse - _TC", "description": "_Test Serialized Item 9", "doctype": "Item", - "has_batch_no": "No", - "has_serial_no": "Yes", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "No", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "has_batch_no": 0, + "has_serial_no": 1, + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 0, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Serialized Item With Series", "item_group": "_Test Item Group Desktops", "item_name": "_Test Serialized Item With Series", @@ -214,16 +214,16 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "No", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 0, "item_code": "_Test Item Home Desktop Manufactured", "item_group": "_Test Item Group Desktops", "item_name": "_Test Item Home Desktop Manufactured", @@ -235,17 +235,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "Yes", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "Yes", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 1, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 1, "item_code": "_Test FG Item 2", "item_group": "_Test Item Group Desktops", "item_name": "_Test FG Item 2", @@ -257,17 +257,17 @@ "doctype": "Item", "expense_account": "_Test Account Cost for Goods Sold - _TC", "cost_center": "_Test Cost Center - _TC", - "has_batch_no": "No", - "has_serial_no": "No", + "has_batch_no": 0, + "has_serial_no": 0, "income_account": "Sales - _TC", - "inspection_required": "No", - "is_asset_item": "No", - "is_pro_applicable": "Yes", - "is_purchase_item": "Yes", - "is_sales_item": "Yes", - "is_service_item": "No", - "is_stock_item": "Yes", - "is_sub_contracted_item": "Yes", + "inspection_required": 0, + "is_asset_item": 0, + "is_pro_applicable": 1, + "is_purchase_item": 1, + "is_sales_item": 1, + "is_service_item": 0, + "is_stock_item": 1, + "is_sub_contracted_item": 1, "item_code": "_Test Variant Item", "item_group": "_Test Item Group Desktops", "item_name": "_Test Variant Item", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 221e4ebe81..3418c9ef1d 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -15,7 +15,7 @@ class LandedCostVoucher(Document): pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description, pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name from `tabPurchase Receipt Item` pr_item where parent = %s - and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", + and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 1)""", pr.purchase_receipt, as_dict=True) for d in pr_items: @@ -69,7 +69,7 @@ class LandedCostVoucher(Document): def set_applicable_charges_for_item(self): based_on = self.distribute_charges_based_on.lower() total = sum([flt(d.get(based_on)) for d in self.get("items")]) - + if not total: frappe.throw(_("Total {0} for all items is zero, may you should change 'Distribute Charges Based On'").format(based_on)) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index c1be1fd9bf..04330368c6 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -152,7 +152,7 @@ class MaterialRequest(BuyingController): item_wh_list = [] for d in self.get("items"): if (not mr_item_rows or d.name in mr_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \ - and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse: + and frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse: item_wh_list.append([d.item_code, d.warehouse]) for item_code, warehouse in item_wh_list: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 034eb07830..53233ce777 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -198,10 +198,7 @@ class PurchaseReceipt(BuyingController): def validate_inspection(self): for d in self.get('items'): #Enter inspection date for all items that require inspection - ins_reqd = frappe.db.sql("select inspection_required from `tabItem` where name = %s", - (d.item_code,), as_dict = 1) - ins_reqd = ins_reqd and ins_reqd[0]['inspection_required'] or 'No' - if ins_reqd == 'Yes' and not d.qa_no: + if frappe.db.get_value("Item", d.item_code, "inspection_required") and not d.qa_no: frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code)) if self.docstatus==1: raise frappe.ValidationError diff --git a/erpnext/stock/doctype/serial_no/serial_no.js b/erpnext/stock/doctype/serial_no/serial_no.js index aa4e73b3b1..eea652b871 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.js +++ b/erpnext/stock/doctype/serial_no/serial_no.js @@ -11,7 +11,7 @@ cur_frm.add_fetch("item_code", "brand", "brand") cur_frm.cscript.onload = function() { cur_frm.set_query("item_code", function() { - return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"}) + return erpnext.queries.item({"is_stock_item": 1, "has_serial_no": 1}) }); }; diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6b5054b902..fb15195291 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -66,7 +66,7 @@ class SerialNo(StockController): Validate whether serial no is required for this item """ item = frappe.get_doc("Item", self.item_code) - if item.has_serial_no!="Yes": + if item.has_serial_no!=1: frappe.throw(_("Item {0} is not setup for Serial Nos. Check Item master").format(self.item_code)) self.item_group = item.item_group @@ -198,7 +198,7 @@ def process_serial_no(sle): update_serial_nos(sle, item_det) def validate_serial_no(sle, item_det): - if item_det.has_serial_no=="No": + if item_det.has_serial_no==0: if sle.serial_no: frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code), SerialNoNotRequiredError) @@ -246,7 +246,7 @@ def validate_serial_no(sle, item_det): def update_serial_nos(sle, item_det): if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 \ - and item_det.has_serial_no == "Yes" and item_det.serial_no_series: + and item_det.has_serial_no == 1 and item_det.serial_no_series: from frappe.model.naming import make_autoname serial_nos = [] for i in xrange(cint(sle.actual_qty)): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 8526117223..5da6e538ce 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -7,7 +7,7 @@ frappe.provide("erpnext.stock"); erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ setup: function() { var me = this; - + this.frm.fields_dict.bom_no.get_query = function() { return { filters:{ 'docstatus': 1 } @@ -15,7 +15,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ }; this.frm.fields_dict.items.grid.get_field('item_code').get_query = function() { - return erpnext.queries.item({is_stock_item: "Yes"}); + return erpnext.queries.item({is_stock_item: 1}); }; this.frm.set_query("purchase_order", function() { @@ -252,13 +252,13 @@ cur_frm.fields_dict['items'].grid.get_field('batch_no').get_query = function(doc var filters = { 'item_code': item.item_code, 'posting_date': me.frm.doc.posting_date || nowdate() - } + } } else { var filters = { 'item_code': item.item_code } } - + if(item.s_warehouse) filters["warehouse"] = item.s_warehouse return { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 1b01f3aa68..4f1e1a8189 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -354,12 +354,12 @@ class StockEntry(StockController): if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty): frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ format(d.idx, d.transfer_qty, self.fg_completed_qty)) - + if self.production_order and self.purpose == "Manufacture" and d.t_warehouse: items_with_target_warehouse.append(d.item_code) - + if self.production_order and self.purpose == "Manufacture": - production_item = frappe.db.get_value("Production Order", + production_item = frappe.db.get_value("Production Order", self.production_order, "production_item") if production_item not in items_with_target_warehouse: frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry") @@ -427,7 +427,7 @@ class StockEntry(StockController): (args.get('item_code')), as_dict = 1) if not item: frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get("item_code"))) - + item = item[0] ret = { @@ -642,7 +642,7 @@ class StockEntry(StockController): mreq_item.warehouse != (item.s_warehouse if self.purpose== "Material Issue" else item.t_warehouse): frappe.throw(_("Item or Warehouse for row {0} does not match Material Request").format(item.idx), frappe.MappingMismatchError) - + def validate_batch(self): if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Subcontract"]: for item in self.get("items"): diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 010f5ead2a..249815f30a 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -64,18 +64,18 @@ class StockLedgerEntry(Document): item_det = item_det[0] - if item_det.is_stock_item != 'Yes': + if item_det.is_stock_item != 1: frappe.throw(_("Item {0} must be a stock Item").format(self.item_code)) # check if batch number is required if self.voucher_type != 'Stock Reconciliation': - if item_det.has_batch_no =='Yes': + if item_det.has_batch_no ==1: if not self.batch_no: frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code)) - elif item_det.has_batch_no =='No' and self.batch_no: + elif item_det.has_batch_no ==0 and self.batch_no: frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) if item_det.has_variants: diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index efa6a8a25d..5d5b1f0228 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -134,12 +134,12 @@ class StockReconciliation(StockController): validate_is_stock_item(item_code, item.is_stock_item, verbose=0) # item should not be serialized - if item.has_serial_no == "Yes": + if item.has_serial_no == 1: raise frappe.ValidationError, _("Serialized Item {0} cannot be updated \ using Stock Reconciliation").format(item_code) # item managed batch-wise not allowed - if item.has_batch_no == "Yes": + if item.has_batch_no == 1: raise frappe.ValidationError, _("Item: {0} managed batch-wise, can not be reconciled using \ Stock Reconciliation, instead use Stock Entry").format(item_code) @@ -242,7 +242,7 @@ class StockReconciliation(StockController): @frappe.whitelist() def get_items(warehouse, posting_date, posting_time): items = frappe.get_list("Item", fields=["name"], filters= - {"is_stock_item": "Yes", "has_serial_no": "No", "has_batch_no": "No"}) + {"is_stock_item": 1, "has_serial_no": 0, "has_batch_no": 0}) for item in items: item.item_code = item.name item.warehouse = warehouse diff --git a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.js b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.js index 20daf91744..eb45ade04c 100644 --- a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.js +++ b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.js @@ -4,7 +4,7 @@ $.extend(cur_frm.cscript, { onload: function() { cur_frm.set_query("item_code", function() { - return erpnext.queries.item({"is_stock_item": "Yes"}); + return erpnext.queries.item({"is_stock_item": 1}); }); }, diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 2024ebe2d4..5a3976399f 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -146,7 +146,7 @@ class Warehouse(Document): frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) for item in frappe.db.sql("""select distinct item_code from ( - select name as item_code from `tabItem` where ifnull(is_stock_item, 'Yes')='Yes' + select name as item_code from `tabItem` where is_stock_item=1 union select distinct item_code from tabBin) a"""): repost_stock(item[0], newdn) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 399946b812..82446ed420 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -37,7 +37,6 @@ def get_item_details(args): item_doc = frappe.get_doc("Item", args.item_code) item = item_doc - validate_item_details(args, item) out = get_basic_details(args, item) @@ -61,7 +60,7 @@ def get_item_details(args): out.update(get_pricing_rule_for_item(args)) if args.get("parenttype") in ("Sales Invoice", "Delivery Note"): - if item_doc.has_serial_no == "Yes" and not args.serial_no: + if item_doc.has_serial_no == 1 and not args.serial_no: out.serial_no = get_serial_nos_by_fifo(args, item_doc) if args.transaction_date and item.lead_time_days: @@ -119,10 +118,10 @@ def validate_item_details(args, item): if args.transaction_type == "selling": # validate if sales item or service item if args.get("order_type") == "Maintenance": - if item.is_service_item != "Yes": + if item.is_service_item != 1: throw(_("Item {0} must be a Service Item.").format(item.name)) - elif item.is_sales_item != "Yes": + elif item.is_sales_item != 1: throw(_("Item {0} must be a Sales Item").format(item.name)) if cint(item.has_variants): @@ -130,10 +129,10 @@ def validate_item_details(args, item): elif args.transaction_type == "buying" and args.parenttype != "Material Request": # validate if purchase item or subcontracted item - if item.is_purchase_item != "Yes": + if item.is_purchase_item != 1: throw(_("Item {0} must be a Purchase Item").format(item.name)) - if args.get("is_subcontracted") == "Yes" and item.is_sub_contracted_item != "Yes": + if args.get("is_subcontracted") == "Yes" and item.is_sub_contracted_item != 1: throw(_("Item {0} must be a Sub-contracted Item").format(item.name)) def get_basic_details(args, item): @@ -338,7 +337,7 @@ def get_batch_qty(batch_no,warehouse,item_code): actual_batch_qty = get_actual_batch_qty(batch_no,warehouse,item_code) if batch_no: return {'actual_batch_qty': actual_batch_qty} - + @frappe.whitelist() def apply_price_list(args): """ diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index f739f94556..51dd8c156c 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -69,7 +69,7 @@ def get_item_warehouse_projected_qty(): from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != '' and exists (select name from `tabItem` where `tabItem`.name = `tabBin`.item_code and - is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and + is_stock_item=1 and (is_purchase_item=1 or is_sub_contracted_item=1) and (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > %s)) and exists (select name from `tabWarehouse` where `tabWarehouse`.name = `tabBin`.warehouse diff --git a/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json b/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json index 463d2e2fb8..340577beef 100644 --- a/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json +++ b/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json @@ -1,17 +1,17 @@ { - "apply_user_permissions": 1, - "creation": "2013-08-20 15:08:10", - "docstatus": 0, - "doctype": "Report", - "idx": 1, - "is_standard": "Yes", - "modified": "2014-06-03 07:18:17.128918", - "modified_by": "Administrator", - "module": "Stock", - "name": "Items To Be Requested", - "owner": "Administrator", - "query": "SELECT\n tabBin.item_code as \"Item:Link/Item:120\",\n tabBin.warehouse as \"Warehouse:Link/Warehouse:120\",\n tabBin.actual_qty as \"Actual:Float:90\",\n tabBin.indented_qty as \"Requested:Float:90\",\n tabBin.reserved_qty as \"Reserved:Float:90\",\n tabBin.ordered_qty as \"Ordered:Float:90\",\n tabBin.projected_qty as \"Projected:Float:90\"\nFROM\n tabBin, tabItem\nWHERE\n tabBin.item_code = tabItem.name\n AND tabItem.is_purchase_item = \"Yes\"\n AND tabBin.projected_qty < 0\nORDER BY\n tabBin.projected_qty ASC", - "ref_doctype": "Item", - "report_name": "Items To Be Requested", + "apply_user_permissions": 1, + "creation": "2013-08-20 15:08:10", + "docstatus": 0, + "doctype": "Report", + "idx": 1, + "is_standard": "Yes", + "modified": "2014-06-03 07:18:17.128918", + "modified_by": "Administrator", + "module": "Stock", + "name": "Items To Be Requested", + "owner": "Administrator", + "query": "SELECT\n tabBin.item_code as \"Item:Link/Item:120\",\n tabBin.warehouse as \"Warehouse:Link/Warehouse:120\",\n tabBin.actual_qty as \"Actual:Float:90\",\n tabBin.indented_qty as \"Requested:Float:90\",\n tabBin.reserved_qty as \"Reserved:Float:90\",\n tabBin.ordered_qty as \"Ordered:Float:90\",\n tabBin.projected_qty as \"Projected:Float:90\"\nFROM\n tabBin, tabItem\nWHERE\n tabBin.item_code = tabItem.name\n AND tabItem.is_purchase_item = 1\n AND tabBin.projected_qty < 0\nORDER BY\n tabBin.projected_qty ASC", + "ref_doctype": "Item", + "report_name": "Items To Be Requested", "report_type": "Query Report" -} \ No newline at end of file +} diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py index f7ea1156a7..78d1c44907 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py @@ -18,7 +18,7 @@ def execute(filters=None): total_qty = total_amount = 0.0 if consumed_details.get(item_code): for cd in consumed_details.get(item_code): - + if (cd.voucher_no not in material_transfer_vouchers): if cd.voucher_type=="Delivery Note": delivered_qty += abs(flt(cd.actual_qty)) @@ -40,7 +40,7 @@ def execute(filters=None): def get_columns(filters): """return columns based on filters""" - + columns = [_("Item") + ":Link/Item:100"] + [_("Item Name") + "::100"] + \ [_("Description") + "::150"] + [_("UOM") + ":Link/UOM:90"] + \ [_("Consumed Qty") + ":Float:110"] + [_("Consumed Amount") + ":Currency:130"] + \ @@ -64,10 +64,10 @@ def get_consumed_details(filters): conditions, values = get_conditions(filters) consumed_details = {} - for d in frappe.db.sql("""select sle.item_code, i.item_name, i.description, - i.stock_uom, sle.actual_qty, sle.stock_value_difference, + for d in frappe.db.sql("""select sle.item_code, i.item_name, i.description, + i.stock_uom, sle.actual_qty, sle.stock_value_difference, sle.voucher_no, sle.voucher_type - from `tabStock Ledger Entry` sle, `tabItem` i + from `tabStock Ledger Entry` sle, `tabItem` i where sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1): consumed_details.setdefault(d.item_code, []).append(d) @@ -77,20 +77,20 @@ def get_suppliers_details(filters): item_supplier_map = {} supplier = filters.get('supplier') - for d in frappe.db.sql("""select pr.supplier, pri.item_code from - `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri - where pr.name=pri.parent and pr.docstatus=1 and - pri.item_code=(select name from `tabItem` where - ifnull(is_stock_item, 'Yes')='Yes' and name=pri.item_code)""", as_dict=1): + for d in frappe.db.sql("""select pr.supplier, pri.item_code from + `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri + where pr.name=pri.parent and pr.docstatus=1 and + pri.item_code=(select name from `tabItem` where + is_stock_item=1 and name=pri.item_code)""", as_dict=1): item_supplier_map.setdefault(d.item_code, []).append(d.supplier) - + if supplier: for item_code, suppliers in item_supplier_map.items(): if supplier not in suppliers: del item_supplier_map[item_code] - + return item_supplier_map def get_material_transfer_vouchers(): - return frappe.db.sql_list("""select name from `tabStock Entry` where - purpose='Material Transfer' and docstatus=1""") \ No newline at end of file + return frappe.db.sql_list("""select name from `tabStock Entry` where + purpose='Material Transfer' and docstatus=1""") diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 1a9e8283d8..ee4303b641 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -81,7 +81,7 @@ def get_bin(item_code, warehouse): def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False): is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item') - if is_stock_item == 'Yes': + if is_stock_item: bin = get_bin(args.get("item_code"), args.get("warehouse")) bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher) return bin @@ -173,4 +173,3 @@ def validate_warehouse_company(warehouse, company): if warehouse_company and warehouse_company != company: frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company), InvalidWarehouseCompany) - diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.js index 431d8e4ba7..d84dc17f53 100644 --- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.js @@ -102,7 +102,7 @@ cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) { return { - filters:{ 'is_service_item': "Yes" } + filters:{ 'is_service_item': 1 } } } diff --git a/erpnext/support/doctype/maintenance_visit/maintenance_visit.js b/erpnext/support/doctype/maintenance_visit/maintenance_visit.js index 9313a917f6..c917918e1d 100644 --- a/erpnext/support/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/support/doctype/maintenance_visit/maintenance_visit.js @@ -77,7 +77,7 @@ cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['purposes'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) { return{ - filters:{ 'is_service_item': "Yes"} + filters:{ 'is_service_item': 1} } } diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 75f33077da..52b94aee79 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -145,7 +145,7 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin bin = frappe.db.sql("""select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom from `tabBin` bin, tabItem item - where bin.item_code = item.name and item.has_serial_no = 'Yes' %s""" % condition) + where bin.item_code = item.name and item.has_serial_no = 1 %s""" % condition) for d in bin: serial_nos = frappe.db.sql("""select count(name) from `tabSerial No` From c505cbc988101e6f18b3c1b29e5e0be4f6fb0270 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 12:34:18 +0530 Subject: [PATCH 22/43] sponsors page updated --- erpnext/change_log/current/spnsorship.md | 2 ++ sponsors.md | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 erpnext/change_log/current/spnsorship.md diff --git a/erpnext/change_log/current/spnsorship.md b/erpnext/change_log/current/spnsorship.md new file mode 100644 index 0000000000..62720acc54 --- /dev/null +++ b/erpnext/change_log/current/spnsorship.md @@ -0,0 +1,2 @@ +- Sales / Purchase Return Enahancement - **[Sponsored by Strella Consulting Sdn Bhd](http://www.strellagroup.com)** + * Now you can make Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity \ No newline at end of file diff --git a/sponsors.md b/sponsors.md index 7578229b3c..5f6c2f2c27 100644 --- a/sponsors.md +++ b/sponsors.md @@ -21,5 +21,13 @@ for Customer #3451 + + + Strella Consulting Sdn Bhd + + + For Sales / Purchase Return Enhancement #3582 + + From 2f3b097d6342d87450fad1394b9a4d5078f012b4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 29 Jul 2015 13:08:27 +0530 Subject: [PATCH 23/43] [fix] indicators for sales order, purchase order #3736 --- .../purchase_order/purchase_order_list.js | 8 +++++++- .../doctype/sales_order/sales_order_list.js | 20 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js index ee0c9bf83d..0de5286d5e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js @@ -5,7 +5,13 @@ frappe.listview_settings['Purchase Order'] = { if(doc.status==="Stopped") { return [__("Stopped"), "darkgrey", "status,=,Stopped"]; } else if(flt(doc.per_received) < 100 && doc.status!=="Stopped") { - return [__("Not Received"), "orange", "per_received,<,100|status,!=,Stopped"]; + if(flt(doc.per_billed) < 100) { + return [__("To Receive and Bill"), "orange", + "per_received,<,100|per_billed,<,100|status,!=,Stopped"]; + } else { + return [__("To Receive"), "orange", + "per_received,<,100|per_billed,=,100|status,!=,Stopped"]; + } } else if(flt(doc.per_received) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") { return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Stopped"]; } else if(flt(doc.per_received) == 100 && flt(doc.per_billed) == 100 && doc.status!=="Stopped") { diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index e0c75b6ebd..e4e67fc0d2 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -5,12 +5,30 @@ frappe.listview_settings['Sales Order'] = { if(doc.status==="Stopped") { return [__("Stopped"), "darkgrey", "status,=,Stopped"]; } else if(flt(doc.per_delivered) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) { + // to bill & overdue return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Stopped"]; + } else if(flt(doc.per_delivered) < 100 && doc.status!=="Stopped") { - return [__("Not Delivered"), "orange", "per_delivered,<,100|status,!=,Stopped"]; + // not delivered + + if(flt(doc.per_billed) < 100) { + // not delivered & not billed + + return [__("To Deliver and Bill"), "orange", + "per_delivered,<,100|per_billed,<,100|status,!=,Stopped"]; + } else { + // not billed + + return [__("To Deliver"), "orange", + "per_delivered,<,100|per_billed,=,100|status,!=,Stopped"]; + } + } else if(flt(doc.per_delivered) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") { + // to bill return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Stopped"]; + } else if(flt(doc.per_delivered) == 100 && flt(doc.per_billed) == 100 && doc.status!=="Stopped") { + return [__("Completed"), "green", "per_delivered,=,100|per_billed,=,100|status,!=,Stopped"]; } }, From 3ad26e4dd1e64f0c14fb4d191044a6767067af87 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 13:15:57 +0530 Subject: [PATCH 24/43] Fix Against Voucher in GL Entry for return against purchase invoice --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 006470f860..7e0ed7f390 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -255,7 +255,7 @@ class PurchaseInvoice(BuyingController): "against": self.against_expense_account, "credit": self.total_amount_to_pay, "remarks": self.remarks, - "against_voucher": self.name, + "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, }) ) From 1e046aa49a988907aa84d9b5887b20731d12242d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 13:16:37 +0530 Subject: [PATCH 25/43] Hide buttons for return entry --- .../purchase_invoice/purchase_invoice.js | 37 +++----- .../doctype/sales_invoice/sales_invoice.js | 19 ++--- .../public/js/controllers/stock_controller.js | 2 +- .../doctype/delivery_note/delivery_note.js | 85 ++++++++++--------- .../purchase_receipt/purchase_receipt.js | 51 ++++++----- .../stock/doctype/stock_entry/stock_entry.js | 4 +- .../stock_reconciliation.js | 4 +- 7 files changed, 101 insertions(+), 101 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 6a02706532..08ed4f3a81 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -20,27 +20,19 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ this._super(); // Show / Hide button - if(doc.docstatus==1 && doc.outstanding_amount > 0) - this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry); - - if(doc.docstatus==1) { - cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); + this.show_general_ledger(); + + if(!doc.is_return) { + if(doc.docstatus==1) { + if(doc.outstanding_amount > 0) { + this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry); + } + + cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); + } - cur_frm.add_custom_button(__('View Ledger'), function() { - frappe.route_options = { - "voucher_no": doc.name, - "from_date": doc.posting_date, - "to_date": doc.posting_date, - "company": doc.company, - group_by_voucher: 0 - }; - frappe.set_route("query-report", "General Ledger"); - }); - } - - if(doc.docstatus===0) { - cur_frm.add_custom_button(__('From Purchase Order'), - function() { + if(doc.docstatus===0) { + cur_frm.add_custom_button(__('From Purchase Order'), function() { frappe.model.map_current_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice", source_doctype: "Purchase Order", @@ -54,8 +46,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ }) }); - cur_frm.add_custom_button(__('From Purchase Receipt'), - function() { + cur_frm.add_custom_button(__('From Purchase Receipt'), function() { frappe.model.map_current_doc({ method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice", source_doctype: "Purchase Receipt", @@ -66,7 +57,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } }) }); - + } } }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index fdc1a58f6b..8348c08a51 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -43,18 +43,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); - if(doc.docstatus==1) { - cur_frm.add_custom_button('View Ledger', function() { - frappe.route_options = { - "voucher_no": doc.name, - "from_date": doc.posting_date, - "to_date": doc.posting_date, - "company": doc.company, - group_by_voucher: 0 - }; - frappe.set_route("query-report", "General Ledger"); - }); - + this.show_general_ledger(); + + if(doc.update_stock) this.show_stock_ledger(); + + if(doc.docstatus==1 && !doc.is_return) { if(cint(doc.update_stock)!=1) { // show Make Delivery Note button only if Sales Invoice is not created from Delivery Note var from_delivery_note = false; @@ -76,7 +69,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } // Show buttons only when pos view is active - if (cint(doc.docstatus==0) && cur_frm.page.current_view_name!=="pos") { + if (cint(doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !doc.is_return) { cur_frm.cscript.sales_order_btn(); cur_frm.cscript.delivery_note_btn(); } diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index c5cad95886..f5f7aa0c3d 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -58,7 +58,7 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ show_general_ledger: function() { var me = this; - if(this.frm.doc.docstatus===1 && cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + if(this.frm.doc.docstatus===1) { cur_frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: me.frm.doc.name, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 794d6fd811..94356da8e1 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -8,57 +8,64 @@ frappe.provide("erpnext.stock.delivery_note"); erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({ refresh: function(doc, dt, dn) { this._super(); + + if (!doc.is_return) { + if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1) { + // show Make Invoice button only if Delivery Note is not created from Sales Invoice + var from_sales_invoice = false; + from_sales_invoice = cur_frm.doc.items.some(function(item) { + return item.against_sales_invoice ? true : false; + }); - if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1) { - // show Make Invoice button only if Delivery Note is not created from Sales Invoice - var from_sales_invoice = false; - from_sales_invoice = cur_frm.doc.items.some(function(item) { - return item.against_sales_invoice ? true : false; - }); + if(!from_sales_invoice) + cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice); + } - if(!from_sales_invoice) - cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice); + if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) + cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note); + + if (doc.docstatus==1) { + cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return); + } + + if(doc.docstatus==0 && !doc.__islocal) { + cur_frm.add_custom_button(__('Make Packing Slip'), + cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]); + } + + if (this.frm.doc.docstatus===0) { + cur_frm.add_custom_button(__('From Sales Order'), + function() { + frappe.model.map_current_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", + source_doctype: "Sales Order", + get_query_filters: { + docstatus: 1, + status: ["!=", "Stopped"], + per_delivered: ["<", 99.99], + project_name: cur_frm.doc.project_name || undefined, + customer: cur_frm.doc.customer || undefined, + company: cur_frm.doc.company + } + }) + }); + } } - - if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) - cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note); - + if (doc.docstatus==1) { - cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return); - this.show_stock_ledger(); - this.show_general_ledger(); - } - - if(doc.docstatus==0 && !doc.__islocal) { - cur_frm.add_custom_button(__('Make Packing Slip'), - cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]); + if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + this.show_general_ledger(); + } } + + erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); // unhide expense_account and cost_center is auto_accounting_for_stock enabled var aii_enabled = cint(sys_defaults.auto_accounting_for_stock) cur_frm.fields_dict["items"].grid.set_column_disp(["expense_account", "cost_center"], aii_enabled); - - if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('From Sales Order'), - function() { - frappe.model.map_current_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", - source_doctype: "Sales Order", - get_query_filters: { - docstatus: 1, - status: ["!=", "Stopped"], - per_delivered: ["<", 99.99], - project_name: cur_frm.doc.project_name || undefined, - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company - } - }) - }); - } - }, make_sales_invoice: function() { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 13e104e15a..fcaf9f8074 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -29,32 +29,37 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend refresh: function() { this._super(); - if(this.frm.doc.docstatus == 1) { - if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) { - cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice); + this.show_stock_ledger(); + if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + this.show_general_ledger(); + } + if(!this.frm.doc.is_return) { + if(this.frm.doc.docstatus==0) { + cur_frm.add_custom_button(__('From Purchase Order'), + function() { + frappe.model.map_current_doc({ + method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", + source_doctype: "Purchase Order", + get_query_filters: { + supplier: cur_frm.doc.supplier || undefined, + docstatus: 1, + status: ["!=", "Stopped"], + per_received: ["<", 99.99], + company: cur_frm.doc.company + } + }) + }); } - cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); - - this.show_stock_ledger(); - this.show_general_ledger(); - } else { - cur_frm.add_custom_button(__('From Purchase Order'), - function() { - frappe.model.map_current_doc({ - method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", - source_doctype: "Purchase Order", - get_query_filters: { - supplier: cur_frm.doc.supplier || undefined, - docstatus: 1, - status: ["!=", "Stopped"], - per_received: ["<", 99.99], - company: cur_frm.doc.company - } - }) - }); + if(this.frm.doc.docstatus == 1) { + if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) { + cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice); + } + + cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); + } } - + this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes"); }, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 8526117223..087dbcae84 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -57,7 +57,9 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ this.toggle_related_fields(this.frm.doc); this.toggle_enable_bom(); this.show_stock_ledger(); - this.show_general_ledger(); + if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + this.show_general_ledger(); + } }, on_submit: function() { diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index f833a251be..4b67c0a4b6 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -83,7 +83,9 @@ erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({ refresh: function() { if(this.frm.doc.docstatus==1) { this.show_stock_ledger(); - this.show_general_ledger(); + if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + this.show_general_ledger(); + } } }, From 9eb9ccd785e4b92b7f1872ece1316208b406b5dd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 13:23:37 +0530 Subject: [PATCH 26/43] Make Debit / Credit Note button in Purchase / Sales Invoice --- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 6 +++--- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 4 ++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 08ed4f3a81..e3245cbfae 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -28,7 +28,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry); } - cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); + cur_frm.add_custom_button(__('Make Debit Note'), this.make_debit_note); } if(doc.docstatus===0) { @@ -103,9 +103,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ }) }, - make_purchase_return: function() { + make_debit_note: function() { frappe.model.open_mapped_doc({ - method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_purchase_return", + method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note", frm: cur_frm }) }, diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 7e0ed7f390..6e85087f16 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -411,6 +411,6 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): 'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype)}) @frappe.whitelist() -def make_purchase_return(source_name, target_doc=None): +def make_debit_note(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc - return make_return_doc("Purchase Invoice", source_name, target_doc) \ No newline at end of file + return make_return_doc("Purchase Invoice", source_name, target_doc) \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 8348c08a51..017ab3a10d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -65,7 +65,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry); } - cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return); + cur_frm.add_custom_button(doc.update_stock ? __('Make Sales Return') : __('Make Credit Note'), + this.make_sales_return); } // Show buttons only when pos view is active From 746c1620552c1509e6c452168a890adefeec4931 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 14:54:33 +0530 Subject: [PATCH 27/43] Return status in listview for return entry --- .../doctype/purchase_invoice/purchase_invoice_list.js | 6 ++++-- .../accounts/doctype/sales_invoice/sales_invoice_list.js | 6 ++++-- erpnext/stock/doctype/delivery_note/delivery_note_list.js | 7 ++++++- .../doctype/purchase_receipt/purchase_receipt_list.js | 7 ++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index dfb8329364..ccad5ce2ad 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -4,9 +4,11 @@ // render frappe.listview_settings['Purchase Invoice'] = { add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company", - "currency"], + "currency", "is_return"], get_indicator: function(doc) { - if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { + if(cint(doc.is_return)==1) { + return [__("Return"), "darkgrey", "is_return,=,1"]; + } else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { if(frappe.datetime.get_diff(doc.due_date) < 0) { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; } else { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index 6d152f8c1d..11c9789195 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -4,9 +4,11 @@ // render frappe.listview_settings['Sales Invoice'] = { add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", - "currency"], + "currency", "is_return"], get_indicator: function(doc) { - if(flt(doc.outstanding_amount)==0) { + if(cint(doc.is_return)==1) { + return [__("Return"), "darkgrey", "is_return,=,1"]; + } else if(flt(doc.outstanding_amount)==0) { return [__("Paid"), "green", "outstanding_amount,=,0"] } else if (flt(doc.outstanding_amount) > 0 && doc.due_date > frappe.datetime.get_today()) { return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"] diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index d30dec91e5..dce6863441 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -1,4 +1,9 @@ frappe.listview_settings['Delivery Note'] = { add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", - "transporter_name", "grand_total"] + "transporter_name", "grand_total", "is_return"], + get_indicator: function(doc) { + if(cint(doc.is_return)==1) { + return [__("Return"), "darkgrey", "is_return,=,1"]; + } + } }; diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index b0559cea73..64d5456bfe 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -1,4 +1,9 @@ frappe.listview_settings['Purchase Receipt'] = { add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted", - "transporter_name"] + "transporter_name", "is_return"], + get_indicator: function(doc) { + if(cint(doc.is_return)==1) { + return [__("Return"), "darkgrey", "is_return,=,1"]; + } + } }; From 8bf0b89595a0bc3fc06b383ae471597ce92fdd46 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 29 Jul 2015 14:55:48 +0530 Subject: [PATCH 28/43] [minor] currency field in list view for item price --- .../stock/doctype/item_price/item_price.json | 241 +++++++++--------- 1 file changed, 121 insertions(+), 120 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index b4e84f174e..546723abb6 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -1,157 +1,158 @@ { - "allow_import": 1, - "autoname": "ITEM-PRICE-.#####", - "creation": "2013-05-02 16:29:48", - "description": "Multiple Item prices.", - "docstatus": 0, - "doctype": "DocType", + "allow_import": 1, + "autoname": "ITEM-PRICE-.#####", + "creation": "2013-05-02 16:29:48", + "description": "Multiple Item prices.", + "docstatus": 0, + "doctype": "DocType", "document_type": "Master", "fields": [ { - "fieldname": "price_list_details", - "fieldtype": "Section Break", - "label": "Price List", - "options": "icon-tags", + "fieldname": "price_list_details", + "fieldtype": "Section Break", + "label": "Price List", + "options": "icon-tags", "permlevel": 0 - }, + }, { - "fieldname": "price_list", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Price List", - "options": "Price List", - "permlevel": 0, + "fieldname": "price_list", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Price List", + "options": "Price List", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "buying", - "fieldtype": "Check", - "in_list_view": 0, - "label": "Buying", - "permlevel": 0, + "fieldname": "buying", + "fieldtype": "Check", + "in_list_view": 0, + "label": "Buying", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "selling", - "fieldtype": "Check", - "in_list_view": 0, - "label": "Selling", - "permlevel": 0, + "fieldname": "selling", + "fieldtype": "Check", + "in_list_view": 0, + "label": "Selling", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "item_details", - "fieldtype": "Section Break", - "label": "", - "options": "icon-tag", + "fieldname": "item_details", + "fieldtype": "Section Break", + "label": "", + "options": "icon-tag", "permlevel": 0 - }, + }, { - "fieldname": "item_code", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 0, - "label": "Item Code", - "oldfieldname": "price_list_name", - "oldfieldtype": "Select", - "options": "Item", - "permlevel": 0, - "reqd": 1, + "fieldname": "item_code", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 0, + "label": "Item Code", + "oldfieldname": "price_list_name", + "oldfieldtype": "Select", + "options": "Item", + "permlevel": 0, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "price_list_rate", - "fieldtype": "Currency", - "in_filter": 1, - "in_list_view": 1, - "label": "Rate", - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "reqd": 1, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "in_filter": 1, + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "reqd": 1, "search_index": 0 - }, + }, { - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "label": "Currency", - "options": "Currency", - "permlevel": 0, + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 0, + "in_list_view": 1, + "label": "Currency", + "options": "Currency", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "col_br_1", - "fieldtype": "Column Break", + "fieldname": "col_br_1", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "item_name", - "fieldtype": "Data", - "label": "Item Name", - "permlevel": 0, + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "item_description", - "fieldtype": "Text", - "label": "Item Description", - "permlevel": 0, + "fieldname": "item_description", + "fieldtype": "Text", + "label": "Item Description", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "permlevel": 0, + "fieldname": "section_break_12", + "fieldtype": "Section Break", + "permlevel": 0, "precision": "" - }, + }, { - "fieldname": "bulk_import_help", - "fieldtype": "HTML", - "label": "Bulk Import Help", - "permlevel": 0, + "fieldname": "bulk_import_help", + "fieldtype": "HTML", + "label": "Bulk Import Help", + "permlevel": 0, "precision": "" } - ], - "icon": "icon-flag", - "idx": 1, - "in_create": 0, - "istable": 0, - "modified": "2015-05-26 03:15:02.324161", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Price", - "owner": "Administrator", + ], + "icon": "icon-flag", + "idx": 1, + "in_create": 0, + "istable": 0, + "modified": "2015-07-29 14:53:58.851413", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Price", + "owner": "Administrator", "permissions": [ { - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Master Manager", - "share": 1, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "share": 1, "write": 1 - }, + }, { - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Master Manager", - "share": 1, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Master Manager", + "share": 1, "write": 1 } - ], - "read_only": 0, + ], + "read_only": 0, "title_field": "item_code" -} \ No newline at end of file +} From 92b6f766121500ecc7bdd8d6e5f6b3eaf25f975f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 29 Jul 2015 15:17:30 +0530 Subject: [PATCH 29/43] [fix] disable save in naming series --- erpnext/setup/doctype/naming_series/naming_series.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/naming_series/naming_series.js b/erpnext/setup/doctype/naming_series/naming_series.js index e781754df5..5917f52d4c 100644 --- a/erpnext/setup/doctype/naming_series/naming_series.js +++ b/erpnext/setup/doctype/naming_series/naming_series.js @@ -1,7 +1,10 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt + cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { + cur_frm.disable_save(); + cur_frm.toolbar.print_icon.addClass("hide"); return cur_frm.call({ doc: cur_frm.doc, method: 'get_transactions', @@ -18,7 +21,7 @@ cur_frm.cscript.update_selects = function(r) { } cur_frm.cscript.select_doc_for_series = function(doc, cdt, cdn) { - cur_frm.toggle_display(['help_html','set_options', 'user_must_always_select', 'update'], + cur_frm.toggle_display(['help_html','set_options', 'user_must_always_select', 'update'], doc.select_doc_for_series); var callback = function(r, rt){ @@ -40,4 +43,4 @@ cur_frm.cscript.prefix = function(doc, dt, dn) { return cur_frm.call_server('get_current', '', function(r) { refresh_field('current_value'); }); -} \ No newline at end of file +} From 9117af3077f298d67ce888c0e429696dd77dde7b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 15:40:59 +0530 Subject: [PATCH 30/43] [fix] Uncheck Is POS option while creating return entry against POS Invoice --- erpnext/controllers/sales_and_purchase_return.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 899d1c1165..de4d280057 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -107,6 +107,8 @@ def make_return_doc(doctype, source_name, target_doc=None): doc.is_return = 1 doc.return_against = source.name doc.ignore_pricing_rule = 1 + if doctype == "Sales Invoice": + doc.is_pos = 0 doc.run_method("calculate_taxes_and_totals") def update_item(source_doc, target_doc, source_parent): From 15216fdd6f0f23c88bec90ddbf461bac6a75a5ed Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 15:47:45 +0530 Subject: [PATCH 31/43] [fix] Get item details based on selected company --- erpnext/stock/doctype/stock_entry/stock_entry.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 1b01f3aa68..487099cc3c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -5,10 +5,9 @@ from __future__ import unicode_literals import frappe import frappe.defaults from frappe import _ -from frappe.utils import cstr, cint, flt, comma_or, get_datetime, getdate +from frappe.utils import cstr, cint, flt, comma_or, getdate from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError -from erpnext.controllers.queries import get_match_cond from erpnext.stock.get_item_details import get_available_qty, get_default_cost_center, get_conversion_factor from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.accounts.utils import validate_fiscal_year @@ -436,8 +435,7 @@ class StockEntry(StockController): 'description' : item.description, 'image' : item.image, 'item_name' : item.item_name, - 'expense_account' : args.get("expense_account") \ - or frappe.db.get_value("Company", args.get("company"), "stock_adjustment_account"), + 'expense_account' : args.get("expense_account"), 'cost_center' : get_default_cost_center(args, item), 'qty' : 0, 'transfer_qty' : 0, @@ -446,6 +444,15 @@ class StockEntry(StockController): 'actual_qty' : 0, 'incoming_rate' : 0 } + for d in [["Account", "expense_account", "default_expense_account"], + ["Cost Center", "cost_center", "cost_center"]]: + company = frappe.db.get_value(d[0], ret.get(d[1]), "company") + if not ret[d[1]] or (company and self.company != company): + ret[d[1]] = frappe.db.get_value("Company", self.company, d[2]) if d[2] else None + + if not ret["expense_account"]: + ret["expense_account"] = frappe.db.get_value("Company", self.company, "stock_adjustment_account") + stock_and_rate = args.get('warehouse') and self.get_warehouse_details(args) or {} ret.update(stock_and_rate) return ret From 453cc374d49b3fac61e2c1cea6119eb62950866b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 12:21:03 +0530 Subject: [PATCH 32/43] - Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice - Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered - Make packing list on validate --- .../doctype/sales_invoice/sales_invoice.py | 29 ++++++++----------- erpnext/change_log/current/packing_list.md | 2 ++ erpnext/controllers/selling_controller.py | 23 +++++++++++---- erpnext/controllers/stock_controller.py | 19 ++++++++++++ erpnext/patches.txt | 1 + erpnext/patches/v5_4/__init__.py | 0 ...x_reserved_qty_and_sle_for_packed_items.py | 24 +++++++++++++++ .../doctype/delivery_note/delivery_note.py | 18 ------------ 8 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 erpnext/change_log/current/packing_list.md create mode 100644 erpnext/patches/v5_4/__init__.py create mode 100644 erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 12e199f74c..f581faab13 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe import frappe.defaults -from frappe.utils import cint, cstr, flt +from frappe.utils import cint, flt from frappe import _, msgprint, throw from erpnext.accounts.party import get_party_account, get_due_date from erpnext.controllers.stock_controller import update_gl_entries_after @@ -66,6 +66,7 @@ class SalesInvoice(SellingController): self.validate_c_form() self.validate_time_logs_are_submitted() self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items") + self.update_packing_list() def on_submit(self): super(SalesInvoice, self).on_submit() @@ -363,6 +364,13 @@ class SalesInvoice(SellingController): d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0 d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0 + def update_packing_list(self): + if cint(self.update_stock) == 1: + from erpnext.stock.doctype.packed_item.packed_item import make_packing_list + make_packing_list(self, 'items') + else: + self.set('packed_items', []) + def get_warehouse(self): user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile` @@ -381,20 +389,6 @@ class SalesInvoice(SellingController): return warehouse def on_update(self): - if cint(self.update_stock) == 1: - # Set default warehouse from POS Profile - if cint(self.is_pos) == 1: - w = self.get_warehouse() - if w: - for d in self.get('items'): - if not d.warehouse: - d.warehouse = cstr(w) - - from erpnext.stock.doctype.packed_item.packed_item import make_packing_list - make_packing_list(self, 'items') - else: - self.set('packed_items', []) - if cint(self.is_pos) == 1: if flt(self.paid_amount) == 0: if self.cash_bank_account: @@ -424,8 +418,9 @@ class SalesInvoice(SellingController): def update_stock_ledger(self): sl_entries = [] for d in self.get_item_list(): - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \ - and d.warehouse: + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse and flt(d['qty']): + self.update_reserved_qty(d) + incoming_rate = 0 if cint(self.is_return) and self.return_against and self.docstatus==1: incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, diff --git a/erpnext/change_log/current/packing_list.md b/erpnext/change_log/current/packing_list.md new file mode 100644 index 0000000000..d4793be1ea --- /dev/null +++ b/erpnext/change_log/current/packing_list.md @@ -0,0 +1,2 @@ +- Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice +- Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered \ No newline at end of file diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 9a0aedf176..a1468e13b6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -174,12 +174,14 @@ class SellingController(StockController): if flt(d.qty) > flt(d.delivered_qty): reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty) - elif self.doctype == "Delivery Note" and d.against_sales_order and not self.is_return: + elif (((self.doctype == "Delivery Note" and d.against_sales_order) + or (self.doctype == "Sales Invoice" and d.sales_order and self.update_stock)) + and not self.is_return): # if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12. # But in this case reserved qty should only be reduced by 10 and not 12 already_delivered_qty = self.get_already_delivered_qty(self.name, - d.against_sales_order, d.so_detail) + d.against_sales_order if self.doctype=="Delivery Note" else d.sales_order, d.so_detail) so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.so_detail) if already_delivered_qty + d.qty > so_qty: @@ -221,12 +223,21 @@ class SellingController(StockController): return frappe.db.sql("""select name from `tabProduct Bundle` where new_item_code=%s and docstatus != 2""", item_code) - def get_already_delivered_qty(self, dn, so, so_detail): - qty = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item` + def get_already_delivered_qty(self, current_docname, so, so_detail): + delivered_via_dn = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item` where so_detail = %s and docstatus = 1 and against_sales_order = %s - and parent != %s""", (so_detail, so, dn)) - return qty and flt(qty[0][0]) or 0.0 + and parent != %s""", (so_detail, so, current_docname)) + + delivered_via_si = frappe.db.sql("""select sum(qty) from `tabSales Invoice Item` + where so_detail = %s and docstatus = 1 + and sales_order = %s + and parent != %s""", (so_detail, so, current_docname)) + + total_delivered_qty = (flt(delivered_via_dn[0][0]) if delivered_via_dn else 0) \ + + (flt(delivered_via_si[0][0]) if delivered_via_si else 0) + + return total_delivered_qty def get_so_qty_and_warehouse(self, so_detail): so_item = frappe.db.sql("""select qty, warehouse from `tabSales Order Item` diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a47314bfd6..cebeaf59bf 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -9,6 +9,8 @@ import frappe.defaults from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map +from erpnext.stock.utils import update_bin + class StockController(AccountsController): def make_gl_entries(self, repost_future_gle=True): @@ -227,6 +229,23 @@ class StockController(AccountsController): incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0 return incoming_rate + + def update_reserved_qty(self, d): + if d['reserved_qty'] < 0 : + # Reduce reserved qty from reserved warehouse mentioned in so + if not d["reserved_warehouse"]: + frappe.throw(_("Reserved Warehouse is missing in Sales Order")) + + args = { + "item_code": d['item_code'], + "warehouse": d["reserved_warehouse"], + "voucher_type": self.doctype, + "voucher_no": self.name, + "reserved_qty": (self.docstatus==1 and 1 or -1)*flt(d['reserved_qty']), + "posting_date": self.posting_date, + "is_amended": self.amended_from and 'Yes' or 'No' + } + update_bin(args) def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 648d5b7c83..ef08098c32 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -180,3 +180,4 @@ execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force= erpnext.patches.v5_1.rename_roles erpnext.patches.v5_1.default_bom execute:frappe.delete_doc("DocType", "Party Type") +erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items diff --git a/erpnext/patches/v5_4/__init__.py b/erpnext/patches/v5_4/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py b/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py new file mode 100644 index 0000000000..0d46d99c34 --- /dev/null +++ b/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py @@ -0,0 +1,24 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.utilities.repost_stock import update_bin_qty, get_reserved_qty, repost_actual_qty + +def execute(): + cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice` + where docstatus = 2 and ifnull(update_stock, 0) = 1""") + + if cancelled_invoices: + frappe.db.sql("""delete from `tabStock Ledger Entry` + where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" + % (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices)) + + for item_code, warehouse in frappe.db.sql("select item_code, warehouse from tabBin where ifnull(reserved_qty, 0) < 0"): + + repost_actual_qty(item_code, warehouse) + + update_bin_qty(item_code, warehouse, { + "reserved_qty": get_reserved_qty(item_code, warehouse) + }) + \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index bf7505baa3..5e11962ea5 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -9,7 +9,6 @@ from frappe.utils import flt, cint from frappe import msgprint, _ import frappe.defaults from frappe.model.mapper import get_mapped_doc -from erpnext.stock.utils import update_bin from erpnext.controllers.selling_controller import SellingController form_grid_templates = { @@ -262,23 +261,6 @@ class DeliveryNote(SellingController): self.make_sl_entries(sl_entries) - def update_reserved_qty(self, d): - if d['reserved_qty'] < 0 : - # Reduce reserved qty from reserved warehouse mentioned in so - if not d["reserved_warehouse"]: - frappe.throw(_("Reserved Warehouse is missing in Sales Order")) - - args = { - "item_code": d['item_code'], - "warehouse": d["reserved_warehouse"], - "voucher_type": self.doctype, - "voucher_no": self.name, - "reserved_qty": (self.docstatus==1 and 1 or -1)*flt(d['reserved_qty']), - "posting_date": self.posting_date, - "is_amended": self.amended_from and 'Yes' or 'No' - } - update_bin(args) - def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) From b24d2efc4b99239f935b87a685e46cb5da2497c6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 16:15:08 +0530 Subject: [PATCH 33/43] [fix] Updated 'Include Holiday' in existing and default leave type records --- .../leave_application/leave_application.py | 2 +- erpnext/hr/doctype/leave_type/test_records.json | 6 ++++-- erpnext/patches.txt | 1 + .../setup/page/setup_wizard/install_fixtures.py | 15 ++++++++++----- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 226e95990f..7cbc59687a 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -245,7 +245,7 @@ def get_total_leave_days(leave_app): } else: ret = { - 'total_leave_days' : flt(tot_days) + 'total_leave_days' : flt(tot_days) } return ret diff --git a/erpnext/hr/doctype/leave_type/test_records.json b/erpnext/hr/doctype/leave_type/test_records.json index 8042e30f52..2ac46cf626 100644 --- a/erpnext/hr/doctype/leave_type/test_records.json +++ b/erpnext/hr/doctype/leave_type/test_records.json @@ -1,11 +1,13 @@ [ { "doctype": "Leave Type", - "leave_type_name": "_Test Leave Type" + "leave_type_name": "_Test Leave Type", + "include_holiday": 1 }, { "doctype": "Leave Type", "is_lwp": 1, - "leave_type_name": "_Test Leave Type LWP" + "leave_type_name": "_Test Leave Type LWP", + "include_holiday": 1 } ] \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ef08098c32..b029d55f5f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -181,3 +181,4 @@ erpnext.patches.v5_1.rename_roles erpnext.patches.v5_1.default_bom execute:frappe.delete_doc("DocType", "Party Type") erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items +execute:frappe.db.sql("update `tabLeave Type` set include_holiday=1") \ No newline at end of file diff --git a/erpnext/setup/page/setup_wizard/install_fixtures.py b/erpnext/setup/page/setup_wizard/install_fixtures.py index 6265e4a36c..dd3ec3a2e6 100644 --- a/erpnext/setup/page/setup_wizard/install_fixtures.py +++ b/erpnext/setup/page/setup_wizard/install_fixtures.py @@ -41,11 +41,16 @@ def install(country=None): {'doctype': 'Expense Claim Type', 'name': _('Travel'), 'expense_type': _('Travel')}, # leave type - {'doctype': 'Leave Type', 'leave_type_name': _('Casual Leave'), 'name': _('Casual Leave'), 'is_encash': 1, 'is_carry_forward': 1, 'max_days_allowed': '3', }, - {'doctype': 'Leave Type', 'leave_type_name': _('Compensatory Off'), 'name': _('Compensatory Off'), 'is_encash': 0, 'is_carry_forward': 0, }, - {'doctype': 'Leave Type', 'leave_type_name': _('Sick Leave'), 'name': _('Sick Leave'), 'is_encash': 0, 'is_carry_forward': 0, }, - {'doctype': 'Leave Type', 'leave_type_name': _('Privilege Leave'), 'name': _('Privilege Leave'), 'is_encash': 0, 'is_carry_forward': 0, }, - {'doctype': 'Leave Type', 'leave_type_name': _('Leave Without Pay'), 'name': _('Leave Without Pay'), 'is_encash': 0, 'is_carry_forward': 0, 'is_lwp':1}, + {'doctype': 'Leave Type', 'leave_type_name': _('Casual Leave'), 'name': _('Casual Leave'), + 'is_encash': 1, 'is_carry_forward': 1, 'max_days_allowed': '3', 'include_holiday': 1}, + {'doctype': 'Leave Type', 'leave_type_name': _('Compensatory Off'), 'name': _('Compensatory Off'), + 'is_encash': 0, 'is_carry_forward': 0, 'include_holiday': 1}, + {'doctype': 'Leave Type', 'leave_type_name': _('Sick Leave'), 'name': _('Sick Leave'), + 'is_encash': 0, 'is_carry_forward': 0, 'include_holiday': 1}, + {'doctype': 'Leave Type', 'leave_type_name': _('Privilege Leave'), 'name': _('Privilege Leave'), + 'is_encash': 0, 'is_carry_forward': 0, 'include_holiday': 1}, + {'doctype': 'Leave Type', 'leave_type_name': _('Leave Without Pay'), 'name': _('Leave Without Pay'), + 'is_encash': 0, 'is_carry_forward': 0, 'is_lwp':1, 'include_holiday': 1}, # Employment Type {'doctype': 'Employment Type', 'employee_type_name': _('Full-time')}, From d1dc62291420077558913b8426ae90b6e7e9890f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 16:37:02 +0530 Subject: [PATCH 34/43] [fix][patch] Reload Leave Type --- erpnext/hr/doctype/leave_type/leave_type.json | 2 +- erpnext/patches.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 4493af504c..87feb65f30 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -69,7 +69,7 @@ ], "icon": "icon-flag", "idx": 1, - "modified": "2015-05-08 05:15:24.194053", + "modified": "2015-07-29 05:15:24", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b029d55f5f..9e03e3e79f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -181,4 +181,5 @@ erpnext.patches.v5_1.rename_roles erpnext.patches.v5_1.default_bom execute:frappe.delete_doc("DocType", "Party Type") erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items +execute:frappe.reload_doctype("Leave Type") execute:frappe.db.sql("update `tabLeave Type` set include_holiday=1") \ No newline at end of file From fd8f34018dd71af87c68bd72f6155ca1094c5e18 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 16:28:29 +0530 Subject: [PATCH 35/43] Change log added --- erpnext/change_log/current/item_cleanup.md | 2 -- erpnext/change_log/current/packing_list.md | 2 -- erpnext/change_log/current/spnsorship.md | 2 -- erpnext/change_log/v5/v5_4_0.md | 9 +++++++++ 4 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 erpnext/change_log/current/item_cleanup.md delete mode 100644 erpnext/change_log/current/packing_list.md delete mode 100644 erpnext/change_log/current/spnsorship.md create mode 100644 erpnext/change_log/v5/v5_4_0.md diff --git a/erpnext/change_log/current/item_cleanup.md b/erpnext/change_log/current/item_cleanup.md deleted file mode 100644 index c2f1e911e7..0000000000 --- a/erpnext/change_log/current/item_cleanup.md +++ /dev/null @@ -1,2 +0,0 @@ -- **Item** form cleanups: "Yes" / "No" type fields changed to checkboxes.
- **Warning**: This could break your 3rd party integrations with Item, if any. diff --git a/erpnext/change_log/current/packing_list.md b/erpnext/change_log/current/packing_list.md deleted file mode 100644 index d4793be1ea..0000000000 --- a/erpnext/change_log/current/packing_list.md +++ /dev/null @@ -1,2 +0,0 @@ -- Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice -- Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered \ No newline at end of file diff --git a/erpnext/change_log/current/spnsorship.md b/erpnext/change_log/current/spnsorship.md deleted file mode 100644 index 62720acc54..0000000000 --- a/erpnext/change_log/current/spnsorship.md +++ /dev/null @@ -1,2 +0,0 @@ -- Sales / Purchase Return Enahancement - **[Sponsored by Strella Consulting Sdn Bhd](http://www.strellagroup.com)** - * Now you can make Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity \ No newline at end of file diff --git a/erpnext/change_log/v5/v5_4_0.md b/erpnext/change_log/v5/v5_4_0.md new file mode 100644 index 0000000000..0a9f29f7d7 --- /dev/null +++ b/erpnext/change_log/v5/v5_4_0.md @@ -0,0 +1,9 @@ +- Sales / Purchase Return Enahancement - **[Sponsored by Strella Consulting Sdn Bhd](http://www.strellagroup.com)** + * Now you can make Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity +- **Item** form cleanups: "Yes" / "No" type fields changed to checkboxes.
+ **Warning**: This could break your 3rd party integrations with Item, if any +- Leave Application: Consideration of **holidays** in calculation of 'Number of Days' is now optional. You can set it in Leave Type record. +- Customer / Supplier can be **freezed** now +- Fix: Reserved qty calculation while delivering Product Bundle via Sales Invoice +- Fix: Deleted stock ledger entries on cancellation of Sales Invoice while Product Bundle delivered +- Fix: Fetch default expense account and cost center from item based on selected company \ No newline at end of file From 7364ebaff64146777b7f74d07cb6311130b25cd7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 16:41:23 +0530 Subject: [PATCH 36/43] minor fix --- erpnext/change_log/v5/v5_4_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/change_log/v5/v5_4_0.md b/erpnext/change_log/v5/v5_4_0.md index 0a9f29f7d7..6c9b72ec5e 100644 --- a/erpnext/change_log/v5/v5_4_0.md +++ b/erpnext/change_log/v5/v5_4_0.md @@ -1,5 +1,5 @@ -- Sales / Purchase Return Enahancement - **[Sponsored by Strella Consulting Sdn Bhd](http://www.strellagroup.com)** - * Now you can make Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity +- Sales / Purchase Return Enahancement - **Sponsored by [Strella Consulting Sdn Bhd](http://www.strellagroup.com)** + * Now you can make Return entry by creating Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity - **Item** form cleanups: "Yes" / "No" type fields changed to checkboxes.
**Warning**: This could break your 3rd party integrations with Item, if any - Leave Application: Consideration of **holidays** in calculation of 'Number of Days' is now optional. You can set it in Leave Type record. From fcc4021e44ccaf7f7abe68988e113d412443b563 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 17:30:57 +0530 Subject: [PATCH 37/43] minor fix in payment reconciliation --- .../doctype/payment_reconciliation/payment_reconciliation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 05c1bc4886..ecbf78ce37 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -190,8 +190,9 @@ class PaymentReconciliation(Document): if flt(p.allocated_amount) > flt(p.amount): frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}") .format(p.idx, p.allocated_amount, p.amount)) - - if flt(p.allocated_amount) > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number): + + invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number) + if abs(flt(p.allocated_amount) - invoice_outstanding) > 0.009: frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}") .format(p.idx, p.allocated_amount, unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number))) From f33787a70638ab880fb16844752334262b9facf6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jul 2015 18:08:10 +0530 Subject: [PATCH 38/43] [fix] Get serial nos using FIFO in Return Delivery Note --- erpnext/stock/get_item_details.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 82446ed420..faf3d98c0f 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -300,7 +300,7 @@ def get_serial_nos_by_fifo(args, item_doc): order by timestamp(purchase_date, purchase_time) asc limit %(qty)s""", { "item_code": args.item_code, "warehouse": args.warehouse, - "qty": cint(args.qty) + "qty": abs(cint(args.qty)) })) def get_actual_batch_qty(batch_no,warehouse,item_code): From e73941b9cc3adaee51b525422f2fb2f25135fbf2 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 29 Jul 2015 18:39:27 +0530 Subject: [PATCH 39/43] Fixes in leave application --- erpnext/hr/doctype/leave_application/leave_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 7cbc59687a..d1ec4b187a 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -238,7 +238,7 @@ def get_total_leave_days(leave_app): ret = {'total_leave_days' : 0.5} if not leave_app.half_day: tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1 - if frappe.db.get_value("Leave Type", self.leave_type, "include_holiday"): + if frappe.db.get_value("Leave Type", leave_app.leave_type, "include_holiday"): holidays = leave_app.get_holidays() ret = { 'total_leave_days' : flt(tot_days)-flt(holidays) From d851bd8d8547a39398f0144b9b51c5237db47a18 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 27 Jul 2015 16:51:04 +0530 Subject: [PATCH 40/43] Fixed test cases related to Time Logs --- .../sales_invoice/test_sales_invoice.py | 30 +--- .../production_order/test_production_order.py | 10 +- .../activity_cost/test_activity_cost.py | 3 +- .../doctype/time_log/test_records.json | 11 +- .../doctype/time_log/test_time_log.py | 149 ++++++++---------- erpnext/projects/doctype/time_log/time_log.py | 5 +- .../time_log_batch/test_time_log_batch.py | 36 +---- 7 files changed, 86 insertions(+), 158 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6d54f0ab6c..2e64f99dff 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5,10 +5,8 @@ from __future__ import unicode_literals import frappe import unittest, copy from frappe.utils import nowdate, add_days, flt -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory -from erpnext.projects.doctype.time_log_batch.test_time_log_batch import * from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction - +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestSalesInvoice(unittest.TestCase): def make(self): @@ -402,32 +400,6 @@ class TestSalesInvoice(unittest.TestCase): jv.cancel() self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8) - def test_time_log_batch(self): - delete_time_log_and_batch() - time_log = create_time_log() - tlb = create_time_log_batch(time_log) - - tlb = frappe.get_doc("Time Log Batch", tlb.name) - tlb.submit() - - si = frappe.get_doc(frappe.copy_doc(test_records[0])) - si.get("items")[0].time_log_batch = tlb.name - si.insert() - si.submit() - - self.assertEquals(frappe.db.get_value("Time Log Batch", tlb.name, "status"), "Billed") - - self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Billed") - - si.cancel() - - self.assertEquals(frappe.db.get_value("Time Log Batch", tlb.name, "status"), "Submitted") - - self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Batched for Billing") - - frappe.delete_doc("Sales Invoice", si.name) - delete_time_log_and_batch() - def test_sales_invoice_gl_entry_without_aii(self): set_perpetual_inventory(0) si = frappe.copy_doc(test_records[1]) diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index ddcc8c7404..838ef38baf 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.utils import flt, get_datetime, time_diff_in_hours +from frappe.utils import flt, get_datetime, time_diff_in_hours, now, add_days from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.manufacturing.doctype.production_order.production_order \ import make_stock_entry, make_time_log, ProductionNotApplicableError,ItemHasVariantError @@ -69,7 +69,7 @@ class TestProductionOrder(unittest.TestCase): def test_make_time_log(self): prod_order = make_prod_order_test_record(item="_Test FG Item 2", - planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True) + planned_start_date=now(), qty=1, do_not_save=True) prod_order.set_production_order_operations() prod_order.insert() @@ -121,15 +121,15 @@ class TestProductionOrder(unittest.TestCase): time_log2 = frappe.copy_doc(time_log) time_log2.update({ "completed_qty": 10, - "from_time": "2014-11-26 00:00:00", - "to_time": "2014-11-26 00:00:00", + "from_time": now(), + "to_time": add_days(now(), 1), "docstatus": 0 }) self.assertRaises(OverProductionLoggedError, time_log2.save) def test_planned_operating_cost(self): prod_order = make_prod_order_test_record(item="_Test FG Item 2", - planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True) + planned_start_date=now(), qty=1, do_not_save=True) prod_order.set_production_order_operations() cost = prod_order.planned_operating_cost prod_order.qty = 2 diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py index 5afd97f96c..58c3f21bef 100644 --- a/erpnext/projects/doctype/activity_cost/test_activity_cost.py +++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py @@ -15,10 +15,11 @@ class TestActivityCost(unittest.TestCase): activity_cost1.update({ "employee": "_T-Employee-0001", "employee_name": "_Test Employee", - "activity_type": "_Test Activity Type", + "activity_type": "_Test Activity Type 1", "billing_rate": 100, "costing_rate": 50 }) activity_cost1.insert() activity_cost2 = frappe.copy_doc(activity_cost1) self.assertRaises(DuplicationError, activity_cost2.insert ) + frappe.db.sql("delete from `tabActivity Cost`") \ No newline at end of file diff --git a/erpnext/projects/doctype/time_log/test_records.json b/erpnext/projects/doctype/time_log/test_records.json index 568c0121a7..0637a088a0 100644 --- a/erpnext/projects/doctype/time_log/test_records.json +++ b/erpnext/projects/doctype/time_log/test_records.json @@ -1,10 +1 @@ -[ - { - "activity_type": "_Test Activity Type", - "docstatus": 1, - "doctype": "Time Log", - "from_time": "2013-01-01 10:00:00.000000", - "note": "_Test Note", - "to_time": "2013-01-01 11:00:00.000000" - } -] +[] \ No newline at end of file diff --git a/erpnext/projects/doctype/time_log/test_time_log.py b/erpnext/projects/doctype/time_log/test_time_log.py index 9b43b0dffe..c9cc51e518 100644 --- a/erpnext/projects/doctype/time_log/test_time_log.py +++ b/erpnext/projects/doctype/time_log/test_time_log.py @@ -5,63 +5,47 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.projects.doctype.time_log.time_log import OverlapError -from erpnext.projects.doctype.time_log.time_log import NotSubmittedError -from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError -from erpnext.manufacturing.doctype.workstation.workstation import NotInWorkingHoursError +import datetime +from frappe.utils import now_datetime, now +from erpnext.projects.doctype.time_log.time_log import OverlapError, NotSubmittedError, NegativeHoursError +from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError from erpnext.manufacturing.doctype.production_order.test_production_order import make_prod_order_test_record class TestTimeLog(unittest.TestCase): def test_duplication(self): - frappe.db.sql("delete from `tabTime Log`") + date = now_datetime() + tl1 = make_time_log_test_record(user= "test@example.com", employee= "_T-Employee-0002", + from_time= date, to_time= date + datetime.timedelta(seconds=1)) - tl1 = frappe.get_doc(frappe.copy_doc(test_records[0])) - tl1.user = "test@example.com" - tl1.insert() - - tl2 = frappe.get_doc(frappe.copy_doc(test_records[0])) - tl2.user = "test@example.com" + tl2 = make_time_log_test_record(user= "test@example.com", employee= "_T-Employee-0002", + from_time= date, to_time= date + datetime.timedelta(seconds=1), do_not_save= 1) self.assertRaises(OverlapError, tl2.insert) - - frappe.db.sql("delete from `tabTime Log`") + tl1.cancel() def test_production_order_status(self): - prod_order = make_prod_order_test_record(item="_Test FG Item 2", qty=1, do_not_submit=True) + prod_order = make_prod_order_test_record(item= "_Test FG Item 2", qty= 1, do_not_submit= True) prod_order.set_production_order_operations() prod_order.save() - time_log = frappe.get_doc({ - "doctype": "Time Log", - "for_manufacturing": 1, - "production_order": prod_order.name, - "qty": 1, - "from_time": "2014-12-26 00:00:00", - "to_time": "2014-12-26 00:00:00" - }) + time_log = make_time_log_test_record(for_manufacturing= 1, production_order= prod_order.name, qty= 1, + employee= "_T-Employee-0003", do_not_save= True) self.assertRaises(NotSubmittedError, time_log.save) def test_time_log_on_holiday(self): - prod_order = make_prod_order_test_record(item="_Test FG Item 2", qty=1, - planned_start_date="2014-11-25 00:00:00", do_not_save=True) + prod_order = make_prod_order_test_record(item= "_Test FG Item 2", qty= 1, + planned_start_date= now(), do_not_save= True) prod_order.set_production_order_operations() prod_order.save() prod_order.submit() - time_log = frappe.get_doc({ - "doctype": "Time Log", - "for_manufacturing": 1, - "production_order": prod_order.name, - "operation": prod_order.operations[0].operation, - "operation_id": prod_order.operations[0].name, - "qty": 1, - "activity_type": "_Test Activity Type", - "from_time": "2013-02-01 10:00:00", - "to_time": "2013-02-01 20:00:00", - "workstation": "_Test Workstation 1" - }) + time_log = make_time_log_test_record(from_time= "2013-02-01 10:00:00", to_time= "2013-02-01 20:00:00", + for_manufacturing= 1, production_order= prod_order.name, qty= 1, + operation= prod_order.operations[0].operation, operation_id= prod_order.operations[0].name, + workstation= "_Test Workstation 1", do_not_save= True) + self.assertRaises(WorkstationHolidayError , time_log.save) time_log.update({ @@ -76,28 +60,23 @@ class TestTimeLog(unittest.TestCase): time_log.cancel() def test_negative_hours(self): - frappe.db.sql("delete from `tabTime Log`") - test_time_log = frappe.new_doc("Time Log") - test_time_log.activity_type = "Communication" - test_time_log.from_time = "2013-01-01 11:00:00.000000" - test_time_log.to_time = "2013-01-01 10:00:00.000000" - self.assertRaises(frappe.ValidationError, test_time_log.save) - frappe.db.sql("delete from `tabTime Log`") - + time_log = make_time_log_test_record(to_time= now_datetime() + datetime.timedelta(minutes=-1), + employee="_T-Employee-0006",do_not_save= True) + self.assertRaises(NegativeHoursError, time_log.save) + def test_total_activity_cost_for_project(self): frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) - frappe.db.sql("""delete from `tabActivity Cost` where employee = "_T-Employee-0001" and activity_type = "_Test Activity Type" """) - activity_cost = frappe.new_doc('Activity Cost') - activity_cost.update({ - "employee": "_T-Employee-0001", - "employee_name": "_Test Employee", - "activity_type": "_Test Activity Type", - "billing_rate": 100, - "costing_rate": 50 - }) - activity_cost.insert() + if not frappe.db.exists('Activity Cost', {"activity_type": "_Test Activity Type"}): + activity_cost = frappe.get_doc({ + "doctype": "Activity Cost", + "employee": "", + "activity_type": "_Test Activity Type", + "billing_rate": 100, + "costing_rate": 50 + }) + activity_cost.insert() frappe.get_doc({ "project_name": "_Test Project 1", @@ -108,40 +87,16 @@ class TestTimeLog(unittest.TestCase): task_name = frappe.db.get_value("Task",{"project": "_Test Project 1"}) - time_log = frappe.get_doc({ - "activity_type": "_Test Activity Type", - "docstatus": 1, - "doctype": "Time Log", - "from_time": "2013-02-02 09:00:00.000000", - "to_time": "2013-02-02 11:00:00.000000", - "employee": "_T-Employee-0001", - "project": "_Test Project 1", - "task": task_name, - "billable": 1 - }) - time_log.save() + time_log = make_time_log_test_record(employee="_T-Employee-0002", hours=2, task= task_name) self.assertEqual(time_log.costing_rate, 50) self.assertEqual(time_log.costing_amount, 100) self.assertEqual(time_log.billing_rate, 100) self.assertEqual(time_log.billing_amount, 200) - time_log.submit() self.assertEqual(frappe.db.get_value("Task", task_name, "total_billing_amount"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_billing_amount"), 200) - time_log2 = frappe.get_doc({ - "activity_type": "_Test Activity Type", - "docstatus": 1, - "doctype": "Time Log", - "from_time": "2013-02-03 09:00:00.000000", - "to_time": "2013-02-03 11:00:00.000000", - "employee": "_T-Employee-0001", - "project": "_Test Project 1", - "task": task_name, - "billable": 1 - }) - time_log2.save() - + time_log2 = make_time_log_test_record(employee="_T-Employee-0003", hours=2, task= task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_billing_amount"), 400) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_billing_amount"), 400) @@ -149,6 +104,38 @@ class TestTimeLog(unittest.TestCase): self.assertEqual(frappe.db.get_value("Task", task_name, "total_billing_amount"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_billing_amount"), 200) + time_log.cancel() -test_records = frappe.get_test_records('Time Log') test_ignore = ["Time Log Batch", "Sales Invoice"] + +def make_time_log_test_record(**args): + args = frappe._dict(args) + + time_log = frappe.new_doc("Time Log") + + time_log.from_time = args.from_time or now_datetime() + time_log.to_time = args.to_time or time_log.from_time + datetime.timedelta(seconds=1) + + if args.hours>0: + time_log.hours = args.hours + time_log.to_time = time_log.from_time + datetime.timedelta(hours= args.hours) + + time_log.project = args.project + time_log.task = args.task + time_log.for_manufacturing = args.for_manufacturing + time_log.production_order = args.production_order + time_log.operation = args.operation + time_log.operation_id = args.operation_id + time_log.workstation = args.workstation + time_log.qty = args.qty or 1 + time_log.activity_type = args.activity_type or "_Test Activity Type" + time_log.billable = args.billable or 1 + time_log.employee = args.employee + time_log.user = args.user + + if not args.do_not_save: + time_log.insert() + if not args.do_not_submit: + time_log.submit() + + return time_log \ No newline at end of file diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py index 136362b0a4..0f50e9cc5a 100644 --- a/erpnext/projects/doctype/time_log/time_log.py +++ b/erpnext/projects/doctype/time_log/time_log.py @@ -10,6 +10,7 @@ from dateutil.relativedelta import relativedelta class OverlapError(frappe.ValidationError): pass class OverProductionLoggedError(frappe.ValidationError): pass class NotSubmittedError(frappe.ValidationError): pass +class NegativeHoursError(frappe.ValidationError): pass from frappe.model.document import Document @@ -101,8 +102,8 @@ class TimeLog(Document): return existing[0] if existing else None def validate_timings(self): - if self.to_time and self.from_time and get_datetime(self.to_time) < get_datetime(self.from_time): - frappe.throw(_("From Time cannot be greater than To Time")) + if self.to_time and self.from_time and get_datetime(self.to_time) <= get_datetime(self.from_time): + frappe.throw(_("To Time must be greater than From Time"), NegativeHoursError) def calculate_total_hours(self): if self.to_time and self.from_time: diff --git a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py index faa0a601e3..1166c768b1 100644 --- a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py +++ b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py @@ -3,43 +3,19 @@ from __future__ import unicode_literals import frappe, unittest +from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_record class TimeLogBatchTest(unittest.TestCase): def test_time_log_status(self): - delete_time_log_and_batch() - time_log = create_time_log() + time_log = make_time_log_test_record(employee= "_T-Employee-0002") - self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Submitted") + self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Submitted") - tlb = create_time_log_batch(time_log) + tlb = create_time_log_batch(time_log.name) - self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Batched for Billing") + self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Batched for Billing") tlb.cancel() - self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Submitted") - - delete_time_log_and_batch() - -def delete_time_log_and_batch(): - for name in frappe.db.sql_list("select name from `tabTime Log Batch` where docstatus=1"): - frappe.get_doc("Time Log Batch", name).cancel() - frappe.delete_doc("Time Log Batch", name) - - for name in frappe.db.sql_list("select name from `tabTime Log` where docstatus=1"): - frappe.get_doc("Time Log", name).cancel() - frappe.delete_doc("Time Log", name) - -def create_time_log(): - from erpnext.projects.doctype.time_log.test_time_log import test_records as time_log_records - time_log = frappe.copy_doc(time_log_records[0]) - time_log.update({ - "from_time": "2013-01-02 10:00:00.000000", - "to_time": "2013-01-02 11:00:00.000000", - "docstatus": 0, - "for_manufacturing": 0 - }) - time_log.insert() - time_log.submit() - return time_log.name + self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Submitted") def create_time_log_batch(time_log): tlb = frappe.get_doc({ From f948fea3bf81feef81742fb418d96491c1690890 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 27 Jul 2015 18:01:49 +0530 Subject: [PATCH 41/43] simulate added to make_time_log in test_time_log --- .../production_order/test_production_order.py | 30 ++++++------------- .../doctype/time_log/test_time_log.py | 29 ++++++++++-------- .../time_log_batch/test_time_log_batch.py | 2 +- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index 838ef38baf..4d653375cb 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -5,10 +5,10 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.utils import flt, get_datetime, time_diff_in_hours, now, add_days +from frappe.utils import flt, time_diff_in_hours, now, add_days from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.manufacturing.doctype.production_order.production_order \ - import make_stock_entry, make_time_log, ProductionNotApplicableError,ItemHasVariantError + import make_stock_entry, ProductionNotApplicableError,ItemHasVariantError from erpnext.stock.doctype.stock_entry import test_stock_entry from erpnext.projects.doctype.time_log.time_log import OverProductionLoggedError @@ -68,6 +68,7 @@ class TestProductionOrder(unittest.TestCase): self.assertRaises(StockOverProductionError, s.submit) def test_make_time_log(self): + from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_record prod_order = make_prod_order_test_record(item="_Test FG Item 2", planned_start_date=now(), qty=1, do_not_save=True) @@ -79,17 +80,13 @@ class TestProductionOrder(unittest.TestCase): d.completed_qty = flt(d.completed_qty) - time_log = make_time_log(prod_order.name, d.operation, \ - d.planned_start_time, d.planned_end_time, prod_order.qty - d.completed_qty, - operation_id=d.name) + time_log = make_time_log_test_record(hours=1, production_order= prod_order.name, operation= d.operation, + completed_qty= prod_order.qty - d.completed_qty, operation_id=d.name, for_manufacturing=1, simulate=True) self.assertEqual(prod_order.name, time_log.production_order) self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty) self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours) - - time_log.save() - time_log.submit() - + manufacturing_settings = frappe.get_doc({ "doctype": "Manufacturing Settings", "allow_production_on_holidays": 0 @@ -101,11 +98,6 @@ class TestProductionOrder(unittest.TestCase): self.assertEqual(prod_order.operations[0].status, "Completed") self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty) - self.assertEqual(get_datetime(prod_order.operations[0].actual_start_time), - get_datetime(time_log.from_time)) - self.assertEqual(get_datetime(prod_order.operations[0].actual_end_time), - get_datetime(time_log.to_time)) - self.assertEqual(prod_order.operations[0].actual_operation_time, 60) self.assertEqual(prod_order.operations[0].actual_operating_cost, 100) @@ -118,13 +110,9 @@ class TestProductionOrder(unittest.TestCase): self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0) self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0) - time_log2 = frappe.copy_doc(time_log) - time_log2.update({ - "completed_qty": 10, - "from_time": now(), - "to_time": add_days(now(), 1), - "docstatus": 0 - }) + time_log2 = make_time_log_test_record(from_time= add_days(time_log.to_time, 1) ,production_order= prod_order.name, operation= d.operation, + completed_qty= 5, operation_id=d.name, for_manufacturing=1, do_not_save=True) + self.assertRaises(OverProductionLoggedError, time_log2.save) def test_planned_operating_cost(self): diff --git a/erpnext/projects/doctype/time_log/test_time_log.py b/erpnext/projects/doctype/time_log/test_time_log.py index c9cc51e518..9bdf5886f7 100644 --- a/erpnext/projects/doctype/time_log/test_time_log.py +++ b/erpnext/projects/doctype/time_log/test_time_log.py @@ -14,15 +14,12 @@ from erpnext.manufacturing.doctype.production_order.test_production_order import class TestTimeLog(unittest.TestCase): def test_duplication(self): - date = now_datetime() - tl1 = make_time_log_test_record(user= "test@example.com", employee= "_T-Employee-0002", - from_time= date, to_time= date + datetime.timedelta(seconds=1)) + tl1 = make_time_log_test_record(user= "test@example.com", employee= "_T-Employee-0002", simulate= True) tl2 = make_time_log_test_record(user= "test@example.com", employee= "_T-Employee-0002", - from_time= date, to_time= date + datetime.timedelta(seconds=1), do_not_save= 1) + from_time= tl1.from_time, to_time= tl1.to_time, do_not_save= 1) self.assertRaises(OverlapError, tl2.insert) - tl1.cancel() def test_production_order_status(self): prod_order = make_prod_order_test_record(item= "_Test FG Item 2", qty= 1, do_not_submit= True) @@ -30,7 +27,7 @@ class TestTimeLog(unittest.TestCase): prod_order.save() time_log = make_time_log_test_record(for_manufacturing= 1, production_order= prod_order.name, qty= 1, - employee= "_T-Employee-0003", do_not_save= True) + employee= "_T-Employee-0003", do_not_save= True, simulate=1) self.assertRaises(NotSubmittedError, time_log.save) @@ -114,11 +111,8 @@ def make_time_log_test_record(**args): time_log = frappe.new_doc("Time Log") time_log.from_time = args.from_time or now_datetime() - time_log.to_time = args.to_time or time_log.from_time + datetime.timedelta(seconds=1) - - if args.hours>0: - time_log.hours = args.hours - time_log.to_time = time_log.from_time + datetime.timedelta(hours= args.hours) + time_log.hours = args.hours or 1 + time_log.to_time = args.to_time or time_log.from_time + datetime.timedelta(hours= time_log.hours) time_log.project = args.project time_log.task = args.task @@ -127,14 +121,23 @@ def make_time_log_test_record(**args): time_log.operation = args.operation time_log.operation_id = args.operation_id time_log.workstation = args.workstation - time_log.qty = args.qty or 1 + time_log.completed_qty = args.completed_qty time_log.activity_type = args.activity_type or "_Test Activity Type" time_log.billable = args.billable or 1 time_log.employee = args.employee time_log.user = args.user if not args.do_not_save: - time_log.insert() + if args.simulate: + while True: + try: + time_log.save() + break + except OverlapError: + time_log.from_time = time_log.from_time + datetime.timedelta(minutes=10) + time_log.to_time = time_log.from_time + datetime.timedelta(hours= time_log.hours) + else: + time_log.save() if not args.do_not_submit: time_log.submit() diff --git a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py index 1166c768b1..9f3591d761 100644 --- a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py +++ b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py @@ -7,7 +7,7 @@ from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_r class TimeLogBatchTest(unittest.TestCase): def test_time_log_status(self): - time_log = make_time_log_test_record(employee= "_T-Employee-0002") + time_log = make_time_log_test_record(simulate=True) self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Submitted") From 9e26bcfeebcf445798fd99da702811c4a1409beb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jul 2015 11:48:18 +0530 Subject: [PATCH 42/43] [fix] Actual tax amount should be negative while creating return entry --- erpnext/controllers/sales_and_purchase_return.py | 7 ++++++- erpnext/controllers/taxes_and_totals.py | 3 --- erpnext/public/js/controllers/taxes_and_totals.js | 4 ---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index de4d280057..6e88bfb0d5 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -109,8 +109,13 @@ def make_return_doc(doctype, source_name, target_doc=None): doc.ignore_pricing_rule = 1 if doctype == "Sales Invoice": doc.is_pos = 0 + + for tax in doc.get("taxes"): + if tax.charge_type == "Actual": + tax.tax_amount = -1 * tax.tax_amount + doc.run_method("calculate_taxes_and_totals") - + def update_item(source_doc, target_doc, source_parent): target_doc.qty = -1* source_doc.qty if doctype == "Purchase Receipt": diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index f22b62488b..6b59cea00d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -77,9 +77,6 @@ class calculate_taxes_and_totals(object): if not self.discount_amount_applied: validate_taxes_and_charges(tax) validate_inclusive_tax(tax, self.doc) - - if self.doc.meta.get_field("is_return") and self.doc.is_return and tax.charge_type == "Actual": - tax.tax_amount = -1 * tax.tax_amount tax.item_wise_tax_detail = {} tax_fields = ["total", "tax_amount_after_discount_amount", diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 07c2d56cf1..1e238d6934 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -94,10 +94,6 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({ tax_fields = ["total", "tax_amount_after_discount_amount", "tax_amount_for_current_item", "grand_total_for_current_item", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] - - if (frappe.meta.get_docfield(me.frm.doc.doctype, "is_return") && me.frm.doc.is_return - && tax.charge_type == "Actual") - tax.tax_amount = -1 * tax.tax_amount; if (cstr(tax.charge_type) != "Actual" && !(me.discount_amount_applied && me.frm.doc.apply_discount_on=="Grand Total")) From 3832e1f9be5dd0311684766a122d95f8fca9cd77 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jul 2015 12:27:53 +0600 Subject: [PATCH 43/43] bumped to version 5.4.0 --- erpnext/__version__.py | 2 +- erpnext/hooks.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/__version__.py b/erpnext/__version__.py index b994a33aa2..dd21361734 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '5.3.1' +__version__ = '5.4.0' diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 3a1a51ebdc..ec3e5f8b08 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -27,7 +27,7 @@ blogs. """ app_icon = "icon-th" app_color = "#e74c3c" -app_version = "5.3.1" +app_version = "5.4.0" github_link = "https://github.com/frappe/erpnext" error_report_email = "support@erpnext.com" diff --git a/setup.py b/setup.py index 8e50ecdb12..30e3aad8db 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "5.3.1" +version = "5.4.0" with open("requirements.txt", "r") as f: install_requires = f.readlines()