diff --git a/backend/src/services/orderSuggestionService.js b/backend/src/services/orderSuggestionService.js index d388743..fab2046 100644 --- a/backend/src/services/orderSuggestionService.js +++ b/backend/src/services/orderSuggestionService.js @@ -578,13 +578,21 @@ class OrderSuggestionService { c.remarks, c.creation_date, c.status, - COALESCE(SUM(h.duration_hours), 0) AS total_hours, + COALESCE(SUM( + CASE + WHEN h.start_time IS NOT NULL + AND h.stop_time IS NOT NULL + AND h.stop_time > h.start_time + THEN (h.stop_time - h.start_time) / 3600 + ELSE 0 + END + ), 0) AS total_hours, COUNT(h.id) AS hour_entries FROM ordrestyring_local.cases c LEFT JOIN ordrestyring_local.debtors d ON d.customer_number = c.customer_number LEFT JOIN ordrestyring_local.hours h - ON h.case_number = c.case_number + ON h.new_case_number = c.case_number WHERE ${whereClauses.join(' OR ')} GROUP BY c.case_number, diff --git a/scripts/run-ordrestyring-simulations.js b/scripts/run-ordrestyring-simulations.js new file mode 100644 index 0000000..564305f --- /dev/null +++ b/scripts/run-ordrestyring-simulations.js @@ -0,0 +1,173 @@ +const path = require('path'); +const mysql = require('mysql2/promise'); +const databaseService = require('../backend/src/services/databaseService'); +const OrderSuggestionService = require('../backend/src/services/orderSuggestionService'); + +require('dotenv').config({ + path: path.join(__dirname, '..', 'backend/.env') +}); + +const MAX_CASES = 9; + +function ensureEnvVar(key) { + if (!process.env[key]) { + throw new Error(`Environment variable ${key} is required to run simulations`); + } +} + +async function loadRichCases(connection, limit = MAX_CASES * 2) { + const [rows] = await connection.execute( + ` + SELECT + c.case_number, + c.customer_number, + c.description, + c.remarks, + c.status, + COALESCE(c.updated_at, c.creation_date, c.created_at) AS latest_activity + FROM ordrestyring_local.cases c + WHERE c.case_number IS NOT NULL + AND c.case_number <> '' + AND EXISTS ( + SELECT 1 + FROM ordrestyring_local.hours h + WHERE h.new_case_number = c.case_number + AND h.new_case_number <> '' + ) + AND EXISTS ( + SELECT 1 + FROM ordrestyring_local.case_materials m + WHERE m.case_number = c.case_number + AND m.case_number <> '' + ) + ORDER BY GREATEST( + CHAR_LENGTH(COALESCE(c.description, '')), + CHAR_LENGTH(COALESCE(c.remarks, '')) + ) DESC, + c.updated_at DESC + LIMIT ? + `, + [limit] + ); + + return rows; +} + +async function createSimulationProject(databaseService, caseRow) { + const projectName = `Simulering: Ordrestyring sag ${caseRow.case_number}`; + const description = `${caseRow.description || ''} ${caseRow.remarks || ''}`.trim() || 'Simuleret beskrivelse'; + const customerName = caseRow.customer_name || caseRow.customer_number || 'Ordrestyring kunde'; + const [result] = await databaseService.pool.execute( + ` + INSERT INTO customer_projects ( + project_name, + customer_name, + customer_number, + project_description + ) VALUES (?, ?, ?, ?) + `, + [ + projectName, + customerName, + caseRow.customer_number || null, + description + ] + ); + + const projectId = result.insertId; + await databaseService.pool.execute( + ` + INSERT INTO roof_geometry ( + project_id, + roof_type, + total_area, + roof_pitch + ) VALUES (?, ?, ?, ?) + `, + [projectId, 'skraat_tag', 120, 30] + ); + + return projectId; +} + +async function cleanupSimulationProject(databaseService, projectId) { + await databaseService.pool.execute('DELETE FROM roof_geometry WHERE project_id = ?', [projectId]); + await databaseService.pool.execute('DELETE FROM customer_projects WHERE id = ?', [projectId]); +} + +function summarizeSuggestion(suggestion) { + const materialsCount = suggestion.materialsDraft.length; + const laborHours = suggestion.laborDraft + .reduce((sum, task) => sum + (Number(task.totalHours) || 0), 0); + + return { + recommendation: suggestion.recommendation?.recommendedPackage?.packageId || suggestion.recommendation?.recommendedPackage?.name || 'Ingen pakke', + description: suggestion.recommendation?.descriptionDraft || suggestion.recommendation?.suggestedDescription || 'Ingen beskrivelse', + materialsCount, + laborHours: Number(laborHours.toFixed(2)), + warnings: suggestion.recommendation?.warnings || [], + clarifications: suggestion.recommendation?.clarifications || [] + }; +} + +async function runSimulation() { + ensureEnvVar('DB_PASSWORD'); + const ordrestyringConn = await mysql.createConnection({ + host: process.env.DB_HOST || '127.0.0.1', + port: parseInt(process.env.DB_PORT, 10) || 3306, + user: process.env.DB_USER || 'tilbudgivern_service', + password: process.env.DB_PASSWORD, + database: 'ordrestyring_local' + }); + + await databaseService.initialize(); + const suggestionService = new OrderSuggestionService(databaseService); + + const richCases = await loadRichCases(ordrestyringConn); + const uniqueCases = []; + const seenCases = new Set(); + for (const item of richCases) { + if (!item.case_number || seenCases.has(item.case_number)) { + continue; + } + seenCases.add(item.case_number); + uniqueCases.push(item); + } + const selectedCases = uniqueCases.slice(0, MAX_CASES); + console.log(`Fetched ${richCases.length} rows, simulating ${selectedCases.length} unique cases`); + const results = []; + + for (const caseRow of selectedCases) { + console.log(`\n=== Simulerer case ${caseRow.case_number} (${caseRow.status || 'ukendt status'}) ===`); + const projectId = await createSimulationProject(databaseService, caseRow); + try { + const suggestion = await suggestionService.getOrderSuggestions(projectId, { limit: 5 }); + const summary = summarizeSuggestion(suggestion); + console.log(JSON.stringify({ + caseNumber: caseRow.case_number, + summary, + matches: suggestion.matches.map(match => ({ + source: match.sourceType, + description: match.description || match.projectDescription || '', + totalHours: match.totalHours, + relevance: match.relevanceScore + })) + }, null, 2)); + results.push({ caseNumber: caseRow.case_number, summary }); + } catch (error) { + console.error(`Fejl ved simulering af ${caseRow.case_number}:`, error.message); + } finally { + await cleanupSimulationProject(databaseService, projectId); + } + } + + console.log('\n--- Simulationsoverblik ---'); + console.log(JSON.stringify(results, null, 2)); + await ordrestyringConn.end(); + await databaseService.pool.end(); +} + +runSimulation().catch(error => { + console.error('Simuleringsscript fejlede:', error.message); + process.exit(1); +});