From 856ee10dc4f9cc56f1d2e9bea215f3002fb9f70b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 17 Jul 2015 15:03:18 +0530 Subject: [PATCH 01/11] [enhancement] update to setup wizard, added users, employees, sample data --- erpnext/accounts/doctype/account/account.py | 2 +- .../purchase_taxes_and_charges_template.py | 7 +- .../sales_taxes_and_charges_template.py | 26 ++-- .../page/setup_wizard/install_fixtures.py | 2 +- .../setup/page/setup_wizard/sample_data.py | 117 ++++++++++++++++ .../setup/page/setup_wizard/setup_wizard.js | 80 ++++++++++- .../setup/page/setup_wizard/setup_wizard.py | 126 ++++++++++++++++-- .../page/setup_wizard/test_setup_data.py | 11 ++ erpnext/startup/notifications.py | 1 + 9 files changed, 342 insertions(+), 30 deletions(-) create mode 100644 erpnext/setup/page/setup_wizard/sample_data.py diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 683734b8799..f1d822a89c5 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -49,7 +49,7 @@ class Account(Document): self.root_type = par.root_type def validate_root_details(self): - #does not exists parent + # does not exists parent if frappe.db.exists("Account", self.name): if not frappe.db.get_value("Account", self.name, "parent_account"): throw(_("Root cannot be edited.")) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py index a2ba72dba60..bffa8e6d74a 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py @@ -4,10 +4,9 @@ from __future__ import unicode_literals from frappe.model.document import Document -from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax +from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \ + import valdiate_taxes_and_charges_template class PurchaseTaxesandChargesTemplate(Document): def validate(self): - for tax in self.get("taxes"): - validate_taxes_and_charges(tax) - validate_inclusive_tax(tax, self) + valdiate_taxes_and_charges_template(self) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py index 6721bd89e17..b36287b6914 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py @@ -5,21 +5,25 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax +from frappe.utils.nestedset import get_root_of class SalesTaxesandChargesTemplate(Document): def validate(self): - if self.is_default == 1: - frappe.db.sql("""update `tabSales Taxes and Charges Template` - set is_default = 0 - where ifnull(is_default,0) = 1 - and name != %s and company = %s""", - (self.name, self.company)) + valdiate_taxes_and_charges_template(self) - # at least one territory - self.validate_table_has_rows("territories") +def valdiate_taxes_and_charges_template(doc): + if not doc.is_default and not frappe.get_all(doc.doctype, filters={"is_default": 1}): + doc.is_default = 1 - for tax in self.get("taxes"): - validate_taxes_and_charges(tax) - validate_inclusive_tax(tax, self) + if doc.is_default == 1: + frappe.db.sql("""update `tab{0}` set is_default = 0 + where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype), + (doc.name, doc.company)) + if doc.meta.get_field("territories"): + if not doc.territories: + doc.append("territories", {"territory": get_root_of("Territory") }) + for tax in doc.get("taxes"): + validate_taxes_and_charges(tax) + validate_inclusive_tax(tax, doc) diff --git a/erpnext/setup/page/setup_wizard/install_fixtures.py b/erpnext/setup/page/setup_wizard/install_fixtures.py index 629c06f6db3..6265e4a36ce 100644 --- a/erpnext/setup/page/setup_wizard/install_fixtures.py +++ b/erpnext/setup/page/setup_wizard/install_fixtures.py @@ -183,4 +183,4 @@ def install(country=None): parent_link_field = ("parent_" + scrub(doc.doctype)) if doc.meta.get_field(parent_link_field) and not doc.get(parent_link_field): doc.flags.ignore_mandatory = True - doc.insert() + doc.insert(ignore_permissions=True) diff --git a/erpnext/setup/page/setup_wizard/sample_data.py b/erpnext/setup/page/setup_wizard/sample_data.py new file mode 100644 index 00000000000..f7fb73b4461 --- /dev/null +++ b/erpnext/setup/page/setup_wizard/sample_data.py @@ -0,0 +1,117 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.utils.make_random import add_random_children, get_random +import frappe.utils + +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"}) + + if selling_items: + for i in range(3): + make_opportunity(selling_items) + make_quote(selling_items) + + make_projects() + + if buying_items: + make_material_request(buying_items) + + frappe.db.commit() + +def make_opportunity(selling_items): + b = frappe.get_doc({ + "doctype": "Opportunity", + "enquiry_from": "Customer", + "customer": get_random("Customer"), + "enquiry_type": "Sales", + "with_items": 1 + }) + + add_random_children(b, "items", rows=len(selling_items), randomize = { + "qty": (1, 5), + "item_code": ("Item", {"is_sales_item": "Yes"}) + }, unique="item_code") + + b.insert(ignore_permissions=True) + + b.add_comment("This is a dummy record") + +def make_quote(selling_items): + qtn = frappe.get_doc({ + "doctype": "Quotation", + "quotation_to": "Customer", + "customer": get_random("Customer"), + "order_type": "Sales" + }) + + add_random_children(qtn, "items", rows=len(selling_items), randomize = { + "qty": (1, 5), + "item_code": ("Item", {"is_sales_item": "Yes"}) + }, unique="item_code") + + qtn.insert(ignore_permissions=True) + + qtn.add_comment("This is a dummy record") + +def make_material_request(buying_items): + for i in buying_items: + mr = frappe.get_doc({ + "doctype": "Material Request", + "material_request_type": "Purchase", + "items": [{ + "schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7), + "item_code": i.name, + "qty": 10 + }] + }) + mr.insert() + mr.submit() + + mr.add_comment("This is a dummy record") + + +def make_issue(): + pass + +def make_projects(): + project = frappe.get_doc({ + "doctype": "Project", + "project_name": "ERPNext Implementation", + }) + current_date = frappe.utils.nowdate() + project.set("tasks", [ + { + "title": "Explore ERPNext", + "start_date": frappe.utils.add_days(current_date, 1), + "end_date": frappe.utils.add_days(current_date, 2) + }, + { + "title": "Run Sales Cycle", + "start_date": frappe.utils.add_days(current_date, 2), + "end_date": frappe.utils.add_days(current_date, 3) + }, + { + "title": "Run Billing Cycle", + "start_date": frappe.utils.add_days(current_date, 3), + "end_date": frappe.utils.add_days(current_date, 4) + }, + { + "title": "Run Purchase Cycle", + "start_date": frappe.utils.add_days(current_date, 4), + "end_date": frappe.utils.add_days(current_date, 5) + }, + { + "title": "Go Live!", + "start_date": frappe.utils.add_days(current_date, 5), + "end_date": frappe.utils.add_days(current_date, 6) + }]) + + project.insert(ignore_permissions=True) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.js b/erpnext/setup/page/setup_wizard/setup_wizard.js index d521adab258..b38bd1c3730 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.js +++ b/erpnext/setup/page/setup_wizard/setup_wizard.js @@ -25,6 +25,7 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) { erpnext.wiz.user.slide, erpnext.wiz.org.slide, erpnext.wiz.branding.slide, + erpnext.wiz.users.slide, erpnext.wiz.taxes.slide, erpnext.wiz.customers.slide, erpnext.wiz.suppliers.slide, @@ -137,7 +138,7 @@ erpnext.wiz.WizardSlide = Class.extend({ }); this.form.make(); } else { - $(this.body).html(this.html) + $(this.body).html(this.html); } if(this.id > 0) { @@ -412,11 +413,30 @@ $.extend(erpnext.wiz, { onload: function(slide) { erpnext.wiz.org.load_chart_of_accounts(slide); erpnext.wiz.org.bind_events(slide); + erpnext.wiz.org.set_fy_dates(slide); }, css_class: "single-column" }, + set_fy_dates: function(slide) { + var country = slide.wiz.get_values().country; + + if(country) { + var fy = erpnext.wiz.fiscal_years[country]; + var current_year = moment(new Date()).year(); + var next_year = current_year + 1; + if(!fy) { + fy = ["01-01", "12-31"]; + next_year = current_year; + } + + slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]); + slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]); + } + + }, + load_chart_of_accounts: function(slide) { var country = slide.wiz.get_values().country; @@ -486,11 +506,41 @@ $.extend(erpnext.wiz, { }, }, + users: { + slide: { + icon: "icon-money", + "title": __("Add Users"), + "help": __("Add users to your organization"), + "fields": [], + before_load: function(slide) { + slide.fields = []; + for(var i=1; i<5; i++) { + slide.fields = slide.fields.concat([ + {fieldtype:"Section Break"}, + {fieldtype:"Data", fieldname:"user_fullname_"+ i, + label:__("Full Name")}, + {fieldtype:"Data", fieldname:"user_email_" + i, + label:__("Email ID"), placeholder:__("user@example.com"), + options: "Email"}, + {fieldtype:"Column Break"}, + {fieldtype: "Check", fieldname: "user_sales_" + i, + label:__("Sales"), default: 1}, + {fieldtype: "Check", fieldname: "user_purchaser_" + i, + label:__("Purchaser"), default: 1}, + {fieldtype: "Check", fieldname: "user_accountant_" + i, + label:__("Accountant"), default: 1}, + ]); + } + }, + css_class: "two-column" + }, + }, + taxes: { slide: { icon: "icon-money", "title": __("Add Taxes"), - "help": __("List your tax heads (e.g. VAT, Excise; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."), + "help": __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."), "fields": [], before_load: function(slide) { slide.fields = []; @@ -526,6 +576,7 @@ $.extend(erpnext.wiz, { label:__("Contact Name") + " " + i, placeholder:__("Contact Name")} ]) } + slide.fields[1].reqd = 1; }, css_class: "two-column" }, @@ -549,6 +600,7 @@ $.extend(erpnext.wiz, { label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}, ]) } + slide.fields[1].reqd = 1; }, css_class: "two-column" }, @@ -578,9 +630,11 @@ $.extend(erpnext.wiz, { {fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1}, {fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")}, {fieldtype:"Column Break"}, + {fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")}, {fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image")}, ]) } + slide.fields[1].reqd = 1; }, css_class: "two-column" }, @@ -627,3 +681,25 @@ $.extend(erpnext.wiz, { }, }); +// Source: https://en.wikipedia.org/wiki/Fiscal_year +// default 1st Jan - 31st Dec + +erpnext.wiz.fiscal_years = { + "Afghanistan": ["12-20", "12-21"], + "Australia": ["07-01", "06-30"], + "Bangladesh": ["07-01", "06-30"], + "Canada": ["04-01", "03-31"], + "Costa Rica": ["10-01", "09-30"], + "Egypt": ["07-01", "06-30"], + "Hong Kong": ["04-01", "03-31"], + "India": ["04-01", "03-31"], + "Iran": ["06-23", "06-22"], + "Italy": ["07-01", "06-30"], + "Myanmar": ["04-01", "03-31"], + "New Zealand": ["04-01", "03-31"], + "Pakistan": ["07-01", "06-30"], + "Singapore": ["04-01", "03-31"], + "South Africa": ["03-01", "02-28"], + "Thailand": ["10-01", "09-30"], + "United Kingdom": ["04-01", "03-31"], +} diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 4bb01d48d4d..f802694d602 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe, json +import frappe, json, copy from frappe.utils import cstr, flt, getdate from frappe import _ @@ -13,6 +13,7 @@ from frappe.geo.country_info import get_country_info from frappe.utils.nestedset import get_root_of from .default_website import website_maker import install_fixtures +from .sample_data import make_sample_data @frappe.whitelist() def setup_account(args=None): @@ -38,6 +39,9 @@ def setup_account(args=None): create_fiscal_year_and_company(args) frappe.local.message_log = [] + create_users(args) + frappe.local.message_log = [] + set_defaults(args) frappe.local.message_log = [] @@ -81,6 +85,7 @@ def setup_account(args=None): frappe.clear_cache() + make_sample_data() except: if args: traceback = frappe.get_traceback() @@ -297,21 +302,45 @@ def create_taxes(args): tax_group = frappe.db.get_value("Account", {"company": args.get("company_name"), "is_group": 1, "account_type": "Tax", "root_type": "Liability"}) if tax_group: - frappe.get_doc({ - "doctype":"Account", - "company": args.get("company_name").strip(), - "parent_account": tax_group, - "account_name": args.get("tax_" + str(i)), - "is_group": 0, - "report_type": "Balance Sheet", - "account_type": "Tax", - "tax_rate": flt(tax_rate) if tax_rate else None - }).insert() + account = make_tax_head(args, i, tax_group, tax_rate) + make_sales_and_purchase_tax_templates(account) + except frappe.NameError, e: if e.args[2][0]==1062: pass else: raise +def make_tax_head(args, i, tax_group, tax_rate): + return frappe.get_doc({ + "doctype":"Account", + "company": args.get("company_name").strip(), + "parent_account": tax_group, + "account_name": args.get("tax_" + str(i)), + "is_group": 0, + "report_type": "Balance Sheet", + "account_type": "Tax", + "tax_rate": flt(tax_rate) if tax_rate else None + }).insert(ignore_permissions=True) + +def make_sales_and_purchase_tax_templates(account): + doc = { + "doctype": "Sales Taxes and Charges Template", + "title": account.name, + "taxes": [{ + "category": "Valuation and Total", + "charge_type": "On Net Total", + "account_head": account.name, + "description": "{0} @ {1}".format(account.account_name, account.tax_rate), + "rate": account.tax_rate + }] + } + + # Sales + frappe.get_doc(copy.deepcopy(doc)).insert() + + # Purchase + doc["doctype"] = "Purchase Taxes and Charges Template" + frappe.get_doc(copy.deepcopy(doc)).insert() def create_items(args): for i in xrange(1,6): @@ -349,9 +378,30 @@ def create_items(args): filename, filetype, content = item_image fileurl = save_file(filename, content, "Item", item, decode=True).file_url frappe.db.set_value("Item", item, "image", fileurl) + + if args.get("item_price_" + str(i)): + item_price = flt(args.get("item_price_" + str(i))) + + if is_sales_item: + price_list_name = frappe.db.get_value("Price List", {"selling": 1}) + make_item_price(item, price_list_name, item_price) + + if is_purchase_item: + price_list_name = frappe.db.get_value("Price List", {"buying": 1}) + make_item_price(item, price_list_name, item_price) + except frappe.NameError: pass +def make_item_price(item, price_list_name, item_price): + frappe.get_doc({ + "doctype": "Item Price", + "price_list": price_list_name, + "item_code": item, + "price_list_rate": item_price + }).insert() + + def create_customers(args): for i in xrange(1,6): customer = args.get("customer_" + str(i)) @@ -451,6 +501,60 @@ def login_as_first_user(args): if args.get("email") and hasattr(frappe.local, "login_manager"): frappe.local.login_manager.login_as(args.get("email")) +def create_users(args): + # create employee for self + emp = frappe.get_doc({ + "doctype": "Employee", + "full_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])), + "user_id": frappe.session.user, + "status": "Active", + "company": args.get("company_name") + }) + emp.flags.ignore_mandatory = True + emp.insert(ignore_permissions = True) + + for i in xrange(1,5): + email = args.get("user_email_" + str(i)) + fullname = args.get("user_fullname_" + str(i)) + if email: + if not fullname: + fullname = email.split("@")[0] + + parts = fullname.split(" ", 1) + + user = frappe.get_doc({ + "doctype": "User", + "email": email, + "first_name": parts[0], + "last_name": parts[1] if len(parts) > 1 else "", + "enabled": 1, + "user_type": "System User" + }) + + # default roles + user.append_roles("Projects User", "Stock User", "Support Team") + + if args.get("user_sales_" + str(i)): + user.append_roles("Sales User", "Sales Manager", "Accounts User") + if args.get("user_purchaser_" + str(i)): + user.append_roles("Purchase User", "Purchase Manager", "Accounts User") + if args.get("user_accountant_" + str(i)): + user.append_roles("Accounts Manager", "Accounts User") + + user.flags.delay_emails = True + user.insert(ignore_permissions=True) + + # create employee + emp = frappe.get_doc({ + "doctype": "Employee", + "full_name": fullname, + "user_id": user.name, + "status": "Active", + "company": args.get("company_name") + }) + emp.flags.ignore_mandatory = True + emp.insert(ignore_permissions = True) + @frappe.whitelist() def load_messages(language): frappe.clear_cache() diff --git a/erpnext/setup/page/setup_wizard/test_setup_data.py b/erpnext/setup/page/setup_wizard/test_setup_data.py index 43fc2cf7824..de54a1d16b5 100644 --- a/erpnext/setup/page/setup_wizard/test_setup_data.py +++ b/erpnext/setup/page/setup_wizard/test_setup_data.py @@ -51,4 +51,15 @@ args = { "timezone": "America/New_York", "password": "password", "email": "test@erpnext.com", +"user_email_1": "testsetup1@example.com", +"user_fullname_1": "test setup user", +"user_sales_1": 1, +"user_purchaser_1": 1, +"user_accountant_1": 1, +"user_email_1": "testsetup2@example.com", +"user_fullname_1": "test setup user", +"user_sales_2": 1, +"user_purchaser_2": 0, +"user_accountant_2": 0 + } diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py index 4190f2debdb..d06537066e7 100644 --- a/erpnext/startup/notifications.py +++ b/erpnext/startup/notifications.py @@ -10,6 +10,7 @@ def get_notification_config(): "Issue": {"status": "Open"}, "Warranty Claim": {"status": "Open"}, "Task": {"status": "Open"}, + "Project": {"status": "Open"}, "Lead": {"status": "Open"}, "Contact": {"status": "Open"}, "Opportunity": {"status": "Open"}, From cc431716edece562d5cac01c77cae6dc55a412b4 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 22 Jul 2015 12:13:47 +0530 Subject: [PATCH 02/11] Fetch Template Bom if no BOM is set against Item Variant in Production Order --- .../doctype/production_order/production_order.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 26af40a4414..93ce5e16f64 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -335,12 +335,15 @@ def get_item_details(item): res = frappe.db.sql("""select stock_uom, description from `tabItem` where (ifnull(end_of_life, "0000-00-00")="0000-00-00" or end_of_life > now()) and name=%s""", item, as_dict=1) - if not res: return {} res = res[0] res["bom_no"] = frappe.db.get_value("BOM", filters={"item": item, "is_default": 1}) + if not res["bom_no"]: + variant_of= frappe.db.get_value("Item", item, "variant_of") + if variant_of: + res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1}) return res @frappe.whitelist() From 098760f0e20bc3a2c9823b64bbaaeaee541a9c98 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 Jul 2015 11:28:21 +0530 Subject: [PATCH 03/11] [fix] SMS status and log --- erpnext/config/crm.py | 5 ++ erpnext/config/selling.py | 5 ++ .../doctype/sms_settings/sms_settings.py | 38 +++++----- .../utilities/doctype/sms_log/sms_log.json | 69 ++++++++++++------- 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py index 3a7ab181431..d7a6b2e2be3 100644 --- a/erpnext/config/crm.py +++ b/erpnext/config/crm.py @@ -42,6 +42,11 @@ def get_data(): "name": "SMS Center", "description":_("Send mass SMS to your contacts"), }, + { + "type": "doctype", + "name": "SMS Log", + "description":_("Logs for maintaining sms delivery status"), + } ] }, { diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 543396462df..62dfe2326db 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -48,6 +48,11 @@ def get_data(): "name": "SMS Center", "description":_("Send mass SMS to your contacts"), }, + { + "type": "doctype", + "name": "SMS Log", + "description":_("Logs for maintaining sms delivery status"), + }, { "type": "doctype", "name": "Newsletter", diff --git a/erpnext/setup/doctype/sms_settings/sms_settings.py b/erpnext/setup/doctype/sms_settings/sms_settings.py index 1403ee5cbdf..909986347f3 100644 --- a/erpnext/setup/doctype/sms_settings/sms_settings.py +++ b/erpnext/setup/doctype/sms_settings/sms_settings.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _, throw, msgprint -from frappe.utils import cstr, nowdate +from frappe.utils import nowdate from frappe.model.document import Document @@ -63,8 +63,7 @@ def send_sms(receiver_list, msg, sender_name = ''): } if frappe.db.get_value('SMS Settings', None, 'sms_gateway_url'): - ret = send_via_gateway(arg) - msgprint(ret) + send_via_gateway(arg) else: msgprint(_("Please Update SMS Settings")) @@ -74,12 +73,17 @@ def send_via_gateway(arg): for d in ss.get("parameters"): args[d.parameter] = d.value - resp = [] + success_list = [] for d in arg.get('receiver_list'): args[ss.receiver_parameter] = d - resp.append(send_request(ss.sms_gateway_url, args)) + status = send_request(ss.sms_gateway_url, args) + if status == 200: + success_list.append(d) - return resp + if len(success_list) > 0: + args.update(arg) + create_sms_log(args, success_list) + frappe.msgprint(_("SMS sent to following numbers: {0}").format("\n" + "\n".join(success_list))) # Send Request # ========================================================= @@ -90,11 +94,8 @@ def send_request(gateway_url, args): headers = {} headers['Accept'] = "text/plain, text/html, */*" conn.request('GET', api_url + urllib.urlencode(args), headers = headers) # send request - resp = conn.getresponse() # get response - resp = resp.read() - if resp.status==200: - create_sms_log() - return resp + resp = conn.getresponse() # get response + return resp.status # Split gateway url to server and api url # ========================================================= @@ -109,12 +110,13 @@ def scrub_gateway_url(url): # Create SMS Log # ========================================================= -def create_sms_log(arg, sent_sms): - sl = frappe.get_doc('SMS Log') - sl.sender_name = arg['sender_name'] +def create_sms_log(args, sent_to): + sl = frappe.new_doc('SMS Log') + sl.sender_name = args['sender_name'] sl.sent_on = nowdate() - sl.receiver_list = cstr(arg['receiver_list']) - sl.message = arg['message'] - sl.no_of_requested_sms = len(arg['receiver_list']) - sl.no_of_sent_sms = sent_sms + sl.message = args['message'] + sl.no_of_requested_sms = len(args['receiver_list']) + sl.requested_numbers = "\n".join(args['receiver_list']) + sl.no_of_sent_sms = len(sent_to) + sl.sent_to = "\n".join(sent_to) sl.save() diff --git a/erpnext/utilities/doctype/sms_log/sms_log.json b/erpnext/utilities/doctype/sms_log/sms_log.json index e3c77416763..ba88c622c3f 100644 --- a/erpnext/utilities/doctype/sms_log/sms_log.json +++ b/erpnext/utilities/doctype/sms_log/sms_log.json @@ -1,32 +1,58 @@ { "autoname": "SMSLOG/.########", - "creation": "2012-03-27 14:36:47.000000", + "creation": "2012-03-27 14:36:47", "docstatus": 0, "doctype": "DocType", "fields": [ - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "permlevel": 0, - "width": "50%" - }, { "fieldname": "sender_name", "fieldtype": "Data", "label": "Sender Name", - "permlevel": 0 + "permlevel": 0, + "read_only": 1 }, { "fieldname": "sent_on", "fieldtype": "Date", "label": "Sent On", - "permlevel": 0 + "permlevel": 0, + "read_only": 1 }, { - "fieldname": "receiver_list", + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0, + "read_only": 0, + "width": "50%" + }, + { + "fieldname": "message", "fieldtype": "Small Text", - "label": "Receiver List", - "permlevel": 0 + "label": "Message", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "sec_break1", + "fieldtype": "Section Break", + "options": "Simple", + "permlevel": 0, + "precision": "", + "read_only": 0 + }, + { + "fieldname": "no_of_requested_sms", + "fieldtype": "Int", + "label": "No of Requested SMS", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "requested_numbers", + "fieldtype": "Small Text", + "label": "Requested Numbers", + "permlevel": 0, + "read_only": 1 }, { "fieldname": "column_break1", @@ -34,28 +60,25 @@ "permlevel": 0, "width": "50%" }, - { - "fieldname": "no_of_requested_sms", - "fieldtype": "Int", - "label": "No of Requested SMS", - "permlevel": 0 - }, { "fieldname": "no_of_sent_sms", "fieldtype": "Int", "label": "No of Sent SMS", - "permlevel": 0 + "permlevel": 0, + "read_only": 1 }, { - "fieldname": "message", + "fieldname": "sent_to", "fieldtype": "Small Text", - "label": "Message", - "permlevel": 0 + "label": "Sent To", + "permlevel": 0, + "precision": "", + "read_only": 1 } ], "icon": "icon-mobile-phone", "idx": 1, - "modified": "2013-12-20 19:24:35.000000", + "modified": "2015-07-22 11:53:25.998578", "modified_by": "Administrator", "module": "Utilities", "name": "SMS Log", From d900e12ae74a36b3e0f47fef4705969f97a9d6cb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 Jul 2015 12:21:55 +0530 Subject: [PATCH 04/11] Change log for sms --- erpnext/change_log/current/sms.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 erpnext/change_log/current/sms.md diff --git a/erpnext/change_log/current/sms.md b/erpnext/change_log/current/sms.md new file mode 100644 index 00000000000..bac293f20f1 --- /dev/null +++ b/erpnext/change_log/current/sms.md @@ -0,0 +1 @@ +- Now system will give SMS delivery message and maintain a log \ No newline at end of file From 8b48ceab8c5b830a674a1f440d0394eb84444bd7 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 22 Jul 2015 12:42:21 +0530 Subject: [PATCH 05/11] Removed logic for get_item_details in Production Planning Tool, Used get_item_details function of Production Order instead --- .../production_planning_tool.py | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) 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 86a14d8c213..271abac10e5 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -9,6 +9,7 @@ from frappe import msgprint, _ from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no +from erpnext.manufacturing.doctype.production_order.production_order import get_item_details class ProductionPlanningTool(Document): def __init__(self, arg1, arg2=None): @@ -27,16 +28,7 @@ class ProductionPlanningTool(Document): return ret def get_item_details(self, item_code): - """ Pull other item details from item master""" - - item = frappe.db.sql("""select description, stock_uom, default_bom - from `tabItem` where name = %s""", item_code, as_dict =1) - ret = { - 'description' : item and item[0]['description'], - 'stock_uom' : item and item[0]['stock_uom'], - 'bom_no' : item and item[0]['default_bom'] - } - return ret + return get_item_details(item_code) def clear_so_table(self): self.set('sales_orders', []) @@ -142,15 +134,14 @@ class ProductionPlanningTool(Document): self.clear_item_table() for p in items: - item_details = frappe.db.sql("""select description, stock_uom, default_bom - from tabItem where name=%s""", p['item_code']) + item_details = get_item_details(p['item_code']) pi = self.append('items', {}) pi.sales_order = p['parent'] pi.warehouse = p['warehouse'] pi.item_code = p['item_code'] - pi.description = item_details and item_details[0][0] or '' - pi.stock_uom = item_details and item_details[0][1] or '' - pi.bom_no = item_details and item_details[0][2] or '' + pi.description = item_details and item_details.description or '' + pi.stock_uom = item_details and item_details.stock_uom or '' + pi.bom_no = item_details and item_details.bom_no or '' pi.so_pending_qty = flt(p['pending_qty']) pi.planned_qty = flt(p['pending_qty']) From 05d81746961b799fbe0cfc8eba54372254b050e3 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 22 Jul 2015 16:31:34 +0530 Subject: [PATCH 06/11] Fetch items from Packing List if Exists in Sales Order while raising Material Request against SO --- erpnext/selling/doctype/sales_order/sales_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index e8a772a2163..d45fbba4867 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -272,6 +272,10 @@ def make_material_request(source_name, target_doc=None): def postprocess(source, doc): 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", @@ -279,7 +283,7 @@ def make_material_request(source_name, target_doc=None): "docstatus": ["=", 1] } }, - "Sales Order Item": { + item_table: { "doctype": "Material Request Item", "field_map": { "parent": "sales_order_no", From d0387f41dfd7578d246346f89079c40aba4d38fa Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 22 Jul 2015 18:44:47 +0530 Subject: [PATCH 07/11] Opening Balance row added to Stock Ledger Report --- .../stock/report/stock_ledger/stock_ledger.py | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 0651ae89610..c2c6d1ae7f5 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -9,8 +9,13 @@ def execute(filters=None): columns = get_columns() sl_entries = get_stock_ledger_entries(filters) item_details = get_item_details(filters) - + opening_row = get_opening_balance(filters, columns) + data = [] + + if opening_row: + data.append(opening_row) + for sle in sl_entries: item_detail = item_details[sle.item_code] @@ -20,7 +25,7 @@ def execute(filters=None): (sle.incoming_rate if sle.actual_qty > 0 else 0.0), sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no, sle.batch_no, sle.serial_no, sle.company]) - + return columns, data def get_columns(): @@ -40,7 +45,7 @@ def get_stock_ledger_entries(filters): where company = %(company)s and posting_date between %(from_date)s and %(to_date)s {sle_conditions} - order by posting_date desc, posting_time desc, name desc"""\ + order by posting_date asc, posting_time asc, name asc"""\ .format(sle_conditions=get_sle_conditions(filters)), filters, as_dict=1) def get_item_details(filters): @@ -73,3 +78,22 @@ def get_sle_conditions(filters): conditions.append("voucher_no=%(voucher_no)s") return "and {}".format(" and ".join(conditions)) if conditions else "" + +def get_opening_balance(filters, columns): + if not (filters.item_code and filters.warehouse and filters.from_date): + return + + from erpnext.stock.stock_ledger import get_previous_sle + last_entry = get_previous_sle({ + "item_code": filters.item_code, + "warehouse": filters.warehouse, + "posting_date": filters.from_date, + "posting_time": "00:00:00" + }) + + row = [""]*len(columns) + row[1] = _("'Opening'") + for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')): + row[i] = last_entry.get(v, 0) + + return row \ No newline at end of file From bb274fce2e30dc7524944f176da54eb9179c2e10 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 22 Jul 2015 18:54:32 +0530 Subject: [PATCH 08/11] [cleanup] Party Type deleted --- erpnext/contacts/__init__.py | 0 erpnext/contacts/doctype/__init__.py | 0 .../contacts/doctype/party_type/__init__.py | 0 .../doctype/party_type/party_type.json | 92 ------------------- .../contacts/doctype/party_type/party_type.py | 9 -- erpnext/modules.txt | 1 - erpnext/patches.txt | 1 + 7 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 erpnext/contacts/__init__.py delete mode 100644 erpnext/contacts/doctype/__init__.py delete mode 100644 erpnext/contacts/doctype/party_type/__init__.py delete mode 100644 erpnext/contacts/doctype/party_type/party_type.json delete mode 100644 erpnext/contacts/doctype/party_type/party_type.py diff --git a/erpnext/contacts/__init__.py b/erpnext/contacts/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/contacts/doctype/__init__.py b/erpnext/contacts/doctype/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/contacts/doctype/party_type/__init__.py b/erpnext/contacts/doctype/party_type/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/contacts/doctype/party_type/party_type.json b/erpnext/contacts/doctype/party_type/party_type.json deleted file mode 100644 index 19ffefba7c3..00000000000 --- a/erpnext/contacts/doctype/party_type/party_type.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "allow_rename": 1, - "autoname": "field:party_type_name", - "creation": "2014-04-07 12:32:18.010384", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Master", - "fields": [ - { - "fieldname": "party_type_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Party Type Name", - "permlevel": 0, - "reqd": 1 - }, - { - "fieldname": "parent_party_type", - "fieldtype": "Link", - "label": "Parent Party Type", - "options": "Party Type", - "permlevel": 0 - }, - { - "default": "Yes", - "fieldname": "allow_children", - "fieldtype": "Select", - "label": "Allow Children", - "options": "Yes\nNo", - "permlevel": 0 - }, - { - "fieldname": "default_price_list", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Price List", - "options": "Price List", - "permlevel": 0 - }, - { - "fieldname": "lft", - "fieldtype": "Int", - "hidden": 1, - "label": "LFT", - "permlevel": 0, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "rgt", - "fieldtype": "Int", - "hidden": 1, - "label": "RGT", - "permlevel": 0, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "old_parent", - "fieldtype": "Data", - "hidden": 1, - "label": "Old Parent", - "permlevel": 0, - "read_only": 1 - } - ], - "modified": "2015-02-05 05:11:42.046004", - "modified_by": "Administrator", - "module": "Contacts", - "name": "Party Type", - "owner": "Administrator", - "permissions": [ - { - "apply_user_permissions": 1, - "create": 1, - "permlevel": 0, - "read": 1, - "role": "Sales User", - "share": 1, - "write": 1 - }, - { - "apply_user_permissions": 1, - "create": 1, - "permlevel": 0, - "read": 1, - "role": "Purchase User", - "share": 1, - "write": 1 - } - ] -} \ No newline at end of file diff --git a/erpnext/contacts/doctype/party_type/party_type.py b/erpnext/contacts/doctype/party_type/party_type.py deleted file mode 100644 index d21216f1611..00000000000 --- a/erpnext/contacts/doctype/party_type/party_type.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils.nestedset import NestedSet - -class PartyType(NestedSet): - nsm_parent_field = 'parent_party_type'; diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 67c856db7fb..dfca2f27c5b 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -9,6 +9,5 @@ Manufacturing Stock Support Utilities -Contacts Shopping Cart Hub Node diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 269dcbaf9c3..c7355073c41 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -177,3 +177,4 @@ erpnext.patches.v5_1.track_operations 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 From 7cfa5f0508be1c5247d03c61e621db0778b8f01a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 23 Jul 2015 12:39:44 +0530 Subject: [PATCH 09/11] [fix] contact name in setup wizard --- .../setup/page/setup_wizard/setup_wizard.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 4bb01d48d4d..141b914d91f 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -367,13 +367,8 @@ def create_customers(args): }).insert() if args.get("customer_contact_" + str(i)): - contact = args.get("customer_contact_" + str(i)).split(" ") - frappe.get_doc({ - "doctype":"Contact", - "customer": customer, - "first_name":contact[0], - "last_name": len(contact) > 1 and contact[1] or "" - }).insert() + create_contact(args.get("customer_contact_" + str(i)), + "customer", customer) except frappe.NameError: pass @@ -390,16 +385,21 @@ def create_suppliers(args): }).insert() if args.get("supplier_contact_" + str(i)): - contact = args.get("supplier_contact_" + str(i)).split(" ") - frappe.get_doc({ - "doctype":"Contact", - "supplier": supplier, - "first_name":contact[0], - "last_name": len(contact) > 1 and contact[1] or "" - }).insert() + create_contact(args.get("supplier_contact_" + str(i)), + "supplier", supplier) except frappe.NameError: pass +def create_contact(contact, party_type, party): + """Create contact based on given contact name""" + contact = contact.strip().split(" ") + + frappe.get_doc({ + "doctype":"Contact", + party_type: party, + "first_name":contact[0], + "last_name": len(contact) > 1 and contact[1] or "" + }).insert() def create_letter_head(args): if args.get("attach_letterhead"): From 13df8a40ef209113f0e18b233f202d68f3bc3395 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Thu, 23 Jul 2015 12:46:59 +0530 Subject: [PATCH 10/11] Validation added to prevent user to Manage Variants if Item Template is Unsaved. Prevented message stating variants updated while saving item template if there are no variants against that item Template --- erpnext/stock/doctype/item/item.js | 8 ++++++-- erpnext/stock/doctype/item/item.py | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 58b1adb8db7..3bd5657d597 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -86,8 +86,12 @@ frappe.ui.form.on("Item", { }, manage_variants: function(frm) { - frappe.route_options = {"item_code": frm.doc.name }; - frappe.set_route("List", "Manage Variants"); + if (cur_frm.doc.__unsaved==1) { + frappe.throw(__("You have unsaved changes. Please save.")) + } else { + frappe.route_options = {"item_code": frm.doc.name }; + frappe.set_route("List", "Manage Variants"); + } } }); diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a2e0ade50c9..d3d8e9c6a18 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -325,7 +325,8 @@ class Item(WebsiteGenerator): for d in variants: update_variant(self.name, d) updated.append(d.item_code) - frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated))) + 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"): From 08fb19ac8c0979ecada0c7253df3fb6a6a327fff Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 23 Jul 2015 12:18:46 +0530 Subject: [PATCH 11/11] Validation on Account for assigning budget --- .../doctype/cost_center/cost_center.js | 2 +- .../doctype/cost_center/cost_center.py | 57 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 6946bcb1064..fb649e6d1b3 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -17,7 +17,7 @@ erpnext.accounts.CostCenterController = frappe.ui.form.Controller.extend({ return { filters:[ ['Account', 'company', '=', me.frm.doc.company], - ['Account', 'report_type', '=', 'Profit and Loss'], + ['Account', 'root_type', '=', 'Expense'], ['Account', 'is_group', '=', '0'], ] } diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index f26c80ba59f..0f51a00cd6b 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -3,9 +3,7 @@ from __future__ import unicode_literals import frappe - -from frappe import msgprint, _ - +from frappe import _ from frappe.utils.nestedset import NestedSet class CostCenter(NestedSet): @@ -14,18 +12,46 @@ class CostCenter(NestedSet): def autoname(self): self.name = self.cost_center_name.strip() + ' - ' + \ frappe.db.get_value("Company", self.company, "abbr") + + + def validate(self): + self.validate_mandatory() + self.validate_accounts() def validate_mandatory(self): if self.cost_center_name != self.company and not self.parent_cost_center: - msgprint(_("Please enter parent cost center"), raise_exception=1) + frappe.throw(_("Please enter parent cost center")) elif self.cost_center_name == self.company and self.parent_cost_center: - msgprint(_("Root cannot have a parent cost center"), raise_exception=1) + frappe.throw(_("Root cannot have a parent cost center")) + + def validate_accounts(self): + if self.is_group==1 and self.get("budgets"): + frappe.throw(_("Budget cannot be set for Group Cost Center")) + + check_acc_list = [] + for d in self.get('budgets'): + if d.account: + account_details = frappe.db.get_value("Account", d.account, + ["is_group", "company", "root_type"], as_dict=1) + if account_details.is_group: + frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account)) + elif account_details.company != self.company: + frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company)) + elif account_details.root_type != "Expense": + frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Expense account") + .format(d.account)) + + if [d.account, d.fiscal_year] in check_acc_list: + frappe.throw(_("Account {0} has been entered more than once for fiscal year {1}") + .format(d.account, d.fiscal_year)) + else: + check_acc_list.append([d.account, d.fiscal_year]) def convert_group_to_ledger(self): if self.check_if_child_exists(): - msgprint(_("Cannot convert Cost Center to ledger as it has child nodes"), raise_exception=1) + frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes")) elif self.check_gle_exists(): - msgprint(_("Cost Center with existing transactions can not be converted to ledger"), raise_exception=1) + frappe.throw(_("Cost Center with existing transactions can not be converted to ledger")) else: self.is_group = 0 self.save() @@ -33,7 +59,7 @@ class CostCenter(NestedSet): def convert_ledger_to_group(self): if self.check_gle_exists(): - msgprint(_("Cost Center with existing transactions can not be converted to group"), raise_exception=1) + frappe.throw(_("Cost Center with existing transactions can not be converted to group")) else: self.is_group = 1 self.save() @@ -46,21 +72,6 @@ class CostCenter(NestedSet): return frappe.db.sql("select name from `tabCost Center` where \ parent_cost_center = %s and docstatus != 2", self.name) - def validate_budget_details(self): - check_acc_list = [] - for d in self.get('budgets'): - if self.is_group==1: - msgprint(_("Budget cannot be set for Group Cost Centers"), raise_exception=1) - - if [d.account, d.fiscal_year] in check_acc_list: - msgprint(_("Account {0} has been entered more than once for fiscal year {1}").format(d.account, d.fiscal_year), raise_exception=1) - else: - check_acc_list.append([d.account, d.fiscal_year]) - - def validate(self): - self.validate_mandatory() - self.validate_budget_details() - def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided from erpnext.setup.doctype.company.company import get_name_with_abbr