def siren_siret_vat_in_name_onchange(self):
     if (
         self.name
         and self.is_company
         and not self.parent_id
         and not self.siren
         and not self.nic
         and not self.siret
         and not self.street
         and not self.city
         and not self.zip
     ):
         name = self.name.replace(" ", "")
         if name:
             vals = False
             if len(name) == 9 and name.isdigit() and siren_is_valid(name):
                 vals = self._opendatasoft_get_from_siren(name)
             elif len(name) == 14 and name.isdigit() and siret_is_valid(name):
                 vals = self._opendatasoft_get_from_siret(name)
             elif (
                 len(name) == 13
                 and name[:2] == "FR"
                 and name[2:].isdigit()
                 and siren_is_valid(name[4:])
             ):
                 vals = self._opendatasoft_get_from_siren(name[4:])
             if vals:
                 self.update(vals)
Example #2
0
 def _check_siret(self):
     """Check the SIREN's and NIC's keys (last digits)"""
     for rec in self:
         if rec.type == "contact" and rec.parent_id:
             continue
         if rec.nic:
             # Check the NIC type and length
             if not rec.nic.isdigit() or len(rec.nic) != 5:
                 raise ValidationError(
                     _("The NIC '%s' is incorrect: it must have "
                       "exactly 5 digits.") % rec.nic)
         if rec.siren:
             # Check the SIREN type, length and key
             if not rec.siren.isdigit() or len(rec.siren) != 9:
                 raise ValidationError(
                     _("The SIREN '%s' is incorrect: it must have "
                       "exactly 9 digits.") % rec.siren)
             if not siren_is_valid(rec.siren):
                 raise ValidationError(
                     _("The SIREN '%s' is invalid: the checksum is wrong.")
                     % rec.siren)
             # Check the NIC key (you need both SIREN and NIC to check it)
             if rec.nic and not siret_is_valid(rec.siren + rec.nic):
                 raise ValidationError(
                     _("The SIRET '%s%s' is invalid: "
                       "the checksum is wrong.") % (rec.siren, rec.nic))
 def _opendatasoft_get_from_siren(self, siren, vat_vies_query=True):
     if siren and siren_is_valid(siren):
         vals = self._opendatasoft_get_first_result(
             "siren:%s AND etablissementsiege:oui" % siren,
             vat_vies_query=vat_vies_query,
         )
         if vals and vals.get("siren") == siren:
             return vals
     return False
Example #4
0
 def _inverse_siret(self):
     for rec in self:
         if rec.siret:
             if siret_is_valid(rec.siret):
                 rec.write({"siren": rec.siret[:9], "nic": rec.siret[9:]})
             elif siren_is_valid(
                     rec.siret[:9]) and rec.siret[9:] == "*****":
                 rec.write({"siren": rec.siret[:9], "nic": False})
             else:
                 raise ValidationError(
                     _("SIRET '%s' is invalid.") % rec.siret)
         else:
             rec.write({"siren": False, "nic": False})
 def vat_onchange(self):
     if (
         self.vat
         and not self.name
         and not self.siren
         and not self.siret
         and self.is_company
         and not self.parent_id
     ):
         vat = self.vat.replace(" ", "").upper()
         if vat and vat.startswith("FR") and len(vat) == 13:
             siren = vat[4:]
             if siren_is_valid(siren):
                 vals = self._opendatasoft_get_from_siren(siren)
                 if vals:
                     self.update(vals)
Example #6
0
async def declare(request, response, siren, year):
    try:
        year = int(year)
    except ValueError:
        raise HttpError(f"Ce n'est pas une année valide: `{year}`")
    if not siren_is_valid(siren):
        raise HttpError(422, f"Numéro SIREN invalide: {siren}")
    if year not in constants.YEARS:
        years = ", ".join([str(y) for y in constants.YEARS])
        raise HttpError(
            422, f"Il est possible de déclarer seulement pour les années {years}"
        )
    data = request.data
    declarant = request["email"]
    data.setdefault("déclarant", {})
    # Use token email as default for declarant email.
    if not data["déclarant"].get("email"):
        data["déclarant"]["email"] = declarant
    schema.validate(data.raw)
    helpers.compute_notes(data)
    schema.cross_validate(data.raw)
    try:
        current = await db.declaration.get(siren, year)
    except db.NoData:
        current = None
    else:
        # Do not force new declarant, in case this is a staff person editing
        declarant = current["declarant"]
        declared_at = current["declared_at"]
        expired = declared_at and declared_at < utils.remove_one_year(utils.utcnow())
        if expired and not request["staff"]:
            raise HttpError(403, "Le délai de modification est écoulé.")
    await db.declaration.put(siren, year, declarant, data)
    response.status = 204
    if data.validated:
        await db.archive.put(siren, year, data, by=request["email"], ip=request.ip)
        if not request["staff"]:
            await db.ownership.put(siren, request["email"])
        # Do not send the success email on update for now (we send too much emails that
        # are unwanted, mainly because when someone loads the frontend app a PUT is
        # automatically sent, without any action from the user.)
        loggers.logger.info(f"{siren}/{year} BY {declarant} FROM {request.ip}")
        if not current or not current.data.validated:
            owners = await db.ownership.emails(siren)
            url = request.domain + data.uri
            emails.success.send(owners, url=url, **data)
 def siren_onchange(self):
     if (
         self.siren
         and siren_is_valid(self.siren)
         and not self.name
         and self.is_company
         and not self.parent_id
     ):
         if self.nic:
             # We only execute the query if the full SIRET is OK
             vals = False
             if siret_is_valid(self.siren + self.nic):
                 siret = self.siren + self.nic
                 vals = self._opendatasoft_get_from_siret(siret)
         else:
             vals = self._opendatasoft_get_from_siren(self.siren)
         if vals:
             self.update(vals)
Example #8
0
async def validate_siren(request, response):
    siren = request.query.get("siren")
    if not siren_is_valid(siren):
        raise HttpError(422, f"Numéro SIREN invalide: {siren}")
    metadata = await helpers.load_from_api_entreprises(siren)
    response.json = metadata
Example #9
0
def _cross_validate(data):
    data = Data(data)
    if data.validated:
        # Those keys are only required if the data is validated
        required = [
            "entreprise.région",
            "entreprise.département",
            "entreprise.adresse",
            "entreprise.commune",
            "entreprise.code_postal",
            "entreprise.code_naf",
            "déclarant.prénom",
            "déclarant.nom",
            "déclarant.téléphone",
        ]
        for path in required:
            assert data.path(path), f"Le champ {path} doit être défini"
        dep = data.path("entreprise.département")
        region = data.path("entreprise.région")
        cp = data.path("entreprise.code_postal")
        msg = "Le département et la région ne correspondent pas"
        assert dep in constants.REGIONS_TO_DEPARTEMENTS[region], msg
        msg = "Le département et le code postal ne correspondent pas"
        assert check_dep_and_cp(dep, cp), msg
        index = data.path("déclaration.index")
        mesures_correctives = data.path("déclaration.mesures_correctives")
        if data.year >= 2020 or index is not None:
            msg = "La date de publication doit être définie"
            assert data.path("déclaration.publication.date"), msg
            msg = "Les modalités de publication ou le site Internet doit être défini"
            assert data.path("déclaration.publication.modalités") or data.path(
                "déclaration.publication.url"
            ), msg
        if index is None:
            msg = "Les mesures correctives ne doivent pas être définies si l'index n'est pas calculable"
            assert not mesures_correctives, msg
        elif index >= 75:
            msg = "Les mesures correctives ne doivent pas être définies pour un index de 75 ou plus"
            assert not mesures_correctives, msg
        else:
            msg = "Les mesures correctives doivent être définies pour un index inférieur à 75"
            assert mesures_correctives, msg
        periode_reference = data.path("déclaration.fin_période_référence")
        assert (
            periode_reference
        ), "Le champ déclaration.fin_période_référence doit être défini"
        try:
            annee_periode_reference = date.fromisoformat(periode_reference).year
        except ValueError:
            annee_periode_reference = None
        assert (
            annee_periode_reference == data.year
        ), "L'année de la date de fin de période ne peut pas être différente de l'année au titre de laquelle les indicateurs sont calculés."

        tranche = data.path("entreprise.effectif.tranche")
        indicateurs_gt_250 = ("indicateurs.promotions", "indicateurs.augmentations")
        indicateurs_lt_250 = "indicateurs.augmentations_et_promotions"
        if tranche == "50:250":
            for path in indicateurs_gt_250:
                msg = f"L'indicateur {path} ne doit pas être défini pour la tranche 50 à 250"
                assert not data.path(path), msg
            msg = f"L'indicateur {indicateurs_lt_250} doit être défini pour la tranche 50 à 250"
            assert data.path(indicateurs_lt_250), msg
        else:
            msg = f"L'indicateur {indicateurs_lt_250} ne peut être défini que pour la tranche 50 à 250"
            assert not data.path(indicateurs_lt_250), msg
            for path in indicateurs_gt_250:
                msg = f"L'indicateur {path} doit être défini"
                assert data.path(path), msg

    for key in SCHEMA.indicateurs_keys:
        path = f"indicateurs.{key}"
        if data.path(f"{path}.non_calculable"):
            msg = f"L'indicateur {path} doit être vide s'il n'est pas calculable"
            assert list(data.path(path).keys()) == ["non_calculable"], msg
        elif data.path(path) is not None:
            resultat = data.path(f"{path}.résultat")
            msg = f"{path}.résultat doit être défini si l'indicateur est calculable"
            if key != "rémunérations" or data.path(f"{path}.population_favorable"):
                # The "rémunérations" indicator is sent through several steps
                # on the "formulaire" frontend. The only way the "formulaire"
                # sent all its data is if there's a `population_favorable`
                # field. However, this latter field is only provided if the
                # `résultat` is not `0`.
                assert resultat is not None, msg

    keys = ["rémunérations", "augmentations", "promotions"]
    for key in keys:
        path = f"indicateurs.{key}"
        if data.path(f"{path}.résultat") == 0:
            msg = f"{path}.population_favorable doit être vide si le résultat est 0"
            assert not data.path(f"{path}.population_favorable"), msg

    # Entreprise
    entreprises = data.path("entreprise.ues.entreprises") or []
    all_siren = [e["siren"] for e in entreprises]
    duplicates = [v for v, c in Counter(all_siren).items() if c > 1]
    assert not duplicates, f"Valeur de siren en double: {','.join(duplicates)}"
    assert (
        data.siren not in all_siren
    ), "L'entreprise déclarante ne doit pas être dupliquée dans les entreprises de l'UES"
    for ues in entreprises:
        msg = f"Invalid siren: {ues['siren']}"
        assert siren_is_valid(ues["siren"]), msg
    if not entreprises:
        msg = "Une entreprise ne doit pas avoir de nom d'UES"
        assert not data.path("entreprise.ues.nom"), msg

    # Rémunérations
    base = "indicateurs.rémunérations"
    if not data.path(f"{base}.non_calculable"):
        date_consultation_cse = data.path(f"{base}.date_consultation_cse")
        mode = data.path(f"{base}.mode")
        if mode == "csp":
            msg = f"{base}.date_consultation_cse ne doit pas être défini si indicateurs.rémunérations.mode vaut 'csp'"
            assert not date_consultation_cse, msg

    # Augmentations et promotions
    base = "indicateurs.augmentations_et_promotions"
    if (
        data.path(f"{base}.résultat") == 0
        and data.path(f"{base}.résultat_nombre_salariés") == 0
    ):
        path = f"{base}.population_favorable"
        msg = f"{path} ne doit pas être défini si résultat=0 et résultat_nombre_salariés=0"
        assert not data.path(path), msg

    # Hautes rémunérations
    base = "indicateurs.hautes_rémunérations"
    if data.path(f"{base}.résultat") == 5:
        msg = f"{base}.population_favorable ne doit pas être défini si résultat vaut 5"
        assert not data.path(f"{base}.population_favorable"), msg