Ver 11.01 better permisions
This commit is contained in:
parent
9c365aa4e1
commit
0fbf30ddc5
Binary file not shown.
@ -33,17 +33,44 @@
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto align-items-center">
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.is_staff or user.is_superuser or user.managed_teams.exists or user.assigned_projects.exists %}
|
||||
{# Dashboard #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.view_project or user.managed_teams.exists or user.assigned_projects.exists %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'home' %}">Dashboard</a></li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'log_attendance' %}">Log Work</a></li>
|
||||
{# Log Work #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.add_worklog %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'log_attendance' %}">Log Work</a></li>
|
||||
{% else %}
|
||||
{# Fallback for existing users if strict mode is not desired, but user requested hiding. #}
|
||||
{# I will leave it visible ONLY if they are supervisors to maintain status quo for them if they lack permissions #}
|
||||
{% if user.managed_teams.exists or user.assigned_projects.exists %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'log_attendance' %}">Log Work</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_staff or user.is_superuser or user.managed_teams.exists or user.assigned_projects.exists %}
|
||||
{# History #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.view_worklog or user.managed_teams.exists or user.assigned_projects.exists %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'work_log_list' %}">History</a></li>
|
||||
{% endif %}
|
||||
|
||||
{# Payroll #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.view_payrollrecord %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'payroll_dashboard' %}">Payroll</a></li>
|
||||
{% endif %}
|
||||
|
||||
{# Loans #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.view_loan %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'loan_list' %}">Loans</a></li>
|
||||
{% endif %}
|
||||
|
||||
{# Receipts #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.add_expensereceipt %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'create_receipt' %}">Receipts</a></li>
|
||||
{% endif %}
|
||||
|
||||
{# Manage #}
|
||||
{% if user.is_staff or user.is_superuser or perms.core.change_worker %}
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'manage_resources' %}">Manage</a></li>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@ -86,6 +86,17 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Total Cost Estimation -->
|
||||
<div class="card p-3 mb-4 bg-light border-0 shadow-sm">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="mb-0 text-muted">Estimated Cost</h5>
|
||||
<small class="text-muted" id="calculationDetails">0 workers × 0 days</small>
|
||||
</div>
|
||||
<h3 class="mb-0 fw-bold text-dark font-monospace" id="estimatedTotal">R 0.00</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<a href="{% url 'home' %}" class="btn btn-light px-4">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary px-5">Save Work Log</button>
|
||||
@ -153,13 +164,7 @@
|
||||
const teamId = this.value;
|
||||
if (teamId && teamWorkersMap[teamId]) {
|
||||
const workerIds = teamWorkersMap[teamId];
|
||||
// Uncheck all first? No, maybe append. Let's append as per common expectations unless explicit clear needed.
|
||||
// Actually, if I change team, I probably expect to select THAT team's workers.
|
||||
// Let's clear and select.
|
||||
// But maybe I want to mix teams.
|
||||
// User didn't specify. Previous logic was: select workers belonging to team.
|
||||
// Let's stick to "select", don't uncheck others.
|
||||
|
||||
// Select workers belonging to the team
|
||||
workerIds.forEach(function(id) {
|
||||
const checkbox = document.querySelector(`input[name="workers"][value="${id}"]`);
|
||||
if (checkbox) {
|
||||
@ -176,6 +181,86 @@
|
||||
var myModal = new bootstrap.Modal(conflictModalEl);
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
// --- Cost Calculation Logic ---
|
||||
const workerRates = {{ worker_rates_json|safe }};
|
||||
const startDateInput = document.getElementById('{{ form.date.id_for_label }}');
|
||||
const endDateInput = document.getElementById('{{ form.end_date.id_for_label }}');
|
||||
const satCheckbox = document.getElementById('{{ form.include_saturday.id_for_label }}');
|
||||
const sunCheckbox = document.getElementById('{{ form.include_sunday.id_for_label }}');
|
||||
const workerCheckboxes = document.querySelectorAll('input[name="workers"]');
|
||||
const totalDisplay = document.getElementById('estimatedTotal');
|
||||
const detailsDisplay = document.getElementById('calculationDetails');
|
||||
|
||||
function calculateTotal() {
|
||||
// 1. Calculate Days
|
||||
let days = 0;
|
||||
const start = startDateInput.value ? new Date(startDateInput.value) : null;
|
||||
const end = endDateInput.value ? new Date(endDateInput.value) : null;
|
||||
|
||||
if (start) {
|
||||
if (!end || end < start) {
|
||||
days = 1;
|
||||
} else {
|
||||
// Iterate dates
|
||||
let curr = new Date(start);
|
||||
// Reset time components to avoid TZ issues
|
||||
curr.setHours(0,0,0,0);
|
||||
const last = new Date(end);
|
||||
last.setHours(0,0,0,0);
|
||||
|
||||
while (curr <= last) {
|
||||
const dayOfWeek = curr.getDay(); // 0 = Sun, 6 = Sat
|
||||
let isWorkingDay = true;
|
||||
|
||||
if (dayOfWeek === 6 && !satCheckbox.checked) isWorkingDay = false;
|
||||
if (dayOfWeek === 0 && !sunCheckbox.checked) isWorkingDay = false;
|
||||
|
||||
if (isWorkingDay) days++;
|
||||
|
||||
curr.setDate(curr.getDate() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Sum Worker Rates
|
||||
let dailyRateSum = 0;
|
||||
let workerCount = 0;
|
||||
|
||||
workerCheckboxes.forEach(cb => {
|
||||
if (cb.checked) {
|
||||
const rate = workerRates[cb.value] || 0;
|
||||
dailyRateSum += rate;
|
||||
workerCount++;
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Update UI
|
||||
const total = dailyRateSum * days;
|
||||
totalDisplay.textContent = 'R ' + total.toLocaleString('en-ZA', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
detailsDisplay.textContent = `${workerCount} worker${workerCount !== 1 ? 's' : ''} × ${days} day${days !== 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
// Attach Listeners
|
||||
if (startDateInput) startDateInput.addEventListener('change', calculateTotal);
|
||||
if (endDateInput) endDateInput.addEventListener('change', calculateTotal);
|
||||
if (satCheckbox) satCheckbox.addEventListener('change', calculateTotal);
|
||||
if (sunCheckbox) sunCheckbox.addEventListener('change', calculateTotal);
|
||||
|
||||
workerCheckboxes.forEach(cb => {
|
||||
cb.addEventListener('change', calculateTotal);
|
||||
});
|
||||
|
||||
// Also update when team changes (since it selects workers programmatically)
|
||||
if (teamSelect) {
|
||||
teamSelect.addEventListener('change', function() {
|
||||
// Give it a moment for the check logic to finish
|
||||
setTimeout(calculateTotal, 100);
|
||||
});
|
||||
}
|
||||
|
||||
// Initial Run
|
||||
calculateTotal();
|
||||
});
|
||||
|
||||
function submitConflict(action) {
|
||||
|
||||
@ -80,13 +80,24 @@
|
||||
<button type="submit" class="btn btn-primary w-100">Filter</button>
|
||||
</div>
|
||||
</form>
|
||||
{% if selected_worker or selected_team or selected_project or selected_payment_status and selected_payment_status != 'all' %}
|
||||
<div class="mt-3">
|
||||
<a href="{% url 'work_log_list' %}?view={{ view_mode }}" class="btn btn-sm btn-link text-decoration-none text-muted">
|
||||
<i class="bi bi-x-circle"></i> Clear all filters
|
||||
</a>
|
||||
|
||||
<hr class="my-4 opacity-10">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-end">
|
||||
<div>
|
||||
{% if selected_worker or selected_team or selected_project or selected_payment_status and selected_payment_status != 'all' %}
|
||||
<a href="{% url 'work_log_list' %}?view={{ view_mode }}" class="btn btn-sm btn-link text-decoration-none text-muted p-0">
|
||||
<i class="bi bi-x-circle"></i> Clear all filters
|
||||
</a>
|
||||
{% else %}
|
||||
<small class="text-muted">Showing all records</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<small class="text-uppercase text-muted fw-bold ls-1 d-block mb-1">Total Value</small>
|
||||
<span class="h2 fw-bold text-primary mb-0">R {{ total_amount|floatformat:2 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -24,6 +24,8 @@ def is_staff_or_supervisor(user):
|
||||
"""Check if user is staff or manages at least one team/project."""
|
||||
if user.is_staff or user.is_superuser:
|
||||
return True
|
||||
if user.has_perm('core.view_project'):
|
||||
return True
|
||||
return user.managed_teams.exists() or user.assigned_projects.exists()
|
||||
|
||||
@login_required
|
||||
@ -141,11 +143,16 @@ def log_attendance(request):
|
||||
conflicts.append(conflict_entry)
|
||||
|
||||
if conflicts:
|
||||
# Prepare worker rates for JS calculation
|
||||
worker_qs = form.fields['workers'].queryset
|
||||
worker_rates = {w.id: float(w.day_rate) for w in worker_qs}
|
||||
|
||||
context = {
|
||||
'form': form,
|
||||
'team_workers_json': json.dumps(team_workers_map),
|
||||
'conflicting_workers': conflicts,
|
||||
'is_conflict': True,
|
||||
'worker_rates_json': json.dumps(worker_rates),
|
||||
}
|
||||
return render(request, 'core/log_attendance.html', context)
|
||||
|
||||
@ -203,9 +210,14 @@ def log_attendance(request):
|
||||
else:
|
||||
form = WorkLogForm(user=request.user if request.user.is_authenticated else None)
|
||||
|
||||
# Pass worker rates for frontend total calculation
|
||||
worker_qs = form.fields['workers'].queryset
|
||||
worker_rates = {w.id: float(w.day_rate) for w in worker_qs}
|
||||
|
||||
context = {
|
||||
'form': form,
|
||||
'team_workers_json': json.dumps(team_workers_map)
|
||||
'team_workers_json': json.dumps(team_workers_map),
|
||||
'worker_rates_json': json.dumps(worker_rates)
|
||||
}
|
||||
|
||||
return render(request, 'core/log_attendance.html', context)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user