@smartsides/oracle-ebs-sdk 1.0.6 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,10 @@ TypeScript SDK for Oracle EBS API - Optimized for Next.js 15+
5
5
  ## Features
6
6
 
7
7
  - ✅ **Full TypeScript Support** - Complete type safety with autocomplete
8
- - ✅ **All API Endpoints** - 9 modules covering all Oracle EBS functionality
8
+ - ✅ **All API Endpoints** - 10 modules covering all Oracle EBS functionality
9
+ - ✅ **AI Assistant** - ChatGPT-style chat with voice support ⭐ NEW
10
+ - ✅ **Auto User Context** - Stores full user object from login automatically
11
+ - ✅ **Smart Defaults** - Employee number auto-extracted for payslip/leave requests
9
12
  - ✅ **Error Handling** - Typed errors with automatic retry logic
10
13
  - ✅ **React Query Integration** - Built-in hooks for Next.js
11
14
  - ✅ **Server Components** - Optimized for Next.js App Router
@@ -34,16 +37,17 @@ const client = new OracleEBSClient({
34
37
  logging: { enabled: true },
35
38
  });
36
39
 
37
- // Login
38
- const { access_token } = await client.auth.login({
40
+ // Login - automatically stores user object
41
+ const response = await client.auth.login({
39
42
  username: 'your-username',
40
43
  password: 'your-password',
41
44
  });
45
+ // User info (EMPLOYEE_NUMBER, USER_ID, etc.) now available to all modules
42
46
 
43
47
  // Get leave types
44
48
  const types = await client.leaves.getRestrictedTypes();
45
49
 
46
- // Create leave request
50
+ // Create leave request - employee number auto-extracted
47
51
  const request = await client.leaves.createRequest({
48
52
  absenceTypeId: '71',
49
53
  startDate: '2026-01-20',
@@ -51,6 +55,11 @@ const request = await client.leaves.createRequest({
51
55
  days: 3,
52
56
  comments: 'Family vacation',
53
57
  });
58
+
59
+ // Get payslip - employee number auto-extracted
60
+ const header = await client.payslip.getHeader({
61
+ periodName: '6 2025'
62
+ });
54
63
  ```
55
64
 
56
65
  ### Next.js App Router (Client Component)
@@ -123,8 +132,12 @@ export default async function Page() {
123
132
  ### Authentication
124
133
 
125
134
  ```typescript
126
- // Login
127
- await client.auth.login({ username, password });
135
+ // Login - automatically stores full user object
136
+ const response = await client.auth.login({ username, password });
137
+ // User object now available to all modules:
138
+ // - userName, ebsVersion
139
+ // - USER_ID, PERSON_ID, EMPLOYEE_NUMBER
140
+ // - responsibilities
128
141
 
129
142
  // Get user context
130
143
  const user = await client.auth.getUserContext();
@@ -133,6 +146,8 @@ const user = await client.auth.getUserContext();
133
146
  await client.auth.logout();
134
147
  ```
135
148
 
149
+ **User Storage**: The SDK automatically stores the complete user object from the login response, making user information available to all modules without additional API calls.
150
+
136
151
  ### Leaves & Absences
137
152
 
138
153
  ```typescript
@@ -167,29 +182,85 @@ const result = await client.sitRequests.submit(data);
167
182
  ### Notifications
168
183
 
169
184
  ```typescript
170
- // Get notifications
185
+ // Get notification list (USER_ID auto-extracted from login)
171
186
  const notifications = await client.notifications.getList();
172
187
 
173
- // Get details
174
- const details = await client.notifications.getDetails({ notificationId: '123' });
188
+ // Get notification details
189
+ const details = await client.notifications.getDetails({
190
+ notificationId: '9341634'
191
+ });
192
+
193
+ // Get all details (SSHR_SIT_REQDTLS)
194
+ const allDetails = await client.notifications.getAllDetails({
195
+ notificationId: '9288875',
196
+ itemKey: '756840',
197
+ analysisCriteriaId: '3643761',
198
+ idFlexNum: '50310'
199
+ });
200
+
201
+ // Get absence details (for FYI notifications)
202
+ const absenceDetails = await client.notifications.getAbsenceDetails({
203
+ itemKey: '760206'
204
+ });
205
+
206
+ // Get action history
207
+ const history = await client.notifications.getActionHistory({
208
+ notificationId: '9330566'
209
+ });
175
210
 
176
- // Process approval
211
+ // Process approval (APPROVE or DECLINE)
177
212
  await client.notifications.processApproval({
178
- notificationId: '123',
213
+ notificationId: '9341634',
179
214
  action: 'APPROVE',
215
+ comments: 'Approved for processing',
216
+ responderUserName: 'optional'
217
+ });
218
+
219
+ // Close FYI notification (USER_ID auto-extracted)
220
+ await client.notifications.closeFyi({
221
+ notificationId: '9336582'
180
222
  });
181
223
  ```
182
224
 
225
+ **Auto User ID**: Notification methods automatically use USER_ID from the logged-in user where needed.
226
+
183
227
  ### Payslip
184
228
 
185
229
  ```typescript
186
- // Get header
187
- const header = await client.payslip.getHeader({ periodName: '6 2025' });
230
+ // Get header - employee number auto-extracted from login
231
+ const header = await client.payslip.getHeader({
232
+ periodName: '6 2025'
233
+ });
234
+
235
+ // Get details (earnings/deductions)
236
+ const details = await client.payslip.getDetails({
237
+ periodName: '6 2025'
238
+ });
239
+
240
+ // Get leave details
241
+ const leaveDetails = await client.payslip.getLeaveDetails({
242
+ periodName: '6 2025'
243
+ });
244
+
245
+ // Get run balances
246
+ const runBalances = await client.payslip.getRunBalances({
247
+ periodName: '6 2025'
248
+ });
188
249
 
189
- // Get details
190
- const details = await client.payslip.getDetails({ periodName: '6 2025' });
250
+ // Get accrual balances
251
+ const accrualBalances = await client.accrualBalances.getBalances({
252
+ calculationDate: '2025-06-30'
253
+ });
254
+
255
+ // Optional: Override employee number (for admin/HR use)
256
+ const header = await client.payslip.getHeader({
257
+ employeeNumber: '12345',
258
+ periodName: '6 2025'
259
+ });
191
260
  ```
192
261
 
262
+ **Auto Employee Number**: All payslip methods automatically use the employee number from the logged-in user. No need to pass it explicitly unless you're querying for a different employee.
263
+
193
264
  ### Employee
194
265
 
195
266
  ```typescript
@@ -223,6 +294,101 @@ const health = await client.health.check();
223
294
  const message = await client.health.ping();
224
295
  ```
225
296
 
297
+ ### AI Assistant 🆕
298
+
299
+ The AI module provides ChatGPT-style conversational AI with persistent chat history.
300
+
301
+ ```typescript
302
+ import { MessageRole } from '@smartsides/oracle-ebs-sdk';
303
+
304
+ // Send a chat message
305
+ const response = await client.ai.sendMessage({
306
+ message: 'Show my latest payslip',
307
+ language: 'en',
308
+ history: [
309
+ { role: MessageRole.USER, content: 'Hello' },
310
+ { role: MessageRole.ASSISTANT, content: 'Hi! How can I help?' }
311
+ ],
312
+ threadId: 'optional-thread-id' // For continuing existing conversation
313
+ });
314
+
315
+ // Response includes:
316
+ // - message: AI response text
317
+ // - ctas: Call-to-action buttons (if any)
318
+ // - metadata: Intent, tokens used, etc.
319
+ // - threadId: Thread ID for conversation persistence
320
+
321
+ // Voice message with thread continuity ⭐ NEW
322
+ // First voice message - creates new thread
323
+ const voiceResponse1 = await client.ai.sendVoice(audioBlob, {
324
+ language: 'en'
325
+ });
326
+ const threadId = voiceResponse1.response.threadId;
327
+
328
+ // Second voice message - continues same thread
329
+ const voiceResponse2 = await client.ai.sendVoice(audioBlob, {
330
+ language: 'en',
331
+ threadId: threadId // Maintains conversation context!
332
+ });
333
+
334
+ // Arabic voice with thread continuity
335
+ const arabicResponse = await client.ai.sendVoice(audioBlob, {
336
+ language: 'ar',
337
+ threadId: threadId // Works with Arabic too!
338
+ });
339
+
340
+ // Thread management
341
+ const threads = await client.ai.getChatThreads();
342
+ const messages = await client.ai.getThreadMessages(threadId);
343
+ await client.ai.deleteThread(threadId);
344
+ ```
345
+
346
+ **Voice Thread Continuity Benefits:**
347
+ - ✅ Multi-turn voice conversations (request → confirmation → submission)
348
+ - ✅ Maintains full conversation context across voice messages
349
+ - ✅ Works with both English and Arabic
350
+ - ✅ Automatic leave type mapping and date extraction
351
+ - ✅ Seamless voice-based leave request submission
352
+
353
+ // Transcribe voice to text
354
+ const transcription = await client.ai.transcribeVoice(audioBlob);
355
+
356
+ // Get all chat threads for current user
357
+ const threads = await client.ai.getChatThreads();
358
+ // Returns: Array of ChatThread objects with id, title, messageCount, etc.
359
+
360
+ // Get messages for a specific thread
361
+ const { thread, messages } = await client.ai.getThreadMessages(threadId);
362
+ // Returns: Thread metadata + array of ThreadMessage objects
363
+
364
+ // Create a new chat thread
365
+ const newThread = await client.ai.createThread();
366
+ // Returns: ChatThread object with new thread ID
367
+
368
+ // Delete a chat thread
369
+ await client.ai.deleteThread(threadId);
370
+ // Deletes thread and all associated messages
371
+ ```
372
+
373
+ **Chat History Features:**
374
+ - ✅ **Persistent Threads**: All conversations saved to PostgreSQL
375
+ - ✅ **Auto-Title Generation**: Thread titles generated from first message
376
+ - ✅ **Message History**: Complete conversation history with timestamps
377
+ - ✅ **User-Scoped**: Each user only sees their own threads
378
+ - ✅ **Secure**: JWT authentication + ownership validation
379
+
380
+ **Available Types:**
381
+ ```typescript
382
+ import {
383
+ MessageRole, // USER | ASSISTANT
384
+ ActionType, // Enum for action types
385
+ UserIntent, // Enum for user intents
386
+ ChatThread, // Thread metadata type
387
+ ThreadMessage, // Message type
388
+ CallToAction, // CTA button type
389
+ } from '@smartsides/oracle-ebs-sdk';
390
+ ```
391
+
226
392
  ## Error Handling
227
393
 
228
394
  ```typescript
@@ -270,6 +436,69 @@ const client = new OracleEBSClient({
270
436
  });
271
437
  ```
272
438
 
439
+ ## 🔒 Security Best Practices
440
+
441
+ ### CRITICAL: Never Decode JWT Tokens
442
+
443
+ **❌ NEVER do this in your application:**
444
+
445
+ ```typescript
446
+ // DON'T decode JWT on the client side
447
+ const token = localStorage.getItem('token');
448
+ const payload = JSON.parse(atob(token.split('.')[1]));
449
+ const employeeNumber = payload.EMPLOYEE_NUMBER;
450
+
451
+ // DON'T pass user context to SDK methods
452
+ await client.payslip.getHeader({ periodName, employeeNumber });
453
+ ```
454
+
455
+ **✅ ALWAYS do this instead:**
456
+
457
+ ```typescript
458
+ // Just set the token and pass business parameters
459
+ client.setToken(token);
460
+ await client.payslip.getHeader({ periodName });
461
+ // Backend extracts employeeNumber from JWT automatically
462
+ ```
463
+
464
+ ### Why This Matters
465
+
466
+ 1. **Security**: JWT payload is base64 encoded, NOT encrypted - anyone can read it
467
+ 2. **Single Source of Truth**: Backend validates and extracts user context
468
+ 3. **Prevents Tampering**: Users cannot access other users' data
469
+ 4. **Maintainability**: JWT structure changes don't affect your code
470
+ 5. **Consistency**: All endpoints follow the same pattern
471
+
472
+ ### How It Works
473
+
474
+ The SDK sends the JWT token in the Authorization header. The backend:
475
+ 1. Validates the token signature
476
+ 2. Checks token expiration
477
+ 3. Extracts user context (employeeNumber, userId, personId, etc.)
478
+ 4. Uses this context to fetch user-specific data
479
+
480
+ ### Affected Methods
481
+
482
+ All user-specific methods automatically use JWT context:
483
+ - ✅ `payslip.getHeader()` - Uses EMPLOYEE_NUMBER from JWT
484
+ - ✅ `payslip.getDetails()` - Uses EMPLOYEE_NUMBER from JWT
485
+ - ✅ `leaves.getRestrictedTypes()` - Uses USER_ID, PERSON_ID from JWT
486
+ - ✅ `leaves.createRequest()` - Uses PERSON_ID, USER_ID from JWT
487
+ - ✅ `notifications.getList()` - Uses USER_ID from JWT
488
+ - ✅ `notifications.closeFyi()` - Uses USER_ID from JWT
489
+ - ✅ `employee.getPersonalInfo()` - Uses context from JWT
490
+ - ✅ `accrualBalances.getBalances()` - Uses context from JWT
491
+
492
+ **You only pass business parameters** (periodName, dates, etc.) - never user context.
493
+
494
+ ### Reference
495
+
496
+ For complete security documentation, see:
497
+ - [Backend JWT Security Pattern](../docs/JWT_SECURITY_PATTERN.md)
498
+ - [Backend Documentation](../docs/index.md#-core-security-principles)
499
+
500
+ ---
501
+
273
502
  ## React Query Hooks
274
503
 
275
504
  All hooks are available from `@smartsides/oracle-ebs-sdk/hooks`:
@@ -282,6 +511,10 @@ All hooks are available from `@smartsides/oracle-ebs-sdk/hooks`:
282
511
  - `useSitSegments(client, params, options)`
283
512
  - `useNotifications(client, options)`
284
513
  - `usePayslipHeader(client, params, options)`
514
+ - `usePayslipDetails(client, params, options)`
515
+ - `usePayslipLeaveDetails(client, params, options)`
516
+ - `usePayslipRunBalances(client, params, options)`
517
+ - `useAccrualBalances(client, params, options)`
285
518
  - `usePersonalInfo(client, options)`
286
519
  - `useHealthCheck(client, options)`
287
520
  - And more...
@@ -305,6 +538,38 @@ const client = createServerClientFromCookies(cookies());
305
538
  const client = createServerClientFromHeaders(headers());
306
539
  ```
307
540
 
541
+ ## Key Improvements (v1.0.6)
542
+
543
+ ### Automatic User Context Storage
544
+ The SDK now automatically stores the complete user object from the login response, eliminating the need to:
545
+ - Make additional API calls for user information
546
+ - Pass employee number to every payslip/leave request
547
+ - Manually manage user context in your application
548
+
549
+ ### Smart Employee Number Extraction
550
+ All payslip and leave methods automatically use the employee number from the logged-in user:
551
+ ```typescript
552
+ // Before (v1.0.2)
553
+ await client.payslip.getHeader({
554
+ employeeNumber: '4446', // Had to pass manually
555
+ periodName: '6 2025'
556
+ });
557
+
558
+ // After (v1.0.6)
559
+ await client.payslip.getHeader({
560
+ periodName: '6 2025' // Employee number auto-extracted!
561
+ });
562
+ ```
563
+
564
+ ### Full User Object Access
565
+ All modules can now access the complete user object:
566
+ - `userName` - Username
567
+ - `ebsVersion` - Oracle EBS version
568
+ - `USER_ID` - User ID
569
+ - `PERSON_ID` - Person ID
570
+ - `EMPLOYEE_NUMBER` - Employee number
571
+ - `responsibilities` - User responsibilities
572
+
308
573
  ## License
309
574
 
310
575
  PROPRIETARY - SmartSides Development Team