def check_doctype_restriction(): restricted_docs = [] employee = get_employee_code() max_priority = frappe.db.sql( "select max(priority) as max_priority from `tabEmployee Doctype Restriction`" ) docs = frappe.db.get_values('Employee Doctype Restriction', { 'employee': employee, 'status': 'Open', 'priority': max_priority[0][0] }, 'document_type', as_dict=True) for doc in docs: url = get_url_to_list(doc.document_type) if doc.document_type == "Employee": url = get_url_to_form(doc.document_type, employee) elif doc.document_type == "Employee Verification": # get name of the doc _emp_ver_name = frappe.db.get_value("Employee Verification", {"employee": employee}) if _emp_ver_name: url = get_url_to_form(doc.document_type, _emp_ver_name) restricted_docs.append({"doc": doc.document_type, "url": url}) return restricted_docs
def _get_message(url=False): name = self.name employee_name = cstr(employee.employee_name) if url: name = get_url_to_form(self.doctype, self.name) employee_name = get_url_to_form("Employee", self.employee, label=employee_name) return (_("New Leave Application") + ": %s - " + _("Employee") + ": %s") % (name, employee_name)
def send_slack_message(webhook_url, message, reference_doctype, reference_name): slack_url = frappe.db.get_value("Slack Webhook URL", webhook_url, "webhook_url") doc_url = get_url_to_form(reference_doctype, reference_name) attachments = [ { "fallback": _("See the document at {0}").format(doc_url), "actions": [ { "type": "button", "text": _("Go to the document"), "url": doc_url, "style": "primary" } ] } ] data = {"text": message, "attachments": attachments} r = requests.post(slack_url, data=json.dumps(data)) if r.ok == True: return 'success' elif r.ok == False: frappe.log_error(r.error, _('Slack Webhook Error')) return 'error'
def trigger_payments(self): """Payment Workflow Utility 1. Get all members due for payment 1. Trigger Payment 1. Update Membership 1. Log success and failed payments """ if not self.enabled: frappe.throw("Please Enable E Mandate Payments in Membership Settings") members = self.get_members_due_for_payment() if not members: return for member in members: try: payment = self.trigger_payment_for_member(member) membership = self.update_membership_details(member, payment) self.successful_transaction.append(membership) except Exception as e: title = "E Mandate Payment Error for {0}".format(member.name) log = frappe.log_error(e, title) self.failed_transaction.append([member.name,get_url_to_form("Error Log", log.name), e]) send_update_email(self.successful_transaction, self.failed_transaction)
def _get_message(url=False): if url: name = get_url_to_form(self.doctype, self.name) else: name = self.name return (_("Leave Application") + ": %s - %s") % (name, _(status))
def send_to_telegram(telegram_user, message, reference_doctype=None, reference_name=None, attachment=None): space = "\n" * 2 telegram_chat_id = frappe.db.get_value('Telegram User Settings', telegram_user, 'telegram_chat_id') telegram_settings = frappe.db.get_value('Telegram User Settings', telegram_user, 'telegram_settings') telegram_token = frappe.db.get_value('Telegram Settings', telegram_settings, 'telegram_token') bot = telegram.Bot(token=telegram_token) if reference_doctype and reference_name: doc_url = get_url_to_form(reference_doctype, reference_name) telegram_doc_link = _("See the document at {0}").format(doc_url) if message: message = space + str(message) + space + str(telegram_doc_link) if type(attachment) is str: attachment = int(attachment) else: if attachment: attachment = 1 if attachment == 1: attachment_url = get_url_for_telegram(reference_doctype, reference_name) message = message + space + attachment_url bot.send_message(chat_id=telegram_chat_id, text=message) else: message = space + str(message) + space bot.send_message(chat_id=telegram_chat_id, text=message)
def assign_to_role(role, reviewer=False): emails = get_emails_from_role(role) print "####### role = {} emails = {}".format(role, emails) name = "" for email in emails: if email == "*****@*****.**": continue print "email = {}".format(email) print "role[-2] = {}".format(role[:-2]) if (reviewer): name = role else: name = role[:-2] add({ "assign_to": email, "doctype": "Task", "name": doc.project + " - " + name, "description": "New Task : " + doc.project + " - " + name + "<br><br>" + "<a href='" + get_url_to_form("Task", doc.project + " - " + name) + "' target='_self'> Click Hear To open Task</a>" })
def get_purchase_orders_items_overdue_list(self): fields_po = "distinct `tabPurchase Order Item`.parent as po" fields_poi = "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," \ "received_qty, qty - received_qty as missing_qty, rate, amount" sql_po = """select {fields} from `tabPurchase Order Item` left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date and received_qty < qty order by `tabPurchase Order Item`.parent DESC, `tabPurchase Order Item`.schedule_date DESC""".format(fields=fields_po) sql_poi = """select {fields} from `tabPurchase Order Item` left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date and received_qty < qty order by `tabPurchase Order Item`.idx""".format( fields=fields_poi) purchase_order_list = frappe.db.sql(sql_po, as_dict=True) purchase_order_items_overdue_list = frappe.db.sql(sql_poi, as_dict=True) for t in purchase_order_items_overdue_list: t.link = get_url_to_form("Purchase Order", t.parent) t.rate = fmt_money(t.rate, 2, t.currency) t.amount = fmt_money(t.amount, 2, t.currency) return purchase_order_list, purchase_order_items_overdue_list
def get_access_token(self, oauth_token, oauth_verifier): auth = tweepy.OAuthHandler( self.consumer_key, self.get_password(fieldname="consumer_secret")) auth.request_token = { "oauth_token": oauth_token, "oauth_token_secret": oauth_verifier } try: auth.get_access_token(oauth_verifier) self.access_token = auth.access_token self.access_token_secret = auth.access_token_secret api = self.get_api() user = api.me() profile_pic = (user._json["profile_image_url"]).replace( "_normal", "") frappe.db.set_value( self.doctype, self.name, { "access_token": auth.access_token, "access_token_secret": auth.access_token_secret, "account_name": user._json["screen_name"], "profile_pic": profile_pic, "session_status": "Active", }, ) frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url_to_form( "Twitter Settings", "Twitter Settings") except TweepError as e: frappe.msgprint(_("Error! Failed to get access token.")) frappe.throw(_("Invalid Consumer Key or Consumer Secret Key"))
def send_slack_message(webhook_url, message, reference_doctype, reference_name): data = {"text": message, "attachments": []} slack_url, show_link = frappe.db.get_value( "Slack Webhook URL", webhook_url, ["webhook_url", "show_document_link"]) if show_link: doc_url = get_url_to_form(reference_doctype, reference_name) link_to_doc = { "fallback": _("See the document at {0}").format(doc_url), "actions": [{ "type": "button", "text": _("Go to the document"), "url": doc_url, "style": "primary", }], } data["attachments"].append(link_to_doc) r = requests.post(slack_url, data=json.dumps(data)) if not r.ok: message = error_messages.get(r.status_code, r.status_code) frappe.log_error(message, _("Slack Webhook Error")) return "error" return "success"
def send_notification_email(doc): if doc.type == 'Energy Point' and doc.email_content is None: return from frappe.utils import get_url_to_form, strip_html doc_link = get_url_to_form(doc.document_type, doc.document_name) header = get_email_header(doc) email_subject = strip_html(doc.subject) frappe.sendmail( recipients = doc.for_user, subject = email_subject, template = "new_notification", args = { 'body_content': doc.subject, 'description': doc.email_content, 'document_type': doc.document_type, 'document_name': doc.document_name, 'doc_link': doc_link }, header = [header, 'orange'], now=frappe.flags.in_test )
def _make_customer(source_name, ignore_permissions=False): quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type"]) if quotation and quotation[0]: lead_name = quotation[0] customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name}) if not customer_name: from erpnext.selling.doctype.lead.lead import _make_customer customer_doclist = _make_customer( lead_name, ignore_permissions=ignore_permissions) customer = frappe.bean(customer_doclist) customer.ignore_permissions = ignore_permissions if quotation[1] == "Shopping Cart": customer.doc.customer_group = frappe.db.get_value( "Shopping Cart Settings", None, "default_customer_group") try: customer.insert() return customer except NameError: if frappe.defaults.get_global_default( 'cust_master_name') == "Customer Name": customer.run_method("autoname") customer.doc.name += "-" + lead_name customer.insert() return customer else: raise except frappe.MandatoryError: from frappe.utils import get_url_to_form frappe.throw(_("Before proceeding, please create Customer from Lead") + \ (" - %s" % get_url_to_form("Lead", lead_name)))
def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE', description=None, notify=0): """ Notify assignee that there is a change in assignment """ if not (assigned_by and owner and doc_type and doc_name): return # self assignment / closing - no message if assigned_by == owner: return from frappe.boot import get_fullnames user_info = get_fullnames() # Search for email address in description -- i.e. assignee from frappe.utils import get_url_to_form assignment = get_url_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name)) if action == 'CLOSE': if owner == frappe.session.get('user'): arg = { 'contact': assigned_by, 'txt': _("The task %s, that you assigned to %s, has been closed.") % (assignment, user_info.get(owner, {}).get('fullname')) } else: arg = { 'contact': assigned_by, 'txt': _("The task %s, that you assigned to %s, has been closed by %s." ) % (assignment, user_info.get(owner, {}).get('fullname'), user_info.get(frappe.session.get('user'), {}).get('fullname')) } else: arg = { 'contact': owner, 'txt': _("A new task, %s, has been assigned to you by %s. %s") % (assignment, user_info.get(frappe.session.get('user'), {}).get('fullname'), description and ("<p>" + _("Description") + ": " + description + "</p>") or ""), 'notify': notify } arg["parenttype"] = "Assignment" from frappe.desk.page.messages import messages messages.post(**arg)
def get_todo_list(self, user_id): todo_list = frappe.db.sql("""select * from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open" 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]): if i >= 10: break if not todo.description and todo.reference_type: 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 += "<li style='line-height: 200%%'>%s [%s]</li>" % ( todo.description, todo.priority) if html: return 1, "<h4>To Do (max 10):</h4><ul>" + html + "</ul><hr>" else: return 0, "<p>To Do</p>"
def execute(): wrong_records = [] for dt in ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Purchase Order", "Purchase Receipt", "Purchase Invoice"): records = frappe.db.sql_list("""select name from `tab{0}` where apply_discount_on = 'Net Total' and ifnull(discount_amount, 0) != 0 and modified >= '2015-02-17' and docstatus=1""".format(dt)) if records: records = [get_url_to_form(dt, d) for d in records] wrong_records.append([dt, records]) if wrong_records: content = """Dear System Manager, Due to an error related to Discount Amount on Net Total, tax calculation might be wrong in the following records. We did not fix the tax amount automatically because it can corrupt the entries, so we request you to check these records and amend if you found the calculation wrong. Please check following Entries: %s Regards, Administrator""" % "\n".join([(d[0] + ": " + ", ".join(d[1])) for d in wrong_records]) try: sendmail_to_system_managers("[Important] [ERPNext] Tax calculation might be wrong, please check.", content) except: pass print "="*50 print content print "="*50
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]): if i >= 10: break if not todo.description and todo.reference_type: 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 += "<li style='line-height: 200%%'>%s [%s]</li>" % (todo.description, todo.priority) if html: return 1, "<h4>To Do (max 10):</h4><ul>" + html + "</ul><hr>" else: return 0, "<p>To Do</p>"
def background_enqueue_run(report_name, filters=None, user=None): """run reports in background""" if not user: user = frappe.session.user report = get_report_doc(report_name) track_instance = \ frappe.get_doc({ "doctype": "Prepared Report", "report_name": report_name, # This looks like an insanity but, without this it'd be very hard to find Prepared Reports matching given condition # We're ensuring that spacing is consistent. e.g. JS seems to put no spaces after ":", Python on the other hand does. "filters": json.dumps(json.loads(filters)), "ref_report_doctype": report_name, "report_type": report.report_type, "query": report.query, "module": report.module, }) track_instance.insert(ignore_permissions=True) frappe.db.commit() track_instance.enqueue_report() return { "name": track_instance.name, "redirect_url": get_url_to_form("Prepared Report", track_instance.name) }
def approve_attendance(doc, method): user = frappe.session.user if not doc.attendance_approver: frappe.throw(_("Please set Attendance Approver on Employee form")) if doc.attendance_approver and user != doc.attendance_approver: frappe.throw( _("Only '{0}' can approve this Attendance.").format( doc.attendance_approver)) else: frappe.db.set_value(doc.doctype, doc.name, "approval_status", "Approved") att_details = { "approver": doc.attendance_approver, "date": doc.att_date, "path": get_url_to_form(doc.doctype, doc.name), "status": "approved" } template = "templates/emails/attendance_notification.html" subject = "Attendance approved for date {0}.".format(doc.att_date) recipients = frappe.db.get_value("Employee", doc.employee, "user_id") message = frappe.get_template(template).render( {"att_details": att_details}) frappe.sendmail(recipients=recipients, subject=subject, message=message)
def stock_entry_received(self): se = frappe.new_doc("Stock Entry") se.posting_date = self.received_date se.purpose = "Repack" se.naming_series = "STE-" se.company = self.company se.volume = self.volume se.volume_rate = self.volume_rate se.volume_cost = self.volume_cost abbr = frappe.db.get_value("Company",self.company,'abbr') if self.get('bom_no'): se.from_bom = 1 se.bom_no = self.bom_no se.fg_completed_qty = flt(self.finished_product_qty) se.based_on = self.based_on for row in self.items: se.append("items",{ 'item_code': row.item_code, 's_warehouse': 'Jobwork - ' + abbr, 'qty': row.received_qty, 'batch_no': row.batch_no, 'basic_rate': row.rate, 'lot_no': row.lot_no, 'packaging_material': row.packaging_material, 'packing_size': row.packing_size, 'batch_yield': row.batch_yield, 'concentration': row.concentration }) se.append("items",{ 'item_code': self.finished_product, 't_warehouse': self.finished_product_warehouse, 'qty': self.finished_product_qty, 'packaging_material': self.packaging_material, 'packing_size': self.packing_size, 'lot_no': self.lot_no, 'concentration': self.concentration, 'batch_yield': self.batch_yield, }) for row in self.additional_costs: se.append('additional_costs', { 'description': row.description, 'amount': row.amount, }) try: se.save() se.submit() self.db_set('received_stock_entry' , se.name) self.db_set('valuation_rate' , se.items[-1].valuation_rate) url = get_url_to_form("Stock Entry", se.name) frappe.msgprint("New Stock Entry - <a href='{url}'>{doc}</a> created of Repack for Finished Product".format(url=url, doc=frappe.bold(se.name))) except: frappe.db.rollback() frappe.throw(_("Error creating Stock Entry"), title="Error")
def cancel_received(self): if self.received_stock_entry: se = frappe.get_doc("Stock Entry",self.received_stock_entry) se.cancel() frappe.db.commit() self.db_set('received_stock_entry','') url = get_url_to_form("Stock Entry", se.name) frappe.msgprint("Cancelled Stock Entry - <a href='{url}'>{doc}</a>".format(url=url, doc=frappe.bold(se.name)))
def callback(oauth_token=None, oauth_verifier=None): if oauth_token and oauth_verifier: twitter_settings = frappe.get_single("Twitter Settings") twitter_settings.get_access_token(oauth_token, oauth_verifier) frappe.db.commit() else: frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url_to_form( "Twitter Settings", "Twitter Settings")
def callback(code=None, error=None, error_description=None): if not error: linkedin_settings = frappe.get_doc("LinkedIn Settings") linkedin_settings.get_access_token(code) linkedin_settings.get_member_profile() frappe.db.commit() else: frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url_to_form("LinkedIn Settings","LinkedIn Settings")
def opportunity_send_mail(self, method): if self.service == 'TGT': link = get_url_to_form("Opportunity", self.name) subject = 'Reg.Opportunity- %s' % self.name content = """Dear Mam<br>Kindly find the new Opportunity. Click on <a href='%s'>View</a> to open the opportunity.<br>Thanks & Regards,<br>ERP """ % link frappe.sendmail(recipients=['*****@*****.**'], subject=subject, message=content)
def get_project_details(): '''Get all projects details''' project_data = [] projects = frappe.db.get_all( "Project", fields=["name", "priority", "percent_complete", "_assign"]) for project in projects: project_users = [] total_tasks = frappe.db.count("Task", filters={"project": project.name}) closed_tasks = frappe.db.count("Task", filters={ "project": project.name, "status": "Completed" }) assigned_users = json.loads(project._assign) if project._assign else [] for assignee in assigned_users: full_name = frappe.db.get_value("User", assignee, "full_name") assignee_tasks = frappe.get_list( "Task", filters={"_assign": ["like", "%{}%".format(assignee)]}, fields=["name", "status"]) user_data = { "name": assignee, "label": full_name, "avatar": get_gravatar_url(assignee), "tasks": [] } for task in assignee_tasks: user_data["tasks"].append({ "url": get_url_to_form("Task", task.name), "status": task.status }) project_users.append(user_data) project_data.append({ "name": project.name, "totalTasks": total_tasks, "closedTasks": closed_tasks, "percentCompleted": project.percent_complete, "priority": project.priority, "assigned": project_users }) return project_data
def get_calendar_events(self): """Get calendar events for given user""" from frappe.desk.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")) or [] for i, e in enumerate(events): e.starts_on_label = format_time(e.starts_on) e.ends_on_label = format_time(e.ends_on) if e.ends_on else None e.date = formatdate(e.starts) e.link = get_url_to_form("Event", e.name) return events
def get_project_list(self, user_id=None): """Get project list""" if not user_id: user_id = frappe.session.user project_list = frappe.db.sql("""select * from `tabProject` where status='Open' and project_type='External' order by modified asc limit 10""", as_dict=True) for t in project_list: t.link = get_url_to_form("Issue", t.name) return project_list
def validate(self): if frappe.db.exists("Supplier Items", { 'supplier': self.supplier, 'item_code': self.item_code }): name = frappe.db.get_value("Supplier Items", { 'supplier': self.supplier, 'item_code': self.item_code }) url = get_url_to_form("Supplier Items", name) frappe.throw( _("Record already exist. <br><b><a href='{url}'>{name}</a></b>." .format(url=url, name=name)))
def get_todo_list(self, user_id=None): """Get to-do list""" if not user_id: user_id = frappe.session.user todo_list = frappe.db.sql("""select * from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open" order by field(priority, 'High', 'Medium', 'Low') asc, date asc limit 20""", (user_id, user_id), as_dict=True) for t in todo_list: t.link = get_url_to_form("ToDo", t.name) return todo_list
def get_member_profile(self): response = requests.get(url="https://api.linkedin.com/v2/me", headers=self.get_headers()) response = frappe.parse_json(response.content.decode()) frappe.db.set_value( self.doctype, self.name, { "person_urn": response["id"], "account_name": response["vanityName"], "session_status": "Active" }) frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url_to_form( "LinkedIn Settings", "LinkedIn Settings")
def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE', description=None, notify=0): """ Notify assignee that there is a change in assignment """ if not (assigned_by and owner and doc_type and doc_name): return # self assignment / closing - no message if assigned_by==owner: return from frappe.boot import get_fullnames user_info = get_fullnames() # Search for email address in description -- i.e. assignee from frappe.utils import get_url_to_form assignment = get_url_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name)) if action=='CLOSE': if owner == frappe.session.get('user'): arg = { 'contact': assigned_by, 'txt': "The task %s, that you assigned to %s, has been \ closed." % (assignment, user_info.get(owner, {}).get('fullname')) } else: arg = { 'contact': assigned_by, 'txt': "The task %s, that you assigned to %s, \ has been closed by %s." % (assignment, user_info.get(owner, {}).get('fullname'), user_info.get(frappe.session.get('user'), {}).get('fullname')) } else: arg = { 'contact': owner, 'txt': "A new task, %s, has been assigned to you by %s. %s" \ % (assignment, user_info.get(frappe.session.get('user'), {}).get('fullname'), description and ("<p>Description: " + description + "</p>") or ""), 'notify': notify } arg["parenttype"] = "Assignment" from frappe.core.page.messages import messages import json messages.post(json.dumps(arg))
def get_calendar_events(self): """Get calendar events for given user""" from frappe.desk.doctype.event.event import get_events from_date, to_date = get_future_date_for_calendaer_event(self.frequency) events = get_events(from_date, to_date) event_count = 0 for i, e in enumerate(events): e.starts_on_label = format_time(e.starts_on) e.ends_on_label = format_time(e.ends_on) if e.ends_on else None e.date = formatdate(e.starts) e.link = get_url_to_form("Event", e.name) event_count += 1 return events, event_count
def send_mail_to_approver(doctype,doc_name,att_date,employee_name,attendance_approver): attendance_doc = frappe.get_doc("Attendance",doc_name) attendance_doc.send_mail_to_approver = 1 attendance_doc.save(ignore_permissions=True) att_details = {"employee": employee_name, "date": att_date, "path": get_url_to_form(doctype, doc_name), "status": "pending"} template = "templates/emails/attendance_notification.html" subject = "Pending Attendance Approval of {0} for date {1}.".format(employee_name,att_date) recipients = frappe.db.get_value("User",attendance_approver, "email") message = frappe.get_template(template).render({"att_details": att_details}) try: frappe.sendmail(recipients=recipients, subject=subject,message= message) return "Success" except: msgprint(_("sendmail Error"), raise_exception=1)
def create_stock_entry(self): se = frappe.new_doc("Stock Entry") se.naming_series = "STE-.fiscal.-" se.stock_entry_type = "Material Receipt" se.purpose = "Material Receipt" se.posting_date = self.posting_date se.posting_time = self.posting_time se.company = self.company se.set_posting_time = self.set_posting_time se.to_warehouse = self.warehouse se.fiscal = self.fiscal for row in self.items: se.append( "items", { 'item_code': row.item_code, 't_warehouse': row.t_warehouse, 'qty': row.qty, 'basic_rate': row.basic_rate, 'uom': row.uom, 'conversion_factor': row.conversion_factor, }) for row in self.additional_costs: se.append( 'additional_costs', { 'posting_date': row.posting_date, 'expense_account': row.expense_account, 'credit_account': row.credit_account, 'description': row.description, 'amount': row.amount, }) try: se.save() se.submit() except: frappe.db.rollback() frappe.throw(_("Error creating Stock Entry"), title="Error") else: self.update_batch_no(se) self.db_set('stock_entry', se.name) url = get_url_to_form("Stock Entry", se.name) frappe.msgprint( "Stock Entry - <a href='{url}'>{doc}</a> created of Material Receipt." .format(url=url, doc=frappe.bold(se.name))) frappe.db.commit()
def get_issue_list(self, user_id=None): """Get issue list""" if not user_id: user_id = frappe.session.user meta = frappe.get_meta("Issue") role_permissions = frappe.permissions.get_role_permissions(meta, user_id) if not role_permissions.get("read"): return None issue_list = frappe.db.sql("""select * from `tabIssue` where status in ("Replied","Open") order by modified asc limit 10""", as_dict=True) for t in issue_list: t.link = get_url_to_form("Issue", t.name) return issue_list
def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE', description=None, notify=0): """ Notify assignee that there is a change in assignment """ if not (assigned_by and owner and doc_type and doc_name): return # self assignment / closing - no message if assigned_by==owner: return from frappe.boot import get_fullnames user_info = get_fullnames() # Search for email address in description -- i.e. assignee from frappe.utils import get_url_to_form assignment = get_url_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name)) owner_name = user_info.get(owner, {}).get('fullname') user_name = user_info.get(frappe.session.get('user'), {}).get('fullname') if action=='CLOSE': if owner == frappe.session.get('user'): arg = { 'contact': assigned_by, 'txt': _("The task {0}, that you assigned to {1}, has been closed.").format(assignment, owner_name) } else: arg = { 'contact': assigned_by, 'txt': _("The task {0}, that you assigned to {1}, has been closed by {2}.").format(assignment, owner_name, user_name) } else: description_html = "<p>{0}</p>".format(description) arg = { 'contact': owner, 'txt': _("A new task, {0}, has been assigned to you by {1}. {2}").format(assignment, user_name, description_html), 'notify': notify } arg["parenttype"] = "Assignment" from frappe.desk.page.messages import messages messages.post(**arg)
def get_purchase_orders_items_overdue_list(self): fields_po = "distinct `tabPurchase Order Item`.parent as po" fields_poi = "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," \ "received_qty, qty - received_qty as missing_qty, rate, amount" sql_po = """select {fields} from `tabPurchase Order Item` left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date and received_qty < qty order by `tabPurchase Order Item`.parent DESC, `tabPurchase Order Item`.schedule_date DESC""".format(fields=fields_po) sql_poi = """select {fields} from `tabPurchase Order Item` left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date and received_qty < qty order by `tabPurchase Order Item`.idx""".format(fields=fields_poi) purchase_order_list = frappe.db.sql(sql_po, as_dict=True) purchase_order_items_overdue_list = frappe.db.sql(sql_poi, as_dict=True) for t in purchase_order_items_overdue_list: t.link = get_url_to_form("Purchase Order", t.parent) t.rate = fmt_money(t.rate, 2, t.currency) t.amount = fmt_money(t.amount, 2, t.currency) return purchase_order_list, purchase_order_items_overdue_list
def background_enqueue_run(report_name, filters=None, user=None): """run reports in background""" if not user: user = frappe.session.user report = get_report_doc(report_name) track_instance = \ frappe.get_doc({ "doctype": "Prepared Report", "report_name": report_name, # This looks like an insanity but, without this it'd be very hard to find Prepared Reports matching given condition # We're ensuring that spacing is consistent. e.g. JS seems to put no spaces after ":", Python on the other hand does. "filters": json.dumps(json.loads(filters)), "ref_report_doctype": report_name, "report_type": report.report_type, "query": report.query, "module": report.module, }) track_instance.insert(ignore_permissions=True) frappe.db.commit() return { "name": track_instance.name, "redirect_url": get_url_to_form("Prepared Report", track_instance.name) }
def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, no_email=True, overwrite=None, update_only = None, ignore_links=False, pre_process=None, via_console=False, from_data_import="No", skip_errors = True, data_import_doc=None, validate_template=False, user=None): """upload data""" # for translations if user: frappe.cache().hdel("lang", user) frappe.set_user_lang(user) if data_import_doc and isinstance(data_import_doc, string_types): data_import_doc = frappe.get_doc("Data Import", data_import_doc) if data_import_doc and from_data_import == "Yes": no_email = data_import_doc.no_email ignore_encoding_errors = data_import_doc.ignore_encoding_errors update_only = data_import_doc.only_update submit_after_import = data_import_doc.submit_after_import overwrite = data_import_doc.overwrite skip_errors = data_import_doc.skip_errors else: # extra input params params = json.loads(frappe.form_dict.get("params") or '{}') if params.get("submit_after_import"): submit_after_import = True if params.get("ignore_encoding_errors"): ignore_encoding_errors = True if not params.get("no_email"): no_email = False if params.get('update_only'): update_only = True if params.get('from_data_import'): from_data_import = params.get('from_data_import') if not params.get('skip_errors'): skip_errors = params.get('skip_errors') frappe.flags.in_import = True frappe.flags.mute_emails = no_email def get_data_keys_definition(): return get_data_keys() def bad_template(): frappe.throw(_("Please do not change the rows above {0}").format(get_data_keys_definition().data_separator)) def check_data_length(): if not data: frappe.throw(_("No data found in the file. Please reattach the new file with data.")) def get_start_row(): for i, row in enumerate(rows): if row and row[0]==get_data_keys_definition().data_separator: return i+1 bad_template() def get_header_row(key): return get_header_row_and_idx(key)[0] def get_header_row_and_idx(key): for i, row in enumerate(header): if row and row[0]==key: return row, i return [], -1 def filter_empty_columns(columns): empty_cols = list(filter(lambda x: x in ("", None), columns)) if empty_cols: if columns[-1*len(empty_cols):] == empty_cols: # filter empty columns if they exist at the end columns = columns[:-1*len(empty_cols)] else: frappe.msgprint(_("Please make sure that there are no empty columns in the file."), raise_exception=1) return columns def make_column_map(): doctype_row, row_idx = get_header_row_and_idx(get_data_keys_definition().doctype) if row_idx == -1: # old style return dt = None for i, d in enumerate(doctype_row[1:]): if d not in ("~", "-"): if d and doctype_row[i] in (None, '' ,'~', '-', _("DocType") + ":"): dt, parentfield = d, None # xls format truncates the row, so it may not have more columns if len(doctype_row) > i+2: parentfield = doctype_row[i+2] doctypes.append((dt, parentfield)) column_idx_to_fieldname[(dt, parentfield)] = {} column_idx_to_fieldtype[(dt, parentfield)] = {} if dt: column_idx_to_fieldname[(dt, parentfield)][i+1] = rows[row_idx + 2][i+1] column_idx_to_fieldtype[(dt, parentfield)][i+1] = rows[row_idx + 4][i+1] def get_doc(start_idx): if doctypes: doc = {} attachments = [] last_error_row_idx = None for idx in range(start_idx, len(rows)): last_error_row_idx = idx # pylint: disable=W0612 if (not doc) or main_doc_empty(rows[idx]): for dt, parentfield in doctypes: d = {} for column_idx in column_idx_to_fieldname[(dt, parentfield)]: try: fieldname = column_idx_to_fieldname[(dt, parentfield)][column_idx] fieldtype = column_idx_to_fieldtype[(dt, parentfield)][column_idx] if not fieldname or not rows[idx][column_idx]: continue d[fieldname] = rows[idx][column_idx] if fieldtype in ("Int", "Check"): d[fieldname] = cint(d[fieldname]) elif fieldtype in ("Float", "Currency", "Percent"): d[fieldname] = flt(d[fieldname]) elif fieldtype == "Date": if d[fieldname] and isinstance(d[fieldname], string_types): d[fieldname] = getdate(parse_date(d[fieldname])) elif fieldtype == "Datetime": if d[fieldname]: if " " in d[fieldname]: _date, _time = d[fieldname].split() else: _date, _time = d[fieldname], '00:00:00' _date = parse_date(d[fieldname]) d[fieldname] = get_datetime(_date + " " + _time) else: d[fieldname] = None elif fieldtype in ("Image", "Attach Image", "Attach"): # added file to attachments list attachments.append(d[fieldname]) elif fieldtype in ("Link", "Dynamic Link", "Data") and d[fieldname]: # as fields can be saved in the number format(long type) in data import template d[fieldname] = cstr(d[fieldname]) except IndexError: pass # scrub quotes from name and modified if d.get("name") and d["name"].startswith('"'): d["name"] = d["name"][1:-1] if sum([0 if not val else 1 for val in d.values()]): d['doctype'] = dt if dt == doctype: doc.update(d) else: if not overwrite and doc.get("name"): d['parent'] = doc["name"] d['parenttype'] = doctype d['parentfield'] = parentfield doc.setdefault(d['parentfield'], []).append(d) else: break return doc, attachments, last_error_row_idx else: doc = frappe._dict(zip(columns, rows[start_idx][1:])) doc['doctype'] = doctype return doc, [], None # used in testing whether a row is empty or parent row or child row # checked only 3 first columns since first two columns can be blank for example the case of # importing the item variant where item code and item name will be blank. def main_doc_empty(row): if row: for i in range(3,0,-1): if len(row) > i and row[i]: return False return True def validate_naming(doc): autoname = frappe.get_meta(doctype).autoname if autoname: if autoname[0:5] == 'field': autoname = autoname[6:] elif autoname == 'naming_series:': autoname = 'naming_series' else: return True if (autoname not in doc) or (not doc[autoname]): from frappe.model.base_document import get_controller if not hasattr(get_controller(doctype), "autoname"): frappe.throw(_("{0} is a mandatory field".format(autoname))) return True users = frappe.db.sql_list("select name from tabUser") def prepare_for_insert(doc): # don't block data import if user is not set # migrating from another system if not doc.owner in users: doc.owner = frappe.session.user if not doc.modified_by in users: doc.modified_by = frappe.session.user def is_valid_url(url): is_valid = False if url.startswith("/files") or url.startswith("/private/files"): url = get_url(url) try: r = requests.get(url) is_valid = True if r.status_code == 200 else False except Exception: pass return is_valid def attach_file_to_doc(doctype, docname, file_url): # check if attachment is already available # check if the attachement link is relative or not if not file_url: return if not is_valid_url(file_url): return files = frappe.db.sql("""Select name from `tabFile` where attached_to_doctype='{doctype}' and attached_to_name='{docname}' and (file_url='{file_url}' or thumbnail_url='{file_url}')""".format( doctype=doctype, docname=docname, file_url=file_url )) if files: # file is already attached return save_url(file_url, None, doctype, docname, "Home/Attachments", 0) # header filename, file_extension = ['',''] if not rows: from frappe.utils.file_manager import get_file # get_file_doc fname, fcontent = get_file(data_import_doc.import_file) filename, file_extension = os.path.splitext(fname) if file_extension == '.xlsx' and from_data_import == 'Yes': from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file rows = read_xlsx_file_from_attached_file(file_id=data_import_doc.import_file) elif file_extension == '.csv': from frappe.utils.csvutils import read_csv_content rows = read_csv_content(fcontent, ignore_encoding_errors) else: frappe.throw(_("Unsupported File Format")) start_row = get_start_row() header = rows[:start_row] data = rows[start_row:] try: doctype = get_header_row(get_data_keys_definition().main_table)[1] columns = filter_empty_columns(get_header_row(get_data_keys_definition().columns)[1:]) except: frappe.throw(_("Cannot change header content")) doctypes = [] column_idx_to_fieldname = {} column_idx_to_fieldtype = {} if skip_errors: data_rows_with_error = header if submit_after_import and not cint(frappe.db.get_value("DocType", doctype, "is_submittable")): submit_after_import = False parenttype = get_header_row(get_data_keys_definition().parent_table) if len(parenttype) > 1: parenttype = parenttype[1] # check permissions if not frappe.permissions.can_import(parenttype or doctype): frappe.flags.mute_emails = False return {"messages": [_("Not allowed to Import") + ": " + _(doctype)], "error": True} # Throw expception in case of the empty data file check_data_length() make_column_map() total = len(data) if validate_template: if total: data_import_doc.total_rows = total return True if overwrite==None: overwrite = params.get('overwrite') # delete child rows (if parenttype) parentfield = None if parenttype: parentfield = get_parent_field(doctype, parenttype) if overwrite: delete_child_rows(data, doctype) import_log = [] def log(**kwargs): if via_console: print((kwargs.get("title") + kwargs.get("message")).encode('utf-8')) else: import_log.append(kwargs) def as_link(doctype, name): if via_console: return "{0}: {1}".format(doctype, name) else: return getlink(doctype, name) # publish realtime task update def publish_progress(achieved, reload=False): if data_import_doc: frappe.publish_realtime("data_import_progress", {"progress": str(int(100.0*achieved/total)), "data_import": data_import_doc.name, "reload": reload}, user=frappe.session.user) error_flag = rollback_flag = False batch_size = frappe.conf.data_import_batch_size or 1000 for batch_start in range(0, total, batch_size): batch = data[batch_start:batch_start + batch_size] for i, row in enumerate(batch): # bypass empty rows if main_doc_empty(row): continue row_idx = i + start_row doc = None publish_progress(i) try: doc, attachments, last_error_row_idx = get_doc(row_idx) validate_naming(doc) if pre_process: pre_process(doc) original = None if parentfield: parent = frappe.get_doc(parenttype, doc["parent"]) doc = parent.append(parentfield, doc) parent.save() else: if overwrite and doc.get("name") and frappe.db.exists(doctype, doc["name"]): original = frappe.get_doc(doctype, doc["name"]) original_name = original.name original.update(doc) # preserve original name for case sensitivity original.name = original_name original.flags.ignore_links = ignore_links original.save() doc = original else: if not update_only: doc = frappe.get_doc(doc) prepare_for_insert(doc) doc.flags.ignore_links = ignore_links doc.insert() if attachments: # check file url and create a File document for file_url in attachments: attach_file_to_doc(doc.doctype, doc.name, file_url) if submit_after_import: doc.submit() # log errors if parentfield: log(**{"row": doc.idx, "title": 'Inserted row for "%s"' % (as_link(parenttype, doc.parent)), "link": get_url_to_form(parenttype, doc.parent), "message": 'Document successfully saved', "indicator": "green"}) elif submit_after_import: log(**{"row": row_idx + 1, "title":'Submitted row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully submitted", "link": get_url_to_form(doc.doctype, doc.name), "indicator": "blue"}) elif original: log(**{"row": row_idx + 1,"title":'Updated row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully updated", "link": get_url_to_form(doc.doctype, doc.name), "indicator": "green"}) elif not update_only: log(**{"row": row_idx + 1, "title":'Inserted row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully saved", "link": get_url_to_form(doc.doctype, doc.name), "indicator": "green"}) else: log(**{"row": row_idx + 1, "title":'Ignored row for %s' % (row[1]), "link": None, "message": "Document updation ignored", "indicator": "orange"}) except Exception as e: error_flag = True # build error message if frappe.local.message_log: err_msg = "\n".join(['<p class="border-bottom small">{}</p>'.format(json.loads(msg).get('message')) for msg in frappe.local.message_log]) else: err_msg = '<p class="border-bottom small">{}</p>'.format(cstr(e)) error_trace = frappe.get_traceback() if error_trace: error_log_doc = frappe.log_error(error_trace) error_link = get_url_to_form("Error Log", error_log_doc.name) else: error_link = None log(**{ "row": row_idx + 1, "title": 'Error for row %s' % (len(row)>1 and frappe.safe_decode(row[1]) or ""), "message": err_msg, "indicator": "red", "link":error_link }) # data with error to create a new file # include the errored data in the last row as last_error_row_idx will not be updated for the last row if skip_errors: if last_error_row_idx == len(rows)-1: last_error_row_idx = len(rows) data_rows_with_error += rows[row_idx:last_error_row_idx] else: rollback_flag = True finally: frappe.local.message_log = [] start_row += batch_size if rollback_flag: frappe.db.rollback() else: frappe.db.commit() frappe.flags.mute_emails = False frappe.flags.in_import = False log_message = {"messages": import_log, "error": error_flag} if data_import_doc: data_import_doc.log_details = json.dumps(log_message) import_status = None if error_flag and data_import_doc.skip_errors and len(data) != len(data_rows_with_error): import_status = "Partially Successful" # write the file with the faulty row from frappe.utils.file_manager import save_file file_name = 'error_' + filename + file_extension if file_extension == '.xlsx': from frappe.utils.xlsxutils import make_xlsx xlsx_file = make_xlsx(data_rows_with_error, "Data Import Template") file_data = xlsx_file.getvalue() else: from frappe.utils.csvutils import to_csv file_data = to_csv(data_rows_with_error) error_data_file = save_file(file_name, file_data, "Data Import", data_import_doc.name, "Home/Attachments") data_import_doc.error_file = error_data_file.file_url elif error_flag: import_status = "Failed" else: import_status = "Successful" data_import_doc.import_status = import_status data_import_doc.save() if data_import_doc.import_status in ["Successful", "Partially Successful"]: data_import_doc.submit() publish_progress(100, True) else: publish_progress(0, True) frappe.db.commit() else: return log_message