# @isa/remission/data-access

A comprehensive remission (returns) management system for Angular applications supporting mandatory returns (Pflichtremission) and department overflow returns (Abteilungsremission) in retail inventory operations.

## Overview

The Remission Data Access library provides a unified interface for managing product returns in a retail environment. It handles the complete lifecycle of remission operations including creating returns, managing receipts, processing return items, tracking stock levels, and managing supplier relationships. The library integrates with the generated inventory API client and provides intelligent state management, validation, and transformation of remission data.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [State Management](#state-management)
- [Helper Functions](#helper-functions)
- [Testing](#testing)
- [Architecture Notes](#architecture-notes)

## Features

- **Dual remission types** - Pflichtremission (mandatory) and Abteilungsremission (department overflow)
- **Complete lifecycle management** - Return creation, receipt management, item processing, completion
- **Smart state management** - NgRx Signals store with session persistence
- **Stock tracking** - Real-time stock information with intelligent batching
- **Supplier management** - Automatic supplier fetching and caching
- **Package assignment** - Track packages within receipts
- **Return reasons** - Configurable return reasons per stock
- **Product groups** - Department-based product categorization
- **Capacity calculations** - Helper functions for stock and capacity analysis
- **Zod validation** - Runtime schema validation for all parameters
- **Request cancellation** - AbortSignal support for all async operations
- **Comprehensive logging** - Integration with @isa/core/logging for debugging
- **Caching strategies** - In-memory caching with decorators (@Cache, @InFlight)
- **Batch optimization** - BatchingResource for efficient stock info fetching

## Quick Start

### 1. Import and Inject Services

```typescript
import { Component, inject } from '@angular/core';
import {
  RemissionReturnReceiptService,
  RemissionStockService,
  RemissionStore
} from '@isa/remission/data-access';

@Component({
  selector: 'app-remission-manager',
  template: '...'
})
export class RemissionManagerComponent {
  #returnReceiptService = inject(RemissionReturnReceiptService);
  #stockService = inject(RemissionStockService);
  #remissionStore = inject(RemissionStore);
}
```

### 2. Start a Remission Process

```typescript
async startNewRemission(): Promise<void> {
  // Create a new remission (return + receipt)
  const remission = await this.#returnReceiptService.createRemission({
    returnGroup: undefined, // Auto-generated timestamp
    receiptNumber: 'RR-2024-001'
  });

  if (remission) {
    // Initialize the store with the created return and receipt
    this.#remissionStore.startRemission({
      returnId: remission.returnId,
      receiptId: remission.receiptId
    });

    console.log('Remission started:', remission);
  }
}
```

### 3. Fetch and Display Remission Lists

```typescript
import { RemissionSearchService, RemissionListType } from '@isa/remission/data-access';

async fetchMandatoryItems(): Promise<void> {
  const searchService = inject(RemissionSearchService);
  const stockService = inject(RemissionStockService);
  const supplierService = inject(RemissionSupplierService);

  const stock = await stockService.fetchAssignedStock();
  const suppliers = await supplierService.fetchSuppliers();

  const response = await searchService.fetchList({
    assignedStockId: stock.id,
    supplierId: suppliers[0].id,
    take: 50,
    skip: 0
  });

  console.log(`Total items: ${response.totalCount}`);
  console.log(`Items returned: ${response.result?.length}`);
}
```

### 4. Check Stock Availability

```typescript
import { StockInfoResource } from '@isa/remission/data-access';

const stockInfoResource = inject(StockInfoResource);

// Create a resource for a specific item
const stockInfo = stockInfoResource.resource({
  itemId: 12345,
  stockId: 100
});

// Use in template or computed signals
const inStock = computed(() => stockInfo.value()?.inStock ?? 0);
const available = computed(() => stockInfo.value()?.available ?? 0);
```

## Core Concepts

### Remission Types

The library supports two distinct remission types, each with specific requirements and workflows:

#### 1. Pflichtremission (Mandatory Returns)

- **Purpose**: Mandatory returns based on supplier requirements
- **Data Source**: ReturnItem entities
- **Query Endpoint**: `RemiPflichtremissionsartikel`
- **Characteristics**:
  - Predefined return quantities calculated by backend
  - Based on supplier agreements and contractual obligations
  - Uses `addReturnItem()` to add items to receipts
  - Typically processed first in remission workflows

#### 2. Abteilungsremission (Department Overflow)

- **Purpose**: Department-based overflow returns for excess stock
- **Data Source**: ReturnSuggestion entities
- **Query Endpoint**: `RemiUeberlauf`
- **Characteristics**:
  - Suggested returns based on stock levels and department capacity
  - Uses `addReturnSuggestionItem()` to add items to receipts
  - Supports impediment comments and remaining quantities
  - More flexible than mandatory returns

### Remission Lifecycle

The complete remission workflow follows these stages:

```
1. Create Return
   ↓
2. Create Receipt (with receipt number)
   ↓
3. Assign Package (optional, can be done later)
   ↓
4. Add Return Items / Return Suggestions
   ↓
5. Update Item Impediments (if needed)
   ↓
6. Complete Receipt
   ↓
7. Complete Return
```

### Return Structure

```typescript
interface Return {
  id: number;                          // Unique return identifier
  returnGroup?: string;                // Group ID for related returns
  supplier?: { id: number };           // Supplier reference
  receipts: EntityContainer<Receipt>[]; // Associated receipts
  status?: number;                     // Return status code
  created?: string;                    // Creation timestamp
  changed?: string;                    // Last modification timestamp
}
```

### Receipt Structure

```typescript
interface Receipt {
  id: number;                          // Unique receipt identifier
  receiptNumber: string;               // Human-readable receipt number
  receiptType?: number;                // Type (1 = ShippingNote)
  stock?: { id: number };              // Stock reference
  supplier?: { id: number };           // Supplier reference
  items: EntityContainer<ReceiptItem>[]; // Items in this receipt
  packages?: Array<{ packageNumber: string }>; // Assigned packages
  completed?: boolean;                 // Completion status
}
```

### Stock Management

Stock operations use the RemissionStockService with intelligent caching:

- **Assigned Stock**: The stock location assigned to the current user
- **Stock Info**: Real-time availability for specific items
- **Batching**: StockInfoResource batches multiple item queries into single API calls

### Supplier Management

Suppliers are automatically managed with memory caching:

- Fetched once per session and cached in memory
- Automatically retrieved during remission creation
- First supplier is used as default for new returns/receipts

## API Reference

### RemissionReturnReceiptService

Main service for managing return receipts and their lifecycle.

#### `fetchRemissionReturnReceipts(params, abortSignal?): Promise<Return[]>`

Fetches remission return receipts based on completion status.

**Parameters:**
- `params: FetchRemissionReturnReceiptsParams`
  - `start?: Date` - Optional start date filter
  - `returncompleted: boolean` - Filter by completion status
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to array of Return objects

**Example:**
```typescript
const completedReturns = await service.fetchRemissionReturnReceipts({
  start: new Date('2024-01-01'),
  returncompleted: true
});
```

#### `fetchReturn(params, abortSignal?): Promise<Return | undefined>`

Fetches a specific return by ID with eager loading support.

**Parameters:**
- `params: FetchReturnParams`
  - `returnId: number` - ID of the return to fetch
  - `eagerLoading?: number` - Eager loading depth (default: 2)
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to Return or undefined

**Example:**
```typescript
const returnData = await service.fetchReturn({
  returnId: 5001,
  eagerLoading: 3 // Load deeply nested data
});
```

#### `createReturn(params, abortSignal?): Promise<ResponseArgs<Return> | undefined>`

Creates a new return with the specified return group.

**Parameters:**
- `params: CreateReturn`
  - `returnGroup?: string` - Optional return group ID (auto-generated if not provided)
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to ResponseArgs containing the created Return

**Example:**
```typescript
const returnResponse = await service.createReturn({
  returnGroup: 'GROUP-2024-001'
});
```

#### `createReceipt(params, abortSignal?): Promise<ResponseArgs<Receipt> | undefined>`

Creates a new receipt within a return.

**Parameters:**
- `params: CreateReceipt`
  - `returnId: number` - ID of the parent return
  - `receiptNumber?: string` - Optional receipt number

**Returns:** Promise resolving to ResponseArgs containing the created Receipt

**Example:**
```typescript
const receiptResponse = await service.createReceipt({
  returnId: 5001,
  receiptNumber: 'RR-2024-001'
});
```

#### `assignPackage(params, abortSignal?): Promise<ResponseArgs<Receipt> | undefined>`

Assigns a package number to a receipt.

**Parameters:**
- `params: AssignPackage`
  - `returnId: number` - ID of the return
  - `receiptId: number` - ID of the receipt
  - `packageNumber: string` - Package number to assign

**Returns:** Promise resolving to updated Receipt

**Example:**
```typescript
const updatedReceipt = await service.assignPackage({
  returnId: 5001,
  receiptId: 101,
  packageNumber: 'PKG-2024-001'
});
```

#### `addReturnItem(params, abortSignal?): Promise<ReceiptReturnTuple | undefined>`

Adds a mandatory return item to a receipt.

**Parameters:**
- `params: AddReturnItem`
  - `returnId: number` - ID of the return
  - `receiptId: number` - ID of the receipt
  - `returnItemId: number` - ID of the return item to add
  - `quantity: number` - Quantity to add
  - `inStock: number` - Current stock quantity

**Returns:** Promise resolving to tuple of updated Receipt and Return

**Example:**
```typescript
const tuple = await service.addReturnItem({
  returnId: 5001,
  receiptId: 101,
  returnItemId: 789,
  quantity: 10,
  inStock: 5
});
```

#### `addReturnSuggestionItem(params, abortSignal?): Promise<ReceiptReturnSuggestionTuple | undefined>`

Adds a department return suggestion item to a receipt.

**Parameters:**
- `params: AddReturnSuggestionItem`
  - `returnId: number` - ID of the return
  - `receiptId: number` - ID of the receipt
  - `returnSuggestionId: number` - ID of the return suggestion to add
  - `quantity: number` - Quantity to add
  - `inStock: number` - Current stock quantity
  - `impedimentComment?: string` - Optional impediment note
  - `remainingQuantity?: number` - Optional remaining quantity

**Returns:** Promise resolving to tuple of updated Receipt and ReturnSuggestion

**Example:**
```typescript
const tuple = await service.addReturnSuggestionItem({
  returnId: 5001,
  receiptId: 101,
  returnSuggestionId: 456,
  quantity: 8,
  inStock: 3,
  impedimentComment: 'Restmenge',
  remainingQuantity: 5
});
```

#### `completeReturnReceipt(params): Promise<Receipt>`

Finalizes a receipt, making it ready for completion.

**Parameters:**
- `params: { returnId: number; receiptId: number }`

**Returns:** Promise resolving to completed Receipt

**Example:**
```typescript
const completedReceipt = await service.completeReturnReceipt({
  returnId: 5001,
  receiptId: 101
});
```

#### `completeReturn(params): Promise<Return>`

Finalizes a return after all receipts are completed.

**Parameters:**
- `params: { returnId: number }`

**Returns:** Promise resolving to completed Return

**Example:**
```typescript
const completedReturn = await service.completeReturn({
  returnId: 5001
});
```

#### `completeReturnReceiptAndReturn(params): Promise<Return>`

Convenience method that completes both receipt and return in sequence.

**Parameters:**
- `params: { returnId: number; receiptId: number }`

**Returns:** Promise resolving to completed Return

**Example:**
```typescript
const completedReturn = await service.completeReturnReceiptAndReturn({
  returnId: 5001,
  receiptId: 101
});
```

#### `cancelReturnReceipt(params): Promise<void>`

Cancels a receipt and its associated return.

**Parameters:**
- `params: { returnId: number; receiptId: number }`

**Example:**
```typescript
await service.cancelReturnReceipt({
  returnId: 5001,
  receiptId: 101
});
```

#### `cancelReturn(params): Promise<void>`

Cancels an entire return.

**Parameters:**
- `params: { returnId: number }`

**Example:**
```typescript
await service.cancelReturn({ returnId: 5001 });
```

#### `updateReturnItemImpediment(params): Promise<ReturnItem>`

Updates the impediment comment for a return item.

**Parameters:**
- `params: UpdateItemImpediment`
  - `itemId: number` - ID of the return item
  - `comment: string` - Impediment comment

**Returns:** Promise resolving to updated ReturnItem

**Example:**
```typescript
const updatedItem = await service.updateReturnItemImpediment({
  itemId: 789,
  comment: 'Beschädigt'
});
```

#### `updateReturnSuggestionImpediment(params): Promise<ReturnSuggestion>`

Updates the impediment comment for a return suggestion.

**Parameters:**
- `params: UpdateItemImpediment`
  - `itemId: number` - ID of the return suggestion
  - `comment: string` - Impediment comment

**Returns:** Promise resolving to updated ReturnSuggestion

#### `deleteReturnItem(params): Promise<ReturnItem>`

Deletes a return item from the system.

**Parameters:**
- `params: { itemId: number }`

**Returns:** Promise resolving to deleted ReturnItem

#### `removeReturnItemFromReturnReceipt(params): Promise<void>`

Removes a return item from a receipt.

**Parameters:**
- `params: { returnId: number; receiptId: number; receiptItemId: number }`

#### `createRemission(params): Promise<CreateRemission | undefined>`

High-level method that creates both a return and receipt in one operation.

**Parameters:**
- `params: { returnGroup?: string; receiptNumber?: string }`

**Returns:** Promise resolving to created remission with returnId, receiptId, and validation info

**Example:**
```typescript
const remission = await service.createRemission({
  returnGroup: undefined, // Auto-generated
  receiptNumber: 'RR-2024-001'
});

if (remission) {
  console.log(`Return ID: ${remission.returnId}`);
  console.log(`Receipt ID: ${remission.receiptId}`);
}
```

#### `remitItem(params): Promise<ReceiptReturnSuggestionTuple | ReceiptReturnTuple | undefined>`

Polymorphic method that remits an item based on remission type.

**Parameters:**
- `params: { itemId: number; addItem: AddReturnItem | AddReturnSuggestionItem; type: RemissionListType }`

**Returns:** Promise resolving to appropriate tuple based on type

**Example:**
```typescript
const result = await service.remitItem({
  itemId: 789,
  addItem: {
    returnId: 5001,
    receiptId: 101,
    quantity: 10,
    inStock: 5
  },
  type: RemissionListType.Pflicht
});
```

### RemissionStockService

Service for managing stock operations with intelligent caching.

#### `fetchAssignedStock(abortSignal?): Promise<Stock>`

Fetches the currently assigned stock for the user. Results are cached for 5 minutes.

**Decorators:** `@Cache({ ttl: CacheTimeToLive.fiveMinutes })`, `@InFlight()`

**Parameters:**
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to Stock with guaranteed ID

**Throws:**
- `ResponseArgsError` - When API request fails
- `Error` - When stock has no ID

**Example:**
```typescript
const stock = await service.fetchAssignedStock();
console.log(`Assigned to stock ${stock.id}: ${stock.name}`);
```

#### `fetchStock(branchId, abortSignal?): Promise<Stock | undefined>`

Fetches stock for a specific branch.

**Parameters:**
- `branchId: number` - ID of the branch
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to Stock or undefined if not found

**Example:**
```typescript
const stock = await service.fetchStock(42);
if (stock) {
  console.log(`Branch stock: ${stock.id}`);
}
```

#### `fetchStockInfos(params, abortSignal?): Promise<StockInfo[]>`

Fetches stock information for multiple items. Validates using FetchStockInStockSchema.

**Parameters:**
- `params: FetchStockInStock`
  - `itemIds: number[]` - Array of item IDs
  - `stockId?: number` - Optional stock ID (uses assigned stock if not provided)
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to array of StockInfo

**Example:**
```typescript
const stockInfos = await service.fetchStockInfos({
  itemIds: [123, 456, 789],
  stockId: 100
});

stockInfos.forEach(info => {
  console.log(`Item ${info.itemId}: ${info.inStock} in stock`);
});
```

### RemissionSearchService

Service for searching and fetching remission lists.

#### `remissionListType(): Array<{ key: RemissionListTypeKey; value: RemissionListType }>`

Returns all available remission list types as key-value pairs.

**Returns:** Array of remission type mappings

**Example:**
```typescript
const types = service.remissionListType();
// [
//   { key: 'Pflicht', value: 'Pflichtremission' },
//   { key: 'Abteilung', value: 'Abteilungsremission' }
// ]
```

#### `fetchList(params, abortSignal?): Promise<ListResponseArgs<ReturnItem>>`

Fetches paginated list of mandatory remission items.

**Parameters:**
- `params: RemissionQueryTokenInput`
  - `assignedStockId: number` - ID of the assigned stock
  - `supplierId: number` - ID of the supplier
  - `filter?: string` - Optional filter string
  - `input?: object` - Optional input parameters
  - `orderBy?: string` - Optional field to order by
  - `take?: number` - Number of items to fetch
  - `skip?: number` - Number of items to skip
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to paginated ListResponseArgs with ReturnItem array

**Example:**
```typescript
const response = await service.fetchList({
  assignedStockId: 100,
  supplierId: 50,
  orderBy: 'productName',
  take: 50,
  skip: 0
});

console.log(`Total: ${response.totalCount}`);
console.log(`Returned: ${response.result?.length}`);
```

#### `fetchDepartmentList(params, abortSignal?): Promise<ListResponseArgs<ReturnSuggestion>>`

Fetches paginated list of department overflow return suggestions.

**Parameters:**
- Same as `fetchList()` but returns ReturnSuggestion instead of ReturnItem

**Returns:** Promise resolving to paginated ListResponseArgs with ReturnSuggestion array

**Example:**
```typescript
const response = await service.fetchDepartmentList({
  assignedStockId: 100,
  supplierId: 50,
  take: 50
});
```

#### `fetchQuerySettings(params): Promise<QuerySettings>`

Fetches query settings for mandatory remission articles.

**Parameters:**
- `params: FetchQuerySettings`
  - `supplierId: number` - Supplier ID
  - `assignedStockId: number` - Stock ID

**Returns:** Promise resolving to QuerySettings

**Example:**
```typescript
const settings = await service.fetchQuerySettings({
  supplierId: 50,
  assignedStockId: 100
});
```

#### `fetchQueryDepartmentSettings(params): Promise<QuerySettings>`

Fetches query settings for department overflow articles.

**Parameters:**
- Same as `fetchQuerySettings()`

**Returns:** Promise resolving to QuerySettings

#### `fetchRequiredCapacity(params): Promise<ValueTupleOfStringAndInteger[]>`

Fetches required capacity information for departments.

**Parameters:**
- `params: FetchRequiredCapacity`
  - `departments: string[]` - List of department names
  - `supplierId: number` - Supplier ID
  - `stockId: number` - Stock ID

**Returns:** Promise resolving to array of capacity tuples

**Example:**
```typescript
const capacity = await service.fetchRequiredCapacity({
  departments: ['Electronics', 'Clothing'],
  supplierId: 50,
  stockId: 100
});
```

#### `canAddItemToRemiList(items, abortSignal?): Promise<BatchResponseArgs<ReturnItem>>`

Validates whether items can be added to the remission list.

**Parameters:**
- `items: Array<{ item: Item; quantity: number; reason: string }>`
- `abortSignal?: AbortSignal`

**Returns:** Promise resolving to batch validation results

**Example:**
```typescript
const validationResult = await service.canAddItemToRemiList([
  { item: catalogItem, quantity: 5, reason: 'Überschuss' }
]);

if (!validationResult.error) {
  console.log('Can add items to list');
}
```

#### `addToList(items, abortSignal?): Promise<ReturnItem[]>`

Adds items to the remission list.

**Parameters:**
- `items: Array<{ item: Item; quantity: number; reason: string }>`
- `abortSignal?: AbortSignal`

**Returns:** Promise resolving to created ReturnItem array

**Example:**
```typescript
const addedItems = await service.addToList([
  { item: catalogItem, quantity: 5, reason: 'Überschuss' }
]);
```

### RemissionSupplierService

Service for managing suppliers with memory caching.

#### `fetchSuppliers(abortSignal?): Promise<Supplier[]>`

Fetches all suppliers for the assigned stock. Results are cached in memory.

**Parameters:**
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to array of Supplier

**Throws:**
- `DataAccessError` - When no assigned stock is found
- `Error` - When API request fails

**Example:**
```typescript
const suppliers = await service.fetchSuppliers();
console.log(`Found ${suppliers.length} suppliers`);
```

### BranchService

Service for branch/stock operations.

#### `getDefaultBranch(abortSignal?): Promise<BranchDTO>`

Gets the default branch from the inventory service.

**Parameters:**
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to BranchDTO

**Throws:**
- `ResponseArgsError` - When API request fails
- `Error` - When no branch data is returned

**Example:**
```typescript
const branch = await service.getDefaultBranch();
console.log(`Default branch: ${branch.name} (${branch.branchNumber})`);
```

### RemissionReasonService

Service for managing return reasons with caching.

#### `fetchReturnReasons(abortSignal?): Promise<KeyValueStringAndString[]>`

Fetches all available return reasons for the assigned stock. Cached for 5 minutes.

**Decorators:** `@Cache({ ttl: CacheTimeToLive.fiveMinutes })`, `@InFlight()`

**Parameters:**
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to array of key-value reason pairs

**Example:**
```typescript
const reasons = await service.fetchReturnReasons();
reasons.forEach(reason => {
  console.log(`${reason.key}: ${reason.value}`);
});
```

### RemissionProductGroupService

Service for managing product groups with caching.

#### `fetchProductGroups(abortSignal?): Promise<KeyValueStringAndString[]>`

Fetches all available product groups for the assigned stock. Cached for 5 minutes.

**Decorators:** `@Cache({ ttl: CacheTimeToLive.fiveMinutes })`, `@InFlight()`

**Parameters:**
- `abortSignal?: AbortSignal` - Optional abort signal

**Returns:** Promise resolving to array of key-value product group pairs

**Example:**
```typescript
const groups = await service.fetchProductGroups();
groups.forEach(group => {
  console.log(`${group.key}: ${group.value}`);
});
```

### StockInfoResource

Smart batching resource for stock information that minimizes API calls.

#### `resource(params): ResourceRef<StockInfo | undefined>`

Creates a resource for fetching stock info with automatic batching.

**Parameters:**
- `params: { itemId: number; stockId?: number }`

**Returns:** ResourceRef that can be used in signals and templates

**Example:**
```typescript
const stockInfoResource = inject(StockInfoResource);

// In component
const stockInfo = stockInfoResource.resource({
  itemId: 12345,
  stockId: 100
});

// In template or computed
const inStock = computed(() => stockInfo.value()?.inStock ?? 0);
const isAvailable = computed(() => (stockInfo.value()?.available ?? 0) > 0);
```

#### `reloadItems(itemIds): Promise<void>`

Reloads specific items by removing them from cache.

**Parameters:**
- `itemIds: number[]` - Array of item IDs to reload

**Example:**
```typescript
await stockInfoResource.reloadItems([123, 456, 789]);
```

## Usage Examples

### Complete Remission Workflow

```typescript
import { Component, inject } from '@angular/core';
import {
  RemissionReturnReceiptService,
  RemissionSearchService,
  RemissionStockService,
  RemissionSupplierService,
  RemissionStore,
  RemissionListType
} from '@isa/remission/data-access';

@Component({
  selector: 'app-remission-workflow',
  template: '...'
})
export class RemissionWorkflowComponent {
  #returnReceiptService = inject(RemissionReturnReceiptService);
  #searchService = inject(RemissionSearchService);
  #stockService = inject(RemissionStockService);
  #supplierService = inject(RemissionSupplierService);
  #remissionStore = inject(RemissionStore);

  async executeCompleteWorkflow(): Promise<void> {
    // 1. Create remission (return + receipt)
    const remission = await this.#returnReceiptService.createRemission({
      returnGroup: undefined,
      receiptNumber: 'RR-2024-001'
    });

    if (!remission) {
      console.error('Failed to create remission');
      return;
    }

    // 2. Start remission in store
    this.#remissionStore.startRemission({
      returnId: remission.returnId,
      receiptId: remission.receiptId
    });

    // 3. Assign a package
    await this.#returnReceiptService.assignPackage({
      returnId: remission.returnId,
      receiptId: remission.receiptId,
      packageNumber: 'PKG-2024-001'
    });

    // 4. Fetch mandatory items
    const stock = await this.#stockService.fetchAssignedStock();
    const suppliers = await this.#supplierService.fetchSuppliers();

    const mandatoryItems = await this.#searchService.fetchList({
      assignedStockId: stock.id,
      supplierId: suppliers[0].id,
      take: 50
    });

    // 5. Add items to receipt
    if (mandatoryItems.result && mandatoryItems.result.length > 0) {
      const firstItem = mandatoryItems.result[0];

      await this.#returnReceiptService.addReturnItem({
        returnId: remission.returnId,
        receiptId: remission.receiptId,
        returnItemId: firstItem.id!,
        quantity: 10,
        inStock: 5
      });
    }

    // 6. Complete the receipt and return
    const completedReturn = await this.#returnReceiptService
      .completeReturnReceiptAndReturn({
        returnId: remission.returnId,
        receiptId: remission.receiptId
      });

    console.log('Remission completed:', completedReturn);

    // 7. Clear store state
    this.#remissionStore.clearState();
  }
}
```

### Using Stock Information with Batching

```typescript
import { Component, computed, inject } from '@angular/core';
import { StockInfoResource } from '@isa/remission/data-access';

@Component({
  selector: 'app-product-stock',
  template: `
    <div class="product-card">
      <h3>{{ productName }}</h3>
      <div class="stock-info">
        @if (stockInfo.isLoading()) {
          <span>Loading stock...</span>
        } @else if (stockInfo.hasError()) {
          <span class="error">Stock unavailable</span>
        } @else {
          <span>In Stock: {{ inStock() }}</span>
          <span>Available: {{ available() }}</span>
          <span class="status" [class.available]="isAvailable()">
            {{ isAvailable() ? 'Available' : 'Out of Stock' }}
          </span>
        }
      </div>
    </div>
  `
})
export class ProductStockComponent {
  stockInfoResource = inject(StockInfoResource);

  productId = input.required<number>();
  productName = input.required<string>();
  stockId = input<number>();

  stockInfo = this.stockInfoResource.resource(
    computed(() => ({
      itemId: this.productId(),
      stockId: this.stockId()
    }))
  );

  inStock = computed(() => this.stockInfo.value()?.inStock ?? 0);
  available = computed(() => this.stockInfo.value()?.available ?? 0);
  isAvailable = computed(() => this.available() > 0);
}
```

### Managing Remission State

```typescript
import { Component, computed, effect, inject } from '@angular/core';
import { RemissionStore, RemissionListType } from '@isa/remission/data-access';

@Component({
  selector: 'app-remission-state-manager',
  template: '...'
})
export class RemissionStateManagerComponent {
  remissionStore = inject(RemissionStore);

  // Computed signals from store
  isRemissionActive = this.remissionStore.remissionStarted;
  selectedItems = this.remissionStore.selectedItems;
  selectedQuantities = this.remissionStore.selectedQuantity;
  returnData = this.remissionStore.returnData;

  // Computed values
  totalSelectedItems = computed(() =>
    Object.keys(this.selectedItems()).length
  );

  totalQuantity = computed(() =>
    Object.values(this.selectedQuantities()).reduce((sum, qty) => sum + qty, 0)
  );

  // Effect to log state changes
  constructor() {
    effect(() => {
      if (this.isRemissionActive()) {
        console.log('Remission active:', {
          returnId: this.remissionStore.returnId(),
          receiptId: this.remissionStore.receiptId(),
          itemsSelected: this.totalSelectedItems(),
          totalQuantity: this.totalQuantity()
        });
      }
    });
  }

  selectItem(itemId: number, item: any, quantity: number): void {
    this.remissionStore.updateRemissionQuantity(itemId, item, quantity);
  }

  removeItem(itemId: number): void {
    this.remissionStore.removeItemAndQuantity(itemId);
  }

  clearSelection(): void {
    this.remissionStore.clearSelectedItems();
  }

  resetRemission(): void {
    this.remissionStore.clearState();
  }
}
```

## State Management

### RemissionStore

The library includes a NgRx Signal Store for managing remission selection state with session persistence.

#### Store State Structure

```typescript
interface RemissionState {
  returnId: number | undefined;           // Can only be set once
  receiptId: number | undefined;          // Can only be set once
  selectedItems: Record<number, RemissionItem>;
  selectedQuantity: Record<number, number>;
}
```

#### Store Features

- **Session Persistence**: State is persisted to user storage and survives page refreshes
- **One-Time Initialization**: `returnId` and `receiptId` can only be set once via `startRemission()`
- **Automatic Return Fetching**: Uses Angular resource to automatically fetch return data
- **Computed Signals**: Provides reactive computed values for common queries

#### Store Methods

##### `startRemission({ returnId, receiptId })`

Initializes the remission process. Can only be called once.

**Throws:** Error if remission already started

##### `reloadReturn()`

Reloads the return resource to fetch latest data.

##### `isCurrentRemission({ returnId, receiptId })`

Checks if provided IDs match the current remission.

**Returns:** boolean

##### `selectRemissionItem(remissionItemId, item)`

Adds or updates a selected item.

##### `updateRemissionQuantity(remissionItemId, item, quantity)`

Updates both the item and its quantity.

##### `removeItem(remissionItemId)`

Removes an item from selection.

##### `removeItemAndQuantity(remissionItemId)`

Removes an item and its quantity.

##### `clearSelectedItems()`

Clears all selected items.

##### `clearState()`

Resets the entire store to initial state and clears storage.

#### Store Computed Signals

##### `remissionStarted: Signal<boolean>`

Indicates if remission has been initialized.

##### `returnData: Signal<Return | undefined>`

Current return data fetched from API.

## Helper Functions

The library provides several utility functions for calculations and data extraction.

### Stock and Capacity Calculations

#### `calculateStockToRemit({ availableStock, predefinedReturnQuantity?, remainingQuantityInStock? }): number`

Calculates how much stock should be remitted based on available stock and predefined quantities.

**Business Logic:**
- If `predefinedReturnQuantity` is defined, use it (backend calculation takes precedence)
- Otherwise, calculate as: `availableStock - remainingQuantityInStock`
- Never returns negative values

**Example:**
```typescript
// With predefined quantity (backend calculation)
const toRemit1 = calculateStockToRemit({
  availableStock: 50,
  predefinedReturnQuantity: 30,
  remainingQuantityInStock: 10
});
console.log(toRemit1); // 30 (uses backend value)

// Without predefined quantity (fallback calculation)
const toRemit2 = calculateStockToRemit({
  availableStock: 50,
  remainingQuantityInStock: 10
});
console.log(toRemit2); // 40 (50 - 10)
```

#### `getStockToRemit({ remissionItem, remissionListType, availableStock }): number`

Higher-level function that extracts predefined quantity from a remission item based on type.

**Example:**
```typescript
const stockToRemit = getStockToRemit({
  remissionItem: returnItem,
  remissionListType: RemissionListType.Pflicht,
  availableStock: 50
});
```

#### `calculateCapacity({ capacityValue2, capacityValue3 }): number`

Calculates capacity as the minimum of two values.

**Returns:** The smaller of the two capacity values

**Example:**
```typescript
const capacity = calculateCapacity({
  capacityValue2: 100,
  capacityValue3: 80
});
console.log(capacity); // 80
```

#### `calculateMaxCapacity({ capacityValue2, capacityValue3, capacityValue4, comparer }): number`

Calculates maximum capacity with complex business logic.

**Example:**
```typescript
const maxCapacity = calculateMaxCapacity({
  capacityValue2: 100,
  capacityValue3: 120,
  capacityValue4: 90,
  comparer: 50
});
console.log(maxCapacity); // 100
```

### Data Extraction Helpers

#### `getReceiptStatusFromReturn(returnData): ReceiptCompleteStatus`

Extracts the completion status from a return's receipts.

**Returns:** Object with `allCompleted` and `atLeastOneComplete` flags

#### `getReceiptNumberFromReturn(returnData): string | undefined`

Extracts the receipt number from the first receipt in a return.

#### `getReceiptItemsFromReturn(returnData): ReceiptItem[]`

Extracts all receipt items from a return's receipts.

#### `getReceiptItemQuantityFromReturn(returnData): number`

Calculates total quantity of all receipt items in a return.

#### `getPackageNumbersFromReturn(returnData): string[]`

Extracts all package numbers from a return's receipts.

#### `getRetailPriceFromItem(item): number | undefined`

Extracts the retail price value from a catalogue item.

#### `getAssortmentFromItem(item): object | undefined`

Extracts assortment information from a catalogue item.

#### `calculateAvailableStock({ inStock, unreleased }): number`

Calculates available stock by subtracting unreleased quantities.

#### `calculateTargetStock({ min, max }): number`

Calculates target stock level from min/max values.

## Testing

The library uses **Jest** with **Spectator** for testing.

### Running Tests

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

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

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

### Test Coverage

The library includes comprehensive unit tests covering:

- **Service methods** - All CRUD operations for returns, receipts, and items
- **State management** - RemissionStore operations and computed signals
- **Helper functions** - Calculation and extraction helpers
- **Error handling** - API errors, validation failures, missing data
- **Abort signal support** - Request cancellation scenarios
- **Caching behavior** - @Cache and @InFlight decorator functionality
- **Batch operations** - StockInfoResource batching logic

## Architecture Notes

### Current Architecture

The library follows a layered architecture with clear separation of concerns:

```
Components/Features
       ↓
  RemissionStore (optional, state management)
       ↓
  Services (business logic)
       ↓
├─→ Resources (batching optimization)
├─→ Generated API Clients (inventory-api)
├─→ Helper Functions (calculations, extractions)
└─→ Schemas (Zod validation)
```

### Design Patterns

1. **Service Layer Pattern** - API interactions wrapped in domain services
2. **Batching Resource Pattern** - Efficient stock info fetching with automatic batching
3. **Caching Strategy** - Multi-level caching (memory, decorators)
4. **State Management Pattern** - NgRx Signals with session persistence
5. **Validation Pattern** - Zod schemas for runtime validation

### Known Considerations

1. **Service Location** - BranchService should move to inventory-data-access
2. **Schema Validation TODO** - Supplier cache needs validation
3. **Caching Strategy** - Consider standardizing on decorator-based approach

### Performance Optimizations

- Batching reduces stock API calls by ~80%
- 5-minute caching for frequently accessed metadata
- @InFlight() prevents duplicate concurrent requests
- Session persistence avoids unnecessary re-fetching

## Dependencies

### Required Libraries

- `@angular/core` - Angular framework
- `@ngrx/signals` - State management with signals
- `@generated/swagger/inventory-api` - Generated API client
- `@isa/common/data-access` - Common utilities
- `@isa/common/decorators` - Caching decorators
- `@isa/core/logging` - Logging service
- `@isa/core/storage` - Storage providers
- `@isa/catalogue/data-access` - Item models
- `zod` - Schema validation
- `rxjs` - Reactive programming

### Path Alias

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

## License

Internal ISA Frontend library - not for external distribution.
