loko/streetup/interventions/migrations/0001_initial.py
2026-07-22 14:48:40 +02:00

606 lines
48 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Generated by Django 5.2.8 on 2026-05-21 13:51
import common.storages
import django.contrib.gis.db.models.fields
import django.db.models.deletion
import interventions.models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('assets', '0003_initial'),
('common', '0002_initial'),
('contenttypes', '0002_remove_content_type_name'),
('contracts', '0001_initial'),
('documents', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='GuidedOperationData',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data', models.JSONField(default=dict, verbose_name='Données')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': "Données d'opérations guidées",
'verbose_name_plural': "Données d'opérations guidées",
},
),
migrations.CreateModel(
name='GuidedOperationTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', models.SlugField(help_text='Identifiant unique. Doit correspondre au nom du fichier HTML: guided_operations/{slug}.html', max_length=100, unique=True, verbose_name='Slug')),
('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')),
],
options={
'verbose_name': "Template d'opérations guidées",
'verbose_name_plural': "Templates d'opérations guidées",
'ordering': ['name_fr'],
},
),
migrations.CreateModel(
name='InterventionSequence',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('current_number', models.PositiveIntegerField(default=0)),
],
),
migrations.CreateModel(
name='MeasurementDefinition',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('value_type', models.CharField(choices=[('numeric', 'Numérique'), ('duration', 'Durée (hh:mm:ss)'), ('datetime', 'Date et heure')], default='numeric', max_length=20, verbose_name='Type de valeur')),
('unit', models.CharField(blank=True, max_length=50)),
('min_value', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('max_value', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('min_duration', models.DurationField(blank=True, null=True, verbose_name='Durée min')),
('max_duration', models.DurationField(blank=True, null=True, verbose_name='Durée max')),
('min_datetime', models.DateTimeField(blank=True, null=True, verbose_name='Date/heure min')),
('max_datetime', models.DateTimeField(blank=True, null=True, verbose_name='Date/heure max')),
],
),
migrations.CreateModel(
name='OperationStatus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label_fr', models.CharField(max_length=50)),
('label_nl', models.CharField(max_length=50)),
('value', models.CharField(max_length=50)),
('order', models.PositiveIntegerField(default=0)),
('bootstrap_icon', models.CharField(blank=True, help_text='Icône Bootstrap (ex: bi-check-circle)', max_length=50, null=True)),
('bootstrap_color', models.CharField(blank=True, choices=[('primary', 'Primary'), ('secondary', 'Secondary'), ('success', 'Success'), ('danger', 'Danger'), ('warning', 'Warning'), ('info', 'Info'), ('light', 'Light'), ('dark', 'Dark')], help_text='Couleur Bootstrap', max_length=20, null=True)),
('requires_comment', models.BooleanField(default=False, help_text='Si coché, un commentaire sera requis lorsque ce statut est sélectionné', verbose_name='Commentaire obligatoire')),
],
options={
'ordering': ['order'],
},
),
migrations.CreateModel(
name='OperationStatusSet',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
],
),
migrations.CreateModel(
name='OperationType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=100, unique=True)),
('name_fr', models.CharField(max_length=255)),
('name_nl', models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name='SourceCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_fr', models.CharField(max_length=200)),
('name_nl', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True)),
('has_reference', models.BooleanField(default=False)),
('is_active', models.BooleanField(default=True)),
],
options={
'ordering': ['name_fr'],
},
),
migrations.CreateModel(
name='GuidedOperationMedia',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field_key', models.CharField(blank=True, default='', help_text='Identifie la question à laquelle ce média est rattaché (vide = général).', max_length=100, verbose_name='Clé de question')),
('file', models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.guided_operation_media_upload_path, verbose_name='Fichier')),
('media_type', models.CharField(choices=[('photo', 'Photo'), ('document', 'Document')], default='photo', max_length=20, verbose_name='Type de média')),
('description', models.CharField(blank=True, max_length=255, verbose_name='Description')),
('uploaded_at', models.DateTimeField(auto_now_add=True)),
('guided_data', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='media', to='interventions.guidedoperationdata', verbose_name='Données guidées')),
('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_guided_media', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': "Média d'opérations guidées",
'verbose_name_plural': "Médias d'opérations guidées",
'ordering': ['field_key', '-uploaded_at'],
},
),
migrations.AddField(
model_name='guidedoperationdata',
name='guided_template',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='data_entries', to='interventions.guidedoperationtemplate', verbose_name='Template guidé'),
),
migrations.CreateModel(
name='Intervention',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(blank=True, db_index=True, max_length=50, unique=True)),
('title', models.CharField(max_length=200)),
('lon', models.FloatField(blank=True, null=True)),
('lat', models.FloatField(blank=True, null=True)),
('address', models.CharField(blank=True, max_length=500, null=True)),
('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_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)),
('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)),
('planned_begin_time', models.DateTimeField(blank=True, null=True)),
('planned_end_time', models.DateTimeField(blank=True, null=True)),
('expected_begin_time', models.DateTimeField(blank=True, null=True)),
('expected_end_time', models.DateTimeField(blank=True, null=True)),
('source_detail', models.CharField(blank=True, null=True)),
('source_ref', models.CharField(blank=True, null=True)),
('origin_prego_nbr', models.IntegerField(blank=True, null=True)),
('origin_fms_nbr', models.IntegerField(blank=True, null=True)),
('pmo_number', models.IntegerField(blank=True, null=True, verbose_name='Numéro PMO')),
('creation_time', models.DateTimeField(auto_now_add=True)),
('last_modified_time', models.DateTimeField(auto_now=True, null=True)),
('transmission_time', models.DateTimeField(blank=True, null=True)),
('init_call_confirmed_time', models.DateTimeField(blank=True, null=True)),
('init_call_received', models.BooleanField(default=False)),
('init_description', models.TextField(blank=True, null=True)),
('osiris', models.IntegerField(blank=True, null=True)),
('linked_folder', models.CharField(blank=True, max_length=1024, null=True)),
('intervention_sheet', models.FileField(blank=True, null=True, storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.intervention_sheet_upload_path)),
('geom', django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=3812)),
('period_validation_enabled', models.BooleanField(default=False)),
('period_number', models.PositiveIntegerField(default=1)),
('period_is_final', models.BooleanField(default=False)),
('created_by_provider', models.BooleanField(default=False, help_text='Indique que cette intervention a été créée par un prestataire externe.', verbose_name='Créée par le prestataire')),
('processed_via_other_intervention', models.BooleanField(default=False, help_text='Indique que le traitement de cette intervention est réalisé via une autre intervention.', verbose_name='Traitement via une autre intervention')),
('reason_for_correction', models.TextField(blank=True, help_text='Raison pour laquelle une correction est demandée.', null=True, verbose_name='Raison de la correction')),
('correction_details', models.TextField(blank=True, help_text='Détails de ce qui a été corrigé.', null=True, verbose_name='Détails de la correction')),
('is_checked', models.BooleanField(default=False, help_text="Indique si l'intervention a été vérifiée par un utilisateur habilité.", verbose_name='Vérifiée')),
('follow_up_performed', models.CharField(blank=True, choices=[('ok', 'OK'), ('attention', 'Points d’attention'), ('not_ok', 'Not OK')], default=None, help_text='Résultat du contrôle / rapport de suivi.', max_length=20, null=True, verbose_name='Résultat du contrôle')),
('asset_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('assigned_member', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_interventions', to='contracts.companymember')),
('assigned_provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_interventions', to='contracts.company')),
('assigned_team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_interventions', to='contracts.companyteam')),
('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")),
('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')),
],
options={
'ordering': ['-creation_time'],
},
),
migrations.AddField(
model_name='guidedoperationdata',
name='intervention',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='guided_operation_data', to='interventions.intervention', verbose_name='Intervention'),
),
migrations.CreateModel(
name='InterventionAsset',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interventions.intervention')),
],
),
migrations.CreateModel(
name='InterventionContractPost',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Quantité')),
('comment', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Commentaire')),
('control_status', models.CharField(blank=True, choices=[('validated', 'Approuvé'), ('refused', 'Refusé'), ('pending', 'A justifier')], null=True)),
('control_comment', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Commentaire de contrôle')),
('contract_post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contracts.contractpost')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interv_contract_posts', to='interventions.intervention')),
],
options={
'ordering': ['contract_post__order_number'],
},
),
migrations.CreateModel(
name='InterventionDocument',
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')),
('status', models.CharField(choices=[('active', 'Actif'), ('archived', 'Archivé')], default='active')),
('file', models.FileField(max_length=255, storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.intervention_document_upload_path, verbose_name='Fichier')),
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.intervention_document_upload_path)),
('upload_date', models.DateTimeField(auto_now_add=True, null=True, verbose_name="Date d'upload")),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='interventions.intervention', verbose_name='Intervention associée')),
('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.PROTECT, related_name='uploaded_intervention_documents', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['upload_date'],
'abstract': False,
},
),
migrations.CreateModel(
name='InterventionEmailLog',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email_type', models.CharField(choices=[('transmission', 'Envoi de la fiche'), ('reminder', 'Rappel'), ('modification', 'Modification'), ('cancellation', 'Annulation'), ('daily_summary', 'Résumé journalier')], max_length=50)),
('sent_at', models.DateTimeField(auto_now_add=True)),
('recipients', models.TextField(help_text='Liste des destinataires (séparés par des virgules)')),
('subject', models.CharField(blank=True, max_length=500, null=True)),
('detail', models.TextField(blank=True, help_text='Détails supplémentaires (ex: motif de modification)', null=True)),
('success', models.BooleanField(default=True)),
('error_message', models.TextField(blank=True, null=True)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='email_logs', to='interventions.intervention')),
('sent_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='intervention_emails_sent', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': "Historique d'envoi d'email",
'verbose_name_plural': "Historiques d'envois d'emails",
'ordering': ['-sent_at'],
},
),
migrations.CreateModel(
name='InterventionNote',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField()),
('note_time', models.DateTimeField()),
('note_type', models.CharField(choices=[('work_done', 'Travaux réalisés'), ('material', 'Matériel à prévoir'), ('equipment', 'Matériel utilisé'), ('important_info', 'Information importante'), ('origin', 'Origine'), ('comment', 'Commentaire'), ('other', 'Autre')], default='work_done', max_length=32, verbose_name='Type de note')),
('updated_at', models.DateTimeField(blank=True, null=True)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='interventions.intervention')),
('note_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interventions_notes', to=settings.AUTH_USER_MODEL)),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_interventions_notes', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='InterventionOccupation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('minutes', models.PositiveIntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_intervention_occupations', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='occupations', to='interventions.intervention')),
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='occupations', to='contracts.companymember')),
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_intervention_occupations', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['created_at'],
},
),
migrations.CreateModel(
name='InterventionPlanificationTimeLine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_time', models.DateTimeField()),
('event_type', models.CharField(choices=[('expected_begin', 'Début prévu'), ('expected_end', 'Fin prévue'), ('planned_begin', 'Début planifié'), ('planned_end', 'Fin planifiée'), ('plan_other', 'Autre')], default='plan_other')),
('event_description', models.CharField(max_length=1024)),
('event_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='planned_actions', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='planned_events', to='interventions.intervention')),
],
options={
'ordering': ['event_time'],
},
),
migrations.CreateModel(
name='InterventionRealizationTimeLine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_time', models.DateTimeField()),
('event_type', models.CharField(choices=[('real_begin', "Démarrage de l'intervention"), ('real_end', "Fin de l'intervention"), ('real_pause', "Pause de l'intervention"), ('real_resume', "Reprise de l'intervention"), ('real_other', 'Autre')], default='real_other')),
('event_description', models.CharField(max_length=1024)),
('event_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='realized_actions', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='realized_events', to='interventions.intervention')),
],
options={
'ordering': ['event_time'],
},
),
migrations.CreateModel(
name='InterventionTimeLine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_time', models.DateTimeField()),
('event_type', models.CharField(choices=[('creation', 'Création'), ('status_change', 'Changement de statut'), ('transfer', 'Transfert')])),
('from_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é')], null=True)),
('to_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é')], null=True)),
('event_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='actions', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='events', to='interventions.intervention')),
],
options={
'ordering': ['event_time'],
},
),
migrations.CreateModel(
name='Operation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_fr', models.CharField(max_length=255)),
('name_nl', models.CharField(max_length=255)),
('comment', models.TextField(blank=True)),
('measured_value', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('measured_duration', models.DurationField(blank=True, null=True, verbose_name='Durée mesurée')),
('measured_datetime', models.DateTimeField(blank=True, null=True, verbose_name='Date/heure mesurée')),
('is_not_applicable', models.BooleanField(default=False, verbose_name='Non applicable')),
('order', models.PositiveIntegerField(default=0)),
('template_group', models.CharField(blank=True, max_length=255, null=True, verbose_name='Groupe de template')),
('asset_object_id', models.PositiveIntegerField(blank=True, null=True)),
('is_custom', models.BooleanField(default=False, verbose_name='Opération personnalisée')),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('is_generic', models.BooleanField(default=False, verbose_name='Opération générique')),
('requires_photo', models.BooleanField(default=False, verbose_name='Requiert une photo')),
('asset_content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operations', to='contenttypes.contenttype')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_operations', to=settings.AUTH_USER_MODEL)),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='operations', to='interventions.intervention')),
('measurement_definition', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.measurementdefinition')),
('status', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.operationstatus')),
('status_set', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.operationstatusset')),
],
options={
'ordering': ['template_group', 'order'],
},
),
migrations.CreateModel(
name='OperationPhoto',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('photo', models.ImageField(storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.operation_photo_upload_path, verbose_name='Photo')),
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to=interventions.models.operation_photo_upload_path)),
('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name="Date d'upload")),
('description', models.CharField(blank=True, max_length=255, null=True, verbose_name='Description')),
('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='interventions.operation')),
('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_operation_photos', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': "Photo d'opération",
'verbose_name_plural': "Photos d'opérations",
'ordering': ['-uploaded_at'],
},
),
migrations.AddField(
model_name='operationstatus',
name='status_set',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='statuses', to='interventions.operationstatusset'),
),
migrations.CreateModel(
name='OperationTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(blank=True, max_length=100, null=True)),
('name', models.CharField(max_length=255, unique=True)),
('description', models.TextField(blank=True)),
('categories', models.ManyToManyField(blank=True, help_text='Si défini, les opérations équipement ne seront créées que pour les assets de ces catégories.', related_name='operation_templates', to='assets.assetcategory', verbose_name='Catégories')),
('guided_template', models.ForeignKey(blank=True, help_text="Si défini, ce template d'opérations déclenchera automatiquement l'assignation du template guidé à l'intervention préventive.", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operation_templates', to='interventions.guidedoperationtemplate', verbose_name='Template guidé associé')),
('thematic', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.thematic')),
],
),
migrations.AddField(
model_name='intervention',
name='equipment_operation_templates',
field=models.ManyToManyField(blank=True, related_name='interventions_equipment', to='interventions.operationtemplate'),
),
migrations.CreateModel(
name='OperationTemplateItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(blank=True, max_length=100, null=True)),
('name_fr', models.CharField(max_length=255)),
('name_nl', models.CharField(max_length=255)),
('order', models.PositiveIntegerField(default=0)),
('is_generic', models.BooleanField(default=False, help_text='Si coché, cette opération sera ajoutée une seule fois par intervention. Sinon, elle sera répétée pour chaque équipement.', verbose_name='Opération générique')),
('requires_photo', models.BooleanField(default=False, verbose_name='Requiert une photo')),
('measurement_definition', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.measurementdefinition')),
('status_set', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.operationstatusset')),
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='interventions.operationtemplate')),
('operation_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.operationtype')),
],
options={
'ordering': ['order'],
},
),
migrations.AddField(
model_name='intervention',
name='source_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.sourcecategory'),
),
migrations.CreateModel(
name='Symptom',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(blank=True, help_text='Code unique optionnel pour identifier le symptôme (ex: pour les raccourcis).', max_length=50, null=True, unique=True)),
('name_fr', models.CharField(max_length=255)),
('name_nl', models.CharField(max_length=255)),
('priority', models.CharField(choices=[('1', 'Urgent'), ('2', 'Elevé'), ('3', 'Normal'), ('4', 'Bas')], default='3')),
('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)),
('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)),
('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')),
('provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contracts.company')),
('teams', models.ManyToManyField(blank=True, help_text='Équipes désignées pour prendre en charge les observations liées à ce symptôme.', related_name='symptoms', to='contracts.companyteam', verbose_name='Équipes désignées')),
('thematic', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='common.thematic')),
],
options={
'ordering': ['thematic', 'order_number'],
},
),
migrations.CreateModel(
name='ReflexSheet',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title_fr', models.CharField(max_length=255)),
('title_nl', models.CharField(max_length=255)),
('comment_fr', models.TextField(blank=True, null=True)),
('comment_nl', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_active', models.BooleanField(default=True)),
('asset_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.assetcategory')),
('contact_persons', models.ManyToManyField(blank=True, related_name='reflex_sheets', to=settings.AUTH_USER_MODEL)),
('thematic', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='common.thematic')),
('symptoms', models.ManyToManyField(related_name='reflex_sheets', to='interventions.symptom')),
],
options={
'ordering': ['thematic', 'title_fr'],
},
),
migrations.AddField(
model_name='intervention',
name='symptom',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='interventions.symptom'),
),
migrations.CreateModel(
name='SymptomRecommendation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('icon', models.CharField(blank=True, max_length=255, null=True)),
('text_fr', models.TextField()),
('text_nl', models.TextField()),
('order_number', models.PositiveIntegerField(blank=True, null=True)),
('document', models.FileField(blank=True, null=True, storage=common.storages.PrivateMediaStorage(), upload_to='symptom_recommendations/')),
('confirm_call', models.BooleanField(default=False)),
('symptom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recommendations', to='interventions.symptom')),
],
options={
'ordering': ['order_number'],
},
),
migrations.CreateModel(
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')),
('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')),
],
options={
'verbose_name': 'Réassignation automatique sur pause',
'verbose_name_plural': 'Réassignations automatiques sur pause',
'unique_together': {('contract', 'pause_reason')},
},
),
migrations.CreateModel(
name='InterventionLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('link_type', models.CharField(choices=[('related', 'Lien manuel'), ('repair', 'Réparation')], default='related', max_length=32)),
('created_at', models.DateTimeField(auto_now_add=True)),
('first', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='links_as_first', to='interventions.intervention')),
('second', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='links_as_second', to='interventions.intervention')),
],
options={
'ordering': ['-created_at'],
'constraints': [models.UniqueConstraint(fields=('first', 'second', 'link_type'), name='uniq_intervention_link'), models.CheckConstraint(condition=models.Q(('first', models.F('second')), _negated=True), name='prevent_self_link')],
},
),
migrations.CreateModel(
name='InterventionLocation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField(db_index=True)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='interventions.intervention')),
],
options={
'indexes': [models.Index(fields=['content_type', 'object_id'], name='ix_ct_obj'), models.Index(fields=['intervention', 'content_type'], name='ix_intv_ct')],
'constraints': [models.UniqueConstraint(fields=('intervention', 'content_type', 'object_id'), name='uniq_intervention_location')],
},
),
migrations.CreateModel(
name='InterventionPostPeriodQuantity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('period_number', models.PositiveIntegerField()),
('quantity', models.DecimalField(decimal_places=2, max_digits=10)),
('validated_at', models.DateTimeField()),
('status', models.CharField(choices=[('pending', 'En attente'), ('validated', 'Validé'), ('invoiced', 'Facturé')], default='pending', max_length=20, verbose_name='Statut')),
('contract_post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='period_quantities', to='contracts.contractpost')),
('intervention', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='period_quantities', to='interventions.intervention')),
('validated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['intervention', 'period_number', 'contract_post__order_number'],
'unique_together': {('intervention', 'contract_post', 'period_number')},
},
),
migrations.AlterUniqueTogether(
name='operationstatus',
unique_together={('status_set', 'value')},
),
migrations.CreateModel(
name='OperationTemplateAssetModelFilter',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('asset_model_object_id', models.PositiveIntegerField(verbose_name='ID du modèle')),
('asset_model_content_type', models.ForeignKey(limit_choices_to={'app_label': 'assets'}, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name="Type de modèle d'asset")),
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='asset_model_filters', to='interventions.operationtemplate', verbose_name='Template')),
],
options={
'verbose_name': "Filtre de modèle d'asset",
'verbose_name_plural': "Filtres de modèles d'assets",
'unique_together': {('template', 'asset_model_content_type', 'asset_model_object_id')},
},
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['status', 'thematic'], name='interventio_status_e47e89_idx'),
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['status', 'creation_time'], name='interventio_status_1befac_idx'),
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['thematic', 'creation_time'], name='interventio_themati_9c147f_idx'),
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['thematic', 'status', 'maintain_type'], name='intv_thematic_status_mtype_idx'),
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['contract', 'status'], name='intv_contract_status_idx'),
),
migrations.AddIndex(
model_name='intervention',
index=models.Index(fields=['begin_time', 'planned_begin_time', 'expected_begin_time', 'maintain_type'], name='intv_chart_begin_maintain_idx'),
),
]