291 lines
21 KiB
Python
291 lines
21 KiB
Python
# Generated by Django 6.0.7 on 2026-07-22 13:42
|
||
|
||
import common.storages
|
||
import contracts.models
|
||
import django.db.models.deletion
|
||
from decimal import Decimal
|
||
from django.conf import settings
|
||
from django.db import migrations, models
|
||
|
||
|
||
class Migration(migrations.Migration):
|
||
|
||
initial = True
|
||
|
||
dependencies = [
|
||
('assets', '0001_initial'),
|
||
('common', '0001_initial'),
|
||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||
]
|
||
|
||
operations = [
|
||
migrations.CreateModel(
|
||
name='Company',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=255, unique=True)),
|
||
('address', models.TextField(blank=True, null=True)),
|
||
('contact_email', models.EmailField(blank=True, max_length=254, null=True)),
|
||
('phone_number', models.CharField(blank=True, max_length=20, null=True)),
|
||
('logo', models.ImageField(blank=True, null=True, upload_to='company_logos/')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Société/Organisme',
|
||
'verbose_name_plural': 'Sociétés/Organismes',
|
||
'ordering': ['name'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderAttachment',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('file', models.FileField(max_length=255, storage=common.storages.PrivateMediaStorage(), upload_to=contracts.models.order_attachment_upload_path, verbose_name='Fichier')),
|
||
('thumbnail', models.ImageField(blank=True, max_length=255, null=True, storage=common.storages.PrivateMediaStorage(), upload_to=contracts.models.order_attachment_upload_path, verbose_name='Vignette')),
|
||
('upload_date', models.DateTimeField(auto_now_add=True, verbose_name="Date d'ajout")),
|
||
],
|
||
options={
|
||
'verbose_name': 'Pièce jointe de commande',
|
||
'verbose_name_plural': 'Pièces jointes de commande',
|
||
'ordering': ['upload_date'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderDocument',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('title', models.CharField(max_length=255)),
|
||
('document', models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=contracts.models.order_document_upload_path)),
|
||
('document_type', models.CharField(choices=[('invoice', 'Facture'), ('claim_declaration', 'Déclaration de créance'), ('state_of_works', 'État de travaux'), ('posts_detail', 'Détail des postes'), ('other', 'Autre')], default='other', max_length=50)),
|
||
('uploaded_at', models.DateTimeField(auto_now_add=True)),
|
||
],
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderProgressState',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('index', models.PositiveIntegerField(blank=True, help_text="Numéro d'ordre de l'état d'avancement pour cette commande", null=True)),
|
||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||
],
|
||
options={
|
||
'verbose_name': "État d'avancement",
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderProgressStateItem',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('quantity', models.DecimalField(decimal_places=2, default=0, help_text='Quantité consommée à cette date', max_digits=10)),
|
||
],
|
||
options={
|
||
'verbose_name': "Item d'état d'avancement",
|
||
'verbose_name_plural': "Items d'états d'avancement",
|
||
'ordering': ['contract_post__order_number'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderQuote',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||
('updated_at', models.DateTimeField(auto_now=True)),
|
||
('is_finalized', models.BooleanField(default=False)),
|
||
('finalized_at', models.DateTimeField(blank=True, null=True)),
|
||
],
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrderQuoteItem',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('quantity', models.DecimalField(decimal_places=2, max_digits=10)),
|
||
],
|
||
options={
|
||
'ordering': ['contract_post__order_number'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractPost',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('code', models.CharField(max_length=255)),
|
||
('description_fr', models.TextField()),
|
||
('description_nl', models.TextField()),
|
||
('order_number', models.PositiveIntegerField()),
|
||
('unit', models.CharField(max_length=255)),
|
||
('unit_price', models.DecimalField(decimal_places=2, max_digits=10)),
|
||
('total_quantity_used', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
|
||
('post_type', models.CharField(blank=True, max_length=100, null=True)),
|
||
('is_coefficient_multipliable', models.BooleanField(default=False)),
|
||
('is_amount_to_be_justified', models.BooleanField(default=False)),
|
||
('coeff_amount_to_be_justified', models.DecimalField(blank=True, decimal_places=2, default=1, max_digits=10, null=True)),
|
||
],
|
||
options={
|
||
'ordering': ['order_number'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='DirectOrderPost',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('quantity', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Quantité')),
|
||
('comment', models.TextField(blank=True, null=True, verbose_name='Commentaire')),
|
||
('control_status', models.CharField(choices=[('pending', 'En attente de validation'), ('validated', 'Approuvé'), ('refused', 'Refusé'), ('invoiced', 'Facturé')], default='pending', max_length=20, verbose_name='Statut de validation')),
|
||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Date de création')),
|
||
('validated_at', models.DateTimeField(blank=True, null=True, verbose_name='Date de validation')),
|
||
('invoiced_quantity', models.DecimalField(decimal_places=2, default=Decimal('0'), help_text='Quantité cumulée sur les déclarations de créance précédentes.', max_digits=10, verbose_name='Quantité déjà facturée')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Commande directe – poste',
|
||
'verbose_name_plural': 'Commandes directes – postes',
|
||
'ordering': ['contract_post__order_number'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='MaintainType',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('code', models.CharField(help_text='Code du type de maintenance (corrective, preventive, ameliorative)', max_length=50, unique=True)),
|
||
('name_fr', models.CharField(max_length=100, verbose_name='Nom (FR)')),
|
||
('name_nl', models.CharField(max_length=100, verbose_name='Nom (NL)')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Type de maintenance',
|
||
'verbose_name_plural': 'Types de maintenance',
|
||
'ordering': ['code'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='UserCompanyTeamAccess',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('can_view', models.BooleanField(default=False, help_text="L'utilisateur peut voir cette équipe et ses membres.", verbose_name='Peut consulter')),
|
||
('can_edit', models.BooleanField(default=False, help_text="L'utilisateur peut créer/supprimer des équipes et des membres, et déplacer des membres entre équipes.", verbose_name='Peut éditer')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Accès utilisateur à une équipe',
|
||
'verbose_name_plural': 'Accès utilisateurs aux équipes',
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ClaimDeclarationAttachment',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('title', models.CharField(help_text='Titre ou description du justificatif', max_length=255, verbose_name='Titre')),
|
||
('document', models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=contracts.models.claim_attachment_upload_path, verbose_name='Fichier')),
|
||
('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name="Date d'ajout")),
|
||
('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_claim_attachments', to=settings.AUTH_USER_MODEL, verbose_name='Ajouté par')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Pièce jointe de déclaration de créance',
|
||
'verbose_name_plural': 'Pièces jointes de déclarations de créance',
|
||
'ordering': ['uploaded_at'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='CompanyMember',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=255)),
|
||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||
],
|
||
options={
|
||
'verbose_name': 'Membre de société/organisme',
|
||
'verbose_name_plural': 'Membres de sociétés/organismes',
|
||
'ordering': ['name'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='CompanyTeam',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('name', models.CharField(max_length=255, unique=True)),
|
||
('is_assets_manager', models.BooleanField(default=False, help_text="Indique si cette équipe est gestionnaire des catégories d'assets associées.", verbose_name="Gestionnaire d'assets")),
|
||
('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')),
|
||
],
|
||
options={
|
||
'verbose_name': 'Équipe de société/organisme',
|
||
'verbose_name_plural': 'Équipes de sociétés/organismes',
|
||
'ordering': ['name'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='CompanyMemberHistory',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('join_date', models.DateField(verbose_name="Date d'entrée")),
|
||
('leave_date', models.DateField(blank=True, null=True, verbose_name='Date de sortie')),
|
||
('changed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_team_changes', to=settings.AUTH_USER_MODEL, verbose_name='Modifié par')),
|
||
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_history', to='contracts.companymember', verbose_name='Membre')),
|
||
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_history', to='contracts.companyteam', verbose_name='Équipe')),
|
||
],
|
||
options={
|
||
'verbose_name': "Historique d'équipe d'un membre",
|
||
'verbose_name_plural': "Historiques d'équipe des membres",
|
||
'ordering': ['member', '-join_date'],
|
||
},
|
||
),
|
||
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.CreateModel(
|
||
name='Contract',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('contract_number', models.CharField(max_length=100, unique=True)),
|
||
('start_date', models.DateField()),
|
||
('end_date', models.DateField()),
|
||
('description', models.TextField(blank=True, null=True)),
|
||
('committed_amount', models.DecimalField(blank=True, decimal_places=2, default=0, max_digits=10, null=True)),
|
||
('internal_link', models.URLField(blank=True, help_text='Lien vers le dossier interne', null=True)),
|
||
('notification_emails', models.CharField(blank=True, max_length=1000, null=True)),
|
||
('cc_notification_emails', models.CharField(blank=True, help_text='Adresses email à ajouter en copie des notifications (séparées par des virgules ou points-virgules)', max_length=1000, null=True, verbose_name='Emails en copie (CC)')),
|
||
('send_email_notifications', models.BooleanField(default=True, help_text='Envoyer des notifications liées aux interventions par email.')),
|
||
('signatory_name', models.CharField(blank=True, help_text='Nom de la personne qui signe les documents côté prestataire.', max_length=255, null=True, verbose_name='Nom du signataire (prestataire)')),
|
||
('signatory_role', models.CharField(blank=True, help_text='Rôle/fonction de la personne qui signe les documents côté prestataire.', max_length=255, null=True, verbose_name='Rôle du signataire (prestataire)')),
|
||
('contact_phone', models.CharField(blank=True, help_text='Numéro de téléphone du technicien de garde ou contact principal pour les urgences.', max_length=50, null=True, verbose_name='Téléphone de contact')),
|
||
('is_active', models.BooleanField(default=True, help_text='Indique si le contrat est actif.', verbose_name='Actif')),
|
||
('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')),
|
||
('daily_summary_hour', models.PositiveSmallIntegerField(default=18, help_text='Heure (0-23) à laquelle le résumé journalier des interventions est envoyé (heure locale).', verbose_name="Heure d'envoi du résumé journalier")),
|
||
('email_only_if_urgent', models.BooleanField(default=False, help_text="Si activé, les notifications email ne sont envoyées que pour les interventions de priorité 'Urgent'.", verbose_name='Emails uniquement si priorité urgente')),
|
||
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contracts', to='contracts.company')),
|
||
('executive_officer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='executive_officer_contracts', to=settings.AUTH_USER_MODEL)),
|
||
('thematics', models.ManyToManyField(blank=True, help_text='Thématiques associées à ce contrat.', related_name='contracts', to='common.thematic', verbose_name='Thématiques')),
|
||
],
|
||
options={
|
||
'ordering': ['contract_number'],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name='ContractOrder',
|
||
fields=[
|
||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||
('order_code', models.CharField(max_length=100)),
|
||
('description', models.TextField(blank=True, null=True)),
|
||
('order_date', models.DateField()),
|
||
('delivery_date', models.DateField()),
|
||
('total_amount', models.DecimalField(blank=True, decimal_places=2, default=0, max_digits=10, null=True)),
|
||
('order_status', models.CharField(choices=[('pending', 'En préparation'), ('sent', 'Envoyé'), ('completed', 'Terminé'), ('invoiced', 'Comptabilisé'), ('paid', 'Payé'), ('cancelled', 'Annulé')], default='pending', max_length=50)),
|
||
('order_file', models.FileField(blank=True, null=True, upload_to=contracts.models.contract_order_upload_path)),
|
||
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='contracts.contract')),
|
||
('ordered_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contract_orders', to=settings.AUTH_USER_MODEL)),
|
||
],
|
||
options={
|
||
'ordering': ['-order_date'],
|
||
},
|
||
),
|
||
]
|