feat: initialize project database migrations and core models structure

This commit is contained in:
Karl Determe 2026-07-22 19:11:00 +02:00
parent 8c83ff764c
commit 7e74a1f8c0
112 changed files with 823 additions and 3312 deletions

View file

@ -1,11 +1,11 @@
asgiref==3.11.0
certifi==2025.11.12
charset-normalizer==3.4.4
crispy-bootstrap5==2025.6
Django==5.2.8
djangorestframework==3.16.1
crispy-bootstrap5==2026.3
Django==6.0.7
djangorestframework==3.17.1
djangorestframework-simplejwt==5.5.1
django-crispy-forms==2.5
django-crispy-forms==2.6
et_xmlfile==2.0.0
geopandas==1.1.1
idna==3.11

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import assets.models.controlcenters
import assets.models.its
@ -26,7 +26,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -78,7 +78,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -183,7 +183,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -246,7 +246,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -490,7 +490,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -598,7 +598,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -702,7 +702,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -737,7 +737,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -764,7 +764,7 @@ class Migration(migrations.Migration):
('buffer', models.FloatField(blank=True, null=True)),
('width', models.FloatField(blank=True, null=True)),
('length', models.FloatField(blank=True, null=True)),
('bulb_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('tulipes', 'Tulipes'), ('jacynthes', 'Jacynthes'), ('jonquilles', 'Jonquilles'), ('narcisses', 'Narcisses'), ('crocus', 'Crocus'), ('perce_neige', 'Perce-neige'), ('autres', 'Autres')], max_length=50), blank=True, default=list, size=None, verbose_name='Types de bulbes')),
('bulb_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('tulipes', 'Tulipes'), ('jacynthes', 'Jacynthes'), ('jonquilles', 'Jonquilles'), ('narcisses', 'Narcisses'), ('crocus', 'Crocus'), ('perce_neige', 'Perce-neige'), ('autres', 'Autres')], max_length=50), blank=True, default=list, verbose_name='Types de bulbes')),
],
options={
'abstract': False,
@ -785,43 +785,6 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.CreateModel(
name='NatureHollowGreenSpace',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geojson', models.TextField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=3812)),
('surface_area', models.FloatField(blank=True, null=True)),
('width', models.FloatField(blank=True, null=True)),
('length', models.FloatField(blank=True, null=True)),
('hgs_type', models.CharField(blank=True, choices=[('rain_garden', 'Jardin de pluie'), ('swale', 'Noue'), ('ditch', 'Fossé'), ('dry_basin', "Bassin sec/d'infiltration"), ('wet_basin', 'Bassin en eau')], max_length=50, null=True, verbose_name="Type d'EVC")),
('coat_type', models.CharField(blank=True, choices=[('earth_stone_mixture', 'Mélange terre-pierre'), ('vegetal', 'Végétal'), ('grassed_structure', 'Structure enherbée'), ('non_aggregated_material', 'Matériau non agrégé'), ('mineral_filled_structure', 'Structure à remplissage minéral'), ('elevated_coating', 'Revêtement surélevé'), ('modular_porous', 'Modulaire poreux'), ('continuous_porous', 'Continu poreux'), ('impermeable', 'Impermeable')], max_length=50, null=True, verbose_name='Type de revêtement')),
],
options={
'verbose_name': 'Espace vert creux',
'verbose_name_plural': 'Espaces verts creux',
},
),
migrations.CreateModel(
name='NatureLocation',
fields=[
@ -842,6 +805,72 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.CreateModel(
name='NatureRWIAsset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geojson', models.TextField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.GeometryField(blank=True, null=True, srid=3812)),
('external_id', models.IntegerField(blank=True, null=True, verbose_name='External ID')),
('code_name', models.CharField(blank=True, choices=[('AV', 'Stormwater inlet (Avaloir)'), ('BS', 'Sequenced border (Bordure séquencée)'), ('BI', "Injection port (Bouche d'injection)"), ('CA', 'Non-return valve (Clapet anti-retour)'), ('ES', 'Sedimentation (Empierrement sédimentation)'), ('KC', 'Kerb cell'), ('PI', "Infiltration well (Puits d'infiltration)"), ('SU', 'Overflow (Surverse)'), ('VI', "Isolation valve (Vanne d'isolation)"), ('VR', 'Control valve (Vanne de régulation)'), ('GT', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('FE', "Trickle of water (Filet d'eau)"), ('AR', 'Rail drain (Avaloir de rail)'), ('BA', 'Leveled edge (Bordure arasée)'), ('CI', 'Infiltration channel (Caniveau infiltrant)'), ('D', 'Drain'), ('TP', 'Overflowing (Trop plein)'), ('RES', 'Separate sewer system (Réseau égout séparatif)'), ('EVC', 'Hollow green space (Espace vert creux)'), ('OED', 'Decentralized underground structure (Ouvrage enterré décentralisé)'), ('OSI', 'Work on an impermeable surface (Ouvrage sur surface imperméable)'), ('TA', 'Alternative technique (Technique alternative)'), ('OA', 'Supplementary work (Ouvrage annexe)')], max_length=50, null=True, verbose_name='Code name')),
('structure_type', models.CharField(blank=True, choices=[('avaloir', 'Stormwater inlet (Avaloir)'), ('bordure sequencee', 'Sequenced border (Bordure séquencée)'), ('bouche injection', "Injection port (Bouche d'injection)"), ('clapet anti-retour', 'Non-return valve (Clapet anti-retour)'), ('empierrement sedimentation', 'Sedimentation (Empierrement sédimentation)'), ('kerb cell', 'Kerb cell'), ('puits infiltration', "Infiltration well (Puits d'infiltration)"), ('surverse', 'Overflow (Surverse)'), ('vanne isolation', "Isolation valve (Vanne d'isolation)"), ('vanne regulation', 'Control valve (Vanne de régulation)'), ('gargouille de trottoir', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('filet eau', "Trickle of water (Filet d'eau)"), ('avaloir de rail', 'Rail drain (Avaloir de rail)'), ('bordure arasee', 'Leveled edge (Bordure arasée)'), ('caniveau infiltrant', 'Infiltration channel (Caniveau infiltrant)'), ('drain', 'Drain'), ('trop plein', 'Overflowing (Trop plein)'), ('reseau egout separatif', 'Separate sewer system (Réseau égout séparatif)'), ('bassin sec/infiltration', 'Dry/infiltration basin (Bassin sec/infiltration)'), ('bassin en eau', 'Wet basin (Bassin en eau)'), ('fosse', 'Ditch (Fossé)'), ('jardin de pluie', 'Rain garden (Jardin de pluie)'), ('noue', 'Swale (Noue)'), ('massif drainant', 'Draining structure (Massif drainant)'), ('tranchee drainante', 'Draining trench (Tranchée drainante)'), ('toiture stockante', 'Retention roof (Toiture stockante)'), ('toiture verte', 'Green roof (Toiture verte)'), ('bassin orage', "Stormwater basin (Bassin d'orage)"), ('SAUL-vide', 'SAUL-empty (SAUL-vide)'), ('SAUL-terre', 'SAUL-earth (SAUL-terre)'), ('citerne', 'Cistern (Citerne)'), ('espace vert sur dalle', 'Green space on slab (Espace vert sur dalle)'), ('bande filtrante', 'Filter strip (Bande filtrante)'), ('collecteur en beton poreux', 'Porous concrete collector (Collecteur en béton poreux)')], max_length=100, null=True, verbose_name='Structure type')),
('geometry_type', models.CharField(blank=True, choices=[('point', 'Point'), ('line', 'Line'), ('polygon', 'Polygon')], max_length=50, null=True, verbose_name='Geometry type')),
('project_manager', models.CharField(blank=True, choices=[('DPV', 'DPV'), ('DITP', 'DITP'), ('DEN', 'DEN'), ('DPO', 'DPO'), ('Beliris', 'Beliris'), ('Commune', 'Commune')], max_length=50, null=True, verbose_name='Project manager')),
('construction_status', models.CharField(blank=True, choices=[('etude', 'Design (Étude)'), ('travaux', 'Construction (Travaux)'), ('reception provisoire', 'Provisional acceptance (Réception provisoire)'), ('reception definitive', 'Final acceptance (Réception définitive)')], max_length=100, null=True, verbose_name='Construction status')),
('commissioning_date', models.DateField(blank=True, null=True, verbose_name='Commissioning date')),
('condition', models.CharField(blank=True, choices=[('bon', 'Good (Bon)'), ('examen a programmer', 'Inspection to schedule (Examen à programmer)'), ('intervention curative necessaire', 'Curative action needed (Intervention curative nécessaire)'), ('refection totale necessaire', 'Total rehabilitation needed (Réfection totale nécessaire)'), ('travaux entretien a realiser', 'Maintenance works to perform (Travaux entretien à réaliser)'), ('travaux maintenance', 'Maintenance works (Travaux maintenance)'), ('travaux maintenance a programmer', 'Maintenance works to schedule (Travaux maintenance à programmer)')], max_length=100, null=True, verbose_name='Condition')),
('administrator', models.CharField(blank=True, max_length=500, null=True, verbose_name='Administrator')),
('monitoring', models.CharField(blank=True, max_length=500, null=True, verbose_name='Monitoring')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('has_filter', models.BooleanField(blank=True, null=True, verbose_name='Has filter')),
('sewer_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Sewer connection')),
('separate_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Separate network connection')),
('hydro_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Hydro network connection')),
('surface_area', models.FloatField(blank=True, null=True, verbose_name='Surface area (m²)')),
('length', models.FloatField(blank=True, null=True, verbose_name='Length (m)')),
('coating_category', models.CharField(blank=True, choices=[('Vegetal', 'Vegetal (Végétal)'), ('Structure enherbee', 'Grassed structure (Structure enherbée)'), ('Materiau non agrege', 'Unbound material (Matériau non agrégé)'), ('Structure a remplissage mineral', 'Mineral-filled structure (Structure à remplissage minéral)'), ('Revetement sureleve', 'Raised coating (Revêtement surélevé)'), ('Modulaire poreux', 'Porous modular (Modulaire poreux)'), ('Continu poreux', 'Porous continuous (Continu poreux)'), ('Impermeable', 'Impermeable (Imperméable)')], max_length=100, null=True, verbose_name='Coating category')),
('coating_type', models.CharField(blank=True, choices=[('Pleine terre', 'Open soil (Pleine terre)'), ('Melange terre-pierre', 'Soil-stone mixture (Mélange terre-pierre)'), ('Paves a joints larges enherbes', 'Grassed wide-joint pavers (Pavés à joints larges enherbés)'), ('Dalles gazon en beton', 'Concrete grass grids (Dalles gazon en béton)'), ('Dalles alveolaires vegetalisees', 'Vegetated cellular tiles (Dalles alvéolaires végétalisées)'), ('Mulch vegetal', 'Woodchips/mulch (Mulch végétal)'), ('Graviers calcaires', 'Limestone gravel (Graviers calcaires)'), ('Graviers non calcaires', 'Non-limestone gravel (Graviers non calcaires)'), ('Terre battue', 'Dirt ground (Terre battue)'), ('Paves a joints larges engravillonnes', 'Gravel wide-joint pavers (Pavés à joints larges engravillonnés)'), ('Dalles alveolaires en gravier', 'Gravel cellular tiles (Dalles alvéolaires en gravier)'), ('Dalles en PEHD remplies de pave', 'HDPE tiles filled with pavers (Dalles en PEHD remplies de pavé)'), ('Grilles', 'Grates (Grilles)'), ('Platelages bois', 'Wooden decking (Platelages bois)'), ('Pave en beton poreux', 'Porous concrete pavers (Pavé en béton poreux)'), ('Dalle en beton poreux', 'Porous concrete slab (Dalle en béton poreux)'), ('Enrobes bitumineux poreux', 'Porous asphalt (Enrobés bitumineux poreux)'), ('Betons poreux', 'Porous concrete (Bétons poreux)'), ('Graviers lies a la resine', 'Resin-bound gravel (Graviers liés à la résine)'), ('Graviers concasses lies aux polymeres', 'Polymer-bound crushed gravel (Graviers concassés liés aux polymères)'), ('Revetements EPDM et ESBR', 'EPDM and SBR rubber coatings (Revêtements EPDM et ESBR)'), ('Revetements en beton de ciment non permeables', 'Non-porous cement concrete coatings (Revêtements en béton de ciment non perméables)'), ('Revetements bitumineux non permeables', 'Non-porous asphalt coatings (Revêtements bitumineux non perméables)'), ('Dallages non permeables', 'Non-porous tiling (Dallages non perméables)'), ('Pavages non permeables', 'Non-porous paving (Pavages non perméables)'), ('Autres revetements non permeables', 'Other non-porous coatings (Autres revêtements non perméables)')], max_length=100, null=True, verbose_name='Coating type')),
],
options={
'verbose_name': 'RWI Asset',
'verbose_name_plural': 'RWI Assets',
},
),
migrations.CreateModel(
name='NatureRWIAssetDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True, verbose_name='Description du document')),
('upload_date', models.DateTimeField(auto_now=True, verbose_name='Date de téléversement')),
('status', models.CharField(choices=[('active', 'Actif'), ('archived', 'Archivé')], default='active')),
('file', models.FileField(max_length=255, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/rwi/', verbose_name='File')),
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/rwi/')),
],
options={
'verbose_name': 'RWI Asset Document',
'verbose_name_plural': 'RWI Asset Documents',
},
),
migrations.CreateModel(
name='NatureRWIZ',
fields=[
@ -867,7 +896,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -902,7 +931,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -936,7 +965,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -986,7 +1015,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1033,7 +1062,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1053,6 +1082,10 @@ class Migration(migrations.Migration):
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('geojson', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Public lighting pole',
'verbose_name_plural': 'Public lighting poles',
},
),
migrations.CreateModel(
name='PublicLightingPoleModel',
@ -1112,7 +1145,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1133,7 +1166,7 @@ class Migration(migrations.Migration):
('geojson', models.TextField(blank=True, null=True)),
('address', models.CharField(blank=True, max_length=2048, null=True)),
('main_function', models.CharField(blank=True, choices=[('protection', 'Protection Asset'), ('antiterrorist', 'Antiterroriste'), ('parking_neutralization', 'Neutralisation stationnement'), ('road_safety', 'Sécurité routière'), ('public_space', 'Espace public'), ('construction', 'Chantier'), ('unidentified', 'Non identifié')], max_length=200, null=True)),
('secondary_functions', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('protection', 'Protection Asset'), ('antiterrorist', 'Antiterroriste'), ('parking_neutralization', 'Neutralisation stationnement'), ('road_safety', 'Sécurité routière'), ('public_space', 'Espace public'), ('construction', 'Chantier'), ('unidentified', 'Non identifié')], max_length=200), blank=True, null=True, size=None)),
('secondary_functions', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('protection', 'Protection Asset'), ('antiterrorist', 'Antiterroriste'), ('parking_neutralization', 'Neutralisation stationnement'), ('road_safety', 'Sécurité routière'), ('public_space', 'Espace public'), ('construction', 'Chantier'), ('unidentified', 'Non identifié')], max_length=200), blank=True, null=True)),
('number_of_blocks', models.IntegerField(blank=True, null=True)),
('length', models.FloatField(blank=True, null=True)),
('recommended_action', models.CharField(blank=True, choices=[('keep', 'Maintenir'), ('remove', 'Enlever'), ('replace', 'Remplacer')], max_length=2048, null=True)),
@ -1151,7 +1184,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1177,7 +1210,7 @@ class Migration(migrations.Migration):
('nearby_vegetation', models.BooleanField(blank=True, null=True)),
('possible_rooting', models.BooleanField(blank=True, null=True)),
('is_defect', models.BooleanField(blank=True, null=True)),
('defect_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('obstructed', 'Obstrué'), ('smell_cut_missing', 'Coupe-odeur manquant'), ('grate_broken', 'Grille cassée'), ('grate_missing', 'Grille manquante'), ('poor_water_flow', 'Mauvais écoulement'), ('other', 'Autre')], max_length=200), blank=True, null=True, size=None)),
('defect_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('obstructed', 'Obstrué'), ('smell_cut_missing', 'Coupe-odeur manquant'), ('grate_broken', 'Grille cassée'), ('grate_missing', 'Grille manquante'), ('poor_water_flow', 'Mauvais écoulement'), ('other', 'Autre')], max_length=200), blank=True, null=True)),
('defect_note', models.TextField(blank=True, null=True)),
('related_image_type', models.CharField(blank=True, max_length=1000, null=True)),
('altitude', models.FloatField(blank=True, null=True)),
@ -1208,7 +1241,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1259,7 +1292,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1345,7 +1378,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1410,7 +1443,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1471,6 +1504,9 @@ class Migration(migrations.Migration):
('name_nl', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True)),
('sgv_picture', models.ImageField(blank=True, null=True, upload_to='sign/signpanels_types/')),
('default_width', models.FloatField(blank=True, null=True, verbose_name='Largeur par défaut (cm)')),
('default_height', models.FloatField(blank=True, null=True, verbose_name='Hauteur par défaut (cm)')),
('default_diameter', models.FloatField(blank=True, null=True, verbose_name='Diamètre par défaut (cm)')),
],
),
migrations.CreateModel(
@ -1480,7 +1516,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1515,7 +1551,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1659,7 +1695,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1707,7 +1743,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1754,7 +1790,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1813,7 +1849,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1852,7 +1888,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1913,7 +1949,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -1934,7 +1970,7 @@ class Migration(migrations.Migration):
('geojson', models.TextField(blank=True, null=True)),
('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Nom du détecteur')),
('hardware', models.CharField(blank=True, max_length=255, null=True, verbose_name='Hardware')),
('phases', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), blank=True, null=True, size=None, verbose_name='Phases associées')),
('phases', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), blank=True, null=True, verbose_name='Phases associées')),
('mentionned_on_plan', models.BooleanField(default=False, verbose_name='Mentionné sur le plan')),
],
options={
@ -1963,7 +1999,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2011,7 +2047,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2087,6 +2123,7 @@ class Migration(migrations.Migration):
('status', models.CharField(choices=[('active', 'Actif'), ('in_preparation', 'En préparation'), ('under_repair', 'En cours de réparation'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('inactive', 'Désactivé'), ('archived', 'Archivé')], default='active')),
('begin_time', models.DateTimeField(blank=True, null=True)),
('current_index', models.CharField(blank=True, max_length=100, null=True)),
('is_radar', models.BooleanField(default=False, verbose_name='Est un radar ?')),
],
options={
'ordering': ['code'],
@ -2116,7 +2153,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2160,7 +2197,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2195,7 +2232,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2220,8 +2257,8 @@ class Migration(migrations.Migration):
('ral', models.CharField(blank=True, max_length=200, null=True, verbose_name='RAL')),
],
options={
'verbose_name': 'Poteau',
'verbose_name_plural': 'Poteaux',
'verbose_name': 'Traffic light pole',
'verbose_name_plural': 'Traffic light poles',
},
),
migrations.CreateModel(
@ -2248,7 +2285,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -2273,6 +2310,101 @@ class Migration(migrations.Migration):
'ordering': ['-commissioning_date'],
},
),
migrations.CreateModel(
name='TrafficLightRadar',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('geojson', models.TextField(blank=True, null=True)),
],
options={
'verbose_name': 'Radar',
'verbose_name_plural': 'Radars',
},
),
migrations.CreateModel(
name='TrafficLightRadarModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('description', models.TextField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
],
options={
'verbose_name': 'Traffic Light Radar Model',
'verbose_name_plural': 'Traffic Light Radar Models',
},
),
migrations.CreateModel(
name='TrafficLightRadarPole',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('geojson', models.TextField(blank=True, null=True)),
('colour', models.CharField(blank=True, max_length=200, null=True, verbose_name='Couleur')),
('ral', models.CharField(blank=True, max_length=200, null=True, verbose_name='RAL')),
],
options={
'verbose_name': 'Radar Pole',
'verbose_name_plural': 'Radar Poles',
},
),
migrations.CreateModel(
name='TrafficLightRadarPoleModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('description', models.TextField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='TrafficLightVoltageHardware',
fields=[
@ -2280,7 +2412,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -79,6 +79,11 @@ class Migration(migrations.Migration):
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fountains', to='assets.artworkstreet'),
),
migrations.AddField(
model_name='assetcategory',
name='allowed_models',
field=models.ManyToManyField(blank=True, help_text="Les types de modèles d'assets (Django) autorisés pour cette catégorie.", related_name='asset_categories', to='contenttypes.contenttype', verbose_name='Modèles Django autorisés'),
),
migrations.AddField(
model_name='assetcategory',
name='parent',
@ -489,26 +494,6 @@ class Migration(migrations.Migration):
name='uploaded_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Ajouté par'),
),
migrations.AddField(
model_name='naturehollowgreenspace',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='naturehollowgreenspace',
name='marked_as_removed_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par'),
),
migrations.AddField(
model_name='naturehollowgreenspace',
name='replaced_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.naturehollowgreenspace', verbose_name='Remplacé par'),
),
migrations.AddField(
model_name='naturehollowgreenspace',
name='updated_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par'),
),
migrations.AddField(
model_name='naturelocation',
name='category',
@ -539,16 +524,46 @@ class Migration(migrations.Migration):
name='location',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nature_benches', to='assets.naturelocation'),
),
migrations.AddField(
model_name='naturerwiasset',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='naturerwiasset',
name='marked_as_removed_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par'),
),
migrations.AddField(
model_name='naturerwiasset',
name='replaced_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.naturerwiasset', verbose_name='Remplacé par'),
),
migrations.AddField(
model_name='naturerwiasset',
name='updated_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par'),
),
migrations.AddField(
model_name='naturerwiassetdocument',
name='managed_document',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_sources', to='documents.manageddocument'),
),
migrations.AddField(
model_name='naturerwiassetdocument',
name='rwi_asset',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='assets.naturerwiasset', verbose_name='RWI Asset'),
),
migrations.AddField(
model_name='naturerwiassetdocument',
name='uploaded_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Ajouté par'),
),
migrations.AddField(
model_name='naturerwiz',
name='updated_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_location_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par'),
),
migrations.AddField(
model_name='naturehollowgreenspace',
name='location',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hollow_green_spaces', to='assets.naturerwiz'),
),
migrations.AddField(
model_name='natureshrub',
name='category',

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [
('assets', '0003_initial'),
('common', '0003_initial'),
('common', '0004_initial'),
('contracts', '0001_initial'),
('documents', '0001_initial'),
('observations', '0001_initial'),
@ -889,6 +889,101 @@ class Migration(migrations.Migration):
name='programming',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='documents', to='assets.trafficlightprogramming', verbose_name='Programmation associée'),
),
migrations.AddField(
model_name='trafficlightradar',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='trafficlightradar',
name='intersection',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='radars', to='assets.trafficlightintersection'),
),
migrations.AddField(
model_name='trafficlightradar',
name='marked_as_removed_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par'),
),
migrations.AddField(
model_name='trafficlightradar',
name='power_publiclighting_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='powered_radars', to='assets.publiclightingpole', verbose_name='Public Lighting Pole (Power)'),
),
migrations.AddField(
model_name='trafficlightradar',
name='power_trafficlights_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='powered_radars', to='assets.trafficlightpole', verbose_name='Traffic Light Pole (Power)'),
),
migrations.AddField(
model_name='trafficlightradar',
name='publiclighting_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.publiclightingpole', verbose_name='Public Lighting Pole (Support)'),
),
migrations.AddField(
model_name='trafficlightradar',
name='replaced_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.trafficlightradar', verbose_name='Remplacé par'),
),
migrations.AddField(
model_name='trafficlightradar',
name='trafficlights_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightpole', verbose_name='Traffic Light Pole (Support)'),
),
migrations.AddField(
model_name='trafficlightradar',
name='updated_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par'),
),
migrations.AddField(
model_name='trafficlightradarmodel',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='trafficlightradar',
name='model',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='assets.trafficlightradarmodel'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='intersection',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='radar_poles', to='assets.trafficlightintersection'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='marked_as_removed_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='replaced_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.trafficlightradarpole', verbose_name='Remplacé par'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='updated_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par'),
),
migrations.AddField(
model_name='trafficlightradar',
name='pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightradarpole', verbose_name='Radar Pole (Support)'),
),
migrations.AddField(
model_name='trafficlightradarpolemodel',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory'),
),
migrations.AddField(
model_name='trafficlightradarpole',
name='model',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.trafficlightradarpolemodel'),
),
migrations.AddField(
model_name='trafficlightvoltagehardware',
name='category',

View file

@ -1,188 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-23 05:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0004_initial'),
]
operations = [
migrations.AlterField(
model_name='artworkfountain',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='artworkmonument',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='cleanglasscontainer',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='cleanlitterbin',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='controlcentergeoasset',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='itsgeoasset',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='naturebench',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='naturegreensurface',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='naturehollowgreenspace',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='natureshrub',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='naturetree',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='publiclightingelectricalcabinet',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='publiclightinglightfixture',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='publiclightingpole',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='roadconcreteblockset',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='roaddrain',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='roadfurniture',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='roadsidewalk',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='roadway',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='signpanel',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='signparkingspot',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='signpole',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='structuregeoasset',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightaccessory',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightcable',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightcontroller',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightcontrollerhardware',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightdetector',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightelectricalcabinet',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightelectricalcabinetcontent',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightlantern',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightnetworkhardware',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightpole',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightprogramming',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='trafficlightvoltagehardware',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
]

View file

@ -1,92 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-27 09:44
import common.storages
import django.contrib.gis.db.models.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0005_alter_artworkfountain_status_and_more'),
('documents', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='NatureGIEPAsset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geojson', models.TextField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.GeometryField(blank=True, null=True, srid=3812)),
('external_id', models.IntegerField(blank=True, null=True, verbose_name='External ID')),
('code_name', models.CharField(blank=True, choices=[('AV', 'Stormwater inlet (Avaloir)'), ('BS', 'Sequenced border (Bordure séquencée)'), ('BI', "Injection port (Bouche d'injection)"), ('CA', 'Non-return valve (Clapet anti-retour)'), ('ES', 'Sedimentation (Empierrement sédimentation)'), ('KC', 'Kerb cell'), ('PI', "Infiltration well (Puits d'infiltration)"), ('SU', 'Overflow (Surverse)'), ('VI', "Isolation valve (Vanne d'isolation)"), ('VR', 'Control valve (Vanne de régulation)'), ('GT', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('FE', "Trickle of water (Filet d'eau)"), ('AR', 'Rail drain (Avaloir de rail)'), ('BA', 'Leveled edge (Bordure arasée)'), ('CI', 'Infiltration channel (Caniveau infiltrant)'), ('D', 'Drain'), ('TP', 'Overflowing (Trop plein)'), ('RES', 'Separate sewer system (Réseau égout séparatif)'), ('EVC', 'Hollow green space (Espace vert creux)'), ('OED', 'Decentralized underground structure (Ouvrage enterré décentralisé)'), ('OSI', 'Work on an impermeable surface (Ouvrage sur surface imperméable)'), ('TA', 'Alternative technique (Technique alternative)'), ('OA', 'Supplementary work (Ouvrage annexe)')], max_length=50, null=True, verbose_name='Code name')),
('structure_type', models.CharField(blank=True, choices=[('avaloir', 'Stormwater inlet (Avaloir)'), ('bordure sequencee', 'Sequenced border (Bordure séquencée)'), ('bouche injection', "Injection port (Bouche d'injection)"), ('clapet anti-retour', 'Non-return valve (Clapet anti-retour)'), ('empierrement sedimentation', 'Sedimentation (Empierrement sédimentation)'), ('kerb cell', 'Kerb cell'), ('puits infiltration', "Infiltration well (Puits d'infiltration)"), ('surverse', 'Overflow (Surverse)'), ('vanne isolation', "Isolation valve (Vanne d'isolation)"), ('vanne regulation', 'Control valve (Vanne de régulation)'), ('gargouille de trottoir', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('filet eau', "Trickle of water (Filet d'eau)"), ('avaloir de rail', 'Rail drain (Avaloir de rail)'), ('bordure arasee', 'Leveled edge (Bordure arasée)'), ('caniveau infiltrant', 'Infiltration channel (Caniveau infiltrant)'), ('drain', 'Drain'), ('trop plein', 'Overflowing (Trop plein)'), ('reseau egout separatif', 'Separate sewer system (Réseau égout séparatif)'), ('bassin sec/infiltration', 'Dry/infiltration basin (Bassin sec/infiltration)'), ('bassin en eau', 'Wet basin (Bassin en eau)'), ('fosse', 'Ditch (Fossé)'), ('jardin de pluie', 'Rain garden (Jardin de pluie)'), ('noue', 'Swale (Noue)'), ('massif drainant', 'Draining structure (Massif drainant)'), ('tranchee drainante', 'Draining trench (Tranchée drainante)'), ('toiture stockante', 'Retention roof (Toiture stockante)'), ('toiture verte', 'Green roof (Toiture verte)'), ('bassin orage', "Stormwater basin (Bassin d'orage)"), ('SAUL-vide', 'SAUL-empty (SAUL-vide)'), ('SAUL-terre', 'SAUL-earth (SAUL-terre)'), ('citerne', 'Cistern (Citerne)'), ('espace vert sur dalle', 'Green space on slab (Espace vert sur dalle)'), ('bande filtrante', 'Filter strip (Bande filtrante)'), ('collecteur en beton poreux', 'Porous concrete collector (Collecteur en béton poreux)')], max_length=100, null=True, verbose_name='Structure type')),
('project_manager', models.CharField(blank=True, choices=[('DPV', 'DPV'), ('DITP', 'DITP'), ('DEN', 'DEN'), ('DPO', 'DPO'), ('Beliris', 'Beliris'), ('Commune', 'Commune')], max_length=50, null=True, verbose_name='Project manager')),
('location_precision', models.CharField(blank=True, choices=[('precise', 'Precise (Précise)'), ('supposee', 'Assumed (Supposée)')], max_length=50, null=True, verbose_name='Location precision')),
('construction_status', models.CharField(blank=True, choices=[('etude', 'Design (Étude)'), ('travaux', 'Construction (Travaux)'), ('reception provisoire', 'Provisional acceptance (Réception provisoire)'), ('reception definitive', 'Final acceptance (Réception définitive)')], max_length=100, null=True, verbose_name='Construction status')),
('commissioning_date', models.DateField(blank=True, null=True, verbose_name='Commissioning date')),
('condition', models.CharField(blank=True, choices=[('bon', 'Good (Bon)'), ('examen a programmer', 'Inspection to schedule (Examen à programmer)'), ('intervention curative necessaire', 'Curative action needed (Intervention curative nécessaire)'), ('refection totale necessaire', 'Total rehabilitation needed (Réfection totale nécessaire)'), ('travaux entretien a realiser', 'Maintenance works to perform (Travaux entretien à réaliser)'), ('travaux maintenance', 'Maintenance works (Travaux maintenance)'), ('travaux maintenance a programmer', 'Maintenance works to schedule (Travaux maintenance à programmer)')], max_length=100, null=True, verbose_name='Condition')),
('administrator', models.CharField(blank=True, max_length=500, null=True, verbose_name='Administrator')),
('monitoring', models.CharField(blank=True, max_length=500, null=True, verbose_name='Monitoring')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('has_filter', models.BooleanField(blank=True, null=True, verbose_name='Has filter')),
('sewer_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Sewer connection')),
('separate_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Separate network connection')),
('hydro_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Hydro network connection')),
('surface_area', models.FloatField(blank=True, null=True, verbose_name='Surface area (m²)')),
('length', models.FloatField(blank=True, null=True, verbose_name='Length (m)')),
('coating_category', models.CharField(blank=True, choices=[('Vegetal', 'Vegetal (Végétal)'), ('Structure enherbee', 'Grassed structure (Structure enherbée)'), ('Materiau non agrege', 'Unbound material (Matériau non agrégé)'), ('Structure a remplissage mineral', 'Mineral-filled structure (Structure à remplissage minéral)'), ('Revetement sureleve', 'Raised coating (Revêtement surélevé)'), ('Modulaire poreux', 'Porous modular (Modulaire poreux)'), ('Continu poreux', 'Porous continuous (Continu poreux)'), ('Impermeable', 'Impermeable (Imperméable)')], max_length=100, null=True, verbose_name='Coating category')),
('coating_type', models.CharField(blank=True, choices=[('Pleine terre', 'Open soil (Pleine terre)'), ('Melange terre-pierre', 'Soil-stone mixture (Mélange terre-pierre)'), ('Paves a joints larges enherbes', 'Grassed wide-joint pavers (Pavés à joints larges enherbés)'), ('Dalles gazon en beton', 'Concrete grass grids (Dalles gazon en béton)'), ('Dalles alveolaires vegetalisees', 'Vegetated cellular tiles (Dalles alvéolaires végétalisées)'), ('Mulch vegetal', 'Woodchips/mulch (Mulch végétal)'), ('Graviers calcaires', 'Limestone gravel (Graviers calcaires)'), ('Graviers non calcaires', 'Non-limestone gravel (Graviers non calcaires)'), ('Terre battue', 'Dirt ground (Terre battue)'), ('Paves a joints larges engravillonnes', 'Gravel wide-joint pavers (Pavés à joints larges engravillonnés)'), ('Dalles alveolaires en gravier', 'Gravel cellular tiles (Dalles alvéolaires en gravier)'), ('Dalles en PEHD remplies de pave', 'HDPE tiles filled with pavers (Dalles en PEHD remplies de pavé)'), ('Grilles', 'Grates (Grilles)'), ('Platelages bois', 'Wooden decking (Platelages bois)'), ('Pave en beton poreux', 'Porous concrete pavers (Pavé en béton poreux)'), ('Dalle en beton poreux', 'Porous concrete slab (Dalle en béton poreux)'), ('Enrobes bitumineux poreux', 'Porous asphalt (Enrobés bitumineux poreux)'), ('Betons poreux', 'Porous concrete (Bétons poreux)'), ('Graviers lies a la resine', 'Resin-bound gravel (Graviers liés à la résine)'), ('Graviers concasses lies aux polymeres', 'Polymer-bound crushed gravel (Graviers concassés liés aux polymères)'), ('Revetements EPDM et ESBR', 'EPDM and SBR rubber coatings (Revêtements EPDM et ESBR)'), ('Revetements en beton de ciment non permeables', 'Non-porous cement concrete coatings (Revêtements en béton de ciment non perméables)'), ('Revetements bitumineux non permeables', 'Non-porous asphalt coatings (Revêtements bitumineux non perméables)'), ('Dallages non permeables', 'Non-porous tiling (Dallages non perméables)'), ('Pavages non permeables', 'Non-porous paving (Pavages non perméables)'), ('Autres revetements non permeables', 'Other non-porous coatings (Autres revêtements non perméables)')], max_length=100, null=True, verbose_name='Coating type')),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.naturegiepasset', verbose_name='Remplacé par')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
],
options={
'verbose_name': 'GIEP Asset',
'verbose_name_plural': 'GIEP Assets',
},
),
migrations.CreateModel(
name='NatureGIEPAssetDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True, verbose_name='Description du document')),
('upload_date', models.DateTimeField(auto_now=True, verbose_name='Date de téléversement')),
('status', models.CharField(choices=[('active', 'Actif'), ('archived', 'Archivé')], default='active')),
('file', models.FileField(max_length=255, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/giep/', verbose_name='File')),
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/giep/')),
('giep_asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='assets.naturegiepasset', verbose_name='GIEP Asset')),
('managed_document', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_sources', to='documents.manageddocument')),
('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Ajouté par')),
],
options={
'verbose_name': 'GIEP Asset Document',
'verbose_name_plural': 'GIEP Asset Documents',
},
),
]

View file

@ -1,111 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-27 09:50
import common.storages
import django.contrib.gis.db.models.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0006_naturegiepasset_naturegiepassetdocument'),
('documents', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='naturegiepassetdocument',
name='giep_asset',
),
migrations.RemoveField(
model_name='naturegiepassetdocument',
name='managed_document',
),
migrations.RemoveField(
model_name='naturegiepassetdocument',
name='uploaded_by',
),
migrations.CreateModel(
name='NatureRWIAsset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geojson', models.TextField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.GeometryField(blank=True, null=True, srid=3812)),
('external_id', models.IntegerField(blank=True, null=True, verbose_name='External ID')),
('code_name', models.CharField(blank=True, choices=[('AV', 'Stormwater inlet (Avaloir)'), ('BS', 'Sequenced border (Bordure séquencée)'), ('BI', "Injection port (Bouche d'injection)"), ('CA', 'Non-return valve (Clapet anti-retour)'), ('ES', 'Sedimentation (Empierrement sédimentation)'), ('KC', 'Kerb cell'), ('PI', "Infiltration well (Puits d'infiltration)"), ('SU', 'Overflow (Surverse)'), ('VI', "Isolation valve (Vanne d'isolation)"), ('VR', 'Control valve (Vanne de régulation)'), ('GT', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('FE', "Trickle of water (Filet d'eau)"), ('AR', 'Rail drain (Avaloir de rail)'), ('BA', 'Leveled edge (Bordure arasée)'), ('CI', 'Infiltration channel (Caniveau infiltrant)'), ('D', 'Drain'), ('TP', 'Overflowing (Trop plein)'), ('RES', 'Separate sewer system (Réseau égout séparatif)'), ('EVC', 'Hollow green space (Espace vert creux)'), ('OED', 'Decentralized underground structure (Ouvrage enterré décentralisé)'), ('OSI', 'Work on an impermeable surface (Ouvrage sur surface imperméable)'), ('TA', 'Alternative technique (Technique alternative)'), ('OA', 'Supplementary work (Ouvrage annexe)')], max_length=50, null=True, verbose_name='Code name')),
('structure_type', models.CharField(blank=True, choices=[('avaloir', 'Stormwater inlet (Avaloir)'), ('bordure sequencee', 'Sequenced border (Bordure séquencée)'), ('bouche injection', "Injection port (Bouche d'injection)"), ('clapet anti-retour', 'Non-return valve (Clapet anti-retour)'), ('empierrement sedimentation', 'Sedimentation (Empierrement sédimentation)'), ('kerb cell', 'Kerb cell'), ('puits infiltration', "Infiltration well (Puits d'infiltration)"), ('surverse', 'Overflow (Surverse)'), ('vanne isolation', "Isolation valve (Vanne d'isolation)"), ('vanne regulation', 'Control valve (Vanne de régulation)'), ('gargouille de trottoir', 'Sidewalk gargoyle (Gargouille de trottoir)'), ('filet eau', "Trickle of water (Filet d'eau)"), ('avaloir de rail', 'Rail drain (Avaloir de rail)'), ('bordure arasee', 'Leveled edge (Bordure arasée)'), ('caniveau infiltrant', 'Infiltration channel (Caniveau infiltrant)'), ('drain', 'Drain'), ('trop plein', 'Overflowing (Trop plein)'), ('reseau egout separatif', 'Separate sewer system (Réseau égout séparatif)'), ('bassin sec/infiltration', 'Dry/infiltration basin (Bassin sec/infiltration)'), ('bassin en eau', 'Wet basin (Bassin en eau)'), ('fosse', 'Ditch (Fossé)'), ('jardin de pluie', 'Rain garden (Jardin de pluie)'), ('noue', 'Swale (Noue)'), ('massif drainant', 'Draining structure (Massif drainant)'), ('tranchee drainante', 'Draining trench (Tranchée drainante)'), ('toiture stockante', 'Retention roof (Toiture stockante)'), ('toiture verte', 'Green roof (Toiture verte)'), ('bassin orage', "Stormwater basin (Bassin d'orage)"), ('SAUL-vide', 'SAUL-empty (SAUL-vide)'), ('SAUL-terre', 'SAUL-earth (SAUL-terre)'), ('citerne', 'Cistern (Citerne)'), ('espace vert sur dalle', 'Green space on slab (Espace vert sur dalle)'), ('bande filtrante', 'Filter strip (Bande filtrante)'), ('collecteur en beton poreux', 'Porous concrete collector (Collecteur en béton poreux)')], max_length=100, null=True, verbose_name='Structure type')),
('geometry_type', models.CharField(blank=True, choices=[('point', 'Point'), ('line', 'Line'), ('polygon', 'Polygon')], max_length=50, null=True, verbose_name='Geometry type')),
('project_manager', models.CharField(blank=True, choices=[('DPV', 'DPV'), ('DITP', 'DITP'), ('DEN', 'DEN'), ('DPO', 'DPO'), ('Beliris', 'Beliris'), ('Commune', 'Commune')], max_length=50, null=True, verbose_name='Project manager')),
('location_precision', models.CharField(blank=True, choices=[('precise', 'Precise (Précise)'), ('supposee', 'Assumed (Supposée)')], max_length=50, null=True, verbose_name='Location precision')),
('construction_status', models.CharField(blank=True, choices=[('etude', 'Design (Étude)'), ('travaux', 'Construction (Travaux)'), ('reception provisoire', 'Provisional acceptance (Réception provisoire)'), ('reception definitive', 'Final acceptance (Réception définitive)')], max_length=100, null=True, verbose_name='Construction status')),
('commissioning_date', models.DateField(blank=True, null=True, verbose_name='Commissioning date')),
('condition', models.CharField(blank=True, choices=[('bon', 'Good (Bon)'), ('examen a programmer', 'Inspection to schedule (Examen à programmer)'), ('intervention curative necessaire', 'Curative action needed (Intervention curative nécessaire)'), ('refection totale necessaire', 'Total rehabilitation needed (Réfection totale nécessaire)'), ('travaux entretien a realiser', 'Maintenance works to perform (Travaux entretien à réaliser)'), ('travaux maintenance', 'Maintenance works (Travaux maintenance)'), ('travaux maintenance a programmer', 'Maintenance works to schedule (Travaux maintenance à programmer)')], max_length=100, null=True, verbose_name='Condition')),
('administrator', models.CharField(blank=True, max_length=500, null=True, verbose_name='Administrator')),
('monitoring', models.CharField(blank=True, max_length=500, null=True, verbose_name='Monitoring')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('has_filter', models.BooleanField(blank=True, null=True, verbose_name='Has filter')),
('sewer_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Sewer connection')),
('separate_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Separate network connection')),
('hydro_network_connection', models.CharField(blank=True, choices=[('connecte', 'Connected (Connecté)'), ('non connecte', 'Not connected (Non connecté)')], max_length=50, null=True, verbose_name='Hydro network connection')),
('surface_area', models.FloatField(blank=True, null=True, verbose_name='Surface area (m²)')),
('length', models.FloatField(blank=True, null=True, verbose_name='Length (m)')),
('coating_category', models.CharField(blank=True, choices=[('Vegetal', 'Vegetal (Végétal)'), ('Structure enherbee', 'Grassed structure (Structure enherbée)'), ('Materiau non agrege', 'Unbound material (Matériau non agrégé)'), ('Structure a remplissage mineral', 'Mineral-filled structure (Structure à remplissage minéral)'), ('Revetement sureleve', 'Raised coating (Revêtement surélevé)'), ('Modulaire poreux', 'Porous modular (Modulaire poreux)'), ('Continu poreux', 'Porous continuous (Continu poreux)'), ('Impermeable', 'Impermeable (Imperméable)')], max_length=100, null=True, verbose_name='Coating category')),
('coating_type', models.CharField(blank=True, choices=[('Pleine terre', 'Open soil (Pleine terre)'), ('Melange terre-pierre', 'Soil-stone mixture (Mélange terre-pierre)'), ('Paves a joints larges enherbes', 'Grassed wide-joint pavers (Pavés à joints larges enherbés)'), ('Dalles gazon en beton', 'Concrete grass grids (Dalles gazon en béton)'), ('Dalles alveolaires vegetalisees', 'Vegetated cellular tiles (Dalles alvéolaires végétalisées)'), ('Mulch vegetal', 'Woodchips/mulch (Mulch végétal)'), ('Graviers calcaires', 'Limestone gravel (Graviers calcaires)'), ('Graviers non calcaires', 'Non-limestone gravel (Graviers non calcaires)'), ('Terre battue', 'Dirt ground (Terre battue)'), ('Paves a joints larges engravillonnes', 'Gravel wide-joint pavers (Pavés à joints larges engravillonnés)'), ('Dalles alveolaires en gravier', 'Gravel cellular tiles (Dalles alvéolaires en gravier)'), ('Dalles en PEHD remplies de pave', 'HDPE tiles filled with pavers (Dalles en PEHD remplies de pavé)'), ('Grilles', 'Grates (Grilles)'), ('Platelages bois', 'Wooden decking (Platelages bois)'), ('Pave en beton poreux', 'Porous concrete pavers (Pavé en béton poreux)'), ('Dalle en beton poreux', 'Porous concrete slab (Dalle en béton poreux)'), ('Enrobes bitumineux poreux', 'Porous asphalt (Enrobés bitumineux poreux)'), ('Betons poreux', 'Porous concrete (Bétons poreux)'), ('Graviers lies a la resine', 'Resin-bound gravel (Graviers liés à la résine)'), ('Graviers concasses lies aux polymeres', 'Polymer-bound crushed gravel (Graviers concassés liés aux polymères)'), ('Revetements EPDM et ESBR', 'EPDM and SBR rubber coatings (Revêtements EPDM et ESBR)'), ('Revetements en beton de ciment non permeables', 'Non-porous cement concrete coatings (Revêtements en béton de ciment non perméables)'), ('Revetements bitumineux non permeables', 'Non-porous asphalt coatings (Revêtements bitumineux non perméables)'), ('Dallages non permeables', 'Non-porous tiling (Dallages non perméables)'), ('Pavages non permeables', 'Non-porous paving (Pavages non perméables)'), ('Autres revetements non permeables', 'Other non-porous coatings (Autres revêtements non perméables)')], max_length=100, null=True, verbose_name='Coating type')),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.naturerwiasset', verbose_name='Remplacé par')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
],
options={
'verbose_name': 'RWI Asset',
'verbose_name_plural': 'RWI Assets',
},
),
migrations.CreateModel(
name='NatureRWIAssetDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True, verbose_name='Description du document')),
('upload_date', models.DateTimeField(auto_now=True, verbose_name='Date de téléversement')),
('status', models.CharField(choices=[('active', 'Actif'), ('archived', 'Archivé')], default='active')),
('file', models.FileField(max_length=255, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/rwi/', verbose_name='File')),
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to='assets_documents/nature/rwi/')),
('managed_document', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_sources', to='documents.manageddocument')),
('rwi_asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='assets.naturerwiasset', verbose_name='RWI Asset')),
('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Ajouté par')),
],
options={
'verbose_name': 'RWI Asset Document',
'verbose_name_plural': 'RWI Asset Documents',
},
),
migrations.DeleteModel(
name='NatureGIEPAsset',
),
migrations.DeleteModel(
name='NatureGIEPAssetDocument',
),
]

View file

@ -1,16 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-27 12:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0007_remove_naturegiepassetdocument_giep_asset_and_more'),
]
operations = [
migrations.DeleteModel(
name='NatureHollowGreenSpace',
),
]

View file

@ -1,28 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-30 13:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_delete_naturehollowgreenspace'),
]
operations = [
migrations.AddField(
model_name='signpaneltype',
name='default_diameter',
field=models.FloatField(blank=True, null=True, verbose_name='Diamètre par défaut (cm)'),
),
migrations.AddField(
model_name='signpaneltype',
name='default_height',
field=models.FloatField(blank=True, null=True, verbose_name='Hauteur par défaut (cm)'),
),
migrations.AddField(
model_name='signpaneltype',
name='default_width',
field=models.FloatField(blank=True, null=True, verbose_name='Largeur par défaut (cm)'),
),
]

View file

@ -1,71 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-22 08:31
import django.contrib.gis.db.models.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0009_signpaneltype_default_diameter_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='TrafficLightRadarModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('description', models.TextField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
],
options={
'verbose_name': 'Traffic Light Radar Model',
'verbose_name_plural': 'Traffic Light Radar Models',
},
),
migrations.CreateModel(
name='TrafficLightRadar',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('geojson', models.TextField(blank=True, null=True)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('intersection', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='radars', to='assets.trafficlightintersection')),
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.trafficlightradar', verbose_name='Remplacé par')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
('model', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='assets.trafficlightradarmodel')),
],
options={
'verbose_name': 'Radar',
'verbose_name_plural': 'Radars',
},
),
]

View file

@ -1,44 +0,0 @@
from django.db import migrations
def create_radar_preventif_category(apps, schema_editor):
Thematic = apps.get_model('common', 'Thematic')
AssetCategory = apps.get_model('assets', 'AssetCategory')
# Get trafficlights thematic
try:
tl_thematic = Thematic.objects.get(code='trafficlights')
except Thematic.DoesNotExist:
tl_thematic = Thematic.objects.create(
code='trafficlights',
name_fr='Feux de signalisation',
name_nl='Verkeerslichten'
)
# Get or create radar_preventif category
category, created = AssetCategory.objects.get_or_create(
code='TL_RADAR_PREVENTIF',
defaults={
'name_fr': 'Radar Préventif',
'name_nl': 'Preventieve radar',
'thematic': tl_thematic
}
)
if not created and category.thematic != tl_thematic:
category.thematic = tl_thematic
category.save()
def reverse_radar_preventif_category(apps, schema_editor):
AssetCategory = apps.get_model('assets', 'AssetCategory')
AssetCategory.objects.filter(code='TL_RADAR_PREVENTIF').delete()
class Migration(migrations.Migration):
dependencies = [
('assets', '0010_trafficlightradarmodel_trafficlightradar'),
('common', '0001_initial'),
]
operations = [
migrations.RunPython(create_radar_preventif_category, reverse_radar_preventif_category),
]

View file

@ -1,28 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-22 12:43
from django.db import migrations, models
def populate_is_radar(apps, schema_editor):
TrafficLightIntersection = apps.get_model('assets', 'TrafficLightIntersection')
TrafficLightRadar = apps.get_model('assets', 'TrafficLightRadar')
# Intersections linked to at least one radar are radar intersections
radar_intersection_ids = TrafficLightRadar.objects.filter(intersection__isnull=False).values_list('intersection_id', flat=True).distinct()
TrafficLightIntersection.objects.filter(id__in=radar_intersection_ids).update(is_radar=True)
class Migration(migrations.Migration):
dependencies = [
('assets', '0011_create_radar_preventif_category'),
]
operations = [
migrations.AddField(
model_name='trafficlightintersection',
name='is_radar',
field=models.BooleanField(default=False, verbose_name='Est un radar ?'),
),
migrations.RunPython(populate_is_radar, migrations.RunPython.noop),
]

View file

@ -1,169 +0,0 @@
from django.db import migrations
def update_rwi_codes(apps, schema_editor):
NatureRWIAsset = apps.get_model('assets', 'NatureRWIAsset')
# We try-catch the GuidedOperationData import because it's in another app,
# and we want to ensure robust execution.
try:
GuidedOperationData = apps.get_model('interventions', 'GuidedOperationData')
except LookupError:
GuidedOperationData = None
# Mappings from structure type to acronym
mappings = {
'avaloir': 'AV',
'bordure sequencee': 'BS',
'bouche injection': 'BI',
'clapet anti-retour': 'CA',
'empierrement sedimentation': 'ES',
'kerb cell': 'KC',
'puits infiltration': 'PI',
'surverse': 'SU',
'vanne isolation': 'VI',
'vanne regulation': 'VR',
'gargouille de trottoir': 'GT',
'filet eau': 'FE',
'avaloir de rail': 'AR',
'bordure arasee': 'BA',
'caniveau infiltrant': 'CI',
'drain': 'D',
'trop plein': 'TP',
'reseau egout separatif': 'RES',
'bassin sec/infiltration': 'OED',
'bassin en eau': 'OED',
'fosse': 'EVC',
'jardin de pluie': 'EVC',
'noue': 'EVC',
'massif drainant': 'OED',
'tranchee drainante': 'OED',
'toiture stockante': 'OSI',
'toiture verte': 'OSI',
'bassin orage': 'OED',
'SAUL-vide': 'OED',
'SAUL-terre': 'TA',
'citerne': 'OED',
'espace vert sur dalle': 'EVC',
'bande filtrante': 'EVC',
'collecteur en beton poreux': 'OED',
'chambre de visite': 'CV',
}
code_updates = {}
for asset in NatureRWIAsset.objects.all():
old_code = asset.code
# Calculate acronym
struct_type_lower = asset.structure_type.strip().lower() if asset.structure_type else None
acronym = None
if asset.code_name:
acronym = asset.code_name.strip().upper()
elif struct_type_lower in mappings:
acronym = mappings[struct_type_lower]
elif asset.geometry_type:
acronym = asset.geometry_type.upper()
else:
acronym = 'UNK'
fid = asset.external_id or asset.id
new_code = f"RWI_{acronym}_{fid:03d}"
if old_code != new_code:
asset.code = new_code
if asset.name_fr == old_code:
asset.name_fr = new_code
if asset.name_nl == old_code:
asset.name_nl = new_code
asset.save(update_fields=['code', 'name_fr', 'name_nl'])
code_updates[old_code] = new_code
# Update GuidedOperationData JSON references
if GuidedOperationData and code_updates:
for gd in GuidedOperationData.objects.all():
modified = False
data = gd.data
if not isinstance(data, dict):
continue
def replace_codes(obj):
nonlocal modified
if isinstance(obj, dict):
for k, v in list(obj.items()):
if k == 'rwi_asset_code' and v in code_updates:
obj[k] = code_updates[v]
modified = True
else:
replace_codes(v)
elif isinstance(obj, list):
for item in obj:
replace_codes(item)
replace_codes(data)
if modified:
gd.data = data
gd.save(update_fields=['data'])
def reverse_rwi_codes(apps, schema_editor):
NatureRWIAsset = apps.get_model('assets', 'NatureRWIAsset')
try:
GuidedOperationData = apps.get_model('interventions', 'GuidedOperationData')
except LookupError:
GuidedOperationData = None
code_updates_reverse = {}
for asset in NatureRWIAsset.objects.all():
old_code = asset.code
# Calculate old format: RWI_{geometry_type.upper()}_{external_id:03d}
geom_type_upper = asset.geometry_type.upper() if asset.geometry_type else 'POLYGON'
fid = asset.external_id or asset.id
original_code = f"RWI_{geom_type_upper}_{fid:03d}"
if old_code != original_code:
asset.code = original_code
if asset.name_fr == old_code:
asset.name_fr = original_code
if asset.name_nl == old_code:
asset.name_nl = original_code
asset.save(update_fields=['code', 'name_fr', 'name_nl'])
code_updates_reverse[old_code] = original_code
# Revert GuidedOperationData JSON references
if GuidedOperationData and code_updates_reverse:
for gd in GuidedOperationData.objects.all():
modified = False
data = gd.data
if not isinstance(data, dict):
continue
def replace_codes(obj):
nonlocal modified
if isinstance(obj, dict):
for k, v in list(obj.items()):
if k == 'rwi_asset_code' and v in code_updates_reverse:
obj[k] = code_updates_reverse[v]
modified = True
else:
replace_codes(v)
elif isinstance(obj, list):
for item in obj:
replace_codes(item)
replace_codes(data)
if modified:
gd.data = data
gd.save(update_fields=['data'])
class Migration(migrations.Migration):
dependencies = [
('assets', '0012_trafficlightintersection_is_radar'),
]
operations = [
migrations.RunPython(update_rwi_codes, reverse_rwi_codes),
]

View file

@ -1,17 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-25 12:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0013_update_rwi_assets_codification'),
]
operations = [
migrations.RemoveField(
model_name='naturerwiasset',
name='location_precision',
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-26 12:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0014_remove_nature_rwi_asset_location_precision'),
]
operations = [
migrations.AddField(
model_name='trafficlightradar',
name='pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.publiclightingpole'),
),
]

View file

@ -1,91 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-26 12:41
import django.contrib.gis.db.models.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0015_trafficlightradar_pole'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='trafficlightradar',
name='publiclighting_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.publiclightingpole'),
),
migrations.AddField(
model_name='trafficlightradar',
name='trafficlights_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightpole'),
),
migrations.CreateModel(
name='TrafficLightRadarPole',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
('serial_number', models.CharField(blank=True, max_length=200, null=True)),
('warranty_duration', models.IntegerField(blank=True, null=True, verbose_name='Duration of the warranty (months)')),
('last_inspection_date', models.DateTimeField(blank=True, null=True)),
('last_preventive_date', models.DateTimeField(blank=True, null=True)),
('last_corrective_date', models.DateTimeField(blank=True, null=True)),
('last_survey_date', models.DateTimeField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('marked_as_removed_date', models.DateTimeField(blank=True, null=True, verbose_name='Date de marquage comme retiré')),
('note', models.TextField(blank=True, null=True, verbose_name='Note')),
('validation_status', models.CharField(blank=True, choices=[('to_be_validated', 'À valider'), ('validated', 'Validé'), ('to_be_completed', 'À compléter'), ('rejected', 'Rejeté')], max_length=50, null=True, verbose_name='Statut de validation')),
('updated_at', models.DateTimeField(auto_now=True)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('geojson', models.TextField(blank=True, null=True)),
('colour', models.CharField(blank=True, max_length=200, null=True, verbose_name='Couleur')),
('ral', models.CharField(blank=True, max_length=200, null=True, verbose_name='RAL')),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('intersection', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='radar_poles', to='assets.trafficlightintersection')),
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='assets.trafficlightradarpole', verbose_name='Remplacé par')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
],
options={
'verbose_name': 'Radar Pole',
'verbose_name_plural': 'Radar Poles',
},
),
migrations.AlterField(
model_name='trafficlightradar',
name='pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightradarpole'),
),
migrations.CreateModel(
name='TrafficLightRadarPoleModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('description', models.TextField(blank=True, null=True)),
('brand', models.CharField(blank=True, max_length=500, null=True)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='trafficlightradarpole',
name='model',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='assets.trafficlightradarpolemodel'),
),
]

View file

@ -1,44 +0,0 @@
from django.db import migrations
def create_radar_pole_category(apps, schema_editor):
Thematic = apps.get_model('common', 'Thematic')
AssetCategory = apps.get_model('assets', 'AssetCategory')
# Get trafficlights thematic
try:
tl_thematic = Thematic.objects.get(code='trafficlights')
except Thematic.DoesNotExist:
tl_thematic = Thematic.objects.create(
code='trafficlights',
name_fr='Feux de signalisation',
name_nl='Verkeerslichten'
)
# Get or create radar_pole category
category, created = AssetCategory.objects.get_or_create(
code='TL_RADAR_POLE',
defaults={
'name_fr': 'Poteau radar',
'name_nl': 'Radarpaal',
'thematic': tl_thematic
}
)
if not created and category.thematic != tl_thematic:
category.thematic = tl_thematic
category.save()
def reverse_radar_pole_category(apps, schema_editor):
AssetCategory = apps.get_model('assets', 'AssetCategory')
AssetCategory.objects.filter(code='TL_RADAR_POLE').delete()
class Migration(migrations.Migration):
dependencies = [
('assets', '0016_trafficlightradar_publiclighting_pole_and_more'),
('common', '0001_initial'),
]
operations = [
migrations.RunPython(create_radar_pole_category, reverse_radar_pole_category),
]

View file

@ -1,27 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-26 16:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0017_create_radar_pole_category'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.AlterModelOptions(
name='publiclightingpole',
options={'verbose_name': 'Public lighting pole', 'verbose_name_plural': 'Public lighting poles'},
),
migrations.AlterModelOptions(
name='trafficlightpole',
options={'verbose_name': 'Traffic light pole', 'verbose_name_plural': 'Traffic light poles'},
),
migrations.AddField(
model_name='assetcategory',
name='allowed_models',
field=models.ManyToManyField(blank=True, help_text="Les types de modèles d'assets (Django) autorisés pour cette catégorie.", related_name='asset_categories', to='contenttypes.contenttype', verbose_name='Modèles Django autorisés'),
),
]

View file

@ -1,70 +0,0 @@
from django.db import migrations
def populate_allowed_models(apps, schema_editor):
AssetCategory = apps.get_model('assets', 'AssetCategory')
ContentType = apps.get_model('contenttypes', 'ContentType')
# Mapping of category codes to Django model name strings (lowercase)
mapping = {
# Traffic lights
'TL_POTEAU': ['trafficlightpole'],
'TL_CABLE': ['trafficlightcable'],
'TL_LANTERNE': ['trafficlightlantern'],
'TL_REGELAAR': ['trafficlightcontroller'],
'TL_ARMOIRE': ['trafficlightelectricalcabinet'],
'TL_DETEC_BP': ['trafficlightdetector'],
'TL_DETEC_RADAR': ['trafficlightdetector'],
'TL_DETEC_VIDEO': ['trafficlightdetector'],
'TL_DETEC_OTHER': ['trafficlightdetector'],
'TL_BORNIER': ['trafficlightaccessory'],
'TL_DIVERS': ['trafficlightaccessory'],
'TL_RADAR_PREVENTIF': ['trafficlightradar'],
'TL_RADAR_POLE': ['trafficlightradarpole'],
# Public lighting
'LI_poles': ['publiclightingpole'],
'LI_lightfixtures': ['publiclightinglightfixture'],
'LI_electrical_cabinets': ['publiclightingelectricalcabinet'],
# Cleanliness
'CL_BASKET': ['cleanlitterbin'],
'CL_BUBBLE': ['cleanglasscontainer'],
# Nature
'NAT_GREEN_AREAS': ['naturegreensurface'],
'NAT_TREES': ['naturetree'],
'NAT_RWI': ['naturerwiasset'],
'NAT_FURNITURES': ['naturebench', 'natureshrub'],
# Artworks
'ART_MONUMENT': ['artworkmonument'],
'ART_FONTAIN': ['artworkfountain'],
}
for category_code, model_names in mapping.items():
try:
category = AssetCategory.objects.get(code=category_code)
for model_name in model_names:
try:
ct = ContentType.objects.get(app_label='assets', model=model_name)
category.allowed_models.add(ct)
except ContentType.DoesNotExist:
print(f"ContentType assets.{model_name} not found.")
except AssetCategory.DoesNotExist:
pass
def clear_allowed_models(apps, schema_editor):
AssetCategory = apps.get_model('assets', 'AssetCategory')
for category in AssetCategory.objects.all():
category.allowed_models.clear()
class Migration(migrations.Migration):
dependencies = [
('assets', '0018_alter_publiclightingpole_options_and_more'),
]
operations = [
migrations.RunPython(populate_allowed_models, clear_allowed_models),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-27 07:05
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0019_populate_assetcategory_allowed_models'),
]
operations = [
migrations.AlterField(
model_name='trafficlightradarpole',
name='model',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.trafficlightradarpolemodel'),
),
]

View file

@ -1,39 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-03 09:16
from django.db import migrations
def set_default_validation_status(apps, schema_editor):
model_names = [
'trafficlightpole', 'trafficlightcable', 'trafficlightlantern', 'trafficlightcontroller',
'trafficlightcontrollerhardware', 'trafficlightdetector', 'trafficlightnetworkhardware',
'trafficlightvoltagehardware', 'trafficlightelectricalcabinet', 'trafficlightelectricalcabinetcontent',
'trafficlightaccessory', 'trafficlightprogramming', 'trafficlightradarpole', 'trafficlightradar',
'publiclightingpole', 'publiclightinglightfixture', 'publiclightingelectricalcabinet', 'artworkmonument',
'artworkfountain', 'naturegreensurface', 'naturetree', 'natureshrub', 'naturebench',
'naturerwiasset', 'structuregeoasset', 'controlcentergeoasset', 'signpole', 'signpanel',
'signparkingspot', 'roadway', 'roadsidewalk', 'roadfurniture', 'roadconcreteblockset',
'roaddrain', 'itsgeoasset', 'cleanlitterbin', 'cleanglasscontainer'
]
for model_name in model_names:
try:
Model = apps.get_model('assets', model_name)
# Update all active assets with no validation status to 'validated'
Model.objects.filter(status='active', validation_status__isnull=True).update(validation_status='validated')
Model.objects.filter(status='active', validation_status='').update(validation_status='validated')
except LookupError:
pass
def reverse_default_validation_status(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('assets', '0020_alter_trafficlightradarpole_model'),
]
operations = [
migrations.RunPython(set_default_validation_status, reverse_default_validation_status),
]

View file

@ -1,48 +0,0 @@
# Generated by hand
from django.db import migrations
def sync_trafficlights_documents(apps, schema_editor):
from assets.models import TrafficLightIntersection as ActiveIntersection, TrafficLightIntersectionDocument as ActiveIntersectionDoc
from assets.views.trafficlights import ensure_intersection_document_folder, generate_dxf_overlay_managed_document
from documents.models import ensure_document_for_legacy
for intersection in ActiveIntersection.objects.all():
try:
ensure_intersection_document_folder(intersection)
except Exception as e:
print(f"Error ensuring document folder for intersection {intersection.id}: {e}")
for doc in ActiveIntersectionDoc.objects.all():
try:
import os
if not doc.file or not doc.file.name:
continue
# If the file does not exist on disk, we skip it gracefully
if not os.path.exists(doc.file.path):
print(f"Migration: File not found on disk, skipping sync for {doc.file.name}")
continue
# Sync it to the ManagedDocument system
ensure_document_for_legacy(doc)
# If it's a DXF, generate the overlay
if doc.file.name.lower().endswith('.dxf') and doc.managed_document:
generate_dxf_overlay_managed_document(doc.managed_document, user=doc.uploaded_by)
except Exception as e:
print(f"Error syncing document during migration for {doc.file.name if doc.file else 'None'}: {e}")
def reverse_sync_trafficlights_documents(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('assets', '0021_set_default_validation_status_for_active'),
('documents', '0003_manageddocument_georeference_coords_and_more'),
]
operations = [
migrations.RunPython(sync_trafficlights_documents, reverse_sync_trafficlights_documents),
]

View file

@ -1,39 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-10 09:13
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0022_sync_trafficlights_documents'),
]
operations = [
migrations.AddField(
model_name='trafficlightradar',
name='power_publiclighting_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='powered_radars', to='assets.publiclightingpole', verbose_name='Public Lighting Pole (Power)'),
),
migrations.AddField(
model_name='trafficlightradar',
name='power_trafficlights_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='powered_radars', to='assets.trafficlightpole', verbose_name='Traffic Light Pole (Power)'),
),
migrations.AlterField(
model_name='trafficlightradar',
name='pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightradarpole', verbose_name='Radar Pole (Support)'),
),
migrations.AlterField(
model_name='trafficlightradar',
name='publiclighting_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.publiclightingpole', verbose_name='Public Lighting Pole (Support)'),
),
migrations.AlterField(
model_name='trafficlightradar',
name='trafficlights_pole',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='radars', to='assets.trafficlightpole', verbose_name='Traffic Light Pole (Support)'),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import authorizations.models
import django.contrib.gis.db.models.fields

View file

@ -1,30 +0,0 @@
from django.db import migrations
def create_authorizations_view(apps, schema_editor):
AppView = apps.get_model('common', 'AppView')
AppView.objects.get_or_create(
code='authorizations',
defaults={
'name_fr': 'Autorisations',
'name_nl': 'Autorisaties',
'icon': 'bi-file-earmark-text',
'url_name': 'authorizations:authorization_list',
'is_default': False,
'order': 40,
}
)
def remove_authorizations_view(apps, schema_editor):
AppView = apps.get_model('common', 'AppView')
AppView.objects.filter(code='authorizations').delete()
class Migration(migrations.Migration):
dependencies = [
('authorizations', '0001_initial'),
('common', '0011_userwarehouselocationaccess_and_more'),
]
operations = [
migrations.RunPython(create_authorizations_view, remove_authorizations_view),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.contrib.gis.db.models.fields
import django.contrib.postgres.fields
@ -113,6 +113,7 @@ class Migration(migrations.Migration):
('can_process_observations', models.BooleanField(default=False, verbose_name='Peut traiter les observations')),
('can_view_projects', models.BooleanField(default=False, verbose_name='Peut voir les projets')),
('can_edit_projects', models.BooleanField(default=False, verbose_name='Peut éditer les projets')),
('can_validate_assets', models.BooleanField(default=False, verbose_name='Peut valider les assets')),
],
options={
'verbose_name': 'Thématique utilisateur',
@ -150,6 +151,18 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Accès Entrepôts',
},
),
migrations.CreateModel(
name='UserWarehouseLocationAccess',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('can_perform_inventory', models.BooleanField(default=False, verbose_name="Peut faire l'inventaire")),
('can_validate_inventory', models.BooleanField(default=False, verbose_name="Peut valider l'inventaire")),
],
options={
'verbose_name': 'Accès Emplacement',
'verbose_name_plural': 'Accès Emplacements',
},
),
migrations.CreateModel(
name='AssetTypePermissionConfig',
fields=[
@ -275,8 +288,8 @@ class Migration(migrations.Migration):
('can_view', models.BooleanField(default=False, verbose_name='Peut visualiser')),
('can_edit', models.BooleanField(default=False, verbose_name='Peut modifier')),
('can_delete', models.BooleanField(default=False, verbose_name='Peut supprimer')),
('editable_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs que cet utilisateur peut modifier sur cet objet. Si renseignée (non vide), remplace complètement les champs autorisés par le rôle.', null=True, size=None, verbose_name='Champs modifiables')),
('visible_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs que cet utilisateur peut voir sur cet objet. Si renseignée (non vide), remplace complètement les champs affichés par défaut.', null=True, size=None, verbose_name='Champs visibles')),
('editable_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs que cet utilisateur peut modifier sur cet objet. Si renseignée (non vide), remplace complètement les champs autorisés par le rôle.', null=True, verbose_name='Champs modifiables')),
('visible_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs que cet utilisateur peut voir sur cet objet. Si renseignée (non vide), remplace complètement les champs affichés par défaut.', null=True, verbose_name='Champs visibles')),
('content_type', models.ForeignKey(help_text="Le modèle Django de l'asset ou de la location concernée.", on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name="Type d'objet")),
],
options={
@ -291,8 +304,8 @@ class Migration(migrations.Migration):
('can_view', models.BooleanField(default=False, verbose_name='Peut visualiser')),
('can_edit', models.BooleanField(default=False, verbose_name='Peut modifier')),
('can_delete', models.BooleanField(default=False, verbose_name='Peut supprimer')),
('editable_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs modifiables pour tous les objets de ce type. Si renseignée (non vide), remplace les champs autorisés par le rôle. Peut être écrasée objet par objet via UserAssetAccess.', null=True, size=None, verbose_name='Champs modifiables')),
('visible_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs visibles pour tous les objets de ce type. Si renseignée (non vide), remplace les champs affichés par défaut. Peut être écrasée objet par objet via UserAssetAccess.', null=True, size=None, verbose_name='Champs visibles')),
('editable_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs modifiables pour tous les objets de ce type. Si renseignée (non vide), remplace les champs autorisés par le rôle. Peut être écrasée objet par objet via UserAssetAccess.', null=True, verbose_name='Champs modifiables')),
('visible_fields', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, help_text='Liste des noms de champs visibles pour tous les objets de ce type. Si renseignée (non vide), remplace les champs affichés par défaut. Peut être écrasée objet par objet via UserAssetAccess.', null=True, verbose_name='Champs visibles')),
('content_type', models.ForeignKey(help_text="Le modèle Django concerné (s'applique à toutes ses instances).", on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name="Type d'asset")),
],
options={
@ -312,14 +325,23 @@ class Migration(migrations.Migration):
('hide_intervention_map_by_default', models.BooleanField(default=False, help_text="Affiche un bouton permettant d'afficher la carte au besoin.", verbose_name='Masquer la carte des interventions par défaut')),
('intervention_list_layout', models.CharField(choices=[('both', 'Tableau et carte'), ('table_only', 'Tableau seul'), ('map_only', 'Carte seule')], default='both', help_text='Préférence de disposition pour la vue liste des interventions.', max_length=20, verbose_name='Disposition de la liste des interventions')),
('intervention_list_filters', models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue liste des interventions.', verbose_name='Filtres de la liste des interventions')),
('mobile_intervention_list_filters', models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue mobile de la liste des interventions.', verbose_name='Filtres de la liste des interventions mobile')),
('intervention_list_sort', models.JSONField(blank=True, default=dict, help_text='Préférences de tri pour la vue liste des interventions.', verbose_name='Tri de la liste des interventions')),
('intervention_list_columns', models.JSONField(blank=True, default=None, help_text='Liste des champs masqués dans le tableau de la vue liste des interventions.', null=True, verbose_name='Colonnes masquées de la liste des interventions')),
('observation_list_filters', models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue liste des observations.', verbose_name='Filtres de la liste des observations')),
('observation_list_sort', models.JSONField(blank=True, default=dict, help_text='Préférences de tri pour la vue liste des observations.', verbose_name='Tri de la liste des observations')),
('project_list_filters', models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue liste des projets.', verbose_name='Filtres de la liste des projets')),
('project_list_sort', models.JSONField(blank=True, default=dict, help_text='Préférences de tri pour la vue liste des projets.', verbose_name='Tri de la liste des projets')),
('assets_list_layout', models.CharField(choices=[('both', 'Tableau et carte'), ('table_only', 'Tableau seul'), ('map_only', 'Carte seule')], default='both', help_text='Préférence de disposition pour la vue liste des assets.', max_length=20, verbose_name='Disposition de la liste des assets')),
('auto_logout_hours', models.PositiveIntegerField(blank=True, help_text="Nombre d'heures après la connexion avant déconnexion automatique. Laisser vide pour désactiver.", null=True, verbose_name='Déconnexion automatique (heures)')),
('panoramax_can_upload', models.BooleanField(default=False, help_text="Permet à l'utilisateur d'uploader de nouvelles photos ou vidéos 360 pour Panoramax.", verbose_name='Peut uploader des photos 360')),
('can_add_unique_tag_documents', models.BooleanField(default=False, help_text="Permet aux utilisateurs externes d'ajouter/modifier des documents avec des tags uniques (CME, PIS, As-Built, etc.).", verbose_name='Peut ajouter des documents avec tags uniques')),
('esign_can_edit', models.BooleanField(default=False, help_text='Permission pour éditer les panneaux et les poteaux.', verbose_name='eSign : Peut éditer')),
('esign_can_manage_rcp', models.BooleanField(default=False, help_text='Permission pour créer, modifier et supprimer les RCP.', verbose_name='eSign : Peut gérer les RCP')),
('esign_can_validate_rcp', models.BooleanField(default=False, help_text='Permission pour valider les RCP.', verbose_name='eSign : Peut valider les RCP')),
('reporting_preferences', models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour les différentes pages du reporting.', verbose_name='Préférences du reporting')),
('assets_index_preferences', models.JSONField(blank=True, default=dict, help_text='Préférences de la page assets (thématiques actives, catégories, filtres, position carte).', verbose_name='Préférences de la page index unifiée')),
('calendar_preferences', models.JSONField(blank=True, default=dict, help_text="Préférences de la page calendrier (mode d'affichage, zoom, filtres actifs, état d'expansion du backlog).", null=True, verbose_name='Préférences du calendrier')),
('accessible_views', models.ManyToManyField(blank=True, help_text="Les vues/applications auxquelles l'utilisateur a accès.", related_name='user_configs', to='common.appview', verbose_name='Vues accessibles')),
('approval_categories', models.ManyToManyField(blank=True, help_text="Catégories d'assets pour lesquelles l'utilisateur approuve des interventions. Si vide, aucun filtre de catégorie n'est appliqué.", related_name='approver_user_configs', to='assets.assetcategory', verbose_name="Catégories d'approbation")),
],

View file

@ -1,7 +1,6 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
@ -12,7 +11,6 @@ class Migration(migrations.Migration):
dependencies = [
('common', '0001_initial'),
('contracts', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
@ -26,88 +24,4 @@ class Migration(migrations.Migration):
name='default_thematic',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_user_accesses', to='common.thematic'),
),
migrations.AddField(
model_name='userconfig',
name='linked_configs',
field=models.ManyToManyField(blank=True, to='common.userconfig', verbose_name='Comptes liés'),
),
migrations.AddField(
model_name='userconfig',
name='roles',
field=models.ManyToManyField(blank=True, to='common.role'),
),
migrations.AddField(
model_name='userconfig',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='config', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='userassettypeaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asset_type_accesses', to='common.userconfig', verbose_name='Configuration utilisateur'),
),
migrations.AddField(
model_name='userassetaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asset_accesses', to='common.userconfig', verbose_name='Configuration utilisateur'),
),
migrations.CreateModel(
name='UserApprovalConfig',
fields=[
],
options={
'verbose_name': 'Configuration approbateur',
'verbose_name_plural': 'Configurations approbateurs',
'ordering': ['user__username'],
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('common.userconfig',),
),
migrations.AddField(
model_name='usercontractaccess',
name='contract',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.contract'),
),
migrations.AddField(
model_name='usercontractaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.userconfig'),
),
migrations.AddField(
model_name='userconfig',
name='contracts',
field=models.ManyToManyField(related_name='user_accesses', through='common.UserContractAccess', to='contracts.contract'),
),
migrations.AddField(
model_name='usercontractstatuspermission',
name='user_contract',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_permissions', to='common.usercontractaccess'),
),
migrations.AddField(
model_name='userthematics',
name='thematic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.thematic'),
),
migrations.AddField(
model_name='userthematics',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userthematics', to='common.userconfig'),
),
migrations.AddField(
model_name='userconfig',
name='thematics',
field=models.ManyToManyField(related_name='user_accesses', through='common.UserThematics', to='common.thematic'),
),
migrations.AddField(
model_name='userthematicstatuspermission',
name='user_thematic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_permissions', to='common.userthematics'),
),
migrations.AddField(
model_name='userwarehouseaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userwarehouses', to='common.userconfig'),
),
]

View file

@ -1,6 +1,7 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
@ -10,74 +11,99 @@ class Migration(migrations.Migration):
dependencies = [
('common', '0002_initial'),
('stock', '0001_initial'),
('contracts', '0001_initial'),
('sign', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='userwarehouseaccess',
name='warehouse',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='stock.warehouse'),
model_name='userconfig',
name='esign_organizations',
field=models.ManyToManyField(blank=True, help_text="Les gestionnaires (communes, Bruxelles Mobilité) pour lesquels l'utilisateur a des permissions.", related_name='user_configs', to='sign.organization', verbose_name='eSign : Gestionnaires'),
),
migrations.AddField(
model_name='userconfig',
name='warehouses',
field=models.ManyToManyField(blank=True, related_name='user_accesses', through='common.UserWarehouseAccess', to='stock.warehouse'),
name='linked_configs',
field=models.ManyToManyField(blank=True, to='common.userconfig', verbose_name='Comptes liés'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['level'], name='common_requ_level_55ef16_idx'),
migrations.AddField(
model_name='userconfig',
name='roles',
field=models.ManyToManyField(blank=True, to='common.role'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['user'], name='common_requ_user_id_3781cb_idx'),
migrations.AddField(
model_name='userconfig',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='config', to=settings.AUTH_USER_MODEL),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['status_code'], name='common_requ_status__b046e1_idx'),
migrations.AddField(
model_name='userassettypeaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asset_type_accesses', to='common.userconfig', verbose_name='Configuration utilisateur'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['timestamp', 'level'], name='common_requ_timesta_264745_idx'),
),
migrations.AlterUniqueTogether(
name='userassettypeaccess',
unique_together={('user_config', 'content_type')},
),
migrations.AddIndex(
migrations.AddField(
model_name='userassetaccess',
index=models.Index(fields=['content_type', 'object_id'], name='common_user_content_e5ef8d_idx'),
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asset_accesses', to='common.userconfig', verbose_name='Configuration utilisateur'),
),
migrations.AlterUniqueTogether(
name='userassetaccess',
unique_together={('user_config', 'content_type', 'object_id')},
migrations.CreateModel(
name='UserApprovalConfig',
fields=[
],
options={
'verbose_name': 'Configuration approbateur',
'verbose_name_plural': 'Configurations approbateurs',
'ordering': ['user__username'],
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('common.userconfig',),
),
migrations.AlterUniqueTogether(
name='usercontractaccess',
unique_together={('user_config', 'contract')},
migrations.AddField(
model_name='usercontractaccess',
name='contract',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.contract'),
),
migrations.AddIndex(
migrations.AddField(
model_name='usercontractaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.userconfig'),
),
migrations.AddField(
model_name='userconfig',
name='contracts',
field=models.ManyToManyField(related_name='user_accesses', through='common.UserContractAccess', to='contracts.contract'),
),
migrations.AddField(
model_name='usercontractstatuspermission',
index=models.Index(fields=['user_contract', 'status', 'can_view'], name='ucsp_status_canview_idx'),
name='user_contract',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_permissions', to='common.usercontractaccess'),
),
migrations.AlterUniqueTogether(
name='usercontractstatuspermission',
unique_together={('user_contract', 'status')},
migrations.AddField(
model_name='userthematics',
name='thematic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.thematic'),
),
migrations.AlterUniqueTogether(
name='userthematics',
unique_together={('user_config', 'thematic')},
migrations.AddField(
model_name='userthematics',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userthematics', to='common.userconfig'),
),
migrations.AddIndex(
migrations.AddField(
model_name='userconfig',
name='thematics',
field=models.ManyToManyField(related_name='user_accesses', through='common.UserThematics', to='common.thematic'),
),
migrations.AddField(
model_name='userthematicstatuspermission',
index=models.Index(fields=['user_thematic', 'status', 'can_view'], name='utsp_status_canview_idx'),
name='user_thematic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_permissions', to='common.userthematics'),
),
migrations.AlterUniqueTogether(
name='userthematicstatuspermission',
unique_together={('user_thematic', 'status')},
),
migrations.AlterUniqueTogether(
name='userwarehouseaccess',
unique_together={('user_config', 'warehouse')},
migrations.AddField(
model_name='userwarehouseaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userwarehouses', to='common.userconfig'),
),
]

View file

@ -0,0 +1,102 @@
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('common', '0003_initial'),
('stock', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='userwarehouseaccess',
name='warehouse',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='stock.warehouse'),
),
migrations.AddField(
model_name='userconfig',
name='warehouses',
field=models.ManyToManyField(blank=True, related_name='user_accesses', through='common.UserWarehouseAccess', to='stock.warehouse'),
),
migrations.AddField(
model_name='userwarehouselocationaccess',
name='user_config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userlocations', to='common.userconfig'),
),
migrations.AddField(
model_name='userwarehouselocationaccess',
name='warehouse_location',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_accesses', to='stock.warehouselocation', verbose_name='Emplacement'),
),
migrations.AddField(
model_name='userconfig',
name='warehouse_locations',
field=models.ManyToManyField(blank=True, related_name='user_configs', through='common.UserWarehouseLocationAccess', to='stock.warehouselocation'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['level'], name='common_requ_level_55ef16_idx'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['user'], name='common_requ_user_id_3781cb_idx'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['status_code'], name='common_requ_status__b046e1_idx'),
),
migrations.AddIndex(
model_name='requestlog',
index=models.Index(fields=['timestamp', 'level'], name='common_requ_timesta_264745_idx'),
),
migrations.AlterUniqueTogether(
name='userassettypeaccess',
unique_together={('user_config', 'content_type')},
),
migrations.AddIndex(
model_name='userassetaccess',
index=models.Index(fields=['content_type', 'object_id'], name='common_user_content_e5ef8d_idx'),
),
migrations.AlterUniqueTogether(
name='userassetaccess',
unique_together={('user_config', 'content_type', 'object_id')},
),
migrations.AlterUniqueTogether(
name='usercontractaccess',
unique_together={('user_config', 'contract')},
),
migrations.AddIndex(
model_name='usercontractstatuspermission',
index=models.Index(fields=['user_contract', 'status', 'can_view'], name='ucsp_status_canview_idx'),
),
migrations.AlterUniqueTogether(
name='usercontractstatuspermission',
unique_together={('user_contract', 'status')},
),
migrations.AlterUniqueTogether(
name='userthematics',
unique_together={('user_config', 'thematic')},
),
migrations.AddIndex(
model_name='userthematicstatuspermission',
index=models.Index(fields=['user_thematic', 'status', 'can_view'], name='utsp_status_canview_idx'),
),
migrations.AlterUniqueTogether(
name='userthematicstatuspermission',
unique_together={('user_thematic', 'status')},
),
migrations.AlterUniqueTogether(
name='userwarehouseaccess',
unique_together={('user_config', 'warehouse')},
),
migrations.AlterUniqueTogether(
name='userwarehouselocationaccess',
unique_together={('user_config', 'warehouse_location')},
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-23 05:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0003_initial'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='assets_index_preferences',
field=models.JSONField(blank=True, default=dict, help_text='Préférences de la page assets (thématiques actives, catégories, filtres, position carte).', verbose_name='Préférences de la page index unifiée'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-24 09:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0004_userconfig_assets_index_preferences'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='calendar_preferences',
field=models.JSONField(blank=True, default=dict, help_text="Préférences de la page calendrier (mode d'affichage, zoom, filtres actifs, état d'expansion du backlog).", null=True, verbose_name='Préférences du calendrier'),
),
]

View file

@ -1,28 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-30 08:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0005_userconfig_calendar_preferences'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='esign_can_edit',
field=models.BooleanField(default=False, help_text='Permission pour éditer les panneaux et les poteaux.', verbose_name='eSign : Peut éditer'),
),
migrations.AddField(
model_name='userconfig',
name='esign_can_manage_rcp',
field=models.BooleanField(default=False, help_text='Permission pour créer, modifier et supprimer les RCP.', verbose_name='eSign : Peut gérer les RCP'),
),
migrations.AddField(
model_name='userconfig',
name='esign_can_validate_rcp',
field=models.BooleanField(default=False, help_text='Permission pour valider les RCP.', verbose_name='eSign : Peut valider les RCP'),
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-30 10:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0006_userconfig_esign_can_edit_and_more'),
('sign', '0003_alter_signpanel_street_alter_signpole_street'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='esign_organizations',
field=models.ManyToManyField(blank=True, help_text="Les gestionnaires (communes, Bruxelles Mobilité) pour lesquels l'utilisateur a des permissions.", related_name='user_configs', to='sign.organization', verbose_name='eSign : Gestionnaires'),
),
]

View file

@ -1,156 +0,0 @@
# Generated by Django on 2026-06-10
from django.db import migrations
from django.contrib.auth.hashers import make_password
from django.conf import settings
import zipfile
import xml.etree.ElementTree as ET
import unicodedata
import random
import string
import os
def normalize_text(text):
nfkd_form = unicodedata.normalize('NFKD', text)
only_ascii = "".join([c for c in nfkd_form if not unicodedata.combining(c)])
cleaned = "".join([c.lower() for c in only_ascii if c.isalnum()])
return cleaned
def generate_password():
first_char = random.choice(string.ascii_uppercase)
rest_chars = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(11))
return first_char + rest_chars
def create_agents_regie(apps, schema_editor):
User = apps.get_model('auth', 'User')
Role = apps.get_model('common', 'Role')
UserConfig = apps.get_model('common', 'UserConfig')
Company = apps.get_model('contracts', 'Company')
CompanyMember = apps.get_model('contracts', 'CompanyMember')
# Find the ODS file path
ods_path = os.path.join(settings.BASE_DIR, '..', 'external_files', 'agents_regie.ods')
if not os.path.exists(ods_path):
ods_path = os.path.join(settings.BASE_DIR, 'external_files', 'agents_regie.ods')
if not os.path.exists(ods_path):
print(f"\n[WARNING] agents_regie.ods not found at {ods_path}. Skipping user creation.")
return
# Parse ODS file
namespaces = {
'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
'office': 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
}
agents = []
with zipfile.ZipFile(ods_path, 'r') as z:
content_xml = z.read('content.xml')
root = ET.fromstring(content_xml)
tables = root.findall('.//table:table', namespaces)
for table in tables:
rows = table.findall('.//table:table-row', namespaces)
for idx, row in enumerate(rows):
cells = []
for cell in row.findall('.//table:table-cell', namespaces):
repeated = cell.get('{' + namespaces['table'] + '}number-columns-repeated')
rep_count = int(repeated) if repeated else 1
text_elems = cell.findall('.//text:p', namespaces)
cell_text = "".join([t.text if t.text else "" for t in text_elems])
for _ in range(rep_count):
cells.append(cell_text)
if any(cells):
while cells and cells[-1] == "":
cells.pop()
if idx == 0:
continue
if len(cells) >= 2:
name = cells[0].strip()
firstname = cells[1].strip()
if name and firstname:
agents.append((name, firstname))
if not agents:
print("\n[WARNING] No agents found in agents_regie.ods. Skipping.")
return
role_tech, _ = Role.objects.get_or_create(name='technician')
company_regie, _ = Company.objects.get_or_create(name='BM - Régie')
created_users = []
passwords_file_lines = [
"=== BM - Régie : Nouveaux Techniciens ===",
f"Généré le : {os.getenv('CURRENT_TIME', '2026-06-10')}",
"-" * 50
]
for name, firstname in agents:
last_name_formatted = name.capitalize()
first_name_formatted = firstname
first_letter = normalize_text(first_name_formatted)[:1]
last_name_normalized = normalize_text(last_name_formatted)
username = f"{first_letter}{last_name_normalized}"
if User.objects.filter(username=username).exists():
continue
password = generate_password()
# Create user
user = User.objects.create(
username=username,
first_name=first_name_formatted,
last_name=last_name_formatted,
password=make_password(password)
)
# Create UserConfig
config = UserConfig.objects.create(
user=user,
company=company_regie
)
config.roles.add(role_tech)
# Create CompanyMember
CompanyMember.objects.create(
user=user,
name=first_name_formatted
)
created_users.append((username, password))
passwords_file_lines.append(f"Login: {username:<15} | Password: {password}")
if created_users:
print("\n" + "="*80)
print("BM - RÉGIE : UTILISATEURS CRÉÉS")
print("="*80)
for username, password in created_users:
print(f" Login: {username:<15} | Mot de passe: {password}")
print("="*80)
# Write to generated_passwords.txt on production server
passwords_file_path = os.path.join(settings.BASE_DIR, 'generated_passwords.txt')
try:
with open(passwords_file_path, 'w', encoding='utf-8') as f:
f.write("\n".join(passwords_file_lines) + "\n")
print(f"Mots de passe sauvegardés dans : {passwords_file_path}")
except Exception as e:
print(f"[ERROR] Impossible d'écrire les mots de passe dans un fichier: {e}")
print("="*80 + "\n")
def delete_agents_regie(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('common', '0007_userconfig_esign_organizations'),
('contracts', '0006_companymember_teams_m2m'),
]
operations = [
migrations.RunPython(create_agents_regie, reverse_code=delete_agents_regie),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-11 11:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0007_userconfig_esign_organizations'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='mobile_intervention_list_filters',
field=models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue mobile de la liste des interventions.', verbose_name='Filtres de la liste des interventions mobile'),
),
]

View file

@ -1,14 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-11 13:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('common', '0008_create_agents_regie'),
('common', '0008_userconfig_mobile_intervention_list_filters'),
]
operations = [
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-16 10:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0009_merge_20260611_1539'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='project_list_filters',
field=models.JSONField(blank=True, default=dict, help_text='Préférences de filtrage pour la vue liste des projets.', verbose_name='Filtres de la liste des projets'),
),
migrations.AddField(
model_name='userconfig',
name='project_list_sort',
field=models.JSONField(blank=True, default=dict, help_text='Préférences de tri pour la vue liste des projets.', verbose_name='Tri de la liste des projets'),
),
]

View file

@ -1,35 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-17 07:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0010_userconfig_project_list_filters_and_more'),
('stock', '0007_add_comment_by_to_inventoryitem'),
]
operations = [
migrations.CreateModel(
name='UserWarehouseLocationAccess',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('can_perform_inventory', models.BooleanField(default=False, verbose_name="Peut faire l'inventaire")),
('can_validate_inventory', models.BooleanField(default=False, verbose_name="Peut valider l'inventaire")),
('user_config', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='userlocations', to='common.userconfig')),
('warehouse_location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_accesses', to='stock.warehouselocation', verbose_name='Emplacement')),
],
options={
'verbose_name': 'Accès Emplacement',
'verbose_name_plural': 'Accès Emplacements',
'unique_together': {('user_config', 'warehouse_location')},
},
),
migrations.AddField(
model_name='userconfig',
name='warehouse_locations',
field=models.ManyToManyField(blank=True, related_name='user_configs', through='common.UserWarehouseLocationAccess', to='stock.warehouselocation'),
),
]

View file

@ -1,112 +0,0 @@
from django.db import migrations, models
def create_water_thematic_and_transfer_data(apps, schema_editor):
Thematic = apps.get_model('common', 'Thematic')
UserThematics = apps.get_model('common', 'UserThematics')
AssetCategory = apps.get_model('assets', 'AssetCategory')
Symptom = apps.get_model('interventions', 'Symptom')
InspectionConfiguration = apps.get_model('interventions', 'InspectionConfiguration')
Intervention = apps.get_model('interventions', 'Intervention')
# 1. Create or get water thematic
water_thematic, created = Thematic.objects.get_or_create(
code='water',
defaults={
'name_fr': 'Eau',
'name_nl': 'Water',
'icon': 'bi-droplet',
'color': '#0d6efd',
}
)
# 2. Replicate UserThematics permissions for 'water' from 'nature'
nature_permissions = UserThematics.objects.filter(thematic__code='nature')
for np in nature_permissions:
UserThematics.objects.get_or_create(
user_config=np.user_config,
thematic=water_thematic,
defaults={
'can_view_assets': np.can_view_assets,
'can_edit_assets': np.can_edit_assets,
'can_view_interventions': np.can_view_interventions,
'can_edit_interventions': np.can_edit_interventions,
'can_edit_locations': np.can_edit_locations,
'can_process_observations': np.can_process_observations,
'can_view_projects': np.can_view_projects,
'can_edit_projects': np.can_edit_projects,
}
)
# 3. Transfer AssetCategory NAT_RWI
try:
giep_cat = AssetCategory.objects.get(code='NAT_RWI')
giep_cat.thematic = water_thematic
giep_cat.save()
# Transfer any subcategories recursively (in case any exist)
def transfer_children(parent_cat):
for child in AssetCategory.objects.filter(parent=parent_cat):
child.thematic = water_thematic
child.save()
transfer_children(child)
transfer_children(giep_cat)
except AssetCategory.DoesNotExist:
pass
# 4. Transfer Symptom instances linked to NAT_RWI
giep_cats = AssetCategory.objects.filter(thematic=water_thematic)
symptoms_to_transfer = Symptom.objects.filter(
models.Q(thematic__code='nature') &
(models.Q(asset_categories__in=giep_cats) | models.Q(name_fr__icontains='giep') | models.Q(name_nl__icontains='giep'))
).distinct()
for symp in symptoms_to_transfer:
symp.thematic = water_thematic
symp.save()
# 5. Transfer InspectionConfiguration
configs_to_transfer = InspectionConfiguration.objects.filter(
models.Q(thematic__code='nature') &
(models.Q(name_fr__icontains='giep') | models.Q(name_nl__icontains='giep') | models.Q(default_category__thematic=water_thematic))
).distinct()
for config in configs_to_transfer:
config.thematic = water_thematic
config.save()
# 6. Transfer Interventions
ContentType = apps.get_model('contenttypes', 'ContentType')
try:
rwiz_ct = ContentType.objects.get(app_label='assets', model='naturerwiz')
rwiasset_ct = ContentType.objects.get(app_label='assets', model='naturerwiasset')
# Interventions with category in water thematic
itvs_by_cat = Intervention.objects.filter(thematic__code='nature', asset_category__thematic=water_thematic)
# Interventions with location in NatureRWIZ
InterventionLocation = apps.get_model('interventions', 'InterventionLocation')
itv_ids_by_loc = InterventionLocation.objects.filter(content_type=rwiz_ct).values_list('intervention_id', flat=True)
itvs_by_loc = Intervention.objects.filter(thematic__code='nature', id__in=itv_ids_by_loc)
# Interventions with asset in NatureRWIAsset
InterventionAsset = apps.get_model('interventions', 'InterventionAsset')
itv_ids_by_asset = InterventionAsset.objects.filter(content_type=rwiasset_ct).values_list('intervention_id', flat=True)
itvs_by_asset = Intervention.objects.filter(thematic__code='nature', id__in=itv_ids_by_asset)
all_giep_itvs = (itvs_by_cat | itvs_by_loc | itvs_by_asset).distinct()
for itv in all_giep_itvs:
itv.thematic = water_thematic
itv.save()
except ContentType.DoesNotExist:
pass
class Migration(migrations.Migration):
dependencies = [
('common', '0011_userwarehouselocationaccess_and_more'),
('assets', '0020_alter_trafficlightradarpole_model'),
('interventions', '0016_inspectionconfiguration_show_equipment'),
]
operations = [
migrations.RunPython(create_water_thematic_and_transfer_data, reverse_code=migrations.RunPython.noop),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-03 09:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0012_create_water_thematic_and_transfer_data'),
]
operations = [
migrations.AddField(
model_name='userthematics',
name='can_validate_assets',
field=models.BooleanField(default=False, verbose_name='Peut valider les assets'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-19 05:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0013_userthematics_can_validate_assets'),
]
operations = [
migrations.AddField(
model_name='userconfig',
name='panoramax_can_upload',
field=models.BooleanField(default=False, help_text="Permet à l'utilisateur d'uploader de nouvelles photos ou vidéos 360 pour Panoramax.", verbose_name='Peut uploader des photos 360'),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import contracts.models
@ -202,6 +202,9 @@ class Migration(migrations.Migration):
('is_provider', models.BooleanField(default=False, help_text='Indique si cette équipe est un prestataire extérieur.', verbose_name='Prestataire')),
('can_generate_daily_report', models.BooleanField(default=False, help_text='Indique si les membres de cette équipe peuvent générer un rapport quotidien des interventions.', verbose_name='Peut générer le rapport journalier')),
('photos_required', models.BooleanField(default=False, help_text="Si activé, au moins une photo doit être ajoutée à l'intervention avant de pouvoir la terminer.", verbose_name='Photos obligatoires')),
('restrict_today_interventions_visibility', models.BooleanField(default=False, help_text="Si activé, les interventions ne sont visibles qu'à partir d'une certaine heure, et uniquement pour le jour même.", verbose_name='Restreindre la visibilité aux interventions du jour')),
('visibility_start_hour', models.TimeField(blank=True, help_text='Heure locale à partir de laquelle les interventions du jour deviennent visibles (ex: 06:00:00).', null=True, verbose_name='Heure de début de visibilité')),
('restrict_to_assigned_interventions', models.BooleanField(default=False, help_text="Si activé, les membres de cette équipe ne voient que les interventions assignées à l'équipe ou directement à un membre.", verbose_name='Restreindre aux interventions assignées')),
('asset_categories', models.ManyToManyField(blank=True, help_text="Catégories d'assets dont cette équipe est responsable.", related_name='managing_teams', to='assets.assetcategory', verbose_name="Catégories d'assets gérées")),
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='contracts.company')),
('thematics', models.ManyToManyField(blank=True, related_name='company_teams', to='common.thematic')),
@ -230,8 +233,8 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='companymember',
name='team',
field=models.ForeignKey(blank=True, help_text='Laisser vide pour un membre non assigné à une équipe.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='contracts.companyteam', verbose_name='Équipe'),
name='teams',
field=models.ManyToManyField(blank=True, help_text='Équipes auxquelles ce membre appartient.', related_name='members', to='contracts.companyteam', verbose_name='Équipes'),
),
migrations.CreateModel(
name='Contract',
@ -253,6 +256,7 @@ class Migration(migrations.Migration):
('needs_approval', models.BooleanField(default=False, help_text="Indique si les interventions liées à ce contrat nécessitent une approbation avant d'être transmises pour traitement.", verbose_name='Nécessite une approbation')),
('needs_checking', models.BooleanField(default=False, help_text="Indique si les interventions liées à ce contrat nécessitent une vérification avant d'être transmises pour traitement.", verbose_name='Nécessite une vérification')),
('uses_occupations', models.BooleanField(default=False, help_text='Indique si les interventions liées à ce contrat utilisent la gestion des occupations (temps passé par membre).', verbose_name='Utilise les occupations')),
('uses_intervention_manager', models.BooleanField(default=False, help_text="Indique si les interventions liées à ce contrat utilisent un gestionnaire d'intervention.", verbose_name="Utilise les gestionnaires d'intervention")),
('occupations_required', models.BooleanField(default=False, help_text='Si activé, au moins une occupation doit être enregistrée avant de terminer une intervention corrective liée à ce contrat.', verbose_name='Occupations obligatoires')),
('warn_if_no_prestation_on_processed', models.BooleanField(default=False, help_text="Si activé, un avertissement est affiché lorsque l'utilisateur tente de passer une intervention au statut 'Traité' sans avoir encodé de prestation.", verbose_name="Avertir si aucune prestation au passage à 'Traité'")),
('send_daily_summary', models.BooleanField(default=False, help_text="Si activé, les fiches d'intervention du jour sont regroupées dans un seul email envoyé en fin de journée, plutôt qu'un email par intervention.", verbose_name='Envoi groupé journalier')),

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('common', '0003_initial'),
('common', '0004_initial'),
('contracts', '0001_initial'),
('projects', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-03 15:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0002_initial'),
]
operations = [
migrations.AddField(
model_name='contract',
name='uses_intervention_manager',
field=models.BooleanField(default=False, help_text="Indique si les interventions liées à ce contrat utilisent un gestionnaire d'intervention.", verbose_name="Utilise les gestionnaires d'intervention"),
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-07 09:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0003_contract_uses_intervention_manager'),
]
operations = [
migrations.AddField(
model_name='contract',
name='auto_close_delay_days',
field=models.PositiveIntegerField(default=30, help_text='Délai en jours après lequel une intervention est clôturée automatiquement.', verbose_name='Délai de clôture automatique (jours)'),
),
migrations.AddField(
model_name='contract',
name='auto_close_interventions',
field=models.BooleanField(default=False, help_text='Indique si les interventions liées à ce contrat doivent être automatiquement clôturées après un certain délai.', verbose_name='Clôture automatique des interventions'),
),
]

View file

@ -1,19 +0,0 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('contracts', '0004_contract_auto_close_delay_days_and_more'),
]
operations = [
migrations.RemoveField(
model_name='contract',
name='auto_close_delay_days',
),
migrations.RemoveField(
model_name='contract',
name='auto_close_interventions',
),
]

View file

@ -1,38 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-10 08:31
from django.db import migrations, models
def copy_team_to_teams(apps, schema_editor):
CompanyMember = apps.get_model('contracts', 'CompanyMember')
for member in CompanyMember.objects.all():
# access the raw DB field team_id to avoid django loading issues if team FK field is already removed or modified
team_id = getattr(member, 'team_id', None)
if team_id is not None:
member.teams.add(team_id)
def copy_teams_to_team(apps, schema_editor):
CompanyMember = apps.get_model('contracts', 'CompanyMember')
for member in CompanyMember.objects.all():
first_team = member.teams.first()
if first_team:
member.team = first_team
member.save(update_fields=['team'])
class Migration(migrations.Migration):
dependencies = [
('contracts', '0005_remove_contract_auto_close'),
]
operations = [
migrations.AddField(
model_name='companymember',
name='teams',
field=models.ManyToManyField(blank=True, help_text='Équipes auxquelles ce membre appartient.', related_name='members', to='contracts.companyteam', verbose_name='Équipes'),
),
migrations.RunPython(copy_team_to_teams, reverse_code=copy_teams_to_team),
migrations.RemoveField(
model_name='companymember',
name='team',
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-17 14:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0006_companymember_teams_m2m'),
]
operations = [
migrations.AddField(
model_name='companyteam',
name='restrict_today_interventions_visibility',
field=models.BooleanField(default=False, help_text="Si activé, les interventions ne sont visibles qu'à partir d'une certaine heure, et uniquement pour le jour même.", verbose_name='Restreindre la visibilité aux interventions du jour'),
),
migrations.AddField(
model_name='companyteam',
name='visibility_start_hour',
field=models.TimeField(blank=True, help_text='Heure locale à partir de laquelle les interventions du jour deviennent visibles (ex: 06:00:00).', null=True, verbose_name='Heure de début de visibilité'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-22 09:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0007_companyteam_restrict_today_interventions_visibility_and_more'),
]
operations = [
migrations.AddField(
model_name='companyteam',
name='restrict_to_assigned_interventions',
field=models.BooleanField(default=False, help_text="Si activé, les membres de cette équipe ne voient que les interventions assignées à l'équipe ou directement à un membre.", verbose_name='Restreindre aux interventions assignées'),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.contrib.gis.db.models.fields
@ -105,6 +105,18 @@ class Migration(migrations.Migration):
'ordering': ['control', 'remark_number'],
},
),
migrations.CreateModel(
name='RemarkHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True)),
('status', models.CharField(choices=[('information', 'Info'), ('to_be_processed', 'NOK'), ('processed', 'Traité'), ('validated', 'OK'), ('canceled', 'Annulé')], default='to_be_processed')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'ordering': ['created_at'],
},
),
migrations.CreateModel(
name='RemarkImage',
fields=[

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
from django.db import migrations, models

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('common', '0003_initial'),
('common', '0004_initial'),
('contracts', '0002_initial'),
('controls', '0002_initial'),
('documents', '0001_initial'),
@ -109,6 +109,21 @@ class Migration(migrations.Migration):
name='remark',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quantities', to='controls.remark'),
),
migrations.AddField(
model_name='remarkhistory',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='remarkhistory',
name='remark',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='controls.remark'),
),
migrations.AddField(
model_name='remarkimage',
name='history_entry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='controls.remarkhistory'),
),
migrations.AddField(
model_name='remarkimage',
name='managed_document',

View file

@ -1,35 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-23 13:10
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('controls', '0003_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='RemarkHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True)),
('status', models.CharField(choices=[('information', 'Info'), ('to_be_processed', 'NOK'), ('processed', 'Traité'), ('validated', 'OK'), ('canceled', 'Annulé')], default='to_be_processed')),
('created_at', models.DateTimeField(auto_now_add=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
('remark', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='controls.remark')),
],
options={
'ordering': ['created_at'],
},
),
migrations.AddField(
model_name='remarkimage',
name='history_entry',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='controls.remarkhistory'),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.db.models.deletion
@ -122,6 +122,8 @@ class Migration(migrations.Migration):
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')),
('is_archived', models.BooleanField(default=False, verbose_name='Archived')),
('unique_params', models.JSONField(blank=True, default=dict, help_text="Additional parameters to determine uniqueness for documents with unique tags (e.g., {'format': 'pdf', 'cables': 'C1,C2'})", verbose_name='Unique parameters')),
('georeference_coords', models.JSONField(blank=True, help_text='Coordinates of the 4 corners: [[lng, lat], [lng, lat], [lng, lat], [lng, lat]]', null=True, verbose_name='Georeference Coordinates')),
('georeference_page', models.IntegerField(default=1, help_text='Specific page of the PDF that has been georeferenced', verbose_name='Georeference Page')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_documents', to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
('current_version', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_for_documents', to='documents.documentversion', verbose_name='Current version')),
('folders', models.ManyToManyField(blank=True, related_name='documents', to='documents.documentfolder', verbose_name='Folders')),

View file

@ -1,100 +0,0 @@
# Generated by hand
from django.db import migrations
from django.utils.text import slugify
def create_giep_folders(apps, schema_editor):
DocumentFolder = apps.get_model('documents', 'DocumentFolder')
DocumentFolderAttachment = apps.get_model('documents', 'DocumentFolderAttachment')
NatureRWIZ = apps.get_model('assets', 'NatureRWIZ')
Thematic = apps.get_model('common', 'Thematic')
ContentType = apps.get_model('contenttypes', 'ContentType')
# 1. Get or create 'assets' folder
assets_folder, _ = DocumentFolder.objects.get_or_create(
slug='assets',
defaults={'name': 'Assets', 'is_predefined': True}
)
# 2. Get water thematic
water_thematic = Thematic.objects.filter(code='water').first()
# Create 'other' document tag
DocumentTag = apps.get_model('documents', 'DocumentTag')
other_tag, _ = DocumentTag.objects.get_or_create(
slug='other',
defaults={
'name_fr': 'Autre',
'name_nl': 'Andere',
'show_in_asset_detail': True
}
)
if water_thematic:
other_tag.thematics.add(water_thematic)
# 3. Create 'water' folder as child of 'assets'
water_folder, _ = DocumentFolder.objects.get_or_create(
slug='water',
defaults={
'name': 'Water',
'is_predefined': True,
}
)
if water_thematic:
water_folder.thematics.add(water_thematic)
water_folder.parent_folders.add(assets_folder)
# 4. Create subfolder for each GIEP zone (NatureRWIZ)
nature_rwiz_content_type = ContentType.objects.get_for_model(NatureRWIZ)
for zone in NatureRWIZ.objects.all():
# Avoid creating duplicate folders for the same GIEP zone
exists = DocumentFolderAttachment.objects.filter(
content_type=nature_rwiz_content_type,
object_id=zone.id,
is_default=True
).exists()
if exists:
continue
folder_name = zone.code
base_slug = slugify(folder_name) or f"giep-{zone.id}"
slug = base_slug
counter = 1
while DocumentFolder.objects.filter(slug=slug).exists():
slug = f"{base_slug}-{counter}"
counter += 1
zone_folder = DocumentFolder.objects.create(
name=folder_name,
slug=slug,
is_predefined=True
)
if water_thematic:
zone_folder.thematics.add(water_thematic)
zone_folder.parent_folders.add(water_folder)
# Attach folder to NatureRWIZ
DocumentFolderAttachment.objects.create(
folder=zone_folder,
content_type=nature_rwiz_content_type,
object_id=zone.id,
is_default=True
)
def remove_giep_folders(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('documents', '0001_initial'),
('assets', '0020_alter_trafficlightradarpole_model'),
]
operations = [
migrations.RunPython(create_giep_folders, remove_giep_folders),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-04 06:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('documents', '0002_create_giep_folders'),
]
operations = [
migrations.AddField(
model_name='manageddocument',
name='georeference_coords',
field=models.JSONField(blank=True, help_text='Coordinates of the 4 corners: [[lng, lat], [lng, lat], [lng, lat], [lng, lat]]', null=True, verbose_name='Georeference Coordinates'),
),
migrations.AddField(
model_name='manageddocument',
name='georeference_page',
field=models.IntegerField(default=1, help_text='Specific page of the PDF that has been georeferenced', verbose_name='Georeference Page'),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.db import migrations, models
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
name='HomeWidget',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('widget_code', models.CharField(choices=[('intervention_stats', 'Statistiques interventions'), ('contracts_summary', 'Résumé contrats'), ('map_today', 'Carte du jour'), ('operator_shortcuts', 'Raccourcis opérateur'), ('assets_links', 'Liens assets'), ('stock_summary', 'Résumé stock'), ('observer_shortcuts', 'Raccourcis observateur'), ('incident_counter', 'Compteur incidents à traiter'), ('intervention_to_verify', 'Compteur interventions à vérifier'), ('intervention_to_plan', 'Compteur interventions à planifier'), ('intervention_to_approve', 'Compteur interventions à approuver'), ('intervention_checked_to_approve', 'Compteur interventions vérifiées à approuver'), ('stock_actions', 'Actions stock')], max_length=50, unique=True, verbose_name='Code du widget')),
('widget_code', models.CharField(choices=[('intervention_stats', 'Statistiques interventions'), ('contracts_summary', 'Résumé contrats'), ('map_today', 'Carte du jour'), ('operator_shortcuts', 'Raccourcis opérateur'), ('assets_links', 'Liens assets'), ('stock_summary', 'Résumé stock'), ('observer_shortcuts', 'Raccourcis observateur'), ('incident_counter', 'Compteur incidents à traiter'), ('intervention_to_verify', 'Compteur interventions à vérifier'), ('intervention_to_plan', 'Compteur interventions à planifier'), ('intervention_to_approve', 'Compteur interventions à approuver'), ('intervention_checked_to_approve', 'Compteur interventions vérifiées à approuver'), ('intervention_finished', 'Compteur interventions terminées'), ('stock_actions', 'Actions stock')], max_length=50, unique=True, verbose_name='Code du widget')),
('default_template_override', models.CharField(blank=True, help_text='Chemin du template optionnel (sinon home/widgets/<widget_code>.html)', max_length=255, null=True, verbose_name='Template par défaut (surcharge)')),
('is_active', models.BooleanField(default=True, verbose_name='Actif')),
],
@ -28,6 +28,22 @@ class Migration(migrations.Migration):
'ordering': ['widget_code'],
},
),
migrations.CreateModel(
name='TeambuildingVote',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('voter_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Nom (optionnel)')),
('vote_gael', models.IntegerField(default=0, verbose_name="La journée 'Gaël Non Di Dju'")),
('vote_charly', models.IntegerField(default=0, verbose_name="La journée 'La cité de Charly'")),
('vote_tequila', models.IntegerField(default=0, verbose_name="La journée 'Tequila Cadena'")),
('vote_vivien', models.IntegerField(default=0, verbose_name="La journée 'Vivien de La Grande Propreté'")),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Vote Teambuilding',
'verbose_name_plural': 'Votes Teambuilding',
},
),
migrations.CreateModel(
name='HomeWidgetConditionGroup',
fields=[
@ -53,6 +69,7 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('condition_type', models.CharField(choices=[('role', 'Rôle'), ('team', 'Équipe'), ('contract', 'Contrat'), ('thematic', 'Thématique'), ('is_intern', 'Statut interne')], max_length=20, verbose_name='Type de condition')),
('is_intern', models.BooleanField(blank=True, help_text="Utilisé si type = 'is_intern'", null=True, verbose_name='Statut interne')),
('is_negated', models.BooleanField(default=False, help_text='Inverser le sens de la condition (ex: NOT role=Admin)', verbose_name='NOT')),
('is_active', models.BooleanField(default=True, verbose_name='Actif')),
('contract', models.ForeignKey(blank=True, help_text="Utilisé si type = 'contract'", null=True, on_delete=django.db.models.deletion.CASCADE, to='contracts.contract', verbose_name='Contrat')),
('role', models.ForeignKey(blank=True, help_text="Utilisé si type = 'role'", null=True, on_delete=django.db.models.deletion.CASCADE, to='common.role', verbose_name='Rôle')),

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-03 09:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='homewidget',
name='widget_code',
field=models.CharField(choices=[('intervention_stats', 'Statistiques interventions'), ('contracts_summary', 'Résumé contrats'), ('map_today', 'Carte du jour'), ('operator_shortcuts', 'Raccourcis opérateur'), ('assets_links', 'Liens assets'), ('stock_summary', 'Résumé stock'), ('observer_shortcuts', 'Raccourcis observateur'), ('incident_counter', 'Compteur incidents à traiter'), ('intervention_to_verify', 'Compteur interventions à vérifier'), ('intervention_to_plan', 'Compteur interventions à planifier'), ('intervention_to_approve', 'Compteur interventions à approuver'), ('intervention_checked_to_approve', 'Compteur interventions vérifiées à approuver'), ('intervention_finished', 'Compteur interventions terminées'), ('stock_actions', 'Actions stock')], max_length=50, unique=True, verbose_name='Code du widget'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-03 09:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0002_alter_homewidget_widget_code'),
]
operations = [
migrations.AddField(
model_name='homewidgetcondition',
name='is_negated',
field=models.BooleanField(default=False, help_text='Inverser le sens de la condition (ex: NOT role=Admin)', verbose_name='NOT'),
),
]

View file

@ -1,29 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-18 08:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0003_homewidgetcondition_is_negated'),
]
operations = [
migrations.CreateModel(
name='TeambuildingVote',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('voter_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Nom (optionnel)')),
('vote_gael', models.IntegerField(default=0, verbose_name="La journée 'Gaël Non Di Dju'")),
('vote_charly', models.IntegerField(default=0, verbose_name="La journée 'La cité de Charly'")),
('vote_tequila', models.IntegerField(default=0, verbose_name="La journée 'Tequila Cadena'")),
('vote_vivien', models.IntegerField(default=0, verbose_name="La journée 'Vivien de La Grande Propreté'")),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Vote Teambuilding',
'verbose_name_plural': 'Votes Teambuilding',
},
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.contrib.gis.db.models.fields
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
dependencies = [
('assets', '0003_initial'),
('common', '0002_initial'),
('common', '0003_initial'),
('contenttypes', '0002_remove_content_type_name'),
('contracts', '0001_initial'),
('documents', '0001_initial'),
@ -153,13 +153,13 @@ class Migration(migrations.Migration):
('location_code', models.CharField(blank=True, max_length=500, null=True)),
('status', models.CharField(choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], default='in_preparation')),
('status_order', models.IntegerField(default=1)),
('pause_reason', models.CharField(blank=True, choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('other', 'Autre')], max_length=50, null=True)),
('pause_reason', models.CharField(blank=True, choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('waiting_for_material_delivery', 'En attente livraison de matériel'), ('waiting_for_supplier', 'En attente sous-traitant'), ('offer_by_provider', 'Offre à faire par le prestataire'), ('waiting_for_validation', 'En attente de validation'), ('under_framework_contract', 'Sous garantie marché transversal'), ('other', 'Autre')], max_length=50, null=True)),
('pause_reason_other', models.CharField(blank=True, max_length=255, null=True)),
('cancellation_type', models.CharField(blank=True, choices=[('duplicate', 'Doublon'), ('to_redirect', 'A rediriger'), ('not_our_assets', 'Pas sous notre gestion'), ('non_relevant', 'Non-relevant'), ('other', 'Autre')], max_length=50, null=True)),
('priority', models.CharField(choices=[('1', 'Urgent'), ('2', 'Elevé'), ('3', 'Normal'), ('4', 'Bas')], default='3')),
('description', models.TextField(blank=True, null=True)),
('maintain_type', models.CharField(choices=[('corrective', 'Maintenance corrective'), ('preventive', 'Maintenance préventive'), ('ameliorative', 'Maintenance améliorative')], default='corrective')),
('type', models.CharField(blank=True, choices=[('fixing', 'Dépannage'), ('repair', 'Réparation'), ('repair_post_preventive', 'Réparation post préventif'), ('replace', 'Remplacement'), ('care', 'Entretien'), ('control', 'Contrôle'), ('inspection', 'Inspection'), ('build', 'Construction')], null=True)),
('type', models.CharField(blank=True, choices=[('fixing', 'Dépannage'), ('repair', 'Réparation'), ('repair_post_preventive', 'Réparation post préventif'), ('replace', 'Remplacement'), ('care', 'Entretien'), ('control', 'Contrôle'), ('inspection', 'Inspection'), ('build', 'Construction'), ('collection', 'Ramassage')], null=True)),
('origin_type', models.CharField(choices=[('damage', 'Avarie'), ('defect', 'Défaut/Panne'), ('demand', 'Demande')], default='damage')),
('begin_time', models.DateTimeField(blank=True, null=True)),
('end_time', models.DateTimeField(blank=True, null=True)),
@ -198,6 +198,7 @@ class Migration(migrations.Migration):
('contract', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interventions', to='contracts.contract')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='created_interventions', to=settings.AUTH_USER_MODEL)),
('guided_operation_template', models.ForeignKey(blank=True, help_text="Assigné automatiquement lors du choix d'un template d'opérations avec un template guidé associé.", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interventions', to='interventions.guidedoperationtemplate', verbose_name="Template d'opérations guidées")),
('intervention_manager', models.ForeignKey(blank=True, help_text='Utilisateur qui approuve cette intervention (spécifique aux contrats concernés).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='managed_interventions', to=settings.AUTH_USER_MODEL, verbose_name="Gestionnaire d'intervention")),
('last_modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='modified_interventions', to=settings.AUTH_USER_MODEL)),
('order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interventions', to='contracts.contractorder')),
('thematic', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.thematic')),
@ -413,6 +414,28 @@ class Migration(migrations.Migration):
name='equipment_operation_templates',
field=models.ManyToManyField(blank=True, related_name='interventions_equipment', to='interventions.operationtemplate'),
),
migrations.CreateModel(
name='InspectionConfiguration',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_fr', models.CharField(max_length=255, verbose_name='Nom (FR)')),
('name_nl', models.CharField(blank=True, max_length=255, verbose_name='Nom (NL)')),
('description', models.TextField(blank=True, verbose_name='Description')),
('maintain_type', models.CharField(choices=[('corrective', 'Maintenance corrective'), ('preventive', 'Maintenance préventive'), ('ameliorative', 'Maintenance améliorative')], default='preventive', max_length=50, verbose_name='Type de maintenance')),
('show_equipment', models.BooleanField(default=True, help_text="Si décoché, le champ équipement sera masqué lors de l'encodage de cette inspection.", verbose_name='Afficher le champ équipement')),
('concerned_categories', models.ManyToManyField(blank=True, help_text='Filtre les localisations et équipements affichés sur la carte par catégorie. Si vide, tous sont affichés.', related_name='inspection_configs', to='assets.assetcategory', verbose_name='Catégories concernées')),
('default_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory', verbose_name='Catégorie par défaut')),
('displayed_asset_types', models.ManyToManyField(blank=True, limit_choices_to=models.Q(('app_label', 'assets')), related_name='inspection_configs_as_asset', to='contenttypes.contenttype', verbose_name="Types d'équipements à afficher sur la carte")),
('displayed_location_types', models.ManyToManyField(blank=True, limit_choices_to=models.Q(('app_label', 'assets')), related_name='inspection_configs_as_location', to='contenttypes.contenttype', verbose_name='Types de localisations à afficher sur la carte')),
('thematic', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='common.thematic', verbose_name='Thématique')),
('operation_templates', models.ManyToManyField(blank=True, to='interventions.operationtemplate', verbose_name="Templates d'opérations")),
],
options={
'verbose_name': "Configuration d'inspection",
'verbose_name_plural': "Configurations d'inspections",
'ordering': ['name_fr'],
},
),
migrations.CreateModel(
name='OperationTemplateItem',
fields=[
@ -448,9 +471,12 @@ class Migration(migrations.Migration):
('check_priority', models.BooleanField(default=False, help_text='Indique si la priorité doit être vérifiée.')),
('origin_type', models.CharField(blank=True, choices=[('damage', 'Avarie'), ('defect', 'Défaut/Panne'), ('demand', 'Demande')], default='damage', null=True)),
('maintenance_type', models.CharField(blank=True, choices=[('infra', 'TL - Infrastructure'), ('controller', 'TL - Contrôleur de traffic'), ('electromeca', 'TU - Electromécanique'), ('civil_engineering', 'TU - Génie civil'), ('equipments', 'ITS - Équipement'), ('clean', 'Clean - Nettoyage'), ('collection', 'Clean - Ramassage'), ('other', 'Autre')], help_text="Type de maintenance concerné (ex: 'controller' pour les contrôleurs de traffic).", max_length=50, null=True)),
('require_equipment_selection', models.BooleanField(default=False, help_text='Si coché, une intervention avec ce symptôme doit obligatoirement être liée à un équipement.', verbose_name='Équipement obligatoire')),
('order_number', models.IntegerField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
('recommended_next_status', models.CharField(blank=True, choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], default='to_be_processed', max_length=50, null=True)),
('auto_create_intervention', models.BooleanField(default=False, help_text="Si coché, une intervention est automatiquement créée lorsque l'observation passe au statut 'À traiter'.", verbose_name="Création automatique d'interventions")),
('auto_create_intervention_status', models.CharField(blank=True, choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], default='to_be_processed', help_text="Statut dans lequel l'intervention automatique doit être créée.", max_length=50, null=True, verbose_name="Statut de l'intervention auto-créée")),
('internal_notification_emails', models.CharField(blank=True, help_text="Liste d'emails pour les notifications internes.", max_length=1000, null=True)),
('asset_categories', models.ManyToManyField(blank=True, related_name='symptoms', to='assets.assetcategory')),
('contract', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contracts.contract')),
@ -487,6 +513,34 @@ class Migration(migrations.Migration):
name='symptom',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.symptom'),
),
migrations.CreateModel(
name='DirectContractRouting',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('thematic', models.CharField(choices=[('structures', 'Structures'), ('trafficlights', 'Traffic Lights'), ('clean', 'Clean'), ('its', 'ITS'), ('nature', 'Nature'), ('roads', 'Roads'), ('publiclighting', 'Public Lighting')], max_length=50, verbose_name='Thématique')),
('asset_code', models.CharField(help_text="Le code de l'ouvrage d'art ou de l'équipement concerné.", max_length=100, verbose_name="Code de l'asset (ex: OA054)")),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.contract', verbose_name='Contrat à forcer')),
('symptom', models.ForeignKey(blank=True, help_text="Si renseigné, cette règle s'applique uniquement à ce symptôme. Sinon, elle s'applique par défaut.", null=True, on_delete=django.db.models.deletion.CASCADE, to='interventions.symptom', verbose_name='Symptôme spécifique')),
],
options={
'verbose_name': 'Routage direct de contrat',
'verbose_name_plural': 'Routages directs de contrats',
},
),
migrations.CreateModel(
name='SymptomContractAutoClose',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('delay_days', models.PositiveIntegerField(default=30, help_text="Délai en jours après lequel l'intervention est clôturée automatiquement.", verbose_name='Délai (jours)')),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auto_close_rules', to='contracts.contract', verbose_name='Contrat')),
('symptom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auto_close_rules', to='interventions.symptom', verbose_name='Symptôme')),
],
options={
'verbose_name': 'Règle de clôture automatique (symptôme × contrat)',
'verbose_name_plural': 'Règles de clôture automatique (symptôme × contrat)',
'ordering': ['symptom', 'contract'],
},
),
migrations.CreateModel(
name='SymptomRecommendation',
fields=[
@ -507,7 +561,7 @@ class Migration(migrations.Migration):
name='ContractPauseReasonAssignment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pause_reason', models.CharField(choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('other', 'Autre')], max_length=50, verbose_name='Motif de pause')),
('pause_reason', models.CharField(choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('waiting_for_material_delivery', 'En attente livraison de matériel'), ('waiting_for_supplier', 'En attente sous-traitant'), ('offer_by_provider', 'Offre à faire par le prestataire'), ('waiting_for_validation', 'En attente de validation'), ('under_framework_contract', 'Sous garantie marché transversal'), ('other', 'Autre')], max_length=50, verbose_name='Motif de pause')),
('assigned_member', models.ForeignKey(blank=True, help_text="Membre à qui réassigner automatiquement l'intervention lors d'une mise en pause avec ce motif. Laisser vide pour désassigner.", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pause_reason_assignments', to='contracts.companymember', verbose_name='Membre assigné')),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pause_reason_assignments', to='contracts.contract', verbose_name='Contrat')),
],
@ -517,6 +571,38 @@ class Migration(migrations.Migration):
'unique_together': {('contract', 'pause_reason')},
},
),
migrations.CreateModel(
name='InspectionConfigurationContract',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inspection_configuration_contracts', to='contracts.contract', verbose_name='Contrat')),
('inspection_configuration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contracts_providers', to='interventions.inspectionconfiguration', verbose_name="Configuration d'inspection")),
('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inspection_configuration_contracts', to='contracts.company', verbose_name='Prestataire')),
],
options={
'verbose_name': "Prestataire et Contrat d'inspection",
'verbose_name_plural': "Prestataires et Contrats d'inspections",
'unique_together': {('inspection_configuration', 'provider', 'contract')},
},
),
migrations.CreateModel(
name='InterventionAssignment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(blank=True, choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], max_length=50, null=True, verbose_name='Statut')),
('date', models.DateField(verbose_name="Date d'assignation")),
('created_at', models.DateTimeField(auto_now_add=True)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_assignments', to='interventions.intervention', verbose_name='Intervention')),
('member', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='daily_assignments', to='contracts.companymember', verbose_name='Membre')),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_assignments', to='contracts.companyteam', verbose_name='Équipe')),
],
options={
'verbose_name': "Assignation quotidienne d'intervention",
'verbose_name_plural': "Assignations quotidiennes d'interventions",
'ordering': ['date', 'team'],
'unique_together': {('intervention', 'date')},
},
),
migrations.CreateModel(
name='InterventionLink',
fields=[
@ -561,6 +647,20 @@ class Migration(migrations.Migration):
'unique_together': {('intervention', 'contract_post', 'period_number')},
},
),
migrations.CreateModel(
name='InterventionSubscription',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Abonné le')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='interventions.intervention', verbose_name='Intervention')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='intervention_subscriptions', to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur')),
],
options={
'verbose_name': 'Abonnement à une intervention',
'verbose_name_plural': 'Abonnements aux interventions',
'unique_together': {('user', 'intervention')},
},
),
migrations.AlterUniqueTogether(
name='operationstatus',
unique_together={('status_set', 'value')},
@ -603,4 +703,12 @@ class Migration(migrations.Migration):
model_name='intervention',
index=models.Index(fields=['begin_time', 'planned_begin_time', 'expected_begin_time', 'maintain_type'], name='intv_chart_begin_maintain_idx'),
),
migrations.AlterUniqueTogether(
name='directcontractrouting',
unique_together={('thematic', 'asset_code', 'symptom')},
),
migrations.AlterUniqueTogether(
name='symptomcontractautoclose',
unique_together={('symptom', 'contract')},
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-24 08:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='intervention',
name='type',
field=models.CharField(blank=True, choices=[('fixing', 'Dépannage'), ('repair', 'Réparation'), ('repair_post_preventive', 'Réparation post préventif'), ('replace', 'Remplacement'), ('care', 'Entretien'), ('control', 'Contrôle'), ('inspection', 'Inspection'), ('build', 'Construction'), ('collection', 'Ramassage')], null=True),
),
]

View file

@ -1,35 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-28 12:44
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0005_userconfig_calendar_preferences'),
('contracts', '0002_initial'),
('interventions', '0002_alter_intervention_type'),
]
operations = [
migrations.CreateModel(
name='InspectionConfiguration',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_fr', models.CharField(max_length=255, verbose_name='Nom (FR)')),
('name_nl', models.CharField(blank=True, max_length=255, verbose_name='Nom (NL)')),
('description', models.TextField(blank=True, verbose_name='Description')),
('maintain_type', models.CharField(choices=[('corrective', 'Maintenance corrective'), ('preventive', 'Maintenance préventive'), ('ameliorative', 'Maintenance améliorative')], default='preventive', max_length=50, verbose_name='Type de maintenance')),
('assigned_provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inspection_configs', to='contracts.company', verbose_name='Prestataire')),
('contract', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inspection_configs', to='contracts.contract', verbose_name='Contrat')),
('operation_templates', models.ManyToManyField(blank=True, to='interventions.operationtemplate', verbose_name="Templates d'opérations")),
('thematic', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='common.thematic', verbose_name='Thématique')),
],
options={
'verbose_name': "Configuration d'inspection",
'verbose_name_plural': "Configurations d'inspections",
'ordering': ['name_fr'],
},
),
]

View file

@ -1,43 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-28 14:19
import django.db.models.deletion
from django.db import migrations, models
def migrate_existing_inspection_configs(apps, schema_editor):
InspectionConfiguration = apps.get_model('interventions', 'InspectionConfiguration')
InspectionConfigurationContract = apps.get_model('interventions', 'InspectionConfigurationContract')
for config in InspectionConfiguration.objects.all():
if config.contract_id and config.assigned_provider_id:
InspectionConfigurationContract.objects.get_or_create(
inspection_configuration=config,
contract_id=config.contract_id,
provider_id=config.assigned_provider_id
)
class Migration(migrations.Migration):
dependencies = [
('contracts', '0002_initial'),
('interventions', '0003_inspectionconfiguration'),
]
operations = [
migrations.CreateModel(
name='InspectionConfigurationContract',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inspection_configuration_contracts', to='contracts.contract', verbose_name='Contrat')),
('inspection_configuration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contracts_providers', to='interventions.inspectionconfiguration', verbose_name="Configuration d'inspection")),
('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inspection_configuration_contracts', to='contracts.company', verbose_name='Prestataire')),
],
options={
'verbose_name': "Prestataire et Contrat d'inspection",
'verbose_name_plural': "Prestataires et Contrats d'inspections",
'unique_together': {('inspection_configuration', 'provider', 'contract')},
},
),
migrations.RunPython(migrate_existing_inspection_configs, reverse_code=migrations.RunPython.noop),
]

View file

@ -1,21 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-28 14:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('interventions', '0004_inspectionconfigurationcontract'),
]
operations = [
migrations.RemoveField(
model_name='inspectionconfiguration',
name='assigned_provider',
),
migrations.RemoveField(
model_name='inspectionconfiguration',
name='contract',
),
]

View file

@ -1,20 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-28 14:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_delete_naturehollowgreenspace'),
('interventions', '0005_remove_inspectionconfiguration_assigned_provider_and_more'),
]
operations = [
migrations.AddField(
model_name='inspectionconfiguration',
name='default_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory', verbose_name='Catégorie par défaut'),
),
]

View file

@ -1,43 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-29 07:44
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('interventions', '0006_inspectionconfiguration_default_category'),
]
operations = [
migrations.CreateModel(
name='InspectionConfigurationAsset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField(verbose_name="ID d'équipement")),
('content_type', models.ForeignKey(limit_choices_to=models.Q(('app_label', 'assets')), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name="Type d'équipement")),
('inspection_configuration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='interventions.inspectionconfiguration', verbose_name="Configuration d'inspection")),
],
options={
'verbose_name': 'Équipement à afficher sur la carte',
'verbose_name_plural': 'Équipements à afficher sur la carte',
'unique_together': {('inspection_configuration', 'content_type', 'object_id')},
},
),
migrations.CreateModel(
name='InspectionConfigurationLocation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField(verbose_name='ID de localisation')),
('content_type', models.ForeignKey(limit_choices_to=models.Q(('app_label', 'assets')), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name='Type de localisation')),
('inspection_configuration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='interventions.inspectionconfiguration', verbose_name="Configuration d'inspection")),
],
options={
'verbose_name': 'Localisation à afficher sur la carte',
'verbose_name_plural': 'Localisations à afficher sur la carte',
'unique_together': {('inspection_configuration', 'content_type', 'object_id')},
},
),
]

View file

@ -1,42 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-29 07:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('interventions', '0007_inspectionconfigurationasset_and_more'),
]
operations = [
migrations.AlterUniqueTogether(
name='inspectionconfigurationlocation',
unique_together=None,
),
migrations.RemoveField(
model_name='inspectionconfigurationlocation',
name='content_type',
),
migrations.RemoveField(
model_name='inspectionconfigurationlocation',
name='inspection_configuration',
),
migrations.AddField(
model_name='inspectionconfiguration',
name='displayed_asset_types',
field=models.ManyToManyField(blank=True, limit_choices_to=models.Q(('app_label', 'assets')), related_name='inspection_configs_as_asset', to='contenttypes.contenttype', verbose_name="Types d'équipements à afficher sur la carte"),
),
migrations.AddField(
model_name='inspectionconfiguration',
name='displayed_location_types',
field=models.ManyToManyField(blank=True, limit_choices_to=models.Q(('app_label', 'assets')), related_name='inspection_configs_as_location', to='contenttypes.contenttype', verbose_name='Types de localisations à afficher sur la carte'),
),
migrations.DeleteModel(
name='InspectionConfigurationAsset',
),
migrations.DeleteModel(
name='InspectionConfigurationLocation',
),
]

View file

@ -1,25 +0,0 @@
# Generated manually
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_delete_naturehollowgreenspace'),
('interventions', '0008_alter_inspectionconfigurationlocation_unique_together_and_more'),
]
operations = [
migrations.AddField(
model_name='inspectionconfiguration',
name='concerned_categories',
field=models.ManyToManyField(
blank=True,
help_text="Filtre les localisations et équipements affichés sur la carte par catégorie. Si vide, tous sont affichés.",
related_name='inspection_configs',
to='assets.assetcategory',
verbose_name='Catégories concernées',
),
),
]

View file

@ -1,21 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-03 15:20
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0009_inspectionconfiguration_concerned_categories'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='intervention',
name='intervention_manager',
field=models.ForeignKey(blank=True, help_text='Utilisateur qui approuve cette intervention (spécifique aux contrats concernés).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='managed_interventions', to=settings.AUTH_USER_MODEL, verbose_name="Gestionnaire d'intervention"),
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-07 09:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0010_intervention_intervention_manager'),
]
operations = [
migrations.AddField(
model_name='symptom',
name='auto_close_delay_days',
field=models.PositiveIntegerField(default=30, help_text='Délai en jours après lequel une intervention est clôturée automatiquement pour ce symptôme.', verbose_name='Délai de clôture automatique (jours)'),
),
migrations.AddField(
model_name='symptom',
name='auto_close_interventions',
field=models.BooleanField(default=False, help_text='Indique si les interventions liées à ce symptôme doivent être automatiquement clôturées après un certain délai.', verbose_name='Clôture automatique des interventions'),
),
]

View file

@ -1,36 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0004_contract_auto_close_delay_days_and_more'),
('interventions', '0011_symptom_auto_close_delay_days_and_more'),
]
operations = [
migrations.CreateModel(
name='SymptomContractAutoClose',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('delay_days', models.PositiveIntegerField(default=30, help_text="Délai en jours après lequel l'intervention est clôturée automatiquement.", verbose_name='Délai (jours)')),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auto_close_rules', to='contracts.contract', verbose_name='Contrat')),
('symptom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auto_close_rules', to='interventions.symptom', verbose_name='Symptôme')),
],
options={
'verbose_name': 'Règle de clôture automatique (symptôme × contrat)',
'verbose_name_plural': 'Règles de clôture automatique (symptôme × contrat)',
'ordering': ['symptom', 'contract'],
'unique_together': {('symptom', 'contract')},
},
),
migrations.RemoveField(
model_name='symptom',
name='auto_close_delay_days',
),
migrations.RemoveField(
model_name='symptom',
name='auto_close_interventions',
),
]

View file

@ -1,20 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0012_symptomcontractautoclose_remove_symptom_auto_close'),
]
operations = [
migrations.AddField(
model_name='symptom',
name='require_equipment_selection',
field=models.BooleanField(
default=False,
help_text="Si coché, une intervention avec ce symptôme doit obligatoirement être liée à un équipement.",
verbose_name='Équipement obligatoire',
),
),
]

View file

@ -1,61 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-11 13:40
import django.db.models.deletion
from django.db import migrations, models
def create_initial_assignments(apps, schema_editor):
Intervention = apps.get_model('interventions', 'Intervention')
InterventionAssignment = apps.get_model('interventions', 'InterventionAssignment')
from datetime import timedelta
for itv in Intervention.objects.filter(assigned_team__isnull=False):
start_dt = itv.planned_begin_time or itv.begin_time or itv.expected_begin_time or itv.creation_time
end_dt = itv.planned_end_time or itv.end_time or itv.expected_end_time or start_dt
if start_dt:
start_date = start_dt.date()
end_date = end_dt.date()
curr = start_date
while curr <= end_date:
InterventionAssignment.objects.get_or_create(
intervention=itv,
date=curr,
defaults={
'team': itv.assigned_team,
'member': itv.assigned_member,
'status': itv.status,
}
)
curr += timedelta(days=1)
class Migration(migrations.Migration):
dependencies = [
('contracts', '0006_companymember_teams_m2m'),
('interventions', '0013_symptom_require_equipment_selection'),
]
operations = [
migrations.CreateModel(
name='InterventionAssignment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(blank=True, choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], max_length=50, null=True, verbose_name='Statut')),
('date', models.DateField(verbose_name="Date d'assignation")),
('created_at', models.DateTimeField(auto_now_add=True)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_assignments', to='interventions.intervention', verbose_name='Intervention')),
('member', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='daily_assignments', to='contracts.companymember', verbose_name='Membre')),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_assignments', to='contracts.companyteam', verbose_name='Équipe')),
],
options={
'verbose_name': "Assignation quotidienne d'intervention",
'verbose_name_plural': "Assignations quotidiennes d'interventions",
'ordering': ['date', 'team'],
'unique_together': {('intervention', 'date')},
},
),
migrations.RunPython(
create_initial_assignments,
reverse_code=migrations.RunPython.noop
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-16 17:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0014_interventionassignment'),
]
operations = [
migrations.AddField(
model_name='symptom',
name='auto_create_intervention',
field=models.BooleanField(default=False, help_text="Si coché, une intervention est automatiquement créée lorsque l'observation passe au statut 'À traiter'.", verbose_name="Création automatique d'interventions"),
),
migrations.AddField(
model_name='symptom',
name='auto_create_intervention_status',
field=models.CharField(blank=True, choices=[('in_preparation', 'En préparation'), ('to_be_approved', 'A approuver'), ('to_be_planned', 'A planifier'), ('to_be_processed', 'A traiter'), ('assigned', 'Pris en charge'), ('in_progress', 'Démarré'), ('on_pause', 'En pause'), ('finished', 'Terminé'), ('processed', 'Traité'), ('to_be_corrected', 'A corriger'), ('corrected', 'Corrigé'), ('validated', 'Validé'), ('invoiced', 'Comptabilisé'), ('closed', 'Finalisé'), ('canceled', 'Annulé')], default='to_be_processed', help_text="Statut dans lequel l'intervention automatique doit être créée.", max_length=50, null=True, verbose_name="Statut de l'intervention auto-créée"),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-25 13:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0015_symptom_auto_create_intervention_and_more'),
]
operations = [
migrations.AddField(
model_name='inspectionconfiguration',
name='show_equipment',
field=models.BooleanField(default=True, help_text="Si décoché, le champ équipement sera masqué lors de l'encodage de cette inspection.", verbose_name='Afficher le champ équipement'),
),
]

View file

@ -1,30 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-06 08:33
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0016_inspectionconfiguration_show_equipment'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='InterventionSubscription',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Abonné le')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='interventions.intervention', verbose_name='Intervention')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='intervention_subscriptions', to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur')),
],
options={
'verbose_name': 'Abonnement à une intervention',
'verbose_name_plural': 'Abonnements aux interventions',
'unique_together': {('user', 'intervention')},
},
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-09 09:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interventions', '0017_interventionsubscription'),
]
operations = [
migrations.AlterField(
model_name='contractpausereasonassignment',
name='pause_reason',
field=models.CharField(choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('waiting_for_material_delivery', 'En attente livraison de matériel'), ('waiting_for_supplier', 'En attente sous-traitant'), ('offer_by_provider', 'Offre à faire par le prestataire'), ('waiting_for_validation', 'En attente de validation'), ('under_framework_contract', 'Sous garantie marché transversal'), ('other', 'Autre')], max_length=50, verbose_name='Motif de pause'),
),
migrations.AlterField(
model_name='intervention',
name='pause_reason',
field=models.CharField(blank=True, choices=[('reschedule', 'Intervention à replanifier'), ('order_material', 'Matériel à commander'), ('treated_in_next_maintenance', 'Traité dans le prochain entretien'), ('waiting_for_material_delivery', 'En attente livraison de matériel'), ('waiting_for_supplier', 'En attente sous-traitant'), ('offer_by_provider', 'Offre à faire par le prestataire'), ('waiting_for_validation', 'En attente de validation'), ('under_framework_contract', 'Sous garantie marché transversal'), ('other', 'Autre')], max_length=50, null=True),
),
]

View file

@ -1,30 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-22 09:11
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0008_companyteam_restrict_to_assigned_interventions'),
('interventions', '0018_alter_contractpausereasonassignment_pause_reason_and_more'),
]
operations = [
migrations.CreateModel(
name='DirectContractRouting',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('thematic', models.CharField(choices=[('structures', 'Structures'), ('trafficlights', 'Traffic Lights'), ('clean', 'Clean'), ('its', 'ITS'), ('nature', 'Nature'), ('roads', 'Roads'), ('publiclighting', 'Public Lighting')], max_length=50, verbose_name='Thématique')),
('asset_code', models.CharField(help_text="Le code de l'ouvrage d'art ou de l'équipement concerné.", max_length=100, verbose_name="Code de l'asset (ex: OA054)")),
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.contract', verbose_name='Contrat à forcer')),
('symptom', models.ForeignKey(blank=True, help_text="Si renseigné, cette règle s'applique uniquement à ce symptôme. Sinon, elle s'applique par défaut.", null=True, on_delete=django.db.models.deletion.CASCADE, to='interventions.symptom', verbose_name='Symptôme spécifique')),
],
options={
'verbose_name': 'Routage direct de contrat',
'verbose_name_plural': 'Routages directs de contrats',
'unique_together': {('thematic', 'asset_code', 'symptom')},
},
),
]

View file

@ -1,49 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-22 09:42
from django.db import migrations
def inject_oa054_routing(apps, schema_editor):
DirectContractRouting = apps.get_model('interventions', 'DirectContractRouting')
Contract = apps.get_model('contracts', 'Contract')
Symptom = apps.get_model('interventions', 'Symptom')
# Find the contracts
contract_x005 = Contract.objects.filter(contract_number='X00.005').first()
contract_x006 = Contract.objects.filter(contract_number='X00.006').first()
symptom_clean = Symptom.objects.filter(code='TU_CLEAN_ROADWAY').first()
# If X00.005 and TU_CLEAN_ROADWAY symptom exist, create the routing rule
if contract_x005 and symptom_clean:
DirectContractRouting.objects.get_or_create(
thematic='structures',
asset_code='OA054',
symptom=symptom_clean,
defaults={'contract': contract_x005}
)
# If X00.006 exists, create the default routing rule for OA054
if contract_x006:
DirectContractRouting.objects.get_or_create(
thematic='structures',
asset_code='OA054',
symptom=None,
defaults={'contract': contract_x006}
)
def reverse_oa054_routing(apps, schema_editor):
DirectContractRouting = apps.get_model('interventions', 'DirectContractRouting')
DirectContractRouting.objects.filter(thematic='structures', asset_code='OA054').delete()
class Migration(migrations.Migration):
dependencies = [
('interventions', '0019_directcontractrouting'),
('contracts', '__first__'),
]
operations = [
migrations.RunPython(inject_oa054_routing, reverse_code=reverse_oa054_routing),
]

View file

@ -712,7 +712,7 @@ class InterventionLink(models.Model):
name="uniq_intervention_link",
),
models.CheckConstraint(
check=~Q(first=F("second")),
condition=~Q(first=F("second")),
name="prevent_self_link",
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
from django.conf import settings
from django.db import migrations, models

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
import notifications.models

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.contrib.gis.db.models.fields
@ -143,7 +143,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True)),
('observation_type', models.CharField(choices=[('incident', 'Incident'), ('inventory', 'Inventaire'), ('remark', 'Remarque')], default='incident', max_length=20)),
('geom', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=3812)),
('status', models.CharField(choices=[('in_preparation', 'En préparation'), ('to_process', 'À traiter'), ('in_progress', 'En cours'), ('processed', 'Traité'), ('not_relevant', 'Non relevant'), ('to_redirect', 'À rediriger'), ('duplicate', 'Doublon'), ('archived', 'Archivé')], default='in_preparation', max_length=20)),
('status', models.CharField(choices=[('in_preparation', 'En préparation'), ('to_process', 'À traiter'), ('in_progress', 'En cours'), ('processed', 'Traité'), ('not_relevant', 'Non relevant'), ('to_redirect', 'À rediriger'), ('duplicate', 'Doublon'), ('closed', 'Clôturé'), ('archived', 'Archivé')], default='in_preparation', max_length=20)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='observations', to='assets.assetcategory')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='observations', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='observations', to='interventions.intervention')),

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('common', '0003_initial'),
('common', '0004_initial'),
('interventions', '0001_initial'),
('observations', '0001_initial'),
('projects', '0001_initial'),

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-06-07 09:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('observations', '0002_initial'),
]
operations = [
migrations.AlterField(
model_name='observation',
name='status',
field=models.CharField(choices=[('in_preparation', 'En préparation'), ('to_process', 'À traiter'), ('in_progress', 'En cours'), ('processed', 'Traité'), ('not_relevant', 'Non relevant'), ('to_redirect', 'À rediriger'), ('duplicate', 'Doublon'), ('closed', 'Clôturé'), ('archived', 'Archivé')], default='in_preparation', max_length=20),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-07-19 05:35
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings
@ -26,6 +26,8 @@ class Migration(migrations.Migration):
('progress', models.IntegerField(default=0, verbose_name='Progression (%)')),
('error_message', models.TextField(blank=True, null=True, verbose_name="Message d'erreur")),
('upload_set_id', models.CharField(blank=True, max_length=100, null=True, verbose_name='ID de session Panoramax')),
('associated_collections', models.JSONField(blank=True, default=list, verbose_name='Collections associées')),
('camera_offset', models.IntegerField(default=0, verbose_name="Offset d'orientation caméra (degrés)")),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Date de création')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Date de mise à jour')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur')),

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-19 13:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('panoramax', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='panoramaxupload',
name='associated_collections',
field=models.JSONField(blank=True, default=list, verbose_name='Collections associées'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2026-07-19 13:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('panoramax', '0002_panoramaxupload_associated_collections'),
]
operations = [
migrations.AddField(
model_name='panoramaxupload',
name='camera_offset',
field=models.IntegerField(default=0, verbose_name="Offset d'orientation caméra (degrés)"),
),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.contrib.gis.db.models.fields
@ -13,7 +13,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('common', '0003_initial'),
('common', '0004_initial'),
('contenttypes', '0002_remove_content_type_name'),
('contracts', '0001_initial'),
('documents', '0001_initial'),

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.db import migrations, models

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import django.db.models.deletion
from django.conf import settings

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.db.models.deletion

View file

@ -1,12 +0,0 @@
from django.contrib.postgres.operations import TrigramExtension
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('schools', '0001_initial'),
]
operations = [
TrigramExtension(),
]

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.8 on 2026-05-21 13:51
# Generated by Django 6.0.7 on 2026-07-22 13:42
import common.storages
import django.contrib.gis.db.models.fields
@ -49,6 +49,9 @@ class Migration(migrations.Migration):
('name_nl', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True)),
('sgv_picture', models.ImageField(blank=True, null=True, upload_to='sign/signpanels_types/')),
('default_width', models.FloatField(blank=True, null=True, verbose_name='Largeur par défaut (cm)')),
('default_height', models.FloatField(blank=True, null=True, verbose_name='Hauteur par défaut (cm)')),
('default_diameter', models.FloatField(blank=True, null=True, verbose_name='Diamètre par défaut (cm)')),
],
),
migrations.CreateModel(
@ -131,7 +134,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -162,6 +165,7 @@ class Migration(migrations.Migration):
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sign_app_panels', to='sign.organization')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='sign.signpanel', verbose_name='Remplacé par')),
('street', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sign_app_panels', to='common.adrstreet')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
],
),
@ -246,7 +250,7 @@ class Migration(migrations.Migration):
('code', models.CharField(max_length=50)),
('name_fr', models.CharField(blank=True, max_length=200, null=True)),
('name_nl', models.CharField(blank=True, max_length=200, null=True)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé')], default='active', max_length=50)),
('status', models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50)),
('obsolescence', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('bad', 'Dégradé'), ('critical', 'Critique'), ('unknown', 'Inconnu')], default='unknown', null=True)),
('installation_date', models.DateTimeField(blank=True, null=True)),
('uninstallation_date', models.DateTimeField(blank=True, null=True)),
@ -270,6 +274,7 @@ class Migration(migrations.Migration):
('marked_as_removed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_marked_as_removed', to=settings.AUTH_USER_MODEL, verbose_name='Marqué comme retiré par')),
('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sign_app_poles', to='sign.organization')),
('replaced_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replaces', to='sign.signpole', verbose_name='Remplacé par')),
('street', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sign_app_poles', to='common.adrstreet')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Mis à jour par')),
],
),
@ -328,16 +333,6 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.AddField(
model_name='signpole',
name='street',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sign_app_poles', to='sign.signstreet'),
),
migrations.AddField(
model_name='signpanel',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sign_app_panels', to='sign.signstreet'),
),
migrations.CreateModel(
name='SignStreetDocument',
fields=[
@ -370,12 +365,12 @@ class Migration(migrations.Migration):
name='signpaneltypearticle',
unique_together={('panel_type', 'article')},
),
migrations.AddIndex(
model_name='signpole',
index=models.Index(fields=['code'], name='sign_signpo_code_16a79b_idx'),
),
migrations.AddIndex(
model_name='signpanel',
index=models.Index(fields=['code'], name='sign_signpa_code_65ed88_idx'),
),
migrations.AddIndex(
model_name='signpole',
index=models.Index(fields=['code'], name='sign_signpo_code_16a79b_idx'),
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-23 05:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sign', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='signpanel',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
migrations.AlterField(
model_name='signpole',
name='status',
field=models.CharField(choices=[('to_be_approved', 'À approuver'), ('planned', 'Planifié'), ('active', 'Actif'), ('damaged', 'Endommagé'), ('to_replace', 'A remplacer'), ('voluntarily_out_of_service', 'Mis hors service volontairement'), ('removed', 'Enlevé'), ('archived', 'Archivé')], default='active', max_length=50),
),
]

View file

@ -1,25 +0,0 @@
# Generated by Django 5.2.8 on 2026-05-24 16:32
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0005_userconfig_calendar_preferences'),
('sign', '0002_alter_signpanel_status_alter_signpole_status'),
]
operations = [
migrations.AlterField(
model_name='signpanel',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sign_app_panels', to='common.adrstreet'),
),
migrations.AlterField(
model_name='signpole',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sign_app_poles', to='common.adrstreet'),
),
]

Some files were not shown because too many files have changed in this diff Show more