diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py index c15c2151c66..d8caea39814 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py @@ -6,9 +6,9 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.model.controller import DocListController +from frappe.model.document import Document -class CurrencyExchange(DocListController): +class CurrencyExchange(Document): def autoname(self): self.name = self.from_currency + "-" + self.to_currency diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index a61955562c9..64df4fe9418 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -18,9 +18,9 @@ content_sequence = [ ["Receivables / Payables", ["collections", "payments", "invoiced_amount", "payables"]], ["Buying", ["new_purchase_requests", "new_supplier_quotations", "new_purchase_orders"]], - ["Selling", ["new_leads", "new_enquiries", "new_quotations", "new_sales_orders"]], - ["Stock", ["new_delivery_notes", "new_purchase_receipts", "new_stock_entries"]], - ["Support", ["new_communications", "new_support_tickets", "open_tickets"]], + ["Selling", ["new_leads", "new_enquiries", "new_quotations", "new_sales_orders"]], + ["Stock", ["new_delivery_notes", "new_purchase_receipts", "new_stock_entries"]], + ["Support", ["new_communications", "new_support_tickets", "open_tickets"]], ["Projects", ["new_projects"]], ["System", ["scheduler_errors"]], ] @@ -35,17 +35,17 @@ digest_template = """ %(with_value)s %(no_value)s
-

To change what you see here, +

To change what you see here, create more digests, go to Setup > Email Digest

""" row_template = """

-%(label)s: +%(label)s: %(currency)s%(value)s

""" -from frappe.model.controller import DocListController -class EmailDigest(DocListController): +from frappe.model.document import Document +class EmailDigest(Document): def __init__(self, arg1, arg2=None): super(EmailDigest, self).__init__(arg1, arg2) self.from_date, self.to_date = self.get_from_to_date() @@ -69,29 +69,29 @@ class EmailDigest(DocListController): p["checked"] = p["name"] in recipient_list and 1 or 0 frappe.response['user_list'] = user_list - + def send(self): # send email only to enabled users valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser` where enabled=1""")] recipients = filter(lambda r: r in valid_users, self.recipient_list.split("\n")) - + common_msg = self.get_common_content() if recipients: for user_id in recipients: msg_for_this_receipient = self.get_msg_html(self.get_user_specific_content(user_id) + \ common_msg) if msg_for_this_receipient: - sendmail(recipients=user_id, + sendmail(recipients=user_id, subject="[ERPNext] [{frequency} Digest] {name}".format( - frequency=self.frequency, name=self.name), + frequency=self.frequency, name=self.name), msg=msg_for_this_receipient) - + def get_digest_msg(self): return self.get_msg_html(self.get_user_specific_content(frappe.session.user) + \ self.get_common_content(), send_only_if_updates=False) - + def get_common_content(self): out = [] for module, content in content_sequence: @@ -103,45 +103,45 @@ class EmailDigest(DocListController): out += [[1, "

" + _(module) + "

"]] + module_out + [[1, "
"]] else: out += module_out - + return out - + def get_user_specific_content(self, user_id): original_session_user = frappe.session.user - + # setting session user for role base event fetching frappe.session.user = user_id - + out = [] for ctype in user_specific_content: if self.get(ctype) and hasattr(self, "get_"+ctype): out.append(getattr(self, "get_"+ctype)(user_id)) - + frappe.session.user = original_session_user - + return out - + def get_msg_html(self, out, send_only_if_updates=True): with_value = [o[1] for o in out if o[0]] - + if with_value: has_updates = True with_value = "\n".join(with_value) else: has_updates = False with_value = "

There were no updates in the items selected for this digest.


" - + if not has_updates and send_only_if_updates: return - + # seperate out no value items no_value = [o[1] for o in out if not o[0]] if no_value: no_value = """

No Updates For:

""" + "\n".join(no_value) - + date = self.frequency == "Daily" and formatdate(self.from_date) or \ "%s to %s" % (formatdate(self.from_date), formatdate(self.to_date)) - + msg = digest_template % { "digest": self.frequency + " Digest", "date": date, @@ -150,23 +150,23 @@ class EmailDigest(DocListController): "no_value": no_value or "", "name": self.name } - + return msg - + def get_income_year_to_date(self): - return self.get_income(frappe.db.get_defaults("year_start_date"), + return self.get_income(frappe.db.get_defaults("year_start_date"), self.meta.get_label("income_year_to_date")) - + def get_bank_balance(self): # account is of type "Bank" or "Cash" accounts = dict([[a["name"], [a["account_name"], 0]] for a in self.get_accounts() if a["account_type"] in ["Bank", "Cash"]]) ackeys = accounts.keys() - + for gle in self.get_gl_entries(None, self.to_date): if gle["account"] in ackeys: accounts[gle["account"]][1] += gle["debit"] - gle["credit"] - + # build html out = self.get_html("Bank/Cash Balance", "", "") for ac in ackeys: @@ -174,37 +174,37 @@ class EmailDigest(DocListController): out += "\n" + self.get_html(accounts[ac][0], self.currency, fmt_money(accounts[ac][1]), style="margin-left: 17px") return sum((accounts[ac][1] for ac in ackeys)), out - + def get_income(self, from_date=None, label=None): # account is PL Account and Credit type account accounts = [a["name"] for a in self.get_accounts() if a["root_type"]=="Income"] - + income = 0 for gle in self.get_gl_entries(from_date or self.from_date, self.to_date): if gle["account"] in accounts: income += gle["credit"] - gle["debit"] - + return income, self.get_html(label or self.meta.get_label("income"), self.currency, fmt_money(income)) - + def get_expenses_booked(self): # account is PL Account and Debit type account accounts = [a["name"] for a in self.get_accounts() if a["root_type"]=="Expense"] - + expense = 0 for gle in self.get_gl_entries(self.from_date, self.to_date): if gle["account"] in accounts: expense += gle["debit"] - gle["credit"] - + return expense, self.get_html(self.meta.get_label("expenses_booked"), self.currency, fmt_money(expense)) - + def get_collections(self): return self.get_party_total("Customer", "credit", self.meta.get_label("collections")) - + def get_payments(self): return self.get_party_total("Supplier", "debit", self.meta.get_label("payments")) - + def get_party_total(self, party_type, gle_field, label): import re # account is of master_type Customer or Supplier @@ -212,10 +212,10 @@ class EmailDigest(DocListController): if a["master_type"]==party_type] # account is "Bank" or "Cash" - bc_accounts = [esc(a["name"], "()|") for a in self.get_accounts() + bc_accounts = [esc(a["name"], "()|") for a in self.get_accounts() if a["account_type"] in ["Bank", "Cash"]] bc_regex = re.compile("""(%s)""" % "|".join(bc_accounts)) - + total = 0 for gle in self.get_gl_entries(self.from_date, self.to_date): # check that its made against a bank or cash account @@ -223,78 +223,78 @@ class EmailDigest(DocListController): bc_regex.findall(gle["against"]): val = gle["debit"] - gle["credit"] total += (gle_field=="debit" and 1 or -1) * val - + return total, self.get_html(label, self.currency, fmt_money(total)) - + def get_invoiced_amount(self): # aka receivables return self.get_booked_total("Customer", "debit", self.meta.get_label("invoiced_amount")) def get_payables(self): return self.get_booked_total("Supplier", "credit", self.meta.get_label("payables")) - + def get_booked_total(self, party_type, gle_field, label): # account is of master_type Customer or Supplier accounts = [a["name"] for a in self.get_accounts() if a["master_type"]==party_type] - + total = 0 for gle in self.get_gl_entries(self.from_date, self.to_date): if gle["account"] in accounts: total += gle[gle_field] return total, self.get_html(label, self.currency, fmt_money(total)) - + def get_new_leads(self): return self.get_new_count("Lead", self.meta.get_label("new_leads")) - + def get_new_enquiries(self): return self.get_new_count("Opportunity", self.meta.get_label("new_enquiries"), docstatus=1) - + def get_new_quotations(self): return self.get_new_sum("Quotation", self.meta.get_label("new_quotations"), "grand_total") - + def get_new_sales_orders(self): return self.get_new_sum("Sales Order", self.meta.get_label("new_sales_orders"), "grand_total") - + def get_new_delivery_notes(self): return self.get_new_sum("Delivery Note", self.meta.get_label("new_delivery_notes"), "grand_total") - + def get_new_purchase_requests(self): return self.get_new_count("Material Request", self.meta.get_label("new_purchase_requests"), docstatus=1) - + def get_new_supplier_quotations(self): return self.get_new_sum("Supplier Quotation", self.meta.get_label("new_supplier_quotations"), "grand_total") - + def get_new_purchase_orders(self): return self.get_new_sum("Purchase Order", self.meta.get_label("new_purchase_orders"), "grand_total") - + def get_new_purchase_receipts(self): return self.get_new_sum("Purchase Receipt", self.meta.get_label("new_purchase_receipts"), "grand_total") - + def get_new_stock_entries(self): return self.get_new_sum("Stock Entry", self.meta.get_label("new_stock_entries"), "total_amount") - + def get_new_support_tickets(self): - return self.get_new_count("Support Ticket", self.meta.get_label("new_support_tickets"), + return self.get_new_count("Support Ticket", self.meta.get_label("new_support_tickets"), filter_by_company=False) - + def get_new_communications(self): - return self.get_new_count("Communication", self.meta.get_label("new_communications"), + return self.get_new_count("Communication", self.meta.get_label("new_communications"), filter_by_company=False) - + def get_new_projects(self): - return self.get_new_count("Project", self.meta.get_label("new_projects"), + return self.get_new_count("Project", self.meta.get_label("new_projects"), filter_by_company=False) - + def get_calendar_events(self, user_id): from frappe.core.doctype.event.event import get_events events = get_events(self.future_from_date.strftime("%Y-%m-%d"), self.future_to_date.strftime("%Y-%m-%d")) - + html = "" if events: for i, e in enumerate(events): @@ -306,18 +306,18 @@ class EmailDigest(DocListController): else: html += "
  • %s [%s - %s]
  • " % \ (e.subject, datetime_in_user_format(e.starts_on), datetime_in_user_format(e.ends_on)) - + if html: return 1, "

    Upcoming Calendar Events (max 10):


    " else: return 0, "

    Calendar Events

    " - + def get_todo_list(self, user_id): todo_list = frappe.db.sql("""select * from `tabToDo` where (owner=%s or assigned_by=%s) order by field(priority, 'High', 'Medium', 'Low') asc, date asc""", (user_id, user_id), as_dict=True) - + html = "" if todo_list: for i, todo in enumerate([todo for todo in todo_list if not todo.checked]): @@ -327,14 +327,14 @@ class EmailDigest(DocListController): todo.description = "%s: %s - %s %s" % \ (todo.reference_type, get_url_to_form(todo.reference_type, todo.reference_name), _("assigned by"), get_fullname(todo.assigned_by)) - + html += "
  • %s [%s]
  • " % (todo.description, todo.priority) - + if html: return 1, "

    To Do (max 10):


    " else: return 0, "

    To Do

    " - + def get_new_count(self, doctype, label, docstatus=0, filter_by_company=True): if filter_by_company: company = """and company="%s" """ % self.company.replace('"', '\"') @@ -342,22 +342,22 @@ class EmailDigest(DocListController): company = "" count = frappe.db.sql("""select count(*) from `tab%s` where docstatus=%s %s and - date(creation)>=%s and date(creation)<=%s""" % + date(creation)>=%s and date(creation)<=%s""" % (doctype, docstatus, company, "%s", "%s"), (self.from_date, self.to_date)) count = count and count[0][0] or 0 - + return count, self.get_html(label, None, count) - + def get_new_sum(self, doctype, label, sum_field): count_sum = frappe.db.sql("""select count(*), sum(ifnull(`%s`, 0)) from `tab%s` where docstatus=1 and company = %s and date(creation)>=%s and date(creation)<=%s""" % (sum_field, doctype, "%s", "%s", "%s"), (self.company, self.from_date, self.to_date)) count, total = count_sum and count_sum[0] or (0, 0) - - return count, self.get_html(label, self.currency, + + return count, self.get_html(label, self.currency, "%s - (%s)" % (fmt_money(total), cstr(count))) - + def get_html(self, label, currency, value, style=None): """get html output""" return row_template % { @@ -366,27 +366,27 @@ class EmailDigest(DocListController): "currency": currency and (currency+" ") or "", "value": value } - + def get_gl_entries(self, from_date=None, to_date=None): """get valid GL Entries filtered by company and posting date""" if from_date==self.from_date and to_date==self.to_date and \ hasattr(self, "gl_entries"): return self.gl_entries - - gl_entries = frappe.db.sql("""select `account`, + + gl_entries = frappe.db.sql("""select `account`, ifnull(credit, 0) as credit, ifnull(debit, 0) as debit, `against` from `tabGL Entry` - where company=%s - and posting_date <= %s %s""" % ("%s", "%s", + where company=%s + and posting_date <= %s %s""" % ("%s", "%s", from_date and "and posting_date>='%s'" % from_date or ""), (self.company, to_date or self.to_date), as_dict=1) - + # cache if it is the normal cases if from_date==self.from_date and to_date==self.to_date: self.gl_entries = gl_entries - + return gl_entries - + def get_accounts(self): if not hasattr(self, "accounts"): self.accounts = frappe.db.sql("""select name, is_pl_account, @@ -395,10 +395,10 @@ class EmailDigest(DocListController): and group_or_ledger = "Ledger" order by lft""", (self.company,), as_dict=1) return self.accounts - + def get_from_to_date(self): today = now_datetime().date() - + # decide from date based on email digest frequency if self.frequency == "Daily": # from date, to_date is yesterday @@ -415,10 +415,10 @@ class EmailDigest(DocListController): to_date = today - relativedelta(days=today.day) return from_date, to_date - + def get_future_from_to_date(self): today = now_datetime().date() - + # decide from date based on email digest frequency if self.frequency == "Daily": # from date, to_date is today @@ -433,14 +433,14 @@ class EmailDigest(DocListController): from_date = today - relativedelta(days=today.day-1) # to date is the last day of the current month to_date = from_date + relativedelta(days=-1, months=1) - + return from_date, to_date def get_next_sending(self): from_date, to_date = self.get_from_to_date() send_date = to_date + timedelta(days=1) - + if self.frequency == "Daily": next_send_date = send_date + timedelta(days=1) elif self.frequency == "Weekly": @@ -448,31 +448,31 @@ class EmailDigest(DocListController): else: next_send_date = send_date + relativedelta(months=1) self.next_send = formatdate(next_send_date) + " at midnight" - + return send_date - + def get_open_tickets(self): open_tickets = frappe.db.sql("""select name, subject, modified, raised_by from `tabSupport Ticket` where status='Open' order by modified desc limit 10""", as_dict=True) - + if open_tickets: return 1, """

    Latest Open Tickets (max 10):

    %s""" % \ "".join(["

    %(name)s: %(subject)s
    by %(raised_by)s on %(modified)s

    " % \ t for t in open_tickets]) else: return 0, "No Open Tickets!" - + def get_scheduler_errors(self): import frappe.utils.scheduler return frappe.utils.scheduler.get_error_report(self.from_date, self.to_date) - + def onload(self): self.get_next_sending() - + def send(): now_date = now_datetime().date() - + for ed in frappe.db.sql("""select name from `tabEmail Digest` where enabled=1 and docstatus<2""", as_list=1): ed_obj = frappe.get_doc('Email Digest', ed[0]) diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py index 424d6a29b15..d992488fc65 100644 --- a/erpnext/stock/doctype/price_list/price_list.py +++ b/erpnext/stock/doctype/price_list/price_list.py @@ -5,10 +5,10 @@ from __future__ import unicode_literals import frappe from frappe import _, throw from frappe.utils import cint -from frappe.model.controller import DocListController +from frappe.model.document import Document import frappe.defaults -class PriceList(DocListController): +class PriceList(Document): def validate(self): if not cint(self.buying) and not cint(self.selling): throw(_("Price List must be applicable for Buying or Selling")) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 6ab754bd36c..79eeddf2a86 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -6,12 +6,12 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt, getdate, add_days, formatdate -from frappe.model.controller import DocListController +from frappe.model.document import Document from datetime import date class StockFreezeError(frappe.ValidationError): pass -class StockLedgerEntry(DocListController): +class StockLedgerEntry(Document): def validate(self): from erpnext.stock.utils import validate_warehouse_company self.validate_mandatory()