From f6a965f5d9e3d892cc37e7628d116f6a117e06a6 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Tue, 8 Mar 2022 13:40:23 +0100 Subject: [PATCH] :sparkles: Revoie les parcours avec entreprise existante MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Met à jour le style des notifications dans la conversation - Améliore la landing page - Améliore la vue de la situation des simulateurs - Les données de l'entreprise courante sont stockées en publicodes - Ajoute la possibilité de réinitialisé la simulation sans les données entreprises --- exoneration-covid/package.json | 2 +- modele-social/package.json | 2 +- .../b\303\251n\303\251ficiaire.yaml" | 2 +- "modele-social/r\303\250gles/dirigeant.yaml" | 119 ++--- .../r\303\250gles/entreprise/base.yaml" | 249 +---------- .../r\303\250gles/entreprise/imposition.yaml" | 235 ++++++++++ .../entreprise/statut-juridique.yaml" | 79 ++++ .../profession-lib\303\251rale.yaml" | 1 + .../r\303\250gles/salari\303\251.yaml" | 3 +- package.json | 4 +- .../mon-entreprise/english/navigation.js | 4 +- .../mon-entreprise/localisation-field.js | 2 +- .../mon-entreprise/partage-simulation.js | 2 +- .../mon-entreprise/simulateur-salarie.js | 2 +- site/source/App.tsx | 39 +- site/source/actions/actions.ts | 15 +- site/source/actions/companyActions.ts | 24 + site/source/actions/companyStatusActions.ts | 15 +- site/source/actions/existingCompanyActions.ts | 42 -- .../ChiffreAffairesActivit\303\251Mixte.tsx" | 33 +- site/source/components/EngineValue.tsx | 23 +- site/source/components/Notifications.tsx | 67 +-- site/source/components/SchemeComparaison.tsx | 6 +- .../ShareSimulationBanner/index.tsx | 11 +- site/source/components/Simulation/index.tsx | 39 +- site/source/components/TypeFormEmbed.js | 35 -- site/source/components/company/Details.tsx | 34 ++ .../SearchDetails.tsx} | 64 +-- .../SearchField.tsx} | 8 +- .../components/conversation/AnswerList.tsx | 149 ++++--- .../components/conversation/Conversation.tsx | 8 +- .../conversation/SeeAnswersButton.tsx | 2 +- ...303\251pendantCotisationsForfaitaires.tsx" | 8 +- .../InstitutionsPartenaires.tsx | 171 +++---- site/source/components/utils/Emoji.tsx | 10 +- .../source/components/utils/EngineContext.tsx | 4 +- .../components/utils/useNextQuestion.tsx | 9 +- .../utils/useSearchParamsSimulationSharing.ts | 24 +- .../components/utils/useSimulationConfig.ts | 33 +- site/source/design-system/message/index.tsx | 69 +-- .../design-system/typography/list.stories.tsx | 27 ++ site/source/hooks/useQuestionList.ts | 34 ++ site/source/locales/rules-en.yaml | 109 ++++- site/source/locales/ui-en.yaml | 30 +- site/source/locales/ui-fr.yaml | 30 +- site/source/pages/Creer/AfterRegistration.tsx | 2 +- site/source/pages/Creer/CreationChecklist.tsx | 2 +- .../Creer/GuideStatut/PreviousAnswers.tsx | 2 +- site/source/pages/Creer/GuideStatut/index.tsx | 4 +- site/source/pages/Creer/Home.tsx | 2 +- .../Fields.tsx" | 2 +- .../PreviousVersion.tsx" | 306 +++++++++++++ .../config.yaml" | 2 +- .../index.tsx" | 347 +++------------ .../source/pages/Gerer/AideOrganismeLocal.tsx | 2 +- site/source/pages/Gerer/Embaucher.tsx | 4 +- site/source/pages/Gerer/Home.tsx | 421 ++++++++---------- .../pages/Landing/ContinueWithCompany.tsx | 6 +- site/source/pages/Landing/Landing.tsx | 4 - site/source/pages/Landing/SearchOrCreate.tsx | 92 ++-- .../EconomieCollaborative/VotreSituation.tsx | 3 +- site/source/pages/Simulateurs/Page/index.tsx | 3 +- .../pages/Simulateurs/Salari\303\251.tsx" | 39 +- .../Simulateurs/configs/artiste-auteur.yaml | 3 +- .../configs/auto-entrepreneur.yaml | 5 +- .../configs/ch\303\264mage-partiel.yaml" | 1 + .../Simulateurs/configs/dirigeant-sasu.yaml | 2 +- .../pages/Simulateurs/configs/dividendes.yaml | 3 +- .../configs/ind\303\251pendant.yaml" | 3 +- .../configs/profession-lib\303\251rale.yaml" | 2 +- ...r\303\251mun\303\251ration-dirigeant.yaml" | 2 +- site/source/pages/Simulateurs/metadata-src.ts | 4 +- site/source/pages/Simulateurs/metadata.tsx | 4 +- ...ucer.ts => choixStatutJuridiqueReducer.ts} | 97 +--- .../reducers/companySituationReducer.ts | 128 ++++++ site/source/reducers/rootReducer.ts | 68 ++- .../selectors/companyStatusSelectors.ts | 6 +- site/source/selectors/simulationSelectors.ts | 20 +- ...eApp.ts => persistChoixStatutJuridique.ts} | 14 +- .../source/storage/persistCompanySituation.ts | 30 ++ site/test/companyStatusSelectors.test.js | 2 +- site/test/persistence.test.ts | 2 +- .../regressions/simulations-dividendes.yaml | 20 +- .../simulations-salari\303\251.yaml" | 12 +- site/test/regressions/simulations.test.ts | 12 +- yarn.lock | 18 +- 86 files changed, 2012 insertions(+), 1567 deletions(-) rename "modele-social/r\303\250gles/entreprise-\303\251tablissement.yaml" => "modele-social/r\303\250gles/entreprise/base.yaml" (72%) create mode 100644 "modele-social/r\303\250gles/entreprise/imposition.yaml" create mode 100644 "modele-social/r\303\250gles/entreprise/statut-juridique.yaml" create mode 100644 site/source/actions/companyActions.ts delete mode 100644 site/source/actions/existingCompanyActions.ts delete mode 100644 site/source/components/TypeFormEmbed.js create mode 100644 site/source/components/company/Details.tsx rename site/source/components/{CompanyDetails.tsx => company/SearchDetails.tsx} (52%) rename site/source/components/{CompanySearchField.tsx => company/SearchField.tsx} (93%) create mode 100644 site/source/design-system/typography/list.stories.tsx create mode 100644 site/source/hooks/useQuestionList.ts create mode 100644 "site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/PreviousVersion.tsx" rename site/source/reducers/{inFranceAppReducer.ts => choixStatutJuridiqueReducer.ts} (50%) create mode 100644 site/source/reducers/companySituationReducer.ts rename site/source/storage/{persistInFranceApp.ts => persistChoixStatutJuridique.ts} (57%) create mode 100644 site/source/storage/persistCompanySituation.ts diff --git a/exoneration-covid/package.json b/exoneration-covid/package.json index 29dad19bee..f63bd6762d 100644 --- a/exoneration-covid/package.json +++ b/exoneration-covid/package.json @@ -27,7 +27,7 @@ "build": "node ../scripts/build-rules.js", "start": "onchange 'règles/**/*.yaml' -- yarn run build", "clean": "rimraf dist node_modules", - "postinstall": "yarn run build", + "prepack": "yarn run build", "up": "yarn version --minor && echo \"ℹ N'oubliez pas de poussez le tag git\"", "test": "node ../scripts/check-changelog.js" } diff --git a/modele-social/package.json b/modele-social/package.json index b71d132661..f385b4fd21 100644 --- a/modele-social/package.json +++ b/modele-social/package.json @@ -28,7 +28,7 @@ "build": "node ../scripts/build-rules.js", "start": "onchange 'règles/**/*.yaml' -- yarn run build", "clean": "rimraf dist node_modules", - "postinstall": "yarn run build", + "prepack": "yarn run build", "up": "yarn version --minor && echo \"ℹ N'oubliez pas de poussez le tag git\"", "test": "node ../scripts/check-changelog.js" } diff --git "a/modele-social/r\303\250gles/b\303\251n\303\251ficiaire.yaml" "b/modele-social/r\303\250gles/b\303\251n\303\251ficiaire.yaml" index 29216e338a..74669d5331 100644 --- "a/modele-social/r\303\250gles/b\303\251n\303\251ficiaire.yaml" +++ "b/modele-social/r\303\250gles/b\303\251n\303\251ficiaire.yaml" @@ -1,5 +1,5 @@ bénéficiaire: - valeur: non + applicable si: entreprise . imposition . IS description: | Un bénéficiaire est un actionnaire dans une SAS ou un associé dans une SARL/EURL. diff --git "a/modele-social/r\303\250gles/dirigeant.yaml" "b/modele-social/r\303\250gles/dirigeant.yaml" index d839babb46..c0be200254 100644 --- "a/modele-social/r\303\250gles/dirigeant.yaml" +++ "b/modele-social/r\303\250gles/dirigeant.yaml" @@ -1,13 +1,37 @@ -dirigeant: - question: Quel est le régime social du dirigeant ? +dirigeant: oui +dirigeant . gérant minoritaire: + titre: Gérant minoritaire ou égalitaire + question: Êtes-vous gérant minoritaire ou égalitaire de votre entreprise ? + non applicable si: + une de ces conditions: + - entreprise . catégorie juridique . EI + - entreprise . catégorie juridique . SARL . unipersonnelle + - entreprise . catégorie juridique . SAS . unipersonnelle par défaut: non - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - auto-entrepreneur - - assimilé salarié - - indépendant + +dirigeant . régime social: + non applicable si: + une de ces conditions: + - entreprise . catégorie juridique . SELARL #TODO NON IMPLEMENTE + - entreprise . catégorie juridique . SELAS #TODO NON IMPLEMENTE + - entreprise . catégorie juridique . autre + par défaut: non + variations: + - si: entreprise . catégorie juridique . EI . auto-entrepreneur + alors: "'auto-entrepreneur'" + - si: + une de ces conditions: + - entreprise . catégorie juridique . SAS + - toutes ces conditions: + - entreprise . catégorie juridique . SARL + - gérant minoritaire + alors: "'assimilé salarié'" + - si: + non applicable si: gérant minoritaire + une de ces conditions: + - entreprise . catégorie juridique . EI + - entreprise . catégorie juridique . SARL + alors: "'indépendant'" dirigeant . rémunération: oui dirigeant . rémunération . totale: @@ -81,14 +105,13 @@ dirigeant . rémunération . nette après impôt: dirigeant . assimilé salarié: description: | Certains dirigeants d'entreprise (c'est notamment le cas pour les SASU) sont considérés par la sécurité sociale comme assimilés aux salariés. Ils sont alors au régime général de la sécurité sociale, avec quelques contraintes cependant. Par exemple, ils ne cotisent pas au chômage, et n'y ont donc pas droit. - formule: dirigeant = 'assimilé salarié' + applicable si: régime social = 'assimilé salarié' + valeur: oui remplace: - règle: contrat salarié par: "'CDI'" - règle: contrat salarié . statut cadre par: oui - - règle: entreprise . imposition - par: "'IS'" rend non applicable: - contrat salarié . convention collective - contrat salarié . activité partielle @@ -152,14 +175,9 @@ dirigeant . assimilé salarié . réduction ACRE . notification taux annuel: simulateur ne prends pas encore en compte le calcul de l'ACRE mois par mois. dirigeant . auto-entrepreneur: + applicable si: régime social = 'auto-entrepreneur' + valeur: oui rend non applicable: contrat salarié - remplace: - - règle: entreprise . imposition - par: "'IR'" - - règle: entreprise . imposition . IR . micro-fiscal - par: oui - - formule: dirigeant = 'auto-entrepreneur' icônes: 🚶 description: | L'auto-entreprise est une entreprise individuelle simplifiée. À l'origine connu sous l'appellation « auto-entrepreneur », le régime de « micro-entrepreneur » est un régime de travailleur indépendant créé pour simplifier la gestion administrative, notamment en remplaçant toutes les cotisations sociales par un prélèvement unique mensuel. @@ -337,40 +355,38 @@ dirigeant . auto-entrepreneur . cotisations et contributions . cotisations . tau remplace: règle: taux vente restauration hébergement par: taux ACRE * taux vente restauration hébergement - description: | Ce taux correspond à la réduction de cotisations qui s'applique pour l'auto-entrepreneur bénéficiant de l'Acre. Un taux de 75% signifie que l'auto-entrepreneur doit s'acquitter de 75% du montant d'origine des cotisations. unité: '%' - formule: - variations: - - si: entreprise . date de création < 01/04/2019 - alors: - grille: - assiette: entreprise . durée d'activité - tranches: - - montant: 25% - plafond: 1 an - - montant: 50% - plafond: 2 ans - - montant: 90% - plafond: 3 ans - - si: entreprise . date de création < 01/04/2020 - alors: - grille: - assiette: entreprise . durée d'activité - tranches: - - montant: 25% - plafond: 1 an - - montant: 75% - plafond: 2 ans - - montant: 90% - plafond: 3 ans - - sinon: - applicable si: entreprise . durée d'activité < 1 an - valeur: 50% + variations: + - si: entreprise . date de création < 01/04/2019 + alors: + grille: + assiette: entreprise . durée d'activité + tranches: + - montant: 25% + plafond: 1 an + - montant: 50% + plafond: 2 ans + - montant: 90% + plafond: 3 ans + - si: entreprise . date de création < 01/04/2020 + alors: + grille: + assiette: entreprise . durée d'activité + tranches: + - montant: 25% + plafond: 1 an + - montant: 75% + plafond: 2 ans + - montant: 90% + plafond: 3 ans + - si: entreprise . durée d'activité < 1 an + alors: 50% + - sinon: 0% références: FAQ Urssaf depuis 04/2020: https://www.autoentrepreneur.urssaf.fr/portail/accueil/une-question/questions-frequentes.html#jai-cree-mon-auto-entreprise-en @@ -496,8 +512,9 @@ dirigeant . auto-entrepreneur . chiffre d'affaires: - net de cotisations dirigeant . indépendant: + applicable si: régime social = 'indépendant' + valeur: oui rend non applicable: contrat salarié - formule: dirigeant = 'indépendant' dirigeant . indépendant . revenu professionnel: description: rémunération du dirigeant au régime des indépendant @@ -970,15 +987,15 @@ dirigeant . indépendant . cotisations facultatives . plafond retraite compléme dirigeant . indépendant . cotisations et contributions . début activité: titre: Cotisations forfaitaires de début d'activité description: | - Lorsque vous commencez votre activité, vos **revenus professionnels + Lorsque vous commencez votre activité, vos **revenus professionnels** n’étant pas connus**, les cotisations et contributions des deux premières années sont calculées sur une **base forfaitaire**. Ces cotisations seront ajustées et régularisées en fonction de vos revenus réels de l’année d’exercice. Si votre revenu est supérieur à la base forfaitaire prise en compte - pour le calcul des cotisations provisionnelles alors vous serez redevable d’un **complément - de cotisations**. + pour le calcul des cotisations provisionnelles alors vous serez redevable d’un + **complément de cotisations**. Ce simulateur calcule les cotisations dites définitives sur la base des revenus réels de votre diff --git "a/modele-social/r\303\250gles/entreprise-\303\251tablissement.yaml" "b/modele-social/r\303\250gles/entreprise/base.yaml" similarity index 72% rename from "modele-social/r\303\250gles/entreprise-\303\251tablissement.yaml" rename to "modele-social/r\303\250gles/entreprise/base.yaml" index 1627c23c2f..60e2216d94 100644 --- "a/modele-social/r\303\250gles/entreprise-\303\251tablissement.yaml" +++ "b/modele-social/r\303\250gles/entreprise/base.yaml" @@ -1,7 +1,16 @@ entreprise: + valeur: oui description: | Le contrat lie une entreprise, identifiée par un code SIREN, et un employé. +entreprise . SIREN: + description: | + Le numéro Siren est un numéro de 9 chiffres unique pour chaque entreprise. Ex : 401237780 + type: texte + +entreprise . nom: + type: texte + entreprise . date de création: question: Quelle est votre date de début d'activité ? par défaut: 01/01/2021 @@ -199,7 +208,7 @@ entreprise . chiffre d'affaires . franchise de TVA . seuil service: entreprise . chiffre d'affaires . franchise de TVA . dépassement: type: notification - valeur: + formule: une de ces conditions: - chiffre d'affaires > seuil vente + seuil service - vente restauration hébergement > seuil vente @@ -226,120 +235,6 @@ entreprise . résultat fiscal: - (- charges) - (- charges . dirigeant) -entreprise . imposition: - question: Comment l'entreprise est-elle imposée ? - description: | - Indiquez si le régime d’imposition des revenus liés à l’activité indépendante relèvent : - - de l’impôt sur le revenu : les bénéfices de l’entreprise sont imposés directement auprès du travailleur indépendant, au barème progressif de l’impôt sur le revenu. - - de l’impôt sur les sociétés : les bénéfices de l’entreprise sont imposés au nom de la société, au taux de l’impôt sur les sociétés. - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - IR - - IS - par défaut: "'IR'" - -entreprise . imposition . IR: - valeur: imposition = 'IR' - titre: Impôt sur le revenu - -entreprise . imposition . IR . micro-fiscal: - rend non applicable: dirigeant . indépendant . cotisations facultatives - - question: Avez-vous opté pour le régime micro-fiscal ? - description: | - Avec le régime micro fiscal, les charges déductibles sont estimées forfaitairement,en fonction d’un pourcentage du chiffre d’affaires. Ce pourcentage dépend du type d’activité : 71% pour les activités de vente, restauration et hébergement (location de meublé de tourisme classé et chambre d’hôte), 50% pour les prestations de service commerciales ou artisanales, 34% pour les activités libérales. - - Cette option permet de simplifier votre comptabilité et peut être avantageuse en termes de revenu imposable et soumis à cotisations et contributions sociales dans le cas où vos charges de fonctionnement sont faibles. - par défaut: non - -entreprise . imposition . IR . micro-fiscal . revenu abattu: - remplace: résultat fiscal - résoudre la référence circulaire: oui - titre: abattement forfaitaire micro-fiscal - description: | - Le micro-entrepreneur est dispensé d'établir une déclaration professionnelle de bénéfices au titre des BNC ou BIC. - - Il lui suffit de porter dans la déclaration complémentaire de revenu (n°2042-C Pro) le montant annuel du chiffre d'affaires brut (BIC) ou des recettes (BNC). - somme: - - entreprise . chiffre d'affaires . vente restauration hébergement - - entreprise . chiffre d'affaires . service BIC - - entreprise . chiffre d'affaires . service BNC - abattement: - produit: - composantes: - - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - taux: 71% - - assiette: entreprise . chiffre d'affaires . service BIC - taux: 50% - - assiette: entreprise . chiffre d'affaires . service BNC - taux: 34% - plancher: - variations: - - si: entreprise . activité . mixte - alors: 610 €/an - - sinon: 305 €/an - -entreprise . imposition . IR . micro-fiscal . alerte seuil dépassés: - type: notification - sévérité: avertissement - formule: chiffre d'affaires . seuil micro dépassé - description: Le seuil annuel de chiffre d'affaires pour le régime micro-fiscal est dépassé. [En savoir plus](/documentation/entreprise/chiffre-d'affaires/seuil-micro-dépassé) - -entreprise . chiffre d'affaires . seuil micro dépassé: - applicable si: imposition . IR - description: | - Le statut de micro-entreprise s'applique tant que le chiffre d'affaires annuel (effectivement encaissé au cours de l'année civile) ne dépasse pas les seuils du régime fiscal de la micro-entreprise. - - En cas de dépassement **sur deux années consécutives**, l'entreprise bascule automatiquement dans le régime de [l'entreprise individuelle](/simulateurs/indépendant). - - À la fin de la première année d'activité, le CA est proratisé par rapport à la durée d'activité. - - Exemple : - > Un contribuable crée une entreprise le 1er août et encaisse des recettes HT de `50 000 €` au cours des cinq mois d'activité de sa première année civile d'exploitation. - > Les recettes de cette première année civile sont ajustées *prorata temporis* pour les comparer au plafond : - > - > `50 000€ x (365/153) = 119 280 €` - - - Les charges ne sont pas déductibles pour le calcul du plafond (comme pour le calcul des cotisations) - - - ### Multi-activité - - Lorsqu'un entrepreneur exerce 2 activités au sein de sa micro-entreprise, le - seuil de chiffre d’affaires à respecter n’est pas pour autant doublé. En - effet l'exercice de plusieurs activités avec la même micro-entreprise - n’augmente pas les seuils. - - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32353 - Article 50-0 du Code général des impôts: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000006199553&cidTexte=LEGITEXT000006069577 - Bofip (dépassement micro-bnc): https://bofip.impots.gouv.fr/bofip/4807-PGP.html - Bofip (dépassement micro-bic): https://bofip.impots.gouv.fr/bofip/1802-PGP.html - autoentrepreneur.urssaf.fr: https://www.autoentrepreneur.urssaf.fr/portail/accueil/une-question/questions-frequentes.html - unité: €/an - # TODO: les seuils micro sont dupliqués à plusieurs endroits (artiste-auteur . - # revenus . BNC . contrôle micro-bnc, tableau de la comparaison de régime, - # économie collaborative). Il faudrait référencer la même valeur partout où - # elle est utilisée. - une de ces conditions: - - entreprise . chiffre d'affaires > 176200 €/an - - entreprise . chiffre d'affaires . service > 72600 €/an - -entreprise . imposition . IR . information sur le report de déficit: - non applicable si: micro-fiscal - type: notification - formule: résultat fiscal < 0 €/an - description: | - Lorsque votre résultat fiscal est négatif, ce dernier vient réduire le revenu imposables du foyer fiscal. - Un déficit peut être imputé jusqu'à 6 ans après sa réalisation. - - [Voir les règles fiscales détaillées](https://bofip.impots.gouv.fr/bofip/2003-PGP.html/identifiant%3DBOI-BIC-DEF-20-10-20170301) - références: - bofip: https://bofip.impots.gouv.fr/bofip/2003-PGP.html/identifiant%3DBOI-BIC-DEF-20-10-20170301 - entreprise . exercice: oui entreprise . exercice . début: type: date @@ -380,123 +275,6 @@ entreprise . exercice . durée maximale: formule: durée >= 24 mois description: La durée maximale d'un exercice comptable est de 24 mois. -entreprise . imposition . IS: - valeur: imposition = 'IS' - titre: Impôt sur les sociétés - -entreprise . imposition . IS . résultat imposable: - titre: Résultat de l'exercice - résumé: Imposable à l'impôt sur les sociétés - valeur: résultat fiscal - -entreprise . imposition . IS . information sur le report de déficit: - type: notification - formule: résultat imposable < 0 €/an - # TODO: Support des références dans les notifications - description: | - Les déficits subits au cours d'un exercice peuvent être reportés sur les exercices suivants (report en avant), ou sur le seul exercice précédent (report en arrière). - -entreprise . imposition . IS . résultat net: - résumé: Après déduction des charges et de l'impôt sur les société - somme: - - chiffre d'affaires - - (- charges) - - (- dirigeant . rémunération . totale) - - (- impôt sur les sociétés) - par défaut: 0€ - -entreprise . imposition . IS . impôt sur les sociétés: - unité: €/an - formule: - barème: - assiette: résultat imposable - multiplicateur: prorata temporis - variations: - - si: exercice . début >= 01/01/2022 - alors: - tranches: - - taux: 15% - plafond: plafond taux réduit 1 - - taux: 25% - - si: exercice . début >= 01/01/2021 - alors: - tranches: - - taux: 15% - plafond: plafond taux réduit 1 - - taux: 26.5% - - si: exercice . début >= 01/01/2020 - alors: - tranches: - - taux: 15% - plafond: plafond taux réduit 1 - - taux: 28% - - si: exercice . début >= 01/01/2019 - alors: - tranches: - - taux: 15% - plafond: plafond taux réduit 1 - - taux: 28% - plafond: plafond taux réduit 2 - - taux: 31% - - si: exercice . début >= 01/01/2018 - alors: - tranches: - - taux: 15% - plafond: plafond taux réduit 1 - - taux: 28% - plafond: plafond taux réduit 2 - - taux: 33.3333% - arrondi: oui - références: - Fiche impots.gouv.fr: https://www.impots.gouv.fr/portail/international-professionnel/impot-sur-les-societes - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23575 - -entreprise . imposition . IS . impôt sur les sociétés . plafond taux réduit 1: - applicable si: éligible taux réduit - valeur: 38120 €/an - -entreprise . imposition . IS . impôt sur les sociétés . plafond taux réduit 2: - applicable si: éligible taux réduit - valeur: 500000 €/an - -entreprise . imposition . IS . impôt sur les sociétés . éligible taux réduit: - formule: - toutes ces conditions: - - chiffre d'affaires <= 7630 k€/an * prorata temporis - - nom: capital détenu au moins à 75 pourcents par des personnes physiques - valeur: oui - -entreprise . imposition . IS . impôt sur les sociétés . prorata temporis: - description: | - Lorsque la durée de l’exercice n'est pas égale à un an, on pro-ratise les - plafonds utilisés dans le barème de l'impôt sur les sociétés. - unité: '%' - formule: exercice . durée / 1 an - # TODO: c'est un peu plus subtil que cela : « En cas d’exercice ouvert ou - # arrêté en cours de mois calendaire, le nombre de jours résiduels concourt à - # la détermination du rapport pour un montant égal au rapport existant entre - # ce nombre et 30. » - références: - Bofip: https://bofip.impots.gouv.fr/bofip/2065-PGP.html/identifiant%3DBOI-IS-LIQ-20-20-20180801 - -entreprise . imposition . IS . impôt sur les sociétés . contribution sociale: - # description: | - # La contribution sociale sur les bénéfices est un impôt distinct de l'impôt sur les sociétés. Son montant n'est pas déductible des résultats. - - # L'assiette bénéficie d'un abattement important, et seules les entreprises réalisant plus de 2,3 millions d'euros de bénéficie sont concernées par cette contribution. - description: | - La contribution sociale sur les bénéfices est un impôt distinct de l’impôt sur les sociétés. Son montant n’est pas déductible des résultats. - - L’assiette bénéficie d’un abattement important, et seules les entreprises réalisant plus de 2,3 millions d’euros de bénéfices sont concernées par cette contribution. - formule: - produit: - taux: 3.3% - assiette: - valeur: impôt sur les sociétés - abattement: 763000 €/an * prorata temporis - références: - Bofip: https://bofip.impots.gouv.fr/bofip/3492-PGP.html/identifiant%3DBOI-IS-AUT-10-20-20130318 - entreprise . charges: synonymes: - charges d'exploitation @@ -865,6 +643,7 @@ entreprise . activité . débit de tabac: par défaut: non établissement: + formule: oui description: | Le salarié travaille dans un établissement de l'entreprise, identifié par un code SIRET. @@ -931,3 +710,9 @@ entreprise . activité . débit de tabac: applicable si: entreprise . date de création < 01/2015 question: Votre établissement bénéficie-t-il du dispositif zone franche urbaine (ZFU) ? par défaut: non + +établissement . ZFU . durée d'implantation en fin d'année: + formule: + durée: + depuis: entreprise . date de création + jusqu'à: 31/12/2019 diff --git "a/modele-social/r\303\250gles/entreprise/imposition.yaml" "b/modele-social/r\303\250gles/entreprise/imposition.yaml" new file mode 100644 index 0000000000..cb716d14d8 --- /dev/null +++ "b/modele-social/r\303\250gles/entreprise/imposition.yaml" @@ -0,0 +1,235 @@ +entreprise . imposition: + question: Comment l'entreprise est-elle imposée ? + description: | + Indiquez si le régime d’imposition des revenus liés à l’activité indépendante relèvent : + - de l’impôt sur le revenu : les bénéfices de l’entreprise sont imposés directement auprès du travailleur indépendant, au barème progressif de l’impôt sur le revenu. + - de l’impôt sur les sociétés : les bénéfices de l’entreprise sont imposés au nom de la société, au taux de l’impôt sur les sociétés. + une possibilité: + choix obligatoire: oui + possibilités: + - IR + - IS + par défaut: + variations: + - si: catégorie juridique . EI + alors: "'IR'" + - sinon: "'IS'" + +entreprise . imposition . IR: + applicable si: imposition = 'IR' + titre: Impôt sur le revenu + valeur: oui + +entreprise . imposition . IR . micro-fiscal: + rend non applicable: dirigeant . indépendant . cotisations facultatives + + question: Avez-vous opté pour le régime micro-fiscal ? + description: | + Avec le régime micro fiscal, les charges déductibles sont estimées forfaitairement,en fonction d’un pourcentage du chiffre d’affaires. Ce pourcentage dépend du type d’activité : 71% pour les activités de vente, restauration et hébergement (location de meublé de tourisme classé et chambre d’hôte), 50% pour les prestations de service commerciales ou artisanales, 34% pour les activités libérales. + + Cette option permet de simplifier votre comptabilité et peut être avantageuse en termes de revenu imposable et soumis à cotisations et contributions sociales dans le cas où vos charges de fonctionnement sont faibles. + par défaut: non + +entreprise . imposition . IR . micro-fiscal . revenu abattu: + remplace: résultat fiscal + résoudre la référence circulaire: oui + titre: abattement forfaitaire micro-fiscal + description: | + Le micro-entrepreneur est dispensé d'établir une déclaration professionnelle de bénéfices au titre des BNC ou BIC. + + Il lui suffit de porter dans la déclaration complémentaire de revenu (n°2042-C Pro) le montant annuel du chiffre d'affaires brut (BIC) ou des recettes (BNC). + somme: + - entreprise . chiffre d'affaires . vente restauration hébergement + - entreprise . chiffre d'affaires . service BIC + - entreprise . chiffre d'affaires . service BNC + abattement: + produit: + composantes: + - assiette: entreprise . chiffre d'affaires . vente restauration hébergement + taux: 71% + - assiette: entreprise . chiffre d'affaires . service BIC + taux: 50% + - assiette: entreprise . chiffre d'affaires . service BNC + taux: 34% + plancher: + variations: + - si: entreprise . activité . mixte + alors: 610 €/an + - sinon: 305 €/an + +entreprise . imposition . IR . micro-fiscal . alerte seuil dépassés: + type: notification + sévérité: avertissement + formule: chiffre d'affaires . seuil micro dépassé + description: Le seuil annuel de chiffre d'affaires pour le régime micro-fiscal est dépassé. [En savoir plus](/documentation/entreprise/chiffre-d'affaires/seuil-micro-dépassé) + +entreprise . chiffre d'affaires . seuil micro dépassé: + applicable si: imposition . IR + description: | + Le statut de micro-entreprise s'applique tant que le chiffre d'affaires annuel (effectivement encaissé au cours de l'année civile) ne dépasse pas les seuils du régime fiscal de la micro-entreprise. + + En cas de dépassement **sur deux années consécutives**, l'entreprise bascule automatiquement dans le régime de [l'entreprise individuelle](/simulateurs/indépendant). + + À la fin de la première année d'activité, le CA est proratisé par rapport à la durée d'activité. + + Exemple : + > Un contribuable crée une entreprise le 1er août et encaisse des recettes HT de `50 000 €` au cours des cinq mois d'activité de sa première année civile d'exploitation. + > Les recettes de cette première année civile sont ajustées *prorata temporis* pour les comparer au plafond : + > + > `50 000€ x (365/153) = 119 280 €` + + + Les charges ne sont pas déductibles pour le calcul du plafond (comme pour le calcul des cotisations) + + + ### Multi-activité + + Lorsqu'un entrepreneur exerce 2 activités au sein de sa micro-entreprise, le + seuil de chiffre d’affaires à respecter n’est pas pour autant doublé. En + effet l'exercice de plusieurs activités avec la même micro-entreprise + n’augmente pas les seuils. + + références: + Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32353 + Article 50-0 du Code général des impôts: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000006199553&cidTexte=LEGITEXT000006069577 + Bofip (dépassement micro-bnc): https://bofip.impots.gouv.fr/bofip/4807-PGP.html + Bofip (dépassement micro-bic): https://bofip.impots.gouv.fr/bofip/1802-PGP.html + autoentrepreneur.urssaf.fr: https://www.autoentrepreneur.urssaf.fr/portail/accueil/une-question/questions-frequentes.html + unité: €/an + # TODO: les seuils micro sont dupliqués à plusieurs endroits (artiste-auteur . + # revenus . BNC . contrôle micro-bnc, tableau de la comparaison de régime, + # économie collaborative). Il faudrait référencer la même valeur partout où + # elle est utilisée. + une de ces conditions: + - entreprise . chiffre d'affaires > 176200 €/an + - entreprise . chiffre d'affaires . service > 72600 €/an + +entreprise . imposition . IR . information sur le report de déficit: + non applicable si: micro-fiscal + type: notification + formule: résultat fiscal < 0 €/an + description: | + Lorsque votre résultat fiscal est négatif, ce dernier vient réduire le revenu imposables du foyer fiscal. + Un déficit peut être imputé jusqu'à 6 ans après sa réalisation. + + [Voir les règles fiscales détaillées](https://bofip.impots.gouv.fr/bofip/2003-PGP.html/identifiant%3DBOI-BIC-DEF-20-10-20170301) + références: + bofip: https://bofip.impots.gouv.fr/bofip/2003-PGP.html/identifiant%3DBOI-BIC-DEF-20-10-20170301 + +entreprise . imposition . IS: + applicable si: imposition = 'IS' + valeur: oui + titre: Impôt sur les sociétés + +entreprise . imposition . IS . résultat imposable: + titre: Résultat de l'exercice + résumé: Imposable à l'impôt sur les sociétés + valeur: résultat fiscal + +entreprise . imposition . IS . information sur le report de déficit: + type: notification + formule: résultat imposable < 0 €/an + # TODO: Support des références dans les notifications + description: | + Les déficits subits au cours d'un exercice peuvent être reportés sur les exercices suivants (report en avant), ou sur le seul exercice précédent (report en arrière). + +entreprise . imposition . IS . résultat net: + résumé: Après déduction des charges et de l'impôt sur les société + somme: + - chiffre d'affaires + - (- charges) + - (- dirigeant . rémunération . totale) + - (- impôt sur les sociétés) + par défaut: 0€ + +entreprise . imposition . IS . impôt sur les sociétés: + unité: €/an + formule: + barème: + assiette: résultat imposable + multiplicateur: prorata temporis + variations: + - si: exercice . début >= 01/01/2022 + alors: + tranches: + - taux: 15% + plafond: plafond taux réduit 1 + - taux: 25% + - si: exercice . début >= 01/01/2021 + alors: + tranches: + - taux: 15% + plafond: plafond taux réduit 1 + - taux: 26.5% + - si: exercice . début >= 01/01/2020 + alors: + tranches: + - taux: 15% + plafond: plafond taux réduit 1 + - taux: 28% + - si: exercice . début >= 01/01/2019 + alors: + tranches: + - taux: 15% + plafond: plafond taux réduit 1 + - taux: 28% + plafond: plafond taux réduit 2 + - taux: 31% + - si: exercice . début >= 01/01/2018 + alors: + tranches: + - taux: 15% + plafond: plafond taux réduit 1 + - taux: 28% + plafond: plafond taux réduit 2 + - taux: 33.3333% + arrondi: oui + références: + Fiche impots.gouv.fr: https://www.impots.gouv.fr/portail/international-professionnel/impot-sur-les-societes + Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23575 + +entreprise . imposition . IS . impôt sur les sociétés . plafond taux réduit 1: + applicable si: éligible taux réduit + valeur: 38120 €/an + +entreprise . imposition . IS . impôt sur les sociétés . plafond taux réduit 2: + applicable si: éligible taux réduit + valeur: 500000 €/an + +entreprise . imposition . IS . impôt sur les sociétés . éligible taux réduit: + formule: + toutes ces conditions: + - chiffre d'affaires <= 7630 k€/an * prorata temporis + - nom: capital détenu au moins à 75 pourcents par des personnes physiques + valeur: oui + +entreprise . imposition . IS . impôt sur les sociétés . prorata temporis: + description: | + Lorsque la durée de l’exercice n'est pas égale à un an, on pro-ratise les + plafonds utilisés dans le barème de l'impôt sur les sociétés. + unité: '%' + formule: exercice . durée / 1 an + # TODO: c'est un peu plus subtil que cela : « En cas d’exercice ouvert ou + # arrêté en cours de mois calendaire, le nombre de jours résiduels concourt à + # la détermination du rapport pour un montant égal au rapport existant entre + # ce nombre et 30. » + références: + Bofip: https://bofip.impots.gouv.fr/bofip/2065-PGP.html/identifiant%3DBOI-IS-LIQ-20-20-20180801 + +entreprise . imposition . IS . impôt sur les sociétés . contribution sociale: + # description: | + # La contribution sociale sur les bénéfices est un impôt distinct de l'impôt sur les sociétés. Son montant n'est pas déductible des résultats. + + # L'assiette bénéficie d'un abattement important, et seules les entreprises réalisant plus de 2,3 millions d'euros de bénéficie sont concernées par cette contribution. + description: | + La contribution sociale sur les bénéfices est un impôt distinct de l’impôt sur les sociétés. Son montant n’est pas déductible des résultats. + + L’assiette bénéficie d’un abattement important, et seules les entreprises réalisant plus de 2,3 millions d’euros de bénéfices sont concernées par cette contribution. + formule: + produit: + taux: 3.3% + assiette: + valeur: impôt sur les sociétés + abattement: 763000 €/an * prorata temporis + références: + Bofip: https://bofip.impots.gouv.fr/bofip/3492-PGP.html/identifiant%3DBOI-IS-AUT-10-20-20130318 diff --git "a/modele-social/r\303\250gles/entreprise/statut-juridique.yaml" "b/modele-social/r\303\250gles/entreprise/statut-juridique.yaml" new file mode 100644 index 0000000000..b8ad787670 --- /dev/null +++ "b/modele-social/r\303\250gles/entreprise/statut-juridique.yaml" @@ -0,0 +1,79 @@ +entreprise . catégorie juridique: + description: | + Les catégories juridiques accessibles via l'API SIRENE + par défaut: non + une possibilité: + choix obligatoire: oui + possibilités: + - EI + - SARL + - SAS + - autre + références: + liste des catégories juridique de l'INSEE: https://www.insee.fr/fr/information/2028129 + note: On se base ici sur les catégories juridiques définies par l'INSEE + +entreprise . catégorie juridique . EI: + titre: 'EI ou EIRL' + valeur: catégorie juridique = 'EI' + +entreprise . catégorie juridique . EI . auto-entrepreneur: + question: Êtes-vous auto-entrepreneur ? + remplace: + règle: imposition . IR . micro-fiscal + par: oui + + par défaut: oui + +entreprise . catégorie juridique . EI . responsabilité limité: + non applicable si: auto-entrepreneur # pour simplifier + titre: 'EIRL' + question: Votre entreprise est-elle une EIRL ? + par défaut: non + +entreprise . catégorie juridique . EI . imposition entreprise: + non applicable si: responsabilité limité + remplace: entreprise . imposition + valeur: "'IR'" + +entreprise . catégorie juridique . SARL: + titre: 'EURL ou SARL' + valeur: catégorie juridique = 'SARL' + +entreprise . catégorie juridique . SARL . unipersonnelle: + titre: EURL + question: Votre entreprise est-elle une EURL ? + par défaut: oui + +entreprise . catégorie juridique . SELARL: + titre: 'SELARL' + valeur: catégorie juridique = 'SELARL' + remplace: + - règle: entreprise . activité + par: "'libérale'" + - règle: entreprise . activité . libérale réglementée + par: oui + +entreprise . catégorie juridique . SELAS: + titre: 'SELARL' + valeur: catégorie juridique = 'SELAS' + remplace: + - règle: entreprise . activité + par: "'libérale'" + - règle: entreprise . activité . libérale réglementée + par: oui + +entreprise . catégorie juridique . SAS: + remplace: #TODO nous ne gérons pas encore les SASU à l'IR + règle: entreprise . imposition + par: "'IS'" + titre: 'SASU ou SAS' + valeur: catégorie juridique = 'SAS' + +entreprise . catégorie juridique . SAS . unipersonnelle: + titre: 'SASU' + question: Votre entreprise est-elle une SASU ? + par défaut: oui + +entreprise . catégorie juridique . autre: + valeur: catégorie juridique = 'autre' diff --git "a/modele-social/r\303\250gles/profession-lib\303\251rale.yaml" "b/modele-social/r\303\250gles/profession-lib\303\251rale.yaml" index 0c8214006e..9cddab3428 100644 --- "a/modele-social/r\303\250gles/profession-lib\303\251rale.yaml" +++ "b/modele-social/r\303\250gles/profession-lib\303\251rale.yaml" @@ -356,6 +356,7 @@ dirigeant . indépendant . PL . CNAVPL: l'organisme qui fédère les différentes caisses existantes (CIPAV, CARPIMKO, CARCDSF, CAVEC etc..) non applicable si: régime général + valeur: oui dirigeant . indépendant . PL . CNAVPL . retraite: titre: retraite de base (CNAVPL) diff --git "a/modele-social/r\303\250gles/salari\303\251.yaml" "b/modele-social/r\303\250gles/salari\303\251.yaml" index 660daab1f4..ba32d5678e 100644 --- "a/modele-social/r\303\250gles/salari\303\251.yaml" +++ "b/modele-social/r\303\250gles/salari\303\251.yaml" @@ -1255,6 +1255,7 @@ contrat salarié . stage . avertissement: description: >- Une convention de stage **n'est pas un contrat de travail**, et ne peut pas être conclue pour réaliser une tâche régulière correspondant à un poste de travail permanent, ou à un accroissement temporaire de l'activité de l'entreprise. [Code de l'éducation - Article L124-7](https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029234119&cidTexte=LEGITEXT000006071191) + Par ailleurs, une entreprise de moins de 20 salariés ne peut pas accueillir plus de **3 stagiaires**, et pas plus de **15% de l’effectif** pour les entreprises de plus de 20 salariés. contrat salarié . stage . contrôle gratification minimale: @@ -3419,7 +3420,7 @@ contrat salarié . régime des impatriés: contrat salarié . régime des impatriés . information: type: notification formule: oui - description: >- + description: |- Pour bénéficier de l'exonération de cotisations vieillesse, il faut remplir les conditions suivantes : - Pouvoir justifier d'une contribution minimale versée ailleurs pour une assurance vieillesse - Ne pas avoir été affilié, au cours des cinq années civiles précédant celle de la prise de fonctions, à un régime français obligatoire d'assurance vieillesse, sauf pour des activités accessoires, de caractère saisonnier ou pour les études. diff --git a/package.json b/package.json index 766965443e..aacf9f62f5 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ }, "resolutions": { "prettier": "^2.5.1", - "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest" + "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest", + "publicodes": "portal:/home/johan/Projets/publicodes/packages/core", + "publicodes-react": "portal:/home/johan/Projets/publicodes/packages/react-ui" }, "packageManager": "yarn@3.2.0", "devDependencies": { diff --git a/site/cypress/integration/mon-entreprise/english/navigation.js b/site/cypress/integration/mon-entreprise/english/navigation.js index 842cb7dd05..1106ffaae3 100644 --- a/site/cypress/integration/mon-entreprise/english/navigation.js +++ b/site/cypress/integration/mon-entreprise/english/navigation.js @@ -55,7 +55,7 @@ describe(`Navigation to income simulator using company name (${ }) it('should allow to retrieve company and show link corresponding to the legal status', function () { cy.contains( - fr ? 'Rechercher une entreprise ' : 'Search for a company ' + fr ? 'Rechercher votre entreprise ' : 'Search for a company ' ).click() cy.get('input').first().type('menoz') cy.contains('834364291').click() @@ -66,7 +66,7 @@ describe(`Navigation to income simulator using company name (${ }) it('should allow auto entrepreneur to access the corresponding income simulator', function () { cy.contains( - fr ? 'Rechercher une entreprise ' : 'Search for a company ' + fr ? 'Rechercher votre entreprise ' : 'Search for a company ' ).click() cy.get('input').first().type('johan girod') cy.contains('834825614').click() diff --git a/site/cypress/integration/mon-entreprise/localisation-field.js b/site/cypress/integration/mon-entreprise/localisation-field.js index 2b7591702b..33013e84bc 100644 --- a/site/cypress/integration/mon-entreprise/localisation-field.js +++ b/site/cypress/integration/mon-entreprise/localisation-field.js @@ -17,7 +17,7 @@ describe('Champs localisation (simulateur salarié)', function () { .type('Steenvoorde') cy.contains('Steenvoorde (59114)').click({ force: true }) cy.contains('Suivant').click({ force: true }) - cy.contains('Voir mes paramètres').click({ force: true }) + cy.contains('Voir ma situation').click({ force: true }) cy.contains('Steenvoorde') }) }) diff --git a/site/cypress/integration/mon-entreprise/partage-simulation.js b/site/cypress/integration/mon-entreprise/partage-simulation.js index ab512d3228..2853320fef 100644 --- a/site/cypress/integration/mon-entreprise/partage-simulation.js +++ b/site/cypress/integration/mon-entreprise/partage-simulation.js @@ -21,7 +21,7 @@ describe('Partage (simulateur salarié)', function () { .invoke('val') .should('match', /1[\s]539[\s]€/) - cy.contains('Voir mes paramètres').click() + cy.contains('Voir ma situation').click() cy.contains('CDD') }) it('should set URL from input value', function () { diff --git a/site/cypress/integration/mon-entreprise/simulateur-salarie.js b/site/cypress/integration/mon-entreprise/simulateur-salarie.js index a64515bacc..67c430971c 100644 --- a/site/cypress/integration/mon-entreprise/simulateur-salarie.js +++ b/site/cypress/integration/mon-entreprise/simulateur-salarie.js @@ -11,7 +11,7 @@ describe('Simulateur salarié', function () { describe('part time contract', function () { before(function () { cy.get('button').contains('SMIC').click() - cy.contains('Voir mes paramètres').click() + cy.contains('Voir ma situation').click() cy.get('div[role="dialog"]').contains('Temps partiel').click() cy.contains('Oui').click() cy.wait(100) diff --git a/site/source/App.tsx b/site/source/App.tsx index bcf358ce65..744fe7c18c 100644 --- a/site/source/App.tsx +++ b/site/source/App.tsx @@ -1,10 +1,3 @@ -import rules from 'modele-social' -import { StrictMode, useContext, useMemo } from 'react' -import { Helmet } from 'react-helmet-async' -import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' -import { Redirect, Route, Switch } from 'react-router-dom' -import styled, { css } from 'styled-components' import Footer from '@/components/layout/Footer/Footer' import Header from '@/components/layout/Header' import Route404 from '@/components/Route404' @@ -18,9 +11,17 @@ import { import { SitePathsContext } from '@/components/utils/SitePathsContext' import { Container, Spacing } from '@/design-system/layout' import { + companySituationSelector, configSituationSelector, situationSelector, } from '@/selectors/simulationSelectors' +import rules from 'modele-social' +import { StrictMode, useContext, useMemo } from 'react' +import { Helmet } from 'react-helmet-async' +import { useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' +import { Redirect, Route, Switch } from 'react-router-dom' +import styled, { css } from 'styled-components' import Accessibilité from './pages/Accessibilité' import Budget from './pages/Budget/Budget' import Créer from './pages/Creer' @@ -39,9 +40,13 @@ import Provider, { ProviderProps } from './Provider' import redirects from './redirects' import { constructLocalizedSitePath } from './sitePaths' import { - retrievePersistedInFranceApp, - setupInFranceAppPersistence, -} from './storage/persistInFranceApp' + retrievePersistedChoixStatutJuridique, + setupChoixStatutJuridiquePersistence, +} from './storage/persistChoixStatutJuridique' +import { + retrievePersistedCompanySituation, + setupCompanySituationPersistence, +} from './storage/persistCompanySituation' import { setupSimulationPersistence } from './storage/persistSimulation' type RootProps = { @@ -71,11 +76,13 @@ export default function Root({ basename={basename} sitePaths={paths} onStoreCreated={(store) => { - setupInFranceAppPersistence(store) + setupChoixStatutJuridiquePersistence(store) + setupCompanySituationPersistence(store) setupSimulationPersistence(store) }} initialStore={{ - inFranceApp: retrievePersistedInFranceApp(), + choixStatutJuridique: retrievePersistedChoixStatutJuridique(), + companySituation: retrievePersistedCompanySituation(), }} > @@ -87,14 +94,16 @@ export default function Root({ } const Router = () => { - const userSituation = useSelector(situationSelector) + const simulatorSituation = useSelector(situationSelector) const configSituation = useSelector(configSituationSelector) + const companySituation = useSelector(companySituationSelector) const situation = useMemo( () => ({ + ...companySituation, ...configSituation, - ...userSituation, + ...simulatorSituation, }), - [configSituation, userSituation] + [configSituation, simulatorSituation, companySituation] ) return ( diff --git a/site/source/actions/actions.ts b/site/source/actions/actions.ts index 4cbbf22edf..6616341a8e 100644 --- a/site/source/actions/actions.ts +++ b/site/source/actions/actions.ts @@ -1,9 +1,9 @@ import { DottedName } from 'modele-social' import Engine from 'publicodes' -import { SimulationConfig, Situation } from '@/reducers/rootReducer' +import { SimulationConfig } from '@/reducers/rootReducer' import { CompanyCreationAction } from './companyCreationChecklistActions' import { CompanyStatusAction } from './companyStatusActions' -import { ActionExistingCompany } from './existingCompanyActions' +import { CompanyActions } from './companyActions' import { HiringChecklistAction } from './hiringChecklistAction' export type Action = @@ -23,7 +23,7 @@ export type Action = > | CompanyCreationAction | CompanyStatusAction - | ActionExistingCompany + | CompanyActions | HiringChecklistAction export const resetSimulation = () => @@ -46,16 +46,11 @@ export const stepAction = (step: DottedName, source?: string) => source, } as const) -export const setSimulationConfig = ( - config: SimulationConfig, - url: string, - initialSituation?: Situation -) => +export const setSimulationConfig = (config: SimulationConfig, url: string) => ({ type: 'SET_SIMULATION', url, config, - initialSituation, } as const) export const setActiveTarget = (targetName: DottedName) => @@ -72,7 +67,7 @@ export const updateSituation = (fieldName: DottedName, value: unknown) => } as const) export const batchUpdateSituation = ( - situation: Parameters['setSituation']>[0] + situation: NonNullable['setSituation']>[0]> ) => ({ type: 'BATCH_UPDATE_SITUATION', diff --git a/site/source/actions/companyActions.ts b/site/source/actions/companyActions.ts new file mode 100644 index 0000000000..fd58375615 --- /dev/null +++ b/site/source/actions/companyActions.ts @@ -0,0 +1,24 @@ +import { FabriqueSocialEntreprise } from '@/api/fabrique-social' +import { ApiCommuneJson } from '@/components/conversation/select/SelectCommune' + +export type CompanyActions = ReturnType< + typeof resetCompany | typeof setCompany | typeof addCommuneDetails +> + +export const resetCompany = () => + ({ + type: 'COMPANY::RESET', + } as const) + +export const addCommuneDetails = (details: ApiCommuneJson) => + ({ + type: 'COMPANY::ADD_COMMUNE_DETAILS', + details, + } as const) + +export const setCompany = (entreprise: FabriqueSocialEntreprise) => { + return { + type: 'COMPANY::SET_EXISTING_COMPANY', + entreprise, + } as const +} diff --git a/site/source/actions/companyStatusActions.ts b/site/source/actions/companyStatusActions.ts index 237e3c879a..aabf4fb248 100644 --- a/site/source/actions/companyStatusActions.ts +++ b/site/source/actions/companyStatusActions.ts @@ -6,7 +6,7 @@ import { useHistory } from 'react-router' import { useNextQuestionUrl } from '@/selectors/companyStatusSelectors' import { LegalStatusRequirements } from '@/types/companyTypes' import { Action } from './actions' -import { addCommuneDetails, setCompany } from './existingCompanyActions' +import { addCommuneDetails, setCompany } from './companyActions' export type CompanyStatusAction = ReturnType< | typeof isSoleProprietorship @@ -78,21 +78,18 @@ const fetchCommuneDetails = async function (codeCommune: string) { const response = await fetch( `https://geo.api.gouv.fr/communes/${codeCommune}?fields=departement,region` ) - return await response.json() + return (await response.json()) as ApiCommuneJson } export const useSetEntreprise = () => { const dispatch = useDispatch() - return async (entreprise: FabriqueSocialEntreprise) => { + return (entreprise: FabriqueSocialEntreprise) => { if (entreprise === null) { return } dispatch(setCompany(entreprise)) - if (entreprise.firstMatchingEtablissement.is_siege) { - const communeDetails: ApiCommuneJson = await fetchCommuneDetails( - entreprise.firstMatchingEtablissement.codeCommuneEtablissement - ) - dispatch(addCommuneDetails(communeDetails)) - } + void fetchCommuneDetails( + entreprise.firstMatchingEtablissement.codeCommuneEtablissement + ).then((communeDetails) => dispatch(addCommuneDetails(communeDetails))) } } diff --git a/site/source/actions/existingCompanyActions.ts b/site/source/actions/existingCompanyActions.ts deleted file mode 100644 index 71c182b372..0000000000 --- a/site/source/actions/existingCompanyActions.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { FabriqueSocialEntreprise } from '@/api/fabrique-social' -import { ApiCommuneJson } from '@/components/conversation/select/SelectCommune' - -export type ActionExistingCompany = ReturnType< - | typeof specifyIfAutoEntrepreneur - | typeof specifyIfDirigeantMajoritaire - | typeof resetEntreprise - | typeof setCompany - | typeof addCommuneDetails -> - -export const specifyIfAutoEntrepreneur = (isAutoEntrepreneur: boolean) => - ({ - type: 'EXISTING_COMPANY::SPECIFY_AUTO_ENTREPRENEUR', - isAutoEntrepreneur, - } as const) - -export const specifyIfDirigeantMajoritaire = ( - isDirigeantMajoritaire: boolean -) => - ({ - type: 'EXISTING_COMPANY::SPECIFY_DIRIGEANT_MAJORITAIRE', - isDirigeantMajoritaire, - } as const) - -export const resetEntreprise = () => - ({ - type: 'EXISTING_COMPANY::RESET', - } as const) - -export const addCommuneDetails = (details: ApiCommuneJson) => - ({ - type: 'EXISTING_COMPANY::ADD_COMMUNE_DETAILS', - details, - } as const) - -export const setCompany = (entreprise: FabriqueSocialEntreprise) => { - return { - type: 'EXISTING_COMPANY::SET_COMPANY', - entreprise, - } as const -} diff --git "a/site/source/components/ChiffreAffairesActivit\303\251Mixte.tsx" "b/site/source/components/ChiffreAffairesActivit\303\251Mixte.tsx" index 3273a43c23..c64e61ec35 100644 --- "a/site/source/components/ChiffreAffairesActivit\303\251Mixte.tsx" +++ "b/site/source/components/ChiffreAffairesActivit\303\251Mixte.tsx" @@ -108,7 +108,6 @@ function ActivitéMixte() { const rule = useEngine().getRule('entreprise . activité . mixte') const defaultChecked = useEngine().evaluate('entreprise . activité . mixte').nodeValue === true - const onMixteChecked = useCallback( (checked: boolean) => { dispatch( @@ -124,21 +123,23 @@ function ActivitéMixte() { ) return ( - - - - Activité mixte - - - - {rule.rawNode.description ?? ''} - - +
+ + + + Activité mixte + + + + {rule.rawNode.description ?? ''} + + +
) } diff --git a/site/source/components/EngineValue.tsx b/site/source/components/EngineValue.tsx index 3383428e7c..43afc6728a 100644 --- a/site/source/components/EngineValue.tsx +++ b/site/source/components/EngineValue.tsx @@ -5,7 +5,6 @@ import Engine, { isNotApplicable, isNotYetDefined, PublicodesExpression, - UNSAFE_isNotApplicable, } from 'publicodes' import React from 'react' import { useTranslation } from 'react-i18next' @@ -94,7 +93,9 @@ export function WhenApplicable({ children: React.ReactNode }) { const engine = useEngine() - if (UNSAFE_isNotApplicable(engine, dottedName)) return null + if (engine.evaluate(dottedName).nodeValue == null) { + return null + } return <>{children} } export function WhenNotApplicable({ @@ -105,7 +106,9 @@ export function WhenNotApplicable({ children: React.ReactNode }) { const engine = useEngine() - if (!UNSAFE_isNotApplicable(engine, dottedName)) return null + if (engine.evaluate(dottedName).nodeValue !== null) { + return null + } return <>{children} } @@ -122,3 +125,17 @@ export function WhenAlreadyDefined({ } return <>{children} } + +export function WhenNotAlreadyDefined({ + dottedName: dottedName, + children, +}: { + dottedName: DottedName + children: React.ReactNode +}) { + const engine = useEngine() + if (!isNotYetDefined(engine.evaluate(dottedName).nodeValue)) { + return null + } + return <>{children} +} diff --git a/site/source/components/Notifications.tsx b/site/source/components/Notifications.tsx index 16fe72d4f5..8957708347 100644 --- a/site/source/components/Notifications.tsx +++ b/site/source/components/Notifications.tsx @@ -11,6 +11,7 @@ import styled from 'styled-components' import RuleLink from './RuleLink' import Emoji from './utils/Emoji' import { Markdown } from './utils/markdown' +import { Message } from '@/design-system' // To add a new notification to a simulator, you should create a publicodes rule // with the "type: notification" attribute. The display can be customized with @@ -62,18 +63,23 @@ export default function Notifications() { ).filter(({ dottedName }) => !hiddenNotifications?.includes(dottedName)) return ( - +
{messages.map(({ sévérité, dottedName, résumé, description }) => ( - - - - {résumé ?? description ?? ''}{' '} - {résumé && ( - - En savoir plus - - )} - + + {résumé ?? description ?? ''}{' '} + {résumé && ( + + En savoir plus + + )} × - + ))} - +
) } -const NotificationsContainer = styled.ul` - list-style-type: none; - padding: 0; -` - -const Notification = styled.li` - display: flex; - position: relative; - flex-direction: row; - align-items: center; - - padding: 0.5rem 1rem; - background-color: ${({ theme }) => theme.colors.bases.primary[100]}; - border: 2px solid; - border-color: ${({ theme }) => theme.colors.bases.primary[500]}; - border-radius: 0.375rem; - - margin-bottom: 1rem; - - &:last-child { - margin-bottom: 0; - } - & img { - height: ${({ theme }) => theme.spacings.xl} !important; - width: ${({ theme }) => theme.spacings.xl} !important; - margin-right: ${({ theme }) => theme.spacings.sm} !important; - } -` - -const NotificationContent = styled.div` - flex-grow: 1; - margin-right: 2rem; - margin-left: 0.5rem; -` - const HideButton = styled(Button)` && { display: flex; diff --git a/site/source/components/SchemeComparaison.tsx b/site/source/components/SchemeComparaison.tsx index ebc09eea39..dd00976faa 100644 --- a/site/source/components/SchemeComparaison.tsx +++ b/site/source/components/SchemeComparaison.tsx @@ -55,7 +55,7 @@ export default function SchemeComparaison({ () => engine.shallowCopy().setSituation({ ...situation, - dirigeant: "'assimilé salarié'", + 'dirigeant . régime social': "'assimilé salarié'", }), [situation] ) @@ -63,7 +63,7 @@ export default function SchemeComparaison({ () => engine.shallowCopy().setSituation({ ...situation, - dirigeant: "'auto-entrepreneur'", + 'dirigeant . régime social': "'auto-entrepreneur'", }), [situation] ) @@ -71,7 +71,7 @@ export default function SchemeComparaison({ () => engine.shallowCopy().setSituation({ ...situation, - dirigeant: "'indépendant'", + 'dirigeant . régime social': "'indépendant'", }), [situation] ) diff --git a/site/source/components/ShareSimulationBanner/index.tsx b/site/source/components/ShareSimulationBanner/index.tsx index 8f2c09abd9..b3d42f3a38 100644 --- a/site/source/components/ShareSimulationBanner/index.tsx +++ b/site/source/components/ShareSimulationBanner/index.tsx @@ -7,7 +7,10 @@ import { CurrentSimulatorDataContext } from '../../pages/Simulateurs/metadata' import { useContext } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' -import { situationSelector } from '@/selectors/simulationSelectors' +import { + companySituationSelector, + situationSelector, +} from '@/selectors/simulationSelectors' import styled from 'styled-components' import { TrackingContext } from '../../ATInternetTracking' import { useParamsFromSituation } from '../utils/useSearchParamsSimulationSharing' @@ -15,7 +18,11 @@ import { ShareSimulationPopup } from './ShareSimulationPopup' export function useUrl() { const language = useTranslation().i18n.language - const situation = useSelector(situationSelector) + const situation = { + ...useSelector(situationSelector), + ...useSelector(companySituationSelector), + } + delete situation['entreprise . SIREN'] const searchParams = useParamsFromSituation(situation) const currentSimulatorData = useContext(CurrentSimulatorDataContext) diff --git a/site/source/components/Simulation/index.tsx b/site/source/components/Simulation/index.tsx index 6ded6f9a15..695fa6e626 100644 --- a/site/source/components/Simulation/index.tsx +++ b/site/source/components/Simulation/index.tsx @@ -1,13 +1,20 @@ -import { Grid, styled } from '@mui/material' import { ConversationProps } from '@/components/conversation/Conversation' import PageFeedback from '@/components/Feedback' import ShareOrSaveSimulationBanner from '@/components/ShareSimulationBanner' +import { PopoverWithTrigger } from '@/design-system' import { Spacing } from '@/design-system/layout' +import { Link } from '@/design-system/typography/link' +import { + companySituationSelector, + firstStepCompletedSelector, +} from '@/selectors/simulationSelectors' +import { Grid, styled } from '@mui/material' import React from 'react' import { Trans } from 'react-i18next' import { useSelector } from 'react-redux' -import { firstStepCompletedSelector } from '@/selectors/simulationSelectors' import { TrackPage } from '../../ATInternetTracking' +import Banner from '../Banner' +import AnswerList from '../conversation/AnswerList' import PreviousSimulationBanner from './../PreviousSimulationBanner' import ExportRecover from './../simulationExplanation/ExportRecover' import { FadeIn, FromTop } from './../ui/animate' @@ -42,6 +49,9 @@ export default function Simulation({ customEndMessages, }: SimulationProps) { const firstStepCompleted = useSelector(firstStepCompletedSelector) + const existingCompany = !!useSelector(companySituationSelector)[ + 'entreprise . SIREN' + ] return ( <> {!firstStepCompleted && } @@ -53,6 +63,7 @@ export default function Simulation({
{!firstStepCompleted && ( <> + {afterQuestionsSlot} @@ -62,10 +73,30 @@ export default function Simulation({ {results} - {afterQuestionsSlot || } + + {afterQuestionsSlot} + + )} + {existingCompany && ( + + Ce simulateur a été prérempli avec les données de votre + entreprise.{' '} + ( + + Voir ma situation + + )} + > + {(close) => } + + + )} + {firstStepCompleted && ( + <> - + )}
diff --git a/site/source/components/TypeFormEmbed.js b/site/source/components/TypeFormEmbed.js deleted file mode 100644 index ae15cc7609..0000000000 --- a/site/source/components/TypeFormEmbed.js +++ /dev/null @@ -1,35 +0,0 @@ -import { Helmet } from 'react-helmet-async' - -let createQueryParams = (params) => - Object.keys(params) - .map((k) => `${k}=${encodeURI(params[k])}`) - .join('&') - -let url = (hiddenVariables) => - 'https://embauchegouv.typeform.com/to/dvbINf?' + - createQueryParams(hiddenVariables) - -let TypeFormEmbed = ({ hiddenVariables }) => ( - -) - -export default TypeFormEmbed diff --git a/site/source/components/company/Details.tsx b/site/source/components/company/Details.tsx new file mode 100644 index 0000000000..4c0aa6b879 --- /dev/null +++ b/site/source/components/company/Details.tsx @@ -0,0 +1,34 @@ +import { Message } from '@/design-system' +import { CardContainer } from '@/design-system/card/Card' +import { Strong } from '@/design-system/typography' +import { H4 } from '@/design-system/typography/heading' +import { Body, Intro } from '@/design-system/typography/paragraphs' +import styled from 'styled-components' +import Value from '../EngineValue' + +export function CompanyDetails() { + return ( + +

+ {' '} + {' '} + +

+ + Crée le{' '} + + + {' '} + et domiciliée à{' '} + + + + +
+ ) +} + +const StyledCompanyContainer = styled(Message).attrs({ border: false })`` diff --git a/site/source/components/CompanyDetails.tsx b/site/source/components/company/SearchDetails.tsx similarity index 52% rename from site/source/components/CompanyDetails.tsx rename to site/source/components/company/SearchDetails.tsx index 222c40f4e3..d3901ca334 100644 --- a/site/source/components/CompanyDetails.tsx +++ b/site/source/components/company/SearchDetails.tsx @@ -1,26 +1,20 @@ import { FabriqueSocialEntreprise } from '@/api/fabrique-social' +import { Spacing } from '@/design-system/layout' import { Strong } from '@/design-system/typography' -import { H3 } from '@/design-system/typography/heading' -import { Body } from '@/design-system/typography/paragraphs' +import { H4 } from '@/design-system/typography/heading' import { useMemo } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { Company } from '@/reducers/inFranceAppReducer' import styled from 'styled-components' -export default function CompanyDetails({ +export default function CompanySearchDetails({ entreprise, }: { - entreprise: FabriqueSocialEntreprise | Company + entreprise: FabriqueSocialEntreprise }) { const { i18n } = useTranslation() - const { - siren, - label, - dateCreationUniteLegale, - firstMatchingEtablissement, - allMatchingEtablissements, - } = entreprise + const { siren, label, dateCreationUniteLegale, firstMatchingEtablissement } = + entreprise const DateFormatter = useMemo( () => @@ -40,12 +34,12 @@ export default function CompanyDetails({ // // ) // } - const siege = allMatchingEtablissements.find((e) => e.is_siege) return ( -

<> @@ -53,33 +47,20 @@ export default function CompanyDetails({ ? highlightLabelToJSX(entreprise.highlightLabel) : label}{' '} ({siren}) - {' '} -

- - - {dateCreationUniteLegale && ( -
- Crée le{' '} - - {DateFormatter.format(new Date(dateCreationUniteLegale))} - -
- )} -
{firstMatchingEtablissement.address}
- {siege && - allMatchingEtablissements.length > 1 && - siege.address !== firstMatchingEtablissement.address && ( -
- Siège : {siege.address} -
- )} -
+ + + + Crée le :{' '} + {DateFormatter.format(new Date(dateCreationUniteLegale))} +
+ Domiciliée à l'adresse :{' '} + {firstMatchingEtablissement.address}
) } function highlightLabelToJSX(highlightLabel: string) { - const highlightRE = /(.*?)(\w+)<\/u><\/b>/gm + const highlightRE = /(.*?)(.+?)<\/u><\/b>/gm let parsedLength = 0 const result = [] let matches @@ -97,16 +78,9 @@ function highlightLabelToJSX(highlightLabel: string) { } const Highlight = styled.strong` - text-decoration: underline; + background-color: ${({ theme }) => theme.colors.bases.secondary[100]}; ` const CompanyContainer = styled.div` - margin-top: 0.5rem; - margin-bottom: 0.5rem; text-align: left; ` - -const InfoContainer = styled(Body)` - display: flex; - flex-direction: column; -` diff --git a/site/source/components/CompanySearchField.tsx b/site/source/components/company/SearchField.tsx similarity index 93% rename from site/source/components/CompanySearchField.tsx rename to site/source/components/company/SearchField.tsx index 1db75e8589..ff4d4542f8 100644 --- a/site/source/components/CompanySearchField.tsx +++ b/site/source/components/company/SearchField.tsx @@ -11,8 +11,8 @@ import useSearchCompany from '@/hooks/useSearchCompany' import { ReactNode, useEffect } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import CompanyDetails from './CompanyDetails' -import { FromTop } from './ui/animate' +import CompanySearchDetails from './SearchDetails' +import { FromTop } from '../ui/animate' const StyledCard = styled(Card)` flex-direction: row; // for Safari <= 13 @@ -100,9 +100,9 @@ function Results({ {results.map((etablissement) => ( - + onSubmit?.(etablissement)} compact> - + ))} diff --git a/site/source/components/conversation/AnswerList.tsx b/site/source/components/conversation/AnswerList.tsx index 0310b53ac2..dd7305151e 100644 --- a/site/source/components/conversation/AnswerList.tsx +++ b/site/source/components/conversation/AnswerList.tsx @@ -1,40 +1,36 @@ import { goToQuestion, resetSimulation } from '@/actions/actions' +import { resetCompany } from '@/actions/companyActions' import Emoji from '@/components/utils/Emoji' import { useEngine } from '@/components/utils/EngineContext' import { useNextQuestions } from '@/components/utils/useNextQuestion' import { Button } from '@/design-system/buttons' -import { H2 } from '@/design-system/typography/heading' +import { Spacing } from '@/design-system/layout' +import { H2, H3 } from '@/design-system/typography/heading' import { Link } from '@/design-system/typography/link' -import { DottedName } from 'modele-social' -import { EvaluatedNode, formatValue } from 'publicodes' -import { useMemo } from 'react' -import { Trans, useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' import { answeredQuestionsSelector, + companySituationSelector, situationSelector, } from '@/selectors/simulationSelectors' +import { Grid } from '@mui/material' +import { DottedName } from 'modele-social' +import { EvaluatedNode } from 'publicodes' +import { useMemo } from 'react' +import { Trans } from 'react-i18next' +import { useDispatch, useSelector } from 'react-redux' import styled from 'styled-components' +import Value from '../EngineValue' import './AnswerList.css' type AnswerListProps = { onClose: () => void } -const Header = styled.div` - display: flex; - flex-direction: row; - align-items: center; -` - -const Title = styled(H2)` - flex-grow: 1; -` - export default function AnswerList({ onClose }: AnswerListProps) { const dispatch = useDispatch() const engine = useEngine() const situation = useSelector(situationSelector) + const companySituation = useSelector(companySituationSelector) const passedQuestions = useSelector(answeredQuestionsSelector) const answeredAndPassedQuestions = useMemo( () => @@ -43,37 +39,79 @@ export default function AnswerList({ onClose }: AnswerListProps) { (answered) => !passedQuestions.some((passed) => answered === passed) ) .concat(passedQuestions) + .filter((answered) => !(answered in companySituation)) .map((dottedName) => engine.evaluate(engine.getRule(dottedName))), - [engine, passedQuestions, situation] + [engine, passedQuestions, situation, companySituation] ) - const nextSteps = useNextQuestions().map((dottedName) => engine.evaluate(engine.getRule(dottedName)) ) + const companyQuestions = useMemo( + () => + (Object.keys(companySituation) as DottedName[]).map((dottedName) => + engine.evaluate(engine.getRule(dottedName)) + ), + [engine, companySituation] + ) return (
+

+ + Ma situation +

+ {!!answeredAndPassedQuestions.length && ( <> -
- - <Emoji emoji="📋 " /> - <Trans>Mes réponses</Trans> - +

+ Données de simulation +

+ + + +
-
- +
+ + )} + + {companyQuestions.length > 0 && ( + <> +

+ Données de l'entreprise +

+ + +
+ +
)} + {!!nextSteps.length && ( <>

@@ -87,16 +125,6 @@ export default function AnswerList({ onClose }: AnswerListProps) { ) } -const TBody = styled.tbody` - font-family: ${({ theme }) => theme.fonts.main}; - & > tr > td { - padding: 0.5rem 0.75rem; - } - & > tr:nth-child(2n) { - background-color: ${({ theme }) => theme.colors.bases.primary[100]}; - } -` - function StepsTable({ rules, onClose, @@ -105,28 +133,45 @@ function StepsTable({ onClose: () => void }) { const dispatch = useDispatch() - const language = useTranslation().i18n.language return ( - - - {rules.map((rule) => ( - - - - + + ))} - -
+ <> + {rules + .filter((rule) => rule.nodeValue !== null) + .map((rule) => ( + + + {rule.title} + + { dispatch(goToQuestion(rule.dottedName)) onClose() }} + title="Modifier" > - {rule.title} + {' '} + - - {formatValue(rule, { language })} -
+ ) } + +const StyledAnswer = styled(Grid)` + text-align: right; +` +const StyledAnswerList = styled(Grid)` + padding: ${({ theme }) => theme.spacings.xs}; + margin: 0 -${({ theme }) => theme.spacings.xs}; + font-family: ${({ theme }) => theme.fonts.main}; + :nth-child(2n) { + background-color: ${({ theme }) => theme.colors.bases.primary[100]}; + } +` diff --git a/site/source/components/conversation/Conversation.tsx b/site/source/components/conversation/Conversation.tsx index fc241ac557..0c9b949e7d 100644 --- a/site/source/components/conversation/Conversation.tsx +++ b/site/source/components/conversation/Conversation.tsx @@ -24,10 +24,14 @@ import { ExplicableRule } from './Explicable' import SeeAnswersButton from './SeeAnswersButton' export type ConversationProps = { + displayNotification: boolean customEndMessages?: React.ReactNode } -export default function Conversation({ customEndMessages }: ConversationProps) { +export default function Conversation({ + customEndMessages, + displayNotification = true, +}: ConversationProps) { const dispatch = useDispatch() const engine = useContext(EngineContext) const currentQuestion = useNextQuestions()[0] @@ -107,7 +111,7 @@ export default function Conversation({ customEndMessages }: ConversationProps) { - + {displayNotification && } diff --git a/site/source/components/conversation/SeeAnswersButton.tsx b/site/source/components/conversation/SeeAnswersButton.tsx index 42ebea2010..51f6b157f9 100644 --- a/site/source/components/conversation/SeeAnswersButton.tsx +++ b/site/source/components/conversation/SeeAnswersButton.tsx @@ -9,7 +9,7 @@ export default function SeeAnswersButton() { ( )} > diff --git "a/site/source/components/simulationExplanation/Ind\303\251pendantCotisationsForfaitaires.tsx" "b/site/source/components/simulationExplanation/Ind\303\251pendantCotisationsForfaitaires.tsx" index 7ad1d27a43..be637c4f5a 100644 --- "a/site/source/components/simulationExplanation/Ind\303\251pendantCotisationsForfaitaires.tsx" +++ "b/site/source/components/simulationExplanation/Ind\303\251pendantCotisationsForfaitaires.tsx" @@ -2,6 +2,7 @@ import Value from '@/components/EngineValue' import { FromBottom } from '@/components/ui/animate' import { useEngine } from '@/components/utils/EngineContext' import { Markdown } from '@/components/utils/markdown' +import { Message } from '@/design-system' import { Button } from '@/design-system/buttons' import { Spacing } from '@/design-system/layout' import { H3 } from '@/design-system/typography/heading' @@ -14,7 +15,7 @@ export default function CotisationsForfaitaires() { ) return ( -
+

{rule.title}

@@ -26,7 +27,7 @@ export default function CotisationsForfaitaires() { {rule.rawNode.description ?? ''} {rule.rawNode.références && ( <> - + + )} -
+
) } diff --git a/site/source/components/simulationExplanation/InstitutionsPartenaires.tsx b/site/source/components/simulationExplanation/InstitutionsPartenaires.tsx index 64257529da..0fae2efec2 100644 --- a/site/source/components/simulationExplanation/InstitutionsPartenaires.tsx +++ b/site/source/components/simulationExplanation/InstitutionsPartenaires.tsx @@ -19,6 +19,7 @@ import { Trans } from 'react-i18next' import { useSelector } from 'react-redux' import { targetUnitSelector } from '@/selectors/simulationSelectors' import styled from 'styled-components' +import { Message } from '@/design-system' export default function InstitutionsPartenaires() { const unit = useSelector(targetUnitSelector) @@ -30,44 +31,49 @@ export default function InstitutionsPartenaires() { Vos institutions partenaires

- - - - - - - - - - - - - - - - - En tant que professionnel de santé conventionné, vous - bénéficiez d'une prise en charge d'une partie de vos - cotisations par l'Assurance Maladie. - - - - {' '} - - - - - - - + + + + + + + + + + + + + + + + + + + En tant que professionnel de santé conventionné, vous + bénéficiez d'une prise en charge d'une partie de vos + cotisations par l'Assurance Maladie. + + + + {' '} + + + + + + + + + + @@ -202,37 +208,41 @@ export function InstitutionsPartenairesArtisteAuteur() { return (

Vos cotisations

- - - - Pour vos revenus en traitement et salaires, ces cotisations sont - « précomptées », c'est à dire payées à la source par le - diffuseur. - - - } - /> - - - - - - {descriptionIRCEC} - + + + + + Pour vos revenus en traitement et salaires, ces cotisations + sont « précomptées », c'est à dire payées à la source par le + diffuseur. + + + } /> - - - + + + + + + {descriptionIRCEC} + + + + + +
) } @@ -246,22 +256,19 @@ export function InstitutionsPartenairesAutoEntrepreneur() { Vos institutions partenaires
- - - - + + + + + + + + ) } -const InstitutionsTable = styled(Grid).attrs({ item: true, xl: 10 })` - border-radius: ${({ theme }) => theme.box.borderRadius}; - box-shadow: ${({ theme }) => theme.elevations[2]}; - padding: ${({ theme }) => theme.spacings.xs} - ${({ theme }) => theme.spacings.md}; -` - const InstitutionLogo = styled.a` img { max-width: 100%; @@ -273,13 +280,9 @@ const InstitutionLine = styled.div` display: flex; flex-direction: row; align-items: center; - padding: 1rem; + padding: ${({ theme }) => theme.spacings.md}; flex-wrap: wrap; - &:not(:first-child) { - border-top: 1px solid var(--lighterColor); - } - > ${InstitutionLogo} { display: block; width: 13ch; diff --git a/site/source/components/utils/Emoji.tsx b/site/source/components/utils/Emoji.tsx index 77f352cf17..6ae3eec4e4 100644 --- a/site/source/components/utils/Emoji.tsx +++ b/site/source/components/utils/Emoji.tsx @@ -2,13 +2,15 @@ import emojiFn from 'react-easy-emoji' import { useTranslation } from 'react-i18next' type PropType = { emoji: string | undefined + alt?: string + title?: string } // This custom component has several advantages over the direct use of the // `emojiFn` provided by `react-easy-emoji` : // - allow to configure the URL to self host twemoji images in production // - using a real React component works better with the translation scripts -export default function Emoji({ emoji }: PropType) { +export default function Emoji({ emoji, alt, title }: PropType) { const language = useTranslation().i18n.language const siteUrl = @@ -24,7 +26,11 @@ export default function Emoji({ emoji }: PropType) { ? { baseUrl: siteUrl + '/twemoji/2/', ext: '.png', + props: { + alt, + title, + }, } - : ({} as any) + : ({ props: { alt, title } } as any) ) } diff --git a/site/source/components/utils/EngineContext.tsx b/site/source/components/utils/EngineContext.tsx index 7c4649a3e8..df9fb3b136 100644 --- a/site/source/components/utils/EngineContext.tsx +++ b/site/source/components/utils/EngineContext.tsx @@ -14,8 +14,8 @@ const engineOptions = { return key || unit }, } -export function engineFactory(rules: Rules) { - return new Engine(rules, engineOptions) +export function engineFactory(rules: Rules, options = {}) { + return new Engine(rules, { ...engineOptions, ...options }) } export const EngineContext = createContext(new Engine()) diff --git a/site/source/components/utils/useNextQuestion.tsx b/site/source/components/utils/useNextQuestion.tsx index 0f19b7e427..c33ccfff94 100644 --- a/site/source/components/utils/useNextQuestion.tsx +++ b/site/source/components/utils/useNextQuestion.tsx @@ -129,21 +129,24 @@ export const useNextQuestions = function (): Array { (node) => engine.evaluate(node).missingVariables ?? {} ) const nextQuestions = useMemo(() => { - const next = getNextQuestions( + let next = getNextQuestions( missingVariables, questionsConfig ?? {}, answeredQuestions, situation ) if (currentQuestion && currentQuestion !== next[0]) { - return [currentQuestion, ...next.filter((val) => val !== currentQuestion)] + next = [currentQuestion, ...next.filter((val) => val !== currentQuestion)] } - return next + return next.filter( + (question) => engine.evaluate(question).nodeValue !== null + ) }, [ missingVariables, questionsConfig, answeredQuestions, situation, + engine, currentQuestion, ]) diff --git a/site/source/components/utils/useSearchParamsSimulationSharing.ts b/site/source/components/utils/useSearchParamsSimulationSharing.ts index 1819883492..86b223ca41 100644 --- a/site/source/components/utils/useSearchParamsSimulationSharing.ts +++ b/site/source/components/utils/useSearchParamsSimulationSharing.ts @@ -7,7 +7,11 @@ import { useEngine } from '@/components/utils/EngineContext' import { configSelector } from '@/selectors/simulationSelectors' import Engine, { ParsedRules, serializeEvaluation } from 'publicodes' import { DottedName } from 'modele-social' -import { updateSituation, setActiveTarget } from '@/actions/actions' +import { + updateSituation, + setActiveTarget, + batchUpdateSituation, +} from '@/actions/actions' type Objectifs = (string | { objectifs: string[] })[] type ShortName = string @@ -28,12 +32,13 @@ export default function useSearchParamsSimulationSharing() { ) useEffect(() => { - const hasConfig = Object.keys(config).length > 0 - // TODO: this check is specific to `useSimulationConfig` and - // `setSimulationConfig`, so we'd prefer not doing it here. Other ideas - // include having the config in a provider rather than in state. - const configLoadedInState = simulationUrl === currentUrl - if (!hasConfig || !configLoadedInState) return + // const hasConfig = Object.keys(config).length > 0 + + // // TODO: this check is specific to `useSimulationConfig` and + // // `setSimulationConfig`, so we'd prefer not doing it here. Other ideas + // // include having the config in a provider rather than in state. + // const configLoadedInState = simulationUrl === currentUrl + // if (!hasConfig || !configLoadedInState) return // On load: if (!urlSituationIsExtracted) { @@ -43,9 +48,8 @@ export default function useSearchParamsSimulationSharing() { dottedNameParamName ) - Object.entries(newSituation).forEach(([dottedName, value]) => { - dispatch(updateSituation(dottedName as DottedName, value)) - }) + dispatch(batchUpdateSituation(newSituation as Situation)) + const newActiveTarget = Object.keys(newSituation).filter((dottedName) => objectifs.includes(dottedName) )[0] diff --git a/site/source/components/utils/useSimulationConfig.ts b/site/source/components/utils/useSimulationConfig.ts index 4a48e58d26..044cfd5253 100644 --- a/site/source/components/utils/useSimulationConfig.ts +++ b/site/source/components/utils/useSimulationConfig.ts @@ -1,14 +1,12 @@ import { setSimulationConfig } from '@/actions/actions' +import { SimulationConfig } from '@/reducers/rootReducer' +import { configSelector } from '@/selectors/simulationSelectors' import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useHistory } from 'react-router' -import { Company } from '@/reducers/inFranceAppReducer' -import { RootState, SimulationConfig, Situation } from '@/reducers/rootReducer' -import { configSelector } from '@/selectors/simulationSelectors' export default function useSimulationConfig( - config: SimulationConfig | undefined, - { useExistingCompanyFromSituation = false } = {} + config: SimulationConfig | undefined ) { const dispatch = useDispatch() // TODO : Reading the URL here is buggy because when we do SPA navigation the @@ -17,31 +15,10 @@ export default function useSimulationConfig( // accessible from the situation config but is defined in the metadata file. const url = useHistory().location.pathname.split('?')[0] - const existingCompany = useSelector( - (state: RootState) => state.inFranceApp.existingCompany - ) - const initialSituation = useExistingCompanyFromSituation - ? getCompanySituation(existingCompany) - : undefined - const lastConfig = useSelector(configSelector) useEffect(() => { if (config && lastConfig !== config) { - dispatch(setSimulationConfig(config ?? {}, url, initialSituation)) + dispatch(setSimulationConfig(config ?? {}, url)) } - }, [config, dispatch, lastConfig, initialSituation, url]) -} - -export function getCompanySituation(company: Company | null): Situation { - return { - ...(company?.localisation && { - 'établissement . localisation': { objet: company.localisation }, - }), - ...(company?.dateCreationUniteLegale && { - 'entreprise . date de création': company.dateCreationUniteLegale.replace( - /(.*)-(.*)-(.*)/, - '$3/$2/$1' - ), - }), - } + }, [config, dispatch, lastConfig, url]) } diff --git a/site/source/design-system/message/index.tsx b/site/source/design-system/message/index.tsx index 05f9c5e876..d2a50e90b0 100644 --- a/site/source/design-system/message/index.tsx +++ b/site/source/design-system/message/index.tsx @@ -1,5 +1,5 @@ import React from 'react' -import styled, { css } from 'styled-components' +import styled, { css, ThemeProvider } from 'styled-components' import baseIcon from './baseIcon.svg' import infoIcon from './infoIcon.svg' import errorIcon from './errorIcon.svg' @@ -11,7 +11,7 @@ type MessageProps = { children: React.ReactNode icon?: boolean border?: boolean - type: MessageType + type?: MessageType light?: boolean } export function Message({ @@ -25,42 +25,47 @@ export function Message({ children = {children} } return ( - - {icon && - (type === 'success' ? ( - - ) : type === 'error' ? ( - - ) : type === 'info' ? ( - - ) : ( - - ))} -
{children}
-
+ ({ ...theme, darkMode: false })}> + + {icon && + (type === 'success' ? ( + + ) : type === 'error' ? ( + + ) : type === 'info' ? ( + + ) : ( + + ))} +
{children}
+
+
) } const StyledMessage = styled.div< - Pick + Pick & { + type: NonNullable + } >` display: flex; + position: relative; align-items: flex-start; ${({ theme, type, border, light }) => { const colorSpace = diff --git a/site/source/design-system/typography/list.stories.tsx b/site/source/design-system/typography/list.stories.tsx new file mode 100644 index 0000000000..cd28fd33f2 --- /dev/null +++ b/site/source/design-system/typography/list.stories.tsx @@ -0,0 +1,27 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react' +import { Li, Ul } from '@/design-system/typography/list' + +// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export +export default { + component: Ul, + // More on argTypes: https://storybook.js.org/docs/react/api/argtypes +} as ComponentMeta + +// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args +const Template: ComponentStory = (args) => ( +
    +
  • Élément 1
  • +
  • Élément 2
  • +
  • Élément 3
  • +
+) + +export const Basic = Template.bind({}) +// More on args: https://storybook.js.org/docs/react/writing-stories/args +Basic.args = {} + +export const XL = Template.bind({}) +// More on args: https://storybook.js.org/docs/react/writing-stories/args +XL.args = { + size: 'XL', +} diff --git a/site/source/hooks/useQuestionList.ts b/site/source/hooks/useQuestionList.ts new file mode 100644 index 0000000000..59da1452fb --- /dev/null +++ b/site/source/hooks/useQuestionList.ts @@ -0,0 +1,34 @@ +import { stepAction, updateSituation } from '@/actions/actions' +import { useEngine } from '@/components/utils/EngineContext' +import { useNextQuestions } from '@/components/utils/useNextQuestion' +import { answeredQuestionsSelector } from '@/selectors/simulationSelectors' +import { DottedName } from 'modele-social' +import { PublicodesExpression, RuleNode } from 'publicodes' +import { useDispatch, useSelector } from 'react-redux' + +export function useQuestionList(): [ + questions: Array, + onQuestionAnswered: ( + dottedName: DottedName + ) => (value?: PublicodesExpression) => void +] { + const answeredQuestions = useSelector(answeredQuestionsSelector) + const nextQuestions = useNextQuestions() + const engine = useEngine() + + const questions = [...answeredQuestions, ...nextQuestions] + .filter((dottedName) => engine.evaluate(dottedName).nodeValue !== null) + .map((dottedName) => engine.getRule(dottedName)) + + const dispatch = useDispatch() + + const onQuestionAnswered = + (dottedName: DottedName) => (value?: PublicodesExpression) => { + if (!answeredQuestions.includes(dottedName)) { + dispatch(stepAction(dottedName)) + } + dispatch(updateSituation(dottedName, value)) + } + + return [questions, onQuestionAnswered] +} diff --git a/site/source/locales/rules-en.yaml b/site/source/locales/rules-en.yaml index 2e64db7e25..2e34c35f11 100644 --- a/site/source/locales/rules-en.yaml +++ b/site/source/locales/rules-en.yaml @@ -2982,21 +2982,23 @@ contrat salarié . régime des impatriés: titre.fr: régime des impatriés contrat salarié . régime des impatriés . information: description.en: >- - [automatic] The following conditions must be met in order to benefit from - the old-age contribution exemption: - Be able to prove a minimum - contribution paid elsewhere for old-age insurance - Not have been - affiliated, during the five calendar years preceding that of taking up the - post, to a compulsory French old-age insurance scheme, except for ancillary - activities of a seasonal nature or for studies. + [automatic] In order to benefit from the exemption from old-age + contributions, the following conditions must be met: + + - Be able to justify a minimum contribution paid elsewhere for old age insurance + + - Not to have been affiliated, during the five calendar years preceding that of the taking up of duties, to a compulsory French old age insurance scheme, except for accessory activities, of a seasonal nature or for studies. + [Lire le texte de loi](https://www.legifrance.gouv.fr/affichCode.do;jsessionid=F5CFB7C90D1D1F529A2CDC9FFD20BD6E.tplgfr34s_3?idSectionTA=LEGISCTA000038510929&cidTexte=LEGITEXT000006073189&dateTexte=20190626) description.fr: >- Pour bénéficier de l'exonération de cotisations vieillesse, il faut remplir - les conditions suivantes : - Pouvoir justifier d'une contribution minimale - versée ailleurs pour une assurance vieillesse - Ne pas avoir été affilié, au - cours des cinq années civiles précédant celle de la prise de fonctions, à un - régime français obligatoire d'assurance vieillesse, sauf pour des activités - accessoires, de caractère saisonnier ou pour les études. + les conditions suivantes : + + - Pouvoir justifier d'une contribution minimale versée ailleurs pour une assurance vieillesse + + - Ne pas avoir été affilié, au cours des cinq années civiles précédant celle de la prise de fonctions, à un régime français obligatoire d'assurance vieillesse, sauf pour des activités accessoires, de caractère saisonnier ou pour les études. + [Lire le texte de loi](https://www.legifrance.gouv.fr/affichCode.do;jsessionid=F5CFB7C90D1D1F529A2CDC9FFD20BD6E.tplgfr34s_3?idSectionTA=LEGISCTA000038510929&cidTexte=LEGITEXT000006073189&dateTexte=20190626) titre.en: '[automatic] information' @@ -3487,12 +3489,13 @@ contrat salarié . stage: contrat salarié . stage . avertissement: description.en: >- [automatic] An internship agreement **is not an employment contract**, and - cannot be concluded to carry out a regular task corresponding to a permanent - job, or a temporary increase in the company's activity. [Education Code - - Article + cannot be concluded to perform a regular task corresponding to a permanent + job, or to a temporary increase in the company's activity. [Code de + l'éducation - Article L124-7](https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029234119&cidTexte=LEGITEXT000006071191) - Furthermore, a company with less than 20 employees may not take on more than **3 trainees**, and no more than **15% of the workforce** for companies with more than 20 employees. + + In addition, a company with less than 20 employees may not host more than **3 trainees**, and no more than **15% of the workforce** for companies with more than 20 employees. description.fr: >- Une convention de stage **n'est pas un contrat de travail**, et ne peut pas être conclue pour réaliser une tâche régulière correspondant à un poste de @@ -3500,6 +3503,7 @@ contrat salarié . stage . avertissement: l'entreprise. [Code de l'éducation - Article L124-7](https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029234119&cidTexte=LEGITEXT000006071191) + Par ailleurs, une entreprise de moins de 20 salariés ne peut pas accueillir plus de **3 stagiaires**, et pas plus de **15% de l’effectif** pour les entreprises de plus de 20 salariés. titre.en: '[automatic] warning' titre.fr: avertissement @@ -3891,8 +3895,6 @@ contrat salarié . vieillesse: titre.en: Basic pension contribution titre.fr: vieillesse dirigeant: - question.en: '[automatic] What is the social regime of the manager?' - question.fr: Quel est le régime social du dirigeant ? titre.en: '[automatic] manager' titre.fr: dirigeant dirigeant . assimilé salarié: @@ -4163,6 +4165,11 @@ dirigeant . auto-entrepreneur . notification calcul ACRE annuel: d'acre sur une meme année. titre.en: '[automatic] notification annual ACRE calculation' titre.fr: notification calcul ACRE annuel +dirigeant . gérant minoritaire: + question.en: '[automatic] Are you a minority or equal shareholder of your company?' + question.fr: Êtes-vous gérant minoritaire ou égalitaire de votre entreprise ? + titre.en: '[automatic] Minority or equal shareholder manager' + titre.fr: Gérant minoritaire ou égalitaire dirigeant . indépendant: titre.en: '[automatic] independent' titre.fr: indépendant @@ -6181,6 +6188,9 @@ dirigeant . indépendant . revenus étrangers . montant: question.fr: Quel est leur montant ? titre.en: '[automatic] income received abroad' titre.fr: revenus perçu à l'étranger +dirigeant . régime social: + titre.en: '[automatic] social regime' + titre.fr: régime social dirigeant . rémunération: titre.en: '[automatic] compensation' titre.fr: rémunération @@ -7171,6 +7181,15 @@ entreprise . ACRE: entreprise . ACRE par défaut: titre.en: '[automatic] Default ACRE' titre.fr: ACRE par défaut +entreprise . SIREN: + description.en: > + [automatic] The Siren number is a unique 9 digit number for each company. Ex + : 401237780 + description.fr: > + Le numéro Siren est un numéro de 9 chiffres unique pour chaque entreprise. + Ex : 401237780 + titre.en: '[automatic] SIREN' + titre.fr: SIREN entreprise . activité: description.en: '[automatic] Your type of activity will determine a large part of the contribution, contribution and tax calculations.' @@ -7397,6 +7416,56 @@ entreprise . capital social: question.fr: Quele est le capital social de la société ? titre.en: '[automatic] Share capital' titre.fr: Capital social +entreprise . catégorie juridique: + description.en: | + [automatic] Legal categories accessible via the SIRENE API + description.fr: | + Les catégories juridiques accessibles via l'API SIRENE + note.en: '[automatic] We base ourselves here on the legal categories defined by INSEE' + note.fr: On se base ici sur les catégories juridiques définies par l'INSEE + titre.en: '[automatic] legal category' + titre.fr: catégorie juridique +entreprise . catégorie juridique . EI: + titre.en: '[automatic] EI or EIRL' + titre.fr: EI ou EIRL +entreprise . catégorie juridique . EI . auto-entrepreneur: + question.en: '[automatic] Are you an auto-entrepreneur?' + question.fr: Êtes-vous auto-entrepreneur ? + titre.en: '[automatic] auto-entrepreneur' + titre.fr: auto-entrepreneur +entreprise . catégorie juridique . EI . imposition entreprise: + titre.en: '[automatic] taxation company' + titre.fr: imposition entreprise +entreprise . catégorie juridique . EI . responsabilité limité: + question.en: '[automatic] Is your business an EIRL?' + question.fr: Votre entreprise est-elle une EIRL ? + titre.en: '[automatic] EIRL' + titre.fr: EIRL +entreprise . catégorie juridique . SARL: + titre.en: '[automatic] EURL or SARL' + titre.fr: EURL ou SARL +entreprise . catégorie juridique . SARL . unipersonnelle: + question.en: '[automatic] Is your company an EURL?' + question.fr: Votre entreprise est-elle une EURL ? + titre.en: '[automatic] EURL' + titre.fr: EURL +entreprise . catégorie juridique . SAS: + titre.en: '[automatic] SASU or SAS' + titre.fr: SASU ou SAS +entreprise . catégorie juridique . SAS . unipersonnelle: + question.en: '[automatic] Is your company a SASU?' + question.fr: Votre entreprise est-elle une SASU ? + titre.en: '[automatic] SASU' + titre.fr: SASU +entreprise . catégorie juridique . SELARL: + titre.en: '[automatic] SELARL' + titre.fr: SELARL +entreprise . catégorie juridique . SELAS: + titre.en: '[automatic] SELARL' + titre.fr: SELARL +entreprise . catégorie juridique . autre: + titre.en: '[automatic] other' + titre.fr: autre entreprise . charges: description.en: > [automatic] @@ -8028,6 +8097,9 @@ entreprise . imposition . IS . résultat net: résumé.fr: Après déduction des charges et de l'impôt sur les société titre.en: '[automatic] net result' titre.fr: résultat net +entreprise . nom: + titre.en: '[automatic] name' + titre.fr: nom entreprise . ratio alternants: description.en: > This fraction determines the additional contribution for learning for the @@ -8991,6 +9063,9 @@ situation personnelle . domiciliation fiscale à l'étranger: question.fr: Votre établissement bénéficie-t-il du dispositif zone franche urbaine (ZFU) ? titre.en: ZFU titre.fr: ZFU +établissement . ZFU . durée d'implantation en fin d'année: + titre.en: '[automatic] duration of implementation at the end of the year' + titre.fr: durée d'implantation en fin d'année établissement . localisation: description.en: |- When a company has more than one establishment, certain contributions are diff --git a/site/source/locales/ui-en.yaml b/site/source/locales/ui-en.yaml index 23276244a4..33ba64689c 100644 --- a/site/source/locales/ui-en.yaml +++ b/site/source/locales/ui-en.yaml @@ -5,6 +5,11 @@ "<0> Pour en savoir plus, rendez-vous sur le site <3>aquoiserventlescotisations": urssaf: fr: <0> To find out more, go to <3>aquoiserventlescotisations.urssaf.fr +"<0><0>Il n'existe pas encore de simulateur de revenu pour votre type d'entreprise sur ce site.<1>Si vous souhaitez que nous développions un nouveau simulateur, laissez-nous message en cliquant sur le bouton \"Faire une suggestion\" en bas de cette page.": + <0><0>There is not yet an income simulator for your type of business on this + site.<1>If you would like us to develop a new simulator, leave us a + message by clicking on the "Make a suggestion" button at the bottom of this + page. <0>Activité mixte: <0>Mixed activity "<0>Covid-19 et chômage partiel : <3>Calculez votre indemnité": "<0>Covid-19 and Short-Time Work: <3>Calculate Your Benefit" <0>Oui: <0>Yes @@ -56,17 +61,22 @@ Covid 19: Covid 19 "Covid-19 : Découvrir les mesures de soutien aux entreprises": "Covid-19: Discovering Business Support Measures" Coût pour l'entreprise: Cost to the company Crée le: Created on +"Crée le :": "Created on :" Créer une: Create a De: From Demande de mobilité: Demand for mobility Destinataire: Levied by Devenir: Become +"Domiciliée à l'adresse :": "Domiciled at the address :" +Données de l'entreprise: Company data +Données de simulation: Simulation data Déclenchement: Applicability Découvrir: Discover Démarches de création: Creation process checklist Désactivée: Inactive Détail annuel des cotisations: Annual detail of my contributions Effacer: Reset +Effacer toutes mes données: Delete all my data Embauche: Hiring process Employeur: Employer En incluant l'indemnité de chômage partiel: Including short-time working allowance @@ -108,6 +118,7 @@ Jusqu’au: Until La somme de: This rule is the sum of Liste des intégrations: List of integrations Liste des statuts juridiques: List of legal statutes +Ma situation: My situation Mensuel: Monthly Mes réponses: My answers Modifier: Modify @@ -155,8 +166,9 @@ Quelques exemples de salaires: Some salary exemples Quelques intégrations: Some integrations Recherche en cours...: Searching... Rechercher: Search -Rechercher une entreprise: Search for a company +Rechercher votre entreprise: Search for a company Recommencer: Start again +Recommencer la simulation: Start the simulation again Rend non applicable les règles suivantes: Makes the following rules not applicable Renseigner mon entreprise: Find my company Responsabilité limitée: Limited liability @@ -213,7 +225,7 @@ Voir la fiche de paie: See the pay slip Voir la répartition des cotisations: View contribution breakdown Voir le code source: See the source code Voir les autres simulateurs: See the other simulators -Voir mes paramètres: See my situation +Voir ma situation: See my situation Votre adresse e-mail: Your email address Votre entreprise: Your company Votre forme juridique: Your legal status @@ -278,6 +290,12 @@ après: arrondi-to-decimals: Rounding to {explanation.decimals.nodeValue} decimal arrondi-to-decimals_plural: Rounding to {explanation.decimals.nodeValue} decimals assiette: base +assistant-DRI: + description: <0><0>This tool will help you to fill in your tax <2>return on + impot.gouv.fr. You will have at the end :<1><0>The list of boxes that + concern you with the amount to fill in<1>An estimate of the social + security contributions to be paid to the Urssaf in 2022<1>My + company associés: choix1: Alone choix2: Several partners @@ -874,7 +892,7 @@ landing: create: body: Assistance in choosing a legal status and a complete list of the steps involved in setting up a business - title: Starting a business + title: I don't have a business yet manage: body: Personalized tools to anticipate the amount of social contributions to be paid and better manage your cash flow. @@ -1617,8 +1635,8 @@ pages: remuneration. To do this, simply enter the total amount allocated in the \"total charged\" box. The simulation can then be refined by answering the various questions." - shortname: SASU - title: SASU Simulator + shortname: SAS(U) + title: SAS(U) Simulator titre: Revenue simulator for SAS(U) executive select-year: access: Access the {{year}} simulator @@ -1852,7 +1870,7 @@ une de ces conditions: one of these applies économieCollaborative: WIP: <0>This assistant is under development. Do not hesitate to send us all your remarks, ideas, questions by clicking on the "Make a suggestion" button - above. + at the bottom of the page. accueil: contenu: <0>Do you have income from <2>online platforms (Airbnb, Abritel, Drivy, Blablacar, Leboncoin, etc.)? You must declare them in most cases. diff --git a/site/source/locales/ui-fr.yaml b/site/source/locales/ui-fr.yaml index 5a9cc0dbd6..b0f95e03e4 100644 --- a/site/source/locales/ui-fr.yaml +++ b/site/source/locales/ui-fr.yaml @@ -6,6 +6,11 @@ urssaf: fr: <0> Pour en savoir plus, rendez-vous sur le site <3>aquoiserventlescotisations.urssaf.fr +"<0><0>Il n'existe pas encore de simulateur de revenu pour votre type d'entreprise sur ce site.<1>Si vous souhaitez que nous développions un nouveau simulateur, laissez-nous message en cliquant sur le bouton \"Faire une suggestion\" en bas de cette page.": + <0><0>Il n'existe pas encore de simulateur de revenu pour votre type + d'entreprise sur ce site.<1>Si vous souhaitez que nous développions un + nouveau simulateur, laissez-nous message en cliquant sur le bouton "Faire une + suggestion" en bas de cette page. <0>Activité mixte: <0>Activité mixte <0>Oui: <0>Oui Assimilé salarié: Assimilé salarié @@ -26,9 +31,14 @@ CompanySearchField: Continuer: Continuer Cotisations sociales: Cotisations sociales Crée le: Crée le +"Crée le :": "Crée le :" Créer une: Créer une Devenir: Devenir +"Domiciliée à l'adresse :": "Domiciliée à l'adresse :" +Données de l'entreprise: Données de l'entreprise +Données de simulation: Données de simulation Découvrir: Découvrir +Effacer toutes mes données: Effacer toutes mes données En savoir plus: En savoir plus Entreprise Individuelle: Entreprise Individuelle Exonérations: Exonérations @@ -49,6 +59,7 @@ J'ai compris: J'ai compris Jusqu’au: Jusqu’au Liste des intégrations: Liste des intégrations Liste des statuts juridiques: Liste des statuts juridiques +Ma situation: Ma situation Mes réponses: Mes réponses Modifier: Modifier Montant de l'impôt sur les sociétés: Montant de l'impôt sur les sociétés @@ -74,7 +85,8 @@ Prévisualisation: Prévisualisation Que cherchez-vous ?: Que cherchez-vous ? Quelques intégrations: Quelques intégrations Rechercher: Rechercher -Rechercher une entreprise: Rechercher une entreprise +Rechercher votre entreprise: Rechercher votre entreprise +Recommencer la simulation: Recommencer la simulation Ressources utiles: Ressources utiles Retour: Retour Retour à la création: Retour à la création @@ -102,7 +114,7 @@ Une idée ?<1>Contactez-nous !: Une idée ?<1>Contactez-n Voir la fiche Urssaf: Voir la fiche Urssaf Voir la fiche de paie: Voir la fiche de paie Voir les autres simulateurs: Voir les autres simulateurs -Voir mes paramètres: Voir mes paramètres +Voir ma situation: Voir ma situation Votre adresse e-mail: Votre adresse e-mail Votre forme juridique: Votre forme juridique Vous êtes dirigeant d'une SAS(U) ? <2>Accéder au simulateur de revenu dédié: Vous êtes dirigeant d'une SAS(U) ? <2>Accéder au simulateur de revenu dédié @@ -156,6 +168,12 @@ après: (NIC). titre: Le numéro SIRET titre: Après la création +assistant-DRI: + description: <0><0>Cet outil vous aidera à remplir votre <2>déclaration de + revenu sur impot.gouv.fr. Vous aurez à la fin :<1><0>La liste des + cases qui vous concernent avec le montant à remplir<1>Une estimation des + cotisations sociales à payer à l'Urssaf en 2022<1>Mon + entreprise associés: choix1: Seul choix2: Plusieurs personnes @@ -626,7 +644,7 @@ landing: create: body: Un accompagnement au choix du statut juridique et la liste complète des démarches de création - title: Créer une entreprise + title: Je n'ai pas encore d'entreprise manage: body: Des outils personnalisés pour anticiper le montant des cotisations sociales à payer et mieux gérer votre trésorerie. @@ -1270,8 +1288,8 @@ pages: alloué à la rémunération du dirigeant. Il vous suffit pour cela saisir le montant total alloué dans la case \"total chargé\". La simulation peut ensuite être affinée en répondant aux différentes questions." - shortname: SASU - title: Simulateur de SASU + shortname: SAS(U) + title: Simulateur de SAS(U) select-year: access: Accéder au simulateur {{year}} back: Retourner au simulateur {{year}} @@ -1453,7 +1471,7 @@ trouver: économieCollaborative: WIP: <0>Cet assistant est en cours de développement. N'hésitez pas à nous faire part de toute vos remarques, idées, questions en cliquant sur le - bouton "Faire une suggestion" ci dessus. + bouton "Faire une suggestion" en bas de la page. accueil: contenu: <0>Vous avez des revenus issus des <2>plateformes en ligne (Airbnb, Abritel, Drivy, Blablacar, Leboncoin, etc.) ? Vous devez les déclarer dans diff --git a/site/source/pages/Creer/AfterRegistration.tsx b/site/source/pages/Creer/AfterRegistration.tsx index 6e49b20d35..032379075c 100644 --- a/site/source/pages/Creer/AfterRegistration.tsx +++ b/site/source/pages/Creer/AfterRegistration.tsx @@ -14,7 +14,7 @@ import siret from './siret.jpg' export default function AfterRegistration() { const sitePaths = useContext(SitePathsContext) const statutChoisi = useSelector( - (state: RootState) => state.inFranceApp.companyStatusChoice + (state: RootState) => state.choixStatutJuridique.companyStatusChoice ) const { t } = useTranslation() const isAutoentrepreneur = statutChoisi?.match('auto-entrepreneur') diff --git a/site/source/pages/Creer/CreationChecklist.tsx b/site/source/pages/Creer/CreationChecklist.tsx index f4c3c90d5b..2ceacf5f8f 100644 --- a/site/source/pages/Creer/CreationChecklist.tsx +++ b/site/source/pages/Creer/CreationChecklist.tsx @@ -33,7 +33,7 @@ export default function CreateCompany({ statut }: CreateCompanyProps) { const { t, i18n } = useTranslation() const sitePaths = useContext(SitePathsContext) const companyCreationChecklist = useSelector( - (state: RootState) => state.inFranceApp.companyCreationChecklist + (state: RootState) => state.choixStatutJuridique.companyCreationChecklist ) const dispatch = useDispatch() diff --git a/site/source/pages/Creer/GuideStatut/PreviousAnswers.tsx b/site/source/pages/Creer/GuideStatut/PreviousAnswers.tsx index aba0c9d7f4..fb26de7864 100644 --- a/site/source/pages/Creer/GuideStatut/PreviousAnswers.tsx +++ b/site/source/pages/Creer/GuideStatut/PreviousAnswers.tsx @@ -67,7 +67,7 @@ const PreviousAnswersItem = styled.li` export default function PreviousAnswers() { const sitePaths = useContext(SitePathsContext) const legalStatus = useSelector( - (state: RootState) => state.inFranceApp.companyLegalStatus + (state: RootState) => state.choixStatutJuridique.companyLegalStatus ) if (Object.values(legalStatus).length < 1) { return null diff --git a/site/source/pages/Creer/GuideStatut/index.tsx b/site/source/pages/Creer/GuideStatut/index.tsx index 065e03f5a0..d2fea0443c 100644 --- a/site/source/pages/Creer/GuideStatut/index.tsx +++ b/site/source/pages/Creer/GuideStatut/index.tsx @@ -25,8 +25,8 @@ const useResetFollowingAnswers = () => { const answeredQuestion = useSelector( (state: RootState) => Object.keys( - state.inFranceApp.companyLegalStatus - ) as (keyof typeof state.inFranceApp.companyLegalStatus)[] + state.choixStatutJuridique.companyLegalStatus + ) as (keyof typeof state.choixStatutJuridique.companyLegalStatus)[] ) useEffect(() => { const companyStatusCurrentQuestionName = (toPairs( diff --git a/site/source/pages/Creer/Home.tsx b/site/source/pages/Creer/Home.tsx index 5220c39f6b..94cf812fce 100644 --- a/site/source/pages/Creer/Home.tsx +++ b/site/source/pages/Creer/Home.tsx @@ -22,7 +22,7 @@ export default function Créer() { const nextQuestionUrl = useNextQuestionUrl() const guideAlreadyStarted = useSelector( (state: RootState) => - !!Object.keys(state.inFranceApp.companyLegalStatus).length + !!Object.keys(state.choixStatutJuridique.companyLegalStatus).length ) return ( diff --git "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/Fields.tsx" "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/Fields.tsx" index 9a9ebdf996..13e6d6ecc8 100644 --- "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/Fields.tsx" +++ "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/Fields.tsx" @@ -11,7 +11,7 @@ import { RuleNode } from 'publicodes' import { useCallback, useContext } from 'react' import { useDispatch, useSelector } from 'react-redux' import { situationSelector } from '@/selectors/simulationSelectors' -import { Question } from './index' +import { Question } from './PreviousVersion' type SubSectionProp = { dottedName: DottedName diff --git "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/PreviousVersion.tsx" "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/PreviousVersion.tsx" new file mode 100644 index 0000000000..f46f2da587 --- /dev/null +++ "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/PreviousVersion.tsx" @@ -0,0 +1,306 @@ +import { Grid } from '@mui/material' +import { updateSituation } from '@/actions/actions' +import RuleInput from '@/components/conversation/RuleInput' +import { Condition, WhenAlreadyDefined } from '@/components/EngineValue' +import PageHeader from '@/components/PageHeader' +import PreviousSimulationBanner from '@/components/PreviousSimulationBanner' +import { FromTop } from '@/components/ui/animate' +import Warning from '@/components/ui/WarningBlock' +import Emoji from '@/components/utils/Emoji' +import useSimulationConfig from '@/components/utils/useSimulationConfig' +import { Strong } from '@/design-system/typography' +import { H2, H3 } from '@/design-system/typography/heading' +import { Li, Ul } from '@/design-system/typography/list' +import { Body, Intro, SmallBody } from '@/design-system/typography/paragraphs' +import { useCallback } from 'react' +import { Trans } from 'react-i18next' +import { useDispatch, useSelector } from 'react-redux' +import { situationSelector } from '@/selectors/simulationSelectors' +import styled from 'styled-components' +import { TrackPage } from '../../../ATInternetTracking' +import simulationConfig from './config.yaml' +import { ExplicationsResultatFiscal } from './ExplicationResultatFiscal' +import { SimpleField, SubSection } from './Fields' +import ResultatsSimples from './RésultatSimple' +import ResultatsParFormulaire from './RésultatsParFormulaire' +import illustration from './undraw_fill_in_mie5.svg' + +/** + * Nous avons proposé une nouvelle vision des résultat plus complète, avec une proposition d'aide pour + * l'ensemble des cases liées aux cotisations sociales. + * + * Hors de propos pour 2021, étant donné que cela prendrait beaucoup de temps à valider par la DGFiP + * En attendant, on propose la version "simple" (mais moins utile). + * + * Le but est de faire valider la version plus complète pour la déclaration de revenu 2021. + */ +const FEATURE_FLAG_RESULTATS_COMPLETS = + !import.meta.env.SSR && document.location.search.includes('next') + +export default function AideDéclarationIndépendant() { + useSimulationConfig(simulationConfig) + + const situation = useSelector(situationSelector) + return ( + <> + + + + Cet outil est une aide à la déclaration de revenus à destination des{' '} + travailleurs indépendants. Il vous permet de + connaître le montant des charges sociales déductibles. + + + Vous restez entièrement responsable d'éventuelles omissions ou + inexactitudes dans votre déclaration. + + + + + + Cet outil vous concerne si vous êtes dans le cas suivant : + + +
    +
  • + Vous cotisez au régime général des travailleurs indépendants +
  • +
+ + + Il ne vous concerne pas si vous êtes dans un des cas suivants : + + +
    +
  • + Vous exercez une activité libérale relevant d’un régime de + retraite des professions libérales en comptabilité d'engagement +
  • +
  • Votre entreprise est domiciliée dans les DOM
  • +
+
+ + +

Imposition

+ + Ces quelques questions permettent de déterminer le type de déclaration + à remplir, ainsi que les modalités de calcul des cotisations sociales. + +
+ {Object.keys(situation).length ? ( + + ) : ( + + )} + + + + + + + + +

Entreprise et activité

+
+ + + + Cette aide à la déclaration concerne uniquement les + entreprises déjà en activité en 2021 + + + + + {/* PLNR */} + + + + +

+ Situation personnelle +

+ + + + + + +

+ Exonérations +

+ + Les calculs de l'exonération COVID 2021 + sont en cours d'implémentation + + + + + + + {FEATURE_FLAG_RESULTATS_COMPLETS && ( + + )} +

+ International +

+ + + + +
+ + + + + + + {/* We can't use a subsection here cause revenu étrangers is not missing when CSG is replaced */} +

+ Revenus étranger +

+ + + + +
+ + + + + +
+
+
+ {FEATURE_FLAG_RESULTATS_COMPLETS ? ( + <> + + + + + + + ) : ( + + + + )} + + ) +} + +function ImpositionSection() { + const dispatch = useDispatch() + + const situation = useSelector(situationSelector) + const setSituation = useCallback( + (value, dottedName) => { + dispatch(updateSituation(dottedName, value)) + }, + [dispatch] + ) + return ( + <> + + {situation['entreprise . imposition'] != null && ( + <> + {/* */} + + {/* */} + + + + + +

+ Quel est votre chiffre d'affaires hors taxes en 2021 ? +

+ + Indiquez le montant hors taxes de votre chiffre d’affaires + ou de vos recettes bruts (avant déduction de l’abattement + forfaitaire pour frais et charges) et avant déduction des + exonérations fiscales dont vous avez bénéficié + + + + +
+ +

+ Quel est votre résultat fiscal en 2021 ?
+ + Charges sociales et exonérations fiscales non incluses{' '} + + +

+ + Le résultat fiscal correspond aux produits moins les + charges. Il peut être positif (bénéfice) ou négatif + (déficit). + + + + +
+
+ +

+ Quel est le montant net de votre rémunération en 2021 ? +
+ Sans tenir compte des charges sociales +

+ + + +
+
+
+ + )} + + ) +} + +export const Question = styled.div` + margin-top: 1em; +` +const BigInput = styled.div` + font-size: 130%; +` diff --git "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/config.yaml" "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/config.yaml" index 910ad22ef5..08622a48db 100644 --- "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/config.yaml" +++ "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/config.yaml" @@ -8,7 +8,7 @@ objectifs: - déclaration indépendants . formulaire 2035 situation: - dirigeant: "'indépendant'" + dirigeant . régime social: "'indépendant'" année: 2021 déclaration indépendants: oui dirigeant . indépendant . PL . CIPAV: non # TODO En attendant la transitivité des remplacements diff --git "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/index.tsx" "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/index.tsx" index 00fd3dbf8f..4b76d0466a 100644 --- "a/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/index.tsx" +++ "b/site/source/pages/Gerer/AideD\303\251clarationInd\303\251pendant/index.tsx" @@ -1,308 +1,93 @@ -import { Grid } from '@mui/material' -import { updateSituation } from '@/actions/actions' -import RuleInput from '@/components/conversation/RuleInput' +import { resetCompany } from '@/actions/companyActions' +import { useSetEntreprise } from '@/actions/companyStatusActions' +import { CompanyDetails } from '@/components/company/Details' +import { CompanySearchField } from '@/components/company/SearchField' import { Condition, WhenAlreadyDefined, - WhenApplicable, + WhenNotAlreadyDefined, } from '@/components/EngineValue' import PageHeader from '@/components/PageHeader' -import PreviousSimulationBanner from '@/components/PreviousSimulationBanner' -import { FromTop } from '@/components/ui/animate' -import Warning from '@/components/ui/WarningBlock' -import Emoji from '@/components/utils/Emoji' -import useSimulationConfig from '@/components/utils/useSimulationConfig' +import { SitePathsContext } from '@/components/utils/SitePathsContext' +import { Message } from '@/design-system' +import { Button } from '@/design-system/buttons' +import { Spacing } from '@/design-system/layout' import { Strong } from '@/design-system/typography' import { H2, H3 } from '@/design-system/typography/heading' +import { Link } from '@/design-system/typography/link' import { Li, Ul } from '@/design-system/typography/list' -import { Body, Intro, SmallBody } from '@/design-system/typography/paragraphs' -import { useCallback } from 'react' +import { Body, Intro } from '@/design-system/typography/paragraphs' +import { Grid } from '@mui/material' +import { useContext } from 'react' import { Trans } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { situationSelector } from '@/selectors/simulationSelectors' -import styled from 'styled-components' -import { TrackPage } from '../../../ATInternetTracking' -import simulationConfig from './config.yaml' -import { ExplicationsResultatFiscal } from './ExplicationResultatFiscal' -import { SimpleField, SubSection } from './Fields' -import ResultatsSimples from './RésultatSimple' -import ResultatsParFormulaire from './RésultatsParFormulaire' +import { useDispatch } from 'react-redux' import illustration from './undraw_fill_in_mie5.svg' -/** - * Nous avons proposé une nouvelle vision des résultat plus complète, avec une proposition d'aide pour - * l'ensemble des cases liées aux cotisations sociales. - * - * Hors de propos pour 2021, étant donné que cela prendrait beaucoup de temps à valider par la DGFiP - * En attendant, on propose la version "simple" (mais moins utile). - * - * Le but est de faire valider la version plus complète pour la déclaration de revenu 2021. - */ -const FEATURE_FLAG_RESULTATS_COMPLETS = - !import.meta.env.SSR && document.location.search.includes('next') - export default function AideDéclarationIndépendant() { - useSimulationConfig(simulationConfig) - - const situation = useSelector(situationSelector) + const setEntreprise = useSetEntreprise() + const sitePaths = useContext(SitePathsContext) + const dispatch = useDispatch() return ( <> - + - Cet outil est une aide à la déclaration de revenus à destination des{' '} - travailleurs indépendants. Il vous permet de - connaître le montant des charges sociales déductibles. + Cet outil vous aidera à remplir votre{' '} + déclaration de revenu sur impot.gouv.fr. Vous aurez + à la fin : - - Vous restez entièrement responsable d'éventuelles omissions ou - inexactitudes dans votre déclaration. - - - - - - Cet outil vous concerne si vous êtes dans le cas suivant : - - -
    +
    • - Vous cotisez au régime général des travailleurs indépendants + La liste des cases qui vous concernent avec le montant à remplir
    • -
    - - - Il ne vous concerne pas si vous êtes dans un des cas suivants : - - -
    • - Vous exercez une activité libérale relevant d’un régime de - retraite des professions libérales en comptabilité d'engagement + Une estimation des cotisations sociales à payer à l'Urssaf en 2022
    • -
    • Votre entreprise est domiciliée dans les DOM
    - - - -

    Imposition

    - - Ces quelques questions permettent de déterminer le type de déclaration - à remplir, ainsi que les modalités de calcul des cotisations sociales. - + +

    Mon entreprise

    - {Object.keys(situation).length ? ( - - ) : ( - - )} - - - - - - - - -

    Entreprise et activité

    -
    - - - - Cette aide à la déclaration concerne uniquement les - entreprises déjà en activité en 2021 - - - - - {/* PLNR */} - - - - -

    - Situation personnelle -

    - - - - - - -

    - Exonérations -

    - - Les calculs de l'exonération COVID 2021 - sont en cours d'implémentation - - - - - - - - {FEATURE_FLAG_RESULTATS_COMPLETS && ( - - )} -

    - International -

    - - - - -
    - - - - - - - {/* We can't use a subsection here cause revenu étrangers is not missing when CSG is replaced */} -

    - Revenus étranger -

    - - - - -
    - - - - - -
    + + + + + Cherchez avec votre nom, le nom de votre entreprise, le SIREN ou + le SIRET + + + + + + + -
    - {FEATURE_FLAG_RESULTATS_COMPLETS ? ( - <> - - - - - - - ) : ( - - - - )} - - ) -} - -function ImpositionSection() { - const dispatch = useDispatch() - - const situation = useSelector(situationSelector) - const setSituation = useCallback( - (value, dottedName) => { - dispatch(updateSituation(dottedName, value)) - }, - [dispatch] - ) - return ( - <> - - {situation['entreprise . imposition'] != null && ( - <> - {/* */} - - {/* */} - - - - - -

    - Quel est votre chiffre d'affaires hors taxes en 2021 ? -

    - - Indiquez le montant hors taxes de votre chiffre d’affaires - ou de vos recettes bruts (avant déduction de l’abattement - forfaitaire pour frais et charges) et avant déduction des - exonérations fiscales dont vous avez bénéficié - - - - -
    - -

    - Quel est votre résultat fiscal en 2021 ?
    - - Charges sociales et exonérations fiscales non incluses{' '} - - -

    - - Le résultat fiscal correspond aux produits moins les - charges. Il peut être positif (bénéfice) ou négatif - (déficit). - - - - -
    -
    - -

    - Quel est le montant net de votre rémunération en 2021 ? -
    - Sans tenir compte des charges sociales -

    - - - -
    -
    -
    - - )} + + + + +

    Cet assistant ne gère pas le cas des dirigeants de SAS(U)

    + + Nous sommes désolés. Si vous rencontrez des difficultés à remplir + votre déclaration, rapprochez-vous de votre comptable. Si vous êtes + sans comptable, vous pouvez contacter le service des impôts. + + + Si vous souhaitez que cet assistant à la déclaration gère votre cas + dans le futur, laissez-nous message en cliquant sur le bouton "Faire + une suggestion" en bas de la page. + + + Ce site propose d'autres outils qui pourraient vous intéresser (par + exemple un simulateur de revenu net après impôt). + + + Découvrir les outils pour mon entreprise + + +
    +
    ) } - -export const Question = styled.div` - margin-top: 1em; -` -const BigInput = styled.div` - font-size: 130%; -` diff --git a/site/source/pages/Gerer/AideOrganismeLocal.tsx b/site/source/pages/Gerer/AideOrganismeLocal.tsx index 9d6b1bf2db..789ba71663 100644 --- a/site/source/pages/Gerer/AideOrganismeLocal.tsx +++ b/site/source/pages/Gerer/AideOrganismeLocal.tsx @@ -8,7 +8,7 @@ import { RootState } from '@/reducers/rootReducer' import aideOrganismeSvg from './aideOrganisme.svg' const aideMidiPyrenéesAutoEntrepreneur = (state: RootState) => { - const company = state.inFranceApp.existingCompany + const company = state.choixStatutJuridique.existingCompany if (!company) { return false } diff --git a/site/source/pages/Gerer/Embaucher.tsx b/site/source/pages/Gerer/Embaucher.tsx index 5779642165..d13326a193 100644 --- a/site/source/pages/Gerer/Embaucher.tsx +++ b/site/source/pages/Gerer/Embaucher.tsx @@ -23,7 +23,7 @@ type EmbaucherProps = { function Embaucher({ onChecklistInitialization, onItemCheck }: EmbaucherProps) { const { t } = useTranslation() const hiringChecklist = useSelector( - (state: RootState) => state.inFranceApp.hiringChecklist + (state: RootState) => state.choixStatutJuridique.hiringChecklist ) return ( @@ -247,7 +247,7 @@ function Embaucher({ onChecklistInitialization, onItemCheck }: EmbaucherProps) { export default connect( (state: RootState) => ({ - hiringChecklist: state.inFranceApp.hiringChecklist, + hiringChecklist: state.choixStatutJuridique.hiringChecklist, }), { onChecklistInitialization: initializeHiringChecklist, diff --git a/site/source/pages/Gerer/Home.tsx b/site/source/pages/Gerer/Home.tsx index 3440eb7a4b..5f3cead18e 100644 --- a/site/source/pages/Gerer/Home.tsx +++ b/site/source/pages/Gerer/Home.tsx @@ -1,26 +1,22 @@ -import { Grid } from '@mui/material' -import { - specifyIfAutoEntrepreneur, - specifyIfDirigeantMajoritaire, -} from '@/actions/existingCompanyActions' -import CompanyDetails from '@/components/CompanyDetails' +import { DottedName } from '@/../../modele-social' +import RuleInput from '@/components/conversation/RuleInput' +import { WhenApplicable, WhenNotApplicable } from '@/components/EngineValue' import PageHeader from '@/components/PageHeader' -import { FromBottom } from '@/components/ui/animate' -import { ScrollToTop } from '@/components/utils/Scroll' +import { FromTop } from '@/components/ui/animate' +import { useEngine } from '@/components/utils/EngineContext' import { SitePathsContext } from '@/components/utils/SitePathsContext' -import { Button } from '@/design-system/buttons' +import useSimulationConfig from '@/components/utils/useSimulationConfig' +import { Message } from '@/design-system' import { Container, Spacing } from '@/design-system/layout' -import Popover from '@/design-system/Popover' -import { H2 } from '@/design-system/typography/heading' -import { Link } from '@/design-system/typography/link' +import { H2, H3 } from '@/design-system/typography/heading' import { Body, Intro } from '@/design-system/typography/paragraphs' -import { useContext, useEffect, useRef, useState } from 'react' +import { useQuestionList } from '@/hooks/useQuestionList' +import { Grid } from '@mui/material' +import Engine from 'publicodes' +import { useContext } from 'react' import { Helmet } from 'react-helmet-async' import { Trans, useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' import { Redirect } from 'react-router' -import { Company } from '@/reducers/inFranceAppReducer' -import { RootState } from '@/reducers/rootReducer' import styled from 'styled-components' import { TrackPage } from '../../ATInternetTracking' import { SimulateurCard } from '../Simulateurs/Home' @@ -34,83 +30,82 @@ import { SecuriteSocialeCard } from './cards/SecuriteSocialeCard' import forms from './forms.svg' import growth from './growth.svg' -export type DirigeantOrNull = keyof SimulatorData | null - -const infereDirigeantSimulateurFromCompanyDetails = ( - company: Company | null -): DirigeantOrNull => { - if (!company) { - return null - } - if (company.isAutoEntrepreneur) { +const infereSimulateurRevenuFromSituation = ( + engine: Engine +): keyof SimulatorData | null => { + if ( + engine.evaluate('entreprise . catégorie juridique . EI . auto-entrepreneur') + .nodeValue + ) { return 'auto-entrepreneur' } + if ( - company.statutJuridique && - ['EIRL', 'EURL', 'EI'].includes(company.statutJuridique) && - inferPLSimulateurFromCompanyDetails(company) + engine.evaluate('entreprise . catégorie juridique . SARL . unipersonnelle') + .nodeValue ) { - return inferPLSimulateurFromCompanyDetails(company) + return 'eurl' } - if (company.statutJuridique === 'EI') { - return 'entreprise-individuelle' + if ( + engine.evaluate('entreprise . catégorie juridique . SAS . unipersonnelle') + .nodeValue + ) { + return 'sasu' } if ( - company.statutJuridique && - ['EIRL', 'SASU', 'EURL'].includes(company.statutJuridique) + engine.evaluate( + 'entreprise . catégorie juridique . EI . responsabilité limité' + ).nodeValue ) { - return company.statutJuridique.toLowerCase() as 'eirl' | 'sasu' | 'eurl' + return 'eirl' } - if (company.statutJuridique === 'SARL') { - return 'indépendant' + if (engine.evaluate('entreprise . catégorie juridique . EI').nodeValue) { + const métierProfessionLibéral = engine.evaluate( + 'dirigeant . indépendant . PL . métier' + ).nodeValue + switch (métierProfessionLibéral) { + case 'avocat': + return 'avocat' + case 'expert-comptable': + return 'expert-comptable' + case 'santé . médecin': + return 'médecin' + case 'santé . chirurgien-dentiste': + return 'chirurgien-dentiste' + case 'santé . sage-femme': + return 'sage-femme' + case 'santé . auxiliaire médical': + return 'auxiliaire-médical' + case 'santé . pharmacien': + return 'pharmacien' + } + if (engine.evaluate('dirigeant . indépendant . PL').nodeValue) { + return 'profession-libérale' + } + return 'entreprise-individuelle' } + const régimeSocial = engine.evaluate('dirigeant . régime social').nodeValue - if (company.statutJuridique === 'SAS') { - return 'sasu' + if (régimeSocial === 'indépendant') { + return 'indépendant' } - + // TODO : assimilé-salarié + // if ( + // régimeSocial === 'assimilé-salarié' + // ) { + // return 'assimilé-salarié' + // } return null } -// Profession Libérale -const inferPLSimulateurFromCompanyDetails = ( - company: Company | null -): DirigeantOrNull => { - if (!company) { - return null - } - const activiteToSimulator = { - 'Activités comptables': 'expert-comptable', - 'Activité des médecins généralistes': 'médecin', - 'Activités de radiodiagnostic et de radiothérapie': 'médecin', - 'Activités chirurgicales': 'médecin', - 'Activité des médecins spécialistes': 'médecin', - 'Activités hospitalières': 'pamc', - 'Pratique dentaire': 'chirurgien-dentiste', - 'Commerce de détail de produits pharmaceutiques en magasin spécialisé': - 'pharmacien', - 'Activités des infirmiers et des sages-femmes': 'pamc', - "Activités des professionnels de la rééducation, de l'appareillage et des pédicures-podologues": - 'auxiliaire-médical', - "Laboratoires d'analyses médicales": 'pharmacien', - 'Arts du spectacle vivant': 'artiste-auteur', - 'Création artistique relevant des arts plastiques': 'artiste-auteur', - 'Autre création artistique': 'artiste-auteur', - 'Activités photographiques': 'artiste-auteur', - } as Record - return activiteToSimulator[company.activitePrincipale] || null -} - export default function Gérer() { const { t, i18n } = useTranslation() - const company = useSelector( - (state: RootState) => state.inFranceApp.existingCompany - ) - const dirigeantSimulateur = - infereDirigeantSimulateurFromCompanyDetails(company) + const dirigeantSimulateur = infereSimulateurRevenuFromSituation(useEngine()) const simulateurs = useSimulatorsData() const sitePaths = useContext(SitePathsContext) - if (!company) { + + if (!useEngine().evaluate('entreprise . SIREN').nodeValue) { + console.log('ooo') return } return ( @@ -120,183 +115,149 @@ export default function Gérer() { - - - Gérer mon activité} - > - - - Vous souhaitez vous verser un revenu ou embaucher ? Vous aurez à - payer des cotisations et des impôts. Anticipez leurs montants - grâce aux simulateurs adaptés à votre situation. - - - - - + Gérer mon activité} + > + + + Vous souhaitez vous verser un revenu ou embaucher ? Vous aurez à + payer des cotisations et des impôts. Anticipez leurs montants grâce + aux simulateurs adaptés à votre situation. + + + + + - {dirigeantSimulateur && ( - theme.colors.bases.primary[600]} - darkMode - > - - - -

    Entreprise et revenus

    - - {dirigeantSimulateur !== null && ( - - )} + theme.colors.bases.primary[600]} + darkMode + > + + + +

    Simulateurs pour votre entreprise

    + + {dirigeantSimulateur ? ( + + ) : ( + + + + + Il n'existe pas encore de simulateur de revenu pour votre + type d'entreprise sur ce site. + + + Si vous souhaitez que nous développions un nouveau + simulateur, laissez-nous message en cliquant sur le bouton + "Faire une suggestion" en bas de cette page. + + + + + )} - {company?.statutJuridique && - ['EIRL', 'EI', 'EURL', 'SARL'].includes( - company.statutJuridique - ) && - !company.isAutoEntrepreneur && ( + + + + + + + - )} - {company?.statutJuridique && - ['SARL', 'SASU', 'SAS'].includes(company.statutJuridique) && ( - - - - - - - )} - - -
    - )} - {dirigeantSimulateur !== 'auto-entrepreneur' && ( - <> -

    - Salariés et embauche -

    - - - - - - )} +
    + + + + + +
    + {dirigeantSimulateur !== 'auto-entrepreneur' && ( + +

    + Salariés et embauche +

    + + + + +
    + )} - + -

    - Ressources utiles -

    - - {dirigeantSimulateur === 'indépendant' && i18n.language === 'fr' && ( - - - - )} - {!company?.isAutoEntrepreneur && ( - - - - )} - {company?.isAutoEntrepreneur && ( - - - - )} +

    + Ressources utiles +

    + + {dirigeantSimulateur === 'indépendant' && i18n.language === 'fr' && ( - + - + )} + + + + + + - + + + + + -
    + + + + + ) } -type CompanySectionProps = { - company: Company | null +const companyDetailsConfig = { + situation: { + 'contrat salarié': 'non', + }, + objectifs: [ + 'dirigeant . régime social', + 'entreprise . imposition', + ] as DottedName[], } +export const AskCompanyMissingDetails = () => { + useSimulationConfig(companyDetailsConfig) -export const CompanySection = ({ company }: CompanySectionProps) => { - const [autoEntrepreneurModal, showAutoEntrepreneurModal] = useState(false) - - const sitePaths = useContext(SitePathsContext) - const companyRef = useRef(null) - useEffect(() => { - if (companyRef.current !== company) { - companyRef.current = company - if ( - company?.statutJuridique === 'EI' && - company?.isAutoEntrepreneur == null && - !inferPLSimulateurFromCompanyDetails(company) - ) { - showAutoEntrepreneurModal(true) - } - } - }, [company]) - - const dispatch = useDispatch() - const handleAnswerAutoEntrepreneur = (isAutoEntrepreneur: boolean) => { - dispatch(specifyIfAutoEntrepreneur(isAutoEntrepreneur)) - showAutoEntrepreneurModal(false) + const [questions, onQuestionAnswered] = useQuestionList() + if (!questions.length) { + return null } - - const { t } = useTranslation() - return ( <> - {autoEntrepreneurModal && ( - <> - - - - - - - - - - - - - )} - - {company && ( - <> - - - - Changer l'entreprise sélectionnée - - - - )} + + Répondez à ces quelques questions rapides pour selectionner les outils + et assistants qui vous conviennent le mieux. + + {questions.map((question) => ( + +

    {question.rawNode.question}

    + +
    + ))} ) } diff --git a/site/source/pages/Landing/ContinueWithCompany.tsx b/site/source/pages/Landing/ContinueWithCompany.tsx index 6cf4d587d3..c664598b01 100644 --- a/site/source/pages/Landing/ContinueWithCompany.tsx +++ b/site/source/pages/Landing/ContinueWithCompany.tsx @@ -1,11 +1,11 @@ import { Grid } from '@mui/material' -import CompanyDetails from '@/components/CompanyDetails' +import CompanySearchDetails from '@/components/company/SearchDetails' import { SitePathsContext } from '@/components/utils/SitePathsContext' import { Card } from '@/design-system/card' import { H3 } from '@/design-system/typography/heading' import { useContext } from 'react' import { Trans } from 'react-i18next' -import { Company } from '@/reducers/inFranceAppReducer' +import { Company } from '@/reducers/choixStatutJuridiqueReducer' type ContinueWithCompanyProps = { company: Company @@ -28,7 +28,7 @@ export const ContinueWithCompany = ({ company }: ContinueWithCompanyProps) => { to={sitePaths.gérer.index} data-testid="currently-selected-company" > - + diff --git a/site/source/pages/Landing/Landing.tsx b/site/source/pages/Landing/Landing.tsx index 4e4e695fac..c8a054bc16 100644 --- a/site/source/pages/Landing/Landing.tsx +++ b/site/source/pages/Landing/Landing.tsx @@ -24,9 +24,6 @@ import SearchOrCreate from './SearchOrCreate' export default function Landing() { const simulators = useSimulatorsData() const sitePaths = useContext(SitePathsContext) - const company = useSelector( - (state: RootState) => state.inFranceApp.existingCompany - ) return ( <> @@ -59,7 +56,6 @@ export default function Landing() { darkMode backgroundColor={(theme) => theme.colors.bases.primary[600]} > - {company && } diff --git a/site/source/pages/Landing/SearchOrCreate.tsx b/site/source/pages/Landing/SearchOrCreate.tsx index 5ac9c40575..985cc84390 100644 --- a/site/source/pages/Landing/SearchOrCreate.tsx +++ b/site/source/pages/Landing/SearchOrCreate.tsx @@ -1,49 +1,70 @@ -import { Grid } from '@mui/material' +import { resetCompany } from '@/actions/companyActions' import { useSetEntreprise } from '@/actions/companyStatusActions' import { FabriqueSocialEntreprise } from '@/api/fabrique-social' -import { CompanySearchField } from '@/components/CompanySearchField' -import Emoji from '@/components/utils/Emoji' +import { CompanyDetails } from '@/components/company/Details' +import { CompanySearchField } from '@/components/company/SearchField' +import Value from '@/components/EngineValue' +import { useEngine } from '@/components/utils/EngineContext' import { SitePathsContext } from '@/components/utils/SitePathsContext' +import { Message } from '@/design-system' +import AnswerGroup from '@/design-system/answer-group' import { Button } from '@/design-system/buttons' -import { H3 } from '@/design-system/typography/heading' +import { Spacing } from '@/design-system/layout' +import { H3, H4 } from '@/design-system/typography/heading' +import { RootState } from '@/reducers/rootReducer' +import { Grid } from '@mui/material' import { useCallback, useContext } from 'react' import { Trans } from 'react-i18next' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useHistory } from 'react-router-dom' -import { RootState } from '@/reducers/rootReducer' -import styled from 'styled-components' export default function SearchOrCreate() { const sitePaths = useContext(SitePathsContext) const statutChoisi = useSelector( - (state: RootState) => state.inFranceApp.companyStatusChoice + (state: RootState) => state.choixStatutJuridique.companyStatusChoice ) + const companySIREN = useEngine().evaluate('entreprise . SIREN').nodeValue const handleCompanySubmit = useHandleCompanySubmit() - + const dispatch = useDispatch() return ( - -

    - Rechercher une entreprise{' '} -

    - -
    - - - - + + {companySIREN ? ( + <> +

    Votre entreprise

    + + + + + + + + ) : ( + <> +

    + Rechercher votre entreprise{' '} +

    + + + + + + )}
    ) @@ -62,12 +83,3 @@ function useHandleCompanySubmit() { ) return handleCompanySubmit } - -const ButtonContainer = styled.h2` - text-align: center; - margin: 0; - @media (min-width: ${({ theme }) => theme.breakpointsWidth.lg}) { - position: relative; - top: 4.25rem; - } -` diff --git a/site/source/pages/Simulateurs/EconomieCollaborative/VotreSituation.tsx b/site/source/pages/Simulateurs/EconomieCollaborative/VotreSituation.tsx index 18935bdb7e..355acd5d06 100644 --- a/site/source/pages/Simulateurs/EconomieCollaborative/VotreSituation.tsx +++ b/site/source/pages/Simulateurs/EconomieCollaborative/VotreSituation.tsx @@ -149,7 +149,8 @@ export default function VotreSituation() { Cet assistant est en cours de développement.{' '} N'hésitez pas à nous faire part de toute vos remarques, idées, - questions en cliquant sur le bouton "Faire une suggestion" ci dessus. + questions en cliquant sur le bouton "Faire une suggestion" en bas de + la page.
    diff --git a/site/source/pages/Simulateurs/Page/index.tsx b/site/source/pages/Simulateurs/Page/index.tsx index d93432ae9a..65f96bee78 100644 --- a/site/source/pages/Simulateurs/Page/index.tsx +++ b/site/source/pages/Simulateurs/Page/index.tsx @@ -53,8 +53,7 @@ export default function PageData(props: PageDataProps) { const year = typeof année === 'number' && année != 2022 ? ` - ${année}` : '' const inIframe = useIsEmbedded() - const fromGérer = !!useLocation<{ fromGérer?: boolean }>().state?.fromGérer - useSimulationConfig(config, { useExistingCompanyFromSituation: fromGérer }) + useSimulationConfig(config) useSearchParamsSimulationSharing() // TODO : Move this logic elsewhere. diff --git "a/site/source/pages/Simulateurs/Salari\303\251.tsx" "b/site/source/pages/Simulateurs/Salari\303\251.tsx" index 4199c0bf6b..a03014f81f 100644 --- "a/site/source/pages/Simulateurs/Salari\303\251.tsx" +++ "b/site/source/pages/Simulateurs/Salari\303\251.tsx" @@ -1,5 +1,8 @@ import Banner from '@/components/Banner' -import Value, { Condition } from '@/components/EngineValue' +import Value, { + Condition, + WhenNotAlreadyDefined, +} from '@/components/EngineValue' import PeriodSwitch from '@/components/PeriodSwitch' import RuleLink from '@/components/RuleLink' import Simulation from '@/components/Simulation' @@ -45,23 +48,27 @@ export default function SalariéSimulation() { } + afterQuestionsSlot={ + + {/** L'équipe Code Du Travail Numérique ne souhaite pas référencer + * le simulateur dirigeant de SASU sur son site. */} + {!import.meta.env.SSR && + !document.referrer?.includes('code.travail.gouv.fr') && ( + + + + Vous êtes dirigeant d'une SAS(U) ?{' '} + + Accéder au simulateur de revenu dédié + + + + + )} + + } > - - {/** L'équipe Code Du Travail Numérique ne souhaite pas référencer - * le simulateur dirigeant de SASU sur son site. */} - {!import.meta.env.SSR && - !document.referrer?.includes('code.travail.gouv.fr') && ( - - - Vous êtes dirigeant d'une SAS(U) ?{' '} - - Accéder au simulateur de revenu dédié - - - - )} - ) diff --git a/site/source/pages/Simulateurs/configs/artiste-auteur.yaml b/site/source/pages/Simulateurs/configs/artiste-auteur.yaml index 8bfc7b626f..df54469061 100644 --- a/site/source/pages/Simulateurs/configs/artiste-auteur.yaml +++ b/site/source/pages/Simulateurs/configs/artiste-auteur.yaml @@ -1,5 +1,6 @@ situation: - dirigeant: "'artiste-auteur'" + dirigeant: non + contrat salarié: non unité par défaut: €/an objectifs: - artiste-auteur . cotisations diff --git a/site/source/pages/Simulateurs/configs/auto-entrepreneur.yaml b/site/source/pages/Simulateurs/configs/auto-entrepreneur.yaml index 2b8ae8cae6..409de15157 100644 --- a/site/source/pages/Simulateurs/configs/auto-entrepreneur.yaml +++ b/site/source/pages/Simulateurs/configs/auto-entrepreneur.yaml @@ -22,8 +22,9 @@ questions: liste noire: - entreprise . charges - entreprise . chiffre d'affaires + - entreprise . activité . mixte unité par défaut: €/an situation: - entreprise . activité . mixte: non - dirigeant: "'auto-entrepreneur'" + entreprise . catégorie juridique: "'EI'" + entreprise . catégorie juridique . EI . auto-entrepreneur: oui diff --git "a/site/source/pages/Simulateurs/configs/ch\303\264mage-partiel.yaml" "b/site/source/pages/Simulateurs/configs/ch\303\264mage-partiel.yaml" index e6ccd5574b..d6fd7073d1 100644 --- "a/site/source/pages/Simulateurs/configs/ch\303\264mage-partiel.yaml" +++ "b/site/source/pages/Simulateurs/configs/ch\303\264mage-partiel.yaml" @@ -12,6 +12,7 @@ questions: - établissement . localisation unité par défaut: €/mois + situation: dirigeant: non contrat salarié . activité partielle: oui diff --git a/site/source/pages/Simulateurs/configs/dirigeant-sasu.yaml b/site/source/pages/Simulateurs/configs/dirigeant-sasu.yaml index 25b0354e09..a47b7f35d9 100644 --- a/site/source/pages/Simulateurs/configs/dirigeant-sasu.yaml +++ b/site/source/pages/Simulateurs/configs/dirigeant-sasu.yaml @@ -27,7 +27,7 @@ questions: unité par défaut: €/an situation: - dirigeant: "'assimilé salarié'" + entreprise . catégorie juridique: "'SAS'" entreprise . résultat fiscal: 0 €/an #TODO : en attendant que la transitivité du remplacement soit implémentée (https://github.com/betagouv/publicodes/issues/55) diff --git a/site/source/pages/Simulateurs/configs/dividendes.yaml b/site/source/pages/Simulateurs/configs/dividendes.yaml index 74330a5d76..bf6dbf1dcc 100644 --- a/site/source/pages/Simulateurs/configs/dividendes.yaml +++ b/site/source/pages/Simulateurs/configs/dividendes.yaml @@ -11,8 +11,7 @@ questions: unité par défaut: €/an situation: - dirigeant: "'assimilé salarié'" # [TODO] [dividendes-indep] - bénéficiaire: oui + dirigeant . régime social: "'assimilé salarié'" # [TODO] [dividendes-indep] entreprise . imposition: "'IS'" impôt . méthode de calcul: "'PFU'" dirigeant . rémunération . imposable: 0 €/an diff --git "a/site/source/pages/Simulateurs/configs/ind\303\251pendant.yaml" "b/site/source/pages/Simulateurs/configs/ind\303\251pendant.yaml" index f6d315bb42..e083a2b0f3 100644 --- "a/site/source/pages/Simulateurs/configs/ind\303\251pendant.yaml" +++ "b/site/source/pages/Simulateurs/configs/ind\303\251pendant.yaml" @@ -17,6 +17,7 @@ questions: - entreprise . imposition - entreprise . exercice . début - entreprise . exercice . fin + - entreprise . catégorie juridique liste: - entreprise - établissement @@ -30,4 +31,4 @@ questions: - entreprise . activité . débit de tabac unité par défaut: €/an situation: - dirigeant: "'indépendant'" + dirigeant . régime social: "'indépendant'" diff --git "a/site/source/pages/Simulateurs/configs/profession-lib\303\251rale.yaml" "b/site/source/pages/Simulateurs/configs/profession-lib\303\251rale.yaml" index b86e45c87e..a84e8c84a3 100644 --- "a/site/source/pages/Simulateurs/configs/profession-lib\303\251rale.yaml" +++ "b/site/source/pages/Simulateurs/configs/profession-lib\303\251rale.yaml" @@ -26,6 +26,6 @@ questions: - entreprise . ZFU unité par défaut: €/an situation: - dirigeant: "'indépendant'" + dirigeant . régime social: "'indépendant'" entreprise . activité: "'libérale'" entreprise . imposition: "'IR'" diff --git "a/site/source/pages/Simulateurs/configs/r\303\251mun\303\251ration-dirigeant.yaml" "b/site/source/pages/Simulateurs/configs/r\303\251mun\303\251ration-dirigeant.yaml" index 24fdd6b29a..ec8b3f459e 100644 --- "a/site/source/pages/Simulateurs/configs/r\303\251mun\303\251ration-dirigeant.yaml" +++ "b/site/source/pages/Simulateurs/configs/r\303\251mun\303\251ration-dirigeant.yaml" @@ -17,6 +17,6 @@ questions: - entreprise . activité unité par défaut: €/an situation: - dirigeant: "'auto-entrepreneur'" + dirigeant . régime social: "'auto-entrepreneur'" entreprise . activité . mixte: non contrat salarié . ATMP . taux réduit: oui diff --git a/site/source/pages/Simulateurs/metadata-src.ts b/site/source/pages/Simulateurs/metadata-src.ts index b719c0f859..daf360972e 100644 --- a/site/source/pages/Simulateurs/metadata-src.ts +++ b/site/source/pages/Simulateurs/metadata-src.ts @@ -128,8 +128,8 @@ const metadataSrc = (t: TFunction<'translation', string>) => { ), }, pathId: 'simulateurs.sasu', - shortName: t('pages.simulateurs.sasu.shortname', 'SASU'), - title: t('pages.simulateurs.sasu.title', 'Simulateur de SASU'), + shortName: t('pages.simulateurs.sasu.shortname', 'SAS(U)'), + title: t('pages.simulateurs.sasu.title', 'Simulateur de SAS(U)'), nextSteps: ['is', 'comparaison-statuts'], }, eurl: { diff --git a/site/source/pages/Simulateurs/metadata.tsx b/site/source/pages/Simulateurs/metadata.tsx index 04ab5c2077..911c46c3dc 100644 --- a/site/source/pages/Simulateurs/metadata.tsx +++ b/site/source/pages/Simulateurs/metadata.tsx @@ -4,12 +4,12 @@ import { SitePathsContext } from '@/components/utils/SitePathsContext' import { H2 } from '@/design-system/typography/heading' import { Link } from '@/design-system/typography/link' import { Body } from '@/design-system/typography/paragraphs' +import { SimulationConfig } from '@/reducers/rootReducer' import React, { createContext, useContext, useMemo } from 'react' import { TFunction, Trans, useTranslation } from 'react-i18next' -import { SimulationConfig } from '@/reducers/rootReducer' import { constructLocalizedSitePath, SitePathsType } from '../../sitePaths' import Créer from '../Creer/Home' -import AideDéclarationIndépendant from '../Gerer/AideDéclarationIndépendant' +import AideDéclarationIndépendant from '../Gerer/AideDéclarationIndépendant/PreviousVersion' import FormulaireMobilitéIndépendant from '../Gerer/DemandeMobilite' import AidesEmbauche from './AidesEmbauche' import ArtisteAuteur from './ArtisteAuteur' diff --git a/site/source/reducers/inFranceAppReducer.ts b/site/source/reducers/choixStatutJuridiqueReducer.ts similarity index 50% rename from site/source/reducers/inFranceAppReducer.ts rename to site/source/reducers/choixStatutJuridiqueReducer.ts index b3ebaba7db..1acb26fc80 100644 --- a/site/source/reducers/inFranceAppReducer.ts +++ b/site/source/reducers/choixStatutJuridiqueReducer.ts @@ -1,10 +1,8 @@ import { Action } from '@/actions/actions' -import { FabriqueSocialEntreprise } from '@/api/fabrique-social' -import { ApiCommuneJson } from '@/components/conversation/select/SelectCommune' -import { omit } from '../utils' -import { combineReducers } from 'redux' import { LegalStatus } from '@/selectors/companyStatusSelectors' import { LegalStatusRequirements } from '@/types/companyTypes' +import { combineReducers } from 'redux' +import { omit } from '../utils' function companyLegalStatus( state: LegalStatusRequirements = {}, @@ -86,95 +84,14 @@ function companyStatusChoice(state: LegalStatus | null = null, action: Action) { return action.statusName } -type StatutJuridique = - | 'EI' - | 'EURL' - | 'SARL' - | 'SAS' - | 'SA' - | 'SASU' - | 'NON_IMPLÉMENTÉ' - -const infereLegalStatusFromCategorieJuridique = ( - catégorieJuridique: string -): StatutJuridique => { - /* - Nous utilisons le code entreprise pour connaitre le statut juridique - (voir https://www.insee.fr/fr/information/2028129) - - En revanche, impossible de différencier EI et auto-entreprise - https://www.sirene.fr/sirene/public/question.action?idQuestion=2933 - */ - - if (catégorieJuridique === '1000') { - return 'EI' - } - if (catégorieJuridique === '5498') { - return 'EURL' - } - if (/^54..$/.exec(catégorieJuridique)) { - return 'SARL' - } - if (/^55..$/.exec(catégorieJuridique)) { - return 'SA' - } - if (catégorieJuridique === '5720') { - return 'SASU' - } - if (/^57..$/.exec(catégorieJuridique)) { - return 'SAS' - } - return 'NON_IMPLÉMENTÉ' -} - -export type Company = Omit & { - statutJuridique?: StatutJuridique - isAutoEntrepreneur?: boolean - isDirigeantMajoritaire?: boolean - localisation?: ApiCommuneJson -} - -function existingCompany( - state: Company | null = null, - action: Action -): Company | null { - if (!action.type.startsWith('EXISTING_COMPANY::')) { - return state - } - if (action.type === 'EXISTING_COMPANY::RESET') { - return null - } - if (action.type === 'EXISTING_COMPANY::SET_COMPANY') { - const statutJuridique = infereLegalStatusFromCategorieJuridique( - action.entreprise.categorieJuridiqueUniteLegale - ) - return { - ...omit(action.entreprise, 'highlightLabel'), - statutJuridique, - } - } - if (state && action.type === 'EXISTING_COMPANY::SPECIFY_AUTO_ENTREPRENEUR') { - return { ...state, isAutoEntrepreneur: action.isAutoEntrepreneur } - } - if ( - state && - action.type === 'EXISTING_COMPANY::SPECIFY_DIRIGEANT_MAJORITAIRE' - ) { - return { ...state, isDirigeantMajoritaire: action.isDirigeantMajoritaire } - } - if (state && action.type === 'EXISTING_COMPANY::ADD_COMMUNE_DETAILS') { - return { ...state, localisation: action.details } - } - return state -} - -const inFranceAppReducer = combineReducers({ +const choixStatutJuridiqueReducer = combineReducers({ companyLegalStatus, companyStatusChoice, companyCreationChecklist, - existingCompany, hiringChecklist, }) -export default inFranceAppReducer +export default choixStatutJuridiqueReducer -export type InFranceAppState = ReturnType +export type ChoixStatutJuridiqueState = ReturnType< + typeof choixStatutJuridiqueReducer +> diff --git a/site/source/reducers/companySituationReducer.ts b/site/source/reducers/companySituationReducer.ts new file mode 100644 index 0000000000..4cef472aaa --- /dev/null +++ b/site/source/reducers/companySituationReducer.ts @@ -0,0 +1,128 @@ +import { DottedName } from '@/../../modele-social' +import { Action } from '@/actions/actions' +import { FabriqueSocialEntreprise } from '@/api/fabrique-social' +import { Situation } from './rootReducer' + +const SAVED_NAMESPACES = [ + 'contrat salarié . ATMP', + 'contrat salarié . convention collective', + 'dirigeant . gérant minoritaire', + 'dirigeant . indépendant . PL . métier', + 'entreprise . ACRE', + 'entreprise . activité', + 'entreprise . catégorie juridique', + 'entreprise . date de création', + 'entreprise . effectif', + 'entreprise . exonérée de TVA', + 'entreprise . imposition', + 'entreprise . SIREN', + 'entreprise . nom', + 'établissement . adresse', + 'établissement . localisation', +] as Array + +export type Company = Omit + +export function companySituation(state: Situation = {}, action: Action) { + switch (action.type) { + case 'UPDATE_SITUATION': + if ( + SAVED_NAMESPACES.some((namespace) => + action.fieldName.startsWith(namespace) + ) + ) { + return { + [action.fieldName]: action.value, + ...state, + } + } + break + case 'COMPANY::SET_EXISTING_COMPANY': + return getCompanySituation(action.entreprise) + case 'COMPANY::RESET': + return {} + case 'COMPANY::ADD_COMMUNE_DETAILS': + return { + ...state, + 'établissement . localisation': { objet: action.details }, + } + case 'SET_SIMULATION': + state['entreprise . SIREN'] ? state : {} + } + return state +} + +export function getCompanySituation(company: Company): Situation { + return { + 'entreprise . date de création': company.dateCreationUniteLegale.replace( + /(.*)-(.*)-(.*)/, + '$3/$2/$1' + ), + 'entreprise . catégorie juridique': `'${getCatégorieFromCode( + company.categorieJuridiqueUniteLegale + )}'`, + 'entreprise . SIREN': `'${company.siren}'`, + 'entreprise . nom': `'${company.label}'`, + } +} + +type CatégorieJuridique = 'EI' | 'SARL' | 'SAS' | 'SELARL' | 'SELAS' | 'AUTRE' + +const getCatégorieFromCode = (code: string): CatégorieJuridique => { + /* + Nous utilisons le code entreprise pour connaitre le statut juridique + (voir https://www.insee.fr/fr/information/2028129) + + En revanche, impossible de différencier EI et auto-entreprise + https://www.sirene.fr/sirene/public/question.action?idQuestion=2933 + */ + + if (code === '1000') { + return 'EI' + } + if (code === '5485') { + return 'SELARL' + } + if (code === '5470') { + return 'AUTRE' + } + if (/^54..$/.exec(code)) { + return 'SARL' + } + if (code === '5785') { + return 'SELAS' + } + if (code === '5710') { + return 'SAS' + } + return 'AUTRE' +} + +// // Profession Libérale +// const inferPLSimulateurFromCompanyDetails = ( +// company: Company | null +// ): DirigeantOrNull => { +// if (!company) { +// return null +// } +// const activiteToSimulator = { +// 'Activités comptables': 'expert-comptable', +// 'Activité des médecins généralistes': 'médecin', +// 'Activités de radiodiagnostic et de radiothérapie': 'médecin', +// 'Activités chirurgicales': 'médecin', +// 'Activité des médecins spécialistes': 'médecin', +// 'Activités hospitalières': 'pamc', +// 'Pratique dentaire': 'chirurgien-dentiste', +// 'Commerce de détail de produits pharmaceutiques en magasin spécialisé': +// 'pharmacien', +// 'Activités des infirmiers et des sages-femmes': 'pamc', +// "Activités des professionnels de la rééducation, de l'appareillage et des pédicures-podologues": +// 'auxiliaire-médical', +// "Laboratoires d'analyses médicales": 'pharmacien', +// 'Arts du spectacle vivant': 'artiste-auteur', +// 'Création artistique relevant des arts plastiques': 'artiste-auteur', +// 'Autre création artistique': 'artiste-auteur', +// 'Activités photographiques': 'artiste-auteur', +// } as Record +// return activiteToSimulator[company.activitePrincipale] || null +// } diff --git a/site/source/reducers/rootReducer.ts b/site/source/reducers/rootReducer.ts index 042107014d..4291ea25ec 100644 --- a/site/source/reducers/rootReducer.ts +++ b/site/source/reducers/rootReducer.ts @@ -1,15 +1,15 @@ import { Action } from '@/actions/actions' -import { getCompanySituation } from '@/components/utils/useSimulationConfig' +import { ApiCommuneJson } from '@/components/conversation/select/SelectCommune' +import { PreviousSimulation } from '@/selectors/previousSimulationSelectors' import { DottedName } from 'modele-social' import { defaultTo, without } from 'ramda' -import { omit } from '../utils' import reduceReducers from 'reduce-reducers' import { combineReducers, Reducer } from 'redux' -import { PreviousSimulation } from '@/selectors/previousSimulationSelectors' import { objectifsSelector } from '../selectors/simulationSelectors' -import inFranceAppReducer from './inFranceAppReducer' +import { omit } from '../utils' +import { companySituation } from './companySituationReducer' +import choixStatutJuridique from './choixStatutJuridiqueReducer' import previousSimulationRootReducer from './previousSimulationRootReducer' -import { ApiCommuneJson } from '@/components/conversation/select/SelectCommune' function explainedVariable( state: DottedName | null = null, @@ -73,7 +73,6 @@ export type Simulation = { url: string hiddenNotifications: Array situation: Situation - initialSituation: Situation targetUnit: string foldedSteps: Array unfoldedStep?: DottedName | null @@ -84,15 +83,14 @@ function simulation( action: Action ): Simulation | null { if (action.type === 'SET_SIMULATION') { - const { config, url, initialSituation } = action + const { config, url } = action return { config, url, hiddenNotifications: [], - situation: initialSituation ?? {}, - initialSituation: initialSituation ?? {}, + situation: {}, targetUnit: config['unité par défaut'] || '€/mois', - foldedSteps: Object.keys(initialSituation ?? {}) as Array, + foldedSteps: [], unfoldedStep: null, } } @@ -111,23 +109,11 @@ function simulation( return { ...state, hiddenNotifications: [], - situation: state.initialSituation, + situation: {}, foldedSteps: [], unfoldedStep: null, } - case 'BATCH_UPDATE_SITUATION': { - return ( - Object.entries(action.situation as any) as Array<[DottedName, unknown]> - ).reduce( - (newState, [fieldName, value]) => - simulation(newState, { - type: 'UPDATE_SITUATION', - fieldName, - value, - }), - state - ) - } + case 'UPDATE_SITUATION': { const objectifs = without( ['entreprise . charges'], @@ -175,32 +161,34 @@ function simulation( } return state } -const existingCompanyReducer = (state: RootState, action: Action) => { - if (action.type.startsWith('EXISTING_COMPANY::') && state.simulation) { - return { - ...state, - simulation: { - ...state.simulation, - situation: { - ...state.simulation.situation, - ...getCompanySituation(state.inFranceApp.existingCompany), - }, - }, - } + +function batchUpdateSituationReducer(state: RootState, action: Action) { + if (action.type !== 'BATCH_UPDATE_SITUATION') { + return state } - return state + return Object.entries(action.situation).reduce( + (newState, [fieldName, value]) => + mainReducer(newState ?? undefined, { + type: 'UPDATE_SITUATION', + fieldName, + value, + }), + state + ) } + const mainReducer = combineReducers({ explainedVariable, simulation, + companySituation, previousSimulation: defaultTo(null) as Reducer, activeTargetInput, - inFranceApp: inFranceAppReducer, + choixStatutJuridique, }) export default reduceReducers( - mainReducer as any, - existingCompanyReducer as Reducer, + mainReducer as Reducer, + batchUpdateSituationReducer as Reducer, previousSimulationRootReducer as Reducer ) as Reducer diff --git a/site/source/selectors/companyStatusSelectors.ts b/site/source/selectors/companyStatusSelectors.ts index 2e632ac3fe..fe3f220d25 100644 --- a/site/source/selectors/companyStatusSelectors.ts +++ b/site/source/selectors/companyStatusSelectors.ts @@ -126,12 +126,12 @@ const possibleStatus = ( ) export const possibleStatusSelector = (state: { - inFranceApp: State + choixStatutJuridique: State }): Record => - possibleStatus(state.inFranceApp.companyLegalStatus) + possibleStatus(state.choixStatutJuridique.companyLegalStatus) export const nextQuestionSelector = (state: RootState): Question | null => { - const legalStatusRequirements = state.inFranceApp.companyLegalStatus + const legalStatusRequirements = state.choixStatutJuridique.companyLegalStatus const questionAnswered = Object.keys( legalStatusRequirements ) as Array diff --git a/site/source/selectors/simulationSelectors.ts b/site/source/selectors/simulationSelectors.ts index 67b891042a..08d4ab906a 100644 --- a/site/source/selectors/simulationSelectors.ts +++ b/site/source/selectors/simulationSelectors.ts @@ -1,5 +1,5 @@ -import { DottedName } from 'modele-social' import { RootState, SimulationConfig, Situation } from '@/reducers/rootReducer' +import { DottedName } from 'modele-social' export const configSelector = (state: RootState): Partial => state.simulation?.config ?? {} @@ -13,7 +13,7 @@ export const objectifsSelector = (state: RootState) => { .flat() const objectifs = [...primaryObjectifs, ...(config['objectifs cachés'] ?? [])] - return objectifs + return objectifs as Array } const emptySituation: Situation = {} @@ -21,23 +21,15 @@ const emptySituation: Situation = {} export const situationSelector = (state: RootState) => state.simulation?.situation ?? emptySituation -export const initialSituationSelector = (state: RootState) => - state.simulation?.initialSituation ?? emptySituation - export const configSituationSelector = (state: RootState) => configSelector(state).situation ?? emptySituation +export const companySituationSelector = (state: RootState) => + state.companySituation + export const firstStepCompletedSelector = (state: RootState) => { const situation = situationSelector(state) - const baseSituation = configSituationSelector(state) - const initialSituation = initialSituationSelector(state) - return ( - Object.keys(situation).filter( - (dottedName) => - !Object.keys(baseSituation).includes(dottedName) && - !Object.keys(initialSituation).includes(dottedName) - ).length > 0 - ) + return Object.keys(situation).length > 0 } export const targetUnitSelector = (state: RootState) => diff --git a/site/source/storage/persistInFranceApp.ts b/site/source/storage/persistChoixStatutJuridique.ts similarity index 57% rename from site/source/storage/persistInFranceApp.ts rename to site/source/storage/persistChoixStatutJuridique.ts index 97ee406b34..baa1bb874d 100644 --- a/site/source/storage/persistInFranceApp.ts +++ b/site/source/storage/persistChoixStatutJuridique.ts @@ -1,5 +1,5 @@ import { Action } from '@/actions/actions' -import { InFranceAppState } from '@/reducers/inFranceAppReducer' +import { ChoixStatutJuridiqueState } from '@/reducers/choixStatutJuridiqueReducer' import { RootState } from '@/reducers/rootReducer' import { Store } from 'redux' import { debounce } from '../utils' @@ -9,18 +9,22 @@ const VERSION = 7 const LOCAL_STORAGE_KEY = 'mon-entreprise::persisted-infranceapp::v' + VERSION -export function setupInFranceAppPersistence(store: Store) { +export function setupChoixStatutJuridiquePersistence( + store: Store +) { const listener = () => { const state = store.getState() safeLocalStorage.setItem( LOCAL_STORAGE_KEY, - JSON.stringify(state.inFranceApp) + JSON.stringify(state.choixStatutJuridique) ) } store.subscribe(debounce(1000, listener)) } -export function retrievePersistedInFranceApp(): InFranceAppState { +export function retrievePersistedChoixStatutJuridique(): ChoixStatutJuridiqueState { const serializedState = safeLocalStorage.getItem(LOCAL_STORAGE_KEY) - return serializedState ? JSON.parse(serializedState) : undefined + return serializedState && serializedState !== 'undefined' + ? JSON.parse(serializedState) + : undefined } diff --git a/site/source/storage/persistCompanySituation.ts b/site/source/storage/persistCompanySituation.ts new file mode 100644 index 0000000000..5cdc67c1cc --- /dev/null +++ b/site/source/storage/persistCompanySituation.ts @@ -0,0 +1,30 @@ +import { Action } from '@/actions/actions' + +import { RootState, Situation } from '@/reducers/rootReducer' +import { Store } from 'redux' +import { debounce } from '../utils' +import * as safeLocalStorage from './safeLocalStorage' + +const VERSION = 1 + +const LOCAL_STORAGE_KEY = `mon-entreprise::companySituation::v${VERSION}` + +export function setupCompanySituationPersistence( + store: Store +) { + const listener = () => { + const state = store.getState() + safeLocalStorage.setItem( + LOCAL_STORAGE_KEY, + JSON.stringify(state.companySituation) + ) + } + store.subscribe(debounce(1000, listener)) +} + +export function retrievePersistedCompanySituation(): Situation | undefined { + const serializedState = safeLocalStorage.getItem(LOCAL_STORAGE_KEY) + return serializedState && serializedState !== 'undefined' + ? (JSON.parse(serializedState) as Situation) + : undefined +} diff --git a/site/test/companyStatusSelectors.test.js b/site/test/companyStatusSelectors.test.js index 52b5faeb8e..3add5cb5af 100644 --- a/site/test/companyStatusSelectors.test.js +++ b/site/test/companyStatusSelectors.test.js @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest' import { nextQuestionSelector } from '@/selectors/companyStatusSelectors' const state = (companyLegalStatus) => ({ - inFranceApp: { + choixStatutJuridique: { companyLegalStatus, existingCompany: null, companyStatusChoice: null, diff --git a/site/test/persistence.test.ts b/site/test/persistence.test.ts index 8795b8618b..72bf61fb51 100644 --- a/site/test/persistence.test.ts +++ b/site/test/persistence.test.ts @@ -29,7 +29,7 @@ const initialSimulation: Simulation = { url: '/someurl', hiddenNotifications: [], situation: {}, - initialSituation: {}, + companySituation: {}, targetUnit: '€/mois', foldedSteps: ['somestep' as DottedName], unfoldedStep: null, diff --git a/site/test/regressions/simulations-dividendes.yaml b/site/test/regressions/simulations-dividendes.yaml index 728d2c8861..5291fee443 100644 --- a/site/test/regressions/simulations-dividendes.yaml +++ b/site/test/regressions/simulations-dividendes.yaml @@ -1,47 +1,47 @@ pfu: - bénéficiaire . dividendes . bruts: 200 €/an impôt . méthode de calcul: "'PFU'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" - bénéficiaire . dividendes . bruts: 20000000 €/an impôt . méthode de calcul: "'PFU'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" barème défauts: - bénéficiaire . dividendes . bruts: 200 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" - bénéficiaire . dividendes . bruts: 20000000 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" - bénéficiaire . dividendes . bruts: 200 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . revenu imposable . autres revenus imposables: 500000 €/an - bénéficiaire . dividendes . bruts: 20000 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . revenu imposable . autres revenus imposables: 50000 €/an barème couple 2 enfants: - bénéficiaire . dividendes . bruts: 200 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . enfants à charge: 2 impôt . foyer fiscal . situation de famille: "'couple'" - bénéficiaire . dividendes . bruts: 20000000 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . enfants à charge: 2 impôt . foyer fiscal . situation de famille: "'couple'" - bénéficiaire . dividendes . bruts: 200 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . revenu imposable . autres revenus imposables: 500000 €/an impôt . foyer fiscal . enfants à charge: 2 impôt . foyer fiscal . situation de famille: "'couple'" - bénéficiaire . dividendes . bruts: 20000 €/an impôt . méthode de calcul: "'barème standard'" - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" impôt . foyer fiscal . revenu imposable . autres revenus imposables: 50000 €/an impôt . foyer fiscal . enfants à charge: 2 impôt . foyer fiscal . situation de famille: "'couple'" diff --git "a/site/test/regressions/simulations-salari\303\251.yaml" "b/site/test/regressions/simulations-salari\303\251.yaml" index f7a33b5f47..7d0b9e7bd6 100644 --- "a/site/test/regressions/simulations-salari\303\251.yaml" +++ "b/site/test/regressions/simulations-salari\303\251.yaml" @@ -86,16 +86,6 @@ atmp: - contrat salarié . rémunération . brut de base: 2000 €/mois contrat salarié . ATMP . taux collectif ATMP: 5% -assimilé salarié: - - dirigeant: "'assimilé salarié'" - contrat salarié . rémunération . brut de base: 5000 €/mois - - dirigeant: "'assimilé salarié'" - contrat salarié . rémunération . brut de base: 1500 €/mois - entreprise . ACRE: oui - - dirigeant: "'assimilé salarié'" - contrat salarié . rémunération . brut de base: 3000 €/mois - entreprise . ACRE: oui - aides: - contrat salarié . rémunération . brut de base: 2000 €/mois contrat salarié . statut JEI: oui @@ -266,7 +256,7 @@ JEI: - contrat salarié . rémunération . brut de base: 20000 €/mois contrat salarié . statut JEI: oui - contrat salarié . rémunération . brut de base: 4000 €/mois - dirigeant: "'assimilé salarié'" + dirigeant . régime social: "'assimilé salarié'" contrat salarié . statut JEI: oui frais pro - titres restaurant: diff --git a/site/test/regressions/simulations.test.ts b/site/test/regressions/simulations.test.ts index 92ad960cb7..752b9e9e0d 100644 --- a/site/test/regressions/simulations.test.ts +++ b/site/test/regressions/simulations.test.ts @@ -29,7 +29,9 @@ import employeeSituations from './simulations-salarié.yaml' type SituationsSpecs = Record const roundResult = (arr: number[]) => arr.map((x) => Math.round(x)) -const engine = engineFactory(rules) +const engine = engineFactory(rules, { + logger: { warn: () => {}, error: (m) => console.error(m), log: () => {} }, +}) const runSimulations = ( situationsSpecs: SituationsSpecs, objectifs: DottedName[], @@ -108,7 +110,7 @@ it('calculate simulations-rémunération-dirigeant (assimilé salarié)', () => remunerationDirigeantConfig.objectifs, { ...remunerationDirigeantConfig.situation, - dirigeant: "'assimilé salarié'", + 'dirigeant . régime social': "'assimilé salarié'", } ) }) @@ -119,7 +121,8 @@ it('calculate simulations-rémunération-dirigeant (auto-entrepreneur)', () => { remunerationDirigeantConfig.objectifs, { ...remunerationDirigeantConfig.situation, - dirigeant: "'auto-entrepreneur'", + 'entreprise . catégorie juridique': "'EI'", + 'entreprise . catégorie juridique . EI . auto-entrepreneur': 'oui', } ) }) @@ -130,7 +133,8 @@ it('calculate simulations-rémunération-dirigeant (indépendant)', () => { remunerationDirigeantConfig.objectifs, { ...remunerationDirigeantConfig.situation, - dirigeant: "'indépendant'", + 'dirigeant . régime social': "'indépendant'", + 'entreprise . imposition': "'IR'", } ) }) diff --git a/yarn.lock b/yarn.lock index be2e2ef395..6a2fc0881f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15820,30 +15820,28 @@ __metadata: languageName: node linkType: hard -"publicodes-react@npm:^1.0.0-beta.32": - version: 1.0.0-beta.32 - resolution: "publicodes-react@npm:1.0.0-beta.32" +"publicodes-react@portal:/home/johan/Projets/publicodes/packages/react-ui::locator=root%40workspace%3A.": + version: 0.0.0-use.local + resolution: "publicodes-react@portal:/home/johan/Projets/publicodes/packages/react-ui::locator=root%40workspace%3A." dependencies: styled-components: ^5.1.0 peerDependencies: publicodes: 1.0.0-beta.32 react: ^17.0.2 - checksum: 2cdf91420982e909869cf9ddc1754943d4e5e8bef7545e4d5afdfc202eb1bbdcb33197bb4ad2978f6cfd02aad3229ec53061ff06680c9f2917638fde79b98b28 languageName: node - linkType: hard + linkType: soft -"publicodes@npm:^1.0.0-beta.32": - version: 1.0.0-beta.32 - resolution: "publicodes@npm:1.0.0-beta.32" +"publicodes@portal:/home/johan/Projets/publicodes/packages/core::locator=root%40workspace%3A.": + version: 0.0.0-use.local + resolution: "publicodes@portal:/home/johan/Projets/publicodes/packages/core::locator=root%40workspace%3A." dependencies: moo: ^0.5.1 nearley: ^2.19.2 yaml: ^1.9.2 peerDependencies: "@types/mocha": ^9.0.0 - checksum: 674a5f1ee9f755cf8f9fd2312523ee70f3963b1c8cb9e0249919ad48269f3dbe831d4fe3e1c13400216bd21b34d2bbd5f71e571b3adc210ebdb1c2dc18ee72d6 languageName: node - linkType: hard + linkType: soft "pump@npm:^2.0.0": version: 2.0.1