Added "skip:ci" tag to multiple project configurations to prevent CI runs for certain libraries. This change affects the following libraries: crm-feature-customer-card-transactions, crm-feature-customer-loyalty-cards, oms-data-access, oms-feature-return-details, oms-feature-return-process, oms-feature-return-summary, remission-data-access, remission-feature-remission-list, remission-feature-remission-return-receipt-details, remission-feature-remission-return-receipt-list, remission-shared-remission-start-dialog, remission-shared-return-receipt-actions, shared-address, shared-delivery, ui-carousel, and ui-dialog. Also updated CI command in package.json to exclude tests with the "skip:ci" tag.
@isa/remission/shared/return-receipt-actions
Angular standalone components for managing return receipt actions including deletion, continuation, and completion workflows in the remission process.
Overview
The Remission Shared Return Receipt Actions library provides two specialized action components for managing return receipts in remission workflows. These components handle critical operations like deleting return receipts, continuing remission processes, and completing remission packages ("Wanne"). They integrate with the RemissionStore and RemissionReturnReceiptService to provide state management and API interactions.
Table of Contents
- Features
- Quick Start
- Component API
- Usage Examples
- Component Details
- Business Logic
- Testing
- Dependencies
Features
- Return Receipt Deletion - Delete return receipts with validation and state cleanup
- Remission Continuation - Continue existing or start new remission workflows
- Remission Completion - Complete remission packages with automatic package assignment
- State Management - Integration with RemissionStore for current remission tracking
- Confirmation Dialogs - User confirmation for destructive and workflow-changing actions
- Navigation - Automatic routing to remission list after operations
- Loading States - Pending state indicators during async operations
- Error Handling - Comprehensive error logging and recovery
- E2E Testing Attributes - Complete data-* attributes for automated testing
Quick Start
1. Import Components
import {
RemissionReturnReceiptActionsComponent,
RemissionReturnReceiptCompleteComponent
} from '@isa/remission/shared/return-receipt-actions';
@Component({
selector: 'app-return-receipt-card',
imports: [
RemissionReturnReceiptActionsComponent,
RemissionReturnReceiptCompleteComponent
],
template: '...'
})
export class ReturnReceiptCardComponent {}
2. Use Return Receipt Actions
@Component({
template: `
<lib-remission-return-receipt-actions
[remissionReturn]="returnData()"
[displayDeleteAction]="true"
(reloadData)="onReloadList()"
></lib-remission-return-receipt-actions>
`
})
export class MyComponent {
returnData = signal<Return>({ id: 123, receipts: [...] });
onReloadList() {
// Refresh the return receipt list
}
}
3. Use Complete Button
@Component({
template: `
<lib-remission-return-receipt-complete
[returnId]="123"
[receiptId]="456"
[itemsLength]="5"
[hasAssignedPackage]="true"
(reloadData)="onReloadList()"
></lib-remission-return-receipt-complete>
`
})
export class MyComponent {
onReloadList() {
// Refresh the data
}
}
Component API
RemissionReturnReceiptActionsComponent
Action buttons for deleting and continuing return receipts.
Selector
<lib-remission-return-receipt-actions></lib-remission-return-receipt-actions>
Inputs
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
remissionReturn |
Return |
Yes | - | Return data containing receipts and metadata |
displayDeleteAction |
boolean |
No | true |
Whether to show the delete button |
Outputs
| Output | Type | Description |
|---|---|---|
reloadData |
void |
Emitted when the list needs to be reloaded |
Key Methods
onDelete(): Promise<void>
- Deletes all receipts associated with the return
- Clears RemissionStore state if deleting current remission
- Emits
reloadDataevent after successful deletion - Handles errors with comprehensive logging
onContinueRemission(): Promise<void>
- Starts a new remission if not already started
- Shows confirmation dialog if different remission is in progress
- Navigates to remission list after starting
- Validates receipt availability before continuing
RemissionReturnReceiptCompleteComponent
Fixed-position button for completing a remission package ("Wanne").
Selector
<lib-remission-return-receipt-complete></lib-remission-return-receipt-complete>
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
returnId |
number |
Yes | Return ID (coerced from string) |
receiptId |
number |
Yes | Receipt ID (coerced from string) |
itemsLength |
number |
Yes | Number of items in the receipt |
hasAssignedPackage |
boolean |
Yes | Whether package number is assigned |
Outputs
| Output | Type | Description |
|---|---|---|
reloadData |
void |
Emitted when data needs to be reloaded |
Key Methods
completeRemission(): Promise<void>
- Ensures package is assigned (#5289 requirement)
- Completes the return receipt and return
- Shows "Wanne abgeschlossen" dialog with options
- Option 1: Complete return group (Beenden)
- Option 2: Start new receipt in same group (Neue Wanne)
- Emits
reloadDataevent after completion
completeSingleReturnReceipt(): Promise<Return>
- Completes the return receipt via API
- Clears RemissionStore state
- Returns the completed return data
Usage Examples
Complete Return Receipt Workflow
import {
RemissionReturnReceiptActionsComponent,
RemissionReturnReceiptCompleteComponent
} from '@isa/remission/shared/return-receipt-actions';
@Component({
selector: 'app-remission-receipt-details',
imports: [
RemissionReturnReceiptActionsComponent,
RemissionReturnReceiptCompleteComponent
],
template: `
<div class="receipt-details">
<!-- Receipt content -->
<div class="receipt-items">
<!-- Item list -->
</div>
<!-- Action buttons at top -->
<lib-remission-return-receipt-actions
[remissionReturn]="returnData()"
[displayDeleteAction]="!isCompleted()"
(reloadData)="handleReload()"
></lib-remission-return-receipt-actions>
<!-- Complete button (fixed position) -->
@if (!isCompleted()) {
<lib-remission-return-receipt-complete
[returnId]="returnId()"
[receiptId]="receiptId()"
[itemsLength]="receiptItems().length"
[hasAssignedPackage]="hasPackage()"
(reloadData)="handleReload()"
></lib-remission-return-receipt-complete>
}
</div>
`
})
export class RemissionReceiptDetailsComponent {
returnData = signal<Return | undefined>(undefined);
returnId = signal(0);
receiptId = signal(0);
receiptItems = signal<ReceiptItem[]>([]);
hasPackage = signal(false);
isCompleted = computed(() => {
return this.returnData()?.completed ?? false;
});
handleReload() {
// Reload receipt data from API
this.fetchReceiptData();
}
async fetchReceiptData() {
// Fetch logic
}
}
Return Receipt List Actions
import { RemissionReturnReceiptActionsComponent } from '@isa/remission/shared/return-receipt-actions';
@Component({
selector: 'app-receipt-list-item',
imports: [RemissionReturnReceiptActionsComponent],
template: `
<div class="receipt-card">
<div class="receipt-header">
<span>Receipt #{{ return().receipts[0].number }}</span>
<span>{{ return().receipts[0].created | date }}</span>
</div>
<div class="receipt-body">
<div>Items: {{ itemCount() }}</div>
<div>Status: {{ status() }}</div>
</div>
<div class="receipt-actions">
<lib-remission-return-receipt-actions
[remissionReturn]="return()"
[displayDeleteAction]="canDelete()"
(reloadData)="reloadList.emit()"
></lib-remission-return-receipt-actions>
</div>
</div>
`
})
export class ReceiptListItemComponent {
return = input.required<Return>();
reloadList = output<void>();
itemCount = computed(() => {
return this.return().receipts[0]?.data?.items?.length ?? 0;
});
status = computed(() => {
return this.return().completed ? 'Completed' : 'In Progress';
});
canDelete = computed(() => {
return !this.return().completed && this.itemCount() === 0;
});
}
Component Details
RemissionReturnReceiptActionsComponent
Button Layout:
-
Delete Button (Secondary, optional):
- Text: "Löschen"
- Disabled when
itemQuantity > 0 - Calls
onDelete()method - Only shown if
displayDeleteActionis true
-
Continue Button (Primary):
- Text: "Befüllen"
- Always enabled
- Calls
onContinueRemission()method
Computed Properties:
returnId = computed(() => this.remissionReturn().id);
receiptIds = computed(() => this.remissionReturn().receipts?.map(r => r.id) || []);
firstReceiptId = computed(() => this.receiptIds()[0]);
itemQuantity = computed(() => getReceiptItemQuantityFromReturn(this.remissionReturn()));
isCurrentRemission = computed(() => this.#store.isCurrentRemission({ returnId, receiptId }));
Delete Flow:
- Iterate through all receipt IDs
- For each receipt, check if it's the current remission
- If current, clear RemissionStore state
- Call
cancelReturnReceiptAPI for each receipt - Call
cancelReturnAPI for the return - Emit
reloadDataevent - Log errors if any occur
Continue Flow:
- Check if remission is already started
- If not started:
- Start remission with first receipt ID
- Navigate to remission list
- If different remission in progress:
- Show confirmation dialog
- Options: Continue current OR start new
- If starting new, clear store and start with first receipt
- Navigate to remission list
RemissionReturnReceiptCompleteComponent
Button Appearance:
- Fixed position (bottom-right corner)
- Large size
- Brand color
- Text: "Wanne abschließen"
- Shows pending state during completion
Completion Flow:
-
Package Assignment Check (#5289):
- If no package assigned, open package assignment dialog
- If dialog canceled, abort completion
-
Complete Receipt:
- Call
completeSingleReturnReceipt() - Clears RemissionStore state
- Call
-
Return Group Handling:
- If return has
returnGroup:- Show "Wanne abgeschlossen" dialog
- Option 1 (Beenden): Complete return group via API
- Option 2 (Neue Wanne): Start new remission in same group
- If return has
-
Emit Reload Event: Notify parent to refresh data
Dialog Content:
Title: "Wanne abgeschlossen"
Message: "Legen Sie abschließend den 'Blank Beizettel' in die abgeschlossene Wanne.
Der Warenbegleitschein wird nach Abschluss der Remission versendet.
Zum Öffnen eines neuen Warenbegleitscheins setzen Sie die Remission fort."
Buttons:
- Neue Wanne (Close button)
- Beenden (Confirm button)
Business Logic
Package Assignment Requirement (#5289)
Before completing a remission, a package number must be assigned. If not already assigned:
- Open package assignment dialog
- User scans or enters package number
- Package is assigned to the receipt
- Completion proceeds
- If user cancels, completion is aborted
Return Group Workflow
Return Groups represent physical containers ("Wannen") for grouped returns:
- Multiple receipts can belong to the same return group
- Completing a receipt offers two options:
- Complete Entire Group: Finalizes all receipts in the group
- Start New Receipt: Creates a new receipt in the same group
State Management Integration
RemissionStore Interactions:
// Check if current remission
isCurrentRemission({ returnId, receiptId })
// Clear state when deleting or completing
clearState()
// Start remission when continuing
startRemission({ returnId, receiptId })
// Reload return data
reloadReturn()
Confirmation Dialogs
Delete Confirmation: Not shown (direct deletion)
Continue with Different Remission:
Title: "Bereits geöffneter Warenbegleitschein"
Message: "Möchten Sie wirklich einen neuen öffnen, oder möchten Sie zuerst den aktuellen abschließen?"
Close: "Aktuellen bearbeiten"
Confirm: "Neuen öffnen"
Completion Confirmation:
Title: "Wanne abgeschlossen"
Message: "Legen Sie abschließend den 'Blank Beizettel' in die abgeschlossene Wanne..."
Close: "Neue Wanne"
Confirm: "Beenden"
Testing
# Run tests
npx nx test remission-return-receipt-actions --skip-nx-cache
# Run with coverage
npx nx test remission-return-receipt-actions --code-coverage --skip-nx-cache
Test Examples
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { RemissionReturnReceiptActionsComponent } from './remission-return-receipt-actions.component';
describe('RemissionReturnReceiptActionsComponent', () => {
let component: RemissionReturnReceiptActionsComponent;
let fixture: ComponentFixture<RemissionReturnReceiptActionsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RemissionReturnReceiptActionsComponent]
}).compileComponents();
fixture = TestBed.createComponent(RemissionReturnReceiptActionsComponent);
component = fixture.componentInstance;
});
it('should disable delete button when items exist', () => {
fixture.componentRef.setInput('remissionReturn', {
id: 123,
receipts: [{ id: 456, data: { items: [{ id: 1 }] } }]
});
fixture.detectChanges();
const deleteBtn = fixture.nativeElement.querySelector('[data-what="return-receipt-delete-button"]');
expect(deleteBtn?.disabled).toBe(true);
});
it('should emit reloadData after successful deletion', async () => {
const reloadSpy = vi.fn();
component.reloadData.subscribe(reloadSpy);
await component.onDelete();
expect(reloadSpy).toHaveBeenCalled();
});
});
Dependencies
@angular/core- Angular framework@angular/router- Navigation@isa/remission/data-access- RemissionStore, RemissionReturnReceiptService, Return types@isa/remission/shared/remission-start-dialog- RemissionStartService@isa/ui/buttons- Button components@isa/ui/dialog- Confirmation dialog@isa/core/tabs- Tab ID injection@isa/core/logging- Logger service
Architecture Notes
State Synchronization: Both components integrate tightly with RemissionStore to ensure UI state matches backend state.
Navigation Integration: Uses Angular Router and tab ID for context-aware navigation within multi-tab environments.
Error Handling: Comprehensive error logging with contextual information for debugging.
User Confirmations: Critical actions (changing remission, completing groups) require explicit user confirmation.
License
Internal ISA Frontend library - not for external distribution.