- Added a comprehensive manual testing guide for the countdown functionality in `test_countdown_manual.md`. - Developed a detailed Selenium test suite in `test_countdown_selenium.py` to verify countdown behavior, including server accessibility, login, countdown progression, and UI elements. - Created a minimal Selenium test version in `test_countdown_selenium_minimal.py` for quick checks without complex setup. - Introduced a verification script in `test_countdown_verification.py` to analyze code correctness, including server checks, build file existence, and countdown logic.
233 lines
5.6 KiB
Markdown
233 lines
5.6 KiB
Markdown
# Materiale Pris Integration Fix
|
|
|
|
## Dato: 2024-11-07
|
|
## Status: ✅ Færdig
|
|
|
|
## Problem
|
|
|
|
Når man tilføjer et materiale i SmartPackageForm via "Tilføj materiale" knappen, viste prisen altid 0.00 kr selvom materialer har priser i databasen.
|
|
|
|
### Symptomer
|
|
- MaterialDialog viste "Standardpris: 0.00 kr/stk"
|
|
- Effektiv pris viste også 0.00 kr/stk
|
|
- Brugerdefineret pris skulle indtastes manuelt
|
|
|
|
### Root Cause
|
|
`/api/materials` endpoint hentede kun data fra `materials` tabellen uden at JOIN'e med `material_prices` tabellen hvor priserne faktisk er gemt.
|
|
|
|
## Løsning
|
|
|
|
### Backend Ændringer
|
|
|
|
**Fil:** `/backend/unified-server.js`
|
|
|
|
**Ændring 1: Tilføjet JOIN med material_prices**
|
|
```javascript
|
|
const materials = await databaseService.query(
|
|
`SELECT
|
|
m.*,
|
|
mp.price,
|
|
mp.unit as price_unit,
|
|
mp.valid_from,
|
|
mp.supplier_name as price_supplier
|
|
FROM materials m
|
|
LEFT JOIN material_prices mp ON m.id = mp.material_id
|
|
AND mp.is_active = 1
|
|
AND mp.id = (
|
|
SELECT id
|
|
FROM material_prices
|
|
WHERE material_id = m.id
|
|
AND is_active = 1
|
|
ORDER BY valid_from DESC, id DESC
|
|
LIMIT 1
|
|
)
|
|
${whereClause}
|
|
ORDER BY m.name ASC
|
|
LIMIT ? OFFSET ?`,
|
|
[...params, parseInt(limit), parseInt(offset)]
|
|
);
|
|
```
|
|
|
|
**Ændring 2: Rettede ambiguous column names**
|
|
```javascript
|
|
// FØR:
|
|
if (search) {
|
|
whereClause += ' AND (name LIKE ? OR description LIKE ? OR sku LIKE ?)';
|
|
}
|
|
|
|
// EFTER:
|
|
if (search) {
|
|
whereClause += ' AND (m.name LIKE ? OR m.description LIKE ? OR m.sku LIKE ?)';
|
|
}
|
|
```
|
|
|
|
### Hvordan det virker
|
|
|
|
1. **LEFT JOIN:** Alle materialer vises, også dem uden priser
|
|
2. **Subquery:** Finder den nyeste aktive pris for hvert materiale
|
|
3. **ORDER BY valid_from DESC:** Bruger den senest gyldige pris
|
|
4. **is_active = 1:** Kun aktive priser inkluderes
|
|
|
|
### Database Schema
|
|
|
|
**Tabeller involveret:**
|
|
- `materials` - Materiale grunddata (id, name, unit, description, etc.)
|
|
- `material_prices` - Priser for materialer (material_id, price, valid_from, etc.)
|
|
|
|
**Relationer:**
|
|
```
|
|
materials.id ← material_prices.material_id (1:N)
|
|
```
|
|
|
|
## Test Resultater
|
|
|
|
### Før Fix
|
|
```bash
|
|
curl http://localhost:4031/api/materials?limit=5
|
|
```
|
|
```json
|
|
{
|
|
"id": 16,
|
|
"name": "B7 tagplader",
|
|
"unit": "m2",
|
|
"price": null // ❌ Ingen pris
|
|
}
|
|
```
|
|
|
|
### Efter Fix
|
|
```bash
|
|
curl http://localhost:4031/api/materials?search=B7%20tag
|
|
```
|
|
```json
|
|
{
|
|
"id": 16,
|
|
"name": "B7 tagplader",
|
|
"unit": "m2",
|
|
"price": "125.00" // ✅ Pris hentet fra material_prices
|
|
}
|
|
```
|
|
|
|
### Test Case: Eternit tagsten
|
|
```json
|
|
{
|
|
"id": 37,
|
|
"name": "Eternit tagsten B6 rød",
|
|
"unit": "m2",
|
|
"price": "145.00"
|
|
}
|
|
```
|
|
|
|
## Frontend Integration
|
|
|
|
MaterialDialog modtager nu automatisk pris fra backend:
|
|
|
|
**Før (linje 105-107 i MaterialDialog.js):**
|
|
```javascript
|
|
const materialPrice = value.price ? parseFloat(value.price) : 0;
|
|
// value.price var altid undefined → price = 0
|
|
```
|
|
|
|
**Efter:**
|
|
```javascript
|
|
const materialPrice = value.price ? parseFloat(value.price) : 0;
|
|
// value.price = "125.00" fra database → price = 125.00 ✅
|
|
```
|
|
|
|
### Autocomplete Visning
|
|
MaterialDialog's Autocomplete viser nu priser:
|
|
```javascript
|
|
getOptionLabel={(option) => {
|
|
const priceInfo = option.price
|
|
? ` - ${parseFloat(option.price).toFixed(2)} kr/${option.unit}`
|
|
: '';
|
|
return `${option.name}${priceInfo}`;
|
|
}}
|
|
```
|
|
|
|
**Resultat:**
|
|
- "B7 tagplader - 125.00 kr/m2"
|
|
- "Eternit tagsten B6 rød - 145.00 kr/m2"
|
|
|
|
## Materialer med Priser
|
|
|
|
Efter fix har følgende materialer priser:
|
|
|
|
| ID | Materiale | Pris | Enhed |
|
|
|----|-----------|------|-------|
|
|
| 16 | B7 tagplader | 125.00 | m2 |
|
|
| 35 | Test materiale | 150.00 | stk |
|
|
| 36 | Test materiale efter fix | 200.00 | m2 |
|
|
| 37 | Eternit tagsten B6 rød | 145.00 | m2 |
|
|
| 38 | Tagpap underlag T400 | 35.50 | m2 |
|
|
| 39 | Tagplade stål 35/1035 sort | 185.00 | m2 |
|
|
| 40 | Tagrender halvrund zink 150mm | 245.00 | m |
|
|
| 41 | Nedløbsrør zink 100mm | 195.00 | m |
|
|
| 42 | Stålskotskinne galvaniseret | 45.00 | m |
|
|
| 43 | Tagisolering mineraluld 200mm | 85.00 | m2 |
|
|
| 44 | Eternit vindskeder B6 | 75.00 | stk |
|
|
| 45 | Eternit rygningsten B6 | 195.00 | m |
|
|
| 46 | Lægter 38x63mm trykimprægneret | 18.50 | m |
|
|
|
|
## Deployment
|
|
|
|
```bash
|
|
# Genstart backend
|
|
pm2 restart tilbudgivern-unified
|
|
|
|
# Frontend kræver ikke rebuild - ændringen er kun i backend
|
|
```
|
|
|
|
### Server Status
|
|
- Backend: Online (port 4031) - 11 restarts
|
|
- Frontend: Online (port 3001) - 5 restarts
|
|
|
|
## Known Limitations
|
|
|
|
1. **Materialer uden priser:** Vises stadig i listen men med `price: null`
|
|
2. **Historiske priser:** Kun den nyeste pris vises (baseret på `valid_from`)
|
|
3. **Multiple leverandører:** Hvis samme materiale har flere priser, vises kun den nyeste
|
|
|
|
## Fremtidige Forbedringer
|
|
|
|
### 1. Pris Import System
|
|
Automatisk import af priser fra leverandører:
|
|
- Bygma prisliste
|
|
- CSV upload
|
|
- API integration
|
|
|
|
### 2. Pris Historik
|
|
Vis prisudvikling over tid:
|
|
```sql
|
|
SELECT
|
|
material_id,
|
|
price,
|
|
valid_from,
|
|
supplier_name
|
|
FROM material_prices
|
|
WHERE material_id = ?
|
|
ORDER BY valid_from DESC
|
|
```
|
|
|
|
### 3. Leverandør Valg
|
|
Lad brugeren vælge mellem forskellige leverandører:
|
|
```javascript
|
|
const [selectedSupplier, setSelectedSupplier] = useState('Bygma');
|
|
// Filtrer priser baseret på supplier_name
|
|
```
|
|
|
|
### 4. Bulk Pris Opdatering
|
|
Interface til at opdatere mange priser på én gang:
|
|
- Upload CSV fil med nye priser
|
|
- Automatisk validering
|
|
- Preview før import
|
|
|
|
## Konklusion
|
|
|
|
✅ Materialer trækker nu automatisk priser fra databasen
|
|
✅ MaterialDialog viser korrekt standardpris
|
|
✅ Autocomplete viser priser i dropdown
|
|
✅ Brugerdefineret pris kan stadig anvendes til at overskrive
|
|
✅ Ingen frontend ændringer nødvendige
|
|
|
|
Systemet er nu fuldt funktionelt med prisintegration!
|