From c0363f6bd16bf471fbf78a620fd32740f3b87bd1 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Thu, 28 Apr 2022 23:04:31 +0200 Subject: [PATCH 01/25] ajout formulaires --- app/entreprises/forms.py | 45 +++++++++++++++++++ app/entreprises/routes.py | 40 +++++++++++++++-- app/templates/entreprises/correspondants.html | 2 +- .../entreprises/fiche_entreprise.html | 5 ++- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index a16190070..610aa3608 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -50,6 +50,7 @@ from app.entreprises.models import ( Entreprise, EntrepriseCorrespondant, EntreprisePreferences, + EntrepriseSite, ) from app.models import Identite, Departement from app.auth.models import User @@ -171,6 +172,7 @@ class EntrepriseModificationForm(FlaskForm): class SiteCreationForm(FlaskForm): + hidden_entreprise_id = HiddenField() nom = _build_string_field("Nom du site (*)") adresse = _build_string_field("Adresse (*)") codepostal = _build_string_field("Code postal (*)") @@ -178,6 +180,49 @@ class SiteCreationForm(FlaskForm): pays = _build_string_field("Pays", required=False) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) + def validate(self): + validate = True + if not FlaskForm.validate(self): + validate = False + + site = EntrepriseSite.query.filter_by( + entreprise_id=self.hidden_entreprise_id.data, nom=self.nom.data + ).first() + + if site is not None: + self.nom.errors.append("Ce site existe déjà (même nom)") + validate = False + + return validate + + +class SiteModificationForm(FlaskForm): + hidden_entreprise_id = HiddenField() + hidden_site_id = HiddenField() + nom = _build_string_field("Nom du site (*)") + adresse = _build_string_field("Adresse (*)") + codepostal = _build_string_field("Code postal (*)") + ville = _build_string_field("Ville (*)") + pays = _build_string_field("Pays", required=False) + submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) + + def validate(self): + validate = True + if not FlaskForm.validate(self): + validate = False + + site = EntrepriseSite.query.filter( + EntrepriseSite.entreprise_id == self.hidden_entreprise_id.data, + EntrepriseSite.id != self.hidden_site_id.data, + EntrepriseSite.nom == self.nom.data, + ).first() + + if site is not None: + self.nom.errors.append("Ce site existe déjà (même nom)") + validate = False + + return validate + class MultiCheckboxField(SelectMultipleField): widget = ListWidget(prefix_label=False) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index b815b2297..447484a9a 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -17,6 +17,7 @@ from app.entreprises.forms import ( EntrepriseCreationForm, EntrepriseModificationForm, SiteCreationForm, + SiteModificationForm, SuppressionConfirmationForm, OffreCreationForm, OffreModificationForm, @@ -110,8 +111,9 @@ def correspondants(): Permet d'afficher une page avec la liste des correspondants des entreprises visibles et une liste des dernières opérations """ correspondants = ( - db.session.query(EntrepriseCorrespondant, Entreprise) - .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) + db.session.query(EntrepriseCorrespondant, EntrepriseSite) + .join(EntrepriseSite, EntrepriseCorrespondant.site_id == EntrepriseSite.id) + .join(Entreprise, EntrepriseSite.entreprise_id == Entreprise.id) .filter_by(visible=True, active=True) ) logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() @@ -700,7 +702,7 @@ def add_site(id): entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( description=f"entreprise {id} inconnue" ) - form = SiteCreationForm() + form = SiteCreationForm(hidden_entreprise_id=id) if form.validate_on_submit(): site = EntrepriseSite( entreprise_id=entreprise.id, @@ -721,6 +723,38 @@ def add_site(id): ) +@bp.route( + "/fiche_entreprise//edit_site/", + methods=["GET", "POST"], +) +def edit_site(id_entreprise, id_site): + site = EntrepriseSite.query.filter_by( + id=id_site, entreprise_id=id_entreprise + ).first_or_404(description=f"site {id_site} inconnu") + form = SiteModificationForm( + hidden_entreprise_id=id_entreprise, hidden_site_id=id_site + ) + if form.validate_on_submit(): + site.nom = form.nom.data.strip() + site.adresse = form.adresse.data.strip() + site.codepostal = form.codepostal.data.strip() + site.ville = form.ville.data.strip() + site.pays = (form.pays.data.strip() if form.pays.data.strip() else "FRANCE",) + db.session.commit() + return redirect(url_for("entreprises.fiche_entreprise", id=site.entreprise_id)) + elif request.method == "GET": + form.nom.data = site.nom + form.adresse.data = site.adresse + form.codepostal.data = site.codepostal + form.ville.data = site.ville + form.pays.data = site.pays + return render_template( + "entreprises/form.html", + title="Modification site", + form=form, + ) + + @bp.route( "/fiche_entreprise//add_correspondant/", methods=["GET", "POST"], diff --git a/app/templates/entreprises/correspondants.html b/app/templates/entreprises/correspondants.html index 047b57aa6..14804dd0a 100644 --- a/app/templates/entreprises/correspondants.html +++ b/app/templates/entreprises/correspondants.html @@ -54,7 +54,7 @@ {{ correspondant[0].mail }} {{ correspondant[0].poste}} {{ correspondant[0].service}} - {{ correspondant[1].nom }} + {{ correspondant[1].nom }} {% endfor %} diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 327a3bf7c..787bc4839 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -61,7 +61,10 @@ Ville : {{ site.ville }}
Pays : {{ site.pays }} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} -
Ajouter correspondant + {% endif %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %} {% for correspondant in site.correspondants %} From a5cec8e06827fa237c0197732683afab2973a5c8 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Mon, 2 May 2022 14:36:39 +0200 Subject: [PATCH 02/25] =?UTF-8?q?affichage=20checkbox=20entreprises=20d?= =?UTF-8?q?=C3=A9sactiv=C3=A9s,=20activer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/forms.py | 9 ++- app/entreprises/routes.py | 58 ++++++++++++++++--- .../entreprises/confirmation_form.html | 2 +- app/templates/entreprises/entreprises.html | 12 +++- .../entreprises/fiche_entreprise.html | 4 ++ 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index 610aa3608..f898e0e4a 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -42,6 +42,7 @@ from wtforms import ( BooleanField, FieldList, FormField, + BooleanField, ) from wtforms.validators import ValidationError, DataRequired, Email, Optional from wtforms.widgets import ListWidget, CheckboxInput @@ -70,6 +71,10 @@ def _build_string_field(label, required=True, render_kw=None): return StringField(label, validators=[Optional()], render_kw=render_kw) +class EntreprisesFilterForm(FlaskForm): + active = BooleanField("Afficher les entreprises désactivés") + + class EntrepriseCreationForm(FlaskForm): siret = _build_string_field( "SIRET (*)", @@ -656,8 +661,10 @@ class SuppressionConfirmationForm(FlaskForm): class DesactivationConfirmationForm(FlaskForm): notes_active = TextAreaField("Notes sur la désactivation", validators=[Optional()]) - submit = SubmitField("Désactiver", render_kw=SUBMIT_MARGE) + submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) +class ActivationConfirmationForm(FlaskForm): + submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) class ValidationConfirmationForm(FlaskForm): submit = SubmitField("Valider", render_kw=SUBMIT_MARGE) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 447484a9a..1607bc665 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -12,10 +12,12 @@ from app.decorators import permission_required from app.entreprises import LOGS_LEN from app.entreprises.forms import ( + ActivationConfirmationForm, CorrespondantsCreationForm, DesactivationConfirmationForm, EntrepriseCreationForm, EntrepriseModificationForm, + EntreprisesFilterForm, SiteCreationForm, SiteModificationForm, SuppressionConfirmationForm, @@ -57,7 +59,7 @@ from sqlalchemy import text from werkzeug.utils import secure_filename -@bp.route("/", methods=["GET"]) +@bp.route("/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesView) def index(): """ @@ -65,6 +67,21 @@ def index(): """ entreprises = Entreprise.query.filter_by(visible=True, active=True) logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() + if current_user.has_permission(Permission.RelationsEntreprisesChange, None): + form = EntreprisesFilterForm() + checked = False + if request.method == "POST": + checked = form.active.data + if checked: + entreprises = Entreprise.query.filter_by(visible=True) + return render_template( + "entreprises/entreprises.html", + title="Entreprises", + entreprises=entreprises, + logs=logs, + form=form, + checked=checked, + ) return render_template( "entreprises/entreprises.html", title="Entreprises", @@ -134,9 +151,9 @@ def fiche_entreprise(id): La fiche entreprise comporte les informations de l'entreprise, les correspondants de l'entreprise et les offres de l'entreprise. """ - entreprise = Entreprise.query.filter_by( - id=id, visible=True, active=True - ).first_or_404(description=f"fiche entreprise {id} inconnue") + entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( + description=f"fiche entreprise {id} inconnue" + ) offres_with_files = [] depts = are.get_depts() for offre in entreprise.offres: @@ -430,21 +447,44 @@ def fiche_entreprise_desactiver(id): """ Permet de désactiver une entreprise """ - entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( - description=f"entreprise {id} inconnue" - ) + entreprise = Entreprise.query.filter_by( + id=id, visible=True, active=True + ).first_or_404(description=f"entreprise {id} inconnue") form = DesactivationConfirmationForm() if form.validate_on_submit(): entreprise.notes_active = form.notes_active.data.strip() entreprise.active = False db.session.commit() flash("L'entreprise a été désactivé.") - return redirect(url_for("entreprises.index")) + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) return render_template( "entreprises/confirmation_form.html", title="Désactiver entreprise", form=form, - info_message="Cliquez sur le bouton Désactiver pour confirmer la désactivation", + info_message="Cliquez sur le bouton Modifier pour confirmer la désactivation", + ) + + +@bp.route("/fiche_entreprise/activer/", methods=["GET", "POST"]) +@permission_required(Permission.RelationsEntreprisesChange) +def fiche_entreprise_activer(id): + """ + Permet d'activer une entreprise + """ + entreprise = Entreprise.query.filter_by( + id=id, visible=True, active=False + ).first_or_404(description=f"entreprise {id} inconnue") + form = ActivationConfirmationForm() + if form.validate_on_submit(): + entreprise.active = True + db.session.commit() + flash("L'entreprise a été activé.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template( + "entreprises/confirmation_form.html", + title="Activer entreprise", + form=form, + info_message="Cliquez sur le bouton Modifier pour confirmer l'activaction", ) diff --git a/app/templates/entreprises/confirmation_form.html b/app/templates/entreprises/confirmation_form.html index fb8737887..5de63ba06 100644 --- a/app/templates/entreprises/confirmation_form.html +++ b/app/templates/entreprises/confirmation_form.html @@ -5,7 +5,7 @@ {% block app_content %}

{{ title }}


-
{{ info_message }}
+
{{ info_message }}

diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html index f6d08cec4..90ddd8ae8 100644 --- a/app/templates/entreprises/entreprises.html +++ b/app/templates/entreprises/entreprises.html @@ -36,6 +36,12 @@

Liste des entreprises

+ {% if form %} +
+ {{ form.hidden_tag() }} + +
+ {% endif %} @@ -53,7 +59,7 @@ {% for entreprise in entreprises %} - + @@ -67,7 +73,11 @@ diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 787bc4839..eba36af24 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -40,7 +40,11 @@
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} Modifier + {% if entreprise.active %} Désactiver + {% else %} + Activer + {% endif %} Ajouter site Ajouter offre {% endif %} From 0485c8769d84a23e4be8ee8426d92adb6223034c Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Wed, 4 May 2022 18:59:29 +0200 Subject: [PATCH 03/25] ajout taxe apprentissage --- app/entreprises/__init__.py | 9 +++ app/entreprises/forms.py | 58 ++++++++++++++++++- app/entreprises/models.py | 11 ++++ app/entreprises/routes.py | 46 +++++++++++++-- app/static/css/entreprises.css | 14 +++++ app/templates/entreprises/entreprises.html | 2 +- .../entreprises/fiche_entreprise.html | 17 ++++++ 7 files changed, 151 insertions(+), 6 deletions(-) diff --git a/app/entreprises/__init__.py b/app/entreprises/__init__.py index 7f100d51e..44fd128e7 100644 --- a/app/entreprises/__init__.py +++ b/app/entreprises/__init__.py @@ -1,6 +1,7 @@ """entreprises.__init__ """ +from datetime import datetime from flask import Blueprint from app.scodoc import sco_etud from app.auth.models import User @@ -47,4 +48,12 @@ def get_civilité(civ): return "Madame" +@bp.app_template_filter() +def check_taxe_now(taxes): + for taxe in taxes: + if taxe.annee == int(datetime.now().strftime("%Y")): + return True + return False + + from app.entreprises import routes diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index f898e0e4a..d2e9eb2f6 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -26,6 +26,7 @@ import re import requests +from datetime import datetime from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed, FileRequired @@ -33,6 +34,7 @@ from markupsafe import Markup from sqlalchemy import text from wtforms import ( StringField, + IntegerField, SubmitField, TextAreaField, SelectField, @@ -44,7 +46,13 @@ from wtforms import ( FormField, BooleanField, ) -from wtforms.validators import ValidationError, DataRequired, Email, Optional +from wtforms.validators import ( + ValidationError, + DataRequired, + Email, + Optional, + NumberRange, +) from wtforms.widgets import ListWidget, CheckboxInput from app.entreprises.models import ( @@ -52,6 +60,7 @@ from app.entreprises.models import ( EntrepriseCorrespondant, EntreprisePreferences, EntrepriseSite, + EntrepriseTaxeApprentissage, ) from app.models import Identite, Departement from app.auth.models import User @@ -608,6 +617,50 @@ class StageApprentissageModificationForm(FlaskForm): raise ValidationError("Champ incorrect (selectionnez dans la liste)") +class TaxeApprentissageForm(FlaskForm): + hidden_entreprise_id = HiddenField() + annee = IntegerField( + "Année (*)", + validators=[ + DataRequired(message=CHAMP_REQUIS), + NumberRange( + max=int(datetime.now().strftime("%Y")), + message=f"L'année doit être inférieure ou égale à {int(datetime.now().strftime('%Y'))}", + ), + ], + default=int(datetime.now().strftime("%Y")), + ) + montant = IntegerField( + "Montant (*)", + validators=[ + DataRequired(message=CHAMP_REQUIS), + NumberRange( + min=1, + message="Le montant doit être supérieur à 0", + ), + ], + default=1, + ) + notes = TextAreaField("Notes") + submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) + + def validate(self): + validate = True + if not FlaskForm.validate(self): + validate = False + + taxe = EntrepriseTaxeApprentissage.query.filter_by( + entreprise_id=self.hidden_entreprise_id.data, annee=self.annee.data + ).first() + if taxe is not None: + self.annee.errors.append( + "Une taxe d'apprentissage a déjà été versé pour cette année" + ) + validate = False + + return validate + + class EnvoiOffreForm(FlaskForm): responsables = FieldList( _build_string_field( @@ -663,9 +716,11 @@ class DesactivationConfirmationForm(FlaskForm): notes_active = TextAreaField("Notes sur la désactivation", validators=[Optional()]) submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) + class ActivationConfirmationForm(FlaskForm): submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) + class ValidationConfirmationForm(FlaskForm): submit = SubmitField("Valider", render_kw=SUBMIT_MARGE) @@ -685,6 +740,7 @@ class PreferencesForm(FlaskForm): mail_entreprise = StringField( "Mail notifications", validators=[Optional(), Email(message="Adresse e-mail invalide")], + description="utilisé pour envoi mail notification application relations entreprises", ) check_siret = BooleanField("Vérification SIRET") submit = SubmitField("Valider", render_kw=SUBMIT_MARGE) diff --git a/app/entreprises/models.py b/app/entreprises/models.py index 4a318d24a..4ff358004 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -155,6 +155,17 @@ class EntrepriseStageApprentissage(db.Model): notes = db.Column(db.Text) +class EntrepriseTaxeApprentissage(db.Model): + __tablename__ = "are_taxe_apprentissage" + id = db.Column(db.Integer, primary_key=True) + entreprise_id = db.Column( + db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade") + ) + annee = db.Column(db.Integer) + montant = db.Column(db.Integer) + notes = db.Column(db.Text) + + class EntrepriseEnvoiOffre(db.Model): __tablename__ = "are_envoi_offre" id = db.Column(db.Integer, primary_key=True) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 1607bc665..c0f47382b 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -30,6 +30,7 @@ from app.entreprises.forms import ( StageApprentissageModificationForm, EnvoiOffreForm, AjoutFichierForm, + TaxeApprentissageForm, ValidationConfirmationForm, ImportForm, PreferencesForm, @@ -46,6 +47,7 @@ from app.entreprises.models import ( EntrepriseEnvoiOffre, EntrepriseOffreDepartement, EntreprisePreferences, + EntrepriseTaxeApprentissage, ) from app.entreprises import app_relations_entreprises as are from app.models import Identite @@ -181,6 +183,11 @@ def fiche_entreprise(id): .join(Identite, Identite.id == EntrepriseStageApprentissage.etudid) .all() ) + taxes = ( + EntrepriseTaxeApprentissage.query.filter_by(entreprise_id=id) + .order_by(EntrepriseTaxeApprentissage.annee.desc()) + .all() + ) return render_template( "entreprises/fiche_entreprise.html", title="Fiche entreprise", @@ -189,6 +196,7 @@ def fiche_entreprise(id): offres=offres_with_files, logs=logs, stages_apprentissages=stages_apprentissages, + taxes=taxes, ) @@ -488,6 +496,32 @@ def fiche_entreprise_activer(id): ) +@bp.route("/fiche_entreprise//add_taxe_apprentissage", methods=["GET", "POST"]) +def add_taxe_apprentissage(id): + """ + Permet d'ajouter une taxe d'apprentissage sur un fiche entreprise + """ + entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( + description=f"entreprise {id} inconnue" + ) + form = TaxeApprentissageForm(hidden_entreprise_id=id) + if form.validate_on_submit(): + taxe = EntrepriseTaxeApprentissage( + entreprise_id=entreprise.id, + annee=form.annee.data, + montant=form.montant.data, + notes=form.notes.data.strip(), + ) + db.session.add(taxe) + db.session.commit() + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template( + "entreprises/form.html", + title="Ajout taxe apprentissage", + form=form, + ) + + @bp.route( "/fiche_entreprise_validation//validate_entreprise", methods=["GET", "POST"] ) @@ -1199,7 +1233,7 @@ def json_etudiants(): if request.args.get("term") is None: abort(400) term = request.args.get("term").strip() - etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).all() + etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).limit(30).all() list = [] for etudiant in etudiants: content = {} @@ -1225,9 +1259,13 @@ def json_responsables(): if request.args.get("term") is None: abort(400) term = request.args.get("term").strip() - responsables = User.query.filter( - User.nom.ilike(f"%{term}%"), User.nom.is_not(None), User.prenom.is_not(None) - ).all() + responsables = ( + User.query.filter( + User.nom.ilike(f"%{term}%"), User.nom.is_not(None), User.prenom.is_not(None) + ) + .limit(30) + .all() + ) list = [] for responsable in responsables: diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 0ede866cb..286381d6c 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -67,6 +67,20 @@ margin-top: 10px; } +.entreprise { + display: flex; + justify-content: space-between; +} + +.entreprise > div { + flex: 1 0 0; +} + +.taxe-apprentissage{ + overflow-y: scroll; + height: 100px; +} + .sites-et-offres { display: flex; justify-content: space-between; diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html index 90ddd8ae8..a45113dea 100644 --- a/app/templates/entreprises/entreprises.html +++ b/app/templates/entreprises/entreprises.html @@ -76,7 +76,7 @@ {% if entreprise.active %}
  • Désactiver
  • {% else %} -
  • Activer
  • +
  • Activer
  • {% endif %}
    diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index eba36af24..b8a1bf890 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -35,6 +35,23 @@ Ville : {{ entreprise.ville }}
    Pays : {{ entreprise.pays }} + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} +
    + Taxe d'apprentissage
    + Ajouter taxe apprentissage +
    +
      + {% if not taxes|check_taxe_now %} +
    • année actuelle : non versé
    • + {% endif %} + {% for taxe in taxes %} +
    • {{ taxe.annee }} : {{ taxe.montant }} euros
    • + {% endfor %} +
    +
    +
    + {% endif %}
    From 48c16d761f56749280368908e9457ba347052ed4 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 6 May 2022 01:33:50 +0200 Subject: [PATCH 04/25] suite taxe apprentissage, association partenaire --- app/entreprises/forms.py | 32 ++++++-- app/entreprises/models.py | 1 + app/entreprises/routes.py | 73 ++++++++++++++++++- app/static/css/entreprises.css | 5 ++ app/templates/entreprises/entreprises.html | 3 +- .../entreprises/fiche_entreprise.html | 12 ++- .../fiche_entreprise_validation.html | 5 +- 7 files changed, 117 insertions(+), 14 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index d2e9eb2f6..0ab88a86e 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -82,6 +82,7 @@ def _build_string_field(label, required=True, render_kw=None): class EntreprisesFilterForm(FlaskForm): active = BooleanField("Afficher les entreprises désactivés") + association = BooleanField("Afficher les associations partenaires") class EntrepriseCreationForm(FlaskForm): @@ -89,6 +90,7 @@ class EntrepriseCreationForm(FlaskForm): "SIRET (*)", render_kw={"placeholder": "Numéro composé de 14 chiffres"}, ) + association = BooleanField("Association") nom_entreprise = _build_string_field("Nom de l'entreprise (*)") adresse = _build_string_field("Adresse de l'entreprise (*)") codepostal = _build_string_field("Code postal de l'entreprise (*)") @@ -168,8 +170,8 @@ class EntrepriseCreationForm(FlaskForm): class EntrepriseModificationForm(FlaskForm): - hidden_entreprise_siret = HiddenField() siret = StringField("SIRET (*)") + association = BooleanField("Association") nom = _build_string_field("Nom de l'entreprise (*)") adresse = _build_string_field("Adresse (*)") codepostal = _build_string_field("Code postal (*)") @@ -179,10 +181,7 @@ class EntrepriseModificationForm(FlaskForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.siret.render_kw = { - "disabled": "", - "value": self.hidden_entreprise_siret.data, - } + self.siret.render_kw = {"disabled": ""} class SiteCreationForm(FlaskForm): @@ -624,6 +623,7 @@ class TaxeApprentissageForm(FlaskForm): validators=[ DataRequired(message=CHAMP_REQUIS), NumberRange( + min=1900, max=int(datetime.now().strftime("%Y")), message=f"L'année doit être inférieure ou égale à {int(datetime.now().strftime('%Y'))}", ), @@ -661,6 +661,28 @@ class TaxeApprentissageForm(FlaskForm): return validate +class TaxeApprentissageModificationForm(FlaskForm): + hidden_annee = HiddenField() + annee = IntegerField("Année (*)") + montant = IntegerField( + "Montant (*)", + validators=[ + DataRequired(message=CHAMP_REQUIS), + NumberRange( + min=1, + message="Le montant doit être supérieur à 0", + ), + ], + default=1, + ) + notes = TextAreaField("Notes") + submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.annee.render_kw = {"disabled": ""} + + class EnvoiOffreForm(FlaskForm): responsables = FieldList( _build_string_field( diff --git a/app/entreprises/models.py b/app/entreprises/models.py index 4ff358004..72975a642 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -10,6 +10,7 @@ class Entreprise(db.Model): codepostal = db.Column(db.Text) ville = db.Column(db.Text) pays = db.Column(db.Text) + association = db.Column(db.Boolean, default=False) visible = db.Column(db.Boolean, default=False) active = db.Column(db.Boolean, default=True) notes_active = db.Column(db.Text) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index c0f47382b..be88e2ad0 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -31,6 +31,7 @@ from app.entreprises.forms import ( EnvoiOffreForm, AjoutFichierForm, TaxeApprentissageForm, + TaxeApprentissageModificationForm, ValidationConfirmationForm, ImportForm, PreferencesForm, @@ -71,11 +72,14 @@ def index(): logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() if current_user.has_permission(Permission.RelationsEntreprisesChange, None): form = EntreprisesFilterForm() - checked = False + checked = [False, False] if request.method == "POST": - checked = form.active.data - if checked: + checked[0] = form.active.data + checked[1] = form.association.data + if checked[0]: entreprises = Entreprise.query.filter_by(visible=True) + if checked[1]: + entreprises = Entreprise.query.filter_by(association=True) return render_template( "entreprises/entreprises.html", title="Entreprises", @@ -317,6 +321,7 @@ def add_entreprise(): entreprise = Entreprise( nom=form.nom_entreprise.data.strip(), siret=form.siret.data.strip(), + association=form.association.data, adresse=form.adresse.data.strip(), codepostal=form.codepostal.data.strip(), ville=form.ville.data.strip(), @@ -387,7 +392,7 @@ def edit_entreprise(id): entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( description=f"entreprise {id} inconnue" ) - form = EntrepriseModificationForm(hidden_entreprise_siret=entreprise.siret) + form = EntrepriseModificationForm(siret=entreprise.siret) if form.validate_on_submit(): nom_entreprise = f"{form.nom.data.strip()}" if entreprise.nom != form.nom.data.strip(): @@ -432,6 +437,7 @@ def edit_entreprise(id): form.pays.data.strip() if form.pays.data.strip() else "FRANCE" ) db.session.add(log) + entreprise.association = form.association.data db.session.commit() flash("L'entreprise a été modifié.") return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) @@ -442,6 +448,7 @@ def edit_entreprise(id): form.codepostal.data = entreprise.codepostal form.ville.data = entreprise.ville form.pays.data = entreprise.pays + form.association.data = entreprise.association return render_template( "entreprises/form_modification_entreprise.html", title="Modification entreprise", @@ -522,6 +529,64 @@ def add_taxe_apprentissage(id): ) +@bp.route( + "/fiche_entreprise//edit_taxe_apprentissage/", + methods=["GET", "POST"], +) +def edit_taxe_apprentissage(id_entreprise, id_taxe): + """ + Permet de modifier une taxe d'apprentissage sur un fiche entreprise + """ + entreprise = Entreprise.query.filter_by( + id=id_entreprise, visible=True + ).first_or_404(description=f"entreprise {id_entreprise} inconnue") + taxe = EntrepriseTaxeApprentissage.query.filter_by(id=id_taxe).first_or_404( + description=f"taxe d'apprentissage {id_taxe} inconnue" + ) + form = TaxeApprentissageModificationForm(annee=taxe.annee) + if form.validate_on_submit(): + taxe.montant = form.montant.data + taxe.notes = form.notes.data.strip() + db.session.commit() + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + elif request.method == "GET": + form.montant.data = taxe.montant + form.notes.data = taxe.notes + return render_template( + "entreprises/form.html", + title="Modification taxe apprentissage", + form=form, + ) + + +@bp.route( + "/fiche_entreprise//delete_taxe_apprentissage/", + methods=["GET", "POST"], +) +def delete_taxe_apprentissage(id_entreprise, id_taxe): + """ + Permet de modifier une taxe d'apprentissage sur un fiche entreprise + """ + entreprise = Entreprise.query.filter_by( + id=id_entreprise, visible=True + ).first_or_404(description=f"entreprise {id_entreprise} inconnue") + taxe = EntrepriseTaxeApprentissage.query.filter_by(id=id_taxe).first_or_404( + description=f"taxe d'apprentissage {id_taxe} inconnue" + ) + form = SuppressionConfirmationForm() + if form.validate_on_submit(): + db.session.delete(taxe) + db.session.commit() + flash("La taxe d'apprentissage a été supprimé de la liste.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template( + "entreprises/confirmation_form.html", + title="Supprimer taxe apprentissage", + form=form, + info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", + ) + + @bp.route( "/fiche_entreprise_validation//validate_entreprise", methods=["GET", "POST"] ) diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 286381d6c..702cee93b 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -115,4 +115,9 @@ border: solid 2px; border-radius: 10px; padding: 10px; +} + +#liste-taxes-apprentissages { + list-style: none; + padding-left: 0; } \ No newline at end of file diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html index a45113dea..e755d1d83 100644 --- a/app/templates/entreprises/entreprises.html +++ b/app/templates/entreprises/entreprises.html @@ -39,7 +39,8 @@ {% if form %}
    {{ form.hidden_tag() }} - + + {% endif %}
    {{ entreprise.siret }}{{ entreprise.siret }} {{ entreprise.nom }} {{ entreprise.adresse }} {{ entreprise.codepostal }}
    diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index b8a1bf890..74fa32394 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -33,7 +33,10 @@ Adresse : {{ entreprise.adresse }}
    Code postal : {{ entreprise.codepostal }}
    Ville : {{ entreprise.ville }}
    - Pays : {{ entreprise.pays }} + Pays : {{ entreprise.pays }}
    + {% if entreprise.association %} + Association + {% endif %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} @@ -41,12 +44,15 @@ Taxe d'apprentissage
    Ajouter taxe apprentissage
    -
      +
        {% if not taxes|check_taxe_now %}
      • année actuelle : non versé
      • {% endif %} {% for taxe in taxes %} -
      • {{ taxe.annee }} : {{ taxe.montant }} euros
      • +
      • + supprimer + {{ taxe.annee }} : {{ taxe.montant }} euros +
      • {% endfor %}
    diff --git a/app/templates/entreprises/fiche_entreprise_validation.html b/app/templates/entreprises/fiche_entreprise_validation.html index 2f243b24d..f980a17f7 100644 --- a/app/templates/entreprises/fiche_entreprise_validation.html +++ b/app/templates/entreprises/fiche_entreprise_validation.html @@ -12,7 +12,10 @@ Adresse : {{ entreprise.adresse }}
    Code postal : {{ entreprise.codepostal }}
    Ville : {{ entreprise.ville }}
    - Pays : {{ entreprise.pays }} + Pays : {{ entreprise.pays }}
    + {% if entreprise.association %} + Association + {% endif %} From e34ae9b3ecff5141743d4bb55cbee4741de115ef Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 6 May 2022 18:50:12 +0200 Subject: [PATCH 05/25] changement historique (log) --- app/entreprises/forms.py | 5 +- app/entreprises/models.py | 4 +- app/entreprises/routes.py | 46 +++++++++++++------ app/static/css/entreprises.css | 4 ++ app/templates/entreprises/entreprises.html | 6 +-- .../entreprises/fiche_entreprise.html | 2 +- 6 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index 0ab88a86e..65d503cca 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -81,8 +81,8 @@ def _build_string_field(label, required=True, render_kw=None): class EntreprisesFilterForm(FlaskForm): - active = BooleanField("Afficher les entreprises désactivés") - association = BooleanField("Afficher les associations partenaires") + active = BooleanField("Toutes les entreprises") + association = BooleanField("Seulement les associations partenaires") class EntrepriseCreationForm(FlaskForm): @@ -662,7 +662,6 @@ class TaxeApprentissageForm(FlaskForm): class TaxeApprentissageModificationForm(FlaskForm): - hidden_annee = HiddenField() annee = IntegerField("Année (*)") montant = IntegerField( "Montant (*)", diff --git a/app/entreprises/models.py b/app/entreprises/models.py index 72975a642..b61ecaec7 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -137,7 +137,9 @@ class EntrepriseLog(db.Model): id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) authenticated_user = db.Column(db.Text) - object = db.Column(db.Integer) + entreprise_id = db.Column(db.Integer) + object = db.Column(db.Text) + object_id = db.Column(db.Integer) text = db.Column(db.Text) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index be88e2ad0..a57c77e13 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -176,7 +176,7 @@ def fiche_entreprise(id): sites = entreprise.sites[:] logs = ( EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) - .filter_by(object=id) + .filter(EntrepriseLog.entreprise_id == id) .limit(LOGS_LEN) .all() ) @@ -216,7 +216,7 @@ def logs_entreprise(id): ) logs = ( EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) - .filter_by(object=id) + .filter(EntrepriseLog.entreprise_id == id) .paginate(page=page, per_page=20) ) return render_template( @@ -362,6 +362,7 @@ def add_entreprise(): log = EntrepriseLog( authenticated_user=current_user.user_name, text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom})", + entreprise_id=entreprise.id, ) db.session.add(log) db.session.commit() @@ -398,7 +399,7 @@ def edit_entreprise(id): if entreprise.nom != form.nom.data.strip(): log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du nom (ancien nom: {entreprise.nom})", ) entreprise.nom = form.nom.data.strip() @@ -406,7 +407,7 @@ def edit_entreprise(id): if entreprise.adresse != form.adresse.data.strip(): log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification de l'adresse (ancienne adresse: {entreprise.adresse})", ) entreprise.adresse = form.adresse.data.strip() @@ -414,7 +415,7 @@ def edit_entreprise(id): if entreprise.codepostal != form.codepostal.data.strip(): log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du code postal (ancien code postal: {entreprise.codepostal})", ) entreprise.codepostal = form.codepostal.data.strip() @@ -422,7 +423,7 @@ def edit_entreprise(id): if entreprise.ville != form.ville.data.strip(): log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification de la ville (ancienne ville: {entreprise.ville})", ) entreprise.ville = form.ville.data.strip() @@ -430,7 +431,7 @@ def edit_entreprise(id): if entreprise.pays != form.pays.data.strip() or not form.pays.data.strip(): log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du pays (ancien pays: {entreprise.pays})", ) entreprise.pays = ( @@ -604,7 +605,8 @@ def validate_entreprise(id): nom_entreprise = f"{entreprise.nom}" log = EntrepriseLog( authenticated_user=current_user.user_name, - text=f"{nom_entreprise} - Validation de la fiche entreprise ({entreprise.nom}) avec un correspondant", + entreprise_id=entreprise.id, + text=f"{nom_entreprise} - Validation de la fiche entreprise ({entreprise.nom})", ) db.session.add(log) db.session.commit() @@ -688,7 +690,9 @@ def add_offre(id): file.save(os.path.join(path, filename)) log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=entreprise.id, + object="offre", + object_id=offre.id, text="Création d'une offre", ) db.session.add(log) @@ -740,7 +744,9 @@ def edit_offre(id): db.session.delete(offre_dept) log = EntrepriseLog( authenticated_user=current_user.user_name, - object=offre.entreprise_id, + entreprise_id=offre.entreprise_id, + object="offre", + object_id=offre.id, text="Modification d'une offre", ) db.session.add(log) @@ -785,7 +791,9 @@ def delete_offre(id): shutil.rmtree(path) log = EntrepriseLog( authenticated_user=current_user.user_name, - object=offre.entreprise_id, + entreprise_id=offre.entreprise_id, + object="offre", + object_id=offre.id, text="Suppression d'une offre", ) db.session.add(log) @@ -925,13 +933,17 @@ def add_correspondant(id_entreprise, id_site): origine=correspondant_entry.origine.data.strip(), notes=correspondant_entry.notes.data.strip(), ) + db.session.add(correspondant) + db.session.commit() + db.session.refresh(correspondant) log = EntrepriseLog( authenticated_user=current_user.user_name, - object=entreprise.id, + entreprise_id=correspondant.entreprise_id, + object="correspondant", + object_id=correspondant.id, text="Création d'un correspondant", ) db.session.add(log) - db.session.add(correspondant) db.session.commit() flash("Le correspondant a été ajouté à la fiche entreprise.") return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) @@ -967,7 +979,9 @@ def edit_correspondant(id): correspondant.notes = form.notes.data.strip() log = EntrepriseLog( authenticated_user=current_user.user_name, - object=correspondant.entreprise_id, + entreprise_id=correspondant.entreprise_id, + object="correspondant", + object_id=correspondant.id, text="Modification d'un correspondant", ) db.session.add(log) @@ -1007,7 +1021,9 @@ def delete_correspondant(id): db.session.delete(correspondant) log = EntrepriseLog( authenticated_user=current_user.user_name, - object=correspondant.entreprise_id, + entreprise_id=correspondant.entreprise_id, + object="correspondant", + object_id=correspondant.id, text="Suppression d'un correspondant", ) db.session.add(log) diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 702cee93b..997bc1cba 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -120,4 +120,8 @@ #liste-taxes-apprentissages { list-style: none; padding-left: 0; +} + +#form-entreprise-filter > label { + margin-right: 20px; } \ No newline at end of file diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html index e755d1d83..5931bae12 100644 --- a/app/templates/entreprises/entreprises.html +++ b/app/templates/entreprises/entreprises.html @@ -37,10 +37,10 @@

    Liste des entreprises

    {% if form %} -
    + {{ form.hidden_tag() }} - - + {{ form.active.label }} + {{ form.association.label }} {% endif %}
    diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 74fa32394..35c9365f1 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -51,7 +51,7 @@ {% for taxe in taxes %}
  • supprimer - {{ taxe.annee }} : {{ taxe.montant }} euros + {{ taxe.annee }} : {{ taxe.montant }} euros {% if taxe.notes %}- {{ taxe.notes}} {% endif %}
  • {% endfor %} From 259b2a449d2371caeafc149dbbfa3be0f53d91d5 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 6 May 2022 18:50:56 +0200 Subject: [PATCH 06/25] taxe apprentissage, changement historique, association --- ...a_ajout_taxe_apprentissage_association_.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py diff --git a/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py b/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py new file mode 100644 index 000000000..8cab659d9 --- /dev/null +++ b/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py @@ -0,0 +1,79 @@ +"""ajout taxe apprentissage, association, changement historique + +Revision ID: 2b872cb116ca +Revises: d5b3bdd1d622 +Create Date: 2022-05-06 18:16:58.727052 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "2b872cb116ca" +down_revision = "d5b3bdd1d622" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "are_taxe_apprentissage", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("entreprise_id", sa.Integer(), nullable=True), + sa.Column("annee", sa.Integer(), nullable=True), + sa.Column("montant", sa.Integer(), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.ForeignKeyConstraint( + ["entreprise_id"], ["are_entreprises.id"], ondelete="cascade" + ), + sa.PrimaryKeyConstraint("id"), + ) + op.add_column( + "are_entreprises", sa.Column("association", sa.Boolean(), nullable=True) + ) + op.add_column("are_logs", sa.Column("entreprise_id", sa.Integer(), nullable=True)) + op.add_column("are_logs", sa.Column("object_id", sa.Integer(), nullable=True)) + op.alter_column( + "are_logs", + "object", + existing_type=sa.INTEGER(), + type_=sa.Text(), + existing_nullable=True, + ) + op.create_index( + op.f("ix_scolar_news_authenticated_user"), + "scolar_news", + ["authenticated_user"], + unique=False, + ) + op.create_index(op.f("ix_scolar_news_date"), "scolar_news", ["date"], unique=False) + op.create_index( + op.f("ix_scolar_news_object"), "scolar_news", ["object"], unique=False + ) + op.create_index(op.f("ix_scolar_news_type"), "scolar_news", ["type"], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_scolar_news_type"), table_name="scolar_news") + op.drop_index(op.f("ix_scolar_news_object"), table_name="scolar_news") + op.drop_index(op.f("ix_scolar_news_date"), table_name="scolar_news") + op.drop_index(op.f("ix_scolar_news_authenticated_user"), table_name="scolar_news") + op.execute( + "alter table are_logs alter column object set data type int using object::integer" + ) + op.alter_column( + "are_logs", + "object", + existing_type=sa.Text(), + type_=sa.INTEGER(), + existing_nullable=True, + ) + op.drop_column("are_logs", "object_id") + op.drop_column("are_logs", "entreprise_id") + op.drop_column("are_entreprises", "association") + op.drop_table("are_taxe_apprentissage") + # ### end Alembic commands ### From 8e9599693015da64ae416e8a06c78a2df1957f18 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Mon, 30 May 2022 18:12:26 +0200 Subject: [PATCH 07/25] lien vers fiche etudiant (stage ou alternance), refonte import export entreprises --- app/entreprises/app_relations_entreprises.py | 7 +++ app/entreprises/routes.py | 51 +++++++++++++++---- app/static/css/entreprises.css | 9 +++- .../entreprises/fiche_entreprise.html | 11 +++- .../entreprises/import_entreprises.html | 11 ++++ 5 files changed, 77 insertions(+), 12 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 95f1264cf..b42fd58b8 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -68,6 +68,13 @@ def get_dept_id_by_acronym(acronym): return None +def get_dept_acronym_by_id(id): + dept = Departement.query.filter_by(id=id).first() + if dept is not None: + return dept.acronym + return None + + def check_offre_depts(depts, offre_depts): """ Retourne vrai si l'utilisateur a le droit de visibilité sur l'offre diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index a57c77e13..3b94fa3c1 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1323,10 +1323,14 @@ def json_etudiants(): content = { "id": f"{etudiant.id}", "value": value, - "info": f"{etudiant.inscription_courante().formsemestre.titre}", + "info": f"Département {are.get_dept_acronym_by_id(etudiant.dept_id)} - {etudiant.inscription_courante().formsemestre.titre}", } else: - content = {"id": f"{etudiant.id}", "value": value} + content = { + "id": f"{etudiant.id}", + "value": value, + "info": f"Département {are.get_dept_acronym_by_id(etudiant.dept_id)}", + } list.append(content) return jsonify(results=list) @@ -1352,7 +1356,7 @@ def json_responsables(): for responsable in responsables: content = {} value = f"{responsable.get_nomplogin()}" - content = {"id": f"{responsable.id}", "value": value, "info": ""} + content = {"id": f"{responsable.id}", "value": value} list.append(content) return jsonify(results=list) @@ -1386,7 +1390,7 @@ def get_import_entreprises_file_sample(): """ Permet de récupérer un fichier exemple vide pour pouvoir importer des entreprises """ - keys = [ + entreprises_titles = [ "siret", "nom_entreprise", "adresse", @@ -1394,9 +1398,39 @@ def get_import_entreprises_file_sample(): "code_postal", "pays", ] - titles = keys[:] + sites_titles = [ + "siret_entreprise", + "nom_site", + "adresse", + "ville", + "code_postal", + "pays", + ] title = "ImportEntreprises" - xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Entreprises") + wb = sco_excel.ScoExcelBook() + ws1 = wb.create_sheet("Entreprises") + ws1.append_row( + [ + ws1.make_cell(it, style, comment) + for (it, style, comment) in zip( + entreprises_titles, + [sco_excel.excel_make_style(bold=True)] * len(entreprises_titles), + [None] * len(entreprises_titles), + ) + ] + ) + ws2 = wb.create_sheet("Sites") + ws2.append_row( + [ + ws2.make_cell(it, style, comment) + for (it, style, comment) in zip( + sites_titles, + [sco_excel.excel_make_style(bold=True)] * len(sites_titles), + [None] * len(sites_titles), + ) + ] + ) + xlsx = wb.generate() filename = title return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) @@ -1487,7 +1521,7 @@ def export_correspondants(): correspondants = ( db.session.query(EntrepriseCorrespondant) .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) - .filter_by(visible=True) + .filter_by(visible=True, active=True) .all() ) if correspondants: @@ -1523,7 +1557,7 @@ def get_import_correspondants_file_sample(): """ Permet de récupérer un fichier exemple vide pour pouvoir importer des correspondants """ - keys = [ + titles = [ "civilite", "nom", "prenom", @@ -1535,7 +1569,6 @@ def get_import_correspondants_file_sample(): "notes", "entreprise_siret", ] - titles = keys[:] title = "ImportCorrespondants" xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Correspondants") filename = title diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 997bc1cba..203b3aeba 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -59,14 +59,19 @@ margin-bottom: -5px; } -.entreprise, .correspondant, .offre, .site{ - border: solid 2px; +.entreprise, .correspondant, .offre, .site, .info-active { + border: solid 2px black; border-radius: 10px; padding: 10px; margin-bottom: 10px; margin-top: 10px; } +.info-active { + border-color: red; + background-color: rgb(250, 220, 220); +} + .entreprise { display: flex; justify-content: space-between; diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 35c9365f1..6d0896052 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -26,6 +26,15 @@

    Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})

    + {% if not entreprise.active %} +
    + La fiche entreprise est désactivée
    + {% if entreprise.notes_active != "" %} + Notes : {{ entreprise.notes_active }} + {% endif %} +
    + {% endif %} +
    SIRET : {{ entreprise.siret }}
    @@ -141,7 +150,7 @@
    - + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} diff --git a/app/templates/entreprises/import_entreprises.html b/app/templates/entreprises/import_entreprises.html index 0b6336603..f0c659512 100644 --- a/app/templates/entreprises/import_entreprises.html +++ b/app/templates/entreprises/import_entreprises.html @@ -23,6 +23,7 @@ {% if not entreprises_import %} +
    Feuille Entreprises
    {{ data[0].date_fin.strftime('%d/%m/%Y') }} {{ (data[0].date_fin-data[0].date_debut).days//7 }} semaines {{ data[0].type_offre }}{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }} {% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %} {{ data[0].notes }}
    @@ -32,6 +33,16 @@
    AttributTypeDescription
    sirettextsiret de l'entreprise
    code_postaltextcode postal de l'entreprise
    paystextpays de l'entreprise
    +
    Feuille Sites
    + + + + + + + + +
    AttributTypeDescription
    siret_entreprisetextsiret de l'entreprise
    nom_sitetextnom du site
    adressetextadresse du site
    villetextville du site
    code_postaltextcode postal du site
    paystextpays du site
    {% endif %} {% if entreprises_import %} From e96d71454537283cfd1cc35d1a0f56ae41be685e Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Tue, 31 May 2022 19:15:24 +0200 Subject: [PATCH 08/25] correction suite aux changements, suite import export --- app/entreprises/app_relations_entreprises.py | 77 +++++++++++++++- app/entreprises/models.py | 15 ++++ app/entreprises/routes.py | 90 ++++++------------- .../fiche_entreprise_validation.html | 60 ++++++++----- 4 files changed, 159 insertions(+), 83 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index b42fd58b8..fce34b3c5 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -38,10 +38,11 @@ from app.entreprises.models import ( EntrepriseOffre, EntrepriseOffreDepartement, EntreprisePreferences, + EntrepriseSite, ) - -from app import email +from app import email, db from app.scodoc import sco_preferences +from app.scodoc import sco_excel from app.models import Departement from app.scodoc.sco_permissions import Permission @@ -212,3 +213,75 @@ def verif_entreprise_data(entreprise_data): if entreprise is not None: return False return True + + +def get_excel_book_are(export=False): + entreprises_keys = [ + "siret", + "nom_entreprise", + "adresse", + "ville", + "code_postal", + "pays", + "association", + "visible", + "active", + "notes_active", + ] + sites_keys = [ + "siret_entreprise", + "nom_site", + "adresse", + "ville", + "code_postal", + "pays", + ] + entreprises_titles = entreprises_keys[:] + sites_titles = sites_keys[:] + wb = sco_excel.ScoExcelBook() + ws1 = wb.create_sheet("Entreprises") + ws1.append_row( + [ + ws1.make_cell(it, style) + for (it, style) in zip( + entreprises_titles, + [sco_excel.excel_make_style(bold=True)] * len(entreprises_titles), + ) + ] + ) + ws2 = wb.create_sheet("Sites") + ws2.append_row( + [ + ws2.make_cell(it, style) + for (it, style) in zip( + sites_titles, + [sco_excel.excel_make_style(bold=True)] * len(sites_titles), + ) + ] + ) + if export: + entreprises = Entreprise.query.filter_by(visible=True).all() + sites = ( + db.session.query(EntrepriseSite) + .join(Entreprise, EntrepriseSite.entreprise_id == Entreprise.id) + .filter_by(visible=True) + .all() + ) + entreprises_lines = [ + [entreprise.to_dict().get(k, "") for k in entreprises_keys] + for entreprise in entreprises + ] + sites_lines = [ + [site.to_dict().get(k, "") for k in sites_keys] for site in sites + ] + for line in entreprises_lines: + cells = [] + for it in line: + cells.append(ws1.make_cell(it)) + ws1.append_row(cells) + for line in sites_lines: + cells = [] + for it in line: + cells.append(ws2.make_cell(it)) + ws2.append_row(cells) + return wb diff --git a/app/entreprises/models.py b/app/entreprises/models.py index b61ecaec7..88bf7d015 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -37,6 +37,10 @@ class Entreprise(db.Model): "code_postal": self.codepostal, "ville": self.ville, "pays": self.pays, + "association": self.association, + "visible": self.visible, + "active": self.active, + "notes_active": self.notes_active, } @@ -59,6 +63,17 @@ class EntrepriseSite(db.Model): cascade="all, delete-orphan", ) + def to_dict(self): + entreprise = Entreprise.query.get_or_404(self.entreprise_id) + return { + "siret_entreprise": entreprise.siret, + "nom_site": self.nom, + "adresse": self.adresse, + "code_postal": self.codepostal, + "ville": self.ville, + "pays": self.pays, + } + class EntrepriseCorrespondant(db.Model): __tablename__ = "are_correspondants" diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 3b94fa3c1..281df66d2 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -138,6 +138,7 @@ def correspondants(): .join(EntrepriseSite, EntrepriseCorrespondant.site_id == EntrepriseSite.id) .join(Entreprise, EntrepriseSite.entreprise_id == Entreprise.id) .filter_by(visible=True, active=True) + .all() ) logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() return render_template( @@ -216,7 +217,7 @@ def logs_entreprise(id): ) logs = ( EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) - .filter(EntrepriseLog.entreprise_id == id) + .filter(EntrepriseLog.entreprise_id == entreprise.id) .paginate(page=page, per_page=20) ) return render_template( @@ -236,12 +237,12 @@ def fiche_entreprise_validation(id): entreprise = Entreprise.query.filter_by(id=id, visible=False).first_or_404( description=f"fiche entreprise (validation) {id} inconnue" ) - correspondants = entreprise.correspondants + sites = entreprise.sites[:] return render_template( "entreprises/fiche_entreprise_validation.html", title="Validation fiche entreprise", entreprise=entreprise, - correspondants=correspondants, + sites=sites, ) @@ -875,8 +876,11 @@ def add_site(id): methods=["GET", "POST"], ) def edit_site(id_entreprise, id_site): + entreprise = Entreprise.query.filter_by( + id=id_entreprise, visible=True + ).first_or_404(description=f"entreprise {id_entreprise} inconnue") site = EntrepriseSite.query.filter_by( - id=id_site, entreprise_id=id_entreprise + id=id_site, entreprise_id=entreprise.id ).first_or_404(description=f"site {id_site} inconnu") form = SiteModificationForm( hidden_entreprise_id=id_entreprise, hidden_site_id=id_site @@ -960,8 +964,11 @@ def edit_correspondant(id): """ Permet de modifier un correspondant """ - correspondant = EntrepriseCorrespondant.query.filter_by(id=id).first_or_404( - description=f"correspondant {id} inconnu" + correspondant = ( + db.session.query(EntrepriseCorrespondant) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) + .filter(EntrepriseCorrespondant.id == id, Entreprise.visible == True) + .first_or_404(description=f"correspondant {id} inconnu") ) form = CorrespondantModificationForm( hidden_entreprise_id=correspondant.entreprise_id, @@ -1013,8 +1020,11 @@ def delete_correspondant(id): """ Permet de supprimer un correspondant """ - correspondant = EntrepriseCorrespondant.query.filter_by(id=id).first_or_404( - description=f"correspondant {id} inconnu" + correspondant = ( + db.session.query(EntrepriseCorrespondant) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) + .filter(EntrepriseCorrespondant.id == id, Entreprise.visible == True) + .first_or_404(description=f"correspondant {id} inconnu") ) form = SuppressionConfirmationForm() if form.validate_on_submit(): @@ -1126,7 +1136,10 @@ def contacts(id): """ Permet d'afficher une page avec la liste des contacts d'une entreprise """ - contacts = EntrepriseContact.query.filter_by(entreprise=id).all() + entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( + description=f"entreprise {id} inconnue" + ) + contacts = EntrepriseContact.query.filter_by(entreprise=entreprise.id).all() return render_template( "entreprises/contacts.html", title="Liste des contacts", @@ -1367,17 +1380,11 @@ def export_entreprises(): """ Permet d'exporter la liste des entreprises sous format excel (.xlsx) """ - entreprises = Entreprise.query.filter_by(visible=True).all() - if entreprises: - keys = ["siret", "nom_entreprise", "adresse", "ville", "code_postal", "pays"] - titles = keys[:] - L = [ - [entreprise.to_dict().get(k, "") for k in keys] - for entreprise in entreprises - ] - title = "Entreprises" - xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) - filename = title + entreprise = Entreprise.query.filter_by(visible=True).first() + if entreprise: + wb = are.get_excel_book_are(export=True) + xlsx = wb.generate() + filename = "ExportEntreprisesSites" return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) else: flash("Aucune entreprise dans la base.") @@ -1390,48 +1397,9 @@ def get_import_entreprises_file_sample(): """ Permet de récupérer un fichier exemple vide pour pouvoir importer des entreprises """ - entreprises_titles = [ - "siret", - "nom_entreprise", - "adresse", - "ville", - "code_postal", - "pays", - ] - sites_titles = [ - "siret_entreprise", - "nom_site", - "adresse", - "ville", - "code_postal", - "pays", - ] - title = "ImportEntreprises" - wb = sco_excel.ScoExcelBook() - ws1 = wb.create_sheet("Entreprises") - ws1.append_row( - [ - ws1.make_cell(it, style, comment) - for (it, style, comment) in zip( - entreprises_titles, - [sco_excel.excel_make_style(bold=True)] * len(entreprises_titles), - [None] * len(entreprises_titles), - ) - ] - ) - ws2 = wb.create_sheet("Sites") - ws2.append_row( - [ - ws2.make_cell(it, style, comment) - for (it, style, comment) in zip( - sites_titles, - [sco_excel.excel_make_style(bold=True)] * len(sites_titles), - [None] * len(sites_titles), - ) - ] - ) + wb = are.get_excel_book_are() xlsx = wb.generate() - filename = title + filename = "ImportEntreprisesSites" return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) diff --git a/app/templates/entreprises/fiche_entreprise_validation.html b/app/templates/entreprises/fiche_entreprise_validation.html index f980a17f7..d4722756b 100644 --- a/app/templates/entreprises/fiche_entreprise_validation.html +++ b/app/templates/entreprises/fiche_entreprise_validation.html @@ -19,31 +19,51 @@
    - {% if correspondants %} -
    - {% for correspondant in correspondants %} +
    + {% if sites %}
    -

    Correspondant

    -
    - Nom : {{ correspondant.nom }}
    - Prénom : {{ correspondant.prenom }}
    - {% if correspondant.telephone %} - Téléphone : {{ correspondant.telephone }}
    - {% endif %} - {% if correspondant.mail %} - Mail : {{ correspondant.mail }}
    - {% endif %} - {% if correspondant.poste %} - Poste : {{ correspondant.poste }}
    - {% endif %} - {% if correspondant.service %} - Service : {{ correspondant.service }}
    +

    Sites

    + {% for site in sites %} +
    + Nom : {{ site.nom }}
    + Adresse : {{ site.adresse }}
    + Code postal : {{ site.codepostal }}
    + Ville : {{ site.ville }}
    + Pays : {{ site.pays }} + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %} + {% for correspondant in site.correspondants %} +
    +
    + Civilité : {{ correspondant.civilite|get_civilité }}
    + Nom : {{ correspondant.nom }}
    + Prénom : {{ correspondant.prenom }}
    + {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
    + {% endif %} + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
    + {% endif %} + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
    + {% endif %} + {% if correspondant.service %} + Service : {{ correspondant.service }}
    + {% endif %} + {% if correspondant.origine %} + Origine : {{ correspondant.origine }}
    + {% endif %} + {% if correspondant.notes %} + Notes : {{ correspondant.notes }}
    + {% endif %} +
    +
    + {% endfor %} {% endif %}
    + {% endfor %}
    - {% endfor %} + {% endif %}
    - {% endif %}
    Valider From 9e751722e6fcfb409f2223d7b4e620dca6027c39 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Wed, 1 Jun 2022 21:17:15 +0200 Subject: [PATCH 09/25] =?UTF-8?q?import=20export=20donn=C3=A9es=20applicat?= =?UTF-8?q?ion=20relations=20entreprises?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 81 +++++- app/entreprises/models.py | 6 +- app/entreprises/routes.py | 243 +----------------- app/templates/entreprises/correspondants.html | 9 - app/templates/entreprises/entreprises.html | 4 +- .../entreprises/import_correspondants.html | 72 ------ .../entreprises/import_entreprises.html | 33 ++- 7 files changed, 101 insertions(+), 347 deletions(-) delete mode 100644 app/templates/entreprises/import_correspondants.html diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index fce34b3c5..ff6b3f808 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -223,21 +223,45 @@ def get_excel_book_are(export=False): "ville", "code_postal", "pays", - "association", - "visible", - "active", - "notes_active", ] sites_keys = [ - "siret_entreprise", + [ + "siret_entreprise", + "id", + "nom_site", + "adresse", + "ville", + "code_postal", + "pays", + ], + [ + "civilite", + "nom", + "prenom", + "telephone", + "mail", + "poste", + "service", + "origine", + "notes", + ], + ] + correspondants_keys = [ + "id", + "civilite", + "nom", + "prenom", + "telephone", + "mail", + "poste", + "service", + "origine", + "notes", "nom_site", - "adresse", - "ville", - "code_postal", - "pays", ] entreprises_titles = entreprises_keys[:] sites_titles = sites_keys[:] + correspondants_titles = correspondants_keys[:] wb = sco_excel.ScoExcelBook() ws1 = wb.create_sheet("Entreprises") ws1.append_row( @@ -254,8 +278,26 @@ def get_excel_book_are(export=False): [ ws2.make_cell(it, style) for (it, style) in zip( - sites_titles, - [sco_excel.excel_make_style(bold=True)] * len(sites_titles), + sites_titles[0], + [sco_excel.excel_make_style(bold=True)] * len(sites_titles[0]), + ) + ] + + [ + ws2.make_cell(it, style) + for (it, style) in zip( + sites_titles[1], + [sco_excel.excel_make_style(bold=True, color=sco_excel.COLORS.RED)] + * len(sites_titles[1]), + ) + ] + ) + ws3 = wb.create_sheet("Correspondants") + ws3.append_row( + [ + ws3.make_cell(it, style) + for (it, style) in zip( + correspondants_titles, + [sco_excel.excel_make_style(bold=True)] * len(correspondants_titles), ) ] ) @@ -267,12 +309,22 @@ def get_excel_book_are(export=False): .filter_by(visible=True) .all() ) + correspondants = ( + db.session.query(EntrepriseCorrespondant) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) + .filter_by(visible=True) + .all() + ) entreprises_lines = [ [entreprise.to_dict().get(k, "") for k in entreprises_keys] for entreprise in entreprises ] sites_lines = [ - [site.to_dict().get(k, "") for k in sites_keys] for site in sites + [site.to_dict().get(k, "") for k in sites_keys[0]] for site in sites + ] + correspondants_lines = [ + [correspondant.to_dict().get(k, "") for k in correspondants_keys] + for correspondant in correspondants ] for line in entreprises_lines: cells = [] @@ -284,4 +336,9 @@ def get_excel_book_are(export=False): for it in line: cells.append(ws2.make_cell(it)) ws2.append_row(cells) + for line in correspondants_lines: + cells = [] + for it in line: + cells.append(ws3.make_cell(it)) + ws3.append_row(cells) return wb diff --git a/app/entreprises/models.py b/app/entreprises/models.py index 88bf7d015..e335edafb 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -67,6 +67,7 @@ class EntrepriseSite(db.Model): entreprise = Entreprise.query.get_or_404(self.entreprise_id) return { "siret_entreprise": entreprise.siret, + "id": self.id, "nom_site": self.nom, "adresse": self.adresse, "code_postal": self.codepostal, @@ -93,8 +94,9 @@ class EntrepriseCorrespondant(db.Model): notes = db.Column(db.Text) def to_dict(self): - entreprise = Entreprise.query.filter_by(id=self.entreprise_id).first() + site = EntrepriseSite.query.get_or_404(self.site_id) return { + "id": self.id, "civilite": self.civilite, "nom": self.nom, "prenom": self.prenom, @@ -104,7 +106,7 @@ class EntrepriseCorrespondant(db.Model): "service": self.service, "origine": self.origine, "notes": self.notes, - "entreprise_siret": entreprise.siret, + "nom_site": site.nom, } diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 281df66d2..0f1cefd97 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1374,9 +1374,9 @@ def json_responsables(): return jsonify(results=list) -@bp.route("/export_entreprises") +@bp.route("/export_donnees") @permission_required(Permission.RelationsEntreprisesExport) -def export_entreprises(): +def export_donnees(): """ Permet d'exporter la liste des entreprises sous format excel (.xlsx) """ @@ -1384,28 +1384,27 @@ def export_entreprises(): if entreprise: wb = are.get_excel_book_are(export=True) xlsx = wb.generate() - filename = "ExportEntreprisesSites" + filename = "ExportApplicationRelationsEntreprises" return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) else: - flash("Aucune entreprise dans la base.") return redirect(url_for("entreprises.index")) -@bp.route("/import_entreprises/get_import_entreprises_file_sample") +@bp.route("/import_donnees/get_file_sample") @permission_required(Permission.RelationsEntreprisesExport) -def get_import_entreprises_file_sample(): +def get_import_donnees_file_sample(): """ Permet de récupérer un fichier exemple vide pour pouvoir importer des entreprises """ wb = are.get_excel_book_are() xlsx = wb.generate() - filename = "ImportEntreprisesSites" + filename = "ImportApplicationRelationsEntreprises" return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) -@bp.route("/import_entreprises", methods=["GET", "POST"]) +@bp.route("/import_donnees", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesExport) -def import_entreprises(): +def import_donnees(): """ Permet d'importer des entreprises a l'aide d'un fichier excel (.xlsx) """ @@ -1418,61 +1417,8 @@ def import_entreprises(): file.save(file_path) data = sco_excel.excel_file_to_list(file_path) os.remove(file_path) - entreprises_import = [] - siret_list = [] - ligne = 0 - titles = ["siret", "nom_entreprise", "adresse", "ville", "code_postal", "pays"] - if data[1][0] != titles: - flash("Veuillez utilisez la feuille excel à remplir") - return render_template( - "entreprises/import_entreprises.html", - title="Importation entreprises", - form=form, - ) - for entreprise_data in data[1][1:]: - ligne += 1 - if ( - are.verif_entreprise_data(entreprise_data) - and entreprise_data[0].replace(" ", "") not in siret_list - ): - siret_list.append(entreprise_data[0].replace(" ", "")) - entreprise = Entreprise( - siret=entreprise_data[0].replace(" ", ""), - nom=entreprise_data[1].strip(), - adresse=entreprise_data[2].strip(), - ville=entreprise_data[3].strip(), - codepostal=entreprise_data[4].strip(), - pays=entreprise_data[5].strip(), - visible=True, - ) - entreprises_import.append(entreprise) - else: - flash(f"Erreur lors de l'importation (ligne {ligne})") - return render_template( - "entreprises/import_entreprises.html", - title="Importation entreprises", - form=form, - ) - - if len(entreprises_import) > 0: - for entreprise in entreprises_import: - db.session.add(entreprise) - log = EntrepriseLog( - authenticated_user=current_user.user_name, - text=f"Importation de {len(entreprises_import)} entreprise(s)", - ) - db.session.add(log) - db.session.commit() - flash(f"Importation réussie de {len(entreprises_import)} entreprise(s)") - return render_template( - "entreprises/import_entreprises.html", - title="Importation entreprises", - form=form, - entreprises_import=entreprises_import, - ) - else: - flash('Feuille "Entreprises" vide') - + print(data) + return redirect(url_for("entreprises.import_donnees")) return render_template( "entreprises/import_entreprises.html", title="Importation entreprises", @@ -1480,175 +1426,6 @@ def import_entreprises(): ) -@bp.route("/export_correspondants") -@permission_required(Permission.RelationsEntreprisesExport) -def export_correspondants(): - """ - Permet d'exporter la liste des correspondants sous format excel (.xlsx) - """ - correspondants = ( - db.session.query(EntrepriseCorrespondant) - .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) - .filter_by(visible=True, active=True) - .all() - ) - if correspondants: - keys = [ - "civilite", - "nom", - "prenom", - "telephone", - "mail", - "poste", - "service", - "origine", - "notes", - "entreprise_siret", - ] - titles = keys[:] - L = [ - [correspondant.to_dict().get(k, "") for k in keys] - for correspondant in correspondants - ] - title = "Correspondants" - xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) - filename = title - return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) - else: - flash("Aucun correspondant dans la base.") - return redirect(url_for("entreprises.correspondants")) - - -@bp.route("/import_correspondants/get_import_correspondants_file_sample") -@permission_required(Permission.RelationsEntreprisesExport) -def get_import_correspondants_file_sample(): - """ - Permet de récupérer un fichier exemple vide pour pouvoir importer des correspondants - """ - titles = [ - "civilite", - "nom", - "prenom", - "telephone", - "mail", - "poste", - "service", - "origine", - "notes", - "entreprise_siret", - ] - title = "ImportCorrespondants" - xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Correspondants") - filename = title - return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) - - -@bp.route("/import_correspondants", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesExport) -def import_correspondants(): - """ - Permet d'importer des correspondants a l'aide d'un fichier excel (.xlsx) - """ - form = ImportForm() - if form.validate_on_submit(): - file = form.fichier.data - file_path = os.path.join( - Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename) - ) - file.save(file_path) - data = sco_excel.excel_file_to_list(file_path) - os.remove(file_path) - correspondants_import = [] - correspondant_list = [] - ligne = 0 - titles = [ - "civilite", - "nom", - "prenom", - "telephone", - "mail", - "poste", - "service", - "origine", - "notes", - "entreprise_siret", - ] - if data[1][0] != titles: - flash("Veuillez utilisez la feuille excel à remplir") - return render_template( - "entreprises/import_correspondants.html", - title="Importation correspondants", - form=form, - ) - for correspondant_data in data[1][1:]: - ligne += 1 - if ( - are.verif_correspondant_data(correspondant_data) - and ( - correspondant_data[1].strip(), - correspondant_data[2].strip(), - correspondant_data[9].strip(), - ) - not in correspondant_list - ): - correspondant_list.append( - ( - correspondant_data[1].strip(), - correspondant_data[2].strip(), - correspondant_data[9].strip(), - ) - ) - entreprise = Entreprise.query.filter_by( - siret=correspondant_data[9].strip() - ).first() - correspondant = EntrepriseCorrespondant( - civilite=correspondant_data[0].strip(), - nom=correspondant_data[1].strip(), - prenom=correspondant_data[2].strip(), - telephone=correspondant_data[3].strip(), - mail=correspondant_data[4].strip(), - poste=correspondant_data[5].strip(), - service=correspondant_data[6].strip(), - origine=correspondant_data[7].strip(), - notes=correspondant_data[8].strip(), - entreprise_id=entreprise.id, - ) - correspondants_import.append(correspondant) - else: - flash(f"Erreur lors de l'importation (ligne {ligne})") - return render_template( - "entreprises/import_correspondants.html", - title="Importation correspondants", - form=form, - ) - - if len(correspondants_import) > 0: - for correspondant in correspondants_import: - db.session.add(correspondant) - log = EntrepriseLog( - authenticated_user=current_user.user_name, - text=f"Importation de {len(correspondants_import)} correspondant(s)", - ) - db.session.add(log) - db.session.commit() - flash( - f"Importation réussie de {len(correspondants_import)} correspondant(s)" - ) - return render_template( - "entreprises/import_correspondants.html", - title="Importation correspondants", - form=form, - correspondants_import=correspondants_import, - ) - else: - flash('Feuille "Correspondants" vide') - return render_template( - "entreprises/import_correspondants.html", - title="Importation correspondants", - form=form, - ) - - @bp.route( "/get_offre_file////" ) diff --git a/app/templates/entreprises/correspondants.html b/app/templates/entreprises/correspondants.html index 14804dd0a..d389884a1 100644 --- a/app/templates/entreprises/correspondants.html +++ b/app/templates/entreprises/correspondants.html @@ -22,15 +22,6 @@
    {% endif %} -
    - {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %} - Importer des correspondants - {% endif %} - {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and correspondants %} - Exporter la liste des correspondants - {% endif %} -
    -

    Liste des correspondants

    diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html index 5931bae12..e58898bd3 100644 --- a/app/templates/entreprises/entreprises.html +++ b/app/templates/entreprises/entreprises.html @@ -27,10 +27,10 @@ Ajouter une entreprise {% endif %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %} - Importer des entreprises + Importer des données {% endif %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and entreprises %} - Exporter la liste des entreprises + Exporter des données {% endif %} diff --git a/app/templates/entreprises/import_correspondants.html b/app/templates/entreprises/import_correspondants.html deleted file mode 100644 index 796c2135b..000000000 --- a/app/templates/entreprises/import_correspondants.html +++ /dev/null @@ -1,72 +0,0 @@ -{# -*- mode: jinja-html -*- #} -{% extends 'base.html' %} -{% import 'bootstrap/wtf.html' as wtf %} - -{% block styles %} -{{super()}} -{% endblock %} - -{% block app_content %} -

    Importation correspondants

    -
    - -
    -
    -
    -

    - (*) champs requis -

    - {{ wtf.quick_form(form, novalidate=True) }} -
    -
    - - {% if not correspondants_import %} -
    - - - - - - - - - - - -
    AttributTypeDescription
    civilitetextcivilite du correspondant (H ou F)
    nomtextnom du correspondant
    prenomtextprenom du correspondant
    telephonetexttelephone du correspondant
    mailtextmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    originetextorigine du correspondant
    notestextnotes sur le correspondant
    entreprise_sirettextSIRET de l'entreprise
    - {% endif %} - - {% if correspondants_import %} -
    Importation de {{ correspondants_import|length }} correspondant(s)
    - {% for correspondant in correspondants_import %} -
    -
    - Civilité : {{ correspondant.civilite|get_civilité }}
    - Nom : {{ correspondant.nom }}
    - Prénom : {{ correspondant.prenom }}
    - {% if correspondant.telephone %} - Téléphone : {{ correspondant.telephone }}
    - {% endif %} - {% if correspondant.mail %} - Mail : {{ correspondant.mail }}
    - {% endif %} - {% if correspondant.poste %} - Poste : {{ correspondant.poste }}
    - {% endif %} - {% if correspondant.service %} - Service : {{ correspondant.service }}
    - {% endif %} - {% if correspondant.origine %} - Origine : {{ correspondant.origine }}
    - {% endif %} - {% if correspondant.notes %} - Notes : {{ correspondant.notes }}
    - {% endif %} - lien vers l'entreprise -
    -
    - {% endfor %} - {% endif %} -{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/import_entreprises.html b/app/templates/entreprises/import_entreprises.html index f0c659512..833de24cd 100644 --- a/app/templates/entreprises/import_entreprises.html +++ b/app/templates/entreprises/import_entreprises.html @@ -10,7 +10,7 @@

    Importation entreprises



    @@ -43,21 +43,20 @@ code_postaltextcode postal du site paystextpays du site +
    Feuille Correspondants
    + + + + + + + + + + + + +
    AttributTypeDescription
    civilitetextcivilite du correspondant (H ou F)
    nomtextnom du correspondant
    prenomtextprenom du correspondant
    telephonetexttelephone du correspondant
    mailtextmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    originetextorigine du correspondant
    notestextnotes sur le correspondant
    nom_sitetextnom du site lié au correspondant
    {% endif %} - - {% if entreprises_import %} -
    Importation de {{ entreprises_import|length }} entreprise(s)
    - {% for entreprise in entreprises_import %} -
    -
    - SIRET : {{ entreprise.siret }}
    - Nom : {{ entreprise.nom }}
    - Adresse : {{ entreprise.adresse }}
    - Code postal : {{ entreprise.codepostal }}
    - Ville : {{ entreprise.ville }}
    - Pays : {{ entreprise.pays }} -
    -
    - {% endfor %} - {% endif %} + {% endblock %} \ No newline at end of file From 74cd1400d82157ff429447cc5d01a7501285efc2 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Thu, 2 Jun 2022 21:49:37 +0200 Subject: [PATCH 10/25] =?UTF-8?q?suite=20import=20de=20donn=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 268 +++++++++--------- app/entreprises/forms.py | 4 +- app/entreprises/routes.py | 51 +++- app/scodoc/sco_excel.py | 64 +++++ ...t_entreprises.html => import_donnees.html} | 2 +- 5 files changed, 246 insertions(+), 143 deletions(-) rename app/templates/entreprises/{import_entreprises.html => import_donnees.html} (98%) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index ff6b3f808..0626aff89 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -47,6 +47,51 @@ from app.models import Departement from app.scodoc.sco_permissions import Permission +ENTREPRISES_KEYS = [ + "siret", + "nom_entreprise", + "adresse", + "ville", + "code_postal", + "pays", +] +SITES_KEYS = [ + [ + "siret_entreprise", + "id", + "nom_site", + "adresse", + "ville", + "code_postal", + "pays", + ], + [ + "civilite", + "nom", + "prenom", + "telephone", + "mail", + "poste", + "service", + "origine", + "notes", + ], +] +CORRESPONDANTS_KEYS = [ + "id", + "civilite", + "nom", + "prenom", + "telephone", + "mail", + "poste", + "service", + "origine", + "notes", + "nom_site", +] + + def get_depts(): """ Retourne une liste contenant les l'id des départements des roles de l'utilisateur courant @@ -59,7 +104,7 @@ def get_depts(): return depts -def get_dept_id_by_acronym(acronym): +def get_dept_id_by_acronym(acronym: str): """ Retourne l'id d'un departement a l'aide de son acronym """ @@ -69,14 +114,14 @@ def get_dept_id_by_acronym(acronym): return None -def get_dept_acronym_by_id(id): +def get_dept_acronym_by_id(id: int): dept = Departement.query.filter_by(id=id).first() if dept is not None: return dept.acronym return None -def check_offre_depts(depts, offre_depts): +def check_offre_depts(depts: list, offre_depts: list): """ Retourne vrai si l'utilisateur a le droit de visibilité sur l'offre """ @@ -115,7 +160,7 @@ def get_offre_files_and_depts(offre: EntrepriseOffre, depts: list): return None -def send_email_notifications_entreprise(subject, entreprise: Entreprise): +def send_email_notifications_entreprise(subject: str, entreprise: Entreprise): txt = [ "Une entreprise est en attente de validation", "Entreprise:", @@ -136,6 +181,92 @@ def send_email_notifications_entreprise(subject, entreprise: Entreprise): return txt +def get_excel_book_are(export: bool = False): + entreprises_titles = ENTREPRISES_KEYS[:] + sites_titles = SITES_KEYS[:] + correspondants_titles = CORRESPONDANTS_KEYS[:] + wb = sco_excel.ScoExcelBook() + ws1 = wb.create_sheet("Entreprises") + ws1.append_row( + [ + ws1.make_cell(it, style) + for (it, style) in zip( + entreprises_titles, + [sco_excel.excel_make_style(bold=True)] * len(entreprises_titles), + ) + ] + ) + ws2 = wb.create_sheet("Sites") + ws2.append_row( + [ + ws2.make_cell(it, style) + for (it, style) in zip( + sites_titles[0], + [sco_excel.excel_make_style(bold=True)] * len(sites_titles[0]), + ) + ] + + [ + ws2.make_cell(it, style) + for (it, style) in zip( + sites_titles[1], + [sco_excel.excel_make_style(bold=True, color=sco_excel.COLORS.RED)] + * len(sites_titles[1]), + ) + ] + ) + ws3 = wb.create_sheet("Correspondants") + ws3.append_row( + [ + ws3.make_cell(it, style) + for (it, style) in zip( + correspondants_titles, + [sco_excel.excel_make_style(bold=True)] * len(correspondants_titles), + ) + ] + ) + if export: + entreprises = Entreprise.query.filter_by(visible=True).all() + sites = ( + db.session.query(EntrepriseSite) + .join(Entreprise, EntrepriseSite.entreprise_id == Entreprise.id) + .filter_by(visible=True) + .all() + ) + correspondants = ( + db.session.query(EntrepriseCorrespondant) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) + .filter_by(visible=True) + .all() + ) + entreprises_lines = [ + [entreprise.to_dict().get(k, "") for k in ENTREPRISES_KEYS] + for entreprise in entreprises + ] + sites_lines = [ + [site.to_dict().get(k, "") for k in SITES_KEYS[0]] for site in sites + ] + correspondants_lines = [ + [correspondant.to_dict().get(k, "") for k in CORRESPONDANTS_KEYS] + for correspondant in correspondants + ] + for line in entreprises_lines: + cells = [] + for it in line: + cells.append(ws1.make_cell(it)) + ws1.append_row(cells) + for line in sites_lines: + cells = [] + for it in line: + cells.append(ws2.make_cell(it)) + ws2.append_row(cells) + for line in correspondants_lines: + cells = [] + for it in line: + cells.append(ws3.make_cell(it)) + ws3.append_row(cells) + return wb + + def verif_correspondant_data(correspondant_data): """ Verifie les données d'une ligne Excel (correspondant) @@ -213,132 +344,3 @@ def verif_entreprise_data(entreprise_data): if entreprise is not None: return False return True - - -def get_excel_book_are(export=False): - entreprises_keys = [ - "siret", - "nom_entreprise", - "adresse", - "ville", - "code_postal", - "pays", - ] - sites_keys = [ - [ - "siret_entreprise", - "id", - "nom_site", - "adresse", - "ville", - "code_postal", - "pays", - ], - [ - "civilite", - "nom", - "prenom", - "telephone", - "mail", - "poste", - "service", - "origine", - "notes", - ], - ] - correspondants_keys = [ - "id", - "civilite", - "nom", - "prenom", - "telephone", - "mail", - "poste", - "service", - "origine", - "notes", - "nom_site", - ] - entreprises_titles = entreprises_keys[:] - sites_titles = sites_keys[:] - correspondants_titles = correspondants_keys[:] - wb = sco_excel.ScoExcelBook() - ws1 = wb.create_sheet("Entreprises") - ws1.append_row( - [ - ws1.make_cell(it, style) - for (it, style) in zip( - entreprises_titles, - [sco_excel.excel_make_style(bold=True)] * len(entreprises_titles), - ) - ] - ) - ws2 = wb.create_sheet("Sites") - ws2.append_row( - [ - ws2.make_cell(it, style) - for (it, style) in zip( - sites_titles[0], - [sco_excel.excel_make_style(bold=True)] * len(sites_titles[0]), - ) - ] - + [ - ws2.make_cell(it, style) - for (it, style) in zip( - sites_titles[1], - [sco_excel.excel_make_style(bold=True, color=sco_excel.COLORS.RED)] - * len(sites_titles[1]), - ) - ] - ) - ws3 = wb.create_sheet("Correspondants") - ws3.append_row( - [ - ws3.make_cell(it, style) - for (it, style) in zip( - correspondants_titles, - [sco_excel.excel_make_style(bold=True)] * len(correspondants_titles), - ) - ] - ) - if export: - entreprises = Entreprise.query.filter_by(visible=True).all() - sites = ( - db.session.query(EntrepriseSite) - .join(Entreprise, EntrepriseSite.entreprise_id == Entreprise.id) - .filter_by(visible=True) - .all() - ) - correspondants = ( - db.session.query(EntrepriseCorrespondant) - .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) - .filter_by(visible=True) - .all() - ) - entreprises_lines = [ - [entreprise.to_dict().get(k, "") for k in entreprises_keys] - for entreprise in entreprises - ] - sites_lines = [ - [site.to_dict().get(k, "") for k in sites_keys[0]] for site in sites - ] - correspondants_lines = [ - [correspondant.to_dict().get(k, "") for k in correspondants_keys] - for correspondant in correspondants - ] - for line in entreprises_lines: - cells = [] - for it in line: - cells.append(ws1.make_cell(it)) - ws1.append_row(cells) - for line in sites_lines: - cells = [] - for it in line: - cells.append(ws2.make_cell(it)) - ws2.append_row(cells) - for line in correspondants_lines: - cells = [] - for it in line: - cells.append(ws3.make_cell(it)) - ws3.append_row(cells) - return wb diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index 65d503cca..a129c7228 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -272,7 +272,7 @@ class OffreCreationForm(FlaskForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.correspondant.choices = [ + self.correspondant.choices = [("", "")] + [ (correspondant.id, f"{correspondant.nom} {correspondant.prenom}") for correspondant in EntrepriseCorrespondant.query.filter_by( entreprise_id=self.hidden_entreprise_id.data @@ -318,7 +318,7 @@ class OffreModificationForm(FlaskForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.correspondant.choices = [ + self.correspondant.choices = [("", "")] + [ (correspondant.id, f"{correspondant.nom} {correspondant.prenom}") for correspondant in EntrepriseCorrespondant.query.filter_by( entreprise_id=self.hidden_entreprise_id.data diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 0f1cefd97..d6536c626 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -58,7 +58,7 @@ from app.scodoc import sco_etud, sco_excel import app.scodoc.sco_utils as scu from app import db -from sqlalchemy import text +from sqlalchemy import text, sql from werkzeug.utils import secure_filename @@ -665,7 +665,9 @@ def add_offre(id): missions=form.missions.data.strip(), duree=form.duree.data.strip(), expiration_date=form.expiration_date.data, - correspondant_id=form.correspondant.data, + correspondant_id=form.correspondant.data + if form.correspondant.data != "" + else None, ) db.session.add(offre) db.session.commit() @@ -728,7 +730,10 @@ def edit_offre(id): offre.missions = form.missions.data.strip() offre.duree = form.duree.data.strip() offre.expiration_date = form.expiration_date.data - offre.correspondant_id = form.correspondant.data + if form.correspondant.data == "": + offre.correspondant_id = sql.null() + else: + offre.correspondant_id = form.correspondant.data if offre_depts_list != form.depts.data: for dept in form.depts.data: if dept not in offre_depts_list: @@ -1415,13 +1420,45 @@ def import_donnees(): Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename) ) file.save(file_path) - data = sco_excel.excel_file_to_list(file_path) + diag, lm = sco_excel.excel_file_to_list_are(file_path) os.remove(file_path) - print(data) + if ( + len(lm) < 3 + or lm[0][0] != are.ENTREPRISES_KEYS + or lm[1][0] != are.SITES_KEYS[0] + are.SITES_KEYS[1] + or lm[2][0] != are.CORRESPONDANTS_KEYS + ): + flash("Veuillez utilisez la feuille excel à remplir") + return redirect(url_for("entreprises.import_donnees")) + + # en cours + entreprises_import = [] + siret_list = [] + ligne = 1 + for entreprise_data in lm[0][1:]: + ligne += 1 + if ( + are.verif_entreprise_data(entreprise_data) + and entreprise_data[0].replace(" ", "") not in siret_list + ): + siret_list.append(entreprise_data[0].replace(" ", "")) + entreprise = Entreprise( + siret=entreprise_data[0].replace(" ", ""), + nom=entreprise_data[1].strip(), + adresse=entreprise_data[2].strip(), + ville=entreprise_data[3].strip(), + codepostal=entreprise_data[4].strip(), + pays=entreprise_data[5].strip(), + visible=True, + ) + entreprises_import.append(entreprise) + else: + flash(f"Erreur lors de l'importation") + return redirect(url_for("entreprises.import_donnees")) return redirect(url_for("entreprises.import_donnees")) return render_template( - "entreprises/import_entreprises.html", - title="Importation entreprises", + "entreprises/import_donnees.html", + title="Importation données", form=form, ) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 5d1b2d729..9ba10a304 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -613,6 +613,17 @@ def excel_file_to_list(filename): ) +def excel_file_to_list_are(filename): + try: + return _excel_to_list_are(filename) + except: + raise ScoValueError( + """Le fichier xlsx attendu n'est pas lisible ! + Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) + """ + ) + + def _excel_to_list(filelike): """returns list of list convert_to_string is a conversion function applied to all non-string values (ie numbers) @@ -673,6 +684,59 @@ def _excel_to_list(filelike): return diag, m +def _excel_to_list_are(filelike): + """returns list of list + convert_to_string is a conversion function applied to all non-string values (ie numbers) + """ + try: + wb = load_workbook(filename=filelike, read_only=True, data_only=True) + except: + log("Excel_to_list: failure to import document") + with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f: + f.write(filelike) + raise ScoValueError( + "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !" + ) + diag = [] # liste de chaines pour former message d'erreur + if len(wb.get_sheet_names()) < 1: + diag.append("Aucune feuille trouvée dans le classeur !") + return diag, None + lm = [] + for sheet_name in wb.get_sheet_names(): + # fill matrix + ws = wb.get_sheet_by_name(sheet_name) + sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace") + values = {} + for row in ws.iter_rows(): + for cell in row: + if cell.value is not None: + values[(cell.row - 1, cell.column - 1)] = str(cell.value) + if not values: + diag.append( + "Aucune valeur trouvée dans la feuille %s !" + % sheet_name.decode(scu.SCO_ENCODING) + ) + return diag, None + indexes = list(values.keys()) + # search numbers of rows and cols + rows = [x[0] for x in indexes] + cols = [x[1] for x in indexes] + nbcols = max(cols) + 1 + nbrows = max(rows) + 1 + m = [] + for _ in range(nbrows): + m.append([""] * nbcols) + + for row_idx, col_idx in indexes: + v = values[(row_idx, col_idx)] + m[row_idx][col_idx] = v + diag.append( + 'Feuille "%s", %d lignes' % (sheet_name.decode(scu.SCO_ENCODING), len(m)) + ) + lm.append(m) + return diag, lm + + def excel_feuille_listeappel( sem, groupname, diff --git a/app/templates/entreprises/import_entreprises.html b/app/templates/entreprises/import_donnees.html similarity index 98% rename from app/templates/entreprises/import_entreprises.html rename to app/templates/entreprises/import_donnees.html index 833de24cd..b0c2f7c12 100644 --- a/app/templates/entreprises/import_entreprises.html +++ b/app/templates/entreprises/import_donnees.html @@ -7,7 +7,7 @@ {% endblock %} {% block app_content %} -

    Importation entreprises

    +

    {{ title }}


    Obtenir la feuille excel à remplir From 1ba595476292525c7a143eef7cbe3b3a6b5f2f9c Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 3 Jun 2022 18:12:28 +0200 Subject: [PATCH 11/25] import feuille entreprises --- app/entreprises/app_relations_entreprises.py | 75 ++++++++++++++++---- app/entreprises/routes.py | 61 +++++++--------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 0626aff89..b854bd69d 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -30,6 +30,7 @@ import re import requests import glob +from flask import redirect, url_for, flash from flask_login import current_user from app.entreprises.models import ( @@ -39,6 +40,7 @@ from app.entreprises.models import ( EntrepriseOffreDepartement, EntreprisePreferences, EntrepriseSite, + EntrepriseLog, ) from app import email, db from app.scodoc import sco_preferences @@ -316,20 +318,70 @@ def verif_correspondant_data(correspondant_data): return True -def verif_entreprise_data(entreprise_data): +def check_entreprises_import(m): + entreprises_import = [] + siret_list = [] + ligne = 1 + if m[0] != ENTREPRISES_KEYS: + flash( + f'Veuillez utilisez la feuille excel à remplir (Feuille "Entreprises", ligne {ligne})' + ) + return redirect(url_for("entreprises.import_donnees")) + for entreprise_data in m[1:]: + ligne += 1 + entreprise_data[0] = entreprise_data[0].strip().replace(" ", "") + siret = entreprise_data[0] + if check_entreprise_import(entreprise_data) and siret not in siret_list: + siret_list.append(siret) + entreprise = Entreprise.query.filter_by(siret=siret).first() + if entreprise is None: + entreprise_import = Entreprise( + siret=siret, + nom=entreprise_data[1].strip(), + adresse=entreprise_data[2].strip(), + ville=entreprise_data[3].strip(), + codepostal=entreprise_data[4].strip(), + pays=entreprise_data[5].strip(), + visible=True, + ) + entreprises_import.append(entreprise_import) + else: + entreprise.nom = entreprise_data[1].strip() + entreprise.adresse = entreprise_data[2].strip() + entreprise.ville = entreprise_data[3].strip() + entreprise.codepostal = entreprise_data[4].strip() + entreprise.pays = ( + entreprise_data[5].strip() + if entreprise_data[5].strip() + else "FRANCE" + ) + else: + flash( + f'Erreur lors de l\'importation (Feuille "Entreprises", ligne {ligne})' + ) + return redirect(url_for("entreprises.import_donnees")) + + if len(entreprises_import) > 0: + log = EntrepriseLog( + authenticated_user=current_user.user_name, + text=f"Importation de {len(entreprises_import)} entreprise(s)", + ) + db.session.add(log) + + return entreprises_import + + +def check_entreprise_import(entreprise_data): """ Verifie les données d'une ligne Excel (entreprise) """ + for data in entreprise_data[:-1]: # champs obligatoires + if data.strip() == "": + return False + + siret = entreprise_data[0] + if EntreprisePreferences.get_check_siret(): - for data in entreprise_data: # champs obligatoires - if data.strip() == "": - return False - else: - for data in entreprise_data[1:]: # champs obligatoires - if data.strip() == "": - return False - if EntreprisePreferences.get_check_siret(): - siret = entreprise_data[0].strip().replace(" ", "") # vérification sur le siret if re.match("^\d{14}$", siret) is None: return False try: @@ -340,7 +392,4 @@ def verif_entreprise_data(entreprise_data): return False except requests.ConnectionError: return False - entreprise = Entreprise.query.filter_by(siret=siret).first() - if entreprise is not None: - return False return True diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index d6536c626..824a68bb9 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1422,44 +1422,33 @@ def import_donnees(): file.save(file_path) diag, lm = sco_excel.excel_file_to_list_are(file_path) os.remove(file_path) - if ( - len(lm) < 3 - or lm[0][0] != are.ENTREPRISES_KEYS - or lm[1][0] != are.SITES_KEYS[0] + are.SITES_KEYS[1] - or lm[2][0] != are.CORRESPONDANTS_KEYS - ): - flash("Veuillez utilisez la feuille excel à remplir") + if len(lm) < 3: + flash("Veuillez utilisez la feuille excel à remplir (3 feuilles)") return redirect(url_for("entreprises.import_donnees")) - - # en cours - entreprises_import = [] - siret_list = [] - ligne = 1 - for entreprise_data in lm[0][1:]: - ligne += 1 - if ( - are.verif_entreprise_data(entreprise_data) - and entreprise_data[0].replace(" ", "") not in siret_list - ): - siret_list.append(entreprise_data[0].replace(" ", "")) - entreprise = Entreprise( - siret=entreprise_data[0].replace(" ", ""), - nom=entreprise_data[1].strip(), - adresse=entreprise_data[2].strip(), - ville=entreprise_data[3].strip(), - codepostal=entreprise_data[4].strip(), - pays=entreprise_data[5].strip(), - visible=True, - ) - entreprises_import.append(entreprise) - else: - flash(f"Erreur lors de l'importation") - return redirect(url_for("entreprises.import_donnees")) - return redirect(url_for("entreprises.import_donnees")) + entreprises_import = are.check_entreprises_import(lm[0]) + for entreprise in entreprises_import: + db.session.add(entreprise) + db.session.commit() + db.session.refresh(entreprise) + site = EntrepriseSite( + entreprise_id=entreprise.id, + nom=entreprise.nom, + adresse=entreprise.adresse, + codepostal=entreprise.codepostal, + ville=entreprise.ville, + pays=entreprise.pays, + ) + db.session.add(site) + db.session.commit() + flash(f"Importation réussie") + return render_template( + "entreprises/import_donnees.html", + title="Importation données", + form=form, + entreprises_import=entreprises_import, + ) return render_template( - "entreprises/import_donnees.html", - title="Importation données", - form=form, + "entreprises/import_donnees.html", title="Importation données", form=form ) From 835d1d38ed9139471d65538fda5e255acda7f2ec Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Wed, 8 Jun 2022 21:07:04 +0200 Subject: [PATCH 12/25] =?UTF-8?q?import=20donn=C3=A9es=20sites=20+=20corre?= =?UTF-8?q?spondants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 223 +++++++++++++----- app/entreprises/routes.py | 24 +- app/templates/entreprises/import_donnees.html | 71 +++++- 3 files changed, 261 insertions(+), 57 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index b854bd69d..580e685f2 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -30,7 +30,7 @@ import re import requests import glob -from flask import redirect, url_for, flash +from flask import flash from flask_login import current_user from app.entreprises.models import ( @@ -184,6 +184,9 @@ def send_email_notifications_entreprise(subject: str, entreprise: Entreprise): def get_excel_book_are(export: bool = False): + """ + Retourne un Excel avec les 3 feuilles "Entreprises", "Sites" et "Correspondants" + """ entreprises_titles = ENTREPRISES_KEYS[:] sites_titles = SITES_KEYS[:] correspondants_titles = CORRESPONDANTS_KEYS[:] @@ -269,56 +272,10 @@ def get_excel_book_are(export: bool = False): return wb -def verif_correspondant_data(correspondant_data): - """ - Verifie les données d'une ligne Excel (correspondant) - correspondant_data[0]: civilite - correspondant_data[1]: nom - correspondant_data[2]: prenom - correspondant_data[3]: telephone - correspondant_data[4]: mail - correspondant_data[5]: poste - correspondant_data[6]: service - correspondant_data[7]: origine - correspondant_data[8]: notes - correspondant_data[9]: entreprise_siret - """ - # champs obligatoires - if ( - correspondant_data[0].strip() == "" - or correspondant_data[1].strip() == "" - or correspondant_data[2].strip() == "" - or correspondant_data[9].strip() == "" - ): - return False - - # civilite entre H ou F - if correspondant_data[0].strip() not in ["H", "F"]: - return False - - # entreprise_id existant - entreprise = Entreprise.query.filter_by(siret=correspondant_data[9].strip()).first() - if entreprise is None: - return False - - # correspondant possède le meme nom et prénom dans la meme entreprise - correspondant = EntrepriseCorrespondant.query.filter_by( - nom=correspondant_data[1].strip(), - prenom=correspondant_data[2].strip(), - entreprise_id=entreprise.id, - ).first() - if correspondant is not None: - return False - - if ( - correspondant_data[3].strip() == "" and correspondant_data[4].strip() == "" - ): # 1 moyen de contact - return False - - return True - - def check_entreprises_import(m): + """ + Verifie la feuille Excel "Entreprises" de l'importation données + """ entreprises_import = [] siret_list = [] ligne = 1 @@ -326,14 +283,14 @@ def check_entreprises_import(m): flash( f'Veuillez utilisez la feuille excel à remplir (Feuille "Entreprises", ligne {ligne})' ) - return redirect(url_for("entreprises.import_donnees")) + return False for entreprise_data in m[1:]: ligne += 1 entreprise_data[0] = entreprise_data[0].strip().replace(" ", "") siret = entreprise_data[0] if check_entreprise_import(entreprise_data) and siret not in siret_list: siret_list.append(siret) - entreprise = Entreprise.query.filter_by(siret=siret).first() + entreprise = Entreprise.query.filter_by(siret=siret, visible=True).first() if entreprise is None: entreprise_import = Entreprise( siret=siret, @@ -341,7 +298,9 @@ def check_entreprises_import(m): adresse=entreprise_data[2].strip(), ville=entreprise_data[3].strip(), codepostal=entreprise_data[4].strip(), - pays=entreprise_data[5].strip(), + pays=entreprise_data[5].strip() + if entreprise_data[5].strip() + else "FRANCE", visible=True, ) entreprises_import.append(entreprise_import) @@ -359,7 +318,7 @@ def check_entreprises_import(m): flash( f'Erreur lors de l\'importation (Feuille "Entreprises", ligne {ligne})' ) - return redirect(url_for("entreprises.import_donnees")) + return False if len(entreprises_import) > 0: log = EntrepriseLog( @@ -371,7 +330,7 @@ def check_entreprises_import(m): return entreprises_import -def check_entreprise_import(entreprise_data): +def check_entreprise_import(entreprise_data: list): """ Verifie les données d'une ligne Excel (entreprise) """ @@ -393,3 +352,157 @@ def check_entreprise_import(entreprise_data): except requests.ConnectionError: return False return True + + +def check_sites_import(m): + """ + Verifie la feuille Excel "Sites" de l'importation données + """ + sites_import = [] + correspondants_import = [] + ligne = 1 + if m[0] != sum(SITES_KEYS, []): + flash( + f'Veuillez utilisez la feuille excel à remplir (Feuille "Sites", ligne {ligne})' + ) + return False, False + for site_data in m[1:]: + ligne += 1 + site_data[0] = site_data[0].strip().replace(" ", "") + if check_site_import(site_data): + correspondant_data = site_data[len(SITES_KEYS[0]) :] + entreprise = Entreprise.query.filter_by( + siret=site_data[0], visible=True + ).first() + if site_data[1].strip() == "": + site_import = EntrepriseSite( + entreprise_id=entreprise.id, + nom=site_data[2].strip(), + adresse=site_data[3].strip(), + codepostal=site_data[4].strip(), + ville=site_data[5].strip(), + pays=site_data[6].strip(), + ) + if correspondant_data == [""] * len(SITES_KEYS[1]): + sites_import.append(site_import) + else: + correspondant_import = EntrepriseCorrespondant( + entreprise_id=entreprise.id, + civilite=correspondant_data[0], + nom=correspondant_data[1], + prenom=correspondant_data[2], + telephone=correspondant_data[3], + mail=correspondant_data[4], + poste=correspondant_data[5], + service=correspondant_data[6], + origine=correspondant_data[7], + notes=correspondant_data[8], + ) + sites_import.append(site_import) + correspondants_import.append([site_import, correspondant_import]) + else: + site_id = site_data[1].strip() + site = EntrepriseSite.query.filter_by(id=site_id).first() + site.nom = site_data[2].strip() + site.adresse = site_data[3].strip() + site.codepostal = site_data[4].strip() + site.ville = site_data[5].strip() + site.pays = site_data[6].strip() + + if correspondant_data != [""] * len(SITES_KEYS[1]): + correspondant_import = EntrepriseCorrespondant( + entreprise_id=entreprise.id, + site_id=site.id, + civilite=correspondant_data[0], + nom=correspondant_data[1], + prenom=correspondant_data[2], + telephone=correspondant_data[3], + mail=correspondant_data[4], + poste=correspondant_data[5], + service=correspondant_data[6], + origine=correspondant_data[7], + notes=correspondant_data[8], + ) + correspondants_import.append([None, correspondant_import]) + else: + flash(f'Erreur lors de l\'importation (Feuille "Sites", ligne {ligne})') + return False, False + + if len(sites_import) > 0: + log = EntrepriseLog( + authenticated_user=current_user.user_name, + text=f"Importation de {len(sites_import)} site(s)", + ) + db.session.add(log) + + if len(correspondants_import) > 0: + log = EntrepriseLog( + authenticated_user=current_user.user_name, + text=f"Importation de {len(correspondants_import)} correspondant(s)", + ) + db.session.add(log) + + return sites_import, correspondants_import + + +def check_site_import(row_data: list): + """ + Verifie les données d'une ligne Excel (sites) + """ + site_data = row_data[: -len(SITES_KEYS[1])] + correspondant_data = row_data[len(SITES_KEYS[0]) :] + for data in [site_data[0]] + site_data[2:]: # champs obligatoires + if data.strip() == "": + return False + + if correspondant_data != [""] * len(SITES_KEYS[1]): + if check_correspondant_import(correspondant_data, site_data) is False: + return False + + entreprise = Entreprise.query.filter_by(siret=site_data[0], visible=True).first() + if entreprise is None: + return False + + site = EntrepriseSite.query.filter_by(nom=site_data[2]).first() + if site_data[1] == "" and site is not None: + return False + + return True + + +def check_correspondant_import(correspondant_data: list, site_data: list = None): + """ + Verifie les données d'une ligne Excel (correspondant) + """ + # champs obligatoires + if ( + correspondant_data[0].strip() == "" + or correspondant_data[1].strip() == "" + or correspondant_data[2].strip() == "" + ): + return False + + # civilite entre H ou F + if correspondant_data[0].strip() not in ["H", "F"]: + return False + + # entreprise_id existant + entreprise = Entreprise.query.filter_by(siret=site_data[0], visible=True).first() + if entreprise is None: + return False + + # correspondant possède le meme nom et prénom dans la meme entreprise + correspondant = EntrepriseCorrespondant.query.filter_by( + nom=correspondant_data[1].strip(), + prenom=correspondant_data[2].strip(), + entreprise_id=entreprise.id, + ).first() + if correspondant is not None: + return False + + if ( + correspondant_data[3].strip() == "" and correspondant_data[4].strip() == "" + ): # 1 moyen de contact + return False + + return True diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 824a68bb9..637d247a9 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1426,6 +1426,13 @@ def import_donnees(): flash("Veuillez utilisez la feuille excel à remplir (3 feuilles)") return redirect(url_for("entreprises.import_donnees")) entreprises_import = are.check_entreprises_import(lm[0]) + sites_import, correspondants_import = are.check_sites_import(lm[1]) + if ( + entreprises_import is False + or sites_import is False + or correspondants_import is False + ): + return redirect(url_for("entreprises.import_donnees")) for entreprise in entreprises_import: db.session.add(entreprise) db.session.commit() @@ -1439,13 +1446,28 @@ def import_donnees(): pays=entreprise.pays, ) db.session.add(site) - db.session.commit() + for site in sites_import: + db.session.add(site) + correspondants = [] + for site, correspondant in correspondants_import: + if site is None: + db.session.add(correspondant) + else: + db.session.add(site) + db.session.commit() + db.session.refresh(site) + correspondant.site_id = site.id + db.session.add(correspondant) + correspondants.append(correspondant) + db.session.commit() flash(f"Importation réussie") return render_template( "entreprises/import_donnees.html", title="Importation données", form=form, entreprises_import=entreprises_import, + sites_import=sites_import, + correspondants_import=correspondants, ) return render_template( "entreprises/import_donnees.html", title="Importation données", form=form diff --git a/app/templates/entreprises/import_donnees.html b/app/templates/entreprises/import_donnees.html index b0c2f7c12..35ed75fb9 100644 --- a/app/templates/entreprises/import_donnees.html +++ b/app/templates/entreprises/import_donnees.html @@ -22,7 +22,7 @@
    - {% if not entreprises_import %} + {% if not entreprises_import and not sites_import and not correspondants_import %}
    Feuille Entreprises
    @@ -58,5 +58,74 @@
    AttributTypeDescription
    nom_sitetextnom du site lié au correspondant
    {% endif %} + + {% if entreprises_import %} +
    Importation de {{ entreprises_import|length }} entreprise(s)
    + {% for entreprise in entreprises_import %} +
    +
    + SIRET : {{ entreprise.siret }}
    + Nom : {{ entreprise.nom }}
    + Adresse : {{ entreprise.adresse }}
    + Code postal : {{ entreprise.codepostal }}
    + Ville : {{ entreprise.ville }}
    + Pays : {{ entreprise.pays }} +
    + {% for site in entreprise.sites %} +
    + Nom : {{ site.nom }}
    + Adresse : {{ site.adresse }}
    + Code postal : {{ site.codepostal }}
    + Ville : {{ site.ville }}
    + Pays : {{ site.pays }} +
    + {% endfor %} +
    + {% endfor %} + {% endif %} + + {% if sites_import %} +
    Importation de {{ sites_import|length }} site(s)
    + {% for site in sites_import %} +
    + Nom : {{ site.nom }}
    + Adresse : {{ site.adresse }}
    + Code postal : {{ site.codepostal }}
    + Ville : {{ site.ville }}
    + Pays : {{ site.pays }} +
    + {% endfor %} + {% endif %} + + {% if correspondants_import %} +
    Importation de {{ correspondants_import|length }} correspondant(s)
    + {% for correspondant in correspondants_import %} +
    +
    + Civilité : {{ correspondant.civilite|get_civilité }}
    + Nom : {{ correspondant.nom }}
    + Prénom : {{ correspondant.prenom }}
    + {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
    + {% endif %} + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
    + {% endif %} + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
    + {% endif %} + {% if correspondant.service %} + Service : {{ correspondant.service }}
    + {% endif %} + {% if correspondant.origine %} + Origine : {{ correspondant.origine }}
    + {% endif %} + {% if correspondant.notes %} + Notes : {{ correspondant.notes }}
    + {% endif %} +
    +
    + {% endfor %} + {% endif %} {% endblock %} \ No newline at end of file From 434e571a98a4a09afb9c83fee413bf56b413d4d4 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Thu, 9 Jun 2022 17:28:05 +0200 Subject: [PATCH 13/25] =?UTF-8?q?import=20donn=C3=A9es=20modifications=20c?= =?UTF-8?q?orrespondants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 68 +++++++++++++++---- app/entreprises/routes.py | 5 +- app/templates/entreprises/import_donnees.html | 7 +- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 580e685f2..77f31706f 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -470,6 +470,43 @@ def check_site_import(row_data: list): return True +def check_correspondants_import(m): + ligne = 1 + if m[0] != CORRESPONDANTS_KEYS: + flash( + f'Veuillez utilisez la feuille excel à remplir (Feuille "Correspondants", ligne {ligne})' + ) + return False + for correspondant_data in m[1:]: + ligne += 1 + if correspondant_data[0] == "": + flash( + f'Erreur lors de l\'importation (Feuille "Correspondants", ligne {ligne})' + ) + return False + correspondant = EntrepriseCorrespondant.query.filter_by( + id=correspondant_data[0] + ).first() + if correspondant is not None and check_correspondant_import( + correspondant_data[1:-1] + ): + correspondant.civilite = correspondant_data[1].strip() + correspondant.nom = correspondant_data[2].strip() + correspondant.prenom = correspondant_data[3].strip() + correspondant.telephone = correspondant_data[4].strip() + correspondant.mail = correspondant_data[5].strip() + correspondant.poste = correspondant_data[6].strip() + correspondant.service = correspondant_data[7].strip() + correspondant.origine = correspondant_data[8].strip() + correspondant.notes = correspondant_data[9].strip() + else: + flash( + f'Erreur lors de l\'importation (Feuille "Correspondants", ligne {ligne})' + ) + return False + return True + + def check_correspondant_import(correspondant_data: list, site_data: list = None): """ Verifie les données d'une ligne Excel (correspondant) @@ -486,23 +523,26 @@ def check_correspondant_import(correspondant_data: list, site_data: list = None) if correspondant_data[0].strip() not in ["H", "F"]: return False - # entreprise_id existant - entreprise = Entreprise.query.filter_by(siret=site_data[0], visible=True).first() - if entreprise is None: - return False - - # correspondant possède le meme nom et prénom dans la meme entreprise - correspondant = EntrepriseCorrespondant.query.filter_by( - nom=correspondant_data[1].strip(), - prenom=correspondant_data[2].strip(), - entreprise_id=entreprise.id, - ).first() - if correspondant is not None: - return False - if ( correspondant_data[3].strip() == "" and correspondant_data[4].strip() == "" ): # 1 moyen de contact return False + if site_data is not None: + # entreprise_id existant + entreprise = Entreprise.query.filter_by( + siret=site_data[0], visible=True + ).first() + if entreprise is None: + return False + + # correspondant possède le meme nom et prénom dans la meme entreprise + correspondant = EntrepriseCorrespondant.query.filter_by( + nom=correspondant_data[1].strip(), + prenom=correspondant_data[2].strip(), + entreprise_id=entreprise.id, + ).first() + if correspondant is not None: + return False + return True diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 637d247a9..a116c7684 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1422,8 +1422,8 @@ def import_donnees(): file.save(file_path) diag, lm = sco_excel.excel_file_to_list_are(file_path) os.remove(file_path) - if len(lm) < 3: - flash("Veuillez utilisez la feuille excel à remplir (3 feuilles)") + if lm is None or len(lm) < 2: + flash("Veuillez utilisez la feuille excel à remplir") return redirect(url_for("entreprises.import_donnees")) entreprises_import = are.check_entreprises_import(lm[0]) sites_import, correspondants_import = are.check_sites_import(lm[1]) @@ -1431,6 +1431,7 @@ def import_donnees(): entreprises_import is False or sites_import is False or correspondants_import is False + or (len(lm) > 2 and are.check_correspondants_import(lm[2]) is False) ): return redirect(url_for("entreprises.import_donnees")) for entreprise in entreprises_import: diff --git a/app/templates/entreprises/import_donnees.html b/app/templates/entreprises/import_donnees.html index 35ed75fb9..c92bf764e 100644 --- a/app/templates/entreprises/import_donnees.html +++ b/app/templates/entreprises/import_donnees.html @@ -69,7 +69,8 @@ Adresse : {{ entreprise.adresse }}
    Code postal : {{ entreprise.codepostal }}
    Ville : {{ entreprise.ville }}
    - Pays : {{ entreprise.pays }} + Pays : {{ entreprise.pays }}
    + Fiche entreprise
    {% for site in entreprise.sites %}
    @@ -92,7 +93,8 @@ Adresse : {{ site.adresse }}
    Code postal : {{ site.codepostal }}
    Ville : {{ site.ville }}
    - Pays : {{ site.pays }} + Pays : {{ site.pays }}
    + Fiche entreprise
    {% endfor %} {% endif %} @@ -123,6 +125,7 @@ {% if correspondant.notes %} Notes : {{ correspondant.notes }}
    {% endif %} + Fiche entreprise
    {% endfor %} From e2c5a19655992911b1fbebe08a47fe268fa89853 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Thu, 9 Jun 2022 20:52:50 +0200 Subject: [PATCH 14/25] =?UTF-8?q?am=C3=A9lioration=20formulaires=20ajout?= =?UTF-8?q?=20correspondants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/forms.py | 14 ++++++++++---- app/static/css/entreprises.css | 5 +++++ .../entreprises/ajout_correspondants.html | 3 +++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index a129c7228..c564b24d4 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -348,8 +348,16 @@ class CorrespondantCreationForm(FlaskForm): validators=[DataRequired(message=CHAMP_REQUIS)], render_kw={"class": "form-control"}, ) - nom = _build_string_field("Nom (*)", render_kw={"class": "form-control"}) - prenom = _build_string_field("Prénom (*)", render_kw={"class": "form-control"}) + nom = StringField( + "Nom (*)", + validators=[DataRequired('Le champ "Nom" est requis')], + render_kw={"class": "form-control"}, + ) + prenom = StringField( + "Prénom (*)", + validators=[DataRequired('Le champ "Prénom" est requis')], + render_kw={"class": "form-control"}, + ) telephone = _build_string_field( "Téléphone (*)", required=False, render_kw={"class": "form-control"} ) @@ -379,7 +387,6 @@ class CorrespondantCreationForm(FlaskForm): if not self.telephone.data and not self.mail.data: msg = "Saisir un moyen de contact (mail ou téléphone)" self.telephone.errors.append(msg) - self.mail.errors.append(msg) validate = False return validate @@ -464,7 +471,6 @@ class CorrespondantModificationForm(FlaskForm): if not self.telephone.data and not self.mail.data: msg = "Saisir un moyen de contact (mail ou téléphone)" self.telephone.errors.append(msg) - self.mail.errors.append(msg) validate = False return validate diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 203b3aeba..14c8f2103 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -129,4 +129,9 @@ #form-entreprise-filter > label { margin-right: 20px; +} + +.title-form { + font-weight: bold; + color: #a94442 } \ No newline at end of file diff --git a/app/templates/entreprises/ajout_correspondants.html b/app/templates/entreprises/ajout_correspondants.html index 18070b7fc..487916038 100644 --- a/app/templates/entreprises/ajout_correspondants.html +++ b/app/templates/entreprises/ajout_correspondants.html @@ -17,6 +17,9 @@
    {{ form.hidden_tag() }} {% for subfield in form.correspondants %} + {% if form.correspondants.errors %} +

    Formulaire Correspondants-{{ loop.index-1 }}

    + {% endif %} {% for subsubfield in subfield %} {% if subsubfield.errors %} {% for error in subsubfield.errors %} From 91d5fb906a589e7a6d53cde3d35ebee2e58a2095 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 10 Jun 2022 15:54:33 +0200 Subject: [PATCH 15/25] =?UTF-8?q?am=C3=A9lioration=20erreurs=20formulaire?= =?UTF-8?q?=20dynamique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/forms.py | 13 ++++++++--- app/static/css/entreprises.css | 2 +- .../entreprises/ajout_correspondants.html | 22 ++++++++++++++++--- .../entreprises/envoi_offre_form.html | 16 +++++++------- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index c564b24d4..61d517b7c 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -471,6 +471,7 @@ class CorrespondantModificationForm(FlaskForm): if not self.telephone.data and not self.mail.data: msg = "Saisir un moyen de contact (mail ou téléphone)" self.telephone.errors.append(msg) + self.mail.errors.append(msg) validate = False return validate @@ -690,7 +691,7 @@ class TaxeApprentissageModificationForm(FlaskForm): class EnvoiOffreForm(FlaskForm): responsables = FieldList( - _build_string_field( + StringField( "Responsable (*)", render_kw={ "placeholder": "Tapez le nom du responsable de formation", @@ -703,6 +704,8 @@ class EnvoiOffreForm(FlaskForm): def validate(self): validate = True + list_select = True + if not FlaskForm.validate(self): validate = False @@ -718,8 +721,12 @@ class EnvoiOffreForm(FlaskForm): .first() ) if responsable is None: - entry.errors.append("Champ incorrect (selectionnez dans la liste)") - validate = False + validate, list_select = False, False + + if list_select is False: + self.responsables.errors.append( + "Champ incorrect (selectionnez dans la liste)" + ) return validate diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 14c8f2103..45d3f7629 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -131,7 +131,7 @@ margin-right: 20px; } -.title-form { +.title-form-error { font-weight: bold; color: #a94442 } \ No newline at end of file diff --git a/app/templates/entreprises/ajout_correspondants.html b/app/templates/entreprises/ajout_correspondants.html index 487916038..fd0fe64b0 100644 --- a/app/templates/entreprises/ajout_correspondants.html +++ b/app/templates/entreprises/ajout_correspondants.html @@ -16,9 +16,10 @@

    {{ form.hidden_tag() }} + {{ form.correspondants.label }} {% for subfield in form.correspondants %} - {% if form.correspondants.errors %} -

    Formulaire Correspondants-{{ loop.index-1 }}

    + {% if subfield.errors %} +

    Formulaire {{ subfield.label.text }}

    {% endif %} {% for subsubfield in subfield %} {% if subsubfield.errors %} @@ -59,7 +60,22 @@ } let newFieldName = `correspondants-${Math.max(...correspondantInputIds) + 1}`; allCorrepondantsFieldWrapper.insertAdjacentHTML('beforeend',` -
  • Retirer ce correspondant
  • +
  • + + + + + + + + + + + +
    + +
    Retirer ce correspondant
    +
  • `); }); } diff --git a/app/templates/entreprises/envoi_offre_form.html b/app/templates/entreprises/envoi_offre_form.html index 6ead6477d..e8f2d55e1 100644 --- a/app/templates/entreprises/envoi_offre_form.html +++ b/app/templates/entreprises/envoi_offre_form.html @@ -18,13 +18,9 @@

    {{ form.hidden_tag() }} - {{ form.responsables.label }}
    - {% for subfield in form.responsables %} - {% if subfield.errors %} - {% for error in subfield.errors %} -

    {{ error }}

    - {% endfor %} - {% endif %} + {{ form.responsables.label }} + {% for error in form.responsables.errors %} +

    {{ error }}

    {% endfor %} {{ form.responsables }}
    @@ -71,7 +67,11 @@ } let newFieldName = `responsables-${Math.max(...responsableInputIds) + 1}`; allResponsablesFieldWrapper.insertAdjacentHTML('beforeend',` -
  • Retirer
  • +
  • + + +
    Retirer
    +
  • `); var as_r = new bsn.AutoSuggest(newFieldName, responsables_options); }); From 5b762cd91124e94493da38ea8735680a9727021c Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 10 Jun 2022 17:55:59 +0200 Subject: [PATCH 16/25] =?UTF-8?q?import=20donn=C3=A9es=20en=20dict=20(plus?= =?UTF-8?q?=20lisible)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 218 ++++++++++--------- app/entreprises/models.py | 2 +- 2 files changed, 118 insertions(+), 102 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 77f31706f..78529ef78 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -60,7 +60,7 @@ ENTREPRISES_KEYS = [ SITES_KEYS = [ [ "siret_entreprise", - "id", + "id_site", "nom_site", "adresse", "ville", @@ -284,35 +284,38 @@ def check_entreprises_import(m): f'Veuillez utilisez la feuille excel à remplir (Feuille "Entreprises", ligne {ligne})' ) return False - for entreprise_data in m[1:]: + l = list_to_dict(m) + for entreprise_data in l: ligne += 1 - entreprise_data[0] = entreprise_data[0].strip().replace(" ", "") - siret = entreprise_data[0] - if check_entreprise_import(entreprise_data) and siret not in siret_list: - siret_list.append(siret) - entreprise = Entreprise.query.filter_by(siret=siret, visible=True).first() + entreprise_data["siret"] = entreprise_data["siret"].replace(" ", "") + if ( + check_entreprise_import(entreprise_data) + and entreprise_data["siret"] not in siret_list + ): + siret_list.append(entreprise_data["siret"]) + entreprise = Entreprise.query.filter_by( + siret=entreprise_data["siret"], visible=True + ).first() if entreprise is None: entreprise_import = Entreprise( - siret=siret, - nom=entreprise_data[1].strip(), - adresse=entreprise_data[2].strip(), - ville=entreprise_data[3].strip(), - codepostal=entreprise_data[4].strip(), - pays=entreprise_data[5].strip() - if entreprise_data[5].strip() + siret=entreprise_data["siret"], + nom=entreprise_data["nom_entreprise"], + adresse=entreprise_data["adresse"], + ville=entreprise_data["ville"], + codepostal=entreprise_data["code_postal"], + pays=entreprise_data["pays"] + if entreprise_data["pays"] else "FRANCE", visible=True, ) entreprises_import.append(entreprise_import) else: - entreprise.nom = entreprise_data[1].strip() - entreprise.adresse = entreprise_data[2].strip() - entreprise.ville = entreprise_data[3].strip() - entreprise.codepostal = entreprise_data[4].strip() + entreprise.nom = entreprise_data["nom_entreprise"] + entreprise.adresse = entreprise_data["adresse"] + entreprise.ville = entreprise_data["ville"] + entreprise.codepostal = entreprise_data["code_postal"] entreprise.pays = ( - entreprise_data[5].strip() - if entreprise_data[5].strip() - else "FRANCE" + entreprise_data["pays"] if entreprise_data["pays"] else "FRANCE" ) else: flash( @@ -330,15 +333,16 @@ def check_entreprises_import(m): return entreprises_import -def check_entreprise_import(entreprise_data: list): +def check_entreprise_import(entreprise_data): """ Verifie les données d'une ligne Excel (entreprise) """ - for data in entreprise_data[:-1]: # champs obligatoires - if data.strip() == "": + champs_obligatoires = ["siret", "nom_entreprise", "adresse", "ville", "code_postal"] + for key, value in entreprise_data.items(): # champs obligatoires + if key in champs_obligatoires and value == "": return False - siret = entreprise_data[0] + siret = entreprise_data["siret"] if EntreprisePreferences.get_check_siret(): if re.match("^\d{14}$", siret) is None: @@ -366,62 +370,61 @@ def check_sites_import(m): f'Veuillez utilisez la feuille excel à remplir (Feuille "Sites", ligne {ligne})' ) return False, False - for site_data in m[1:]: + l = list_to_dict(m) + for site_data in l: ligne += 1 - site_data[0] = site_data[0].strip().replace(" ", "") + site_data["siret_entreprise"] = site_data["siret_entreprise"].replace(" ", "") if check_site_import(site_data): - correspondant_data = site_data[len(SITES_KEYS[0]) :] entreprise = Entreprise.query.filter_by( - siret=site_data[0], visible=True + siret=site_data["siret_entreprise"], visible=True ).first() - if site_data[1].strip() == "": + if site_data["id_site"] == "": site_import = EntrepriseSite( entreprise_id=entreprise.id, - nom=site_data[2].strip(), - adresse=site_data[3].strip(), - codepostal=site_data[4].strip(), - ville=site_data[5].strip(), - pays=site_data[6].strip(), + nom=site_data["nom_site"], + adresse=site_data["adresse"], + codepostal=site_data["code_postal"], + ville=site_data["ville"], + pays=site_data["pays"], ) - if correspondant_data == [""] * len(SITES_KEYS[1]): + if site_data["civilite"] == "": sites_import.append(site_import) else: correspondant_import = EntrepriseCorrespondant( entreprise_id=entreprise.id, - civilite=correspondant_data[0], - nom=correspondant_data[1], - prenom=correspondant_data[2], - telephone=correspondant_data[3], - mail=correspondant_data[4], - poste=correspondant_data[5], - service=correspondant_data[6], - origine=correspondant_data[7], - notes=correspondant_data[8], + civilite=site_data["civilite"], + nom=site_data["nom"], + prenom=site_data["prenom"], + telephone=site_data["telephone"], + mail=site_data["mail"], + poste=site_data["poste"], + service=site_data["service"], + origine=site_data["origine"], + notes=site_data["notes"], ) sites_import.append(site_import) correspondants_import.append([site_import, correspondant_import]) else: - site_id = site_data[1].strip() - site = EntrepriseSite.query.filter_by(id=site_id).first() - site.nom = site_data[2].strip() - site.adresse = site_data[3].strip() - site.codepostal = site_data[4].strip() - site.ville = site_data[5].strip() - site.pays = site_data[6].strip() + site = EntrepriseSite.query.filter_by(id=site_data["id_site"]).first() + site.nom = site_data["nom_site"] + site.adresse = site_data["adresse"] + site.codepostal = site_data["code_postal"] + site.ville = site_data["ville"] + site.pays = site_data["pays"] - if correspondant_data != [""] * len(SITES_KEYS[1]): + if site_data["civilite"] != "": correspondant_import = EntrepriseCorrespondant( entreprise_id=entreprise.id, site_id=site.id, - civilite=correspondant_data[0], - nom=correspondant_data[1], - prenom=correspondant_data[2], - telephone=correspondant_data[3], - mail=correspondant_data[4], - poste=correspondant_data[5], - service=correspondant_data[6], - origine=correspondant_data[7], - notes=correspondant_data[8], + civilite=site_data["civilite"], + nom=site_data["nom"], + prenom=site_data["prenom"], + telephone=site_data["telephone"], + mail=site_data["mail"], + poste=site_data["poste"], + service=site_data["service"], + origine=site_data["origine"], + notes=site_data["notes"], ) correspondants_import.append([None, correspondant_import]) else: @@ -445,26 +448,34 @@ def check_sites_import(m): return sites_import, correspondants_import -def check_site_import(row_data: list): +def check_site_import(site_data): """ Verifie les données d'une ligne Excel (sites) """ - site_data = row_data[: -len(SITES_KEYS[1])] - correspondant_data = row_data[len(SITES_KEYS[0]) :] - for data in [site_data[0]] + site_data[2:]: # champs obligatoires - if data.strip() == "": + champs_obligatoires = [ + "siret_entreprise", + "nom_site", + "adresse", + "ville", + "code_postal", + "pays", + ] + for key, value in site_data.items(): # champs obligatoires + if key in champs_obligatoires and value == "": return False - if correspondant_data != [""] * len(SITES_KEYS[1]): - if check_correspondant_import(correspondant_data, site_data) is False: + if site_data["civilite"] != "": + if check_correspondant_import(site_data) is False: return False - entreprise = Entreprise.query.filter_by(siret=site_data[0], visible=True).first() + entreprise = Entreprise.query.filter_by( + siret=site_data["siret_entreprise"], visible=True + ).first() if entreprise is None: return False - site = EntrepriseSite.query.filter_by(nom=site_data[2]).first() - if site_data[1] == "" and site is not None: + site = EntrepriseSite.query.filter_by(nom=site_data["nom_site"]).first() + if site_data["id_site"] == "" and site is not None: return False return True @@ -477,28 +488,27 @@ def check_correspondants_import(m): f'Veuillez utilisez la feuille excel à remplir (Feuille "Correspondants", ligne {ligne})' ) return False - for correspondant_data in m[1:]: + l = list_to_dict(m) + for correspondant_data in l: ligne += 1 - if correspondant_data[0] == "": + if correspondant_data["id"] == "": flash( f'Erreur lors de l\'importation (Feuille "Correspondants", ligne {ligne})' ) return False correspondant = EntrepriseCorrespondant.query.filter_by( - id=correspondant_data[0] + id=correspondant_data["id"] ).first() - if correspondant is not None and check_correspondant_import( - correspondant_data[1:-1] - ): - correspondant.civilite = correspondant_data[1].strip() - correspondant.nom = correspondant_data[2].strip() - correspondant.prenom = correspondant_data[3].strip() - correspondant.telephone = correspondant_data[4].strip() - correspondant.mail = correspondant_data[5].strip() - correspondant.poste = correspondant_data[6].strip() - correspondant.service = correspondant_data[7].strip() - correspondant.origine = correspondant_data[8].strip() - correspondant.notes = correspondant_data[9].strip() + if correspondant is not None and check_correspondant_import(correspondant_data): + correspondant.civilite = correspondant_data["civilite"] + correspondant.nom = correspondant_data["nom"] + correspondant.prenom = correspondant_data["prenom"] + correspondant.telephone = correspondant_data["telephone"] + correspondant.mail = correspondant_data["mail"] + correspondant.poste = correspondant_data["poste"] + correspondant.service = correspondant_data["service"] + correspondant.origine = correspondant_data["origine"] + correspondant.notes = correspondant_data["notes"] else: flash( f'Erreur lors de l\'importation (Feuille "Correspondants", ligne {ligne})' @@ -507,42 +517,48 @@ def check_correspondants_import(m): return True -def check_correspondant_import(correspondant_data: list, site_data: list = None): +def check_correspondant_import(correspondant_data): """ Verifie les données d'une ligne Excel (correspondant) """ - # champs obligatoires - if ( - correspondant_data[0].strip() == "" - or correspondant_data[1].strip() == "" - or correspondant_data[2].strip() == "" - ): - return False + champs_obligatoires = ["civilite", "nom", "prenom"] + for key, value in correspondant_data.items(): # champs obligatoires + if key in champs_obligatoires and value == "": + return False # civilite entre H ou F - if correspondant_data[0].strip() not in ["H", "F"]: + if correspondant_data["civilite"] not in ["H", "F"]: return False if ( - correspondant_data[3].strip() == "" and correspondant_data[4].strip() == "" + correspondant_data["telephone"] == "" and correspondant_data["mail"] == "" ): # 1 moyen de contact return False - if site_data is not None: + if "id" in correspondant_data: # entreprise_id existant entreprise = Entreprise.query.filter_by( - siret=site_data[0], visible=True + siret=correspondant_data["id"], visible=True ).first() if entreprise is None: return False # correspondant possède le meme nom et prénom dans la meme entreprise correspondant = EntrepriseCorrespondant.query.filter_by( - nom=correspondant_data[1].strip(), - prenom=correspondant_data[2].strip(), + nom=correspondant_data["nom"].strip(), + prenom=correspondant_data["prenom"].strip(), entreprise_id=entreprise.id, ).first() if correspondant is not None: return False return True + + +def list_to_dict(m): + l = [] + for data in m[1:]: + new_dict = {title: value.strip() for title, value in zip(m[0], data)} + l.append(new_dict) + + return l diff --git a/app/entreprises/models.py b/app/entreprises/models.py index e335edafb..bb4ad6169 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -67,7 +67,7 @@ class EntrepriseSite(db.Model): entreprise = Entreprise.query.get_or_404(self.entreprise_id) return { "siret_entreprise": entreprise.siret, - "id": self.id, + "id_site": self.id, "nom_site": self.nom, "adresse": self.adresse, "code_postal": self.codepostal, From 3b7ac4eec47ed90584a5dcb5fe48f0822414aae8 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Fri, 10 Jun 2022 18:10:41 +0200 Subject: [PATCH 17/25] =?UTF-8?q?corrections=20import=20donn=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 8 ++--- app/templates/entreprises/import_donnees.html | 36 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 78529ef78..64208cb37 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -535,18 +535,18 @@ def check_correspondant_import(correspondant_data): ): # 1 moyen de contact return False - if "id" in correspondant_data: + if "siret_entreprise" in correspondant_data: # entreprise_id existant entreprise = Entreprise.query.filter_by( - siret=correspondant_data["id"], visible=True + siret=correspondant_data["siret_entreprise"], visible=True ).first() if entreprise is None: return False # correspondant possède le meme nom et prénom dans la meme entreprise correspondant = EntrepriseCorrespondant.query.filter_by( - nom=correspondant_data["nom"].strip(), - prenom=correspondant_data["prenom"].strip(), + nom=correspondant_data["nom"], + prenom=correspondant_data["prenom"], entreprise_id=entreprise.id, ).first() if correspondant is not None: diff --git a/app/templates/entreprises/import_donnees.html b/app/templates/entreprises/import_donnees.html index c92bf764e..22d4f9c24 100644 --- a/app/templates/entreprises/import_donnees.html +++ b/app/templates/entreprises/import_donnees.html @@ -26,31 +26,33 @@
    Feuille Entreprises
    - - - - - + + + + +
    AttributTypeDescription
    sirettextsiret de l'entreprise
    nom_entreprisetextnom de l'entreprise
    adressetextadresse de l'entreprise
    villetextville de l'entreprise
    code_postaltextcode postal de l'entreprise
    siret (*)textsiret de l'entreprise
    nom_entreprise (*)textnom de l'entreprise
    adresse (*)textadresse de l'entreprise
    ville (*)textville de l'entreprise
    code_postal (*)textcode postal de l'entreprise
    paystextpays de l'entreprise
    Feuille Sites
    - - - - - - + + + + + + + +
    AttributTypeDescription
    siret_entreprisetextsiret de l'entreprise
    nom_sitetextnom du site
    adressetextadresse du site
    villetextville du site
    code_postaltextcode postal du site
    paystextpays du site
    siret_entreprise (*)textsiret de l'entreprise
    id_site (*)textid du site (ne rien remplir pour créer un site)
    nom_site (*)textnom du site
    adresse (*)textadresse du site
    ville (*)textville du site
    code_postal (*)textcode postal du site
    pays (*)textpays du site
    -
    Feuille Correspondants
    +
    Feuille Correspondants (à utiliser pour les modifications)
    - - - - - + + + + + From 9f7d6a3020ebf96913a4acc18d073fc08721faf5 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Mon, 4 Jul 2022 21:12:56 +0200 Subject: [PATCH 18/25] =?UTF-8?q?li=C3=A9=20migrations=20appli=20relations?= =?UTF-8?q?=20entreprises?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/app_relations_entreprises.py | 8 +- app/entreprises/models.py | 4 +- app/entreprises/routes.py | 54 +++++++------ ...7_ajout_taxe_apprentissage_association_.py | 58 ++++++++++++++ ...a_ajout_taxe_apprentissage_association_.py | 79 ------------------- 5 files changed, 95 insertions(+), 108 deletions(-) create mode 100644 migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py delete mode 100644 migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 64208cb37..b5b14dab7 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -40,7 +40,7 @@ from app.entreprises.models import ( EntrepriseOffreDepartement, EntreprisePreferences, EntrepriseSite, - EntrepriseLog, + EntrepriseHistorique, ) from app import email, db from app.scodoc import sco_preferences @@ -324,7 +324,7 @@ def check_entreprises_import(m): return False if len(entreprises_import) > 0: - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, text=f"Importation de {len(entreprises_import)} entreprise(s)", ) @@ -432,14 +432,14 @@ def check_sites_import(m): return False, False if len(sites_import) > 0: - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, text=f"Importation de {len(sites_import)} site(s)", ) db.session.add(log) if len(correspondants_import) > 0: - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, text=f"Importation de {len(correspondants_import)} correspondant(s)", ) diff --git a/app/entreprises/models.py b/app/entreprises/models.py index bb4ad6169..4edbe458b 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -149,8 +149,8 @@ class EntrepriseOffre(db.Model): } -class EntrepriseLog(db.Model): - __tablename__ = "are_logs" +class EntrepriseHistorique(db.Model): + __tablename__ = "are_historique" id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) authenticated_user = db.Column(db.Text) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index a116c7684..ef15f32b8 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -41,7 +41,7 @@ from app.entreprises.models import ( Entreprise, EntrepriseOffre, EntrepriseCorrespondant, - EntrepriseLog, + EntrepriseHistorique, EntrepriseContact, EntrepriseSite, EntrepriseStageApprentissage, @@ -69,7 +69,11 @@ def index(): Permet d'afficher une page avec la liste des entreprises (visible) et une liste des dernières opérations """ entreprises = Entreprise.query.filter_by(visible=True, active=True) - logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() + logs = ( + EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc()) + .limit(LOGS_LEN) + .all() + ) if current_user.has_permission(Permission.RelationsEntreprisesChange, None): form = EntreprisesFilterForm() checked = [False, False] @@ -103,9 +107,9 @@ def logs(): Permet d'afficher les logs (toutes les entreprises) """ page = request.args.get("page", 1, type=int) - logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).paginate( - page=page, per_page=20 - ) + logs = EntrepriseHistorique.query.order_by( + EntrepriseHistorique.date.desc() + ).paginate(page=page, per_page=20) return render_template( "entreprises/logs.html", title="Logs", @@ -140,7 +144,11 @@ def correspondants(): .filter_by(visible=True, active=True) .all() ) - logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() + logs = ( + EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc()) + .limit(LOGS_LEN) + .all() + ) return render_template( "entreprises/correspondants.html", title="Correspondants", @@ -176,8 +184,8 @@ def fiche_entreprise(id): offres_with_files.append(offre_with_files) sites = entreprise.sites[:] logs = ( - EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) - .filter(EntrepriseLog.entreprise_id == id) + EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc()) + .filter(EntrepriseHistorique.entreprise_id == id) .limit(LOGS_LEN) .all() ) @@ -216,8 +224,8 @@ def logs_entreprise(id): description=f"logs fiche entreprise {id} inconnu" ) logs = ( - EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) - .filter(EntrepriseLog.entreprise_id == entreprise.id) + EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc()) + .filter(EntrepriseHistorique.entreprise_id == entreprise.id) .paginate(page=page, per_page=20) ) return render_template( @@ -360,7 +368,7 @@ def add_entreprise(): if current_user.has_permission(Permission.RelationsEntreprisesValidate, None): entreprise.visible = True nom_entreprise = f"{entreprise.nom}" - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom})", entreprise_id=entreprise.id, @@ -398,7 +406,7 @@ def edit_entreprise(id): if form.validate_on_submit(): nom_entreprise = f"{form.nom.data.strip()}" if entreprise.nom != form.nom.data.strip(): - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du nom (ancien nom: {entreprise.nom})", @@ -406,7 +414,7 @@ def edit_entreprise(id): entreprise.nom = form.nom.data.strip() db.session.add(log) if entreprise.adresse != form.adresse.data.strip(): - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification de l'adresse (ancienne adresse: {entreprise.adresse})", @@ -414,7 +422,7 @@ def edit_entreprise(id): entreprise.adresse = form.adresse.data.strip() db.session.add(log) if entreprise.codepostal != form.codepostal.data.strip(): - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du code postal (ancien code postal: {entreprise.codepostal})", @@ -422,7 +430,7 @@ def edit_entreprise(id): entreprise.codepostal = form.codepostal.data.strip() db.session.add(log) if entreprise.ville != form.ville.data.strip(): - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification de la ville (ancienne ville: {entreprise.ville})", @@ -430,7 +438,7 @@ def edit_entreprise(id): entreprise.ville = form.ville.data.strip() db.session.add(log) if entreprise.pays != form.pays.data.strip() or not form.pays.data.strip(): - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Modification du pays (ancien pays: {entreprise.pays})", @@ -604,7 +612,7 @@ def validate_entreprise(id): if form.validate_on_submit(): entreprise.visible = True nom_entreprise = f"{entreprise.nom}" - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, text=f"{nom_entreprise} - Validation de la fiche entreprise ({entreprise.nom})", @@ -691,7 +699,7 @@ def add_offre(id): file = form.fichier.data filename = secure_filename(file.filename) file.save(os.path.join(path, filename)) - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=entreprise.id, object="offre", @@ -748,7 +756,7 @@ def edit_offre(id): offre_id=offre.id, dept_id=dept ).first_or_404() db.session.delete(offre_dept) - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=offre.entreprise_id, object="offre", @@ -795,7 +803,7 @@ def delete_offre(id): ) if os.path.isdir(path): shutil.rmtree(path) - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=offre.entreprise_id, object="offre", @@ -945,7 +953,7 @@ def add_correspondant(id_entreprise, id_site): db.session.add(correspondant) db.session.commit() db.session.refresh(correspondant) - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=correspondant.entreprise_id, object="correspondant", @@ -989,7 +997,7 @@ def edit_correspondant(id): correspondant.service = form.service.data.strip() correspondant.origine = form.origine.data.strip() correspondant.notes = form.notes.data.strip() - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=correspondant.entreprise_id, object="correspondant", @@ -1034,7 +1042,7 @@ def delete_correspondant(id): form = SuppressionConfirmationForm() if form.validate_on_submit(): db.session.delete(correspondant) - log = EntrepriseLog( + log = EntrepriseHistorique( authenticated_user=current_user.user_name, entreprise_id=correspondant.entreprise_id, object="correspondant", diff --git a/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py b/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py new file mode 100644 index 000000000..c8635598e --- /dev/null +++ b/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py @@ -0,0 +1,58 @@ +"""ajout taxe apprentissage, association, changement historique + +Revision ID: 0b337376e9f7 +Revises: ee21c76c8183 +Create Date: 2022-07-04 21:10:53.946385 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '0b337376e9f7' +down_revision = 'ee21c76c8183' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('are_historique', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('authenticated_user', sa.Text(), nullable=True), + sa.Column('entreprise_id', sa.Integer(), nullable=True), + sa.Column('object', sa.Text(), nullable=True), + sa.Column('object_id', sa.Integer(), nullable=True), + sa.Column('text', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('are_taxe_apprentissage', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('entreprise_id', sa.Integer(), nullable=True), + sa.Column('annee', sa.Integer(), nullable=True), + sa.Column('montant', sa.Integer(), nullable=True), + sa.Column('notes', sa.Text(), nullable=True), + sa.ForeignKeyConstraint(['entreprise_id'], ['are_entreprises.id'], ondelete='cascade'), + sa.PrimaryKeyConstraint('id') + ) + op.drop_table('are_logs') + op.add_column('are_entreprises', sa.Column('association', sa.Boolean(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('are_entreprises', 'association') + op.create_table('are_logs', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('date', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True), + sa.Column('authenticated_user', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('object', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('text', sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name='are_logs_pkey') + ) + op.drop_table('are_taxe_apprentissage') + op.drop_table('are_historique') + # ### end Alembic commands ### diff --git a/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py b/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py deleted file mode 100644 index 8cab659d9..000000000 --- a/migrations/versions/2b872cb116ca_ajout_taxe_apprentissage_association_.py +++ /dev/null @@ -1,79 +0,0 @@ -"""ajout taxe apprentissage, association, changement historique - -Revision ID: 2b872cb116ca -Revises: d5b3bdd1d622 -Create Date: 2022-05-06 18:16:58.727052 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = "2b872cb116ca" -down_revision = "d5b3bdd1d622" -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "are_taxe_apprentissage", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("entreprise_id", sa.Integer(), nullable=True), - sa.Column("annee", sa.Integer(), nullable=True), - sa.Column("montant", sa.Integer(), nullable=True), - sa.Column("notes", sa.Text(), nullable=True), - sa.ForeignKeyConstraint( - ["entreprise_id"], ["are_entreprises.id"], ondelete="cascade" - ), - sa.PrimaryKeyConstraint("id"), - ) - op.add_column( - "are_entreprises", sa.Column("association", sa.Boolean(), nullable=True) - ) - op.add_column("are_logs", sa.Column("entreprise_id", sa.Integer(), nullable=True)) - op.add_column("are_logs", sa.Column("object_id", sa.Integer(), nullable=True)) - op.alter_column( - "are_logs", - "object", - existing_type=sa.INTEGER(), - type_=sa.Text(), - existing_nullable=True, - ) - op.create_index( - op.f("ix_scolar_news_authenticated_user"), - "scolar_news", - ["authenticated_user"], - unique=False, - ) - op.create_index(op.f("ix_scolar_news_date"), "scolar_news", ["date"], unique=False) - op.create_index( - op.f("ix_scolar_news_object"), "scolar_news", ["object"], unique=False - ) - op.create_index(op.f("ix_scolar_news_type"), "scolar_news", ["type"], unique=False) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f("ix_scolar_news_type"), table_name="scolar_news") - op.drop_index(op.f("ix_scolar_news_object"), table_name="scolar_news") - op.drop_index(op.f("ix_scolar_news_date"), table_name="scolar_news") - op.drop_index(op.f("ix_scolar_news_authenticated_user"), table_name="scolar_news") - op.execute( - "alter table are_logs alter column object set data type int using object::integer" - ) - op.alter_column( - "are_logs", - "object", - existing_type=sa.Text(), - type_=sa.INTEGER(), - existing_nullable=True, - ) - op.drop_column("are_logs", "object_id") - op.drop_column("are_logs", "entreprise_id") - op.drop_column("are_entreprises", "association") - op.drop_table("are_taxe_apprentissage") - # ### end Alembic commands ### From ec987ad8fa15ae21da119b8fa4b3d1821ff53975 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Wed, 6 Jul 2022 20:37:20 +0200 Subject: [PATCH 19/25] ajout file d'ariane --- app/static/css/entreprises.css | 54 +++- app/templates/entreprises/contacts.html | 16 +- .../entreprises/fiche_entreprise.html | 13 +- app/templates/entreprises/import_donnees.html | 251 +++++++++--------- app/templates/entreprises/logs.html | 11 + .../entreprises/logs_entreprise.html | 14 + app/templates/entreprises/nav.html | 28 +- .../entreprises/offres_expirees.html | 14 + app/templates/entreprises/preferences.html | 12 +- 9 files changed, 266 insertions(+), 147 deletions(-) diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index 45d3f7629..ce3a4ccda 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -1,28 +1,37 @@ -.nav-entreprise>ul { - padding-left: 0; +.nav-entreprise { + text-align: left; +} + +.nav-entreprise ul { + padding: 0; } .nav-entreprise li{ list-style: none; display: inline-block; padding: 10px; + border: 2px black solid; + border-radius: 10px; +} + +.nav-entreprise li:hover{ + background-color: rgb(212, 212, 212); } .nav-entreprise>ul>li>a { text-decoration: none; color: black; padding: 15px; - +} + +.nav-entreprise>ul>li>a:hover { + text-decoration: underline; } .form-error { color: #a94442; } -.nav-entreprise>ul>li>a:hover { - color: red; -} - .boutons .btn { margin-top: 5px; margin-bottom: 5px; @@ -133,5 +142,34 @@ .title-form-error { font-weight: bold; - color: #a94442 + color: #a94442; +} + +.breadcrumbs { + padding: 0; +} + +.breadcrumbs_item { + display: inline-block; +} + +.breadcrumbs_item:not(:last-of-type)::after { + content: '\203a'; + margin: 0 5px; + color: #777; +} + +.breadcrumbs_link { + text-decoration: none; + color: #777; +} + +.breadcrumbs_link:hover { + text-decoration: underline; + color: #333; +} + +.breadcrumbs_link-active { + color: #333; + font-weight: bold; } \ No newline at end of file diff --git a/app/templates/entreprises/contacts.html b/app/templates/entreprises/contacts.html index 2896627e5..70ad459e0 100644 --- a/app/templates/entreprises/contacts.html +++ b/app/templates/entreprises/contacts.html @@ -9,7 +9,21 @@ {% endblock %} {% block app_content %} -
    +
    + +
    + +

    Liste des contacts

    {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} Ajouter contact diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 6d0896052..0b132cf12 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -9,6 +9,17 @@ {% endblock %} {% block app_content %} +
    + +
    + {% if logs %}

    Dernières opérations sur cette fiche Voir tout

    @@ -55,7 +66,7 @@
      {% if not taxes|check_taxe_now %} -
    • année actuelle : non versé
    • +
    • année actuelle : non versée
    • {% endif %} {% for taxe in taxes %}
    • diff --git a/app/templates/entreprises/import_donnees.html b/app/templates/entreprises/import_donnees.html index 22d4f9c24..bbc575e0c 100644 --- a/app/templates/entreprises/import_donnees.html +++ b/app/templates/entreprises/import_donnees.html @@ -7,130 +7,143 @@ {% endblock %} {% block app_content %} -

      {{ title }}

      -
      -
      - Obtenir la feuille excel à remplir + -
      -
      -
      -

      - (*) champs requis -

      - {{ wtf.quick_form(form, novalidate=True) }} + +
      +

      {{ title }}

      +
      + +
      +
      +
      +

      + (*) champs requis +

      + {{ wtf.quick_form(form, novalidate=True) }} +
      -
      - {% if not entreprises_import and not sites_import and not correspondants_import %} -
      Feuille Entreprises
      -
    AttributTypeDescription
    civilitetextcivilite du correspondant (H ou F)
    nomtextnom du correspondant
    prenomtextprenom du correspondant
    telephonetexttelephone du correspondant
    mailtextmail du correspondant
    civilite (*)textcivilite du correspondant (H ou F)
    nom (*)textnom du correspondant
    prenom (*)textprenom du correspondant
    telephone (*)texttelephone du correspondant
    mail (*)textmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    originetextorigine du correspondant
    - - - - - - - -
    AttributTypeDescription
    siret (*)textsiret de l'entreprise
    nom_entreprise (*)textnom de l'entreprise
    adresse (*)textadresse de l'entreprise
    ville (*)textville de l'entreprise
    code_postal (*)textcode postal de l'entreprise
    paystextpays de l'entreprise
    -
    Feuille Sites
    - - - - - - - - - - -
    AttributTypeDescription
    siret_entreprise (*)textsiret de l'entreprise
    id_site (*)textid du site (ne rien remplir pour créer un site)
    nom_site (*)textnom du site
    adresse (*)textadresse du site
    ville (*)textville du site
    code_postal (*)textcode postal du site
    pays (*)textpays du site
    -
    Feuille Correspondants (à utiliser pour les modifications)
    - - - - - - - - - - - - -
    AttributTypeDescription
    civilite (*)textcivilite du correspondant (H ou F)
    nom (*)textnom du correspondant
    prenom (*)textprenom du correspondant
    telephone (*)texttelephone du correspondant
    mail (*)textmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    originetextorigine du correspondant
    notestextnotes sur le correspondant
    nom_sitetextnom du site lié au correspondant
    - {% endif %} + {% if not entreprises_import and not sites_import and not correspondants_import %} +
    Feuille Entreprises
    + + + + + + + + +
    AttributTypeDescription
    siret (*)textsiret de l'entreprise
    nom_entreprise (*)textnom de l'entreprise
    adresse (*)textadresse de l'entreprise
    ville (*)textville de l'entreprise
    code_postal (*)textcode postal de l'entreprise
    paystextpays de l'entreprise
    +
    Feuille Sites
    + + + + + + + + + + +
    AttributTypeDescription
    siret_entreprise (*)textsiret de l'entreprise
    id_site (*)textid du site (ne rien remplir pour créer un site)
    nom_site (*)textnom du site
    adresse (*)textadresse du site
    ville (*)textville du site
    code_postal (*)textcode postal du site
    pays (*)textpays du site
    +
    Feuille Correspondants (à utiliser pour les modifications)
    + + + + + + + + + + + + +
    AttributTypeDescription
    civilite (*)textcivilite du correspondant (H ou F)
    nom (*)textnom du correspondant
    prenom (*)textprenom du correspondant
    telephone (*)texttelephone du correspondant
    mail (*)textmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    originetextorigine du correspondant
    notestextnotes sur le correspondant
    nom_sitetextnom du site lié au correspondant
    + {% endif %} - {% if entreprises_import %} -
    Importation de {{ entreprises_import|length }} entreprise(s)
    - {% for entreprise in entreprises_import %} -
    -
    - SIRET : {{ entreprise.siret }}
    - Nom : {{ entreprise.nom }}
    - Adresse : {{ entreprise.adresse }}
    - Code postal : {{ entreprise.codepostal }}
    - Ville : {{ entreprise.ville }}
    - Pays : {{ entreprise.pays }}
    - Fiche entreprise -
    - {% for site in entreprise.sites %} -
    - Nom : {{ site.nom }}
    - Adresse : {{ site.adresse }}
    - Code postal : {{ site.codepostal }}
    - Ville : {{ site.ville }}
    - Pays : {{ site.pays }} + {% if entreprises_import %} +
    Importation de {{ entreprises_import|length }} entreprise(s)
    + {% for entreprise in entreprises_import %} +
    +
    + SIRET : {{ entreprise.siret }}
    + Nom : {{ entreprise.nom }}
    + Adresse : {{ entreprise.adresse }}
    + Code postal : {{ entreprise.codepostal }}
    + Ville : {{ entreprise.ville }}
    + Pays : {{ entreprise.pays }}
    + Fiche entreprise
    - {% endfor %} -
    - {% endfor %} - {% endif %} - - {% if sites_import %} -
    Importation de {{ sites_import|length }} site(s)
    - {% for site in sites_import %} -
    - Nom : {{ site.nom }}
    - Adresse : {{ site.adresse }}
    - Code postal : {{ site.codepostal }}
    - Ville : {{ site.ville }}
    - Pays : {{ site.pays }}
    - Fiche entreprise -
    - {% endfor %} - {% endif %} - - {% if correspondants_import %} -
    Importation de {{ correspondants_import|length }} correspondant(s)
    - {% for correspondant in correspondants_import %} -
    -
    - Civilité : {{ correspondant.civilite|get_civilité }}
    - Nom : {{ correspondant.nom }}
    - Prénom : {{ correspondant.prenom }}
    - {% if correspondant.telephone %} - Téléphone : {{ correspondant.telephone }}
    - {% endif %} - {% if correspondant.mail %} - Mail : {{ correspondant.mail }}
    - {% endif %} - {% if correspondant.poste %} - Poste : {{ correspondant.poste }}
    - {% endif %} - {% if correspondant.service %} - Service : {{ correspondant.service }}
    - {% endif %} - {% if correspondant.origine %} - Origine : {{ correspondant.origine }}
    - {% endif %} - {% if correspondant.notes %} - Notes : {{ correspondant.notes }}
    - {% endif %} - Fiche entreprise + {% for site in entreprise.sites %} +
    + Nom : {{ site.nom }}
    + Adresse : {{ site.adresse }}
    + Code postal : {{ site.codepostal }}
    + Ville : {{ site.ville }}
    + Pays : {{ site.pays }} +
    + {% endfor %}
    -
    - {% endfor %} - {% endif %} + {% endfor %} + {% endif %} + + {% if sites_import %} +
    Importation de {{ sites_import|length }} site(s)
    + {% for site in sites_import %} +
    + Nom : {{ site.nom }}
    + Adresse : {{ site.adresse }}
    + Code postal : {{ site.codepostal }}
    + Ville : {{ site.ville }}
    + Pays : {{ site.pays }}
    + Fiche entreprise +
    + {% endfor %} + {% endif %} + + {% if correspondants_import %} +
    Importation de {{ correspondants_import|length }} correspondant(s)
    + {% for correspondant in correspondants_import %} +
    +
    + Civilité : {{ correspondant.civilite|get_civilité }}
    + Nom : {{ correspondant.nom }}
    + Prénom : {{ correspondant.prenom }}
    + {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
    + {% endif %} + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
    + {% endif %} + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
    + {% endif %} + {% if correspondant.service %} + Service : {{ correspondant.service }}
    + {% endif %} + {% if correspondant.origine %} + Origine : {{ correspondant.origine }}
    + {% endif %} + {% if correspondant.notes %} + Notes : {{ correspondant.notes }}
    + {% endif %} + Fiche entreprise +
    +
    + {% endfor %} + {% endif %} +
    {% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/logs.html b/app/templates/entreprises/logs.html index c6cba95f4..2072c4a84 100644 --- a/app/templates/entreprises/logs.html +++ b/app/templates/entreprises/logs.html @@ -2,6 +2,17 @@ {% extends 'base.html' %} {% block app_content %} + +

    Dernières opérations

    {% if logs.items %} diff --git a/app/templates/entreprises/logs_entreprise.html b/app/templates/entreprises/logs_entreprise.html index cebea0405..d54c4d0af 100644 --- a/app/templates/entreprises/logs_entreprise.html +++ b/app/templates/entreprises/logs_entreprise.html @@ -2,6 +2,20 @@ {% extends 'base.html' %} {% block app_content %} + +

    Dernières opérations - {{ entreprise.nom }}

    {% if logs.items %} diff --git a/app/templates/entreprises/nav.html b/app/templates/entreprises/nav.html index 3cd9636dc..3df51b87b 100644 --- a/app/templates/entreprises/nav.html +++ b/app/templates/entreprises/nav.html @@ -1,14 +1,16 @@ {# -*- mode: jinja-html -*- #} - \ No newline at end of file +
    + +
    \ No newline at end of file diff --git a/app/templates/entreprises/offres_expirees.html b/app/templates/entreprises/offres_expirees.html index d4bc2e415..f0bd4b2f6 100644 --- a/app/templates/entreprises/offres_expirees.html +++ b/app/templates/entreprises/offres_expirees.html @@ -2,6 +2,20 @@ {% extends 'base.html' %} {% block app_content %} + +

    Offres expirées - {{ entreprise.nom }}

    {% if offres_expirees %} diff --git a/app/templates/entreprises/preferences.html b/app/templates/entreprises/preferences.html index 625143728..39b45de8b 100644 --- a/app/templates/entreprises/preferences.html +++ b/app/templates/entreprises/preferences.html @@ -5,11 +5,13 @@ {% block app_content %} {% include 'entreprises/nav.html' %} -

    Préférences module gestion entreprises

    -
    -
    -
    - {{ wtf.quick_form(form, novalidate=True) }} +
    +

    Préférences module gestion entreprises

    +
    +
    +
    + {{ wtf.quick_form(form, novalidate=True) }} +
    {% endblock %} \ No newline at end of file From 48912032c497a6d80e60f55dd8ce6767f87c14ed Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 7 Jul 2022 11:56:18 +0200 Subject: [PATCH 20/25] =?UTF-8?q?Am=C3=A9lioration=20sco=5Fexcel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/entreprises/routes.py | 4 +- app/scodoc/sco_excel.py | 159 ++++++++++++++++---------------------- 2 files changed, 70 insertions(+), 93 deletions(-) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index ef15f32b8..6a831d866 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -1419,7 +1419,7 @@ def get_import_donnees_file_sample(): @permission_required(Permission.RelationsEntreprisesExport) def import_donnees(): """ - Permet d'importer des entreprises a l'aide d'un fichier excel (.xlsx) + Permet d'importer des entreprises à partir d'un fichier excel (.xlsx) """ form = ImportForm() if form.validate_on_submit(): @@ -1428,7 +1428,7 @@ def import_donnees(): Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename) ) file.save(file_path) - diag, lm = sco_excel.excel_file_to_list_are(file_path) + diag, lm = sco_excel.excel_workbook_to_list(file_path) os.remove(file_path) if lm is None or len(lm) < 2: flash("Veuillez utilisez la feuille excel à remplir") diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index b21652093..8e1ceabc5 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -40,10 +40,9 @@ from openpyxl.comments import Comment from openpyxl import Workbook, load_workbook from openpyxl.cell import WriteOnlyCell from openpyxl.styles import Font, Border, Side, Alignment, PatternFill +from openpyxl.worksheet.worksheet import Worksheet import app.scodoc.sco_utils as scu -from app.scodoc import notesdb -from app.scodoc import sco_preferences from app import log from app.scodoc.sco_exceptions import ScoValueError @@ -593,71 +592,87 @@ def excel_feuille_saisie(e, titreannee, description, lines): def excel_bytes_to_list(bytes_content): try: filelike = io.BytesIO(bytes_content) - return _excel_to_list(filelike) - except: + except Exception as exc: raise ScoValueError( """Le fichier xlsx attendu n'est pas lisible ! Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ..) """ - ) + ) from exc + return _excel_to_list(filelike) def excel_file_to_list(filename): try: return _excel_to_list(filename) - except: + except Exception as exc: raise ScoValueError( """Le fichier xlsx attendu n'est pas lisible ! Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) """ - ) + ) from exc def excel_file_to_list_are(filename): try: return _excel_to_list_are(filename) - except: + except Exception as exc: raise ScoValueError( """Le fichier xlsx attendu n'est pas lisible ! Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) """ - ) + ) from exc + + +def _open_workbook(filelike, dump_debug=False) -> Workbook: + """Open document. + On error, if dump-debug is True, dump data in /tmp for debugging purpose + """ + try: + workbook = load_workbook(filename=filelike, read_only=True, data_only=True) + except Exception as exc: + log("Excel_to_list: failure to import document") + if dump_debug: + dump_filename = "/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX + log(f"Dumping problemetic file on {dump_filename}") + with open(dump_filename, "wb") as f: + f.write(filelike) + raise ScoValueError( + "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel xlsx !" + ) from exc + return workbook def _excel_to_list(filelike): - """returns list of list - convert_to_string is a conversion function applied to all non-string values (ie numbers) - """ - try: - wb = load_workbook(filename=filelike, read_only=True, data_only=True) - except: - log("Excel_to_list: failure to import document") - with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f: - f.write(filelike) - raise ScoValueError( - "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !" - ) + """returns list of list""" + workbook = _open_workbook(filelike) diag = [] # liste de chaines pour former message d'erreur - # n'utilise que la première feuille - if len(wb.get_sheet_names()) < 1: + if len(workbook.get_sheet_names()) < 1: diag.append("Aucune feuille trouvée dans le classeur !") return diag, None - if len(wb.get_sheet_names()) > 1: + # n'utilise que la première feuille: + if len(workbook.get_sheet_names()) > 1: diag.append("Attention: n'utilise que la première feuille du classeur !") + sheet_name = workbook.get_sheet_names()[0] + ws = workbook[sheet_name] + matrix, diag_sheet = _excel_sheet_to_list(ws, sheet_name) + diag += diag_sheet + return diag, matrix + + +def _excel_sheet_to_list(sheet: Worksheet, sheet_name: str) -> tuple[list, list]: + """read a spreadsheet sheet, and returns: + - diag : a list of strings (error messages aimed at helping the user) + - a list of lists: the spreadsheet cells + """ + diag = [] # fill matrix - sheet_name = wb.get_sheet_names()[0] - ws = wb.get_sheet_by_name(sheet_name) - sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace") values = {} - for row in ws.iter_rows(): + for row in sheet.iter_rows(): for cell in row: if cell.value is not None: values[(cell.row - 1, cell.column - 1)] = str(cell.value) if not values: - diag.append( - "Aucune valeur trouvée dans la feuille %s !" - % sheet_name.decode(scu.SCO_ENCODING) - ) + diag.append(f"Aucune valeur trouvée dans la feuille {sheet_name} !") return diag, None indexes = list(values.keys()) # search numbers of rows and cols @@ -665,76 +680,38 @@ def _excel_to_list(filelike): cols = [x[1] for x in indexes] nbcols = max(cols) + 1 nbrows = max(rows) + 1 - m = [] + matrix = [] for _ in range(nbrows): - m.append([""] * nbcols) + matrix.append([""] * nbcols) for row_idx, col_idx in indexes: v = values[(row_idx, col_idx)] - # if isinstance(v, six.text_type): - # v = v.encode(scu.SCO_ENCODING, "backslashreplace") - # elif convert_to_string: - # v = convert_to_string(v) - m[row_idx][col_idx] = v - diag.append( - 'Feuille "%s", %d lignes' % (sheet_name.decode(scu.SCO_ENCODING), len(m)) - ) - # diag.append(str(M)) - # - return diag, m + matrix[row_idx][col_idx] = v + diag.append(f'Feuille "{sheet_name}", {len(matrix)} lignes') + + return diag, matrix -def _excel_to_list_are(filelike): - """returns list of list - convert_to_string is a conversion function applied to all non-string values (ie numbers) +def _excel_workbook_to_list(filelike): + """Lit un classeur (workbook): chaque feuille est lue + et est convertie en une liste de listes. + Returns: + - diag : a list of strings (error messages aimed at helping the user) + - a list of lists: the spreadsheet cells """ - try: - wb = load_workbook(filename=filelike, read_only=True, data_only=True) - except: - log("Excel_to_list: failure to import document") - with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f: - f.write(filelike) - raise ScoValueError( - "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !" - ) + workbook = _open_workbook(filelike) diag = [] # liste de chaines pour former message d'erreur - if len(wb.get_sheet_names()) < 1: + if len(workbook.get_sheet_names()) < 1: diag.append("Aucune feuille trouvée dans le classeur !") return diag, None - lm = [] - for sheet_name in wb.get_sheet_names(): + matrix_list = [] + for sheet_name in workbook.get_sheet_names(): # fill matrix - ws = wb.get_sheet_by_name(sheet_name) - sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace") - values = {} - for row in ws.iter_rows(): - for cell in row: - if cell.value is not None: - values[(cell.row - 1, cell.column - 1)] = str(cell.value) - if not values: - diag.append( - "Aucune valeur trouvée dans la feuille %s !" - % sheet_name.decode(scu.SCO_ENCODING) - ) - return diag, None - indexes = list(values.keys()) - # search numbers of rows and cols - rows = [x[0] for x in indexes] - cols = [x[1] for x in indexes] - nbcols = max(cols) + 1 - nbrows = max(rows) + 1 - m = [] - for _ in range(nbrows): - m.append([""] * nbcols) - - for row_idx, col_idx in indexes: - v = values[(row_idx, col_idx)] - m[row_idx][col_idx] = v - diag.append( - 'Feuille "%s", %d lignes' % (sheet_name.decode(scu.SCO_ENCODING), len(m)) - ) - lm.append(m) - return diag, lm + sheet = workbook.get_sheet_by_name(sheet_name) + matrix, diag_sheet = _excel_sheet_to_list(sheet, sheet_name) + diag += diag_sheet + matrix_list.append(matrix) + return diag, matrix_list def excel_feuille_listeappel( From d8ff5152d1127426606af348f5c8cf2da9261517 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 7 Jul 2022 11:59:47 +0200 Subject: [PATCH 21/25] =?UTF-8?q?Am=C3=A9lioration=20sco=5Fexcel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_excel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 8e1ceabc5..9233b5e52 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -612,9 +612,9 @@ def excel_file_to_list(filename): ) from exc -def excel_file_to_list_are(filename): +def excel_workbook_to_list(filename): try: - return _excel_to_list_are(filename) + return _excel_workbook_to_list(filename) except Exception as exc: raise ScoValueError( """Le fichier xlsx attendu n'est pas lisible ! From f2f3fd066050e94acb87234da9024699128fdb40 Mon Sep 17 00:00:00 2001 From: Arthur ZHU Date: Thu, 7 Jul 2022 15:30:50 +0200 Subject: [PATCH 22/25] correction sco_excel --- app/scodoc/sco_excel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 9233b5e52..2c95c7ef4 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -654,7 +654,7 @@ def _excel_to_list(filelike): diag.append("Attention: n'utilise que la première feuille du classeur !") sheet_name = workbook.get_sheet_names()[0] ws = workbook[sheet_name] - matrix, diag_sheet = _excel_sheet_to_list(ws, sheet_name) + diag_sheet, matrix = _excel_sheet_to_list(ws, sheet_name) diag += diag_sheet return diag, matrix @@ -708,7 +708,7 @@ def _excel_workbook_to_list(filelike): for sheet_name in workbook.get_sheet_names(): # fill matrix sheet = workbook.get_sheet_by_name(sheet_name) - matrix, diag_sheet = _excel_sheet_to_list(sheet, sheet_name) + diag_sheet, matrix = _excel_sheet_to_list(sheet, sheet_name) diag += diag_sheet matrix_list.append(matrix) return diag, matrix_list From 75923c161acacb1dd5dca8070db354b0ac55eae0 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 7 Jul 2022 16:39:27 +0200 Subject: [PATCH 23/25] black migration + version 9.3.16 --- ...7_ajout_taxe_apprentissage_association_.py | 80 ++++++++++++------- sco_version.py | 2 +- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py b/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py index c8635598e..46b7bee63 100644 --- a/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py +++ b/migrations/versions/0b337376e9f7_ajout_taxe_apprentissage_association_.py @@ -10,49 +10,67 @@ import sqlalchemy as sa from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = '0b337376e9f7' -down_revision = 'ee21c76c8183' +revision = "0b337376e9f7" +down_revision = "ee21c76c8183" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('are_historique', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('authenticated_user', sa.Text(), nullable=True), - sa.Column('entreprise_id', sa.Integer(), nullable=True), - sa.Column('object', sa.Text(), nullable=True), - sa.Column('object_id', sa.Integer(), nullable=True), - sa.Column('text', sa.Text(), nullable=True), - sa.PrimaryKeyConstraint('id') + op.create_table( + "are_historique", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column( + "date", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=True, + ), + sa.Column("authenticated_user", sa.Text(), nullable=True), + sa.Column("entreprise_id", sa.Integer(), nullable=True), + sa.Column("object", sa.Text(), nullable=True), + sa.Column("object_id", sa.Integer(), nullable=True), + sa.Column("text", sa.Text(), nullable=True), + sa.PrimaryKeyConstraint("id"), ) - op.create_table('are_taxe_apprentissage', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('entreprise_id', sa.Integer(), nullable=True), - sa.Column('annee', sa.Integer(), nullable=True), - sa.Column('montant', sa.Integer(), nullable=True), - sa.Column('notes', sa.Text(), nullable=True), - sa.ForeignKeyConstraint(['entreprise_id'], ['are_entreprises.id'], ondelete='cascade'), - sa.PrimaryKeyConstraint('id') + op.create_table( + "are_taxe_apprentissage", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("entreprise_id", sa.Integer(), nullable=True), + sa.Column("annee", sa.Integer(), nullable=True), + sa.Column("montant", sa.Integer(), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.ForeignKeyConstraint( + ["entreprise_id"], ["are_entreprises.id"], ondelete="cascade" + ), + sa.PrimaryKeyConstraint("id"), + ) + op.drop_table("are_logs") + op.add_column( + "are_entreprises", sa.Column("association", sa.Boolean(), nullable=True) ) - op.drop_table('are_logs') - op.add_column('are_entreprises', sa.Column('association', sa.Boolean(), nullable=True)) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('are_entreprises', 'association') - op.create_table('are_logs', - sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), - sa.Column('date', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True), - sa.Column('authenticated_user', sa.TEXT(), autoincrement=False, nullable=True), - sa.Column('object', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('text', sa.TEXT(), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name='are_logs_pkey') + op.drop_column("are_entreprises", "association") + op.create_table( + "are_logs", + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "date", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=True, + ), + sa.Column("authenticated_user", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("object", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("text", sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("id", name="are_logs_pkey"), ) - op.drop_table('are_taxe_apprentissage') - op.drop_table('are_historique') + op.drop_table("are_taxe_apprentissage") + op.drop_table("are_historique") # ### end Alembic commands ### diff --git a/sco_version.py b/sco_version.py index ed3c66a32..256794b73 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.3.15" +SCOVERSION = "9.3.16" SCONAME = "ScoDoc" From 07cd5041ff35cbf5f1652dad569bfb7e9b6b4e44 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 7 Jul 2022 17:21:23 +0200 Subject: [PATCH 24/25] gitignore symlinks --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 87df45de6..b60367ec8 100644 --- a/.gitignore +++ b/.gitignore @@ -173,3 +173,6 @@ Thumbs.db .idea/ copy + +# Symlinks static ScoDoc +app/static/links/[0-9]*.*[0-9] From 5806cd29a74796b4f482340350cd5d48baf2ce9f Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 7 Jul 2022 22:29:27 +0200 Subject: [PATCH 25/25] Fix: API /formsemestres_courants --- app/api/departements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/departements.py b/app/api/departements.py index fca5d6a82..853dc7120 100644 --- a/app/api/departements.py +++ b/app/api/departements.py @@ -153,4 +153,4 @@ def liste_semestres_courant(dept_ident: str): FormSemestre.date_fin >= app.db.func.now(), ) - return jsonify([d.to_dict() for d in formsemestres]) + return jsonify([d.to_dict(convert_parcours=True) for d in formsemestres])