# POLSTRIN SAP Business One Service Layer Connector Node.js + TypeScript connector pro SAP Business One Service Layer REST/OData API, dedikovaný instalaci **POLSTRIN DESIGN s.r.o.** (`https://ws.polstrin.cz:50000`, SAP B1 10.0, verze `1000310`). Používá pouze Service Layer (`/b1s/v1`), ne SAP DI API. Vznikl jako kopie obecné služby `sap-bo`; hlavní rozdíly: - SAP credentials se **nepředávají v hlavičkách**, ale čtou se z environment variables (AppFactory secrets, viz AGENTS.md „Secrets v parametrech“). - Jedna sdílená Service Layer session přežívá mezi requesty (re-login při expiraci/401 řeší `SessionManager`). - Volitelná ochrana `/api` rout sdíleným klíčem: když je nastavený secret `API_KEY`, každý request musí poslat hlavičku `X-Api-Key`. - Generické endpointy `/api/entities/...` pro práci s libovolným entity setem — hlavně pro UDO tabulky POLSTRIN addonů (`U_ADN_*`, `U_DFX_*`, `U_PVT_*`, `U_VCZ_*`, `VYROBNI_PLAN`, `VYROBNI_DAVKA`). Detaily v [documentation/polstrin-specifika.md](documentation/polstrin-specifika.md). Součástí repozitáře je AppFactory HTTP wrapper s endpointy: - `GET /` - `GET /health` - `GET /docs` - `GET /openapi.json` - `POST /api/session/login` (ověří spojení, vrací verzi Service Layeru a session timeout) - `POST /api/session/logout` - `GET /api/system/info` (verze SAP, dostupné entity sety, UDF/UDT/UDO, admin info) - `GET /api/entities` (seznam všech entity setů) - `GET|POST /api/entities/{entitySet}`, `GET /api/entities/{entitySet}/all` - `GET|PATCH|DELETE /api/entities/{entitySet}/{id}` (`?idType=number` pro číselné klíče) - `GET /api/` - `GET /api//all` - `GET /api//{id}` - `POST /api/` - `PATCH /api//{id}` - `DELETE /api//{id}` pouze pro obecně mazatelné resource ## Instalace ```bash npm install npm run build npm test ``` ## Konfigurace (secrets přes environment variables) Všechny SAP credentials se nastavují přes AppFactory portál jako variables/secrets; aplikace je čte z environment variables. Nikdy se nepředávají v requestech, nelogují se a necommitují. Lokálně vytvoř `.env` podle `.env.example`: ```env SAP_B1_BASE_URL=https://ws.polstrin.cz:50000 SAP_B1_COMPANY_DB= SAP_B1_USERNAME= SAP_B1_PASSWORD= SAP_B1_LANGUAGE= SAP_B1_TIMEOUT_MS=30000 SAP_B1_REJECT_UNAUTHORIZED=true SAP_B1_RETRY_COUNT=2 SAP_B1_RETRY_DELAY_MS=250 API_KEY= ``` `SAP_B1_BASE_URL` může být buď root Service Layer hostu, nebo přímo URL končící `/b1s/v1`. Pro self-signed certifikáty lze v interním prostředí nastavit `SAP_B1_REJECT_UNAUTHORIZED=false`; v produkci preferuj důvěryhodný certifikát a ponech `true`. ## Autentizace HTTP API SAP credentials jsou vnitřní secret služby — klienti je neposílají. Když je nastavený secret `API_KEY`, musí každý request na `/api/...` obsahovat hlavičku: ```http X-Api-Key: ``` Bez nastaveného `API_KEY` jsou `/api` routy otevřené (vhodné jen pro interní síť). Ve Swaggeru (`/docs`) se klíč vyplňuje přes `Authorize`. ## Použití ### Login a logout ```ts import { SapBusinessOneServiceLayer, loadSapB1ConfigFromEnv } from "./src"; const sap = new SapBusinessOneServiceLayer(loadSapB1ConfigFromEnv()); await sap.login(); await sap.logout(); ``` Login volá `POST /b1s/v1/Login`, uloží cookies `B1SESSION` a `ROUTEID` a posílá je v dalších requestech. Při expiraci session a odpovědi `401` connector jednou provede re-login a request zopakuje. ### Vypsání Business Partners ```ts const partners = await sap.businessPartners.list({ select: ["CardCode", "CardName", "CardType"], filter: "CardType eq 'cCustomer'", top: 50, orderby: "CardName asc" }); console.log(partners.value); ``` ### Načtení všech záznamů přes nextLink ```ts const allItems = await sap.items.listAll({ select: ["ItemCode", "ItemName"], top: 100 }); ``` Connector podporuje starší `odata.nextLink` i novější `@odata.nextLink`. ### Vytvoření objednávky ```ts const order = await sap.orders.create({ CardCode: "C001", DocDueDate: "2026-07-15", DocumentLines: [ { ItemCode: "A00001", Quantity: 2, UnitPrice: 100 } ] }); ``` ### Aktualizace položky ```ts await sap.items.update("A00001", { ItemName: "Updated item name" }); ``` ## Resource moduly Implementované resource moduly: - `businessPartners` - `items` - `orders` - `invoices` - `purchaseOrders` - `deliveryNotes` - `stockTransfers` Každý modul má: - `list(query?)` - `listAll(query?)` - `get(id)` - `create(data)` - `update(id, data)` - `delete(id)` pouze tam, kde je povolené mazání U marketing dokumentů (`Orders`, `Invoices`, `PurchaseOrders`, `DeliveryNotes`) a skladových převodek je delete v connectoru záměrně blokovaný. SAP Business One obvykle řeší rušení dokumentů storno/cancel operacemi podle typu dokladu a nastavení firmy. ## Zpracování chyb Connector převádí chyby na `SapB1Error`: - `status` HTTP status - `code` SAP error code, pokud jej Service Layer vrátí - `message` bezpečná chybová zpráva - `retryable` příznak pro dočasné chyby Retry se používá pro `408`, `429` a `5xx`. Citlivé hodnoty jako heslo, cookies a session tokeny se při logování redigují. ## Testy Testy používají mockované HTTP odpovědi přes axios adapter nebo fake klienty. Nevolají reálný SAP. ```bash npm test ``` Pokryté oblasti: - autentizace a cookies - logout - automatický re-login po `401` - OData query parametry - resource URL builder ## AppFactory Aplikace poslouchá na `0.0.0.0` a portu `PORT` s výchozí hodnotou `3000`. `ROOT_PATH` se používá pro dokumentaci a testovací requesty za reverse proxy, například `/apps/polstrin-sap`. `/docs` je Swagger UI servírované přímo jako HTML (stejně jako sousední `google-service`), bez statického middleware – proto nevzniká redirect `/docs` → `/docs/`, který by za proxy zahodil prefix. Assety se načítají z CDN, spec URL je `ROOT_PATH + /openapi.json`. V Swaggeru použij `Authorize` pro vyplnění `X-Api-Key` (pokud je `API_KEY` nastavený) a potom `Try it out` u konkrétní operace. OpenAPI `servers` se nastaví z `ROOT_PATH` (jinak `/`), takže za proxy volá například `/apps/polstrin-sap/api/business-partners`, ne root doménu. ## TODO ověřit v konkrétní instalaci SAP Business One - Přesné enum hodnoty a povinná pole pro jednotlivé entity se mohou lišit podle lokalizace, add-onů a verze SAP Business One. - U rušení dokladů ověř konkrétní Service Layer akce dostupné pro daný typ dokladu a firemní nastavení. - U velkých datasetů ověř server-side limity stránkování a maximální povolené `$top`. - Ověř, zda konkrétní instalace vrací OData metadata ve starším formátu `odata.*` nebo novějším `@odata.*`.