# Dicotec_ProyesSync

Magento 2 module that sends newly-placed orders to the **Proyes / Musmar API**
(`POST /ordine-web.php`), following the `ordine-web-payload` schema from the
*Proyes - Musmar API v.1.2* documentation.

## What it does

- Listens to the `sales_order_place_after` event (fires for storefront checkout,
  REST/GraphQL order creation, and admin "Create Order").
- Maps the Magento order to the `ordine-web-payload` JSON structure:
  customer/shipping-address fields, `vettore_cod` (shipping), `pagamento_cod`
  (payment), and a `carrello` array of ordered SKUs/variants (`cod_art`,
  `colore`, `taglia`, `gruppo`, `qta`, `prezzo`, `sconto`, `totale`).
- POSTs it to `{api_base_url}/ordine-web.php` with `Authorization: Bearer <token>`.
- Stores the result (`sent` / `error` + message) on the order, visible as a
  new **Proyes Sync** column in Sales > Orders.
- Never blocks order placement: API/config failures are caught and logged to
  `var/log/proyessync.log`, and the order can be resent later.
- Ships a CLI command to manually (re)send a single order:
  ```
  bin/magento dicotec:proyessync:resync-order 000000123
  ```

## Installation

1. Copy the `app/code/Dicotec/ProyesSync` folder into your Magento root
   (or `require` it via Composer if you package it as such).
2. Run:
   ```
   bin/magento module:enable Dicotec_ProyesSync
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```
   `setup:upgrade` applies the declarative schema, adding
   `proyes_sync_status`, `proyes_order_ref`, and `proyes_sync_message`
   columns to `sales_order`, and `proyes_sync_status` to `sales_order_grid`.

## Configuration

Go to **Stores > Configuration > Dicotec > Proyes Order Sync**.

### General / API Connection
- **Enable Order Sync** — must be Yes for anything to fire.
- **API Base URL** — defaults to `https://proyes.it/api/v1/musmar`.
- **API Bearer Token (JWT)** — obtained from Dicotec srl, stored encrypted.
- **Request Timeout**, **Debug Logging**.

### Field & Attribute Mapping
The Proyes payload needs data Magento doesn't natively expose 1:1, so the
following are configurable:

| Setting | Purpose |
|---|---|
| `cod_art` attribute code | Product attribute holding the Proyes article code (falls back to SKU). |
| `colore` / `taglia` attribute codes | Your configurable attribute codes for color/size, read off the ordered child product (or the order's captured option info if the attribute isn't on the product). |
| `gruppo` attribute code + default | Attribute for the Proyes size-group code; falls back to a configurable default (e.g. `STD`) if not set on the product. |
| `cpc` customer attribute code | Optional customer EAV attribute (default `proyes_cpc`) storing a customer's known Proyes code, so repeat customers aren't re-created. Leave unset for new customers. |
| Default `cod_catastale_nazione` | Used for non-Italian shipping addresses (Italy always sends `Z000`). |
| **Shipping Method → vettore_cod map** | One `magento_method_code=proyes_code` pair per line, e.g. `flatrate_flatrate=STD`. Look up valid codes via `GET /vettori.php`. |
| **Payment Method → pagamento_cod map** | Same format, e.g. `checkmo=CONTR`. Look up valid codes via `GET /pagamenti.php`. |

**Important:** Explicit config mappings always take priority. When a
shipping or payment method has no entry, the module now falls back
automatically (mirroring Dicotec's own reference connector) instead of
hard-failing:
- **vettore_cod** falls back to the first carrier returned by `GET /vettori.php`.
- **pagamento_cod** falls back to keyword matching against payment names
  from `GET /pagamenti.php` (e.g. a method code containing "paypal" matches
  a payment named "PayPal"; "cashondelivery"/"checkmo" match "Contanti"/
  "Consegna"; "scalapay" matches "Scalapay"), then to the first payment
  method in that list.

It only throws (and the order is marked `error`, safe to resync later) if
there's no explicit mapping *and* the fallback API call also returns nothing
usable — so add the missing config line if you want deterministic control
over a given method rather than relying on the automatic fallback.

### Missing address data
Required text fields (`nome`, `cognome`, `indirizzo`, `civico`, `cap`,
`citta`) are never sent empty — if Magento has no value, the module sends a
configurable placeholder (default: `sconosciuto`) instead, and `provincia`
falls back to a configurable default code (default: `NA`). Street parsing
splits the address into "indirizzo" / "civico" by pulling out the last
numeric token as the house number, the same approach used by Dicotec's
reference connector.

## Extending

- `Dicotec\ProyesSync\Model\OrderPayloadBuilder` is where all field mapping
  logic lives — adjust `buildCarrello()` / `getOptionValue()` if your color
  and size are modeled differently (e.g. not as simple/configurable products).
- `Dicotec\ProyesSync\Model\OrderSyncService::sync()` is the single entry
  point used by both the event observer and the CLI command — call it from
  your own code (e.g. a queue consumer) if you'd rather send orders
  asynchronously instead of inline during checkout.
- The client (`Model\Client\ProyesApiClient`) only implements
  `POST /ordine-web.php`. The same pattern (Curl + Bearer token from
  `Helper\Config`) can be reused to call the other read endpoints documented
  in the API (`/articolo.php`, `/giacenza.php`, `/clienti.php`, etc.) if you
  later need Magento to pull catalog/stock data from Proyes as well.

## Notes on the API spec

- All numeric fields in `articolo-carrello` (`qta`, `prezzo`, `sconto`,
  `totale`) are documented as JSON strings, not numbers — this module
  formats them as fixed 2-decimal strings accordingly.
- `spese_spedizione`, when sent as a numeric string, **overrides** Proyes'
  own shipping-cost calculation — the module only includes it when the order
  actually has a shipping charge greater than zero.
