Files
tilbudgivern/docs/features/CUSTOM_INSTALLATION_TASKS.md
alexpolo1 4a372d2e28 clean up
2025-10-30 18:28:58 +00:00

12 KiB
Raw Blame History

Custom Installation Opgaver - Manual

📋 Oversigt

Dette system gør det muligt at manuelt oprette installation opgaver for materialer der IKKE har montage vejledninger. Tømmeren kan selv definere:

  • Opgave navn: fx "Opsætning af lægter"
  • Tid pr. enhed: fx 10 minutter pr. løbende meter
  • Enhed: per_meter, per_sqm, per_piece, per_project
  • Materialekobling: Varenummer på materialet
  • Værktøjer: Liste af nødvendige værktøjer
  • Timepris: Kan tilpasses pr. opgave

Alt er redigerbart og justerbart efter behov.

🗄️ Database Struktur

Nye felter i smart_package_tasks tabel:

time_unit           -- Enhed: 'per_sqm', 'per_meter', 'per_piece', 'per_project'
time_per_unit       -- Timer pr. enhed (fx 0.17 = 10 min)
is_custom           -- Boolean: Er det en manuelt oprettet opgave?
material_varenr     -- Varenummer på materialet

🔧 API Endpoints

1. Opret Custom Task

POST /api/smart-packages/custom-tasks

{
  "packageId": 1,
  "name": "Opsætning af lægter",
  "description": "Præcis montage af lægter med korrekt afstand (cc 370mm). Inkluderer skruearbejde og kontrol af vaterpas.",
  "timeUnit": "per_meter",
  "timePerUnit": 0.17,
  "hourlyRate": 580,
  "materialVarenr": "147072",
  "tools": ["skruemaskine", "vaterpas", "målebånd", "snor"],
  "skills": ["tømrerarbejde", "præcision"],
  "optional": false
}

Response:

{
  "success": true,
  "taskId": 42,
  "order": 4,
  "message": "Custom task 'Opsætning af lægter' oprettet med 0.17 timer pr. per_meter"
}

2. Hent Alle Custom Tasks

GET /api/smart-packages/custom-tasks

Query params:

  • packageId - Filtrer på specifik pakke
  • materialVarenr - Filtrer på specifikt materiale

Response:

{
  "success": true,
  "tasks": [
    {
      "id": 42,
      "package_id": 1,
      "name": "Opsætning af lægter",
      "description": "...",
      "time_unit": "per_meter",
      "time_per_unit": 0.17,
      "time_display": "10 min pr. løbende meter",
      "hourly_rate": 580,
      "material_varenr": "147072",
      "material_name": "Lægter 38x73mm",
      "package_name": "B7 Tag Udskiftning",
      "required_tools": ["skruemaskine", "vaterpas"],
      "is_custom": 1,
      "created_at": "2025-10-29T10:30:00"
    }
  ],
  "count": 1
}

3. Opdater Task (inkl. custom felter)

PUT /api/smart-packages/tasks/:taskId

{
  "name": "Opsætning af lægter - opdateret",
  "timeUnit": "per_meter",
  "timePerUnit": 0.20,
  "materialVarenr": "147072",
  "tools": ["skruemaskine", "vaterpas", "målebånd"]
}

💡 Eksempler på Custom Tasks

Eksempel 1: Lægter (løbende meter)

POST /api/smart-packages/custom-tasks
{
  "packageId": 1,
  "name": "Opsætning af lægter",
  "description": "Præcis montage af lægter med korrekt cc-mål 370mm",
  "timeUnit": "per_meter",        // Pr. løbende meter
  "timePerUnit": 0.17,             // 10 minutter = 0.166 timer
  "hourlyRate": 580,
  "materialVarenr": "147072",
  "tools": ["skruemaskine", "vaterpas", "målebånd", "snor"]
}

Beregning:

  • 100 løbende meter lægter × 0.17 timer = 17 timer
  • 17 timer × 580 kr = 9,860 kr

Eksempel 2: Spær (pr. styk)

POST /api/smart-packages/custom-tasks
{
  "packageId": 1,
  "name": "Montage af spær",
  "description": "Opsætning af bærende spær med korrekt afstand og vinkel",
  "timeUnit": "per_piece",         // Pr. styk
  "timePerUnit": 0.75,             // 45 minutter pr. spær
  "hourlyRate": 580,
  "tools": ["boremaskine", "vaterpas", "vinkelmåler", "sav"]
}

Beregning:

  • 12 spær × 0.75 timer = 9 timer
  • 9 timer × 580 kr = 5,220 kr

Eksempel 3: Tagpap (pr. m²)

POST /api/smart-packages/custom-tasks
{
  "packageId": 2,
  "name": "Udlægning af tagpap",
  "description": "Udlægning af diffusionsåben tagpap med 10cm overlap",
  "timeUnit": "per_sqm",           // Pr. m²
  "timePerUnit": 0.08,             // ~5 minutter pr. m²
  "hourlyRate": 580,
  "materialVarenr": "TAG123",
  "tools": ["saks", "hæftepistol", "målebånd"]
}

Beregning:

  • 100 m² tagpap × 0.08 timer = 8 timer
  • 8 timer × 580 kr = 4,640 kr

Eksempel 4: Hele projekt (fast tid)

POST /api/smart-packages/custom-tasks
{
  "packageId": 3,
  "name": "Oprydning og aflevering",
  "description": "Komplet oprydning, bortskaffelse af affald og aflevering",
  "timeUnit": "per_project",       // Fast tid for hele projektet
  "timePerUnit": 4.0,              // 4 timer totalt
  "hourlyRate": 580
}

Beregning:

  • Fast pris: 4 timer × 580 kr = 2,320 kr

🔄 Workflow: Fra Materiale til Opgave

Trin 1: Identificer materialer uden vejledning

-- Find materialer uden installation manual
SELECT 
  m.varenr,
  m.material_name,
  m.category
FROM materials m
LEFT JOIN installation_manuals im ON m.varenr = im.material_varenr
WHERE im.id IS NULL
  AND m.category IN ('Tømmer', 'Tagmateriale', 'Isolering')
ORDER BY m.material_name;

Trin 2: Opret custom task for materialet

curl -X POST http://localhost:5001/api/smart-packages/custom-tasks \
  -H "Content-Type: application/json" \
  -d '{
    "packageId": 1,
    "name": "Opsætning af lægter",
    "timeUnit": "per_meter",
    "timePerUnit": 0.17,
    "materialVarenr": "147072"
  }'

Trin 3: Tilføj til smart pakke

Custom tasken er nu automatisk tilknyttet pakken og kan bruges i tilbudsgivning.

📊 time_unit Enheder

Enhed Værdi Beskrivelse Eksempel
per_meter per_meter Timer pr. løbende meter Lægter, tagrender
per_sqm per_sqm Timer pr. m² Tagpap, isolering, gulve
per_piece per_piece Timer pr. styk Spær, døre, vinduer
per_project per_project Fast tid for hele projektet Oprydning, inspektion
per_hour per_hour Timer pr. time (sjældent) Konsulentarbejde

🧮 Tidsberegning Konvertering

Fra minutter til timer (decimal):

// 10 minutter
const minutes = 10;
const hours = minutes / 60;  // 0.166667

// Afrund til 2 decimaler
const timePerUnit = Math.round(hours * 100) / 100;  // 0.17

Almindelige konverteringer:

Minutter Timer (decimal) Brug i API
5 min 0.08 0.08
10 min 0.17 0.17
15 min 0.25 0.25
20 min 0.33 0.33
30 min 0.50 0.50
45 min 0.75 0.75
60 min 1.00 1.00

🎯 Use Cases

Use Case 1: Tømrer har erfaring med lægter

Scenario: Tømmer ved at det tager ca. 10 min pr. løbende meter at montere lægter.

Løsning:

{
  "name": "Opsætning af lægter",
  "timeUnit": "per_meter",
  "timePerUnit": 0.17,  // 10 min
  "materialVarenr": "147072"
}

Use Case 2: Kompleks opgave med variabel tid

Scenario: Spær montage tager 30-60 min afhængigt af størrelse. Brug gennemsnit.

Løsning:

{
  "name": "Montage af spær",
  "timeUnit": "per_piece",
  "timePerUnit": 0.75,  // 45 min gennemsnit
  "description": "Tid varierer: 30-60 min pr. spær afhængigt af dimension"
}

Use Case 3: Ingen klar enhed - brug projekt

Scenario: Oprydning tager samme tid uanset størrelse.

Løsning:

{
  "name": "Oprydning og aflevering",
  "timeUnit": "per_project",
  "timePerUnit": 4.0,  // 4 timer fast
  "description": "Inkluderer bortskaffelse, oprydning og slutinspektion"
}

🔍 View: custom_installation_tasks

Der er oprettet et database view til at få overblik:

SELECT * FROM custom_installation_tasks
ORDER BY created_at DESC;

Output:

+----+----------------------+------------------+--------------+-----------------------------+
| id | task_name            | time_unit        | time_per_unit| time_display                |
+----+----------------------+------------------+--------------+-----------------------------+
| 42 | Opsætning af lægter  | per_meter        | 0.17         | 10 min pr. løbende meter    |
| 43 | Montage af spær      | per_piece        | 0.75         | 45 min pr. styk             |
| 44 | Udlægning af tagpap  | per_sqm          | 0.08         | 5 min pr. m²                |
+----+----------------------+------------------+--------------+-----------------------------+

🚀 Integration med Tilbudsgivning

Dynamisk beregning i quote system

// Beregn timer baseret på time_unit
function calculateTaskHours(task, geometry) {
  switch(task.time_unit) {
    case 'per_meter':
      // Fx lægter: 250 løbende meter × 0.17 timer
      return geometry.total_length * task.time_per_unit;
    
    case 'per_sqm':
      // Fx tagpap: 100 m² × 0.08 timer
      return geometry.total_area * task.time_per_unit;
    
    case 'per_piece':
      // Fx spær: 12 styk × 0.75 timer
      return geometry.count * task.time_per_unit;
    
    case 'per_project':
      // Fast tid
      return task.time_per_unit;
    
    default:
      return task.estimated_hours || 0;
  }
}

// Brug i tilbud
const taskHours = calculateTaskHours(task, projectGeometry);
const taskCost = taskHours * task.hourly_rate;

Validering & Best Practices

DO:

  1. Brug erfaring: Basér tid på faktisk erfaring fra tidligere projekter
  2. Vær realistisk: Inkluder pauser, opstilling, nedpakning
  3. Dokumentér: Skriv klar beskrivelse af hvad der er inkluderet
  4. Tilføj værktøjer: Angiv nødvendige værktøjer for planlægning
  5. Kobl materiale: Angiv materialVarenr for klar sammenhæng

DON'T:

  1. Undgå for optimistiske tider: Husk spildtid, fejl, justeringer
  2. Glem ikke forberedelse: Opstilling tager også tid
  3. Ignorer kompleksitet: Svære forhold kræver mere tid
  4. Brug ikke gamle data: Opdater baseret på ny erfaring

📝 Eksempel Script: Bulk Oprettelse

// bulk-create-custom-tasks.js
const tasks = [
  {
    packageId: 1,
    name: "Opsætning af lægter",
    timeUnit: "per_meter",
    timePerUnit: 0.17,
    materialVarenr: "147072"
  },
  {
    packageId: 1,
    name: "Montage af spær",
    timeUnit: "per_piece",
    timePerUnit: 0.75
  },
  {
    packageId: 1,
    name: "Udlægning af tagpap",
    timeUnit: "per_sqm",
    timePerUnit: 0.08,
    materialVarenr: "TAG123"
  }
];

async function createTasks() {
  for (const task of tasks) {
    const response = await fetch('http://localhost:5001/api/smart-packages/custom-tasks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(task)
    });
    
    const result = await response.json();
    console.log(`✅ ${result.message}`);
  }
}

createTasks();

🔄 Opdatering af Eksisterende Tasks

Ændre tid pr. enhed:

curl -X PUT http://localhost:5001/api/smart-packages/tasks/42 \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Opsætning af lægter",
    "timePerUnit": 0.20,
    "timeUnit": "per_meter"
  }'

Ændre enhed type:

# Skift fra per_project til per_sqm
curl -X PUT http://localhost:5001/api/smart-packages/tasks/45 \
  -H "Content-Type: application/json" \
  -d '{
    "timeUnit": "per_sqm",
    "timePerUnit": 0.15
  }'

📊 Statistik & Rapportering

Hent custom tasks grupperet på enhed:

SELECT 
  time_unit,
  COUNT(*) as task_count,
  AVG(time_per_unit) as avg_time,
  MIN(time_per_unit) as min_time,
  MAX(time_per_unit) as max_time
FROM smart_package_tasks
WHERE is_custom = 1
GROUP BY time_unit;

Find mest brugte custom tasks:

SELECT 
  t.task_name,
  t.time_unit,
  t.time_per_unit,
  COUNT(spu.id) as times_used
FROM smart_package_tasks t
LEFT JOIN smart_package_usage spu ON t.package_id = spu.package_id
WHERE t.is_custom = 1
GROUP BY t.id
ORDER BY times_used DESC
LIMIT 10;

🎓 Tømmer Cheat Sheet

Hurtig reference til almindelige opgaver:

Opgave Enhed Typisk tid timePerUnit
Lægter opsætning per_meter 10 min 0.17
Spær montage per_piece 45 min 0.75
Tagpap udlægning per_sqm 5 min 0.08
Isolering per_sqm 8 min 0.13
Vindspær per_meter 12 min 0.20
Tagrender per_meter 15 min 0.25
Nedrivning per_sqm 6 min 0.10
Oprydning per_project 4 timer 4.00

Oprettet: 2025-10-29
Version: 1.0
Status: Produktionsklar