def process_workflow_actions(doc, state): workflow = get_workflow_name(doc.get('doctype')) if not workflow: return if state == "on_trash": clear_workflow_actions(doc.get('doctype'), doc.get('name')) return if is_workflow_action_already_created(doc): return clear_old_workflow_actions(doc) update_completed_workflow_actions(doc) clear_doctype_notifications('Workflow Action') next_possible_transitions = get_next_possible_transitions(workflow, get_doc_workflow_state(doc), doc) if not next_possible_transitions: return user_data_map = get_users_next_action_data(next_possible_transitions, doc) if not user_data_map: return create_workflow_actions_for_users(user_data_map.keys(), doc) if send_email_alert(workflow): enqueue(send_workflow_action_email, queue='short', users_data=list(user_data_map.values()), doc=doc)
def process_workflow_actions(doc, state): workflow = get_workflow_name(doc.get('doctype')) if not workflow: return if state == "on_trash": clear_workflow_actions(doc.get('doctype'), doc.get('name')) return if is_workflow_action_already_created(doc): return clear_old_workflow_actions(doc) update_completed_workflow_actions(doc) clear_doctype_notifications('Workflow Action') next_possible_transitions = get_next_possible_transitions(workflow, get_doc_workflow_state(doc)) if not next_possible_transitions: return user_data_map = get_users_next_action_data(next_possible_transitions, doc) if not user_data_map: return create_workflow_actions_for_users(user_data_map.keys(), doc) if send_email_alert(workflow): enqueue(send_workflow_action_email, queue='short', users_data=list(user_data_map.values()), doc=doc)
def recall_purchase_receipt(doc, state): workflow = get_workflow_name(doc.get('doctype')) if not workflow: return else: this_doc_workflow_state = get_doc_workflow_state(doc) if this_doc_workflow_state == "Reversed": quality_inspections = frappe.db.get_all( 'Quality Inspection', filters={'reference_name': doc.get("name")}, fields=['name'], as_list=False) q_documents =[frappe.get_doc("Quality Inspection", x.get("name"))\ for x in quality_inspections if quality_inspections] submitted_qis = [x for x in q_documents if x.get("docstatus") == 1] if len(submitted_qis) > 0: frappe.throw( "It seems that some inspection documents are already submitted.\ To recall specific items please recall them from Quality Inspection doctype" ) return else: list( map(lambda x: x.delete(ignore_permissions=True), q_documents)) doc.db_set('workflow_state', "Draft", notify=True, commit=True, update_modified=True) return
def process_workflow_log(doc, state): if state == "before_save": workflow = get_workflow_name(doc.get('doctype')) if not workflow: this_doc_workflow_state = "Draft" else: if is_workflow_action_already_created(doc): return this_doc_workflow_state = get_doc_workflow_state(doc) if not this_doc_workflow_state: this_doc_workflow_state = "Draft" the_decision = "Actioned To: " + this_doc_workflow_state elif state == "before_submit": the_decision = "Document Approved!" #LET THE USER GIVE A MEMO FOR APPROVING DOCUMENT. comment_on_action(doc, state) elif state == "on_cancel": the_decision = "Document Cancelled/Revoked!" #LET THE USER GIVE A MEMO FOR CANCELLING DOCUMENT. comment_on_action(doc, state) #frappe.msgprint("Logging: " + state) log_actions(doc, the_decision) #================Generation of Quality Inspection======================== #frappe.throw("doctype: " + doc.get('doctype') + ", state: " + state + ", workflow: " + get_doc_workflow_state(doc)) if doc.get( 'doctype' ) == "Purchase Receipt" and state == "before_save" and get_doc_workflow_state( doc) == "Pending Inspection": #function to insert into Quality Inspection #frappe.msgprint("Logging: " + get_doc_workflow_state(doc)) create_quality_inspection(doc)
def update_invoice_state(doc, state): if doc.docstatus == 1: return workflow = get_workflow_name(doc.get('doctype')) if workflow: new_workflow_state = get_doc_workflow_state(doc) old_workflow_state = frappe.db.get_value(doc.get('doctype'), doc.get('name'), 'workflow_state') if old_workflow_state == new_workflow_state: return else: #WORKFLOW STATE CHANGED. SO UPDATE INVOICE STATE AS WELL. invoice_docs = [ frappe.get_doc("Purchase Invoice", x.get("invoice_number")) for x in doc.get("invoices") ] for d in invoice_docs: frappe.db.set_value(d.get("doctype"), d.get("name"), "workflow_state", new_workflow_state) d.notify_update() #invoice_to_update = frappe.get_doc(doc.get("reference_doctype"), doc.get("reference_name")) #invoice_to_update.flags.ignore_permissions = True #invoice_to_update.set("workflow_state", new_workflow_state) #invoice_to_update.save() return
def process_workflow_custom_actions(doc, state): workflow = get_workflow_name(doc.get('doctype')) current_state = doc.status docname = doc.name full_user_name = frappe.db.get_value("User", frappe.session.user, "full_name") #frappe.msgprint("Current state "+str(current_state)) #if current_state== "Cancelled" or current_state =="Terminated" or current_state =="Rejected": #frappe.publish_realtime(event='eval_js', message='alert("{0}")', user=frappe.session.user) # msgprint with server and client side action frappe.msgprint( msg='You ' + current_state + " document " + docname + " please click the appropriate reason. If you need to add a comment please scroll to the bottom of this document and tag specific users", title='Document ' + docname + ' ' + current_state, #raise_exception=FileNotFoundError primary_action={ 'label': _('Alert stakeholders for action'), 'server_action': 'dotted.path.to.method', 'args': { "comment_type": "Comment", "comment_email": full_user_name, "reference_doctype": "Material Request", "reference_name": docname, content: "" } })
def load_workflows(self): # get active workflow workflow_name = get_workflow_name(self.name) if workflow_name and frappe.db.exists("Workflow", workflow_name): workflow = frappe.get_doc("Workflow", workflow_name) frappe.response.docs.append(workflow) for d in workflow.get("workflow_document_states"): frappe.response.docs.append(frappe.get_doc("Workflow State", d.state))
def load_workflows(self): # get active workflow workflow_name = get_workflow_name(self.name) workflow_docs = [] if workflow_name and frappe.db.exists("Workflow", workflow_name): workflow = frappe.get_doc("Workflow", workflow_name) workflow_docs.append(workflow) for d in workflow.get("states"): workflow_docs.append(frappe.get_doc("Workflow State", d.state)) self.set("__workflow_docs", workflow_docs, as_value=True)
def get_email_template(doc): """Returns next_action_email_template for workflow state (if available) based on doc current workflow state """ workflow_name = get_workflow_name(doc.get('doctype')) doc_state = get_doc_workflow_state(doc) template_name = frappe.db.get_value('Workflow Document State', { 'parent': workflow_name, 'state': doc_state }, 'next_action_email_template') if not template_name: return return frappe.get_doc('Email Template', template_name)
def validate_invoices_in_po_v2(doc, state): workflow = get_workflow_name(doc.get('doctype')) if workflow: new_workflow_state = get_doc_workflow_state(doc) old_workflow_state = frappe.db.get_value(doc.get('doctype'), doc.get('name'), 'workflow_state') if old_workflow_state == new_workflow_state: return elif new_workflow_state in ["Pending Payment Voucher", "Credit Note"]: doc.set("to_be_sent_to_pv", True) frappe.msgprint("Document checking successful") return
def set_purchase_request_as_checked(doc): workflow = get_workflow_name(doc.get('doctype')) if workflow: new_workflow_state = get_doc_workflow_state(doc) old_workflow_state = frappe.db.get_value(doc.get('doctype'), doc.get('name'), 'workflow_state') doc.set("checked", False) if old_workflow_state == new_workflow_state: return elif new_workflow_state not in ["Voteholder Checking"]: doc.set("checked", True) frappe.msgprint("Document checking successful") return
def add_workflows(doclist): from frappe.model.workflow import get_workflow_name doctype = doclist[0].name # get active workflow workflow_name = get_workflow_name(doctype) if workflow_name and frappe.db.exists("Workflow", workflow_name): doclist += frappe.get_doclist("Workflow", workflow_name) # add workflow states (for icons and style) for state in map(lambda d: d.state, doclist.get({"doctype": "Workflow Document State"})): doclist += frappe.get_doclist("Workflow State", state)
def execute(): for doctype in ['Expense Claim', 'Leave Application']: active_workflow = get_workflow_name(doctype) if not active_workflow: continue workflow_states = frappe.get_all('Workflow Document State', filters=[['parent', '=', active_workflow]], fields=['*']) for state in workflow_states: if state.update_field: continue status_field = 'approval_status' if doctype=="Expense Claim" else 'status' frappe.set_value('Workflow Document State', state.name, 'update_field', status_field) frappe.set_value('Workflow Document State', state.name, 'update_value', state.state)
def get_email_template(doc): """Returns next_action_email_template for workflow state (if available) based on doc current workflow state """ workflow_name = get_workflow_name(doc.get("doctype")) doc_state = get_doc_workflow_state(doc) template_name = frappe.db.get_value( "Workflow Document State", { "parent": workflow_name, "state": doc_state }, "next_action_email_template", ) if not template_name: return return frappe.get_doc("Email Template", template_name)
def execute(): for doctype in ['Expense Claim', 'Leave Application']: active_workflow = get_workflow_name(doctype) if not active_workflow: continue workflow_states = frappe.get_all( 'Workflow Document State', filters=[['parent', '=', active_workflow]], fields=['*']) for state in workflow_states: if state.update_field: continue status_field = 'approval_status' if doctype == "Expense Claim" else 'status' frappe.set_value('Workflow Document State', state.name, 'update_field', status_field) frappe.set_value('Workflow Document State', state.name, 'update_value', state.state)
def execute(): for doctype in ["Expense Claim", "Leave Application"]: active_workflow = get_workflow_name(doctype) if not active_workflow: continue workflow_states = frappe.get_all( "Workflow Document State", filters=[["parent", "=", active_workflow]], fields=["*"]) for state in workflow_states: if state.update_field: continue status_field = "approval_status" if doctype == "Expense Claim" else "status" frappe.set_value("Workflow Document State", state.name, "update_field", status_field) frappe.set_value("Workflow Document State", state.name, "update_value", state.state)
def validate_workflow_conditions(doc): workflow = get_workflow_name(doc.doctype) if not workflow: return workflow_doc = frappe.get_doc("Workflow", workflow) current_state = doc.get(workflow_doc.workflow_state_field) roles = frappe.get_roles() transitions = [] for transition in workflow_doc.transitions: if transition.next_state == current_state and transition.allowed in roles: if not is_transition_condition_satisfied(transition, doc): continue transitions.append(transition.as_dict()) if not transitions: frappe.throw( _("You are not allowed to update as per the conditions set in {} Workflow." ).format(get_link_to_form("Workflow", workflow)), title=_("Insufficient Permissions"), )
def recall_quality_inspection_item(doc, state): workflow = get_workflow_name(doc.get('doctype')) if not workflow: return else: this_doc_workflow_state = get_doc_workflow_state(doc) #frappe.msgprint(f"State: {this_doc_workflow_state}") if this_doc_workflow_state == "Reversed": pr_name = doc.get("reference_name") item_code = doc.get("item_code") purchase_receipt = frappe.get_doc("Purchase Receipt", pr_name) if purchase_receipt: item_row_id = frappe.db.sql( f"""SELECT name FROM `tabPurchase Receipt Item`\ WHERE parent ='{pr_name}' AND item_code ='{item_code}' """)[0][0] row_doc = frappe.get_doc("Purchase Receipt Item", item_row_id) #frappe.msgprint(f"rowID: {item_row_id} and row_doc") row_doc.flags.ignore_permissions = True row_doc.delete() #WE RE-UPDATE THE PERCENTAGE OF GOODS INSPECTED. update_percentage_inspected(doc, "Submitted") #RE-UPDATE THE PURCHASE RECEIPT. WE MAY NEED TO RELOOK WHETHER THE TOTALS ARE UPDATED APPROPRIATELY. purchase_receipt.flags.ignore_permissions = True purchase_receipt.run_method("set_missing_values") purchase_receipt.save() purchase_receipt.reload() purchase_receipt.notify_update() frappe.msgprint( f"The item has been reversed and the related GRN: {pr_name} has been updated appropriately." ) #doc.flags.ignore_permissions = True #doc.delete() #DELETE THIS INSPECTION DOCUMENT #frappe.set_route("List", "Quality Inspection",{"reference_name": pr_name}) elif this_doc_workflow_state == "Cancelled": pass
def validate_invoices_in_po(doc, state): workflow = get_workflow_name(doc.get('doctype')) if workflow: new_workflow_state = get_doc_workflow_state(doc) old_workflow_state = frappe.db.get_value(doc.get('doctype'), doc.get('name'), 'workflow_state') if old_workflow_state == new_workflow_state: return elif new_workflow_state in ["Pending Payment Voucher", "Credit Note"]: purchase_receipt = doc.get("items")[0].purchase_receipt purchase_order = doc.get("items")[0].purchase_order if not purchase_receipt or not purchase_order: frappe.throw( "Sorry. This invoice was not validly received with a system generated PO or GRN" ) else: total_po_amount = frappe.db.get_value( "Purchase Order", purchase_order, 'total') or 0.0 '''invoices_list = frappe.db.get_all('Purchase Invoice Item', filters={ 'purchase_order': purchase_order }, fields=['parent'], group_by='parent', as_list = False)''' sql_to_run = f"""SELECT DISTINCT parent FROM `tabPurchase Invoice Item`\ WHERE purchase_order = '{purchase_order}'\ AND parent IN (SELECT name FROM `tabPurchase Invoice`\ WHERE workflow_state in ('Pending Payment Voucher', 'Credit Note'))""" invoices_list = frappe.db.sql(sql_to_run, as_dict=True) invoices_arr = [invoice.parent for invoice in invoices_list] #frappe.throw("Purchase Order: {0}".format(invoices_arr)) total_accepted_amount = frappe.db.get_list( 'Purchase Invoice', filters={ 'name': ["IN", invoices_arr], 'is_return': False }, #fields="`tabPurchase Invoice`.name, sum(`tabPurchase Invoice`.total) as total") or [0.0] fields="sum(`tabPurchase Invoice`.total) as total") or [{ "total": 0.0 }] total_returns = frappe.db.get_list( 'Purchase Invoice', filters={ 'name': ['IN', invoices_arr], 'is_return': True }, #fields='`tabPurchase Invoice`.name, sum(`tabPurchase Invoice`.total)*-1 as total') or [0.0] fields='sum(`tabPurchase Invoice`.total)*-1 as total') or [ { "total": 0.0 } ] po = total_po_amount or 0.0 ta = total_accepted_amount[0].total or 0.0 tr = total_returns[0].total or 0.0 #frappe.throw("Purchase Order: {0} Accepted Amount: {1} Returns: {2} for {3}".\ # format(total_po_amount,total_accepted_amount[0].total, total_returns, invoices_arr)) this_invoice_total = doc.get( "total") if doc.get("total") > 0 else doc.get("total") * -1 if (po == (ta + tr + this_invoice_total)): invoices_arr.append(doc.get("name")) documents = [ frappe.get_doc("Purchase Invoice", x) for x in invoices_arr ] documents_to_be_paid = [ x for x in documents if x.get("is_return") == False ] list( map(lambda x: raise_payment_request(x, "Submitted"), documents_to_be_paid)) #Raise all documents #CLOSE the PO from mtrh_dev.mtrh_dev.purchase_receipt_utils import close_purchase_order po_doc = frappe.get_doc("Purchase Order", purchase_order) close_purchase_order(po_doc) return
def get_workflow(self): return get_workflow_name(self.name)
def get_doc_workflow_state(doc): workflow_name = get_workflow_name(doc.get('doctype')) workflow_state_field = get_workflow_state_field(workflow_name) return doc.get(workflow_state_field)
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) }))