# Laravel fix request: PlentyOne delivery confirmation

**Endpoint affected:** `POST /api/sales/orders/mark-plentyone-order-delivery`

## The bug we're seeing

Roughly **1–2 of every 100 PlentyOne deliveries** end up in a broken state
on PlentyOne:

- The **confirmation comment** ("we shipped …") **is posted** on the order.
- The **shipping package / tracking number is NOT registered** on the order.

From the Nuxt frontend's point of view this looks like a success because
the Laravel endpoint returns 2xx, so we mark the shipment as confirmed
(`IsCommissionedConfirmed = true`, `ack_commissioned_laravel = true`) and
the issue stays invisible until a customer complains that the tracking
number is missing on Plenty.

## What we suspect

Laravel currently does (roughly, in order) on each call:

1. Send order comment to Plenty.
2. Send shipping package (tracking number) to Plenty.
3. Send the customer notification mail.

If step 1 succeeds but step 2 silently fails (network blip, Plenty timeout,
2xx response without a real `packageId`, etc.), the order ends up with a
comment but no package — exactly the state we see.

## What we'd like changed

Please change `sales/orders/mark-plentyone-order-delivery` so that:

### 1. Reorder: tracking first, comment second, mail last

Run the shipping-package call to Plenty **first**. Only if it returns
2xx **and** the response contains a valid `packageId`, continue to the
next step.

Suggested order:
1. POST shipping package (tracking number) to Plenty.
2. POST order comment to Plenty.
3. Send customer notification mail.

### 2. Verify-after-write (defence in depth)

Right after the shipping-package POST returns 2xx, do a `GET` on the
order's shipping packages and confirm the tracking number we sent is
actually present in the returned list. If it isn't, treat that as a
tracking failure — same as step 1 failing.

This protects us against Plenty accepting a request but silently dropping
the package, which we believe is part of what's happening.

### 3. Skip-on-failure

If the tracking step fails (either non-2xx, or 2xx-but-not-verified):

- **Do not** post the order comment.
- **Do not** send the customer notification mail.
- **Do not** return 2xx to Nuxt.

The whole operation should be all-or-nothing from our perspective.

### 4. Return a structured error on failure

When any step fails, return a non-2xx HTTP response with a body like:

```json
{
  "status": 500,
  "step": "tracking",
  "trackingAdded": false,
  "commentAdded": false,
  "mailSent": false,
  "plentyResponse": { "...": "raw upstream response or error" },
  "message": "Plenty did not register the shipping package"
}
```

`step` is one of `"tracking" | "comment" | "mail"` so we can show
operators what failed.

### 5. Log every step

Persist the per-step result and raw Plenty payloads to wherever Laravel
already keeps integration logs. Please key the log rows by:

- `plentyone_order_id` (Plenty's internal order id; we send it as `id`
  in the request body).
- our `referenceId` (the value we send under `details.referenceId` — this
  is `M_Inout.DocumentNo` in our ERP).

That way when we get a customer complaint we can correlate a Plenty order
id with whatever happened on Laravel's side at confirmation time.

### 6. Resubmit safety

We already have a resubmit flow on our side
(`server/api/commission/parcels/resubmit.post.ts`) that re-calls the same
endpoint when a confirmation has gone wrong. Once you reorder the steps,
this resubmit should be idempotent: if a previous partial run posted the
comment, the resubmit should detect that and **not** post a second one,
just complete the missing tracking package step. (Ideally the partial
state can no longer occur once the reorder is in, but the resubmit needs
to remain safe to call.)

## What we send you today (for reference)

The request body Nuxt sends to
`POST /api/sales/orders/mark-plentyone-order-delivery` looks like this:

```json
{
  "orderSource":   { /* full C_OrderSource record incl. marketplace_url, marketplace_key, marketplace_secret */ },
  "id":            "<plentyone_order_id>",
  "details": {
    "shippingDate":   "YYYY-MM-DD",
    "carrierCode":    "DHL",
    "shippingMethod": "Paket",
    "referenceId":    "<M_Inout.DocumentNo>",
    "weight":         0
  },
  "trackingCodes": {
    "number":           "<tracking number>",
    "url":              "<tracking url>",
    "trackingNoArray":  ["additional", "tracking", "numbers"]   // optional, multi-parcel only
  },
  "mail": {
    "email":                    "customer@example.com",
    "isSentCustomTrackingMail": true,
    "orderNumber":              "...",
    "name":                     "...",
    "company":                  "LogYou GmbH",
    "carrier":                  "DHL",
    "lines": [ { "description": "...", "quantity": 1 } ],
    "address1": "...", "address2": "...", "city": "...",
    "country":  "...", "postal":  "..."
  }
}
```

Nothing about that payload needs to change — the fix is purely in how
Laravel sequences and gates the three downstream actions on Plenty.

## Acceptance criteria

We'll consider this fixed when:

- **Happy path:** Plenty's package endpoint succeeds → behaviour identical
  to today (200 returned, comment + mail + package all present on Plenty).
- **Plenty 5xx / timeout on package:** non-2xx returned to Nuxt, **no
  comment posted**, **no mail sent**, log row written with `step=tracking`.
- **Plenty 200 but missing on verify GET:** same as above — non-2xx, no
  comment, no mail, log row written with `step=tracking`.
- **Resubmit safety:** calling the endpoint a second time for the same
  order doesn't produce a duplicate comment on Plenty.

## What we'll do on our side (Nuxt)

For our own diagnostics we'll add a small log column on `M_Inout`
(`marketplace_confirm_log`) and persist your full response body to it on
every call. That gives us forensic data while the fix is rolling out and
afterwards — but it changes nothing about how we call your endpoint or
what we expect from it.

Thanks!
