Files
tilbudgivern/backend/populate-packages.js
alexpolo1 bdb9e524db Implement manual and automated tests for auto-save countdown feature
- 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.
2025-11-14 11:28:58 +00:00

275 lines
15 KiB
JavaScript

require('dotenv').config();
const mysql = require('mysql2/promise');
const axios = require('axios');
async function addTasks(conn, packageId, packageName, category, estimatedHours) {
// Check if package already has tasks
const [existing] = await conn.execute('SELECT COUNT(*) as count FROM package_tasks WHERE package_id = ?', [packageId]);
if (existing[0].count > 0) {
console.log(` ✓ Pakke ${packageId} har allerede opgaver`);
return;
}
let tasks = [];
// Generate tasks based on package name/category
if (packageName.includes('Tagrende') || packageName.includes('tagrende')) {
tasks = [
{ name: 'Forberedelse og opmåling', desc: 'Opmåle facadelængde og planlægge montering', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af konsoljern', desc: 'Montere konsoljern med korrekt afstand (60cm)', hours: 1.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering af tagrende', desc: 'Montere tagrende med korrekt fald (1:500)', hours: 1.5, phase: 'hovedarbejde', order: 2 },
{ name: 'Montering af rende-tilbehør', desc: 'Montere hjørner, afslutninger og rendekrog', hours: 0.5, phase: 'hovedarbejde', order: 3 },
{ name: 'Slutkontrol', desc: 'Kontrollere tæthed og fald', hours: 0.5, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('Nedløb') || packageName.includes('nedløb')) {
tasks = [
{ name: 'Opmåling', desc: 'Måle højde og planlægge fastgørelse', hours: 0.3, phase: 'forberedelse', order: 1 },
{ name: 'Montering af rørbeslag', desc: 'Montere rørbeslag med korrekt afstand', hours: 0.5, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering af nedløbsrør', desc: 'Montere nedløbsrør og samle sektioner', hours: 0.7, phase: 'hovedarbejde', order: 2 },
{ name: 'Tilslutning og afslutning', desc: 'Tilslutte til tagrende og montere nedløbsbrønd', hours: 0.5, phase: 'afslutning', order: 1 }
];
} else if (packageName.includes('spær') || packageName.includes('Spær')) {
tasks = [
{ name: 'Inspektion af eksisterende spær', desc: 'Kontrollere tilstand og identificere problemer', hours: 1.0, phase: 'forberedelse', order: 1 },
{ name: 'Opmåling og tilskæring', desc: 'Opmåle og tilskære nyt tømmer', hours: 1.5, phase: 'forberedelse', order: 2 },
{ name: 'Opretning af spær', desc: 'Montere forstærkninger og rette skader', hours: 3.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Kontrol af konstruktion', desc: 'Kontrollere stabilitet og vinkelrethed', hours: 0.5, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('B7') || packageName.includes('tagplade')) {
tasks = [
{ name: 'Forberedelse', desc: 'Kontrollere underlaget og planlægge oplægning', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af lægter', desc: 'Montere bære- og kontralægter', hours: 2.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Oplægning af B7 plader', desc: 'Lægge B7 plader med korrekt overlæg', hours: 2.5, phase: 'hovedarbejde', order: 2 },
{ name: 'Fastgørelse', desc: 'Skrue plader fast med tagskruer', hours: 1.0, phase: 'hovedarbejde', order: 3 },
{ name: 'Slutkontrol', desc: 'Kontrollere tæthed og finish', hours: 0.5, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('Rygning') || packageName.includes('rygning')) {
tasks = [
{ name: 'Opmåling', desc: 'Måle rygningslængde og forberede materiale', hours: 0.3, phase: 'forberedelse', order: 1 },
{ name: 'Montering af rygningselementer', desc: 'Montere rygning med korrekt overlæg', hours: 1.5, phase: 'hovedarbejde', order: 1 },
{ name: 'Tætning og afslutning', desc: 'Tætne ender og kontrollere finish', hours: 0.5, phase: 'afslutning', order: 1 }
];
} else if (packageName.includes('Taghætte') || packageName.includes('taghætte')) {
tasks = [
{ name: 'Udskæring i tag', desc: 'Lave hul til taghætte med korrekt dimension', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af taghætte', desc: 'Montere taghætte og tætne omkring', hours: 0.8, phase: 'hovedarbejde', order: 1 },
{ name: 'Afslutning', desc: 'Kontrollere tæthed og finish', hours: 0.2, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('undertag') || packageName.includes('Undertag')) {
tasks = [
{ name: 'Forberedelse', desc: 'Kontrollere spærkonstruktion', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af lægter', desc: 'Montere lægter til undertag', hours: 1.5, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering af undertag', desc: 'Lægge undertag med korrekt overlæg', hours: 2.0, phase: 'hovedarbejde', order: 2 },
{ name: 'Tætning', desc: 'Tætne samlinger og gennemføringer', hours: 1.0, phase: 'hovedarbejde', order: 3 }
];
} else if (packageName.includes('Velux') || packageName.includes('tagvindue')) {
tasks = [
{ name: 'Opmåling og udskæring', desc: 'Opmåle og udskære hul i tag', hours: 1.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af vindue', desc: 'Montere Velux vindue i ramme', hours: 2.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering af inddækning', desc: 'Montere inddækningskit omkring vindue', hours: 1.5, phase: 'hovedarbejde', order: 2 },
{ name: 'Tætning og isolering', desc: 'Tætne og isolere omkring vindue', hours: 1.0, phase: 'afslutning', order: 1 },
{ name: 'Test og justering', desc: 'Teste åbning/lukning og justere', hours: 0.5, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('vindskede') || packageName.includes('Vindskede')) {
tasks = [
{ name: 'Opmåling', desc: 'Måle gavllængde og tilskære brædder', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af vindskeder', desc: 'Montere vindskeder med beslag', hours: 2.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Behandling', desc: 'Grundmaling eller oliering', hours: 1.0, phase: 'afslutning', order: 1 }
];
} else if (packageName.includes('nedrivning') || packageName.includes('Nedrivning')) {
tasks = [
{ name: 'Sikkerhedsforanstaltninger', desc: 'Opsætte afspærring og sikkerhedsudstyr', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Nedtagning', desc: 'Nedtage materialer forsigtig', hours: 3.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Sortering og bortkørsel', desc: 'Sortere affald og køre til genbrugsstation', hours: 1.5, phase: 'afslutning', order: 1 }
];
} else if (packageName.includes('trækbånd') || packageName.includes('Trækbånd')) {
tasks = [
{ name: 'Opmåling', desc: 'Måle og planlægge placering', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Montering af beslag', desc: 'Montere vægbeslag og tagbeslag', hours: 1.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering af trækbånd', desc: 'Montere og spænde trækbånd', hours: 1.0, phase: 'hovedarbejde', order: 2 },
{ name: 'Kontrol', desc: 'Kontrollere spænding og fastgørelse', hours: 0.5, phase: 'inspektion', order: 1 }
];
} else if (packageName.includes('skortrende') || packageName.includes('Skortrende')) {
tasks = [
{ name: 'Opmåling', desc: 'Måle omkring skorsten', hours: 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Tilskæring af zink/kobber', desc: 'Tilskære og forme skortrende', hours: 2.0, phase: 'hovedarbejde', order: 1 },
{ name: 'Montering', desc: 'Montere skortrende med beslag', hours: 2.0, phase: 'hovedarbejde', order: 2 },
{ name: 'Lodning og tætning', desc: 'Lodde samlinger og tætne', hours: 1.5, phase: 'afslutning', order: 1 }
];
} else {
// Generic tasks for other packages
const hoursPerTask = estimatedHours / 4;
tasks = [
{ name: 'Forberedelse', desc: 'Klargøre arbejdsområde og materialer', hours: hoursPerTask * 0.5, phase: 'forberedelse', order: 1 },
{ name: 'Hovedarbejde', desc: 'Udføre hovedopgaven', hours: hoursPerTask * 2.5, phase: 'hovedarbejde', order: 1 },
{ name: 'Afslutning', desc: 'Rydde op og afslutte arbejde', hours: hoursPerTask * 0.5, phase: 'afslutning', order: 1 },
{ name: 'Kvalitetskontrol', desc: 'Kontrollere resultat', hours: hoursPerTask * 0.5, phase: 'inspektion', order: 1 }
];
}
// Insert tasks
for (const task of tasks) {
await conn.execute(
`INSERT INTO package_tasks (package_id, task_name, task_description, estimated_hours, task_order, task_phase)
VALUES (?, ?, ?, ?, ?, ?)`,
[packageId, task.name, task.desc, task.hours, task.order, task.phase]
);
}
console.log(` ✓ Tilføjet ${tasks.length} opgaver til pakke ${packageId}`);
}
async function addMaterials(conn, packageId, packageName, description) {
// Check if package already has materials
const [existing] = await conn.execute('SELECT COUNT(*) as count FROM package_materials WHERE package_id = ?', [packageId]);
if (existing[0].count > 0) {
console.log(` ✓ Pakke ${packageId} har allerede materialer`);
return;
}
// Try to parse VARENUMRE from description
const varenumreRegex = /(\d{6,})/g;
const matches = description.match(varenumreRegex);
if (matches && matches.length > 0) {
// Get materials from API
const response = await axios.get('http://localhost:4031/api/materials');
const allMaterials = response.data.materials || [];
let addedCount = 0;
for (const varenr of matches) {
const material = allMaterials.find(m => m.varenummer === varenr);
if (material && material.price) {
await conn.execute(
`INSERT INTO package_materials
(package_id, material_name, material_category, quantity, unit, unit_price, total_price, supplier, base_quantity)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
packageId,
material.name,
material.category || 'Diverse',
1,
material.enhed || 'stk',
parseFloat(material.price),
parseFloat(material.price),
material.leverandoer || 'Bygma',
1
]
);
addedCount++;
}
}
if (addedCount > 0) {
console.log(` ✓ Tilføjet ${addedCount} materialer til pakke ${packageId}`);
return;
}
}
// If no VARENUMRE found or no matches, add generic materials based on package type
let materials = [];
if (packageName.includes('Tagrende') || packageName.includes('tagrende')) {
materials = [
{ name: 'Tagrende 3m', category: 'Tag', qty: 1, unit: 'stk', price: 250, supplier: 'Lindab' },
{ name: 'Konsoljern', category: 'Tag', qty: 5, unit: 'stk', price: 25, supplier: 'Lindab' },
{ name: 'Rendeafslutning', category: 'Tag', qty: 2, unit: 'stk', price: 45, supplier: 'Lindab' },
{ name: 'Rendekrog', category: 'Tag', qty: 2, unit: 'stk', price: 35, supplier: 'Lindab' }
];
} else if (packageName.includes('Nedløb') || packageName.includes('nedløb')) {
materials = [
{ name: 'Nedløbsrør 3m', category: 'Tag', qty: 1, unit: 'stk', price: 180, supplier: 'Lindab' },
{ name: 'Rørholder', category: 'Tag', qty: 3, unit: 'stk', price: 22, supplier: 'Lindab' },
{ name: 'Nedløbsbøjning', category: 'Tag', qty: 2, unit: 'stk', price: 65, supplier: 'Lindab' }
];
} else if (packageName.includes('B7') && !packageName.includes('Rygning') && !packageName.includes('Taghætte')) {
materials = [
{ name: 'B7 Bølgeplade 2,5m', category: 'Tag', qty: 10, unit: 'm²', price: 125, supplier: 'Bygma' },
{ name: 'Tagskruer', category: 'Tag', qty: 100, unit: 'stk', price: 2.5, supplier: 'Bygma' },
{ name: 'Bærelægte 38x73', category: 'Tømrer', qty: 20, unit: 'lbm', price: 25, supplier: 'Bygma' }
];
} else if (packageName.includes('Rygning')) {
materials = [
{ name: 'Rygning til B7', category: 'Tag', qty: 1, unit: 'lbm', price: 180, supplier: 'Bygma' },
{ name: 'Rygningselement', category: 'Tag', qty: 1, unit: 'stk', price: 95, supplier: 'Bygma' }
];
} else if (packageName.includes('spær') || packageName.includes('Spær')) {
materials = [
{ name: 'Spærtræ 200mm', category: 'Tømrer', qty: 10, unit: 'lbm', price: 85, supplier: 'Bygma' },
{ name: 'Spærskruer', category: 'Beslag', qty: 50, unit: 'stk', price: 3.5, supplier: 'Bygma' },
{ name: 'Træbeslag', category: 'Beslag', qty: 10, unit: 'stk', price: 45, supplier: 'Bygma' }
];
} else {
// Generic material for other packages
materials = [
{ name: 'Diverse materialer', category: 'Diverse', qty: 1, unit: 'stk', price: 500, supplier: 'Bygma' }
];
}
// Insert materials
for (const mat of materials) {
await conn.execute(
`INSERT INTO package_materials
(package_id, material_name, material_category, quantity, unit, unit_price, total_price, supplier, base_quantity)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
packageId,
mat.name,
mat.category,
mat.qty,
mat.unit,
mat.price,
mat.qty * mat.price,
mat.supplier,
mat.qty
]
);
}
console.log(` ✓ Tilføjet ${materials.length} materialer til pakke ${packageId}`);
}
(async () => {
const conn = await mysql.createConnection({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
// Get all active packages
const [packages] = await conn.execute(
'SELECT id, name, description, category, estimated_hours FROM material_packages WHERE is_active = 1 ORDER BY id'
);
console.log(`🔄 STARTER POPULERING AF ${packages.length} PAKKER...\n`);
let tasksAdded = 0;
let materialsAdded = 0;
for (const pkg of packages) {
console.log(`\n📦 Pakke ${pkg.id}: ${pkg.name}`);
try {
const [tasksBefore] = await conn.execute('SELECT COUNT(*) as count FROM package_tasks WHERE package_id = ?', [pkg.id]);
await addTasks(conn, pkg.id, pkg.name, pkg.category, pkg.estimated_hours || 4);
const [tasksAfter] = await conn.execute('SELECT COUNT(*) as count FROM package_tasks WHERE package_id = ?', [pkg.id]);
if (tasksAfter[0].count > tasksBefore[0].count) tasksAdded++;
const [matsBefore] = await conn.execute('SELECT COUNT(*) as count FROM package_materials WHERE package_id = ?', [pkg.id]);
await addMaterials(conn, pkg.id, pkg.name, pkg.description || '');
const [matsAfter] = await conn.execute('SELECT COUNT(*) as count FROM package_materials WHERE package_id = ?', [pkg.id]);
if (matsAfter[0].count > matsBefore[0].count) materialsAdded++;
} catch (error) {
console.log(` ❌ Fejl: ${error.message}`);
}
}
console.log(`\n\n✅ FÆRDIG!`);
console.log(` ${tasksAdded} pakker fik opgaver`);
console.log(` ${materialsAdded} pakker fik materialer`);
await conn.end();
})();