Files
tilbudgivern/test_pdf_generation.py
T
alexpolo1andAI Assistant 80632c996b feat: Complete Ordrestyring GraphQL integration with automatic offer creation
FEATURES IMPLEMENTED:
✅ Full GraphQL integration with Ordrestyring API
✅ Automatic offer creation from Tilbudsgiveren quotes
✅ Customer management (find existing or create new)
✅ Task/Opgave management with full quote description
✅ Material lines (tilbudslinjer) with pricing
✅ Internal notes in bemærkninger field
✅ Department assignment (Tømrer = ID 2)
✅ Status management (Tilbudsgiver Oprettet Tilbud)

KEY COMPONENTS:

1. OrdrestyringQuoteService (backend/src/services/ordrestyringQuoteService.js)
   - generateDetailedQuoteDescription(): Creates formatted quote text
   - findOrCreateCustomer(): Handles customer, contact, delivery address
   - createOfferLines(): Updates default task & adds material lines
   - sendQuoteToOrdrestyring(): Main orchestration method
   - getOfferWithTasks(): Query offers with task details

2. Smart Task Management:
   - Finds and updates existing default 'Opgave 1' task
   - Renames to 'Tilbudsgivertekst' for clarity
   - Adds full 2000+ character quote description
   - Creates material lines under the task
   - Fallback: Creates new task if none exists

3. GraphQL Mutations & Queries:
   - createOffer: Creates offer with customer, status, department
   - updateOfferTask: Updates existing task with description
   - createOfferTask: Fallback for new task creation
   - createOfferLine: Adds material lines with pricing
   - Proper error handling and logging throughout

4. Database Integration:
   - Migration: add_ordrestyring_columns.sql
   - Tracks ordrestyring_offer_id, ordrestyring_offer_number
   - Status synchronization with ordrestyring_status
   - Timestamp tracking with ordrestyring_sent_at

5. Route Updates (backend/routes/ordrestyring.js):
   - POST /api/ordrestyring/send-quote-graphql
   - Optional customerId parameter (uses existing or creates new)
   - Validation for projectId and calculationId
   - Returns complete offer details

TECHNICAL HIGHLIGHTS:

• GraphQL Client: graphql-request v7.3.1
• API Endpoint: https://graphql.ordrestyring.dk/graphql
• Authentication: Bearer token in headers
• Error Handling: Graceful fallbacks, detailed logging
• Field Discovery: Used introspection to find correct types
  - UpdateOfferTaskInput (not OfferTaskInput)
  - Task fields: header & text (not name & description)
  - productNumber required for offer lines

TESTING:

• Test scripts: test_roesevangen_ordrestyring.py
• Verification: test_check_offer.js
• Live testing with Project 22 (Røsevangen 44)
• Confirmed: Single task with full description
• Confirmed: 10 material lines with correct pricing
• Confirmed: Department, status, and internal notes working

RESULT:
Complete end-to-end integration from Tilbudsgiveren → Ordrestyring
- Creates professional offers with all data
- Single 'Tilbudsgivertekst' task (not duplicate Opgave 1)
- Full quote description visible in task
- Material lines with quantities and prices
- Ready for production use

Co-authored-by: AI Assistant <[email protected]>
2025-10-31 21:19:00 +00:00

133 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
Test script to verify PDF generation works correctly
"""
import sys
import os
sys.path.append('/mnt/HC_Volume_103713257/tilbudgivern/backend')
from pdf_generator import TilbudgivernPDFGenerator
from datetime import datetime
import json
# Create test data that matches the expected structure in pdf_generator.py
test_data = {
'quote_number': f'TG-{datetime.now().strftime("%Y%m%d")}-TEST',
'project': {
'customer_name': 'Test Kunde A/S',
'customer_address': 'Testvej 123, 4000 Roskilde',
'project_description': 'udskiftning af tag på enfamiliehus'
},
'geometry': {
'total_area': 105.0,
'roof_type': 'Komplet tagudskiftning med B7 eternit',
'roof_pitch': 35
},
'labor': {
'total_work_hours': 85.5,
'carpenter_count': 2,
'hourly_rate': 580.0
},
'materials': [
{
'material_category': 'Eternit B7 tagplader 920x1320mm',
'quantity': 223,
'total_price': 54635.00
},
{
'material_category': 'Lindab Rainline tagrende 125mm',
'quantity': 14,
'total_price': 3430.00
},
{
'material_category': 'Trykimprægnerede lægter 30x50mm',
'quantity': 228,
'total_price': 4218.00
},
{
'material_category': 'Eternit rygning inkl. elementer',
'quantity': 12,
'total_price': 2040.00
},
{
'material_category': 'Lindab nedløb komplet system',
'quantity': 4,
'total_price': 2180.00
}
],
'calculation': {
'total_labor_cost': 49590.00, # 85.5 * 580
'total_material_cost': 66503.00,
'overhead_percentage': 15.0,
'profit_percentage': 20.0,
'vat_percentage': 25.0
}
}
# Output paths
output_pdf = '/mnt/HC_Volume_103713257/tilbudgivern/Test_Tilbud.pdf'
output_json = '/mnt/HC_Volume_103713257/tilbudgivern/test_data.json'
# Save test data to JSON
print("💾 Gemmer test data til JSON...")
with open(output_json, 'w', encoding='utf-8') as f:
json.dump(test_data, f, indent=2, ensure_ascii=False)
print(f"✅ JSON gemt: {output_json}")
# Generate PDF
print("\n🔨 GENERERER TEST PDF...")
print(f"📋 Kunde: {test_data['project']['customer_name']}")
print(f"📍 Adresse: {test_data['project']['customer_address']}")
print(f"📐 Tagareal: {test_data['geometry']['total_area']} m²")
print(f"⏱️ Arbejdstimer: {test_data['labor']['total_work_hours']} timer")
try:
generator = TilbudgivernPDFGenerator()
success = generator.generate_pdf(test_data, output_pdf)
if success:
# Check if file exists and has content
if os.path.exists(output_pdf):
file_size = os.path.getsize(output_pdf)
print(f"\n✅ PDF GENERERET SUCCESFULDT!")
print(f"📄 Fil: {output_pdf}")
print(f"📊 Størrelse: {file_size:,} bytes")
if file_size > 1000:
print(f"✓ PDF indeholder data (ikke tom)")
# Calculate expected values
labor = test_data['calculation']['total_labor_cost']
materials = test_data['calculation']['total_material_cost']
subtotal = labor + materials
overhead = subtotal * 0.15
profit = subtotal * 0.20
before_vat = subtotal + overhead + profit
vat = before_vat * 0.25
total_with_vat = before_vat + vat
print(f"\n💰 PRISBEREGNING:")
print(f" Arbejdsløn: {labor:>12,.2f} kr")
print(f" Materialer: {materials:>12,.2f} kr")
print(f" ─────────────────────────────")
print(f" Subtotal: {subtotal:>12,.2f} kr")
print(f" Overhead (15%): {overhead:>12,.2f} kr")
print(f" Profit (20%): {profit:>12,.2f} kr")
print(f" Moms (25%): {vat:>12,.2f} kr")
print(f" ═════════════════════════════")
print(f" TOTAL: {total_with_vat:>12,.2f} kr")
print(f"\n✓ PDF kan nu åbnes og verificeres")
else:
print(f"⚠️ ADVARSEL: PDF er meget lille ({file_size} bytes) - muligvis tom!")
else:
print(f"❌ FEJL: PDF fil blev ikke oprettet!")
else:
print(f"❌ FEJL: PDF generering returnerede False")
except Exception as e:
print(f"❌ FEJL ved generering: {e}")
import traceback
traceback.print_exc()