intelli-dust / vario-ng
VarioNG (VOLVO VIBE) REST API client for PHP - v3.0 with vibe.volvocars.biz support
Installs: 9
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 2
Forks: 0
Open Issues: 0
pkg:composer/intelli-dust/vario-ng
Requires
- php: >=5.4.0
- ext-curl: *
- ext-json: *
- tcdent/php-restclient: >=0.1.7
Requires (Dev)
- php: >=5.5.7
- phpunit/phpunit: >=4.5
- tcdent/php-restclient: >=0.1.7
This package is not auto-updated.
Last update: 2026-01-15 07:05:41 UTC
README
Rest Client for Volvo Cars VIBE (VarioNG) API - https://vibe.volvocars.biz/
⚠️ Breaking Changes in v3.0:
- New base URL:
vibe.volvocars.biz(oldvario-ng.comdeprecated as of Jan 15, 2026) postStockOnHand()removed, replaced withpostRetailerStockOnHand()andpostImporterStockOnHand()- Response format changed to include
details[]array - JWT token auto-refresh on expiration
Installation
Via Composer
composer require intelli-dust/vario-ng
Manual Installation
- Clone the repository:
git clone https://github.com/IntelliDust/VarioNG.git
- Install dependencies:
cd VarioNG
composer install
- Include in your project:
require_once 'vendor/autoload.php';
Configuration
Basic Configuration
$client = new VarioNGClient([ 'userName' => 'YOUR_USERNAME', 'password' => 'YOUR_PASSWORD', 'environment' => 'production' // or 'qa' for testing ]);
Advanced Configuration
$client = new VarioNGClient([ 'userName' => 'YOUR_USERNAME', 'password' => 'YOUR_PASSWORD', 'base_url' => 'https://vibe.volvocars.biz/ws/dcs/', // Custom URL 'environment' => 'production', 'user_agent' => 'MyApp/1.0', 'jwt_timeout' => 120 // minutes ]);
Environment Options
- production:
https://vibe.volvocars.biz/ws/dcs/ - qa:
https://qa-vibe.volvocars.biz/ws/dcs/
Workflow (Pracovný postup)
1. Inicializácia a autentifikácia
<?php require_once 'vendor/autoload.php'; // Krok 1: Vytvorenie klienta $client = new VarioNGClient([ 'userName' => 'YOUR_USERNAME', 'password' => 'YOUR_PASSWORD', 'environment' => 'production' // alebo 'qa' pre testovanie ]); // Krok 2: Prihlásenie (získanie JWT tokenu) try { $client->logIn(); // Token je automaticky uložený a bude sa používať pri všetkých požiadavkách // Token je platný 120 minút a automaticky sa obnovuje pri expirácii } catch (Exception $e) { die("Login failed: " . $e->getMessage()); }
2. Odoslanie dát (Create/Update)
Všeobecný workflow pre všetky endpoint-y:
// Krok 1: Pripravte dáta v správnej štruktúre $data = [ 'authorizationID' => 'YOUR_CODE', // Retailer alebo Importer kód // bodID sa vygeneruje automaticky, ak nie je poskytnutý // requestedBy sa nastaví automaticky podľa metódy // toDelete sa nastaví automaticky na 'N' // ... špecifické dáta pre každý endpoint ]; // Krok 2: Odošlite dáta try { $response = $client->postJobCard($data); // alebo iná metóda // Krok 3: Spracujte odpoveď if ($response->details[0]->rc == '1') { echo "Úspech! BodID: " . $response->bodID . "\n"; echo "Správa: " . $response->details[0]->message . "\n"; } } catch (Exception $e) { echo "Chyba: " . $e->getMessage() . "\n"; }
3. Mazanie dát (Delete)
Delete operácie vyžadujú iba kľúčové polia:
// Pre Job Card: retailerCode, woNumber, woYear $deleteData = [ 'authorizationID' => 'SK006', 'workOrder' => [ 'retailerCode' => 'SK006', 'woNumber' => '202600024', 'woYear' => '2026' ] ]; try { // Druhý parameter 'Y' značí delete operáciu $response = $client->postJobCard($deleteData, 'Y'); echo "Zmazané!\n"; } catch (Exception $e) { echo "Chyba pri mazaní: " . $e->getMessage() . "\n"; }
4. Kompletný workflow pre Job Card
<?php require_once 'vendor/autoload.php'; // === INICIALIZÁCIA === $client = new VarioNGClient([ 'userName' => 'IMP02108', 'password' => 'your_password', 'environment' => 'production' ]); try { // === AUTENTIFIKÁCIA === $client->logIn(); echo "✓ Prihlásený\n"; // === PRÍPRAVA DÁT === $jobCard = [ 'authorizationID' => 'SK006', 'workOrder' => [ 'retailerCode' => 'SK006', 'woNumber' => '202600024', 'woYear' => '2026', 'chassis' => 'TEST1234567890123', 'regNo' => 'AA-123BB', 'body' => 'S-90', 'woOpenDate' => '2026-01-09', 'woCloseDate' => '2026-01-09', 'custCode' => 'TEST001', 'custName' => 'Example Company s.r.o.', 'custPhone' => '0900123456', 'custEMail' => 'test@example.com' ], 'wJobs' => [ [ 'jobID' => '1', 'consumerNotes' => 'Service work' ] ], 'labor' => [ [ 'seqNumber' => '1', 'jobID' => '1', 'operCode' => '17300-2', 'operDesc' => 'Service after 239713 km', 'qty' => 3.000, 'listPrice' => 204.00, 'netAmount' => 204.00, 'curCode' => 'EUR', 'technician' => 'Tech01' ] ], 'parts' => [ [ 'seqNumber' => '1', 'jobID' => '1', 'spCode' => '32140029', 'spDesc' => 'Oil filter', 'usedQty' => 1.000, 'listPrice' => 20.14, 'curCode' => 'EUR', 'volvoOriginal' => 'Y' ] ] ]; // === ODOSLANIE === echo "Odosielam job card...\n"; $response = $client->postJobCard($jobCard); // === SPRACOVANIE ODPOVEDE === echo "✓ Job card odoslaný\n"; echo " BodID: {$response->bodID}\n"; echo " AuthorizationID: {$response->authorizationID}\n"; echo " RC: {$response->details[0]->rc}\n"; echo " Správa: {$response->details[0]->message}\n"; // === MAZANIE (VOLITEĽNÉ) === echo "\nMažem job card...\n"; $response = $client->postJobCard($jobCard, 'Y'); echo "✓ Job card zmazaný\n"; } catch (Exception $e) { // === ERROR HANDLING === echo "❌ Chyba: " . $e->getMessage() . "\n"; // Parsovanie error kódov if (strpos($e->getMessage(), 'RC:4403') !== false) { echo " → Chýba povinné pole\n"; } elseif (strpos($e->getMessage(), 'RC:164') !== false) { echo " → JWT token expiroval, skúste znova\n"; } elseif (strpos($e->getMessage(), 'RC:4411') !== false) { echo " → Duplicitný záznam\n"; } }
5. Workflow pre Stock Inventory (Mesačný zber)
⚠️ Dôležité: Stock inventory sa odosiela raz za mesiac!
// === RETAILER STOCK (D1) === $retailerStock = [ 'authorizationID' => 'SK006', 'stockInventory' => [ 'retailerCode' => 'SK006', 'stockMonth' => date('Ym'), // YYYYMM formát (napr. 202601) 'stockInventoryDet' => [ [ 'spCode' => '32140029', 'spDesc' => 'Oil filter', 'onHand' => 150, 'allocated' => 25, 'unitCost' => 18.50, 'listPrice' => 22.00, 'curCode' => 'EUR', 'volvoOriginal' => 'Y' ] // ... ďalšie položky ] ] ]; try { $client->logIn(); $response = $client->postRetailerStockOnHand($retailerStock); echo "✓ Retailer stock odoslaný pre mesiac: " . date('Ym') . "\n"; } catch (Exception $e) { if (strpos($e->getMessage(), 'RC:4411') !== false) { echo "Stock už bol odoslaný pre tento mesiac!\n"; } else { echo "Chyba: " . $e->getMessage() . "\n"; } } // === IMPORTER STOCK (D2) === $importerStock = [ 'authorizationID' => 'IMP021', 'stockInventory' => [ 'importerCode' => 'IMP021', 'stockMonth' => date('Ym'), 'stockInventoryDet' => [ // ... položky inventára ] ] ]; $response = $client->postImporterStockOnHand($importerStock);
6. Best Practices
6.1 Vždy testujte najprv na QA prostredí
// Pre testovanie $client = new VarioNGClient([ 'userName' => 'test_user', 'password' => 'test_pass', 'environment' => 'qa' // QA endpoint ]); // Keď testy prejdú, prepnite na production $client = new VarioNGClient([ 'userName' => 'prod_user', 'password' => 'prod_pass', 'environment' => 'production' ]);
6.2 Logujte všetky operácie
try { $response = $client->postJobCard($jobCard); // Logujte bodID pre tracovanie error_log("Job card sent: bodID={$response->bodID}, RC={$response->details[0]->rc}"); } catch (Exception $e) { // Logujte chyby error_log("Job card failed: " . $e->getMessage()); }
6.3 Validujte dáta pred odoslaním
// Kontrola povinných polí if (empty($jobCard['workOrder']['retailerCode'])) { throw new Exception("retailerCode je povinný!"); } if (empty($jobCard['workOrder']['woNumber'])) { throw new Exception("woNumber je povinný!"); } // Kontrola formátu dátumov if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $jobCard['workOrder']['woOpenDate'])) { throw new Exception("Nesprávny formát dátumu! Použite YYYY-MM-DD"); }
6.4 Spracovávajte všetky chyby
try { $client->logIn(); $response = $client->postJobCard($jobCard); } catch (Exception $e) { $errorMsg = $e->getMessage(); // Kontrola špecifických error kódov if (strpos($errorMsg, 'RC:-1') !== false) { // Autentifikácia zlyhala echo "Nesprávne prihlasovacie údaje\n"; } elseif (strpos($errorMsg, 'RC:164') !== false) { // JWT expiroval - automaticky obnovený, opakujte operáciu echo "Token expiroval, opakujem...\n"; $response = $client->postJobCard($jobCard); } elseif (strpos($errorMsg, 'RC:4403') !== false) { // Chýba povinné pole preg_match('/Field is: (\w+)/', $errorMsg, $matches); echo "Chýba povinné pole: " . $matches[1] . "\n"; } elseif (strpos($errorMsg, 'RC:4417') !== false) { // Duplicitný part code echo "Duplicitný part code v zozname!\n"; } else { // Iná chyba echo "Neočakávaná chyba: {$errorMsg}\n"; } }
6.5 Použite transakčný prístup
// Ak odosielate viacero položiek, sledujte úspešnosť $results = []; foreach ($jobCards as $jobCard) { try { $response = $client->postJobCard($jobCard); $results[] = [ 'wo' => $jobCard['workOrder']['woNumber'], 'status' => 'success', 'bodID' => $response->bodID ]; } catch (Exception $e) { $results[] = [ 'wo' => $jobCard['workOrder']['woNumber'], 'status' => 'failed', 'error' => $e->getMessage() ]; } } // Report echo "Úspešných: " . count(array_filter($results, fn($r) => $r['status'] === 'success')) . "\n"; echo "Neúspešných: " . count(array_filter($results, fn($r) => $r['status'] === 'failed')) . "\n";
7. Časté scenáre
7.1 Denne: Odoslanie job cards
$client->logIn(); foreach ($dailyJobCards as $jobCard) { try { $response = $client->postJobCard($jobCard); echo "✓ WO {$jobCard['workOrder']['woNumber']} odoslaný\n"; } catch (Exception $e) { echo "✗ WO {$jobCard['workOrder']['woNumber']} zlyhal: {$e->getMessage()}\n"; } }
7.2 Denne: Odoslanie sales parts
$client->logIn(); foreach ($salesInvoices as $invoice) { $response = $client->postSalesPart($invoice); }
7.3 Mesačne: Stock inventory (prvý deň mesiaca)
if (date('d') == '01') { $client->logIn(); // Retailer stock $response = $client->postRetailerStockOnHand($retailerStock); // Importer stock (ak máte centrálny sklad) $response = $client->postImporterStockOnHand($importerStock); }
7.4 Pri predaji vozidla: Car sales + Lead
$client->logIn(); // Najprv odošlite lead (ak ešte nebol odoslaný) $response = $client->postLead($leadData); // Potom car sales $response = $client->postSalesCar($carSalesData);
8. Debugging
8.1 Zapnite error reporting
error_reporting(E_ALL); ini_set('display_errors', 1);
8.2 Logujte raw responses
// Dočasne upravte parseResponse() pre debugging protected function parseResponse($response, $operationName) { error_log("Raw response for {$operationName}: " . $response->response); // ... zvyšok kódu }
8.3 Validujte JSON
$json = json_encode($jobCard); if (json_last_error() !== JSON_ERROR_NONE) { die("Invalid JSON: " . json_last_error_msg()); }
Usage Examples
1. Authentication
try { $client->logIn(); echo "Login successful!\n"; } catch (Exception $e) { echo "Login failed: " . $e->getMessage() . "\n"; }
2. Post Job Card (Service Work Order)
$jobCard = [ 'authorizationID' => 'RET001', 'workOrder' => [ 'retailerCode' => 'RET001', 'woNumber' => 'WO123456', 'woYear' => '2026', 'chassis' => '1HGBH41JXMN109186', 'regNo' => 'ABC123', 'woOpenDate' => '2026-01-10', 'woCloseDate' => '2026-01-13', 'custCode' => 'CUST001', 'custName' => 'John Doe', 'custPhone' => '421901234567', 'custEMail' => 'john@example.com' ], 'wJobs' => [ [ 'laborCode' => 'LAB001', 'laborDesc' => 'Oil change', 'technicianID' => 'TECH01', 'laborHours' => 1.5 ] ], 'parts' => [ [ 'spCode' => 'PART001', 'spDesc' => 'Engine oil 5W30', 'quantity' => 5, 'unitPrice' => 8.50 ] ] ]; try { $response = $client->postJobCard($jobCard); echo "Job card posted successfully! BodID: " . $response->bodID . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
3. Post Sales Part Invoice
$salesPart = [ 'authorizationID' => 'RET001', 'salesinvoice' => [ 'retailerCode' => 'RET001', 'invNo' => 'INV789', 'invDate' => '2026-01-13', 'custCode' => 'CUST002', 'custName' => 'Jane Smith', 'invLineItems' => [ [ 'spCode' => 'PART002', 'spDesc' => 'Brake pads', 'quantity' => 4, 'unitPrice' => 45.00, 'listPrice' => 50.00 ] ] ] ]; try { $response = $client->postSalesPart($salesPart); echo "Sales part posted! BodID: " . $response->bodID . "\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
4. Post Retailer Stock Inventory
$retailerStock = [ 'authorizationID' => 'RET001', 'stockInventory' => [ 'retailerCode' => 'RET001', 'stockMonth' => '202601', // YYYYMM 'stockInventoryDet' => [ [ 'spCode' => 'PART001', 'spDesc' => 'Engine oil 5W30', 'onHand' => 100, 'allocated' => 20, 'unitCost' => 7.50, 'listPrice' => 8.50, 'curCode' => 'EUR', 'volvoOriginal' => 'Y' ] ] ] ]; try { $response = $client->postRetailerStockOnHand($retailerStock); echo "Retailer stock posted!\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
5. Post Importer Stock Inventory
$importerStock = [ 'authorizationID' => 'IMP001', 'stockInventory' => [ 'importerCode' => 'IMP001', 'stockMonth' => '202601', 'stockInventoryDet' => [ [ 'spCode' => 'PART003', 'spDesc' => 'Air filter', 'onHand' => 500, 'allocated' => 50, 'unitCost' => 12.00, 'listPrice' => 15.00, 'curCode' => 'EUR', 'volvoOriginal' => 'Y' ] ] ] ]; try { $response = $client->postImporterStockOnHand($importerStock); echo "Importer stock posted!\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
6. Post Car Sales
$carSales = [ 'authorizationID' => 'RET001', 'vehsales' => [ 'retailerCode' => 'RET001', 'chassis' => '1HGBH41JXMN109186', 'invNo' => 'CARINV001', 'invDate' => '2026-01-13', 'custCode' => 'CUST003', 'custName' => 'Bob Johnson', 'custPhone' => '421901234567', 'custEMail' => 'bob@example.com', 'listPrice' => 45000.00, 'discountTotal' => 2000.00, 'netPrice' => 43000.00, 'taxTotal' => 8600.00, 'transactionPrice' => 51600.00, 'salesAdvName' => 'Sales Advisor 1', 'deliveryDate' => '2026-01-20' ] ]; try { $response = $client->postSalesCar($carSales); echo "Car sale posted!\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
7. Post Lead (with Test Drive and Offer)
$lead = [ 'authorizationID' => 'RET001', 'lead' => [ 'leadID' => 'LEAD001', 'retailerCode' => 'RET001', 'leadDate' => '2026-01-10', 'leadSource' => '05', // Online source 'custCode' => 'CUST004', 'custName' => 'Alice Brown', 'custPhone' => '421901234567', 'custEMail' => 'alice@example.com', 'body' => 'XC90', 'testDrive' => [ [ 'testDriveDate' => '2026-01-12', 'testDriveResult' => 'POSITIVE' ] ], 'offer' => [ [ 'offerDate' => '2026-01-13', 'offerAmount' => 52000.00, 'offerStatus' => 'PENDING' ] ] ] ]; try { $response = $client->postLead($lead); echo "Lead posted!\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
8. Delete Operations
All methods support deletion via toDelete parameter:
// Delete a job card (only key fields required) $jobCardDelete = [ 'authorizationID' => 'RET001', 'workOrder' => [ 'retailerCode' => 'RET001', 'woNumber' => 'WO123456', 'woYear' => '2026' ] ]; try { $response = $client->postJobCard($jobCardDelete, 'Y'); // toDelete = 'Y' echo "Job card deleted!\n"; } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; }
Error Handling
Response Codes
| RC | Meaning | Action |
|---|---|---|
| 1 | Success | Operation completed |
| -1 | Authorization failed | Check credentials |
| 164 | JWT expired | Auto-refreshed by client |
| 4403 | Mandatory field empty | Check required fields |
| 4410 | Invalid field value | Validate data |
| 4411 | Duplicate submission | Already submitted this month |
| 4417 | Duplicate part code | Check for duplicates |
Example Error Handling
try { $client->logIn(); $response = $client->postJobCard($jobCard); // Success echo "BodID: " . $response->bodID . "\n"; echo "Message: " . $response->details[0]->message . "\n"; } catch (Exception $e) { // Parse error message if (strpos($e->getMessage(), 'RC:4403') !== false) { echo "Missing mandatory field!\n"; } elseif (strpos($e->getMessage(), 'RC:164') !== false) { echo "Token expired, retrying...\n"; // Auto-handled, retry operation } else { echo "Error: " . $e->getMessage() . "\n"; } }
Migration from v1.0
Breaking Changes
1. URL Change (Required!)
// OLD (v1.0): 'base_url' => 'https://www.vario-ng.com/vci/vciws/postVNG/' // NEW (v3.0): 'environment' => 'production' // Auto-selects vibe.volvocars.biz
2. Stock Inventory Split (Required!)
// OLD (v1.0): $client->postStockOnHand($data); // REMOVED! // NEW (v3.0): $client->postRetailerStockOnHand($data); // For retailer inventory // OR $client->postImporterStockOnHand($data); // For importer inventory
3. Response Format
// v3.0 responses include details array: $response->bodID $response->authorizationID $response->details[0]->rc $response->details[0]->message
Migration Checklist
- Update
base_urlor setenvironmentparameter - Replace
postStockOnHand()calls with appropriate method - Update response parsing if accessing raw responses
- Test on QA environment first
- Deploy before January 15, 2026
API Methods
| Method | Description | Category |
|---|---|---|
logIn() |
Authenticate and get JWT token | Auth |
postJobCard($data, $toDelete) |
Submit service work order | A&B |
postSalesPart($data, $toDelete) |
Submit parts sales invoice | F |
postRetailerStockOnHand($data) |
Submit retailer inventory | D1 |
postImporterStockOnHand($data) |
Submit importer inventory | D2 |
postSalesCar($data, $toDelete) |
Submit car sales invoice | H |
postLead($data, $toDelete) |
Submit lead/test drive/offer | J/K/L |
Testing
Run tests:
composer test
Or manually test endpoints in tests/ directory:
export VIBE_USERNAME="your_username" export VIBE_PASSWORD="your_password" php tests/test_login.php php tests/test_all_endpoints.php
See tests/README.md for detailed testing instructions.
Requirements
- PHP >= 5.4.0
- ext-curl
- ext-json
- tcdent/php-restclient >= 0.1.7
License
GPL-3.0-only
Support
Issues: https://github.com/IntelliDust/VarioNG/issues
Documentation
docs/- Complete API specificationsTODO/- Migration plans and endpoint mappingtests/- Usage examples and test suite
Credits
- Author: Slavoj SANTA Hruska
- Email: vyvoj@santa3d.sk
- Homepage: https://github.com/IntelliDust/VarioNG
Changelog
v3.0.0 (2026-01-13)
- BREAKING: New base URL
vibe.volvocars.biz(old domain deprecated) - BREAKING:
postStockOnHand()removed, split intopostRetailerStockOnHand()andpostImporterStockOnHand() - BREAKING: Response format changed to include
details[]array - Added JWT token auto-refresh on expiration
- Added
postSalesCar()for vehicle sales tracking - Added
postLead()for lead management - Added delete support via
toDeleteparameter - Added environment switching (production/QA)
- Added UUID auto-generation for
bodID - Enhanced error handling with specific RC codes
v0.1.10 (Previous)
- Legacy v-1.0 implementation