# Procurement Module

The Procurement module coordinates purchasing from request through vendor payment. It exposes REST endpoints under `/api` and stores data in several tables.

## Database Schema

### procurement_requests
- `id` INT PRIMARY KEY
- `description` TEXT
- `justification` TEXT
- `estimated_cost` DECIMAL
- `quantity` INT
- `preferred_vendor` VARCHAR(100)
- `needed_by` DATE
- `project_id` INT nullable
- `inventory_item_id` INT nullable
 - `location_id` INT nullable
- `status` VARCHAR DEFAULT `Pending`
- `requester_id` INT
- `created_at` DATETIME

### rfqs
- `id` INT PRIMARY KEY
- `procurement_request_id` INT
- `vendor_id` INT
- `status` VARCHAR DEFAULT `pending`
- `sent_at` DATETIME
- `due_at` DATETIME
- `created_at` DATETIME

### purchase_orders
- `id` INT PRIMARY KEY
- `rfq_id` INT nullable
- `vendor_id` INT
- `order_date` DATE
- `project_id` INT nullable
- `inventory_item_id` INT nullable
- `status` VARCHAR DEFAULT `Open`
- `total_amount` DECIMAL
- `created_at` DATETIME

### goods_receipts
- `id` INT PRIMARY KEY
- `purchase_order_id` INT
- `received_date` DATE
- `quantity_received` INT
- `notes` TEXT
- `created_at` DATETIME

### vendor_invoices
- `id` INT PRIMARY KEY
- `purchase_order_id` INT
- `invoice_number` VARCHAR
- `invoice_date` DATE
- `amount` DECIMAL
- `status` VARCHAR DEFAULT `unpaid`
- `created_at` DATETIME

Both procurement requests and purchase orders include optional `project_id` and `inventory_item_id` fields. When `inventory_item_id` and `quantity` are present the controllers check `consumables.currentQuantity` to skip external purchasing if stock is available.

## API Endpoints

Standard CRUD actions follow `/api/{resource}`. Additional actions allow approvals and closure.

### Procurement Requests
```
GET    /api/procurement_requests            # list
POST   /api/procurement_requests            # create
GET    /api/procurement_requests/{id}       # view
PUT    /api/procurement_requests/{id}       # update
DELETE /api/procurement_requests/{id}       # delete
```

### RFQs
```
GET    /api/rfqs                            # list
POST   /api/rfqs                            # create
POST   /api/rfqs/approve/{id}               # approve
POST   /api/rfqs/close/{id}                 # close
```

### Purchase Orders
```
GET    /api/purchase_orders                 # list
POST   /api/purchase_orders                 # create
POST   /api/purchase_orders/approve/{id}    # approve
POST   /api/purchase_orders/close/{id}      # close
```

### Goods Receipts
```
GET    /api/goods_receipts                  # list
POST   /api/goods_receipts                  # create
POST   /api/goods_receipts/approve/{id}     # approve
POST   /api/goods_receipts/close/{id}       # close
```

### Vendor Invoices
```
GET    /api/vendor_invoices                 # list
POST   /api/vendor_invoices                 # create
POST   /api/vendor_invoices/approve/{id}    # approve
POST   /api/vendor_invoices/close/{id}      # close
```

## Sample Workflow

1. **Issue RFQ** – create an RFQ referencing the procurement request.
2. **Approve Purchase Order** – once pricing is accepted, approve the PO.
3. **Record Goods Receipt** – log delivered items against the PO.
4. **Process Invoice** – capture the vendor invoice for payment.

### Example: RFQ Issuance
```bash
# POST /api/rfqs
curl -X POST -b cookie.txt \
  -H 'Content-Type: application/json' \
  -d '{"procurement_request_id":1,"vendor_id":5}' \
  http://localhost/api/rfqs
```

### Example: PO Approval
```bash
# POST /api/purchase_orders/approve/10
curl -X POST -b cookie.txt \
  http://localhost/api/purchase_orders/approve/10
```

### Example: Goods Receipt
```bash
# POST /api/goods_receipts
curl -X POST -b cookie.txt \
  -H 'Content-Type: application/json' \
  -d '{"purchase_order_id":10,"quantity_received":5}' \
  http://localhost/api/goods_receipts
```

### Example: Invoice Processing
```bash
# POST /api/vendor_invoices
curl -X POST -b cookie.txt \
  -H 'Content-Type: application/json' \
  -d '{"purchase_order_id":10,"invoice_number":"INV-99","amount":2500}' \
http://localhost/api/vendor_invoices
```

## Two-envelope technical & financial template

Use the `Two-Envelope Technical & Financial` RFQ template (code `RBF_TECH_FIN_ENVELOPE`) when you need vendors to lodge a separate technical proposal and commercial/financial proposal. The seed data provisions the template automatically; you can manage it under **Procurement ▸ RFQ Templates**.

- **Submission workflow** – vendors upload two files when responding: a PDF (or DOCX) with solution details and a spreadsheet/PDF covering pricing only. Any pricing found in the technical file can trigger disqualification.
- **Evaluation guidance** – the template notes a default 70/30 weighting (technical/commercial). Adjust the weights in the compare tab after the tender box is opened, or edit the RFQ header’s scoring config via the UI/API.
- **Clarifications** – the template’s timeline section reminds buyers to state deadlines and contact details; update these before publishing the RFQ.

When creating an RFQ from a template (`Create from Template` button in the RFQ list), select this entry to pull in the two-envelope instructions automatically.

## Workflow Integration

Modules can initiate purchasing automatically using generic workflow steps. The
typical sequence starts with `query_budget` to load available funds, then a
`condition` step compares `budget_amount` to `estimated_cost`. If the condition
passes a `save_record` step inserts the row into `procurement_requests`.

Example snippet:

```json
[
  { "type": "query_budget", "category": "{category}" },
  {
    "type": "condition",
    "conditions": [
      { "field": "budget_amount", "operator": ">=", "value": "{estimated_cost}" }
    ]
  },
  {
    "type": "save_record",
    "table": "procurement_requests",
    "data": {
      "description": "Parts for {vehicle}",
      "estimated_cost": "{estimated_cost}",
      "quantity": "{quantity}",
      "inventory_item_id": "{inventory_item_id}"
    }
  }
]
```

A sample notification workflow named `procurement_request_notification_workflow.json`
listens for the `procurement.requestCreated` event. When triggered it sends an
email to the addresses stored under the `fleet_issue_notify` setting. Import the
JSON via `/api/workflows` and activate it for your tenants to alert the fleet
team whenever a new request is created.

## Visibility Settings

Three settings control who can see procurement requests:

- `procurement.view_all_roles` – comma separated role names that may view every request.
- `procurement.branch_manager_roles` – roles limited to their assigned locations.
- `procurement.branch_manager_view` – determines the field (`location` or `branch`) used when filtering for branch managers.

Update these via the Settings module to adjust access.

## Running API Tests

Run the setup script followed by the procurement test suite. Specify the driver through `DB_DRIVER`:

```bash
DB_DRIVER=oci ../test_setup.sh
phpunit -c api/phpunit.xml
jest
```

Execute these tests only when procurement code changes unless a full run is required.

