8.1 KiB
Ordrestyring GraphQL Migration - Quick Start Guide
Status: 🚀 Ready to Start
Date: October 23, 2025
🎯 Top Priority: FinalReview Create Offer
Problem
FinalReview.js forsøger at sende tilbud til et endpoint der ikke eksisterer:
// Line 84 - VIRKER IKKE!
const response = await fetch(`${apiBaseUrl}/api/ordrestyring/submit-quote`, {
method: 'POST',
body: JSON.stringify(orderData)
});
Solution: Brug GraphQL createOffer Mutation
📋 Working Example fra apitest
File: apitest/examples/curl_create_offer.sh
GraphQL Mutation:
mutation CreateOffer($input: CreateOfferInput!) {
createOffer(input: $input) {
id
number
createdAt
totals {
salesPrice
salesPriceWithVat
}
}
}
Input Format:
{
"customerId": 5358,
"description": "Test offer from API script"
}
Authentication:
Authorization: Bearer <ORDRESTYRING_API_TOKEN>
Endpoint:
https://graphql.ordrestyring.dk/graphql
✅ Step-by-Step Implementation
Step 1: Install GraphQL Client (5 min)
cd /mnt/HC_Volume_103713257/tilbudgivern
npm install graphql-request graphql
Step 2: Create GraphQL Client (10 min)
File: backend/src/services/graphqlClient.js
const { GraphQLClient } = require('graphql-request');
const client = new GraphQLClient('https://graphql.ordrestyring.dk/graphql', {
headers: {
authorization: `Bearer ${process.env.ORDRESTYRING_API_TOKEN || '<ORDRESTYRING_API_TOKEN>'}`,
},
});
module.exports = client;
Step 3: Create Offer Mutation Definition (15 min)
File: backend/src/graphql/mutations/createOffer.js
const CREATE_OFFER_MUTATION = `
mutation CreateOffer($input: CreateOfferInput!) {
createOffer(input: $input) {
id
number
createdAt
customer {
id
name
}
offerLines {
id
description
quantity
unitPrice
total
}
totals {
salesPrice
salesPriceWithVat
vat
}
status {
id
name
}
}
}
`;
module.exports = { CREATE_OFFER_MUTATION };
Step 4: Create Backend Route (30 min)
File: backend/routes/offers.js (NEW FILE)
const express = require('express');
const router = express.Router();
const graphqlClient = require('../src/services/graphqlClient');
const { CREATE_OFFER_MUTATION } = require('../src/graphql/mutations/createOffer');
/**
* POST /api/ordrestyring/offers/create
* Create new offer in Ordrestyring using GraphQL
*/
router.post('/create', async (req, res) => {
try {
const { customer, project, materials, labor, totals, quote } = req.body;
// Map frontend data to GraphQL input
const offerInput = {
customerId: customer.id || 5358, // Default for testing
description: `${project.type} projekt - ${project.address || 'Ingen adresse'}`,
// Add offer lines from materials
offerLines: [
...(materials || []).map(material => ({
description: material.name,
quantity: parseFloat(material.quantity),
unitPrice: parseFloat(material.unitPrice),
unit: material.unit || 'stk',
discount: parseFloat(material.discount || 0)
})),
// Add labor as offer lines
...(labor?.tasks || []).map(task => ({
description: `Arbejdstimer: ${task.name}`,
quantity: parseFloat(task.hours),
unitPrice: parseFloat(task.hourlyRate),
unit: 'timer'
}))
],
// Additional fields
notes: project.notes || '',
validUntil: quote?.validUntil || new Date(Date.now() + 30*24*60*60*1000).toISOString()
};
console.log('Creating offer with input:', JSON.stringify(offerInput, null, 2));
// Execute GraphQL mutation
const result = await graphqlClient.request(CREATE_OFFER_MUTATION, {
input: offerInput
});
console.log('Offer created:', result.createOffer);
// Return success
res.json({
success: true,
offerNumber: result.createOffer.number,
offerId: result.createOffer.id,
message: `Tilbud ${result.createOffer.number} oprettet succesfuldt`,
data: result.createOffer
});
} catch (error) {
console.error('Error creating offer:', error);
res.status(500).json({
success: false,
error: error.message || 'Fejl ved oprettelse af tilbud',
details: error.response?.errors || []
});
}
});
module.exports = router;
Step 5: Register Route in Main Server (5 min)
File: unified-server.js (eller hvor routes registreres)
// Add near other route registrations
const offersRouter = require('./backend/routes/offers');
app.use('/api/ordrestyring/offers', offersRouter);
Step 6: Update Frontend (10 min)
File: frontend/src/components/FinalReview.js
Change line 84-92:
// OLD - DOESN'T WORK
const response = await fetch(`${apiBaseUrl}/api/ordrestyring/submit-quote`, {
// NEW - WORKS!
const response = await fetch(`${apiBaseUrl}/api/ordrestyring/offers/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData)
});
const result = await response.json();
if (result.success) {
setSubmitResult({
success: true,
offerNumber: result.offerNumber, // ✅ Now returns real offer number!
offerId: result.offerId,
message: result.message,
viewUrl: `https://app.ordrestyring.dk/offers/${result.offerId}` // Optional link
});
}
Step 7: Test (20 min)
# 1. Restart server
pm2 restart tilbudgivern-backend
# 2. Test in browser
# - Gå til FinalReview siden
# - Udfyld tilbud data
# - Klik "Send til ordrestyring"
# - Verificer success message med tilbud nummer
# 3. Verificer i Ordrestyring
# - Login på https://app.ordrestyring.dk
# - Tjek om tilbud blev oprettet
# - Verificer data er korrekt
🔍 Debugging
Common Issues
Issue 1: Authentication Error
GraphQL errors: [{"type":"Rebing\\GraphQL\\Error\\AuthorizationError"}]
Solution: Verificer API token er korrekt i .env
Issue 2: Invalid Input
GraphQL errors: [{"message":"Variable \"$input\" got invalid value..."}]
Solution: Tjek at alle required fields er udfyldt
Issue 3: Customer Not Found
GraphQL errors: [{"message":"Customer with id 5358 not found"}]
Solution: Brug et gyldigt customer ID fra Ordrestyring
Test GraphQL Directly
cd /mnt/HC_Volume_103713257/tilbudgivern/apitest/examples
./curl_create_offer.sh
Expected response:
{
"data": {
"createOffer": {
"id": 12345,
"number": "TIL-2025-001",
"createdAt": "2025-10-23T10:30:00Z",
"totals": {
"salesPrice": 50000.00,
"salesPriceWithVat": 62500.00
}
}
}
}
📊 Total Time Estimate
- Step 1-3: 30 min (Setup)
- Step 4-5: 35 min (Backend)
- Step 6: 10 min (Frontend)
- Step 7: 20 min (Testing)
Total: ~1.5 hours for working createOffer implementation
🎯 Success Criteria
✅ FinalReview sender tilbud til /api/ordrestyring/offers/create
✅ Backend kalder GraphQL createOffer mutation
✅ Tilbud oprettes i Ordrestyring system
✅ Bruger ser tilbud nummer i success message
✅ Data mappes korrekt (materialer + arbejdstimer)
📚 Resources
- GraphQL Endpoint:
https://graphql.ordrestyring.dk/graphql - API Key:
<ORDRESTYRING_API_TOKEN> - Example Script:
apitest/examples/curl_create_offer.sh - Full Migration Plan:
TODO_ANALYZE_AND_MIGRATE_ORDRESTYRING_TO_GRAPHQL.md - API Inventory:
ORDRESTYRING_API_INVENTORY.md
🚀 Next Steps After CreateOffer
- Migrate calendar endpoints (unified-server.js lines 9511-9519)
- Migrate work breakdown (LaborInput.js)
- Migrate case endpoints (ordrestyring.js)
- Refactor services to GraphQL only
- Remove all REST v2 code
See full plan in TODO_ANALYZE_AND_MIGRATE_ORDRESTYRING_TO_GRAPHQL.md
Start Here: Step 1 - Install GraphQL Client
Questions?: Check ORDRESTYRING_API_INVENTORY.md