167 lines
6.4 KiB
Markdown
167 lines
6.4 KiB
Markdown
# Comparison Offers Feature Documentation
|
||
|
||
## Overview
|
||
|
||
The comparison offers feature requires users to provide at least 3 comparison offers for each cost position in their application. This ensures transparency and cost-effectiveness in funding requests. Users can alternatively provide a justification if comparison offers are not applicable for specific cost positions.
|
||
|
||
## Features
|
||
|
||
- **3 Offers Requirement**: Each cost position must have at least 3 comparison offers
|
||
- **Justification Alternative**: Users can mark that no offers are required and provide a detailed justification
|
||
- **Attachment Support**: Each offer can be linked to an uploaded attachment (e.g., quotes, invoices)
|
||
- **Visual Indicators**: Cost positions without sufficient offers are highlighted with warning icons
|
||
- **Status Tracking**: Real-time status display showing offer count and completion status
|
||
|
||
## Database Schema
|
||
|
||
### `comparison_offers` Table
|
||
Stores comparison offers for cost positions:
|
||
- `id` (INT, PRIMARY KEY) - Unique identifier
|
||
- `application_id` (INT) - References the application
|
||
- `cost_position_index` (INT) - Index of the cost position in the costs array
|
||
- `supplier_name` (VARCHAR 255) - Name of the supplier/vendor
|
||
- `amount` (INT) - Offer amount in cents
|
||
- `description` (TEXT) - Optional description of the offer
|
||
- `url` (VARCHAR 500) - Optional URL to offer document or webpage
|
||
- `attachment_id` (INT) - Optional reference to an attachment
|
||
- `created_at` (DATETIME) - Creation timestamp
|
||
- `updated_at` (DATETIME) - Last update timestamp
|
||
|
||
### `cost_position_justifications` Table
|
||
Stores justifications for positions without offers:
|
||
- `id` (INT, PRIMARY KEY) - Unique identifier
|
||
- `application_id` (INT) - References the application
|
||
- `cost_position_index` (INT) - Index of the cost position
|
||
- `no_offers_required` (INT) - Boolean flag (0/1)
|
||
- `justification` (TEXT) - Required explanation when no_offers_required is 1
|
||
- `created_at` (DATETIME) - Creation timestamp
|
||
- `updated_at` (DATETIME) - Last update timestamp
|
||
|
||
## API Endpoints
|
||
|
||
### Create Comparison Offer
|
||
`POST /api/applications/{pa_id}/costs/{cost_position_index}/offers`
|
||
- Request body:
|
||
```json
|
||
{
|
||
"supplier_name": "Supplier GmbH",
|
||
"amount": 1500.50,
|
||
"description": "Optional description",
|
||
"url": "https://beispiel.de/angebot.pdf",
|
||
"attachment_id": 123
|
||
}
|
||
```
|
||
- Returns: ComparisonOfferResponse with offer details
|
||
|
||
### Get Cost Position Offers
|
||
`GET /api/applications/{pa_id}/costs/{cost_position_index}/offers`
|
||
- Returns: CostPositionOffersResponse with all offers and justification info
|
||
|
||
### Delete Comparison Offer
|
||
`DELETE /api/applications/{pa_id}/costs/{cost_position_index}/offers/{offer_id}`
|
||
- Removes a specific offer
|
||
|
||
### Update Cost Position Justification
|
||
`PUT /api/applications/{pa_id}/costs/{cost_position_index}/justification`
|
||
- Request body:
|
||
```json
|
||
{
|
||
"no_offers_required": true,
|
||
"justification": "Detailed explanation why no offers are needed..."
|
||
}
|
||
```
|
||
|
||
## Frontend Components
|
||
|
||
### ComparisonOffersDialog
|
||
Main dialog for managing offers for a cost position:
|
||
- Add/remove comparison offers
|
||
- Add URL links to online offers
|
||
- Link attachments to offers (either URL or attachment required)
|
||
- Mark position as not requiring offers
|
||
- Provide justification when needed
|
||
|
||
### CostPositionsList
|
||
Enhanced cost positions list with offer status:
|
||
- Visual indicators for offer status
|
||
- Warning icons for incomplete positions
|
||
- Quick access to manage offers
|
||
- Real-time status updates
|
||
|
||
## User Interface
|
||
|
||
### Status Indicators
|
||
- ✅ **Green checkmark**: Position has 3+ offers
|
||
- ℹ️ **Blue info icon**: Position marked as not requiring offers (with justification)
|
||
- ⚠️ **Yellow warning**: Position needs more offers or justification
|
||
- 🔄 **Loading spinner**: Status being loaded
|
||
|
||
### Validation Rules
|
||
1. Each cost position must have either:
|
||
- At least 3 comparison offers, OR
|
||
- Be marked as "no offers required" with a justification (min. 10 characters)
|
||
2. Cost positions without sufficient offers are highlighted in the list
|
||
3. Offers must include:
|
||
- Supplier name (required, must be unique per cost position)
|
||
- Amount (required, must be greater than 0)
|
||
- Either URL or attachment (at least one required)
|
||
- Description (optional)
|
||
4. Duplicate suppliers: Each supplier can only have one offer per cost position
|
||
5. URL format: Must be a valid HTTP/HTTPS URL (protocol is auto-added if missing)
|
||
6. Amount format: Accepts both comma and period as decimal separator
|
||
|
||
## Migration
|
||
|
||
To add the comparison offers tables to your database:
|
||
|
||
```sql
|
||
mysql -u your_user -p your_database < src/migrations/add_comparison_offers_tables.sql
|
||
mysql -u your_user -p your_database < src/migrations/add_url_to_comparison_offers.sql
|
||
```
|
||
|
||
## Usage Example
|
||
|
||
### In Cost Positions List
|
||
```tsx
|
||
<CostPositionsList
|
||
costs={formData.costs}
|
||
paId={applicationId}
|
||
paKey={applicationKey}
|
||
isAdmin={false}
|
||
readOnly={false}
|
||
attachments={availableAttachments}
|
||
/>
|
||
```
|
||
|
||
### Managing Offers
|
||
1. Click "Angebote" button next to any cost position
|
||
2. In the dialog:
|
||
- Add up to 5 comparison offers
|
||
- Link existing attachments to offers
|
||
- OR check "no offers required" and provide justification
|
||
3. Save changes
|
||
|
||
## Best Practices
|
||
|
||
1. **Encourage Early Collection**: Remind users to collect offers before starting the application
|
||
2. **Clear Instructions**: Provide examples of valid justifications for positions without offers
|
||
3. **Attachment Organization**: Name attachment files clearly to easily link them to offers
|
||
4. **Regular Validation**: Check offer status before submission
|
||
|
||
## Error Handling
|
||
|
||
- **Duplicate Suppliers**: Shows user-friendly error message when trying to add the same supplier twice
|
||
- **Invalid URLs**: Automatically prepends https:// if missing, validates format
|
||
- **Invalid Amounts**: Real-time validation with support for German number format (comma as decimal)
|
||
- **Missing Data**: Clear error messages when required fields are missing
|
||
|
||
## Future Enhancements
|
||
|
||
1. **Bulk Import**: Import multiple offers from CSV/Excel
|
||
2. **Templates**: Save common suppliers for quick reuse
|
||
3. **Price Comparison**: Automatic highlighting of best offer
|
||
4. **History Tracking**: Track changes to offers over time
|
||
5. **Export Function**: Export all offers as PDF/Excel report
|
||
6. **Smart Validation**: Detect duplicate suppliers or suspicious patterns
|
||
7. **URL Validation**: Automatic checking of URL availability and validity
|
||
8. **Screenshot Capture**: Automatic capture of web-based offers for archival |