Files
tilbudgivern/archive/docs/MIGRATION_QUICK_START.md
2026-08-13 14:29:15 +02:00

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

  1. Migrate calendar endpoints (unified-server.js lines 9511-9519)
  2. Migrate work breakdown (LaborInput.js)
  3. Migrate case endpoints (ordrestyring.js)
  4. Refactor services to GraphQL only
  5. 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