# NGauge Delivery – Palletline

Yii2 module integrating the [Palletline](https://www.palletline.com) pallet network REST API into the NGauge platform.

## Requirements

- PHP 8.0+
- Yii2 ~2.0
- yii2-bootstrap4 ^2.0
- guzzlehttp/guzzle ^7.0
- A Palletline API key (contact your Palletline account manager)

## Installation

Add the module via Composer:

```bash
composer require portalpeople/ngauge-delivery-palletline
```

Register the module in your application config:

```php
// common/config/main.php
'modules' => [
    'palletline' => [
        'class' => \PortalPeople\palletline\Module::class,
    ],
],
```

Bootstrap the module:

```php
// common/config/main.php
'bootstrap' => ['palletline'],
```

## Configuration

Navigate to `/palletline/config` in your application to configure:

| Setting       | Description                                                         |
|---------------|---------------------------------------------------------------------|
| Enabled       | Enable or disable the module                                        |
| API URL       | Default: `https://api.palletline.com`                               |
| API Key       | Your Palletline API key (stored encrypted)                          |
| Auto-Confirm  | If enabled, consignments are auto-confirmed to the depot on submit. If disabled, they are held on the customer portal for manual review. **Cannot be undone once confirmed.** |
| Timeout       | HTTP request timeout in seconds (default: 30)                       |
| Debug         | Log all API requests/responses to Yii application log               |

Alternatively, configure directly in `params.php`:

```php
// common/config/params.php
'plugins.palletline' => [
    'enabled'     => true,
    'apiUrl'      => 'https://api.palletline.com',
    'apiKey'      => '<encrypted-value>',
    'autoConfirm' => false,
    'timeout'     => 30,
    'debug'       => false,
],
```

## Usage

### Uploading a rate card

Authenticated users can POST a PDF rate card to `/palletline/rate-card/upload`
using the multipart field `rateFile`. The endpoint extracts fixed-layout PDF
text, parses structured Markdown tables when present, and returns a preview
with `validRows` and `reviewRows`. Persistence is disabled by default; include
`confirmImport=1` in the same POST only after reviewing the response. Rows that
cannot be reconstructed are marked `requires_review` and are never imported.

### Creating a consignment

```php
$palletline = Yii::$app->getModule('palletline');

$form = new \PortalPeople\palletline\models\ConsignmentForm();
$form->consignmentNumber     = 'MY-ORDER-001';
$form->consignmentType       = 'D';
$form->lifts                 = 1;
$form->weight                = 450;
$form->billUnitType          = 'HP';
$form->billUnitAmount        = 1;
$form->serviceCode           = 'A';
$form->deliveryContactName   = 'John Smith';
$form->deliveryTelephone     = '01234 567890';
$form->deliveryCompanyName   = 'ACME Ltd';
$form->deliveryLine          = '10 High Street';
$form->deliveryTown          = 'Bristol';
$form->deliveryCounty        = 'Bristol';
$form->deliveryPostCode      = 'BS1 1AA';
$form->deliveryCountry       = 'GB';

$manifest = $form->getManifest($palletline->autoConfirm);
$result   = Yii::$app->palletlineClient->createConsignment($manifest, true);

$responseId = $result['Response']['Detail']['ImportDetail']['ResponseID'] ?? null;
```

### Getting labels

```php
$pdf = Yii::$app->palletlineClient->getLabelsById($responseId);
// stream to browser or save to disk
```

### Checking consignment status

```php
$status = Yii::$app->palletlineClient->getConStatusById($responseId);
```

## API Concepts

### `commit` parameter

- `commit=false` — validates the consignment data only, no data is stored. Useful for pre-submission validation.
- `commit=true` — stores the consignment. Combined with `Confirm`:
  - `Confirm=no` — consignment is held on the customer portal at [portal.palletline.com](https://portal.palletline.com) and must be manually manifested.
  - `Confirm=yes` — consignment is **immediately and irrevocably** transmitted to the depot system.

### Pallet types

| Code | Description          | Dimensions (L×W×H)    | Max Weight |
|------|----------------------|-----------------------|------------|
| FP   | Full Pallet          | 1.2×1.2×2.2m          | 1200 kg    |
| LP   | Light Pallet         | 1.2×1.2×2.2m          | 750 kg     |
| HP   | Half Pallet          | 1.2×1.2×1.1m          | 500 kg     |
| QP   | Quarter Pallet       | 1.2×1.2×0.8m          | 250 kg     |
| MQP  | Mini Quarter Pallet  | 1.2×1.2×0.6m          | 150 kg     |
| EPL  | Euro Pallet          | 0.8×1.2×2.2m          | 500 kg     |
| XLP  | XL Pallet            | 1.6×1.2×2.2m          | 1200 kg    |

### Notification System Groups

| Code | Event                                      |
|------|--------------------------------------------|
| 1    | Scanned out of sortation facility          |
| 2    | Scanned in to sortation facility           |
| 3    | Consignment out for delivery               |
| 4    | Delivery completed (POD available)         |
| 5    | ETA details (also triggers SMS)            |
| 6    | Image available (POD with signature)       |
| 7    | Scheduled AM delivery                      |
| 8    | Scheduled PM delivery                      |

## Available Endpoints (PalletlineClient)

| Method                                       | API Endpoint                              |
|----------------------------------------------|-------------------------------------------|
| `createConsignment(array, bool)`             | POST `/createconsignment`                 |
| `getConStatusById(string)`                   | GET `/constatusbyid/{id}`                 |
| `getConStatusByConNo(string)`                | GET `/constatusbyconno/{conNo}`           |
| `getConStatusByTrackingId(string)`           | GET `/constatusbytrackingId/{id}`         |
| `getConsEntered(string $date)`               | GET `/consEntered/{date}`                 |
| `getConsignment(string $responseId)`         | GET `/getconsignment/{id}`                |
| `manifestConsignment(string $responseId)`    | GET `/manifestconsignment/{id}`           |
| `deleteConsignment(string $responseId)`      | GET `/deletepcconsignment/{id}`           |
| `getTrackingNotesByConNo(string)`            | GET `/gettrackingnotesbyconno/{conNo}`    |
| `getTrackingNotes(string $responseId)`       | GET `/gettrackingnotes/{id}`              |
| `getAvailableServices(...)`                  | GET `/availableservices/{type}/...`       |
| `getLabelsById(string $responseId)`          | GET `/GetLabelsById/{id}` (PDF)           |
| `getLabelsByConNo(string $conNo)`            | GET `/GetLabelsByConNo/{conNo}` (PDF)     |
| `getPodByTrackingId(string)`                 | GET `/getpodbytrackingid/{id}` (PDF)      |
| `testConnection()`                           | GET `/availableservices/D/GB/WS138NE/GB/BS37` |

## Changelog

See [CHANGELOG.md](CHANGELOG.md).
