feat(interventions): add combined filter for manager, controller and inspector with shortnames and teams
This commit is contained in:
parent
b86d805cb5
commit
3d9e7c3202
11 changed files with 647 additions and 69 deletions
Binary file not shown.
|
|
@ -8407,5 +8407,29 @@ msgstr "Cannot modify material from a canceled preparation order."
|
||||||
msgid "Bon annulé"
|
msgid "Bon annulé"
|
||||||
msgstr "Canceled order"
|
msgstr "Canceled order"
|
||||||
|
|
||||||
|
msgid "Rôle et personne assignée"
|
||||||
|
msgstr "Role and assigned person"
|
||||||
|
|
||||||
|
msgid "Rôle"
|
||||||
|
msgstr "Role"
|
||||||
|
|
||||||
|
msgid "Personne"
|
||||||
|
msgstr "Person"
|
||||||
|
|
||||||
|
msgid "Gestionnaire"
|
||||||
|
msgstr "Manager"
|
||||||
|
|
||||||
|
msgid "Contrôleur"
|
||||||
|
msgstr "Controller"
|
||||||
|
|
||||||
|
msgid "Inspecteur"
|
||||||
|
msgstr "Inspector"
|
||||||
|
|
||||||
|
msgid "-- Sélectionner un rôle… --"
|
||||||
|
msgstr "-- Select a role… --"
|
||||||
|
|
||||||
|
msgid "-- Choisir une personne… --"
|
||||||
|
msgstr "-- Choose a person… --"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8514,5 +8514,29 @@ msgstr "Impossible de modifier le matériel d'un bon de préparation annulé."
|
||||||
msgid "Bon annulé"
|
msgid "Bon annulé"
|
||||||
msgstr "Bon annulé"
|
msgstr "Bon annulé"
|
||||||
|
|
||||||
|
msgid "Rôle et personne assignée"
|
||||||
|
msgstr "Rôle et personne assignée"
|
||||||
|
|
||||||
|
msgid "Rôle"
|
||||||
|
msgstr "Rôle"
|
||||||
|
|
||||||
|
msgid "Personne"
|
||||||
|
msgstr "Personne"
|
||||||
|
|
||||||
|
msgid "Gestionnaire"
|
||||||
|
msgstr "Gestionnaire"
|
||||||
|
|
||||||
|
msgid "Contrôleur"
|
||||||
|
msgstr "Contrôleur"
|
||||||
|
|
||||||
|
msgid "Inspecteur"
|
||||||
|
msgstr "Inspecteur"
|
||||||
|
|
||||||
|
msgid "-- Sélectionner un rôle… --"
|
||||||
|
msgstr "-- Sélectionner un rôle… --"
|
||||||
|
|
||||||
|
msgid "-- Choisir une personne… --"
|
||||||
|
msgstr "-- Choisir une personne… --"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8448,4 +8448,28 @@ msgstr "Kan materiaal van een geannuleerde voorbereidingsbon niet wijzigen."
|
||||||
msgid "Bon annulé"
|
msgid "Bon annulé"
|
||||||
msgstr "Geannuleerde bon"
|
msgstr "Geannuleerde bon"
|
||||||
|
|
||||||
|
msgid "Rôle et personne assignée"
|
||||||
|
msgstr "Rol en toegewezen persoon"
|
||||||
|
|
||||||
|
msgid "Rôle"
|
||||||
|
msgstr "Rol"
|
||||||
|
|
||||||
|
msgid "Personne"
|
||||||
|
msgstr "Persoon"
|
||||||
|
|
||||||
|
msgid "Gestionnaire"
|
||||||
|
msgstr "Beheerder"
|
||||||
|
|
||||||
|
msgid "Contrôleur"
|
||||||
|
msgstr "Controleur"
|
||||||
|
|
||||||
|
msgid "Inspecteur"
|
||||||
|
msgstr "Inspecteur"
|
||||||
|
|
||||||
|
msgid "-- Sélectionner un rôle… --"
|
||||||
|
msgstr "-- Selecteer een rol… --"
|
||||||
|
|
||||||
|
msgid "-- Choisir une personne… --"
|
||||||
|
msgstr "-- Kies een persoon… --"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,7 @@ document.getElementById('reset-filters').addEventListener('click', async functio
|
||||||
togglePauseReasonFilter();
|
togglePauseReasonFilter();
|
||||||
filterCategoriesByThematics();
|
filterCategoriesByThematics();
|
||||||
filterProvidersAndContracts();
|
filterProvidersAndContracts();
|
||||||
|
resetRolePersonFilter();
|
||||||
|
|
||||||
// Explicitly clear date fields
|
// Explicitly clear date fields
|
||||||
document.getElementById('begin_date_min').value = '';
|
document.getElementById('begin_date_min').value = '';
|
||||||
|
|
@ -2185,8 +2186,8 @@ function filterProvidersAndContracts() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (typeof updateInterventionManagers === 'function') {
|
if (typeof updateRolePersonManagers === 'function') {
|
||||||
updateInterventionManagers();
|
updateRolePersonManagers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2278,7 +2279,9 @@ function countActiveFilters() {
|
||||||
'contract': false,
|
'contract': false,
|
||||||
'assigned_team': false,
|
'assigned_team': false,
|
||||||
'assigned_member': false,
|
'assigned_member': false,
|
||||||
|
'assigned_controller': false,
|
||||||
'intervention_manager': false,
|
'intervention_manager': false,
|
||||||
|
'inspector': false,
|
||||||
'pause_reason': false,
|
'pause_reason': false,
|
||||||
'begin_date_min': false,
|
'begin_date_min': false,
|
||||||
'begin_date_max': false,
|
'begin_date_max': false,
|
||||||
|
|
@ -2330,7 +2333,8 @@ function getCurrentFilters() {
|
||||||
// Get all filter parameters
|
// Get all filter parameters
|
||||||
const filterKeys = [
|
const filterKeys = [
|
||||||
'thematic', 'status', 'priority', 'maintain_type', 'category',
|
'thematic', 'status', 'priority', 'maintain_type', 'category',
|
||||||
'type', 'provider', 'contract', 'assigned_team', 'assigned_member', 'intervention_manager',
|
'type', 'provider', 'contract', 'assigned_team', 'assigned_member',
|
||||||
|
'assigned_controller', 'intervention_manager', 'inspector',
|
||||||
'pause_reason', 'locations', 'overdue', 'to_be_planned_soon'
|
'pause_reason', 'locations', 'overdue', 'to_be_planned_soon'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -2407,7 +2411,8 @@ function countActiveFilters() {
|
||||||
// Define filter parameters used in the global filter modal
|
// Define filter parameters used in the global filter modal
|
||||||
const filterParams = [
|
const filterParams = [
|
||||||
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
||||||
'provider', 'contract', 'assigned_team', 'assigned_member', 'intervention_manager', 'pause_reason',
|
'provider', 'contract', 'assigned_team', 'assigned_member',
|
||||||
|
'assigned_controller', 'intervention_manager', 'inspector', 'pause_reason',
|
||||||
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
||||||
'overdue', 'to_be_planned_soon'
|
'overdue', 'to_be_planned_soon'
|
||||||
];
|
];
|
||||||
|
|
@ -2451,7 +2456,8 @@ function savePreferencesFromUrl() {
|
||||||
// Collect all filter parameters
|
// Collect all filter parameters
|
||||||
const filterParams = [
|
const filterParams = [
|
||||||
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
||||||
'provider', 'contract', 'assigned_team', 'assigned_member', 'intervention_manager', 'pause_reason',
|
'provider', 'contract', 'assigned_team', 'assigned_member',
|
||||||
|
'assigned_controller', 'intervention_manager', 'inspector', 'pause_reason',
|
||||||
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
||||||
'overdue', 'to_be_planned_soon'
|
'overdue', 'to_be_planned_soon'
|
||||||
];
|
];
|
||||||
|
|
@ -2504,7 +2510,8 @@ async function savePreferencesFromForm(urlParams) {
|
||||||
// Collect all filter parameters from URLSearchParams
|
// Collect all filter parameters from URLSearchParams
|
||||||
const filterParams = [
|
const filterParams = [
|
||||||
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
'thematic', 'status', 'priority', 'maintain_type', 'category', 'type',
|
||||||
'provider', 'contract', 'assigned_team', 'assigned_member', 'intervention_manager', 'pause_reason',
|
'provider', 'contract', 'assigned_team', 'assigned_member',
|
||||||
|
'assigned_controller', 'intervention_manager', 'inspector', 'pause_reason',
|
||||||
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
'begin_date_min', 'begin_date_max', 'end_date_min', 'end_date_max', 'locations',
|
||||||
'overdue', 'to_be_planned_soon'
|
'overdue', 'to_be_planned_soon'
|
||||||
];
|
];
|
||||||
|
|
@ -2558,6 +2565,7 @@ async function savePreferencesFromForm(urlParams) {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
updateFilterBadge();
|
updateFilterBadge();
|
||||||
initDateFilterPresets();
|
initDateFilterPresets();
|
||||||
|
initRolePersonFilter();
|
||||||
|
|
||||||
// Save preferences when table sort changes (user action only)
|
// Save preferences when table sort changes (user action only)
|
||||||
const $table = $('#interventions-table');
|
const $table = $('#interventions-table');
|
||||||
|
|
@ -2637,62 +2645,153 @@ if (filterModal) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateInterventionManagers() {
|
// ==================== Role & Person Filter (Gestionnaire / Contrôleur / Inspecteur) ====================
|
||||||
const checkedContracts = document.querySelectorAll('.filter-options[data-field="contract"] input[type="checkbox"]:checked');
|
|
||||||
const container = document.getElementById('intervention-manager-options-container');
|
|
||||||
const row = document.getElementById('intervention-manager-filter-row');
|
|
||||||
if (!container || !row) return;
|
|
||||||
|
|
||||||
// Get currently selected managers from URL or from checkboxes if they already exist
|
function getRolePersonsData() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const scriptEl = document.getElementById('role-persons-data');
|
||||||
const selectedManagers = new Set(urlParams.getAll('intervention_manager'));
|
if (!scriptEl) return { manager: [], controller: [], inspector: [] };
|
||||||
|
|
||||||
// Also check if any are currently checked in the UI before re-drawing
|
|
||||||
const existingChecked = container.querySelectorAll('input[type="checkbox"]:checked');
|
|
||||||
existingChecked.forEach(cb => selectedManagers.add(cb.value));
|
|
||||||
|
|
||||||
// Gather unique managers
|
|
||||||
const managersMap = new Map();
|
|
||||||
let anyUsesManager = false;
|
|
||||||
|
|
||||||
checkedContracts.forEach(cb => {
|
|
||||||
const optionDiv = cb.closest('.filter-option');
|
|
||||||
if (optionDiv) {
|
|
||||||
const usesManager = optionDiv.getAttribute('data-uses-manager') === 'true';
|
|
||||||
if (usesManager) {
|
|
||||||
anyUsesManager = true;
|
|
||||||
const managersStr = optionDiv.getAttribute('data-managers');
|
|
||||||
if (managersStr) {
|
|
||||||
try {
|
try {
|
||||||
const managers = JSON.parse(managersStr);
|
return JSON.parse(scriptEl.textContent) || { manager: [], controller: [], inspector: [] };
|
||||||
managers.forEach(mgr => {
|
|
||||||
managersMap.set(String(mgr.id), mgr.name);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error parsing managers JSON", e);
|
console.error("Error parsing role-persons data", e);
|
||||||
|
return { manager: [], controller: [], inspector: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncRolePersonHiddenInputs() {
|
||||||
|
const roleSelect = document.getElementById('filter-person-role');
|
||||||
|
const personSelect = document.getElementById('filter-person-select');
|
||||||
|
const hiddenMgr = document.getElementById('hidden-filter-intervention-manager');
|
||||||
|
const hiddenCtrl = document.getElementById('hidden-filter-assigned-controller');
|
||||||
|
const hiddenInsp = document.getElementById('hidden-filter-inspector');
|
||||||
|
|
||||||
|
if (!roleSelect || !personSelect) return;
|
||||||
|
|
||||||
|
const role = roleSelect.value;
|
||||||
|
const personId = personSelect.value;
|
||||||
|
|
||||||
|
if (hiddenMgr) hiddenMgr.value = (role === 'manager' && personId) ? personId : '';
|
||||||
|
if (hiddenCtrl) hiddenCtrl.value = (role === 'controller' && personId) ? personId : '';
|
||||||
|
if (hiddenInsp) hiddenInsp.value = (role === 'inspector' && personId) ? personId : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function populatePersonSelect(role, preselectedId) {
|
||||||
|
const personSelect = document.getElementById('filter-person-select');
|
||||||
|
if (!personSelect) return;
|
||||||
|
|
||||||
|
const chooseLabel = typeof gettext === 'function' ? gettext("-- Choisir une personne… --") : "-- Choisir une personne… --";
|
||||||
|
personSelect.innerHTML = `<option value="">${chooseLabel}</option>`;
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
personSelect.disabled = true;
|
||||||
|
syncRolePersonHiddenInputs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = getRolePersonsData();
|
||||||
|
let persons = data[role] || [];
|
||||||
|
|
||||||
|
// If role is manager, filter by checked contracts if any are checked
|
||||||
|
if (role === 'manager') {
|
||||||
|
const checkedContractBoxes = document.querySelectorAll('.filter-options[data-field="contract"] input[type="checkbox"]:checked');
|
||||||
|
if (checkedContractBoxes.length > 0) {
|
||||||
|
const checkedContractIds = Array.from(checkedContractBoxes).map(cb => parseInt(cb.value, 10)).filter(Boolean);
|
||||||
|
if (checkedContractIds.length > 0) {
|
||||||
|
const filtered = persons.filter(p => {
|
||||||
|
const pContracts = (p.contracts || []).map(cid => parseInt(cid, 10));
|
||||||
|
return pContracts.some(cid => checkedContractIds.includes(cid));
|
||||||
|
});
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
persons = filtered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
persons.forEach(p => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = p.id;
|
||||||
|
opt.textContent = p.name;
|
||||||
|
if (preselectedId && String(p.id) === String(preselectedId)) {
|
||||||
|
opt.selected = true;
|
||||||
|
}
|
||||||
|
personSelect.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (anyUsesManager && managersMap.size > 0) {
|
personSelect.disabled = false;
|
||||||
// Build the HTML for options
|
if (preselectedId) {
|
||||||
let html = '';
|
personSelect.value = String(preselectedId);
|
||||||
managersMap.forEach((name, id) => {
|
}
|
||||||
const isChecked = selectedManagers.has(id) ? 'checked' : '';
|
syncRolePersonHiddenInputs();
|
||||||
html += `
|
}
|
||||||
<div class="filter-option">
|
|
||||||
<input type="checkbox" name="intervention_manager" id="intervention_manager_${id}" value="${id}" ${isChecked}>
|
function updateRolePersonManagers() {
|
||||||
<label for="intervention_manager_${id}">${name}</label>
|
const roleSelect = document.getElementById('filter-person-role');
|
||||||
</div>
|
const personSelect = document.getElementById('filter-person-select');
|
||||||
`;
|
if (!roleSelect || !personSelect) return;
|
||||||
|
|
||||||
|
if (roleSelect.value === 'manager') {
|
||||||
|
const currentPersonId = personSelect.value;
|
||||||
|
populatePersonSelect('manager', currentPersonId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetRolePersonFilter() {
|
||||||
|
const roleSelect = document.getElementById('filter-person-role');
|
||||||
|
const personSelect = document.getElementById('filter-person-select');
|
||||||
|
if (roleSelect) roleSelect.value = '';
|
||||||
|
if (personSelect) {
|
||||||
|
const chooseLabel = typeof gettext === 'function' ? gettext("-- Choisir une personne… --") : "-- Choisir une personne… --";
|
||||||
|
personSelect.innerHTML = `<option value="">${chooseLabel}</option>`;
|
||||||
|
personSelect.disabled = true;
|
||||||
|
}
|
||||||
|
syncRolePersonHiddenInputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRolePersonFilter() {
|
||||||
|
const roleSelect = document.getElementById('filter-person-role');
|
||||||
|
const personSelect = document.getElementById('filter-person-select');
|
||||||
|
const clearBtn = document.getElementById('clear-role-person-filter');
|
||||||
|
|
||||||
|
if (!roleSelect || !personSelect) return;
|
||||||
|
|
||||||
|
roleSelect.addEventListener('change', function() {
|
||||||
|
populatePersonSelect(this.value);
|
||||||
});
|
});
|
||||||
container.innerHTML = html;
|
|
||||||
row.classList.remove('d-none');
|
personSelect.addEventListener('change', function() {
|
||||||
|
syncRolePersonHiddenInputs();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (clearBtn) {
|
||||||
|
clearBtn.addEventListener('click', function() {
|
||||||
|
resetRolePersonFilter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine initial role and person from server context or URL parameters
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
let initRole = typeof SELECTED_ROLE !== 'undefined' ? SELECTED_ROLE : '';
|
||||||
|
let initPersonId = typeof SELECTED_PERSON_ID !== 'undefined' ? SELECTED_PERSON_ID : '';
|
||||||
|
|
||||||
|
if (!initRole) {
|
||||||
|
if (urlParams.get('intervention_manager')) {
|
||||||
|
initRole = 'manager';
|
||||||
|
initPersonId = urlParams.get('intervention_manager');
|
||||||
|
} else if (urlParams.get('assigned_controller')) {
|
||||||
|
initRole = 'controller';
|
||||||
|
initPersonId = urlParams.get('assigned_controller');
|
||||||
|
} else if (urlParams.get('inspector')) {
|
||||||
|
initRole = 'inspector';
|
||||||
|
initPersonId = urlParams.get('inspector');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initRole) {
|
||||||
|
roleSelect.value = initRole;
|
||||||
|
populatePersonSelect(initRole, initPersonId);
|
||||||
} else {
|
} else {
|
||||||
container.innerHTML = '';
|
personSelect.disabled = true;
|
||||||
row.classList.add('d-none');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -770,21 +770,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Gestionnaires (dynamic) -->
|
|
||||||
<div class="row mt-3 d-none" id="intervention-manager-filter-row">
|
|
||||||
<div class="col-md-12 filter-section">
|
|
||||||
<div class="filter-section-header">
|
|
||||||
<label>{% translate "Gestionnaire d'intervention" %}</label>
|
|
||||||
<div class="filter-section-buttons">
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" data-field="intervention_manager">{% translate "Tous" %}</button>
|
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" data-field="intervention_manager">{% translate "Aucun" %}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="filter-options" data-field="intervention_manager" id="intervention-manager-options-container">
|
|
||||||
<!-- Dynamic options will be inserted here by JS -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Équipes / Membres assignés -->
|
<!-- Équipes / Membres assignés -->
|
||||||
|
|
@ -825,6 +810,42 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Rôle et Responsable / Intervenant assigné (Gestionnaire / Contrôleur / Inspecteur) -->
|
||||||
|
<div class="row mt-3 pt-3 border-top">
|
||||||
|
<div class="col-md-12 filter-section">
|
||||||
|
<div class="filter-section-header">
|
||||||
|
<label class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-person-badge me-2 text-primary"></i>{% translate "Rôle et personne assignée" %}
|
||||||
|
</label>
|
||||||
|
<div class="filter-section-buttons">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="clear-role-person-filter">
|
||||||
|
<i class="bi bi-x-circle me-1"></i>{% translate "Effacer" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-2 align-items-center mt-1">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label for="filter-person-role" class="form-label small text-muted mb-1">{% translate "Rôle" %}</label>
|
||||||
|
<select class="form-select form-select-sm" id="filter-person-role">
|
||||||
|
<option value="">{% translate "-- Sélectionner un rôle… --" %}</option>
|
||||||
|
<option value="manager" {% if selected_role == "manager" %}selected{% endif %}>{% translate "Gestionnaire" %}</option>
|
||||||
|
<option value="controller" {% if selected_role == "controller" %}selected{% endif %}>{% translate "Contrôleur" %}</option>
|
||||||
|
<option value="inspector" {% if selected_role == "inspector" %}selected{% endif %}>{% translate "Inspecteur" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label for="filter-person-select" class="form-label small text-muted mb-1">{% translate "Personne" %}</label>
|
||||||
|
<select class="form-select form-select-sm" id="filter-person-select" {% if not selected_role %}disabled{% endif %}>
|
||||||
|
<option value="">{% translate "-- Choisir une personne… --" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="intervention_manager" id="hidden-filter-intervention-manager" value="{% if selected_role == 'manager' %}{{ selected_person_id }}{% endif %}">
|
||||||
|
<input type="hidden" name="assigned_controller" id="hidden-filter-assigned-controller" value="{% if selected_role == 'controller' %}{{ selected_person_id }}{% endif %}">
|
||||||
|
<input type="hidden" name="inspector" id="hidden-filter-inspector" value="{% if selected_role == 'inspector' %}{{ selected_person_id }}{% endif %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Localisations (full width) -->
|
<!-- Localisations (full width) -->
|
||||||
<div class="filter-section mt-3 pt-3 border-top">
|
<div class="filter-section mt-3 pt-3 border-top">
|
||||||
<div class="filter-section-header">
|
<div class="filter-section-header">
|
||||||
|
|
@ -1466,8 +1487,12 @@
|
||||||
const SAVED_SORT = {{ saved_sort|default:"{}"|safe }};
|
const SAVED_SORT = {{ saved_sort|default:"{}"|safe }};
|
||||||
const SAVED_COLUMNS = {{ saved_columns|default:"null"|safe }};
|
const SAVED_COLUMNS = {{ saved_columns|default:"null"|safe }};
|
||||||
const subcatNoneLabel = "{% translate '(aucune sous-catégorie)' %}";
|
const subcatNoneLabel = "{% translate '(aucune sous-catégorie)' %}";
|
||||||
|
const SELECTED_ROLE = '{{ selected_role|default:""|escapejs }}';
|
||||||
|
const SELECTED_PERSON_ID = '{{ selected_person_id|default:""|escapejs }}';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{{ role_persons_data|json_script:"role-persons-data" }}
|
||||||
|
|
||||||
|
|
||||||
<script src="{% url 'javascript-catalog' %}"></script>
|
<script src="{% url 'javascript-catalog' %}"></script>
|
||||||
<script src="{% static 'common/base_script.js' %}"></script>
|
<script src="{% static 'common/base_script.js' %}"></script>
|
||||||
|
|
|
||||||
|
|
@ -2004,6 +2004,198 @@ class InterventionEndDateFilteringTests(TestCase):
|
||||||
self.assertIn(itv_reopened, apply_intervention_filters_from_querydict(qs, params_august, user=self.user))
|
self.assertIn(itv_reopened, apply_intervention_filters_from_querydict(qs, params_august, user=self.user))
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionRolePersonFilteringTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.User = get_user_model()
|
||||||
|
self.admin_user = self.User.objects.create_user(
|
||||||
|
username='admin_role_test',
|
||||||
|
password='pwd',
|
||||||
|
first_name='Jean',
|
||||||
|
last_name='Dupont'
|
||||||
|
)
|
||||||
|
self.role_admin, _ = Role.objects.get_or_create(name='admin')
|
||||||
|
self.config = UserConfig.objects.create(user=self.admin_user, is_intern=True)
|
||||||
|
self.config.roles.add(self.role_admin)
|
||||||
|
|
||||||
|
self.thematic = Thematic.objects.create(
|
||||||
|
code='thematic_role_test',
|
||||||
|
name_fr='Thématique Test',
|
||||||
|
name_nl='Thema Test',
|
||||||
|
allow_planning_without_contract=True
|
||||||
|
)
|
||||||
|
UserThematics.objects.create(
|
||||||
|
user_config=self.config,
|
||||||
|
thematic=self.thematic,
|
||||||
|
can_view_interventions=True,
|
||||||
|
can_edit_interventions=True,
|
||||||
|
can_inspect=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.company = Company.objects.create(name='Entreprise Test')
|
||||||
|
self.control_team = CompanyTeam.objects.create(
|
||||||
|
name='Équipe Contrôle Qualité',
|
||||||
|
company=self.company,
|
||||||
|
operation_types=[CompanyTeam.OPERATION_CONTROLS]
|
||||||
|
)
|
||||||
|
self.control_team.thematics.add(self.thematic)
|
||||||
|
|
||||||
|
self.controller_user = self.User.objects.create_user(
|
||||||
|
username='ctrl_user_test',
|
||||||
|
password='pwd',
|
||||||
|
first_name='Marc',
|
||||||
|
last_name='Lambert'
|
||||||
|
)
|
||||||
|
self.controller_member = CompanyMember.objects.create(
|
||||||
|
name='Marc Lambert',
|
||||||
|
user=self.controller_user
|
||||||
|
)
|
||||||
|
self.controller_member.teams.add(self.control_team)
|
||||||
|
|
||||||
|
self.inspector_user = self.User.objects.create_user(
|
||||||
|
username='insp_user_test',
|
||||||
|
password='pwd',
|
||||||
|
first_name='Claire',
|
||||||
|
last_name='Bernard'
|
||||||
|
)
|
||||||
|
self.insp_config = UserConfig.objects.create(user=self.inspector_user, is_intern=True)
|
||||||
|
UserThematics.objects.create(
|
||||||
|
user_config=self.insp_config,
|
||||||
|
thematic=self.thematic,
|
||||||
|
can_view_interventions=True,
|
||||||
|
can_inspect=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.manager_user = self.User.objects.create_user(
|
||||||
|
username='mgr_user_test',
|
||||||
|
password='pwd',
|
||||||
|
first_name='Paul',
|
||||||
|
last_name='Martin'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Interventions
|
||||||
|
self.itv_manager = Intervention.objects.create(
|
||||||
|
title='Intervention avec gestionnaire',
|
||||||
|
thematic=self.thematic,
|
||||||
|
created_by=self.admin_user,
|
||||||
|
intervention_manager=self.manager_user
|
||||||
|
)
|
||||||
|
self.itv_controller = Intervention.objects.create(
|
||||||
|
title='Intervention avec contrôleur',
|
||||||
|
thematic=self.thematic,
|
||||||
|
created_by=self.admin_user,
|
||||||
|
assigned_controller=self.controller_member
|
||||||
|
)
|
||||||
|
self.itv_inspection_checked = Intervention.objects.create(
|
||||||
|
title='Intervention avec inspection liée',
|
||||||
|
thematic=self.thematic,
|
||||||
|
created_by=self.admin_user,
|
||||||
|
)
|
||||||
|
self.itv_type_inspection = Intervention.objects.create(
|
||||||
|
title='Intervention type inspection',
|
||||||
|
thematic=self.thematic,
|
||||||
|
created_by=self.admin_user,
|
||||||
|
type='inspection',
|
||||||
|
assigned_member=CompanyMember.objects.create(
|
||||||
|
name='Claire Bernard',
|
||||||
|
user=self.inspector_user
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
from inspections.models import Inspection
|
||||||
|
self.inspection = Inspection.objects.create(
|
||||||
|
checked_intervention=self.itv_inspection_checked,
|
||||||
|
inspector=self.inspector_user,
|
||||||
|
asset_content_type=ContentType.objects.get_for_model(Intervention),
|
||||||
|
asset_object_id=self.itv_inspection_checked.id
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_format_shortname_helpers(self):
|
||||||
|
from interventions.views.helpers import _format_user_shortname_with_team, _format_member_shortname_with_team
|
||||||
|
|
||||||
|
# User with team (via CompanyMember)
|
||||||
|
formatted_ctrl_user = _format_user_shortname_with_team(self.controller_user)
|
||||||
|
self.assertIn("Marc L.", formatted_ctrl_user)
|
||||||
|
self.assertIn("Équipe Contrôle Qualité", formatted_ctrl_user)
|
||||||
|
|
||||||
|
# Member with team
|
||||||
|
formatted_member = _format_member_shortname_with_team(self.controller_member)
|
||||||
|
self.assertEqual(formatted_member, "Marc L. (Équipe Contrôle Qualité)")
|
||||||
|
|
||||||
|
# User without team
|
||||||
|
formatted_mgr = _format_user_shortname_with_team(self.manager_user)
|
||||||
|
self.assertEqual(formatted_mgr, "Paul M.")
|
||||||
|
|
||||||
|
def test_interventions_index_role_person_context(self):
|
||||||
|
self.client.login(username='admin_role_test', password='pwd')
|
||||||
|
url = reverse('interventions:interventions_index')
|
||||||
|
|
||||||
|
# 1. No filter (follows default redirect)
|
||||||
|
resp = self.client.get(url, follow=True)
|
||||||
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
self.assertIn('role_persons_data', resp.context)
|
||||||
|
self.assertEqual(resp.context['selected_role'], '')
|
||||||
|
self.assertEqual(resp.context['selected_person_id'], '')
|
||||||
|
|
||||||
|
# 2. Filter by manager
|
||||||
|
resp_mgr = self.client.get(f"{url}?intervention_manager={self.manager_user.id}")
|
||||||
|
self.assertEqual(resp_mgr.status_code, 200)
|
||||||
|
self.assertEqual(resp_mgr.context['selected_role'], 'manager')
|
||||||
|
self.assertEqual(resp_mgr.context['selected_person_id'], str(self.manager_user.id))
|
||||||
|
|
||||||
|
# 3. Filter by controller
|
||||||
|
resp_ctrl = self.client.get(f"{url}?assigned_controller={self.controller_member.id}")
|
||||||
|
self.assertEqual(resp_ctrl.status_code, 200)
|
||||||
|
self.assertEqual(resp_ctrl.context['selected_role'], 'controller')
|
||||||
|
self.assertEqual(resp_ctrl.context['selected_person_id'], str(self.controller_member.id))
|
||||||
|
|
||||||
|
# 4. Filter by inspector
|
||||||
|
resp_insp = self.client.get(f"{url}?inspector={self.inspector_user.id}")
|
||||||
|
self.assertEqual(resp_insp.status_code, 200)
|
||||||
|
self.assertEqual(resp_insp.context['selected_role'], 'inspector')
|
||||||
|
self.assertEqual(resp_insp.context['selected_person_id'], str(self.inspector_user.id))
|
||||||
|
|
||||||
|
def test_filter_by_assigned_controller_and_inspector(self):
|
||||||
|
from django.http import QueryDict
|
||||||
|
from interventions.views.helpers import apply_intervention_filters_from_querydict
|
||||||
|
|
||||||
|
qs = Intervention.objects.all()
|
||||||
|
|
||||||
|
# Filter by controller via apply_intervention_filters_from_querydict
|
||||||
|
params_ctrl = QueryDict(f'assigned_controller={self.controller_member.id}')
|
||||||
|
filtered_ctrl = list(apply_intervention_filters_from_querydict(qs, params_ctrl, user=self.admin_user))
|
||||||
|
self.assertIn(self.itv_controller, filtered_ctrl)
|
||||||
|
self.assertNotIn(self.itv_manager, filtered_ctrl)
|
||||||
|
|
||||||
|
# Filter by inspector via apply_intervention_filters_from_querydict
|
||||||
|
params_insp = QueryDict(f'inspector={self.inspector_user.id}')
|
||||||
|
filtered_insp = list(apply_intervention_filters_from_querydict(qs, params_insp, user=self.admin_user))
|
||||||
|
self.assertIn(self.itv_inspection_checked, filtered_insp)
|
||||||
|
self.assertIn(self.itv_type_inspection, filtered_insp)
|
||||||
|
self.assertNotIn(self.itv_controller, filtered_insp)
|
||||||
|
self.assertNotIn(self.itv_manager, filtered_insp)
|
||||||
|
|
||||||
|
# Test via interventions_geojson
|
||||||
|
self.client.login(username='admin_role_test', password='pwd')
|
||||||
|
geojson_url = reverse('interventions:interventions_geojson')
|
||||||
|
|
||||||
|
# GeoJSON: controller
|
||||||
|
resp_ctrl = self.client.get(f"{geojson_url}?assigned_controller={self.controller_member.id}")
|
||||||
|
self.assertEqual(resp_ctrl.status_code, 200)
|
||||||
|
features_ctrl = resp_ctrl.json().get('features', [])
|
||||||
|
itv_ids_ctrl = [f['properties']['id'] for f in features_ctrl]
|
||||||
|
self.assertIn(self.itv_controller.id, itv_ids_ctrl)
|
||||||
|
self.assertNotIn(self.itv_manager.id, itv_ids_ctrl)
|
||||||
|
|
||||||
|
# GeoJSON: inspector
|
||||||
|
resp_insp = self.client.get(f"{geojson_url}?inspector={self.inspector_user.id}")
|
||||||
|
self.assertEqual(resp_insp.status_code, 200)
|
||||||
|
features_insp = resp_insp.json().get('features', [])
|
||||||
|
itv_ids_insp = [f['properties']['id'] for f in features_insp]
|
||||||
|
self.assertIn(self.itv_inspection_checked.id, itv_ids_insp)
|
||||||
|
self.assertIn(self.itv_type_inspection.id, itv_ids_insp)
|
||||||
|
self.assertNotIn(self.itv_controller.id, itv_ids_insp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ def index(request):
|
||||||
for mgr in managers_qs:
|
for mgr in managers_qs:
|
||||||
managers_data.append({
|
managers_data.append({
|
||||||
'id': mgr.id,
|
'id': mgr.id,
|
||||||
'name': _get_user_short_name(mgr)
|
'name': _format_user_shortname_with_team(mgr)
|
||||||
})
|
})
|
||||||
managers_json = json.dumps(managers_data)
|
managers_json = json.dumps(managers_data)
|
||||||
|
|
||||||
|
|
@ -356,6 +356,103 @@ def index(request):
|
||||||
list(m_thematics),
|
list(m_thematics),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# Build role-persons data (Gestionnaire / Contrôleur / Inspecteur)
|
||||||
|
# 1. Managers
|
||||||
|
user_access_map = defaultdict(set)
|
||||||
|
for uca in UserContractAccess.objects.filter(contract__in=contracts_qs, can_approve=True):
|
||||||
|
user_access_map[uca.user_config.user_id].add(uca.contract_id)
|
||||||
|
managed_itv_map = defaultdict(set)
|
||||||
|
for itv_data in Intervention.objects.filter(contract__in=contracts_qs, intervention_manager__isnull=False).values('intervention_manager_id', 'contract_id').distinct():
|
||||||
|
managed_itv_map[itv_data['intervention_manager_id']].add(itv_data['contract_id'])
|
||||||
|
|
||||||
|
manager_users_qs = (
|
||||||
|
User.objects.filter(
|
||||||
|
Q(config__usercontractaccess__contract__in=contracts_qs, config__usercontractaccess__can_approve=True) |
|
||||||
|
Q(managed_interventions__contract__in=contracts_qs) |
|
||||||
|
Q(config__roles__name='manager')
|
||||||
|
)
|
||||||
|
.prefetch_related('companymember_set__teams', 'config__company')
|
||||||
|
.distinct()
|
||||||
|
.order_by('first_name', 'last_name', 'username')
|
||||||
|
)
|
||||||
|
manager_items = []
|
||||||
|
for mgr in manager_users_qs:
|
||||||
|
c_ids = list(user_access_map[mgr.id] | managed_itv_map[mgr.id])
|
||||||
|
manager_items.append({
|
||||||
|
'id': mgr.id,
|
||||||
|
'name': _format_user_shortname_with_team(mgr),
|
||||||
|
'contracts': c_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 2. Controllers
|
||||||
|
controller_members_qs = (
|
||||||
|
CompanyMember.objects.filter(
|
||||||
|
Q(teams__operation_types__contains=[CompanyTeam.OPERATION_CONTROLS]) |
|
||||||
|
Q(controlled_interventions__isnull=False)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if allowed_company_ids is not None:
|
||||||
|
controller_members_qs = controller_members_qs.filter(teams__company_id__in=allowed_company_ids)
|
||||||
|
if allowed_thematic_ids:
|
||||||
|
controller_members_qs = controller_members_qs.filter(teams__thematics__id__in=allowed_thematic_ids)
|
||||||
|
controller_members_qs = (
|
||||||
|
controller_members_qs
|
||||||
|
.select_related('user')
|
||||||
|
.prefetch_related('teams__company')
|
||||||
|
.distinct()
|
||||||
|
.order_by('name')
|
||||||
|
)
|
||||||
|
controller_items = [
|
||||||
|
{
|
||||||
|
'id': m.id,
|
||||||
|
'name': _format_member_shortname_with_team(m),
|
||||||
|
}
|
||||||
|
for m in controller_members_qs
|
||||||
|
]
|
||||||
|
|
||||||
|
# 3. Inspectors
|
||||||
|
from inspections.models import Inspection
|
||||||
|
inspector_users_qs = (
|
||||||
|
User.objects.filter(
|
||||||
|
Q(config__userthematics__can_inspect=True) |
|
||||||
|
Q(inspections__isnull=False) |
|
||||||
|
Q(companymember__teams__operation_types__contains=[CompanyTeam.OPERATION_INSPECTIONS]) |
|
||||||
|
Q(companymember__assigned_inspections__isnull=False)
|
||||||
|
)
|
||||||
|
.prefetch_related('companymember_set__teams', 'config__company')
|
||||||
|
.distinct()
|
||||||
|
.order_by('first_name', 'last_name', 'username')
|
||||||
|
)
|
||||||
|
inspector_items = [
|
||||||
|
{
|
||||||
|
'id': u.id,
|
||||||
|
'name': _format_user_shortname_with_team(u),
|
||||||
|
}
|
||||||
|
for u in inspector_users_qs
|
||||||
|
]
|
||||||
|
|
||||||
|
role_persons_data = {
|
||||||
|
'manager': manager_items,
|
||||||
|
'controller': controller_items,
|
||||||
|
'inspector': inspector_items,
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_managers = _expand_csv_params(request.GET.getlist('intervention_manager'))
|
||||||
|
selected_controllers = _expand_csv_params(request.GET.getlist('assigned_controller'))
|
||||||
|
selected_inspectors = _expand_csv_params(request.GET.getlist('inspector'))
|
||||||
|
|
||||||
|
selected_role = ''
|
||||||
|
selected_person_id = ''
|
||||||
|
if selected_managers:
|
||||||
|
selected_role = 'manager'
|
||||||
|
selected_person_id = str(selected_managers[0])
|
||||||
|
elif selected_controllers:
|
||||||
|
selected_role = 'controller'
|
||||||
|
selected_person_id = str(selected_controllers[0])
|
||||||
|
elif selected_inspectors:
|
||||||
|
selected_role = 'inspector'
|
||||||
|
selected_person_id = str(selected_inspectors[0])
|
||||||
|
|
||||||
# Status classes (Bootstrap contextual classes)
|
# Status classes (Bootstrap contextual classes)
|
||||||
status_classes = {
|
status_classes = {
|
||||||
'in_preparation': 'secondary',
|
'in_preparation': 'secondary',
|
||||||
|
|
@ -415,7 +512,12 @@ def index(request):
|
||||||
'selected_teams': request.GET.getlist('assigned_team'),
|
'selected_teams': request.GET.getlist('assigned_team'),
|
||||||
'selected_members': request.GET.getlist('assigned_member'),
|
'selected_members': request.GET.getlist('assigned_member'),
|
||||||
'selected_control_teams': request.GET.getlist('assigned_control_team'),
|
'selected_control_teams': request.GET.getlist('assigned_control_team'),
|
||||||
'selected_controllers': request.GET.getlist('assigned_controller'),
|
'selected_controllers': selected_controllers,
|
||||||
|
'selected_managers': selected_managers,
|
||||||
|
'selected_inspectors': selected_inspectors,
|
||||||
|
'selected_role': selected_role,
|
||||||
|
'selected_person_id': selected_person_id,
|
||||||
|
'role_persons_data': role_persons_data,
|
||||||
'selected_pause_reasons': pause_reasons,
|
'selected_pause_reasons': pause_reasons,
|
||||||
'selected_preparation_states': preparation_states,
|
'selected_preparation_states': preparation_states,
|
||||||
'begin_date_min': begin_date_min or '',
|
'begin_date_min': begin_date_min or '',
|
||||||
|
|
@ -471,6 +573,7 @@ def interventions_geojson(request):
|
||||||
assigned_controllers = _expand_csv_params(request.GET.getlist('assigned_controller'))
|
assigned_controllers = _expand_csv_params(request.GET.getlist('assigned_controller'))
|
||||||
pause_reasons = _expand_csv_params(request.GET.getlist('pause_reason'))
|
pause_reasons = _expand_csv_params(request.GET.getlist('pause_reason'))
|
||||||
intervention_managers = _expand_csv_params(request.GET.getlist('intervention_manager'))
|
intervention_managers = _expand_csv_params(request.GET.getlist('intervention_manager'))
|
||||||
|
inspectors = _expand_csv_params(request.GET.getlist('inspector'))
|
||||||
preparation_states = _expand_csv_params(request.GET.getlist('preparation_state'))
|
preparation_states = _expand_csv_params(request.GET.getlist('preparation_state'))
|
||||||
begin_date_min = request.GET.get('begin_date_min') or None
|
begin_date_min = request.GET.get('begin_date_min') or None
|
||||||
begin_date_max = request.GET.get('begin_date_max') or None
|
begin_date_max = request.GET.get('begin_date_max') or None
|
||||||
|
|
@ -555,6 +658,21 @@ def interventions_geojson(request):
|
||||||
qs = qs.filter(Q(assigned_control_team_id__in=assigned_control_teams) | Q(assigned_controller__teams__in=assigned_control_teams)).distinct()
|
qs = qs.filter(Q(assigned_control_team_id__in=assigned_control_teams) | Q(assigned_controller__teams__in=assigned_control_teams)).distinct()
|
||||||
if intervention_managers:
|
if intervention_managers:
|
||||||
qs = qs.filter(intervention_manager_id__in=intervention_managers)
|
qs = qs.filter(intervention_manager_id__in=intervention_managers)
|
||||||
|
if inspectors:
|
||||||
|
from inspections.models import Inspection
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
ct = ContentType.objects.get_for_model(Intervention)
|
||||||
|
insp_target_qs = Inspection.objects.filter(
|
||||||
|
Q(inspector_id__in=inspectors) |
|
||||||
|
Q(assigned_member_id__in=inspectors) |
|
||||||
|
Q(assigned_member__user_id__in=inspectors)
|
||||||
|
)
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(quality_inspections__in=insp_target_qs) |
|
||||||
|
Q(generated_inspections__in=insp_target_qs) |
|
||||||
|
Q(id__in=insp_target_qs.filter(asset_content_type=ct).values('asset_object_id')) |
|
||||||
|
(Q(type='inspection') & (Q(assigned_member_id__in=inspectors) | Q(assigned_member__user_id__in=inspectors)))
|
||||||
|
).distinct()
|
||||||
if followed_only == '1' and request.user.is_authenticated:
|
if followed_only == '1' and request.user.is_authenticated:
|
||||||
qs = qs.filter(subscriptions__user=request.user)
|
qs = qs.filter(subscriptions__user=request.user)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,8 @@ __all__ = [
|
||||||
'_flatten_asset_collection',
|
'_flatten_asset_collection',
|
||||||
'_force_delete_operations_for_assets',
|
'_force_delete_operations_for_assets',
|
||||||
'_format_location_label',
|
'_format_location_label',
|
||||||
|
'_format_member_shortname_with_team',
|
||||||
|
'_format_user_shortname_with_team',
|
||||||
'_get_allowed_asset_category_ids',
|
'_get_allowed_asset_category_ids',
|
||||||
'_get_asset_classes',
|
'_get_asset_classes',
|
||||||
'_get_asset_model_classes',
|
'_get_asset_model_classes',
|
||||||
|
|
@ -1765,7 +1767,10 @@ def apply_intervention_filters_from_querydict(qs, params, user=None):
|
||||||
contracts = _expand_csv_params(params.getlist('contract'))
|
contracts = _expand_csv_params(params.getlist('contract'))
|
||||||
assigned_teams = _expand_csv_params(params.getlist('assigned_team'))
|
assigned_teams = _expand_csv_params(params.getlist('assigned_team'))
|
||||||
assigned_members = _expand_csv_params(params.getlist('assigned_member'))
|
assigned_members = _expand_csv_params(params.getlist('assigned_member'))
|
||||||
|
assigned_control_teams = _expand_csv_params(params.getlist('assigned_control_team'))
|
||||||
|
assigned_controllers = _expand_csv_params(params.getlist('assigned_controller'))
|
||||||
intervention_managers = _expand_csv_params(params.getlist('intervention_manager'))
|
intervention_managers = _expand_csv_params(params.getlist('intervention_manager'))
|
||||||
|
inspectors = _expand_csv_params(params.getlist('inspector'))
|
||||||
pause_reasons = _expand_csv_params(params.getlist('pause_reason'))
|
pause_reasons = _expand_csv_params(params.getlist('pause_reason'))
|
||||||
preparation_states = _expand_csv_params(params.getlist('preparation_state'))
|
preparation_states = _expand_csv_params(params.getlist('preparation_state'))
|
||||||
begin_date_min = params.get('begin_date_min') or None
|
begin_date_min = params.get('begin_date_min') or None
|
||||||
|
|
@ -1812,8 +1817,27 @@ def apply_intervention_filters_from_querydict(qs, params, user=None):
|
||||||
qs = qs.filter(assigned_member_id__in=assigned_members)
|
qs = qs.filter(assigned_member_id__in=assigned_members)
|
||||||
if assigned_teams:
|
if assigned_teams:
|
||||||
qs = qs.filter(Q(assigned_team_id__in=assigned_teams) | Q(assigned_member__teams__in=assigned_teams)).distinct()
|
qs = qs.filter(Q(assigned_team_id__in=assigned_teams) | Q(assigned_member__teams__in=assigned_teams)).distinct()
|
||||||
|
if assigned_controllers:
|
||||||
|
qs = qs.filter(assigned_controller_id__in=assigned_controllers)
|
||||||
|
if assigned_control_teams:
|
||||||
|
qs = qs.filter(Q(assigned_control_team_id__in=assigned_control_teams) | Q(assigned_controller__teams__in=assigned_control_teams)).distinct()
|
||||||
if intervention_managers:
|
if intervention_managers:
|
||||||
qs = qs.filter(intervention_manager_id__in=intervention_managers)
|
qs = qs.filter(intervention_manager_id__in=intervention_managers)
|
||||||
|
if inspectors:
|
||||||
|
from inspections.models import Inspection
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
ct = ContentType.objects.get_for_model(Intervention)
|
||||||
|
insp_target_qs = Inspection.objects.filter(
|
||||||
|
Q(inspector_id__in=inspectors) |
|
||||||
|
Q(assigned_member_id__in=inspectors) |
|
||||||
|
Q(assigned_member__user_id__in=inspectors)
|
||||||
|
)
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(quality_inspections__in=insp_target_qs) |
|
||||||
|
Q(generated_inspections__in=insp_target_qs) |
|
||||||
|
Q(id__in=insp_target_qs.filter(asset_content_type=ct).values('asset_object_id')) |
|
||||||
|
(Q(type='inspection') & (Q(assigned_member_id__in=inspectors) | Q(assigned_member__user_id__in=inspectors)))
|
||||||
|
).distinct()
|
||||||
if followed_only == '1' and user and user.is_authenticated:
|
if followed_only == '1' and user and user.is_authenticated:
|
||||||
qs = qs.filter(subscriptions__user=user)
|
qs = qs.filter(subscriptions__user=user)
|
||||||
|
|
||||||
|
|
@ -2387,6 +2411,54 @@ def _get_user_short_name(user):
|
||||||
return display
|
return display
|
||||||
|
|
||||||
|
|
||||||
|
def _format_user_shortname_with_team(user):
|
||||||
|
"""
|
||||||
|
Format a User's short name with team/company in parentheses.
|
||||||
|
E.g., "Prénom N. (Nom Équipe)" or "Prénom N."
|
||||||
|
"""
|
||||||
|
if not user:
|
||||||
|
return ""
|
||||||
|
short_name = _get_user_short_name(user)
|
||||||
|
teams = [
|
||||||
|
t.name
|
||||||
|
for cm in user.companymember_set.all()
|
||||||
|
for t in cm.teams.all()
|
||||||
|
if t.name
|
||||||
|
]
|
||||||
|
if teams:
|
||||||
|
team_str = ", ".join(dict.fromkeys(teams))
|
||||||
|
elif hasattr(user, 'config') and user.config and user.config.company:
|
||||||
|
team_str = user.config.company.name
|
||||||
|
else:
|
||||||
|
team_str = ""
|
||||||
|
return f"{short_name} ({team_str})" if team_str else short_name
|
||||||
|
|
||||||
|
|
||||||
|
def _format_member_shortname_with_team(member):
|
||||||
|
"""
|
||||||
|
Format a CompanyMember's short name with team in parentheses.
|
||||||
|
E.g., "Prénom N. (Nom Équipe)" or "Prénom N."
|
||||||
|
"""
|
||||||
|
if not member:
|
||||||
|
return ""
|
||||||
|
if member.user and (member.user.first_name or member.user.last_name):
|
||||||
|
short_name = _get_user_short_name(member.user)
|
||||||
|
elif member.name:
|
||||||
|
parts = member.name.strip().split()
|
||||||
|
if len(parts) >= 2 and parts[-1].isalpha():
|
||||||
|
short_name = f"{parts[0]} {parts[-1][0].upper()}."
|
||||||
|
else:
|
||||||
|
short_name = member.name
|
||||||
|
elif member.user:
|
||||||
|
short_name = getattr(member.user, 'username', '')
|
||||||
|
else:
|
||||||
|
short_name = ""
|
||||||
|
|
||||||
|
teams = [t.name for t in member.teams.all() if t.name]
|
||||||
|
team_str = ", ".join(dict.fromkeys(teams)) if teams else ""
|
||||||
|
return f"{short_name} ({team_str})" if team_str else short_name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _get_available_occupation_members(user, intervention):
|
def _get_available_occupation_members(user, intervention):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue