# @isa/availability/data-access

A comprehensive product availability service for Angular applications supporting multiple order types and delivery methods across retail operations.

## Overview

The Availability Data Access library provides a unified interface for checking product availability across six different order types: in-store pickup (Rücklage), customer pickup (Abholung), standard shipping (Versand), digital shipping (DIG-Versand), B2B shipping (B2B-Versand), and digital downloads (Download). It integrates with the generated availability API client and provides intelligent routing, validation, and transformation of availability data.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [Order Types](#order-types)
- [Validation and Business Rules](#validation-and-business-rules)
- [Error Handling](#error-handling)
- [Testing](#testing)
- [Architecture Notes](#architecture-notes)

## Features

- **Six order type support** - InStore, Pickup, Delivery, DIG-Versand, B2B-Versand, Download
- **Intelligent routing** - Automatic endpoint selection based on order type
- **Zod validation** - Runtime schema validation for all parameters
- **Request cancellation** - AbortSignal support for all operations
- **Batch and single-item APIs** - Flexible interfaces for different use cases
- **Preferred availability selection** - Automatic selection of preferred suppliers
- **Business rule enforcement** - Download validation, B2B logistician override
- **Type-safe transformations** - Adapter pattern for API request/response mapping
- **Comprehensive logging** - Integration with @isa/core/logging for debugging
- **Stock integration** - Direct stock service integration for in-store availability

## Quick Start

### 1. Import and Inject

```typescript
import { Component, inject } from '@angular/core';
import { AvailabilityService } from '@isa/availability/data-access';

@Component({
  selector: 'app-product-detail',
  template: '...'
})
export class ProductDetailComponent {
  #availabilityService = inject(AvailabilityService);
}
```

### 2. Check Availability for Multiple Items

```typescript
async checkAvailability(): Promise<void> {
  const availabilities = await this.#availabilityService.getAvailabilities({
    orderType: 'Versand',
    items: [
      { itemId: 123, ean: '1234567890123', quantity: 2 },
      { itemId: 456, ean: '9876543210987', quantity: 1 }
    ]
  });

  // Result: { '123': Availability, '456': Availability }
  const item123Availability = availabilities['123'];
  console.log(`Item 123 status: ${item123Availability.status}`);
  console.log(`Item 123 quantity: ${item123Availability.qty}`);
}
```

### 3. Check Availability for Single Item

```typescript
async checkSingleItem(): Promise<void> {
  const availability = await this.#availabilityService.getAvailability({
    orderType: 'Versand',
    item: { itemId: 123, ean: '1234567890123', quantity: 1 }
  });

  if (availability) {
    console.log(`Available: ${availability.qty} units`);
    console.log(`Price: ${availability.price?.value?.value}`);
  } else {
    console.log('Item not available');
  }
}
```

### 4. Request Cancellation

```typescript
async checkWithCancellation(): Promise<void> {
  const abortController = new AbortController();

  // Cancel after 5 seconds
  setTimeout(() => abortController.abort(), 5000);

  try {
    const availabilities = await this.#availabilityService.getAvailabilities(
      {
        orderType: 'Versand',
        items: [{ itemId: 123, ean: '1234567890123', quantity: 1 }]
      },
      abortController.signal
    );
  } catch (error) {
    console.log('Request cancelled or failed');
  }
}
```

## Core Concepts

### Order Types

The library supports six distinct order types, each with specific requirements and behavior:

#### 1. InStore (Rücklage)
- **Purpose**: Branch-based in-store availability for customer reservation
- **Endpoint**: Stock service (not availability API)
- **Required**: branchId, itemsIds array
- **Special handling**: Uses RemissionStockService to fetch real-time stock quantities

#### 2. Pickup (Abholung)
- **Purpose**: Customer pickup at branch location
- **Endpoint**: Store availability API
- **Required**: branchId, items array with itemId, ean, quantity
- **Special handling**: Uses store endpoint with branch context

#### 3. Delivery (Versand)
- **Purpose**: Standard shipping to customer address
- **Endpoint**: Shipping availability API
- **Required**: items array with itemId, ean, quantity
- **Special handling**: Excludes supplier/logistician fields to prevent automatic orderType change

#### 4. DIG-Versand
- **Purpose**: Digital shipping for webshop customers
- **Endpoint**: Shipping availability API
- **Required**: items array with itemId, ean, quantity
- **Special handling**: Standard transformation, includes supplier/logistician

#### 5. B2B-Versand
- **Purpose**: Business-to-business shipping with specific logistician
- **Endpoint**: Store availability API
- **Required**: items array with itemId, ean, quantity
- **Special handling**:
  - Automatically fetches default branch (no branchId parameter needed)
  - Fetches logistician '2470' and overrides response logisticianId
  - Uses store endpoint (not shipping)

#### 6. Download
- **Purpose**: Digital product downloads
- **Endpoint**: Shipping availability API
- **Required**: items array with itemId, ean (no quantity)
- **Special handling**:
  - Quantity forced to 1
  - Validates download availability (supplier 16 with 0 stock = unavailable)
  - Validates status codes against whitelist

### Availability Response Structure

```typescript
interface Availability {
  itemId: number;                    // Product item ID
  status: AvailabilityType;          // Availability status code (see below)
  qty: number;                       // Available quantity
  ssc?: string;                      // Shipping service code
  sscText?: string;                  // Shipping service description
  supplierId?: number;               // Supplier ID
  supplier?: string;                 // Supplier name
  logisticianId?: number;            // Logistician ID
  logistician?: string;              // Logistician name
  price?: Price;                     // Current price with VAT
  priceMaintained?: boolean;         // Price maintenance flag
  at?: string;                       // Estimated delivery date (ISO format)
  altAt?: string;                    // Alternative delivery date
  requestStatusCode?: string;        // Request status from API
  preferred?: number;                // Preferred availability flag (1 = preferred)
}
```

### Availability Type Codes

```typescript
const AvailabilityType = {
  NotSet: 0,                  // Not determined
  NotAvailable: 1,            // Not available
  PrebookAtBuyer: 2,          // Pre-order at buyer
  PrebookAtRetailer: 32,      // Pre-order at retailer
  PrebookAtSupplier: 256,     // Pre-order at supplier
  TemporaryNotAvailable: 512, // Temporarily unavailable
  Available: 1024,            // Available for immediate delivery
  OnDemand: 2048,             // Available on demand
  AtProductionDate: 4096,     // Available at production date
  Discontinued: 8192,         // Discontinued product
  EndOfLife: 16384,           // End of life product
};
```

### Validation with Zod

All input parameters are validated using Zod schemas before processing:

```typescript
// Example: Delivery availability params
const params = {
  orderType: 'Versand',
  items: [
    {
      itemId: '123',        // Coerced to number
      ean: '1234567890123',
      quantity: '2',        // Coerced to number
      price: { ... }
    }
  ]
};

// Validation happens automatically
const result = await service.getAvailabilities(params);
// Throws ZodError if validation fails
```

## API Reference

### AvailabilityService

Main service for checking product availability across order types.

#### `getAvailabilities(params, abortSignal?): Promise<{ [itemId: string]: Availability }>`

Checks availability for multiple items based on order type.

**Parameters:**
- `params: GetAvailabilityInputParams` - Availability parameters (automatically validated)
- `abortSignal?: AbortSignal` - Optional abort signal for request cancellation

**Returns:** Promise resolving to dictionary mapping itemId to Availability

**Throws:**
- `ZodError` - If params validation fails
- `ResponseArgsError` - If API returns an error
- `Error` - If default branch/logistician not found (B2B only)

**Example:**
```typescript
const availabilities = await service.getAvailabilities({
  orderType: 'Versand',
  items: [
    { itemId: 123, ean: '1234567890', quantity: 2 },
    { itemId: 456, ean: '0987654321', quantity: 1 }
  ]
});

// Result: { '123': Availability, '456': Availability }
```

#### `getAvailability(params, abortSignal?): Promise<Availability | undefined>`

Checks availability for a single item.

**Parameters:**
- `params: GetSingleItemAvailabilityInputParams` - Single item parameters (automatically validated)
- `abortSignal?: AbortSignal` - Optional abort signal for request cancellation

**Returns:** Promise resolving to Availability, or undefined if not available

**Throws:**
- `ZodError` - If params validation fails
- `ResponseArgsError` - If API returns an error

**Example:**
```typescript
const availability = await service.getAvailability({
  orderType: 'Versand',
  item: { itemId: 123, ean: '1234567890', quantity: 1 }
});

if (availability) {
  console.log(`Available: ${availability.qty} units`);
}
```

### AvailabilityFacade

Pass-through facade for AvailabilityService.

**Note**: This facade is currently under architectural review. It provides no additional value over direct service injection and may be removed in a future refactoring. Consider injecting `AvailabilityService` directly.

```typescript
// Current pattern (via facade)
#availabilityFacade = inject(AvailabilityFacade);

// Recommended pattern (direct service)
#availabilityService = inject(AvailabilityService);
```

### Helper Functions

#### `isDownloadAvailable(availability): boolean`

Validates if a download item is available based on business rules.

**Business Rules:**
- Supplier ID 16 with 0 stock = unavailable
- Must have valid availability type code (see VALID_DOWNLOAD_STATUS_CODES)

**Parameters:**
- `availability: Availability | null | undefined` - Availability to validate

**Returns:** true if download is available, false otherwise

**Example:**
```typescript
import { isDownloadAvailable } from '@isa/availability/data-access';

if (isDownloadAvailable(availability)) {
  console.log('Download ready');
}
```

#### `selectPreferredAvailability(availabilities): Availability | undefined`

Selects the preferred availability from a list (marked with `preferred === 1`).

**Parameters:**
- `availabilities: Availability[]` - List of availability options

**Returns:** The preferred availability, or undefined if none found

**Example:**
```typescript
import { selectPreferredAvailability } from '@isa/availability/data-access';

const preferred = selectPreferredAvailability(apiResponse);
```

#### `calculateEstimatedDate(availability): string | undefined`

Calculates the estimated shipping/delivery date based on API response.

**Business Rule:**
- If requestStatusCode === '32', use altAt (alternative date)
- Otherwise, use at (standard date)

**Parameters:**
- `availability: Availability | null | undefined` - Availability data

**Returns:** The estimated date string (ISO format), or undefined

**Example:**
```typescript
import { calculateEstimatedDate } from '@isa/availability/data-access';

const estimatedDate = calculateEstimatedDate(availability);
console.log(`Delivery expected: ${estimatedDate}`);
```

#### `hasValidPrice(availability): boolean`

Type guard to check if an availability has a valid price.

**Parameters:**
- `availability: Availability | null | undefined` - Availability to check

**Returns:** true if availability has a price with a value > 0

**Example:**
```typescript
import { hasValidPrice } from '@isa/availability/data-access';

if (hasValidPrice(availability)) {
  // TypeScript narrows type - price is guaranteed to exist
  console.log(`Price: ${availability.price.value.value}`);
}
```

#### `isPriceMaintained(availability): boolean`

Checks if an availability is price-maintained.

**Parameters:**
- `availability: Availability | null | undefined` - Availability to check

**Returns:** true if price-maintained flag is set

## Usage Examples

### Checking In-Store Availability (Rücklage)

```typescript
import { Component, inject } from '@angular/core';
import { AvailabilityService } from '@isa/availability/data-access';

@Component({
  selector: 'app-in-store-check',
  template: '...'
})
export class InStoreCheckComponent {
  #availabilityService = inject(AvailabilityService);

  async checkInStoreAvailability(branchId: number, itemIds: number[]): Promise<void> {
    const availabilities = await this.#availabilityService.getAvailabilities({
      orderType: 'Rücklage',
      branchId: branchId,
      itemsIds: itemIds
    });

    for (const [itemId, availability] of Object.entries(availabilities)) {
      console.log(`Item ${itemId}: ${availability.qty} in stock`);
    }
  }
}
```

### Checking Pickup Availability (Abholung)

```typescript
async checkPickupAvailability(branchId: number): Promise<void> {
  const availabilities = await this.#availabilityService.getAvailabilities({
    orderType: 'Abholung',
    branchId: branchId,
    items: [
      { itemId: 123, ean: '1234567890', quantity: 2 },
      { itemId: 456, ean: '0987654321', quantity: 1 }
    ]
  });

  // Check if items are available for pickup
  for (const [itemId, availability] of Object.entries(availabilities)) {
    if (availability.status === AvailabilityType.Available) {
      console.log(`Item ${itemId} ready for pickup at branch ${branchId}`);
    }
  }
}
```

### Checking Standard Delivery (Versand)

```typescript
import { AvailabilityType, calculateEstimatedDate } from '@isa/availability/data-access';

async checkDeliveryAvailability(): Promise<void> {
  const availabilities = await this.#availabilityService.getAvailabilities({
    orderType: 'Versand',
    items: [
      {
        itemId: 123,
        ean: '1234567890',
        quantity: 1,
        price: {
          value: { value: 19.99, currency: 'EUR', currencySymbol: '€' },
          vat: { value: 3.18, inPercent: 19, label: '19%', vatType: 1 }
        }
      }
    ]
  });

  const item123 = availabilities['123'];
  if (item123) {
    const estimatedDate = calculateEstimatedDate(item123);
    console.log(`Available for delivery: ${item123.qty} units`);
    console.log(`Estimated delivery: ${estimatedDate}`);
    console.log(`Supplier: ${item123.supplier} (ID: ${item123.supplierId})`);
  }
}
```

### Checking B2B Delivery (B2B-Versand)

```typescript
async checkB2BDelivery(): Promise<void> {
  // No branchId required - automatically uses default branch
  // Logistician '2470' is automatically fetched and applied
  const availabilities = await this.#availabilityService.getAvailabilities({
    orderType: 'B2B-Versand',
    items: [
      { itemId: 123, ean: '1234567890', quantity: 10 }
    ]
  });

  const item123 = availabilities['123'];
  if (item123) {
    console.log(`B2B availability: ${item123.qty} units`);
    console.log(`Logistician: ${item123.logisticianId} (overridden to 2470)`);
  }
}
```

### Checking Download Availability

```typescript
import { isDownloadAvailable } from '@isa/availability/data-access';

async checkDownloadAvailability(): Promise<void> {
  const availabilities = await this.#availabilityService.getAvailabilities({
    orderType: 'Download',
    items: [
      { itemId: 123, ean: '1234567890' }  // No quantity needed
    ]
  });

  const item123 = availabilities['123'];
  if (item123 && isDownloadAvailable(item123)) {
    console.log('Download ready for immediate delivery');
  } else {
    console.log('Download not available');
  }
}
```

### Single Item with AbortSignal

```typescript
async checkSingleItemWithTimeout(): Promise<void> {
  const abortController = new AbortController();

  // Set 10 second timeout
  const timeoutId = setTimeout(() => {
    abortController.abort();
  }, 10000);

  try {
    const availability = await this.#availabilityService.getAvailability(
      {
        orderType: 'Versand',
        item: { itemId: 123, ean: '1234567890', quantity: 1 }
      },
      abortController.signal
    );

    clearTimeout(timeoutId);

    if (availability) {
      console.log(`Item available: ${availability.qty} units`);
    }
  } catch (error) {
    clearTimeout(timeoutId);
    console.error('Request failed or timed out', error);
  }
}
```

### Handling Multiple Order Types

```typescript
import { OrderType } from '@isa/checkout/data-access';

async checkMultipleOrderTypes(
  orderType: OrderType,
  items: Array<{ itemId: number; ean: string; quantity: number }>
): Promise<void> {
  let params: GetAvailabilityInputParams;

  switch (orderType) {
    case 'Rücklage':
      params = {
        orderType: 'Rücklage',
        branchId: this.selectedBranchId,
        itemsIds: items.map(i => i.itemId)
      };
      break;
    case 'Abholung':
      params = {
        orderType: 'Abholung',
        branchId: this.selectedBranchId,
        items: items
      };
      break;
    case 'Versand':
    case 'DIG-Versand':
      params = {
        orderType: orderType,
        items: items
      };
      break;
    case 'B2B-Versand':
      params = {
        orderType: 'B2B-Versand',
        items: items
      };
      break;
    case 'Download':
      params = {
        orderType: 'Download',
        items: items.map(i => ({ itemId: i.itemId, ean: i.ean }))
      };
      break;
  }

  const availabilities = await this.#availabilityService.getAvailabilities(params);

  console.log(`${orderType} availability:`, availabilities);
}
```

## Order Types

### Parameter Requirements by Order Type

| Order Type | Required Parameters | Optional | Notes |
|------------|-------------------|----------|-------|
| **Rücklage** (InStore) | `orderType`, `itemsIds` | `branchId` | Uses stock service |
| **Abholung** (Pickup) | `orderType`, `branchId`, `items` | - | Store endpoint |
| **Versand** (Delivery) | `orderType`, `items` | - | Shipping endpoint, excludes supplier/logistician |
| **DIG-Versand** | `orderType`, `items` | - | Shipping endpoint |
| **B2B-Versand** | `orderType`, `items` | - | Fetches default branch + logistician 2470 |
| **Download** | `orderType`, `items` (no quantity) | - | Quantity forced to 1, validation applied |

### Item Structure by Order Type

#### InStore (Rücklage)
```typescript
{
  orderType: 'Rücklage',
  branchId?: number,           // Optional branch ID
  itemsIds: number[]           // Array of item IDs only
}
```

#### Pickup, Delivery, DIG-Versand, B2B-Versand
```typescript
{
  orderType: 'Abholung' | 'Versand' | 'DIG-Versand' | 'B2B-Versand',
  branchId?: number,           // Required only for Abholung
  items: Array<{
    itemId: number,
    ean: string,
    quantity: number,
    price?: Price              // Optional price information
  }>
}
```

#### Download
```typescript
{
  orderType: 'Download',
  items: Array<{
    itemId: number,
    ean: string,
    price?: Price              // Optional price information
    // No quantity field - always 1
  }>
}
```

## Validation and Business Rules

### Zod Schema Validation

All parameters are validated using Zod schemas before processing:

**Type Coercion:**
```typescript
// String to number coercion
{ itemId: '123' }  →  { itemId: 123 }
{ quantity: '2' }  →  { quantity: 2 }

// Validation requirements
itemId: z.coerce.number().int().positive()  // Must be positive integer
quantity: z.coerce.number().int().positive().default(1)  // Positive with default
ean: z.string()  // Required string
```

**Minimum Array Lengths:**
```typescript
items: z.array(ItemSchema).min(1)  // At least 1 item required
itemsIds: z.array(z.coerce.number()).min(1)  // At least 1 ID required
```

### Download Validation Rules

Downloads have special validation requirements enforced by `isDownloadAvailable()`:

1. **Supplier 16 with 0 stock = unavailable**
   ```typescript
   if (availability.supplierId === 16 && availability.qty === 0) {
     return false;  // Not available
   }
   ```

2. **Valid status codes for downloads**
   ```typescript
   const VALID_CODES = [
     AvailabilityType.PrebookAtBuyer,       // 2
     AvailabilityType.PrebookAtRetailer,    // 32
     AvailabilityType.PrebookAtSupplier,    // 256
     AvailabilityType.Available,            // 1024
     AvailabilityType.OnDemand,             // 2048
     AvailabilityType.AtProductionDate      // 4096
   ];
   ```

### B2B Special Handling

B2B-Versand has unique requirements:

1. **Automatic default branch fetching**
   - No branchId parameter required
   - Service automatically fetches default branch via `BranchService`
   - Throws error if default branch has no ID

2. **Logistician 2470 override**
   - Automatically fetches logistician '2470'
   - Overrides all availability responses with this logisticianId
   - Throws error if logistician 2470 not found

3. **Store endpoint usage**
   - Uses store availability endpoint (not shipping)
   - Similar to Pickup but with automatic branch selection

### Preferred Availability Selection

When multiple availability options exist for an item:

```typescript
// API might return multiple availabilities per item
// The service automatically selects the preferred one
const preferred = availabilities.find(av => av.preferred === 1);
```

Only the preferred availability is included in the result dictionary.

## Error Handling

### Error Types

#### ZodError
Thrown when input parameters fail validation:

```typescript
try {
  await service.getAvailabilities({
    orderType: 'Versand',
    items: []  // Empty array - fails min(1) validation
  });
} catch (error) {
  if (error instanceof ZodError) {
    console.error('Validation error:', error.errors);
    // error.errors contains detailed validation failures
  }
}
```

#### ResponseArgsError
Thrown when the API returns an error:

```typescript
import { ResponseArgsError } from '@isa/common/data-access';

try {
  await service.getAvailabilities(params);
} catch (error) {
  if (error instanceof ResponseArgsError) {
    console.error('API error:', error.message);
    // Check error.message for details
  }
}
```

#### Error (Generic)
Thrown for business logic failures:

```typescript
try {
  // B2B-Versand without default branch
  await service.getAvailabilities({
    orderType: 'B2B-Versand',
    items: [{ itemId: 123, ean: '123', quantity: 1 }]
  });
} catch (error) {
  if (error.message === 'Default branch has no ID') {
    console.error('Branch configuration error');
  }
  if (error.message === 'Logistician 2470 not found') {
    console.error('Logistician configuration error');
  }
}
```

### Error Context Logging

The service automatically logs errors with context:

```typescript
// Logged automatically on error
{
  orderType: 'Versand',
  itemIds: [123, 456],
  additional: { /* context-specific data */ }
}
```

### Request Cancellation

Use AbortSignal to cancel in-flight requests:

```typescript
const controller = new AbortController();

// Start request
const promise = service.getAvailabilities(params, controller.signal);

// Cancel if needed
controller.abort();

try {
  await promise;
} catch (error) {
  // Handle cancellation or other errors
  console.log('Request cancelled or failed');
}
```

## Testing

The library uses **Vitest** with **Angular Testing Utilities** for testing.

### Running Tests

```bash
# Run tests for this library
npx nx test availability-data-access --skip-nx-cache

# Run tests with coverage
npx nx test availability-data-access --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test availability-data-access --watch
```

### Test Structure

The library includes comprehensive unit tests covering:

- **Order type routing** - Validates correct endpoint selection for each order type
- **Validation** - Tests Zod schema validation for all parameter types
- **Business rules** - Tests download validation, B2B logistician override, etc.
- **Error handling** - Tests API errors, validation failures, missing data
- **Abort signal support** - Tests request cancellation
- **Multiple items** - Tests batch processing
- **Preferred selection** - Tests preferred availability selection logic

### Example Test

```typescript
import { TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { AvailabilityService } from './availability.service';

describe('AvailabilityService', () => {
  let service: AvailabilityService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        AvailabilityService,
        // Mock providers...
      ]
    });
    service = TestBed.inject(AvailabilityService);
  });

  it('should fetch standard delivery availability', async () => {
    const result = await service.getAvailabilities({
      orderType: 'Versand',
      items: [{ itemId: 123, ean: '1234567890', quantity: 3 }]
    });

    expect(result).toHaveProperty('123');
    expect(result['123'].itemId).toBe(123);
  });
});
```

## Architecture Notes

### Current Architecture

The library follows a layered architecture:

```
Components/Features
       ↓
  AvailabilityFacade (optional, pass-through)
       ↓
  AvailabilityService (main business logic)
       ↓
├─→ RemissionStockService (InStore)
├─→ AvailabilityRequestAdapter (request mapping)
├─→ Generated API Client (availability-api)
└─→ Helper functions (transformers, validators)
```

### Known Architectural Considerations

#### 1. Facade Evaluation (Medium Priority)

The `AvailabilityFacade` is currently under evaluation:

**Current State:**
- Pass-through wrapper with no added value
- Just delegates to AvailabilityService
- No orchestration logic

**Recommendation:**
- Consider removal if no orchestration is planned
- Update components to inject AvailabilityService directly
- Keep facade only if future orchestration is planned

**Impact:** Low risk, reduces one layer of indirection

#### 2. Order Type Handler Duplication (High Priority)

The service contains 6 similar handler methods with significant code duplication:

**Current State:**
- ~180 lines of duplicated code
- Bug fixes need to be applied to multiple methods

**Proposed Refactoring:**
- Template Method + Strategy pattern
- Handler registry with common workflow
- Post-processing hooks for special cases

**Impact:** High value, reduces complexity significantly

#### 3. Cross-Domain Dependency

The library depends on `@isa/remission/data-access` for `BranchService`:

**Current State:**
- Direct dependency on remission domain
- Availability domain cannot be used without remission domain

**Proposed Solution:**
- Create abstract `DefaultBranchProvider` interface
- Inject provider instead of concrete BranchService
- Implement at app level for domain independence

**Impact:** Improves domain boundaries and testability

### Performance Considerations

1. **Parallel Requests** - B2B-Versand fetches branch and logistician in parallel
2. **Early Validation** - Zod validation fails fast before API calls
3. **Preferred Selection** - Efficient filtering with Array.find()
4. **Request Cancellation** - AbortSignal support prevents wasted bandwidth

### Future Enhancements

Potential improvements identified:

1. **Caching Layer** - Cache availability responses for short periods
2. **Batch Optimization** - Optimize multiple availability checks
3. **Retry Logic** - Automatic retry for transient failures
4. **Analytics Integration** - Track availability check patterns
5. **Schema Simplification** - Reduce single-item schema duplication

## Dependencies

### Required Libraries

- `@angular/core` - Angular framework
- `@generated/swagger/availability-api` - Generated API client
- `@isa/common/data-access` - Common data access utilities
- `@isa/core/logging` - Logging service
- `@isa/checkout/data-access` - Supplier and OrderType
- `@isa/remission/data-access` - Stock and branch services
- `@isa/oms/data-access` - Logistician service
- `zod` - Schema validation
- `rxjs` - Reactive programming

### Path Alias

Import from: `@isa/availability/data-access`

## License

Internal ISA Frontend library - not for external distribution.
