/** * Script til at oprette erfaringsbaserede SmartPackages * Baseret på kundens faktiske priser og erfaringer */ const axios = require('axios'); const mysql = require('mysql2/promise'); const LOCAL_API = 'http://localhost:4031'; // Database connection const dbConfig = { host: 'localhost', user: 'tilbudgivern_service', password: process.env.DB_PASSWORD, database: 'tilbudgivern' }; /** * Hent pris fra database med 20% avance */ async function getPriceWithMarkup(varenr) { const connection = await mysql.createConnection(dbConfig); try { const [rows] = await connection.execute( `SELECT mp.price, mp.unit, m.name FROM materials m LEFT JOIN material_prices mp ON m.id = mp.material_id WHERE m.sku = ? AND mp.is_active = 1 ORDER BY mp.valid_from DESC LIMIT 1`, [varenr] ); if (rows.length > 0) { const basePrice = parseFloat(rows[0].price); const priceWithMarkup = basePrice * 1.20; // 20% avance return { price: priceWithMarkup, unit: rows[0].unit, name: rows[0].name }; } // Returner placeholder hvis ikke fundet - tilføjes senere return null; } finally { await connection.end(); } } /** * Opret materiale entry (bruges til at oprette pakkerne nu, priser tilføjes senere) */ function createMaterialEntry(name, productNumber, quantity, unit, description = '') { return { name, productNumber, quantity: typeof quantity === 'string' ? 1 : quantity, quantityFormula: typeof quantity === 'string' ? quantity : null, unit, unitPrice: 0, // Placeholder - opdateres når priser tilføjes description, needsPriceUpdate: true }; } /** * Opret SmartPackage via API */ async function createSmartPackage(packageData) { try { const response = await axios.post(`${LOCAL_API}/api/smart-packages`, packageData); console.log(`✅ Oprettet pakke: ${packageData.name} (ID: ${response.data.id})`); return response.data; } catch (error) { console.error(`❌ Fejl ved oprettelse af ${packageData.name}:`, error.response?.data || error.message); throw error; } } /** * PAKKE 1: Tagrende med Konsoljern i stål (Lindab materialer) */ async function createTagrendePakke() { console.log('\n📦 Opretter Tagrende pakke...'); // Materialeliste med formler for beregning const materials = [ createMaterialEntry( 'Tagrende 3 meter', '077424', 'FORMULA:facadelængde/3', 'stk', 'Antal beregnes automatisk: facadelængde / 3 meter' ), createMaterialEntry( 'Ende bund', '107744', 4, 'stk', '1 i hver ende - 4 stk generisk' ), createMaterialEntry( 'Samle stykke', '077419', 'FORMULA:(facadelængde/3)-1', 'stk', '1 samle beslag pr 3 meter tagrende' ), createMaterialEntry( 'Konsol jern', '077407', 'FORMULA:facadelængde/0.6', 'stk', 'Beregnes automatisk: maks 60cm afstand' ), createMaterialEntry( 'Hjørne indvendig', '077426', 1, 'stk', 'Huskeliste - tilpas efter behov' ), createMaterialEntry( 'Hjørne udvendig', '077431', 1, 'stk', 'Huskeliste - tilpas efter behov' ) ]; const packageData = { name: 'Tagrende med Konsoljern (Lindab)', description: 'Komplet tagrende system med konsoljern i stål. Materialer fra Lindab.\n\nArbeidsberegning: 10 meter tager 5 timer (0,5 time pr meter)', category: 'Tag - Tagrende', estimatedHours: 'FORMULA:facadelængde*0.5', // 10m = 5 timer materials, requiresGeometry: true, geometryFields: ['facadelængde'] }; return await createSmartPackage(packageData); } /** * PAKKE 2: Nedløb */ async function createNedløbPakke() { console.log('\n📦 Opretter Nedløb pakke...'); const materials = [ { name: 'Nedløb 3 meter', productNumber: '077438', quantity: 'FORMULA:nedløb_antal', // Sættes af bruger unit: 'stk' }, { name: 'Rørholder', productNumber: '184937', quantity: 'FORMULA:nedløb_antal*2', // 2 pr nedløb unit: 'stk' }, { name: 'Tudstykke', productNumber: '122214', quantity: 'FORMULA:nedløb_antal', // 1 pr nedløb unit: 'stk' }, { name: 'Silikone', productNumber: '050043', quantity: 'FORMULA:nedløb_antal', // 1 pr nedløb unit: 'stk' }, { name: 'Brøndkrave', productNumber: '061637', quantity: 'FORMULA:nedløb_antal', // 1 pr nedløb unit: 'stk' }, { name: 'Bøjning', productNumber: '076623', quantity: 'FORMULA:nedløb_antal*2', // 2 pr nedløb unit: 'stk', description: 'Bøjninger varierer - tilpas efter behov' } ]; const materialsWithPrices = []; for (const m of materials) { const priceData = await getPriceWithMarkup(m.productNumber); if (priceData) { materialsWithPrices.push({ name: priceData.name || m.name, quantity: 1, // Placeholder quantityFormula: m.quantity, unit: priceData.unit, unitPrice: priceData.price, description: m.description }); } else { console.warn(`⚠️ Kunne ikke finde pris for ${m.productNumber} - ${m.name}`); } } const packageData = { name: 'Nedløb komplet', description: 'Komplet nedløbssystem med tilbehør.\n\nVælg antal nedløb - systemet beregner automatisk tilbehør.\n\nArbeidsberegning: 1 nedløb = 1 time', category: 'Tag - Nedløb', estimatedHours: 'FORMULA:nedløb_antal*1', // 1 stk = 1 time materials: materialsWithPrices, requiresGeometry: true, geometryFields: ['nedløb_antal'] }; return await createSmartPackage(packageData); } /** * PAKKE 3: Opretning af spær */ async function createSpærPakke() { console.log('\n📦 Opretter Opretning af spær pakke...'); const materials = [ { name: 'Spær træ 200mm', productNumber: '146304720010450', quantity: 'FORMULA:(huslængde+1)*husbredde', // LBM beregning unit: 'løbende meter' }, { name: 'Spånskruer 5,0x100', productNumber: '310615', quantity: 'FORMULA:((huslængde+1)*husbredde)*5', // 5 skruer pr LBM unit: 'stk' }, { name: 'Muresnor', productNumber: '195408', quantity: 1, unit: 'stk' }, { name: 'Bits TX 20', productNumber: '224770', quantity: 1, unit: 'stk' }, { name: 'Snorvatterpas', productNumber: '064736', quantity: 2, unit: 'stk' }, { name: 'Rundsavsklinge', productNumber: '182788', quantity: 1, unit: 'stk' }, { name: 'Stiksavsklinger', productNumber: '147761', quantity: 1, unit: 'stk' } ]; const materialsWithPrices = []; for (const m of materials) { const priceData = await getPriceWithMarkup(m.productNumber); if (priceData) { materialsWithPrices.push({ name: priceData.name || m.name, quantity: typeof m.quantity === 'string' ? 1 : m.quantity, quantityFormula: typeof m.quantity === 'string' ? m.quantity : null, unit: priceData.unit, unitPrice: priceData.price }); } else { console.warn(`⚠️ Kunne ikke finde pris for ${m.productNumber} - ${m.name}`); } } const packageData = { name: 'Opretning af spær', description: 'Komplet opretning af tagspær med materialer og værktøj.\n\nLBM beregnes automatisk: (huslængde + 1) × husbredde\n\nArbeidsberegning: 5 meter = 0,5 time', category: 'Tag - Spær', estimatedHours: 'FORMULA:((huslængde+1)*husbredde)*0.1', // 5m = 0.5t => 1m = 0.1t materials: materialsWithPrices, requiresGeometry: true, geometryFields: ['huslængde', 'husbredde'] }; return await createSmartPackage(packageData); } /** * PAKKE 4: Montering af B7 inkl. lægter */ async function createB7Pakke() { console.log('\n📦 Opretter B7 montering pakke...'); const materials = [ { name: 'B7 Plade', productNumber: '147072', quantity: 'FORMULA:tagflade_m2*2.13', // 2,13 plade pr m² unit: 'm²' }, { name: 'Lægter', productNumber: '1632038073', quantity: 'FORMULA:tagflade_m2*2.17', // 2,17 m pr m² unit: 'løbende meter', description: 'Lægteafstand 46 cm' }, { name: 'Tagskruer', productNumber: '119229', quantity: 'FORMULA:tagflade_m2*4.25', // 4,25 skrue pr m² unit: 'stk' }, { name: 'Ringsøm', productNumber: '101933', quantity: 'FORMULA:tagflade_m2*15', // 15 søm pr m² unit: 'stk', description: 'Pakke med 2880 stk' }, { name: 'EPDM bånd 30 meter', productNumber: '029218', quantity: 'FORMULA:tagflade_m2*2.17', // 2,17 m pr m² unit: 'løbende meter' } ]; const materialsWithPrices = []; for (const m of materials) { const priceData = await getPriceWithMarkup(m.productNumber); if (priceData) { materialsWithPrices.push({ name: priceData.name || m.name, quantity: typeof m.quantity === 'string' ? 1 : m.quantity, quantityFormula: typeof m.quantity === 'string' ? m.quantity : null, unit: priceData.unit, unitPrice: priceData.price, description: m.description }); } else { console.warn(`⚠️ Kunne ikke finde pris for ${m.productNumber} - ${m.name}`); } } const packageData = { name: 'B7 montering inkl. lægter', description: 'Komplet B7 tagbeklædning med lægter og fastgørelse.\n\nArbeidsberegning: 0,5 time pr m²', category: 'Tag - B7', estimatedHours: 'FORMULA:tagflade_m2*0.5', // 0,5 time pr m² materials: materialsWithPrices, requiresGeometry: true, geometryFields: ['tagflade_m2'] }; return await createSmartPackage(packageData); } /** * PAKKE 5: Montering af rygning B7 */ async function createRygningB7Pakke() { console.log('\n📦 Opretter Rygning B7 pakke...'); const materials = [ { name: 'Rygning', productNumber: '057437', quantity: 'FORMULA:rygning_længde', // 1 stk pr meter unit: 'løbende meter' }, { name: 'Rygningselement', productNumber: '038218', quantity: 'FORMULA:rygning_længde', // 1 stk pr meter unit: 'løbende meter' }, { name: 'Tagskruer 100 stk', productNumber: '144855', quantity: 'FORMULA:rygning_længde*4', // 4 skrue pr meter unit: 'stk' }, { name: 'EPDM bånd 30 meter', productNumber: '029218', quantity: 'FORMULA:rygning_længde*0.4', // 0,4 meter pr meter unit: 'løbende meter' } ]; const materialsWithPrices = []; for (const m of materials) { const priceData = await getPriceWithMarkup(m.productNumber); if (priceData) { materialsWithPrices.push({ name: priceData.name || m.name, quantity: typeof m.quantity === 'string' ? 1 : m.quantity, quantityFormula: typeof m.quantity === 'string' ? m.quantity : null, unit: priceData.unit, unitPrice: priceData.price }); } else { console.warn(`⚠️ Kunne ikke finde pris for ${m.productNumber} - ${m.name}`); } } const packageData = { name: 'Rygning B7', description: 'Komplet rygningsløsning til B7 tag.\n\nArbeidsberegning: Tilføjes senere', category: 'Tag - Rygning', estimatedHours: 'FORMULA:rygning_længde*0.25', // Estimeret 15 min pr meter materials: materialsWithPrices, requiresGeometry: true, geometryFields: ['rygning_længde'] }; return await createSmartPackage(packageData); } /** * PAKKE 6: Montering af taghætte B7 */ async function createTaghætteB7Pakke() { console.log('\n📦 Opretter Taghætte B7 pakke...'); const materials = [ { name: 'Taghætte', productNumber: '126813', quantity: 1, unit: 'stk' } ]; const materialsWithPrices = []; for (const m of materials) { const priceData = await getPriceWithMarkup(m.productNumber); if (priceData) { materialsWithPrices.push({ name: priceData.name || m.name, quantity: m.quantity, unit: priceData.unit, unitPrice: priceData.price }); } else { console.warn(`⚠️ Kunne ikke finde pris for ${m.productNumber} - ${m.name}`); } } const packageData = { name: 'Taghætte B7', description: 'Montering af taghætte til B7 tag.\n\nArbeidsberegning: Tilføjes senere', category: 'Tag - Taghætte', estimatedHours: 1, // Estimeret materials: materialsWithPrices, requiresGeometry: false }; return await createSmartPackage(packageData); } /** * Hovedfunktion */ async function main() { console.log('🚀 Starter oprettelse af erfaringsbaserede SmartPackages...\n'); console.log('📌 Alle priser hentes fra database med 20% avance\n'); try { // Opret alle pakker await createTagrendePakke(); await createNedløbPakke(); await createSpærPakke(); await createB7Pakke(); await createRygningB7Pakke(); await createTaghætteB7Pakke(); console.log('\n✅ Alle pakker oprettet succesfuldt!'); console.log('\n📝 Næste skridt:'); console.log(' 1. Sikr at geometri gemmes i database (facadelængde, husbredde, m² osv.)'); console.log(' 2. Implementer beregningslogik for formler (FORMULA:...)'); console.log(' 3. Test pakkerne i frontend'); console.log(' 4. Få manglende pakker fra kunde (B6, Betontegl, Velux osv.)'); } catch (error) { console.error('\n❌ Fejl under oprettelse:', error); process.exit(1); } } // Kør scriptet if (require.main === module) { main(); } module.exports = { createTagrendePakke, createNedløbPakke, createSpærPakke, createB7Pakke, createRygningB7Pakke, createTaghætteB7Pakke };