diff --git a/accounts/doctype/cost_center/cost_center.py b/accounts/doctype/cost_center/cost_center.py index e57b6a3758..a7672452aa 100644 --- a/accounts/doctype/cost_center/cost_center.py +++ b/accounts/doctype/cost_center/cost_center.py @@ -81,7 +81,7 @@ class DocType(DocTypeNestedSet): """ Cost Center name must be unique """ - if (self.doc.__islocal or not self.doc.name) and webnotes.conn.sql("select name from `tabCost Center` where cost_center_name = %s and company_name=%s", (self.doc.cost_center_name, self.doc.company_name)): + if (self.doc.fields.get("__islocal") or not self.doc.name) and webnotes.conn.sql("select name from `tabCost Center` where cost_center_name = %s and company_name=%s", (self.doc.cost_center_name, self.doc.company_name)): msgprint("Cost Center Name already exists, please rename", raise_exception=1) self.validate_mandatory() diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index 64d84b0459..429cc104a7 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -108,8 +108,8 @@ class DocType: and not 'Accounts Manager' in webnotes.user.get_roles(): msgprint(_("Account") + ": " + self.doc.account + _(" has been freezed. \ Only Accounts Manager can do transaction against this account"), raise_exception=1) - - if ret and ret[0]["company"] != self.doc.company: + + if self.doc.is_cancelled in ("No", None) and ret and ret[0]["company"] != self.doc.company: msgprint(_("Account") + ": " + self.doc.account + _(" does not belong to the company") \ + ": " + self.doc.company, raise_exception=1) @@ -124,9 +124,10 @@ class DocType: return self.cost_center_company[self.doc.cost_center] - if self.doc.cost_center and _get_cost_center_company() != self.doc.company: - msgprint(_("Cost Center") + ": " + self.doc.cost_center \ - + _(" does not belong to the company") + ": " + self.doc.company, raise_exception=True) + if self.doc.is_cancelled in ("No", None) and \ + self.doc.cost_center and _get_cost_center_company() != self.doc.company: + msgprint(_("Cost Center") + ": " + self.doc.cost_center \ + + _(" does not belong to the company") + ": " + self.doc.company, raise_exception=True) def check_freezing_date(self, adv_adj): """ diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index 28dba65b95..a2c422f38a 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1283,18 +1283,6 @@ "read_only": 0, "report_hide": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Accounts Manager", - "submit": 0, - "write": 0 - }, { "amend": 1, "cancel": 1, @@ -1302,33 +1290,10 @@ "doctype": "DocPerm", "permlevel": 0, "report": 1, - "role": "Accounts Manager", - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 0, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, "role": "Accounts User", "submit": 1, "write": 1 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1, - "report": 0, - "role": "Accounts User", - "submit": 0, - "write": 0 - }, { "doctype": "DocPerm", "match": "customer", @@ -1336,9 +1301,4 @@ "report": 1, "role": "Customer" }, - { - "doctype": "DocPerm", - "permlevel": 0, - "role": "Retail User" - } ] \ No newline at end of file diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 2703d27f69..d2b0a0ea0d 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -154,6 +154,11 @@ wn.module_page["Accounts"] = [ route: "query-report/Sales Register", doctype: "Sales Invoice" }, + { + "label":wn._("Purchase Register"), + route: "query-report/Purchase Register", + doctype: "Purchase Invoice" + }, ] }, { @@ -182,6 +187,11 @@ wn.module_page["Accounts"] = [ right: true, icon: "icon-list", items: [ + { + "label":wn._("Bank Reconciliation Statement"), + route: "query-report/Bank Reconciliation Statement", + doctype: "Journal Voucher" + }, { "label":wn._("Delivered Items To Be Billed"), route: "query-report/Delivered Items To Be Billed", @@ -192,6 +202,21 @@ wn.module_page["Accounts"] = [ route: "query-report/Ordered Items To Be Billed", doctype: "Sales Invoice" }, + { + "label":wn._("Bank Clearance Summary"), + route: "query-report/Bank Clearance Summary", + doctype: "Journal Voucher" + }, + { + "label":wn._("Payment Collection With Ageing"), + route: "query-report/Payment Collection With Ageing", + doctype: "Journal Voucher" + }, + { + "label":wn._("Payment Made With Ageing"), + route: "query-report/Payment Made With Ageing", + doctype: "Journal Voucher" + }, ] } ] diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js index 137f5e941c..6a7d6045ea 100644 --- a/accounts/page/general_ledger/general_ledger.js +++ b/accounts/page/general_ledger/general_ledger.js @@ -186,7 +186,6 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ var totals = this.make_summary_row("Totals", this.account); var grouped_ledgers = {}; - $.each(data, function(i, item) { if((me.is_default("company") ? true : me.apply_filter(item, "company")) && (me.account ? me.is_child_account(me.account, item.account) @@ -217,8 +216,7 @@ erpnext.GeneralLedger = wn.views.GridReport.extend({ grouped_ledgers[item.account].totals.debit += item.debit; grouped_ledgers[item.account].totals.credit += item.credit; } - - if(me.account) { + if(item.account) { item.against_account = me.voucher_accounts[item.voucher_type + ":" + item.voucher_no][(item.debit > 0 ? "credits" : "debits")].join(", "); } diff --git a/accounts/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index 34c8ccd2be..71aeb35ce0 100644 --- a/accounts/report/accounts_payable/accounts_payable.py +++ b/accounts/report/accounts_payable/accounts_payable.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import getdate, nowdate, flt, cstr +from webnotes import msgprint, _ +from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data def execute(filters=None): if not filters: filters = {} @@ -19,7 +21,6 @@ def execute(filters=None): and nowdate() or filters.get("report_date") data = [] - total_invoiced_amount = total_paid = total_outstanding = 0 for gle in entries: if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: @@ -36,7 +37,7 @@ def execute(filters=None): paid_amount = get_paid_amount(gle, filters.get("report_date") or nowdate(), entries_after_report_date) outstanding_amount = invoiced_amount - paid_amount - + if abs(flt(outstanding_amount)) > 0.01: row = [gle.posting_date, gle.account, gle.voucher_type, gle.voucher_no, gle.remarks, account_supplier_type_map.get(gle.account), due_date, bill_no, @@ -47,16 +48,9 @@ def execute(filters=None): ageing_based_on_date = due_date else: ageing_based_on_date = gle.posting_date + row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) - - # Add to total - total_invoiced_amount += flt(invoiced_amount) - total_paid += flt(paid_amount) - total_outstanding += flt(outstanding_amount) data.append(row) - if data: - data.append(["", "", "", "", "", "", "", "Total", "", total_invoiced_amount, total_paid, - total_outstanding, "", "", "", ""]) return columns, data @@ -75,8 +69,7 @@ def get_gl_entries(filters, before_report_date=True): gl_entries = [] gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % - (conditions) % (", ".join(['%s']*len(supplier_accounts))), - tuple(supplier_accounts), as_dict=1) + (conditions), tuple(supplier_accounts), as_dict=1) return gl_entries def get_conditions(filters, before_report_date=True): @@ -87,13 +80,16 @@ def get_conditions(filters, before_report_date=True): supplier_accounts = [] if filters.get("account"): supplier_accounts = [filters["account"]] - elif filters.get("company"): + else: supplier_accounts = webnotes.conn.sql_list("""select name from `tabAccount` where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % conditions, filters) if supplier_accounts: - conditions += " and account in (%s)" + conditions += " and account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) + else: + msgprint(_("No Supplier Accounts found. Supplier Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) if filters.get("report_date"): if before_report_date: @@ -133,20 +129,4 @@ def get_paid_amount(gle, report_date, entries_after_report_date): and against_voucher = %s and name != %s and ifnull(is_cancelled, 'No') = 'No'""", (gle.account, report_date, gle.voucher_type, gle.voucher_no, gle.name))[0][0] - return flt(paid_amount) - -def get_ageing_data(ageing_based_on_date, age_on, outstanding_amount): - val1 = val2 = val3 = val4 = diff = 0 - diff = age_on and ageing_based_on_date \ - and (getdate(age_on) - getdate(ageing_based_on_date)).days or 0 - - if diff <= 30: - val1 = outstanding_amount - elif 30 < diff <= 60: - val2 = outstanding_amount - elif 60 < diff <= 90: - val3 = outstanding_amount - elif diff > 90: - val4 = outstanding_amount - - return [diff, val1, val2, val3, val4] \ No newline at end of file + return flt(paid_amount) \ No newline at end of file diff --git a/accounts/report/accounts_payable/accounts_payable.txt b/accounts/report/accounts_payable/accounts_payable.txt index 8920a0bf2e..6de97f64e6 100644 --- a/accounts/report/accounts_payable/accounts_payable.txt +++ b/accounts/report/accounts_payable/accounts_payable.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-22 16:16:03", "docstatus": 0, - "modified": "2013-04-23 14:54:27", + "modified": "2013-04-30 17:55:54", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index d385b36b90..d791fad23f 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import getdate, nowdate, flt, cstr def execute(filters=None): @@ -18,7 +19,6 @@ def execute(filters=None): and nowdate() or filters.get("report_date") data = [] - total_invoiced_amount = total_payment = total_outstanding = 0 for gle in entries: if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \ or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date: @@ -41,17 +41,9 @@ def execute(filters=None): else: ageing_based_on_date = gle.posting_date row += get_ageing_data(ageing_based_on_date, age_on, outstanding_amount) - - # Add to total - total_invoiced_amount += flt(invoiced_amount) - total_payment += flt(payment_amount) - total_outstanding += flt(outstanding_amount) - + data.append(row) - if data: - data.append(["", "", "", "", "", "", "Total", total_invoiced_amount, total_payment, - total_outstanding, "", "", "", ""]) - + return columns, data def get_columns(): @@ -67,8 +59,7 @@ def get_gl_entries(filters, upto_report_date=True): conditions, customer_accounts = get_conditions(filters, upto_report_date) return webnotes.conn.sql("""select * from `tabGL Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by posting_date, account""" % - (conditions) % (", ".join(['%s']*len(customer_accounts))), - tuple(customer_accounts), as_dict=1) + (conditions), tuple(customer_accounts), as_dict=1) def get_conditions(filters, upto_report_date=True): conditions = "" @@ -78,13 +69,16 @@ def get_conditions(filters, upto_report_date=True): customer_accounts = [] if filters.get("account"): customer_accounts = [filters["account"]] - elif filters.get("company"): + else: customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` where ifnull(master_type, '') = 'Customer' and docstatus < 2 %s""" % conditions, filters) if customer_accounts: - conditions += " and account in (%s)" + conditions += " and account in (%s)" % (", ".join(['%s']*len(customer_accounts))) + else: + msgprint(_("No Customer Accounts found. Customer Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) if filters.get("report_date"): if upto_report_date: @@ -105,7 +99,7 @@ def get_account_territory_map(): def get_si_due_date_map(): """ get due_date from sales invoice """ si_due_date_map = {} - for t in webnotes.conn.sql("""select name, due_date from `tabSales Invoice` group by name"""): + for t in webnotes.conn.sql("""select name, due_date from `tabSales Invoice`"""): si_due_date_map[t[0]] = t[1] return si_due_date_map diff --git a/accounts/report/accounts_receivable/accounts_receivable.txt b/accounts/report/accounts_receivable/accounts_receivable.txt index 28f7e4f44d..7eb344fc08 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.txt +++ b/accounts/report/accounts_receivable/accounts_receivable.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-16 11:31:13", "docstatus": 0, - "modified": "2013-04-16 11:31:13", + "modified": "2013-04-30 17:54:47", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/report/bank_clearance_summary/__init__.py b/accounts/report/bank_clearance_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/accounts/report/bank_clearance_summary/bank_clearance_summary.js new file mode 100644 index 0000000000..76adfd3174 --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.js @@ -0,0 +1,32 @@ +wn.query_reports["Bank Clearance Summary"] = { + "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": "Bank Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "account_type": "Bank or Cash" + } + } + } + }, + ] +} \ No newline at end of file diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/accounts/report/bank_clearance_summary/bank_clearance_summary.py new file mode 100644 index 0000000000..49ac1a46c8 --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -0,0 +1,54 @@ +# 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 . + +from __future__ import unicode_literals +import webnotes +from webnotes import _, msgprint + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + data = get_entries(filters) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Clearance Date:Date:110", "Against Account:Link/Account:200", + "Debit:Currency:120", "Credit:Currency:120" + ] + +def get_conditions(filters): + conditions = "" + if not filters.get("account"): + msgprint(_("Please select Bank Account"), raise_exception=1) + else: + conditions += " and jvd.account = %(account)s" + + if filters.get("from_date"): conditions += " and jv.posting_date>=%(from_date)s" + if filters.get("to_date"): conditions += " and jv.posting_date<=%(to_date)s" + + return conditions + +def get_entries(filters): + conditions = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s + order by jv.name DESC""" % conditions, filters, as_list=1) + return entries \ No newline at end of file diff --git a/accounts/report/bank_clearance_summary/bank_clearance_summary.txt b/accounts/report/bank_clearance_summary/bank_clearance_summary.txt new file mode 100644 index 0000000000..3dd2079f3a --- /dev/null +++ b/accounts/report/bank_clearance_summary/bank_clearance_summary.txt @@ -0,0 +1,21 @@ +[ + { + "creation": "2013-05-01 12:13:25", + "docstatus": 0, + "modified": "2013-05-01 12:13:25", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Bank Clearance Summary", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Bank Clearance Summary" + } +] \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/__init__.py b/accounts/report/bank_reconciliation_statement/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js new file mode 100644 index 0000000000..28ac9205a6 --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -0,0 +1,25 @@ +wn.query_reports["Bank Reconciliation Statement"] = { + "filters": [ + { + "fieldname":"account", + "label": "Bank Account", + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "No", + "account_type": "Bank or Cash" + } + } + } + }, + { + "fieldname":"report_date", + "label": "Date", + "fieldtype": "Date", + "default": get_today() + }, + ] +} \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py new file mode 100644 index 0000000000..1345cd8006 --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -0,0 +1,79 @@ +# 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 . + +from __future__ import unicode_literals +import webnotes +from webnotes import _, msgprint +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + data = get_entries(filters) + + from accounts.utils import get_balance_on + balance_as_per_company = get_balance_on(filters["account"], filters["report_date"]) + + total_debit, total_credit = 0,0 + for d in data: + total_debit += flt(d[4]) + total_credit += flt(d[5]) + + if webnotes.conn.get_value("Account", filters["account"], "debit_or_credit") == 'Debit': + bank_bal = flt(balance_as_per_company) - flt(total_debit) + flt(total_credit) + else: + bank_bal = flt(balance_as_per_company) + flt(total_debit) - flt(total_credit) + + data += [ + ["", "", "", "Balance as per company books", balance_as_per_company, ""], + ["", "", "", "Amounts not reflected in bank", total_debit, total_credit], + ["", "", "", "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" + ] + +def get_conditions(filters): + conditions = "" + if not filters.get("account"): + msgprint(_("Please select Bank Account"), raise_exception=1) + else: + conditions += " and jvd.account = %(account)s" + + if not filters.get("report_date"): + msgprint(_("Please select Date on which you want to run the report"), raise_exception=1) + else: + conditions += """ and jv.posting_date <= %(report_date)s + and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s""" + + return conditions + +def get_entries(filters): + conditions = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jv.posting_date, jv.clearance_date, + jvd.against_account, jvd.debit, jvd.credit + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 and ifnull(jv.cheque_no, '')!= '' %s + order by jv.name DESC""" % conditions, filters, as_list=1) + + return entries \ No newline at end of file diff --git a/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt new file mode 100644 index 0000000000..9867c5ded7 --- /dev/null +++ b/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-04-30 18:30:21", + "docstatus": 0, + "modified": "2013-05-01 10:53:12", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 0, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Bank Reconciliation Statement", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Bank Reconciliation Statement" + } +] \ No newline at end of file diff --git a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt index c84854f39d..e84c597b1d 100644 --- a/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt +++ b/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 17:55:23", + "creation": "2013-02-25 10:38:57", "docstatus": 0, - "modified": "2013-02-23 14:35:28", + "modified": "2013-05-01 11:56:43", "modified_by": "Administrator", "owner": "Administrator" }, diff --git a/accounts/report/payment_collection_with_ageing/__init__.py b/accounts/report/payment_collection_with_ageing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js new file mode 100644 index 0000000000..d608fbd1d8 --- /dev/null +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.js @@ -0,0 +1,42 @@ +wn.query_reports["Payment Collection With Ageing"] = { + "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": "Customer 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/payment_collection_with_ageing/payment_collection_with_ageing.py b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py new file mode 100644 index 0000000000..12688525c6 --- /dev/null +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.py @@ -0,0 +1,90 @@ +# 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 . + +from __future__ import unicode_literals +import webnotes +from webnotes import msgprint, _ +from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + entries = get_entries(filters) + si_posting_date_map = get_si_posting_date_map() + + data = [] + for d in entries: + against_invoice_date = d.against_invoice and si_posting_date_map[d.against_invoice] or "" + + row = [d.name, d.account, d.posting_date, d.against_invoice, against_invoice_date, + d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] + + if d.against_invoice: + row += get_ageing_data(against_invoice_date, d.posting_date, d.credit or -1*d.debit) + else: + row += ["", "", "", "", ""] + + data.append(row) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Against Invoice:Link/Sales Invoice:130", + "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", + "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", + "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_conditions(filters): + conditions = "" + + customer_accounts = [] + if filters.get("account"): + customer_accounts = [filters["account"]] + else: + cond = filters.get("company") and (" and company = '%s'" % filters["company"]) or "" + customer_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Customer' and docstatus < 2 %s""" % cond) + + if customer_accounts: + conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(customer_accounts))) + else: + msgprint(_("No Customer Accounts found. Customer Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) + + if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] + if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] + + return conditions, customer_accounts + +def get_entries(filters): + conditions, customer_accounts = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jvd.against_invoice, jvd.debit, jvd.credit, jv.cheque_no, jv.cheque_date, jv.remark + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % + (conditions), tuple(customer_accounts), as_dict=1) + + return entries + +def get_si_posting_date_map(): + si_posting_date_map = {} + for t in webnotes.conn.sql("""select name, posting_date from `tabSales Invoice`"""): + si_posting_date_map[t[0]] = t[1] + + return si_posting_date_map \ No newline at end of file diff --git a/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt new file mode 100644 index 0000000000..3405d197a7 --- /dev/null +++ b/accounts/report/payment_collection_with_ageing/payment_collection_with_ageing.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-02 12:09:51", + "docstatus": 0, + "modified": "2013-05-02 12:09:51", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Payment Collection With Ageing", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Payment Collection With Ageing" + } +] \ No newline at end of file diff --git a/accounts/report/payment_made_with_ageing/__init__.py b/accounts/report/payment_made_with_ageing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js new file mode 100644 index 0000000000..533fe614d7 --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.js @@ -0,0 +1,40 @@ +wn.query_reports["Payment Made With Ageing"] = { + "filters": [ + { + fieldname: "from_date", + label: "From Date", + fieldtype: "Date", + default: wn.defaults.get_user_default("year_start_date"), + }, + { + fieldname:"to_date", + label: "To Date", + fieldtype: "Date", + default: get_today() + }, + { + fieldname:"account", + label: "Supplier Account", + fieldtype: "Link", + options: "Account", + get_query: function() { + return { + query: "accounts.utils.get_account_list", + filters: { + is_pl_account: "No", + debit_or_credit: "Credit", + company: wn.query_report.filters_by_name.company.get_value(), + master_type: "Supplier" + } + } + } + }, + { + fieldname:"company", + label: "Company", + fieldtype: "Link", + options: "Company", + default: sys_defaults.company + }, + ] +} \ No newline at end of file diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py new file mode 100644 index 0000000000..e7f13ef121 --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.py @@ -0,0 +1,89 @@ +# 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 . + +from __future__ import unicode_literals +import webnotes +from webnotes import msgprint, _ +from accounts.report.accounts_receivable.accounts_receivable import get_ageing_data + +def execute(filters=None): + if not filters: filters = {} + + columns = get_columns() + entries = get_entries(filters) + pi_posting_date_map = get_pi_posting_date_map() + + data = [] + for d in entries: + against_voucher_date = d.against_voucher and pi_posting_date_map[d.against_voucher] or "" + + row = [d.name, d.account, d.posting_date, d.against_voucher, against_voucher_date, + d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] + + if d.against_voucher: + row += get_ageing_data(against_voucher_date, d.posting_date, d.debit or -1*d.credit) + else: + row += ["", "", "", "", ""] + + data.append(row) + + return columns, data + +def get_columns(): + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130", + "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", + "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", + "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" + ] + +def get_conditions(filters): + conditions = "" + supplier_accounts = [] + if filters.get("account"): + supplier_accounts = [filters["account"]] + else: + cond = filters.get("company") and (" and company = '%s'" % filters["company"]) or "" + supplier_accounts = webnotes.conn.sql_list("""select name from `tabAccount` + where ifnull(master_type, '') = 'Supplier' and docstatus < 2 %s""" % cond) + + if supplier_accounts: + conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(supplier_accounts))) + else: + msgprint(_("No Supplier Accounts found. Supplier Accounts are identified based on \ + 'Master Type' value in account record."), raise_exception=1) + + if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] + if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] + + return conditions, supplier_accounts + +def get_entries(filters): + conditions, supplier_accounts = get_conditions(filters) + entries = webnotes.conn.sql("""select jv.name, jvd.account, jv.posting_date, + jvd.against_voucher, jvd.debit, jvd.credit, jv.cheque_no, jv.cheque_date, jv.remark + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % + (conditions), tuple(supplier_accounts), as_dict=1) + + return entries + +def get_pi_posting_date_map(): + pi_posting_date_map = {} + for t in webnotes.conn.sql("""select name, posting_date from `tabPurchase Invoice`"""): + pi_posting_date_map[t[0]] = t[1] + + return pi_posting_date_map \ No newline at end of file diff --git a/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt new file mode 100644 index 0000000000..c5c85da89a --- /dev/null +++ b/accounts/report/payment_made_with_ageing/payment_made_with_ageing.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-02 12:10:21", + "docstatus": 0, + "modified": "2013-05-02 12:10:21", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Journal Voucher", + "report_name": "Payment Made With Ageing", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Payment Made With Ageing" + } +] \ No newline at end of file diff --git a/accounts/report/purchase_register/__init__.py b/accounts/report/purchase_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accounts/report/purchase_register/purchase_register.js b/accounts/report/purchase_register/purchase_register.js new file mode 100644 index 0000000000..21e0547f4a --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.js @@ -0,0 +1,42 @@ +wn.query_reports["Purchase 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": "Credit", + "company": company, + "master_type": "Supplier" + } + } + } + }, + { + "fieldname":"company", + "label": "Company", + "fieldtype": "Link", + "options": "Company", + "default": sys_defaults.company + } + ] +} \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py new file mode 100644 index 0000000000..c131c17b4b --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.py @@ -0,0 +1,146 @@ +# 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 . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import flt + +def execute(filters=None): + if not filters: filters = {} + columns, expense_accounts, tax_accounts = get_columns() + + invoice_list = get_invoices(filters) + invoice_expense_map = get_invoice_expense_map(invoice_list) + invoice_tax_map = get_invoice_tax_map(invoice_list) + invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) + account_map = get_account_details(invoice_list) + + data = [] + for inv in invoice_list: + # invoice details + purchase_order = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_order", [])) + purchase_receipt = ", ".join(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])) + row = [inv.name, inv.posting_date, inv.supplier, inv.credit_to, + account_map.get(inv.credit_to), inv.project_name, inv.bill_no, inv.bill_date, + inv.remarks, purchase_order, purchase_receipt] + + # map expense values + for expense_acc in expense_accounts: + row.append(invoice_expense_map.get(inv.name, {}).get(expense_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.total_tax, inv.grand_total] + data.append(row) + + return columns, data + + +def get_columns(): + """return columns based on filters""" + columns = [ + "Invoice:Link/Purchase Invoice:120", "Posting Date:Date:80", "Supplier:Link/Supplier:120", + "Supplier Account:Link/Account:120", "Account Group:LInk/Account:120", + "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", + "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" + ] + + expense_accounts = webnotes.conn.sql_list("""select distinct expense_head + from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' + order by expense_head""") + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' + and docstatus = 1 and ifnull(account_head, '') != '' order by account_head""") + + columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ + ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ + ["Total Tax:Currency:120"] + ["Grand Total:Currency:120"] + + return columns, expense_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, credit_to, project_name, supplier, + bill_no, bill_date, remarks, net_total, total_tax, grand_total + from `tabPurchase Invoice` where docstatus = 1 %s + order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + +def get_invoice_expense_map(invoice_list): + expense_details = webnotes.conn.sql("""select parent, expense_head, sum(amount) as amount + from `tabPurchase Invoice Item` where parent in (%s) group by parent, expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_expense_map = {} + for d in expense_details: + invoice_expense_map.setdefault(d.parent, webnotes._dict()).setdefault(d.expense_head, []) + invoice_expense_map[d.parent][d.expense_head] = flt(d.amount) + + return invoice_expense_map + +def get_invoice_tax_map(invoice_list): + tax_details = webnotes.conn.sql("""select parent, account_head, sum(tax_amount) as tax_amount + from `tabPurchase 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_po_pr_map(invoice_list): + pi_items = webnotes.conn.sql("""select parent, purchase_order, purchase_receipt + from `tabPurchase Invoice Item` where parent in (%s) + and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) + + invoice_po_pr_map = {} + for d in pi_items: + if d.purchase_order: + invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( + "purchase_order", []).append(d.purchase_order) + if d.purchase_receipt: + invoice_po_pr_map.setdefault(d.parent, webnotes._dict()).setdefault( + "purchase_receipt", []).append(d.purchase_receipt) + + return invoice_po_pr_map + +def get_account_details(invoice_list): + account_map = {} + accounts = list(set([inv.credit_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[acc.name] = acc.parent_account + + return account_map \ No newline at end of file diff --git a/accounts/report/purchase_register/purchase_register.txt b/accounts/report/purchase_register/purchase_register.txt new file mode 100644 index 0000000000..847f5f3b6c --- /dev/null +++ b/accounts/report/purchase_register/purchase_register.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-04-29 16:13:11", + "docstatus": 0, + "modified": "2013-04-30 17:51:19", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Purchase Invoice", + "report_name": "Purchase Register", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Purchase Register" + } +] \ No newline at end of file diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 07772cb4e2..23d2227fc2 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -94,8 +94,8 @@ def get_conditions(filters): 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 + return webnotes.conn.sql("""select name, posting_date, debit_to, 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) @@ -145,7 +145,6 @@ def get_customer_deatils(invoice_list): 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 @@ -155,7 +154,6 @@ def get_account_details(invoice_list): 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 index 5aef814596..dbf41af0d0 100644 --- a/accounts/report/sales_register/sales_register.txt +++ b/accounts/report/sales_register/sales_register.txt @@ -2,11 +2,12 @@ { "creation": "2013-04-23 18:15:29", "docstatus": 0, - "modified": "2013-04-23 18:15:29", + "modified": "2013-04-30 17:53:10", "modified_by": "Administrator", "owner": "Administrator" }, { + "add_total_row": 1, "doctype": "Report", "is_standard": "Yes", "name": "__common__", diff --git a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt index 00384386f5..8aa94bf50a 100644 --- a/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt +++ b/accounts/search_criteria/bank_clearance_report/bank_clearance_report.txt @@ -1,29 +1,32 @@ [ { - "owner": "Administrator", + "creation": "2012-05-14 18:05:41", "docstatus": 0, - "creation": "2012-04-03 12:49:50", + "modified": "2013-04-30 14:49:06", "modified_by": "Administrator", - "modified": "2012-04-03 12:49:50" + "owner": "Administrator" }, { - "description": "Bank Clearance report", - "parent_doc_type": "Journal Voucher", - "module": "Accounts", - "standard": "Yes", - "sort_order": "DESC", - "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Fiscal Year':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", - "dis_filters": "fiscal_year", - "doc_type": "Journal Voucher Detail", - "name": "__common__", - "doctype": "Search Criteria", - "sort_by": "ID", - "page_len": 50, + "columns": "Journal Voucher\u0001ID,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher\u0001Clearance Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Voucher Date,Journal Voucher\u0001Posting Date,Journal Voucher Detail\u0001Against Payable,Journal Voucher Detail\u0001Against Receivable", "criteria_name": "Bank Clearance report", - "columns": "Journal Voucher\u0001ID,Journal Voucher Detail\u0001Account,Journal Voucher Detail\u0001Debit,Journal Voucher Detail\u0001Credit,Journal Voucher\u0001Clearance Date,Journal Voucher\u0001Cheque No,Journal Voucher\u0001Cheque Date,Journal Voucher\u0001Voucher Date,Journal Voucher\u0001Posting Date,Journal Voucher Detail\u0001Against Payable,Journal Voucher Detail\u0001Against Receivable" + "custom_query": "", + "description": "Bank Clearance report", + "dis_filters": "fiscal_year", + "disabled": 0, + "doc_type": "Journal Voucher Detail", + "doctype": "Search Criteria", + "filters": "{'Journal Voucher\u0001Submitted':1,'Journal Voucher\u0001Voucher Type':'','Journal Voucher\u0001Is Opening':'','Journal Voucher\u0001Fiscal Year':'','Journal Voucher\u0001Company':'','Journal Voucher\u0001TDS Applicable':'','Journal Voucher\u0001TDS Category':''}", + "module": "Accounts", + "name": "__common__", + "page_len": 50, + "parent_doc_type": "Journal Voucher", + "report_script": null, + "sort_by": "ID", + "sort_order": "DESC", + "standard": "Yes" }, { - "name": "bank_clearance_report", - "doctype": "Search Criteria" + "doctype": "Search Criteria", + "name": "bank_clearance_report" } ] \ No newline at end of file diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py index bd27f07687..be2e2946a9 100644 --- a/buying/doctype/purchase_order/test_purchase_order.py +++ b/buying/doctype/purchase_order/test_purchase_order.py @@ -27,6 +27,12 @@ class TestPurchaseOrder(unittest.TestCase): po.insert() self.assertEquals(len(po.doclist.get({"parentfield": "po_raw_material_details"})), 2) + def test_warehouse_company_validation(self): + from controllers.buying_controller import WrongWarehouseCompany + po = webnotes.bean(copy=test_records[0]) + po.doc.company = "_Test Company 1" + self.assertRaises(WrongWarehouseCompany, po.insert) + test_dependencies = ["BOM"] diff --git a/buying/doctype/supplier/supplier.js b/buying/doctype/supplier/supplier.js index 3834bda92c..202be71dfe 100644 --- a/buying/doctype/supplier/supplier.js +++ b/buying/doctype/supplier/supplier.js @@ -47,7 +47,7 @@ cur_frm.cscript.make_address = function() { if(!cur_frm.address_list) { cur_frm.address_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['address_html'].wrapper, - page_length: 2, + page_length: 5, new_doctype: "Address", custom_new_doc: function(doctype) { var address = wn.model.make_new_doc_and_get_name('Address'); @@ -78,7 +78,7 @@ cur_frm.cscript.make_contact = function() { if(!cur_frm.contact_list) { cur_frm.contact_list = new wn.ui.Listing({ parent: cur_frm.fields_dict['contact_html'].wrapper, - page_length: 2, + page_length: 5, new_doctype: "Contact", custom_new_doc: function(doctype) { var contact = wn.model.make_new_doc_and_get_name('Contact'); diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 85d8b9d9ea..0c25b9855e 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -26,10 +26,13 @@ from webnotes.model.utils import round_floats_in_doc from controllers.stock_controller import StockController +class WrongWarehouseCompany(Exception): pass + class BuyingController(StockController): def validate(self): super(BuyingController, self).validate() self.validate_stock_or_nonstock_items() + self.validate_warehouse_belongs_to_company() if self.meta.get_field("currency"): self.company_currency = get_company_currency(self.doc.company) self.validate_conversion_rate("currency", "conversion_rate") @@ -42,15 +45,24 @@ class BuyingController(StockController): # set total in words self.set_total_in_words() - + + def validate_warehouse_belongs_to_company(self): + for warehouse, company in webnotes.conn.get_values("Warehouse", + self.doclist.get_distinct_values("warehouse"), "company").items(): + if company and company != self.doc.company: + webnotes.msgprint(_("Warehouse must belong to company") + \ + (": %s (%s, %s)" % (warehouse, company, self.doc.company)), + raise_exception=WrongWarehouseCompany) + 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): + if 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) + if nonstock_items: + webnotes.msgprint(_("Stock and non-stock items can not be entered in 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 diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index 80571c17da..f254bf2349 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,6 +1,12 @@ erpnext.updates = [ + ["2nd May", ["Buying: Warehouse must belong to same company as transaction", + "Price List: Added Currency Field. One price list can have only one currency", + "Item: Naming can now be by series or item code", + "Naming Series: Set number of digits in series (optionally)"]], + ["18th April", ["Cost Center: Set a default Cost Center for a Company"]], ["12th April", ["Employee: List of Leave Approvers who can approve the Employee's Leave Applications"]], ["10th April", ["Redesigned File Uploads and added File Manager in Setup"]], + ["3rd April", ["Update Manager: Open source users can update their ERPNext instance from Setup > Update Manager"]], ["27th March", ["Rename multiple items together. Go to Setup > Rename Tool"]], ["26th March", ["Added project to Stock Ledger and Balance", "Added Default Cash Account in Company."]], diff --git a/hr/doctype/deduction_type/deduction_type.txt b/hr/doctype/deduction_type/deduction_type.txt index 4c9ad7ac4c..f3467936f5 100644 --- a/hr/doctype/deduction_type/deduction_type.txt +++ b/hr/doctype/deduction_type/deduction_type.txt @@ -1,12 +1,13 @@ [ { - "creation": "2013-01-10 16:34:13", + "creation": "2013-01-22 16:50:30", "docstatus": 0, - "modified": "2013-01-22 14:25:38", + "modified": "2013-05-02 11:22:59", "modified_by": "Administrator", "owner": "Administrator" }, { + "allow_rename": 1, "autoname": "field:deduction_name", "doctype": "DocType", "document_type": "Master", diff --git a/hr/doctype/earning_type/earning_type.txt b/hr/doctype/earning_type/earning_type.txt index 18cac6821a..d69f48619c 100644 --- a/hr/doctype/earning_type/earning_type.txt +++ b/hr/doctype/earning_type/earning_type.txt @@ -1,12 +1,13 @@ [ { - "creation": "2013-01-10 16:34:13", + "creation": "2013-01-24 11:03:32", "docstatus": 0, - "modified": "2013-01-23 16:32:07", + "modified": "2013-05-02 11:22:48", "modified_by": "Administrator", "owner": "Administrator" }, { + "allow_rename": 1, "autoname": "field:earning_name", "doctype": "DocType", "document_type": "Master", diff --git a/manufacturing/doctype/bom/bom.js b/manufacturing/doctype/bom/bom.js index 4e8fbc95d1..f0c15fa2a9 100644 --- a/manufacturing/doctype/bom/bom.js +++ b/manufacturing/doctype/bom/bom.js @@ -59,27 +59,22 @@ cur_frm.fields_dict["bom_operations"].grid.on_row_delete = function(cdt, cdn){ set_operation_no(doc); } -cur_frm.cscript.item = function(doc, dt, dn) { - if (doc.item) { - get_server_fields('get_item_details', doc.item, '', doc, dt, dn, 1); - } -} +cur_frm.add_fetch("item", "description", "description"); +cur_frm.add_fetch("item", "stock_uom", "uom"); cur_frm.cscript.workstation = function(doc,dt,dn) { var d = locals[dt][dn]; - if (d.workstation) { - var callback = function(r, rt) { - calculate_op_cost(doc, dt, dn); - calculate_total(doc); - } - get_server_fields('get_workstation_details', d.workstation, - 'bom_operations', doc, dt, dn, 1, callback); - } + wn.model.with_doc("Workstation", d.workstation, function(i, v) { + d.hour_rate = v.hour_rate; + refresh_field("hour_rate"); + calculate_op_cost(doc); + calculate_total(doc); + }); } cur_frm.cscript.hour_rate = function(doc, dt, dn) { - calculate_op_cost(doc, dt, dn); + calculate_op_cost(doc); calculate_total(doc); } @@ -114,7 +109,7 @@ var get_bom_material_detail= function(doc, cdt, cdn) { $.extend(d, r.message); refresh_field("bom_materials"); doc = locals[doc.doctype][doc.name]; - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); }, freeze: true @@ -124,7 +119,7 @@ var get_bom_material_detail= function(doc, cdt, cdn) { cur_frm.cscript.qty = function(doc, cdt, cdn) { - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); } @@ -134,12 +129,12 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) { msgprint("You can not change rate if BOM mentioned agianst any item"); get_bom_material_detail(doc, cdt, cdn); } else { - calculate_rm_cost(doc, cdt, cdn); + calculate_rm_cost(doc); calculate_total(doc); } } -var calculate_op_cost = function(doc, dt, dn) { +var calculate_op_cost = function(doc) { var op = getchildren('BOM Operation', doc.name, 'bom_operations'); total_op_cost = 0; for(var i=0;i 0) { + cur_frm.set_intro(wn._("Backups will be uploaded to") + ": " + + wn.utils.comma_and(active_services)); + } else { + cur_frm.set_intro(""); } - }) - } -} - -cur_frm.cscript.backup_right_now = function(doc) { - msgprint("Backing up and uploading. This may take a few minutes.") - wn.call({ - method: "setup.doctype.backup_manager.backup_manager.take_backups_dropbox", - callback: function(r) { - msgprint("Backups taken. Please check your email for the response.") } - }) -} -//gdrive -cur_frm.cscript.allow_gdrive_access = function(doc) { - if (doc.send_notifications_to == '') { - msgprint("Please enter email address.") - } - else { - wn.call({ - method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url", - callback: function(r) { - if(!r.exc) { - window.open(r.message.authorize_url); + + }, + + validate_send_notifications_to: function() { + if(!cur_frm.doc.send_notifications_to) { + msgprint(wn._("Please specify") + ": " + + wn._(wn.meta.get_label(cur_frm.doctype, "send_notifications_to"))); + return false; + } + + return true; + }, + + allow_dropbox_access: function() { + if(cur_frm.cscript.validate_send_notifications_to()) { + wn.call({ + method: "setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url", + callback: function(r) { + if(!r.exc) { + cur_frm.set_value("dropbox_access_secret", r.message.secret); + cur_frm.set_value("dropbox_access_key", r.message.key); + cur_frm.save(null, function() { + window.open(r.message.url); + }); + } } - } - }) - } -} - -cur_frm.cscript.validate_gdrive = function(doc) { - wn.call({ - method: "setup.doctype.backup_manager.backup_googledrive.gdrive_callback", - args: { - verification_code: doc.verification_code - }, - }); -} - -cur_frm.cscript.upload_backups_to_dropbox = function(doc) { - cur_frm.save() -} - -cur_frm.cscript.upload_backups_to_gdrive = function(doc) { - cur_frm.save() -} + }); + } + }, + + allow_gdrive_access: function() { + if(cur_frm.cscript.validate_send_notifications_to()) { + wn.call({ + method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url", + callback: function(r) { + if(!r.exc) { + window.open(r.message.authorize_url); + } + } + }); + } + }, + + validate_gdrive: function() { + wn.call({ + method: "setup.doctype.backup_manager.backup_googledrive.gdrive_callback", + args: { + verification_code: cur_frm.doc.verification_code + }, + }); + }, + + upload_backups_to_dropbox: function() { + cur_frm.save(); + }, + + upload_backups_to_gdrive: function() { + cur_frm.save(); + }, +}); \ No newline at end of file diff --git a/setup/doctype/backup_manager/backup_manager.py b/setup/doctype/backup_manager/backup_manager.py index a8ecd636db..c91cf1110f 100644 --- a/setup/doctype/backup_manager/backup_manager.py +++ b/setup/doctype/backup_manager/backup_manager.py @@ -20,8 +20,8 @@ def take_backups_if(freq): if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_dropbox")==freq: take_backups_dropbox() - if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq: - take_backups_gdrive() + # if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq: + # take_backups_gdrive() @webnotes.whitelist() def take_backups_dropbox(): @@ -35,7 +35,7 @@ def take_backups_dropbox(): except Exception: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) - print error_message + webnotes.errprint(error_message) send_email(False, "Dropbox", error_message) #backup to gdrive @@ -51,7 +51,7 @@ def take_backups_gdrive(): except Exception: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + webnotes.getTraceback()) - print error_message + webnotes.errprint(error_message) send_email(False, "Google Drive", error_message) def send_email(success, service_name, error_status=None): diff --git a/setup/doctype/backup_manager/backup_manager.txt b/setup/doctype/backup_manager/backup_manager.txt index 9a43f34746..2d6f191e5e 100644 --- a/setup/doctype/backup_manager/backup_manager.txt +++ b/setup/doctype/backup_manager/backup_manager.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-15 11:06:59", + "creation": "2013-04-30 12:58:38", "docstatus": 0, - "modified": "2013-03-15 17:27:33", + "modified": "2013-05-02 11:42:08", "modified_by": "Administrator", "owner": "Administrator" }, @@ -109,6 +109,7 @@ "doctype": "DocField", "fieldname": "sync_with_gdrive", "fieldtype": "Section Break", + "hidden": 1, "label": "Sync with Google Drive" }, { diff --git a/setup/doctype/global_defaults/global_defaults.py b/setup/doctype/global_defaults/global_defaults.py index 8275513dc9..191a47edf2 100644 --- a/setup/doctype/global_defaults/global_defaults.py +++ b/setup/doctype/global_defaults/global_defaults.py @@ -31,6 +31,7 @@ keydict = { 'item_group': 'default_item_group', 'customer_group': 'default_customer_group', 'cust_master_name': 'cust_master_name', + "item_naming_by": "item_naming_by", 'supplier_type': 'default_supplier_type', 'supp_master_name': 'supp_master_name', 'territory': 'default_territory', diff --git a/setup/doctype/global_defaults/global_defaults.txt b/setup/doctype/global_defaults/global_defaults.txt index 7f81618469..853bb57705 100644 --- a/setup/doctype/global_defaults/global_defaults.txt +++ b/setup/doctype/global_defaults/global_defaults.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 11:08:14", + "creation": "2013-04-01 15:05:24", "docstatus": 0, - "modified": "2013-03-28 15:41:03", + "modified": "2013-05-02 15:05:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -27,8 +27,6 @@ "permlevel": 0 }, { - "amend": 0, - "cancel": 0, "create": 1, "doctype": "DocPerm", "name": "__common__", @@ -167,6 +165,13 @@ "read_only": 0, "width": "50%" }, + { + "doctype": "DocField", + "fieldname": "item_naming_by", + "fieldtype": "Select", + "label": "Item Naming By", + "options": "Item Code\nNaming Series" + }, { "doctype": "DocField", "fieldname": "default_item_group", @@ -502,6 +507,11 @@ "label": "SMS Sender Name", "read_only": 0 }, + { + "amend": 0, + "cancel": 0, + "doctype": "DocPerm" + }, { "doctype": "DocPerm" } diff --git a/setup/doctype/naming_series/naming_series.py b/setup/doctype/naming_series/naming_series.py index 7b804f8a96..3a6b36d1ff 100644 --- a/setup/doctype/naming_series/naming_series.py +++ b/setup/doctype/naming_series/naming_series.py @@ -123,7 +123,14 @@ class DocType: def validate_series_name(self, n): import re - if not re.match('[a-zA-Z0-9]+(([-/][a-zA-Z0-9])?[-/][a-zA-Z0-9]*)*',n): + if "." in n: + parts = n.split(".") + if len(parts) > 2: + msgprint("Only one dot (.) allowed in " + n, raise_exception=1) + if not re.match("#+$", parts[-1]): + msgprint("Numbering series must be in hashes (e.g. ####)", raise_exception=1) + n = n[0] + if not re.match("^[a-zA-Z0-9-/]*$", n): msgprint('Special Characters except "-" and "/" not allowed in naming series') raise Exception diff --git a/setup/doctype/naming_series/naming_series.txt b/setup/doctype/naming_series/naming_series.txt index 3de9e5cfab..0dab9e970d 100644 --- a/setup/doctype/naming_series/naming_series.txt +++ b/setup/doctype/naming_series/naming_series.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-10 16:34:23", + "creation": "2013-01-25 11:35:08", "docstatus": 0, - "modified": "2013-01-22 14:56:34", + "modified": "2013-05-02 15:34:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -25,8 +25,6 @@ "permlevel": 0 }, { - "amend": 0, - "cancel": 0, "create": 1, "doctype": "DocPerm", "name": "__common__", @@ -62,7 +60,7 @@ "fieldname": "help_html", "fieldtype": "HTML", "label": "Help HTML", - "options": "
\nEdit list of Series in the box below. Each Series Prefix on a new line.

\nAllowed special characters are \"/\" and \"-\"
\nExamples:
\nINV-
\nINV-10-
\nINVK-
\n
" + "options": "
\nEdit list of Series in the box below. Rules:\n
    \n
  • Each Series Prefix on a new line.
  • \n
  • Allowed special characters are \"/\" and \"-\"
  • \n
  • Optionally, set the number of digits in the series using dot (.) followed by hashes (#). For example, \".####\" means that the series will have four digits. Default is five digits.
  • \n
\nExamples:
\nINV-
\nINV-10-
\nINVK-
\nINV-.####
\n
" }, { "doctype": "DocField", diff --git a/setup/doctype/price_list/price_list.txt b/setup/doctype/price_list/price_list.txt index a230f5b98c..bce8aae6e7 100644 --- a/setup/doctype/price_list/price_list.txt +++ b/setup/doctype/price_list/price_list.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:09", "docstatus": 0, - "modified": "2013-01-22 14:56:41", + "modified": "2013-05-02 14:44:24", "modified_by": "Administrator", "owner": "Administrator" }, @@ -52,13 +52,21 @@ "oldfieldtype": "Data", "reqd": 1 }, + { + "doctype": "DocField", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, { "depends_on": "price_list_name", "doctype": "DocField", "fieldname": "how_to_upload", "fieldtype": "HTML", "label": "How to upload", - "options": "
Use the Data Import Tool to upload, update Item Prices in bulk:\n
    \n
  1. Go to Data Import Tool.\n
  2. Select \"Item\"\n
  3. Check on \"With Data\"\n
  4. Download \"Item Price\" from Child Tables.\n
  5. Update the prices required and add new rows if required.\n
  6. Check on \"Overwrite\"\n
  7. Upload the modified sheet.\n
\n" + "options": "
Use the Data Import Tool to upload, update Item Prices in bulk:\n
    \n
  1. Go to Data Import Tool.\n
  2. Select \"Item\"\n
  3. Check on \"With Data\"\n
  4. Download \"Item Price\" from Child Tables.\n
  5. Update the prices required and add new rows if required.\n
  6. Check on \"Overwrite\"\n
  7. Upload the modified sheet.\n
\n" }, { "cancel": 0, @@ -78,7 +86,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "role": "Sales Master Manager", "write": 1 } diff --git a/setup/doctype/price_list/test_price_list.py b/setup/doctype/price_list/test_price_list.py index 53b86a39fc..30262dc8aa 100644 --- a/setup/doctype/price_list/test_price_list.py +++ b/setup/doctype/price_list/test_price_list.py @@ -1,6 +1,7 @@ test_records = [ [{ "doctype": "Price List", - "price_list_name": "_Test Price List" + "price_list_name": "_Test Price List", + "currency": "INR" }] ] \ No newline at end of file diff --git a/stock/doctype/batch/batch.txt b/stock/doctype/batch/batch.txt index 4722996c0f..c6ed3c370a 100644 --- a/stock/doctype/batch/batch.txt +++ b/stock/doctype/batch/batch.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-01 19:09:43", + "creation": "2013-03-05 14:50:38", "docstatus": 0, - "modified": "2013-03-01 08:22:16", + "modified": "2013-05-01 15:49:47", "modified_by": "Administrator", "owner": "harshada@webnotestech.com" }, @@ -24,14 +24,19 @@ "permlevel": 0 }, { + "cancel": 1, + "create": 1, "doctype": "DocPerm", "name": "__common__", "parent": "Batch", "parentfield": "permissions", "parenttype": "DocType", + "permlevel": 0, "read": 1, + "report": 1, "role": "Material Master Manager", - "submit": 0 + "submit": 0, + "write": 1 }, { "doctype": "DocType", @@ -94,28 +99,6 @@ "oldfieldtype": "Date" }, { - "doctype": "DocField", - "fieldname": "trash_reason", - "fieldtype": "Small Text", - "label": "Trash Reason", - "oldfieldname": "trash_reason", - "oldfieldtype": "Small Text", - "read_only": 1 - }, - { - "cancel": 1, - "create": 1, - "doctype": "DocPerm", - "permlevel": 0, - "report": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "doctype": "DocPerm", - "match": "", - "permlevel": 1 + "doctype": "DocPerm" } ] \ No newline at end of file diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 8d09cbd676..0a31dfe80e 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -326,6 +326,17 @@ if (sys_defaults.auto_inventory_accounting) { } // cost center + cur_frm.cscript.cost_center = function(doc, cdt, cdn){ + var d = locals[cdt][cdn]; + if(d.cost_center) { + var cl = getchildren('Delivery Note Item', doc.name, cur_frm.cscript.fname, doc.doctype); + for(var i = 0; i < cl.length; i++){ + if(!cl[i].cost_center) cl[i].cost_center = d.cost_center; + } + } + refresh_field(cur_frm.cscript.fname); + } + cur_frm.fields_dict.delivery_note_details.grid.get_field("cost_center").get_query = function(doc) { return { query: "accounts.utils.get_cost_center_list", diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js index 8b3e04484a..a344ad3ce9 100644 --- a/stock/doctype/item/item.js +++ b/stock/doctype/item/item.js @@ -14,11 +14,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +cur_frm.add_fetch("price_list_name", "currency", "ref_currency") + cur_frm.cscript.refresh = function(doc) { // make sensitive fields(has_serial_no, is_stock_item, valuation_method) // read only if any stock ledger entry exists - cur_frm.toggle_enable("item_code", doc.__islocal); + cur_frm.toggle_display("naming_series", sys_defaults.item_naming_by=="Naming Series" + && doc.__islocal) + cur_frm.toggle_display("item_code", sys_defaults.item_naming_by!="Naming Series" + && doc.__islocal) + if ((!doc.__islocal) && (doc.is_stock_item == 'Yes')) { var callback = function(r, rt) { diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py index 4767742e3e..63275047ad 100644 --- a/stock/doctype/item/item.py +++ b/stock/doctype/item/item.py @@ -23,7 +23,17 @@ from webnotes.model.bean import getlist from webnotes import msgprint, _ from webnotes.model.controller import DocListController + +class PriceListCurrencyMismatch(Exception): pass + class DocType(DocListController): + def autoname(self): + if webnotes.conn.get_default("item_naming_by")=="Naming Series": + from webnotes.model.doc import make_autoname + self.doc.item_code = make_autoname(self.doc.naming_series+'.#####') + + self.doc.name = self.doc.item_code + def validate(self): if not self.doc.stock_uom: msgprint(_("Please enter Default Unit of Measure"), raise_exception=1) @@ -33,7 +43,7 @@ class DocType(DocListController): self.add_default_uom_in_conversion_factor_table() self.valiadte_item_type() self.check_for_active_boms() - self.check_ref_rate_detail() + self.validate_price_lists() self.fill_customer_code() self.check_item_tax() self.validate_barcode() @@ -55,12 +65,13 @@ class DocType(DocListController): ch.conversion_factor = 1 def check_stock_uom_with_bin(self): - bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", - self.doc.item_code) - if self.doc.stock_uom and bin and cstr(bin[0][0]) \ - and cstr(bin[0][0]) != cstr(self.doc.stock_uom): - msgprint(_("Please Update Stock UOM with the help of Stock UOM Replace Utility."), - raise_exception=1) + if not self.doc.fields.get("__islocal"): + bin = webnotes.conn.sql("select stock_uom from `tabBin` where item_code = %s", + self.doc.name) + if self.doc.stock_uom and bin and cstr(bin[0][0]) \ + and cstr(bin[0][0]) != cstr(self.doc.stock_uom): + msgprint(_("Please Update Stock UOM with the help of Stock UOM Replace Utility."), + raise_exception=1) def validate_conversion_factor(self): check_list = [] @@ -120,16 +131,21 @@ class DocType(DocListController): if cstr(self.doc.fields.get(d)) != 'Yes': _check_for_active_boms(fl[d]) - def check_ref_rate_detail(self): - check_list=[] + def validate_price_lists(self): + price_lists=[] for d in getlist(self.doclist,'ref_rate_details'): - if [cstr(d.price_list_name), cstr(d.ref_currency), - cint(d.selling), cint(d.buying)] in check_list: - msgprint("Ref Rate is entered twice for Price List : '%s' and Currency : '%s'." % - (d.price_list_name,d.ref_currency), raise_exception=1) + if d.price_list_name in price_lists: + msgprint(_("Cannot have two prices for same Price List") + ": " + d.price_list_name, + raise_exception= webnotes.DuplicateEntryError) else: - check_list.append([cstr(d.price_list_name),cstr(d.ref_currency)]) - + price_list_currency = webnotes.conn.get_value("Price List", d.price_list_name, "currency") + if price_list_currency and d.ref_currency != price_list_currency: + msgprint(_("Currency does not match Price List Currency for Price List") \ + + ": " + d.price_list_name, raise_exception=PriceListCurrencyMismatch) + + price_lists.append(d.price_list_name) + + def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ cust_code=[] @@ -154,13 +170,14 @@ class DocType(DocListController): def validate_barcode(self): if self.doc.barcode: - duplicate = webnotes.conn.sql("select name from tabItem where barcode = %s and name != %s", (self.doc.barcode, self.doc.name)) + duplicate = webnotes.conn.sql("""select name from tabItem where barcode = %s + and name != %s""", (self.doc.barcode, self.doc.name)) if duplicate: msgprint("Barcode: %s already used in item: %s" % (self.doc.barcode, cstr(duplicate[0][0])), raise_exception = 1) def check_non_asset_warehouse(self): - if self.doc.is_asset_item == "Yes": + if not self.doc.__islocal and self.doc.is_asset_item == "Yes": existing_qty = webnotes.conn.sql("select t1.warehouse, t1.actual_qty from tabBin t1, tabWarehouse t2 where t1.item_code=%s and (t2.warehouse_type!='Fixed Asset' or t2.warehouse_type is null) and t1.warehouse=t2.name and t1.actual_qty > 0", self.doc.name) for e in existing_qty: msgprint("%s Units exist in Warehouse %s, which is not an Asset Warehouse." % diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index a311c4c5bc..d6eaf345ed 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-28 10:35:28", "docstatus": 0, - "modified": "2013-04-23 11:44:39", + "modified": "2013-05-02 15:10:53", "modified_by": "Administrator", "owner": "Administrator" }, @@ -34,7 +34,6 @@ "parent": "Item", "parentfield": "permissions", "parenttype": "DocType", - "permlevel": 0, "read": 1, "submit": 0 }, @@ -51,6 +50,13 @@ "oldfieldtype": "Section Break", "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "\nITEM" + }, { "description": "Item will be saved by this name in the data base.", "doctype": "DocField", diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py index dbbeecc85b..f5a688ca70 100644 --- a/stock/doctype/item/test_item.py +++ b/stock/doctype/item/test_item.py @@ -20,6 +20,22 @@ import webnotes test_ignore = ["BOM"] +class TestItem(unittest.TestCase): + def test_duplicate_price_list(self): + item = webnotes.bean(copy=test_records[0]) + item.doc.item_code = "_Test Item 10" + item_price = item.doclist.get({"doctype": "Item Price"})[0] + item.doclist.append(webnotes.doc(item_price.fields.copy())) + self.assertRaises(webnotes.DuplicateEntryError, item.insert) + + def test_price_list_mismatch(self): + from stock.doctype.item.item import PriceListCurrencyMismatch + item = webnotes.bean(copy=test_records[0]) + item.doc.item_code = "_Test Item 11" + item_price = item.doclist.get({"doctype": "Item Price"})[0].ref_currency="USD" + self.assertRaises(PriceListCurrencyMismatch, item.insert) + + test_records = [ [{ "doctype": "Item", @@ -45,7 +61,14 @@ test_records = [ "warehouse": "_Test Warehouse", "warehouse_reorder_level": 20, "warehouse_reorder_qty": 20 - }], + }, { + "doctype": "Item Price", + "parentfield": "ref_rate_details", + "price_list_name": "_Test Price List", + "ref_rate": 100, + "ref_currency": "INR" + } + ], [{ "doctype": "Item", "item_code": "_Test Item Home Desktop 100", diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py index 080989f2d9..f5dbb52bbe 100644 --- a/stock/doctype/material_request/test_material_request.py +++ b/stock/doctype/material_request/test_material_request.py @@ -263,6 +263,12 @@ class TestMaterialRequest(unittest.TestCase): se = webnotes.bean(copy=se_doclist) self.assertRaises(webnotes.MappingMismatchError, se.insert) + def test_warehouse_company_validation(self): + from controllers.buying_controller import WrongWarehouseCompany + mr = webnotes.bean(copy=test_records[0]) + mr.doc.company = "_Test Company 1" + self.assertRaises(WrongWarehouseCompany, mr.insert) + test_records = [ [ { diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py index 3c83de3260..8fdb50e7a4 100644 --- a/stock/doctype/stock_ledger/stock_ledger.py +++ b/stock/doctype/stock_ledger/stock_ledger.py @@ -114,7 +114,8 @@ class DocType: def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = '', rejected=None): exists = webnotes.conn.sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) if is_submit: - if exists and exists[0][2] != 2 and purpose not in ['Material Transfer', 'Sales Return']: + if exists and exists[0][2] != 2 and \ + purpose not in ['Material Transfer', "Material Receipt", 'Sales Return']: msgprint("Serial No: %s already %s" % (serial_no, exists and exists[0][1]), raise_exception = 1) elif exists: s = Document('Serial No', exists and exists[0][0]) diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py index 26501beab9..f3a04581a9 100644 --- a/stock/doctype/warehouse/test_warehouse.py +++ b/stock/doctype/warehouse/test_warehouse.py @@ -2,7 +2,8 @@ test_records = [ [{ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse", - "warehouse_type": "_Test Warehouse Type" + "warehouse_type": "_Test Warehouse Type", + "company": "_Test Company" }], [{ "doctype": "Warehouse", diff --git a/stock/page/stock_ageing/stock_ageing.js b/stock/page/stock_ageing/stock_ageing.js index e257bd4722..b9b4c8053b 100644 --- a/stock/page/stock_ageing/stock_ageing.js +++ b/stock/page/stock_ageing/stock_ageing.js @@ -95,6 +95,8 @@ erpnext.StockAgeing = erpnext.StockGridReport.extend({ this.data = [].concat(this._data); + this.serialized_buying_rates = this.get_serialized_buying_rates(); + $.each(this.data, function(i, d) { me.reset_item_values(d); }); diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js index 4d59f42228..aee364e213 100644 --- a/support/doctype/support_ticket/support_ticket.js +++ b/support/doctype/support_ticket/support_ticket.js @@ -29,17 +29,13 @@ $.extend(cur_frm.cscript, { erpnext.hide_naming_series(); cur_frm.cscript.make_listing(doc); if(!doc.__islocal) { - if(in_list(user_roles,'System Manager')) { - if(doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); - if(doc.status=='Closed') cur_frm.add_custom_button('Re-Open Ticket', cur_frm.cscript['Re-Open Ticket']); - }else if(doc.allocated_to) { - cur_frm.set_df_property('status','read_only', 1); - if(user==doc.allocated_to && doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); + if(user_roles.indexOf("Support Manager")!==-1) { + if(doc.status!='Closed') cur_frm.add_custom_button('Close Ticket', cur_frm.cscript['Close Ticket']); + if(doc.status=='Closed') cur_frm.add_custom_button('Re-Open Ticket', cur_frm.cscript['Re-Open Ticket']); } - cur_frm.set_df_property('subject','read_only', 1); - cur_frm.set_df_property('description','hidden', 1); - cur_frm.set_df_property('raised_by','read_only', 1); + cur_frm.toggle_enable(["subject", "raised_by"], false); + cur_frm.toggle_display("description", false); } refresh_field('status'); }, diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index 5625f1179c..8a705f41c7 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -45,6 +45,10 @@ class DocType(TransactionBase): def validate(self): self.update_status() + if self.doc.status == "Closed": + from webnotes.widgets.form.assign_to import clear + clear(self.doc.doctype, self.doc.name) + def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Waiting for Customer') if comm.lead and not self.doc.lead: diff --git a/utilities/doctype/rename_tool/rename_tool.py b/utilities/doctype/rename_tool/rename_tool.py index 2e368ced70..5accf3c6b7 100644 --- a/utilities/doctype/rename_tool/rename_tool.py +++ b/utilities/doctype/rename_tool/rename_tool.py @@ -34,7 +34,8 @@ def upload(select_doctype=None, rows=None): rename_log = [] for row in rows: - if len(row) > 2: + # if row has some content + if len(row) > 1 and row[0] and row[1]: try: if rename_doc(select_doctype, row[0], row[1]): rename_log.append(_("Successful: ") + row[0] + " -> " + row[1]) @@ -45,5 +46,5 @@ def upload(select_doctype=None, rows=None): rename_log.append("" + \ _("Failed: ") + row[0] + " -> " + row[1] + "") rename_log.append("" + repr(e) + "") - + return rename_log \ No newline at end of file