# @isa/oms/utils/translation

A lightweight translation utility library for OMS receipt types providing human-readable German translations through both service-based and pipe-based interfaces.

## Overview

The OMS Translation utility library provides a simple, extensible translation system for the OMS `ReceiptType` enum. It offers a clean abstraction for translating receipt type codes (numeric enums) into human-readable German text, with support for custom translation overrides via Angular's dependency injection system. The library includes both a service for programmatic translations and an Angular pipe for declarative template usage.

## Table of Contents

- [Features](#features)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [Receipt Types](#receipt-types)
- [Custom Translations](#custom-translations)
- [Testing](#testing)
- [Architecture Notes](#architecture-notes)

## Features

- **13 receipt type translations** - Complete German translation coverage for all OMS receipt types
- **Service-based translation** - `ReceiptTypeTranslationService` for programmatic use
- **Angular pipe** - `omsReceiptTypeTranslation` pipe for template-based translation
- **Dependency injection** - Full DI support with `providedIn: 'root'`
- **Custom overrides** - Replace default translations with custom implementations
- **Fallback handling** - Automatic enum name fallback for missing translations
- **Type-safe** - Strongly typed translation dictionary with TypeScript
- **Zero configuration** - Works out of the box with sensible defaults
- **Lightweight** - Minimal dependencies, purely translation focused

## Quick Start

### 1. Import and Use the Service

```typescript
import { Component, inject } from '@angular/core';
import { ReceiptTypeTranslationService } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-display',
  template: '...'
})
export class ReceiptDisplayComponent {
  #translationService = inject(ReceiptTypeTranslationService);

  displayReceiptType(type: ReceiptType): void {
    const translation = this.#translationService.translate(type);
    console.log(translation); // Output: "Lieferschein"
  }
}
```

### 2. Use the Pipe in Templates

```typescript
import { Component } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-list',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <div>
      <span>{{ receiptType | omsReceiptTypeTranslation }}</span>
    </div>
  `
})
export class ReceiptListComponent {
  receiptType = ReceiptType.ShippingNote; // Displays: "Lieferschein"
}
```

### 3. Simple Example with Multiple Types

```typescript
import { Component, inject } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-types',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <ul>
      @for (type of receiptTypes; track type) {
        <li>{{ type | omsReceiptTypeTranslation }}</li>
      }
    </ul>
  `
})
export class ReceiptTypesComponent {
  receiptTypes = [
    ReceiptType.ShippingNote,      // "Lieferschein"
    ReceiptType.Invoice,            // "Rechnung"
    ReceiptType.CreditNote,         // "Gutschrift"
    ReceiptType.ReturnReceipt       // "Retourenbeleg"
  ];
}
```

## Core Concepts

### Translation Dictionary

The library provides a default translation dictionary mapping each `ReceiptType` enum value to its German translation:

```typescript
const receiptTypeTranslations = {
  [ReceiptType.NotSet]: 'Nicht gesetzt',
  [ReceiptType.ShippingNote]: 'Lieferschein',
  [ReceiptType.CreditNote]: 'Gutschrift',
  [ReceiptType.CollectiveShippingNote]: 'Sammellieferschein',
  [ReceiptType.CollectiveCreditNote]: 'Sammelgutschrift',
  [ReceiptType.BonusCardCollectiveShippingNote]: 'Bonuskarte Sammellieferschein',
  [ReceiptType.BonusCardCollectiveCreditNote]: 'Bonuskarte Sammelgutschrift',
  [ReceiptType.PaymentReceipt]: 'Zahlungsbeleg',
  [ReceiptType.Invoice]: 'Rechnung',
  [ReceiptType.CollectiveInvoice]: 'Sammelrechnung',
  [ReceiptType.ProformaInvoice]: 'Proforma-Rechnung',
  [ReceiptType.CashReceipt]: 'Kassenbeleg',
  [ReceiptType.ReturnReceipt]: 'Retourenbeleg',
};
```

### ReceiptType Enum Structure

The `ReceiptType` enum from `@isa/oms/data-access` uses bit flag values:

```typescript
export enum ReceiptType {
  NotSet = 0,                              // No receipt type set
  ShippingNote = 1,                        // Standard shipping document
  CreditNote = 2,                          // Credit/refund document
  CollectiveShippingNote = 4,              // Batch shipping document
  CollectiveCreditNote = 8,                // Batch credit document
  BonusCardCollectiveShippingNote = 16,    // Bonus card batch shipping
  BonusCardCollectiveCreditNote = 32,      // Bonus card batch credit
  PaymentReceipt = 64,                     // Payment confirmation
  Invoice = 128,                           // Customer invoice
  CollectiveInvoice = 256,                 // Batch invoice
  ProformaInvoice = 512,                   // Pro forma invoice
  CashReceipt = 1024,                      // Cash register receipt
  ReturnReceipt = 2048,                    // Return/remission receipt
}
```

**Note**: While the enum uses bit flag values (powers of 2), the current implementation treats them as discrete values, not as combinable flags. Each receipt has a single type.

### Dependency Injection Pattern

The library uses Angular's dependency injection system with an `InjectionToken`:

```typescript
export const RECEIPT_TYPE_TRANSLATION = new InjectionToken<ReceiptTypeTranslation>(
  'RECEIPT_TYPE_TRANSLATION',
  {
    factory() {
      return receiptTypeTranslations; // Default translations
    },
  }
);
```

This pattern enables:
1. **Default behavior** - Automatic injection of default translations
2. **Custom overrides** - Provide custom translations at any level (root, module, component)
3. **Testability** - Easy mocking in unit tests
4. **Type safety** - TypeScript ensures translation completeness

### Fallback Mechanism

The service includes intelligent fallback handling:

```typescript
translate(type: ReceiptType): string {
  return this.#translation[type] ?? ReceiptType[type];
}
```

**Behavior**:
- **Translation exists**: Returns the German translation
- **Translation missing**: Returns the enum name as a string (e.g., "ShippingNote")
- **Invalid type**: Returns the numeric value as a string (e.g., "999")

This ensures the system never crashes, even with unexpected or new receipt types.

## API Reference

### ReceiptTypeTranslationService

Main service for programmatic receipt type translation.

#### `translate(type: ReceiptType): string`

Translates a receipt type enum value to its German text representation.

**Parameters:**
- `type: ReceiptType` - The receipt type enum value to translate

**Returns:** `string` - German translation, or enum name if translation missing

**Example:**
```typescript
import { inject } from '@angular/core';
import { ReceiptTypeTranslationService } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

const service = inject(ReceiptTypeTranslationService);

service.translate(ReceiptType.Invoice);           // "Rechnung"
service.translate(ReceiptType.ShippingNote);      // "Lieferschein"
service.translate(ReceiptType.NotSet);            // "Nicht gesetzt"
```

**Fallback behavior:**
```typescript
// Missing translation
service.translate(999 as ReceiptType);  // "999"

// All standard types have translations
service.translate(ReceiptType.CashReceipt);  // "Kassenbeleg"
```

### ReceiptTypeTranslationPipe

Angular pipe for declarative template-based translation.

#### `transform(value?: ReceiptType): string`

**Pipe Name:** `omsReceiptTypeTranslation`

**Parameters:**
- `value?: ReceiptType` - Receipt type to translate (defaults to `ReceiptType.NotSet`)

**Returns:** `string` - German translation

**Example:**
```typescript
import { Component } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <!-- Simple usage -->
    <span>{{ receiptType | omsReceiptTypeTranslation }}</span>

    <!-- With default fallback (NotSet) -->
    <span>{{ undefined | omsReceiptTypeTranslation }}</span>

    <!-- In conditional -->
    @if (receipt) {
      <span>{{ receipt.type | omsReceiptTypeTranslation }}</span>
    }
  `
})
export class ExampleComponent {
  receiptType = ReceiptType.Invoice;
}
```

### Types and Tokens

#### `ReceiptTypeTranslation`

Type representing the translation dictionary structure.

```typescript
export type ReceiptTypeTranslation = {
  [ReceiptType.NotSet]: string;
  [ReceiptType.ShippingNote]: string;
  [ReceiptType.CreditNote]: string;
  [ReceiptType.CollectiveShippingNote]: string;
  [ReceiptType.CollectiveCreditNote]: string;
  [ReceiptType.BonusCardCollectiveShippingNote]: string;
  [ReceiptType.BonusCardCollectiveCreditNote]: string;
  [ReceiptType.PaymentReceipt]: string;
  [ReceiptType.Invoice]: string;
  [ReceiptType.CollectiveInvoice]: string;
  [ReceiptType.ProformaInvoice]: string;
  [ReceiptType.CashReceipt]: string;
  [ReceiptType.ReturnReceipt]: string;
};
```

#### `RECEIPT_TYPE_TRANSLATION`

Injection token for providing custom translations.

```typescript
export const RECEIPT_TYPE_TRANSLATION: InjectionToken<ReceiptTypeTranslation>;
```

### Provider Functions

#### `provideReceiptTypeTranslation(translation: ReceiptTypeTranslation): Provider[]`

Creates providers for custom receipt type translations.

**Parameters:**
- `translation: ReceiptTypeTranslation` - Custom translation dictionary

**Returns:** `Provider[]` - Angular providers array

**Example:**
```typescript
import { provideReceiptTypeTranslation } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

const customTranslations = {
  [ReceiptType.NotSet]: 'Kein Typ',
  [ReceiptType.ShippingNote]: 'Versandschein',
  [ReceiptType.Invoice]: 'Faktura',
  // ... other translations
};

export const appConfig = {
  providers: [
    provideReceiptTypeTranslation(customTranslations)
  ]
};
```

## Usage Examples

### Basic Template Usage

```typescript
import { Component } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-header',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <div class="receipt-header">
      <h2>{{ receiptType | omsReceiptTypeTranslation }}</h2>
      <p>Receipt #{{ receiptNumber }}</p>
    </div>
  `
})
export class ReceiptHeaderComponent {
  receiptType = ReceiptType.Invoice;
  receiptNumber = '12345';
}
```

### Service-Based Translation in Component Logic

```typescript
import { Component, inject, OnInit } from '@angular/core';
import { ReceiptTypeTranslationService } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

interface Receipt {
  id: number;
  type: ReceiptType;
  amount: number;
}

@Component({
  selector: 'app-receipt-summary',
  template: `
    <div>
      <h3>Receipt Summary</h3>
      <p>{{ summaryText }}</p>
    </div>
  `
})
export class ReceiptSummaryComponent implements OnInit {
  #translationService = inject(ReceiptTypeTranslationService);

  summaryText = '';

  receipts: Receipt[] = [
    { id: 1, type: ReceiptType.Invoice, amount: 100 },
    { id: 2, type: ReceiptType.CreditNote, amount: 50 },
    { id: 3, type: ReceiptType.ShippingNote, amount: 0 }
  ];

  ngOnInit(): void {
    this.summaryText = this.generateSummary();
  }

  private generateSummary(): string {
    return this.receipts
      .map(r => {
        const typeText = this.#translationService.translate(r.type);
        return `${typeText}: €${r.amount}`;
      })
      .join(', ');
    // Output: "Rechnung: €100, Gutschrift: €50, Lieferschein: €0"
  }
}
```

### Dynamic Receipt Type Display

```typescript
import { Component, input, computed } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-badge',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <span [class]="badgeClass()">
      {{ type() | omsReceiptTypeTranslation }}
    </span>
  `,
  styles: [`
    .badge { padding: 4px 8px; border-radius: 4px; }
    .badge-invoice { background: #e3f2fd; color: #1976d2; }
    .badge-credit { background: #e8f5e9; color: #388e3c; }
    .badge-shipping { background: #fff3e0; color: #f57c00; }
    .badge-return { background: #fce4ec; color: #c2185b; }
  `]
})
export class ReceiptBadgeComponent {
  type = input.required<ReceiptType>();

  badgeClass = computed(() => {
    const typeValue = this.type();
    switch (typeValue) {
      case ReceiptType.Invoice:
      case ReceiptType.CollectiveInvoice:
        return 'badge badge-invoice';
      case ReceiptType.CreditNote:
      case ReceiptType.CollectiveCreditNote:
        return 'badge badge-credit';
      case ReceiptType.ShippingNote:
      case ReceiptType.CollectiveShippingNote:
        return 'badge badge-shipping';
      case ReceiptType.ReturnReceipt:
        return 'badge badge-return';
      default:
        return 'badge';
    }
  });
}
```

### List Display with Filtering

```typescript
import { Component, signal, computed } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

interface ReceiptListItem {
  id: number;
  type: ReceiptType;
  date: Date;
  customer: string;
}

@Component({
  selector: 'app-receipt-list',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <div>
      <h3>Receipts</h3>
      <select (change)="onFilterChange($event)">
        <option value="">All Types</option>
        @for (type of receiptTypes; track type.value) {
          <option [value]="type.value">{{ type.value | omsReceiptTypeTranslation }}</option>
        }
      </select>

      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Type</th>
            <th>Customer</th>
            <th>Date</th>
          </tr>
        </thead>
        <tbody>
          @for (receipt of filteredReceipts(); track receipt.id) {
            <tr>
              <td>{{ receipt.id }}</td>
              <td>{{ receipt.type | omsReceiptTypeTranslation }}</td>
              <td>{{ receipt.customer }}</td>
              <td>{{ receipt.date | date }}</td>
            </tr>
          }
        </tbody>
      </table>
    </div>
  `
})
export class ReceiptListComponent {
  receiptTypes = [
    { value: ReceiptType.Invoice },
    { value: ReceiptType.CreditNote },
    { value: ReceiptType.ShippingNote },
    { value: ReceiptType.ReturnReceipt }
  ];

  allReceipts = signal<ReceiptListItem[]>([
    { id: 1, type: ReceiptType.Invoice, date: new Date(), customer: 'Customer A' },
    { id: 2, type: ReceiptType.CreditNote, date: new Date(), customer: 'Customer B' },
    { id: 3, type: ReceiptType.ShippingNote, date: new Date(), customer: 'Customer C' },
  ]);

  selectedFilter = signal<ReceiptType | null>(null);

  filteredReceipts = computed(() => {
    const filter = this.selectedFilter();
    if (filter === null) return this.allReceipts();
    return this.allReceipts().filter(r => r.type === filter);
  });

  onFilterChange(event: Event): void {
    const value = (event.target as HTMLSelectElement).value;
    this.selectedFilter.set(value ? Number(value) as ReceiptType : null);
  }
}
```

### Integration with OMS Return Details

Real-world example from the OMS feature library:

```typescript
import { Component, input } from '@angular/core';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';

interface ReturnData {
  receiptType: ReceiptType;
  receiptNumber: string;
  // ... other properties
}

@Component({
  selector: 'oms-feature-return-details-data',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  template: `
    <div class="return-details">
      <div class="receipt-info">
        <label>Receipt Type:</label>
        <span>{{ data().receiptType | omsReceiptTypeTranslation }}</span>
      </div>
      <div class="receipt-number">
        <label>Receipt Number:</label>
        <span>{{ data().receiptNumber }}</span>
      </div>
    </div>
  `
})
export class ReturnDetailsDataComponent {
  data = input.required<ReturnData>();
}
```

### Programmatic Translation with Type Guards

```typescript
import { Component, inject } from '@angular/core';
import { ReceiptTypeTranslationService } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-receipt-validator',
  template: '...'
})
export class ReceiptValidatorComponent {
  #translationService = inject(ReceiptTypeTranslationService);

  validateAndDescribeReceipt(type: ReceiptType | number): string {
    // Type guard to ensure valid ReceiptType
    if (!this.isValidReceiptType(type)) {
      return `Invalid receipt type: ${type}`;
    }

    const translation = this.#translationService.translate(type);
    const category = this.categorizeReceipt(type);

    return `${translation} (Category: ${category})`;
  }

  private isValidReceiptType(type: ReceiptType | number): type is ReceiptType {
    return Object.values(ReceiptType).includes(type as ReceiptType);
  }

  private categorizeReceipt(type: ReceiptType): string {
    // Categorize based on receipt type
    if (type === ReceiptType.Invoice || type === ReceiptType.CollectiveInvoice) {
      return 'Billing';
    }
    if (type === ReceiptType.CreditNote || type === ReceiptType.CollectiveCreditNote) {
      return 'Refund';
    }
    if (type === ReceiptType.ShippingNote || type === ReceiptType.CollectiveShippingNote) {
      return 'Shipping';
    }
    return 'Other';
  }
}
```

## Receipt Types

### Complete Receipt Type Reference

| Enum Value | Numeric Value | German Translation | Description |
|------------|--------------|-------------------|-------------|
| `NotSet` | 0 | Nicht gesetzt | No receipt type assigned |
| `ShippingNote` | 1 | Lieferschein | Standard delivery/shipping document |
| `CreditNote` | 2 | Gutschrift | Credit note for refunds |
| `CollectiveShippingNote` | 4 | Sammellieferschein | Batch/collective shipping document |
| `CollectiveCreditNote` | 8 | Sammelgutschrift | Batch/collective credit note |
| `BonusCardCollectiveShippingNote` | 16 | Bonuskarte Sammellieferschein | Bonus card batch shipping |
| `BonusCardCollectiveCreditNote` | 32 | Bonuskarte Sammelgutschrift | Bonus card batch credit |
| `PaymentReceipt` | 64 | Zahlungsbeleg | Payment confirmation document |
| `Invoice` | 128 | Rechnung | Customer invoice |
| `CollectiveInvoice` | 256 | Sammelrechnung | Batch/collective invoice |
| `ProformaInvoice` | 512 | Proforma-Rechnung | Pro forma invoice (quotation) |
| `CashReceipt` | 1024 | Kassenbeleg | Cash register receipt |
| `ReturnReceipt` | 2048 | Retourenbeleg | Return/remission receipt |

### Receipt Type Categories

#### Shipping Documents
- **ShippingNote** (1) - Individual shipping
- **CollectiveShippingNote** (4) - Batch shipping
- **BonusCardCollectiveShippingNote** (16) - Bonus card shipping

#### Credit Documents
- **CreditNote** (2) - Individual credit
- **CollectiveCreditNote** (8) - Batch credit
- **BonusCardCollectiveCreditNote** (32) - Bonus card credit

#### Invoice Documents
- **Invoice** (128) - Standard invoice
- **CollectiveInvoice** (256) - Batch invoice
- **ProformaInvoice** (512) - Quotation invoice

#### Payment Documents
- **PaymentReceipt** (64) - Payment confirmation
- **CashReceipt** (1024) - Cash transaction

#### Return Documents
- **ReturnReceipt** (2048) - Returns/remissions

### Bit Flag Architecture

The enum uses bit flag values (powers of 2), allowing for potential future combination operations:

```typescript
// Current usage: Single discrete values
const type = ReceiptType.Invoice; // 128

// Potential future usage: Bitwise combinations (not currently implemented)
// const combined = ReceiptType.Invoice | ReceiptType.ShippingNote; // 129
```

**Important**: The current implementation does not support bitwise operations. Each receipt has exactly one type.

## Custom Translations

### Providing Custom Translations

You can override the default translations at any level of your application using the `provideReceiptTypeTranslation` function:

#### Application-Level Override

```typescript
import { ApplicationConfig } from '@angular/core';
import { provideReceiptTypeTranslation } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

const customTranslations = {
  [ReceiptType.NotSet]: 'Kein Typ',
  [ReceiptType.ShippingNote]: 'Versandschein',
  [ReceiptType.CreditNote]: 'Kreditnote',
  [ReceiptType.CollectiveShippingNote]: 'Sammel-Versandschein',
  [ReceiptType.CollectiveCreditNote]: 'Sammel-Kreditnote',
  [ReceiptType.BonusCardCollectiveShippingNote]: 'Bonuskarte Sammel-Versandschein',
  [ReceiptType.BonusCardCollectiveCreditNote]: 'Bonuskarte Sammel-Kreditnote',
  [ReceiptType.PaymentReceipt]: 'Zahlungsnachweis',
  [ReceiptType.Invoice]: 'Faktura',
  [ReceiptType.CollectiveInvoice]: 'Sammel-Faktura',
  [ReceiptType.ProformaInvoice]: 'Proforma-Faktura',
  [ReceiptType.CashReceipt]: 'Kassenbon',
  [ReceiptType.ReturnReceipt]: 'Rückgabe-Beleg',
};

export const appConfig: ApplicationConfig = {
  providers: [
    provideReceiptTypeTranslation(customTranslations)
  ]
};
```

#### Component-Level Override

```typescript
import { Component } from '@angular/core';
import { provideReceiptTypeTranslation } from '@isa/oms/utils/translation';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

const shortTranslations = {
  [ReceiptType.NotSet]: 'N/A',
  [ReceiptType.ShippingNote]: 'LS',
  [ReceiptType.CreditNote]: 'GS',
  [ReceiptType.CollectiveShippingNote]: 'SLS',
  [ReceiptType.CollectiveCreditNote]: 'SGS',
  [ReceiptType.BonusCardCollectiveShippingNote]: 'BK-SLS',
  [ReceiptType.BonusCardCollectiveCreditNote]: 'BK-SGS',
  [ReceiptType.PaymentReceipt]: 'ZB',
  [ReceiptType.Invoice]: 'RE',
  [ReceiptType.CollectiveInvoice]: 'SRE',
  [ReceiptType.ProformaInvoice]: 'PRO',
  [ReceiptType.CashReceipt]: 'KB',
  [ReceiptType.ReturnReceipt]: 'RB',
};

@Component({
  selector: 'app-compact-receipt-view',
  standalone: true,
  imports: [ReceiptTypeTranslationPipe],
  providers: [
    provideReceiptTypeTranslation(shortTranslations)
  ],
  template: `
    <span class="receipt-code">
      {{ receiptType | omsReceiptTypeTranslation }}
    </span>
  `
})
export class CompactReceiptViewComponent {
  receiptType = ReceiptType.Invoice; // Displays: "RE"
}
```

#### Testing with Custom Translations

```typescript
import { TestBed } from '@angular/core/testing';
import { ReceiptTypeTranslationService, provideReceiptTypeTranslation } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

describe('Custom Translation', () => {
  it('should use custom translations', () => {
    const customTranslations = {
      [ReceiptType.NotSet]: 'Custom NotSet',
      [ReceiptType.ShippingNote]: 'Custom Shipping',
      // ... other translations
    };

    TestBed.configureTestingModule({
      providers: [provideReceiptTypeTranslation(customTranslations)]
    });

    const service = TestBed.inject(ReceiptTypeTranslationService);
    expect(service.translate(ReceiptType.ShippingNote)).toBe('Custom Shipping');
  });
});
```

### Direct Token Override

For advanced scenarios, you can provide the translation directly using the injection token:

```typescript
import { Component } from '@angular/core';
import { RECEIPT_TYPE_TRANSLATION } from '@isa/oms/utils/translation';
import { ReceiptType } from '@isa/oms/data-access';

@Component({
  selector: 'app-advanced',
  providers: [
    {
      provide: RECEIPT_TYPE_TRANSLATION,
      useValue: {
        [ReceiptType.NotSet]: 'Custom Translation',
        // ... other translations
      }
    }
  ],
  template: '...'
})
export class AdvancedComponent {}
```

## Testing

The library uses **Jest** with **jest-preset-angular** for testing.

### Running Tests

```bash
# Run tests for this library
npx nx test oms-utils-translation --skip-nx-cache

# Run tests with coverage
npx nx test oms-utils-translation --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test oms-utils-translation --watch
```

### Test Examples

#### Testing the Service

```typescript
import { TestBed } from '@angular/core/testing';
import { ReceiptTypeTranslationService } from './receipt-type-translation.service';
import { ReceiptType } from '@isa/oms/data-access';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [ReceiptTypeTranslationService]
    });
    service = TestBed.inject(ReceiptTypeTranslationService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should translate ShippingNote correctly', () => {
    expect(service.translate(ReceiptType.ShippingNote)).toBe('Lieferschein');
  });

  it('should translate Invoice correctly', () => {
    expect(service.translate(ReceiptType.Invoice)).toBe('Rechnung');
  });

  it('should translate NotSet correctly', () => {
    expect(service.translate(ReceiptType.NotSet)).toBe('Nicht gesetzt');
  });

  it('should fallback to enum name for unknown types', () => {
    const unknownType = 9999 as ReceiptType;
    expect(service.translate(unknownType)).toBe('9999');
  });

  it('should translate all receipt types', () => {
    const types = [
      ReceiptType.NotSet,
      ReceiptType.ShippingNote,
      ReceiptType.CreditNote,
      ReceiptType.CollectiveShippingNote,
      ReceiptType.CollectiveCreditNote,
      ReceiptType.BonusCardCollectiveShippingNote,
      ReceiptType.BonusCardCollectiveCreditNote,
      ReceiptType.PaymentReceipt,
      ReceiptType.Invoice,
      ReceiptType.CollectiveInvoice,
      ReceiptType.ProformaInvoice,
      ReceiptType.CashReceipt,
      ReceiptType.ReturnReceipt,
    ];

    types.forEach(type => {
      const result = service.translate(type);
      expect(result).toBeTruthy();
      expect(typeof result).toBe('string');
      expect(result.length).toBeGreaterThan(0);
    });
  });
});
```

#### Testing the Pipe

```typescript
import { TestBed } from '@angular/core/testing';
import { ReceiptTypeTranslationPipe } from './receipt-type-translation.pipe';
import { ReceiptTypeTranslationService } from './receipt-type-translation.service';
import { ReceiptType } from '@isa/oms/data-access';

describe('ReceiptTypeTranslationPipe', () => {
  let pipe: ReceiptTypeTranslationPipe;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        ReceiptTypeTranslationPipe,
        ReceiptTypeTranslationService
      ]
    });
    pipe = TestBed.inject(ReceiptTypeTranslationPipe);
  });

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

  it('should transform ShippingNote', () => {
    expect(pipe.transform(ReceiptType.ShippingNote)).toBe('Lieferschein');
  });

  it('should transform Invoice', () => {
    expect(pipe.transform(ReceiptType.Invoice)).toBe('Rechnung');
  });

  it('should use NotSet as default', () => {
    expect(pipe.transform()).toBe('Nicht gesetzt');
  });

  it('should handle undefined by using default', () => {
    expect(pipe.transform(undefined as any)).toBe('Nicht gesetzt');
  });
});
```

#### Testing Custom Translations

```typescript
import { TestBed } from '@angular/core/testing';
import {
  ReceiptTypeTranslationService,
  provideReceiptTypeTranslation,
  ReceiptTypeTranslation
} from './receipt-type-translation.service';
import { ReceiptType } from '@isa/oms/data-access';

describe('Custom Receipt Type Translation', () => {
  it('should use custom translation dictionary', () => {
    const customTranslations: ReceiptTypeTranslation = {
      [ReceiptType.NotSet]: 'Test NotSet',
      [ReceiptType.ShippingNote]: 'Test Shipping',
      [ReceiptType.CreditNote]: 'Test Credit',
      [ReceiptType.CollectiveShippingNote]: 'Test Collective Shipping',
      [ReceiptType.CollectiveCreditNote]: 'Test Collective Credit',
      [ReceiptType.BonusCardCollectiveShippingNote]: 'Test Bonus Shipping',
      [ReceiptType.BonusCardCollectiveCreditNote]: 'Test Bonus Credit',
      [ReceiptType.PaymentReceipt]: 'Test Payment',
      [ReceiptType.Invoice]: 'Test Invoice',
      [ReceiptType.CollectiveInvoice]: 'Test Collective Invoice',
      [ReceiptType.ProformaInvoice]: 'Test Proforma',
      [ReceiptType.CashReceipt]: 'Test Cash',
      [ReceiptType.ReturnReceipt]: 'Test Return',
    };

    TestBed.configureTestingModule({
      providers: [provideReceiptTypeTranslation(customTranslations)]
    });

    const service = TestBed.inject(ReceiptTypeTranslationService);

    expect(service.translate(ReceiptType.ShippingNote)).toBe('Test Shipping');
    expect(service.translate(ReceiptType.Invoice)).toBe('Test Invoice');
  });
});
```

### Test Coverage Goals

The library should maintain high test coverage:
- **Service tests**: All translation methods and fallback behavior
- **Pipe tests**: Transform method with various inputs
- **Custom provider tests**: Override functionality
- **Integration tests**: Pipe using service correctly

## Architecture Notes

### Current Architecture

The library follows a simple, focused architecture:

```
Components/Features
       ↓
  ReceiptTypeTranslationPipe (template usage)
       ↓
  ReceiptTypeTranslationService (business logic)
       ↓
  RECEIPT_TYPE_TRANSLATION (injection token)
       ↓
  receiptTypeTranslations (default dictionary)
```

### Design Principles

1. **Single Responsibility** - Library focuses solely on receipt type translation
2. **Dependency Injection** - Full Angular DI integration for flexibility
3. **Type Safety** - TypeScript ensures translation completeness
4. **Fallback Safety** - Never crashes on unknown types
5. **Extensibility** - Easy to override translations at any level
6. **Minimal Dependencies** - Only depends on Angular core and OMS data-access

### Architectural Considerations

#### 1. Standalone vs Module-Based (Completed)

**Current State**: The pipe is a traditional module-based component (not standalone).

**Recommendation**: Migrate to standalone component in line with Angular 20 best practices:

```typescript
// Current
@Pipe({ name: 'omsReceiptTypeTranslation' })
export class ReceiptTypeTranslationPipe {}

// Recommended
@Pipe({
  name: 'omsReceiptTypeTranslation',
  standalone: true
})
export class ReceiptTypeTranslationPipe {}
```

**Impact**: Low risk, improves consistency with modern Angular patterns

#### 2. Internationalization (i18n) Integration (Future Enhancement)

**Current State**: Hardcoded German translations.

**Future Option**: Integrate with Angular i18n or external translation library:

```typescript
// Potential future implementation
@Injectable({ providedIn: 'root' })
export class ReceiptTypeTranslationService {
  #translateService = inject(TranslateService);

  translate(type: ReceiptType): string {
    return this.#translateService.instant(`receipt.type.${ReceiptType[type]}`);
  }
}
```

**Impact**: Medium priority if multi-language support is needed

#### 3. Translation Completeness Validation (Low Priority)

**Current State**: TypeScript ensures all enum values have translations.

**Potential Enhancement**: Runtime validation to ensure completeness:

```typescript
function validateTranslationCompleteness(
  translations: ReceiptTypeTranslation
): void {
  const allTypes = Object.values(ReceiptType).filter(v => typeof v === 'number');
  const missingTranslations = allTypes.filter(
    type => !translations[type as ReceiptType]
  );

  if (missingTranslations.length > 0) {
    console.warn('Missing translations for:', missingTranslations);
  }
}
```

**Impact**: Low priority, type safety already provides compile-time checks

### Performance Considerations

1. **Service Singleton** - `providedIn: 'root'` ensures single instance
2. **Pure Pipe** - Pipe should be marked as pure for Angular optimization:
   ```typescript
   @Pipe({
     name: 'omsReceiptTypeTranslation',
     pure: true  // Recommended addition
   })
   ```
3. **Dictionary Lookup** - O(1) constant time translation lookup
4. **No External Calls** - All translations in memory, no HTTP requests

### Future Enhancements

Potential improvements identified:

1. **Standalone Migration** - Convert pipe to standalone component
2. **Pure Pipe Flag** - Add explicit pure flag for clarity
3. **Multi-Language Support** - Integration with i18n system
4. **Translation Caching** - Optional caching for repeated translations (likely unnecessary)
5. **Abbreviation Support** - Optional short form translations (e.g., "LS" for Lieferschein)
6. **Category Grouping** - Helper methods to group by category (shipping, invoice, etc.)

## Dependencies

### Required Libraries

- `@angular/core` - Angular framework (dependency injection, pipes)
- `@isa/oms/data-access` - OMS data models (ReceiptType enum)

### Path Alias

Import from: `@isa/oms/utils/translation`

### Export Structure

```typescript
// Main exports
export { ReceiptTypeTranslationService } from './lib/receipt-type/receipt-type-translation.service';
export { ReceiptTypeTranslationPipe } from './lib/receipt-type/receipt-type-translation.pipe';
export {
  RECEIPT_TYPE_TRANSLATION,
  provideReceiptTypeTranslation,
  type ReceiptTypeTranslation
} from './lib/receipt-type/receipt-type-translation.service';
```

## Best Practices

### 1. Prefer the Pipe in Templates

```typescript
// ✅ Good - declarative and clear
template: `<span>{{ type | omsReceiptTypeTranslation }}</span>`

// ❌ Avoid - unnecessary service injection for simple display
template: `<span>{{ getTranslation(type) }}</span>`
```

### 2. Use the Service for Business Logic

```typescript
// ✅ Good - service for programmatic translation
generateReport(): string {
  return this.#translationService.translate(this.receiptType);
}

// ❌ Avoid - pipe is for templates only
```

### 3. Type Safety First

```typescript
// ✅ Good - type-safe translation
translate(type: ReceiptType): string {
  return this.#service.translate(type);
}

// ❌ Avoid - loosing type safety
translate(type: any): string {
  return this.#service.translate(type);
}
```

### 4. Custom Translations at Appropriate Level

```typescript
// ✅ Good - app-level for global overrides
export const appConfig: ApplicationConfig = {
  providers: [provideReceiptTypeTranslation(customTranslations)]
};

// ❌ Avoid - component-level unless truly component-specific
```

### 5. Test Translation Coverage

```typescript
// ✅ Good - test all enum values
it('should translate all receipt types', () => {
  Object.values(ReceiptType)
    .filter(v => typeof v === 'number')
    .forEach(type => {
      const result = service.translate(type as ReceiptType);
      expect(result).toBeTruthy();
    });
});
```

### 6. Handle Unknown Types Gracefully

```typescript
// ✅ Good - fallback handling
const translation = this.#service.translate(type);
// Always returns a string, never null/undefined

// ✅ Good - explicit handling
const translation = this.#service.translate(type);
if (translation === ReceiptType[type]) {
  console.warn('Unknown receipt type:', type);
}
```

### 7. Use Standalone Flag for Pipe

```typescript
// ✅ Recommended - modern Angular pattern
@Pipe({
  name: 'omsReceiptTypeTranslation',
  standalone: true,
  pure: true
})
export class ReceiptTypeTranslationPipe {}
```

## License

Internal ISA Frontend library - not for external distribution.
