# Description Building

This document covers the `inventory:generate-descriptions` command — a one-off tool to generate product descriptions for the ~11,754 Somerset4x4 products that have no description, using data already held in the PSP and fitment tables.

---

## Background

When product data was imported from Allmakes, some products received a description (the marketing intro sentence + fitment table). Others were imported with no description at all. The `inventory:clean-descriptions` command stripped the fitment table from the ones that had it. This command fills in the gap for the products that have nothing.

The goal is to generate consistent descriptions that match the style of the existing ones so the site can launch, with the intention to review and improve them later.

---

## Data sources

The description is assembled from three sources already in the database:

| Source | Table | Used for |
|--------|-------|---------|
| Product type (part name) | `allmakes_psp_results.description` | Primary — e.g. `LAMP - SIDE REPEATER - FRONT` |
| Quality grade | `allmakes_psp_results.psp_quality_grade` | OEM vs Replacement phrasing |
| Vehicle list | `allmakes_psp_fitments` | "Fits DEFENDER 1986 - 2006 and FREELANDER 1." |
| Vehicle list (fallback) | `products_products.vehicle_fitment` (JSON) | Used when no fitments table rows exist |
| Product type (fallback) | `products_products.name` | Used when no PSP result exists |

### Coverage at time of writing (company_id = 2)

| Situation | Count |
|-----------|-------|
| Products needing a description | 11,754 |
| Have PSP result (clean product type available) | 328 |
| Have fitment table rows (vehicles available) | 7,981 |
| Have fitments but no PSP result | 7,840 |
| Have neither — rely on product name only | ~3,566 |

---

## Description template

```
This {PRODUCT_TYPE} has been available for sale as {QUALITY} product from Allmakes 4x4
for over 10 years and has had ZERO warranty cases in the last 12 months. Fits {VEHICLES}.
```

**Note on "over 10 years":** The actual years-available figure was scraped from the Allmakes PSP page but is not stored in the database. "Over 10 years" is used as a consistent placeholder. It can be refined in a future pass if the data is re-scraped.

---

## How each part is built

### Product type

1. **PSP result exists** → use `allmakes_psp_results.description`, then strip any trailing vehicle code suffix.

   The PSP description often appends an abbreviated vehicle list at the end, e.g.:
   - `"LAMP - SIDE REPEATER - FRONT - D2/DEF ALL/F1"` → strip → `"LAMP - SIDE REPEATER - FRONT"`
   - `"HOLDER ASSY - BULB - F1"` → strip → `"HOLDER ASSY - BULB"`
   - `"PUMP - WATER"` → no suffix → keep as-is

   Stripping rule: if the last ` - ` segment contains `/` (multiple vehicle codes) or is ≤ 4 uppercase characters (e.g. `F1`, `D2`), it is treated as a vehicle suffix and removed.

2. **No PSP result** → use `products_products.name` directly. Most names without PSP data are plain part names like `"NUT - HEX"` or `"SWITCH"`. The same suffix-stripping rule is applied.

3. **Neither produces usable text** → product is skipped and counted in the summary.

### Quality phrase

| `psp_quality_grade` value | Phrase used |
|---------------------------|-------------|
| `O` | `an Original Equipment Manufacturer` |
| `G` | `a Genuine` |
| `null`, `R`, `TF`, `RTF`, anything else | `a Replacement` |

Products without a PSP result default to `a Replacement`.

### Vehicles list

**Source priority:**
1. `allmakes_psp_fitments` — distinct `vehiclename` values linked via `products_product_suppliers.product_code`
2. `products_products.vehicle_fitment` JSON column — extract distinct `vehicle` keys as fallback

**Formatting:**
- 1 vehicle: `Fits DEFENDER 1986 - 2006.`
- 2 vehicles: `Fits DEFENDER 1986 - 2006 and FREELANDER 1.`
- 3+ vehicles: `Fits DEFENDER 1986 - 2006, DEFENDER 2007 > and FREELANDER 1.`
- No vehicles: the "Fits …" sentence is omitted entirely.

---

## Command

**File:** `app/Console/Commands/GenerateDescriptionsCommand.php`

**Usage:**

```bash
# Always dry-run first — shows the first 20 generated descriptions without saving
php artisan inventory:generate-descriptions --dry-run

# Spot-check a small batch to review output quality before running everything
php artisan inventory:generate-descriptions --dry-run --limit=50

# Run for real (defaults to company_id = 2)
php artisan inventory:generate-descriptions

# Different company
php artisan inventory:generate-descriptions --company=3
```

**Options:**

| Option | Default | Description |
|--------|---------|-------------|
| `--company` | `2` | The `company_id` to process |
| `--dry-run` | off | Preview mode — prints samples, writes nothing |
| `--limit` | none | Cap to N products (useful for spot-checks) |

**Safe to re-run:** Yes — only processes products where `description` is NULL, empty, or `<p></p>`. Never overwrites an existing description.

---

## Example output

```
This LAMP - SIDE REPEATER - FRONT has been available for sale as a Replacement
product from Allmakes 4x4 for over 10 years and has had ZERO warranty cases in
the last 12 months. Fits DEFENDER 1986 - 2006, DEFENDER 2007 >, DISCOVERY 2
1998 - 2004 and FREELANDER 1.
```

---

## Run order

These two description commands should be run in this order on a fresh database:

1. `php artisan inventory:clean-descriptions` — strip fitment tables from products that already have a description
2. `php artisan inventory:generate-descriptions` — build descriptions for products that have none
  
Both are safe to re-run and skip already-processed products.

---

## Future improvements

- Re-scrape Allmakes PSP to capture actual "years available" per product and replace the "over 10 years" placeholder
- Review and manually improve descriptions for high-value products (Terrafirma, OEM lines)
- Consider adding product-specific selling points from the PSP quality notes fields
