# Parámetros de correo — Warehouse (`MailerinfoController::changeCodes`)

Referencia para refactorización de plantillas de email de Warehouse Receipt.

**Fuente:** `src/NvCarga/Bundle/Controller/MailerinfoController.php`
**Método:** `changeCodes($string, $entity, $type, $aux = null)`
**Servicio:** `nvcarga.mailerservices` → delega en este controlador

---

## 1. Modos de uso para Warehouse


| `$type`         | `$entity`               | `$aux`  | Uso típico                                                          |
| --------------- | ----------------------- | ------- | ------------------------------------------------------------------- |
| `'WHrec'`       | `WHrec`                 | —       | Creación, notificaciones, finalización (`sendwhrec` con tipo WHrec) |
| `'Statuswhrec'` | `Statuswhrec` (estatus) | `WHrec` | Cambio de estatus (`StatusController`)                              |


### Contexto inicial (`$type = 'WHrec'`)


| Variable                      | Origen                                    |
| ----------------------------- | ----------------------------------------- |
| `$shipper`                    | `$entity->getShipper()`                   |
| `$receiver`                   | `$entity->getReceiver()`                  |
| `$listrec`                    | `$entity->getReceipts()` (paquetes hijos) |
| `$customer`                   | `$shipper`                                |
| `$thisnumber`                 | `$entity->getNumber()`                    |
| `$linksign` / `$linkshiptype` | `'whrec'`                                 |
| `$agency`                     | `$entity->getAgency()`                    |
| `$agencydes`                  | `$entity->getAgencydes()` si existe       |
| `$maincompany`                | `$entity->getMaincompany()`               |
| `$first`                      | mismo `$entity` (WHrec)                   |


### Contexto inicial (`$type = 'Statuswhrec'`)


| Variable                 | Origen                                         |
| ------------------------ | ---------------------------------------------- |
| `$first`                 | `$aux` (el **WHrec**)                          |
| `$entity`                | registro de **estatus**                        |
| `$listrec`               | `getReceipts()` + `getOldreceipts()` del WHrec |
| `$shipper` / `$receiver` | del WHrec en `$aux`                            |
| `$thisnumber`            | número del WHrec                               |


**Medidas de conversión:** entidades `Measure` con `name = 'Lb'` y `name = 'CF'` de la empresa (para `[TOTALPESO]` y `[TOTALVOLUMEN]`).

**Si un tag no se reemplaza:** queda literal `[TAG]` en el HTML (no hay valor predeterminado global).

**Salida:** el método aplica `nl2br()` al string final.

---

## 2. Tags oficiales en UI (`getMailtags()`)

Lista que muestra la interfaz al editar `Bodyemail` con `tableclass = WHrec` o `Statuswhrec`.

### `WHrec` — creación / notificaciones


| Tag                         | Descripción UI            | Origen del valor                                            |
| --------------------------- | ------------------------- | ----------------------------------------------------------- |
| `[NOMBREEMPRESA]`           | Nombre Empresa            | `maincompany.getName()`                                     |
| `[NUMERO]`                  | Número de Warehouse       | `whrec.getNumber()`                                         |
| `[DESTINATARIO]`            | Nombre Destinatario       | `receiver.getComplete()`                                    |
| `[REMITENTE]`               | Nombre Remitente          | `shipper.name + lastname`                                   |
| `[FECHACREACION]`           | Fecha de Creación         | `whrec.getCreationdate()` con `maincompany.getFormatdate()` |
| `[DETALLES]`                | Detalles de Paquetes      | Tabla HTML por cada `Receipt` en `$listrec`                 |
| `[TOTALPIEZAS]`             | Total de Piezas           | Suma de `receipt.getNpack()` en `$listrec`                  |
| `[TOTALPESO]`               | Peso Total                | Suma de pesos de receipts con conversión Lb/Kg              |
| `[TOTALVOLUMEN]`            | Volumen Total             | Suma de volúmenes de receipts con conversión CF/Mt3         |
| `[TIPOENVIO]`               | Tipo de Envío             | `translator(trans(whrec.getInstruction()))`                 |
| `[NOTA]`                    | Nota                      | `whrec.getNote()`                                           |
| `[AUXILIAR]`                | Auxiliar                  | `whrec.getAux()`                                            |
| `[AGENTE]`                  | Agente                    | `whrec.getAgent().getName()` o `<br>`                       |
| `[NOMBREAGENCIA]`           | Nombre Agencia            | `agency.getName()`                                          |
| `[DIRECCIONAGENCIA]`        | Dirección Agencia         | `agency.getAddress()`                                       |
| `[EMAILAGENCIA]`            | Email Agencia             | `agency.getEmail()`                                         |
| `[TELEFONOAGENCIA]`         | Teléfono Agencia          | `agency.getPhone()`                                         |
| `[LINKCASILLERO]`           | Enlace para Ingresar      | `<a href="https://{homepage}/">`                            |
| `[LINKTRACKING]`            | Enlace para Tracking      | `<a href="https://{homepage}/tracking/tracking">`           |
| `[LINKEMPRESA]`             | Enlace Empresa            | `<a href="http://{maincompany.getUrl()}/">`                 |
| `[ENLACEFIRMA]`             | Enlace firmar declaración | Si plan firma y sin `sigimage`: link casillero              |
| `[ENLACEPUBLICOFIRMA]`      | Enlace público firma      | URL `whrec/public/{id}/{pubtoken}`                          |
| `[ENLACEPUBLICOTIPOENVIO]`  | Enlace público tipo envío | URL `whrec/public/instruction/{id}/{pubtoken}`              |
| `[FECHAAUX]`                | Fecha auxiliar (config)   | `whrec.getAuxdate()` o `N/A`                                |
| `[NOMBREAGENCIADESTINO]`    | Agencia destino (config)  | `agencydes.getName()` o `N/A`                               |
| `[DIRECCIONAGENCIADESTINO]` | Dirección agencia destino | `agencydes.getAddress()` o `N/A`                            |
| `[EMAILAGENCIADESTINO]`     | Email agencia destino     | `agencydes.getEmail()` o `N/A`                              |
| `[TELEFONOAGENCIADESTINO]`  | Teléfono agencia destino  | `agencydes.getPhone()` o `N/A`                              |


Condicionales en UI:

- `[ENLACEFIRMA]` / `[ENLACEPUBLICOFIRMA]`: solo si `maincompany.plan.signvalue`
- `[FECHAAUX]`: solo si `configwhrec.auxdate`
- Tags agencia destino: solo si `configwhrec.agencydes`

### `Statuswhrec` — cambio de estatus

Incluye la mayoría de tags de WHrec, más:


| Tag                  | Descripción UI         | Origen del valor                               |
| -------------------- | ---------------------- | ---------------------------------------------- |
| `[FECHASTATUS]`      | Fecha del Estatus      | `status.getDate()` con fecha + hora            |
| `[STATUS]`           | Nombre del Estatus     | `status.step.name` (o string del step)         |
| `[CIUDADSTATUS]`     | Ciudad del Estatus     | `status.place` → ciudad, estado, país; o `N/A` |
| `[COMENTARIOSTATUS]` | Comentario del Estatus | `status.comment` o `N/A`                       |


`[TIPOENVIO]` en status: `translator(whrec.getInstruction())` vía `status.getWHrec()`.

---

## 3. Tabla completa — resolución por tag


| Tag                         | WHrec | Status | Origen detallado                           |
| --------------------------- | ----- | ------ | ------------------------------------------ |
| `[NOMBREEMPRESA]`           | ✓     | ✓      | `maincompany.getName()`                    |
| `[NUMERO]`                  | ✓     | ✓      | Número del WHrec (`$thisnumber`)           |
| `[DESTINATARIO]`            | ✓     | ✓      | `receiver.getComplete()`                   |
| `[REMITENTE]`               | ✓     | ✓      | `shipper.name + lastname`                  |
| `[FECHACREACION]`           | ✓     | ✓      | `first.getCreationdate()` formateada       |
| `[DETALLES]`                | ✓     | ✓      | HTML por cada receipt en `$listrec`        |
| `[TOTALPIEZAS]`             | ✓*    | ✓*     | Σ `npack`; *solo si `$listrec` no vacío    |
| `[TOTALPESO]`               | ✓*    | ✓*     | Σ peso receipts con conversión de unidades |
| `[TOTALVOLUMEN]`            | ✓*    | ✓*     | Σ volumen receipts con conversión CF/Mt3   |
| `[TIPOENVIO]`               | ✓     | ✓      | WHrec: `getInstruction()` traducido        |
| `[NOTA]`                    | ✓     | —      | `whrec.getNote()`                          |
| `[AUXILIAR]`                | ✓     | —      | `whrec.getAux()`                           |
| `[AGENTE]`                  | ✓     | ✓      | `first.getAgent().getName()` o `<br>`      |
| `[NOMBREAGENCIA]`           | ✓     | ✓      | `agency.getName()`                         |
| `[DIRECCIONAGENCIA]`        | ✓     | ✓      | `agency.getAddress()`                      |
| `[EMAILAGENCIA]`            | ✓     | ✓      | `agency.getEmail()`                        |
| `[TELEFONOAGENCIA]`         | ✓     | ✓      | `agency.getPhone()`                        |
| `[LINKCASILLERO]`           | ✓     | ✓      | Link HTML homepage                         |
| `[LINKTRACKING]`            | ✓     | ✓      | Link HTML tracking                         |
| `[LINKEMPRESA]`             | ✓     | ✓      | Link HTML URL empresa                      |
| `[ENLACEFIRMA]`             | ✓†    | ✓†     | †Plan firma; usa `$entity->getId()`        |
| `[ENLACEPUBLICOFIRMA]`      | ✓†    | ✓†     | URL pública con `pubtoken` del `$entity`   |
| `[ENLACEPUBLICOTIPOENVIO]`  | ✓     | —      | Solo WHrec (`linkshiptype` definido)       |
| `[FECHAAUX]`                | ✓     | —      | `whrec.getAuxdate()` o `N/A`               |
| `[FECHASTATUS]`             | —     | ✓      | `status.getDate()`                         |
| `[STATUS]`                  | —     | ✓      | Nombre del step del status                 |
| `[CIUDADSTATUS]`            | —     | ✓      | Lugar del status                           |
| `[COMENTARIOSTATUS]`        | —     | ✓      | Comentario del status                      |
| `[NOMBREAGENCIADESTINO]`    | ✓‡    | ✓‡     | ‡`agencydes` o `N/A`                       |
| `[DIRECCIONAGENCIADESTINO]` | ✓‡    | ✓‡     | idem                                       |
| `[EMAILAGENCIADESTINO]`     | ✓‡    | ✓‡     | idem                                       |
| `[TELEFONOAGENCIADESTINO]`  | ✓‡    | ✓‡     | idem                                       |
| `[NUMEROCLIENTE]`           | ○     | ○      | `shipper.getNumber()` si hay customer      |
| `[CONSIGNEE]` / `[SHIPPER]` | ○     | ○      | Alias de nombres receiver/shipper          |


**Leyenda:** ✓ = aplica · ✓* = requiere receipts en `$listrec` · ✓† = condicional plan firma · ✓‡ = condicional config agencia destino · ○ = funciona con datos pero no listado en `getMailtags` WHrec · — = no aplica en ese modo

### Tags del `switch` que no aplican a Warehouse

Quedan sin reemplazar si se usan por error: tags de Pickup, Bill, Alert, Pobox, Reempack, consolidado, pagos, etc. (`[NUMEROPICKUP]`, `[TOTALFACTURA]`, `[LINKPAGOSTRIPE]`, `[PAIDINVOICEAMOUNT]`, …).

---

## 4. Detalle: `[DETALLES]`

Por cada `Receipt` en `$listrec` genera bloque HTML con:

- Tracking: `rec.getTracking()`
- Carrier: `rec.getCarrier()` (si `$type != 'Pickup'`)
- Tipo de paquete, descripción, piezas (`npack`)
- Dimensiones: `length x width x height`
- Peso: `rec.getWeight()`
- Volumen: `round(rec.getVolume(), 2)`

Solo reemplaza si `$listrec` tiene al menos un elemento.

---

## 5. Detalle: totales desde paquetes (`$listrec`)

### `[TOTALPIEZAS]`

```
Σ receipt.getNpack()  para cada receipt en $listrec
```

- **WHrec:** solo `getReceipts()`
- **Statuswhrec:** `getReceipts()` + `getOldreceipts()`

### `[TOTALPESO]`

Por cada receipt:


| `unitweight` | Factor (empresa en Lb) | Factor (empresa en Kg) |
| ------------ | ---------------------- | ---------------------- |
| `Lb`         | 1 o 0.453592           | 1 o 2.20462            |
| `Kg`         | según label Measure Lb | según label Measure Lb |
| `Ninguna`    | 1                      | 1                      |


```
totalpeso += receipt.getWeight() * factor
```

### `[TOTALVOLUMEN]`

Por cada receipt:


| `unitvolume`        | Factor (empresa en CF) | Factor (empresa en Mt3) |
| ------------------- | ---------------------- | ----------------------- |
| `CF`                | 1 o 0.0283168          | —                       |
| `Mt3`               | 1 o 35.3147            | según label Measure CF  |
| `Ninguna` / default | 1                      | 1                       |


```
totalvol += receipt.getVolume() * factor
resultado = round(totalvol, 2)
```

**Si `$listrec` está vacío:** `[TOTALPIEZAS]`, `[TOTALPESO]` y `[TOTALVOLUMEN]` **no se sustituyen**.

---

## 6. Enlaces de firma y tipo de envío

### `[ENLACEFIRMA]`

- Requiere `$linksign = 'whrec'`
- Si `plan.signvalue` y el entity no tiene `sigimage`:
  - `https://{SERVER_NAME}/whrec/pobox/{entity.id}/{entity.systoken}`
- Si no: reemplaza por `<br>`

### `[ENLACEPUBLICOFIRMA]`

- Similar con ruta `/whrec/public/{id}/{pubtoken}`

### `[ENLACEPUBLICOTIPOENVIO]`

- Solo WHrec (`$linkshiptype` definido)
- `https://{SERVER_NAME}/whrec/public/instruction/{id}/{pubtoken}`

**Nota:** Con `$type = 'Statuswhrec'`, `$entity` es el **status**; los enlaces usan `entity.getId()` y `entity.getPubtoken()` del status, no del WHrec. Verificar si es comportamiento deseado.

---

## 7. Reemplazos manuales fuera de `changeCodes`

En `WHrecAPIController::sendwhrec()` y `WHrecController` (finalización), **antes** de `changeCodes`:


| Tag                  | Valor manual                              |
| -------------------- | ----------------------------------------- |
| `[STATUS]`           | `"Finalizado"`                            |
| `[FECHASTATUS]`      | `whrec.getDelidate()` o fecha actual      |
| `[CIUDADSTATUS]`     | `whrec.getReceiver().getCity().getName()` |
| `[COMENTARIOSTATUS]` | `$comment` del método                     |


Luego se llama:

```php
$body = $this->get('nvcarga.mailerservices')->changeCodes($body, $whrec, 'WHrec');
```

---

## 8. Invocaciones en el proyecto

```php
// Warehouse directo
changeCodes($body, $whrec, 'WHrec');

// Cambio de estatus
changeCodes($body, $status, 'Statuswhrec', $whrec);
```


| Archivo                   | Contexto                       |
| ------------------------- | ------------------------------ |
| `WHrecController.php`     | Emails creación / finalización |
| `WHrecAPIController.php`  | API — creación, `sendwhrec`    |
| `StatusController.php`    | Email cambio estatus WHrec     |
| `UtilsController.php`     | Utilidades email WHrec         |
| `PickupAPIController.php` | Referencia WHrec en pickup     |


---

## 9. Diferencia con Twilio (WhatsApp)


| Canal        | Tag              | Origen volumen                                              |
| ------------ | ---------------- | ----------------------------------------------------------- |
| **Email**    | `[TOTALVOLUMEN]` | Suma de `Receipt.getVolume()` en `$listrec`                 |
| **WhatsApp** | `[TOTALVOLUME]`  | `WHrec.getVolume()` en `TwilioController::getValueParams()` |


No son intercambiables. Ver también: `docs/twilio-template-parameters.md`.

---

## 10. Notas para refactorización

1. Duplicar lógica de totales: `sendwhrec()` calcula `$peso`/`$volumen` localmente pero **no los usa**; `changeCodes` recalcula desde receipts.
2. `[ENLACEFIRMA]` con `Statuswhrec` puede apuntar al id del status, no del WHrec.
3. `getMailtags()['WHrec']` es subconjunto de lo que el `switch` puede resolver; validar plantillas contra la tabla §3.
4. Factores de conversión en email (WHrecAPI) vs `changeCodes` (Mailerinfo) usan coeficientes ligeramente distintos en peso Lb→Kg (0.453592 vs 0.453592 en Mailerinfo; WHrecAPI usa 0.453592 para Lb case when company is Kg - same). Volume CF: same logic.

---

## 11. Implementación v2 — `WarehouseReceiptEmailService`

**Archivo:** `src/Service/WarehouseReceipt/WarehouseReceiptEmailService.php`

Toda la lógica de reemplazo está centralizada en dos métodos:


| Método                                                                | Usado en                                           |
| --------------------------------------------------------------------- | -------------------------------------------------- |
| `generateContentWhrec(string, WHrec, bool $includeOldReceipts=false)` | WHREC (creación, preview, delivered fallback)      |
| `generateContentWhrecStatus(string, Statuswhrec)`                     | STATUSWHREC (cambio estatus, delivered con status) |


`generateContentWhrecStatus` delega en `generateContentWhrec($html, $whrec, true)` y luego aplica los tags de estatus encima. Esto garantiza que todos los tags WHrec estén disponibles también en los emails de estatus sin duplicar código.

### Mapeos de adaptación v2


| Tag doc / legacy                                      | Comportamiento en v2                                                                                                                                      |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[REMITENTE]`                                         | `Customer::getName() . ' ' . getLastname()` (nombre completo)                                                                                             |
| `[DESTINATARIO]`                                      | `Baddress::getFullName()`                                                                                                                                 |
| `[FECHACREACION]`                                     | `maincompany.getFormatdate()` (antes usaba `receiptBy.getDateFormat()`)                                                                                   |
| `[TIPOENVIO]`                                         | `translator->trans(instruction->value)` (traducido)                                                                                                       |
| `[DETALLES]`                                          | HTML por receipt (tracking, carrier, tipo, descripción, piezas, dimensiones, peso, volumen). Si no hay receipts, el tag se deja sin reemplazar.           |
| `[TOTALPIEZAS]`, `[TOTALPESO]`, `[TOTALVOLUMEN]`      | Solo se reemplazan cuando `totalReceipts > 0`; calculados con `ReceiptRepository::totalsOfWarehouseReceipts`.                                             |
| `[AGENTE]`                                            | `agent.getName()` o `<br>` si no hay agente (igual que legacy)                                                                                            |
| `[ENLACEFIRMA]`                                       | Condicional `plan.signvalue`; si sin firma: link `/whrec/pobox/{id}/{systoken}` usando `WHrec.getSystoken()`. Siempre apunta al WHrec, no al Statuswhrec. |
| `[ENLACEPUBLICOFIRMA]`                                | `/whrec/public/{id}/{pubtoken}` con `WHrec.getPubtoken()`                                                                                                 |
| `[ENLACEPUBLICOTIPOENVIO]`                            | `/whrec/public/instruction/{id}/{pubtoken}` con `WHrec.getPubtoken()`                                                                                     |
| `[FECHAAUX]`                                          | `N/A` — WHrec en v2 no tiene campo `auxdate`; `Configwhrec` tampoco tiene flag `auxdate`.                                                                 |
| `[NOMBREAGENCIADESTINO]` … `[TELEFONOAGENCIADESTINO]` | `whrec.getCurrentagency()` o `N/A` — `agencydes` del sistema legacy no existe como campo separado en v2.                                                  |
| `[NUMEROCLIENTE]`                                     | `shipper.getNumber()`                                                                                                                                     |
| `[CONSIGNEE]` / `[SHIPPER]`                           | Alias de nombre completo de receiver/shipper                                                                                                              |
| `[FECHASTATUS]` (solo Status)                         | Fecha + hora: `formatdate . ' H:i:s'`                                                                                                                     |
| `[CIUDADSTATUS]` (solo Status)                        | `place.getName(), state, country` o `N/A`                                                                                                                 |
| `[COMENTARIOSTATUS]` (solo Status)                    | Comentario del estatus o `N/A`                                                                                                                            |


### Bugs corregidos respecto a la versión anterior


| Bug                            | Descripción                                                                                                                                          |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[TELEFONOAGENCIA]` en Status  | Usaba `getEmail()` en lugar de `getPhone()`.                                                                                                         |
| `[AUXILIAR]` en Status         | Había un espacio de más en el patrón (`[AUXILIAR]` ), causando que el tag nunca se reemplazara.                                                      |
| `[TIPOENVIO]` en Status        | Era sobrescrito con `whrec.getStatus()->value` (el campo `Receiptstatus`, no la instrucción de envío). Ahora se usa siempre `instruction` traducida. |
| `[AGENTE]` duplicado en Status | Había dos reemplazos de `[AGENTE]` en cascada; ahora hay uno solo via delegación.                                                                    |
| Tags faltantes en Status       | El path STATUSWHREC solo reemplazaba ~15 tags; ahora hereda todos los tags de WHrec via delegación.                                                  |


