- 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.
12 KiB
Ordrestyring GraphQL Integration
Oversigt
Denne integration sender komplette tilbud til Ordrestyring via GraphQL API med detaljeret beskrivelse i professionelt format.
Features
✅ Detaljeret tilbudsbeskrivelse med alle sektioner:
- ARBEJDE DER UDFØRES (geometri, arbejdstimer, opgavebeskrivelse)
- MATERIALER (komplet liste med priser)
- PRISSPECIFIKATION (arbejde, materialer, moms, totaler)
- VORES LØFTE TIL DIG (firma kvalitetsløfter)
- GARANTIER OG SERVICE (garantier og support)
- Standard tekster og disclaimers
✅ Interne beregninger i bemærkninger (ikke synlige for kunde):
- Overhead og fortjeneste gemmes i
notesfeltet - Kun synligt internt i Ordrestyring systemet
- Fuld transparens internt uden at afsløre fortjeneste til kunde
✅ Automatisk status: Sætter "Tilbudsgiver Oprettet Tilbud" når tilbud oprettes
✅ Dansk prisformatering med tusindtalsseparator (195.906,94 kr)
✅ GraphQL API integration via https://graphql.ordrestyring.dk/graphql
✅ Automatisk synkronisering mellem lokal database og Ordrestyring
✅ Status tracking af tilbud i begge systemer
API Endpoints
1. Preview Tilbudsbeskrivelse
Endpoint: POST /api/ordrestyring/preview-description
Preview tilbudsbeskrivelse uden at sende til Ordrestyring.
Request:
{
"projectId": 1,
"calculationId": 1
}
Response:
{
"success": true,
"description": "Tilbud på udskiftning af tag...\n\nARBEJDE DER UDFØRES:\n...",
"internalNotes": "INTERNE BEREGNINGER (kun synlig internt):\n\nSubtotal: 116.093,00 kr\nOverhead (15%): 17.413,95 kr\nFortjeneste (20%): 23.218,60 kr\n...",
"priceExVat": 156725.55,
"priceInclVat": 195906.94,
"materials": [...]
}
2. Send Tilbud til Ordrestyring
Endpoint: POST /api/ordrestyring/send-quote-graphql
Send tilbud til Ordrestyring med komplet beskrivelse via GraphQL API.
Request:
{
"projectId": 1,
"calculationId": 1,
"customerId": 5358
}
Response:
{
"success": true,
"message": "Tilbud sendt til Ordrestyring via GraphQL",
"offer": {
"id": 12345,
"number": "TIL-2024-00123",
"createdAt": "2024-01-15T10:30:00Z",
"customer": {
"id": 5358,
"name": "Anders Jensen"
},
"totals": {
"salesPrice": 156725.55,
"salesPriceWithVat": 195906.94
},
"status": {
"id": 999926,
"text": "Tilbudsgiver Oprettet Tilbud"
}
},
"description": "..."
}
Funktionalitet:
- Sender komplet tilbudsbeskrivelse til kunde (uden overhead/fortjeneste)
- Gemmer interne beregninger i
notesfeltet (kun synligt internt i Ordrestyring) - Sætter automatisk status til "Tilbudsgiver Oprettet Tilbud"
- Opdaterer lokal database med Ordrestyring reference
3. Hent Tilbud fra Ordrestyring
Endpoint: GET /api/ordrestyring/offer/:offerId
Hent detaljer om tilbud fra Ordrestyring.
Response:
{
"success": true,
"offer": {
"id": 12345,
"number": "TIL-2024-00123",
"description": "...",
"customer": {
"id": 5358,
"name": "Anders Jensen",
"email": "[email protected]"
},
"lines": [
{
"id": 1,
"description": "Eternit B7 tagplader",
"quantity": 223,
"unit": "stk",
"unitPrice": 245.00,
"total": 54635.00
}
],
"totals": {...},
"status": {...}
}
}
4. Opdater Tilbud Status
Endpoint: POST /api/ordrestyring/offer/:offerId/status
Opdater status på tilbud i Ordrestyring.
Request:
{
"statusId": 2
}
Status IDs:
- 1: Draft
- 2: Sent
- 3: Accepted
- 4: Rejected
Response:
{
"success": true,
"message": "Tilbud status opdateret",
"offer": {
"id": 12345,
"number": "TIL-2024-00123",
"status": {
"id": 2,
"text": "Sent"
}
}
}
Database Schema
Nye kolonner i generated_quotes tabel:
ALTER TABLE generated_quotes
ADD COLUMN ordrestyring_offer_id INT NULL COMMENT 'Offer ID fra Ordrestyring GraphQL API',
ADD COLUMN ordrestyring_offer_number VARCHAR(50) NULL COMMENT 'Offer nummer fra Ordrestyring',
ADD COLUMN ordrestyring_sent_at DATETIME NULL COMMENT 'Tidspunkt for afsendelse til Ordrestyring',
ADD COLUMN ordrestyring_status VARCHAR(50) NULL COMMENT 'Status fra Ordrestyring';
Service Arkitektur
OrdrestyringQuoteService
Location: backend/src/services/ordrestyringQuoteService.js
Metoder:
-
generateDetailedQuoteDescription(projectId, calculationId)
- Henter projekt, geometri, arbejdstimer, materialer, beregninger
- Formaterer komplet beskrivelse med alle sektioner (ARBEJDE, MATERIALER, PRISER, LØFTER, GARANTIER)
- Genererer interne noter med overhead og fortjeneste (gemmes i
notes) - Beregner priser med overhead og fortjeneste inkluderet
- Returnerer beskrivelse, internalNotes og pris data
-
getOfferStatusId(statusText)
- Henter alle tilgængelige statusser fra Ordrestyring
- Finder status ID baseret på tekst (case-insensitive)
- Fallback til "Nyt tilbud" hvis status ikke findes
- Bruges til at sætte "Tilbudsgiver Oprettet Tilbud" status
-
sendQuoteToOrdrestyring(projectId, calculationId, customerId)
- Genererer beskrivelse via generateDetailedQuoteDescription()
- Henter status ID for "Tilbudsgiver Oprettet Tilbud"
- Sender GraphQL createOffer mutation med:
description: Komplet tilbudsbeskrivelse (til kunde)notes: Interne beregninger med overhead/fortjeneste (kun internt)statusId: "Tilbudsgiver Oprettet Tilbud"lines: Materialer som offer linjer
- Gemmer Ordrestyring reference i lokal database
- Returnerer offer data fra Ordrestyring
-
getOfferFromOrdrestyring(offerId)
- Henter tilbud via GraphQL query
- Returnerer komplet offer med linjer og status
-
updateOfferStatus(offerId, statusId)
- Opdaterer status via GraphQL mutation
- Logger status ændring
Interne Beregninger
Overhead og fortjeneste beregnes men vises KUN i interne noter:
Kunde Beskrivelse (description)
PRISSPECIFIKATION:
Arbejdsløn: 49.590,00 kr
Materialer: 66.503,00 kr
─────────────────────────────
Subtotal: 116.093,00 kr
Moms (25%): 39.181,39 kr
─────────────────────────────
SAMLET PRIS EKS. MOMS: 156.725,55 kr
INKL. MOMS (25%): 195.906,94 kr
Interne Noter (notes - kun synligt internt)
INTERNE BEREGNINGER (kun synlig internt):
Subtotal (arbejde + materialer): 116.093,00 kr
Overhead (15%): 17.413,95 kr
Fortjeneste (20%): 23.218,60 kr
─────────────────────────────
Pris ex. moms: 156.725,55 kr
Moms (25%): 39.181,39 kr
Pris inkl. moms: 195.906,94 kr
Projektinfo:
- Projekt ID: 1
- Beregning ID: 1
- Oprettet: 2024-01-15T10:30:00Z
- Timepris: 580,00 kr/time
- Timer total: 85.5 timer
Dette sikrer:
- ✅ Kunden ser kun arbejde, materialer og totaler
- ✅ Interne medarbejdere kan se fuld kalkulation i Ordrestyring
- ✅ Transparens internt uden at afsløre fortjeneste eksternt
Tilbudsstatus
Når tilbud sendes til Ordrestyring sættes automatisk status:
"Tilbudsgiver Oprettet Tilbud"
Servicen finder automatisk det korrekte status ID ved at:
- Hente alle tilgængelige statusser fra Ordrestyring API
- Søge efter "Tilbudsgiver Oprettet Tilbud" (case-insensitive)
- Bruge det fundne status ID i createOffer mutation
- Fallback til "Nyt tilbud" hvis status ikke findes
Status kan senere opdateres via updateOfferStatus() metoden.
Beskrivelsesformat
Genereret beskrivelse følger dette format:
Tilbud på [projekt beskrivelse].
ARBEJDE DER UDFØRES:
[Tag type / arbejdstype]
Opmåling og forberedelse
• Samlet areal: X m²
• Taghældning: X°
• Taghøjde: X m
Arbejdsudførelse
• Arbejdstimer: X timer med X tømrere
• Timepris: X kr/time
• Alt affald bortkøres
• Området ryddes og efterlades rent
MATERIALER:
• Material 1: X stk - XX.XXX,XX kr
• Material 2: X stk - XX.XXX,XX kr
...
Vi indhenter tilbud fra flere leverandører...
PRISSPECIFIKATION:
Arbejdsløn: XX.XXX,XX kr
Materialer: XX.XXX,XX kr
─────────────────────────────
Subtotal: XX.XXX,XX kr
Moms (25%): XX.XXX,XX kr
─────────────────────────────
SAMLET PRIS EKS. MOMS: XXX.XXX,XX kr
INKL. MOMS (25%): XXX.XXX,XX kr
*Hvis der bruges kortere tid...
VORES LØFTE TIL DIG:
• Præcision og ordentlighed i alt hvad vi laver
• Tradition og transformation går hånd i hånd
• Meningsfuldt, ordentligt og bæredygtigt håndværk
• Vi kommer til tiden og står inde for vores arbejde
Vi leverer altid det aftalte...
GARANTIER OG SERVICE:
• Gratis tagtjek tilbydes
• Professionel rådgivning i valg af løsninger
• Korrekt dokumentation og tryghed
• Kvalitet der holder i mange år
TILBUDDET ER GYLDIGT I 30 DAGE
Der tages forbehold for rød og svamp...
Med venlig hilsen
Tømrer- og Snedkermester Mikael Holck ApS
...
Test Script
Location: test_ordrestyring_integration.py
Kør test:
python3 test_ordrestyring_integration.py
Test scriptet:
- Genererer preview af beskrivelse
- Spørger om tilladelse til at sende
- Sender tilbud til Ordrestyring
- Henter tilbuddet tilbage for verifikation
Frontend Integration
FinalReview Component
Tilføj "Send til Ordrestyring" knap:
const sendToOrdrestyring = async () => {
try {
setLoading(true);
const response = await fetch('/api/ordrestyring/send-quote-graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
projectId: project.id,
calculationId: calculation.id,
customerId: project.ordrestyring_customer_id // Fra kunde record
})
});
const data = await response.json();
if (data.success) {
setOfferNumber(data.offer.number);
showSuccess(`Tilbud sendt til Ordrestyring: ${data.offer.number}`);
} else {
showError(data.error);
}
} catch (error) {
showError('Fejl ved afsendelse til Ordrestyring');
} finally {
setLoading(false);
}
};
return (
<div>
{/* Eksisterende UI */}
<button
onClick={sendToOrdrestyring}
disabled={loading || !project.ordrestyring_customer_id}
className="btn-primary"
>
📤 Send til Ordrestyring
</button>
{offerNumber && (
<div className="success-message">
✅ Sendt til Ordrestyring: {offerNumber}
</div>
)}
</div>
);
Configuration
Environment Variables
# .env
ORDRESTYRING_GRAPHQL_URL=https://graphql.ordrestyring.dk/graphql
ORDRESTYRING_API_KEY=cRD7xfeoiGh1OhzV
Dependencies
Tilføjet i backend/package.json:
{
"dependencies": {
"graphql": "^16.8.1",
"graphql-request": "^6.1.0"
}
}
Workflow
- Kunde opretter projekt → Projekt gemt i customer_projects
- AI/Manual beregning → Beregning gemt i project_calculations
- Final Review → Bruger ser komplet tilbud
- Send til Ordrestyring → Click button
- generateDetailedQuoteDescription() genererer beskrivelse
- sendQuoteToOrdrestyring() sender via GraphQL
- Offer ID og nummer gemt i database
- Status tracking → Synkroniser status mellem systemer
- Kunde godkender → updateOfferStatus(offerId, 3)
Error Handling
Service håndterer fejl gracefully:
- Database fejl: Logger og returnerer specifik fejl
- GraphQL fejl: Parser fejl fra Ordrestyring API
- Network fejl: Timeout efter 10 sekunder
- Validation fejl: Checker required fields før API kald
Alle fejl logges via Winston logger:
logger.error('Error sending quote to Ordrestyring:', error);
Security
- ✅ API key gemt i environment variable
- ✅ Authorization header med Bearer token
- ✅ Input validation på alle endpoints
- ✅ SQL injection protection via parameterized queries
- ✅ HTTPS til Ordrestyring API
Fremtidige Forbedringer
- Webhook integration: Modtag status opdateringer fra Ordrestyring
- Batch sending: Send flere tilbud på én gang
- Automatic sync: Periodisk synkronisering af status
- Customer mapping: Automatisk match mellem lokale kunder og Ordrestyring kunder
- Template system: Tilpassede beskrivelser per kunde type
Support
Ved fejl eller spørgsmål, se:
- GraphQL dokumentation:
apitest/API_INTEGRATION_GUIDE.md - Service implementation:
backend/src/services/ordrestyringQuoteService.js - Test examples:
apitest/examples/