From f31221d0d33cf186e7a7015ccb7de9ae66947067 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 9 Jul 2014 09:13:00 +0530 Subject: [PATCH 01/53] Changed `raise Exception` to `frappe.throw` --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 834865bcd6..5e29487b50 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -339,8 +339,8 @@ class SalesInvoice(SellingController): def validate_pos(self): if not self.cash_bank_account and flt(self.paid_amount): - msgprint(_("Cash or Bank Account is mandatory for making payment entry")) - raise Exception + frappe.throw(_("Cash or Bank Account is mandatory for making payment entry")) + if flt(self.paid_amount) + flt(self.write_off_amount) \ - flt(self.grand_total) > 1/(10**(self.precision("grand_total") + 1)): frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total""")) @@ -431,9 +431,8 @@ class SalesInvoice(SellingController): submitted = frappe.db.sql("""select name from `tabSales Order` where docstatus = 1 and name = %s""", d.sales_order) if not submitted: - msgprint(_("Sales Order {0} is not submitted").format(d.sales_order)) - raise Exception - + frappe.throw(_("Sales Order {0} is not submitted").format(d.sales_order)) + if d.delivery_note: submitted = frappe.db.sql("""select name from `tabDelivery Note` where docstatus = 1 and name = %s""", d.delivery_note) @@ -682,7 +681,7 @@ def manage_recurring_invoices(next_date=None, commit=True): if exception_list: exception_message = "\n\n".join([cstr(d) for d in exception_list]) - raise Exception, exception_message + frappe.throw(exception_message) def make_new_invoice(ref_wrapper, posting_date): from erpnext.accounts.utils import get_fiscal_year From 3f606b6c640c6c5195660572252a6439e44d6867 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 9 Jul 2014 09:27:02 +0530 Subject: [PATCH 02/53] Update project.py --- erpnext/projects/doctype/project/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index bce9c52688..4a14c55c3a 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -55,7 +55,7 @@ class Project(Document): "event_type": "Private", "ref_type": self.doctype, "ref_name": self.name - }).insert() + }).insert(ignore_permissions=True) def on_trash(self): delete_events(self.doctype, self.name) From 8adb21121691a1d8d772c7e150b7539025487f5b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 9 Jul 2014 11:09:33 +0530 Subject: [PATCH 03/53] Fixed patch: erpnext/patches/v4_1/fix_delivery_and_billing_status.py --- erpnext/patches.txt | 2 +- ...for_draft_so.py => fix_delivery_and_billing_status.py} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename erpnext/patches/v4_1/{fix_delivery_and_billing_status_for_draft_so.py => fix_delivery_and_billing_status.py} (80%) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1c2112d7b8..b7fc5e7768 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -67,4 +67,4 @@ erpnext.patches.v4_0.set_naming_series_property_setter erpnext.patches.v4_1.set_outgoing_email_footer erpnext.patches.v4_1.fix_jv_remarks erpnext.patches.v4_1.fix_sales_order_delivered_status -erpnext.patches.v4_1.fix_delivery_and_billing_status_for_draft_so \ No newline at end of file +erpnext.patches.v4_1.fix_delivery_and_billing_status diff --git a/erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py similarity index 80% rename from erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py rename to erpnext/patches/v4_1/fix_delivery_and_billing_status.py index 8d38338e04..2dbc825085 100644 --- a/erpnext/patches/v4_1/fix_delivery_and_billing_status_for_draft_so.py +++ b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py @@ -5,8 +5,8 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered' - where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and docstatus = 0""") + frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered' + where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""") - frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed' - where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and docstatus = 0""") \ No newline at end of file + frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed' + where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""") From 5e0de79c0db47aacb0fb374abc6550cb8f5e66c8 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 9 Jul 2014 11:08:59 +0530 Subject: [PATCH 04/53] Account validated in general ledger report. Fixed #1912 --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 0fa7e446fe..dd8052835e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -63,6 +63,8 @@ def get_gl_entries(filters): def get_conditions(filters): conditions = [] if filters.get("account"): + if not frappe.db.exists("Account", filters["account"]): + frappe.throw(_("Account {0} is not valid").format(filters["account"])) lft, rgt = frappe.db.get_value("Account", filters["account"], ["lft", "rgt"]) conditions.append("""account in (select name from tabAccount where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) From fcfa42d9927343c31a62d3b9f8c2620b9ad48fe1 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 9 Jul 2014 11:31:11 +0530 Subject: [PATCH 05/53] Minor fixes #1917 --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 9141697a77..9a3b3121f4 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -183,7 +183,7 @@ def get_pricing_rules(args): group_condition = _get_tree_conditions(parenttype) if group_condition: conditions += " and " + group_condition - + if not args.price_list: args.price_list = None conditions += " and ifnull(for_price_list, '') in (%(price_list)s, '')" if args.get("transaction_date"): From 8e4fcca2169078931db6fede3d3265d10e5865ea Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 9 Jul 2014 12:50:50 +0530 Subject: [PATCH 06/53] Get query fixes in address in contact listing. Fixes #1911 --- erpnext/buying/doctype/supplier/supplier.js | 6 ++++-- erpnext/selling/doctype/customer/customer.js | 6 ++++-- erpnext/setup/doctype/sales_partner/sales_partner.js | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 99d0e4e9f5..c2b29a4a17 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -69,7 +69,8 @@ cur_frm.cscript.make_address = function() { page_length: 5, new_doctype: "Address", get_query: function() { - return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where supplier='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_address desc" + return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where supplier='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc" }, as_dict: 1, no_results_message: __('No addresses created'), @@ -87,7 +88,8 @@ cur_frm.cscript.make_contact = function() { page_length: 5, new_doctype: "Contact", get_query: function() { - return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where supplier='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_contact desc" + return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where supplier='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc" }, as_dict: 1, no_results_message: __('No contacts created'), diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index e8407581d1..9a4a356d17 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -86,7 +86,8 @@ cur_frm.cscript.make_address = function() { page_length: 5, new_doctype: "Address", get_query: function() { - return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where customer='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_address desc" + return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where customer='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc" }, as_dict: 1, no_results_message: __('No addresses created'), @@ -104,7 +105,8 @@ cur_frm.cscript.make_contact = function() { page_length: 5, new_doctype: "Contact", get_query: function() { - return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where customer='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_contact desc" + return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where customer='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc" }, as_dict: 1, no_results_message: __('No contacts created'), diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js index 02af9f7276..64c4e2106c 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.js +++ b/erpnext/setup/doctype/sales_partner/sales_partner.js @@ -41,7 +41,8 @@ cur_frm.cscript.make_address = function() { frappe.set_route("Form", "Address", address.name); }, get_query: function() { - return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where sales_partner='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_address desc" + return "select name, address_type, address_line1, address_line2, city, state, country, pincode, fax, email_id, phone, is_primary_address, is_shipping_address from tabAddress where sales_partner='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_address desc" }, as_dict: 1, no_results_message: __('No addresses created'), @@ -64,7 +65,8 @@ cur_frm.cscript.make_contact = function() { frappe.set_route("Form", "Contact", contact.name); }, get_query: function() { - return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where sales_partner='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_contact desc" + return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where sales_partner='" + + cur_frm.doc.name.replace("'", "\\'") + "' and docstatus != 2 order by is_primary_contact desc" }, as_dict: 1, no_results_message: __('No contacts created'), From 7e848b19240f472535884b79d0d6a373f388533c Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 9 Jul 2014 12:54:56 +0530 Subject: [PATCH 07/53] Title mandatory in Note. Fixes #1913 --- erpnext/utilities/doctype/note/note.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/doctype/note/note.json b/erpnext/utilities/doctype/note/note.json index 2ee6d9ad4f..6cf756c3aa 100644 --- a/erpnext/utilities/doctype/note/note.json +++ b/erpnext/utilities/doctype/note/note.json @@ -12,7 +12,8 @@ "in_list_view": 1, "label": "Title", "permlevel": 0, - "print_hide": 1 + "print_hide": 1, + "reqd": 1 }, { "description": "Help: To link to another record in the system, use \"#Form/Note/[Note Name]\" as the Link URL. (don't use \"http://\")", @@ -48,7 +49,7 @@ ], "icon": "icon-file-text", "idx": 1, - "modified": "2014-05-27 03:49:13.934698", + "modified": "2014-07-09 12:54:11.897597", "modified_by": "Administrator", "module": "Utilities", "name": "Note", From 0c3bb75fd9dbe38ca687bbf71000fcbc326b9411 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 9 Jul 2014 12:59:56 +0530 Subject: [PATCH 08/53] Added error_report_email in hooks --- erpnext/hooks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1dde6386d2..df15916f7a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -7,6 +7,8 @@ app_icon = "icon-th" app_color = "#e74c3c" app_version = __version__ +error_report_email = "support@erpnext.com" + app_include_js = "assets/js/erpnext.min.js" app_include_css = "assets/css/erpnext.css" web_include_js = "assets/js/erpnext-web.min.js" From 48d3b54e02ccdaf0fe18736a9cd1de4c577e8fb5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 9 Jul 2014 13:15:03 +0530 Subject: [PATCH 09/53] Fixes in queries --- erpnext/controllers/queries.py | 100 ++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 16d27d47fc..2650c66ac2 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -32,34 +32,46 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select name, employee_name from `tabEmployee` where status = 'Active' and docstatus < 2 - and (%(key)s like "%(txt)s" - or employee_name like "%(txt)s") - %(mcond)s + and ({key} like %(txt)s + or employee_name like %(txt)s) + {mcond} order by - if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999), - if(locate("%(_txt)s", employee_name), locate("%(_txt)s", employee_name), 99999), + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999), name, employee_name - limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len}) + limit %(start)s, %(page_len)s""".format(**{ + 'key': searchfield, + 'mcond': get_match_cond(doctype) + }), { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace("%", ""), + 'start': start, + 'page_len': page_len + }) # searches for leads which are not converted def lead_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select name, lead_name, company_name from `tabLead` where docstatus < 2 and ifnull(status, '') != 'Converted' - and (%(key)s like "%(txt)s" - or lead_name like "%(txt)s" - or company_name like "%(txt)s") - %(mcond)s + and ({key} like %(txt)s + or lead_name like %(txt)s + or company_name like %(txt)s) + {mcond} order by - if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999), - if(locate("%(_txt)s", lead_name), locate("%(_txt)s", lead_name), 99999), - if(locate("%(_txt)s", company_name), locate("%(_txt)s", company_name), 99999), + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, lead_name), locate(%(_txt)s, lead_name), 99999), + if(locate(%(_txt)s, company_name), locate(%(_txt)s, company_name), 99999), name, lead_name - limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len}) + limit %(start)s, %(page_len)s""".format(**{ + 'key': searchfield, + 'mcond':get_match_cond(doctype) + }), { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace("%", ""), + 'start': start, + 'page_len': page_len + }) # searches for customer def customer_query(doctype, txt, searchfield, start, page_len, filters): @@ -72,19 +84,25 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): fields = ", ".join(fields) - return frappe.db.sql("""select %(field)s from `tabCustomer` + return frappe.db.sql("""select {fields} from `tabCustomer` where docstatus < 2 - and (%(key)s like "%(txt)s" - or customer_name like "%(txt)s") - %(mcond)s + and ({key} like %(txt)s + or customer_name like %(txt)s) + {mcond} order by - if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999), - if(locate("%(_txt)s", customer_name), locate("%(_txt)s", customer_name), 99999), + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, customer_name), locate(%(_txt)s, customer_name), 99999), name, customer_name - limit %(start)s, %(page_len)s""" % {'field': fields,'key': searchfield, - 'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""), - 'mcond':get_match_cond(doctype), - 'start': start, 'page_len': page_len}) + limit %(start)s, %(page_len)s""".format(**{ + "fields": fields, + "key": searchfield, + "mcond": get_match_cond(doctype) + }), { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace("%", ""), + 'start': start, + 'page_len': page_len + }) # searches for supplier def supplier_query(doctype, txt, searchfield, start, page_len, filters): @@ -95,19 +113,25 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "supplier_name", "supplier_type"] fields = ", ".join(fields) - return frappe.db.sql("""select %(field)s from `tabSupplier` + return frappe.db.sql("""select {field} from `tabSupplier` where docstatus < 2 - and (%(key)s like "%(txt)s" - or supplier_name like "%(txt)s") - %(mcond)s + and ({key} like %(txt)s + or supplier_name like %(txt)s) + {mcond} order by - if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999), - if(locate("%(_txt)s", supplier_name), locate("%(_txt)s", supplier_name), 99999), + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999), name, supplier_name - limit %(start)s, %(page_len)s """ % {'field': fields,'key': searchfield, - 'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""), - 'mcond':get_match_cond(doctype), 'start': start, - 'page_len': page_len}) + limit %(start)s, %(page_len)s """.format(**{ + 'field': fields, + 'key': searchfield, + 'mcond':get_match_cond(doctype) + }), { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace("%", ""), + 'start': start, + 'page_len': page_len + }) def tax_account_query(doctype, txt, searchfield, start, page_len, filters): tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount From a96cf5b96b691e7d4efa325e3014375b42f6e1b5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 10 Jul 2014 12:16:39 +0530 Subject: [PATCH 10/53] Print Hide other charges calculation --- erpnext/selling/doctype/sales_order/sales_order.json | 4 ++-- erpnext/stock/doctype/delivery_note/delivery_note.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index ebe1d84eda..bb5211312c 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -420,7 +420,7 @@ "label": "Taxes and Charges Calculation", "oldfieldtype": "HTML", "permlevel": 0, - "print_hide": 0 + "print_hide": 1 }, { "fieldname": "section_break_43", @@ -883,7 +883,7 @@ "idx": 1, "is_submittable": 1, "issingle": 0, - "modified": "2014-07-07 17:47:40.089520", + "modified": "2014-07-10 02:43:45.504009", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index f1dc5a390e..9b43a71934 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -427,6 +427,7 @@ "label": "Taxes and Charges Calculation", "oldfieldtype": "HTML", "permlevel": 0, + "print_hide": 1, "read_only": 0 }, { @@ -1008,7 +1009,7 @@ "idx": 1, "in_create": 0, "is_submittable": 1, - "modified": "2014-06-23 07:55:47.859869", + "modified": "2014-07-10 02:45:47.673011", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From d5fb5d91e99a510770908db87b6c868e17858b36 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 9 Jul 2014 17:36:38 +0530 Subject: [PATCH 11/53] overflow validation fixed --- .../doctype/purchase_invoice/purchase_invoice.py | 1 + .../doctype/sales_invoice/sales_invoice.py | 6 ++++-- .../doctype/purchase_order/purchase_order.py | 1 + erpnext/controllers/status_updater.py | 15 +++++++++------ .../installation_note/installation_note.py | 3 ++- .../stock/doctype/delivery_note/delivery_note.py | 3 ++- .../doctype/purchase_receipt/purchase_receipt.py | 1 + 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 80aa73a197..74a9628209 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -30,6 +30,7 @@ class PurchaseInvoice(BuyingController): 'target_ref_field': 'amount', 'source_field': 'amount', 'percent_join_field': 'purchase_order', + 'overflow_type': 'billing' }] def validate(self): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5e29487b50..0f6737cc87 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -36,7 +36,8 @@ class SalesInvoice(SellingController): 'join_field': 'so_detail', 'percent_join_field': 'sales_order', 'status_field': 'billing_status', - 'keyword': 'Billed' + 'keyword': 'Billed', + 'overflow_type': 'billing' }] def validate(self): @@ -134,7 +135,8 @@ class SalesInvoice(SellingController): 'keyword':'Delivered', 'second_source_dt': 'Delivery Note Item', 'second_source_field': 'qty', - 'second_join_field': 'prevdoc_detail_docname' + 'second_join_field': 'prevdoc_detail_docname', + 'overflow_type': 'delivery' }) def on_update_after_submit(self): diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 2109d72aa2..f9f5103726 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -24,6 +24,7 @@ class PurchaseOrder(BuyingController): 'target_ref_field': 'qty', 'source_field': 'qty', 'percent_join_field': 'prevdoc_docname', + 'overflow_type': 'order' }] def validate(self): diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 90eacd9820..3c6355488f 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -132,11 +132,12 @@ class StatusUpdater(Document): if not item[args['target_ref_field']]: msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code)) elif args.get('no_tolerance'): - item['reduce_by'] = item[args['target_field']] - \ - item[args['target_ref_field']] + item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']] if item['reduce_by'] > .01: - msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}").format(item.item_code)) - throw(_("{0} must be less than or equal to {1}").format(_(item.target_ref_field), item[args["target_ref_field"]])) + msgprint(_("Allowance for over-{0} crossed for Item {1}") + .format(args["overflow_type"], item.item_code)) + throw(_("{0} must be reduced by {1} or you should increase overflow tolerance") + .format(_(item.target_ref_field.title()), item["reduce_by"])) else: self.check_overflow_with_tolerance(item, args) @@ -156,8 +157,10 @@ class StatusUpdater(Document): item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) item['reduce_by'] = item[args['target_field']] - item['max_allowed'] - msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}.").format(item["item_code"])) - throw(_("{0} must be less than or equal to {1}").format(item["target_ref_field"].title(), item["max_allowed"])) + msgprint(_("Allowance for over-{0} crossed for Item {1}.") + .format(args["overflow_type"], item["item_code"])) + throw(_("{0} must be reduced by {1} or you should increase overflow tolerance") + .format(_(item["target_ref_field"].title()), item["reduce_by"])) def update_qty(self, change_modified=True): """ diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py index 7d2d3d0663..0477abc265 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.py +++ b/erpnext/selling/doctype/installation_note/installation_note.py @@ -28,7 +28,8 @@ class InstallationNote(TransactionBase): 'source_field': 'qty', 'percent_join_field': 'prevdoc_docname', 'status_field': 'installation_status', - 'keyword': 'Installed' + 'keyword': 'Installed', + 'overflow_type': 'installation' }] def validate(self): diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index c1ddf63a44..13da9078b0 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -29,7 +29,8 @@ class DeliveryNote(SellingController): 'source_field': 'qty', 'percent_join_field': 'against_sales_order', 'status_field': 'delivery_status', - 'keyword': 'Delivered' + 'keyword': 'Delivered', + 'overflow_type': 'delivery' }] def onload(self): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 71c07eba02..74f1198752 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -27,6 +27,7 @@ class PurchaseReceipt(BuyingController): 'target_ref_field': 'qty', 'source_field': 'qty', 'percent_join_field': 'prevdoc_docname', + 'overflow_type': 'receipt' }] def onload(self): From ee8b6f28b897b8fbbef2a4c30539afa8c1da476e Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 10 Jul 2014 15:27:00 +0530 Subject: [PATCH 12/53] minor fix --- erpnext/accounts/doctype/c_form/c_form.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py index e0f008f8ad..88ced9a813 100644 --- a/erpnext/accounts/doctype/c_form/c_form.py +++ b/erpnext/accounts/doctype/c_form/c_form.py @@ -55,12 +55,12 @@ class CForm(Document): def get_invoice_details(self, invoice_no): """ Pull details from invoices for referrence """ - - inv = frappe.db.get_value("Sales Invoice", invoice_no, - ["posting_date", "territory", "net_total", "grand_total"], as_dict=True) - return { - 'invoice_date' : inv.posting_date, - 'territory' : inv.territory, - 'net_total' : inv.net_total, - 'grand_total' : inv.grand_total - } + if invoice_no: + inv = frappe.db.get_value("Sales Invoice", invoice_no, + ["posting_date", "territory", "net_total", "grand_total"], as_dict=True) + return { + 'invoice_date' : inv.posting_date, + 'territory' : inv.territory, + 'net_total' : inv.net_total, + 'grand_total' : inv.grand_total + } From 28c4bee0b1d434cead996080668213ef1d54c917 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 10 Jul 2014 16:43:16 +0530 Subject: [PATCH 13/53] Production Planning Tool - Fixed bom get query --- .../production_planning_tool/production_planning_tool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0415c911e1..d57f639f7f 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js @@ -37,8 +37,8 @@ cur_frm.fields_dict['pp_details'].grid.get_field('item_code').get_query = functi }); } -cur_frm.fields_dict['pp_details'].grid.get_field('bom_no').get_query = function(doc) { - var d = locals[this.doctype][this.docname]; +cur_frm.fields_dict['pp_details'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; if (d.item_code) { return { query: "erpnext.controllers.queries.bom", From 9b7b9b2e9313bb3d9db95aaa3e5b5f92ef30915e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 10 Jul 2014 18:44:41 +0530 Subject: [PATCH 14/53] Bank Reconciliation Statement Print Format --- .../bank_reconciliation_statement.html | 46 +++++++++++++++++++ .../bank_reconciliation_statement.py | 19 ++++---- 2 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html new file mode 100644 index 0000000000..91e07ab7da --- /dev/null +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html @@ -0,0 +1,46 @@ +

{%= __("Bank Reconciliation Statement") %}

+

{%= filters.account %}

+
+ + + + + + + + + + + + {% for(var i=0, l=data.length; i + + + + + + + {% } else { %} + + + + + + + + {% } %} + {% } %} + +
{%= __("Posting Date") %}{%= __("Journal Voucher") %}{%= __("Reference") %}{%= __("Debit") %}{%= __("Credit") %}
{%= dateutil.str_to_user(data[i].posting_date) %}{%= data[i].journal_voucher %}{%= __("Against") %}: {%= data[i].against_account %} + {% if (data[i].reference) { %} +
{%= __("Reference") %}: {%= data[i].reference %} + {% if (data[i].ref_date) { %} +
{%= __("Reference Date") %}: {%= dateutil.str_to_user(data[i].ref_date) %} + {% } %} + {% } %} + {% if (data[i].clearance_date) { %} +
{%= __("Clearance Date") %}: {%= dateutil.str_to_user(data[i].clearance_date) %} + {% } %} +
{%= format_currency(data[i].debit) %}{%= format_currency(data[i].credit) %}
{%= data[i].journal_voucher %}{%= format_currency(data[i].debit) %}{%= format_currency(data[i].credit) %}
+

Printed On {%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}

diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index e87fbd3226..119de09e41 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -19,28 +19,29 @@ def execute(filters=None): total_debit, total_credit = 0,0 for d in data: - total_debit += flt(d[4]) - total_credit += flt(d[5]) + total_debit += flt(d[2]) + total_credit += flt(d[3]) bank_bal = flt(balance_as_per_company) - flt(total_debit) + flt(total_credit) data += [ get_balance_row("Balance as per company books", balance_as_per_company), - ["", "", "", "Amounts not reflected in bank", total_debit, total_credit], + ["", "Amounts not reflected in bank", total_debit, total_credit, "", "", "", ""], get_balance_row("Balance as per bank", bank_bal) ] return columns, data def get_columns(): - return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100", - "Clearance Date:Date:110", "Against Account:Link/Account:200", - "Debit:Currency:120", "Credit:Currency:120" + return ["Posting Date:Date:100", "Journal Voucher:Link/Journal Voucher:200", + "Debit:Currency:120", "Credit:Currency:120", + "Against Account:Link/Account:200", "Reference::100", "Ref Date:Date:110", "Clearance Date:Date:110" ] def get_entries(filters): entries = frappe.db.sql("""select - jv.name, jv.posting_date, jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit + jv.posting_date, jv.name, jvd.debit, jvd.credit, + jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv where jvd.parent = jv.name and jv.docstatus=1 @@ -52,6 +53,6 @@ def get_entries(filters): def get_balance_row(label, amount): if amount > 0: - return ["", "", "", label, amount, 0] + return ["", label, amount, 0, "", "", "", ""] else: - return ["", "", "", label, 0, amount] + return ["", label, 0, amount, "", "", "", ""] From e6cee7b41f1c45563dfdf67d09fee6fc1d7c9b82 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 10 Jul 2014 19:06:39 +0530 Subject: [PATCH 15/53] Expense account can be liability account for stock reconciliation --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index bb3ab69ca1..27437a396a 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -240,7 +240,7 @@ class StockController(AccountsController): else: is_expense_account = frappe.db.get_value("Account", item.get("expense_account"), "report_type")=="Profit and Loss" - if self.doctype != "Purchase Receipt" and not is_expense_account: + if self.doctype not in ("Purchase Receipt", "Stock Reconciliation") and not is_expense_account: frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account") .format(item.get("expense_account"))) if is_expense_account and not item.get("cost_center"): From 614fb750b7fb883757f7b5c53ac97ff10ed8cf25 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 10:47:50 +0530 Subject: [PATCH 16/53] set qty as per stock uom in entire purchase cycle --- erpnext/controllers/buying_controller.py | 8 ++++++++ erpnext/stock/get_item_details.py | 1 + 2 files changed, 9 insertions(+) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index afccdfa0e3..acb00245e6 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -17,6 +17,7 @@ class BuyingController(StockController): self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name") self.is_item_table_empty() + self.set_qty_as_per_stock_uom() self.validate_stock_or_nonstock_items() self.validate_warehouse() @@ -317,3 +318,10 @@ class BuyingController(StockController): def is_item_table_empty(self): if not len(self.get(self.fname)): frappe.throw(_("Item table can not be blank")) + + def set_qty_as_per_stock_uom(self): + for d in self.get(self.fname): + if d.meta.get_field("stock_qty") and not d.stock_qty: + if not d.conversion_factor: + frappe.throw(_("Row {0}: Conversion Factor is mandatory")) + d.stock_qty = flt(d.qty) * flt(d.conversion_factor) \ No newline at end of file diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f9a1d9fa1a..82b396fb39 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -152,6 +152,7 @@ def get_basic_details(args, item_doc): "min_order_qty": flt(item.min_order_qty) if args.parenttype == "Material Request" else "", "conversion_factor": 1.0, "qty": 1.0, + "stock_qty": 1.0, "price_list_rate": 0.0, "base_price_list_rate": 0.0, "rate": 0.0, From 5c38488590d8398ce4bb242473884303b3661113 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 11:43:00 +0530 Subject: [PATCH 17/53] Utility: reset serial no status and warehouse --- erpnext/utilities/repost_stock.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index e4028b69f6..1c6782ad36 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -190,3 +190,18 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin "posting_date": posting_date, "posting_time": posting_time }) + +def reset_serial_no_status_and_warehouse(serial_nos=[]): + if not serial_nos: + serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where status != 'Not in Use' + and docstatus = 0""") + for serial_no in serial_nos: + try: + sr = frappe.get_doc("Serial No", serial_no) + sr.via_stock_ledger = True + sr.save() + except: + pass + + frappe.db.sql("""update `tabSerial No` set warehouse='' where status in ('Delivered', 'Purchase Returned')""") + \ No newline at end of file From b0a8d000b1c1d7551fc37d6df8e95e1b37a5da94 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 11:56:03 +0530 Subject: [PATCH 18/53] Utility: reset serial no status and warehouse --- erpnext/utilities/repost_stock.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 1c6782ad36..4205893893 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -198,6 +198,10 @@ def reset_serial_no_status_and_warehouse(serial_nos=[]): for serial_no in serial_nos: try: sr = frappe.get_doc("Serial No", serial_no) + last_sle = sr.get_last_sle() + if flt(last_sle.actual_qty) > 0: + sr.warehouse = last_sle.warehouse + sr.via_stock_ledger = True sr.save() except: From d5fd5359095dabe9ee9e4c145d470a926ffc0446 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 12:39:50 +0530 Subject: [PATCH 19/53] Minor fix, if no default company --- erpnext/public/js/queries.js | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index b57b765ad6..4bb3302bd8 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -71,7 +71,7 @@ $.extend(erpnext.queries, { warehouse: function(doc) { return { - filters: [["Warehouse", "company", "in", ["", doc.company]]] + filters: [["Warehouse", "company", "in", ["", cstr(doc.company)]]] } } }); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 10241983ad..7274ece1fa 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -92,7 +92,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ set_default_account: function() { var me = this; - if(cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + if(cint(frappe.defaults.get_default("auto_accounting_for_stock")) && this.frm.doc.company) { var account_for = "stock_adjustment_account"; if (this.frm.doc.purpose == "Purchase Return") From e8c5cb8c9a7487e59b1205f106117c7cc4061f82 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 14 Jul 2014 13:10:24 +0530 Subject: [PATCH 20/53] Fixed Address and Contact report name --- erpnext/config/buying.py | 2 +- erpnext/config/selling.py | 2 +- .../customer_addresses_and_contacts.json | 30 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index bc6251982b..1b9e5a23b0 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -150,7 +150,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Supplier Addresses And Contacts", + "name": "Supplier Addresses and Contacts", "doctype": "Supplier" }, { diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 200ab6d4ba..c95f15aa83 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -206,7 +206,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Customer Addresses And Contacts", + "name": "Customer Addresses and Contacts", "doctype": "Contact" }, { diff --git a/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json b/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json index 9bde272c61..deb90b71cd 100644 --- a/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json +++ b/erpnext/selling/report/customer_addresses_and_contacts/customer_addresses_and_contacts.json @@ -1,17 +1,17 @@ { - "apply_user_permissions": 1, - "creation": "2012-10-04 18:45:27", - "docstatus": 0, - "doctype": "Report", - "idx": 1, - "is_standard": "Yes", - "modified": "2014-06-03 07:18:17.006732", - "modified_by": "Administrator", - "module": "Selling", - "name": "Customer Addresses And Contacts", - "owner": "Administrator", - "query": "SELECT\n\t`tabCustomer`.name as customer_id,\n\t`tabCustomer`.customer_name,\n\t`tabCustomer`.customer_group,\n\t`tabAddress`.address_line1,\n\t`tabAddress`.address_line2,\n\t`tabAddress`.city,\n\t`tabAddress`.state,\n\t`tabAddress`.pincode,\n\t`tabAddress`.country,\n\t`tabAddress`.is_primary_address, \n\t`tabContact`.first_name,\n\t`tabContact`.last_name,\n\t`tabContact`.phone,\n\t`tabContact`.mobile_no,\n\t`tabContact`.email_id,\n\t`tabContact`.is_primary_contact\nFROM\n\t`tabCustomer`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.customer=`tabCustomer`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.customer=`tabCustomer`.name\n\t)\nWHERE\n\t`tabCustomer`.docstatus<2\nORDER BY\n\t`tabCustomer`.name asc", - "ref_doctype": "Customer", - "report_name": "Customer Addresses And Contacts", + "apply_user_permissions": 1, + "creation": "2012-10-04 18:45:27", + "docstatus": 0, + "doctype": "Report", + "idx": 1, + "is_standard": "Yes", + "modified": "2014-07-14 07:18:17.006732", + "modified_by": "Administrator", + "module": "Selling", + "name": "Customer Addresses and Contacts", + "owner": "Administrator", + "query": "SELECT\n\t`tabCustomer`.name as customer_id,\n\t`tabCustomer`.customer_name,\n\t`tabCustomer`.customer_group,\n\t`tabAddress`.address_line1,\n\t`tabAddress`.address_line2,\n\t`tabAddress`.city,\n\t`tabAddress`.state,\n\t`tabAddress`.pincode,\n\t`tabAddress`.country,\n\t`tabAddress`.is_primary_address, \n\t`tabContact`.first_name,\n\t`tabContact`.last_name,\n\t`tabContact`.phone,\n\t`tabContact`.mobile_no,\n\t`tabContact`.email_id,\n\t`tabContact`.is_primary_contact\nFROM\n\t`tabCustomer`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.customer=`tabCustomer`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.customer=`tabCustomer`.name\n\t)\nWHERE\n\t`tabCustomer`.docstatus<2\nORDER BY\n\t`tabCustomer`.name asc", + "ref_doctype": "Customer", + "report_name": "Customer Addresses And Contacts", "report_type": "Query Report" -} \ No newline at end of file +} From 7700c62fa8ec7290ef2aaeaab975bd55cfee3e16 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 14:21:21 +0530 Subject: [PATCH 21/53] Minor fix --- erpnext/utilities/repost_stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 4205893893..159825a495 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -191,7 +191,7 @@ def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, postin "posting_time": posting_time }) -def reset_serial_no_status_and_warehouse(serial_nos=[]): +def reset_serial_no_status_and_warehouse(serial_nos=None): if not serial_nos: serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where status != 'Not in Use' and docstatus = 0""") From 1829bd84d3d2d388583548a6b73e9cf85959bfe2 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 14 Jul 2014 14:26:28 +0530 Subject: [PATCH 22/53] hotfix: website_image --- erpnext/setup/doctype/item_group/item_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 745345e58a..c6f49a1546 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -82,7 +82,7 @@ def get_child_groups(item_group_name): def get_item_for_list_in_html(context): # add missing absolute link in files # user may forget it during upload - if context.get("website_image", "").startswith("files/"): + if (context.get("website_image") or "").startswith("files/"): context["website_image"] = "/" + context["website_image"] return frappe.get_template("templates/includes/product_in_grid.html").render(context) From c70d2d23a8d11c93455e7751cef30fc7f1fbda15 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 11:27:25 +0530 Subject: [PATCH 23/53] Support Ticket: set resolution_date as None if missing --- .../doctype/support_ticket/support_ticket.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/erpnext/support/doctype/support_ticket/support_ticket.py b/erpnext/support/doctype/support_ticket/support_ticket.py index 9517ea48de..4cdc20ac0f 100644 --- a/erpnext/support/doctype/support_ticket/support_ticket.py +++ b/erpnext/support/doctype/support_ticket/support_ticket.py @@ -8,31 +8,31 @@ from erpnext.utilities.transaction_base import TransactionBase from frappe.utils import now, extract_email_id class SupportTicket(TransactionBase): - + def get_sender(self, comm): return frappe.db.get_value('Support Email Settings',None,'support_email') def get_subject(self, comm): return '[' + self.name + '] ' + (comm.subject or 'No Subject Specified') - + def get_content(self, comm): signature = frappe.db.get_value('Support Email Settings',None,'support_signature') content = comm.content if signature: content += '

' + signature + '

' return content - + def get_portal_page(self): return "ticket" - + def validate(self): self.update_status() self.set_lead_contact(self.raised_by) - + if self.status == "Closed": from frappe.widgets.form.assign_to import clear clear(self.doctype, self.name) - + def set_lead_contact(self, email_id): import email.utils email_id = email.utils.parseaddr(email_id) @@ -41,8 +41,8 @@ class SupportTicket(TransactionBase): self.lead = frappe.db.get_value("Lead", {"email_id": email_id}) if not self.contact: self.contact = frappe.db.get_value("Contact", {"email_id": email_id}) - - if not self.company: + + if not self.company: self.company = frappe.db.get_value("Lead", self.lead, "company") or \ frappe.db.get_default("company") @@ -53,15 +53,16 @@ class SupportTicket(TransactionBase): if self.status=="Closed" and status !="Closed": self.resolution_date = now() if self.status=="Open" and status !="Open": - self.resolution_date = "" + # if no date, it should be set as None and not a blank string "", as per mysql strict config + self.resolution_date = None @frappe.whitelist() def set_status(name, status): st = frappe.get_doc("Support Ticket", name) st.status = status st.save() - + def auto_close_tickets(): - frappe.db.sql("""update `tabSupport Ticket` set status = 'Closed' - where status = 'Replied' + frappe.db.sql("""update `tabSupport Ticket` set status = 'Closed' + where status = 'Replied' and date_sub(curdate(),interval 15 Day) > modified""") From e73ed2afaed1279c8ca87ba77c1e7aaf2ea56fc5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 11:48:34 +0530 Subject: [PATCH 24/53] Setup Wizard: fixed attachment issue #1940 --- .../setup/page/setup_wizard/setup_wizard.py | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index ca52efb119..343cba56c4 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -113,9 +113,11 @@ def update_user_name(args): last_name=%(last_name)s WHERE name=%(name)s""", args) if args.get("attach_user"): - filename, filetype, content = args.get("attach_user").split(",") - fileurl = save_file(filename, content, "User", args.get("name"), decode=True).file_url - frappe.db.set_value("User", args.get("name"), "user_image", fileurl) + attach_user = args.get("attach_user").split(",") + if len(attach_user)==3: + filename, filetype, content = attach_user + fileurl = save_file(filename, content, "User", args.get("name"), decode=True).file_url + frappe.db.set_value("User", args.get("name"), "user_image", fileurl) add_all_roles_to(args.get("name")) @@ -319,9 +321,11 @@ def create_items(args): }).insert() if args.get("item_img_" + str(i)): - filename, filetype, content = args.get("item_img_" + str(i)).split(",") - fileurl = save_file(filename, content, "Item", item, decode=True).file_url - frappe.db.set_value("Item", item, "image", fileurl) + item_image = args.get("item_img_" + str(i)).split(",") + if len(item_image)==3: + filename, filetype, content = item_image + fileurl = save_file(filename, content, "Item", item, decode=True).file_url + frappe.db.set_value("Item", item, "image", fileurl) def create_customers(args): for i in xrange(1,6): @@ -374,17 +378,21 @@ def create_letter_head(args): "is_default": 1 }).insert() - filename, filetype, content = args.get("attach_letterhead").split(",") - fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url - frappe.db.set_value("Letter Head", _("Standard"), "content", "" % fileurl) + attach_letterhead = args.get("attach_letterhead").split(",") + if len(attach_letterhead)==3: + filename, filetype, content = attach_letterhead + fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url + frappe.db.set_value("Letter Head", _("Standard"), "content", "" % fileurl) def create_logo(args): if args.get("attach_logo"): - filename, filetype, content = args.get("attach_logo").split(",") - fileurl = save_file(filename, content, "Website Settings", "Website Settings", - decode=True).file_url - frappe.db.set_value("Website Settings", "Website Settings", "banner_html", - "" % fileurl) + attach_logo = args.get("attach_logo").split(",") + if len(attach_logo)==3: + filename, filetype, content = attach_logo + fileurl = save_file(filename, content, "Website Settings", "Website Settings", + decode=True).file_url + frappe.db.set_value("Website Settings", "Website Settings", "banner_html", + "" % fileurl) def add_all_roles_to(name): user = frappe.get_doc("User", name) From d93fa2f78b5e0444eeadc0b383a06ac93ff659f1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 15:48:02 +0530 Subject: [PATCH 25/53] Fixes in Stock Entry and test cases --- .../doctype/sales_invoice/test_records.json | 602 +++++++++--------- .../sales_invoice/test_sales_invoice.py | 1 + .../stock/doctype/stock_entry/stock_entry.py | 5 +- 3 files changed, 305 insertions(+), 303 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index b0828f5c7e..eb86672ccd 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -1,385 +1,385 @@ [ { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "debit_to": "_Test Customer - _TC", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "debit_to": "_Test Customer - _TC", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", "entries": [ { - "amount": 500.0, - "base_amount": 500.0, - "base_rate": 500.0, - "cost_center": "_Test Cost Center - _TC", - "description": "138-CMS Shoe", - "doctype": "Sales Invoice Item", - "income_account": "Sales - _TC", - "item_name": "138-CMS Shoe", - "parentfield": "entries", - "qty": 1.0, + "amount": 500.0, + "base_amount": 500.0, + "base_rate": 500.0, + "cost_center": "_Test Cost Center - _TC", + "description": "138-CMS Shoe", + "doctype": "Sales Invoice Item", + "income_account": "Sales - _TC", + "item_name": "138-CMS Shoe", + "parentfield": "entries", + "qty": 1.0, "rate": 500.0 } - ], - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total": 561.8, - "grand_total_export": 561.8, - "is_pos": 0, - "naming_series": "_T-Sales Invoice-", - "net_total": 500.0, + ], + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 561.8, + "grand_total_export": 561.8, + "is_pos": 0, + "naming_series": "_T-Sales Invoice-", + "net_total": 500.0, "other_charges": [ { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 6 - }, + }, { - "account_head": "_Test Account Service Tax - _TC", - "charge_type": "On Net Total", - "description": "Service Tax", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 6.36 } - ], - "plc_conversion_rate": 1.0, - "posting_date": "2013-01-23", - "price_list_currency": "INR", + ], + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", "sales_team": [ { - "allocated_percentage": 65.5, - "doctype": "Sales Team", - "parentfield": "sales_team", + "allocated_percentage": 65.5, + "doctype": "Sales Team", + "parentfield": "sales_team", "sales_person": "_Test Sales Person 1" - }, + }, { - "allocated_percentage": 34.5, - "doctype": "Sales Team", - "parentfield": "sales_team", + "allocated_percentage": 34.5, + "doctype": "Sales Team", + "parentfield": "sales_team", "sales_person": "_Test Sales Person 2" } - ], - "selling_price_list": "_Test Price List", + ], + "selling_price_list": "_Test Price List", "territory": "_Test Territory" - }, + }, { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "debit_to": "_Test Customer - _TC", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "debit_to": "_Test Customer - _TC", + "doctype": "Sales Invoice", + "due_date": "2013-03-07", "entries": [ { - "amount": 500.0, - "base_amount": 500.0, - "base_rate": 500.0, - "cost_center": "_Test Cost Center - _TC", - "description": "_Test Item", - "doctype": "Sales Invoice Item", - "expense_account": "_Test Account Cost for Goods Sold - _TC", - "income_account": "Sales - _TC", - "item_code": "_Test Item", - "item_name": "_Test Item", - "parentfield": "entries", - "price_list_rate": 500.0, + "amount": 500.0, + "base_amount": 500.0, + "base_rate": 500.0, + "cost_center": "_Test Cost Center - _TC", + "description": "_Test Item", + "doctype": "Sales Invoice Item", + "expense_account": "_Test Account Cost for Goods Sold - _TC", + "income_account": "Sales - _TC", + "item_code": "_Test Item", + "item_name": "_Test Item", + "parentfield": "entries", + "price_list_rate": 500.0, "qty": 1.0 } - ], - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total": 630.0, - "grand_total_export": 630.0, - "is_pos": 0, - "naming_series": "_T-Sales Invoice-", - "net_total": 500.0, + ], + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 630.0, + "grand_total_export": 630.0, + "is_pos": 0, + "naming_series": "_T-Sales Invoice-", + "net_total": 500.0, "other_charges": [ { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 16 - }, + }, { - "account_head": "_Test Account Service Tax - _TC", - "charge_type": "On Net Total", - "description": "Service Tax", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 10 } - ], - "plc_conversion_rate": 1.0, - "posting_date": "2013-03-07", - "price_list_currency": "INR", - "selling_price_list": "_Test Price List", + ], + "plc_conversion_rate": 1.0, + "posting_date": "2013-03-07", + "price_list_currency": "INR", + "selling_price_list": "_Test Price List", "territory": "_Test Territory" - }, + }, { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "debit_to": "_Test Customer - _TC", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "debit_to": "_Test Customer - _TC", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", "entries": [ { - "cost_center": "_Test Cost Center - _TC", - "doctype": "Sales Invoice Item", - "income_account": "Sales - _TC", - "item_code": "_Test Item Home Desktop 100", - "item_name": "_Test Item Home Desktop 100", - "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}", - "parentfield": "entries", - "price_list_rate": 50, - "qty": 10, - "rate": 50, + "cost_center": "_Test Cost Center - _TC", + "doctype": "Sales Invoice Item", + "income_account": "Sales - _TC", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}", + "parentfield": "entries", + "price_list_rate": 50, + "qty": 10, + "rate": 50, "stock_uom": "_Test UOM" - }, + }, { - "cost_center": "_Test Cost Center - _TC", - "doctype": "Sales Invoice Item", - "income_account": "Sales - _TC", - "item_code": "_Test Item Home Desktop 200", - "item_name": "_Test Item Home Desktop 200", - "parentfield": "entries", - "price_list_rate": 150, - "qty": 5, - "rate": 150, + "cost_center": "_Test Cost Center - _TC", + "doctype": "Sales Invoice Item", + "income_account": "Sales - _TC", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "parentfield": "entries", + "price_list_rate": 150, + "qty": 5, + "rate": 150, "stock_uom": "_Test UOM" } - ], - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total_export": 0, - "is_pos": 0, - "naming_series": "_T-Sales Invoice-", + ], + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total_export": 0, + "is_pos": 0, + "naming_series": "_T-Sales Invoice-", "other_charges": [ { - "account_head": "_Test Account Shipping Charges - _TC", - "charge_type": "Actual", - "cost_center": "_Test Cost Center - _TC", - "description": "Shipping Charges", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account Shipping Charges - _TC", + "charge_type": "Actual", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 100 - }, + }, { - "account_head": "_Test Account Customs Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Customs Duty", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account Customs Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 10 - }, + }, { - "account_head": "_Test Account Excise Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Excise Duty", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 12 - }, + }, { - "account_head": "_Test Account Education Cess - _TC", - "charge_type": "On Previous Row Amount", - "cost_center": "_Test Cost Center - _TC", - "description": "Education Cess", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "rate": 2, + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Previous Row Amount", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "rate": 2, "row_id": 3 - }, + }, { - "account_head": "_Test Account S&H Education Cess - _TC", - "charge_type": "On Previous Row Amount", - "cost_center": "_Test Cost Center - _TC", - "description": "S&H Education Cess", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "rate": 1, + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Previous Row Amount", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "rate": 1, "row_id": 3 - }, + }, { - "account_head": "_Test Account CST - _TC", - "charge_type": "On Previous Row Total", - "cost_center": "_Test Cost Center - _TC", - "description": "CST", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "rate": 2, + "account_head": "_Test Account CST - _TC", + "charge_type": "On Previous Row Total", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "rate": 2, "row_id": 5 - }, + }, { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", "rate": 12.5 - }, + }, { - "account_head": "_Test Account Discount - _TC", - "charge_type": "On Previous Row Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Discount", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "rate": -10, + "account_head": "_Test Account Discount - _TC", + "charge_type": "On Previous Row Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "rate": -10, "row_id": 7 } - ], - "plc_conversion_rate": 1.0, - "posting_date": "2013-01-23", - "price_list_currency": "INR", - "selling_price_list": "_Test Price List", + ], + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "selling_price_list": "_Test Price List", "territory": "_Test Territory" - }, + }, { - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "debit_to": "_Test Customer - _TC", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "debit_to": "_Test Customer - _TC", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", "entries": [ { - "cost_center": "_Test Cost Center - _TC", - "doctype": "Sales Invoice Item", - "income_account": "Sales - _TC", - "item_code": "_Test Item Home Desktop 100", - "item_name": "_Test Item Home Desktop 100", - "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}", - "parentfield": "entries", - "price_list_rate": 62.5, - "qty": 10, + "cost_center": "_Test Cost Center - _TC", + "doctype": "Sales Invoice Item", + "income_account": "Sales - _TC", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "item_tax_rate": "{\"_Test Account Excise Duty - _TC\": 10}", + "parentfield": "entries", + "price_list_rate": 62.5, + "qty": 10, "stock_uom": "_Test UOM" - }, + }, { - "cost_center": "_Test Cost Center - _TC", - "doctype": "Sales Invoice Item", - "income_account": "Sales - _TC", - "item_code": "_Test Item Home Desktop 200", - "item_name": "_Test Item Home Desktop 200", - "parentfield": "entries", - "price_list_rate": 190.66, - "qty": 5, + "cost_center": "_Test Cost Center - _TC", + "doctype": "Sales Invoice Item", + "income_account": "Sales - _TC", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "parentfield": "entries", + "price_list_rate": 190.66, + "qty": 5, "stock_uom": "_Test UOM" } - ], - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total_export": 0, - "is_pos": 0, - "naming_series": "_T-Sales Invoice-", + ], + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total_export": 0, + "is_pos": 0, + "naming_series": "_T-Sales Invoice-", "other_charges": [ { - "account_head": "_Test Account Excise Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Excise Duty", - "doctype": "Sales Taxes and Charges", - "idx": 1, - "included_in_print_rate": 1, - "parentfield": "other_charges", + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "idx": 1, + "included_in_print_rate": 1, + "parentfield": "other_charges", "rate": 12 - }, + }, { - "account_head": "_Test Account Education Cess - _TC", - "charge_type": "On Previous Row Amount", - "cost_center": "_Test Cost Center - _TC", - "description": "Education Cess", - "doctype": "Sales Taxes and Charges", - "idx": 2, - "included_in_print_rate": 1, - "parentfield": "other_charges", - "rate": 2, + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Previous Row Amount", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "idx": 2, + "included_in_print_rate": 1, + "parentfield": "other_charges", + "rate": 2, "row_id": 1 - }, + }, { - "account_head": "_Test Account S&H Education Cess - _TC", - "charge_type": "On Previous Row Amount", - "cost_center": "_Test Cost Center - _TC", - "description": "S&H Education Cess", - "doctype": "Sales Taxes and Charges", - "idx": 3, - "included_in_print_rate": 1, - "parentfield": "other_charges", - "rate": 1, + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Previous Row Amount", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "idx": 3, + "included_in_print_rate": 1, + "parentfield": "other_charges", + "rate": 1, "row_id": 1 - }, + }, { - "account_head": "_Test Account CST - _TC", - "charge_type": "On Previous Row Total", - "cost_center": "_Test Cost Center - _TC", - "description": "CST", - "doctype": "Sales Taxes and Charges", - "idx": 4, - "included_in_print_rate": 1, - "parentfield": "other_charges", - "rate": 2, + "account_head": "_Test Account CST - _TC", + "charge_type": "On Previous Row Total", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "doctype": "Sales Taxes and Charges", + "idx": 4, + "included_in_print_rate": 1, + "parentfield": "other_charges", + "rate": 2, "row_id": 3 - }, + }, { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "idx": 5, - "included_in_print_rate": 1, - "parentfield": "other_charges", + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "idx": 5, + "included_in_print_rate": 1, + "parentfield": "other_charges", "rate": 12.5 - }, + }, { - "account_head": "_Test Account Customs Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Customs Duty", - "doctype": "Sales Taxes and Charges", - "idx": 6, - "parentfield": "other_charges", + "account_head": "_Test Account Customs Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "doctype": "Sales Taxes and Charges", + "idx": 6, + "parentfield": "other_charges", "rate": 10 - }, + }, { - "account_head": "_Test Account Shipping Charges - _TC", - "charge_type": "Actual", - "cost_center": "_Test Cost Center - _TC", - "description": "Shipping Charges", - "doctype": "Sales Taxes and Charges", - "idx": 7, - "parentfield": "other_charges", + "account_head": "_Test Account Shipping Charges - _TC", + "charge_type": "Actual", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "doctype": "Sales Taxes and Charges", + "idx": 7, + "parentfield": "other_charges", "rate": 100 - }, + }, { - "account_head": "_Test Account Discount - _TC", - "charge_type": "On Previous Row Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Discount", - "doctype": "Sales Taxes and Charges", - "idx": 8, - "parentfield": "other_charges", - "rate": -10, + "account_head": "_Test Account Discount - _TC", + "charge_type": "On Previous Row Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "doctype": "Sales Taxes and Charges", + "idx": 8, + "parentfield": "other_charges", + "rate": -10, "row_id": 7 } - ], - "plc_conversion_rate": 1.0, - "posting_date": "2013-01-23", - "price_list_currency": "INR", - "selling_price_list": "_Test Price List", + ], + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "selling_price_list": "_Test Price List", "territory": "_Test Territory" } -] \ No newline at end of file +] diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 85e57822e2..1d22e0930a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -674,6 +674,7 @@ class TestSalesInvoice(unittest.TestCase): "notification_email_address": "test@example.com, test1@example.com, test2@example.com", "repeat_on_day_of_month": getdate(today).day, "posting_date": today, + "due_date": None, "fiscal_year": get_fiscal_year(today)[0], "invoice_period_from_date": get_first_day(today), "invoice_period_to_date": get_last_day(today) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 586179de28..2609ff5042 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -204,7 +204,7 @@ class StockEntry(StockController): if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) - allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") + allow_negative_stock = cint(frappe.db.get_default("allow_negative_stock")) for d in self.get('mtn_details'): args = frappe._dict({ @@ -219,7 +219,8 @@ class StockEntry(StockController): # get actual stock at source warehouse d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0 - if d.s_warehouse and not allow_negative_stock and d.actual_qty <= d.transfer_qty: + # validate qty during submit + if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty)) From f214bfcfc3ab83b351eb48bd13d2352948c446ba Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 17:52:08 +0530 Subject: [PATCH 26/53] Fixes in load defaults of transaction.js --- erpnext/public/js/transaction.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index 1790a47252..ae5864d491 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -20,16 +20,13 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ currency: currency, price_list_currency: currency, status: "Draft", - fiscal_year: frappe.defaults.get_user_default("fiscal_year"), is_subcontracted: "No", }, function(fieldname, value) { if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) me.frm.set_value(fieldname, value); }); - if(!this.frm.doc.company) { - this.frm.set_value("company", frappe.defaults.get_user_default("company")); - } else { + if(this.frm.doc.company) { cur_frm.script_manager.trigger("company"); } } @@ -332,7 +329,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule", args: { args: this._get_args(item) }, callback: function(r) { - if (!r.exc) { + if (!r.exc && r.message) { me._set_values_for_item_list(r.message); if(calculate_taxes_and_totals) me.calculate_taxes_and_totals(); } From 4b5e89a067646648f4827bd586cf12e6669b52e9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 19:23:58 +0530 Subject: [PATCH 27/53] Fixes in Stock Entry test cases --- .../doctype/stock_entry/test_stock_entry.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 260223d4b8..1fdc016d7d 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -21,7 +21,7 @@ class TestStockEntry(unittest.TestCase): frappe.db.sql("""delete from `tabMaterial Request`""") self._clear_stock_account_balance() - frappe.db.set_value("Stock Settings", None, "auto_indent", True) + frappe.db.set_value("Stock Settings", None, "auto_indent", 1) st1 = frappe.copy_doc(test_records[0]) st1.insert() @@ -664,15 +664,17 @@ class TestStockEntry(unittest.TestCase): def test_serial_no_not_exists(self): self._clear_stock_account_balance() frappe.db.sql("delete from `tabSerial No` where name in ('ABCD', 'EFGH')") + make_serialized_item() se = frappe.copy_doc(test_records[0]) se.purpose = "Material Issue" - se.get("mtn_details")[0].item_code = "_Test Serialized Item" + se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series" se.get("mtn_details")[0].qty = 2 se.get("mtn_details")[0].s_warehouse = "_Test Warehouse 1 - _TC" se.get("mtn_details")[0].t_warehouse = None se.get("mtn_details")[0].serial_no = "ABCD\nEFGH" se.get("mtn_details")[0].transfer_qty = 2 se.insert() + self.assertRaises(SerialNoNotExistsError, se.submit) def test_serial_duplicate(self): @@ -699,8 +701,8 @@ class TestStockEntry(unittest.TestCase): return se, serial_nos def test_serial_item_error(self): - self._clear_stock_account_balance() se, serial_nos = self.test_serial_by_series() + make_serialized_item("_Test Serialized Item", "ABCD\nEFGH") se = frappe.copy_doc(test_records[0]) se.purpose = "Material Transfer" @@ -735,6 +737,8 @@ class TestStockEntry(unittest.TestCase): def test_serial_warehouse_error(self): self._clear_stock_account_balance() + make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC") + t = make_serialized_item() serial_nos = get_serial_nos(t.get("mtn_details")[0].serial_no) @@ -818,11 +822,16 @@ class TestStockEntry(unittest.TestCase): self.assertRaises (StockFreezeError, se.submit) frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) -def make_serialized_item(): +def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): se = frappe.copy_doc(test_records[0]) - se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series" + se.get("mtn_details")[0].item_code = item_code or "_Test Serialized Item With Series" + se.get("mtn_details")[0].serial_no = serial_no se.get("mtn_details")[0].qty = 2 se.get("mtn_details")[0].transfer_qty = 2 + + if target_warehouse: + se.get("mtn_details")[0].t_warehouse = target_warehouse + se.insert() se.submit() return se From b8189d7d2337ecd33a7ee3cbbce78d204acf4e5a Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 19:24:53 +0530 Subject: [PATCH 28/53] Auto Re-order Item for default warehouse only, if Warehouse wise re-order not specified --- erpnext/stock/utils.py | 95 ++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 340e5511ff..10f5cc2776 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -176,7 +176,6 @@ def get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries): def reorder_item(): """ Reorder item if stock reaches reorder level""" - # if initial setup not completed, return if not frappe.db.sql("select name from `tabFiscal Year` limit 1"): return @@ -185,46 +184,69 @@ def reorder_item(): frappe.local.auto_indent = cint(frappe.db.get_value('Stock Settings', None, 'auto_indent')) if frappe.local.auto_indent: - material_requests = {} - bin_list = frappe.db.sql("""select item_code, 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 - (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > curdate()))""", - as_dict=True) + _reorder_item() - for bin in bin_list: - #check if re-order is required - item_reorder = frappe.db.get("Item Reorder", - {"parent": bin.item_code, "warehouse": bin.warehouse}) - if item_reorder: - reorder_level = item_reorder.warehouse_reorder_level - reorder_qty = item_reorder.warehouse_reorder_qty - material_request_type = item_reorder.material_request_type or "Purchase" - else: - reorder_level, reorder_qty = frappe.db.get_value("Item", bin.item_code, - ["re_order_level", "re_order_qty"]) - material_request_type = "Purchase" +def _reorder_item(): + # {"Purchase": {"Company": [{"item_code": "", "warehouse": "", "reorder_qty": 0.0}]}, "Transfer": {...}} + material_requests = {"Purchase": {}, "Transfer": {}} - if flt(reorder_level) and flt(bin.projected_qty) < flt(reorder_level): - if flt(reorder_level) - flt(bin.projected_qty) > flt(reorder_qty): - reorder_qty = flt(reorder_level) - flt(bin.projected_qty) + item_warehouse_projected_qty = get_item_warehouse_projected_qty() + warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`""")) + default_company = (frappe.defaults.get_defaults()["company"] or + frappe.db.sql("""select name from tabCompany limit 1""")[0][0]) - company = frappe.db.get_value("Warehouse", bin.warehouse, "company") or \ - frappe.defaults.get_defaults()["company"] or \ - frappe.db.sql("""select name from tabCompany limit 1""")[0][0] + def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type): + if warehouse not in item_warehouse_projected_qty[item_code]: + # likely a disabled warehouse or a warehouse where BIN does not exist + return - material_requests.setdefault(material_request_type, frappe._dict()).setdefault( - company, []).append(frappe._dict({ - "item_code": bin.item_code, - "warehouse": bin.warehouse, - "reorder_qty": reorder_qty - }) - ) + reorder_level = flt(reorder_level) + reorder_qty = flt(reorder_qty) + projected_qty = item_warehouse_projected_qty[item_code][warehouse] - if material_requests: - create_material_request(material_requests) + if reorder_level and projected_qty < reorder_level: + deficiency = reorder_level - projected_qty + if deficiency > reorder_qty: + reorder_qty = deficiency + + company = warehouse_company.get(warehouse) or default_company + + material_requests[material_request_type].setdefault(company, []).append({ + "item_code": item_code, + "warehouse": warehouse, + "reorder_qty": reorder_qty + }) + + for item_code in item_warehouse_projected_qty: + item = frappe.get_doc("Item", item_code) + if item.get("item_reorder"): + for d in item.get("item_reorder"): + add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level, + d.warehouse_reorder_qty, d.material_request_type) + + else: + # raise for default warehouse + add_to_material_request(item_code, item.default_warehouse, item.re_order_level, item.re_order_qty, "Purchase") + + if material_requests: + create_material_request(material_requests) + +def get_item_warehouse_projected_qty(): + item_warehouse_projected_qty = {} + + for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, 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 + (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 + and ifnull(disabled, 0)=0)""", nowdate()): + + item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty) + + return item_warehouse_projected_qty def create_material_request(material_requests): """ Create indent on reaching reorder level """ @@ -263,6 +285,7 @@ def create_material_request(material_requests): }) for d in items: + d = frappe._dict(d) item = frappe.get_doc("Item", d.item_code) mr.append("indent_details", { "doctype": "Material Request Item", From 96cb130263cef2192fc2c3f5d81327e015b159e2 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 14 Jul 2014 14:52:49 +0530 Subject: [PATCH 29/53] Default letter head in general ledger and bank reco statement print --- .../bank_reconciliation_statement.html | 3 +++ erpnext/accounts/report/general_ledger/general_ledger.html | 3 +++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html index 91e07ab7da..1a401f5e68 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html @@ -1,3 +1,6 @@ +
+ {%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %} +

{%= __("Bank Reconciliation Statement") %}

{%= filters.account %}


diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 63cd1a19d2..fd5fa5fa54 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -1,3 +1,6 @@ +
+ {%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %} +

{%= __("Statement of Account") %}

{%= filters.account || "General Ledger" %}


From 143400d444cf51de9afa09fb8423531e2243ab7e Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 11:00:13 +0530 Subject: [PATCH 30/53] Batch no get query fix --- erpnext/stock/doctype/stock_entry/stock_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 2609ff5042..82a099eef6 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -676,9 +676,9 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): batch_nos = None args = { - 'item_code': filters['item_code'], - 's_warehouse': filters['s_warehouse'], - 'posting_date': filters['posting_date'], + 'item_code': filters.get("item_code"), + 's_warehouse': filters.get('s_warehouse'), + 'posting_date': filters,get('posting_date'), 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype), "start": start, From d16081c4d93ee0acbf39eacc3dc62b4b8cf8a027 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 11:08:19 +0530 Subject: [PATCH 31/53] Batch no get query fix --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 82a099eef6..7629c3cefd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -678,7 +678,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): args = { 'item_code': filters.get("item_code"), 's_warehouse': filters.get('s_warehouse'), - 'posting_date': filters,get('posting_date'), + 'posting_date': filters.get('posting_date'), 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype), "start": start, From fa1cd5faff541bad9ca99267a3d87cebf6f18dd1 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 11:52:50 +0530 Subject: [PATCH 32/53] Fixed root type of liability accounts --- erpnext/patches.txt | 1 + erpnext/setup/doctype/company/company.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b7fc5e7768..1a903de743 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -68,3 +68,4 @@ erpnext.patches.v4_1.set_outgoing_email_footer erpnext.patches.v4_1.fix_jv_remarks erpnext.patches.v4_1.fix_sales_order_delivered_status erpnext.patches.v4_1.fix_delivery_and_billing_status +execute:frappe.db.sql("update `tabAccount` set root_type='Liability' where root_type='Income' and report_type='Balance Sheet'") \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index d24c7e78e2..f8e043a927 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -241,7 +241,7 @@ class Company(Document): [_('Sales'),_('Direct Income'),'Ledger','Income Account','Profit and Loss', None, 'Income'], [_('Service'),_('Direct Income'),'Ledger','Income Account','Profit and Loss', None, 'Income'], [_('Indirect Income'),_('Income'),'Group','Income Account','Profit and Loss', None, 'Income'], - [_('Source of Funds (Liabilities)'), None,'Group', None,'Balance Sheet', None, 'Income'], + [_('Source of Funds (Liabilities)'), None,'Group', None,'Balance Sheet', None, 'Liability'], [_('Capital Account'),_('Source of Funds (Liabilities)'),'Group', None,'Balance Sheet', None, 'Liability'], [_('Reserves and Surplus'),_('Capital Account'),'Ledger', None,'Balance Sheet', None, 'Liability'], [_('Shareholders Funds'),_('Capital Account'),'Ledger', None,'Balance Sheet', None, 'Liability'], From e26204e61d94e39dc759df7c7ec6c6ae81cb06e3 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 12:22:58 +0530 Subject: [PATCH 33/53] From date and to date in general ledger print format --- erpnext/accounts/report/general_ledger/general_ledger.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index fd5fa5fa54..a639266819 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -2,7 +2,12 @@ {%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}

{%= __("Statement of Account") %}

-

{%= filters.account || "General Ledger" %}

+

{%= filters.account || "General Ledger" %}

+
+ {%= dateutil.str_to_user(filters.from_date) %} + {%= __(" to ") %} + {%= dateutil.str_to_user(filters.to_date) %} +

From 5f2f9552249394ca47adbc42b8e7c3651bd0a27d Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 12:29:56 +0530 Subject: [PATCH 34/53] From date and to date in general ledger print format --- erpnext/accounts/report/general_ledger/general_ledger.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index a639266819..190d45555a 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -5,7 +5,7 @@

{%= filters.account || "General Ledger" %}

{%= dateutil.str_to_user(filters.from_date) %} - {%= __(" to ") %} + {%= __("to") %} {%= dateutil.str_to_user(filters.to_date) %}

From fa0db761da07cb343896d7cf17b6ce91d06e1649 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 17 Jul 2014 12:30:55 +0530 Subject: [PATCH 35/53] Fix in Stock Entry test case --- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 1fdc016d7d..0f6a33fb73 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -664,7 +664,7 @@ class TestStockEntry(unittest.TestCase): def test_serial_no_not_exists(self): self._clear_stock_account_balance() frappe.db.sql("delete from `tabSerial No` where name in ('ABCD', 'EFGH')") - make_serialized_item() + make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC") se = frappe.copy_doc(test_records[0]) se.purpose = "Material Issue" se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series" From d4e080be291b59288645667592006c9479ad81e6 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 19:31:57 +0530 Subject: [PATCH 36/53] Country mandatory in company master --- erpnext/setup/doctype/company/company.json | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 51a1ac5ede..4209e7d770 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -53,6 +53,15 @@ "permlevel": 0, "reqd": 0 }, + { + "fieldname": "country", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Country", + "options": "Country", + "permlevel": 0, + "reqd": 1 + }, { "fieldname": "charts_section", "fieldtype": "Section Break", @@ -60,18 +69,10 @@ "label": "Chart of Accounts", "permlevel": 0 }, - { - "fieldname": "country", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Country", - "options": "Country", - "permlevel": 0, - "reqd": 0 - }, { "fieldname": "chart_of_accounts", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, "label": "Chart of Accounts", "options": "Chart of Accounts", @@ -348,7 +349,7 @@ ], "icon": "icon-building", "idx": 1, - "modified": "2014-05-27 03:49:08.597191", + "modified": "2014-07-17 19:30:24.487672", "modified_by": "Administrator", "module": "Setup", "name": "Company", From d141cf729366a8de14b6b910de4b426b1d090b63 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 18 Jul 2014 10:31:34 +0530 Subject: [PATCH 37/53] Minor fix in reorder item --- erpnext/stock/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 10f5cc2776..100d338fd6 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -192,7 +192,7 @@ def _reorder_item(): item_warehouse_projected_qty = get_item_warehouse_projected_qty() warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`""")) - default_company = (frappe.defaults.get_defaults()["company"] or + default_company = (frappe.defaults.get_defaults().get("company") or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]) def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type): From b1c5738a677fc982bbc700d2464b02e3a75aa55c Mon Sep 17 00:00:00 2001 From: nabinhait Date: Sat, 19 Jul 2014 16:53:45 +0530 Subject: [PATCH 38/53] Fixes overflow tolerance --- erpnext/controllers/status_updater.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 3c6355488f..7a4a30004b 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -152,7 +152,7 @@ class StatusUpdater(Document): overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) / item[args['target_ref_field']]) * 100 - + print overflow_percent - tolerance if overflow_percent - tolerance > 0.01: item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) item['reduce_by'] = item[args['target_field']] - item['max_allowed'] @@ -266,8 +266,7 @@ def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None): if not tolerance: if global_tolerance == None: - global_tolerance = flt(frappe.db.get_value('Global Defaults', None, - 'tolerance')) + global_tolerance = flt(frappe.db.get_value('Stock Settings', None, 'tolerance')) tolerance = global_tolerance item_tolerance[item_code] = tolerance From 711e8bec3df43c2893fb2ba4f88d621ea2475fb1 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Sat, 19 Jul 2014 17:00:15 +0530 Subject: [PATCH 39/53] Fixes overflow tolerance --- erpnext/controllers/status_updater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 7a4a30004b..6a2dce00fc 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -152,7 +152,7 @@ class StatusUpdater(Document): overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) / item[args['target_ref_field']]) * 100 - print overflow_percent - tolerance + if overflow_percent - tolerance > 0.01: item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) item['reduce_by'] = item[args['target_field']] - item['max_allowed'] From b1e7bf16bab7811fd82e718c28f2ff371e880986 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Sat, 19 Jul 2014 17:56:43 +0530 Subject: [PATCH 40/53] Translate while creating India specific accounts --- .../company/fixtures/india/__init__.py | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/erpnext/setup/doctype/company/fixtures/india/__init__.py b/erpnext/setup/doctype/company/fixtures/india/__init__.py index fa45ab0364..d52b30402e 100644 --- a/erpnext/setup/doctype/company/fixtures/india/__init__.py +++ b/erpnext/setup/doctype/company/fixtures/india/__init__.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ def install(company): docs = [ @@ -27,44 +28,45 @@ def install(company): 'group_or_ledger': 2, 'account_type': 3, 'report_type': 4, - 'tax_rate': 5 + 'tax_rate': 5, + 'root_type': 6 } acc_list_india = [ - ['CENVAT Capital Goods','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT Service Tax','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT Service Tax Cess 1','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT Service Tax Cess 2','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT Edu Cess','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['CENVAT SHE Cess','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['Excise Duty 4','Tax Assets','Ledger','Tax','Balance Sheet','4.00'], - ['Excise Duty 8','Tax Assets','Ledger','Tax','Balance Sheet','8.00'], - ['Excise Duty 10','Tax Assets','Ledger','Tax','Balance Sheet','10.00'], - ['Excise Duty 14','Tax Assets','Ledger','Tax','Balance Sheet','14.00'], - ['Excise Duty Edu Cess 2','Tax Assets','Ledger','Tax','Balance Sheet','2.00'], - ['Excise Duty SHE Cess 1','Tax Assets','Ledger','Tax','Balance Sheet','1.00'], - ['P L A','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['P L A - Cess Portion','Tax Assets','Ledger','Chargeable','Balance Sheet', None], - ['Edu. Cess on Excise','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'], - ['Edu. Cess on Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'], - ['Edu. Cess on TDS','Duties and Taxes','Ledger','Tax','Balance Sheet','2.00'], - ['Excise Duty @ 4','Duties and Taxes','Ledger','Tax','Balance Sheet','4.00'], - ['Excise Duty @ 8','Duties and Taxes','Ledger','Tax','Balance Sheet','8.00'], - ['Excise Duty @ 10','Duties and Taxes','Ledger','Tax','Balance Sheet','10.00'], - ['Excise Duty @ 14','Duties and Taxes','Ledger','Tax','Balance Sheet','14.00'], - ['Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','10.3'], - ['SHE Cess on Excise','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'], - ['SHE Cess on Service Tax','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'], - ['SHE Cess on TDS','Duties and Taxes','Ledger','Tax','Balance Sheet','1.00'], - ['Professional Tax','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['VAT','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Advertisement)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Commission)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Contractor)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Interest)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Rent)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None], - ['TDS (Salary)','Duties and Taxes','Ledger','Chargeable','Balance Sheet', None] + [_('CENVAT Capital Goods'),_(_('Tax Assets')),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT Service Tax'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT Service Tax Cess 1'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT Service Tax Cess 2'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT Edu Cess'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('CENVAT SHE Cess'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('Excise Duty 4'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','4.00', 'Asset'], + [_('Excise Duty 8'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','8.00', 'Asset'], + [_('Excise Duty 10'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','10.00', 'Asset'], + [_('Excise Duty 14'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','14.00', 'Asset'], + [_('Excise Duty Edu Cess 2'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','2.00', 'Asset'], + [_('Excise Duty SHE Cess 1'),_('Tax Assets'),'Ledger','Tax','Balance Sheet','1.00', 'Asset'], + [_('P L A'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('P L A - Cess Portion'),_('Tax Assets'),'Ledger','Chargeable','Balance Sheet', None, 'Asset'], + [_('Edu. Cess on Excise'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'], + [_('Edu. Cess on Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'], + [_('Edu. Cess on TDS'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','2.00', 'Liability'], + [_('Excise Duty @ 4'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','4.00', 'Liability'], + [_('Excise Duty @ 8'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','8.00', 'Liability'], + [_('Excise Duty @ 10'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','10.00', 'Liability'], + [_('Excise Duty @ 14'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','14.00', 'Liability'], + [_('Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','10.3', 'Liability'], + [_('SHE Cess on Excise'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'], + [_('SHE Cess on Service Tax'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'], + [_('SHE Cess on TDS'),_('Duties and Taxes'),'Ledger','Tax','Balance Sheet','1.00', 'Liability'], + [_('Professional Tax'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('VAT'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Advertisement)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Commission)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Contractor)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Interest)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Rent)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'], + [_('TDS (Salary)'),_('Duties and Taxes'),'Ledger','Chargeable','Balance Sheet', None, 'Liability'] ] for lst in acc_list_india: From 059f1e09a8252e9b31718b5449bcfc2304e95349 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 14 Jul 2014 19:06:52 +0530 Subject: [PATCH 41/53] Balance Sheet - first cut --- .../doctype/fiscal_year/fiscal_year.json | 6 +- .../accounts/report/balance_sheet/__init__.py | 0 .../report/balance_sheet/balance_sheet.js | 31 ++++++ .../report/balance_sheet/balance_sheet.json | 15 +++ .../report/balance_sheet/balance_sheet.py | 99 +++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 erpnext/accounts/report/balance_sheet/__init__.py create mode 100644 erpnext/accounts/report/balance_sheet/balance_sheet.js create mode 100644 erpnext/accounts/report/balance_sheet/balance_sheet.json create mode 100644 erpnext/accounts/report/balance_sheet/balance_sheet.py diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index dcd5a7608c..0f7aefd312 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -55,7 +55,7 @@ ], "icon": "icon-calendar", "idx": 1, - "modified": "2014-05-27 03:49:10.942338", + "modified": "2014-07-14 05:30:56.843180", "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year", @@ -82,5 +82,7 @@ "read": 1, "role": "All" } - ] + ], + "sort_field": "name", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/report/balance_sheet/__init__.py b/erpnext/accounts/report/balance_sheet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js new file mode 100644 index 0000000000..9bdd298077 --- /dev/null +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -0,0 +1,31 @@ +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.query_reports["Balance Sheet"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("company"), + "reqd": 1 + }, + { + "fieldname":"fiscal_year", + "label": __("Fiscal Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname": "periodicity", + "label": __("Periodicity"), + "fieldtype": "Select", + "options": "Yearly\nQuarterly\nMonthly", + "default": "Yearly", + "reqd": 1 + } + ] +} diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.json b/erpnext/accounts/report/balance_sheet/balance_sheet.json new file mode 100644 index 0000000000..af299bb29c --- /dev/null +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.json @@ -0,0 +1,15 @@ +{ + "apply_user_permissions": 1, + "creation": "2014-07-14 05:24:20.385279", + "docstatus": 0, + "doctype": "Report", + "is_standard": "Yes", + "modified": "2014-07-14 05:24:20.385279", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Balance Sheet", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "Balance Sheet", + "report_type": "Script Report" +} \ No newline at end of file diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py new file mode 100644 index 0000000000..dc71e92138 --- /dev/null +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -0,0 +1,99 @@ +# 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 frappe.utils import cstr, flt +from frappe import _ + +def execute(filters=None): + company = filters.company + fiscal_year = filters.fiscal_year + depth = 3 + end_date = frappe.db.get_value("Fiscal Year", fiscal_year, "year_end_date") + + for root_type, balance_must_be in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): + accounts, account_gl_entries = get_accounts_and_gl_entries(root_type, company, end_date) + if accounts: + accounts, accounts_map = filter_accounts(accounts, depth=depth) + + for d in accounts: + d.debit = d.credit = 0 + for account_name in ([d.name] + (d.invisible_children or [])): + for each in account_gl_entries.get(account_name, []): + d.debit += flt(each.debit) + d.credit += flt(each.credit) + + for d in reversed(accounts): + if d.parent_account: + accounts_map[d.parent_account]["debit"] += d.debit + accounts_map[d.parent_account]["credit"] += d.credit + + for d in accounts: + d.balance = d["debit"] - d["credit"] + if d.balance: + d.balance *= (1 if balance_must_be=="Debit" else -1) + print (" " * d["indent"] * 2) + d["account_name"], d["balance"], balance_must_be + + return [], [] + +def get_accounts_and_gl_entries(root_type, company, end_date): + # root lft, rgt + root_account = frappe.db.sql("""select lft, rgt from `tabAccount` + where company=%s and root_type=%s order by lft limit 1""", + (company, root_type), as_dict=True) + + if not root_account: + return None, None + + lft, rgt = root_account[0].lft, root_account[0].rgt + + accounts = frappe.db.sql("""select * from `tabAccount` + where company=%(company)s and lft >= %(lft)s and rgt <= %(rgt)s order by lft""", + { "company": company, "lft": lft, "rgt": rgt }, as_dict=True) + + gl_entries = frappe.db.sql("""select * from `tabGL Entry` + where company=%(company)s + and posting_date <= %(end_date)s + and account in (select name from `tabAccount` + where lft >= %(lft)s and rgt <= %(rgt)s)""", + { + "company": company, + "end_date": end_date, + "lft": lft, + "rgt": rgt + }, + as_dict=True) + + account_gl_entries = {} + for entry in gl_entries: + account_gl_entries.setdefault(entry.account, []).append(entry) + + return accounts, account_gl_entries + +def filter_accounts(accounts, depth): + parent_children_map = {} + accounts_map = {} + for d in accounts: + accounts_map[d.name] = d + parent_children_map.setdefault(d.parent_account or None, []).append(d) + + data = [] + def add_to_data(parent, level): + if level < depth: + for child in (parent_children_map.get(parent) or []): + child.indent = level + data.append(child) + add_to_data(child.name, level + 1) + + else: + # include all children at level lower than the depth + parent_account = accounts_map[parent] + parent_account["invisible_children"] = [] + for d in accounts: + if d.lft > parent_account.lft and d.rgt < parent_account.rgt: + parent_account["invisible_children"].append(d.name) + + add_to_data(None, 0) + + return data, accounts_map From fa576a22e333b192cdfcdc8f82626288812edba6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 17 Jul 2014 19:12:28 +0530 Subject: [PATCH 42/53] Balance Sheet --- .../report/balance_sheet/balance_sheet.html | 46 +++++++ .../report/balance_sheet/balance_sheet.js | 44 ++++++- .../report/balance_sheet/balance_sheet.py | 120 +++++++++++++++--- erpnext/config/accounts.py | 6 + 4 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 erpnext/accounts/report/balance_sheet/balance_sheet.html diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.html b/erpnext/accounts/report/balance_sheet/balance_sheet.html new file mode 100644 index 0000000000..a6a33f594e --- /dev/null +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.html @@ -0,0 +1,46 @@ + + +

{%= __("Balance Sheet") %}

+

{%= filters.fiscal_year %}

+
+
+ + + + {% for(var i=2, l=report.columns.length; i{%= report.columns[i].label %} + {% } %} + + + + {% for(var j=0, k=data.length; j + + {% for(var i=2, l=report.columns.length; i + {% var fieldname = report.columns[i].field; %} + {% if (!is_null(row[fieldname])) { %} + {%= format_currency(row[fieldname]) %} + {% } %} + + {% } %} + + {% } %} + +
+ {%= row.account_name %} +
+

Printed On {%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}

diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 9bdd298077..378a687378 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -1,7 +1,9 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.query_reports["Balance Sheet"] = { +frappe.provide("erpnext.balance_sheet"); + +erpnext.balance_sheet = frappe.query_reports["Balance Sheet"] = { "filters": [ { "fieldname":"company", @@ -26,6 +28,44 @@ frappe.query_reports["Balance Sheet"] = { "options": "Yearly\nQuarterly\nMonthly", "default": "Yearly", "reqd": 1 + }, + { + "fieldname": "depth", + "label": __("Depth"), + "fieldtype": "Select", + "options": "3\n4\n5", + "default": "3" } - ] + ], + "formatter": function(row, cell, value, columnDef, dataContext) { + if (columnDef.df.fieldname=="account") { + var link = $("") + .text(dataContext.account_name) + .attr("onclick", 'erpnext.balance_sheet.open_general_ledger("' + dataContext.account + '")'); + + var span = $("") + .css("padding-left", (cint(dataContext.indent) * 21) + "px") + .append(link); + + value = span.wrap("

").parent().html(); + + } else { + value = frappe.query_reports["Balance Sheet"].default_formatter(row, cell, value, columnDef, dataContext); + } + + if (!dataContext.parent_account) { + value = $(value).css("font-weight", "bold").wrap("

").parent().html(); + } + + return value; + }, + "open_general_ledger": function(account) { + if (!account) return; + + frappe.route_options = { + "account": account, + "company": frappe.query_report.filters_by_name.company.get_value() + }; + frappe.set_route("query-report", "General Ledger"); + } } diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index dc71e92138..3bf424c89b 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -2,40 +2,76 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +import babel.dates import frappe -from frappe.utils import cstr, flt +from frappe.utils import (cstr, flt, cint, + getdate, get_first_day, get_last_day, add_months, add_days, now_datetime) from frappe import _ def execute(filters=None): company = filters.company fiscal_year = filters.fiscal_year - depth = 3 - end_date = frappe.db.get_value("Fiscal Year", fiscal_year, "year_end_date") + depth = cint(filters.depth) or 3 + start_date, end_date = frappe.db.get_value("Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]) + period_list = get_period_list(start_date, end_date, filters.get("periodicity") or "Yearly", fiscal_year) - for root_type, balance_must_be in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): + out = [] + for (root_type, balance_must_be) in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): + data = [] accounts, account_gl_entries = get_accounts_and_gl_entries(root_type, company, end_date) if accounts: accounts, accounts_map = filter_accounts(accounts, depth=depth) for d in accounts: - d.debit = d.credit = 0 for account_name in ([d.name] + (d.invisible_children or [])): for each in account_gl_entries.get(account_name, []): - d.debit += flt(each.debit) - d.credit += flt(each.credit) + for period_start_date, period_end_date, period_key, period_label in period_list: + each.posting_date = getdate(each.posting_date) + + # check if posting date is within the period + if ((not period_start_date or (each.posting_date >= period_start_date)) + and (each.posting_date <= period_end_date)): + + d[period_key] = d.get(period_key, 0.0) + flt(each.debit) - flt(each.credit) for d in reversed(accounts): if d.parent_account: - accounts_map[d.parent_account]["debit"] += d.debit - accounts_map[d.parent_account]["credit"] += d.credit + for period_start_date, period_end_date, period_key, period_label in period_list: + accounts_map[d.parent_account][period_key] = accounts_map[d.parent_account].get(period_key, 0.0) + d.get(period_key, 0.0) - for d in accounts: - d.balance = d["debit"] - d["credit"] - if d.balance: - d.balance *= (1 if balance_must_be=="Debit" else -1) - print (" " * d["indent"] * 2) + d["account_name"], d["balance"], balance_must_be + for i, d in enumerate(accounts): + has_value = False + row = {"account_name": d["account_name"], "account": d["name"], "indent": d["indent"], "parent_account": d["parent_account"]} + for period_start_date, period_end_date, period_key, period_label in period_list: + if d.get(period_key): + d[period_key] *= (1 if balance_must_be=="Debit" else -1) - return [], [] + row[period_key] = d.get(period_key, 0.0) + if row[period_key]: + has_value = True + + if has_value: + data.append(row) + + if data: + row = {"account_name": _("Total ({0})").format(balance_must_be), "account": None} + for period_start_date, period_end_date, period_key, period_label in period_list: + if period_key in data[0]: + row[period_key] = data[0].get(period_key, 0.0) + data[0][period_key] = "" + + data.append(row) + + # blank row after Total + data.append({}) + + out.extend(data) + + columns = [{"fieldname": "account", "label": _("Account"), "fieldtype": "Link", "options": "Account", "width": 300}] + for period_start_date, period_end_date, period_key, period_label in period_list: + columns.append({"fieldname": period_key, "label": period_label, "fieldtype": "Currency", "width": 150}) + + return columns, out def get_accounts_and_gl_entries(root_type, company, end_date): # root lft, rgt @@ -97,3 +133,57 @@ def filter_accounts(accounts, depth): add_to_data(None, 0) return data, accounts_map + +def get_period_list(start_date, end_date, periodicity, fiscal_year): + """Get a list of tuples that represents (period_start_date, period_end_date, period_key) + Periodicity can be (Yearly, Quarterly, Monthly)""" + + start_date = getdate(start_date) + end_date = getdate(end_date) + today = now_datetime().date() + + if periodicity == "Yearly": + period_list = [(None, end_date, fiscal_year, fiscal_year)] + else: + months_to_add = { + "Half-yearly": 6, + "Quarterly": 3, + "Monthly": 1 + }[periodicity] + + period_list = [] + + # start with first day, so as to avoid year start dates like 2-April if every they occur + next_date = get_first_day(start_date) + + for i in xrange(12 / months_to_add): + next_date = add_months(next_date, months_to_add) + + if next_date == get_first_day(next_date): + # if first day, get the last day of previous month + next_date = add_days(next_date, -1) + else: + # get the last day of the month + next_date = get_last_day(next_date) + + # checking in the middle of the fiscal year? don't show future periods + if next_date > today: + break + + elif next_date <= end_date: + key = next_date.strftime("%b_%Y").lower() + label = babel.dates.format_date(next_date, "MMM YYYY", locale=(frappe.local.lang or "").replace("-", "_")) + period_list.append((None, next_date, key, label)) + + # if it ends before a full year + if next_date == end_date: + break + + else: + # if it ends before a full year + key = end_date.strftime("%b_%Y").lower() + label = babel.dates.format_date(end_date, "MMM YYYY", locale=(frappe.local.lang or "").replace("-", "_")) + period_list.append((None, end_date, key, label)) + break + + return period_list diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 2d757f1ef5..0e3e2d4ae5 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -195,6 +195,12 @@ def get_data(): "doctype": "Purchase Invoice", "is_query_report": True }, + { + "type": "report", + "name": "Balance Sheet", + "doctype": "GL Entry", + "is_query_report": True + }, { "type": "page", "name": "financial-analytics", From 825d01461663a486293f9db869272d47614e0ea8 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 18 Jul 2014 18:05:26 +0530 Subject: [PATCH 43/53] Commonfied code for Financial Statements and added Profit and Loss Statement --- .../report/balance_sheet/balance_sheet.js | 69 +---- .../report/balance_sheet/balance_sheet.py | 186 +------------ ...e_sheet.html => financial_statements.html} | 21 +- .../accounts/report/financial_statements.py | 252 ++++++++++++++++++ .../profit_and_loss_statement/__init__.py | 0 .../profit_and_loss_statement.js | 6 + .../profit_and_loss_statement.json | 17 ++ .../profit_and_loss_statement.py | 42 +++ erpnext/config/accounts.py | 6 + erpnext/public/js/financial_statements.js | 68 +++++ 10 files changed, 417 insertions(+), 250 deletions(-) rename erpnext/accounts/report/{balance_sheet/balance_sheet.html => financial_statements.html} (56%) create mode 100644 erpnext/accounts/report/financial_statements.py create mode 100644 erpnext/accounts/report/profit_and_loss_statement/__init__.py create mode 100644 erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js create mode 100644 erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.json create mode 100644 erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py create mode 100644 erpnext/public/js/financial_statements.js diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 378a687378..a28008e9b6 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -1,71 +1,6 @@ // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.provide("erpnext.balance_sheet"); +frappe.require("assets/erpnext/js/financial_statements.js"); -erpnext.balance_sheet = frappe.query_reports["Balance Sheet"] = { - "filters": [ - { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("company"), - "reqd": 1 - }, - { - "fieldname":"fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), - "reqd": 1 - }, - { - "fieldname": "periodicity", - "label": __("Periodicity"), - "fieldtype": "Select", - "options": "Yearly\nQuarterly\nMonthly", - "default": "Yearly", - "reqd": 1 - }, - { - "fieldname": "depth", - "label": __("Depth"), - "fieldtype": "Select", - "options": "3\n4\n5", - "default": "3" - } - ], - "formatter": function(row, cell, value, columnDef, dataContext) { - if (columnDef.df.fieldname=="account") { - var link = $("") - .text(dataContext.account_name) - .attr("onclick", 'erpnext.balance_sheet.open_general_ledger("' + dataContext.account + '")'); - - var span = $("") - .css("padding-left", (cint(dataContext.indent) * 21) + "px") - .append(link); - - value = span.wrap("

").parent().html(); - - } else { - value = frappe.query_reports["Balance Sheet"].default_formatter(row, cell, value, columnDef, dataContext); - } - - if (!dataContext.parent_account) { - value = $(value).css("font-weight", "bold").wrap("

").parent().html(); - } - - return value; - }, - "open_general_ledger": function(account) { - if (!account) return; - - frappe.route_options = { - "account": account, - "company": frappe.query_report.filters_by_name.company.get_value() - }; - frappe.set_route("query-report", "General Ledger"); - } -} +frappe.query_reports["Balance Sheet"] = erpnext.financial_statements; diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 3bf424c89b..dd6abfd01c 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -2,188 +2,22 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import babel.dates import frappe -from frappe.utils import (cstr, flt, cint, - getdate, get_first_day, get_last_day, add_months, add_days, now_datetime) -from frappe import _ +from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) + +print_path = "accounts/report/financial_statements.html" def execute(filters=None): - company = filters.company - fiscal_year = filters.fiscal_year - depth = cint(filters.depth) or 3 - start_date, end_date = frappe.db.get_value("Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]) - period_list = get_period_list(start_date, end_date, filters.get("periodicity") or "Yearly", fiscal_year) - - out = [] - for (root_type, balance_must_be) in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): - data = [] - accounts, account_gl_entries = get_accounts_and_gl_entries(root_type, company, end_date) - if accounts: - accounts, accounts_map = filter_accounts(accounts, depth=depth) - - for d in accounts: - for account_name in ([d.name] + (d.invisible_children or [])): - for each in account_gl_entries.get(account_name, []): - for period_start_date, period_end_date, period_key, period_label in period_list: - each.posting_date = getdate(each.posting_date) - - # check if posting date is within the period - if ((not period_start_date or (each.posting_date >= period_start_date)) - and (each.posting_date <= period_end_date)): - - d[period_key] = d.get(period_key, 0.0) + flt(each.debit) - flt(each.credit) - - for d in reversed(accounts): - if d.parent_account: - for period_start_date, period_end_date, period_key, period_label in period_list: - accounts_map[d.parent_account][period_key] = accounts_map[d.parent_account].get(period_key, 0.0) + d.get(period_key, 0.0) - - for i, d in enumerate(accounts): - has_value = False - row = {"account_name": d["account_name"], "account": d["name"], "indent": d["indent"], "parent_account": d["parent_account"]} - for period_start_date, period_end_date, period_key, period_label in period_list: - if d.get(period_key): - d[period_key] *= (1 if balance_must_be=="Debit" else -1) - - row[period_key] = d.get(period_key, 0.0) - if row[period_key]: - has_value = True - - if has_value: - data.append(row) - - if data: - row = {"account_name": _("Total ({0})").format(balance_must_be), "account": None} - for period_start_date, period_end_date, period_key, period_label in period_list: - if period_key in data[0]: - row[period_key] = data[0].get(period_key, 0.0) - data[0][period_key] = "" - - data.append(row) - - # blank row after Total - data.append({}) - - out.extend(data) - - columns = [{"fieldname": "account", "label": _("Account"), "fieldtype": "Link", "options": "Account", "width": 300}] - for period_start_date, period_end_date, period_key, period_label in period_list: - columns.append({"fieldname": period_key, "label": period_label, "fieldtype": "Currency", "width": 150}) - - return columns, out - -def get_accounts_and_gl_entries(root_type, company, end_date): - # root lft, rgt - root_account = frappe.db.sql("""select lft, rgt from `tabAccount` - where company=%s and root_type=%s order by lft limit 1""", - (company, root_type), as_dict=True) - - if not root_account: - return None, None - - lft, rgt = root_account[0].lft, root_account[0].rgt - - accounts = frappe.db.sql("""select * from `tabAccount` - where company=%(company)s and lft >= %(lft)s and rgt <= %(rgt)s order by lft""", - { "company": company, "lft": lft, "rgt": rgt }, as_dict=True) - - gl_entries = frappe.db.sql("""select * from `tabGL Entry` - where company=%(company)s - and posting_date <= %(end_date)s - and account in (select name from `tabAccount` - where lft >= %(lft)s and rgt <= %(rgt)s)""", - { - "company": company, - "end_date": end_date, - "lft": lft, - "rgt": rgt - }, - as_dict=True) - - account_gl_entries = {} - for entry in gl_entries: - account_gl_entries.setdefault(entry.account, []).append(entry) - - return accounts, account_gl_entries - -def filter_accounts(accounts, depth): - parent_children_map = {} - accounts_map = {} - for d in accounts: - accounts_map[d.name] = d - parent_children_map.setdefault(d.parent_account or None, []).append(d) + process_filters(filters) + period_list = get_period_list(filters.fiscal_year, filters.periodicity, from_beginning=True) data = [] - def add_to_data(parent, level): - if level < depth: - for child in (parent_children_map.get(parent) or []): - child.indent = level - data.append(child) - add_to_data(child.name, level + 1) + for (root_type, balance_must_be) in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): + result = get_data(filters.company, root_type, balance_must_be, period_list, filters.depth) + data.extend(result or []) - else: - # include all children at level lower than the depth - parent_account = accounts_map[parent] - parent_account["invisible_children"] = [] - for d in accounts: - if d.lft > parent_account.lft and d.rgt < parent_account.rgt: - parent_account["invisible_children"].append(d.name) + columns = get_columns(period_list) - add_to_data(None, 0) + return columns, data - return data, accounts_map -def get_period_list(start_date, end_date, periodicity, fiscal_year): - """Get a list of tuples that represents (period_start_date, period_end_date, period_key) - Periodicity can be (Yearly, Quarterly, Monthly)""" - - start_date = getdate(start_date) - end_date = getdate(end_date) - today = now_datetime().date() - - if periodicity == "Yearly": - period_list = [(None, end_date, fiscal_year, fiscal_year)] - else: - months_to_add = { - "Half-yearly": 6, - "Quarterly": 3, - "Monthly": 1 - }[periodicity] - - period_list = [] - - # start with first day, so as to avoid year start dates like 2-April if every they occur - next_date = get_first_day(start_date) - - for i in xrange(12 / months_to_add): - next_date = add_months(next_date, months_to_add) - - if next_date == get_first_day(next_date): - # if first day, get the last day of previous month - next_date = add_days(next_date, -1) - else: - # get the last day of the month - next_date = get_last_day(next_date) - - # checking in the middle of the fiscal year? don't show future periods - if next_date > today: - break - - elif next_date <= end_date: - key = next_date.strftime("%b_%Y").lower() - label = babel.dates.format_date(next_date, "MMM YYYY", locale=(frappe.local.lang or "").replace("-", "_")) - period_list.append((None, next_date, key, label)) - - # if it ends before a full year - if next_date == end_date: - break - - else: - # if it ends before a full year - key = end_date.strftime("%b_%Y").lower() - label = babel.dates.format_date(end_date, "MMM YYYY", locale=(frappe.local.lang or "").replace("-", "_")) - period_list.append((None, end_date, key, label)) - break - - return period_list diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.html b/erpnext/accounts/report/financial_statements.html similarity index 56% rename from erpnext/accounts/report/balance_sheet/balance_sheet.html rename to erpnext/accounts/report/financial_statements.html index a6a33f594e..403e67e5bb 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.html +++ b/erpnext/accounts/report/financial_statements.html @@ -1,20 +1,27 @@ +{% + if (report.columns.length > 6) { + frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application.")); + } +%} + -

{%= __("Balance Sheet") %}

+

{%= __(report.report_name) %}

+

{%= filters.company %}

{%= filters.fiscal_year %}


- + {% for(var i=2, l=report.columns.length; i{%= report.columns[i].label %} {% } %} @@ -24,12 +31,12 @@ {% for(var j=0, k=data.length; j {% for(var i=2, l=report.columns.length; i diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py new file mode 100644 index 0000000000..3490146eec --- /dev/null +++ b/erpnext/accounts/report/financial_statements.py @@ -0,0 +1,252 @@ +# 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 frappe import _, _dict +from frappe.utils import (cstr, flt, cint, + getdate, get_first_day, get_last_day, add_months, add_days, now_datetime, localize_date) + +def process_filters(filters): + filters.depth = cint(filters.depth) or 3 + if not filters.periodicity: + filters.periodicity = "Yearly" + +def get_period_list(fiscal_year, periodicity, from_beginning=False): + """Get a list of dict {"to_date": to_date, "key": key, "label": label} + Periodicity can be (Yearly, Quarterly, Monthly)""" + + start_date, end_date = frappe.db.get_value("Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]) + start_date = getdate(start_date) + end_date = getdate(end_date) + today = now_datetime().date() + + if periodicity == "Yearly": + period_list = [_dict({"to_date": end_date, "key": fiscal_year, "label": fiscal_year})] + else: + months_to_add = { + "Half-yearly": 6, + "Quarterly": 3, + "Monthly": 1 + }[periodicity] + + period_list = [] + + # start with first day, so as to avoid year to_dates like 2-April if ever they occur + to_date = get_first_day(start_date) + + for i in xrange(12 / months_to_add): + to_date = add_months(to_date, months_to_add) + + if to_date == get_first_day(to_date): + # if to_date is the first day, get the last day of previous month + to_date = add_days(to_date, -1) + else: + # to_date should be the last day of the new to_date's month + to_date = get_last_day(to_date) + + if to_date > today: + # checking in the middle of the currenct fiscal year? don't show future periods + key = today.strftime("%b_%Y").lower() + label = localize_date(today, "MMM YYYY") + period_list.append(_dict({"to_date": today, "key": key, "label": label})) + break + + elif to_date <= end_date: + # the normal case + key = to_date.strftime("%b_%Y").lower() + label = localize_date(to_date, "MMM YYYY") + period_list.append(_dict({"to_date": to_date, "key": key, "label": label})) + + # if it ends before a full year + if to_date == end_date: + break + + else: + # if a fiscal year ends before a 12 month period + key = end_date.strftime("%b_%Y").lower() + label = localize_date(end_date, "MMM YYYY") + period_list.append(_dict({"to_date": end_date, "key": key, "label": label})) + break + + # common processing + for opts in period_list: + opts["key"] = opts["key"].replace(" ", "_").replace("-", "_") + + if from_beginning: + # set start date as None for all fiscal periods, used in case of Balance Sheet + opts["from_date"] = None + else: + opts["from_date"] = start_date + + return period_list + +def get_data(company, root_type, balance_must_be, period_list, depth, ignore_opening_and_closing_entries=False): + accounts = get_accounts(company, root_type) + if not accounts: + return None + + accounts, accounts_by_name = filter_accounts(accounts, depth) + gl_entries_by_account = get_gl_entries(company, root_type, period_list[0]["from_date"], period_list[-1]["to_date"], + accounts[0].lft, accounts[0].rgt, ignore_opening_and_closing_entries=ignore_opening_and_closing_entries) + + calculate_values(accounts, gl_entries_by_account, period_list) + accumulate_values_into_parents(accounts, accounts_by_name, period_list) + out = prepare_data(accounts, balance_must_be, period_list) + + if out: + add_total_row(out, balance_must_be, period_list) + + return out + +def calculate_values(accounts, gl_entries_by_account, period_list): + for d in accounts: + for name in ([d.name] + (d.collapsed_children or [])): + for entry in gl_entries_by_account.get(name, []): + for period in period_list: + entry.posting_date = getdate(entry.posting_date) + + # check if posting date is within the period + if entry.posting_date <= period.to_date: + d[period.key] = d.get(period.key, 0.0) + flt(entry.debit) - flt(entry.credit) + + +def accumulate_values_into_parents(accounts, accounts_by_name, period_list): + """accumulate children's values in parent accounts""" + for d in reversed(accounts): + if d.parent_account: + for period in period_list: + accounts_by_name[d.parent_account][period.key] = accounts_by_name[d.parent_account].get(period.key, 0.0) + \ + d.get(period.key, 0.0) + +def prepare_data(accounts, balance_must_be, period_list): + out = [] + for d in accounts: + # add to output + has_value = False + row = { + "account_name": d.account_name, + "account": d.name, + "parent_account": d.parent_account, + "indent": flt(d.indent) + } + for period in period_list: + if d.get(period.key): + # change sign based on Debit or Credit, since calculation is done using (debit - credit) + d[period.key] *= (1 if balance_must_be=="Debit" else -1) + has_value = True + + row[period.key] = flt(d.get(period.key, 0.0), 3) + + if has_value: + out.append(row) + + return out + +def add_total_row(out, balance_must_be, period_list): + row = { + "account_name": _("Total ({0})").format(balance_must_be), + "account": None + } + for period in period_list: + row[period.key] = out[0].get(period.key, 0.0) + out[0][period.key] = "" + + out.append(row) + + # blank row after Total + out.append({}) + +def get_accounts(company, root_type): + # root lft, rgt + root_account = frappe.db.sql("""select lft, rgt from `tabAccount` + where company=%s and root_type=%s order by lft limit 1""", + (company, root_type), as_dict=True) + + if not root_account: + return None + + lft, rgt = root_account[0].lft, root_account[0].rgt + + accounts = frappe.db.sql("""select * from `tabAccount` + where company=%(company)s and lft >= %(lft)s and rgt <= %(rgt)s order by lft""", + { "company": company, "lft": lft, "rgt": rgt }, as_dict=True) + + return accounts + +def filter_accounts(accounts, depth): + parent_children_map = {} + accounts_by_name = {} + for d in accounts: + accounts_by_name[d.name] = d + parent_children_map.setdefault(d.parent_account or None, []).append(d) + + filtered_accounts = [] + def add_to_list(parent, level): + if level < depth: + for child in (parent_children_map.get(parent) or []): + child.indent = level + filtered_accounts.append(child) + add_to_list(child.name, level + 1) + + else: + # include all children at level lower than the depth + parent_account = accounts_by_name[parent] + parent_account["collapsed_children"] = [] + for d in accounts: + if d.lft > parent_account.lft and d.rgt < parent_account.rgt: + parent_account["collapsed_children"].append(d.name) + + add_to_list(None, 0) + + return filtered_accounts, accounts_by_name + +def get_gl_entries(company, root_type, from_date, to_date, root_lft, root_rgt, ignore_opening_and_closing_entries=False): + """Returns a dict like { "account": [gl entries], ... }""" + additional_conditions = [] + + if ignore_opening_and_closing_entries: + additional_conditions.append("and ifnull(is_opening, 'No')='No' and ifnull(voucher_type, '')!='Period Closing Voucher'") + + if from_date: + additional_conditions.append("and posting_date >= %(from_date)s") + + gl_entries = frappe.db.sql("""select * from `tabGL Entry` + where company=%(company)s + {additional_conditions} + and posting_date <= %(to_date)s + and account in (select name from `tabAccount` + where lft >= %(lft)s and rgt <= %(rgt)s) + order by account, posting_date""".format(additional_conditions="\n".join(additional_conditions)), + { + "company": company, + "from_date": from_date, + "to_date": to_date, + "lft": root_lft, + "rgt": root_rgt + }, + as_dict=True) + + gl_entries_by_account = {} + for entry in gl_entries: + gl_entries_by_account.setdefault(entry.account, []).append(entry) + + return gl_entries_by_account + +def get_columns(period_list): + columns = [{ + "fieldname": "account", + "label": _("Account"), + "fieldtype": "Link", + "options": "Account", + "width": 300 + }] + for period in period_list: + columns.append({ + "fieldname": period.key, + "label": period.label, + "fieldtype": "Currency", + "width": 150 + }) + + return columns diff --git a/erpnext/accounts/report/profit_and_loss_statement/__init__.py b/erpnext/accounts/report/profit_and_loss_statement/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js new file mode 100644 index 0000000000..d047fea5fb --- /dev/null +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -0,0 +1,6 @@ +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.require("assets/erpnext/js/financial_statements.js"); + +frappe.query_reports["Profit and Loss Statement"] = erpnext.financial_statements; diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.json b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.json new file mode 100644 index 0000000000..a7608d8cae --- /dev/null +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.json @@ -0,0 +1,17 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2014-07-18 11:43:33.173207", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "is_standard": "Yes", + "modified": "2014-07-18 11:43:33.173207", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Profit and Loss Statement", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "Profit and Loss Statement", + "report_type": "Script Report" +} \ No newline at end of file diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py new file mode 100644 index 0000000000..556883661b --- /dev/null +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -0,0 +1,42 @@ +# 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 frappe import _ +from frappe.utils import flt +from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) + +print_path = "accounts/report/financial_statements.html" + +def execute(filters=None): + process_filters(filters) + period_list = get_period_list(filters.fiscal_year, filters.periodicity) + + data = [] + income = get_data(filters.company, "Income", "Credit", period_list, filters.depth, + ignore_opening_and_closing_entries=True) + expense = get_data(filters.company, "Expense", "Debit", period_list, filters.depth, + ignore_opening_and_closing_entries=True) + net_total = get_net_total(income, expense, period_list) + + data.extend(income or []) + data.extend(expense or []) + if net_total: + data.append(net_total) + + columns = get_columns(period_list) + + return columns, data + +def get_net_total(income, expense, period_list): + if income and expense: + net_total = { + "account_name": _("Net Profit / Loss"), + "account": None + } + + for period in period_list: + net_total[period.key] = flt(income[-2][period.key] - expense[-2][period.key], 3) + + return net_total diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 0e3e2d4ae5..722bf77f83 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -201,6 +201,12 @@ def get_data(): "doctype": "GL Entry", "is_query_report": True }, + { + "type": "report", + "name": "Profit and Loss Statement", + "doctype": "GL Entry", + "is_query_report": True + }, { "type": "page", "name": "financial-analytics", diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js new file mode 100644 index 0000000000..dbe41ff345 --- /dev/null +++ b/erpnext/public/js/financial_statements.js @@ -0,0 +1,68 @@ +frappe.provide("erpnext.financial_statements"); + +erpnext.financial_statements = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("company"), + "reqd": 1 + }, + { + "fieldname":"fiscal_year", + "label": __("Fiscal Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname": "periodicity", + "label": __("Periodicity"), + "fieldtype": "Select", + "options": "Yearly\nHalf-yearly\nQuarterly\nMonthly", + "default": "Yearly", + "reqd": 1 + }, + { + "fieldname": "depth", + "label": __("Depth"), + "fieldtype": "Select", + "options": "3\n4\n5", + "default": "3" + } + ], + "formatter": function(row, cell, value, columnDef, dataContext) { + if (columnDef.df.fieldname=="account") { + var link = $("") + .text(dataContext.account_name) + .attr("onclick", 'erpnext.financial_statements.open_general_ledger("' + dataContext.account + '")'); + + var span = $("") + .css("padding-left", (cint(dataContext.indent) * 21) + "px") + .append(link); + + value = span.wrap("

").parent().html(); + + } else { + value = erpnext.financial_statements.default_formatter(row, cell, value, columnDef, dataContext); + } + + if (!dataContext.parent_account) { + value = $(value).css("font-weight", "bold").wrap("

").parent().html(); + } + + return value; + }, + "open_general_ledger": function(account) { + if (!account) return; + + frappe.route_options = { + "account": account, + "company": frappe.query_report.filters_by_name.company.get_value() + }; + frappe.set_route("query-report", "General Ledger"); + } +}; From db4ba39824dd0a188c743aadfdc9404a26a80a31 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 18 Jul 2014 18:20:44 +0530 Subject: [PATCH 44/53] Better handling of ignore zero values --- erpnext/accounts/report/financial_statements.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 3490146eec..59b28da779 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -134,10 +134,13 @@ def prepare_data(accounts, balance_must_be, period_list): if d.get(period.key): # change sign based on Debit or Credit, since calculation is done using (debit - credit) d[period.key] *= (1 if balance_must_be=="Debit" else -1) - has_value = True row[period.key] = flt(d.get(period.key, 0.0), 3) + if abs(row[period.key]) >= 0.005: + # ignore zero values + has_value = True + if has_value: out.append(row) From 6cc5babd2e67731a2c1248fb6fcf55c86cf3e237 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 21 Jul 2014 11:43:03 +0530 Subject: [PATCH 45/53] Use include directive to embed common print format --- erpnext/accounts/report/balance_sheet/balance_sheet.html | 1 + erpnext/accounts/report/balance_sheet/balance_sheet.py | 2 -- .../profit_and_loss_statement/profit_and_loss_statement.html | 1 + .../profit_and_loss_statement/profit_and_loss_statement.py | 2 -- 4 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 erpnext/accounts/report/balance_sheet/balance_sheet.html create mode 100644 erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.html diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.html b/erpnext/accounts/report/balance_sheet/balance_sheet.html new file mode 100644 index 0000000000..d4ae54d4f3 --- /dev/null +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.html @@ -0,0 +1 @@ +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index dd6abfd01c..425257ffb5 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals import frappe from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) -print_path = "accounts/report/financial_statements.html" - def execute(filters=None): process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity, from_beginning=True) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.html b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.html new file mode 100644 index 0000000000..d4ae54d4f3 --- /dev/null +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.html @@ -0,0 +1 @@ +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 556883661b..9886f2f497 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -7,8 +7,6 @@ from frappe import _ from frappe.utils import flt from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) -print_path = "accounts/report/financial_statements.html" - def execute(filters=None): process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity) From f457bce0e7a97110d2d82d7bba984449a654fc9d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 21 Jul 2014 12:03:04 +0530 Subject: [PATCH 46/53] Changed localize_date to formatdate --- erpnext/accounts/report/financial_statements.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 59b28da779..30650fff7d 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -4,8 +4,8 @@ from __future__ import unicode_literals import frappe from frappe import _, _dict -from frappe.utils import (cstr, flt, cint, - getdate, get_first_day, get_last_day, add_months, add_days, now_datetime, localize_date) +from frappe.utils import (flt, cint, getdate, get_first_day, get_last_day, + add_months, add_days, now_datetime, formatdate) def process_filters(filters): filters.depth = cint(filters.depth) or 3 @@ -48,14 +48,14 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): if to_date > today: # checking in the middle of the currenct fiscal year? don't show future periods key = today.strftime("%b_%Y").lower() - label = localize_date(today, "MMM YYYY") + label = formatdate(today, "MMM YYYY") period_list.append(_dict({"to_date": today, "key": key, "label": label})) break elif to_date <= end_date: # the normal case key = to_date.strftime("%b_%Y").lower() - label = localize_date(to_date, "MMM YYYY") + label = formatdate(to_date, "MMM YYYY") period_list.append(_dict({"to_date": to_date, "key": key, "label": label})) # if it ends before a full year @@ -65,7 +65,7 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): else: # if a fiscal year ends before a 12 month period key = end_date.strftime("%b_%Y").lower() - label = localize_date(end_date, "MMM YYYY") + label = formatdate(end_date, "MMM YYYY") period_list.append(_dict({"to_date": end_date, "key": key, "label": label})) break From 1c6313679b32adb03a71e4bb9eae2563ef1a7034 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 21 Jul 2014 13:08:49 +0530 Subject: [PATCH 47/53] Update material_request.js --- erpnext/stock/doctype/material_request/material_request.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index e53a92aad7..7a47765a2c 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -11,8 +11,7 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten this._super(); this.frm.set_query("item_code", this.frm.cscript.fname, function() { return { - query: "erpnext.controllers.queries.item_query", - filters: {'is_stock_item': 'Yes'} + query: "erpnext.controllers.queries.item_query" } }); }, From 5f0459c32176e851f9f01ce3f17417d35d3c26e4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 21 Jul 2014 16:13:06 +0530 Subject: [PATCH 48/53] Fixes to Financial Statements --- .../report/balance_sheet/balance_sheet.py | 38 +++++++++++++-- .../accounts/report/financial_statements.py | 46 +++++++++---------- .../profit_and_loss_statement.py | 25 +++++----- erpnext/public/js/financial_statements.js | 19 +++++--- 4 files changed, 83 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 425257ffb5..4edc9b9515 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -3,19 +3,51 @@ from __future__ import unicode_literals import frappe +from frappe import _ +from frappe.utils import flt from erpnext.accounts.report.financial_statements import (process_filters, get_period_list, get_columns, get_data) def execute(filters=None): process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity, from_beginning=True) + asset = get_data(filters.company, "Asset", "Debit", period_list, filters.depth) + liability = get_data(filters.company, "Liability", "Credit", period_list, filters.depth) + equity = get_data(filters.company, "Equity", "Credit", period_list, filters.depth) + provisional_profit_loss = get_provisional_profit_loss(asset, liability, equity, period_list) + data = [] - for (root_type, balance_must_be) in (("Asset", "Debit"), ("Liability", "Credit"), ("Equity", "Credit")): - result = get_data(filters.company, root_type, balance_must_be, period_list, filters.depth) - data.extend(result or []) + data.extend(asset or []) + data.extend(liability or []) + data.extend(equity or []) + if provisional_profit_loss: + data.append(provisional_profit_loss) columns = get_columns(period_list) return columns, data +def get_provisional_profit_loss(asset, liability, equity, period_list): + if asset and (liability or equity): + provisional_profit_loss = { + "account_name": _("Provisional Profit / Loss (Credit)"), + "account": None, + "is_profit_loss": True + } + has_value = False + + for period in period_list: + effective_liability = 0.0 + if liability: + effective_liability += flt(liability[-2][period.key]) + if equity: + effective_liability += flt(equity[-2][period.key]) + + provisional_profit_loss[period.key] = flt(asset[-2][period.key]) - effective_liability + + if provisional_profit_loss[period.key]: + has_value = True + + if has_value: + return provisional_profit_loss diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 30650fff7d..45d5b3a737 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _, _dict from frappe.utils import (flt, cint, getdate, get_first_day, get_last_day, - add_months, add_days, now_datetime, formatdate) + add_months, add_days, formatdate) def process_filters(filters): filters.depth = cint(filters.depth) or 3 @@ -19,7 +19,6 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): start_date, end_date = frappe.db.get_value("Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]) start_date = getdate(start_date) end_date = getdate(end_date) - today = now_datetime().date() if periodicity == "Yearly": period_list = [_dict({"to_date": end_date, "key": fiscal_year, "label": fiscal_year})] @@ -45,18 +44,9 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): # to_date should be the last day of the new to_date's month to_date = get_last_day(to_date) - if to_date > today: - # checking in the middle of the currenct fiscal year? don't show future periods - key = today.strftime("%b_%Y").lower() - label = formatdate(today, "MMM YYYY") - period_list.append(_dict({"to_date": today, "key": key, "label": label})) - break - - elif to_date <= end_date: + if to_date <= end_date: # the normal case - key = to_date.strftime("%b_%Y").lower() - label = formatdate(to_date, "MMM YYYY") - period_list.append(_dict({"to_date": to_date, "key": key, "label": label})) + period_list.append(_dict({ "to_date": to_date })) # if it ends before a full year if to_date == end_date: @@ -64,14 +54,19 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): else: # if a fiscal year ends before a 12 month period - key = end_date.strftime("%b_%Y").lower() - label = formatdate(end_date, "MMM YYYY") - period_list.append(_dict({"to_date": end_date, "key": key, "label": label})) + period_list.append(_dict({ "to_date": end_date })) break # common processing for opts in period_list: - opts["key"] = opts["key"].replace(" ", "_").replace("-", "_") + key = opts["to_date"].strftime("%b_%Y").lower() + label = formatdate(opts["to_date"], "MMM YYYY") + opts.update({ + "key": key.replace(" ", "_").replace("-", "_"), + "label": label, + "year_start_date": start_date, + "year_end_date": end_date + }) if from_beginning: # set start date as None for all fiscal periods, used in case of Balance Sheet @@ -81,14 +76,14 @@ def get_period_list(fiscal_year, periodicity, from_beginning=False): return period_list -def get_data(company, root_type, balance_must_be, period_list, depth, ignore_opening_and_closing_entries=False): +def get_data(company, root_type, balance_must_be, period_list, depth, ignore_closing_entries=False): accounts = get_accounts(company, root_type) if not accounts: return None accounts, accounts_by_name = filter_accounts(accounts, depth) gl_entries_by_account = get_gl_entries(company, root_type, period_list[0]["from_date"], period_list[-1]["to_date"], - accounts[0].lft, accounts[0].rgt, ignore_opening_and_closing_entries=ignore_opening_and_closing_entries) + accounts[0].lft, accounts[0].rgt, ignore_closing_entries=ignore_closing_entries) calculate_values(accounts, gl_entries_by_account, period_list) accumulate_values_into_parents(accounts, accounts_by_name, period_list) @@ -121,6 +116,9 @@ def accumulate_values_into_parents(accounts, accounts_by_name, period_list): def prepare_data(accounts, balance_must_be, period_list): out = [] + year_start_date = period_list[0]["year_start_date"].strftime("%Y-%m-%d") + year_end_date = period_list[-1]["year_end_date"].strftime("%Y-%m-%d") + for d in accounts: # add to output has_value = False @@ -128,7 +126,9 @@ def prepare_data(accounts, balance_must_be, period_list): "account_name": d.account_name, "account": d.name, "parent_account": d.parent_account, - "indent": flt(d.indent) + "indent": flt(d.indent), + "year_start_date": year_start_date, + "year_end_date": year_end_date } for period in period_list: if d.get(period.key): @@ -204,12 +204,12 @@ def filter_accounts(accounts, depth): return filtered_accounts, accounts_by_name -def get_gl_entries(company, root_type, from_date, to_date, root_lft, root_rgt, ignore_opening_and_closing_entries=False): +def get_gl_entries(company, root_type, from_date, to_date, root_lft, root_rgt, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" additional_conditions = [] - if ignore_opening_and_closing_entries: - additional_conditions.append("and ifnull(is_opening, 'No')='No' and ifnull(voucher_type, '')!='Period Closing Voucher'") + if ignore_closing_entries: + additional_conditions.append("and ifnull(voucher_type, '')!='Period Closing Voucher'") if from_date: additional_conditions.append("and posting_date >= %(from_date)s") diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 9886f2f497..3a3123fc36 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -11,30 +11,29 @@ def execute(filters=None): process_filters(filters) period_list = get_period_list(filters.fiscal_year, filters.periodicity) - data = [] - income = get_data(filters.company, "Income", "Credit", period_list, filters.depth, - ignore_opening_and_closing_entries=True) - expense = get_data(filters.company, "Expense", "Debit", period_list, filters.depth, - ignore_opening_and_closing_entries=True) - net_total = get_net_total(income, expense, period_list) + income = get_data(filters.company, "Income", "Credit", period_list, filters.depth, ignore_closing_entries=True) + expense = get_data(filters.company, "Expense", "Debit", period_list, filters.depth, ignore_closing_entries=True) + net_profit_loss = get_net_profit_loss(income, expense, period_list) + data = [] data.extend(income or []) data.extend(expense or []) - if net_total: - data.append(net_total) + if net_profit_loss: + data.append(net_profit_loss) columns = get_columns(period_list) return columns, data -def get_net_total(income, expense, period_list): +def get_net_profit_loss(income, expense, period_list): if income and expense: - net_total = { + net_profit_loss = { "account_name": _("Net Profit / Loss"), - "account": None + "account": None, + "is_profit_loss": True } for period in period_list: - net_total[period.key] = flt(income[-2][period.key] - expense[-2][period.key], 3) + net_profit_loss[period.key] = flt(income[-2][period.key] - expense[-2][period.key], 3) - return net_total + return net_profit_loss diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index dbe41ff345..5e3ba0e116 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -38,7 +38,7 @@ erpnext.financial_statements = { if (columnDef.df.fieldname=="account") { var link = $("") .text(dataContext.account_name) - .attr("onclick", 'erpnext.financial_statements.open_general_ledger("' + dataContext.account + '")'); + .attr("onclick", "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(dataContext) + ")"); var span = $("") .css("padding-left", (cint(dataContext.indent) * 21) + "px") @@ -51,17 +51,24 @@ erpnext.financial_statements = { } if (!dataContext.parent_account) { - value = $(value).css("font-weight", "bold").wrap("

").parent().html(); + var $value = $(value).css("font-weight", "bold"); + if (dataContext.is_profit_loss && dataContext[columnDef.df.fieldname] < 0) { + $value.addClass("text-danger"); + } + + value = $value.wrap("

").parent().html(); } return value; }, - "open_general_ledger": function(account) { - if (!account) return; + "open_general_ledger": function(data) { + if (!data.account) return; frappe.route_options = { - "account": account, - "company": frappe.query_report.filters_by_name.company.get_value() + "account": data.account, + "company": frappe.query_report.filters_by_name.company.get_value(), + "from_date": data.year_start_date, + "to_date": data.year_end_date }; frappe.set_route("query-report", "General Ledger"); } From 649d18c4f73beeb7880349d611938b8cbf500839 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 21 Jul 2014 17:17:59 +0530 Subject: [PATCH 49/53] [minor] fix server error for new production order if sales order is invalid --- .../doctype/production_order/production_order.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 2d41d0a6a4..1486d2a684 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -39,16 +39,16 @@ class ProductionOrder(Document): def validate_sales_order(self): if self.sales_order: so = frappe.db.sql("""select name, delivery_date from `tabSales Order` - where name=%s and docstatus = 1""", self.sales_order, as_dict=1)[0] + where name=%s and docstatus = 1""", self.sales_order, as_dict=1) - if not so.name: + if len(so): + if not self.expected_delivery_date: + self.expected_delivery_date = so[0].delivery_date + + self.validate_production_order_against_so() + else: frappe.throw(_("Sales Order {0} is not valid") % self.sales_order) - if not self.expected_delivery_date: - self.expected_delivery_date = so.delivery_date - - self.validate_production_order_against_so() - def validate_warehouse(self): from erpnext.stock.utils import validate_warehouse_company From 994d9075b4161ca3becd61bb77ecc04fd9c82916 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 21 Jul 2014 17:36:50 +0530 Subject: [PATCH 50/53] Change TRAVIS_BRANCH to develop --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7e5f21d48a..6eab0e4af6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ install: - sudo apt-get update - sudo apt-get purge -y mysql-common - sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev - - CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@$TRAVIS_BRANCH && + - CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@$develop && - pip install --editable . before_script: From 7fcb3c9865be43131e02c4a338a75979b47e39f9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 21 Jul 2014 17:51:14 +0530 Subject: [PATCH 51/53] [minor] Item query - search by description --- erpnext/controllers/queries.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 2650c66ac2..4a30eed652 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -167,7 +167,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): where tabItem.docstatus < 2 and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and (tabItem.`{key}` LIKE %(txt)s - or tabItem.item_name LIKE %(txt)s) + or tabItem.item_name LIKE %(txt)s + or tabItem.description LIKE %(txt)s) {fcond} {mcond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), From 4c5d7617fbccae85f2522acf248a91923ed44b5c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 21 Jul 2014 18:02:22 +0530 Subject: [PATCH 52/53] [minor] Update if missing, source and target warehouse in stock entry detail table, when they are specified in the form --- .../stock/doctype/stock_entry/stock_entry.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 7274ece1fa..dfcf07f506 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -230,6 +230,36 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse; }, + source_mandatory: ["Material Issue", "Material Transfer", "Purchase Return"], + target_mandatory: ["Material Receipt", "Material Transfer", "Sales Return"], + + from_warehouse: function(doc) { + var me = this; + this.set_warehouse_if_missing("s_warehouse", doc.from_warehouse, function(row) { + return me.source_mandatory.indexOf(me.frm.doc.purpose)!==-1; + }); + }, + + to_warehouse: function(doc) { + var me = this; + this.set_warehouse_if_missing("t_warehouse", doc.to_warehouse, function(row) { + return me.target_mandatory.indexOf(me.frm.doc.purpose)!==-1; + }); + }, + + set_warehouse_if_missing: function(fieldname, value, condition) { + for (var i=0, l=(this.frm.doc.mtn_details || []).length; i Date: Mon, 21 Jul 2014 18:13:05 +0530 Subject: [PATCH 53/53] [fix] Show against account in General Ledger Print Format --- erpnext/accounts/report/general_ledger/general_ledger.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 190d45555a..0fc02b16d0 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -27,7 +27,7 @@
- {%= row.account_name %} + {%= row.account_name %} {%= data[i].voucher_type%}
{%= data[i].voucher_no %}
{%= data[i].account %} -
{%= __("Against") %}: {%= data[i].account %} +
{%= __("Against") %}: {%= data[i].against_account %}
{%= __("Remarks") %}: {%= data[i].remarks %}
{%= format_currency(data[i].debit) %} {%= format_currency(data[i].credit) %}