def add_comment(doctype, name): doc = frappe.get_doc(doctype, name) if doctype == "Comment": link = get_link_to_form( doc.comment_doctype, doc.comment_docname, "{0} {1}".format(_(doc.comment_doctype), doc.comment_docname) ) doc.add_comment( "Like", _("Comment: {0} in {1}").format("<b>" + doc.comment + "</b>", link), reference_doctype=doc.comment_doctype, reference_name=doc.comment_docname, ) elif doctype == "Communication": link = get_link_to_form( doc.reference_doctype, doc.reference_name, "{0} {1}".format(_(doc.reference_doctype), doc.reference_name) ) doc.add_comment( "Like", _("Communication: {0} in {1}").format("<b>" + doc.subject + "</b>", link), reference_doctype=doc.reference_doctype, reference_name=doc.reference_name, ) else: doc.add_comment("Like", _("Liked"))
def _get_message(url=False): name = self.name employee_name = cstr(employee.employee_name) if url: name = get_link_to_form(self.doctype, self.name) employee_name = get_link_to_form("Employee", self.employee, label=employee_name) return (_("New Leave Application") + ": %s - " + _("Employee") + ": %s") % (name, employee_name)
def _get_message(url=False): name = self.name employee_name = cstr(employee.employee_name) if url: name = get_link_to_form(self.doctype, self.name) employee_name = get_link_to_form("Employee", self.employee, label=employee_name) message = (_("Leave Application") + ": %s") % (name)+"<br>" message += (_("Employee") + ": %s") % (employee_name)+"<br>" message += (_("Leave Type") + ": %s") % (self.leave_type)+"<br>" message += (_("From Date") + ": %s") % (self.from_date)+"<br>" message += (_("To Date") + ": %s") % (self.to_date) return message
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_link_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 notify_mentions(doc): if doc.communication_type != "Comment": return if doc.reference_doctype and doc.reference_name and doc.content and doc.comment_type=="Comment": mentions = extract_mentions(doc.content) if not mentions: return sender_fullname = get_fullname(frappe.session.user) parent_doc_label = "{0} {1}".format(_(doc.reference_doctype), doc.reference_name) subject = _("{0} mentioned you in a comment in {1}").format(sender_fullname, parent_doc_label) message = frappe.get_template("templates/emails/mentioned_in_comment.html").render({ "sender_fullname": sender_fullname, "comment": doc, "link": get_link_to_form(doc.reference_doctype, doc.reference_name, label=parent_doc_label) }) recipients = [frappe.db.get_value("User", {"enabled": 1, "username": username, "user_type": "System User"}) for username in mentions] frappe.sendmail( recipients=recipients, sender=frappe.session.user, subject=subject, message=message, bulk=True )
def _get_message(url=False): if url: name = get_link_to_form(self.doctype, self.name) else: name = self.name return (_("Leave Application") + ": %s - %s") % (name, _(status))
def daily(): users = frappe.db.sql("""select email, name from tabUser where name in (select parent from tabUserRole where role='Accounts Manager')""", as_dict=1) today = nowdate() end = add_days(today, 3) sales_invoices = frappe.db.sql("""select name from `tabSales Invoice` where (date(due_date) between date(%(start)s) and date(%(end)s)) and outstanding_amount > 0 and docstatus=1""", { "start": today, "end": end, }, as_dict=1) if sales_invoices: subject = "Outstanding sales invoices due %s" % today message = "<p>Please review and follow up with the customers on the outstanding sales invoices below:</p><ul>" from frappe.utils import get_link_to_form for si in sales_invoices: message += "<li>" + get_link_to_form("Sales Invoice", si.name, si.name) + "</li>" message += "</ul><p><b>Note:</b> The list above contains the invoices that are either overdue or have its due date within the next 3 business days</p>" frappe.sendmail(recipients=[u.email for u in users], subject=subject, message=message, reply_to=EMAIL_SENDER, bulk=True) formatted_si = ", ".join(si.name for si in sales_invoices) for u in [u.name for u in users]: todo_doc = frappe.get_doc({ "doctype": "ToDo", "owner": u, "description": subject + ": " + formatted_si, "priority": "Medium", "status": "Open", "role": "Accounts Manager", "date": nowdate(), "assigned_by": frappe.session.user, }) todo_doc.insert(ignore_permissions=True)
def _get_message(url=False): if url: name = get_link_to_form(self.doctype, self.name) else: name = self.name message = "Leave Application: {name}".format(name=name)+"<br>" message += "Leave Type: {leave_type}".format(leave_type=self.leave_type)+"<br>" message += "From Date: {from_date}".format(from_date=self.from_date)+"<br>" message += "To Date: {to_date}".format(to_date=self.to_date)+"<br>" message += "Status: {status}".format(status=_(status)) return message
def notify_assignment(shared_by, doc_type, doc_name, description=None, notify=0): if not (shared_by and doc_type and doc_name): return from frappe.utils import get_link_to_form document = get_link_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name)) arg = { 'contact': shared_by, 'txt': _("A new document {0} has been shared by with you {1}.").format(document, shared_by), 'notify': notify }
def add_comment(doctype, name): doc = frappe.get_doc(doctype, name) if doctype=="Communication" and doc.reference_doctype and doc.reference_name: link = get_link_to_form(doc.reference_doctype, doc.reference_name, "{0} {1}".format(_(doc.reference_doctype), doc.reference_name)) doc.add_comment("Like", _("{0}: {1} in {2}").format(_(doc.communication_type), "<b>" + doc.subject + "</b>", link), link_doctype=doc.reference_doctype, link_name=doc.reference_name) else: doc.add_comment("Like", _("Liked"))
def get_html_table(self, columns=None, data=None): date_time = global_date_format(now()) + ' ' + format_time(now()) report_doctype = frappe.db.get_value('Report', self.report, 'ref_doctype') return frappe.render_template('frappe/templates/emails/auto_email_report.html', { 'title': self.name, 'description': self.description, 'date_time': date_time, 'columns': columns, 'data': data, 'report_url': get_url_to_report(self.report, self.report_type, report_doctype), 'report_name': self.report, 'edit_report_settings': get_link_to_form('Auto Email Report', self.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_link_to_form assignment = get_link_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.chat import chat chat.post(**arg)
def validate_duplicate(self): data = frappe.db.sql(""" select name from `tabStudent Leave Application` where ((%(from_date)s > from_date and %(from_date)s < to_date) or (%(to_date)s > from_date and %(to_date)s < to_date) or (%(from_date)s <= from_date and %(to_date)s >= to_date)) and name != %(name)s and student = %(student)s and docstatus < 2 """, { 'from_date': self.from_date, 'to_date': self.to_date, 'student': self.student, 'name': self.name }, as_dict=1) if data: link = get_link_to_form("Student Leave Application", data[0].name) frappe.throw(_("Leave application {0} already exists against the student {1}") .format(link, self.student))
def notify_mentions(doc): if doc.communication_type != "Comment": return if doc.reference_doctype and doc.reference_name and doc.content and doc.comment_type=="Comment": mentions = extract_mentions(doc.content) if not mentions: return sender_fullname = get_fullname(frappe.session.user) title_field = frappe.get_meta(doc.reference_doctype).get_title_field() title = doc.reference_name if title_field == "name" else \ frappe.db.get_value(doc.reference_doctype, doc.reference_name, title_field) if title != doc.reference_name: parent_doc_label = "{0}: {1} (#{2})".format(_(doc.reference_doctype), title, doc.reference_name) else: parent_doc_label = "{0}: {1}".format(_(doc.reference_doctype), doc.reference_name) subject = _("{0} mentioned you in a comment").format(sender_fullname) recipients = [frappe.db.get_value("User", {"enabled": 1, "name": name, "user_type": "System User"}, "email") for name in mentions] link = get_link_to_form(doc.reference_doctype, doc.reference_name, label=parent_doc_label) frappe.sendmail( recipients=recipients, sender=frappe.session.user, subject=subject, template="mentioned_in_comment", args={ "body_content": _("{0} mentioned you in a comment in {1}").format(sender_fullname, link), "comment": doc, "link": link }, header=[_('New Mention'), 'orange'] )
def validate_dates(self): if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"): if self.from_date and getdate(self.from_date) < getdate(): allowed_role = frappe.db.get_single_value( "HR Settings", "role_allowed_to_create_backdated_leave_application") user = frappe.get_doc("User", frappe.session.user) user_roles = [d.role for d in user.roles] if not allowed_role: frappe.throw( _("Backdated Leave Application is restricted. Please set the {} in {}" ). format( frappe.bold( "Role Allowed to Create Backdated Leave Application" ), get_link_to_form("HR Settings", "HR Settings"))) if (allowed_role and allowed_role not in user_roles): frappe.throw( _("Only users with the {0} role can create backdated leave applications" ).format(allowed_role)) if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): frappe.throw(_("To date cannot be before from date")) if self.half_day and self.half_day_date \ and (getdate(self.half_day_date) < getdate(self.from_date) or getdate(self.half_day_date) > getdate(self.to_date)): frappe.throw( _("Half Day Date should be between From Date and To Date")) if not is_lwp(self.leave_type): self.validate_dates_across_allocation() self.validate_back_dated_application()
def notify_mentions(self): if self.comment_doctype and self.comment_docname and self.comment and self.comment_type == "Comment": mentions = extract_mentions(self.comment) if not mentions: return sender_fullname = get_fullname(frappe.session.user) parent_doc_label = "{0} {1}".format(_(self.comment_doctype), self.comment_docname) subject = _("{0} mentioned you in a comment in {1}").format( sender_fullname, parent_doc_label) message = frappe.get_template( "templates/emails/mentioned_in_comment.html").render({ "sender_fullname": sender_fullname, "comment": self, "link": get_link_to_form(self.comment_doctype, self.comment_docname, label=parent_doc_label) }) recipients = [ frappe.db.get_value("User", { "enabled": 1, "username": username, "user_type": "System User" }) for username in mentions ] frappe.sendmail(recipients=recipients, sender=frappe.session.user, subject=subject, message=message, bulk=True)
def validate_doc(self): if (self.enabled and self.document_type == "Issue" and not frappe.db.get_single_value( "Support Settings", "track_service_level_agreement")): frappe.throw( _("{0} is not enabled in {1}").format( frappe.bold("Track Service Level Agreement"), get_link_to_form("Support Settings", "Support Settings"), )) if self.default_service_level_agreement and frappe.db.exists( "Service Level Agreement", { "document_type": self.document_type, "default_service_level_agreement": "1", "name": ["!=", self.name], }, ): frappe.throw( _("Default Service Level Agreement for {0} already exists."). format(self.document_type)) if self.start_date and self.end_date: self.validate_from_to_dates(self.start_date, self.end_date) if (self.entity_type and self.entity and frappe.db.exists( "Service Level Agreement", { "entity_type": self.entity_type, "entity": self.entity, "name": ["!=", self.name] }, )): frappe.throw( _("Service Level Agreement for {0} {1} already exists." ).format(frappe.bold(self.entity_type), frappe.bold(self.entity)))
def get_product_discount_rule(pricing_rule, item_details, doc=None): free_item = (pricing_rule.free_item if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code) if not free_item: frappe.throw( _("Free item not set in the pricing rule {0}").format( get_link_to_form("Pricing Rule", pricing_rule.name))) item_details.free_item_data = { 'item_code': free_item, 'qty': pricing_rule.free_qty or 1, 'rate': pricing_rule.free_item_rate or 0, 'price_list_rate': pricing_rule.free_item_rate or 0, 'is_free_item': 1 } item_data = frappe.get_cached_value( 'Item', free_item, ['item_name', 'description', 'stock_uom'], as_dict=1) item_details.free_item_data.update(item_data) item_details.free_item_data[ 'uom'] = pricing_rule.free_item_uom or item_data.stock_uom item_details.free_item_data['conversion_factor'] = get_conversion_factor( free_item, item_details.free_item_data['uom']).get("conversion_factor", 1) if item_details.get("parenttype") == 'Purchase Order': item_details.free_item_data[ 'schedule_date'] = doc.schedule_date if doc else today() if item_details.get("parenttype") == 'Sales Order': item_details.free_item_data[ 'delivery_date'] = doc.delivery_date if doc else today()
def validate_doc(self): if not frappe.db.get_single_value( "Support Settings", "track_service_level_agreement") and self.enable: frappe.throw( _("{0} is not enabled in {1}").format( frappe.bold("Track Service Level Agreement"), get_link_to_form("Support Settings", "Support Settings"))) if self.default_service_level_agreement: if frappe.db.exists("Service Level Agreement", { "default_service_level_agreement": "1", "name": ["!=", self.name] }): frappe.throw( _("A Default Service Level Agreement already exists.")) else: if self.start_date and self.end_date: if getdate(self.start_date) >= getdate(self.end_date): frappe.throw( _("Start Date of Agreement can't be greater than or equal to End Date." )) if getdate(self.end_date) < getdate(frappe.utils.getdate()): frappe.throw( _("End Date of Agreement can't be less than today.")) if self.entity_type and self.entity: if frappe.db.exists( "Service Level Agreement", { "entity_type": self.entity_type, "entity": self.entity, "name": ["!=", self.name] }): frappe.throw( _("Service Level Agreement with Entity Type {0} and Entity {1} already exists." ).format(self.entity_type, self.entity))
def send_exit_questionnaire(interviews): interviews = get_interviews(interviews) validate_questionnaire_settings() email_success = [] email_failure = [] for exit_interview in interviews: interview = frappe.get_doc("Exit Interview", exit_interview.get("name")) if interview.get("questionnaire_email_sent"): continue employee = frappe.get_doc("Employee", interview.employee) email = get_employee_email(employee) context = interview.as_dict() context.update(employee.as_dict()) template_name = frappe.db.get_single_value( "HR Settings", "exit_questionnaire_notification_template") template = frappe.get_doc("Email Template", template_name) if email: frappe.sendmail( recipients=email, subject=template.subject, message=frappe.render_template(template.response, context), reference_doctype=interview.doctype, reference_name=interview.name, ) interview.db_set("questionnaire_email_sent", True) interview.notify_update() email_success.append(email) else: email_failure.append(get_link_to_form("Employee", employee.name)) show_email_summary(email_success, email_failure)
def validate_duplication(self): """Check if the Attendance Record is Unique""" attendance_record = None if self.course_schedule: attendance_record = frappe.db.exists('Student Attendance', { 'student': self.student, 'course_schedule': self.course_schedule, 'docstatus': ('!=', 2), 'name': ('!=', self.name) }) else: attendance_record = frappe.db.exists('Student Attendance', { 'student': self.student, 'student_group': self.student_group, 'date': self.date, 'docstatus': ('!=', 2), 'name': ('!=', self.name), 'course_schedule': '' }) if attendance_record: record = get_link_to_form('Student Attendance', attendance_record) frappe.throw(_('Student Attendance record {0} already exists against the Student {1}') .format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
def on_trash(self): linked_doctypes = [ "Delivery Note", "Sales Invoice", "POS Invoice", "Purchase Receipt", "Purchase Invoice", "Stock Entry", "Stock Reconciliation", "Sales Order", "Purchase Order", "Material Request", ] invoice_links = [] for doctype in linked_doctypes: item_doctype = doctype + " Item" if doctype == "Stock Entry": item_doctype = doctype + " Detail" invoices = frappe.db.get_all(item_doctype, { "item_code": self.new_item_code, "docstatus": 1 }, ["parent"]) for invoice in invoices: invoice_links.append( get_link_to_form(doctype, invoice["parent"])) if len(invoice_links): frappe.throw( "This Product Bundle is linked with {0}. You will have to cancel these documents in order to delete this Product Bundle" .format(", ".join(invoice_links)), title=_("Not Allowed"), )
def validate_payment_methods(self): if not self.payments: frappe.throw( _("Payment methods are mandatory. Please add at least one payment method." )) default_mode = [d.default for d in self.payments if d.default] if not default_mode: frappe.throw(_("Please select a default mode of payment")) if len(default_mode) > 1: frappe.throw( _("You can only select one mode of payment as default")) invalid_modes = [] for d in self.payments: account = frappe.db.get_value("Mode of Payment Account", { "parent": d.mode_of_payment, "company": self.company }, "default_account") if not account: invalid_modes.append( get_link_to_form("Mode of Payment", d.mode_of_payment)) if invalid_modes: if invalid_modes == 1: msg = _( "Please set default Cash or Bank account in Mode of Payment {}" ) else: msg = _( "Please set default Cash or Bank account in Mode of Payments {}" ) frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
def validate_for_duplicate_items(self): check_list, chk_dupl_itm = [], [] if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): return if self.doctype == "Sales Invoice" and self.is_consolidated: return if self.doctype == "POS Invoice": return for d in self.get('items'): if self.doctype == "Sales Invoice": stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or ''] non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note] elif self.doctype == "Delivery Note": stock_items = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] non_stock_items = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] elif self.doctype in ["Sales Order", "Quotation"]: stock_items = [d.item_code, d.description, d.warehouse, ''] non_stock_items = [d.item_code, d.description] if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code)) duplicate_items_msg += "<br><br>" duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format( frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"), get_link_to_form("Selling Settings", "Selling Settings") ) if stock_items in check_list: frappe.throw(duplicate_items_msg) else: check_list.append(stock_items) else: if non_stock_items in chk_dupl_itm: frappe.throw(duplicate_items_msg) else: chk_dupl_itm.append(non_stock_items)
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_link_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 create_separate_ledger_entries(self, alloc_on_from_date, alloc_on_to_date, submit, lwp): """Creates separate ledger entries for application period falling into separate allocations""" # for creating separate ledger entries existing allocation periods should be consecutive if ( submit and alloc_on_from_date and alloc_on_to_date and add_days(alloc_on_from_date.to_date, 1) != alloc_on_to_date.from_date ): frappe.throw( _( "Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}." ).format( get_link_to_form("Leave Allocation", alloc_on_from_date.name), get_link_to_form("Leave Allocation", alloc_on_to_date), ) ) raise_exception = False if frappe.flags.in_patch else True if alloc_on_from_date: first_alloc_end = alloc_on_from_date.to_date second_alloc_start = add_days(alloc_on_from_date.to_date, 1) else: first_alloc_end = add_days(alloc_on_to_date.from_date, -1) second_alloc_start = alloc_on_to_date.from_date leaves_in_first_alloc = get_number_of_leave_days( self.employee, self.leave_type, self.from_date, first_alloc_end, self.half_day, self.half_day_date, ) leaves_in_second_alloc = get_number_of_leave_days( self.employee, self.leave_type, second_alloc_start, self.to_date, self.half_day, self.half_day_date, ) args = dict( is_lwp=lwp, holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or "", ) if leaves_in_first_alloc: args.update( dict(from_date=self.from_date, to_date=first_alloc_end, leaves=leaves_in_first_alloc * -1) ) create_leave_ledger_entry(self, args, submit) if leaves_in_second_alloc: args.update( dict(from_date=second_alloc_start, to_date=self.to_date, leaves=leaves_in_second_alloc * -1) ) create_leave_ledger_entry(self, args, submit)
def validate_active_employee(employee): if frappe.db.get_value("Employee", employee, "status") == "Inactive": frappe.throw( _("Transactions cannot be created for an Inactive Employee {0}." ).format(get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)
def validate_disabled_warehouse(warehouse): if frappe.db.get_value("Warehouse", warehouse, "disabled"): frappe.throw( _("Disabled Warehouse {0} cannot be used for this transaction."). format(get_link_to_form('Warehouse', warehouse)))
def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, row_id): id_link = get_link_to_form("Invoice Discounting", inv_disc) if id_status != expected_status: frappe.throw(_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(d.idx, expected_status, id_link))
def send_mail(deferred_process): title = _("Error while processing deferred accounting for {0}").format(deferred_process) link = get_link_to_form('Process Deferred Accounting', deferred_process) content = _("Deferred accounting failed for some invoices:") + "\n" content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link) sendmail_to_system_managers(title, content)
def __validate_batch_no(self, row, key): if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'): link = get_link_to_form('Purchase Order', row.purchase_order) msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the Purchase Order {link}' frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
def validate_duplicate(self): year = frappe.db.sql("""select name from `tabAcademic Year` where school=%s and academic_year_name=%s and docstatus<2 and name!=%s""", (self.school, self.academic_year_name, self.name)) if year: frappe.throw(_("An academic year with this name {0} and this school {1} already exist.").format(self.academic_year_name, get_link_to_form("School", self.school)), title=_("Duplicate Entry"))
def check_if_child_exists(name): child_tasks = frappe.get_all("Task", filters={"parent_task": name}) child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks] return child_tasks
def set_po_item_rate(doc): if doc.items: for item in doc.items: if item.purchase_order_item: rate = frappe.db.get_value('Purchase Order Item', item.purchase_order_item, 'rate') if item.maintain_fix_rate == 1 and rate != item.rate: frappe.throw('Not allowed to change the rate for <b>Row {0}</b> as <b>Maintain Fix Rate</b> is checked on the purchase order {1}'.format(item.idx, get_link_to_form('Purchase Order', item.purchase_order)))
def check_if_child_exists(name): child_tasks = frappe.get_all("Project Activities", filters={"parent_project_activities": name}) child_tasks = [get_link_to_form("Project Activities", project_activities.name) for project_activities in child_tasks] return child_tasks
def calculate_annual_eligible_hra_exemption(doc): basic_component, hra_component = frappe.db.get_value( "Company", doc.company, ["basic_component", "hra_component"]) if not (basic_component and hra_component): frappe.throw( _("Please set Basic and HRA component in Company {0}").format( get_link_to_form("Company", doc.company))) annual_exemption = monthly_exemption = hra_amount = basic_amount = 0 if hra_component and basic_component: assignments = get_salary_assignments(doc.employee, doc.payroll_period) if not assignments and doc.docstatus == 1: frappe.throw( _("Salary Structure must be submitted before submission of {0}" ).format(doc.doctype)) assignment_dates = [assignment.from_date for assignment in assignments] for idx, assignment in enumerate(assignments): if has_hra_component(assignment.salary_structure, hra_component): basic_salary_amt, hra_salary_amt = get_component_amt_from_salary_slip( doc.employee, assignment.salary_structure, basic_component, hra_component, assignment.from_date, ) to_date = get_end_date_for_assignment(assignment_dates, idx, doc.payroll_period) frequency = frappe.get_value("Salary Structure", assignment.salary_structure, "payroll_frequency") basic_amount += get_component_pay(frequency, basic_salary_amt, assignment.from_date, to_date) hra_amount += get_component_pay(frequency, hra_salary_amt, assignment.from_date, to_date) if hra_amount: if doc.monthly_house_rent: annual_exemption = calculate_hra_exemption( assignment.salary_structure, basic_amount, hra_amount, doc.monthly_house_rent, doc.rented_in_metro_city, ) if annual_exemption > 0: monthly_exemption = annual_exemption / 12 else: annual_exemption = 0 return frappe._dict({ "hra_amount": hra_amount, "annual_exemption": annual_exemption, "monthly_exemption": monthly_exemption, })
def validate_job_card(self): if not self.time_logs: frappe.throw( _("Time logs are required for {0} {1}").format( frappe.bold("Job Card"), get_link_to_form("Job Card", self.name)))
def throw_overlap_error(self, d): form_link = get_link_to_form("Leave Application", d.name) msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format( self.employee, d["leave_type"], formatdate(d["from_date"]), formatdate(d["to_date"]), form_link ) frappe.throw(msg, OverlapError)
def create_calendar_events(self): if self.at_start: start_at = frappe.get_doc("School Event", self.at_start) if getdate(start_at.starts_on) != getdate(self.term_start_date): start_at.db_set("starts_on", self.term_start_date) start_at.db_set("ends_on", self.term_start_date) frappe.msgprint(_("Date for the start of the term {0} has been updated on the School Event Calendar {1}").format(self.term_start_date, get_link_to_form("School Event", start_at.name))) if self.at_end: end_at = frappe.get_doc("School Event", self.at_end) if getdate(end_at.ends_on) != getdate(self.term_end_date): end_at.db_set("starts_on", self.term_end_date) end_at.db_set("ends_on", self.term_end_date) frappe.msgprint(_("Date for the end of the term {0} has been updated on the School Event Calendar {1}").format(self.term_end_date, get_link_to_form("School Event", end_at.name))) if not self.at_start: start_term = frappe.get_doc({ "doctype": "School Event", "owner": frappe.session.user, "subject": "Start of the " + cstr(self.name) + " Academic Term", "starts_on": getdate(self.term_start_date), "ends_on": getdate(self.term_start_date), "school": self.school, "event_category": "Other", "event_type": "Public", "all_day": "1", "color": "#7575ff", "reference_type": "Academic Term", "reference_name": self.name }) start_term.insert() self.db_set("at_start", start_term.name) frappe.msgprint(_("Date for the start of the term {0} has been created on the School Event Calendar {1}").format(self.term_start_date, get_link_to_form("School Event", start_term.name))) if not self.at_end: end_term = frappe.get_doc({ "doctype": "School Event", "owner": frappe.session.user, "subject": "End of the " + cstr(self.name) + " Academic Term", "starts_on": getdate(self.term_end_date), "ends_on": getdate(self.term_end_date), "school": self.school, "event_category": "Other", "event_type": "Public", "all_day": "1", "color": "#7575ff", "reference_type": "Academic Term", "reference_name": self.name }) end_term.insert() self.db_set("at_end", end_term.name) frappe.msgprint(_("Date for the end of the term {0} has been created on the School Event Calendar {1}").format(self.term_end_date, get_link_to_form("School Event", end_term.name)))
def validate_serial_no(sle, item_det): serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else [] validate_material_transfer_entry(sle) if item_det.has_serial_no==0: if serial_nos: frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code), SerialNoNotRequiredError) elif not sle.is_cancelled: if serial_nos: if cint(sle.actual_qty) != flt(sle.actual_qty): frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty)) if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)): frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(abs(sle.actual_qty), sle.item_code, len(serial_nos)), SerialNoQtyError) if len(serial_nos) != len(set(serial_nos)): frappe.throw(_("Duplicate Serial No entered for Item {0}").format(sle.item_code), SerialNoDuplicateError) for serial_no in serial_nos: if frappe.db.exists("Serial No", serial_no): sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order", "delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type", "purchase_document_no", "company"], as_dict=1) if sr.item_code!=sle.item_code: if not allow_serial_nos_with_different_item(serial_no, sle): frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no, sle.item_code), SerialNoItemError) if cint(sle.actual_qty) > 0 and has_serial_no_exists(sr, sle): doc_name = frappe.bold(get_link_to_form(sr.purchase_document_type, sr.purchase_document_no)) frappe.throw(_("Serial No {0} has already been received in the {1} #{2}") .format(frappe.bold(serial_no), sr.purchase_document_type, doc_name), SerialNoDuplicateError) if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation'] and sle.voucher_type == sr.delivery_document_type): return_against = frappe.db.get_value(sle.voucher_type, sle.voucher_no, 'return_against') if return_against and return_against != sr.delivery_document_no: frappe.throw(_("Serial no {0} has been already returned").format(sr.name)) if cint(sle.actual_qty) < 0: if sr.warehouse!=sle.warehouse: frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no, sle.warehouse), SerialNoWarehouseError) if sle.voucher_type in ("Delivery Note", "Sales Invoice"): if sr.batch_no and sr.batch_no != sle.batch_no: frappe.throw(_("Serial No {0} does not belong to Batch {1}").format(serial_no, sle.batch_no), SerialNoBatchError) if not sle.is_cancelled and not sr.warehouse: frappe.throw(_("Serial No {0} does not belong to any Warehouse") .format(serial_no), SerialNoWarehouseError) # if Sales Order reference in Serial No validate the Delivery Note or Invoice is against the same if sr.sales_order: if sle.voucher_type == "Sales Invoice": if not frappe.db.exists("Sales Invoice Item", {"parent": sle.voucher_no, "item_code": sle.item_code, "sales_order": sr.sales_order}): frappe.throw( _("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}") .format(sr.name, sle.item_code, sr.sales_order) ) elif sle.voucher_type == "Delivery Note": if not frappe.db.exists("Delivery Note Item", {"parent": sle.voucher_no, "item_code": sle.item_code, "against_sales_order": sr.sales_order}): invoice = frappe.db.get_value("Delivery Note Item", {"parent": sle.voucher_no, "item_code": sle.item_code}, "against_sales_invoice") if not invoice or frappe.db.exists("Sales Invoice Item", {"parent": invoice, "item_code": sle.item_code, "sales_order": sr.sales_order}): frappe.throw( _("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}") .format(sr.name, sle.item_code, sr.sales_order) ) # if Sales Order reference in Delivery Note or Invoice validate SO reservations for item if sle.voucher_type == "Sales Invoice": sales_order = frappe.db.get_value("Sales Invoice Item", {"parent": sle.voucher_no, "item_code": sle.item_code}, "sales_order") if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code): validate_so_serial_no(sr, sales_order) elif sle.voucher_type == "Delivery Note": sales_order = frappe.get_value("Delivery Note Item", {"parent": sle.voucher_no, "item_code": sle.item_code}, "against_sales_order") if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code): validate_so_serial_no(sr, sales_order) else: sales_invoice = frappe.get_value("Delivery Note Item", {"parent": sle.voucher_no, "item_code": sle.item_code}, "against_sales_invoice") if sales_invoice: sales_order = frappe.db.get_value("Sales Invoice Item", { "parent": sales_invoice, "item_code": sle.item_code}, "sales_order") if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code): validate_so_serial_no(sr, sales_order) elif cint(sle.actual_qty) < 0: # transfer out frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError) elif cint(sle.actual_qty) < 0 or not item_det.serial_no_series: frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code), SerialNoRequiredError) elif serial_nos: for serial_no in serial_nos: sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1) if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse: frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}") .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
def check_if_child_exists(name): child_tasks = frappe.get_all("Task", filters={"parent_task": name}) child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks] return child_tasks
def validate_operation_id(self): if (self.get("operation_id") and self.get("operation_row_number") and self.operation and self.work_order and frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name") != self.operation_id): work_order = frappe.bold(get_link_to_form("Work Order", self.work_order)) frappe.throw(_("Operation {0} does not belong to the work order {1}") .format(frappe.bold(self.operation), work_order), OperationMismatchError)
def workflow_manager(self): from frappe.model.workflow import get_workflow_name if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name): self.previous_doc = frappe.db.get_value(self.doctype, self.name, "*", as_dict=True) else: self.previous_doc = None self._current_action = self._action if get_workflow_name(self.doctype) is not None: if not self.is_new(): # frappe.msgprint("1") transactions = frappe.db.sql(""" SELECT state,next_state, thecondition ,idx ,`action` , allowed FROM `tabWorkflow Transition` WHERE `parent` = '{workflow_name}' and `action` = 'Approve' order by `idx` asc """.format(workflow_name = get_workflow_name(self.doctype))) frappe.clear_cache(doctype=self.doctype) transactions = list(transactions) print "****************************\n" print "History" print self.get('workflow_history') print "****************************\n" print "****************************\n" print "Transactions" print transactions print "****************************\n" same_states = [] transactions_index = 0 for i in range(0,len(transactions)): if self.workflow_state == transactions[i][0]: same_states.append(i) transactions_index +=1 print "****************************\n" print "Same States" print same_states , self.workflow_state print "****************************\n" if self._action == "submit" or self._action == "update_after_submit": current_transaction = transactions[len(transactions)-1] elif self._action == "cancel": child = self.append('workflow_history', {}) child.user = frappe.session.user child.action = self._action if self.previous_doc and 'workflow_state' in self.previous_doc: child.previous_state = self.previous_doc['workflow_state'] child.new_state = self.workflow_state return elif len(same_states) == 0: #Check if last status frappe.throw(_("Its not Allowed")) else: # frappe.msgprint("2") current_transaction = None # for i in range(0,len(same_states)): if len(same_states): i = 0 # frappe.msgprint("3") length = len(self.get('workflow_history'))-1 if len(self.get('workflow_history')) > 0 else 0 print self.get('workflow_history') prev_transaction = transactions[ same_states[i] - 1] print length last_elem = self.get('workflow_history')[length] #if length >= 0 else None print last_elem print "****************************\n" print "Last Transaction and last history" print prev_transaction[0] , last_elem.name print "****************************\n" #Action if last_elem is None or last_elem.new_state is None: current_transaction = transactions[0] #Update elif str(prev_transaction[0]) == str(last_elem.new_state): current_transaction = prev_transaction #Update elif last_elem.new_state == self.workflow_state: current_transaction = transactions[same_states[i]] self._current_action = "update" if current_transaction is None: frappe.throw("There's no workflow state, Refresh the page") condition = str(current_transaction[2]).split(":") if len(condition) == 1: check_func = condition[0] if not self.run_method(check_func): frappe.throw(_("You are not permitted.")) if current_transaction[5] not in frappe.get_roles(frappe.session.user): frappe.throw(_("You are not Allowed .")) # print current_transaction[2] if current_transaction[2] is not None and self._current_action != "update": if len(condition) == 2: try: fcall = self.run_method(condition[0]) if fcall: self.run_method("before_submit") self.db_set("workflow_state", condition[1]) self.db_set("docstatus", "1") # submitted self.run_method("on_submit") except AttributeError as e: frappe.throw("Check workflow transaction condition :"+ str(e)) else: pass # frappe.msgprint("before add to history") child = self.append('workflow_history', {}) child.user = frappe.session.user child.action = self._current_action if self.previous_doc and 'workflow_state' in self.previous_doc: child.previous_state = self.previous_doc['workflow_state'] child.new_state = self.workflow_state from frappe.utils import get_link_to_form , get_url , get_fullname #frappe.get_doc("Workflow Transition" , "" , "allowed") owner_employee = frappe.get_doc('Employee',{'name' : self.employee } ) #if self.workflow_state = or owner_user = frappe.db.get_value("User", owner_employee.user_id, "email") frappe.sendmail( recipients=(owner_user), sender= frappe.db.get_value("User", frappe.session.user, "email"), subject = "New Message from " + get_fullname(frappe.session.user), message = frappe.get_template("templates/emails/new_message.html").render({ "from": get_fullname(frappe.session.user), "message": "Hello This is me", "link": get_link_to_form(self.doctype , self.name) }))
def verify_and_initiate_transaction(doc, entered_password=None, otp=None): if isinstance(doc, string_types): doc = frappe._dict(json.loads(doc)) obp_doc_name = doc['name'] retry_count = doc['retry_count'] + 1 frappe.db.set_value(doc['doctype'], doc['name'], 'retry_count', retry_count) if doc['doctype'] == 'Bulk Outward Bank Payment': row = doc['outward_bank_payment_details'][0] data = { 'party_type': row['party_type'], 'party': row['party'], 'amount': row['amount'], 'remarks': doc['remarks'], 'transaction_type': doc['transaction_type'], 'company_bank_account': doc['company_bank_account'], 'reconcile_action': doc['reconcile_action'], 'bobp': doc['name'] } obp_doc_name = frappe.db.exists('Outward Bank Payment', data) if not obp_doc_name: data['doctype'] = 'Outward Bank Payment' obp_doc = frappe.get_doc(data) obp_doc.save(ignore_permissions=True) obp_doc.submit() status = frappe.db.get_value('Outward Bank Payment', obp_doc.name, 'workflow_state') frappe.db.set_value( 'Outward Bank Payment Details', { 'parent': doc['name'], 'party_type': row['party_type'], 'party': row['party'], 'amount': row['amount'] }, 'outward_bank_payment', get_link_to_form('Outward Bank Payment', obp_doc.name)) frappe.db.set_value( 'Outward Bank Payment Details', { 'parent': doc['name'], 'party_type': row['party_type'], 'party': row['party'], 'amount': row['amount'], 'outward_bank_payment': obp_doc.name }, 'status', status) obp_doc_name = obp_doc.name if entered_password and otp: integration_doc_name = frappe.get_value( 'Bank API Integration', {'bank_account': doc['company_bank_account']}, 'name') defined_password = frappe.utils.password.get_decrypted_password( 'Bank API Integration', integration_doc_name, fieldname='transaction_password') if not entered_password == defined_password: frappe.throw(_("Invalid Password")) initiate_transaction_with_otp(obp_doc_name, otp) if entered_password and not otp: integration_doc_name = frappe.get_value( 'Bank API Integration', {'bank_account': doc['company_bank_account']}, 'name') defined_password = frappe.utils.password.get_decrypted_password( 'Bank API Integration', integration_doc_name, fieldname='transaction_password') if not entered_password == defined_password: frappe.throw(_("Invalid Password")) initiate_transaction_without_otp(obp_doc_name) if otp and not entered_password: initiate_transaction_with_otp(obp_doc_name, otp) retry_count = frappe.db.get_value(doc['doctype'], doc['name'], 'retry_count') workflow_state = frappe.db.get_value('Outward Bank Payment', obp_doc_name, 'workflow_state') if workflow_state == 'Approved' and retry_count == 3: frappe.db.set_value(doc.doctype, doc.name, 'workflow_state', 'Verification Failed') if workflow_state in ['Initiated', 'Initiation Pending']: if doc['doctype'] == 'Bulk Outward Bank Payment': bobp = frappe.get_doc('Bulk Outward Bank Payment', doc['name']) bobp.bulk_create_obp_records()
def check_nextdoc_docstatus(self): # Checks Delivery Note submit_dn = frappe.db.sql_list( """ select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1""", self.name) if submit_dn: submit_dn = [ get_link_to_form("Delivery Note", dn) for dn in submit_dn ] frappe.throw( _("Delivery Notes {0} must be cancelled before cancelling this Sales Order" ).format(", ".join(submit_dn))) # Checks Sales Invoice submit_rv = frappe.db.sql_list( """select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""", self.name) if submit_rv: submit_rv = [ get_link_to_form("Sales Invoice", si) for si in submit_rv ] frappe.throw( _("Sales Invoice {0} must be cancelled before cancelling this Sales Order" ).format(", ".join(submit_rv))) #check maintenance schedule submit_ms = frappe.db.sql_list( """ select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.sales_order = %s and t1.docstatus = 1""", self.name) if submit_ms: submit_ms = [ get_link_to_form("Maintenance Schedule", ms) for ms in submit_ms ] frappe.throw( _("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order" ).format(", ".join(submit_ms))) # check maintenance visit submit_mv = frappe.db.sql_list( """ select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""", self.name) if submit_mv: submit_mv = [ get_link_to_form("Maintenance Visit", mv) for mv in submit_mv ] frappe.throw( _("Maintenance Visit {0} must be cancelled before cancelling this Sales Order" ).format(", ".join(submit_mv))) # check work order pro_order = frappe.db.sql_list( """ select name from `tabWork Order` where sales_order = %s and docstatus = 1""", self.name) if pro_order: pro_order = [ get_link_to_form("Work Order", po) for po in pro_order ] frappe.throw( _("Work Order {0} must be cancelled before cancelling this Sales Order" ).format(", ".join(pro_order)))
def update_transaction_status(obp_name=None, bobp_name=None): bulk_update = True if obp_name or bobp_name: bulk_update = False if obp_name: obp_list = [{'name': obp_name}] if bobp_name: obp_list = frappe.db.get_all( 'Outward Bank Payment', { 'workflow_state': [ 'in', ['Initiated', 'Initiation Pending', 'Transaction Pending'] ], 'bobp': ['=', bobp_name] }) if bulk_update: obp_list = frappe.db.get_all( 'Outward Bank Payment', { 'workflow_state': [ 'in', ['Initiated', 'Initiation Pending', 'Transaction Pending'] ] }) failed_obp_list = [] if not obp_list: frappe.throw(_("No transaction found in the initiated state.")) for doc in obp_list: res = None workflow_state = None obp_doc = frappe.get_doc('Outward Bank Payment', doc['name']) prov, config = get_api_provider_class(obp_doc.company_bank_account) unique_id = frappe.db.get_value( 'Bank API Integration', {'bank_account': obp_doc.company_bank_account}, 'unique_id') filters = {"UNIQUEID": obp_doc.name if not unique_id else unique_id} try: res = prov.get_transaction_status(filters) if res['status'] == 'SUCCESS' and 'utr_number' in res: workflow_state = 'Transaction Completed' elif res['status'] in ['FAILURE', 'DUPLICATE']: workflow_state = 'Transaction Failed' elif 'PENDING' in res['status']: workflow_state = 'Transaction Pending' else: workflow_state = 'Transaction Error' except: workflow_state = 'Transaction Error' res = frappe.get_traceback() log_name = log_request(obp_doc.name, 'Update Transaction Status', filters, config, res) frappe.db.set_value('Outward Bank Payment', {'name': obp_doc.name}, 'workflow_state', workflow_state) frappe.db.commit() if workflow_state in [ 'Transaction Pending', 'Transaction Error', 'Transaction Failed' ] and not bulk_update: if not obp_doc.bobp: frappe.throw( _(f'An error occurred while making request. Kindly check request log for more info {get_link_to_form("Bank API Request Log", log_name)}' )) else: failed_obp_list.append( get_link_to_form("Outward Bank Payment", doc['name'])) if failed_obp_list and not bulk_update: failed_obp = ','.join(failed_obp_list) frappe.throw( _(f"Transaction status update failed for the below obp(s) {failed_obp}" )) if bobp_name and not bulk_update: frappe.msgprint(_("Transaction Status Updated"))