UNPKG

@ritas-inc/sapb1commandapi-client

Version:

A stateless TypeScript client for SAP B1 Service Layer Command API with comprehensive error handling, type safety, and batch operations

498 lines (400 loc) 10.8 kB
# @ritas-inc/sapb1commandapi-client A stateless TypeScript client for the SAP B1 Service Layer Command API. This client provides a clean, type-safe interface for managing production plans and work orders in SAP Business One. ## Features - 🚀 **Modern TypeScript**: Built for Node.js 24+ with native ESM support - 🔒 **Type-safe**: Full TypeScript support with Zod schema validation - 🔄 **Automatic retry**: Built-in retry logic for network failures - 🏗️ **Stateless design**: No session management needed - perfect for serverless - 📦 **Minimal dependencies**: Only axios, axios-retry, and zod required - 🛡️ **Comprehensive error handling**: Detailed error messages with RFC 7807 Problem Details format - **Consistent API responses**: All responses follow a unified success/error structure ## Requirements - Node.js >= 24.0.0 - SAP B1 Service Layer Command API endpoint ## Installation ```bash npm install @ritas-inc/sapb1commandapi-client ``` ## Quick Start ```typescript import { SAPB1CommandClient } from '@ritas-inc/sapb1commandapi-client'; // Initialize the client const client = new SAPB1CommandClient({ baseUrl: 'https://your-api-endpoint.com', timeout: 30000, retryConfig: { retries: 3 } }); // Authenticate const authResponse = await client.auth.login({ dbName: 'YourDatabase', user: 'manager', password: 'your-password' }); if (authResponse.success) { const userId = authResponse.data.userId; // Create a plan const planResponse = await client.plans.create(userId, 12345, [ { itemCode: 'ITEM001', quantity: 10 }, { itemCode: 'ITEM002', quantity: 5 } ]); if (planResponse.success) { console.log(`Created plan with ID: ${planResponse.data.planId}`); } } ``` ## API Response Structure All API responses follow a consistent structure: ### Success Response ```typescript { success: true, data: T, // Response data (type varies by endpoint) metadata?: { // Optional metadata // Additional information like timestamps, user info, etc. } } ``` ### Error Response ```typescript { success: false, problem: { status: number, // HTTP status code type: 'unauthorized' | 'bad-request' | 'not-found' | 'internal-server-error', // Error type identifier title: string, // Error title detail: string, // Detailed error message instance: string, // Request path context: { request: string, // Request method and path responseText: string // Raw response text }, issues: string[] // List of specific issues } } ``` ## API Reference ### Client Configuration ```typescript const client = new SAPB1CommandClient({ baseUrl: string; // Required: API base URL timeout?: number; // Optional: Request timeout (default: 30000ms) retryConfig?: { retries?: number; // Number of retries (default: 3) retryDelay?: (retryCount: number) => number; retryCondition?: (error: any) => boolean; }; headers?: Record<string, string>; // Optional: Custom headers }); ``` ### Authentication #### Login ```typescript const response = await client.auth.login({ dbName: string; user: string; password: string; }); // Success response type: { success: true, data: { userId: string }, metadata?: { CompanyDB: string, UserName: string } } ``` The `userId` from the response must be passed to all subsequent authenticated requests. ### Plans Management #### Create Plan ```typescript const response = await client.plans.create( userId: string, user: number, // Planner user ID products: Array<{ itemCode: string; quantity: number; // Must be positive }> ); // Success response type: { success: true, data: { planId: number } } ``` #### Update Plan Status ```typescript const response = await client.plans.updateStatus( userId: string, planId: number, status: 'Draft' | 'Generating' | 'Generated' | 'Releasing' | 'Released' | 'Completed' | 'Canceling' | 'Canceled' ); ``` #### Update Plan Products ```typescript const response = await client.plans.updateProducts( userId: string, planId: number, products: Array<{ itemCode: string; quantity: number; }> ); ``` #### Cancel Plan ```typescript const response = await client.plans.cancel(userId: string, planId: number); ``` ### Work Orders Management #### Create Work Order ```typescript const response = await client.workOrders.create( userId: string, planId: number, workOrder: { itemCode: string; quantity: number; injections?: number; }, origin?: { origin: 'manual' | 'workorder', originAbsoluteEntry: number, originDocumentNumber: number } ); // Success response type: { success: true, data: { absoluteEntry: number, documentEntry: number, createDate: string } } ``` #### Release Work Order ```typescript const response = await client.workOrders.release( userId: string, workOrderId: number ); ``` #### Cancel Work Order ```typescript const response = await client.workOrders.cancel( userId: string, workOrderId: number ); ``` ### Batch Operations #### Create Multiple Work Orders ```typescript const response = await client.workOrders.batch.create(userId, [ { planId: number, workOrder: { itemCode: string, quantity: number, injections: number }, origin: { origin: 'manual' | 'workorder', originAbsoluteEntry: number, originDocumentNumber: number } }, // ... more work orders ]); // Success response type: { success: true, data: Array<{ absoluteEntry: number, documentEntry: number, createDate: string }> } ``` #### Release Multiple Work Orders ```typescript const response = await client.workOrders.batch.release( userId: string, workOrderIds: number[] ); // Success response type: { success: true, data: Array<{ absoluteEntry: number }> } ``` #### Cancel Multiple Work Orders ```typescript const response = await client.workOrders.batch.cancel( userId: string, workOrderIds: number[] ); // Success response type: { success: true, data: Array<{ absoluteEntry: number }> } ``` ### Health Check ```typescript const health = await client.health(); // No authentication required ``` ## Error Handling The client provides detailed error information through custom error classes: ```typescript import { SAPB1APIError, AuthError, NetworkError, ValidationError, isErrorResponse } from '@ritas-inc/sapb1commandapi-client'; try { const response = await client.auth.login({ ... }); // Type-safe error checking if (!response.success) { console.error('Login failed:', response.problem.detail); console.error('Issues:', response.problem.issues); return; } // Success - TypeScript knows response.data exists const userId = response.data.userId; } catch (error) { if (error instanceof AuthError) { console.error('Authentication failed:', error.detail); console.error('Issues:', error.issues); } else if (error instanceof ValidationError) { console.error('Validation failed:', error.issues); } else if (error instanceof NetworkError) { console.error('Network error:', error.message); } else if (error instanceof SAPB1APIError) { console.error('API error:', { status: error.status, type: error.type, title: error.title, detail: error.detail, issues: error.issues }); } } ``` ### Using the Error Response Helper ```typescript const response = await client.plans.create(userId, user, products); if (isErrorResponse(response)) { // TypeScript knows this is an error response console.error(`Error: ${response.problem.detail}`); } else { // TypeScript knows this is a success response console.log(`Created plan: ${response.data.planId}`); } ``` ## Stateless Design This client is completely stateless - it does not store any session information. You must: 1. Store the `userId` returned from login 2. Pass the `userId` to every authenticated API call 3. Handle session expiry (30 minutes) by re-authenticating when needed This design makes the client perfect for: - Serverless functions - Multi-tenant applications - Microservices - Load-balanced environments ## TypeScript Support All methods are fully typed with TypeScript. The client exports all necessary types: ```typescript import type { // Request types AuthRequest, CreatePlanRequest, UpdatePlanStatusRequest, UpdatePlanProductsRequest, CreateWorkOrderItem, // Response types AuthResponse, CreatePlanResponse, UpdatePlanStatusResponse, CreateWorkOrderResponse, CreateWorkOrderBatchResponse, // Enum types PlanStatus, WorkOrderOriginType, // Configuration ClientConfig } from '@ritas-inc/sapb1commandapi-client'; ``` ## Testing This package includes comprehensive tests: ### Unit Tests ```bash npm test ``` ### Integration Tests Integration tests perform a complete API lifecycle test: 1. Authenticate with SAP B1 2. Create plan with 3 products 3. Update plan with 4 products 4. Generate 4 work orders 5. Release and cancel individual work orders 6. Batch release and cancel work orders Configure test environment: ```bash cp .env.test.example .env.test # Edit .env.test with your test API credentials INTEGRATION_TEST=true npm test ``` See [tests/README.md](tests/README.md) for detailed testing documentation. ## Migration Guide from v1.0.x Version 1.1.0 introduces a new response structure. Here are the key changes: ### Response Structure All responses now follow a consistent success/error pattern: ```typescript // Before (v1.0.x) const { userId } = await client.auth.login(credentials); // After (v1.1.0) const response = await client.auth.login(credentials); if (response.success) { const userId = response.data.userId; } ``` ### Error Handling Errors are now included in the response object: ```typescript // Before (v1.0.x) try { const plan = await client.plans.create(userId, user, products); } catch (error) { // Handle error } // After (v1.1.0) const response = await client.plans.create(userId, user, products); if (!response.success) { console.error(response.problem.detail); } ``` ### Type Imports Types are now exported from schema files: ```typescript // Before (v1.0.x) import type { AuthRequest } from '@ritas-inc/sapb1commandapi-client/types'; // After (v1.1.0) import type { AuthRequest } from '@ritas-inc/sapb1commandapi-client'; ``` ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## Support For issues and feature requests, please use the [GitHub issue tracker](https://github.com/ritas-inc/sapb1commandapi-client/issues).