{
  "name": "RFP 05 - Proposal Assembly and Operations",
  "nodes": [
    {
      "id": "a204ed846f888d27",
      "name": "Assembly Contract",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1260,
        -460
      ],
      "parameters": {
        "content": "## Proposal Assembly & Operations\nAssembles only APPROVED answers, preserves evidence IDs, and creates a human-readable proposal package plus an operations summary. External submission is intentionally absent.",
        "width": 820,
        "height": 320,
        "color": 5
      }
    },
    {
      "id": "609df056abc419c9",
      "name": "Manual Assembly Test",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -1180,
        -80
      ],
      "parameters": {}
    },
    {
      "id": "5514c2734cf38698",
      "name": "Approved Project Assembly Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1180,
        120
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "rfp-control-room-assemble",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "62a7384e3fb36bb0",
      "name": "Load Assembly Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -920,
        20
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const b=$json.body??$json; return [{json:{project_id:String(b.project_id??\"RFP-DEMO-001\"),requested_by:String(b.requested_by??\"demo-user\"),requested_at:new Date().toISOString()}}];"
      }
    },
    {
      "id": "3ac181781b265ab3",
      "name": "Read Requirement Matrix",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -660,
        -60
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Requirement Matrix"
        },
        "filtersUI": {},
        "combineFilters": "AND",
        "options": {}
      },
      "alwaysOutputData": true
    },
    {
      "id": "433a18dec5b46a4a",
      "name": "Read Review Queue",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -660,
        100
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "read",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Review Queue"
        },
        "filtersUI": {},
        "combineFilters": "AND",
        "options": {}
      },
      "alwaysOutputData": true
    },
    {
      "id": "77d558cc9df827c6",
      "name": "Assemble Approved Proposal",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -360,
        20
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const req=$(\"Load Assembly Request\").item.json; const matrix=$(\"Read Requirement Matrix\").all().map(i=>i.json); const reviews=$(\"Read Review Queue\").all().map(i=>i.json);\nconst rows=[...matrix,...reviews].filter(x=>String(x.project_id)===req.project_id); const byId=new Map(); for(const r of rows){const prev=byId.get(r.requirement_id); if(!prev||String(r.updated_at)>String(prev.updated_at)) byId.set(r.requirement_id,r);}\nconst approved=[...byId.values()].filter(x=>String(x.status).toUpperCase()===\"APPROVED\").sort((a,b)=>Number(a.sequence||0)-Number(b.sequence||0)); const blocked=[...byId.values()].filter(x=>![\"APPROVED\",\"REJECTED\"].includes(String(x.status).toUpperCase()));\nconst body=[`# Response package — ${req.project_id}`,`Generated: ${new Date().toISOString()}`,\"\",...approved.flatMap((x,i)=>[`## ${i+1}. ${x.requirement_text}`,\"\",String(x.final_answer||x.draft_answer||\"\"),\"\",`Evidence: ${x.evidence_ids||\"None\"}`,\"\"])].join(\"\\n\");\nreturn [{json:{...req,approved_count:approved.length,blocked_count:blocked.length,assembly_status:blocked.length?\"INCOMPLETE\":\"READY_FOR_FINAL_REVIEW\",proposal_markdown:body,evidence_manifest:[...new Set(approved.flatMap(x=>String(x.evidence_ids||\"\").split(\",\")).filter(Boolean))].join(\",\"),blocked_requirement_ids:blocked.map(x=>x.requirement_id).join(\",\"),event_id:`ASM-${Date.now()}-${req.project_id}`,event_type:\"PROPOSAL_ASSEMBLED\",created_at:new Date().toISOString()}}];"
      }
    },
    {
      "id": "a5418044fa1877d2",
      "name": "Append Assembly Audit",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -70,
        -40
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Audit Log"
        },
        "dataMode": "autoMapInputData",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      },
      "alwaysOutputData": false
    },
    {
      "id": "960b1f4ef629b237",
      "name": "Email Owner Assembly Summary",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        -70,
        100
      ],
      "parameters": {
        "resource": "email",
        "operation": "send",
        "fromEmail": "Sales Evidence Control Room <SET_FROM_EMAIL>",
        "toEmail": "SET_OWNER_EMAIL",
        "subject": "={{ 'RFP package ' + $json.assembly_status + ' — ' + $json.project_id }}",
        "emailFormat": "text",
        "text": "={{ `Project: ${$json.project_id}\\nStatus: ${$json.assembly_status}\\nApproved answers: ${$json.approved_count}\\nBlocked requirements: ${$json.blocked_count}\\nEvidence manifest: ${$json.evidence_manifest}` }}",
        "options": {
          "appendAttribution": false
        }
      }
    },
    {
      "id": "7c6a32061f0d5059",
      "name": "Return Proposal Package",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        240,
        20
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      }
    },
    {
      "id": "5fb76c8d9b08ba89",
      "name": "Optional publishing adapters",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -560,
        430
      ],
      "parameters": {
        "content": "## Disconnected by design\nAfter final human review, connect either Google Docs or Microsoft Word/SharePoint publishing. Do not connect an email/submission node directly to this workflow; proposal delivery must remain a separate authorized action.",
        "width": 850,
        "height": 270,
        "color": 6
      }
    },
    {
      "id": "eaf9f726f9fa899b",
      "name": "Google Docs Create - Disconnected",
      "type": "n8n-nodes-base.googleDocs",
      "typeVersion": 2,
      "position": [
        -330,
        760
      ],
      "parameters": {
        "resource": "document",
        "operation": "create",
        "title": "={{ $json.project_id + ' response package' }}",
        "options": {}
      }
    },
    {
      "id": "cc96a28ace7cf391",
      "name": "SharePoint Upload - Disconnected Adapter",
      "type": "n8n-nodes-base.microsoftSharePoint",
      "typeVersion": 1,
      "position": [
        20,
        760
      ],
      "parameters": {
        "resource": "file",
        "operation": "upload",
        "options": {}
      }
    }
  ],
  "connections": {
    "Manual Assembly Test": {
      "main": [
        [
          {
            "node": "Load Assembly Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approved Project Assembly Webhook": {
      "main": [
        [
          {
            "node": "Load Assembly Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Assembly Request": {
      "main": [
        [
          {
            "node": "Read Requirement Matrix",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Review Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Review Queue": {
      "main": [
        [
          {
            "node": "Assemble Approved Proposal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assemble Approved Proposal": {
      "main": [
        [
          {
            "node": "Append Assembly Audit",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Owner Assembly Summary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Return Proposal Package",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Africa/Lagos",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "executionTimeout": 600
  }
}
