You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

207 line
6.6 KiB

  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. wn.ui.form.States = Class.extend({
  23. init: function(opts) {
  24. $.extend(this, opts);
  25. this.state_fieldname = wn.workflow.get_state_fieldname(this.frm.doctype);
  26. // no workflow?
  27. if(!this.state_fieldname)
  28. return;
  29. this.update_fields = wn.workflow.get_update_fields(this.frm.doctype);
  30. var me = this;
  31. $(this.frm.wrapper).bind("render_complete", function() {
  32. me.refresh();
  33. })
  34. },
  35. make: function() {
  36. this.parent = this.frm.appframe.$w
  37. .find(".title-button-area-1")
  38. .empty()
  39. .toggle(true)
  40. .css({"margin-right":"5px"});
  41. this.workflow_button = $('<button class="btn btn-default dropdown-toggle">\
  42. <i class="icon-small"></i> <span class="state-text"></span>\
  43. <span class="caret"></span></button>')
  44. .appendTo(this.parent).dropdown();
  45. this.dropdown = $('<ul class="dropdown-menu">').insertAfter(this.workflow_button);
  46. this.help_btn = $('<button class="btn btn-default"><i class="icon-question-sign"></i></button').
  47. insertBefore(this.workflow_button);
  48. this.setup_help();
  49. this.bind_action();
  50. },
  51. setup_help: function() {
  52. var me = this;
  53. this.help_btn.click(function() {
  54. wn.workflow.setup(me.frm.doctype);
  55. var state = me.get_state();
  56. var d = new wn.ui.Dialog({
  57. title: "Workflow: "
  58. + wn.workflow.workflows[me.frm.doctype].name
  59. })
  60. var next_html = $.map(wn.workflow.get_transitions(me.frm.doctype, state),
  61. function(d) {
  62. return d.action.bold() + wn._(" by Role ") + d.allowed;
  63. }).join(", ") || wn._("None: End of Workflow").bold();
  64. $(d.body).html("<p>"+wn._("Current status")+": " + state.bold() + "</p>"
  65. + "<p>"+wn._("Document is only editable by users of role")+": "
  66. + wn.workflow.get_document_state(me.frm.doctype,
  67. state).allow_edit.bold() + "</p>"
  68. + "<p>"+wn._("Next actions")+": "+ next_html +"</p>"
  69. + (me.frm.doc.__islocal ? ("<div class='alert'>"
  70. +wn._("Workflow will start after saving.")+"</div>") : "")
  71. + "<p class='help'>"+wn._("Note: Other permission rules may also apply")+"</p>"
  72. ).css({padding: '15px'});
  73. d.show();
  74. });
  75. },
  76. refresh: function() {
  77. // hide if its not yet saved
  78. if(this.frm.doc.__islocal) {
  79. this.set_default_state();
  80. this.parent.toggle(false);
  81. return;
  82. }
  83. this.make();
  84. // state text
  85. var state = this.get_state();
  86. if(state) {
  87. // show current state on the button
  88. this.workflow_button.find(".state-text").text(state);
  89. var state_doc = wn.model.get("Workflow State", {name:state})[0];
  90. // set the icon
  91. this.workflow_button.find('i').removeClass()
  92. .addClass("icon-white")
  93. .addClass("icon-" + state_doc.icon);
  94. // set the style
  95. this.workflow_button.removeClass().addClass("btn btn-default dropdown-toggle")
  96. if(state_doc && state_doc.style)
  97. this.workflow_button.addClass("btn-" + state_doc.style.toLowerCase());
  98. // show actions from that state
  99. this.show_actions(state);
  100. if(this.frm.doc.__islocal) {
  101. this.workflow_button.attr('disabled', true);
  102. }
  103. }
  104. },
  105. show_actions: function(state) {
  106. var $ul = this.dropdown;
  107. $ul.empty();
  108. $.each(wn.workflow.get_transitions(this.frm.doctype, state), function(i, d) {
  109. if(in_list(user_roles, d.allowed)) {
  110. d.icon = wn.model.get("Workflow State", {name:d.next_state})[0].icon;
  111. $(repl('<li><a href="#" data-action="%(action)s">\
  112. <i class="icon icon-%(icon)s"></i> %(action)s</a></li>', d))
  113. .appendTo($ul);
  114. }
  115. });
  116. // disable the button if user cannot change state
  117. var is_final = !$ul.find("li").length;
  118. this.workflow_button
  119. .attr('disabled', is_final);
  120. this.workflow_button.find(".caret").toggle(is_final ? false : true)
  121. },
  122. set_default_state: function() {
  123. var default_state = wn.workflow.get_default_state(this.frm.doctype);
  124. if(default_state) {
  125. this.frm.set_value(this.state_fieldname, default_state);
  126. }
  127. },
  128. get_state: function() {
  129. if(!this.frm.doc[this.state_fieldname]) {
  130. this.set_default_state();
  131. }
  132. return this.frm.doc[this.state_fieldname];
  133. },
  134. bind_action: function() {
  135. var me = this;
  136. this.dropdown.on("click", "[data-action]", function() {
  137. var action = $(this).attr("data-action");
  138. // capture current state
  139. var doc_before_action = copy_dict(me.frm.doc);
  140. // set new state
  141. var next_state = wn.workflow.get_next_state(me.frm.doctype,
  142. me.frm.doc[me.state_fieldname], action);
  143. me.frm.doc[me.state_fieldname] = next_state;
  144. var new_state = wn.workflow.get_document_state(me.frm.doctype, next_state);
  145. var new_docstatus = cint(new_state.doc_status);
  146. // update field and value
  147. if(new_state.update_field) {
  148. me.frm.set_value(new_state.update_field, new_state.update_value);
  149. }
  150. // revert state on error
  151. var on_error = function() {
  152. // reset in locals
  153. locals[me.frm.doctype][me.frm.docname] = doc_before_action;
  154. me.frm.refresh();
  155. }
  156. if(new_docstatus==1 && me.frm.doc.docstatus==0) {
  157. me.frm.savesubmit(null, on_error);
  158. } else if(new_docstatus==0 && me.frm.doc.docstatus==0) {
  159. me.frm.save("Save", null, null, on_error);
  160. } else if(new_docstatus==1 && me.frm.doc.docstatus==1) {
  161. me.frm.save("Update", null, null, on_error);
  162. } else if(new_docstatus==2 && me.frm.doc.docstatus==1) {
  163. me.frm.savecancel(null, on_error);
  164. } else {
  165. msgprint(wn._("Document Status transition from ") + me.frm.doc.docstatus + " "
  166. + wn._("to") +
  167. new_docstatus + " " + wn._("is not allowed."));
  168. return false;
  169. }
  170. // hide dropdown
  171. $(this).parents(".dropdown-menu:first").prev().dropdown('toggle');
  172. return false;
  173. })
  174. }
  175. });