{
  "info": {
    "_postman_id": "payroll-maintenance-e2e-aya",
    "name": "E2E - Payroll + Maintenance aya ",
    "description": "End-to-end Postman collection for the user stories: create payroll run, approve & pay payroll, attendance filters, schedule/execute unscheduled maintenance visit. Uses {{base_url}} and saves {{auth_token}} automatically.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      { "key": "token", "value": "{{auth_token}}", "type": "string" }
    ]
  },
  "variable": [
    { "key": "base_url", "value": "http://localhost:8000/api/v1", "type": "string" },
    { "key": "auth_token", "value": "", "type": "string" },
    { "key": "payroll_run_id", "value": "", "type": "string" },
    { "key": "slip_ids", "value": "[]", "type": "string" },
    { "key": "first_slip_id", "value": "", "type": "string" },
    { "key": "payment_account_id", "value": "162", "type": "string" },
    { "key": "visit_id", "value": "", "type": "string" },
    { "key": "start_date", "value": "2026-02-01", "type": "string" },
    { "key": "end_date", "value": "2026-02-19", "type": "string" }
  ],
  "item": [
    {
      "name": "Auth",
      "item": [
        {
          "name": "Login (save auth_token)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Accept", "value": "application/json" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"phone_or_email\": \"admin@admin.com\",\n  \"password\": \"admin\"\n}" },
            "url": { "raw": "{{base_url}}/auth/login", "host": ["{{base_url}}"], "path": ["auth", "login"] },
            "description": "Login and store token to collection variable `auth_token`. Use admin/admin for local dev."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test('login - status 200', function () { pm.response.to.have.status(200); });",
                  "var json = pm.response.json();",
                  "var token = json?.data?.token || json?.token || json?.access_token || null;",
                  "if (!token) { pm.test('token present', function() { pm.expect(token, 'token').to.not.be.null; }); } else { pm.collectionVariables.set('auth_token', token); pm.test('auth_token saved', function(){ pm.expect(pm.collectionVariables.get('auth_token')).to.be.a('string'); }); }"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Payroll - E2E",
      "item": [
        {
          "name": "Create Payroll Run",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Accept", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"company_id\": 1,\n  \"department_id\": 1,\n  \"period_from\": \"2026-02-01\",\n  \"period_to\": \"2026-02-28\",\n  \"employee_ids\": [],\n  \"project_ids\": [],\n  \"components\": { \"basic\": true, \"allowances\": true, \"deductions\": true, \"overtime\": true, \"tax\": true },\n  \"frequency\": \"monthly\",\n  \"notes\": \"E2E test payroll run\"\n}"
            },
            "url": { "raw": "{{base_url}}/payroll/runs", "host": ["{{base_url}}"], "path": ["payroll", "runs"] }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test('create payroll - 200', function () { pm.response.to.have.status(200); });",
                  "var json = pm.response.json();",
                  "pm.test('has payroll id', function(){ pm.expect(json.data.id).to.be.a('number'); });",
                  "pm.collectionVariables.set('payroll_run_id', json.data.id + '');",
                  "// save salary slip ids if present",
                  "var slips = json.data.salary_slips || json.data.salary_slips?.data || [];",
                  "var ids = Array.isArray(slips) ? slips.map(s => s.id).filter(Boolean) : [];",
                  "pm.collectionVariables.set('slip_ids', JSON.stringify(ids));",
                  "if (ids.length > 0) { pm.collectionVariables.set('first_slip_id', ids[0]+''); }",
                  "pm.test('salary slips saved (if any)', function() { pm.expect(pm.collectionVariables.get('slip_ids')).to.be.a('string'); });"
                ]
              }
            }
          ]
        },
        {
          "name": "Validate Payroll Run",
          "request": {
            "method": "POST",
            "header": [ { "key": "Accept", "value": "application/json" } ],
            "url": { "raw": "{{base_url}}/payroll/runs/{{payroll_run_id}}/validate", "host": ["{{base_url}}"], "path": ["payroll", "runs", "{{payroll_run_id}}", "validate"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('validate - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        },
        {
          "name": "Submit Payroll Run",
          "request": {
            "method": "POST",
            "header": [ { "key": "Accept", "value": "application/json" } ],
            "url": { "raw": "{{base_url}}/payroll/runs/{{payroll_run_id}}/submit", "host": ["{{base_url}}"], "path": ["payroll", "runs", "{{payroll_run_id}}", "submit"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('submit - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        },
        {
          "name": "Approve Payroll Run",
          "request": {
            "method": "POST",
            "header": [ { "key": "Accept", "value": "application/json" } ],
            "url": { "raw": "{{base_url}}/payroll/runs/{{payroll_run_id}}/approve", "host": ["{{base_url}}"], "path": ["payroll", "runs", "{{payroll_run_id}}", "approve"] }
          },
          "event": [
            {
              "listen": "test",
              "script": { "exec": [
                "pm.test('approve - 200', function(){ pm.response.to.have.status(200); });",
                "var json = pm.response.json();",
                "pm.test('status == approved', function(){ pm.expect(json.data.status).to.eql('approved'); });"
              ] }
            }
          ]
        },
        {
          "name": "Get Salary Slips (for run)",
          "request": {
            "method": "GET",
            "header": [ { "key": "Accept", "value": "application/json" } ],
            "url": { "raw": "{{base_url}}/payroll/runs/{{payroll_run_id}}/salary-slips?page=1&per_page=20", "host": ["{{base_url}}"], "path": ["payroll", "runs", "{{payroll_run_id}}", "salary-slips"], "query": [ { "key": "page", "value": "1" }, { "key": "per_page", "value": "20" } ] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('get salary slips - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        },
        {
          "name": "Process Entire Payroll Run (pay)",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ],
            "body": { "mode": "raw", "raw": "{\n  \"payment_method\": \"bank_transfer\",\n  \"payment_account_id\": {{payment_account_id}},\n  \"notes\": \"E2E payroll payment\"\n}" },
            "url": { "raw": "{{base_url}}/payroll/payments/process-payroll/{{payroll_run_id}}", "host": ["{{base_url}}"], "path": ["payroll", "payments", "process-payroll", "{{payroll_run_id}}"] }
          },
          "event": [
            {
              "listen": "test",
              "script": { "exec": [
                "pm.test('process payroll run - 200', function(){ pm.response.to.have.status(200); });",
                "var json = pm.response.json();",
                "pm.test('payment record returned', function(){ pm.expect(json.data.id).to.be.a('number'); });",
                "pm.collectionVariables.set('last_payment_id', json.data.id + '');"
              ] }
            }
          ]
        },
        {
          "name": "Process Specific Salary Slips (bulk)",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ],
            "body": { "mode": "raw", "raw": "{\n  \"slip_ids\": {{slip_ids}},\n  \"payment_method\": \"bank_transfer\",\n  \"payment_account_id\": {{payment_account_id}},\n  \"notes\": \"E2E partial slips payment\"\n}" },
            "url": { "raw": "{{base_url}}/payroll/payments/process-slips", "host": ["{{base_url}}"], "path": ["payroll", "payments", "process-slips"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('process slips - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        }
      ]
    },
    {
      "name": "Attendance",
      "item": [
        {
          "name": "Get Attendances Between Two Dates (filters)",
          "request": {
            "method": "GET",
            "header": [ { "key": "Accept", "value": "application/json" } ],
            "url": { "raw": "{{base_url}}/attendances/between-two-dates?start_date={{start_date}}&end_date={{end_date}}&per_page=20", "host": ["{{base_url}}"], "path": ["attendances", "between-two-dates"], "query": [ { "key": "start_date", "value": "{{start_date}}" }, { "key": "end_date", "value": "{{end_date}}" }, { "key": "per_page", "value": "20" } ] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('attendance filters - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        }
      ]
    },
    {
      "name": "Maintenance - Handover Visit E2E",
      "item": [
        {
          "name": "Create Handover Visit",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ],
            "body": { "mode": "raw", "raw": "{\n  \"project_id\": 1,\n  \"planned_date\": \"2026-03-01\",\n  \"notes\": \"E2E test handover visit\",\n  \"asset_ids\": [1]\n}" },
            "url": { "raw": "{{base_url}}/maintenance/handover-visits", "host": ["{{base_url}}"], "path": ["maintenance", "handover-visits"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('create handover - 200', function(){ pm.response.to.have.status(200); });", "var json = pm.response.json(); pm.collectionVariables.set('visit_id', json.data.id + ''); pm.test('visit id saved', function(){ pm.expect(pm.collectionVariables.get('visit_id')).to.be.a('string'); });" ] } } ]
        },
        {
          "name": "Schedule Handover Visit",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ],
            "body": { "mode": "raw", "raw": "{\n  \"scheduled_at\": \"2026-03-05 09:00:00\"\n}" },
            "url": { "raw": "{{base_url}}/maintenance/handover-visits/{{visit_id}}/schedule", "host": ["{{base_url}}"], "path": ["maintenance", "handover-visits", "{{visit_id}}", "schedule"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('schedule - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        },
        {
          "name": "Complete Handover Visit",
          "request": {
            "method": "POST",
            "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ],
            "body": { "mode": "raw", "raw": "{\n  \"received_assets\": [ { \"asset_id\": 1, \"condition\": \"good\", \"notes\": \"ok\" } ]\n}" },
            "url": { "raw": "{{base_url}}/maintenance/handover-visits/{{visit_id}}/complete", "host": ["{{base_url}}"], "path": ["maintenance", "handover-visits", "{{visit_id}}", "complete"] }
          },
          "event": [ { "listen": "test", "script": { "exec": [ "pm.test('complete - 200', function(){ pm.response.to.have.status(200); });" ] } } ]
        }
      ]
    }
  ]
}
