Merge branch 'master' into responsive
diff --git a/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 621604b..a70c932 100644
--- a/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -72,7 +72,38 @@
["Stock Received But Not Billed - _TC", 750.0, 0],
["_Test Account Shipping Charges - _TC", 100.0, 0],
["_Test Account VAT - _TC", 120.0, 0],
- ["Expenses Included In Valuation - _TC", 0, 250.0]
+ ["Expenses Included In Valuation - _TC", 0, 250.0],
+ ])
+
+ for i, gle in enumerate(gl_entries):
+ self.assertEquals(expected_values[i][0], gle.account)
+ self.assertEquals(expected_values[i][1], gle.debit)
+ self.assertEquals(expected_values[i][2], gle.credit)
+
+ webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+
+ def test_gl_entries_with_aia_for_non_stock_items(self):
+ webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
+ self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
+
+ pi = webnotes.bean(copy=test_records[1])
+ pi.doclist[1].item_code = "_Test Non Stock Item"
+ pi.doclist[1].expense_head = "_Test Account Cost for Goods Sold - _TC"
+ pi.doclist.pop(2)
+ pi.doclist.pop(3)
+ pi.run_method("calculate_taxes_and_totals")
+ pi.insert()
+ pi.submit()
+
+ gl_entries = webnotes.conn.sql("""select account, debit, credit
+ from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
+ order by account asc""", pi.doc.name, as_dict=1)
+ self.assertTrue(gl_entries)
+
+ expected_values = sorted([
+ ["_Test Supplier - _TC", 0, 620],
+ ["_Test Account Cost for Goods Sold - _TC", 500.0, 0],
+ ["_Test Account VAT - _TC", 120.0, 0],
])
for i, gle in enumerate(gl_entries):
@@ -106,7 +137,6 @@
self.assertEqual(tax.account_head, expected_values[i][0])
self.assertEqual(tax.tax_amount, expected_values[i][1])
self.assertEqual(tax.total, expected_values[i][2])
- # print tax.account_head, tax.tax_amount, tax.item_wise_tax_detail
expected_values = [
["_Test Item Home Desktop 100", 90, 59],
@@ -142,7 +172,6 @@
self.assertEqual(tax.account_head, expected_values[i][0])
self.assertEqual(tax.tax_amount, expected_values[i][1])
self.assertEqual(tax.total, expected_values[i][2])
- # print tax.account_head, tax.tax_amount, tax.item_wise_tax_detail
expected_values = [
["_Test FG Item", 90, 7059],
diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js
index 3e9ea92..2703d27 100644
--- a/accounts/page/accounts_home/accounts_home.js
+++ b/accounts/page/accounts_home/accounts_home.js
@@ -149,6 +149,11 @@
route: "query-report/Accounts Payable",
doctype: "Purchase Invoice"
},
+ {
+ "label":wn._("Sales Register"),
+ route: "query-report/Sales Register",
+ doctype: "Sales Invoice"
+ },
]
},
{
diff --git a/accounts/report/sales_register/__init__.py b/accounts/report/sales_register/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/accounts/report/sales_register/__init__.py
diff --git a/accounts/report/sales_register/sales_register.js b/accounts/report/sales_register/sales_register.js
new file mode 100644
index 0000000..8224619
--- /dev/null
+++ b/accounts/report/sales_register/sales_register.js
@@ -0,0 +1,42 @@
+wn.query_reports["Sales Register"] = {
+ "filters": [
+ {
+ "fieldname":"from_date",
+ "label": "From Date",
+ "fieldtype": "Date",
+ "default": wn.defaults.get_user_default("year_start_date"),
+ "width": "80"
+ },
+ {
+ "fieldname":"to_date",
+ "label": "To Date",
+ "fieldtype": "Date",
+ "default": get_today()
+ },
+ {
+ "fieldname":"account",
+ "label": "Account",
+ "fieldtype": "Link",
+ "options": "Account",
+ "get_query": function() {
+ var company = wn.query_report.filters_by_name.company.get_value();
+ return {
+ "query": "accounts.utils.get_account_list",
+ "filters": {
+ "is_pl_account": "No",
+ "debit_or_credit": "Debit",
+ "company": company,
+ "master_type": "Customer"
+ }
+ }
+ }
+ },
+ {
+ "fieldname":"company",
+ "label": "Company",
+ "fieldtype": "Link",
+ "options": "Company",
+ "default": sys_defaults.company
+ }
+ ]
+}
\ No newline at end of file
diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py
new file mode 100644
index 0000000..07772cb
--- /dev/null
+++ b/accounts/report/sales_register/sales_register.py
@@ -0,0 +1,161 @@
+# ERPNext - web based ERP (http://erpnext.com)
+# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import flt
+
+def execute(filters=None):
+ if not filters: filters = {}
+ columns, income_accounts, tax_accounts = get_columns()
+
+ invoice_list = get_invoices(filters)
+ invoice_income_map = get_invoice_income_map(invoice_list)
+ invoice_tax_map = get_invoice_tax_map(invoice_list)
+
+ invoice_so_dn_map = get_invoice_so_dn_map(invoice_list)
+ customer_map = get_customer_deatils(invoice_list)
+ account_map = get_account_details(invoice_list)
+
+ data = []
+ for inv in invoice_list:
+ # invoice details
+ sales_order = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("sales_order", []))
+ delivery_note = ", ".join(invoice_so_dn_map.get(inv.name, {}).get("delivery_note", []))
+ # webnotes.errprint(customer_map.get(inv.customer, []))
+ row = [inv.name, inv.posting_date, inv.customer, inv.debit_to,
+ account_map.get(inv.debit_to), customer_map.get(inv.customer), inv.project_name,
+ inv.remarks, sales_order, delivery_note]
+
+ # map income values
+ for income_acc in income_accounts:
+ row.append(invoice_income_map.get(inv.name, {}).get(income_acc))
+
+ # net total
+ row.append(inv.net_total)
+
+ # tax account
+ for tax_acc in tax_accounts:
+ row.append(invoice_tax_map.get(inv.name, {}).get(tax_acc))
+
+ # total tax, grand total
+ row += [inv.other_charges_total, inv.grand_total]
+
+ data.append(row)
+
+ return columns, data
+
+
+def get_columns():
+ """return columns based on filters"""
+ columns = [
+ "Invoice:Link/Sales Invoice:120", "Posting Date:Date:80", "Customer:Link/Customer:120",
+ "Customer Account:Link/Account:120", "Account Group:LInk/Account:120",
+ "Territory:Link/Territory:80", "Project:Link/Project:80",
+ "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100"
+ ]
+
+ income_accounts = webnotes.conn.sql_list("""select distinct income_account
+ from `tabSales Invoice Item` where docstatus = 1 order by income_account""")
+
+ tax_accounts = webnotes.conn.sql_list("""select distinct account_head
+ from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
+ and docstatus = 1 order by account_head""")
+
+ columns = columns + [(account + ":Currency:120") for account in income_accounts] + \
+ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \
+ ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"]
+
+ return columns, income_accounts, tax_accounts
+
+def get_conditions(filters):
+ conditions = ""
+
+ if filters.get("company"): conditions += " and company=%(company)s"
+ if filters.get("account"): conditions += " and account = %(account)s"
+
+ if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
+ if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
+
+ return conditions
+
+def get_invoices(filters):
+ conditions = get_conditions(filters)
+ return webnotes.conn.sql("""select name, posting_date, territory, debit_to, territory,
+ project_name, customer, remarks, net_total, other_charges_total, grand_total
+ from `tabSales Invoice` where docstatus = 1 %s
+ order by posting_date desc, name desc""" % conditions, filters, as_dict=1)
+
+def get_invoice_income_map(invoice_list):
+ income_details = webnotes.conn.sql("""select parent, income_account, sum(amount) as amount
+ from `tabSales Invoice Item` where parent in (%s) group by parent, income_account""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+ invoice_income_map = {}
+ for d in income_details:
+ invoice_income_map.setdefault(d.parent, webnotes._dict()).setdefault(d.income_account, [])
+ invoice_income_map[d.parent][d.income_account] = flt(d.amount)
+
+ return invoice_income_map
+
+def get_invoice_tax_map(invoice_list):
+ tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount
+ from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+ invoice_tax_map = {}
+ for d in tax_details:
+ invoice_tax_map.setdefault(d.parent, webnotes._dict()).setdefault(d.account_head, [])
+ invoice_tax_map[d.parent][d.account_head] = flt(d.tax_amount)
+
+ return invoice_tax_map
+
+def get_invoice_so_dn_map(invoice_list):
+ si_items = webnotes.conn.sql("""select parent, sales_order, delivery_note
+ from `tabSales Invoice Item` where parent in (%s)
+ and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+ invoice_so_dn_map = {}
+ for d in si_items:
+ if d.sales_order:
+ invoice_so_dn_map.setdefault(d.parent, webnotes._dict()).setdefault(
+ "sales_order", []).append(d.sales_order)
+ if d.delivery_note:
+ invoice_so_dn_map.setdefault(d.parent, webnotes._dict()).setdefault(
+ "delivery_note", []).append(d.delivery_note)
+
+ return invoice_so_dn_map
+
+def get_customer_deatils(invoice_list):
+ customer_map = {}
+ customers = list(set([inv.customer for inv in invoice_list]))
+ for cust in webnotes.conn.sql("""select name, territory from `tabCustomer`
+ where name in (%s)""" % ", ".join(["%s"]*len(customers)), tuple(customers), as_dict=1):
+ customer_map.setdefault(cust.name, "")
+ customer_map[cust.name] = cust.territory
+
+ return customer_map
+
+def get_account_details(invoice_list):
+ account_map = {}
+ accounts = list(set([inv.debit_to for inv in invoice_list]))
+ for acc in webnotes.conn.sql("""select name, parent_account from tabAccount
+ where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1):
+ account_map.setdefault(acc.name, "")
+ account_map[acc.name] = acc.parent_account
+
+ return account_map
\ No newline at end of file
diff --git a/accounts/report/sales_register/sales_register.txt b/accounts/report/sales_register/sales_register.txt
new file mode 100644
index 0000000..5aef814
--- /dev/null
+++ b/accounts/report/sales_register/sales_register.txt
@@ -0,0 +1,21 @@
+[
+ {
+ "creation": "2013-04-23 18:15:29",
+ "docstatus": 0,
+ "modified": "2013-04-23 18:15:29",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Report",
+ "is_standard": "Yes",
+ "name": "__common__",
+ "ref_doctype": "Sales Invoice",
+ "report_name": "Sales Register",
+ "report_type": "Script Report"
+ },
+ {
+ "doctype": "Report",
+ "name": "Sales Register"
+ }
+]
\ No newline at end of file
diff --git a/accounts/search_criteria/sales_register/sales_register.js b/accounts/search_criteria/sales_register/sales_register.js
index 5a09713..872e198 100644
--- a/accounts/search_criteria/sales_register/sales_register.js
+++ b/accounts/search_criteria/sales_register/sales_register.js
@@ -28,5 +28,5 @@
this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Grand Total <='].df.filter_hide = 1;
this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Fiscal Year'].df.filter_hide = 1;
this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Sales Partner'].df.filter_hide = 1;
- this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Is Opening'].df.filter_hide = 1;
+ this.filter_fields_dict['Sales Invoice'+FILTER_SEP +'Is Opening Entry'].df.filter_hide = 1;
}
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 429737e..85d8b9d 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -29,6 +29,7 @@
class BuyingController(StockController):
def validate(self):
super(BuyingController, self).validate()
+ self.validate_stock_or_nonstock_items()
if self.meta.get_field("currency"):
self.company_currency = get_company_currency(self.doc.company)
self.validate_conversion_rate("currency", "conversion_rate")
@@ -41,7 +42,24 @@
# set total in words
self.set_total_in_words()
-
+
+ def validate_stock_or_nonstock_items(self):
+ items = [d.item_code for d in self.doclist.get({"parentfield": self.fname})]
+ if self.stock_items and len(items) > len(self.stock_items):
+ nonstock_items = list(set(items) - set(self.stock_items))
+ webnotes.msgprint(_("Stock and non-stock items can not be entered at the same ") +
+ self.doc.doctype + _(""". You should make separate documents for them.
+ Stock Items: """) + ", ".join(self.stock_items) + _("""
+ Non-stock Items: """) + ", ".join(nonstock_items), raise_exception=1)
+
+ elif items and not self.stock_items:
+ tax_for_valuation = [d.account_head for d in
+ self.doclist.get({"parentfield": "purchase_tax_details"})
+ if d.category in ["Valuation", "Valuation and Total"]]
+ if tax_for_valuation:
+ webnotes.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total'
+ as all items are non-stock items"""), raise_exception=1)
+
def update_item_details(self):
for item in self.doclist.get({"parentfield": self.fname}):
ret = get_item_details({