323 lines
16 KiB
HTML
323 lines
16 KiB
HTML
|
|
{% extends "shared/frontend/base.html" %}
|
||
|
|
|
||
|
|
{% block title %}{{ agreement.agreement_number }} - Fastpris Aftale{% endblock %}
|
||
|
|
|
||
|
|
{% block content %}
|
||
|
|
<div class="container-fluid py-4">
|
||
|
|
<!-- Header -->
|
||
|
|
<div class="row mb-4">
|
||
|
|
<div class="col">
|
||
|
|
<nav aria-label="breadcrumb">
|
||
|
|
<ol class="breadcrumb">
|
||
|
|
<li class="breadcrumb-item"><a href="/fixed-price-agreements">Fastpris Aftaler</a></li>
|
||
|
|
<li class="breadcrumb-item active">{{ agreement.agreement_number }}</li>
|
||
|
|
</ol>
|
||
|
|
</nav>
|
||
|
|
<h1 class="h3 mb-0">📋 {{ agreement.agreement_number }}</h1>
|
||
|
|
<p class="text-muted">{{ agreement.customer_name }}</p>
|
||
|
|
</div>
|
||
|
|
<div class="col-auto">
|
||
|
|
{% if agreement.status == 'active' %}
|
||
|
|
<span class="badge bg-success fs-6">Aktiv</span>
|
||
|
|
{% elif agreement.status == 'suspended' %}
|
||
|
|
<span class="badge bg-warning fs-6">Suspenderet</span>
|
||
|
|
{% elif agreement.status == 'expired' %}
|
||
|
|
<span class="badge bg-danger fs-6">Udløbet</span>
|
||
|
|
{% elif agreement.status == 'cancelled' %}
|
||
|
|
<span class="badge bg-secondary fs-6">Annulleret</span>
|
||
|
|
{% endif %}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Overview Cards -->
|
||
|
|
<div class="row g-3 mb-4">
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<h6 class="text-muted mb-2">Månedlige Timer</h6>
|
||
|
|
<h3 class="mb-0">{{ '%.0f'|format(agreement.monthly_hours or 0) }} t</h3>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<h6 class="text-muted mb-2">Timepris</h6>
|
||
|
|
<h3 class="mb-0">{{ '%.0f'|format(agreement.hourly_rate or 0) }} kr</h3>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<h6 class="text-muted mb-2">Denne Måned</h6>
|
||
|
|
<h3 class="mb-0">{{ '%.1f'|format(agreement.current_used_hours or 0) }} / {{ '%.0f'|format(agreement.monthly_hours or 0) }} t</h3>
|
||
|
|
<small class="text-muted">{{ '%.1f'|format(agreement.current_remaining_hours or 0) }}t tilbage</small>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<h6 class="text-muted mb-2">Binding</h6>
|
||
|
|
<h3 class="mb-0">{{ agreement.binding_months }} mdr</h3>
|
||
|
|
{% if agreement.binding_end_date %}
|
||
|
|
<small class="text-muted">Til {{ agreement.binding_end_date }}</small>
|
||
|
|
{% endif %}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Tabs -->
|
||
|
|
<ul class="nav nav-tabs mb-3" role="tablist">
|
||
|
|
<li class="nav-item">
|
||
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#details-tab">Detaljer</button>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item">
|
||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#periods-tab">
|
||
|
|
Perioder <span class="badge bg-secondary">{{ periods|length }}</span>
|
||
|
|
</button>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item">
|
||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#sager-tab">
|
||
|
|
Sager <span class="badge bg-secondary">{{ sager|length }}</span>
|
||
|
|
</button>
|
||
|
|
</li>
|
||
|
|
<li class="nav-item">
|
||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#time-tab">
|
||
|
|
Tidsregistreringer <span class="badge bg-secondary">{{ time_entries|length }}</span>
|
||
|
|
</button>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
|
||
|
|
<!-- Tab Content -->
|
||
|
|
<div class="tab-content">
|
||
|
|
<!-- Details Tab -->
|
||
|
|
<div class="tab-pane fade show active" id="details-tab">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<div class="row">
|
||
|
|
<div class="col-md-6">
|
||
|
|
<h5 class="mb-3">Aftale Information</h5>
|
||
|
|
<table class="table table-sm">
|
||
|
|
<tr>
|
||
|
|
<th width="40%">Kunde ID:</th>
|
||
|
|
<td>{{ agreement.customer_id }}</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Kunde:</th>
|
||
|
|
<td>{{ agreement.customer_name }}</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Start Dato:</th>
|
||
|
|
<td>{{ agreement.start_date }}</td>
|
||
|
|
</tr>
|
||
|
|
{% if agreement.end_date %}
|
||
|
|
<tr>
|
||
|
|
<th>Slut Dato:</th>
|
||
|
|
<td>{{ agreement.end_date }}</td>
|
||
|
|
</tr>
|
||
|
|
{% endif %}
|
||
|
|
<tr>
|
||
|
|
<th>Oprettet:</th>
|
||
|
|
<td>{{ agreement.created_at }}</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-6">
|
||
|
|
<h5 class="mb-3">Priser & Vilkår</h5>
|
||
|
|
<table class="table table-sm">
|
||
|
|
<tr>
|
||
|
|
<th width="40%">Månedlige Timer:</th>
|
||
|
|
<td>{{ '%.0f'|format(agreement.monthly_hours or 0) }} timer</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Normal Timepris:</th>
|
||
|
|
<td>{{ '%.0f'|format(agreement.hourly_rate or 0) }} kr</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Overtid Timepris:</th>
|
||
|
|
<td>{{ '%.0f'|format(agreement.overtime_rate or 0) }} kr {% if agreement.overtime_rate and agreement.hourly_rate %}({{ '%.0f'|format((agreement.overtime_rate / agreement.hourly_rate - 1) * 100) }}%){% endif %}</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Afrunding:</th>
|
||
|
|
<td>{% if agreement.rounding_minutes == 0 %}Ingen{% else %}{{ agreement.rounding_minutes }} min{% endif %}</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Bindingsperiode:</th>
|
||
|
|
<td>{{ agreement.binding_months }} måneder</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th>Opsigelsesfrist:</th>
|
||
|
|
<td>{{ agreement.notice_period_days }} dage</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Periods Tab -->
|
||
|
|
<div class="tab-pane fade" id="periods-tab">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
<div class="table-responsive">
|
||
|
|
<table class="table table-hover">
|
||
|
|
<thead class="table-light">
|
||
|
|
<tr>
|
||
|
|
<th>Periode</th>
|
||
|
|
<th>Status</th>
|
||
|
|
<th>Brugte Timer</th>
|
||
|
|
<th>Resterende Timer</th>
|
||
|
|
<th>Overtid</th>
|
||
|
|
<th>Månedlig Værdi</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
{% for period in periods %}
|
||
|
|
<tr>
|
||
|
|
<td>
|
||
|
|
<strong>{{ period.period_start }}</strong> til {{ period.period_end }}
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
{% if period.status == 'active' %}
|
||
|
|
<span class="badge bg-success">Aktiv</span>
|
||
|
|
{% elif period.status == 'pending_approval' %}
|
||
|
|
<span class="badge bg-warning">⚠️ Overtid</span>
|
||
|
|
{% elif period.status == 'ready_to_bill' %}
|
||
|
|
<span class="badge bg-info">Klar til faktura</span>
|
||
|
|
{% elif period.status == 'billed' %}
|
||
|
|
<span class="badge bg-secondary">Faktureret</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>{{ '%.1f'|format(period.used_hours or 0) }}t</td>
|
||
|
|
<td>{{ '%.1f'|format(period.remaining_hours or 0) }}t</td>
|
||
|
|
<td>
|
||
|
|
{% if period.overtime_hours and period.overtime_hours > 0 %}
|
||
|
|
<span class="text-danger">+{{ '%.1f'|format(period.overtime_hours) }}t</span>
|
||
|
|
{% else %}
|
||
|
|
<span class="text-muted">-</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>{{ '%.0f'|format(period.base_amount or 0) }} kr</td>
|
||
|
|
</tr>
|
||
|
|
{% endfor %}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Sager Tab -->
|
||
|
|
<div class="tab-pane fade" id="sager-tab">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
{% if sager %}
|
||
|
|
<div class="table-responsive">
|
||
|
|
<table class="table table-hover">
|
||
|
|
<thead class="table-light">
|
||
|
|
<tr>
|
||
|
|
<th>Sag ID</th>
|
||
|
|
<th>Titel</th>
|
||
|
|
<th>Status</th>
|
||
|
|
<th>Oprettet</th>
|
||
|
|
<th>Handlinger</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
{% for sag in sager %}
|
||
|
|
<tr>
|
||
|
|
<td><strong>#{{ sag.id }}</strong></td>
|
||
|
|
<td>{{ sag.titel }}</td>
|
||
|
|
<td>
|
||
|
|
{% if sag.status == 'open' %}
|
||
|
|
<span class="badge bg-success">Åben</span>
|
||
|
|
{% elif sag.status == 'in_progress' %}
|
||
|
|
<span class="badge bg-primary">I gang</span>
|
||
|
|
{% elif sag.status == 'closed' %}
|
||
|
|
<span class="badge bg-secondary">Lukket</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>{{ sag.created_at.strftime('%Y-%m-%d') if sag.created_at else '-' }}</td>
|
||
|
|
<td>
|
||
|
|
<a href="/sag/{{ sag.id }}" class="btn btn-sm btn-outline-primary">
|
||
|
|
<i class="bi bi-eye"></i> Vis
|
||
|
|
</a>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
{% endfor %}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
{% else %}
|
||
|
|
<div class="text-center text-muted py-5">
|
||
|
|
<i class="bi bi-inbox fs-1 mb-3"></i>
|
||
|
|
<p>Ingen sager endnu</p>
|
||
|
|
</div>
|
||
|
|
{% endif %}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Time Entries Tab -->
|
||
|
|
<div class="tab-pane fade" id="time-tab">
|
||
|
|
<div class="card border-0 shadow-sm">
|
||
|
|
<div class="card-body">
|
||
|
|
{% if time_entries %}
|
||
|
|
<div class="table-responsive">
|
||
|
|
<table class="table table-sm table-hover">
|
||
|
|
<thead class="table-light">
|
||
|
|
<tr>
|
||
|
|
<th>Dato</th>
|
||
|
|
<th>Sag</th>
|
||
|
|
<th>Beskrivelse</th>
|
||
|
|
<th>Timer</th>
|
||
|
|
<th>Afrundet</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
{% for entry in time_entries %}
|
||
|
|
<tr>
|
||
|
|
<td>{{ entry.created_at.strftime('%Y-%m-%d') if entry.created_at else '-' }}</td>
|
||
|
|
<td>
|
||
|
|
{% if entry.sag_id %}
|
||
|
|
<a href="/sag/{{ entry.sag_id }}">#{{ entry.sag_id }}</a>
|
||
|
|
{% if entry.sag_titel %}
|
||
|
|
<br><small class="text-muted">{{ entry.sag_titel[:30] }}</small>
|
||
|
|
{% endif %}
|
||
|
|
{% else %}
|
||
|
|
<span class="text-muted">-</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
<td>
|
||
|
|
<small>{{ entry.note[:50] if entry.note else '-' }}</small>
|
||
|
|
</td>
|
||
|
|
<td>{{ '%.2f'|format(entry.approved_hours or entry.original_hours or 0) }}t</td>
|
||
|
|
<td>
|
||
|
|
{% if entry.rounded_to %}
|
||
|
|
<span class="badge bg-info">{{ entry.rounded_to }} min</span>
|
||
|
|
{% else %}
|
||
|
|
<span class="text-muted">-</span>
|
||
|
|
{% endif %}
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
{% endfor %}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
{% else %}
|
||
|
|
<div class="text-center text-muted py-5">
|
||
|
|
<i class="bi bi-clock fs-1 mb-3"></i>
|
||
|
|
<p>Ingen tidsregistreringer endnu</p>
|
||
|
|
</div>
|
||
|
|
{% endif %}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{% endblock %}
|