{
  "processId" : 1,
  "id" : 1,
  "name" : "purchase-order",
  "locked" : false,
  "version" : 11,
  "active" : false,
  "nodes" : [ {
    "id" : 1,
    "type" : "start",
    "name" : "workflow.start",
    "posX" : 500.0,
    "posY" : 20.0,
    "description" : null,
    "sourcePosition" : "bottom",
    "targetPosition" : "none",
    "transitions" : [ {
      "id" : 1,
      "type" : "default",
      "name" : "",
      "color" : "#000000",
      "script" : "",
      "animated" : false,
      "source" : 1,
      "target" : 2
    } ],
    "script" : null,
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 2,
    "type" : "action",
    "name" : "Create PO folder",
    "posX" : 500.0,
    "posY" : 159.1534008683068,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 2,
      "type" : "default",
      "name" : "",
      "color" : "#000000",
      "script" : null,
      "animated" : false,
      "source" : 2,
      "target" : 3
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.Calendar;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\n// Read and increment counter from APP_COUNTER table\nSqlQueryResults counterResult = ws.repository.executeSqlQuery(\n    \"SELECT counter_value FROM APP_COUNTER WHERE counter_key = 'purchase_order'\");\nint currentValue = Integer.parseInt(counterResult.getResults().get(0).getColumns().get(0));\nint nextValue = currentValue + 1;\nws.repository.executeSqlQuery(\n    \"UPDATE APP_COUNTER SET counter_value = \" + nextValue + \" WHERE counter_key = 'purchase_order'\");\n\n// Build PO reference: PO-YYYY-NNN\nint year = Calendar.getInstance().get(Calendar.YEAR);\nString poNumber = String.format(\"PO-%d-%03d\", year, currentValue);\nInput poNumberInput = new Input();\npoNumberInput.setName(\"poNumber\");\npoNumberInput.setValue(poNumber);\ncontext.put(\"poNumber\", poNumberInput);\nFileLogger.info(\"purchase-order-request\", \"PO number generated: \" + poNumber);\n\n// Step 1: create year folder if it does not exist (folder type)\nString yearFolderPath = \"/okm:root/Purchase Order Request/\" + year;\nif (!ws.repository.hasNode(yearFolderPath)) {\n  ws.folder.createMissingFolders(yearFolderPath);\n}\nFileLogger.info(\"purchase-order-request\", \"Year folder ready: \" + yearFolderPath);\n\n// Step 2: create the PO record inside the year folder (record type)\nString fldUuid = ws.repository.getNodeUuid(yearFolderPath);\nString recordPath = yearFolderPath + \"/\" + poNumber;\nif (!ws.repository.hasNode(recordPath)) {\n  ws.record.create(fldUuid, poNumber, \"\", 0);\n}\nFileLogger.info(\"purchase-order-request\", \"PO record created: \" + recordPath);\n\n// Step 3: create \"others\" subfolder inside the record for non-budget documents\nString recordUuid = ws.repository.getNodeUuid(recordPath);\nString othersFolderUuid = \"\";\nString othersFolderPath = recordPath + \"/others\";\nif (!ws.repository.hasNode(othersFolderPath)) {\n  Folder fld = ws.folder.create(recordUuid, \"others\");\n  othersFolderUuid = fld.getUuid();\n} else {\n  othersFolderUuid = ws.repository.getNodeUuid(othersFolderPath);  \n}\nFileLogger.info(\"purchase-order-request\", \"Others folder ready: \" + othersFolderPath);\n\n// Store Upload context objects so each upload field targets the correct destination\nUpload budgetUpload = new Upload();\nbudgetUpload.setName(\"budgetDocumentData\");\nbudgetUpload.setFolderUuid(recordUuid);\ncontext.put(\"budgetDocumentData\", budgetUpload);\n\nUpload otherUpload = new Upload();\notherUpload.setName(\"otherDocumentsData\");\notherUpload.setFolderUuid(othersFolderUuid);\ncontext.put(\"otherDocumentsData\", otherUpload);\n\ncontext.put(\"poRecordPath\", recordPath);\ncontext.put(\"poRecordUuid\", recordUuid);\ncontext.put(\"poOthersFolderUuid\", othersFolderUuid);\n\n// Attach this workflow process instance to the PO record node\nWorkflowUtils.setProcessInstanceNode(context, recordUuid);\nFileLogger.info(\"purchase-order-request\", \"Process instance attached to record \" + recordUuid);",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 3,
    "type" : "task",
    "name" : "Request Submission",
    "posX" : 501.7149059334298,
    "posY" : 296.013024602026,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 3,
      "type" : "default",
      "name" : "submit",
      "color" : "#2196F3",
      "script" : null,
      "animated" : false,
      "source" : 3,
      "target" : 12
    } ],
    "script" : null,
    "form" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE workflow-form PUBLIC \"-//OpenKM//DTD Workflow Form 1.0//EN\"\n                                \"https://www.openkm.com/dtd/workflow-form-1.0.dtd\">\n<workflow-form>\n  <text label=\"&lt;strong&gt;Purchase Order Request&lt;/strong&gt;\" name=\"headerTitle\" />\n  <input label=\"PO Reference\" name=\"poNumberDisplay\" type=\"text\" readonly=\"true\" data=\"poNumber\" />\n  <input label=\"Request Title\" name=\"requestTitle\" type=\"text\" timeFormat=\"none\">\n    <validator type=\"req\"/>\n  </input>\n  <select label=\"Department\" name=\"department\" type=\"simple\">\n    <option label=\"IT\" value=\"it\"/>\n    <option label=\"Finance\" value=\"finance\"/>\n    <option label=\"Operations\" value=\"operations\"/>\n    <option label=\"Sales\" value=\"sales\"/>\n    <option label=\"HR\" value=\"hr\"/>\n    <validator type=\"req\"/>\n  </select>\n  <input label=\"Estimated Amount\" name=\"estimatedAmount\" type=\"text\" timeFormat=\"none\">\n    <validator type=\"req\"/>\n    <validator type=\"num\"/>\n  </input>\n  <select label=\"Currency\" name=\"currency\" type=\"simple\">\n    <option label=\"EUR\" value=\"EUR\"/>\n    <option label=\"USD\" value=\"USD\"/>\n    <option label=\"GBP\" value=\"GBP\"/>\n    <validator type=\"req\"/>\n  </select>\n  <select label=\"Priority\" name=\"priority\" type=\"simple\">\n    <option label=\"Low\" value=\"low\"/>\n    <option label=\"Medium\" value=\"medium\"/>\n    <option label=\"High\" value=\"high\"/>\n    <option label=\"Urgent\" value=\"urgent\"/>\n    <validator type=\"req\"/>\n  </select>\n  <input label=\"Required By Date\" name=\"requiredByDate\" type=\"date\">\n    <validator type=\"req\"/>\n  </input>\n  <input label=\"Vendor / Supplier\" name=\"vendor\" type=\"text\" timeFormat=\"none\" />\n  <textarea label=\"Justification / Description\" name=\"description\" type=\"text\">\n    <validator type=\"req\"/>\n  </textarea>\n  <upload label=\"Budget Document (proforma, quote)\" name=\"budgetDocument\" data=\"budgetDocumentData\">\n    <validator type=\"req\"/>\n  </upload>\n  <upload label=\"Other Supporting Documents\" name=\"otherDocuments\" data=\"otherDocumentsData\" multiple=\"true\" />\n  <button label=\"Submit Request\" name=\"submit\" transition=\"submit\" style=\"yes\" color=\"success\" validate=\"true\" />\n</workflow-form>",
    "assignExpr" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\n\n// Assign to the user who started the workflow\nActor actor = (Actor) context.get(\"initiator\");\nreturn actor.getId();",
    "dueDate" : "",
    "repeat" : "",
    "notifyAssignment" : true,
    "notificationSubject" : "Purchase Order Request - Action required",
    "notificationBody" : "<p>Hi ${initiator.name},</p><p>Your Purchase Order request form is ready to be filled in.</p>",
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 4,
    "type" : "task",
    "name" : "Budget Validation",
    "posX" : 500.1091980623313,
    "posY" : 732.8186357153478,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 4,
      "type" : "default",
      "name" : "approved",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 4,
      "target" : 15
    }, {
      "id" : 5,
      "type" : "default",
      "name" : "rejected",
      "color" : "#F44336",
      "script" : null,
      "animated" : false,
      "source" : 4,
      "target" : 9
    }, {
      "id" : 6,
      "type" : "default",
      "name" : "needs_clarification",
      "color" : "#FFC107",
      "script" : null,
      "animated" : false,
      "source" : 4,
      "target" : 13
    } ],
    "script" : null,
    "form" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE workflow-form PUBLIC \"-//OpenKM//DTD Workflow Form 1.0//EN\"\n                                \"https://www.openkm.com/dtd/workflow-form-1.0.dtd\">\n<workflow-form>\n  <text label=\"&lt;strong&gt;Budget Validation&lt;/strong&gt;\" name=\"headerBudget\" />\n  <input label=\"PO Reference\" name=\"poNumberDisplay\" type=\"text\" readonly=\"true\" data=\"poNumber\" />\n  <input label=\"Request Title\" name=\"requestTitle\" type=\"text\" readonly=\"true\" data=\"requestTitle\" />\n  <select label=\"Department\" name=\"department\" type=\"simple\" readonly=\"true\" data=\"department\">\n    <option label=\"IT\" value=\"it\"/>\n    <option label=\"Finance\" value=\"finance\"/>\n    <option label=\"Operations\" value=\"operations\"/>\n    <option label=\"Sales\" value=\"sales\"/>\n    <option label=\"HR\" value=\"hr\"/>\n  </select>\n  <input label=\"Estimated Amount\" name=\"estimatedAmount\" type=\"text\" readonly=\"true\" data=\"estimatedAmount\" />\n  <select label=\"Currency\" name=\"currency\" type=\"simple\" readonly=\"true\" data=\"currency\">\n    <option label=\"EUR\" value=\"EUR\"/>\n    <option label=\"USD\" value=\"USD\"/>\n    <option label=\"GBP\" value=\"GBP\"/>\n  </select>\n  <select label=\"Priority\" name=\"priority\" type=\"simple\" readonly=\"true\" data=\"priority\">\n    <option label=\"Low\" value=\"low\"/>\n    <option label=\"Medium\" value=\"medium\"/>\n    <option label=\"High\" value=\"high\"/>\n    <option label=\"Urgent\" value=\"urgent\"/>\n  </select>\n  <textarea label=\"Justification / Description\" name=\"description\" type=\"text\" readonly=\"true\" data=\"description\" />\n  <input label=\"Budget Document\" name=\"budgetDocumentUuid\" type=\"folder\" readonly=\"true\" data=\"budgetDocumentUuid\" />\n  <input label=\"Other Documents Folder\" name=\"othersFolderUuid\" type=\"folder\" readonly=\"true\" data=\"othersFolderUuid\" />\n  <textarea label=\"Budget Reviewer Comments\" name=\"budgetComments\" type=\"text\">\n    <validator type=\"req\"/>\n  </textarea>\n  <button label=\"Approve\" name=\"approved\" transition=\"approved\" style=\"yes\" color=\"success\" validate=\"true\" />\n  <button label=\"Needs Clarification\" name=\"needs_clarification\" transition=\"needs_clarification\" style=\"change\" color=\"warning\" validate=\"true\" />\n  <button label=\"Reject\" name=\"rejected\" transition=\"rejected\" confirmation=\"Are you sure you want to reject this request?\" style=\"no\" color=\"danger\" validate=\"true\" />\n</workflow-form>",
    "assignExpr" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.okmflow.rest.dto.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\n// Check if this task has already been assigned before (re-entry after Clarification Request)\ndef procIns = context.get(\"processInstance\");\nlong piId = procIns.getId();\nList<TaskInstanceDTO> prevTasks = WorkflowUtils.getTaskInstances(piId, \"Budget Validation\");\nif (prevTasks != null && !prevTasks.isEmpty()) {\n  for (TaskInstanceDTO t : prevTasks) {\n    if (t.getActor() != null && !t.getActor().isEmpty()) {\n      return t.getActor();\n    }\n  }\n}\n\n// First time: apply business rule based on estimated amount\nInput estimatedAmountInput = (Input) context.get(\"estimatedAmount\");\ndouble amount = Double.parseDouble(estimatedAmountInput.getValue());\n\nif (amount < 3000) {\n  List<String> userList = new ArrayList();\n  for (CommonUser commonUser : ws.auth.getUsersByRole(\"ROLE_FINANCE\")) {\n    userList.add(commonUser.getId());\n  }\n  return userList;\n} else {\n  return \"financeManager\";\n}",
    "dueDate" : "",
    "repeat" : "",
    "notifyAssignment" : true,
    "notificationSubject" : "Purchase Order - Budget Validation required",
    "notificationBody" : "<p>A new Purchase Order request requires budget validation.</p><p>PO Reference: ${poNumber.value}</p>",
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 5,
    "type" : "task",
    "name" : "Clarification Request",
    "posX" : -95.44461670201828,
    "posY" : 783.0840391866266,
    "description" : "",
    "sourcePosition" : "top",
    "targetPosition" : "bottom",
    "transitions" : [ {
      "id" : 7,
      "type" : "default",
      "name" : "resubmit",
      "color" : "#2196F3",
      "script" : null,
      "animated" : false,
      "source" : 5,
      "target" : 14
    } ],
    "script" : null,
    "form" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE workflow-form PUBLIC \"-//OpenKM//DTD Workflow Form 1.0//EN\"\n                                \"https://www.openkm.com/dtd/workflow-form-1.0.dtd\">\n<workflow-form>\n  <text label=\"&lt;strong&gt;Clarification Required&lt;/strong&gt;\" name=\"headerClarification\" />\n  <input label=\"PO Reference\" name=\"poNumberDisplay\" type=\"text\" readonly=\"true\" data=\"poNumber\" />\n  <textarea label=\"Reviewer Comments (read)\" name=\"budgetComments\" type=\"text\" readonly=\"true\" data=\"budgetComments\" />\n  <input label=\"Request Title\" name=\"requestTitle\" type=\"text\" data=\"requestTitle\">\n    <validator type=\"req\"/>\n  </input>\n  <select label=\"Department\" name=\"department\" type=\"simple\" data=\"department\">\n    <option label=\"IT\" value=\"it\"/>\n    <option label=\"Finance\" value=\"finance\"/>\n    <option label=\"Operations\" value=\"operations\"/>\n    <option label=\"Sales\" value=\"sales\"/>\n    <option label=\"HR\" value=\"hr\"/>\n    <validator type=\"req\"/>\n  </select>\n  <input label=\"Estimated Amount\" name=\"estimatedAmount\" type=\"text\" timeFormat=\"none\" data=\"estimatedAmount\">\n    <validator type=\"req\"/>\n    <validator type=\"num\"/>\n  </input>\n  <select label=\"Currency\" name=\"currency\" type=\"simple\" data=\"currency\">\n    <option label=\"EUR\" value=\"EUR\"/>\n    <option label=\"USD\" value=\"USD\"/>\n    <option label=\"GBP\" value=\"GBP\"/>\n    <validator type=\"req\"/>\n  </select>\n  <select label=\"Priority\" name=\"priority\" type=\"simple\" data=\"priority\">\n    <option label=\"Low\" value=\"low\"/>\n    <option label=\"Medium\" value=\"medium\"/>\n    <option label=\"High\" value=\"high\"/>\n    <option label=\"Urgent\" value=\"urgent\"/>\n    <validator type=\"req\"/>\n  </select>\n  <textarea label=\"Justification / Description\" name=\"description\" type=\"text\" data=\"description\">\n    <validator type=\"req\"/>\n  </textarea>\n  <input label=\"Budget Document\" name=\"budgetDocumentUuid\" type=\"folder\" readonly=\"true\" data=\"budgetDocumentUuid\" />\n  <upload label=\"Replace Budget Document\" name=\"clarificationUpdateBudgetDocument\" data=\"updateBudgetDocument\" type=\"update\" >\n  </upload>\n  <input label=\"Other Documents Folder\" name=\"othersFolderUuid\" type=\"folder\" readonly=\"true\" data=\"othersFolderUuid\" />\n  <upload label=\"Other Supporting Documents\" name=\"clarificationOtherDocuments\" data=\"otherDocumentsData\" multiple=\"true\" type=\"update\"  />\n  <button label=\"Resubmit for Validation\" name=\"resubmit\" transition=\"resubmit\" style=\"yes\" color=\"success\" validate=\"true\" />\n</workflow-form>",
    "assignExpr" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\n\n// Assign to the user who started the workflow\nActor actor = (Actor) context.get(\"initiator\");\nreturn actor.getId();",
    "dueDate" : "",
    "repeat" : "",
    "notifyAssignment" : true,
    "notificationSubject" : "Purchase Order - Clarification required",
    "notificationBody" : "<p>Your Purchase Order request requires clarification before it can be approved.</p><p>PO Reference: ${poNumber.value}</p>",
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 6,
    "type" : "task",
    "name" : "Manager Approval",
    "posX" : 499.9445754326882,
    "posY" : 1203.2366095813404,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 8,
      "type" : "default",
      "name" : "approved",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 6,
      "target" : 16
    }, {
      "id" : 9,
      "type" : "default",
      "name" : "rejected",
      "color" : "#F44336",
      "script" : null,
      "animated" : false,
      "source" : 6,
      "target" : 10
    } ],
    "script" : null,
    "form" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE workflow-form PUBLIC \"-//OpenKM//DTD Workflow Form 1.0//EN\"\n                                \"https://www.openkm.com/dtd/workflow-form-1.0.dtd\">\n<workflow-form>\n  <text label=\"&lt;strong&gt;Manager Approval&lt;/strong&gt;\" name=\"headerManager\" />\n  <input label=\"PO Reference\" name=\"poNumberDisplay\" type=\"text\" readonly=\"true\" data=\"poNumber\" />\n  <input label=\"Request Title\" name=\"requestTitle\" type=\"text\" readonly=\"true\" data=\"requestTitle\" />\n  <select label=\"Department\" name=\"department\" type=\"simple\" readonly=\"true\" data=\"department\">\n    <option label=\"IT\" value=\"it\"/>\n    <option label=\"Finance\" value=\"finance\"/>\n    <option label=\"Operations\" value=\"operations\"/>\n    <option label=\"Sales\" value=\"sales\"/>\n    <option label=\"HR\" value=\"hr\"/>\n  </select>\n  <input label=\"Estimated Amount\" name=\"estimatedAmount\" type=\"text\" readonly=\"true\" data=\"estimatedAmount\" />\n  <select label=\"Currency\" name=\"currency\" type=\"simple\" readonly=\"true\" data=\"currency\">\n    <option label=\"EUR\" value=\"EUR\"/>\n    <option label=\"USD\" value=\"USD\"/>\n    <option label=\"GBP\" value=\"GBP\"/>\n  </select>\n  <select label=\"Priority\" name=\"priority\" type=\"simple\" readonly=\"true\" data=\"priority\">\n    <option label=\"Low\" value=\"low\"/>\n    <option label=\"Medium\" value=\"medium\"/>\n    <option label=\"High\" value=\"high\"/>\n    <option label=\"Urgent\" value=\"urgent\"/>\n  </select>\n  <input label=\"Required By Date\" name=\"requiredByDate\" type=\"date\" readonly=\"true\" data=\"requiredByDate\" />\n  <input label=\"Vendor / Supplier\" name=\"vendor\" type=\"text\" readonly=\"true\" data=\"vendor\" />\n  <textarea label=\"Justification / Description\" name=\"description\" type=\"text\" readonly=\"true\" data=\"description\" />\n  <textarea label=\"Budget Reviewer Comments\" name=\"budgetComments\" type=\"text\" readonly=\"true\" data=\"budgetComments\" />\n  <input label=\"Budget Document\" name=\"budgetDocumentUuid\" type=\"folder\" readonly=\"true\" data=\"budgetDocumentUuid\" />\n  <input label=\"Other Documents Folder\" name=\"othersFolderUuid\" type=\"folder\" readonly=\"true\" data=\"othersFolderUuid\" />\n  <textarea label=\"Manager Comments\" name=\"managerComments\" type=\"text\">\n    <validator type=\"req\"/>\n  </textarea>\n  <button label=\"Approve\" name=\"approved\" transition=\"approved\" style=\"yes\" color=\"success\" validate=\"true\" />\n  <button label=\"Reject\" name=\"rejected\" transition=\"rejected\" confirmation=\"Are you sure you want to reject this request?\" style=\"no\" color=\"danger\" validate=\"true\" />\n</workflow-form>",
    "assignExpr" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\n\n// Assign according to requester's department\nSelect departmentSelect = (Select) context.get(\"department\");\nString department = departmentSelect.getValue();\n\nif (\"it\".equals(department)) {\n  return \"itManager\";\n} else if (\"finance\".equals(department)) {\n  return \"financeManager\";\n} else if (\"operations\".equals(department)) {\n  return \"operationsManager\";\n} else if (\"sales\".equals(department)) {\n  return \"salesManager\";\n} else if (\"hr\".equals(department)) {\n  return \"hrManager\";\n} else {\n  // Provisional fallback until specific roles are defined\n  return \"okmAdmin\";\n}",
    "dueDate" : "",
    "repeat" : "",
    "notifyAssignment" : true,
    "notificationSubject" : "Purchase Order - Manager Approval required",
    "notificationBody" : "<p>A Purchase Order request requires your approval.</p><p>PO Reference: ${poNumber.value}</p>",
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 7,
    "type" : "task",
    "name" : "Purchase Order",
    "posX" : 501.2311676508335,
    "posY" : 1602.4779099777954,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 10,
      "type" : "default",
      "name" : "issued",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 7,
      "target" : 17
    } ],
    "script" : null,
    "form" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE workflow-form PUBLIC \"-//OpenKM//DTD Workflow Form 1.0//EN\"\n                                \"https://www.openkm.com/dtd/workflow-form-1.0.dtd\">\n<workflow-form>\n  <text label=\"&lt;strong&gt;Issue Purchase Order&lt;/strong&gt;\" name=\"headerPO\" />\n  <input label=\"PO Reference\" name=\"poNumberDisplay\" type=\"text\" readonly=\"true\" data=\"poNumber\" />\n  <input label=\"Request Title\" name=\"requestTitle\" type=\"text\" readonly=\"true\" data=\"requestTitle\" />\n  <input label=\"Estimated Amount\" name=\"estimatedAmount\" type=\"text\" readonly=\"true\" data=\"estimatedAmount\" />\n  <select label=\"Currency\" name=\"currency\" type=\"simple\" readonly=\"true\" data=\"currency\">\n    <option label=\"EUR\" value=\"EUR\"/>\n    <option label=\"USD\" value=\"USD\"/>\n    <option label=\"GBP\" value=\"GBP\"/>\n  </select>\n  <textarea label=\"Manager Comments\" name=\"managerComments\" type=\"text\" readonly=\"true\" data=\"managerComments\" />\n  <input label=\"Budget Document\" name=\"budgetDocumentUuid\" type=\"folder\" readonly=\"true\" data=\"budgetDocumentUuid\" />\n  <input label=\"Other Documents Folder\" name=\"othersFolderUuid\" type=\"folder\" readonly=\"true\" data=\"othersFolderUuid\" />\n  <separator label=\"Purchase Order Details\" name=\"poDetailsSeparator\" />\n  <input label=\"PO Number (official)\" name=\"poOfficialNumber\" type=\"text\" timeFormat=\"none\">\n    <validator type=\"req\"/>\n  </input>\n  <input label=\"Actual Amount\" name=\"actualAmount\" type=\"text\" timeFormat=\"none\">\n    <validator type=\"req\"/>\n    <validator type=\"num\"/>\n  </input>\n  <input label=\"Vendor Confirmed\" name=\"vendorConfirmed\" type=\"text\" timeFormat=\"none\">\n    <validator type=\"req\"/>\n  </input>\n  <input label=\"Expected Delivery Date\" name=\"deliveryDate\" type=\"date\">\n    <validator type=\"req\"/>\n  </input>\n  <textarea label=\"PO Notes\" name=\"poNotes\" type=\"text\" />\n  <button label=\"Issue Purchase Order\" name=\"issued\" transition=\"issued\" style=\"yes\" color=\"success\" validate=\"true\" />\n</workflow-form>",
    "assignExpr" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.okmflow.rest.dto.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\n// Assign to the same actor who handled Budget Validation\ndef procIns = context.get(\"processInstance\");\nlong piId = procIns.getId();\nList<TaskInstanceDTO> prevTasks = WorkflowUtils.getTaskInstances(piId, \"Budget Validation\");\nif (prevTasks != null && !prevTasks.isEmpty()) {\n  for (TaskInstanceDTO t : prevTasks) {\n    if (t.getActor() != null && !t.getActor().isEmpty()) {\n      return t.getActor();\n    }\n  }\n}\n\n// Fallback if no previous actor is found (should not normally happen)\nreturn \"okmAdmin\";",
    "dueDate" : "",
    "repeat" : "",
    "notifyAssignment" : true,
    "notificationSubject" : "Purchase Order - Ready to issue",
    "notificationBody" : "<p>Purchase Order request has been approved and is ready to be issued.</p><p>PO Reference: ${poNumber.value}</p>",
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 8,
    "type" : "end",
    "name" : "workflow.end",
    "posX" : 804.5940961141936,
    "posY" : 2145.3689157526032,
    "description" : "",
    "sourcePosition" : null,
    "targetPosition" : "top",
    "transitions" : [ ],
    "script" : null,
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 9,
    "type" : "mail",
    "name" : "Mail - Rejected by Budget",
    "posX" : 897.2838781047446,
    "posY" : 1070.030435058582,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 11,
      "type" : "default",
      "name" : "",
      "color" : "#F44336",
      "script" : null,
      "animated" : false,
      "source" : 9,
      "target" : 8
    } ],
    "script" : null,
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : "${initiator.email}",
    "subject" : "Purchase Order ${poNumber.value} - Rejected at Budget Validation",
    "body" : "<p>Hi ${initiator.name},</p><p>Your Purchase Order request <strong>${poNumber.value}</strong> has been rejected at the Budget Validation stage.</p><p><strong>Reviewer comments:</strong> ${budgetComments.value}</p><p>Regards,<br/>Purchase Order System</p>"
  }, {
    "id" : 10,
    "type" : "mail",
    "name" : "Mail - Rejected by Manager",
    "posX" : 988.4232048414365,
    "posY" : 1448.154813990766,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 12,
      "type" : "default",
      "name" : "",
      "color" : "#F44336",
      "script" : null,
      "animated" : false,
      "source" : 10,
      "target" : 8
    } ],
    "script" : null,
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : "${initiator.email}",
    "subject" : "Purchase Order ${poNumber.value} - Rejected by Manager",
    "body" : "<p>Hi ${initiator.name},</p><p>Your Purchase Order request <strong>${poNumber.value}</strong> has been rejected by the manager.</p><p><strong>Manager comments:</strong> ${managerComments.value}</p><p>Regards,<br/>Purchase Order System</p>"
  }, {
    "id" : 11,
    "type" : "mail",
    "name" : "Mail - PO Issued",
    "posX" : 640.7205916260075,
    "posY" : 1982.6686700989867,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 13,
      "type" : "default",
      "name" : "",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 11,
      "target" : 8
    } ],
    "script" : null,
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : "${initiator.email}",
    "subject" : "Purchase Order ${poNumber.value} - Approved and Issued",
    "body" : "<p>Hi ${initiator.name},</p><p>Your Purchase Order request <strong>${poNumber.value}</strong> has been approved and the purchase order has been issued.</p><p><strong>Official PO Number:</strong> ${poOfficialNumber.value}</p><p><strong>Vendor:</strong> ${vendorConfirmed.value}</p><p><strong>Expected Delivery:</strong> ${deliveryDate_fmt.value}</p><p>Regards,<br/>Purchase Order System</p>"
  }, {
    "id" : 12,
    "type" : "action",
    "name" : "Save metadata",
    "posX" : 547.8374375106025,
    "posY" : 556.4918289942427,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 14,
      "type" : "default",
      "name" : "",
      "color" : "#000000",
      "script" : null,
      "animated" : false,
      "source" : 12,
      "target" : 4
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.sdk4j.util.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\n\n// Read submitted form values from context\nInput requestTitleInput = (Input) context.get(\"requestTitle\");\nSelect departmentSelect = (Select) context.get(\"department\");\nInput estimatedAmountInput = (Input) context.get(\"estimatedAmount\");\nSelect currencySelect = (Select) context.get(\"currency\");\nSelect prioritySelect = (Select) context.get(\"priority\");\nInput requiredByDateInput = (Input) context.get(\"requiredByDate\");\nInput vendorInput = (Input) context.get(\"vendor\");\nTextArea descriptionTextArea = (TextArea) context.get(\"description\");\nInput poNumberInput = (Input) context.get(\"poNumber\");\n\nString requestTitle = requestTitleInput.getValue();\nString department = departmentSelect.getValue();\nString estimatedAmount = estimatedAmountInput.getValue();\nString currency = currencySelect.getValue();\nString priority = prioritySelect.getValue();\nString requiredByDate = requiredByDateInput.getValue();\nString vendor = vendorInput.getValue();\nString description = descriptionTextArea.getValue();\nString poNumber = poNumberInput.getValue();\n\n// Request date captured automatically (system date, for reporting)\nString requestDate = ISO8601.formatBasic(Calendar.getInstance());\n\n// Build property map\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.poNumber\", poNumber);\nproperties.put(\"okp:purchase_order.requestTitle\", requestTitle);\nproperties.put(\"okp:purchase_order.department\", \"[\\\"\" + department + \"\\\"]\");\nproperties.put(\"okp:purchase_order.requestDate\", requestDate);\nproperties.put(\"okp:purchase_order.estimatedAmount\", estimatedAmount);\nproperties.put(\"okp:purchase_order.currency\", \"[\\\"\" + currency + \"\\\"]\");\nproperties.put(\"okp:purchase_order.priority\", \"[\\\"\" + priority + \"\\\"]\");\nproperties.put(\"okp:purchase_order.requiredByDate\", requiredByDate);\nproperties.put(\"okp:purchase_order.vendor\", vendor != null ? vendor : \"\");\nproperties.put(\"okp:purchase_order.description\", description);\nproperties.put(\"okp:purchase_order.status\", \"[\\\"pending_budget\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n} else {\n  ws.propertyGroup.addGroup(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Metadata saved for \" + poNumber);\n\n// Build read-only reference fields for Budget Validation form:\n// the record should contain exactly one document (the uploaded budget document)\nString budgetDocumentUuid = \"\";\nList<Document> recordDocs = ws.document.getChildren(uuid);\nif (recordDocs != null && !recordDocs.isEmpty()) {\n  Document firstDoc = recordDocs.get(0);\n  budgetDocumentUuid = firstDoc.getUuid();\n}\n\nInput budgetDocumentUuidInput = new Input();\nbudgetDocumentUuidInput.setName(\"budgetDocumentUuid\");\nbudgetDocumentUuidInput.setValue(budgetDocumentUuid);\ncontext.put(\"budgetDocumentUuid\", budgetDocumentUuidInput);\n\nString othersFolderUuid = (String) context.get(\"poOthersFolderUuid\");\nInput othersFolderUuidInput = new Input();\nothersFolderUuidInput.setName(\"othersFolderUuid\");\nothersFolderUuidInput.setValue(othersFolderUuid);\ncontext.put(\"othersFolderUuid\", othersFolderUuidInput);",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 13,
    "type" : "action",
    "name" : "Set status - Clarification",
    "posX" : -51.442535051386685,
    "posY" : 1040.981346145562,
    "description" : "",
    "sourcePosition" : "top",
    "targetPosition" : "right",
    "transitions" : [ {
      "id" : 15,
      "type" : "default",
      "name" : "",
      "color" : "#FFC107",
      "script" : null,
      "animated" : false,
      "source" : 13,
      "target" : 5
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.status\", \"[\\\"clarification\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Status updated to clarification\");\n\n// Build an Upload object to let the user replace the budget document (new version)\nInput budgetDocumentUuidInput = (Input) context.get(\"budgetDocumentUuid\");\nUpload updateBudgetUpload = new Upload();\nupdateBudgetUpload.setType(\"update\");\nupdateBudgetUpload.setDocumentUuid(budgetDocumentUuidInput.getValue());\ncontext.put(\"updateBudgetDocument\", updateBudgetUpload);",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 14,
    "type" : "action",
    "name" : "Set status - Pending Budget",
    "posX" : 137.05435597287365,
    "posY" : 587.4347796462278,
    "description" : "",
    "sourcePosition" : "right",
    "targetPosition" : "left",
    "transitions" : [ {
      "id" : 16,
      "type" : "default",
      "name" : "",
      "color" : "#2196F3",
      "script" : null,
      "animated" : false,
      "source" : 14,
      "target" : 4
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.status\", \"[\\\"pending_budget\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Status updated to pending_budget\");",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 15,
    "type" : "action",
    "name" : "Set status - Pending Approval",
    "posX" : 547.4549709543454,
    "posY" : 1073.1911975211806,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 17,
      "type" : "default",
      "name" : "",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 15,
      "target" : 6
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.status\", \"[\\\"pending_approval\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Status updated to pending_approval\");",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 16,
    "type" : "action",
    "name" : "Set status - Approved",
    "posX" : 548.7415631724908,
    "posY" : 1468.5215969156382,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 18,
      "type" : "default",
      "name" : "",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 16,
      "target" : 7
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.status\", \"[\\\"approved\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Status updated to approved\");",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  }, {
    "id" : 17,
    "type" : "action",
    "name" : "Set status - Issued",
    "posX" : 574.8580943518332,
    "posY" : 1857.2362348471386,
    "description" : "",
    "sourcePosition" : "bottom",
    "targetPosition" : "top",
    "transitions" : [ {
      "id" : 19,
      "type" : "default",
      "name" : "",
      "color" : "#4CAF50",
      "script" : null,
      "animated" : false,
      "source" : 17,
      "target" : 11
    } ],
    "script" : "import com.openkm.sdk4j.impl.OKMWebservices;\nimport com.openkm.sdk4j.bean.*;\nimport com.openkm.okmflow.util.*;\nimport com.openkm.okmflow.bean.*;\nimport com.openkm.bean.form.*;\nimport com.openkm.util.*;\nimport java.util.*;\nimport java.text.*;\n\nOKMWebservices ws = WebservicesHelper.getInstance();\n\nString uuid = (String) context.get(\"poRecordUuid\");\nMap<String,String> properties = new HashMap();\nproperties.put(\"okp:purchase_order.status\", \"[\\\"issued\\\"]\");\n\nif (ws.propertyGroup.hasGroup(uuid, \"okg:purchase_order\")) {\n  ws.propertyGroup.setProperties(uuid, \"okg:purchase_order\", properties);\n}\n\nFileLogger.info(\"purchase-order-request\", \"Status updated to issued\");\n\n// Format delivery date (stored as yyyyMMddHHmmss) to yyyy-MM-dd for the Mail node\nSimpleDateFormat raw = new SimpleDateFormat(\"yyyyMMddHHmmss\");\nSimpleDateFormat fmt = new SimpleDateFormat(\"yyyy-MM-dd\");\n\nInput deliveryDateRaw = (Input) context.get(\"deliveryDate\");\nInput deliveryDateFmt = new Input();\ndeliveryDateFmt.setValue(fmt.format(raw.parse(deliveryDateRaw.getValue())));\ncontext.put(\"deliveryDate_fmt\", deliveryDateFmt);",
    "form" : null,
    "assignExpr" : null,
    "dueDate" : null,
    "repeat" : null,
    "notifyAssignment" : true,
    "notificationSubject" : null,
    "notificationBody" : null,
    "recipients" : null,
    "subject" : null,
    "body" : null
  } ]
}