Integrare partener: eTSM / Transport 3PL

Ghid complet pentru echipele care leagă eTSM de acest 3PL (Supply Chain Management Center): trimiterea PO către partener, recepția în eTSM și înscrierea recepției în stocul 3PL. Această pagină publică (https://www.flowscmc.ro/eTSM) centralizează contractul tehnic, URL-urile, antetele, variabilele de mediu relevante și contextul de business. Pentru REST general (articole, PO, SO, retururi) vezi documentația API și index JSON /api.

Instanță: https://www.flowscmc.ro

Cuprins

Context business în 3PL

Aplicația se adresează operatorilor logistici (3PL) și clienților acestora, pentru operațiuni în relațiile contractuale dintre aceștia.

  • PO (comandă achiziție) — generată de CLIENT, către PARTENERI (furnizori), pentru livrare la 3PL (inbound). PO-ul din 3PL se transmite în eTSM; în eTSM se programează și se recepționează; recepția din eTSM se transmite în 3PL. Recepția poate fi parțială în 3PL (mai multe tranșe până la acoperirea cantităților).
  • SO (comandă vânzare) — generată de CLIENT, către PARTENERI (destinatari): livrare de către 3PL sau mărfuri puse la dispoziție (pregătite) de 3PL și livrate printr-un transportator.
  • Retururilecreate de CLIENT, din PO sau din SO; regulile de validare în 3PL sunt la reguli aplicație.
  • Tip depozit — stoc OK / stoc not OK (ex. deteriorate, nevandabile), conform configurării.
  • Punct de lucru — loc de încărcare/descărcare al clientului sau partenerului.

Utilizatori platformă - roluri (rezumat)

Drepturile efective depind de permisiuni și contract; mai jos este modelul funcțional uzual.

  • ADMIN PLATFORMA (SCMC) - creează 3PL, clienți, parteneri, useri admin pentru entități, module unde e cazul; poate deschide aplicația în numele adminilor alocați doar pentru vizualizare; modificările sensibile se fac din userul ADMIN PLATFORMA; poate efectua operațiunile specifice ADMIN 3PL / CLIENT / PARTENER.
  • ADMIN 3PL - modificări la nivel 3PL, useri și roluri colegi, clienți/parteneri, depozite, vizualizare PO/SO, recepții, stocuri, articole.
  • ADMIN CLIENT - modificări la nivel client, useri, parteneri, puncte de lucru, vizualizare PO/SO, adăugare PO/SO, stocuri, articole, adăugare articole.
  • ADMIN PARTENER - modificări la nivel partener, useri, puncte de lucru; vizualizări PO/SO/stocuri în limitele contractului (configurabile în 3PL).

Integrarea tehnică eTSM ↔ 3PL folosește secret server-side (Bearer), nu înlocuiește rolurile de mai sus pentru utilizatorii umani din UI.

Flux cap-coadă: PO în 3PL → eTSM → recepție înapoi în 3PL

  1. În 3PL se creează și se validează PO-ul (client → partener, livrare la 3PL).
  2. PO-ul este transmis către sistemul partenerului (eTSM): serverul 3PL face POST JSON la URL-ul configurat (TRANSPORT_3PL_BOOKING_URL), cu Authorization: Bearer (TRANSPORT_3PL_TOKEN). Butonul din ecranul PO apare doar dacă TRANSPORT_3PL_SHOW_BUTTON=true (textul: TRANSPORT_3PL_LINK_LABEL). Cu TRANSPORT_3PL_AUTO_SUBMIT_PO_ON_CREATE=true (vezi trimitere automată), același apel rulează imediat după salvare (formular, API, import). Nu setați ambele pe true: dacă sunt ambele true, rămâne doar trimiterea automată (butonul este ascuns).
  3. În eTSM au loc programarea și recepția fizică (cantități reale, eventual în mai multe tranșe).
  4. După recepție în eTSM, aplicația partenerului înscrie recepția în 3PL prin una din variantele de la Recepție în 3PL (Sanctum sau Bearer integrare). Recepțiile pot fi totale (un singur apel care acoperă tot restul ne-recepționat pe liniile incluse) sau parțiale (mai multe apeluri pe același PO până la epuizarea cantităților; cantitățile se cumulează pe linie).

Endpoint-uri - rezumat

Direcție Metodă URL Autentificare
3PL server → partener (eTSM) POST https://www.euscagency.com/etsm_test/platforme/logistic/api/index.php Authorization: Bearer = TRANSPORT_3PL_TOKEN
Partener → 3PL (test) GET https://www.flowscmc.ro/api/integrations/etsm/ping Authorization: Bearer = ETSM_INBOUND_BEARER_TOKEN
Partener → 3PL (recepție) POST https://www.flowscmc.ro/api/integrations/etsm/receptions Bearer integrare + X-Company-Id (vezi secțiunea recepție)
Client API / user tehnic POST https://www.flowscmc.ro/api/receptions Sanctum (POST https://www.flowscmc.ro/api/auth/token) + X-Company-Id
Discovery GET https://www.flowscmc.ro/api Public (fără secret)

Rutele /api/integrations/etsm/* au limitare de rată (throttle); evitați burst-uri mari din același IP.

Recepție din eTSM → 3PL (REST)

Aplicația partenerului alege una din variante (același corp JSON):

A) API standard - utilizator API (Sanctum)

  • URL: POST https://www.flowscmc.ro/api/receptions
  • Token: POST https://www.flowscmc.ro/api/auth/token (email + parolă); apoi Authorization: Bearer <token Sanctum>
  • Companie: X-Company-Id: <id client 3PL> (aceeași companie client ca pe PO)
  • Condiții: utilizatorul are permisiune de creare recepții; module api și content-inbound activate pentru companie
  • Idempotency (opțional): la POST /api/receptions puteți trimite antetul Idempotency-Key (UUID, 8–128 caractere) pentru a evita duplicate la retry - doar când autentificarea este Sanctum

B) Integrare server eTSM → 3PL (fără user API)

  • URL: POST https://www.flowscmc.ro/api/integrations/etsm/receptions
  • Autentificare: Authorization: Bearer <ETSM_INBOUND_BEARER_TOKEN> - secret generat pe 3PL (openssl rand -hex 32 în .env), comunicat securizat echipei eTSM; nu se expune în browser
  • Companie: același X-Company-Id ca la varianta A
  • Condiții: aceleași module api + content-inbound pentru compania din antet
  • Ping: GET https://www.flowscmc.ro/api/integrations/etsm/ping cu același Bearer → {"ok":true,"integration":"etsm"}

Identificatori: purchase_order_id din 3PL (din payload PO la booking sau GET https://www.flowscmc.ro/api/purchase-orders). Pe linii: po_item_id = ID linie PO din 3PL, nu doar cod articol.

Corp JSON (exemplu):

{
  "purchase_order_id": 10,
  "warehouse_id": 3,
  "transport_ecc": false,
  "selected_items": [
    { "po_item_id": 101, "quantity_um3pl": 24 },
    { "po_item_id": 102, "quantity_um3pl": 5.5 }
  ]
}
  • Total (un apel): în selected_items puteți include toate liniile PO cu cantitățile rămase de recepționat într-o singură cerere, dacă fluxul eTSM o permite.
  • Parțial (mai multe apeluri): trimiteți doar liniile/cantitățile din tranșa curentă; apeluri ulterioare pentru același PO până se acoperă tot ce e pe PO (cantități cumulate pe linie ≤ cantitatea din PO).
  • Validări: PO nu în stările received / cancelled; cantități cumulate ≤ PO; warehouse_id aparține aceluiași operator 3PL ca și clientul din X-Company-Id

Coduri răspuns - /api/integrations/etsm/*

HTTPSemnificație
200GET …/ping reușit
201POST …/receptions - recepție creată (corp JSON standard API)
400X-Company-Id lipsă sau invalid; sau companie inexistentă
401Bearer lipsă sau token incorect
403Module API sau inbound neactivate pentru compania din antet
422Validare eșuată (PO, depozit, cantități, linii)
503ETSM_INBOUND_BEARER_TOKEN gol pe 3PL, sau mentenanță API activă

Antete HTTP recomandate

Pentru apelurile JSON către /api/* (inclusiv integrare eTSM):

  • Accept: application/json
  • Content-Type: application/json (la POST cu corp)
  • Authorization: Bearer … (Sanctum sau ETSM_INBOUND_BEARER_TOKEN, după caz)
  • X-Company-Id: <număr> - obligatoriu la creare recepție (ambele variante)

Reguli 3PL în aplicație (ce înseamnă „conform regulilor din 3PL”)

Nu este un text de contract separat: sunt reguli implementate (validări, politici, permisiuni). Rezumat pentru integrare și business; detalii complete în interfață, Documentație și codul sursă.

Drepturi și acces (Spatie / roluri)

  • Fiecare acțiune depinde de permisiuni (ex. create receptions, create returns, view purchase_orders) și de compania curentă / context 3PL.
  • Partenerii văd doar datele la care au acces în politici (ex. retururi unde apar ca partener).

Articole în catalog

  • Creare articol: utilizatori din compania client, cu permisiunea de creare; nu personal 3PL sau utilizatori portal partener (exceptând super-admin platformă). 3PL poate completa câmpuri operaționale acolo unde fluxul o permite (ex. măsurători UM), conform modulelor și ecranului.

Recepții (inbound, inclusiv apel API / integrare eTSM)

  • PO trebuie să aparțină aceluiași X-Company-Id (client 3PL) ca în cerere.
  • PO nu poate fi în stadiu received sau cancelled (nu se mai recepționează pe el).
  • warehouse_id trebuie să fie al aceluiași operator 3PL ca și clientul din companie.
  • Cantitățile recepționate pe fiecare linie (article + UM) nu pot depăși restul ne-recepționat din PO; este permisă o recepție totală într-un singur apel (toate liniile/cantitățile rămase) sau recepții parțiale repetate până la epuizare.
  • Pentru varianta API standard: utilizatorul/tokenul trebuie să poată crea recepții; pentru varianta eTSM Bearer: trebuie module api și content-inbound activate pentru compania din antet.

Retururi (din PO sau SO)

  • Sursă unică: fie return_source=so + sales_order_id, fie return_source=po + purchase_order_id (nu ambele).
  • Din SO: SO trebuie să fie LIVRAT; nu se acceptă SO anulate.
  • Din PO: PO nu poate fi draft sau anulat/cancelled.
  • Liniile de retur: articol + UM trebuie să existe pe documentul sursă; cantitatea returnată nu poate depăși ce e încă returnabil (la SO: față de cantitatea livrată minus retururi deja înregistrate; la PO: doar pentru ce a fost recepționat, minus retururi deja făcute).
  • La PO: dacă pentru articolul/UM respectiv nu s-a recepționat nimic, returul nu este permis.
  • Obligatoriu: warehouse_id, minim o linie, articole din catalogul companiei client; transport_correlation_uuid (UUID); dacă e bifat transport 3PL (transport_ecc), max_reception_date devine obligatoriu.
  • Creare retur: permisiune create returns și companie curentă (sau super-admin).

Trimitere PO către partener (booking)

  • Partenerul răspunde în limitele timeout-ului configurat; răspunsul trebuie JSON curat (fără notice-uri PHP înainte de JSON).

Recomandări tehnice pentru integrare robustă

  • Păstrați legătura între eTSM și 3PL cu transport_correlation_uuid (UUID stabil pe PO), purchase_order_id (cheie numerică în API 3PL), internal_code (cod PO uman), plus client_internal_code / partner_internal_code și purchase_order_status la momentul booking.
  • La booking, răspundeți în limitele timeout-ului 3PL (30s); dacă procesarea e asincronă, returnați 200 + referință și procesați în fundal.
  • Înainte de POST …/receptions (Sanctum sau integrare), verificați starea PO și cantitățile deja recepționate pentru a reduce erori 422.
  • La varianta Sanctum, folosiți Idempotency-Key pentru retry-uri sigure; la varianta integrare, implementați deduplicare/idempotency în eTSM dacă retrimiteți aceeași tranșă.

Endpoint pe serverul partener (primire PO de la 3PL)

  • URL apelat de 3PL: https://www.euscagency.com/etsm_test/platforme/logistic/api/index.php
  • Metodă: POST
  • Content-Type: application/json - citiți corpul brut (php://input etc.); nu vă bazați pe $_POST pentru obiecte imbricate.
  • Autentificare: Bearer = valoarea TRANSPORT_3PL_TOKEN din configurarea 3PL
  • Timeout client 3PL: 30 secunde

Mod payload (valori efective pe această instanță)

  • partner_payload_mode: strict - strict: aceleași chei la rădăcină; neaplicabile = null. full: aceeași schemă + chei suplimentare.
  • send_company_and_user: true
  • partner_reception_as_so_shape: true - mapare internă pentru fluxuri care trimit recepție în formă SO la partener.
  • simulate (3PL): false - dacă true, 3PL nu execută HTTP real către partener.
  • show_po_booking_button (3PL): true - buton manual pe pagina PO; exclusiv față de auto-submit (vezi mai jos).
  • auto_submit_po_on_create (3PL): false - dacă true, la creare PO se trimite booking automat (după commit DB), dacă booking-ul e activ (simulare sau URL+token).
  • Text buton UI: Rezervă transport pe site partener (TRANSPORT_3PL_LINK_LABEL)

Trimitere automată PO la partener la creare

În .env: TRANSPORT_3PL_AUTO_SUBMIT_PO_ON_CREATE=true pentru trimitere la creare; TRANSPORT_3PL_SHOW_BUTTON=true pentru buton manual (eticheta: TRANSPORT_3PL_LINK_LABEL). Cele două nu pot fi ambele true: dacă le setați ambele, 3PL păstrează doar auto-submit și ascunde butonul.

  • Booking trebuie să fie „activ”: fie TRANSPORT_3PL_SIMULATE=true, fie TRANSPORT_3PL_BOOKING_URL + TRANSPORT_3PL_TOKEN setate.
  • Se execută după tranzacția care salvează PO-ul și liniile (formular web, POST /api/purchase-orders, import PO).
  • La publicare draft → PO final, se aplică aceeași regulă.
  • Nu se trimite pentru draft-uri, PO fără linii, sau status cancelled.
  • Dacă partenerul răspunde cu eroare sau timeout, PO-ul rămâne creat; detaliile apar în jurnalul de activitate / log (ca la trimiterea manuală).

Sursa câmpurilor este aceeași ca la trimiterea manuală (când butonul este afișat): payload din PO + linii, apoi mod strict/full și mapări TRANSPORT_3PL_PARTNER_* înainte de POST. Ruta POST …/transport-3pl/submit răspunde 403 dacă butonul este dezactivat (TRANSPORT_3PL_SHOW_BUTTON=false sau conflict rezolvat în favoarea auto-submit).

JSON efectiv în POST (3PL → partener), tip PO

Exemplu generat pe această instanță cu aceleași reguli ca la trimiterea reală (mod strict, send_company_and_user = true). La rădăcină nu sunt doar liniile: vezi și identificatorii PO. În items[] fiecare linie are po_item_id (ID linie în 3PL). În consola browser, console.table pe linii este doar un rezumat; obiectul complet este payload.

{
    "type": "PO",
    "transport_correlation_uuid": "550e8400-e29b-41d4-a716-446655440000",
    "client_3pl_id": 5,
    "partner_id": 12,
    "partner_work_point_id": 3,
    "source_warehouse_id": null,
    "warehouse_id": null,
    "stock_type": "STOC_OK",
    "planned_date_time": "2026-04-15T00:00",
    "max_delivery_date": null,
    "purchase_order_id": 10,
    "planned_date": "2026-04-15",
    "internal_code": "PO-1-00001",
    "purchase_order_status": "sent",
    "client_internal_code": "CLI-01",
    "partner_internal_code": "FURN-99",
    "sales_order_id": null,
    "max_reception_date": null,
    "items": [
        {
            "article_id": 20,
            "article_code": "SKU-1",
            "description": "Exemplu articol",
            "um3pl": "BUC",
            "quantity_um3pl": 24,
            "available_stock": 0,
            "primary_attribute_id": null,
            "attribute_value": null,
            "po_item_id": 101,
            "lot_number": null
        }
    ]
}

Corelare PO ↔ eTSM ↔ recepție în 3PL

Flux business: PO generat în 3PL (de CLIENT, către PARTENERI, livrare la 3PL) se transmite în eTSM; în eTSM se programează și se recepționează; recepția din eTSM se transmite în 3PL (POST /api/receptions sau POST /api/integrations/etsm/receptions). Recepțiile pot fi parțiale (mai multe apeluri pe același PO). SO și retururile urmează regulile din secțiunea Context business.

Ce folosiți ca identificatori:

  • transport_correlation_uuid — UUID stabil pe documentul PO în 3PL; ideal pentru deduplicare și suport între sisteme.
  • purchase_order_id — ID numeric 3PL; obligatoriu în corpul recepției către API (împreună cu X-Company-Id).
  • internal_code — codul PO afișat utilizatorilor (ex. PO-00001).
  • po_item_id — pe fiecare element din items[]; la recepție trimiteți cantități pe această cheie, nu doar pe cod articol.
  • purchase_order_status, client_internal_code, partner_internal_code — context la momentul booking (utile în eTSM fără lookup suplimentar).

Chei la rădăcină (mod strict - booking)

Fiecare request conține aceste chei; cele neaplicabile sunt null.

type,  transport_correlation_uuid,  client_3pl_id,  partner_id,  partner_work_point_id,  source_warehouse_id,  warehouse_id,  stock_type,  planned_date_time,  max_delivery_date,  purchase_order_id,  planned_date,  internal_code,  purchase_order_status,  client_internal_code,  partner_internal_code,  sales_order_id,  max_reception_date,  items 

Chei pe linie (items[])

Fiecare element din items are aceeași structură; nefolosit = null.

article_id,  article_code,  description,  um3pl,  quantity_um3pl,  available_stock,  primary_attribute_id,  attribute_value,  po_item_id,  lot_number 

transport_correlation_uuid

UUID stabil pentru același document / încercare de booking. Folosiți-l pentru deduplicare, suport și legare cu fluxuri viitoare (ex. callback-uri).

Exemplu scurt PO (schematic)

Schema completă pe fire, cu toate cheile modului strict, este la JSON efectiv trimis.

{
  "type": "PO",
  "transport_correlation_uuid": "…",
  "purchase_order_id": 10,
  "internal_code": "PO-00001",
  "warehouse_id": null,
  "planned_date": "2026-04-15",
  "items": [ … ]
}

Răspuns HTTP așteptat (booking partener → 3PL)

  • Preferabil HTTP 200 cu corp JSON valid. Orice text înainte de JSON (ex. notice-uri PHP) poate strica parsarea - în producție: display_errors=Off.
  • Dacă returnați HTTP 200 cu "success": false în JSON, 3PL tratează rezervarea ca eșuată.
  • Puteți include booking_reference, data.order_id etc.; 3PL păstrează răspunsul decodabil.

La eșec, utilizatorii 3PL văd mesajul extras din răspuns (sau fragment din corp).

Documentație REST API (3PL) · GET /api (index) · Webhooks (documentație publică)

Folosim cookie-uri esențiale pentru funcționarea aplicației. Cookie-urile opționale ne ajută să îmbunătățim experiența. Află mai mult