- 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.
275 lines
15 KiB
JavaScript
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();
|
|
})();
|