Compare commits

...

35 Commits

Author SHA1 Message Date
leonard_montalbano
0db3acf271 fix des diff avec le master 2022-07-08 15:11:45 +02:00
leonard_montalbano
8b3c0ea414 Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into new_api 2022-07-08 15:06:04 +02:00
leonard_montalbano
e822ab4da0 Merge branch 'new_api' of https://scodoc.org/git/leonard.montalbano/ScoDoc into new_api 2022-07-08 14:28:34 +02:00
5806cd29a7 Fix: API /formsemestres_courants 2022-07-07 22:29:27 +02:00
07cd5041ff gitignore symlinks 2022-07-07 17:21:23 +02:00
11f6492965 Merge branch 'entreprises' of https://scodoc.org/git/viennet/ScoDoc 2022-07-07 16:40:22 +02:00
75923c161a black migration + version 9.3.16 2022-07-07 16:39:27 +02:00
f2f3fd0660 correction sco_excel 2022-07-07 15:30:50 +02:00
0140c404ea Merge branch 'entreprises' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises 2022-07-07 14:46:41 +02:00
d8ff5152d1 Amélioration sco_excel 2022-07-07 11:59:47 +02:00
48912032c4 Amélioration sco_excel 2022-07-07 11:56:18 +02:00
ec987ad8fa ajout file d'ariane 2022-07-06 20:37:20 +02:00
17b73936de Merge branch 'entreprises' of https://scodoc.org/git/arthur.zhu/ScoDoc into entreprises 2022-07-06 17:35:42 +02:00
9f7d6a3020 lié migrations appli relations entreprises 2022-07-04 21:12:56 +02:00
9d26c3126a Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises 2022-07-04 20:45:58 +02:00
leonard_montalbano
bab08c8cd8 Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into new_api 2022-06-29 11:22:39 +02:00
3b7ac4eec4 corrections import données 2022-06-10 18:10:41 +02:00
f0edcb28f7 Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises 2022-06-10 17:58:07 +02:00
5b762cd911 import données en dict (plus lisible) 2022-06-10 17:55:59 +02:00
91d5fb906a amélioration erreurs formulaire dynamique 2022-06-10 15:54:33 +02:00
e2c5a19655 amélioration formulaires ajout correspondants 2022-06-09 20:52:50 +02:00
434e571a98 import données modifications correspondants 2022-06-09 17:28:05 +02:00
835d1d38ed import données sites + correspondants 2022-06-08 21:07:04 +02:00
1ba5954762 import feuille entreprises 2022-06-03 18:12:28 +02:00
74cd1400d8 suite import de données 2022-06-02 21:49:37 +02:00
9e751722e6 import export données application relations entreprises 2022-06-01 21:17:15 +02:00
e96d714545 correction suite aux changements, suite import export 2022-05-31 19:15:24 +02:00
8e95996930 lien vers fiche etudiant (stage ou alternance), refonte import export entreprises 2022-05-30 18:12:26 +02:00
259b2a449d taxe apprentissage, changement historique, association 2022-05-06 18:50:56 +02:00
e34ae9b3ec changement historique (log) 2022-05-06 18:50:12 +02:00
48c16d761f suite taxe apprentissage, association partenaire 2022-05-06 01:33:50 +02:00
0485c8769d ajout taxe apprentissage 2022-05-04 18:59:29 +02:00
54a71b513a Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises 2022-05-04 00:08:34 +02:00
a5cec8e068 affichage checkbox entreprises désactivés, activer 2022-05-02 14:36:39 +02:00
c0363f6bd1 ajout formulaires 2022-04-28 23:04:31 +02:00
30 changed files with 1589 additions and 665 deletions

3
.gitignore vendored
View File

@ -173,3 +173,6 @@ Thumbs.db
.idea/
copy
# Symlinks static ScoDoc
app/static/links/[0-9]*.*[0-9]

View File

@ -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])

View File

@ -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

View File

@ -30,6 +30,7 @@ import re
import requests
import glob
from flask import flash
from flask_login import current_user
from app.entreprises.models import (
@ -38,14 +39,61 @@ from app.entreprises.models import (
EntrepriseOffre,
EntrepriseOffreDepartement,
EntreprisePreferences,
EntrepriseSite,
EntrepriseHistorique,
)
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
ENTREPRISES_KEYS = [
"siret",
"nom_entreprise",
"adresse",
"ville",
"code_postal",
"pays",
]
SITES_KEYS = [
[
"siret_entreprise",
"id_site",
"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
@ -58,7 +106,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
"""
@ -68,7 +116,14 @@ def get_dept_id_by_acronym(acronym):
return None
def check_offre_depts(depts, offre_depts):
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: list, offre_depts: list):
"""
Retourne vrai si l'utilisateur a le droit de visibilité sur l'offre
"""
@ -107,7 +162,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:",
@ -128,69 +183,168 @@ def send_email_notifications_entreprise(subject, entreprise: Entreprise):
return txt
def verif_correspondant_data(correspondant_data):
def get_excel_book_are(export: bool = False):
"""
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
Retourne un Excel avec les 3 feuilles "Entreprises", "Sites" et "Correspondants"
"""
# champs obligatoires
if (
correspondant_data[0].strip() == ""
or correspondant_data[1].strip() == ""
or correspondant_data[2].strip() == ""
or correspondant_data[9].strip() == ""
):
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 check_entreprises_import(m):
"""
Verifie la feuille Excel "Entreprises" de l'importation données
"""
entreprises_import = []
siret_list = []
ligne = 1
if m[0] != ENTREPRISES_KEYS:
flash(
f'Veuillez utilisez la feuille excel à remplir (Feuille "Entreprises", ligne {ligne})'
)
return False
l = list_to_dict(m)
for entreprise_data in l:
ligne += 1
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=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["nom_entreprise"]
entreprise.adresse = entreprise_data["adresse"]
entreprise.ville = entreprise_data["ville"]
entreprise.codepostal = entreprise_data["code_postal"]
entreprise.pays = (
entreprise_data["pays"] if entreprise_data["pays"] else "FRANCE"
)
else:
flash(
f'Erreur lors de l\'importation (Feuille "Entreprises", ligne {ligne})'
)
return False
# civilite entre H ou F
if correspondant_data[0].strip() not in ["H", "F"]:
return False
if len(entreprises_import) > 0:
log = EntrepriseHistorique(
authenticated_user=current_user.user_name,
text=f"Importation de {len(entreprises_import)} entreprise(s)",
)
db.session.add(log)
# 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
return entreprises_import
def verif_entreprise_data(entreprise_data):
def check_entreprise_import(entreprise_data):
"""
Verifie les données d'une ligne Excel (entreprise)
"""
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["siret"]
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:
@ -201,7 +355,210 @@ 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 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
l = list_to_dict(m)
for site_data in l:
ligne += 1
site_data["siret_entreprise"] = site_data["siret_entreprise"].replace(" ", "")
if check_site_import(site_data):
entreprise = Entreprise.query.filter_by(
siret=site_data["siret_entreprise"], visible=True
).first()
if site_data["id_site"] == "":
site_import = EntrepriseSite(
entreprise_id=entreprise.id,
nom=site_data["nom_site"],
adresse=site_data["adresse"],
codepostal=site_data["code_postal"],
ville=site_data["ville"],
pays=site_data["pays"],
)
if site_data["civilite"] == "":
sites_import.append(site_import)
else:
correspondant_import = EntrepriseCorrespondant(
entreprise_id=entreprise.id,
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 = 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 site_data["civilite"] != "":
correspondant_import = EntrepriseCorrespondant(
entreprise_id=entreprise.id,
site_id=site.id,
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:
flash(f'Erreur lors de l\'importation (Feuille "Sites", ligne {ligne})')
return False, False
if len(sites_import) > 0:
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 = EntrepriseHistorique(
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(site_data):
"""
Verifie les données d'une ligne Excel (sites)
"""
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 site_data["civilite"] != "":
if check_correspondant_import(site_data) is False:
return False
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["nom_site"]).first()
if site_data["id_site"] == "" and site is not None:
return False
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
l = list_to_dict(m)
for correspondant_data in l:
ligne += 1
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["id"]
).first()
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})'
)
return False
return True
def check_correspondant_import(correspondant_data):
"""
Verifie les données d'une ligne Excel (correspondant)
"""
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["civilite"] not in ["H", "F"]:
return False
if (
correspondant_data["telephone"] == "" and correspondant_data["mail"] == ""
): # 1 moyen de contact
return False
if "siret_entreprise" in correspondant_data:
# entreprise_id existant
entreprise = Entreprise.query.filter_by(
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"],
prenom=correspondant_data["prenom"],
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

View File

@ -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,
@ -42,14 +44,23 @@ from wtforms import (
BooleanField,
FieldList,
FormField,
BooleanField,
)
from wtforms.validators import (
ValidationError,
DataRequired,
Email,
Optional,
NumberRange,
)
from wtforms.validators import ValidationError, DataRequired, Email, Optional
from wtforms.widgets import ListWidget, CheckboxInput
from app.entreprises.models import (
Entreprise,
EntrepriseCorrespondant,
EntreprisePreferences,
EntrepriseSite,
EntrepriseTaxeApprentissage,
)
from app.models import Identite, Departement
from app.auth.models import User
@ -69,11 +80,17 @@ def _build_string_field(label, required=True, render_kw=None):
return StringField(label, validators=[Optional()], render_kw=render_kw)
class EntreprisesFilterForm(FlaskForm):
active = BooleanField("Toutes les entreprises")
association = BooleanField("Seulement les associations partenaires")
class EntrepriseCreationForm(FlaskForm):
siret = _build_string_field(
"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 (*)")
@ -153,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 (*)")
@ -164,13 +181,11 @@ 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):
hidden_entreprise_id = HiddenField()
nom = _build_string_field("Nom du site (*)")
adresse = _build_string_field("Adresse (*)")
codepostal = _build_string_field("Code postal (*)")
@ -178,6 +193,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)
@ -214,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
@ -260,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
@ -290,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"}
)
@ -321,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
@ -558,9 +623,75 @@ 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(
min=1900,
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 TaxeApprentissageModificationForm(FlaskForm):
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(
StringField(
"Responsable (*)",
render_kw={
"placeholder": "Tapez le nom du responsable de formation",
@ -573,6 +704,8 @@ class EnvoiOffreForm(FlaskForm):
def validate(self):
validate = True
list_select = True
if not FlaskForm.validate(self):
validate = False
@ -588,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
@ -611,7 +748,11 @@ 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):
@ -633,6 +774,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)

View File

@ -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)
@ -36,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,
}
@ -58,6 +63,18 @@ 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,
"id_site": self.id,
"nom_site": self.nom,
"adresse": self.adresse,
"code_postal": self.codepostal,
"ville": self.ville,
"pays": self.pays,
}
class EntrepriseCorrespondant(db.Model):
__tablename__ = "are_correspondants"
@ -77,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,
@ -88,7 +106,7 @@ class EntrepriseCorrespondant(db.Model):
"service": self.service,
"origine": self.origine,
"notes": self.notes,
"entreprise_siret": entreprise.siret,
"nom_site": site.nom,
}
@ -131,12 +149,14 @@ 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)
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)
@ -155,6 +175,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)

View File

@ -12,11 +12,14 @@ 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,
OffreCreationForm,
OffreModificationForm,
@ -27,6 +30,8 @@ from app.entreprises.forms import (
StageApprentissageModificationForm,
EnvoiOffreForm,
AjoutFichierForm,
TaxeApprentissageForm,
TaxeApprentissageModificationForm,
ValidationConfirmationForm,
ImportForm,
PreferencesForm,
@ -36,13 +41,14 @@ from app.entreprises.models import (
Entreprise,
EntrepriseOffre,
EntrepriseCorrespondant,
EntrepriseLog,
EntrepriseHistorique,
EntrepriseContact,
EntrepriseSite,
EntrepriseStageApprentissage,
EntrepriseEnvoiOffre,
EntrepriseOffreDepartement,
EntreprisePreferences,
EntrepriseTaxeApprentissage,
)
from app.entreprises import app_relations_entreprises as are
from app.models import Identite
@ -52,18 +58,40 @@ 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
@bp.route("/", methods=["GET"])
@bp.route("/", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesView)
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]
if request.method == "POST":
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",
entreprises=entreprises,
logs=logs,
form=form,
checked=checked,
)
return render_template(
"entreprises/entreprises.html",
title="Entreprises",
@ -79,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",
@ -110,11 +138,17 @@ 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)
.all()
)
logs = (
EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc())
.limit(LOGS_LEN)
.all()
)
logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all()
return render_template(
"entreprises/correspondants.html",
title="Correspondants",
@ -132,9 +166,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:
@ -150,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_by(object=id)
EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc())
.filter(EntrepriseHistorique.entreprise_id == id)
.limit(LOGS_LEN)
.all()
)
@ -162,6 +196,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",
@ -170,6 +209,7 @@ def fiche_entreprise(id):
offres=offres_with_files,
logs=logs,
stages_apprentissages=stages_apprentissages,
taxes=taxes,
)
@ -184,8 +224,8 @@ def logs_entreprise(id):
description=f"logs fiche entreprise {id} inconnu"
)
logs = (
EntrepriseLog.query.order_by(EntrepriseLog.date.desc())
.filter_by(object=id)
EntrepriseHistorique.query.order_by(EntrepriseHistorique.date.desc())
.filter(EntrepriseHistorique.entreprise_id == entreprise.id)
.paginate(page=page, per_page=20)
)
return render_template(
@ -205,12 +245,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,
)
@ -290,6 +330,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(),
@ -327,9 +368,10 @@ def add_entreprise():
if current_user.has_permission(Permission.RelationsEntreprisesValidate, None):
entreprise.visible = True
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
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,
)
db.session.add(log)
db.session.commit()
@ -360,51 +402,52 @@ 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"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{form.nom.data.strip()}</a>"
if entreprise.nom != form.nom.data.strip():
log = EntrepriseLog(
log = EntrepriseHistorique(
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()
db.session.add(log)
if entreprise.adresse != form.adresse.data.strip():
log = EntrepriseLog(
log = EntrepriseHistorique(
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()
db.session.add(log)
if entreprise.codepostal != form.codepostal.data.strip():
log = EntrepriseLog(
log = EntrepriseHistorique(
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()
db.session.add(log)
if entreprise.ville != form.ville.data.strip():
log = EntrepriseLog(
log = EntrepriseHistorique(
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()
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,
object=entreprise.id,
entreprise_id=entreprise.id,
text=f"{nom_entreprise} - Modification du pays (ancien pays: {entreprise.pays})",
)
entreprise.pays = (
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))
@ -415,6 +458,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",
@ -428,21 +472,128 @@ 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/<int:id>", 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",
)
@bp.route("/fiche_entreprise/<int:id>/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/<int:id_entreprise>/edit_taxe_apprentissage/<int:id_taxe>",
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/<int:id_entreprise>/delete_taxe_apprentissage/<int:id_taxe>",
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",
)
@ -461,9 +612,10 @@ def validate_entreprise(id):
if form.validate_on_submit():
entreprise.visible = True
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
log = EntrepriseLog(
log = EntrepriseHistorique(
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()
@ -521,7 +673,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()
@ -545,9 +699,11 @@ 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,
object=entreprise.id,
entreprise_id=entreprise.id,
object="offre",
object_id=offre.id,
text="Création d'une offre",
)
db.session.add(log)
@ -582,7 +738,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:
@ -597,9 +756,11 @@ 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,
object=offre.entreprise_id,
entreprise_id=offre.entreprise_id,
object="offre",
object_id=offre.id,
text="Modification d'une offre",
)
db.session.add(log)
@ -642,9 +803,11 @@ def delete_offre(id):
)
if os.path.isdir(path):
shutil.rmtree(path)
log = EntrepriseLog(
log = EntrepriseHistorique(
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)
@ -700,7 +863,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 +884,41 @@ def add_site(id):
)
@bp.route(
"/fiche_entreprise/<int:id_entreprise>/edit_site/<int:id_site>",
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=entreprise.id
).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/<int:id_entreprise>/add_correspondant/<int:id_site>",
methods=["GET", "POST"],
@ -752,13 +950,17 @@ def add_correspondant(id_entreprise, id_site):
origine=correspondant_entry.origine.data.strip(),
notes=correspondant_entry.notes.data.strip(),
)
log = EntrepriseLog(
db.session.add(correspondant)
db.session.commit()
db.session.refresh(correspondant)
log = EntrepriseHistorique(
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))
@ -775,8 +977,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,
@ -792,9 +997,11 @@ 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,
object=correspondant.entreprise_id,
entreprise_id=correspondant.entreprise_id,
object="correspondant",
object_id=correspondant.id,
text="Modification d'un correspondant",
)
db.session.add(log)
@ -826,15 +1033,20 @@ 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():
db.session.delete(correspondant)
log = EntrepriseLog(
log = EntrepriseHistorique(
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)
@ -937,7 +1149,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",
@ -1125,7 +1340,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 = {}
@ -1134,10 +1349,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)
@ -1151,68 +1370,56 @@ 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:
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)
@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)
"""
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 = "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
"""
keys = [
"siret",
"nom_entreprise",
"adresse",
"ville",
"code_postal",
"pays",
]
titles = keys[:]
title = "ImportEntreprises"
xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Entreprises")
filename = title
wb = are.get_excel_book_are()
xlsx = wb.generate()
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)
Permet d'importer des entreprises à partir d'un fichier excel (.xlsx)
"""
form = ImportForm()
if form.validate_on_submit():
@ -1221,237 +1428,58 @@ def import_entreprises():
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_workbook_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:
if lm is None or len(lm) < 2:
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)
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
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:
db.session.add(entreprise)
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,
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,
)
else:
flash('Feuille "Entreprises" vide')
return render_template(
"entreprises/import_entreprises.html",
title="Importation entreprises",
form=form,
)
@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)
.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
"""
keys = [
"civilite",
"nom",
"prenom",
"telephone",
"mail",
"poste",
"service",
"origine",
"notes",
"entreprise_siret",
]
titles = keys[:]
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(site)
for site in sites_import:
db.session.add(site)
correspondants = []
for site, correspondant in correspondants_import:
if site is None:
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')
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_correspondants.html",
title="Importation correspondants",
form=form,
"entreprises/import_donnees.html", title="Importation données", form=form
)

View File

@ -132,13 +132,10 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
if sco_preferences.get_preference(
"bul_show_minmax_mod", formsemestre_id
):
rang_minmax = (
'%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>'
% (
mod["mod_rang_txt"],
scu.fmt_note(mod["stats"]["min"]),
scu.fmt_note(mod["stats"]["max"]),
)
rang_minmax = '%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>' % (
mod["mod_rang_txt"],
scu.fmt_note(mod["stats"]["min"]),
scu.fmt_note(mod["stats"]["max"]),
)
else:
rang_minmax = mod["mod_rang_txt"] # vide si pas option rang

View File

@ -75,7 +75,7 @@ import sco_version
def assemble_bulletins_pdf(
formsemestre_id: int,
story,
story: list,
bul_title: str,
infos,
pagesbookmarks=None,
@ -107,7 +107,7 @@ def assemble_bulletins_pdf(
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
)
document.build(story)
document.multiBuild(story)
data = report.getvalue()
return data

View File

@ -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,60 +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_workbook_to_list(filename):
try:
return _excel_workbook_to_list(filename)
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]
diag_sheet, matrix = _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
@ -654,23 +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_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
"""
workbook = _open_workbook(filelike)
diag = [] # liste de chaines pour former message d'erreur
if len(workbook.get_sheet_names()) < 1:
diag.append("Aucune feuille trouvée dans le classeur !")
return diag, None
matrix_list = []
for sheet_name in workbook.get_sheet_names():
# fill matrix
sheet = workbook.get_sheet_by_name(sheet_name)
diag_sheet, matrix = _excel_sheet_to_list(sheet, sheet_name)
diag += diag_sheet
matrix_list.append(matrix)
return diag, matrix_list
def excel_feuille_listeappel(

View File

@ -121,8 +121,7 @@ def _list_dept_logos(dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
:return: le résultat de la recherche ou None si aucune image trouvée
"""
allowed_ext = "|".join(scu.LOGOS_IMAGES_ALLOWED_TYPES)
# parse filename 'logo_<logoname>.<ext> . be carefull: logoname may include '.'
filename_parser = re.compile(f"{prefix}(([^.]*.)+)({allowed_ext})")
filename_parser = re.compile(f"{prefix}([^.]*).({allowed_ext})")
logos = {}
path_dir = Path(scu.SCODOC_LOGOS_DIR)
if dept_id:
@ -136,7 +135,7 @@ def _list_dept_logos(dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
if os.access(path_dir.joinpath(entry).absolute(), os.R_OK):
result = filename_parser.match(entry.name)
if result:
logoname = result.group(1)[:-1] # retreive logoname from filename (less final dot)
logoname = result.group(1)
logos[logoname] = Logo(logoname=logoname, dept_id=dept_id).select()
return logos if len(logos.keys()) > 0 else None
@ -192,9 +191,6 @@ class Logo:
)
self.mm = "Not initialized: call the select or create function before access"
def __repr__(self) -> str:
return f"Logo(logoname='{self.logoname}', filename='{self.filename}')"
def _set_format(self, fmt):
self.suffix = fmt
self.filepath = self.basepath + "." + fmt

View File

@ -303,12 +303,9 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
)
for mod in options:
if can_change:
c_link = (
'<a class="discretelink" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">%s</a>'
% (
mod["moduleimpl_id"],
mod["descri"] or "<i>(inscrire des étudiants)</i>",
)
c_link = '<a class="discretelink" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">%s</a>' % (
mod["moduleimpl_id"],
mod["descri"] or "<i>(inscrire des étudiants)</i>",
)
else:
c_link = mod["descri"]

View File

@ -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;
@ -59,14 +68,33 @@
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;
}
.entreprise > div {
flex: 1 0 0;
}
.taxe-apprentissage{
overflow-y: scroll;
height: 100px;
}
.sites-et-offres {
display: flex;
justify-content: space-between;
@ -101,4 +129,47 @@
border: solid 2px;
border-radius: 10px;
padding: 10px;
}
#liste-taxes-apprentissages {
list-style: none;
padding-left: 0;
}
#form-entreprise-filter > label {
margin-right: 20px;
}
.title-form-error {
font-weight: bold;
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;
}

View File

@ -16,7 +16,11 @@
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.correspondants.label }}
{% for subfield in form.correspondants %}
{% if subfield.errors %}
<p class="title-form-error">Formulaire {{ subfield.label.text }}</p>
{% endif %}
{% for subsubfield in subfield %}
{% if subsubfield.errors %}
{% for error in subsubfield.errors %}
@ -56,7 +60,22 @@
}
let newFieldName = `correspondants-${Math.max(...correspondantInputIds) + 1}`;
allCorrepondantsFieldWrapper.insertAdjacentHTML('beforeend',`
<li><label for="${newFieldName}">Correspondants-${Math.max(...correspondantInputIds) + 1}</label><table id="${newFieldName}"><tr><th><label for="${newFieldName}-civilite">Civilité (*)</label></th><td><select class="form-control" id="${newFieldName}-civilite" name="${newFieldName}-civilite" required><option value="H">Monsieur</option><option value="F">Madame</option></select></td></tr><tr><th><label for="${newFieldName}-nom">Nom (*)</label></th><td><input class="form-control" id="${newFieldName}-nom" name="${newFieldName}-nom" required type="text" value=""></td></tr><tr><th><label for="${newFieldName}-prenom">Prénom (*)</label></th><td><input class="form-control" id="${newFieldName}-prenom" name="${newFieldName}-prenom" required type="text" value=""></td></tr><tr><th><label for="${newFieldName}-telephone">Téléphone (*)</label></th><td><input class="form-control" id="${newFieldName}-telephone" name="${newFieldName}-telephone" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-mail">Mail (*)</label></th><td><input class="form-control" id="${newFieldName}-mail" name="${newFieldName}-mail" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-poste">Poste</label></th><td><input class="form-control" id="${newFieldName}-poste" name="${newFieldName}-poste" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-service">Service</label></th><td><input class="form-control" id="${newFieldName}-service" name="${newFieldName}-service" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-origine">Origine</label></th><td><input class="form-control" id="${newFieldName}-origine" name="${newFieldName}-origine" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-notes">Notes</label></th><td><input class="form-control" id="${newFieldName}-notes" name="${newFieldName}-notes" type="text" value=""></td></tr></table><input id="${newFieldName}-csrf_token" name="${newFieldName}-csrf_token" type="hidden" value=${csrf_token}><div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer ce correspondant</div></li>
<li>
<label for="${newFieldName}">Correspondants-${Math.max(...correspondantInputIds) + 1}</label>
<table id="${newFieldName}">
<tr><th><label for="${newFieldName}-civilite">Civilité (*)</label></th><td><select class="form-control" id="${newFieldName}-civilite" name="${newFieldName}-civilite" required><option value="H">Monsieur</option><option value="F">Madame</option></select></td></tr>
<tr><th><label for="${newFieldName}-nom">Nom (*)</label></th><td><input class="form-control" id="${newFieldName}-nom" name="${newFieldName}-nom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-prenom">Prénom (*)</label></th><td><input class="form-control" id="${newFieldName}-prenom" name="${newFieldName}-prenom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-telephone">Téléphone (*)</label></th><td><input class="form-control" id="${newFieldName}-telephone" name="${newFieldName}-telephone" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-mail">Mail (*)</label></th><td><input class="form-control" id="${newFieldName}-mail" name="${newFieldName}-mail" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-poste">Poste</label></th><td><input class="form-control" id="${newFieldName}-poste" name="${newFieldName}-poste" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-service">Service</label></th><td><input class="form-control" id="${newFieldName}-service" name="${newFieldName}-service" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-origine">Origine</label></th><td><input class="form-control" id="${newFieldName}-origine" name="${newFieldName}-origine" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-notes">Notes</label></th><td><input class="form-control" id="${newFieldName}-notes" name="${newFieldName}-notes" type="text" value=""></td></tr>
</table>
<input id="${newFieldName}-csrf_token" name="${newFieldName}-csrf_token" type="hidden" value=${csrf_token}>
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer ce correspondant</div>
</li>
`);
});
}

View File

@ -5,7 +5,7 @@
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div style="color:red;">{{ info_message }}</div>
<div>{{ info_message }}</div>
<br>
<div class="row">
<div class="col-md-4">

View File

@ -9,7 +9,21 @@
{% endblock %}
{% block app_content %}
<div class="container" style="margin-bottom: 10px;">
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise_id) }}" class="breadcrumbs_link">Fiche entreprise</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Contacts</a>
</li>
</ul>
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des contacts</h1>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" style="margin-bottom:10px;" href="{{ url_for('entreprises.add_contact', id=entreprise_id) }}">Ajouter contact</a>

View File

@ -22,15 +22,6 @@
</div>
{% endif %}
<div class="container boutons">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_correspondants') }}">Importer des correspondants</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and correspondants %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_correspondants') }}">Exporter la liste des correspondants</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des correspondants</h1>
<table id="table-correspondants">
@ -54,7 +45,7 @@
<td>{{ correspondant[0].mail }}</td>
<td>{{ correspondant[0].poste}}</td>
<td>{{ correspondant[0].service}}</td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant[1].id) }}">{{ correspondant[1].nom }}</a></td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant[1].entreprise_id) }}">{{ correspondant[1].nom }}</a></td>
</tr>
{% endfor %}
</tbody>

View File

@ -27,15 +27,22 @@
<a class="btn btn-default" href="{{ url_for('entreprises.add_entreprise') }}">Ajouter une entreprise</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_entreprises') }}">Importer des entreprises</a>
<a class="btn btn-default" href="{{ url_for('entreprises.import_donnees') }}">Importer des données</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and entreprises %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_entreprises') }}">Exporter la liste des entreprises</a>
<a class="btn btn-default" href="{{ url_for('entreprises.export_donnees') }}">Exporter des données</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des entreprises</h1>
{% if form %}
<form id="form-entreprise-filter" method="POST" action="">
{{ form.hidden_tag() }}
<input id="active" name="active" type="checkbox" onChange="form.submit()" {% if checked[0] %} checked {% endif %}> {{ form.active.label }}
<input id="association" name="association" type="checkbox" onChange="form.submit()" {% if checked[1] %} checked {% endif %}> {{ form.association.label }}
</form>
{% endif %}
<table id="table-entreprises">
<thead>
<tr>
@ -53,7 +60,7 @@
<tbody>
{% for entreprise in entreprises %}
<tr>
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}">{{ entreprise.siret }}</a></td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}" {% if not entreprise.active %} style="color:red" {% endif %}>{{ entreprise.siret }}</a></td>
<td>{{ entreprise.nom }}</td>
<td>{{ entreprise.adresse }}</td>
<td>{{ entreprise.codepostal }}</td>
@ -67,7 +74,11 @@
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_entreprise', id=entreprise.id) }}">Modifier</a></li>
{% if entreprise.active %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_desactiver', id=entreprise.id)}}" style="color:red">Désactiver</a></li>
{% else %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_activer', id=entreprise.id)}}" style="color:lightgreen">Activer</a></li>
{% endif %}
</ul>
</div>
</td>

View File

@ -18,13 +18,9 @@
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.responsables.label }}<br>
{% for subfield in form.responsables %}
{% if subfield.errors %}
{% for error in subfield.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{% endif %}
{{ form.responsables.label }}
{% for error in form.responsables.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{{ form.responsables }}
<div style="margin-bottom: 10px;">
@ -71,7 +67,11 @@
}
let newFieldName = `responsables-${Math.max(...responsableInputIds) + 1}`;
allResponsablesFieldWrapper.insertAdjacentHTML('beforeend',`
<li><label for="${newFieldName}">Responsable (*)</label><input class="form-control" id="${newFieldName}" name="${newFieldName}" type="text" value="" placeholder="Tapez le nom du responsable de formation"><div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer</div></li>
<li>
<label for="${newFieldName}">Responsable (*)</label>
<input class="form-control" id="${newFieldName}" name="${newFieldName}" type="text" value="" placeholder="Tapez le nom du responsable de formation">
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer</div>
</li>
`);
var as_r = new bsn.AutoSuggest(newFieldName, responsables_options);
});

View File

@ -9,6 +9,17 @@
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Fiche entreprise</a>
</li>
</ul>
</div>
{% if logs %}
<div class="container">
<h3>Dernières opérations sur cette fiche <a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id) }}">Voir tout</a></h3>
@ -26,6 +37,15 @@
<div class="container fiche-entreprise">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
{% if not entreprise.active %}
<div class="info-active">
La fiche entreprise est désactivée<br>
{% if entreprise.notes_active != "" %}
Notes : {{ entreprise.notes_active }}
{% endif %}
</div>
{% endif %}
<div class="entreprise">
<div>
SIRET : {{ entreprise.siret }}<br>
@ -33,14 +53,41 @@
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
Taxe d'apprentissage<br>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_taxe_apprentissage', id=entreprise.id) }}">Ajouter taxe apprentissage</a>
<div class="taxe-apprentissage">
<ul id="liste-taxes-apprentissages">
{% if not taxes|check_taxe_now %}
<li>année actuelle : non versée</li>
{% endif %}
{% for taxe in taxes %}
<li>
<a href="{{ url_for('entreprises.delete_taxe_apprentissage', id_entreprise=entreprise.id, id_taxe=taxe.id) }}"><img title="Supprimer taxe d'apprentissage" alt="supprimer" width="10" height="9" border="0" src="/ScoDoc/static/icons/delete_small_img.png" /></a>
<a href="{{ url_for('entreprises.edit_taxe_apprentissage', id_entreprise=entreprise.id, id_taxe=taxe.id) }}">{{ taxe.annee }}</a> : {{ taxe.montant }} euros {% if taxe.notes %}- {{ taxe.notes}} {% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_entreprise', id=entreprise.id) }}">Modifier</a>
{% if entreprise.active %}
<a class="btn btn-danger" href="{{ url_for('entreprises.fiche_entreprise_desactiver', id=entreprise.id) }}">Désactiver</a>
{% else %}
<a class="btn btn-success" href="{{ url_for('entreprises.fiche_entreprise_activer', id=entreprise.id) }}">Activer</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_site', id=entreprise.id) }}">Ajouter site</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_offre', id=entreprise.id) }}">Ajouter offre</a>
{% endif %}
@ -61,7 +108,10 @@
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<br><a class="btn btn-primary" href="{{ url_for('entreprises.add_correspondant', id_entreprise=entreprise.id, id_site=site.id) }}">Ajouter correspondant</a>
<div>
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_site', id_entreprise=entreprise.id, id_site=site.id) }}">Modifier</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_correspondant', id_entreprise=entreprise.id, id_site=site.id) }}">Ajouter correspondant</a>
</div>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
@ -111,7 +161,7 @@
<td>{{ data[0].date_fin.strftime('%d/%m/%Y') }}</td>
<td>{{ (data[0].date_fin-data[0].date_debut).days//7 }} semaines</td>
<td>{{ data[0].type_offre }}</td>
<td>{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}</td>
<td><a href="{{ url_for('scolar.ficheEtud', scodoc_dept=data[1].dept_id|get_dept_acronym, etudid=data[0].etudid) }}">{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}</a></td>
<td>{% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %}</td>
<td>{{ data[0].notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}

View File

@ -12,35 +12,58 @@
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
</div>
{% if correspondants %}
<div>
{% for correspondant in correspondants %}
<div class="sites-et-offres">
{% if sites %}
<div>
<h3>Correspondant</h3>
<div class="correspondant">
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
<h3>Sites</h3>
{% for site in sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
<div class="correspondant">
<div>
Civilité : {{ correspondant.civilite|get_civilité }}<br>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
{% if correspondant.origine %}
Origine : {{ correspondant.origine }}<br>
{% endif %}
{% if correspondant.notes %}
Notes : {{ correspondant.notes }}<br>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
{% endif %}
</div>
{% endif %}
<div>
<a class="btn btn-success" href="{{ url_for('entreprises.validate_entreprise', id=entreprise.id) }}">Valider</a>

View File

@ -1,72 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>Importation correspondants</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_correspondants_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not correspondants_import %}
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>civilite</td><td>text</td><td>civilite du correspondant (H ou F)</td></tr>
<tr><td>nom</td><td>text</td><td>nom du correspondant</td></tr>
<tr><td>prenom</td><td>text</td><td>prenom du correspondant</td></tr>
<tr><td>telephone</td><td>text</td><td>telephone du correspondant</td></tr>
<tr><td>mail</td><td>text</td><td>mail du correspondant</td></tr>
<tr><td>poste</td><td>text</td><td>poste du correspondant</td></tr>
<tr><td>service</td><td>text</td><td>service dans lequel travaille le correspondant</td></tr>
<tr><td>origine</td><td>text</td><td>origine du correspondant</td></tr>
<tr><td>notes</td><td>text</td><td>notes sur le correspondant</td></tr>
<tr><td>entreprise_siret</td><td>text</td><td>SIRET de l'entreprise</td></tr>
</table>
{% endif %}
{% if correspondants_import %}
<br><div>Importation de {{ correspondants_import|length }} correspondant(s)</div>
{% for correspondant in correspondants_import %}
<div class="correspondant">
<div>
Civilité : {{ correspondant.civilite|get_civilité }}<br>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
{% if correspondant.origine %}
Origine : {{ correspondant.origine }}<br>
{% endif %}
{% if correspondant.notes %}
Notes : {{ correspondant.notes }}<br>
{% endif %}
<a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant.entreprise_id )}}" target="_blank">lien vers l'entreprise</a>
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,149 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Importation données</a>
</li>
</ul>
</div>
<div class="container">
<h1>{{ title }}</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_donnees_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not entreprises_import and not sites_import and not correspondants_import %}
<div>Feuille Entreprises</div>
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>siret (*)</td><td>text</td><td>siret de l'entreprise</td></tr>
<tr><td>nom_entreprise (*)</td><td>text</td><td>nom de l'entreprise</td></tr>
<tr><td>adresse (*)</td><td>text</td><td>adresse de l'entreprise</td></tr>
<tr><td>ville (*)</td><td>text</td><td>ville de l'entreprise</td></tr>
<tr><td>code_postal (*)</td><td>text</td><td>code postal de l'entreprise</td></tr>
<tr><td>pays</td><td>text</td><td>pays de l'entreprise</td></tr>
</table>
<div>Feuille Sites</div>
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>siret_entreprise (*)</td><td>text</td><td>siret de l'entreprise</td></tr>
<tr><td>id_site (*)</td><td>text</td><td>id du site (ne rien remplir pour créer un site)</td></tr>
<tr><td>nom_site (*)</td><td>text</td><td>nom du site</td></tr>
<tr><td>adresse (*)</td><td>text</td><td>adresse du site</td></tr>
<tr><td>ville (*)</td><td>text</td><td>ville du site</td></tr>
<tr><td>code_postal (*)</td><td>text</td><td>code postal du site</td></tr>
<tr><td>pays (*)</td><td>text</td><td>pays du site</td></tr>
</table>
<div>Feuille Correspondants (à utiliser pour les modifications)</div>
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>civilite (*)</td><td>text</td><td>civilite du correspondant (H ou F)</td></tr>
<tr><td>nom (*)</td><td>text</td><td>nom du correspondant</td></tr>
<tr><td>prenom (*)</td><td>text</td><td>prenom du correspondant</td></tr>
<tr><td>telephone (*)</td><td>text</td><td>telephone du correspondant</td></tr>
<tr><td>mail (*)</td><td>text</td><td>mail du correspondant</td></tr>
<tr><td>poste</td><td>text</td><td>poste du correspondant</td></tr>
<tr><td>service</td><td>text</td><td>service dans lequel travaille le correspondant</td></tr>
<tr><td>origine</td><td>text</td><td>origine du correspondant</td></tr>
<tr><td>notes</td><td>text</td><td>notes sur le correspondant</td></tr>
<tr><td>nom_site</td><td>text</td><td>nom du site lié au correspondant</td></tr>
</table>
{% endif %}
{% if entreprises_import %}
<br><div>Importation de {{ entreprises_import|length }} entreprise(s)</div>
{% for entreprise in entreprises_import %}
<div class="entreprise">
<div>
SIRET : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}<br>
<a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}" target="_blank">Fiche entreprise</a>
</div>
{% for site in entreprise.sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
</div>
{% endfor %}
</div>
{% endfor %}
{% endif %}
{% if sites_import %}
<br><div>Importation de {{ sites_import|length }} site(s)</div>
{% for site in sites_import %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}<br>
<a href="{{ url_for('entreprises.fiche_entreprise', id=site.entreprise_id) }}" target="_blank">Fiche entreprise</a>
</div>
{% endfor %}
{% endif %}
{% if correspondants_import %}
<br><div>Importation de {{ correspondants_import|length }} correspondant(s)</div>
{% for correspondant in correspondants_import %}
<div class="correspondant">
<div>
Civilité : {{ correspondant.civilite|get_civilité }}<br>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
{% if correspondant.origine %}
Origine : {{ correspondant.origine }}<br>
{% endif %}
{% if correspondant.notes %}
Notes : {{ correspondant.notes }}<br>
{% endif %}
<a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant.entreprise_id) }}" target="_blank">Fiche entreprise</a>
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,52 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>Importation entreprises</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_entreprises_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not entreprises_import %}
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>siret</td><td>text</td><td>siret de l'entreprise</td></tr>
<tr><td>nom_entreprise</td><td>text</td><td>nom de l'entreprise</td></tr>
<tr><td>adresse</td><td>text</td><td>adresse de l'entreprise</td></tr>
<tr><td>ville</td><td>text</td><td>ville de l'entreprise</td></tr>
<tr><td>code_postal</td><td>text</td><td>code postal de l'entreprise</td></tr>
<tr><td>pays</td><td>text</td><td>pays de l'entreprise</td></tr>
</table>
{% endif %}
{% if entreprises_import %}
<br><div>Importation de {{ entreprises_import|length }} entreprise(s)</div>
{% for entreprise in entreprises_import %}
<div class="entreprise">
<div>
SIRET : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -2,6 +2,17 @@
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Dernières opérations</a>
</li>
</ul>
</div>
<div class="container">
<h3>Dernières opérations</h3>
{% if logs.items %}

View File

@ -2,6 +2,20 @@
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}" class="breadcrumbs_link">Fiche entreprise</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Dernières opérations</a>
</li>
</ul>
</div>
<div class="container">
<h3>Dernières opérations - {{ entreprise.nom }}</h3>
{% if logs.items %}

View File

@ -1,14 +1,16 @@
{# -*- mode: jinja-html -*- #}
<nav class="nav-entreprise">
<ul>
<li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
<li><a href="{{ url_for('entreprises.correspondants') }}">Correspondants</a></li>
{% endif %}
<li><a href="{{ url_for('entreprises.offres_recues') }}">Offres reçues</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesValidate, None) %}
<li><a href="{{ url_for('entreprises.validation') }}">Entreprises à valider</a></li>
<li><a href="{{ url_for('entreprises.preferences') }}">Préférences</a></li>
{% endif %}
</ul>
</nav>
<div class="container">
<nav class="nav-entreprise">
<ul>
<li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
<li><a href="{{ url_for('entreprises.correspondants') }}">Correspondants</a></li>
{% endif %}
<li><a href="{{ url_for('entreprises.offres_recues') }}">Offres reçues</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesValidate, None) %}
<li><a href="{{ url_for('entreprises.validation') }}">Entreprises à valider</a></li>
<li><a href="{{ url_for('entreprises.preferences') }}">Préférences</a></li>
{% endif %}
</ul>
</nav>
</div>

View File

@ -2,6 +2,20 @@
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}" class="breadcrumbs_link">Fiche entreprise</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Offres expirées</a>
</li>
</ul>
</div>
<div class="container">
<h1>Offres expirées - {{ entreprise.nom }}</h1>
{% if offres_expirees %}

View File

@ -5,11 +5,13 @@
{% block app_content %}
{% include 'entreprises/nav.html' %}
<h1>Préférences module gestion entreprises</h1>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form, novalidate=True) }}
<div class="container">
<h1>Préférences module gestion entreprises</h1>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,76 @@
"""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 ###