diff --git a/accounts/doctype/pos_setting/pos_setting.txt b/accounts/doctype/pos_setting/pos_setting.txt index 4e30b57593b..80cb1ecdc9c 100755 --- a/accounts/doctype/pos_setting/pos_setting.txt +++ b/accounts/doctype/pos_setting/pos_setting.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 11:03:07", + "creation": "2013-04-30 12:58:25", "docstatus": 0, - "modified": "2013-03-26 12:48:18", + "modified": "2013-05-03 14:36:24", "modified_by": "Administrator", "owner": "Administrator" }, @@ -40,6 +40,7 @@ "doctype": "DocField", "fieldname": "user", "fieldtype": "Link", + "in_list_view": 1, "label": "User", "oldfieldname": "user", "oldfieldtype": "Link", @@ -99,6 +100,7 @@ "doctype": "DocField", "fieldname": "company", "fieldtype": "Link", + "in_list_view": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -210,12 +212,6 @@ "oldfieldtype": "Select", "options": "link:Print Heading" }, - { - "create": 1, - "doctype": "DocPerm", - "role": "System Manager", - "write": 1 - }, { "create": 1, "doctype": "DocPerm", diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index cbacf58c5cd..6475bb57aee 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -45,7 +45,7 @@ class DocType(SellingController): def validate(self): super(DocType, self).validate() - + self.fetch_missing_values() self.validate_posting_time() self.so_dn_required() self.validate_proj_cust() @@ -137,7 +137,27 @@ class DocType(SellingController): def on_update_after_submit(self): self.validate_recurring_invoice() self.convert_to_recurring() - + + def fetch_missing_values(self): + # fetch contact and address details for customer, if they are not mentioned + if not (self.doc.contact_person and self.doc.customer_address): + for fieldname, val in self.get_default_address_and_contact("customer").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + # fetch missing item values + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get("item_code"): + ret = get_obj('Sales Common').get_item_details(item.fields, self) + for fieldname, value in ret.items(): + if self.meta.get_field(fieldname, parentfield="entries") and \ + not item.fields.get(fieldname): + item.fields[fieldname] = value + + # fetch pos details, if they are not fetched + if cint(self.doc.is_pos): + self.set_pos_fields(for_validate=True) + def update_time_log_batch(self, sales_invoice): for d in self.doclist.get({"doctype":"Sales Invoice Item"}): if d.time_log_batch: @@ -153,60 +173,42 @@ class DocType(SellingController): webnotes.msgprint(_("Time Log Batch status must be 'Submitted'") + ":" + d.time_log_batch, raise_exception=True) - def set_pos_fields(self): + def set_pos_fields(self, for_validate=False): """Set retail related fields from pos settings""" - pos = self.pos_details - - if pos: - val = webnotes.conn.sql("""select name from `tabAccount` - where name = %s and docstatus != 2""", - (cstr(self.doc.customer) + " - " + self.get_company_abbr())) - - val = val and val[0][0] or '' - if not val: val = pos[0]['customer_account'] or '' + if cint(self.doc.is_pos) != 1: + return + + if self.pos_settings: + pos = self.pos_settings[0] + + self.doc.conversion_rate = flt(pos.conversion_rate) if not self.doc.debit_to: - webnotes.conn.set(self.doc,'debit_to',val) - - lst = ['territory', 'naming_series', 'currency', 'charge', 'letter_head', 'tc_name', - 'price_list_name', 'company', 'select_print_heading', 'cash_bank_account'] + self.doc.debit_to = self.doc.customer and webnotes.conn.get_value("Account", { + "name": self.doc.customer + " - " + self.get_company_abbr(), + "docstatus": ["!=", 2] + }) or pos.customer_account - for i in lst: - self.doc.fields[i] = pos[0][i] or '' + for fieldname in ('territory', 'naming_series', 'currency', 'charge', 'letter_head', 'tc_name', + 'price_list_name', 'company', 'select_print_heading', 'cash_bank_account'): + if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)): + self.doc.fields[fieldname] = pos.get(fieldname) - self.set_pos_item_values() - - self.doc.conversion_rate = flt(pos[0]['conversion_rate']) or 0 + # set pos values in items + for doc in self.doclist.get({"parentfield": "entries"}): + if doc.fields.get('item_code'): + for fieldname, val in self.apply_pos_settings(doc.fields).items(): + if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)): + doc.fields[fieldname] = val - #fetch terms - if self.doc.tc_name: + # fetch terms + if self.doc.tc_name and not self.doc.terms: self.get_tc_details() - #fetch charges - if self.doc.charge: + # fetch charges + if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})): self.get_other_charges() - - def set_pos_item_values(self): - """Set default values related to pos for previously created sales invoice.""" - if cint(self.doc.is_pos) == 1: - dtl = self.pos_details - - for d in getlist(self.doclist,'entries'): - # overwrite if mentioned in item - item = webnotes.conn.sql("""select default_income_account, - default_sales_cost_center, default_warehouse, purchase_account - from tabItem where name = %s""", (d.item_code,), as_dict=1) - - d.income_account = (item and item[0]['default_income_account']) \ - or (dtl and dtl[0]['income_account']) or d.income_account - d.cost_center = (item and item[0]['default_sales_cost_center']) \ - or (dtl and dtl[0]['cost_center']) or d.cost_center - d.warehouse = (item and item[0]['default_warehouse']) \ - or (dtl and dtl[0]['warehouse']) or d.warehouse - d.expense_account = (item and item[0].purchase_account) \ - or (dtl and dtl[0].expense_account) or d.expense_account - def get_customer_account(self): """Get Account Head to which amount needs to be Debited based on Customer""" if not self.doc.company: @@ -299,60 +301,59 @@ class DocType(SellingController): args = args and json.loads(args) or {} if args.get('item_code'): ret = get_obj('Sales Common').get_item_details(args, self) - return self.get_pos_details(args, ret) - else: - for doc in self.doclist: + + if cint(self.doc.is_pos) == 1 and self.pos_settings: + ret = self.apply_pos_settings(args, ret) + + return ret + + elif cint(self.doc.is_pos) == 1 and self.pos_settings: + for doc in self.doclist.get({"parentfield": "entries"}): if doc.fields.get('item_code'): - arg = { - 'item_code':doc.fields.get('item_code'), - 'income_account':doc.fields.get('income_account'), - 'cost_center': doc.fields.get('cost_center'), - 'warehouse': doc.fields.get('warehouse'), - 'expense_account': doc.fields.get('expense_account'), - } - - ret = self.get_pos_details(arg) + ret = self.apply_pos_settings(doc.fields) for r in ret: if not doc.fields.get(r): doc.fields[r] = ret[r] @property - def pos_details(self): - if not hasattr(self, "_pos_details"): + def pos_settings(self): + if not hasattr(self, "_pos_settings"): dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s and company = %s""", (webnotes.session['user'], self.doc.company), as_dict=1) if not dtl: dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where ifnull(user,'') = '' and company = %s""", self.doc.company, as_dict=1) - self._pos_details = dtl + self._pos_settings = dtl - return self._pos_details + return self._pos_settings - def get_pos_details(self, args, ret = {}): - if args['item_code'] and cint(self.doc.is_pos) == 1: - dtl = self.pos_details - - item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, - default_warehouse, purchase_account from tabItem where name = %s""", - args['item_code'], as_dict=1) - - ret['income_account'] = item and item[0].get('default_income_account') \ - or (dtl and dtl[0].get('income_account') or args.get('income_account')) - - ret['cost_center'] = item and item[0].get('default_sales_cost_center') \ - or (dtl and dtl[0].get('cost_center') or args.get('cost_center')) + def apply_pos_settings(self, args, ret=None): + if not ret: ret = {} + + pos = self.pos_settings[0] + + item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, + default_warehouse, purchase_account from tabItem where name = %s""", + args.get('item_code'), as_dict=1) + + if item: + item = item[0] - ret['warehouse'] = item and item[0].get('default_warehouse') \ - or (dtl and dtl[0].get('warehouse') or args.get('warehouse')) + ret.update({ + "income_account": item.get("default_income_account") \ + or pos.get("income_account") or args.get("income_account"), + "cost_center": item.get("default_sales_cost_center") \ + or pos.get("cost_center") or args.get("cost_center"), + "warehouse": item.get("default_warehouse") \ + or pos.get("warehouse") or args.get("warehouse"), + "expense_account": item.get("purchase_account") \ + or pos.get("expense_account") or args.get("expense_account") + }) - ret['expense_account'] = item and item[0].get('purchase_account') \ - or (dtl and dtl[0].get('expense_account') or args.get('expense_account')) - - if ret['warehouse']: - actual_qty = webnotes.conn.sql("""select actual_qty from `tabBin` - where item_code = %s and warehouse = %s""", - (args['item_code'], ret['warehouse'])) - ret['actual_qty']= actual_qty and flt(actual_qty[0][0]) or 0 + if ret.get("warehouse"): + ret["actual_qty"] = flt(webnotes.conn.get_value("Bin", + {"item_code": args.get("item_code"), "warehouse": args.get("warehouse")}, + "actual_qty")) return ret def get_barcode_details(self, barcode): diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index fd8dc648d24..b46cdd17770 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -90,6 +90,9 @@ class TestSalesInvoice(unittest.TestCase): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + old_default_company = webnotes.conn.get_default("company") + webnotes.conn.set_default("company", "_Test Company") + self._insert_purchase_receipt() self._insert_pos_settings() @@ -106,7 +109,8 @@ class TestSalesInvoice(unittest.TestCase): # check stock ledger entries sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type = 'Sales Invoice' and voucher_no = %s""", si.doc.name, as_dict=1)[0] + where voucher_type = 'Sales Invoice' and voucher_no = %s""", + si.doc.name, as_dict=1)[0] self.assertTrue(sle) self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty], ["_Test Item", "_Test Warehouse", -5.0]) @@ -145,6 +149,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(gl_count[0][0], 16) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", old_default_company) def test_sales_invoice_gl_entry_with_aii_no_item_code(self): webnotes.defaults.set_global_default("auto_inventory_accounting", 1) @@ -337,7 +342,7 @@ class TestSalesInvoice(unittest.TestCase): # change posting date but keep recuring day to be today si7 = webnotes.bean(copy=base_si.doclist) si7.doc.fields.update({ - "posting_date": add_to_date(today, days=-3) + "posting_date": add_to_date(today, days=-1) }) si7.insert() si7.submit() @@ -345,7 +350,7 @@ class TestSalesInvoice(unittest.TestCase): # setting so that _test function works si7.doc.posting_date = today self._test_recurring_invoice(si7, True) - + def _test_recurring_invoice(self, base_si, first_and_last_day): from webnotes.utils import add_months, get_last_day, getdate from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices @@ -361,7 +366,8 @@ class TestSalesInvoice(unittest.TestCase): manage_recurring_invoices(next_date=next_date, commit=False) recurred_invoices = webnotes.conn.sql("""select name from `tabSales Invoice` - where recurring_id=%s and docstatus=1 order by name desc""", base_si.doc.recurring_id) + where recurring_id=%s and docstatus=1 order by name desc""", + base_si.doc.recurring_id) self.assertEquals(i+2, len(recurred_invoices)) @@ -395,7 +401,7 @@ class TestSalesInvoice(unittest.TestCase): for i in xrange(count): base_si = _test(i) -test_dependencies = ["Journal Voucher", "POS Setting"] +test_dependencies = ["Journal Voucher", "POS Setting", "Contact", "Address"] test_records = [ [ diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js index 6a9f91a7194..d2b0a0ea0de 100644 --- a/accounts/page/accounts_home/accounts_home.js +++ b/accounts/page/accounts_home/accounts_home.js @@ -187,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", @@ -197,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/report/accounts_payable/accounts_payable.py b/accounts/report/accounts_payable/accounts_payable.py index a10dc7e7e7d..71aeb35ce07 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 = {} @@ -67,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): @@ -79,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: @@ -125,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_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index a8c6d2311eb..d791fad23f2 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): @@ -58,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 = "" @@ -69,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: @@ -96,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/bank_clearance_summary/__init__.py b/accounts/report/bank_clearance_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d 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 00000000000..76adfd31740 --- /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 00000000000..49ac1a46c89 --- /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 00000000000..3dd2079f3a5 --- /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 00000000000..e69de29bb2d 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 00000000000..28ac9205a66 --- /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 00000000000..1345cd8006f --- /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 00000000000..9867c5ded7f --- /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 c84854f39d9..e84c597b1df 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 00000000000..e69de29bb2d 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 00000000000..d608fbd1d8c --- /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 00000000000..12688525c65 --- /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 00000000000..3405d197a79 --- /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 00000000000..e69de29bb2d 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 00000000000..533fe614d78 --- /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 00000000000..e7f13ef1213 --- /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 00000000000..c5c85da89a8 --- /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/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index 01dc93700b2..c131c17b4b0 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -141,7 +141,6 @@ def get_account_details(invoice_list): 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.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.py b/accounts/report/sales_register/sales_register.py index a0020c45a06..23d2227fc20 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -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/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py index bd27f076879..be2e2946a92 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 3834bda92c5..202be71dfe6 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/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js index 2df5f6fb07e..56328cc5744 100644 --- a/buying/page/buying_home/buying_home.js +++ b/buying/page/buying_home/buying_home.js @@ -98,6 +98,17 @@ wn.module_page["Buying"] = [ }, ] }, + { + title: wn._("Reports"), + right: true, + icon: "icon-list", + items: [ + { + "label":wn._("Item-wise Purchase History"), + route: "query-report/Item-wise Purchase History", + }, + ] + } ] pscript['onload_buying-home'] = function(wrapper) { diff --git a/buying/report/__init__.py b/buying/report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/buying/report/item_wise_purchase_history/__init__.py b/buying/report/item_wise_purchase_history/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt new file mode 100644 index 00000000000..3f9e702f5f1 --- /dev/null +++ b/buying/report/item_wise_purchase_history/item_wise_purchase_history.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-03 14:55:53", + "docstatus": 0, + "modified": "2013-05-03 15:13:34", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Currency:100\",\n\tpo_item.stock_uom as \"UOM:Link/UOM:80\",\n\tpo_item.purchase_rate as \"Rate:Currency:120\",\n\tpo_item.amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n\tpo_item.project_name as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Currency:120\",\n\tifnull(po_item.billed_qty, 0) as \"Billed Qty:Currency:120\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item\nwhere\n\tpo.name = po_item.parent and po.docstatus = 1\norder by po.name desc", + "ref_doctype": "Purchase Order", + "report_name": "Item-wise Purchase History", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Item-wise Purchase History" + } +] \ No newline at end of file diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index e167dc57b77..9e181bc8fe4 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,7 +45,14 @@ 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(_("Company mismatch for Warehouse") + (": %s" % (warehouse,)), + 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: diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index b768f1aa1e8..38c00269654 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,10 @@ 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)", + "Login: Disable Signup link in the login page"]], + ["30th April", ["Price List: Valid for all countries or only valid for specific countries"]], ["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"]], diff --git a/hr/doctype/deduction_type/deduction_type.txt b/hr/doctype/deduction_type/deduction_type.txt index 4c9ad7ac4c3..f3467936f5f 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 18cac6821a2..d69f48619cc 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/hr/doctype/salary_slip/salary_slip.py b/hr/doctype/salary_slip/salary_slip.py index 3edf4109547..d1ce3ccf1db 100644 --- a/hr/doctype/salary_slip/salary_slip.py +++ b/hr/doctype/salary_slip/salary_slip.py @@ -152,7 +152,7 @@ class DocType(TransactionBase): d.e_modified_amount = round(flt(d.e_amount)*flt(self.doc.payment_days)/cint(self.doc.total_days_in_month), 2) elif not self.doc.payment_days: d.e_modified_amount = 0 - self.doc.gross_pay += d.e_modified_amount + self.doc.gross_pay += flt(d.e_modified_amount) def calculate_ded_total(self): """ @@ -165,7 +165,7 @@ class DocType(TransactionBase): elif not self.doc.payment_days: d.d_modified_amount = 0 - self.doc.total_deduction += d.d_modified_amount + self.doc.total_deduction += flt(d.d_modified_amount) def calculate_net_pay(self): """ diff --git a/patches/april_2013/p08_price_list_country.py b/patches/april_2013/p08_price_list_country.py new file mode 100644 index 00000000000..65643cc2399 --- /dev/null +++ b/patches/april_2013/p08_price_list_country.py @@ -0,0 +1,5 @@ +import webnotes + +def execute(): + webnotes.reload_doc("Setup", "DocType", "Price List") + webnotes.conn.sql("""update `tabPrice List` set valid_for_all_countries=1""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index c2630de6ffb..432f8f9d35e 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -154,12 +154,12 @@ patch_list = [ "patches.january_2013.remove_tds_entry_from_gl_mapper", "patches.january_2013.update_number_format", "patches.january_2013.purchase_price_list", + "execute:webnotes.reload_doc('core', 'doctype', 'print_format') #2013-01", "execute:webnotes.reload_doc('accounts','Print Format','Payment Receipt Voucher')", "patches.january_2013.update_fraction_for_usd", "patches.january_2013.enable_currencies", "patches.january_2013.remove_unwanted_permission", "patches.january_2013.remove_landed_cost_master", - "execute:webnotes.reload_doc('core', 'doctype', 'print_format')", "patches.january_2013.reload_print_format", "patches.january_2013.rebuild_tree", "execute:webnotes.reload_doc('core','doctype','docfield') #2013-01-28", @@ -249,4 +249,5 @@ patch_list = [ "execute:webnotes.reset_perms('File Data')", "patches.april_2013.p07_update_file_data_2", "patches.april_2013.rebuild_sales_browser", + "patches.april_2013.p08_price_list_country", ] \ No newline at end of file diff --git a/selling/doctype/customer/customer.js b/selling/doctype/customer/customer.js index 403b83f5823..40a7846430e 100644 --- a/selling/doctype/customer/customer.js +++ b/selling/doctype/customer/customer.js @@ -57,7 +57,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'); @@ -88,7 +88,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, custom_new_doc: function(doctype) { var contact = wn.model.make_new_doc_and_get_name('Contact'); contact = locals['Contact'][contact]; diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 7b1528bafe2..78f842291df 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -374,7 +374,7 @@ class DocType(TransactionBase): msgprint("Please Enter Appropriate Conversion Rate for Customer's Currency to Base Currency (%s --> %s)" % (obj.doc.currency, default_currency), raise_exception = 1) if (obj.doc.price_list_currency == default_currency and flt(obj.doc.plc_conversion_rate) != 1.00) or not obj.doc.plc_conversion_rate or (obj.doc.price_list_currency != default_currency and flt(obj.doc.plc_conversion_rate) == 1.00): - msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency ( (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1) + msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1) diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js index 994bb4a5550..682978bd17c 100644 --- a/selling/page/selling_home/selling_home.js +++ b/selling/page/selling_home/selling_home.js @@ -157,6 +157,14 @@ wn.module_page["Selling"] = [ "label":wn._("Sales Orders Pending to be Delivered"), route: "query-report/Sales Orders Pending To Be Delivered" }, + { + "label":wn._("Sales Person-wise Transaction Summary"), + route: "query-report/Sales Person-wise Transaction Summary", + }, + { + "label":wn._("Item-wise Sales History"), + route: "query-report/Item-wise Sales History", + }, ] } ] diff --git a/selling/report/item_wise_sales_history/__init__.py b/selling/report/item_wise_sales_history/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/selling/report/item_wise_sales_history/item_wise_sales_history.txt b/selling/report/item_wise_sales_history/item_wise_sales_history.txt new file mode 100644 index 00000000000..cf13bdc3eaf --- /dev/null +++ b/selling/report/item_wise_sales_history/item_wise_sales_history.txt @@ -0,0 +1,23 @@ +[ + { + "creation": "2013-05-03 14:38:34", + "docstatus": 0, + "modified": "2013-05-03 15:15:11", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Currency:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.basic_rate as \"Rate:Currency:120\",\n\tso_item.amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project_name as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Currency:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item\nwhere\n\tso.name = so_item.parent\n\tand so.docstatus = 1\norder by so.name desc", + "ref_doctype": "Sales Order", + "report_name": "Item-wise Sales History", + "report_type": "Query Report" + }, + { + "doctype": "Report", + "name": "Item-wise Sales History" + } +] \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/__init__.py b/selling/report/sales_person_wise_transaction_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js new file mode 100644 index 00000000000..4a8e6787ff7 --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -0,0 +1,54 @@ +wn.query_reports["Sales Person-wise Transaction Summary"] = { + "filters": [ + { + fieldname: "sales_person", + label: "Sales Person", + fieldtype: "Link", + options: "Sales Person" + }, + { + fieldname: "doc_type", + label: "Document Type", + fieldtype: "Select", + options: "Sales Order\nDelivery Note\nSales Invoice", + default: "Sales Order" + }, + { + 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:"company", + label: "Company", + fieldtype: "Link", + options: "Company", + default: sys_defaults.company + }, + { + fieldname:"item_group", + label: "Item Group", + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname:"brand", + label: "Brand", + fieldtype: "Link", + options: "Brand", + }, + { + fieldname:"customer", + label: "Customer", + fieldtype: "Link", + options: "Customer", + }, + ] +} \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py new file mode 100644 index 00000000000..612cb7425d6 --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -0,0 +1,81 @@ +# 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(filters) + data = get_entries(filters) + + return columns, data + +def get_columns(filters): + if not filters.get("doc_type"): + msgprint(_("Please select the document type first"), raise_exception=1) + + return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", + "Customer:Link/Customer:140", "Territory:Link/Territory:100", "Posting Date:Date:100", + "Item Code:Link/Item:120", "Qty:Currency:100", "Amount:Currency:120", + "Sales Person:Link/Sales Person:140", "Contribution %:Currency:110", + "Contribution Amount:Currency:140"] + +def get_entries(filters): + date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date" + conditions, items = get_conditions(filters, date_field) + entries = webnotes.conn.sql("""select dt.name, dt.customer, dt.territory, dt.%s, + dt_item.item_code, dt_item.qty, dt_item.amount, st.sales_person, + st.allocated_percentage, dt_item.amount*st.allocated_percentage/100 + from `tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st + where st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = '%s' + and dt.docstatus = 1 %s order by st.sales_person, dt.name desc""" % + (date_field, filters["doc_type"], filters["doc_type"], filters["doc_type"], conditions), + tuple(items), as_list=1) + + return entries + +def get_conditions(filters, date_field): + conditions = "" + if filters.get("company"): conditions += " and dt.company = '%s'" % filters["company"] + if filters.get("customer"): conditions += " and dt.customer = '%s'" % filters["customer"] + + if filters.get("from_date"): conditions += " and dt.%s >= '%s'" % \ + (date_field, filters["from_date"]) + if filters.get("to_date"): conditions += " and dt.%s <= '%s'" % (date_field, filters["to_date"]) + + if filters.get("sales_person"): conditions += " and st.sales_person = '%s'" % \ + filters["sales_person"] + + items = get_items(filters) + if items: + conditions += " and dt_item.item_code in (%s)" % ', '.join(['%s']*len(items)) + + return conditions, items + +def get_items(filters): + if filters.get("item_group"): key = "item_group" + elif filters.get("brand"): key = "brand" + else: key = "" + + items = [] + if key: + items = webnotes.conn.sql_list("""select name from tabItem where %s = %s""" % + (key, '%s'), (filters[key])) + + return items \ No newline at end of file diff --git a/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt new file mode 100644 index 00000000000..abd69f3f2fb --- /dev/null +++ b/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.txt @@ -0,0 +1,22 @@ +[ + { + "creation": "2013-05-03 11:31:05", + "docstatus": 0, + "modified": "2013-05-03 11:31:05", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "add_total_row": 1, + "doctype": "Report", + "is_standard": "Yes", + "name": "__common__", + "ref_doctype": "Sales Order", + "report_name": "Sales Person-wise Transaction Summary", + "report_type": "Script Report" + }, + { + "doctype": "Report", + "name": "Sales Person-wise Transaction Summary" + } +] \ No newline at end of file diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py index a705f0efa9d..533b37ddb0d 100644 --- a/setup/doctype/backup_manager/backup_googledrive.py +++ b/setup/doctype/backup_manager/backup_googledrive.py @@ -92,8 +92,7 @@ def backup_to_gdrive(): mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream" #Compare Local File with Server File - param = {} - children = drive_service.children().list(folderId=files_folder_id, **param).execute() + children = drive_service.children().list(folderId=files_folder_id).execute() for child in children.get('items', []): file = drive_service.files().get(fileId=child['id']).execute() if filename == file['title'] and size == int(file['fileSize']): diff --git a/setup/doctype/backup_manager/backup_manager.js b/setup/doctype/backup_manager/backup_manager.js index 8d2d8de853a..c78be3a1eb6 100644 --- a/setup/doctype/backup_manager/backup_manager.js +++ b/setup/doctype/backup_manager/backup_manager.js @@ -1,67 +1,90 @@ -cur_frm.cscript.refresh = function(doc) { - cur_frm.disable_save(); -} - -//dropbox -cur_frm.cscript.allow_dropbox_access = function(doc) { - if (doc.send_notifications_to == '') { - msgprint("Please enter email address.") - } - else { - 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); - }); - } +$.extend(cur_frm.cscript, { + refresh: function() { + cur_frm.disable_save(); + + if(!(cint(cur_frm.doc.dropbox_access_allowed) || + cint(cur_frm.doc.gdrive_access_allowed))) { + cur_frm.set_intro(wn._("You can start by selecting backup frequency and \ + granting access for sync")); + } else { + var services = { + "dropbox": wn._("Dropbox"), + "gdrive": wn._("Google Drive") + } + var active_services = []; + + $.each(services, function(service, label) { + var access_allowed = cint(cur_frm.doc[service + "_access_allowed"]); + var frequency = cur_frm.doc["upload_backups_to_" + service]; + if(access_allowed && frequency && frequency !== "Never") { + active_services.push(label + " [" + frequency + "]"); + } + }); + + if(active_services.length > 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 a8ecd636db0..c91cf1110fa 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 9a43f34746a..2d6f191e5e8 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 8275513dc9a..dc7f6b4b735 100644 --- a/setup/doctype/global_defaults/global_defaults.py +++ b/setup/doctype/global_defaults/global_defaults.py @@ -21,6 +21,7 @@ import webnotes.defaults from webnotes.utils import cint keydict = { + # "key in defaults": "key in Global Defaults" "print_style": "print_style", "fiscal_year": "current_fiscal_year", 'company': 'default_company', @@ -31,6 +32,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 7f816184691..853bb577056 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 7b804f8a96a..3a6b36d1ff4 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 3de9e5cfabc..0dab9e970dc 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.js b/setup/doctype/price_list/price_list.js index 75d3d0f1c13..b68627f73f7 100644 --- a/setup/doctype/price_list/price_list.js +++ b/setup/doctype/price_list/price_list.js @@ -14,10 +14,46 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +cur_frm.cscript.onload = function() { + cur_frm.cscript.show_item_prices(); +} + cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.set_intro(""); if(doc.__islocal) { cur_frm.set_intro("Save this list to begin."); return; + } else { + cur_frm.cscript.show_item_prices(); } } + +cur_frm.cscript.show_item_prices = function() { + var item_price = wn.model.get("Item Price", {price_list_name: cur_frm.doc.name}); + + var show = item_price && item_price.length; + + cur_frm.toggle_display("item_prices_section", show); + $(cur_frm.fields_dict.item_prices.wrapper).empty(); + if (!show) return; + + var out = '\ + \ + \ + \ + \ + \ + \ + ' + + $.map(item_price.sort(function(a, b) { return a.parent.localeCompare(b.parent); }), function(d) { + return '' + + '' + + '' + + '' + + '' + + '' + }).join("\n") + + '\ +
' + wn._("Item Code") + '' + wn._("Price") + '' + wn._("Valid For Selling") + '' + wn._("Valid For Buying") + '
' + d.parent + '' + format_currency(d.ref_rate, d.ref_currency) + '' + (cint(d.selling) ? '' : "") + '' + (cint(d.buying) ? '' : "") + '
'; + $(out).appendTo($(cur_frm.fields_dict.item_prices.wrapper)); +} diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py index a88309b6db7..ae49bf868dd 100644 --- a/setup/doctype/price_list/price_list.py +++ b/setup/doctype/price_list/price_list.py @@ -16,73 +16,20 @@ from __future__ import unicode_literals import webnotes - -from webnotes.model.doc import Document -from webnotes import msgprint +from webnotes import msgprint, _ +from webnotes.utils import cint class DocType: def __init__(self, d, dl): self.doc, self.doclist = d, dl - self.cl = [] - - # validate currency - def is_currency_valid(self, currency): - if currency in self.cl: - return 1 - - if webnotes.conn.sql("select name from tabCurrency where name=%s", currency): - self.cl.append(currency) - return 1 - else: - return 0 - - def download_template(self, arg=None): - """download 3 column template with all Items""" - default_currency = webnotes.conn.get_default('currency') - item_list = webnotes.conn.sql("""select name from tabItem where - (ifnull(is_sales_item,'')='Yes' or ifnull(is_service_item,'')='Yes')""") - data = [self.get_price(i[0], default_currency) for i in item_list] - return [['Item', 'Rate', 'Currency']] + data - - def get_price(self, item, default_currency): - rate = webnotes.conn.sql("""select ref_rate, ref_currency from `tabItem Price` - where parent=%s and price_list_name=%s""", (item, self.doc.name)) - return [item, rate and rate[0][0] or 0, rate and rate[0][1] or default_currency] - - # update prices in Price List - def update_prices(self): - from webnotes.utils.datautils import read_csv_content_from_attached_file - data = read_csv_content_from_attached_file(self.doc) - webnotes.conn.auto_commit_on_many_writes = 1 - - updated = 0 - - for line in data: - if line and len(line)==3 and line[0]!='Item': - # if item exists - if webnotes.conn.sql("select name from tabItem where name=%s", line[0]): - if self.is_currency_valid(line[2]): - # if price exists - ref_ret_detail = webnotes.conn.sql("select name from `tabItem Price` where parent=%s and price_list_name=%s and ref_currency=%s", \ - (line[0], self.doc.name, line[2])) - if ref_ret_detail: - webnotes.conn.sql("update `tabItem Price` set ref_rate=%s where name=%s", (line[1], ref_ret_detail[0][0])) - else: - d = Document('Item Price') - d.parent = line[0] - d.parentfield = 'ref_rate_details' - d.parenttype = 'Item' - d.price_list_name = self.doc.name - d.ref_rate = line[1] - d.ref_currency = line[2] - d.save(1) - updated += 1 - else: - msgprint("[Ignored] Unknown currency '%s' for Item '%s'" % (line[2], line[0])) - else: - msgprint("[Ignored] Did not find Item '%s'" % line[1]) - - msgprint("%s items updated" % updated) - webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file + def onload(self): + self.doclist.extend(webnotes.conn.sql("""select * from `tabItem Price` + where price_list_name=%s""", self.doc.name, as_dict=True, update={"doctype": "Item Price"})) + + def validate(self): + if not (cint(self.doc.valid_for_all_countries) or len(self.doclist.get({"parentfield": "valid_for_countries"}))): + msgprint(_("""Please check "Valid For All Countries" or \ + enter atlease one row in the "Countries" table."""), raise_exception=True) + diff --git a/setup/doctype/price_list/price_list.txt b/setup/doctype/price_list/price_list.txt index a230f5b98ca..781eae5f68f 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:45:00", "modified_by": "Administrator", "owner": "Administrator" }, @@ -53,12 +53,54 @@ "reqd": 1 }, { - "depends_on": "price_list_name", + "doctype": "DocField", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "default": "1", + "doctype": "DocField", + "fieldname": "valid_for_all_countries", + "fieldtype": "Check", + "label": "Valid for all countries" + }, + { + "description": "A list of Countries, for which, this Price List is valid", + "doctype": "DocField", + "fieldname": "valid_for_countries", + "fieldtype": "Table", + "label": "Valid for the following countries", + "options": "Price List Country" + }, + { + "doctype": "DocField", + "fieldname": "item_prices_section", + "fieldtype": "Section Break", + "label": "Item Prices" + }, + { + "doctype": "DocField", + "fieldname": "item_prices", + "fieldtype": "HTML", + "label": "Item Prices" + }, + { + "depends_on": "eval:!doc.__islocal", + "doctype": "DocField", + "fieldname": "section_break_1", + "fieldtype": "Section Break", + "label": "How to upload" + }, + { + "depends_on": "eval:!doc.__islocal", "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 +120,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 53b86a39fca..fe87821904d 100644 --- a/setup/doctype/price_list/test_price_list.py +++ b/setup/doctype/price_list/test_price_list.py @@ -1,6 +1,8 @@ test_records = [ [{ "doctype": "Price List", - "price_list_name": "_Test Price List" + "price_list_name": "_Test Price List", + "currency": "INR", + "valid_for_all_countries": 1 }] ] \ No newline at end of file diff --git a/setup/doctype/price_list_country/__init__.py b/setup/doctype/price_list_country/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/setup/doctype/price_list_country/price_list_country.py b/setup/doctype/price_list_country/price_list_country.py new file mode 100644 index 00000000000..928aa9ff9f2 --- /dev/null +++ b/setup/doctype/price_list_country/price_list_country.py @@ -0,0 +1,8 @@ +# For license information, please see license.txt + +from __future__ import unicode_literals +import webnotes + +class DocType: + def __init__(self, d, dl): + self.doc, self.doclist = d, dl \ No newline at end of file diff --git a/setup/doctype/price_list_country/price_list_country.txt b/setup/doctype/price_list_country/price_list_country.txt new file mode 100644 index 00000000000..640b0a80527 --- /dev/null +++ b/setup/doctype/price_list_country/price_list_country.txt @@ -0,0 +1,36 @@ +[ + { + "creation": "2013-04-29 18:24:32", + "docstatus": 0, + "modified": "2013-04-29 18:24:32", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "autoname": "PLCNTRY-.#####", + "doctype": "DocType", + "istable": 1, + "module": "Setup", + "name": "__common__" + }, + { + "doctype": "DocField", + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "name": "__common__", + "options": "Country", + "parent": "Price List Country", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "reqd": 1 + }, + { + "doctype": "DocType", + "name": "Price List Country" + }, + { + "doctype": "DocField" + } +] \ No newline at end of file diff --git a/startup/website.py b/startup/website.py index 295e5bb0cab..be8eba6def1 100644 --- a/startup/website.py +++ b/startup/website.py @@ -1,4 +1,5 @@ import webnotes, conf, os +from webnotes.utils import cint, cstr def get_templates_path(): return os.path.join(os.path.dirname(conf.__file__), "app", "website", "templates") @@ -61,12 +62,14 @@ def update_template_args(page_name, args): settings = webnotes.doc("Website Settings", "Website Settings") for k in ["banner_html", "brand_html", "copyright", "address", "twitter_share_via", - "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]: + "favicon", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", + "disable_signup"]: if k in settings.fields: args[k] = settings.fields.get(k) - for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]: - args[k] = int(args.get(k) or 0) + for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share", + "disable_signup"]: + args[k] = cint(args.get(k) or 0) args.url = quote(str(get_request_site_address(full_address=True)), str("")) args.encoded_title = quote(str(args.title or ""), str("")) diff --git a/stock/doctype/batch/batch.txt b/stock/doctype/batch/batch.txt index 4722996c0f8..c6ed3c370a0 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/item/item.js b/stock/doctype/item/item.js index 8b3e04484a2..a344ad3ce92 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 6b6dfb37005..63275047ad7 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() @@ -121,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=[] diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index 5c2f48d061a..12033c91d5e 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-28 15:56:38", + "creation": "2013-04-25 10:56:55", "docstatus": 0, - "modified": "2013-04-23 11:44:50", + "modified": "2013-05-02 15:10:00", "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", @@ -881,24 +887,27 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Material Master Manager", - "write": 1, - "report": 1 - }, - { - "cancel": 0, - "create": 0, - "doctype": "DocPerm", + "permlevel": 0, + "report": 1, "role": "Material Manager", - "write": 0, - "report": 1 + "write": 1 }, { "cancel": 0, - "create": 0, + "create": 1, "doctype": "DocPerm", + "permlevel": 0, + "report": 1, "role": "Material User", - "write": 0, - "report": 1 + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "doctype": "DocPerm", + "permlevel": 0, + "report": 1, + "role": "Material Master Manager", + "write": 1 } ] \ No newline at end of file diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py index dbbeecc85b3..f5a688ca701 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/item_price/item_price.txt b/stock/doctype/item_price/item_price.txt index 721902bc72b..ad0b840c112 100644 --- a/stock/doctype/item_price/item_price.txt +++ b/stock/doctype/item_price/item_price.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-08 15:37:16", + "creation": "2013-04-29 15:18:04", "docstatus": 0, - "modified": "2013-03-21 17:29:15", + "modified": "2013-04-29 19:16:45", "modified_by": "Administrator", "owner": "Administrator" }, @@ -68,13 +68,13 @@ "doctype": "DocField", "fieldname": "selling", "fieldtype": "Check", - "label": "For Selling" + "label": "Valid For Selling" }, { "description": "Allow this price in purchase related forms", "doctype": "DocField", "fieldname": "buying", "fieldtype": "Check", - "label": "For Buying" + "label": "Valid For Buying" } ] \ No newline at end of file diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py index 080989f2d90..f5dbb52bbef 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_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index 7c406f8e8cb..c3ce2d7f40b 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -25,6 +25,8 @@ class TestStockEntry(unittest.TestCase): where item_code='_Test Item'""") self.assertTrue(mr_name) + + webnotes.conn.set_default("company", self.old_default_company) def test_warehouse_company_validation(self): from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany @@ -71,7 +73,7 @@ class TestStockEntry(unittest.TestCase): webnotes.defaults.set_global_default("auto_inventory_accounting", 0) def test_material_issue_gl_entry(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") + self._clear_stock() webnotes.defaults.set_global_default("auto_inventory_accounting", 1) mr = webnotes.bean(copy=test_records[0]) @@ -111,9 +113,10 @@ class TestStockEntry(unittest.TestCase): ) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", self.old_default_company) def test_material_transfer_gl_entry(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") + self._clear_stock() webnotes.defaults.set_global_default("auto_inventory_accounting", 1) mr = webnotes.bean(copy=test_records[0]) @@ -145,6 +148,7 @@ class TestStockEntry(unittest.TestCase): self.assertFalse(gl_entries) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + webnotes.conn.set_default("company", self.old_default_company) def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): # check stock ledger entries @@ -173,6 +177,9 @@ class TestStockEntry(unittest.TestCase): def _clear_stock(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.conn.sql("""delete from `tabBin`""") + + self.old_default_company = webnotes.conn.get_default("company") + webnotes.conn.set_default("company", "_Test Company") def _insert_material_receipt(self): self._clear_stock() @@ -185,6 +192,8 @@ class TestStockEntry(unittest.TestCase): se2.insert() se2.submit() + webnotes.conn.set_default("company", self.old_default_company) + def _get_actual_qty(self): return flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse"}, "actual_qty")) @@ -463,6 +472,8 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 - 5, actual_qty_2) + webnotes.conn.set_default("company", self.old_default_company) + return se, pr.doc.name def test_over_stock_return(self): @@ -563,6 +574,8 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 - 5, actual_qty_2) + webnotes.conn.set_default("company", self.old_default_company) + return se, pr.doc.name test_records = [ diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py index 3c83de32600..8fdb50e7a44 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 26501beab90..f3a04581a94 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/utilities/doctype/address/test_address.py b/utilities/doctype/address/test_address.py new file mode 100644 index 00000000000..eddd9c754de --- /dev/null +++ b/utilities/doctype/address/test_address.py @@ -0,0 +1,14 @@ +test_records = [ + [{ + "doctype": "Address", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "address_type": "Office", + "address_title": "_Test Address", + "address_line1": "_Test Address Line 1", + "city": "_Test City", + "country": "India", + "phone": "+91 0000000000", + "is_primary_address": 1 + }], +] \ No newline at end of file diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 4b34ba1286c..5d7d1a84b1a 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -22,6 +22,23 @@ from webnotes.model.doc import addchild from webnotes.model.controller import DocListController class TransactionBase(DocListController): + def get_default_address_and_contact(self, party_type): + """get a dict of default field values of address and contact for a given party type + party_type can be one of: customer, supplier""" + ret = {} + + # {customer: self.doc.fields.get("customer")} + args = {party_type: self.doc.fields.get(party_type)} + + address_text, address_name = self.get_address_text(**args) + ret.update({ + # customer_address + (party_type + "_address"): address_name, + "address_display": address_text + }) + ret.update(self.get_contact_text(**args)) + return ret + # Get Customer Default Primary Address - first load def get_default_customer_address(self, args=''): address_text, address_name = self.get_address_text(customer=self.doc.customer) @@ -73,7 +90,7 @@ class TransactionBase(DocListController): details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" % cond, as_dict = 1) else: details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_primary_address desc limit 1" % cond, as_dict = 1) - + extract = lambda x: details and details[0] and details[0].get(x,'') or '' address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),('\n','state'),(' ','pincode'),('\n','country'),('\nPhone: ','phone'),('\nFax: ', 'fax')] address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])]) diff --git a/website/doctype/about_us_settings/about_us_settings.txt b/website/doctype/about_us_settings/about_us_settings.txt index ef6122ec61a..baf9cc0af8c 100644 --- a/website/doctype/about_us_settings/about_us_settings.txt +++ b/website/doctype/about_us_settings/about_us_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 12:48:00", + "creation": "2013-03-19 12:02:15", "docstatus": 0, - "modified": "2013-03-12 14:48:34", + "modified": "2013-05-03 11:01:30", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "allow_attach": 1, "description": "Settings for the About Us Page", "doctype": "DocType", - "document_type": "Master", + "document_type": "Other", "issingle": 1, "module": "Website", "name": "__common__" @@ -21,7 +21,8 @@ "parent": "About Us Settings", "parentfield": "fields", "parenttype": "DocType", - "permlevel": 0 + "permlevel": 0, + "read_only": 0 }, { "create": 1, diff --git a/website/doctype/website_settings/website_settings.txt b/website/doctype/website_settings/website_settings.txt index 615c0a5b1b0..9c15480f07a 100644 --- a/website/doctype/website_settings/website_settings.txt +++ b/website/doctype/website_settings/website_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-26 06:51:18", + "creation": "2013-04-30 12:58:46", "docstatus": 0, - "modified": "2013-04-17 11:51:30", + "modified": "2013-05-02 15:51:14", "modified_by": "Administrator", "owner": "Administrator" }, @@ -219,13 +219,16 @@ "reqd": 0 }, { - "description": "Enter domain names associated to this website, each on a new line", "doctype": "DocField", - "fieldname": "domain_list", - "fieldtype": "Text", - "hidden": 1, - "label": "Domain List", - "reqd": 0 + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "description": "Disable Customer Signup link in Login page", + "doctype": "DocField", + "fieldname": "disable_signup", + "fieldtype": "Check", + "label": "Disable Signup" }, { "create": 1,