@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
Markdown
# @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).