diff --git a/streetup/common/apps.py b/streetup/common/apps.py index fc9a8bd..be8d2e7 100644 --- a/streetup/common/apps.py +++ b/streetup/common/apps.py @@ -10,3 +10,9 @@ class CommonConfig(AppConfig): from django.contrib.auth.signals import user_logged_in from .middleware import set_login_time user_logged_in.connect(set_login_time) + + # Charger les extensions conditionnelles + try: + from common import bm_extensions + except ImportError: + pass diff --git a/streetup/common/hooks.py b/streetup/common/hooks.py new file mode 100644 index 0000000..983ae00 --- /dev/null +++ b/streetup/common/hooks.py @@ -0,0 +1,106 @@ +from django.db.models import Q + +class ExtensionHooks: + def __init__(self): + self._form_cleaners = [] + self._search_annotators = [] + self._search_filters = [] + self._pdf_fields_extractors = [] + self._export_headers = [] + self._export_row_extractors = [] + self._custom_allowed_fields = [] + self._custom_export_fields = [] + self._inline_validators = [] + self._custom_source_ref_value_extractor = None + + def register_custom_source_ref_value_extractor(self, func): + self._custom_source_ref_value_extractor = func + return func + + def get_custom_source_ref_value(self, intervention): + if self._custom_source_ref_value_extractor: + return self._custom_source_ref_value_extractor(intervention) + return None + + def register_custom_export_field(self, label, extractor): + self._custom_export_fields.append((label, extractor)) + return (label, extractor) + + def get_custom_export_fields(self): + return self._custom_export_fields + + def register_inline_validator(self, func): + self._inline_validators.append(func) + return func + + def run_inline_edit_validator(self, field, new_value, intervention): + for validator in self._inline_validators: + res = validator(field, new_value, intervention) + if res[0] is not None: + return res + return (None, None) + + def register_form_cleaner(self, func): + self._form_cleaners.append(func) + return func + + def register_search_annotator(self, func): + self._search_annotators.append(func) + return func + + def register_search_filter(self, func): + self._search_filters.append(func) + return func + + def register_pdf_fields_extractor(self, func): + self._pdf_fields_extractors.append(func) + return func + + def register_export_headers(self, headers_list): + self._export_headers.extend(headers_list) + + def register_export_row_extractor(self, func): + self._export_row_extractors.append(func) + return func + + def register_custom_allowed_field(self, field_name_or_list): + if isinstance(field_name_or_list, list): + self._custom_allowed_fields.extend(field_name_or_list) + else: + self._custom_allowed_fields.append(field_name_or_list) + + def run_form_cleaners(self, form, cleaned_data): + for cleaner in self._form_cleaners: + cleaned_data = cleaner(form, cleaned_data) + return cleaned_data + + def apply_search_annotations(self, queryset): + for annotator in self._search_annotators: + queryset = annotator(queryset) + return queryset + + def get_search_filters(self, word): + q_obj = Q() + for filter_func in self._search_filters: + q_obj |= filter_func(word) + return q_obj + + def extract_pdf_fields(self, intervention): + fields = [] + for extractor in self._pdf_fields_extractors: + fields.extend(extractor(intervention)) + return fields + + def get_export_headers(self): + return self._export_headers + + def extract_export_row(self, intervention): + row_data = [] + for extractor in self._export_row_extractors: + row_data.extend(extractor(intervention)) + return row_data + + def get_custom_allowed_fields(self): + return self._custom_allowed_fields + +hooks = ExtensionHooks() diff --git a/streetup/contracts/views_daily_report.py b/streetup/contracts/views_daily_report.py index e2af144..8bfd9a3 100644 --- a/streetup/contracts/views_daily_report.py +++ b/streetup/contracts/views_daily_report.py @@ -106,10 +106,13 @@ def _collect_report_data(companies, report_date): # Colonnes COL_KEYS = [ 'damage', 'defect', 'demand', - 'observation', 'prego', - 'finished', 'other_status', - 'total', + 'observation', ] + dynamic_cols = getattr(settings, 'DAILY_REPORT_SOURCE_COLUMNS', {}) + for key in dynamic_cols.keys(): + if key not in COL_KEYS: + COL_KEYS.append(key) + COL_KEYS.extend(['finished', 'other_status', 'total']) by_thematic = defaultdict(lambda: {k: 0 for k in COL_KEYS}) by_person = defaultdict(int) @@ -142,9 +145,9 @@ def _collect_report_data(companies, report_date): if sc_name == 'observation': row['observation'] += 1 totals['observation'] += 1 - elif sc_name == 'prego': - row['prego'] += 1 - totals['prego'] += 1 + elif sc_name in dynamic_cols: + row[sc_name] += 1 + totals[sc_name] += 1 # --- Statut --- if itv.status_order >= STATUS_ORDERS['finished']: @@ -179,26 +182,37 @@ COLUMN_HEADERS = [ ('defect', 'Défaut/Panne'), ('demand', 'Demande'), ('observation', 'StreetUp'), - ('prego', 'Issu de Prego'), +] + +# Add dynamic source columns from settings +dynamic_cols = getattr(settings, 'DAILY_REPORT_SOURCE_COLUMNS', {}) +for key, cfg in dynamic_cols.items(): + COLUMN_HEADERS.append((key, cfg['label'])) + +COLUMN_HEADERS.extend([ ('finished', 'Résolu'), ('other_status', 'En cours'), ('total', 'Total'), -] +]) # Couleurs spéciales (R, G, B) pour l'en-tête et la ligne totale de certaines colonnes # Les colonnes absentes de ce dict héritent de la couleur par défaut. COLUMN_SPECIAL_COLORS_RGB = { - 'prego': (255, 102, 0), # Orange 'finished': (112, 173, 71), # Vert 'other_status': ( 0, 176, 240), # Bleu ciel } # Version hex pour Excel (AARRGGBB sans alpha, format RRGGBB) COLUMN_SPECIAL_COLORS_HEX = { - 'prego': 'FF6600', 'finished': '70AD47', 'other_status': '00B0F0', } +for key, cfg in dynamic_cols.items(): + if 'color_rgb' in cfg: + COLUMN_SPECIAL_COLORS_RGB[key] = cfg['color_rgb'] + if 'color_hex' in cfg: + COLUMN_SPECIAL_COLORS_HEX[key] = cfg['color_hex'] + # --------------------------------------------------------------------------- # PDF generation diff --git a/streetup/interventions/models.py b/streetup/interventions/models.py index 58cd002..d3678c7 100644 --- a/streetup/interventions/models.py +++ b/streetup/interventions/models.py @@ -353,8 +353,6 @@ class Intervention(models.Model): source_category = models.ForeignKey(SourceCategory, on_delete=models.SET_NULL, null=True, blank=True) source_detail = models.CharField(null=True,blank=True) source_ref = models.CharField(null=True,blank=True) - origin_prego_nbr = models.IntegerField(null=True, blank=True) - origin_fms_nbr = models.IntegerField(null=True, blank=True) pmo_number = models.IntegerField(null=True, blank=True, verbose_name=_("Numéro PMO")) asset_category = models.ForeignKey(AssetCategory, on_delete=models.SET_NULL, null=True, blank=True) symptom = models.ForeignKey(Symptom, on_delete=models.SET_NULL, null=True, blank=True) diff --git a/streetup/interventions/permissions.py b/streetup/interventions/permissions.py index cc273d6..9b25eb5 100644 --- a/streetup/interventions/permissions.py +++ b/streetup/interventions/permissions.py @@ -427,7 +427,9 @@ def get_allowed_update_fields(user, intervention): # Source, Référence, Détail, FMS, Type d'origine et Symptôme if intervention.status_order < STATUS_ORDERS.get('to_be_processed') and user_config.is_intern: - allowed_fields.update(['source_category', 'source_ref', 'source_detail', 'origin_fms_nbr', 'origin_type', 'symptom']) + allowed_fields.update(['source_category', 'source_ref', 'source_detail', 'origin_type', 'symptom']) + from common.hooks import hooks + allowed_fields.update(hooks.get_custom_allowed_fields()) # Priorité : modifiable par les internes tant que le statut n'est pas 'démarré' (in_progress) if intervention.status_order < STATUS_ORDERS.get('in_progress') and user_config.is_intern: diff --git a/streetup/interventions/services/export_service.py b/streetup/interventions/services/export_service.py index d2f3293..22596ae 100644 --- a/streetup/interventions/services/export_service.py +++ b/streetup/interventions/services/export_service.py @@ -108,48 +108,55 @@ def generate_interventions_excel(start_date=None, end_date=None, output_format=' ws = wb.active ws.title = "Interventions" - # Définir les en-têtes - headers = [ - 'Code', - 'Titre', - 'Date de création', - 'Date de transmission', - 'Date début prévue', - 'Date fin prévue', - 'Date début planifiée', - 'Date fin planifiée', - 'Date de début', - 'Date de fin', - 'Date appel initial confirmé', - 'Type d\'origine', - 'Priorité', - 'Statut', - 'Symptôme', - 'Contrat', - 'Source catégorie', - 'Numéro PREGO', - 'Numéro FMS', - 'Documents joints', - 'Notes', - 'Créé par', - 'Équipe (créateur)', - 'Thématique (code)', - 'Thématique (nom)', - 'Commune', - 'Localisation code 1', - 'Localisation label 1', - 'Localisation code 2', - 'Localisation label 2', - 'Localisation code 3', - 'Localisation label 3', - 'Localisation code 4', - 'Localisation label 4', - 'Catégorie (code)', - 'Catégorie (nom)', - 'Modèle (code)', - 'Modèle (nom)', + # Définir les en-têtes et les largeurs de colonne de manière dynamique + column_definitions = [ + ('Code', 15), + ('Titre', 30), + ('Date de création', 20), + ('Date de transmission', 20), + ('Date début prévue', 20), + ('Date fin prévue', 20), + ('Date début planifiée', 20), + ('Date fin planifiée', 20), + ('Date de début', 20), + ('Date de fin', 20), + ('Date appel initial confirmé', 20), + ('Type d\'origine', 15), + ('Priorité', 12), + ('Statut', 15), + ('Symptôme', 30), + ('Contrat', 30), + ('Source catégorie', 20), ] + from common.hooks import hooks + for header in hooks.get_export_headers(): + column_definitions.append((header, 15)) + + column_definitions.extend([ + ('Documents joints', 50), + ('Notes', 50), + ('Créé par', 20), + ('Équipe (créateur)', 25), + ('Thématique (code)', 15), + ('Thématique (nom)', 25), + ('Commune', 20), + ('Localisation code 1', 15), + ('Localisation label 1', 25), + ('Localisation code 2', 15), + ('Localisation label 2', 25), + ('Localisation code 3', 15), + ('Localisation label 3', 25), + ('Localisation code 4', 15), + ('Localisation label 4', 25), + ('Equipement code', 15), + ('Equipement nom', 25), + ('Modèle code', 15), + ('Modèle nom', 30), + ]) + + headers = [col[0] for col in column_definitions] + # Écrire les en-têtes avec style for col_num, header in enumerate(headers, 1): cell = ws.cell(row=1, column=col_num) @@ -159,49 +166,10 @@ def generate_interventions_excel(start_date=None, end_date=None, output_format=' cell.alignment = Alignment(horizontal="center", vertical="center") # Ajuster la largeur des colonnes - column_widths = { - 'A': 15, # Code - 'B': 40, # Titre - 'C': 20, # Date création - 'D': 20, # Date transmission - 'E': 20, # Date début prévue - 'F': 20, # Date fin prévue - 'G': 20, # Date début planifiée - 'H': 20, # Date fin planifiée - 'I': 20, # Date début - 'J': 20, # Date fin - 'K': 20, # Date appel initial confirmé - 'L': 15, # Type origine - 'M': 12, # Priorité - 'N': 15, # Statut - 'O': 30, # Symptôme - 'P': 30, # Contrat - 'Q': 20, # Source catégorie - 'R': 15, # PREGO - 'S': 15, # FMS - 'T': 50, # Documents joints - 'U': 50, # Notes - 'V': 20, # Créé par - 'W': 25, # Équipe (créateur) - 'X': 15, # Thématique code - 'Y': 25, # Thématique nom - 'Z': 20, # Commune - 'AA': 15, # Loc code 1 - 'AB': 25, # Loc label 1 - 'AC': 15, # Loc code 2 - 'AD': 25, # Loc label 2 - 'AE': 15, # Loc code 3 - 'AF': 25, # Loc label 3 - 'AG': 15, # Loc code 4 - 'AH': 25, # Loc label 4 - 'AI': 15, # Cat code - 'AJ': 25, # Cat nom - 'AK': 15, # Model code - 'AL': 30, # Model nom - } - - for col, width in column_widths.items(): - ws.column_dimensions[col].width = width + from openpyxl.utils import get_column_letter + for i, col_def in enumerate(column_definitions, start=1): + col_letter = get_column_letter(i) + ws.column_dimensions[col_letter].width = col_def[1] # Forcer la langue française pour l'export with override('fr'): @@ -348,8 +316,7 @@ def generate_interventions_excel(start_date=None, end_date=None, output_format=' symptom_name, contrat_info, source_category_name, - intervention.origin_prego_nbr or '', - intervention.origin_fms_nbr or '', + *hooks.extract_export_row(intervention), documents_str, notes_str, created_by, diff --git a/streetup/interventions/static/interventions/add/source.js b/streetup/interventions/static/interventions/add/source.js index 9703d77..280bcf1 100644 --- a/streetup/interventions/static/interventions/add/source.js +++ b/streetup/interventions/static/interventions/add/source.js @@ -1,147 +1,54 @@ document.addEventListener('DOMContentLoaded', function () { const sourceCategorySelect = document.getElementById('id_source_category'); const sourceDetailContainer = document.getElementById('source-detail-container'); - const sourceRefContainer = document.getElementById('source-ref-container'); // PREGO - const sourceRefGenericContainer = document.getElementById('source-ref-generic-container'); // OBSERVATION - const sourceRefInput = document.getElementById('id_source_ref'); - const noFmsCheckbox = document.getElementById('id_no_origin_fms'); - const fmsInput = document.getElementById('id_origin_fms_nbr'); + const sourceRefContainer = document.getElementById('source-ref-container'); - // Fonction pour afficher/masquer le champ détail de la source - function toggleSourceDetail() { + function handleSourceChange() { + if (!sourceCategorySelect) return; const selectedValue = sourceCategorySelect.value; - // Rechercher la catégorie sélectionnée dans sourceCategoriesData - const selectedCategory = sourceCategoriesData.find(sc => sc.id == selectedValue); + const selectedOption = sourceCategorySelect.options[sourceCategorySelect.selectedIndex]; + const selectedText = selectedOption ? selectedOption.text : ''; - if (selectedCategory && selectedCategory.has_reference && !['PREGO','OBSERVATION','OBSERVATIE'].includes(selectedCategory.name)) { - sourceDetailContainer.classList.remove('d-none'); // Afficher - } else { - sourceDetailContainer.classList.add('d-none'); // Masquer + let selectedCategory = null; + if (typeof sourceCategoriesData !== 'undefined' && Array.isArray(sourceCategoriesData)) { + selectedCategory = sourceCategoriesData.find(sc => sc.id == selectedValue); + } + + // Dispatch a CustomEvent for forks (like Bruxelles Mobilité) to run custom logic + const event = new CustomEvent('streetup:source-changed', { + detail: { + value: selectedValue, + text: selectedText, + category: selectedCategory + } + }); + document.dispatchEvent(event); + + // Standard core behavior: + // 1. Toggle source detail visibility + if (sourceDetailContainer) { + if (selectedCategory && selectedCategory.has_reference) { + // If it is observation, the core template has it as read-only. + // We show detail container if category requires detail and has no reference. + // Normally, we show detail if has_reference is true. + sourceDetailContainer.classList.remove('d-none'); + } else { + sourceDetailContainer.classList.add('d-none'); + } + } + + // 2. Toggle source reference container visibility + if (sourceRefContainer) { + if (selectedCategory && selectedCategory.has_reference) { + sourceRefContainer.classList.remove('d-none'); + } else { + sourceRefContainer.classList.add('d-none'); + } } } - // Fonction pour afficher/masquer le champ référence de la source - function syncNoFmsState() { - if (!noFmsCheckbox || !fmsInput) { - return; - } - - if (noFmsCheckbox.checked) { - fmsInput.value = ''; - fmsInput.setAttribute('disabled', 'disabled'); - } else { - fmsInput.removeAttribute('disabled'); - } + if (sourceCategorySelect) { + handleSourceChange(); + sourceCategorySelect.addEventListener('change', handleSourceChange); } - function toggleSourceRef() { - const selectedText = sourceCategorySelect.options[sourceCategorySelect.selectedIndex].text || ''; - const selectedUpper = selectedText.trim().toUpperCase(); - const mntPrefix = document.getElementById('mnt-prefix'); - const pregoInput = document.getElementById('id_origin_prego_nbr'); - - // Reset visibility by default - if (sourceRefContainer) sourceRefContainer.classList.add('d-none'); - if (sourceRefGenericContainer) sourceRefGenericContainer.classList.add('d-none'); - if (mntPrefix) mntPrefix.style.display = 'none'; - if (sourceRefInput) { - sourceRefInput.removeAttribute('readonly'); - sourceRefInput.classList.remove('bg-light'); - sourceRefInput.setAttribute('title', ''); - sourceRefInput.removeAttribute('aria-readonly'); - } - - // PREGO -> show PREGO block with MNT prefix + FMS - if (selectedUpper === 'PREGO') { - if (sourceRefContainer) sourceRefContainer.classList.remove('d-none'); - if (mntPrefix) mntPrefix.style.display = 'inline-block'; - - if (pregoInput) { - pregoInput.setAttribute('pattern', '\\d{1,6}'); - pregoInput.setAttribute('maxlength', '6'); - pregoInput.setAttribute('title', gettext('Veuillez entrer un entier de maximum 6 chiffres')); - pregoInput.addEventListener('input', function () { - this.value = this.value.replace(/\D/g, '').slice(0, 6); - }); - } - if (fmsInput) { - fmsInput.removeAttribute('disabled'); - fmsInput.setAttribute('pattern', '\\d{1,9}'); - fmsInput.setAttribute('maxlength', '9'); - fmsInput.addEventListener('input', function () { - this.value = this.value.replace(/\D/g, '').slice(0, 9); - }); - } - } - // OBSERVATION (FR/NL) -> show generic source_ref only - else if (selectedUpper === 'OBSERVATION' || selectedUpper === 'OBSERVATIE') { - if (sourceRefGenericContainer) sourceRefGenericContainer.classList.remove('d-none'); - // Make the reference read-only but still submitted - if (sourceRefInput) { - sourceRefInput.setAttribute('readonly', 'readonly'); - sourceRefInput.setAttribute('aria-readonly', 'true'); - // subtle visual cue it cannot be edited - sourceRefInput.classList.add('bg-light'); - const v = sourceRefInput.value || ''; - if (v) { - sourceRefInput.setAttribute('title', gettext('Référence non modifiable')); - } - } - - // Clean PREGO/FMS constraints - if (pregoInput) { - pregoInput.removeAttribute('pattern'); - pregoInput.removeAttribute('maxlength'); - pregoInput.removeAttribute('title'); - } - if (fmsInput) { - fmsInput.value = ''; - fmsInput.setAttribute('disabled', 'disabled'); - fmsInput.removeAttribute('pattern'); - fmsInput.removeAttribute('maxlength'); - } - if (noFmsCheckbox) { - noFmsCheckbox.checked = false; - } - } - // Others -> nothing specific - else { - // ensure PREGO constraints removed - if (pregoInput) { - pregoInput.removeAttribute('pattern'); - pregoInput.removeAttribute('maxlength'); - pregoInput.removeAttribute('title'); - } - if (fmsInput) { - fmsInput.removeAttribute('disabled'); - fmsInput.removeAttribute('pattern'); - fmsInput.removeAttribute('maxlength'); - } - if (noFmsCheckbox) { - noFmsCheckbox.checked = false; - } - } - - syncNoFmsState(); - } - - // Exécuter une première fois si besoin à l'initialisation : - if (sourceCategorySelect !== null) { - toggleSourceDetail(); - toggleSourceRef(); - // Événement pour surveiller les changements sur le champ Catgégorie de la source - sourceCategorySelect.addEventListener('change', toggleSourceDetail); - sourceCategorySelect.addEventListener('change', toggleSourceRef); - } - - if (noFmsCheckbox) { - noFmsCheckbox.addEventListener('change', syncNoFmsState); - } - - - - - - window.toggleSourceDetail = toggleSourceDetail; - window.syncNoFmsState = syncNoFmsState; - window.toggleSourceRef = toggleSourceRef; }); diff --git a/streetup/interventions/static/interventions/add/validation.js b/streetup/interventions/static/interventions/add/validation.js index f5c1c73..a754cbe 100644 --- a/streetup/interventions/static/interventions/add/validation.js +++ b/streetup/interventions/static/interventions/add/validation.js @@ -179,52 +179,11 @@ document.addEventListener('DOMContentLoaded', function () { }); } - // --- Règles spécifiques à la source PREGO --- - const sourceSel = document.getElementById('id_source_category'); - const pregoEl = document.getElementById('id_origin_prego_nbr'); - const fmsEl = document.getElementById('id_origin_fms_nbr'); - const noFmsEl = document.getElementById('id_no_origin_fms'); - const docsEl = document.getElementById('id_documents'); - - const isPrego = (() => { - if (!sourceSel) return false; - const opt = sourceSel.options[sourceSel.selectedIndex]; - const val = (opt?.value || '').trim().toLowerCase(); - const txt = (opt?.text || '').trim().toLowerCase(); - return val === 'prego' || txt === 'prego'; - })(); - - if (isPrego) { - // 1️⃣ Numéro PREGO obligatoire - rules.push({ - el: pregoEl, - test: () => pregoEl && pregoEl.value.trim() !== '', - message: gettext('Veuillez indiquer un numéro PREGO.') - }); - - // 2️⃣ Numéro FMS ou case "Pas de FMS" obligatoire - rules.push({ - el: fmsEl, - test: () => { - const hasNoFms = !!noFmsEl?.checked; - const fmsVal = (fmsEl?.value || '').trim(); - return hasNoFms || fmsVal !== ''; - }, - message: gettext('Veuillez indiquer un numéro FMS ou cocher « Pas de FMS ».') - }); - - // 3️⃣ Si FMS présent → fichier obligatoire - rules.push({ - el: docsEl, - test: () => { - const fmsVal = (fmsEl?.value || '').trim(); - if (!fmsVal) return true; // pas de FMS => pas de contrainte - const files = Array.from(docsEl?.files || []); - return files.length > 0; - }, - message: gettext('Veuillez joindre au moins un document pour une référence FMS.') - }); - } + // Dispatch CustomEvent to allow custom validation rules injection (e.g., PREGO/FMS validations in forks) + const customRulesEvent = new CustomEvent('streetup:collect-validation-rules', { + detail: { rules: rules, isVisible: isVisible, gettext: gettext } + }); + document.dispatchEvent(customRulesEvent); if (!['artworks', 'nature', 'roads', 'its', 'clean'].includes(thematic)) { @@ -340,8 +299,12 @@ document.addEventListener('DOMContentLoaded', function () { // avertissements via modale (FMS, équipement structures, et statuts des assets/locations) const submitWarnings = []; - const fmsWarnMsg = getFmsWarningMessage(); - if (fmsWarnMsg) submitWarnings.push(fmsWarnMsg); + + // Dispatch CustomEvent to collect custom warnings (e.g., FMS file validation in forks) + const customWarningsEvent = new CustomEvent('streetup:collect-submit-warnings', { + detail: { warnings: submitWarnings, gettext: gettext } + }); + document.dispatchEvent(customWarningsEvent); // Vérifier les statuts des assets/locations const assetLocationWarnings = await getAssetLocationStatusWarningsAsync(); @@ -460,31 +423,7 @@ document.addEventListener('DOMContentLoaded', function () { -// --- Helper: faut-il avertir que aucun fichier ne contient le n° FMS ? --- -function getFmsWarningMessage() { - const sourceSel = document.getElementById('id_source_category'); - const fmsEl = document.getElementById('id_origin_fms_nbr'); - const docsEl = document.getElementById('id_documents'); - // seulement si corrective & source PREGO - const opt = sourceSel?.options[sourceSel.selectedIndex]; - const val = (opt?.value || '').trim().toLowerCase(); - const txt = (opt?.text || '').trim().toLowerCase(); - const isPrego = (val === 'prego' || txt === 'prego'); - - if (getMaintainType() !== 'corrective' || !isPrego) return null; - - const fmsVal = (fmsEl?.value || '').trim(); - if (!fmsVal) return null; // pas de FMS → pas d’avertissement - const files = Array.from(docsEl?.files || []); - if (files.length === 0) return null; // règle "fichier obligatoire si FMS" s’en charge - - const hasMatch = files.some(f => String(f.name || '').includes(fmsVal)); - if (hasMatch) return null; - - // Aucun fichier ne contient le n° FMS → message pour la modale - return gettext(`Aucun fichier joint ne contient le n° FMS saisi (${fmsVal}). Voulez-vous envoyer quand même ?`); -} // --- Modale d’avertissement FMS (création lazy) --- // --- Helper: check for inactive assets/locations (corrective maintenance only) --- @@ -604,6 +543,5 @@ function ensureFmsWarningModal() { window.focusFirstFormError = focusFirstFormError; -window.getFmsWarningMessage = getFmsWarningMessage; window.getAssetLocationStatusWarningsAsync = getAssetLocationStatusWarningsAsync; window.ensureFmsWarningModal = ensureFmsWarningModal; diff --git a/streetup/interventions/templates/interventions/add/_intervention_add_form.html b/streetup/interventions/templates/interventions/add/_intervention_add_form.html index 1102855..1f14bfd 100644 --- a/streetup/interventions/templates/interventions/add/_intervention_add_form.html +++ b/streetup/interventions/templates/interventions/add/_intervention_add_form.html @@ -292,29 +292,11 @@ {{ form.source_detail }} - -
+ +
{{ form.source_ref }}
-
- -
- - {{ form.origin_prego_nbr }} - {{ form.origin_prego_nbr.errors }} -
-
- - {{ form.origin_fms_nbr }} - {{ form.origin_fms_nbr.errors }} -
- {{ form.no_origin_fms }} - -
- {{ form.no_origin_fms.errors }} -
-
diff --git a/streetup/interventions/templates/interventions/detail/card_origine.html b/streetup/interventions/templates/interventions/detail/card_origine.html index 587b350..2ed135e 100644 --- a/streetup/interventions/templates/interventions/detail/card_origine.html +++ b/streetup/interventions/templates/interventions/detail/card_origine.html @@ -81,14 +81,12 @@ - {% if intervention.source_ref or intervention.origin_prego_nbr or 'source_ref' in allowed_update_fields %} -
+ {% if intervention.source_ref or 'source_ref' in allowed_update_fields %} +
{% translate "Référence" %}:
- {% if intervention.source_category.name_fr == 'PREGO' and intervention.origin_prego_nbr %} - MNT-{{ intervention.origin_prego_nbr }} - {% elif source_observation %} + {% if source_observation %} {{ source_observation.code }} {% elif intervention.source_ref %} {{ intervention.source_ref }} @@ -103,24 +101,6 @@
{% endif %} - {% if intervention.origin_fms_nbr or 'origin_fms_nbr' in allowed_update_fields %} -
- FMS N°: -
- - {% if intervention.origin_fms_nbr %} - {{ intervention.origin_fms_nbr }} - {% else %} - - - {% endif %} - -
-
- -
-
- {% endif %} - {% if intervention.source_detail or 'source_detail' in allowed_update_fields %}
{% translate "Détail" %}: diff --git a/streetup/interventions/templates/interventions/interventions_calendar.html b/streetup/interventions/templates/interventions/interventions_calendar.html index 5b27edd..3d2f9ec 100644 --- a/streetup/interventions/templates/interventions/interventions_calendar.html +++ b/streetup/interventions/templates/interventions/interventions_calendar.html @@ -259,7 +259,7 @@
- + diff --git a/streetup/interventions/templates/interventions/interventions_index.html b/streetup/interventions/templates/interventions/interventions_index.html index 0a22140..8ca2787 100644 --- a/streetup/interventions/templates/interventions/interventions_index.html +++ b/streetup/interventions/templates/interventions/interventions_index.html @@ -833,7 +833,7 @@
+ placeholder="{% translate 'Titre, code, localisation, contrat…' %}">