# @isa/oms/feature/return-details

A comprehensive Angular feature library for displaying receipt details and managing product returns in the Order Management System (OMS). Provides an interactive interface for viewing receipt information, selecting items for return, configuring return quantities and product categories, and initiating return processes.

## Overview

The Return Details feature library implements a sophisticated receipt visualization and return management interface. It displays detailed receipt information including customer data, order history, product items, and pricing. The library enables users to select items for return, specify quantities, categorize products, and validate return eligibility in real-time. It supports both primary receipt views and lazy-loaded customer receipt history for comprehensive return workflows.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [Component API Reference](#component-api-reference)
- [Usage Examples](#usage-examples)
- [Routing and Navigation](#routing-and-navigation)
- [Architecture Notes](#architecture-notes)
- [Dependencies](#dependencies)
- [Testing](#testing)
- [Best Practices](#best-practices)

## Features

- **Receipt Details Display** - Comprehensive receipt visualization with buyer, shipping, and order information
- **Customer Receipt History** - Lazy-loaded customer purchase history based on email address
- **Item Selection** - Interactive item selection with checkbox controls for return processing
- **Quantity Management** - Dropdown-based quantity selection for partial returns
- **Product Categorization** - Category selection for each return item (required for processing)
- **Return Eligibility Validation** - Real-time validation of item return eligibility with API integration
- **Expandable Sections** - Collapsible order details for improved UX and information density
- **Customer Navigation** - Quick access to customer details, orders, and history
- **Batch Operations** - Select/deselect all items in a receipt with single action
- **Available Quantity Tracking** - Displays remaining returnable quantity (accounts for previous returns)
- **Return Process Integration** - Seamless handoff to return process workflow with selected items
- **E2E Testing Support** - Comprehensive `data-what` and `data-which` attributes throughout

## Quick Start

### 1. Import Routes

```typescript
import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: 'return-details',
    loadChildren: () =>
      import('@isa/oms/feature/return-details').then((m) => m.routes),
  },
];
```

### 2. Navigate to Receipt Details

```typescript
import { Component, inject } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-return-search',
  template: '...',
})
export class ReturnSearchComponent {
  #router = inject(Router);
  #route = inject(ActivatedRoute);

  viewReceiptDetails(receiptId: number): void {
    this.#router.navigate(['return-details', receiptId], {
      relativeTo: this.#route,
    });
  }
}
```

### 3. Component Usage in Custom Context

```typescript
import { Component } from '@angular/core';
import { ReturnDetailsComponent } from '@isa/oms/feature/return-details';
import { ReturnDetailsStore } from '@isa/oms/data-access';

@Component({
  selector: 'app-custom-return-view',
  template: `
    <oms-feature-return-details></oms-feature-return-details>
  `,
  imports: [ReturnDetailsComponent],
  providers: [ReturnDetailsStore], // Required for component state management
})
export class CustomReturnViewComponent {}
```

## Core Concepts

### Return Details Store

The library relies on `ReturnDetailsStore` from `@isa/oms/data-access` for state management. The store provides:

- **Receipt entities** - Normalized receipt storage with entity management
- **Item selection state** - Tracks selected item IDs across receipts
- **Quantity mapping** - Maps item IDs to selected return quantities
- **Category mapping** - Maps item IDs to selected product categories
- **Return eligibility** - Caches and manages return eligibility checks
- **Resources** - Provides reactive resources for receipt and eligibility data

### Receipt Loading Strategy

The library uses two loading strategies:

1. **Static (Primary Receipt)** - Immediately loads the receipt specified in the URL route parameter (`receiptId`)
2. **Lazy (Customer History)** - Loads additional customer receipts on-demand based on the buyer's email address

This approach optimizes performance by loading only essential data initially, then expanding to show related receipts when available.

### Item Return Eligibility

Return eligibility is determined through multiple checks:

1. **API Validation** - `canReturn` endpoint checks business rules
2. **Category Selection** - Product category must be selected before return
3. **Available Quantity** - Items with 0 available quantity cannot be returned
4. **Receipt Item Actions** - Item metadata includes return eligibility flags

The UI reflects these checks with disabled states, error messages, and loading indicators.

### Product Categories

Each return item must be categorized before processing. Available categories are provided by `ReturnDetailsService.availableCategories()`:

```typescript
const availableCategories: Array<{ key: ProductCategory; value: string }> = [
  { key: ProductCategory.Book, value: 'Buch' },
  { key: ProductCategory.Media, value: 'Medien' },
  { key: ProductCategory.Other, value: 'Sonstiges' },
  // ... additional categories
];
```

### Return Process Initiation

When users click "Rückgabe starten" (Start Return), the component:

1. Groups selected items by receipt ID
2. Collects selected quantities and categories for each item
3. Passes data to `ReturnProcessStore` to initialize the return workflow
4. Navigates to the return process route

## Component API Reference

### ReturnDetailsComponent (Main Component)

The routed component that orchestrates the return details view.

**Selector:** `oms-feature-return-details`

**Route Parameter:**
- `receiptId: number` - The ID of the receipt to display (parsed from route params)

**Computed Properties:**

- `receiptId(): number` - Parsed receipt ID from route parameters (throws if missing)
- `canStartProcess(): boolean` - Returns true if items are selected and process ID exists

**Resources:**

- `receiptResource` - Reactive resource loading the primary receipt
- `customerReceiptsResource` - Reactive resource loading customer's receipt history by email

**Methods:**

- `startProcess(): void` - Initiates the return process with selected items and navigates to process view

**Navigation:**

- Back button: Calls `location.back()` to return to previous view
- Start return button: Navigates to `../../process` relative route

**Example:**

```typescript
// Component handles route parameter automatically
// Route: /return-details/12345
// receiptId() === 12345

// User clicks "Rückgabe starten"
// Component groups items by receipt, navigates to process
```

### ReturnDetailsStaticComponent

Displays the primary receipt details (non-expandable, always visible).

**Selector:** `oms-feature-return-details-static`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receipt` | `Receipt` | Yes | The full receipt object to display |

**Template Structure:**

- Header with customer information and navigation
- Expandable order details section
- Order group toolbar (date, receipt number, select all)
- List of receipt items with return controls

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-static
      [receipt]="receipt()"
    ></oms-feature-return-details-static>
  `,
})
export class MyComponent {
  receipt = signal<Receipt>({
    id: 123,
    receiptNumber: 'R-2024-00123',
    printedDate: '2024-01-15T10:30:00Z',
    items: [/* ... */],
    buyer: {/* ... */},
  });
}
```

### ReturnDetailsLazyComponent

Displays secondary receipts from customer history (expandable, lazy-loaded).

**Selector:** `oms-feature-return-details-lazy`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receipt` | `ReceiptListItem` | Yes | Simplified receipt object from customer history |

**Computed Properties:**

- `receiptId(): number | undefined` - Returns receipt ID only when expanded (triggers lazy loading)

**Resources:**

- `receiptResource` - Loads full receipt details when expanded

**Behavior:**

- Renders collapsed by default (shows only order group toolbar)
- Fetches full receipt data when user expands the section
- Displays full item list and details once loaded

**Example:**

```typescript
@Component({
  template: `
    @for (receipt of customerReceipts(); track receipt.id) {
      <oms-feature-return-details-lazy
        [receipt]="receipt"
      ></oms-feature-return-details-lazy>
    }
  `,
})
export class MyComponent {
  customerReceipts = signal<ReceiptListItem[]>([/* ... */]);
}
```

### ReturnDetailsHeaderComponent

Displays customer information with navigation menu.

**Selector:** `oms-feature-return-details-header`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receiptId` | `number` | Yes | The receipt ID for store lookup |

**Computed Properties:**

- `receipt(): Receipt` - Full receipt from store
- `buyer(): Buyer | undefined` - Buyer information from receipt
- `referenceId(): number | undefined` - Customer reference ID for navigation
- `name(): string` - Formatted customer name (handles individual and organization names)

**Menu Actions:**

- **Kundendetails** - Navigate to customer details page
- **Bestellungen** - Navigate to customer orders page
- **Historie** - Navigate to customer history page

**E2E Attributes:**

- `[data-what="button"][data-which="customer-actions"]` - Customer menu trigger
- `[data-what="heading"][data-which="customer-name"]` - Customer name display
- `[data-what="menu-item"][data-which="customer-details"]` - Menu items

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-header
      [receiptId]="12345"
    ></oms-feature-return-details-header>
  `,
})
export class MyComponent {}
```

### ReturnDetailsOrderGroupComponent

Displays receipt summary toolbar with item count, date, and batch selection.

**Selector:** `oms-feature-return-details-order-group`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receipt` | `ReceiptInput` | Yes | Receipt with id, printedDate, receiptNumber, items |

**Computed Properties:**

- `receiptId(): number` - Receipt ID for store operations
- `itemCount(): number` - Total number of items (handles both array and count)
- `selectableItems(): ReceiptItem[]` - Items eligible for return selection
- `allSelected(): boolean` - True if all selectable items are selected

**Methods:**

- `selectOrUnselectAll(): void` - Toggles selection state for all items in receipt

**Behavior:**

- Displays as expandable trigger when used with `uiExpandableTrigger` directive
- Shows chevron icon when expandable context is available
- Batch select/deselect button only visible when selectable items exist

**Example:**

```typescript
@Component({
  template: `
    <ng-container uiExpandable>
      <oms-feature-return-details-order-group
        uiExpandableTrigger
        [receipt]="receipt()"
      ></oms-feature-return-details-order-group>

      <div *uiExpanded>
        <!-- Expanded content -->
      </div>
    </ng-container>
  `,
})
export class MyComponent {
  receipt = signal({
    id: 123,
    printedDate: '2024-01-15T10:30:00Z',
    receiptNumber: 'R-2024-00123',
    items: [/* ... */],
  });
}
```

### ReturnDetailsOrderGroupItemComponent

Displays individual product item with pricing, details, and return status.

**Selector:** `oms-feature-return-details-order-group-item`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `item` | `ReceiptItem` | Yes | The receipt item with product details and pricing |

**Computed Properties:**

- `selected(): boolean` - Item selection state from store
- `canReturn(): { result: boolean; message?: string }` - Return eligibility from API
- `canReturnReceiptItem(): boolean` - Checks if item is in returnable items list
- `canReturnMessage(): string` - Explanation message for return eligibility
- `quantity(): number` - Original quantity from order
- `availableQuantity(): number` - Remaining returnable quantity

**Template Features:**

- Product image with router link to product details
- Product title, contributors, and pricing information
- Product format icon and details (manufacturer, EAN, publication date)
- Return controls (quantity, category, checkbox)
- Warning messages for non-returnable items
- Partial return notification

**E2E Attributes:**

- `[data-what="return-item-row"][data-which="<EAN>"]` - Item row container
- `[data-what="product-image"][data-which="<EAN>"]` - Product image
- `[data-what="product-name"][data-which="<EAN>"]` - Product name
- `[data-what="product-price"][data-which="<EAN>"]` - Price display

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-order-group-item
      [item]="receiptItem()"
      class="border-b border-isa-neutral-300"
    ></oms-feature-return-details-order-group-item>
  `,
})
export class MyComponent {
  receiptItem = signal<ReceiptItem>({
    id: 456,
    product: {
      ean: '9783123456789',
      name: 'Example Book',
      contributors: 'John Author',
      format: 'HC',
      formatDetail: 'Hardcover',
      manufacturer: 'Example Publisher',
      publicationDate: '2024-01-01',
    },
    price: {
      value: { value: 29.99, currency: 'EUR' },
      vat: { inPercent: 7 },
    },
  });
}
```

### ReturnDetailsOrderGroupItemControlsComponent

Interactive controls for quantity selection, category assignment, and item selection.

**Selector:** `oms-feature-return-details-order-group-item-controls`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `item` | `ReceiptItem` | Yes | The receipt item for control configuration |

**Computed Properties:**

- `selected(): boolean` - Selection state from store
- `selectedQuantity(): number` - Selected quantity from store
- `quantityDropdownValues(): number[]` - Array of selectable quantities (1 to available)
- `productCategory(): ProductCategory` - Selected category from store
- `selectable(): boolean` - True if item can be selected (eligibility passed)
- `canReturnReceiptItem(): boolean` - Return eligibility flag

**Resources:**

- `canReturnResource` - Reactive resource for return eligibility validation

**Methods:**

- `setProductCategory(category: ProductCategory): void` - Updates category in store
- `setQuantity(quantity: number): void` - Updates quantity in store
- `setSelected(selected: boolean): void` - Updates selection state in store

**UI Elements:**

- **Quantity Dropdown** - Only visible when available quantity > 1
- **Category Dropdown** - Required for all returnable items
- **Selection Checkbox** - Bullet-style checkbox for return selection
- **Loading Spinner** - Displayed during eligibility validation

**E2E Attributes:**

- `[data-what="return-item-checkbox"][data-which="<EAN>"]` - Selection checkbox
- `[data-what="load-spinner"][data-which="can-return"]` - Loading indicator

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-order-group-item-controls
      [item]="receiptItem()"
    ></oms-feature-return-details-order-group-item-controls>
  `,
})
export class MyComponent {
  receiptItem = signal<ReceiptItem>({
    id: 456,
    product: { ean: '9783123456789', /* ... */ },
    /* ... */
  });
}
```

### ReturnDetailsDataComponent

Displays minimal receipt metadata (date and type) in collapsed view.

**Selector:** `oms-feature-return-details-data`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receipt` | `ReceiptInput` | Yes | Receipt with printedDate and receiptType |

**Template Structure:**

- Receipt date with time (formatted: `dd.MM.yyyy | HH:mm Uhr`)
- Receipt type (translated to German)

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-data
      *uiCollapsed
      [receipt]="receipt()"
    ></oms-feature-return-details-data>
  `,
})
export class MyComponent {
  receipt = signal({
    printedDate: '2024-01-15T10:30:00Z',
    receiptType: 'Invoice',
  });
}
```

### ReturnDetailsOrderGroupDataComponent

Displays comprehensive receipt metadata including buyer and shipping information.

**Selector:** `oms-feature-return-details-order-group-data`

**Inputs:**

| Input | Type | Required | Description |
|-------|------|----------|-------------|
| `receipt` | `ReceiptInput` | Yes | Receipt with buyer, shipping, order, printedDate, receiptType |

**Computed Properties:**

- `buyerName(): string` - Formatted buyer full name
- `shippingName(): string` - Formatted shipping recipient full name

**Template Structure:**

- Receipt date and type
- Order number (Vorgang-ID)
- Order date
- Buyer address (if available)
- Shipping address (if available)

**Example:**

```typescript
@Component({
  template: `
    <oms-feature-return-details-order-group-data
      *uiExpanded
      [receipt]="receipt()"
    ></oms-feature-return-details-order-group-data>
  `,
})
export class MyComponent {
  receipt = signal<Receipt>({
    printedDate: '2024-01-15T10:30:00Z',
    receiptType: 'Invoice',
    order: {
      data: {
        orderNumber: 'ORD-2024-00123',
        orderDate: '2024-01-14T15:00:00Z',
      },
    },
    buyer: {
      firstName: 'John',
      lastName: 'Doe',
      address: {
        street: 'Main St',
        streetNumber: '123',
        zipCode: '12345',
        city: 'Berlin',
      },
    },
    shipping: {
      firstName: 'Jane',
      lastName: 'Smith',
      address: {
        street: 'Second Ave',
        streetNumber: '456',
        zipCode: '54321',
        city: 'Munich',
      },
    },
  });
}
```

## Usage Examples

### Basic Receipt Details View

```typescript
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-receipt-list',
  template: `
    <div class="receipt-list">
      @for (receipt of receipts(); track receipt.id) {
        <button
          (click)="viewDetails(receipt.id)"
          class="receipt-item"
        >
          {{ receipt.receiptNumber }} - {{ receipt.printedDate | date }}
        </button>
      }
    </div>
  `,
})
export class ReceiptListComponent {
  #router = inject(Router);

  receipts = signal([
    { id: 101, receiptNumber: 'R-2024-00101', printedDate: '2024-01-15' },
    { id: 102, receiptNumber: 'R-2024-00102', printedDate: '2024-01-16' },
  ]);

  viewDetails(receiptId: number): void {
    // Navigate to return details with receipt ID
    this.#router.navigate(['/oms/return-details', receiptId]);
  }
}
```

### Programmatic Item Selection

```typescript
import { Component, inject, effect } from '@angular/core';
import { ReturnDetailsStore } from '@isa/oms/data-access';

@Component({
  selector: 'app-auto-select-returns',
  template: `
    <oms-feature-return-details></oms-feature-return-details>
  `,
  providers: [ReturnDetailsStore],
})
export class AutoSelectReturnsComponent {
  #store = inject(ReturnDetailsStore);

  constructor() {
    // Automatically select all eligible items when loaded
    effect(() => {
      const returnableItems = this.#store.returnableItems();
      if (returnableItems.length > 0) {
        const itemIds = returnableItems.map((item) => item.id);
        this.#store.addSelectedItems(itemIds);
      }
    });
  }
}
```

### Custom Return Process Handler

```typescript
import { Component, inject } from '@angular/core';
import { ReturnDetailsStore, ReturnProcessStore } from '@isa/oms/data-access';
import { Router } from '@angular/router';

@Component({
  selector: 'app-custom-return-handler',
  template: `
    <oms-feature-return-details></oms-feature-return-details>

    <button
      uiButton
      color="brand"
      [disabled]="!canProcess()"
      (click)="processReturn()"
    >
      Start Custom Return Process
    </button>
  `,
  providers: [ReturnDetailsStore],
})
export class CustomReturnHandlerComponent {
  #returnDetailsStore = inject(ReturnDetailsStore);
  #returnProcessStore = inject(ReturnProcessStore);
  #router = inject(Router);

  canProcess = computed(() => {
    const selectedItems = this.#returnDetailsStore.selectedItems();
    const categories = this.#returnDetailsStore.itemCategoryMap();

    // Ensure all selected items have categories
    return selectedItems.every((item) =>
      categories[item.id] !== ProductCategory.Unknown
    );
  });

  processReturn(): void {
    const selectedItems = this.#returnDetailsStore.selectedItems();
    const quantities = this.#returnDetailsStore.selectedQuantityMap();
    const categories = this.#returnDetailsStore.itemCategoryMap();
    const receipts = this.#returnDetailsStore.receiptsEntityMap();

    // Group items by receipt
    const itemsByReceipt = groupBy(selectedItems, (item) => item.receipt?.id);

    const returns = Object.entries(itemsByReceipt).map(([receiptId, items]) => ({
      receipt: receipts[Number(receiptId)],
      items: items.map((item) => ({
        receiptItem: item,
        quantity: quantities[item.id],
        category: categories[item.id],
      })),
    }));

    // Initialize custom return process
    this.#returnProcessStore.startProcess({
      processId: Date.now().toString(),
      returns,
    });

    // Navigate to custom handler
    this.#router.navigate(['/custom-return-process']);
  }
}
```

### Monitoring Return Eligibility

```typescript
import { Component, inject, effect } from '@angular/core';
import { ReturnDetailsStore } from '@isa/oms/data-access';

@Component({
  selector: 'app-eligibility-monitor',
  template: `
    <oms-feature-return-details></oms-feature-return-details>

    <div class="eligibility-summary">
      <p>Total Items: {{ totalItems() }}</p>
      <p>Returnable: {{ returnableItems().length }}</p>
      <p>Selected: {{ selectedItems().length }}</p>
    </div>
  `,
  providers: [ReturnDetailsStore],
})
export class EligibilityMonitorComponent {
  #store = inject(ReturnDetailsStore);

  totalItems = computed(() => {
    const items = this.#store.items();
    return Object.keys(items).length;
  });

  returnableItems = this.#store.returnableItems;
  selectedItems = this.#store.selectedItems;

  constructor() {
    // Log when eligibility changes
    effect(() => {
      const returnable = this.returnableItems();
      console.log('Returnable items:', returnable.length);

      returnable.forEach((item) => {
        const canReturn = this.#store.getCanReturn(() => item)();
        console.log(`Item ${item.id}:`, canReturn);
      });
    });
  }
}
```

### Filtering Customer Receipt History

```typescript
import { Component, computed, signal } from '@angular/core';
import { ReturnDetailsLazyComponent } from '@isa/oms/feature/return-details';

@Component({
  selector: 'app-filtered-receipt-history',
  template: `
    <input
      type="text"
      [(ngModel)]="searchTerm"
      placeholder="Filter receipts..."
    />

    @for (receipt of filteredReceipts(); track receipt.id) {
      <oms-feature-return-details-lazy
        [receipt]="receipt"
      ></oms-feature-return-details-lazy>
    }
  `,
  imports: [ReturnDetailsLazyComponent, FormsModule],
})
export class FilteredReceiptHistoryComponent {
  searchTerm = signal('');
  customerReceipts = signal<ReceiptListItem[]>([/* ... */]);

  filteredReceipts = computed(() => {
    const term = this.searchTerm().toLowerCase();
    if (!term) return this.customerReceipts();

    return this.customerReceipts().filter((receipt) =>
      receipt.receiptNumber.toLowerCase().includes(term)
    );
  });
}
```

## Routing and Navigation

### Route Configuration

The library exports a `routes` constant for lazy loading:

```typescript
// libs/oms/feature/return-details/src/lib/routes.ts
import { Routes } from '@angular/router';
import { ReturnDetailsComponent } from './return-details.component';

export const routes: Routes = [
  { path: ':receiptId', component: ReturnDetailsComponent }
];
```

### Integration in Parent Routes

```typescript
// App routing configuration
import { Routes } from '@angular/router';

export const omsRoutes: Routes = [
  {
    path: 'oms',
    children: [
      {
        path: 'return-details',
        loadChildren: () =>
          import('@isa/oms/feature/return-details').then((m) => m.routes),
      },
      {
        path: 'process',
        loadChildren: () =>
          import('@isa/oms/feature/return-process').then((m) => m.routes),
      },
    ],
  },
];
```

### Navigation Patterns

#### From Search Results

```typescript
// Navigate to receipt details from search
this.#router.navigate(['/oms/return-details', receiptId]);
```

#### Back to Search

```typescript
// User clicks back button - uses Location.back()
this.location.back();
```

#### To Return Process

```typescript
// After selecting items, navigate to process (relative navigation)
this.#router.navigate(['../../', 'process'], {
  relativeTo: this._activatedRoute,
});
// Resolves to: /oms/process (assuming current: /oms/return-details/123)
```

#### To Customer Details

```typescript
// From header menu, navigate to customer page
const referenceId = 12345;
const timestamp = Date.now();
this.#router.navigate(['/kunde', timestamp, 'customer', 'search', referenceId]);
```

### Route Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `receiptId` | `number` | Yes | The ID of the receipt to display |

**Parameter Parsing:**

```typescript
// Component automatically parses receiptId from route
receiptId = computed<number>(() => {
  const params = this.params();
  if (params) {
    return z.coerce.number().parse(params['receiptId']);
  }
  throw new Error('No receiptId found in route params');
});
```

### Tab-Based Navigation

The component uses `injectTabId()` from `@isa/core/tabs` for process identification:

```typescript
private processId = injectTabId();
// Used to track return process across navigation
```

This enables multiple concurrent return processes in different browser tabs.

## Architecture Notes

### Component Hierarchy

```
ReturnDetailsComponent (Routed, provides ReturnDetailsStore)
├── Back Button (Location.back())
├── ReturnDetailsStaticComponent (Primary receipt, always visible)
│   ├── ReturnDetailsHeaderComponent (Customer info + navigation menu)
│   ├── Expandable Section
│   │   ├── ReturnDetailsOrderGroupDataComponent (Expanded: full details)
│   │   └── ReturnDetailsDataComponent (Collapsed: minimal info)
│   ├── ReturnDetailsOrderGroupComponent (Toolbar: date, count, select all)
│   └── ReturnDetailsOrderGroupItemComponent[] (Product items)
│       └── ReturnDetailsOrderGroupItemControlsComponent (Quantity, category, checkbox)
├── ProgressBar (While loading customer receipts)
├── ReturnDetailsLazyComponent[] (Customer receipt history, lazy-loaded)
│   ├── ReturnDetailsOrderGroupComponent (Expandable trigger)
│   └── Expandable Section
│       ├── ProgressBar (While loading full receipt)
│       ├── ReturnDetailsOrderGroupDataComponent (Full details)
│       └── ReturnDetailsOrderGroupItemComponent[] (Product items)
└── Start Return Button (Fixed bottom-right, initiates process)
```

### State Management Architecture

```
ReturnDetailsStore (NgRx Signals)
├── Receipt Entities (Normalized receipts by ID)
├── Item Entities (Normalized items by ID)
├── Selection State
│   ├── selectedItemIds: Set<number>
│   ├── selectedQuantityMap: Record<itemId, quantity>
│   └── itemCategoryMap: Record<itemId, ProductCategory>
├── Return Eligibility Cache
│   └── canReturnResource(item) → { result: boolean, message?: string }
└── Computed Values
    ├── selectedItems(): ReceiptItem[]
    ├── returnableItems(): ReceiptItem[]
    └── availableQuantityMap(): Record<itemId, availableQty>
```

### Resource-Based Data Loading

The library uses Angular's `resource()` API for reactive data fetching:

```typescript
// Primary receipt - loads immediately
receiptResource = this.#store.receiptResource(this.receiptId);

// Customer receipts - loads based on buyer email
customerReceiptsResource = resource({
  params: this.receiptResource.value,
  loader: async ({ params, abortSignal }) => {
    const email = params.buyer?.communicationDetails?.email;
    if (!email) return [];

    return await this.#returnDetailsService.fetchReceiptsByEmail(
      { email },
      abortSignal
    );
  },
});

// Lazy receipt - loads only when expanded
receiptResource = this.#store.receiptResource(this.receiptId);
// receiptId computed from expandable state
```

### Performance Optimizations

1. **Lazy Loading** - Customer receipt history loads on-demand
2. **Expandable Sections** - Full receipt details load only when expanded
3. **OnPush Change Detection** - All components use `ChangeDetectionStrategy.OnPush`
4. **Resource Caching** - Resources automatically cache and deduplicate requests
5. **Computed Signals** - Efficient reactive computations with memoization
6. **Track By** - Optimized `@for` loops with `track` expressions

### Integration Points

**Dependencies on OMS Data Access:**

- `ReturnDetailsStore` - Primary state management
- `ReturnProcessStore` - Return process orchestration
- `ReturnDetailsService` - API integration
- `ReceiptItem`, `Receipt`, `ReceiptListItem` - Data models
- `canReturnReceiptItem()`, `getReceiptItemAction()` - Helper functions

**Dependencies on Shared UI:**

- `@isa/ui/buttons` - Button components
- `@isa/ui/expandable` - Collapsible sections
- `@isa/ui/input-controls` - Dropdowns and checkboxes
- `@isa/ui/item-rows` - Product display layout
- `@isa/ui/toolbar` - Receipt toolbar
- `@isa/ui/progress-bar` - Loading indicators
- `@isa/ui/menu` - Customer navigation menu

**Dependencies on Shared Features:**

- `@isa/shared/product-image` - Product image loading
- `@isa/shared/product-router-link` - Product detail navigation

### Known Architectural Considerations

#### 1. Store Provider Scope (High Priority)

**Current State:**
- `ReturnDetailsStore` is provided at component level in `ReturnDetailsComponent`
- Store state is scoped to the component instance
- Navigation away destroys store state

**Implication:**
- Users cannot navigate back and preserve selection state
- Multi-receipt workflows require reselection on return

**Recommendation:**
- Consider route-level provider for persistent state during session
- Implement state restoration from session storage if needed

#### 2. Customer Receipt Loading Strategy (Medium Priority)

**Current State:**
- All customer receipts load simultaneously after primary receipt
- No pagination or incremental loading
- Large receipt histories could impact performance

**Recommendation:**
- Implement virtual scrolling for receipt lists
- Add pagination controls for customers with many receipts
- Consider limiting initial load to recent receipts

#### 3. Return Eligibility Validation Timing (Low Priority)

**Current State:**
- Eligibility checks trigger when category changes
- Each category change triggers API call
- No debouncing or batching

**Potential Improvement:**
- Batch eligibility checks for multiple items
- Debounce category changes to reduce API calls
- Pre-fetch eligibility for all items on load

## Dependencies

### Required Libraries

- `@angular/core` - Angular framework (v20.1.2)
- `@angular/router` - Routing and navigation
- `@angular/common` - Common Angular utilities (DatePipe, Location, etc.)
- `@angular/forms` - FormsModule for ngModel
- `@isa/oms/data-access` - OMS data access layer and state management
- `@isa/core/tabs` - Tab ID management
- `@isa/core/logging` - Logging service
- `@isa/ui/buttons` - Button components
- `@isa/ui/expandable` - Expandable section directives
- `@isa/ui/input-controls` - Input components (checkbox, dropdown)
- `@isa/ui/item-rows` - Item display layout
- `@isa/ui/toolbar` - Toolbar component
- `@isa/ui/progress-bar` - Progress indicators
- `@isa/ui/menu` - Menu components
- `@isa/shared/product-image` - Product image directive
- `@isa/shared/product-router-link` - Product navigation
- `@isa/icons` - Icon library
- `@ng-icons/core` - Icon rendering
- `zod` - Schema validation
- `lodash` - Utility functions (groupBy)

### Path Alias

Import from: `@isa/oms/feature/return-details`

### Peer Dependencies

The library requires the following services to be available through dependency injection:

- `ReturnDetailsStore` (must be provided at component or route level)
- `ReturnProcessStore` (for return process integration)
- `ReturnDetailsService` (from `@isa/oms/data-access`)

## Testing

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

### Running Tests

```bash
# Run tests for this library
npx nx test oms-feature-return-details --skip-nx-cache

# Run tests with coverage
npx nx test oms-feature-return-details --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test oms-feature-return-details --watch
```

### Test Structure

The library includes unit tests for key components:

- **ReturnDetailsHeaderComponent** - Customer information and navigation menu
- **ReturnDetailsOrderGroupItemControlsComponent** - Item control interactions

### Example Test (ReturnDetailsHeaderComponent)

```typescript
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { ReturnDetailsHeaderComponent } from './return-details-header.component';
import { ReturnDetailsStore } from '@isa/oms/data-access';
import { signal } from '@angular/core';

describe('ReturnDetailsHeaderComponent', () => {
  let spectator: Spectator<ReturnDetailsHeaderComponent>;

  const createComponent = createComponentFactory({
    component: ReturnDetailsHeaderComponent,
  });

  const getReceiptMock = signal<Receipt>({
    id: 12345,
    items: [],
    buyer: {
      reference: { id: 12345 },
      firstName: 'John',
      lastName: 'Doe',
      buyerNumber: '123456',
    },
  });

  const storeMock = {
    getReceipt: () => getReceiptMock,
  };

  beforeEach(() => {
    spectator = createComponent({
      props: {
        receiptId: 12345,
      },
      providers: [
        {
          provide: ReturnDetailsStore,
          useValue: storeMock,
        },
      ],
    });
  });

  it('should create', () => {
    expect(spectator.component).toBeTruthy();
  });

  describe('[data-what="button"][data-which="customer-actions"]', () => {
    it('should be disabled if referenceId() returns undefined', () => {
      jest.spyOn(spectator.component, 'referenceId').mockReturnValue(undefined);
      spectator.detectComponentChanges();

      const button = spectator.query(
        '[data-what="button"][data-which="customer-actions"]'
      );

      expect(button).toBeTruthy();
      expect(button).toBeDisabled();
    });

    it('should be enabled if referenceId() returns a value', () => {
      jest.spyOn(spectator.component, 'referenceId').mockReturnValue(12345);
      spectator.detectComponentChanges();

      const button = spectator.query(
        '[data-what="button"][data-which="customer-actions"]'
      );

      expect(button).toBeTruthy();
      expect(button).not.toBeDisabled();
    });
  });
});
```

### Testing Best Practices

1. **Mock ReturnDetailsStore** - Always provide mock store in tests
2. **Test E2E Attributes** - Verify `data-what` and `data-which` attributes exist
3. **Test User Interactions** - Verify click handlers, form inputs, navigation
4. **Test Computed Properties** - Ensure reactive computations work correctly
5. **Test Error States** - Verify error messages and disabled states
6. **Test Loading States** - Verify progress indicators during data fetching

### E2E Testing Support

All interactive elements include `data-what` and `data-which` attributes for automated testing:

```typescript
// Product item row
<ui-item-row data-what="return-item-row" [attr.data-which]="item.product.ean">

// Selection checkbox
<input
  type="checkbox"
  data-what="return-item-checkbox"
  [attr.data-which]="item().product.ean"
/>

// Customer actions button
<button
  data-what="button"
  data-which="customer-actions"
>

// Menu items
<a data-what="menu-item" data-which="customer-details">
```

## Best Practices

### State Management

**Do:**

```typescript
// Use store for all selection state
this.#store.addSelectedItems([itemId]);
this.#store.setQuantity(itemId, quantity);
this.#store.setProductCategory(itemId, category);
```

**Don't:**

```typescript
// Don't manage selection state locally
this.selectedItems.push(item); // ❌ Wrong
```

### Component Inputs

**Do:**

```typescript
// Use required inputs with proper typing
item = input.required<ReceiptItem>();
receipt = input.required<Receipt>();
```

**Don't:**

```typescript
// Don't use optional inputs when data is required
@Input() item?: ReceiptItem; // ❌ Old pattern
```

### Resource Loading

**Do:**

```typescript
// Use resources for async data with proper error handling
customerReceiptsResource = resource({
  params: this.receiptResource.value,
  loader: async ({ params, abortSignal }) => {
    try {
      return await this.service.fetch(params, abortSignal);
    } catch (error) {
      this.#logger.error('Failed to fetch', error);
      return [];
    }
  },
});
```

**Don't:**

```typescript
// Don't use manual subscription management
ngOnInit() {
  this.service.fetch().subscribe(data => {
    this.receipts = data; // ❌ Old pattern
  });
}
```

### Expandable Sections

**Do:**

```typescript
// Use expandable directives correctly
<ng-container uiExpandable #expandable="uiExpandable">
  <div uiExpandableTrigger>Click to expand</div>
  <div *uiExpanded>Expanded content</div>
  <div *uiCollapsed>Collapsed content</div>
</ng-container>
```

**Don't:**

```typescript
// Don't mix expandable patterns
<div uiExpandable *uiExpanded> // ❌ Wrong nesting
```

### Navigation

**Do:**

```typescript
// Use relative navigation for known routes
this.#router.navigate(['../../process'], {
  relativeTo: this._activatedRoute,
});

// Use Location.back() for browser back
this.location.back();
```

**Don't:**

```typescript
// Don't use hardcoded absolute paths
this.#router.navigate(['/oms/return/process']); // ❌ Brittle
```

### E2E Attributes

**Do:**

```typescript
// Always include data-what and data-which for interactive elements
<button
  data-what="button"
  data-which="start-return"
  (click)="startProcess()"
>

// Use dynamic data-which for item-specific elements
<div
  data-what="product-item"
  [attr.data-which]="item.product.ean"
>
```

**Don't:**

```typescript
// Don't omit E2E attributes
<button (click)="startProcess()"> // ❌ Missing attributes
```

### Logging

**Do:**

```typescript
// Use logger with context
#logger = logger(() => ({
  component: 'ReturnDetailsComponent',
  receiptId: this.receiptId(),
}));

this.#logger.error('Failed to load receipt', error);
```

**Don't:**

```typescript
// Don't use console.log
console.log('Error:', error); // ❌ No context, no production logging
```

### Error Handling

**Do:**

```typescript
// Handle errors gracefully with fallbacks
try {
  return await this.service.fetch(email, abortSignal);
} catch (error) {
  this.#logger.error('Failed to fetch customer receipts', error);
  return []; // Return empty array as fallback
}
```

**Don't:**

```typescript
// Don't let errors propagate unhandled
return await this.service.fetch(email); // ❌ No error handling
```

### Performance

**Do:**

```typescript
// Use OnPush change detection
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
})

// Use track expressions in @for loops
@for (item of items(); track item.id) {
  <app-item [item]="item"></app-item>
}

// Lazy load expensive operations
receiptId = computed(() => {
  if (!this.expandable().expanded()) return undefined;
  return this.receipt().id;
});
```

**Don't:**

```typescript
// Don't use default change detection
@Component({
  changeDetection: ChangeDetectionStrategy.Default, // ❌
})

// Don't use @for without track
@for (item of items()) { // ❌ Poor performance
```

## License

Internal ISA Frontend library - not for external distribution.
