diff --git a/.editorconfig b/.editorconfig index 3a04b5e17..610ff3dc5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,24 +8,18 @@ trim_trailing_whitespace = true # tab_width doesn't make much sense as it can be left to the reader to decide. indent_style = tab insert_final_newline = true +max_line_length = 80 [**.{js,jsx,ts,tsx}] indent_size = 2 -max_line_length = 80 - [**.{yml,yaml}] # Spaces are mandatory for yaml files: indent_style = space indent_size = 2 -# A high max_line_length is needed as prettier doesn't manage property-name -# line-wrapping correctly: -# See https://github.com/prettier/prettier/issues/5599 -max_line_length = 1000 -trim_trailing_whitespace = false [*.md] -trim_trailing_whitespace = false indent_style = space indent_size = 4 +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 7e0cec355..000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -dist -publicodes/example/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 9c12a1792..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,111 +0,0 @@ -module.exports = { - root: true, - parser: "babel-eslint", - parserOptions: { - "ecmaFeatures": { - "jsx": true - } - }, - env: { - "browser": true, - "commonjs": true, - "es6": true, - }, - globals: { - "process": false - }, - plugins: [ - "react", - "react-hooks", - "mocha" - ], - rules: { - "quotes": [ - 1, - "single", - { - "avoidEscape": true - } - ], - "no-console": 1, - "no-restricted-globals": [ - 2, - "length" - ], - "no-global-assign": 0, - "no-unsafe-negation": 0, - "react/prop-types": 0, - "react/jsx-no-target-blank": 0, - "react/no-unescaped-entities": 0, - "react/display-name": 1, - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - "mocha/no-skipped-tests": "warn", - "mocha/no-exclusive-tests": "error" - }, - settings: { - "react": { - "version": "detect" - } - }, - overrides: [ - { - files: [ "**/*.{ts,tsx}" ], - parser: "@typescript-eslint/parser", - parserOptions: { - "ecmaFeatures": { - "jsx": true - }, - "tsconfigRootDir": __dirname, - "project": [ "./mon-entreprise/tsconfig.json", "./publicodes/tsconfig.json" ] - }, - plugins: [ "@typescript-eslint" ], - rules: { - "@typescript-eslint/no-empty-function": 0, - "@typescript-eslint/no-use-before-define": 0, - "@typescript-eslint/member-delimiter-style": [2, { - multiline: { - delimiter: "none" - } - }], - "@typescript-eslint/explicit-function-return-type": 0, - '@typescript-eslint/prefer-string-starts-ends-with': 1, - '@typescript-eslint/no-unnecessary-type-assertion': 1, // has false positives (Object.values result) v 2.29.0 - '@typescript-eslint/no-inferrable-types': 1, // causes problems with unknown values v 2.29.0 typescript v 3.8.3 - '@typescript-eslint/no-var-requires': 'off', - // TODO - enable these new recommended rules, a first step would be to switch from "off" to "warn" - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-extra-semi': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/restrict-plus-operands': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', - '@typescript-eslint/naming-convention': 'off', - '@typescript-eslint/prefer-regexp-exec': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" - ] - }, - { - files: ["**/*.test.js"], - env: { - mocha: true - } - } - ], - extends: [ - "eslint:recommended", - "plugin:react/recommended", - "prettier", - "prettier/react", - "prettier/@typescript-eslint" - ] -} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index e88f81505..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: ['https://mon-entreprise.fr/budget'] diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml deleted file mode 100644 index e5187a9d8..000000000 --- a/.github/workflows/deploy.yaml +++ /dev/null @@ -1,222 +0,0 @@ -name: Déploiement -on: - pull_request: - types: [opened, synchronize] - push: - branches: [master, demo, next] - - # We display the release notes in the "news" section of mon-entreprise.fr so - # we want to re-deploy the site when a new release is published or edited on - # GitHub. - release: - types: [published, edited] - - # The /stats data is generated during the build. To keep the daily data fresh, - # we relaunch a nightly full build of the app - schedule: - - cron: "0 4 * * *" - -jobs: - deploy-context: - runs-on: ubuntu-18.04 - outputs: - env-name: ${{ steps.deploy-env.outputs.name }} - fr_url: ${{ steps.base-urls.outputs.fr }} - en_url: ${{ steps.base-urls.outputs.en }} - publicodes_url: ${{ steps.base-urls.outputs.publicodes }} - steps: - - id: deploy-env - run: - echo "::set-output name=name::${{ github.event.number || '${GITHUB_REF#refs/*/}' }}" - - id: base-urls - run: - echo "::set-output name=fr::${{ steps.deploy-env.outputs.name == 'master' && 'https://mon-entreprise.fr' || format('https://{0}--mon-entreprise.netlify.app', steps.deploy-env.outputs.name) }}"; - echo "::set-output name=en::${{ steps.deploy-env.outputs.name == 'master' && 'https://mycompanyinfrance.fr' || format('https://{0}-en--mon-entreprise.netlify.app', steps.deploy-env.outputs.name) }}"; - echo "::set-output name=publicodes::${{ steps.deploy-env.outputs.name == 'master' && 'https://publi.codes' || format('https://{0}-publicodes--mon-entreprise.netlify.app', steps.deploy-env.outputs.name) }}"; - - build: - needs: deploy-context - env: - FR_BASE_URL: ${{ needs.deploy-context.outputs.fr_url }} - EN_BASE_URL: ${{ needs.deploy-context.outputs.en_url }} - PUBLICODES_BASE_URL: ${{ needs.deploy-context.outputs.publicodes_url }} - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - env: - # Secrets of all kinds for fetching stats & releases - GITHUB_API_SECRET: ${{ secrets.GITHUB_TOKEN }} - ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }} - ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }} - ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }} - - name: Build app - run: yarn workspace mon-entreprise build - env: - AT_INTERNET_SITE_ID: ${{ needs.deploy-context.outputs.env-name == 'master' && 617190 || 617189 }} - NODE_ENV: production - - name: Replace site placeholders in netlify.toml redirection file - run: - sed -i "s|:SITE_FR|$FR_BASE_URL|g" netlify.toml; - sed -i "s|:SITE_EN|$EN_BASE_URL|g" netlify.toml; - sed -i "s|:SITE_PUBLICODES|$PUBLICODES_BASE_URL|g" netlify.toml - - uses: actions/upload-artifact@v2 - with: - name: static-site - path: | - mon-entreprise/dist/** - netlify.toml - if-no-files-found: error - - deploy-preview: - needs: [build, deploy-context] - runs-on: ubuntu-18.04 - if: needs.deploy-context.outputs.env-name != 'master' - strategy: - matrix: - site: ['', 'en', 'publicodes'] - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 - with: - name: static-site - - id: deploy-netlify - uses: nwtgck/actions-netlify@v1.1 - with: - publish-dir: './mon-entreprise/dist' - netlify-config-path: ./netlify.toml - production-deploy: false - github-token: ${{ secrets.GITHUB_TOKEN }} - enable-commit-status: true - enable-commit-comment: false - github-deployment-environment: ${{ needs.deploy-context.outputs.env-name }} - alias: ${{ needs.deploy-context.outputs.env-name }}${{ matrix.site && format('-{0}', matrix.site) }} - deploy-message: ${{ github.event.pull_request.title || needs.deploy-context.outputs.env-name }} (${{ matrix.site || 'fr' }}) - - # Disabled because we create our own customized comment - enable-pull-request-comment: false - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - timeout-minutes: 1 - - deploy-prod: - needs: [build, deploy-context] - runs-on: ubuntu-18.04 - if: needs.deploy-context.outputs.env-name == 'master' - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 - with: - name: static-site - - id: deploy-netlify - uses: nwtgck/actions-netlify@v1.1 - with: - publish-dir: './mon-entreprise/dist' - netlify-config-path: ./netlify.toml - production-deploy: true - github-token: ${{ secrets.GITHUB_TOKEN }} - enable-commit-status: true - enable-commit-comment: false - github-deployment-environment: master - deploy-message: Deploy production branch - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - timeout-minutes: 1 - - post-comment: - runs-on: ubuntu-18.04 - if: github.event_name == 'pull_request' - needs: [deploy-preview, deploy-context] - steps: - - name: Find Comment - uses: peter-evans/find-comment@v1 - id: find-comment - with: - issue-number: ${{ github.event.pull_request.number }} #e.g. 1 - comment-author: 'github-actions[bot]' - body-includes: netlify - - name: Create comment - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - 🚀 La branche est déployée ! - - - mon-entreprise : ${{ needs.deploy-context.outputs.fr_url }} - - mycompanyinfrance : ${{ needs.deploy-context.outputs.en_url }} - - publicodes : ${{ needs.deploy-context.outputs.publicodes_url }} - - - end-to-end-test: - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - runs-on: ubuntu-16.04 - # We need to specify always() https://github.com/actions/runner/issues/491 - if: always() && (needs.deploy-prod.result == 'success' || needs.deploy-preview.result == 'success') - needs: [deploy-context, deploy-prod, deploy-preview] - - strategy: - fail-fast: false - matrix: - site: ['fr', 'en', 'publicodes'] - include: - - site: fr - integrationFolder: mon-entreprise - baseUrl: ${{ needs.deploy-context.outputs.fr_url }} - language: fr - test-external: ${{ needs.deploy-context.outputs.env-name == 'master' }} - - site: en - integrationFolder: mon-entreprise - baseUrl: ${{ needs.deploy-context.outputs.en_url }} - language: en - - site: publicodes - baseUrl: ${{ needs.deploy-context.outputs.publicodes_url }} - integrationFolder: publi.codes - language: fr - - # TODO : activate parallelization https://github.com/cypress-io/github-action#parallel (missing https://github.com/cypress-io/github-action#custom-build-id) - # containers: [1, 2] - # TODO : browser: ['firefox', 'chrome'] - - steps: - - name: Checkout - uses: actions/checkout@v2 - - uses: actions/cache@v2 - # Custom cache as we do not care about installing all the other dependancies - with: - path: | - ~/.cache/Cypress - node_modules - key: cypress-cache-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - - run: npm i cypress cypress-plugin-tab - - name: Test mon-entreprise - uses: cypress-io/github-action@v2 - with: - install: false - working-directory: mon-entreprise - record: true - tag: ${{ matrix.site }},${{ needs.deploy-context.outputs.env-name }}-deploy - config: integrationFolder=cypress/integration/${{ matrix.integrationFolder }},baseUrl=${{ matrix.baseUrl }} - env: language=${{ matrix.language }} - env: - COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} - - - name: Test external integration - if: matrix.test-external - uses: cypress-io/github-action@v2 - with: - install: false - working-directory: mon-entreprise - record: true - tag: external-integration - config: integrationFolder=cypress/integration/external,baseUrl=${{ matrix.baseUrl }} - \ No newline at end of file diff --git a/.github/workflows/publish-publicodes.yaml b/.github/workflows/publish-publicodes.yaml deleted file mode 100644 index 36fd22e7f..000000000 --- a/.github/workflows/publish-publicodes.yaml +++ /dev/null @@ -1,45 +0,0 @@ -name: Publication du paquet publicodes -on: - push: - paths: - - publicodes/** - - .github/workflows/publish-publicodes.yaml - -jobs: - test: - if: contains(join(github.event.commits.*.message, ' | '), '📦 Publicodes v1.0.0-beta.') - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - working-directory: ./publicodes/example/publicodes-react - run: | - yarn install - yarn test - - publish: - needs: test - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_PUBLISH_SECRET }} - dry-run: ${{ github.ref != 'refs/heads/master' }} - package: ./publicodes/core/package.json - tag: next - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_PUBLISH_SECRET }} - dry-run: ${{ github.ref != 'refs/heads/master' }} - package: ./publicodes/ui-react/package.json - tag: next diff --git a/.github/workflows/test-publish.yaml b/.github/workflows/test-publish.yaml new file mode 100644 index 000000000..b2d939e86 --- /dev/null +++ b/.github/workflows/test-publish.yaml @@ -0,0 +1,76 @@ +name: Test and Publish +on: [push] + +jobs: + lint: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 + - run: yarn install --frozen-lockfile + - run: yarn lint:prettier + + test: + name: Unit tests + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn install + - run: yarn test + + test-type: + name: Type checking + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn install + - run: yarn test:type + + test-example-app: + name: Test example app + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 + - run: yarn install --frozen-lockfile + - working-directory: ./example/publicodes-react + run: | + yarn install + yarn test + + # This job could be in a separate workflow triggered when all the tests passes + # using the `workflow_run` event, but it makes it difficult to retrieve the + # commit message. + publish: + if: contains(join(github.event.commits.*.message, ' | '), '📦 Publicodes v1.0.0-beta.') + needs: [test, test-type, test-example-app] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 + - run: yarn install --frozen-lockfile + - uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_PUBLISH_SECRET }} + dry-run: ${{ github.ref != 'refs/heads/master' }} + package: ./core/package.json + - uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_PUBLISH_SECRET }} + dry-run: ${{ github.ref != 'refs/heads/master' }} + package: ./ui-react/package.json diff --git a/.github/workflows/test-regressions.yaml b/.github/workflows/test-regressions.yaml deleted file mode 100644 index b8084f7e2..000000000 --- a/.github/workflows/test-regressions.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Règles (non-regression) -on: - pull_request: - paths: - - modele-social/règles/** - - publicodes/core/** - - mon-entreprise/test/regressions/** -jobs: - test: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - run: yarn test:regressions diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index 324e7baa8..000000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: Tests -on: push - -jobs: - lint: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - run: yarn lint - - typecheck: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - env: - # Secrets of all kinds for fetching stats & releases - GITHUB_API_SECRET: ${{ secrets.GITHUB_TOKEN }} - ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }} - ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }} - ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }} - - run: yarn test:type - - unit: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - run: yarn test - - i18n: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-v2 - - run: yarn install --frozen-lockfile - - working-directory: mon-entreprise - run: - yarn run i18n:rules:check; - yarn run i18n:ui:check - - - diff --git a/.github/workflows/zammad-bot.yaml b/.github/workflows/zammad-bot.yaml deleted file mode 100644 index 165df262b..000000000 --- a/.github/workflows/zammad-bot.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# This bot post a comment when issues with a given label are closed. -name: Message du robot Zammad -on: - issues: - types: [closed] - -jobs: - comment-when-issue-close: - if: contains(github.event.issue.labels.*.name, '🏓 retour utilisateur') - runs-on: ubuntu-latest - steps: - # Note: we could detect if the comment was already posted in the issue to - # avoid posting it multiple times in case the issue was re-opened and - # re-closed. https://github.com/peter-evans/create-or-update-comment - - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.issue.number }} - body: | - Ce ticket vient d'être fermé 🎉 - - Il est temps de prévenir les utilisateurs qui nous ont fait ce retour : - https://mon-entreprise.zammad.com/#search/tags%3A%23${{ github.event.issue.number }} - - Laissez un 👍 quand c'est fait ! diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 946789e61..000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -16.0.0 diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 62d24c8fa..000000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -.eslintrc.js -dist diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 37710b391..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "recommendations": [ - "esbenp.prettier-vscode", - "ban.spellright", - "jpoissonnier.vscode-styled-components", - "bungcip.better-toml", - "mikestead.dotenv" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b8b3839ef..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "editor.formatOnSave": true, - "spellright.language": ["fr", "en"], - "spellright.documentTypes": ["yaml", "git-commit", "markdown"], - "typescript.tsdk": "node_modules/typescript/lib", - "editor.tabSize": 2, - "eslint.enable": true, - "cSpell.words": [ - "mycompanyinfrance", - "smarttag" - ], - "search.exclude": { - "**/dist": true - } -} diff --git a/.yarnclean b/.yarnclean deleted file mode 100644 index 7bbe5ebab..000000000 --- a/.yarnclean +++ /dev/null @@ -1 +0,0 @@ -@types/react-native diff --git a/publicodes/CHANGELOG.md b/CHANGELOG.md similarity index 85% rename from publicodes/CHANGELOG.md rename to CHANGELOG.md index 8507b2857..91dbb2890 100644 --- a/publicodes/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,8 +26,11 @@ - Fix bug sur le mécanisme minimum, une valeur non applicable n'est plus considérée comme valant "0" (#1493) -## 1.0.0-beta.16 (release candidate) +## 1.0.0-beta.16 **core** - Répare un bug dans le mécanisme résoudre le cycle +- Suppression des variables temporelles +- Optimisation de la désactivation de branches +- Meilleures performances diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f0f241e8..d76dbf946 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,217 +6,15 @@ Voici quelques informations pour démarrer : ## Rapport de bug, nouvelles fonctionnalités -Nous utilisons GitHub pour suivre tous les bugs et discussions sur les nouvelles fonctionnalités. Pour rapporter un bug ou proposer une évolution vous pouvez [ouvrir une nouvelle discussion](https://github.com/betagouv/mon-entreprise/issues/new). N'hésitez pas à utiliser la recherche pour vérifier si le sujet n'est pas déjà traité dans une discussion ouverte. +Nous utilisons GitHub pour suivre tous les bugs et discussions sur les nouvelles fonctionnalités. Pour rapporter un bug ou proposer une évolution vous pouvez [ouvrir une nouvelle discussion](https://github.com/betagouv/publicodes/discussions). N'hésitez pas à utiliser la recherche pour vérifier si le sujet n'est pas déjà traité dans une discussion ouverte. -## Développement - -Si vous voulez participer au développement de nouvelles fonctionnalités, vous pouvez consulter la liste des «[good first issue](https://github.com/betagouv/mon-entreprise/issues?q=is%3Aopen+is%3Aissue+label%3A%22%3Anew%3A+good+first+issue%22) ». Ce sont des fonctionnalités intéressantes qui ne sont normalement pas trop complexe à implémenter. N'hésitez pas à poser toutes vos questions sur ces issues ! - -### Technologies - -L'application est écrite en JavaScript, elle est exécuté uniquement côté client — il n'y a pas de serveur applicatif, nous générons des fichiers `.html` statiques - -Nous utilisons : - -- [TypeScript](https://www.typescriptlang.org) pour ajouter un système de typage à notre code JavaScript. Le typage n'est pas utilisé partout et il n'est pas obligatoire de le prendre en compte pour contribuer. -- [Yarn](https://yarnpkg.com/fr) pour la gestion des dépendances (à la place de NPM qui est souvent utilisé dans les applications JavaScript) -- [React](https://reactjs.org) pour la gestion de l'interface utilisateur -- [Redux](https://redux.js.org) pour gérer le “state” de l'application côté client -- [Prettier](https://prettier.io/) pour formater le code source, l'idéal est de configurer votre éditeur de texte pour que les fichiers soit formatés automatiquement quand vous sauvegardez un fichier. Si vous utilisez [VS Code](https://code.visualstudio.com/) cette configuration est automatique. -- [Webpack](https://webpack.js.org) pour le “bundling” -- [Eslint](http://eslint.org) qui permet par exemple d'éviter de garder des variables inutilisées -- [Ramda](https://ramdajs.com) comme libraire d'utilitaires pour manipuler les listes/objects/etc (c'est une alternative à lodash ou underscore) -- [Mocha](https://mochajs.org), [Jest](https://jestjs.io) et [Cypress](https://www.cypress.io) pour les l'execution des tests. Plus d'informations dans la section consacrée aux tests. - -### Démarrage - -Si l'historique des commits est trop volumineux, vous pouvez utiliser le paramètre `depth` de git pour ne télécharger que les derniers commits. - -``` -# Clone this repo on your computer -git clone --depth 100 git@github.com:betagouv/mon-entreprise.git && cd mon-entreprise - -# Install the Javascript dependencies through Yarn -yarn install - -# Watch changes in publicodes and run the server for mon-entreprise -yarn start -``` - -L'application est exécuté sur https://localhost:8080/mon-entreprise pour la version française et http://localhost:8080/infrance pour la version anglaise. - -Pour activer le tracing Redux: - -``` -REDUX_TRACE=true yarn start -``` - -### Messages de commit - -A mettre sans retenue dans les messages de commit : - -https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages - -- 🎨 `:art:` when working on the app's visual style -- 🐎 `:racehorse:` when improving performance -- 📝 `:memo:` when writing docs -- 🐛 `:bug:` when fixing a bug -- 🔥 `:fire:` when removing code or files -- 💚 `:green_heart:` when fixing the CI build -- ✅ `:white_check_mark:` when adding tests -- ⬆️ `:arrow_up:` when upgrading dependencies -- :sparkles: `:sparkles:` when formatting, renaming, reorganizing files - -Et ceux spécifiques au projet : - -- :gear: `:gear:` pour une contribution au moteur qui traite les YAML -- :hammer: `:hammer:` pour une contribution à la base de règles -- :calendar: `:calendar:` pour un changement de règle du à une évolution temporelle (en attendant mieux) -- :chart_with_upwards_trend: `:chart_with_upwards_trend:` pour une amélioration du tracking -- :alien: `:alien:` pour ajouter des traductions -- :wheelchair: `:wheelchair:` pour corriger les problèmes liés à l'accessibilité -- :fountain_pen: `:fountain_pen:` pour séparer les commits liés à la modification du contenu -- :mag: `:mag:` pour les modifications liées au référencement naturel - -### Tests - -Pour executer les tests unitaires : - -```sh -$ yarn run test-common -``` - -Pour le snapshot testing : - -```sh -$ yarn run test:regressions -``` - -Si vous souhaitez mettre à jour les snapshots vous pouvez utiliser le paramètre `--updateSnapshot`, son raccourci `-u`, ou encore le [mode interactif](https://jestjs.io/docs/en/snapshot-testing#interactive-snapshot-mode). - -Enfin pour les tests d'intégration : - -```sh -$ yarn run cypress run -``` - -### Traduction 👽 - -Le site est disponible en français, et en anglais sur https://mycompanyinfrance.com - -Les traductions se trouvent dans le répertoire `source/locales`. - -La librairie utilisée pour la traduction de l'UI est -[react-i18next](https://react.i18next.com/). - -Lorsque l'on introduit une nouvelle chaîne de caractère dans l'UI il faut -systématiquement penser à gérer sa traduction, via un composant ``, ou -via la fonction `t` - -Le circle-ci fait une analyse statique du code pour repérer les chaînes non -traduites, dans le moteur et l'UI : - -```sh -$ yarn run i18n:rules:check -$ yarn run i18n:ui:check -``` - -Pour traduire automatiquement les chaînes manquantes via l'api Deepl : - -```sh -$ yarn run i18n:rules:translate -$ yarn run i18n:ui:translate -``` - -N'oubliez pas de vérifier sur le diff que rien n'est choquant. - -### CI/CD - -- Nous utilisons des [Github actions](https://github.com/features/actions) pour faire tourner les builds et - tests. -- [Netlify](https://www.netlify.com/), s'occupe de l’hébergement du site sur Internet avec gestion des DNS. - -### Analyse des bundles - -La commande `yarn run build:analyse-bundle` gènere une visualisation interactive du -contenu packagé, cf. -[webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) - -### Modifier publicodes - -Publicodes dispose désormais de son propre dépôt GitHub https://github.com/betagouv/publicodes - -Néanmoins pour certaines nouvelles fonctionnalités de mon-entreprise nous concervons le besoin de modifier publicodes avec le moins de frictions possible. Pour tester une évolution du moteur il serait en effet trop lourd d'avoir à ouvrir d'abord une PR côté publicodes, la merger, publier une nouvelle version du paquet, puis ré-intégrer cette nouvelle version sur mon-entreprise. - -C'est pourquoi nous intégrons le code source du publicode dans le sous-répertoire `publicodes/`. La commande `git subtree` nous permet de synchroniser les changements effectués dans l'un ou l'autre des dépôts. - -La première chose à faire est d'ajouter une nouvelle `remote` pour `betagouv/publicodes`, ici nous l'appelons simplement `publicodes` : - -```sh -git remote add publicodes git@github.com:betagouv/publicodes.git -``` - -Ensuite il est possible de remonter les changements effectués dans le sous-repertoire `publicodes/` vers la branche master de la remote `publicodes`. - -```sh -$ git subtree push --prefix=publicodes publicodes master -``` - -Dans l'autre sens il est possible de rapatrier les changements avec la commande - -```sh -$ git subtree pull --prefix=publicodes publicodes master --squash -``` - -## Développement de modèles Publicodes - -### Traduction des normes (lois) en règles Publicodes - -Checklist: - -- [ ] Lire les articles de vulgarisation (sur le site de l'URSSAF, des impôts, etc.). -- [ ] Utiliser un moteur de recherche spécialisé, comme [RFPaye](https://rfpaye.grouperf.com/). -- [ ] [Lire les normes][wiki normes] et noter leurs référence dans les règles Publicodes. - -[wiki normes]: https://github.com/betagouv/mon-entreprise/wiki/Comment-lire-les-normes-(la-loi)-efficacement-pour-r%C3%A9diger-des-r%C3%A8gles-Publicodes%3F - -### Tests - -Pour tester les règles, il est recommandé de: - -- faire tourner un simulateur et vérifier à la main l'adéquation des règles avec les normes - traduites ; -- créer des cas de tests de non-régression sous la forme de nouveaux snapshots (cf. - `mon-entreprise/test/regressions`). - -## Documentation - -### Publicodes - -Un tutoriel sur publicodes est disponible sur https://publi.codes. - -Un wiki contenant des informations intéressantes sur publicodes et le -raisonnement ayant abouti à ce langage sont dispos sur le repository -[betagouv/publicodes](https://github.com/betagouv/publicodes/wiki), qui est par -ailleurs inutilisé. - -Pour se familiariser avec les règles, vous pouvez jeter un œil aux fichiers -contenant les règles elles-mêmes (dans le dossier `rules`) mais cela peut -s'avérer assez abrupt. - -Essayez plutôt de jeter un oeil [aux tests](./publicodes/test/mécanismes/expressions.yaml) -dans un premier temps, puis au [mécanismes en -place](./publicodes/source/mecanisms). - -## Publier une nouvelle version des paquets publicodes - - +## Publier une nouvelle version sur NPM Voici la marche à suivre pour publier une nouvelle version : -1. Renseigner les modifications dans publicodes/CHANGELOG.md +1. Renseigner les modifications dans `CHANGELOG.md` 2. Remplacer les références à la précédente version par la nouvelle version dans les packages.json -3. Ajouter tous les changement dans un commit avec le message suivant : +3. Ajouter tous les changements dans un commit avec le message suivant : ``` 📦 Publicodes v1.0.0-beta. ``` diff --git a/README.md b/README.md index 76b4ea0de..22e8da984 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,41 @@ -Ce dépôt contient : +> 🇬🇧 Most of the documentation (including issues and commit messages) is written in French, please raise an [issue](https://github.com/betagouv/publicodes/issues/new) if you are interested and do not speak French. We intend to translate the language and the documentation in the coming weeks. -- Le code source du site [mon-entreprise.fr](https://mon-entreprise.fr) -- Les [règles publicodes](https://github.com/betagouv/mon-entreprise/tree/master/modele-social) pour le calcul des cotisations sociales, des impôts et des droits sociaux. +## Publicodes -## mon-entreprise.fr +[![Npm version](https://img.shields.io/npm/v/publicodes)](https://www.npmjs.com/package/publicodes) +[![Gitter chat](https://badges.gitter.im/publicodes/publicodes.png)](https://gitter.im/publicodes/community) -[![Statut déploiement](https://github.com/betagouv/mon-entreprise/actions/workflows/deploy.yaml/badge.svg?branch=master)](https://github.com/betagouv/mon-entreprise/actions/workflows/deploy.yaml?query=branch%3Amaster++)  -[![Statut test](https://github.com/betagouv/mon-entreprise/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/betagouv/mon-entreprise/actions/workflows/test.yaml?query=branch%3Amaster++) +Publicodes est un langage déclaratif pour encoder les algorithmes d'intérêt +public. Il permet de réaliser des calculs généraux tout en fournissant une +explication permettant de comprendre et de documenter ces calculs. -Site développé en partenariat avec l'Urssaf, qui a pour mission d'accompagner des créateurs d’entreprise dans le développement de leur activité. +Publicodes est adapté pour modéliser des domaines métiers complexes pouvant être +décomposés en règles élémentaires simples (comme la [législation socio-fiscale](https://github.com/betagouv/mon-entreprise/tree/master/publicodes), +[un bilan carbone](https://github.com/laem/futureco-data/blob/master/co2.yaml), +un estimateur de rendement locatif, etc.). -Il propose notamment des simulateurs de cotisations sociales très complets, basés sur le language déclaratif [publicodes](https://publi.codes). On peut ainsi calculer le coût d'une embauche, un salaire net après impôt, ses revenus d'auto-entrepreneur ou encore ceux d'un dirigeant de SASU ou d'indépendant +Il permet de générer facilement des simulateurs web interactifs où l'on peut affiner +progressivement le résultat affiché, et d'exposer une documentation du calcul explorable. -> 🧮 [Voir la liste des simulateurs](https://mon-entreprise.fr/simulateurs) +## Installation -Les développeurs ont la possibilité d'intégrer ces simulateurs sur d'autres sites, ou de réutiliser les règles pour effectuer leur propre calculs. +``` +npm install publicodes +``` -> 🧰 [Voir les outils à disposition des développeurs](https://mon-entreprise.fr/int%C3%A9gration) +## Documentation -Tous les outils proposés sur mon-entreprise.fr sont propulsés par [publicodes](https://publi.codes), un nouveau langage pour les algorithmes d'intérêt public. +- [Se lancer](https://publi.codes/langage/se-lancer) +- [Principes de base](https://publi.codes/langage/principes-de-base) +- [Bac à sable](https://publi.codes/studio) -## Contribuer +## Projets phares -Si vous souhaitez contribuer à l'un des deux projets, rendez-vous sur [CONTRIBUTING.md](./CONTRIBUTING.md). - -## 🇬🇧 English users - -This repository powers [mycompanyinfrance.fr](https://mycompanyinfrance.fr) and [mon-entreprise.fr](https://mon-entreprise.fr) - -Most of the documentation (including issues and commit message) is written in french, please raise an [issue](https://github.com/betagouv/mon-entreprise/issues/new) if you are interested and do not speak French. - -## 🗜️ Compatibility - -The website will run well on modern browsers. Internet Explorer is not supported anymore (it should work but with visual glitches and performance issues). - -This compatibility is tested thanks to [BrowserStack](http://browserstack.com/)'s free open source program. - -![Logo de Browserstack, notre solution de tests manuels](https://i.imgur.com/dQwLjXA.png) +- **[mon-entreprise.fr](https://mon-entreprise.fr/simulateurs)** utilise publicodes + pour spécifier l'ensemble des calculs relatifs à la législation socio-fiscale + en France. Le site permet entre autre de simuler une fiche de paie complète, + de calculer les cotisations sociales pour un indépendant ou encore connaître + le montant du chômage partiel. +- **[futur.eco](https://futur.eco/)** utilise publicodes pour calculer les bilans + carbone d'un grand nombre d'activités, plats, transports ou biens. +- **[Nos Gestes Climat](https://ecolab.ademe.fr/apps/climat)** utilise publicodes pour proposer un calculateur d'empreinte climat personnel de référence complètement ouvert diff --git a/publicodes/core/.gitignore b/core/.gitignore similarity index 100% rename from publicodes/core/.gitignore rename to core/.gitignore diff --git a/babel.config.json b/core/babel.config.json similarity index 78% rename from babel.config.json rename to core/babel.config.json index 130fee74c..4f9bcc1da 100644 --- a/babel.config.json +++ b/core/babel.config.json @@ -8,16 +8,9 @@ } } ], - [ - "@babel/preset-react", - { - "runtime": "automatic" - } - ], "@babel/preset-typescript" ], "plugins": [ - "babel-plugin-styled-components", "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator", diff --git a/publicodes/core/esm/index.js b/core/esm/index.js similarity index 100% rename from publicodes/core/esm/index.js rename to core/esm/index.js diff --git a/publicodes/core/esm/index.min.js b/core/esm/index.min.js similarity index 100% rename from publicodes/core/esm/index.min.js rename to core/esm/index.min.js diff --git a/publicodes/core/esm/package.json b/core/esm/package.json similarity index 100% rename from publicodes/core/esm/package.json rename to core/esm/package.json diff --git a/publicodes/core/package.json b/core/package.json similarity index 70% rename from publicodes/core/package.json rename to core/package.json index 646ca39b9..6ff92f1be 100644 --- a/publicodes/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "publicodes", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "description": "A declarative language for encoding public algorithm", "main": "dist/index.js", "types": "dist/types/index.d.ts", @@ -25,14 +25,27 @@ ], "private": false, "devDependencies": { + "@babel/preset-typescript": "^7.14.5", + "babel-loader": "^8.2.2", "chai": "^4.2.0", + "dedent-js": "1.0.1", "intl": "^1.2.5", - "typescript": "^4.2.4", - "dedent-js": "1.0.1" + "json-loader": "^0.5.7", + "mocha": "^9.0.1", + "mochapack": "^2.1.2", + "nearley-loader": "^2.0.0", + "rimraf": "^3.0.2", + "typescript": "^4.3.2", + "webpack-cli": "^4.7.2", + "yaml-loader": "^0.6.0" }, "dependencies": { + "@babel/core": "^7.14.6", + "@babel/preset-env": "^7.14.5", + "@types/webpack-env": "^1.16.0", "moo": "^0.5.1", "nearley": "^2.19.2", + "webpack": "^5.39.1", "yaml": "^1.9.2" }, "scripts": { @@ -41,7 +54,7 @@ "prepare": "yarn run rimraf dist && yarn run build", "build": "yarn run webpack --config webpack.config.js && yarn run tsc", "build:watch": "concurrently \"yarn run webpack --watch --config webpack.config.js\" \"yarn run tsc -w\"", - "test:file": "yarn mocha-webpack --include test/setupIntl.js --webpack-config ./webpack.test.js ", + "test:file": "yarn mochapack --include test/setupIntl.js --webpack-config ./webpack.test.js ", "test": "yarn test:file \"./{,!(node_modules)/**/}!(webpack).test.js\"" }, "engines": { diff --git a/publicodes/core/source/AST/findCycles.ts b/core/source/AST/findCycles.ts similarity index 100% rename from publicodes/core/source/AST/findCycles.ts rename to core/source/AST/findCycles.ts diff --git a/publicodes/core/source/AST/graph.ts b/core/source/AST/graph.ts similarity index 100% rename from publicodes/core/source/AST/graph.ts rename to core/source/AST/graph.ts diff --git a/publicodes/core/source/AST/index.ts b/core/source/AST/index.ts similarity index 94% rename from publicodes/core/source/AST/index.ts rename to core/source/AST/index.ts index bbbdcf15f..4a1977cc9 100644 --- a/publicodes/core/source/AST/index.ts +++ b/core/source/AST/index.ts @@ -174,8 +174,6 @@ export const traverseASTNode: TraverseFunction = (fn, node) => { return traverseUnitéNode(fn, node) case 'variations': return traverseVariationNode(fn, node) - case 'variable temporelle': - return traverseVariableTemporelle(fn, node) case 'replacementRule': return traverseReplacementNode(fn, node) default: @@ -297,16 +295,14 @@ const traversePlancherNode: TraverseFunction<'plancher'> = (fn, node) => ({ }, }) -const traverseRésoudreRéférenceCirculaireNode: TraverseFunction<'résoudre référence circulaire'> = ( - fn, - node -) => ({ - ...node, - explanation: { - ...node.explanation, - valeur: fn(node.explanation.valeur), - }, -}) +const traverseRésoudreRéférenceCirculaireNode: TraverseFunction<'résoudre référence circulaire'> = + (fn, node) => ({ + ...node, + explanation: { + ...node.explanation, + valeur: fn(node.explanation.valeur), + }, + }) const traversePlafondNode: TraverseFunction<'plafond'> = (fn, node) => ({ ...node, @@ -382,17 +378,3 @@ const traverseVariationNode: TraverseFunction<'variations'> = (fn, node) => ({ consequence: fn(consequence), })), }) - -const traverseVariableTemporelle: TraverseFunction<'variable temporelle'> = ( - fn, - node -) => ({ - ...node, - explanation: { - period: { - end: node.explanation.period.end && fn(node.explanation.period.end), - start: node.explanation.period.start && fn(node.explanation.period.start), - }, - value: fn(node.explanation.value), - }, -}) diff --git a/publicodes/core/source/AST/types.ts b/core/source/AST/types.ts similarity index 89% rename from publicodes/core/source/AST/types.ts rename to core/source/AST/types.ts index 66886e52a..18437a557 100644 --- a/publicodes/core/source/AST/types.ts +++ b/core/source/AST/types.ts @@ -23,12 +23,10 @@ import { SommeNode } from '../mecanisms/sum' import { SynchronisationNode } from '../mecanisms/synchronisation' import { TauxProgressifNode } from '../mecanisms/tauxProgressif' import { UnitéNode } from '../mecanisms/unité' -import { VariableTemporelleNode } from '../mecanisms/variableTemporelle' import { VariationNode } from '../mecanisms/variations' import { ReferenceNode } from '../reference' import { ReplacementRule } from '../replacement' import { RuleNode } from '../rule' -import { Temporal } from '../temporal' export type ConstantNode = { type: 'boolean' | 'objet' | 'number' | 'string' @@ -64,7 +62,6 @@ export type ASTNode = ( | SynchronisationNode | TauxProgressifNode | UnitéNode - | VariableTemporelleNode | VariationNode | ConstantNode | ReplacementRule @@ -109,14 +106,24 @@ export type Unit = { } // Idée : une évaluation est un n-uple : (value, unit, missingVariable, isApplicable) -// Une temporalEvaluation est une liste d'evaluation sur chaque période. : [(Evaluation, Period)] type EvaluationDecoration = { nodeValue: Evaluation missingVariables: Record unit?: Unit - temporalValue?: Temporal } export type Types = number | boolean | string | Record -export type Evaluation = T | false | null +// TODO: type NotYetDefined & NotApplicable properly (see #14) then refactor any code depending on these: +export type NotYetDefined = null +export function isNotYetDefined(value): value is NotYetDefined { + return value === null +} +export type NotApplicable = false +export function isNotApplicable(value): value is NotApplicable { + return typeof value === 'boolean' && value === false +} +export type Evaluation = + | T + | NotApplicable + | NotYetDefined export type EvaluatedNode = ASTNode & EvaluationDecoration diff --git a/publicodes/core/source/date.ts b/core/source/date.ts similarity index 100% rename from publicodes/core/source/date.ts rename to core/source/date.ts diff --git a/publicodes/core/source/error.ts b/core/source/error.ts similarity index 100% rename from publicodes/core/source/error.ts rename to core/source/error.ts diff --git a/publicodes/core/source/evaluation.ts b/core/source/evaluation.ts similarity index 51% rename from publicodes/core/source/evaluation.ts rename to core/source/evaluation.ts index 447b67c42..ebefa3bd2 100644 --- a/publicodes/core/source/evaluation.ts +++ b/core/source/evaluation.ts @@ -7,17 +7,8 @@ import { NodeKind, } from './AST/types' import { warning } from './error' -import { convertNodeToUnit, simplifyNodeUnit } from './nodeUnits' +import { convertNodeToUnit } from './nodeUnits' import parse from './parse' -import { - concatTemporals, - liftTemporalNode, - mapTemporal, - pureTemporal, - Temporal, - temporalAverage, - zipTemporals, -} from './temporal' export const collectNodeMissing = ( node: EvaluatedNode | ASTNode @@ -75,37 +66,17 @@ export const evaluateArray: ( node.explanation.map(evaluate), node.name ) + const values = evaluatedNodes.map(({ nodeValue }) => nodeValue) + const nodeValue = values.some((value) => value === null) + ? null + : values.reduce(reducer, start) - const temporalValues = concatTemporals( - evaluatedNodes.map( - ({ temporalValue, nodeValue }) => - temporalValue ?? pureTemporal(nodeValue) - ) - ) - const temporalValue = mapTemporal((values) => { - if (values.some((value) => value === null)) { - return null - } - return values.reduce(reducer, start) - }, temporalValues) - - const baseEvaluation = { + return { ...node, missingVariables: mergeAllMissing(evaluatedNodes), explanation: evaluatedNodes, ...(evaluatedNodes[0] && { unit: evaluatedNodes[0].unit }), - } - if (temporalValue.length === 1) { - return { - ...baseEvaluation, - nodeValue: temporalValue[0].value, - } - } - - return { - ...baseEvaluation, - temporalValue, - nodeValue: temporalAverage(temporalValue as any), + nodeValue, } } @@ -132,71 +103,3 @@ export const parseObject = (objectShape, value, context) => { }) ) } - -export function evaluateObject( - effet: (this: Engine, explanations: any) => any -) { - return function (node) { - const evaluations = Object.fromEntries( - Object.entries((node as any).explanation).map(([key, value]) => [ - key, - this.evaluate(value as any), - ]) - ) - const temporalExplanations = mapTemporal( - Object.fromEntries, - concatTemporals( - Object.entries(evaluations).map(([key, node]) => - zipTemporals(pureTemporal(key), liftTemporalNode(node as ASTNode)) - ) - ) - ) - const temporalExplanation = mapTemporal((explanations) => { - const evaluation = effet.call(this, explanations) - return { - ...evaluation, - explanation: { - ...explanations, - ...evaluation.explanation, - }, - } - }, temporalExplanations) - - const sameUnitTemporalExplanation: Temporal< - ASTNode & EvaluatedNode & { nodeValue: number } - > = convertNodesToSameUnit - .call( - this, - temporalExplanation.map((x) => x.value), - node.nodeKind - ) - .map((node, i) => ({ - ...temporalExplanation[i], - value: simplifyNodeUnit(node), - })) - - const temporalValue = mapTemporal( - ({ nodeValue }) => nodeValue, - sameUnitTemporalExplanation - ) - const nodeValue = temporalAverage(temporalValue) - const baseEvaluation = { - ...node, - nodeValue, - unit: sameUnitTemporalExplanation[0].value.unit, - explanation: evaluations, - missingVariables: mergeAllMissing(Object.values(evaluations)), - } - if (sameUnitTemporalExplanation.length === 1) { - return { - ...baseEvaluation, - explanation: (sameUnitTemporalExplanation[0] as any).value.explanation, - } - } - return { - ...baseEvaluation, - temporalValue, - temporalExplanation, - } - } as EvaluationFunction -} diff --git a/publicodes/core/source/evaluationFunctions.ts b/core/source/evaluationFunctions.ts similarity index 100% rename from publicodes/core/source/evaluationFunctions.ts rename to core/source/evaluationFunctions.ts diff --git a/publicodes/core/source/format.ts b/core/source/format.ts similarity index 79% rename from publicodes/core/source/format.ts rename to core/source/format.ts index d54970a27..8016f3c89 100644 --- a/publicodes/core/source/format.ts +++ b/core/source/format.ts @@ -2,33 +2,35 @@ import { Evaluation, Unit } from './AST/types' import { simplifyNodeUnit } from './nodeUnits' import { formatUnit, serializeUnit } from './units' -export const numberFormatter = ({ - style, - maximumFractionDigits = 2, - minimumFractionDigits = 0, - language, -}: { - style?: string - maximumFractionDigits?: number - minimumFractionDigits?: number - language?: string -}) => (value: number) => { - // When we format currency we don't want to display a single decimal digit - // ie 8,1€ but we want to display 8,10€ - const adaptedMinimumFractionDigits = - style === 'currency' && - maximumFractionDigits >= 2 && - minimumFractionDigits === 0 && - !Number.isInteger(value) - ? 2 - : minimumFractionDigits - return Intl.NumberFormat(language, { +export const numberFormatter = + ({ style, - currency: 'EUR', - maximumFractionDigits, - minimumFractionDigits: adaptedMinimumFractionDigits, - }).format(value) -} + maximumFractionDigits = 2, + minimumFractionDigits = 0, + language, + }: { + style?: string + maximumFractionDigits?: number + minimumFractionDigits?: number + language?: string + }) => + (value: number) => { + // When we format currency we don't want to display a single decimal digit + // ie 8,1€ but we want to display 8,10€ + const adaptedMinimumFractionDigits = + style === 'currency' && + maximumFractionDigits >= 2 && + minimumFractionDigits === 0 && + !Number.isInteger(value) + ? 2 + : minimumFractionDigits + return Intl.NumberFormat(language, { + style, + currency: 'EUR', + maximumFractionDigits, + minimumFractionDigits: adaptedMinimumFractionDigits, + }).format(value) + } export const formatCurrency = ( nodeValue: number | undefined, @@ -168,9 +170,10 @@ export function serializeValue( { nodeValue, unit }: { nodeValue: Evaluation; unit?: Unit }, { format }: { format: formatUnit } ) { - const serializedUnit = (unit && typeof nodeValue === 'number' - ? serializeUnit(unit, nodeValue, format) - : '' + const serializedUnit = ( + unit && typeof nodeValue === 'number' + ? serializeUnit(unit, nodeValue, format) + : '' )?.replace(/\s*\/\s*/g, '/') return `${nodeValue} ${serializedUnit}`.trim() } diff --git a/publicodes/core/source/grammar.ne b/core/source/grammar.ne similarity index 86% rename from publicodes/core/source/grammar.ne rename to core/source/grammar.ne index 93c12ff1f..2a2207d59 100644 --- a/publicodes/core/source/grammar.ne +++ b/core/source/grammar.ne @@ -7,8 +7,7 @@ @{% const { - string, date, variable, temporalNumericValue, binaryOperation, - unaryOperation, boolean, number, numberWithUnit, JSONObject + string, date, variable, binaryOperation, unaryOperation, boolean, number, numberWithUnit, JSONObject } = require('./grammarFunctions') const moo = require("moo"); @@ -61,11 +60,6 @@ main -> NumericValue -> AdditionSubstraction {% id %} | Negation {% id %} - | TemporalNumericValue {% id %} - -TemporalNumericValue -> - NumericValue %space %periodWord %space %date {% ([value,,word,,dateString]) => temporalNumericValue(value, word, date([dateString])) %} - | NumericValue %space %periodWord %colon Date {% ([value,,word,,date]) => temporalNumericValue(value, word, date) %} NumericTerminal -> Variable {% id %} diff --git a/publicodes/core/source/grammarFunctions.js b/core/source/grammarFunctions.js similarity index 70% rename from publicodes/core/source/grammarFunctions.js rename to core/source/grammarFunctions.js index 70d7c8722..0a812f9f6 100644 --- a/publicodes/core/source/grammarFunctions.js +++ b/core/source/grammarFunctions.js @@ -1,28 +1,24 @@ /* Those are postprocessor functions for the Nearley grammar.ne. The advantage of putting them here is to get prettier's JS formatting, since Nealrey doesn't support it https://github.com/kach/nearley/issues/310 */ import { normalizeDateString } from './date' -import { parsePeriod } from './temporal' -export let binaryOperation = (operationType) => ([A, , operator, , B]) => ({ - [operator]: { - operationType, - explanation: [A, B], - }, -}) +export let binaryOperation = + (operationType) => + ([A, , operator, , B]) => ({ + [operator]: { + operationType, + explanation: [A, B], + }, + }) -export let unaryOperation = (operationType) => ([operator, , A]) => ({ - [operator]: { - operationType, - explanation: [number([{ value: '0' }]), A], - }, -}) - -export let temporalNumericValue = (variable, word, date) => ({ - temporalValue: { - explanation: variable, - period: parsePeriod(word.value.slice(2), date), - }, -}) +export let unaryOperation = + (operationType) => + ([operator, , A]) => ({ + [operator]: { + operationType, + explanation: [number([{ value: '0' }]), A], + }, + }) export let variable = ([firstFragment, nextFragment], _, reject) => { const fragments = [firstFragment, ...nextFragment].map(({ value }) => value) diff --git a/publicodes/core/source/index.ts b/core/source/index.ts similarity index 81% rename from publicodes/core/source/index.ts rename to core/source/index.ts index 807f83643..d678a00f7 100644 --- a/publicodes/core/source/index.ts +++ b/core/source/index.ts @@ -16,14 +16,17 @@ const emptyCache = (): Cache => ({ _meta: { parentRuleStack: [], evaluationRuleStack: [], + disableApplicabilityContextCounter: 0, }, nodes: new Map(), + nodesApplicability: new Map(), }) type Cache = { _meta: { parentRuleStack: Array evaluationRuleStack: Array + disableApplicabilityContextCounter: number inversionFail?: | { given: string @@ -34,6 +37,7 @@ type Cache = { filter?: string } nodes: Map + nodesApplicability: Map } export type EvaluationOptions = Partial<{ @@ -41,7 +45,14 @@ export type EvaluationOptions = Partial<{ }> export { reduceAST, makeASTTransformer as transformAST } from './AST/index' -export { Evaluation, Unit } from './AST/types' +export { + Evaluation, + Unit, + NotYetDefined, + isNotYetDefined, + NotApplicable, + isNotApplicable, +} from './AST/types' export { capitalise0, formatValue } from './format' export { simplifyNodeUnit } from './nodeUnits' export { default as serializeEvaluation } from './serializeEvaluation' @@ -49,7 +60,7 @@ export { parseUnit, serializeUnit } from './units' export { parsePublicodes, utils } export { Rule, RuleNode, ASTNode, EvaluatedNode } -type PublicodesExpression = string | Record | number +export type PublicodesExpression = string | Record | number export type Logger = { log(message: string): void @@ -150,8 +161,17 @@ export default class Engine { evaluate(value: PublicodesExpression): EvaluatedNode evaluate(value: PublicodesExpression | ASTNode): EvaluatedNode { const cachedNode = this.cache.nodes.get(value) + // The evaluation of parent applicabilty is slightly different from + // regular rules since we cut some of the paths (sums) for optimization. + // That's why we need to have a separate cache for this evaluation. + if (cachedNode !== undefined) { return cachedNode + } else if (this.inApplicabilityEvaluationContext) { + const cachedNodeApplicability = this.cache.nodesApplicability.get(value) + if (cachedNodeApplicability) { + return cachedNodeApplicability + } } let parsedNode: ASTNode @@ -173,7 +193,16 @@ export default class Engine { this, parsedNode ) - this.cache.nodes.set(value, evaluatedNode) + + // TODO: In most cases the two evaluation provide the same result, this + // could be optimized. The idea would be to use the “nodesApplicability” + // cache iff the rule uses a sum mechanism (ie, some paths are cut from + // the full evaluaiton). + if (!this.inApplicabilityEvaluationContext) { + this.cache.nodes.set(value, evaluatedNode) + } else { + this.cache.nodesApplicability.set(value, evaluatedNode) + } return evaluatedNode } @@ -189,6 +218,13 @@ export default class Engine { newEngine.cache = this.cache return newEngine } + + get inApplicabilityEvaluationContext(): boolean { + return ( + this.cache._meta.parentRuleStack.length > 0 && + this.cache._meta.disableApplicabilityContextCounter === 0 + ) + } } /** diff --git a/publicodes/core/source/mecanisms/abattement.ts b/core/source/mecanisms/abattement.ts similarity index 100% rename from publicodes/core/source/mecanisms/abattement.ts rename to core/source/mecanisms/abattement.ts diff --git a/publicodes/core/source/mecanisms/applicable.ts b/core/source/mecanisms/applicable.ts similarity index 100% rename from publicodes/core/source/mecanisms/applicable.ts rename to core/source/mecanisms/applicable.ts diff --git a/publicodes/core/source/mecanisms/arrondi.ts b/core/source/mecanisms/arrondi.ts similarity index 75% rename from publicodes/core/source/mecanisms/arrondi.ts rename to core/source/mecanisms/arrondi.ts index 58d1cd88a..ed219e20e 100644 --- a/publicodes/core/source/mecanisms/arrondi.ts +++ b/core/source/mecanisms/arrondi.ts @@ -2,7 +2,9 @@ import { EvaluationFunction, simplifyNodeUnit } from '..' import { mergeAllMissing } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import parse from '../parse' -import { ASTNode } from '../AST/types' +import { ASTNode, EvaluatedNode } from '../AST/types' +import { serializeUnit } from '../units' +import { evaluationError } from '../error' export type ArrondiNode = { explanation: { @@ -24,6 +26,19 @@ const evaluate: EvaluationFunction<'arrondi'> = function (node) { let arrondi = node.explanation.arrondi if (nodeValue !== false) { arrondi = this.evaluate(arrondi) + + if ( + typeof (arrondi as EvaluatedNode).nodeValue === 'number' && + !serializeUnit((arrondi as EvaluatedNode).unit)?.match(/décimales?/) + ) { + evaluationError( + this.options.logger, + this.cache._meta.evaluationRuleStack[0], + `L'unité ${serializeUnit( + (arrondi as EvaluatedNode).unit + )} de l'arrondi est inconnu. Vous devez utiliser l'unité “décimales”` + ) + } } return { diff --git a/publicodes/core/source/mecanisms/barème.ts b/core/source/mecanisms/barème.ts similarity index 66% rename from publicodes/core/source/mecanisms/barème.ts rename to core/source/mecanisms/barème.ts index 40ba14ff8..087f92bda 100644 --- a/publicodes/core/source/mecanisms/barème.ts +++ b/core/source/mecanisms/barème.ts @@ -3,12 +3,6 @@ import { ASTNode } from '../AST/types' import { defaultNode, mergeAllMissing } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import parse from '../parse' -import { - liftTemporal2, - liftTemporalNode, - mapTemporal, - temporalAverage, -} from '../temporal' import { convertUnit, parseUnit } from '../units' import { evaluatePlafondUntilActiveTranche, @@ -78,44 +72,28 @@ const evaluate: EvaluationFunction<'barème'> = function (node) { const evaluate = this.evaluate.bind(this) const assiette = this.evaluate(node.explanation.assiette) const multiplicateur = this.evaluate(node.explanation.multiplicateur) - const temporalTranchesPlafond = liftTemporal2( - (assiette, multiplicateur) => - evaluatePlafondUntilActiveTranche.call(this, { - parsedTranches: node.explanation.tranches, - assiette, - multiplicateur, - }), - liftTemporalNode(assiette as any), - liftTemporalNode(multiplicateur as any) + const tranches = evaluateBarème( + evaluatePlafondUntilActiveTranche.call(this, { + parsedTranches: node.explanation.tranches, + assiette, + multiplicateur, + }), + assiette, + evaluate ) - const temporalTranches = liftTemporal2( - (tranches, assiette) => evaluateBarème(tranches, assiette, evaluate), - temporalTranchesPlafond, - liftTemporalNode(assiette as any) - ) - const temporalValue = mapTemporal( - (tranches) => - tranches.reduce( - (value, { nodeValue }) => - nodeValue == null ? null : value + nodeValue, - 0 - ), - temporalTranches + const nodeValue = tranches.reduce( + (value, { nodeValue }) => (nodeValue == null ? null : value + nodeValue), + 0 ) + return { ...node, - nodeValue: temporalAverage(temporalValue), - ...(temporalValue.length > 1 - ? { - temporalValue, - } - : { missingVariables: mergeAllMissing(temporalTranches[0].value) }), + nodeValue, + missingVariables: mergeAllMissing(tranches), explanation: { assiette, multiplicateur, - ...(temporalTranches.length > 1 - ? { temporalTranches } - : { tranches: temporalTranches[0].value }), + tranches, }, unit: assiette.unit, } as any diff --git a/publicodes/core/source/mecanisms/composantes.ts b/core/source/mecanisms/composantes.ts similarity index 100% rename from publicodes/core/source/mecanisms/composantes.ts rename to core/source/mecanisms/composantes.ts diff --git a/publicodes/core/source/mecanisms/condition-allof.ts b/core/source/mecanisms/condition-allof.ts similarity index 100% rename from publicodes/core/source/mecanisms/condition-allof.ts rename to core/source/mecanisms/condition-allof.ts diff --git a/publicodes/core/source/mecanisms/condition-oneof.ts b/core/source/mecanisms/condition-oneof.ts similarity index 100% rename from publicodes/core/source/mecanisms/condition-oneof.ts rename to core/source/mecanisms/condition-oneof.ts diff --git a/publicodes/core/source/mecanisms/durée.ts b/core/source/mecanisms/durée.ts similarity index 100% rename from publicodes/core/source/mecanisms/durée.ts rename to core/source/mecanisms/durée.ts diff --git a/core/source/mecanisms/grille.ts b/core/source/mecanisms/grille.ts new file mode 100644 index 000000000..05763dd95 --- /dev/null +++ b/core/source/mecanisms/grille.ts @@ -0,0 +1,89 @@ +import { EvaluationFunction } from '..' +import { ASTNode } from '../AST/types' +import { defaultNode, mergeAllMissing } from '../evaluation' +import { registerEvaluationFunction } from '../evaluationFunctions' +import parse from '../parse' +import { + evaluatePlafondUntilActiveTranche, + parseTranches, + TrancheNodes, +} from './trancheUtils' + +export type GrilleNode = { + explanation: { + assiette: ASTNode + multiplicateur: ASTNode + tranches: TrancheNodes + } + nodeKind: 'grille' +} + +export default function parseGrille(v, context): GrilleNode { + const explanation = { + assiette: parse(v.assiette, context), + multiplicateur: v.multiplicateur + ? parse(v.multiplicateur, context) + : defaultNode(1), + tranches: parseTranches(v.tranches, context), + } + return { + explanation, + nodeKind: 'grille', + } +} + +const evaluate: EvaluationFunction<'grille'> = function (node) { + const evaluate = this.evaluate.bind(this) + const assiette = this.evaluate(node.explanation.assiette) + const multiplicateur = this.evaluate(node.explanation.multiplicateur) + const tranches = evaluatePlafondUntilActiveTranche + .call(this, { + parsedTranches: node.explanation.tranches, + assiette, + multiplicateur, + }) + .map((tranche) => { + if (tranche.isActive === false) { + return tranche + } + const montant = evaluate(tranche.montant) + return { + ...tranche, + montant, + nodeValue: montant.nodeValue, + unit: montant.unit, + missingVariables: mergeAllMissing([montant, tranche]), + } + }) + + let activeTranches + const activeTranche = tranches.find((tranche) => tranche.isActive) + if (activeTranche) { + activeTranches = [activeTranche] + } else if (tranches[tranches.length - 1].isAfterActive === false) { + activeTranches = [{ nodeValue: false }] + } else { + activeTranches = tranches.filter((tranche) => tranche.isActive === null) + } + + const nodeValue = !activeTranches[0] + ? false + : activeTranches[0].isActive === null + ? null + : activeTranches[0].nodeValue + + return { + ...node, + nodeValue, + missingVariables: mergeAllMissing(activeTranches), + explanation: { + ...node.explanation, + assiette, + multiplicateur, + tranches, + }, + unit: activeTranches[0]?.unit ?? undefined, + } as any +} + +registerEvaluationFunction('grille', evaluate) diff --git a/publicodes/core/source/mecanisms/inversion.ts b/core/source/mecanisms/inversion.ts similarity index 100% rename from publicodes/core/source/mecanisms/inversion.ts rename to core/source/mecanisms/inversion.ts diff --git a/publicodes/core/source/mecanisms/max.ts b/core/source/mecanisms/max.ts similarity index 100% rename from publicodes/core/source/mecanisms/max.ts rename to core/source/mecanisms/max.ts diff --git a/publicodes/core/source/mecanisms/min.ts b/core/source/mecanisms/min.ts similarity index 100% rename from publicodes/core/source/mecanisms/min.ts rename to core/source/mecanisms/min.ts diff --git a/publicodes/core/source/mecanisms/nonApplicable.ts b/core/source/mecanisms/nonApplicable.ts similarity index 100% rename from publicodes/core/source/mecanisms/nonApplicable.ts rename to core/source/mecanisms/nonApplicable.ts diff --git a/publicodes/core/source/mecanisms/one-possibility.ts b/core/source/mecanisms/one-possibility.ts similarity index 100% rename from publicodes/core/source/mecanisms/one-possibility.ts rename to core/source/mecanisms/one-possibility.ts diff --git a/publicodes/core/source/mecanisms/operation.ts b/core/source/mecanisms/operation.ts similarity index 65% rename from publicodes/core/source/mecanisms/operation.ts rename to core/source/mecanisms/operation.ts index f093548b0..897473604 100644 --- a/publicodes/core/source/mecanisms/operation.ts +++ b/core/source/mecanisms/operation.ts @@ -1,13 +1,11 @@ import { EvaluationFunction } from '..' -import { ASTNode } from '../AST/types' +import { ASTNode, EvaluatedNode } from '../AST/types' import { convertToDate } from '../date' import { warning } from '../error' import { mergeAllMissing } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import { convertNodeToUnit } from '../nodeUnits' import parse from '../parse' -import { liftTemporal2, pureTemporal, temporalAverage } from '../temporal' -import { EvaluatedNode } from '../AST/types' import { inferUnit, serializeUnit } from '../units' const knownOperations = { @@ -43,10 +41,24 @@ const parseOperation = (k, symbol) => (v, context) => { } const evaluate: EvaluationFunction<'operation'> = function (node) { + // When we only need to evaluate the applicability of a rule, we don't enter + // inside “sum terms” since we know that the sum will always be applicable. + // However, if somewhere in the evaluation stack we do a comparison, we need + // to disable this optimization since in this case we'll need the exact value + // of sums in the evaluation subtree. + const disableApplicabilityContext = ['≠', '=', '<', '>', '≤', '≥'].includes( + node.operator + ) + if (disableApplicabilityContext && this.inApplicabilityEvaluationContext) { + this.cache._meta.disableApplicabilityContextCounter += 1 + } const explanation = node.explanation.map((node) => this.evaluate(node)) as [ EvaluatedNode, EvaluatedNode ] + if (disableApplicabilityContext && this.inApplicabilityEvaluationContext) { + this.cache._meta.disableApplicabilityContextCounter -= 1 + } let [node1, node2] = explanation const missingVariables = mergeAllMissing([node1, node2]) @@ -74,7 +86,26 @@ const evaluate: EvaluationFunction<'operation'> = function (node) { ) } } - const baseNode = { + + const operatorFunction = knownOperations[node.operationKind][0] + + const a = node1.nodeValue as string | false + const b = node2.nodeValue as string | false + + const nodeValue = + !['≠', '='].includes(node.operator) && a === false && b === false + ? false + : ['<', '>', '≤', '≥', '∕', '×'].includes(node.operator) && + (a === false || b === false) + ? false + : a !== false && + b !== false && + ['≠', '=', '<', '>', '≤', '≥'].includes(node.operator) && + [a, b].every((value) => value.match?.(/[\d]{2}\/[\d]{2}\/[\d]{4}/)) + ? operatorFunction(convertToDate(a), convertToDate(b)) + : operatorFunction(a, b) + + return { ...node, explanation, ...((node.operationKind === '*' || @@ -84,40 +115,7 @@ const evaluate: EvaluationFunction<'operation'> = function (node) { unit: inferUnit(node.operationKind, [node1.unit, node2.unit]), }), missingVariables, - } - - const operatorFunction = knownOperations[node.operationKind][0] - - const temporalValue = liftTemporal2( - (a: string | false, b: string | false) => { - if (!['≠', '='].includes(node.operator) && a === false && b === false) { - return false - } - if ( - ['<', '>', '≤', '≥', '∕', '×'].includes(node.operator) && - (a === false || b === false) - ) { - return false - } - if ( - a !== false && - b !== false && - ['≠', '=', '<', '>', '≤', '≥'].includes(node.operator) && - [a, b].every((value) => value.match?.(/[\d]{2}\/[\d]{2}\/[\d]{4}/)) - ) { - return operatorFunction(convertToDate(a), convertToDate(b)) - } - return operatorFunction(a, b) - }, - node1.temporalValue ?? (pureTemporal(node1.nodeValue) as any), - node2.temporalValue ?? (pureTemporal(node2.nodeValue) as any) - ) - const nodeValue = temporalAverage(temporalValue, baseNode.unit) - - return { - ...baseNode, nodeValue, - ...(temporalValue.length > 1 && { temporalValue }), } } diff --git a/publicodes/core/source/mecanisms/parDéfaut.ts b/core/source/mecanisms/parDéfaut.ts similarity index 100% rename from publicodes/core/source/mecanisms/parDéfaut.ts rename to core/source/mecanisms/parDéfaut.ts diff --git a/publicodes/core/source/mecanisms/plafond.ts b/core/source/mecanisms/plafond.ts similarity index 100% rename from publicodes/core/source/mecanisms/plafond.ts rename to core/source/mecanisms/plafond.ts diff --git a/publicodes/core/source/mecanisms/plancher.ts b/core/source/mecanisms/plancher.ts similarity index 100% rename from publicodes/core/source/mecanisms/plancher.ts rename to core/source/mecanisms/plancher.ts diff --git a/publicodes/core/source/mecanisms/product.ts b/core/source/mecanisms/product.ts similarity index 74% rename from publicodes/core/source/mecanisms/product.ts rename to core/source/mecanisms/product.ts index 612df9b95..2d636700e 100644 --- a/publicodes/core/source/mecanisms/product.ts +++ b/core/source/mecanisms/product.ts @@ -1,7 +1,7 @@ import { EvaluationFunction } from '..' import { ASTNode } from '../AST/types' import { warning } from '../error' -import { defaultNode, evaluateObject, parseObject } from '../evaluation' +import { defaultNode, mergeAllMissing, parseObject } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import { convertNodeToUnit, simplifyNodeUnit } from '../nodeUnits' import { areUnitConvertible, convertUnit, inferUnit } from '../units' @@ -32,12 +32,12 @@ export const mecanismProduct = (v, context) => { } as ProductNode } -const productEffect: EvaluationFunction = function ({ - assiette, - taux, - facteur, - plafond, -}: any) { +const evaluateProduit: EvaluationFunction<'produit'> = function (node) { + const assiette = this.evaluate(node.explanation.assiette) + const taux = this.evaluate(node.explanation.taux) + const facteur = this.evaluate(node.explanation.facteur) + let plafond = this.evaluate(node.explanation.plafond) + if (assiette.unit) { try { plafond = convertNodeToUnit(assiette.unit, plafond) @@ -72,16 +72,20 @@ const productEffect: EvaluationFunction = function ({ nodeValue = convertUnit(unit, assiette.unit, nodeValue) unit = assiette.unit } + return simplifyNodeUnit({ + ...node, + missingVariables: mergeAllMissing([assiette, taux, facteur, plafond]), nodeValue, unit, - explanation: { - plafondActif: assiette.nodeValue > plafond.nodeValue, + assiette, + taux, + facteur, + plafond, + plafondActif: (assiette.nodeValue as any) > (plafond as any).nodeValue, }, }) } -const evaluate = evaluateObject<'produit'>(productEffect) - -registerEvaluationFunction('produit', evaluate) +registerEvaluationFunction('produit', evaluateProduit) diff --git a/publicodes/core/source/mecanisms/recalcul.ts b/core/source/mecanisms/recalcul.ts similarity index 93% rename from publicodes/core/source/mecanisms/recalcul.ts rename to core/source/mecanisms/recalcul.ts index f9eb7e1ca..1f7443b9b 100644 --- a/publicodes/core/source/mecanisms/recalcul.ts +++ b/core/source/mecanisms/recalcul.ts @@ -18,7 +18,7 @@ export type RecalculNode = { const evaluateRecalcul: EvaluationFunction<'recalcul'> = function (node) { if (this.cache._meta.inRecalcul) { - return (defaultNode(false) as any) as RecalculNode & EvaluatedNode + return defaultNode(null) as any as RecalculNode & EvaluatedNode } const amendedSituation = node.explanation.amendedSituation @@ -62,9 +62,6 @@ const evaluateRecalcul: EvaluationFunction<'recalcul'> = function (node) { }, missingVariables: evaluatedNode.missingVariables, ...('unit' in evaluatedNode && { unit: evaluatedNode.unit }), - ...(evaluatedNode.temporalValue && { - temporalValue: evaluatedNode.temporalValue, - }), } } diff --git a/core/source/mecanisms/résoudre-référence-circulaire.ts b/core/source/mecanisms/résoudre-référence-circulaire.ts new file mode 100644 index 000000000..acfb823cf --- /dev/null +++ b/core/source/mecanisms/résoudre-référence-circulaire.ts @@ -0,0 +1,109 @@ +import { EvaluationFunction } from '..' +import { ASTNode, ConstantNode, Unit } from '../AST/types' +import { registerEvaluationFunction } from '../evaluationFunctions' +import parse from '../parse' +import { Context } from '../parsePublicodes' +import uniroot from '../uniroot' +import { UnitéNode } from './unité' + +export type RésoudreRéférenceCirculaireNode = { + explanation: { + ruleToSolve: string + valeur: ASTNode + } + nodeKind: 'résoudre référence circulaire' +} + +export const evaluateRésoudreRéférenceCirculaire: EvaluationFunction<'résoudre référence circulaire'> = + function (node) { + const originalCache = this.cache + let inversionNumberOfIterations = 0 + + const evaluateWithValue = ( + n: number, + unit: Unit = { numerators: [], denominators: [] } + ) => { + inversionNumberOfIterations++ + this.resetCache() + + this.parsedSituation[node.explanation.ruleToSolve] = { + unit: unit, + nodeKind: 'unité', + explanation: { + nodeKind: 'constant', + nodeValue: n, + type: 'number', + } as ConstantNode, + } as UnitéNode + return this.evaluate(node.explanation.valeur) + } + + let nodeValue: number | null | undefined = null + + const x0 = 0 + let valeur = evaluateWithValue(x0) + + const y0 = valeur.nodeValue as number + const unit = valeur.unit + const missingVariables = valeur.missingVariables + let i = 0 + if (y0 !== null) { + // The `uniroot` function parameter. It will be called with its `min` and + // `max` arguments, so we can use our cached nodes if the function is called + // with the already computed x1 or x2. + const test = (x: number): number => { + if (x === x0) { + return y0 - x0 + } + valeur = evaluateWithValue(x, unit) + const y = valeur.nodeValue + i++ + return (y as number) - x + } + + const defaultMin = -1_000_000 + const defaultMax = 100_000_000 + + nodeValue = uniroot(test, defaultMin, defaultMax, 0.5, 30, 2) + } + + this.cache = originalCache + + if (nodeValue === undefined) { + nodeValue = null + this.cache._meta.inversionFail = true + } + if (nodeValue !== null) { + valeur = evaluateWithValue(nodeValue, unit) + } + delete this.parsedSituation[node.explanation.ruleToSolve] + + return { + ...node, + unit, + nodeValue, + explanation: { + ...node.explanation, + valeur, + inversionNumberOfIterations, + }, + missingVariables, + } + } + +export default function parseRésoudreRéférenceCirculaire(v, context: Context) { + return { + explanation: { + ruleToSolve: context.dottedName, + valeur: parse(v.valeur, context), + }, + nodeKind: 'résoudre référence circulaire', + } as RésoudreRéférenceCirculaireNode +} + +parseRésoudreRéférenceCirculaire.nom = 'résoudre la référence circulaire' + +registerEvaluationFunction( + 'résoudre référence circulaire', + evaluateRésoudreRéférenceCirculaire +) diff --git a/publicodes/core/source/mecanisms/situation.ts b/core/source/mecanisms/situation.ts similarity index 100% rename from publicodes/core/source/mecanisms/situation.ts rename to core/source/mecanisms/situation.ts diff --git a/publicodes/core/source/mecanisms/sum.tsx b/core/source/mecanisms/sum.tsx similarity index 53% rename from publicodes/core/source/mecanisms/sum.tsx rename to core/source/mecanisms/sum.tsx index 54eba4734..ce45f3d9c 100644 --- a/publicodes/core/source/mecanisms/sum.tsx +++ b/core/source/mecanisms/sum.tsx @@ -18,4 +18,17 @@ export const mecanismSum = (v, context) => { } as SommeNode } -registerEvaluationFunction('somme', evaluate) +registerEvaluationFunction('somme', function (node) { + if (this.inApplicabilityEvaluationContext) { + return { + // With a clearer distinction between `getApplicability` and + // `getValue` we could avoid faking a `nodeValue: true` and instead + // simply return `isApplicable: true, nodeValue: undefined` + nodeValue: true, + nodeKind: 'somme', + missingVariables: {}, + explanation: [], + } + } + return evaluate.call(this, node) +}) diff --git a/publicodes/core/source/mecanisms/synchronisation.ts b/core/source/mecanisms/synchronisation.ts similarity index 100% rename from publicodes/core/source/mecanisms/synchronisation.ts rename to core/source/mecanisms/synchronisation.ts diff --git a/publicodes/core/source/mecanisms/tauxProgressif.ts b/core/source/mecanisms/tauxProgressif.ts similarity index 100% rename from publicodes/core/source/mecanisms/tauxProgressif.ts rename to core/source/mecanisms/tauxProgressif.ts diff --git a/publicodes/core/source/mecanisms/trancheUtils.ts b/core/source/mecanisms/trancheUtils.ts similarity index 100% rename from publicodes/core/source/mecanisms/trancheUtils.ts rename to core/source/mecanisms/trancheUtils.ts diff --git a/publicodes/core/source/mecanisms/unité.ts b/core/source/mecanisms/unité.ts similarity index 100% rename from publicodes/core/source/mecanisms/unité.ts rename to core/source/mecanisms/unité.ts diff --git a/publicodes/core/source/mecanisms/variations.ts b/core/source/mecanisms/variations.ts similarity index 67% rename from publicodes/core/source/mecanisms/variations.ts rename to core/source/mecanisms/variations.ts index 1c8c3b9c0..3dc760138 100644 --- a/publicodes/core/source/mecanisms/variations.ts +++ b/core/source/mecanisms/variations.ts @@ -1,17 +1,10 @@ import { EvaluationFunction } from '..' -import { ASTNode, Unit } from '../AST/types' +import { ASTNode, EvaluatedNode, Unit } from '../AST/types' import { warning } from '../error' import { bonus, defaultNode, mergeAllMissing } from '../evaluation' import { registerEvaluationFunction } from '../evaluationFunctions' import { convertNodeToUnit } from '../nodeUnits' import parse from '../parse' -import { - liftTemporal2, - pureTemporal, - sometime, - Temporal, - temporalAverage, -} from '../temporal' export type VariationNode = { explanation: Array<{ @@ -62,12 +55,12 @@ export default function parseVariations(v, context): VariationNode { } const evaluate: EvaluationFunction<'variations'> = function (node) { - const [temporalValue, explanation, unit] = node.explanation.reduce< + const [nodeValue, explanation, unit] = node.explanation.reduce< [ - Temporal, + EvaluatedNode['nodeValue'], VariationNode['explanation'], Unit | undefined, - Temporal + boolean | null ] >( ( @@ -75,11 +68,7 @@ const evaluate: EvaluationFunction<'variations'> = function (node) { { condition, consequence }, i: number ) => { - const previousConditionsAlwaysTrue = !sometime( - (value) => value !== true, - previousConditions - ) - if (previousConditionsAlwaysTrue) { + if (previousConditions === true) { return [ evaluation, [...explanations, { condition, consequence }], @@ -88,24 +77,19 @@ const evaluate: EvaluationFunction<'variations'> = function (node) { ] } const evaluatedCondition = this.evaluate(condition) - const currentCondition = liftTemporal2( - (previousCond, currentCond) => - previousCond === null - ? previousCond - : !previousCond && - (currentCond === null ? null : currentCond !== false), - previousConditions, - evaluatedCondition.temporalValue ?? - pureTemporal(evaluatedCondition.nodeValue) - ) + const currentCondition = + previousConditions === null + ? previousConditions + : !previousConditions && + (evaluatedCondition.nodeValue === null + ? null + : evaluatedCondition.nodeValue !== false) + evaluatedCondition.missingVariables = bonus( evaluatedCondition.missingVariables ) - const currentConditionAlwaysFalse = !sometime( - (x) => x !== false, - currentCondition - ) - if (currentConditionAlwaysFalse) { + + if (currentCondition === false) { return [ evaluation, [...explanations, { condition: evaluatedCondition, consequence }], @@ -128,15 +112,8 @@ const evaluate: EvaluationFunction<'variations'> = function (node) { ) } } - const currentValue = liftTemporal2( - (cond, value) => cond && value, - currentCondition, - evaluatedConsequence.temporalValue ?? - pureTemporal(evaluatedConsequence.nodeValue) - ) - const or = (a, b) => a || b return [ - liftTemporal2(or, evaluation, currentValue), + currentCondition && evaluatedConsequence.nodeValue, [ ...explanations, { @@ -146,19 +123,18 @@ const evaluate: EvaluationFunction<'variations'> = function (node) { }, ], unit || evaluatedConsequence.unit, - liftTemporal2(or, previousConditions, currentCondition), + previousConditions || currentCondition, ] }, - [pureTemporal(false), [], undefined, pureTemporal(false)] + [false, [], undefined, false] ) - const nodeValue = temporalAverage(temporalValue, unit) const missingVariables = mergeAllMissing( explanation.reduce( - (values, { condition, consequence }) => [ + (values, { condition, satisfied, consequence }) => [ ...values, condition, - consequence, + ...(satisfied ? [consequence] : []), ], [] ) @@ -170,7 +146,6 @@ const evaluate: EvaluationFunction<'variations'> = function (node) { ...(unit !== undefined && { unit }), explanation, missingVariables, - ...(temporalValue.length > 1 && { temporalValue }), } } diff --git a/publicodes/core/source/nodeUnits.ts b/core/source/nodeUnits.ts similarity index 66% rename from publicodes/core/source/nodeUnits.ts rename to core/source/nodeUnits.ts index 504ae21e3..fc2eeb369 100644 --- a/publicodes/core/source/nodeUnits.ts +++ b/core/source/nodeUnits.ts @@ -1,5 +1,4 @@ import { EvaluatedNode, Unit } from './AST/types' -import { mapTemporal } from './temporal' import { convertUnit, simplifyUnit } from './units' export function simplifyNodeUnit(node) { @@ -15,20 +14,12 @@ export function convertNodeToUnit( to: Unit | undefined, node: Node ): Node { - const temporalValue = - node.temporalValue && node.unit - ? mapTemporal( - (value) => convertUnit(node.unit, to, value as number), - node.temporalValue - ) - : node.temporalValue return { ...node, nodeValue: node.unit && typeof node.nodeValue === 'number' ? convertUnit(node.unit, to, node.nodeValue) : node.nodeValue, - ...(temporalValue && { temporalValue }), unit: to, } } diff --git a/publicodes/core/source/parse.ts b/core/source/parse.ts similarity index 98% rename from publicodes/core/source/parse.ts rename to core/source/parse.ts index 46bbb96ba..68a8c871b 100644 --- a/publicodes/core/source/parse.ts +++ b/core/source/parse.ts @@ -28,7 +28,6 @@ import { mecanismSum } from './mecanisms/sum' import { mecanismSynchronisation } from './mecanisms/synchronisation' import tauxProgressif from './mecanisms/tauxProgressif' import unité from './mecanisms/unité' -import variableTemporelle from './mecanisms/variableTemporelle' import variations, { devariate } from './mecanisms/variations' import { Context } from './parsePublicodes' import parseReference from './reference' @@ -190,7 +189,6 @@ const parseFunctions = { somme: mecanismSum, multiplication: mecanismProduct, produit: mecanismProduct, - temporalValue: variableTemporelle, barème, grille, 'taux progressif': tauxProgressif, diff --git a/publicodes/core/source/parsePublicodes.ts b/core/source/parsePublicodes.ts similarity index 100% rename from publicodes/core/source/parsePublicodes.ts rename to core/source/parsePublicodes.ts diff --git a/publicodes/core/source/reference.ts b/core/source/reference.ts similarity index 100% rename from publicodes/core/source/reference.ts rename to core/source/reference.ts diff --git a/publicodes/core/source/replacement.tsx b/core/source/replacement.tsx similarity index 100% rename from publicodes/core/source/replacement.tsx rename to core/source/replacement.tsx diff --git a/publicodes/core/source/rule.ts b/core/source/rule.ts similarity index 100% rename from publicodes/core/source/rule.ts rename to core/source/rule.ts diff --git a/publicodes/core/source/ruleUtils.ts b/core/source/ruleUtils.ts similarity index 100% rename from publicodes/core/source/ruleUtils.ts rename to core/source/ruleUtils.ts diff --git a/publicodes/core/source/serializeEvaluation.ts b/core/source/serializeEvaluation.ts similarity index 100% rename from publicodes/core/source/serializeEvaluation.ts rename to core/source/serializeEvaluation.ts diff --git a/mon-entreprise/source/types/import-markdown.d.ts b/core/source/types/import-markdown.d.ts similarity index 100% rename from mon-entreprise/source/types/import-markdown.d.ts rename to core/source/types/import-markdown.d.ts diff --git a/mon-entreprise/source/types/import-nearley.d.ts b/core/source/types/import-nearley.d.ts similarity index 100% rename from mon-entreprise/source/types/import-nearley.d.ts rename to core/source/types/import-nearley.d.ts diff --git a/mon-entreprise/source/types/import-yaml.d.ts b/core/source/types/import-yaml.d.ts similarity index 100% rename from mon-entreprise/source/types/import-yaml.d.ts rename to core/source/types/import-yaml.d.ts diff --git a/publicodes/core/source/uniroot.ts b/core/source/uniroot.ts similarity index 100% rename from publicodes/core/source/uniroot.ts rename to core/source/uniroot.ts diff --git a/publicodes/core/source/units.ts b/core/source/units.ts similarity index 97% rename from publicodes/core/source/units.ts rename to core/source/units.ts index 55e602370..1e486a277 100644 --- a/publicodes/core/source/units.ts +++ b/core/source/units.ts @@ -101,13 +101,12 @@ const equals = (a: T, b: T) => { } } -export const removeOnce = ( - element: T, - eqFn: (a: T, b: T) => boolean = equals -) => (list: Array): Array => { - const index = list.findIndex((e) => eqFn(e, element)) - return list.filter((_, i) => i !== index) -} +export const removeOnce = + (element: T, eqFn: (a: T, b: T) => boolean = equals) => + (list: Array): Array => { + const index = list.findIndex((e) => eqFn(e, element)) + return list.filter((_, i) => i !== index) + } const simplify = ( unit: Unit, diff --git a/publicodes/core/test/.eslintrc.yaml b/core/test/.eslintrc.yaml similarity index 100% rename from publicodes/core/test/.eslintrc.yaml rename to core/test/.eslintrc.yaml diff --git a/publicodes/core/test/cycles.test.js b/core/test/cycles.test.js similarity index 100% rename from publicodes/core/test/cycles.test.js rename to core/test/cycles.test.js diff --git a/publicodes/core/test/date.test.js b/core/test/date.test.js similarity index 100% rename from publicodes/core/test/date.test.js rename to core/test/date.test.js diff --git a/publicodes/core/test/format.test.js b/core/test/format.test.js similarity index 93% rename from publicodes/core/test/format.test.js rename to core/test/format.test.js index 2f1cd8b45..2f2a68f13 100644 --- a/publicodes/core/test/format.test.js +++ b/core/test/format.test.js @@ -21,6 +21,12 @@ describe('format engine values', () => { expect(formatValue(10, { displayedUnit: '%' })).to.equal('10 %') expect(formatValue(100, { displayedUnit: '%' })).to.equal('100 %') expect(formatValue(10.2, { displayedUnit: '%' })).to.equal('10,2 %') + expect( + formatValue({ + nodeValue: 441, + unit: parseUnit('%.kgCO2e'), + }) + ).to.equal('4,41 kgCO2e') }) it('format values', () => { diff --git a/publicodes/core/test/inversion.test.js b/core/test/inversion.test.js similarity index 100% rename from publicodes/core/test/inversion.test.js rename to core/test/inversion.test.js diff --git a/publicodes/core/test/library.test.js b/core/test/library.test.js similarity index 100% rename from publicodes/core/test/library.test.js rename to core/test/library.test.js diff --git a/publicodes/core/test/load-mecanism-tests.js b/core/test/load-mecanism-tests.js similarity index 100% rename from publicodes/core/test/load-mecanism-tests.js rename to core/test/load-mecanism-tests.js diff --git a/publicodes/core/test/mecanisms.test.js b/core/test/mecanisms.test.js similarity index 100% rename from publicodes/core/test/mecanisms.test.js rename to core/test/mecanisms.test.js diff --git a/publicodes/core/test/missingVariables.test.js b/core/test/missingVariables.test.js similarity index 54% rename from publicodes/core/test/missingVariables.test.js rename to core/test/missingVariables.test.js index 4320bce4d..58489b6cd 100644 --- a/publicodes/core/test/missingVariables.test.js +++ b/core/test/missingVariables.test.js @@ -1,22 +1,25 @@ import { expect } from 'chai' import Engine from '../source/index' +import { parse } from 'yaml' describe('Missing variables', function () { it('should identify missing variables', function () { - const rawRules = { - ko: 'oui', - sum: 'oui', - 'sum . startHere': { - formule: 2, - 'non applicable si': 'sum . evt . ko', - }, - 'sum . evt': { - formule: { 'une possibilité': ['ko'] }, - titre: 'Truc', - question: '?', - }, - 'sum . evt . ko': {}, - } + // Rules in tests can be expressed in YAML like to for more clarity than JS objects + const rawRules = parse(` +ko: oui +sum: oui +sum . startHere: + formule: 2 + non applicable si: sum . evt . ko +sum . evt: + formule: + une possibilité: + - ko + titre: Truc + question: '?' +sum . evt . ko: +`) + const result = Object.keys( new Engine(rawRules).evaluate('sum . startHere').missingVariables ) @@ -129,60 +132,74 @@ describe('Missing variables', function () { expect(result).to.be.empty }) - // TODO : réparer ce test - it.skip('should report missing variables in variations', function () { - const rawRules = { - top: 'oui', - 'top . startHere': { - formule: { somme: ['variations'] }, - }, - 'top . variations': { - formule: { - variations: [ - { - si: 'dix', - alors: { - barème: { - assiette: 2008, - multiplicateur: 'deux', - tranches: [ - { plafond: 1, taux: 0.1 }, - { plafond: 2, taux: 'trois' }, - { taux: 10 }, - ], - }, - }, - }, - { - si: '3 > 4', - alors: { - barème: { - assiette: 2008, - multiplicateur: 'quatre', - tranches: [ - { plafond: 1, taux: 0.1 }, - { plafond: 2, taux: 1.8 }, - { 'au-dessus de': 2, taux: 10 }, - ], - }, - }, - }, - ], - }, - }, - 'top . dix': {}, - 'top . deux': {}, - 'top . trois': {}, - 'top . quatre': {}, - } + it('should report missing variables in simple variations', function () { + const rawRules = parse(` + +somme: a + b +a: 10 +b: + formule: + variations: + - si: a > 100 + alors: c + - sinon: 0 +c: + question: Alors ?`) const result = Object.keys( - new Engine(rawRules).evaluate('top . startHere').missingVariables + new Engine(rawRules).evaluate('somme').missingVariables ) - expect(result).to.include('top . dix') - expect(result).to.include('top . deux') - expect(result).to.include('top . trois') - expect(result).not.to.include('top . quatre') + expect(result).to.have.lengthOf(0) + }) + + // TODO : réparer ce test + it('should report missing variables in variations', function () { + const rawRules = parse(` +startHere: + formule: + somme: + - variations +variations: + formule: + variations: + - si: dix + alors: + barème: + assiette: 2008 + multiplicateur: deux + tranches: + - plafond: 1 + taux: 0.1 + - plafond: 2 + taux: trois + - taux: 10 + - si: 3 > 4 + alors: + barème: + assiette: 2008 + multiplicateur: quatre + tranches: + - plafond: 1 + taux: 0.1 + - plafond: 2 + taux: 1.8 + - au-dessus de: 2 + taux: 10 + +dix: {} +deux: {} +trois: {} +quatre: {} + + `) + const result = Object.keys( + new Engine(rawRules).evaluate('startHere').missingVariables + ) + + expect(result).to.include('dix') + expect(result).to.include('deux') + expect(result).to.include('trois') + expect(result).not.to.include('quatre') }) }) @@ -249,4 +266,98 @@ describe('nextSteps', function () { expect(result).to.eql(['top . sum . evt']) }) + + it("Parent's other descendands in sums should not be included as missing variables", function () { + // See https://github.com/betagouv/publicodes/issues/33 + const rawRules = parse(` +transport: + somme: + - voiture + - avion + +transport . voiture: + formule: empreinte * km + +transport . voiture . empreinte: 0.12 +transport . voiture . km: + question: COMBIENKM + par défaut: 1000 + +transport . avion: + applicable si: usager + formule: empreinte * km + +transport . avion . km: + question: COMBIENKM + par défaut: 10000 + +transport . avion . empreinte: 0.300 + +transport . avion . usager: + question: Prenez-vous l'avion ? + par défaut: oui +`) + const result = Object.keys( + new Engine(rawRules).evaluate('transport . avion').missingVariables + ) + + expect(result).deep.to.equal([ + 'transport . avion . km', + 'transport . avion . usager', + ]) + expect(result).to.have.lengthOf(2) + }) + it("Parent's other descendands in sums should not be included as missing variables - 2", function () { + // See https://github.com/betagouv/publicodes/issues/33 + const rawRules = parse(` +avion: + question: prenez-vous l'avion ? + par défaut: oui + +avion . impact: + formule: + somme: + - au sol + - en vol + +avion . impact . en vol: + question: Combien de temps passé en vol ? + par défaut: 10 + +avion . impact . au sol: 5 +`) + const result = Object.keys( + new Engine(rawRules).evaluate('avion . impact . au sol').missingVariables + ) + + expect(result).deep.to.equal(['avion']) + expect(result).to.have.lengthOf(1) + }) + + it("Parent's other descendands in sums in applicability should be included as missing variables", function () { + // See https://github.com/betagouv/publicodes/issues/33 + const rawRules = parse(` +a: + applicable si: d > 3 + valeur: oui + +d: + formule: + somme: + - e + - 8 + +e: + question: Vous venez à combien à la soirée ? + par défaut: 3 + +a . b: 20 + 9 +`) + const result = Object.keys( + new Engine(rawRules).evaluate('a . b').missingVariables + ) + + expect(result).deep.to.equal(['e']) + expect(result).to.have.lengthOf(1) + }) }) diff --git a/publicodes/core/test/mécanismes/abattement.yaml b/core/test/mécanismes/abattement.yaml similarity index 100% rename from publicodes/core/test/mécanismes/abattement.yaml rename to core/test/mécanismes/abattement.yaml diff --git a/publicodes/core/test/mécanismes/applicable.yaml b/core/test/mécanismes/applicable.yaml similarity index 99% rename from publicodes/core/test/mécanismes/applicable.yaml rename to core/test/mécanismes/applicable.yaml index 7e63c942c..f0044447a 100644 --- a/publicodes/core/test/mécanismes/applicable.yaml +++ b/core/test/mécanismes/applicable.yaml @@ -16,7 +16,6 @@ prévoyance obligatoire cadre: statut cadre: non valeur attendue: false - variable: par défaut: oui applicable comme mécanisme chainé: diff --git a/publicodes/core/test/mécanismes/arrondi.yaml b/core/test/mécanismes/arrondi.yaml similarity index 97% rename from publicodes/core/test/mécanismes/arrondi.yaml rename to core/test/mécanismes/arrondi.yaml index 00cb0a373..d694a0fa5 100644 --- a/publicodes/core/test/mécanismes/arrondi.yaml +++ b/core/test/mécanismes/arrondi.yaml @@ -1,4 +1,3 @@ - arrondi oui: formule: valeur: 30.4167 jours @@ -24,7 +23,7 @@ arrondi décimales: demie part: formule: valeur: 0.5 * 100.2€ - arrondi: oui + arrondi: oui exemples: - valeur attendue: 50 @@ -48,7 +47,7 @@ cotisation retraite: Arrondi: formule: valeur: cotisation retraite - arrondi: oui + arrondi: oui exemples: - nom: arrondi en dessous diff --git a/publicodes/core/test/mécanismes/barème.yaml b/core/test/mécanismes/barème.yaml similarity index 100% rename from publicodes/core/test/mécanismes/barème.yaml rename to core/test/mécanismes/barème.yaml diff --git a/publicodes/core/test/mécanismes/composantes.yaml b/core/test/mécanismes/composantes.yaml similarity index 100% rename from publicodes/core/test/mécanismes/composantes.yaml rename to core/test/mécanismes/composantes.yaml diff --git a/publicodes/core/test/mécanismes/conversion-unité.yaml b/core/test/mécanismes/conversion-unité.yaml similarity index 99% rename from publicodes/core/test/mécanismes/conversion-unité.yaml rename to core/test/mécanismes/conversion-unité.yaml index 79f7c7068..aaf0b79ca 100644 --- a/publicodes/core/test/mécanismes/conversion-unité.yaml +++ b/core/test/mécanismes/conversion-unité.yaml @@ -27,7 +27,6 @@ Conversion de variable: douches par mois: 30 valeur attendue: 45 unité attendue: kCo2/mois - Conversion de variable et expressions: unité: kCo2/an diff --git a/publicodes/core/test/mécanismes/date.yaml b/core/test/mécanismes/date.yaml similarity index 100% rename from publicodes/core/test/mécanismes/date.yaml rename to core/test/mécanismes/date.yaml diff --git a/publicodes/core/test/mécanismes/durée.yaml b/core/test/mécanismes/durée.yaml similarity index 100% rename from publicodes/core/test/mécanismes/durée.yaml rename to core/test/mécanismes/durée.yaml diff --git a/publicodes/core/test/mécanismes/encadrement.yaml b/core/test/mécanismes/encadrement.yaml similarity index 98% rename from publicodes/core/test/mécanismes/encadrement.yaml rename to core/test/mécanismes/encadrement.yaml index cd60d1382..c6dbb5324 100644 --- a/publicodes/core/test/mécanismes/encadrement.yaml +++ b/core/test/mécanismes/encadrement.yaml @@ -47,10 +47,9 @@ plancher: exemples: - valeur attendue: 2500 - encadrement inférieur et supérieur: formule: - somme: + somme: - 500 - 400 plafond: 800 diff --git a/publicodes/core/test/mécanismes/expressions.yaml b/core/test/mécanismes/expressions.yaml similarity index 99% rename from publicodes/core/test/mécanismes/expressions.yaml rename to core/test/mécanismes/expressions.yaml index 5fa31be95..2c5e00f5b 100644 --- a/publicodes/core/test/mécanismes/expressions.yaml +++ b/core/test/mécanismes/expressions.yaml @@ -306,17 +306,16 @@ chaine de charactère: chaine de charactère: "'je t'y vois'" valeur attendue: je t'y vois - a: oui b: 5 a . b: b + 5 a . c: b + 5 désambiguation du nom de règle 1: formule: a . b - exemples: + exemples: - valeur attendue: 10 désambiguation du nom de règle 2: formule: a . c - exemples: - - valeur attendue: 15 \ No newline at end of file + exemples: + - valeur attendue: 15 diff --git a/publicodes/core/test/mécanismes/grille.yaml b/core/test/mécanismes/grille.yaml similarity index 96% rename from publicodes/core/test/mécanismes/grille.yaml rename to core/test/mécanismes/grille.yaml index 4796dde59..33fac7ea4 100644 --- a/publicodes/core/test/mécanismes/grille.yaml +++ b/core/test/mécanismes/grille.yaml @@ -59,7 +59,7 @@ Grille avec valeur manquante: situation: assiette: 3000 valeur attendue: 300 - - nom: 'assiette au delà du plagond' + - nom: 'assiette au delà du plafond' situation: assiette: 5000 valeur attendue: false diff --git a/publicodes/core/test/mécanismes/le-maximum-de.yaml b/core/test/mécanismes/le-maximum-de.yaml similarity index 100% rename from publicodes/core/test/mécanismes/le-maximum-de.yaml rename to core/test/mécanismes/le-maximum-de.yaml diff --git a/publicodes/core/test/mécanismes/le-minimum-de.yaml b/core/test/mécanismes/le-minimum-de.yaml similarity index 100% rename from publicodes/core/test/mécanismes/le-minimum-de.yaml rename to core/test/mécanismes/le-minimum-de.yaml diff --git a/publicodes/core/test/mécanismes/multiplication.yaml b/core/test/mécanismes/multiplication.yaml similarity index 100% rename from publicodes/core/test/mécanismes/multiplication.yaml rename to core/test/mécanismes/multiplication.yaml diff --git a/publicodes/core/test/mécanismes/paramètres-nommés.yaml b/core/test/mécanismes/paramètres-nommés.yaml similarity index 90% rename from publicodes/core/test/mécanismes/paramètres-nommés.yaml rename to core/test/mécanismes/paramètres-nommés.yaml index 6031479db..ea07a84cc 100644 --- a/publicodes/core/test/mécanismes/paramètres-nommés.yaml +++ b/core/test/mécanismes/paramètres-nommés.yaml @@ -9,13 +9,13 @@ test: paramètre nommés: formule: test exemples: - - situation: + - situation: test: cotisation . assiette valeur attendue: 1000 - situation: test: cotisation . taux employeur valeur attendue: 4 - + cotisation 2: formule: multiplication: @@ -28,12 +28,11 @@ paramètre nommés imbriqués: formule: cotisation 2 . assiette . plafond exemples: - valeur attendue: 100 - paramètre nommé utilisé dans la règle: formule: - valeur [ref assiette] : 500€ - abattement: + valeur [ref assiette]: 500€ + abattement: valeur: assiette * 10% plancher: 100€ exemples: diff --git a/publicodes/core/test/mécanismes/question-conditionelle.yaml b/core/test/mécanismes/question-conditionelle.yaml similarity index 100% rename from publicodes/core/test/mécanismes/question-conditionelle.yaml rename to core/test/mécanismes/question-conditionelle.yaml diff --git a/publicodes/core/test/mécanismes/recalcul.yaml b/core/test/mécanismes/recalcul.yaml similarity index 100% rename from publicodes/core/test/mécanismes/recalcul.yaml rename to core/test/mécanismes/recalcul.yaml diff --git a/publicodes/core/test/mécanismes/remplace.yaml b/core/test/mécanismes/remplace.yaml similarity index 100% rename from publicodes/core/test/mécanismes/remplace.yaml rename to core/test/mécanismes/remplace.yaml diff --git a/publicodes/core/test/mécanismes/rend-non-applicable.yaml b/core/test/mécanismes/rend-non-applicable.yaml similarity index 100% rename from publicodes/core/test/mécanismes/rend-non-applicable.yaml rename to core/test/mécanismes/rend-non-applicable.yaml diff --git a/publicodes/core/test/mécanismes/résoudre-référence-circulaire.yaml b/core/test/mécanismes/résoudre-référence-circulaire.yaml similarity index 88% rename from publicodes/core/test/mécanismes/résoudre-référence-circulaire.yaml rename to core/test/mécanismes/résoudre-référence-circulaire.yaml index 667b637e7..ffaab1228 100644 --- a/publicodes/core/test/mécanismes/résoudre-référence-circulaire.yaml +++ b/core/test/mécanismes/résoudre-référence-circulaire.yaml @@ -1,16 +1,16 @@ -fx: +fx: x: résoudre la référence circulaire: oui valeur: fx exemples: - nom: affine - situation: - fx: 200 - x + situation: + fx: 200 - x valeur attendue: 100 - nom: quadratique - situation: + situation: fx: 0.2 * x * x - 400 * x + 500 - valeur attendue: 2003.743 + valeur attendue: 2003.743 # CF https://www.wolframalpha.com/input/?i=x%3D0.2x%C2%B2-400x%2B500 CA: @@ -18,17 +18,16 @@ CA: plancher: 0€ formule: inversion numérique: - avec: + avec: - net net: résoudre la référence circulaire: oui unité: € - formule: CA - 50% * net + formule: CA - 50% * net - -net après impôt: - formule: 80% * net +net après impôt: + formule: 80% * net unité: € cycle avec inversion et situation vide: @@ -59,7 +58,6 @@ cycle avec la règle du cycle fixée dans la situation: - situation: net: 1000 valeur attendue: 1500 - # TODO : à corriger # cycle avec une règle reliée fixée dans la situation: # valeur: net diff --git a/publicodes/core/test/mécanismes/somme.yaml b/core/test/mécanismes/somme.yaml similarity index 100% rename from publicodes/core/test/mécanismes/somme.yaml rename to core/test/mécanismes/somme.yaml diff --git a/publicodes/core/test/mécanismes/synchronisation.yaml b/core/test/mécanismes/synchronisation.yaml similarity index 100% rename from publicodes/core/test/mécanismes/synchronisation.yaml rename to core/test/mécanismes/synchronisation.yaml diff --git a/publicodes/core/test/mécanismes/taux-progressif.yaml b/core/test/mécanismes/taux-progressif.yaml similarity index 100% rename from publicodes/core/test/mécanismes/taux-progressif.yaml rename to core/test/mécanismes/taux-progressif.yaml diff --git a/publicodes/core/test/mécanismes/toutes-ces-conditions.yaml b/core/test/mécanismes/toutes-ces-conditions.yaml similarity index 100% rename from publicodes/core/test/mécanismes/toutes-ces-conditions.yaml rename to core/test/mécanismes/toutes-ces-conditions.yaml diff --git a/publicodes/core/test/mécanismes/une-de-ces-conditions.yaml b/core/test/mécanismes/une-de-ces-conditions.yaml similarity index 100% rename from publicodes/core/test/mécanismes/une-de-ces-conditions.yaml rename to core/test/mécanismes/une-de-ces-conditions.yaml diff --git a/publicodes/core/test/mécanismes/unité.yaml b/core/test/mécanismes/unité.yaml similarity index 92% rename from publicodes/core/test/mécanismes/unité.yaml rename to core/test/mécanismes/unité.yaml index 6eb67bbaa..ea289f8ef 100644 --- a/publicodes/core/test/mécanismes/unité.yaml +++ b/core/test/mécanismes/unité.yaml @@ -3,11 +3,10 @@ cotisation retraite: valeur sans unité: formule: valeur: 100 - unité: € + unité: € exemples: - unité attendue: € - conversion d'unité: formule: valeur: 12 mois @@ -18,7 +17,7 @@ conversion d'unité: unité chainée: formule: - produit: + produit: assiette: 10 €/mois taux: 50% unité: €/an diff --git a/publicodes/core/test/mécanismes/variations.yaml b/core/test/mécanismes/variations.yaml similarity index 99% rename from publicodes/core/test/mécanismes/variations.yaml rename to core/test/mécanismes/variations.yaml index f28b5f89f..fc984285a 100644 --- a/publicodes/core/test/mécanismes/variations.yaml +++ b/core/test/mécanismes/variations.yaml @@ -166,7 +166,7 @@ variations sans unité: exemples: - valeur attendue: 7 unité attendue: '%' - + taux réduit: variations dans un produit: formule: @@ -184,4 +184,4 @@ variations dans un produit: valeur attendue: 79.35 - situation: taux réduit: non - valeur attendue: 120.75 \ No newline at end of file + valeur attendue: 120.75 diff --git a/publicodes/core/test/ruleUtils.test.js b/core/test/ruleUtils.test.js similarity index 100% rename from publicodes/core/test/ruleUtils.test.js rename to core/test/ruleUtils.test.js diff --git a/publicodes/core/test/rules/co2.yaml b/core/test/rules/co2.yaml similarity index 100% rename from publicodes/core/test/rules/co2.yaml rename to core/test/rules/co2.yaml diff --git a/publicodes/core/test/rules/sasu.yaml b/core/test/rules/sasu.yaml similarity index 100% rename from publicodes/core/test/rules/sasu.yaml rename to core/test/rules/sasu.yaml diff --git a/publicodes/core/test/serializeEvaluation.test.js b/core/test/serializeEvaluation.test.js similarity index 100% rename from publicodes/core/test/serializeEvaluation.test.js rename to core/test/serializeEvaluation.test.js diff --git a/publicodes/core/test/setupIntl.js b/core/test/setupIntl.js similarity index 100% rename from publicodes/core/test/setupIntl.js rename to core/test/setupIntl.js diff --git a/publicodes/core/test/units.test.js b/core/test/units.test.js similarity index 100% rename from publicodes/core/test/units.test.js rename to core/test/units.test.js diff --git a/publicodes/core/tsconfig.json b/core/tsconfig.json similarity index 100% rename from publicodes/core/tsconfig.json rename to core/tsconfig.json diff --git a/publicodes/core/webpack.config.js b/core/webpack.config.js similarity index 100% rename from publicodes/core/webpack.config.js rename to core/webpack.config.js diff --git a/publicodes/core/webpack.test.js b/core/webpack.test.js similarity index 100% rename from publicodes/core/webpack.test.js rename to core/webpack.test.js diff --git a/publicodes/docs/api.md b/docs/api.md similarity index 100% rename from publicodes/docs/api.md rename to docs/api.md diff --git a/publicodes/docs/communauté.md b/docs/communauté.md similarity index 100% rename from publicodes/docs/communauté.md rename to docs/communauté.md diff --git a/publicodes/docs/introduction.md b/docs/introduction.md similarity index 96% rename from publicodes/docs/introduction.md rename to docs/introduction.md index bfac4eac5..4545c4aab 100644 --- a/publicodes/docs/introduction.md +++ b/docs/introduction.md @@ -13,7 +13,7 @@ nombre de repas: 5 repas prix: nombre de repas * prix d'un repas -prix . HT: prix * (1 - TVA) +prix . HT: prix / (1 + TVA) prix . TVA: 20% ``` diff --git a/publicodes/docs/mecanisms.yaml b/docs/mecanisms.yaml similarity index 93% rename from publicodes/docs/mecanisms.yaml rename to docs/mecanisms.yaml index 4bf0ccc36..03d1c19ec 100644 --- a/publicodes/docs/mecanisms.yaml +++ b/docs/mecanisms.yaml @@ -3,8 +3,8 @@ applicable si: description: >- Renvoie `non` si la condition est égale à `non`. Renvoie la valeur sinon. - Permet de désactiver une règle ou une valeur. - + Permet de désactiver une règle ou une valeur. + retourne: Valeur | non exemples: base: >- @@ -18,8 +18,8 @@ non applicable si: description: >- Renvoit `non` si la condition n'est pas égale à `non` - Permet de désactiver une règle ou une valeur. - + Permet de désactiver une règle ou une valeur. + retourne: Valeur | non exemples: base: >- @@ -65,7 +65,7 @@ toutes ces conditions: produit: description: >- C'est une multiplication adaptée pour exprimer au mieux les cotisations. - + Sa propriété `assiette` est multipliée par un pourcentage `taux`, ou par un `facteur` quand ce nom est plus approprié. @@ -111,7 +111,7 @@ variations: `non`. - Ce mécanisme peut aussi être utilisé au sein d'un autre mécanisme avec des attributs, + Ce mécanisme peut aussi être utilisé au sein d'un autre mécanisme avec des attributs, tel que `produit` ou `barème`. arguments: - si: condition à vérifier @@ -185,7 +185,7 @@ le maximum de: mécanisme `encadrement`. exemples: base: >- - max: + max: le maximum de: - 50 - 100 @@ -199,7 +199,7 @@ le minimum de: Pour plafonner une valeur, préférer l'utilisation du mécanisme `encadrement`. exemples: base: >- - min: + min: le minimum de: - 50 - 100 @@ -277,7 +277,6 @@ barème: - taux: 0.6% arrondi: oui - grille: description: >- C'est un barème sous la forme d'une grille de correspondance simple. C'est @@ -345,7 +344,7 @@ composantes: méthode de calcul mais diffèrent selons certains paramètres. Pour ne pas définir deux variables quasi-redondantes, on utilise ce mécanisme. - Cela permet d'avoir une écriture factorisée, plus facile à lire. + Cela permet d'avoir une écriture factorisée, plus facile à lire. Dans les calculs, `composantes` se comporte **exactement comme une somme**. La documentation, elle, sera adaptée pour montrer chaque composante. @@ -356,9 +355,9 @@ composantes: Chaque composante peut également contenir un champs `attributs` de type objet contenant les mécanismes chainés à appliquer à la composante en question. - Lorsque l'on utilise l'attribut `nom`, cela aboutit à la définition de règles + Lorsque l'on utilise l'attribut `nom`, cela aboutit à la définition de règles imbriquées pour chacun des termes de la somme. - + exemples: base: >- composante: @@ -368,36 +367,36 @@ composantes: - taux: 2% - taux: 4% plafond: plafond sécurité sociale - + Cotisations: >- - cotisation 1: - produit: - assiette: assiette de base - composantes: - - attributs: - nom: employeur - taux: 5% - - attributs: - nom: salarié - taux: 1% + cotisation 1: + produit: + assiette: assiette de base + composantes: + - attributs: + nom: employeur + taux: 5% + - attributs: + nom: salarié + taux: 1% - cotisations salariales : - somme: - - cotisation 1 . salarié - # ... + cotisations salariales : + somme: + - cotisation 1 . salarié + # ... TVA: >- - prix: - produit: - assiette: 50€ - composantes: - - attributs: - nom: HT - - attributs: - nom: TVA - taux: 20% + prix: + produit: + assiette: 50€ + composantes: + - attributs: + nom: HT + - attributs: + nom: TVA + taux: 20% - vérification: - prix = prix . HT + prix . TVA + vérification: + prix = prix . HT + prix . TVA abattement: chainable: oui @@ -458,13 +457,12 @@ unité: Permet de convertir explicitement une unité. Affiche un avertissement si la conversion n'est pas possible à cause d'unités incompatibles. - + exemples: base: >- - salaire: + salaire: valeur: 35 k€/an unité: €/mois - par défaut: chainable: oui @@ -472,19 +470,16 @@ par défaut: Permet de donner une valeur par défaut pour le calcul, sans influer sur les variables manquantes retournées. Utile dans le cas d'une situation incomplète où l'on veut quand même retourner une première estimation. - + exemples: base: >- - prix TTC: + prix TTC: assiette: prix HT * (100% + TVA) - TVA: + TVA: par défaut: 20% - - - synchronisation: - description: >- + description: >- Permet de récupérer certaines informations, telles que les codes postaux des villes, à partir d'APIs externes, telles que l'[API des communes de France](https://geo.api.gouv.fr/decoupage-administratif/communes). @@ -527,34 +522,33 @@ inversion numérique: résoudre la référence circulaire: description: | - Active le calcul itératif pour trouver la valeur de la règle qui résout + Active le calcul itératif pour trouver la valeur de la règle qui résout la référence circulaire. - - Il est possible pour une règle de se référencer elle-même. Par défaut, le + + Il est possible pour une règle de se référencer elle-même. Par défaut, le moteur considère qu'il s'agit d'un cycle non voulu, et renvoie 'null' comme valeur pour la règle en question, en affichant un avertissement. - Mais dans certains cas, la formule est bonne et le cycle est voulu. La valeur de la + Mais dans certains cas, la formule est bonne et le cycle est voulu. La valeur de la règle attendue est donc celle qui résout l'équation obtenue via la référence cyclique. - Lorsque l'on active cette fonctionnalité, le moteur va procéder par essai-erreur jusqu'à + Lorsque l'on active cette fonctionnalité, le moteur va procéder par essai-erreur jusqu'à trouver cette valeur. Note : la résolution de cycle est coûteuse en temps de calcul. Il faut donc veiller à ne pas la cumuler avec l'évaluation d'un autre mécanisme coûteux comme l'inversion numérique par exemple. - exemples: base: >- - x: + x: valeur: 4 * x - 5 résoudre la référence circulaire: oui calcul du revenu professionnel: >- chiffre d'affaires: 10000 €/an - + cotisations: 25% * revenu professionnel - - revenu professionnel: + + revenu professionnel: valeur: chiffre d'affaires - cotisations résoudre la référence circulaire: oui diff --git a/publicodes/docs/principes-de-base.md b/docs/principes-de-base.md similarity index 100% rename from publicodes/docs/principes-de-base.md rename to docs/principes-de-base.md diff --git a/publicodes/docs/se-lancer.md b/docs/se-lancer.md similarity index 100% rename from publicodes/docs/se-lancer.md rename to docs/se-lancer.md diff --git a/publicodes/example/publicodes-react/.env b/example/publicodes-react/.env similarity index 100% rename from publicodes/example/publicodes-react/.env rename to example/publicodes-react/.env diff --git a/publicodes/example/publicodes-react/.gitignore b/example/publicodes-react/.gitignore similarity index 100% rename from publicodes/example/publicodes-react/.gitignore rename to example/publicodes-react/.gitignore diff --git a/publicodes/example/publicodes-react/README.md b/example/publicodes-react/README.md similarity index 100% rename from publicodes/example/publicodes-react/README.md rename to example/publicodes-react/README.md diff --git a/publicodes/example/publicodes-react/__mocks__/CO2-douche.publicodes.yaml.js b/example/publicodes-react/__mocks__/CO2-douche.publicodes.yaml.js similarity index 100% rename from publicodes/example/publicodes-react/__mocks__/CO2-douche.publicodes.yaml.js rename to example/publicodes-react/__mocks__/CO2-douche.publicodes.yaml.js diff --git a/publicodes/example/publicodes-react/package.json b/example/publicodes-react/package.json similarity index 94% rename from publicodes/example/publicodes-react/package.json rename to example/publicodes-react/package.json index 38ca523b5..a6b21e01d 100644 --- a/publicodes/example/publicodes-react/package.json +++ b/example/publicodes-react/package.json @@ -6,8 +6,8 @@ "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", - "publicodes": "../../core/publicodes-1.0.0-beta.15.tgz", - "publicodes-react": "../../ui-react/publicodes-react-1.0.0-beta.15.tgz", + "publicodes": "../../core/publicodes-1.0.0-beta.16.tgz", + "publicodes-react": "../../ui-react/publicodes-react-1.0.0-beta.16.tgz", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router": "^5.2.0", diff --git a/publicodes/example/publicodes-react/public/index.html b/example/publicodes-react/public/index.html similarity index 100% rename from publicodes/example/publicodes-react/public/index.html rename to example/publicodes-react/public/index.html diff --git a/publicodes/example/publicodes-react/public/robots.txt b/example/publicodes-react/public/robots.txt similarity index 100% rename from publicodes/example/publicodes-react/public/robots.txt rename to example/publicodes-react/public/robots.txt diff --git a/publicodes/example/publicodes-react/src/App.css b/example/publicodes-react/src/App.css similarity index 100% rename from publicodes/example/publicodes-react/src/App.css rename to example/publicodes-react/src/App.css diff --git a/publicodes/example/publicodes-react/src/App.js b/example/publicodes-react/src/App.js similarity index 100% rename from publicodes/example/publicodes-react/src/App.js rename to example/publicodes-react/src/App.js diff --git a/publicodes/example/publicodes-react/src/CO2-douche.publicodes.yaml b/example/publicodes-react/src/CO2-douche.publicodes.yaml similarity index 95% rename from publicodes/example/publicodes-react/src/CO2-douche.publicodes.yaml rename to example/publicodes-react/src/CO2-douche.publicodes.yaml index d4a2d64f8..739632430 100644 --- a/publicodes/example/publicodes-react/src/CO2-douche.publicodes.yaml +++ b/example/publicodes-react/src/CO2-douche.publicodes.yaml @@ -1,8 +1,8 @@ douche: titre: Impact carbone d'une douche description: Impact carbone liée aux douches prise au cours de l'année - icônes: 🚿 🍃 - note: A titre de comparaison, l'empreinte carbone d'un burger est estimée à 0.279kg + icônes: 🚿 🍃 + note: A titre de comparaison, l'empreinte carbone d'un burger est estimée à 0.279kg unité: kgCO2eq / an valeur: fréquence * impact par litre * litres consommés @@ -14,9 +14,8 @@ douche . fréquence: 5 par semaine: 5 douche/semaine * 52 semaines/an # Publicodes ne gère pas encore nativement l'unité semaine 2 par jour: 2 douche/jour - douche . impact par litre: - somme: + somme: - eau . impact par litre froid - chauffage . impact par litre @@ -34,7 +33,6 @@ chauffage: chauffage . type: par défaut: "'électricité'" - chauffage . impact par kWh: notes: | La base carbone de l'ADEME ne permet malheureusement pas de faire des liens profonds vers les chiffres utilisés. @@ -50,7 +48,7 @@ chauffage . énergie consommée par litre: analyse du prix d'une douche: https://www.econologie.com/forums/plomberie-et-sanitaire/prix-reel-d-un-bain-ou-d-une-douche-pour-l-eau-et-chauffage-t12727.html chauffage . impact par litre: - produit: + produit: assiette: 0.0325 kWh/litre facteur: unité: kgCO2eq/kWh @@ -60,13 +58,13 @@ chauffage . impact par litre: - si: type = 'fioul' alors: 0.324 - si: type = 'électricité' - alors: 0.059 + alors: 0.059 douche . litres consommés: produit: assiette: durée moyenne facteur: débit - + douche . durée moyenne: question: Combien de temps dure votre douche en général ? par défaut: 10 min/douche @@ -74,14 +72,14 @@ douche . durée moyenne: expresse: 5 min/douche moyenne: 10 min/douche lente: 20 min/douche - + douche . débit: valeur: 18 litre/min références: économise l'eau: https://www.jeconomiseleau.org/index.php/particuliers/economies-par-usage/la-douche-et-le-bain douche . pomme de douche économe: - remplace: + remplace: règle: débit par: 9 litre/min question: Utilisez-vous une pomme de douche économe ? diff --git a/publicodes/example/publicodes-react/src/Publicodes.js b/example/publicodes-react/src/Publicodes.js similarity index 100% rename from publicodes/example/publicodes-react/src/Publicodes.js rename to example/publicodes-react/src/Publicodes.js diff --git a/publicodes/example/publicodes-react/src/Publicodes.test.js b/example/publicodes-react/src/Publicodes.test.js similarity index 100% rename from publicodes/example/publicodes-react/src/Publicodes.test.js rename to example/publicodes-react/src/Publicodes.test.js diff --git a/publicodes/example/publicodes-react/src/index.css b/example/publicodes-react/src/index.css similarity index 100% rename from publicodes/example/publicodes-react/src/index.css rename to example/publicodes-react/src/index.css diff --git a/publicodes/example/publicodes-react/src/index.js b/example/publicodes-react/src/index.js similarity index 100% rename from publicodes/example/publicodes-react/src/index.js rename to example/publicodes-react/src/index.js diff --git a/publicodes/example/publicodes-react/src/logo.png b/example/publicodes-react/src/logo.png similarity index 100% rename from publicodes/example/publicodes-react/src/logo.png rename to example/publicodes-react/src/logo.png diff --git a/publicodes/example/publicodes-react/src/reportWebVitals.js b/example/publicodes-react/src/reportWebVitals.js similarity index 100% rename from publicodes/example/publicodes-react/src/reportWebVitals.js rename to example/publicodes-react/src/reportWebVitals.js diff --git a/publicodes/example/publicodes-react/src/setupTests.js b/example/publicodes-react/src/setupTests.js similarity index 100% rename from publicodes/example/publicodes-react/src/setupTests.js rename to example/publicodes-react/src/setupTests.js diff --git a/publicodes/example/publicodes-react/yarn.lock b/example/publicodes-react/yarn.lock similarity index 99% rename from publicodes/example/publicodes-react/yarn.lock rename to example/publicodes-react/yarn.lock index aa7af411b..8d7aa2131 100644 --- a/publicodes/example/publicodes-react/yarn.lock +++ b/example/publicodes-react/yarn.lock @@ -9320,7 +9320,6 @@ publicodes-react@../../ui-react/publicodes-react-1.0.0-beta.13.tgz: focus-trap-react "^3.1.2" publicodes "1.0.0-beta.13" ramda "^0.27.0" - react-easy-emoji "^1.4.0" react-helmet "^6.1.0" react-markdown "^4.3.1" styled-components "^5.1.0" @@ -9528,14 +9527,6 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-easy-emoji@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-easy-emoji/-/react-easy-emoji-1.4.0.tgz#dfcbb743bf8439af265aa25a1e72998c6d2225ff" - integrity sha512-TcufijpuWKgYgzbySEBukNef+y0HI/4PAJ4gc9vb1CF7Q4CcAS2ZV8VMZk0ObtKKwJJfVgAHVt86nXWOed8QXg== - dependencies: - lodash.assign "^4.0.8" - string-replace-to-array "^1.0.1" - react-error-overlay@^6.0.9: version "6.0.9" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 6c19f5cbb..000000000 --- a/jest.config.js +++ /dev/null @@ -1,185 +0,0 @@ -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html - -module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/tmp/jest_rs", - - // Automatically clear mock calls and instances between every test - // clearMocks: false, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: null, - - // The directory where Jest should output its coverage files - // coverageDirectory: null, - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", - - // An array of directory names to be searched recursively up from the requiring module's location - moduleDirectories: ['node_modules', 'sources'], - - // An array of file extensions your modules use - moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'], - - // A map from regular expressions to module names that allow to stub out resources with a single module - moduleNameMapper: { - '\\.css$': 'mon-entreprise/test/regressions/styleMock.js', - }, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [''], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - testMatch: ['**/*.jest.js'], - // [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - transform: { - // It's not possible to have 2 piped transformers like in webpack - // ie ['jest-transform-nearley', 'babel-jest'], so we removed ES6 module from nearley output. - '\\.ne$': require.resolve('jest-transform-nearley'), - '\\.yaml$': require.resolve('yaml-jest'), - '\\.(js|tsx?)$': require.resolve('babel-jest'), - }, - - // An array of regexp pattern strings that are matched against all source file - // paths, matched files will skip transformation - transformIgnorePatterns: ['node_modules/(?!ramda|publicodes)/'], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, -} diff --git a/modele-social/.yarnrc b/modele-social/.yarnrc deleted file mode 100644 index 5770a9574..000000000 --- a/modele-social/.yarnrc +++ /dev/null @@ -1,2 +0,0 @@ -version-tag-prefix modele-social-v -version-git-message "⬆ Mise à jour du paquet \"modele-social\" vers %s" diff --git a/modele-social/README.md b/modele-social/README.md deleted file mode 100644 index 02e875d4b..000000000 --- a/modele-social/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Modèle social français en publicodes - -Ce paquet contient les règles publicodes utilisées sur https://mon-entreprise.fr -pour le calcul des cotisations sociales, des impôts et des droits sociaux. - -### Installation - -``` -npm install publicodes modele-social -``` - -### Exemple d'utilisation - -```js -import Engine, { formatValue } from 'publicodes' -import rules from 'modele-social' - -const engine = new Engine(rules) - -const net = engine - .setSituation({ - 'contrat salarié . rémunération . brut de base': '3000 €/mois', - }) - .evaluate('contrat salarié . rémunération . net') - -console.log(formatValue(net)) -``` - -👉 **[Voir le tutoriel complet](https://mon-entreprise.fr/int%C3%A9gration/biblioth%C3%A8que-de-calcul)** diff --git a/modele-social/build.js b/modele-social/build.js deleted file mode 100644 index c7595cfd7..000000000 --- a/modele-social/build.js +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-env node */ - -const fs = require('fs') -const path = require('path') -const yaml = require('yaml') - -const publicodesDir = path.resolve(__dirname, './règles') -const outDir = path.resolve(__dirname, './dist') - -if (!fs.existsSync(outDir)) { - fs.mkdirSync(outDir) -} - -function concatenateFilesInDir(dirPath = publicodesDir) { - return fs - .readdirSync(dirPath) - .map((filename) => { - const fullpath = path.join(dirPath, filename) - if (fs.statSync(fullpath).isDirectory()) { - return concatenateFilesInDir(fullpath) - } else { - return filename.endsWith('.yaml') ? fs.readFileSync(fullpath) : '' - } - }) - .reduce((acc, cur) => acc + '\n' + cur, '') -} - -function readRules() { - return yaml.parse(concatenateFilesInDir()) -} - -// Note: we can't put the output file in the fs.watched directory - -function writeJSFile() { - const rules = readRules() - const names = Object.keys(rules) - const jsString = `module.exports = ${JSON.stringify(rules, null, 2)}` - fs.writeFileSync(path.resolve(outDir, 'index.js'), jsString) - fs.writeFileSync( - path.resolve(outDir, 'names.ts'), - `\nexport type Names = ${names.map((name) => `"${name}"`).join('\n | ')}\n` - ) -} - -writeJSFile() -exports.watchDottedNames = () => fs.watch(publicodesDir, writeJSFile) diff --git a/modele-social/index.d.ts b/modele-social/index.d.ts deleted file mode 100644 index 8cb6cf61e..000000000 --- a/modele-social/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Currenty we systematically bundle all the rules even if we only need a -// sub-section of them. We might support "code-splitting" the rules in the -// future. -import { Rule } from 'publicodes' -import { Names } from './dist/names' - -export type DottedName = Names -declare let rules: Record -export default rules diff --git a/modele-social/package.json b/modele-social/package.json deleted file mode 100644 index 5c2e1eff4..000000000 --- a/modele-social/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "modele-social", - "version": "0.2.0", - "description": "Les règles publicodes du système social français", - "main": "./dist/index.js", - "types": "./index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/betagouv/mon-entreprise.git", - "directory": "modele-social" - }, - "bugs": "https://github.com/betagouv/mon-entreprise/issues?q=is%3Aopen+is%3Aissue+label%3A%22%F0%9F%93%95+l%C3%A9gislation%22", - "homepage": "https://mon-entreprise.fr/int%C3%A9gration/biblioth%C3%A8que-de-calcul", - "license": "MIT", - "files": [ - "dist/index.js" - ], - "devDependencies": { - "publicodes": "^1.0.0-beta.15", - "yaml": "^1.10.0" - }, - "peerDependencies": { - "publicodes": "^1.0.0-beta.15" - }, - "scripts": { - "build": "node build.js", - "clean": "rimraf dist node_modules", - "prepare": "yarn run build", - "up": "yarn version --minor && echo \"ℹ N'oubliez pas de poussez le tag git\"", - "test": "echo 1" - } -} diff --git a/modele-social/règles/artiste-auteur.yaml b/modele-social/règles/artiste-auteur.yaml deleted file mode 100644 index d0aa23f8f..000000000 --- a/modele-social/règles/artiste-auteur.yaml +++ /dev/null @@ -1,251 +0,0 @@ -artiste-auteur: - description: Le régime des artistes-auteurs - icônes: 👩‍🎨 - formule: oui - -artiste-auteur . revenus: oui -artiste-auteur . revenus . traitements et salaires: - titre: Revenu en traitements et salaires - par défaut: 0 €/an - résumé: Le montant brut hors TVA de vos droits d'auteur (recettes précomptées) - -artiste-auteur . revenus . BNC: - unité: €/an - formule: - valeur: recettes - abattement: - variations: - - si: micro-bnc - alors: charges forfaitaires - - sinon: frais réels - -artiste-auteur . revenus . BNC . micro-bnc: - non applicable si: contrôle micro-bnc - applicable si: recettes > 0 €/an - par défaut: oui - question: Souhaitez-vous opter pour le régime micro-BNC ? - résumé: Avec abattement forfaitaire fiscal de 34 % au titre des frais professionnels - -artiste-auteur . revenus . BNC . contrôle micro-bnc: - description: Vos revenus ne vous permettent pas d'opter pour le régime micro-BNC. - formule: - toutes ces conditions: - - recettes != 0 - - recettes > 72500 €/an - -artiste-auteur . revenus . BNC . recettes: - titre: Revenu en BNC - par défaut: 0 €/an - résumé: Le montant de vos recettes brutes hors TVA - -artiste-auteur . revenus . BNC . frais réels: - par défaut: 0 €/an - question: Régime des frais réels BNC - résumé: Montant de vos dépenses (frais professionnels, amortissements…) qui seront imputés à vos recettes afin d’établir vos bénéfices ou déficits - applicable si: recettes > 0 €/an - non applicable si: micro-bnc - -artiste-auteur . revenus . BNC . charges forfaitaires: - formule: 34% * recettes - -artiste-auteur . cotisations: - formule: - somme: - - vieillesse - - CSG-CRDS - - formation professionnelle - arrondi: oui - références: - Urssaf.fr: https://www.urssaf.fr/portail/home/espaces-dedies/artistes-auteurs-diffuseurs-comm/vous-etes-artiste-auteur/taux-des-cotisations.html - -artiste-auteur . cotisations . assiette: - description: Les revenus des artistes-auteurs peuvent être catégorisés soit comme des traitements et salaires, soit comme des bénéfices non commerciaux. Les cotisations sociales sont payées sur la somme des revenus de ces deux catégories. - formule: - somme: - - revenus . traitements et salaires - - revenus . BNC * 1.15 - -artiste-auteur . cotisations . option surcotisation: - applicable si: - toutes ces conditions: - - assiette > 0 - - assiette < assiette surcotisation - remplace: - règle: assiette - dans: vieillesse - par: assiette surcotisation - question: Souhaitez-vous surcotiser pour augmenter vos droits à retraite ? - description: Vos revenus sont en dessous des seuils vous permettant de valider 4 trimestres de retraite. Vous pouvez choisir de surcotiser pour augmenter vos droits. - par défaut: non - références: - Urssaf.fr: https://www.urssaf.fr/portail/home/espaces-dedies/artistes-auteurs-diffuseurs-comm/vous-etes-artiste-auteur/la-surcotisation.html - -artiste-auteur . cotisations . assiette surcotisation: 900 heures/an * SMIC horaire - -artiste-auteur . cotisations . vieillesse: - titre: Retraite de base - formule: - produit: - assiette: assiette - composantes: - - attributs: - nom: plafonnée - taux: contrat salarié . vieillesse . salarié . plafonnée . taux - 0.75% - plafond: contrat salarié . plafond sécurité sociale - - attributs: - nom: déplafonnée - taux: contrat salarié . vieillesse . salarié . déplafonnée . taux - 0.4% - -artiste-auteur . cotisations . CSG-CRDS: - formule: - somme: - - CSG - - CRDS - -artiste-auteur . cotisations . CSG-CRDS . assiette: - formule: - somme: - - cotisations . assiette - - (- CSG-CRDS . abattement) - -artiste-auteur . cotisations . CSG-CRDS . abattement: - formule: - produit: - assiette: revenus . traitements et salaires - taux: 1.75% - plafond: 4 * contrat salarié . plafond sécurité sociale - -artiste-auteur . cotisations . CSG-CRDS . CSG: - formule: - produit: - assiette: CSG-CRDS . assiette - taux: 9.20% - -artiste-auteur . cotisations . CSG-CRDS . CRDS: - formule: - produit: - assiette: CSG-CRDS . assiette - taux: 0.50% - -artiste-auteur . cotisations . formation professionnelle: - formule: - produit: - assiette: assiette - taux: 0.35% - -artiste-auteur . cotisations . IRCEC: - titre: Retraite complémentaire - description: | - Si vous êtes artiste-auteur professionnel et que vous êtes rémunéré en - droits d’auteur, l’IRCEC est l’organisme de Sécurité sociale qui assure la - gestion de votre retraite complémentaire obligatoire. - formule: - somme: - - cotisation RAAP - - cotisation RACD - - cotisation RACL - références: - Guide IRCEC 2021: http://www.ircec.fr/wp-content/uploads/2021/02/guide-ircec-2021.pdf - -artiste-auteur . cotisations . IRCEC . cotisation RAAP: - applicable si: assiette > seuil d'affiliation - description: | - Vous pouvez bénéficier d'un taux réduit à votre demande si vos revenus - n'atteignent pas à seuil minimal pour une année donnée. Ce taux réduit - s'applique également à vos revenus déjà soumis à cotisation auprès du RACL - ou du RACD. - formule: - variations: - - si: taux réduit - alors: - produit: - assiette: assiette - taux: 4% - - sinon: - barème: - assiette: assiette - tranches: - - taux: 4% - plafond: - variations: - - si: profession . RACD - alors: cotisation RACD . plafond - - si: profession . RACL - alors: cotisation RACL . plafond - - taux: 8% - arrondi: oui - -artiste-auteur . cotisations . IRCEC . cotisation RAAP . seuil d'affiliation: 9135 €/an - -artiste-auteur . cotisations . IRCEC . cotisation RAAP . taux réduit: - applicable si: assiette < 3 * seuil d'affiliation - question: Souhaitez-vous bénéficier d'un taux réduit pour votre cotisation retraite ? Vos droits seront réduits d'autant. - par défaut: non - description: | - Le régime RAAP vous permet d'opter pour un taux réduit de 4% au lieu de 8% - si vous en faite la demande. - références: - Guide IRCEC 2021: http://www.ircec.fr/wp-content/uploads/2021/02/guide-ircec-2021.pdf#page=5 - -artiste-auteur . cotisations . IRCEC . profession: - question: Exercez-vous l'une de professions suivantes ? - description: | - Selon la nature de leur activité, les artistes-auteurs cotisent à - un ou plusieurs régimes de retraite complémentaire gérés par - l’IRCEC : dans tous les cas et si vous atteignez le seuil - d’affiliation, au RAAP, puis selon votre activité artistique au RACD - et/ou au RACL. - formule: - une possibilité: - choix obligatoire: non - possibilités: - - RACD - - RACL - par défaut: non - -artiste-auteur . cotisations . IRCEC . profession . RACD: - icônes: 🎞️ - titre: auteur ou compositeur dramatique, de spectacle vivant, de films - description: | - Les professions suivantes cotisent au RACD : - - Les auteurs dramatiques exerçant l’une des professions suivantes : scénariste, dialoguiste, adaptateur, réalisateur, auteur de la bible littéraire, auteur graphique d’animation, créateur des personnages originaux et des décors s’il s’agit d’un univers original, etc. - - Les auteurs et compositeurs dramatiques et du spectacle vivant : théâtre, danse, opéra, cirque, arts de la rue, etc. - formule: profession = 'RACD' - -artiste-auteur . cotisations . IRCEC . profession . RACL: - icônes: 🎙️ - titre: auteur ou compositeur lyrique, dialoguiste de doublage - description: Les auteurs et compositeurs d’œuvres musicales et les dialoguistes de doublage cotisent au RACL. - formule: profession = 'RACL' - -artiste-auteur . cotisations . IRCEC . régime RACL: - question: Cotisez-vous au RACL ? - par défaut: non - -artiste-auteur . cotisations . IRCEC . cotisation RACD: - applicable si: profession . RACD - formule: - produit: - assiette: assiette - plafond: - nom: plafond - valeur: 496250 €/an - taux: 8% - arrondi: oui - -artiste-auteur . cotisations . IRCEC . cotisation RACL: - applicable si: profession . RACL - formule: - barème: - assiette: assiette - tranches: - - taux: 0% - plafond: 2739 €/an - - taux: 6.5% - plafond: - nom: plafond - valeur: 376665 €/an - - taux: - nom: cotisation de solidarité - valeur: 1.5% - arrondi: oui diff --git a/modele-social/règles/base.yaml b/modele-social/règles/base.yaml deleted file mode 100644 index 4e2b67e83..000000000 --- a/modele-social/règles/base.yaml +++ /dev/null @@ -1,71 +0,0 @@ -période: oui -période . jours ouvrés moyen par mois: - formule: 21 jour ouvré/mois - note: On retient 21 comme nombre de jours ouvrés moyen par mois - -période . semaines par mois: - unité: semaines/mois - formule: 52 semaines/an / 12 mois/an - -période . début d'année: - formule: 01/01/2021 - -période . fin d'année: - formule: 31/12/2021 - -plafond sécurité sociale temps plein: - description: Le plafond de Sécurité sociale est le montant maximum des rémunérations à prendre en compte pour le calcul de certaines cotisations. - acronyme: PSS - formule: 3428 €/mois - références: - Urssaf.fr: https://www.urssaf.fr/portail/home/taux-et-baremes/plafonds.html - arrêté 2021: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000042748904 - note: Le plafond de la Sécurité sociale n'a pas été revalorisé en 2021 par rapport à 2020. - -plafond horaire sécurité sociale: - acronyme: PHSS - formule: - valeur: plafond sécurité sociale temps plein / 1607 heures/an - arrondi: oui - unité: €/heure - références: - Article D242-19 du code de la sécurité sociale: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000033516173&cidTexte=LEGITEXT000006073189 - -plafond journalier sécurité sociale: - acronyme: PJSS - formule: - valeur: plafond sécurité sociale temps plein / 218 jours/an - arrondi: oui - unité: €/jour - références: - Article D242-17 du code de la sécurité sociale: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006073189&idArticle=LEGIARTI000006736124 - -SMIC horaire: - formule: - variations: - - si: établissement . localisation . département = 'Mayotte' - alors: 7.74 €/heure - - sinon: 10.25 €/heure - références: - décret: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000042677359?r=s75zUOEVpR - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F2300 - -SMIC temps plein: - unité: €/mois - formule: - produit: - assiette: contrat salarié . temps de travail . base légale * période . semaines par mois - facteur: SMIC horaire - références: - décret: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000042677359?r=s75zUOEVpR - -SMIC temps plein . net imposable: - titre: SMIC net imposable - description: Montant du SMIC net imposable pour un temps plein. - formule: - recalcul: - règle: contrat salarié . rémunération . net imposable . base - avec: - contrat salarié . rémunération . brut de base: SMIC temps plein - références: - barème PAS: https://bofip.impots.gouv.fr/bofip/11255-PGP.html diff --git a/modele-social/règles/chômage-partiel.yaml b/modele-social/règles/chômage-partiel.yaml deleted file mode 100644 index da61069a8..000000000 --- a/modele-social/règles/chômage-partiel.yaml +++ /dev/null @@ -1,15 +0,0 @@ -chômage partiel: oui - -chômage partiel . revenu net habituel: - formule: - recalcul: - règle: contrat salarié . rémunération . net - avec: - contrat salarié . activité partielle: non - -chômage partiel . coût employeur habituel: - formule: - recalcul: - règle: contrat salarié . prix du travail - avec: - contrat salarié . activité partielle: non diff --git a/modele-social/règles/conventions-collectives/bâtiment.yaml b/modele-social/règles/conventions-collectives/bâtiment.yaml deleted file mode 100644 index 222780080..000000000 --- a/modele-social/règles/conventions-collectives/bâtiment.yaml +++ /dev/null @@ -1,175 +0,0 @@ -contrat salarié . convention collective . BTP: - formule: convention collective = 'BTP' - titre: Bâtiment - icônes: 👷‍♀️ - description: >- - L'entreprise dépend de la convention collective nationale du bâtiment. Cette - convention définit trois catégories de salariés : les ouvriers, les ETAM - (employés, techniciens et agents de maîtrise) et les cadres. - rend non applicable: CDD . indemnité compensatrice de congés payés - -contrat salarié . convention collective . BTP . catégorie: - question: À quelle catégorie la salarié appartient-t'il ? - par défaut: "'ouvrier'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - ouvrier - - etam - - cadre - -contrat salarié . convention collective . BTP . catégorie . ouvrier: - titre: Ouvrier - icônes: 👨‍🔧 - formule: catégorie = 'ouvrier' - -contrat salarié . convention collective . BTP . catégorie . ouvrier . prévoyance complémentaire: - produit: - assiette: rémunération . brut de base - plafond: 3 * plafond sécurité sociale - composantes: - - attributs: - nom: employeur - remplace: prévoyance . employeur - taux: 1.72% - - attributs: - nom: salarié - remplace: prévoyance . salarié - taux: 0.87% - -contrat salarié . convention collective . BTP . catégorie . etam: - titre: ETAM - description: Employé, technicien, angent de maîtrise - icônes: 👷‍♂️ - formule: catégorie = 'etam' - remplace: - - règle: retraite complémentaire . employeur . taux tranche 1 - par: 4.47% - - règle: retraite complémentaire . employeur . taux tranche 2 - par: 12.70% - - règle: retraite complémentaire . salarié . taux tranche 1 - par: 3.40% - - règle: retraite complémentaire . salarié . taux tranche 2 - par: 8.89% - note: >- - Répartition conventionnelle fixée par l’article 5 de l’Accord du BTP du 13 décembre 1990. - -contrat salarié . convention collective . BTP . catégorie . etam . prévoyance complémentaire: - produit: - assiette: rémunération . brut de base - plafond: 3 * plafond sécurité sociale - composantes: - - attributs: - nom: employeur - remplace: prévoyance . employeur - taux: 1.25% - - attributs: - nom: salarié - remplace: prévoyance . salarié - taux: 0.60% - -contrat salarié . convention collective . BTP . catégorie . cadre: - formule: catégorie = 'cadre' - titre: Cadre - icônes: 👩‍💼 - remplace: - - règle: statut cadre - par: oui - - -contrat salarié . convention collective . BTP . catégorie . cadre . prévoyance complémentaire: - barème: - assiette: rémunération . brut de base - multiplicateur: plafond sécurité sociale - composantes: - - attributs: - nom: employeur - remplace: prévoyance . employeur - tranches: - - taux: 1.50% - plafond: 1 - - taux: 50% * 2.40% - plafond: 4 - - taux: 50% * 3.60% - plafond: 8 - - attributs: - nom: salarié - remplace: prévoyance . salarié - tranches: - - taux: 0% - plafond: 1 - - taux: 50% * 2.40% - plafond: 4 - - taux: 50% * 3.60% - plafond: 8 - - -contrat salarié . convention collective . BTP . cotisations conventionnelles: - remplace: cotisations . patronales . conventionnelles - formule: - somme: - - congés intempéries - - OPPBTP - -contrat salarié . convention collective . BTP . congés intempéries: - formule: - produit: - assiette: cotisations . assiette - taux: - variations: - - si: caisse de rattachement = 'idf' - alors: 19.80% - - si: caisse de rattachement = 'nord ouest' - alors: 19.95% - - si: caisse de rattachement = 'grand ouest' - alors: 19.95% - - si: caisse de rattachement = 'centre ouest' - alors: 20.30% - - si: caisse de rattachement = 'centre' - alors: 20.40% - - si: caisse de rattachement = 'grand est' - alors: 20.00% - - si: caisse de rattachement = 'rhône alpes auvergne' - alors: 19.80% - - si: caisse de rattachement = 'méditerranée' - alors: 19.60% - - si: caisse de rattachement = 'sud ouest' - alors: 19.90% - références: - CIBTP: https://www.cibtp.fr/ - Article L3141-30 du Code du Travail: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=DF6E6424807679A6EDC2915496BEA32D.tplgfr22s_2?idArticle=LEGIARTI000033020675&cidTexte=LEGITEXT000006072050&dateTexte=20200320 - -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement: - question: À quelle caisse l'entreprise est-elle rattachée pour le versement de la cotisation congés intempéries ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - idf - - nord ouest - - grand ouest - - centre ouest - - centre - - grand est - - rhône alpes auvergne - - méditerranée - - sud ouest - par défaut: "'idf'" - -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . idf: - titre: Île-de-France -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . nord ouest: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . grand ouest: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . centre ouest: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . centre: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . grand est: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . rhône alpes auvergne: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . méditerranée: -contrat salarié . convention collective . BTP . congés intempéries . caisse de rattachement . sud ouest: - -contrat salarié . convention collective . BTP . OPPBTP: - formule: - produit: - assiette: rémunération . brut de base * 1.1314 - taux: 0.11% diff --git a/modele-social/règles/conventions-collectives/experts-comptables.yaml b/modele-social/règles/conventions-collectives/experts-comptables.yaml deleted file mode 100644 index 2e53ae7f7..000000000 --- a/modele-social/règles/conventions-collectives/experts-comptables.yaml +++ /dev/null @@ -1,22 +0,0 @@ -contrat salarié . convention collective . compta: - formule: convention collective = 'compta' - titre: Experts-comptables et commissaires aux comptes - icônes: 🧮 - description: >- - Cette convention collective concerne les experts comptables inscrits à - l'ordre, les commissaires aux comptes inscrits à la compagnie, ainsi que les - centres de gestion agréés et les associations agréées (AGC). - références: - Légifrance: https://www.legifrance.gouv.fr/affichIDCC.do?idConvention=KALICONT000005635826 - Synthèse Dicotravail: https://www.dicotravail.com/convention-collective/experts-comptables-jo-3020-idcc-787/ - -contrat salarié . convention collective . compta . majoration heures supplémentaires: - remplace: temps de travail . heures supplémentaires . majoration - formule: - barème: - assiette: temps de travail . heures supplémentaires - multiplicateur: période . semaines par mois - tranches: - - taux: 10% - plafond: 4 heures/semaine - - taux: 25% diff --git a/modele-social/règles/conventions-collectives/hôtels-cafés-restaurants.yaml b/modele-social/règles/conventions-collectives/hôtels-cafés-restaurants.yaml deleted file mode 100644 index 705ed51b7..000000000 --- a/modele-social/règles/conventions-collectives/hôtels-cafés-restaurants.yaml +++ /dev/null @@ -1,39 +0,0 @@ -contrat salarié . convention collective . HCR: - formule: convention collective = 'HCR' - titre: hôtels, cafés restaurants HCR - icônes: 🍴 - description: L'entreprise est un hôtel, café, restaurant ou assimilé. - -contrat salarié . convention collective . HCR . montant forfaitaire d'un repas: - remplace: - règle: rémunération . avantages en nature . nourriture . montant . repas forfaitaire - formule: 3.62 €/repas - -contrat salarié . convention collective . HCR . majoration heures supplémentaires: - remplace: temps de travail . heures supplémentaires . majoration - formule: - barème: - assiette: temps de travail . heures supplémentaires - multiplicateur: période . semaines par mois - tranches: - - taux: 10% - plafond: 4 heures/semaine - - taux: 20% - plafond: 8 heures/semaine - - taux: 50% - -contrat salarié . convention collective . HCR . prévoyance conventionnelle: - produit: - assiette: rémunération . brut de base - plafond: plafond sécurité sociale - composantes: - - attributs: - nom: employeur - remplace: prévoyance . employeur - taux: 0.40% - - attributs: - nom: salarié - remplace: prévoyance . salarié - taux: 0.40% - références: - Prévoyance HCR: https://www.hcrprevoyance.fr/contenu/documents/modalites_pratiques/HCR%20027_20-2%20-%20Fiche%20Garantie%20Conventionnelle%20Prevoyance.pdf diff --git a/modele-social/règles/conventions-collectives/optique.yaml b/modele-social/règles/conventions-collectives/optique.yaml deleted file mode 100644 index 553b2480c..000000000 --- a/modele-social/règles/conventions-collectives/optique.yaml +++ /dev/null @@ -1,87 +0,0 @@ -contrat salarié . convention collective . optique: - formule: convention collective = 'optique' - titre: Optique - icônes: 👓 - -contrat salarié . convention collective . optique . prime d'ancienneté: - applicable si: convention collective = 'optique' - remplace: rémunération . primes . ancienneté - formule: - produit: - assiette: salaire minimum conventionnel - taux: - variations: - - si: ancienneté >= 15 ans - alors: 15% - - si: ancienneté >= 12 ans - alors: 12% - - si: ancienneté >= 9 ans - alors: 9% - - si: ancienneté >= 6 ans - alors: 6% - - si: ancienneté >= 3 ans - alors: 3% - - sinon: 0% - références: - Légifrance: https://www.legifrance.gouv.fr/affichIDCC.do?idSectionTA=KALISCTA000005736434&cidTexte=KALITEXT000005649634&idConvention=KALICONT000005635912 - -contrat salarié . convention collective . optique . salaire minimum conventionnel: - unité: €/mois - formule: - variations: - - si: coefficient < 110 - alors: 0 - - si: coefficient < 115 - alors: 1485 - - si: coefficient < 130 - alors: 1555 - - si: coefficient < 140 - alors: 1585 - - si: coefficient < 160 - alors: 1645 - - si: coefficient < 180 - alors: 1650 - - si: coefficient < 195 - alors: 1660 - - si: coefficient < 210 - alors: 1715 - - si: coefficient < 220 - alors: 1845 - - si: coefficient < 230 - alors: 1920 - - si: coefficient < 250 - alors: 1945 - - si: coefficient < 280 - alors: 2150 - - si: coefficient < 300 - alors: 2305 - - si: coefficient < 330 - alors: 2560 - - si: coefficient < 350 - alors: 2715 - - si: coefficient < 380 - alors: 2970 - - sinon: 3170 - -contrat salarié . convention collective . optique . coefficient: - question: Quel est le coefficient correspondant au poste du salarié ? - description: >- - Se référer à la [grille fournie par la convention collective](http://opticiensreunis.org/storage/pdf/D4AciCiqHMr9mgqlTgjW0hvfPyE4w6ZxGTCihzYy.pdf#page=27). - par défaut: 110 points - -contrat salarié . convention collective . optique . prévoyance: - non applicable si: prévoyance obligatoire cadre - formule: - barème: - assiette: rémunération . brut de base - tranches: - - taux: 0.46% - plafond: 4 * plafond sécurité sociale - -contrat salarié . convention collective . optique . prévoyance . employeur: - remplace: contrat salarié . prévoyance . employeur - formule: 60% * prévoyance - -contrat salarié . convention collective . optique . prévoyance . salarié: - remplace: contrat salarié . prévoyance . salarié - formule: 40% * prévoyance diff --git a/modele-social/règles/conventions-collectives/spectacle-vivant.yaml b/modele-social/règles/conventions-collectives/spectacle-vivant.yaml deleted file mode 100644 index ab9b11e1e..000000000 --- a/modele-social/règles/conventions-collectives/spectacle-vivant.yaml +++ /dev/null @@ -1,238 +0,0 @@ -contrat salarié . convention collective . SVP: - titre: Spectacle vivant privé - formule: convention collective = 'SVP' - icônes: 🎭 - description: | - L'entreprise dépend de la convention collective nationale des entreprises privée du spectacle - rend non applicable: CDD . indemnité compensatrice de congés payés - -contrat salarié . convention collective . SVP . cotisations patronales: - titre: cotisations conventionnelles - remplace: cotisations . patronales . conventionnelles - formule: - somme: - - intermittents du spectacle . caisse des congés spectacle - - FCAP - - prévoyance - -contrat salarié . convention collective . SVP . FCAP: - titre: Fond commun d'aide au paritarisme - description: | - Le Fonds Commun d’Aide au Paritarisme du Spectacle Vivant Privé (FCAP-SVP) résulte de l’application du titre V – Financement du paritarisme la CCN des entreprises du secteur privé du spectacle vivant. Il a pour but de : - - - Permettre aux organisations d’employeurs et de salariés d’exercer leurs missions et de favoriser l’application dans le temps de la Convention collective, - - De couvrir les frais engagés par les organisations syndicales, - - De couvrir les frais relatifs au dispositif des Conseillers Conventionnels des Salariés, au nombre de 28 - - De financer le rapport de branche du spectacle vivant privé. - - unité: €/an - - # TODO : - note: les minimum et maximum sont fixé par entreprise, et non par salarié - formule: - produit: - plafond: plafond sécurité sociale - assiette: rémunération . brut - taux: 0.1% - plancher: 80 €.employés/an / entreprise . effectif - plafond: 300 €.employés/an / entreprise . effectif - - références: - Titre V de IDCC 3090: https://www.legifrance.gouv.fr/affichIDCC.do;?idSectionTA=KALISCTA000028157274&cidTexte=KALITEXT000028157267&idConvention=KALICONT000028157262 - Note explicative AUDIENS: http://www.cheque-intermittents.com/wp-content/uploads/2015/05/FCAP-SVP-EXPLIC_final.pdf - -contrat salarié . convention collective . SVP . prévoyance: - formule: - produit: - plafond: plafond sécurité sociale - assiette: cotisations . assiette - taux: 1.20% - non applicable si: prévoyance obligatoire cadre - note: Dans le cas du statut cadre, la prévoyance obligatoire est plus avantageuse, c'est donc cette dernière qui est prise en compte - références: - notice audiens: https://www.audiens.org/files/live/sites/siteAudiens/files/03_documents/entreprise/CCN/CCN-SVP-2015.pdf - Article 12.6, Titre VII, IDCC 3090: https://www.legifrance.gouv.fr/affichIDCCArticle.do;?idArticle=KALIARTI000028157451&cidTexte=KALITEXT000028157267&dateTexte=29990101&categorieLien=id - -contrat salarié . intermittents du spectacle: - applicable si: - toutes ces conditions: - - CDD . motif = 'classique . usage' - - convention collective . SVP - question: A quel statut d'intermittent est rattaché l'employé ? - par défaut: "'technicien'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - technicien - - artiste - -contrat salarié . intermittents du spectacle . formation professionnelle: - remplace: formation professionnelle - formule: - somme: - - 50 €/mois - - produit: - assiette: rémunération . brut - taux: 2.10% - -contrat salarié . intermittents du spectacle . caisse des congés spectacle: - formule: - produit: - assiette: rémunération . brut - taux: 15.40% - références: - audiens.org: https://www.audiens.org/files/live/sites/siteAudiens/files/03_documents/entreprise/Fiches-techniques/Conges-Spectacles-Mode-d-emploi-employeur-2019.pdf - Article L3141-30 du Code du Travail: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=DF6E6424807679A6EDC2915496BEA32D.tplgfr22s_2?idArticle=LEGIARTI000033020675&cidTexte=LEGITEXT000006072050&dateTexte=20200320 - -contrat salarié . intermittents du spectacle . retraite complémentaire techniciens et cadre: - applicable si: - une de ces conditions: - - statut cadre - - technicien - formule: oui - remplace: - - règle: retraite complémentaire . employeur . taux tranche 1 - par: 3.94% - - règle: retraite complémentaire . salarié . taux tranche 1 - par: 3.93% - références: - audiens.org: https://www.audiens.org/solutions/entreprises-la-retraite-complementaire-agirc-arcco-au-1er-janvier-2019.html - -contrat salarié . intermittents du spectacle . technicien: - formule: intermittents du spectacle = 'technicien' - -contrat salarié . intermittents du spectacle . technicien . non cadre: - formule: statut cadre = non - remplace: - - règle: retraite complémentaire . employeur . taux tranche 2 - par: 10.80% - - règle: retraite complémentaire . salarié . taux tranche 2 - par: 10.79% - - règle: plafond sécurité sociale - par: plafond sécurité sociale temps plein - dans: - - retraite complémentaire - - contribution d'équilibre général - - contribution d'équilibre technique - références: - audiens.org: https://www.audiens.org/solutions/entreprises-la-retraite-complementaire-agirc-arcco-au-1er-janvier-2019.html - -contrat salarié . intermittents du spectacle . artiste: - formule: intermittents du spectacle = 'artiste' - description: | - Sont considérés comme artistes du spectacle : - - L'artiste lyrique - - L'artiste dramatique - - L'artiste chorégraphique - - L'artiste de variétés - - Le musicien - - Le chansonnier - - L'artiste de complément - - Le chef d'orchestre - - L'arrangeur-orchestrateur - - Le metteur en scène, le réalisateur et le chorégraphe, pour l'exécution matérielle de leur conception artistique - - L'artiste de cirque - - Le marionnettiste - - Les personnes dont l'activité est reconnue comme un métier d'artiste-interprète par les conventions collectives du spectacle vivant étendues. - - références: - Article L7121-2: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000032859810&cidTexte=LEGITEXT000006072050&dateTexte=20160709 - -contrat salarié . intermittents du spectacle . artiste . non cadre: - formule: statut cadre = non - remplace: - - règle: plafond sécurité sociale - par: plafond sécurité sociale temps plein - dans: - - retraite complémentaire - - contribution d'équilibre général - - contribution d'équilibre technique - - règle: retraite complémentaire . employeur . taux tranche 1 - par: 4.45% - - règle: retraite complémentaire . employeur . taux tranche 2 - par: 10.80% - - règle: retraite complémentaire . salarié . taux tranche 1 - par: 4.44% - - règle: retraite complémentaire . salarié . taux tranche 2 - par: 10.79% - références: - audiens.org: https://www.audiens.org/solutions/entreprises-la-retraite-complementaire-agirc-arcco-au-1er-janvier-2019.html - -contrat salarié . intermittents du spectacle . artiste . activité accessoire: - question: | - L'artiste est-il rémunéré pour une activité accessoire (dispense de cours, stages, etc) ? - par défaut: non - -contrat salarié . intermittents du spectacle . artiste . réduction de taux: - # TODO : centraliser les exonérations sous un namespace commun pour plus de facilité dans leur activiation / desactivation - rend non applicable: réduction générale - non applicable si: activité accessoire - remplace: - # - règle: exonérations . taux réduit - # par: oui - - règle: maladie . taux employeur - par: maladie . taux employeur * réduction de taux - - règle: maladie . taux salarié - par: maladie . taux salarié * réduction de taux - - règle: vieillesse . employeur . plafonnée . taux - par: vieillesse . employeur . plafonnée . taux * réduction de taux - - règle: vieillesse . employeur . déplafonnée . taux - par: vieillesse . employeur . déplafonnée . taux * réduction de taux - - règle: vieillesse . salarié . plafonnée . taux - par: vieillesse . salarié . plafonnée . taux * réduction de taux - - règle: vieillesse . salarié . déplafonnée . taux - par: vieillesse . salarié . déplafonnée . taux * réduction de taux - - règle: allocations familiales . taux - par: allocations familiales . taux * réduction de taux - - règle: établissement . taux du versement transport - par: établissement . taux du versement transport * réduction de taux - - règle: FNAL . taux - par: FNAL . taux * réduction de taux - formule: 70% - -contrat salarié . intermittents du spectacle . artiste . réduction de taux . ATMP: - remplace: ATMP . taux - formule: - variations: - - si: régime alsace moselle - alors: 1.54% - - sinon: 1.12% - -contrat salarié . intermittents du spectacle . artiste . nombre jours travaillés: - question: Pour combien de jours continus l'artiste est-il engagé ? - par défaut: 5 jours - -contrat salarié . intermittents du spectacle . artiste . plafond proratisé: - applicable si: nombre jours travaillés < 5 - unité: €/mois - - remplace: - règle: plafond sécurité sociale - dans: - - FNAL - - vieillesse - formule: - produit: - assiette: plafond horaire sécurité sociale - facteur: 12 * nombre jours travaillés - -contrat salarié . intermittents du spectacle . artiste . acteur de complément: - non applicable si: activité accessoire - question: L'artiste est-il un acteur de complément engagé à la journée pour une production cinématographique ? - par defaut: non - -contrat salarié . intermittents du spectacle . artiste . acteur de complément . nombre jours travaillés: - remplace: artiste . nombre jours travaillés - formule: 1 - -contrat salarié . intermittents du spectacle . artiste . acteur de complément . assiette forfaitaire: - applicable si: rémunération . brut < 6% * plafond sécurité sociale temps plein - remplace: - - contrat salarié . cotisations . assiette forfaitaire - - règle: nombre jours travaillés - par: 1 - formule: - produit: - assiette: SMIC horaire - facteur: 9 diff --git a/modele-social/règles/conventions-collectives/sport.yaml b/modele-social/règles/conventions-collectives/sport.yaml deleted file mode 100644 index f9f193a82..000000000 --- a/modele-social/règles/conventions-collectives/sport.yaml +++ /dev/null @@ -1,294 +0,0 @@ -contrat salarié . convention collective . sport: - formule: convention collective = 'sport' - titre: Sport - icônes: 🎽 - description: | - L'entreprise dépend de la convention collective nationale des sportifs (CCNS) - Les disciplines concernées sont tous les sports pour lesquels il existe une fédération française agréée par le ministère de la Jeunesse et des Sports. - -contrat salarié . convention collective . sport . cotisations: oui - -contrat salarié . convention collective . sport . cotisations . patronales: - titre: cotisations conventionnelles - remplace: - - règle: cotisations . patronales . conventionnelles - formule: - somme: - - prévoyance . employeur - - financement du paritarisme - -contrat salarié . convention collective . sport . cotisations . financement du paritarisme: - # TODO - note: se calcule sur la masse salariale - formule: - produit: - assiette: cotisations . assiette - taux: 0.06% - plancher: 3 €.employé/an / entreprise . effectif - -contrat salarié . convention collective . sport . cotisations . prévoyance: - remplace: - - règle: cotisations . salariales . conventionnelles - par: prévoyance . salarié - - règle: avantages sociaux - par: - somme: - - prévoyance . employeur - - avantages sociaux - formule: - produit: - assiette: cotisations . assiette - plafond: 8 * plafond sécurité sociale - composantes: - - attributs: - nom: employeur - taux: 0.29% - - attributs: - nom: salarié - taux: 0.29% - références: - Article 10.8 de la CCNS (IDCC 2511): https://www.legifrance.gouv.fr/affichIDCCArticle.do;?idArticle=KALIARTI000033304755&cidTexte=KALITEXT000017577657&dateTexte=29990101&categorieLien=id - -contrat salarié . convention collective . sport . cotisations . régime frais de santé: - remplace: contrat salarié . complémentaire santé . forfait - formule: - produit: - assiette: plafond sécurité sociale temps plein - taux: taux - -contrat salarié . convention collective . sport . cotisations . régime frais de santé . taux: - formule: - variations: - - si: régime alsace moselle - alors: - variations: - - si: option . R1 - alors: 0.59% - - si: option . R2 - alors: 0.77% - - si: option . R3 - alors: 0.89% - - sinon: - variations: - - si: option . R1 - alors: 0.92% - - si: option . R2 - alors: 1.17% - - si: option . R3 - alors: 1.32% - référence: - unamens.fr: https://www.umanens.fr/reglementation-couverture-sante-obligatoire/ccn-sport - unamens (notice pdf): https://www.umanens.fr/documents/doc-offres-2018/sport/juin-2019/CCN_SPORT_PLAQ_EMPLOYEUR_2019.pdf - -contrat salarié . convention collective . sport . cotisations . régime frais de santé . option: - question: Quel option a été choisi pour le régime des frais de santé ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - R1 - - R2 - - R3 - par défaut: "'R1'" - références: - unamens.fr: https://www.umanens.fr/reglementation-couverture-sante-obligatoire/ccn-sport -contrat salarié . convention collective . sport . cotisations . régime frais de santé . option . R1: - formule: option = 'R1' -contrat salarié . convention collective . sport . cotisations . régime frais de santé . option . R2: - formule: option = 'R2' -contrat salarié . convention collective . sport . cotisations . régime frais de santé . option . R3: - formule: option = 'R3' - -contrat salarié . convention collective . sport . cotisations . formation professionnelle: - remplace: contrat salarié . formation professionnelle - formule: - somme: - - plan de formation - - professionnalisation - - CIF CDI - - CIF CDD - références: - Article 8.6 de la CCNS (IDCC2511): https://www.legifrance.gouv.fr/affichIDCCArticle.do;?idArticle=KALIARTI000034406905&cidTexte=KALITEXT000017577657&dateTexte=29990101&categorieLien=id - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . plan de formation: - formule: - produit: - assiette: cotisations . assiette - taux: - variations: - - si: entreprise . effectif < 20 - alors: 1.45% - - si: entreprise . effectif >= 20 - alors: 0.90% - plancher: versement minimum - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . plan de formation . versement minimum: - applicable si: entreprise . effectif < 10 - formule: 30 €/mois - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . professionnalisation: - formule: - produit: - assiette: cotisations . assiette - taux: - variations: - - si: entreprise . effectif < 20 - alors: 0.15% - - si: entreprise . effectif >= 20 - alors: 0.50% - plancher: versement minimum - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . professionnalisation . versement minimum: - applicable si: entreprise . effectif < 10 - formule: 5 €/mois - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . CIF CDI: - applicable si: - toutes ces conditions: - - CDI - - entreprise . effectif >= 20 - formule: - produit: - assiette: cotisations . assiette - taux: 0.20% - -contrat salarié . convention collective . sport . cotisations . formation professionnelle . CIF CDD: - applicable si: CDD - formule: - produit: - assiette: cotisations . assiette - taux: 1% - -contrat salarié . convention collective . sport . cotisations . assiette franchisée: - formule: - valeur: cotisations . assiette - abattement: franchise - -contrat salarié . convention collective . sport . joueur entraineur: - question: Le joueur est-il aussi entraineur ? - par défaut: non - -contrat salarié . convention collective . sport . exonération cotisation AT: - non applicable si: - une de ces conditions: - - joueur entraineur - - refus - remplace: - règle: ATMP - par: non - formule: oui - -contrat salarié . convention collective . sport . exonération cotisation AT . refus: - titre: refus exonération AT - question: L'employeur a-t'il refusé d'être exonéré de cotisations AT ? - par défaut: non - -contrat salarié . convention collective . sport . cotisations . assiette forfaitaire: - applicable si: assiette franchisée < SMIC horaire * 115 heures/mois - remplace: contrat salarié . cotisations . assiette forfaitaire - formule: - grille: - assiette: assiette franchisée - multiplicateur: SMIC horaire / 1 mois - unité: €/mois - tranches: - - montant: 5 * SMIC horaire - plafond: 45 heures - - montant: 15 * SMIC horaire - plafond: 60 heures - - montant: 25 * SMIC horaire - plafond: 80 heures - - montant: 35 * SMIC horaire - plafond: 100 heures - - montant: 50 * SMIC horaire - plafond: 115 heures - -contrat salarié . convention collective . sport . primes . nombre de manifestations: - question: Combien de manifestations rémunérées le joueur a-t'il effectué ? - #TODO : gérer la période - par défaut: 0 manifestations - -contrat salarié . convention collective . sport . primes: - titre: primes de manifestation - #TODO non applicable si: période = 'année' - remplace: rémunération . primes . activité . conventionnelles - unité: €/mois - formule: - somme: - - manifestation 1 - - manifestation 2 - - manifestation 3 - - manifestation 4 - - manifestation 5 - - autres manifestations - -contrat salarié . convention collective . sport . primes . manifestation 1: - question: Quelle prime pour la première manifestation ? - applicable si: nombre de manifestations > 0 - par défaut: 100 € - -contrat salarié . convention collective . sport . primes . manifestation 1 . franchise: - titre: franchise manifestation 1 - formule: - valeur: manifestation 1 - plafond: 70% * plafond journalier sécurité sociale - -contrat salarié . convention collective . sport . primes . manifestation 2: - question: Quelle prime pour la deuxième manifestation ? - applicable si: nombre de manifestations > 1 - par défaut: 100 € - -contrat salarié . convention collective . sport . primes . manifestation 2 . franchise: - titre: franchise manifestation 2 - formule: - valeur: manifestation 2 - plafond: 70% * plafond journalier sécurité sociale - -contrat salarié . convention collective . sport . primes . manifestation 3: - question: Quelle prime pour la troisième manifestation ? - applicable si: nombre de manifestations > 2 - par défaut: 100 € - -contrat salarié . convention collective . sport . primes . manifestation 3 . franchise: - titre: franchise manifestation 3 - formule: - valeur: manifestation 3 - plafond: 70% * plafond journalier sécurité sociale - -contrat salarié . convention collective . sport . primes . manifestation 4: - question: Quelle prime pour la quatrième manifestation ? - applicable si: nombre de manifestations > 3 - par défaut: 100 € - -contrat salarié . convention collective . sport . primes . manifestation 4 . franchise: - titre: franchise manifestation 4 - formule: - valeur: manifestation 4 - plafond: 70% * plafond journalier sécurité sociale - -contrat salarié . convention collective . sport . primes . manifestation 5: - question: Quelle prime pour la cinquième manifestation ? - applicable si: nombre de manifestations > 4 - par défaut: 100 € - -contrat salarié . convention collective . sport . primes . manifestation 5 . franchise: - titre: franchise manifestation 5 - formule: - valeur: manifestation 5 - plafond: 70% * plafond journalier sécurité sociale - -contrat salarié . convention collective . sport . primes . autres manifestations: - question: Quelles primes pour les autres manifestations ? - applicable si: nombre de manifestations > 5 - par défaut: 100 € - -contrat salarié . convention collective . sport . cotisations . franchise: - applicable si: entreprise . effectif < 10 - unité: €/mois - formule: - somme: - - primes . manifestation 1 . franchise - - primes . manifestation 2 . franchise - - primes . manifestation 3 . franchise - - primes . manifestation 4 . franchise - - primes . manifestation 5 . franchise diff --git a/modele-social/règles/dirigeant.yaml b/modele-social/règles/dirigeant.yaml deleted file mode 100644 index 3b1ac4a29..000000000 --- a/modele-social/règles/dirigeant.yaml +++ /dev/null @@ -1,1323 +0,0 @@ -dirigeant: - question: Quel est le régime social du dirigeant ? - par défaut: non - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - auto-entrepreneur - - assimilé salarié - - indépendant - -dirigeant . rémunération: oui -dirigeant . rémunération . totale: - question: Quel montant total pensez-vous dégager pour votre rémunération ? - description: | - C'est ce que l'entreprise dépense en tout pour la rémunération du dirigeant. Cette rémunération "super-brute" inclut toutes les cotisations sociales à payer. On peut aussi considérer que c'est la valeur monétaire du travail du dirigeant. - titre: Rémunération totale - unité: €/an - résumé: Incluant les cotisations et contributions - variations: - - si: assimilé salarié - alors: contrat salarié . rémunération . total - - si: entreprise . imposition . IS - alors: - somme: - - nette après impôt - - impôt - - cotisations - par défaut: entreprise . chiffre d'affaires - entreprise . charges - plancher: cotisations - - sinon: entreprise . chiffre d'affaires - entreprise . charges - -dirigeant . rémunération . nette: - titre: rémunération nette - question: Quelle est votre rémunération nette ? - résumé: Après déduction des cotisations, contributions et charges - somme: - - rémunération . totale - - (- cotisations) - -dirigeant . rémunération . cotisations: - variations: - - si: assimilé salarié - alors: contrat salarié . cotisations - - si: indépendant - alors: indépendant . cotisations et contributions - - si: auto-entrepreneur - alors: auto-entrepreneur . cotisations et contributions - -dirigeant . rémunération . imposable: - titre: Rémunération imposable - variations: - - si: assimilé salarié - alors: contrat salarié . rémunération . net imposable - - si: indépendant - alors: indépendant . revenu professionnel - - si: auto-entrepreneur - alors: auto-entrepreneur . impôt . revenu imposable - -dirigeant . rémunération . impôt: - produit: - assiette: imposable - taux: impôt . taux d'imposition - - -dirigeant . rémunération . nette après impôt: - titre: Rémunération après impôt - unité: €/an - arrondi: oui - question: Quel est le revenu net après impôt souhaité ? - description: >- - Le revenu net après déduction de l'impôt - sur le revenu et des cotisations sociales. - valeur: rémunération . nette - impôt - résumé: Ce que vous rapporte cette activité - -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é' - 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 - - contrat salarié . profession spécifique - - contrat salarié . rémunération . primes - - contrat salarié . rémunération . primes . fin d'année - - contrat salarié . rémunération . primes . activité - - contrat salarié . frais professionnels - - contrat salarié . chômage - - contrat salarié . réduction générale - - contrat salarié . allocations familiales . taux réduit - - contrat salarié . maladie . taux employeur . taux réduit - - contrat salarié . lodeom - - contrat salarié . AGS - - contrat salarié . APEC - - contrat salarié . contribution au dialogue social - - contrat salarié . temps de travail . temps partiel - - contrat salarié . temps de travail . heures supplémentaires - - contrat salarié . déduction forfaitaire spécifique - - contrat salarié . régime des impatriés - - contrat salarié . rémunération . contrôle smic - - entreprise . association non lucrative - références: - Le régime des dirigeants: https://www.urssaf.fr/portail/home/employeur/creer/choisir-une-forme-juridique/le-statut-du-dirigeant/les-dirigeants-rattaches-au-regi.html - note: Nous ne gérons pas le cas des SAS(U) à l'IR pour l'instant - -dirigeant . assimilé salarié . réduction ACRE: - applicable si: entreprise . ACRE - formule: - produit: - assiette: - somme: - - contrat salarié . maladie - - contrat salarié . allocations familiales - - contrat salarié . vieillesse - taux: taux - -dirigeant . assimilé salarié . réduction ACRE . taux: - titre: taux ACRE - formule: - taux progressif: - assiette: contrat salarié . cotisations . assiette - multiplicateur: plafond sécurité sociale temps plein - tranches: - - plafond: 75% - taux: 100% - - plafond: 100% - taux: 0% - -dirigeant . assimilé salarié . réduction ACRE . notification taux annuel: - formule: oui - type: notification - description: | - Le taux ACRE utilisé est une moyenne annuelle. Le - simulateur ne prends pas encore en compte le calcul de l'ACRE mois par mois. - -dirigeant . auto-entrepreneur: - 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. - -dirigeant . auto-entrepreneur . net de cotisations: - titre: Revenu net de cotisations - arrondi: oui - - unité: €/an - remplace: rémunération . nette - identifiant court: auto-entrepreneur-net - résumé: Avant impôt - question: Quel revenu avant impôt voulez-vous toucher ? - description: Il s'agit du revenu après déductions des cotisations, avant le paiement de l'impôt sur le revenu. - formule: entreprise . chiffre d'affaires - cotisations et contributions - -dirigeant . auto-entrepreneur . cotisations et contributions: - unité: €/mois - somme: - - cotisations - - TFC - - contribution formation professionnelle - références: - Imposition du micro-entrepreneur: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23267 - -dirigeant . auto-entrepreneur . cotisations et contributions . TFC: - titre: Taxes pour frais de chambre - unité: €/mois - note: | - Nous n'avons pas intégré les exceptions suivantes : - - - Artisans en double immatriculation CCI-CMA - - Les taux de l'Alsace et de la Moselle - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32847 - somme: - - commerce - - métiers - -dirigeant . auto-entrepreneur . cotisations et contributions . TFC . commerce: - titre: taxe pour frais de chambre de commerce - unité: €/mois - applicable si: entreprise . activité = 'commerciale ou industrielle' - produit: - composantes: - - assiette: entreprise . chiffre d'affaires . service BNC - taux: 0.044% - - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - taux: 0.015% - -dirigeant . auto-entrepreneur . cotisations et contributions . TFC . métiers: - unité: €/mois - titre: taxe pour frais de chambre des métiers - applicable si: entreprise . activité = 'artisanale' - produit: - composantes: - - assiette: entreprise . chiffre d'affaires . service BNC - taux: - nom: taux service - valeur: 0.48% - - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - taux: - nom: taux vente - valeur: 0.22% - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32847 - -dirigeant . auto-entrepreneur . cotisations et contributions . TFC . métiers . taux Alsace: - remplace: - - règle: taux service - par: 0.65% - - règle: taux vente - par: 0.29% - une de ces conditions: - - établissement . localisation . département = 'Bas-Rhin' - - établissement . localisation . département = 'Haut-Rhin' - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32847 - -dirigeant . auto-entrepreneur . cotisations et contributions . TFC . métiers . taux Moselle: - remplace: - - règle: taux service - par: 0.83% - - règle: taux vente - par: 0.37% - valeur: établissement . localisation . département = 'Moselle' - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32847 - -dirigeant . auto-entrepreneur . cotisations et contributions . contribution formation professionnelle: - titre: Contribution à la formation professionnelle - description: | - En plus des charges sociales, les auto-entrepreneurs sont redevables d’une - contribution à la formation professionnelle leur permettant de bénéficier du - droit à la formation professionnelle (à condition d’avoir déclaré un chiffre - d’affaires positif au cours des 12 derniers mois). - acronyme: CFP - unité: €/mois - références: - Article L6331-48 du code du travail: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006072050&idArticle=LEGIARTI000006904325 - autoentrepreneur.urssaf.fr: https://www.autoentrepreneur.urssaf.fr/portail/accueil/sinformer-sur-le-statut/lessentiel-du-statut.html#cout-durant-vie-auto-entreprise - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23459 - shine.fr: https://www.shine.fr/blog/formation-professionnelle-auto-entrepreneur/ - note: | - Les taux implémentés sont ceux prélevés par l'Urssaf. - produit: - composantes: - - attributs: - nom: revenus BIC - assiette: entreprise . chiffre d'affaires . BIC - taux: - variations: - - si: entreprise . activité = 'artisanale' - alors: 0.3% - - sinon: 0.1% - - attributs: - nom: revenus BNC - assiette: entreprise . chiffre d'affaires . service BNC - taux: - variations: - - si: entreprise . activité . libérale réglementée - alors: 0.2% - - sinon: 0.1% - -dirigeant . auto-entrepreneur . cotisations et contributions . cotisations: - - description: | - Les cotisations sociales donnent à l'auto-entrepreneur accès à une - protection sociale minimale : une retraite, des soins de santé, des - allocations familiales, etc. - - L'auto-entreprise est un régime simplifié : plutôt qu'une fiche de paie - complexe, toutes les cotisations sont regroupées dans un *forfait* dont le - taux dépend de la catégorie d'activité. - produit: - composantes: - - assiette: entreprise . chiffre d'affaires . service - taux: - nom: taux prestation de service - valeur: 22% - - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - taux: - nom: taux vente restauration hébergement - valeur: 12.8% - - références: - guide urssaf (PDF): https://www.autoentrepreneur.urssaf.fr/portail/files/Guides/Metropole/Presentation_AE.pdf - La protection sociale du micro-entrepreneur: https://bpifrance-creation.fr/encyclopedie/micro-entreprise-regime-auto-entrepreneur/fiscal-social-comptable/protection-sociale - economie.gouv.fr: https://www.economie.gouv.fr/entreprises/micro-entreprise-auto-entreprise-charges-sociales - actualité urssaf.fr (2019): https://www.autoentrepreneur.urssaf.fr/portail/accueil/sinformer-sur-le-statut/toutes-les-actualites/nouveautes-2019--ce-qui-change-e.html - - -dirigeant . auto-entrepreneur . cotisations et contributions . cotisations . taux ACRE: - titre: taux ACRE auto-entrepreneur - applicable si: entreprise . ACRE - 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% - - 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 - FAQ Urssaf avant 04/2020: https://www.autoentrepreneur.urssaf.fr/portail/accueil/une-question/questions-frequentes.html#quest-ce-qui-change-pour-moi-si - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32318 - -dirigeant . auto-entrepreneur . cotisations et contributions . cotisations . taux ACRE . prestation de service: - remplace: taux prestation de service - titre: taux prestation de service avec ACRE - variations: - - si: - toutes ces conditions: - - entreprise . activité . libérale réglementée - - entreprise . date de création >= 01/04/2020 - alors: 12.10% - - sinon: taux ACRE * taux prestation de service - références: - urssaf.fr: https://www.autoentrepreneur.urssaf.fr/portail/files/Guides/Metropole/Presentation_AE.pdf - -dirigeant . auto-entrepreneur . notification calcul ACRE annuel: - formule: entreprise . ACRE - type: notification - description: | - Le taux ACRE utilisé est celui correspondant au mois courant. Le - simulateur ne prends pas encore en compte le chevauchement de 2 période - d'acre sur une meme année. - -dirigeant . auto-entrepreneur . impôt: oui -dirigeant . auto-entrepreneur . impôt . revenu imposable: - titre: revenu imposable auto-entrepreneur - 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). - - valeur: entreprise . chiffre d'affaires - 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 - - références: - Légifrance: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000006199553&cidTexte=LEGITEXT000006069577 - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23267 - -dirigeant . auto-entrepreneur . impôt . versement libératoire: - rend non applicable: revenu imposable - description: > - Avec l'option pour le versement libératoire, l’impôt sur le revenu est payé en même temps que vos cotisations (par mois ou par trimestre) avec application d’un taux spécifique en fonction de votre activité. - Pour en bénéficier, votre revenu fiscal de référence ne doit pas excéder 27 086 € en 2018 - question: Bénéficiez-vous du versement libératoire de l'impôt sur le revenu ? - par défaut: non - -dirigeant . auto-entrepreneur . impôt . versement libératoire . contrôle seuil: - type: notification - formule: impôt . foyer fiscal . revenu fiscal de référence > 27519 €/an - description: | - Le versement libératoire n'est pas disponible si le revenu fiscal de - référence de votre ménage est supérieur à 27 519 € par part en 2018 - -dirigeant . auto-entrepreneur . impôt . versement libératoire . montant: - titre: versement libératoire auto-entrepreneur - description: | - Si vous avez opté pour le versement libératoire, l’impôt sur le revenu est - payé en même temps que vos cotisations (par mois ou par trimestre) avec - application d’un taux spécifique en fonction de votre activité - produit: - composantes: - - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - taux: 1% - - assiette: entreprise . chiffre d'affaires . service BIC - taux: 1.7% - - assiette: entreprise . chiffre d'affaires . service BNC - taux: 2.2% - -dirigeant . auto-entrepreneur . net après impôt: - titre: revenu net après impôt - identifiant court: auto-entrepreneur-net-apres-impot - résumé: Avant déduction des dépenses liées à l'activité - unité: €/an - remplace: rémunération . nette après impôt - arrondi: oui - question: Quel est le revenu net après impôt souhaité ? - description: >- - Le revenu net de l'auto-entrepreneur après déduction de l'impôt - sur le revenu et des cotisations sociales. - - - **Attention :** Pour bien évaluer la rentabilité de l'entreprise, il ne faut pas - oublier de retrancher à ce montant les dépenses engagées dans le cadre de - l'activité. Cela peut inclure par exemple : - - - L'achat des matière premières - - - L'achat des outils / materiel - - - L'abonnement à des services payants - - - La location d'un local - - - etc... - valeur: net de cotisations - rémunération . impôt - -dirigeant . auto-entrepreneur . chiffre d'affaires: - question: Quel est votre chiffre d'affaires ? - résumé: Montant total des recettes (hors taxe) - remplace: entreprise . chiffre d'affaires - inversion numérique: - avec: - - rémunération . totale - - net après impôt - - net de cotisations - -dirigeant . indépendant: - 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 - unité: €/an - arrondi: oui - résoudre la référence circulaire: oui - variations: - - si: entreprise . imposition = 'IS' - alors: - somme: - - rémunération . nette - - cotisations et contributions . non déductibles - - sinon: entreprise . résultat fiscal - - -dirigeant . indépendant . assiette des cotisations: - unité: €/an - description: Il s'agit de l'assiette des cotisations sociales, nombre forcément positif - valeur: - nom: sans plancher - somme: - - revenu professionnel - - contrats madelin . part déductible fiscalement - plancher: 0 - -dirigeant . indépendant . conjoint collaborateur: - question: Avez-vous un conjoint collaborateur ? - description: | - Permet au conjoint du dirigeant d'être couvert par la protection sociale moyennant le paiement de cotisations sociales supplémentaires. - Pour en bénéficier, l'époux(se) ou partenaire de Pacs du dirigeant doit: - - exercer une activité professionnelle régulière et habituelle dans l'entreprise - - faire l'objet d'une mention au RCS pour les commerçants ou au répertoire des métiers (RM) pour les artisans - - ne pas être rémunéré - - ne pas être associé de la société. - par défaut: non - références: - secu-independants.fr: https://www.secu-independants.fr/cotisations/conjoint/conjoint-collaborateur/?reg=lorraine&pro=artisan&act=retraite&ae=non#c46535 - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F33429 - -dirigeant . indépendant . cotisations et contributions . non déductibles: - titre: Cotisations et contributions non déductibles fiscalement - somme: - - CSG et CRDS . non déductible - - contrats madelin . part non-déductible fiscalement - -dirigeant . indépendant . cotisations et contributions . exonérations . ACRE: - applicable si: entreprise . ACRE - formule: - produit: - assiette: - somme: - - maladie - - retraite de base - - indemnités journalières maladie - - invalidité et décès - - allocations familiales - taux: taux - facteur: prorata sur l'année - arrondi: oui - références: - Fiche secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-cotisations/acre/ - -dirigeant . indépendant . cotisations et contributions . PSS proratisé: - titre: plafond de la sécurité sociale proratisé - description: | - Le plafond de la sécurité sociale, proratisé par la durée d'activité pendant l'année (dans le cas d'activité crée ou cessée en cours d'année). - - Utile pour calculer les cotisations forfaitaires de début d'activité ou le montant de l'ACRE - formule: - unité: €/an - produit: - assiette: plafond sécurité sociale temps plein - taux: - valeur: entreprise . durée d'activité . en fin d'année / 1 an - plafond: 100% - arrondi: oui - -dirigeant . indépendant . cotisations et contributions . exonérations . ACRE . prorata sur l'année: - description: | - Comme le calcul des cotisations indépendants s'effectue sur l'année entière, - l'exonération est proratisée en fonction de la durée effective de l'ACRE sur l'année courante. - - Par exemple, pour une entreprise crée le 1 fevrier 2018, le calcul du prorata pour les - cotisations 2019 sera le suivant : - - `31 jours d'acre restant en 2019 / 365 jours = 8,5%` - - formule: (1 an - entreprise . durée d'activité . en début d'année) / 1 an - -dirigeant . indépendant . cotisations et contributions . exonérations . ACRE . taux: - formule: - taux progressif: - assiette: assiette des cotisations - multiplicateur: PSS proratisé - tranches: - - taux: 100% - plafond: 75% - - taux: 0% - plafond: 100% - -dirigeant . indépendant . conjoint collaborateur . assiette: - question: Sur quelle base le conjoint cotise-t'il ? - description: | - Le conjoint collaborateur dispose de trois choix d’assiette pour le calcul de ces cotisations : - - 1/3 du Plafond de Sécurité Sociale - - Option sur le revenu du chef avec partage ( ½ ou 1/3) - - Option sur le revenu du chef sans partage ( ½ ou 1/3) - par défaut: "'forfaitaire'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - forfaitaire - - revenu sans partage - - revenu avec partage -dirigeant . indépendant . conjoint collaborateur . assiette . forfaitaire: - titre: assiette forfaitaire - description: | - Le conjoint collaborateur paiera des cotisations équivalentes à un revenu - professionnel forfaitaire, fixé à 1/3 du plafond de la sécurité sociale, - à l’exception de la cotisation indemnités journalières qui est calculée sur - une assiette équivalente à 40% du PASS. - formule: assiette = 'forfaitaire' - -dirigeant . indépendant . conjoint collaborateur . assiette . revenu avec partage: - description: | - Le conjoint collaborateur et le gérant paieront des cotisations sociales chacun sur une part du revenu professionnel. - **Cette option baisse le montant des cotisations à payer pour le gérant, mais elle diminue également ses contreparties sociales (pension de retraite, indemnité décès, etc)** - formule: assiette = 'revenu avec partage' - remplace: - règle: assiette des cotisations - par: assiette des cotisations - cotisations . assiette - dans: - - cotisations et contributions . retraite de base - - cotisations et contributions . retraite complémentaire - - cotisations et contributions . invalidité et décès -dirigeant . indépendant . conjoint collaborateur . assiette . revenu sans partage: - description: Le conjoint collaborateur paiera des cotisations sociales calculées sur une base d'un pourcentage du assiette des cotisations du gérant de l'entreprise (un tiers ou la moitié). - formule: assiette = 'revenu sans partage' - -dirigeant . indépendant . conjoint collaborateur . assiette . pourcentage: - question: À quelle proportion du revenu le conjoint cotise-t'il ? - par défaut: "'tiers'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - tiers - - moitié - -dirigeant . indépendant . conjoint collaborateur . assiette . pourcentage . tiers: - formule: pourcentage = 'tiers' - titre: '1/3' - -dirigeant . indépendant . conjoint collaborateur . assiette . pourcentage . moitié: - formule: pourcentage = 'moitié' - titre: '1/2' - -dirigeant . indépendant . conjoint collaborateur . cotisations . assiette: - titre: assiette conjoint collaborateur - formule: - produit: - assiette: assiette des cotisations - taux: 1 / 3 - variations: - - si: assiette . forfaitaire - alors: - assiette: plafond sécurité sociale temps plein - - si: assiette . pourcentage . moitié - alors: - taux: 50% - - sinon: rien - -dirigeant . indépendant . conjoint collaborateur . cotisations: - titre: Cotisations conjoint collaborateur - formule: - somme: - - retraite de base - - retraite complémentaire - - invalidité et décès - - indemnités journalières maladie - -dirigeant . indépendant . conjoint collaborateur . cotisations . assiette retraite: - le maximum de: - - cotisations . assiette - - 5.25% * plafond sécurité sociale temps plein - - 200 heures/an * SMIC horaire - unité: €/an - arrondi: oui - -dirigeant . indépendant . conjoint collaborateur . cotisations . retraite de base: - unité: €/an - barème: - assiette: assiette retraite - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: 17.75% - plafond: 1 - - taux: 0.6% - arrondi: oui - -dirigeant . indépendant . conjoint collaborateur . cotisations . retraite complémentaire: - unité: €/an - barème: - assiette: retraite complémentaire . assiette - tranches: - - taux: 7% - plafond: cotisations et contributions . retraite complémentaire . plafond - - taux: 8% - plafond: 4 * plafond sécurité sociale temps plein - arrondi: oui - -dirigeant . indépendant . conjoint collaborateur . cotisations . retraite complémentaire . assiette: - titre: assiette retraite complémentaire - formule: - le minimum de: - - variations: - - si: entreprise . activité = 'artisanale' - alors: 4 * plafond sécurité sociale temps plein - - sinon: 3 * plafond sécurité sociale temps plein - - assiette retraite - -dirigeant . indépendant . conjoint collaborateur . cotisations . invalidité et décès . assiette: - titre: assiette invalidité et décès - formule: - le maximum de: - - cotisations . assiette - - 20% * plafond sécurité sociale temps plein -dirigeant . indépendant . conjoint collaborateur . cotisations . invalidité et décès: - unité: €/an - produit: - assiette: assiette - taux: 1.3% - plafond: plafond sécurité sociale temps plein - arrondi: oui - -dirigeant . indépendant . conjoint collaborateur . cotisations . indemnités journalières maladie: - unité: €/an - produit: - assiette: 40% * plafond sécurité sociale temps plein - taux: cotisations et contributions . indemnités journalières maladie . taux - arrondi: oui - -dirigeant . indépendant . cotisations et contributions . cotisations: - références: - assiettes et taux: https://www.secu-independants.fr/baremes/cotisations-et-contributions - formule: - somme: - - maladie - - retraite de base - - retraite complémentaire - - indemnités journalières maladie - - invalidité et décès - - allocations familiales - - PCV - - (- exonérations) - -dirigeant . indépendant . cotisations et contributions: - description: | - C'est le montant total dû par l'indépendant au titre des cotisations et - contributions obligatoires ainsi qu'au titre de ses cotisations facultatives - telles que les contrats Madelin. - - Ce montant inclut la réduction de cotisation "covid" en 2020. - - somme: - - cotisations et contributions . cotisations - - conjoint collaborateur . cotisations - - contrats madelin . montant - - CSG et CRDS - - contributions spéciales - - formation professionnelle - note: | - À la différence des cotisations, les contributions ne sont pas réintroduites - pour le calcul de la CSG/CRDS. Elles ne bénéficient pas non plus de la - réduction ACRE. - -dirigeant . indépendant . assiette minimale: - non applicable si: situation personnelle . RSA - valeur: oui - description: | - Si le revenu du chef d'entreprise est déficitaire ou inférieur aux bases de calcul, certaines cotisations seront portées à un montant minimum. - Les cotisations pour les indemnités journalières, retraite de base, invalidité-décès et pour la formation ne sont plus calculées selon le revenu du chef d'entreprise mais selon une "assiette" (montant retenu qui sert de base au calcul d'un impôt ou d'une taxe). - - Les cotisations minimales ne s'appliquent pas si vous bénéficiez du RSA ou de la prime d’activité. - références: - cotisations minimales: https://www.secu-independants.fr/cotisations/calcul-cotisations/cotisations-minimales/ - -dirigeant . indépendant . assiette minimale . maladie: - titre: assiette minimale maladie - description: | - Si le revenu du chef d'entreprise est déficitaire ou inférieur aux bases de calcul, certaines cotisations seront portées à un montant minimum. - produit: - assiette: plafond sécurité sociale temps plein - taux: 40% - références: - cotisations minimales: https://www.secu-independants.fr/cotisations/calcul-cotisations/cotisations-minimales/ - - -dirigeant . indépendant . assiette minimale . retraite: - titre: assiette minimale retraite - description: La cotisation minimale de retraite de base permet de valider 3 trimestres de retraite, quel que soit le revenu. - produit: - assiette: plafond sécurité sociale temps plein - taux: 11.5% - références: - cotisations minimales: https://www.secu-independants.fr/cotisations/calcul-cotisations/cotisations-minimales/ - - - -dirigeant . indépendant . cotisations et contributions . contributions spéciales: - description: | - Certains régimes spéciaux peuvent ajouter des contributions additionnelles - (par exemple, la CURPS pour les CPAM) - formule: non - -dirigeant . indépendant . cotisations et contributions . PCV: - titre: Prestations complémentaires vieillesse - acronyme: PCV - formule: non - description: | - Certaines catégories professionnelles bénéficient de - prestations complémentaires vieillesse (PCV), auparavant nommées « avantage - social vieillesse » (ASV). Cela concerne les médecins généralistes, les - chirurgiens-dentistes, les sages-femmes, les auxiliaires médicaux et les - directeurs de laboratoires. Ce régime résulte de la prise en charge - partielle par l’Assurance maladie de leurs cotisations d’assurance - vieillesse sous réserve qu’ils aient exercé leur activité dans le cadre - conventionnel. - -dirigeant . indépendant . cotisations et contributions . déduction tabac: - applicable si: entreprise . activité . débit de tabac - question: Quel est le montant des revenus issus de la vente de tabac que vous souhaitez exonérer de cotisation vieillesse ? - description: | - Si vous exercez une activité de débit de tabac simultanément à une activité commerciale, vous avez la possibilité d’opter pour le calcul de votre cotisation d’assurance vieillesse sur le seul revenu tiré de votre activité commerciale (en effet, les remises pour débit de tabac sont soumises par ailleurs à un prélèvement vieillesse particulier). Nous attirons cependant votre attention sur le fait qu’en cotisant sur une base moins importante, excluant les revenus de débit de tabac, vos droits à retraite pour l’assurance vieillesse des commerçants en seront diminués. - par défaut: 0 €/an - -dirigeant . indépendant . cotisations et contributions . déduction tabac . revenus déduits: - titre: assiette des cotisations (avec déduction tabac) - applicable si: déduction tabac - remplace: - règle: assiette des cotisations - dans: - - retraite de base - - retraite complémentaire - - invalidité et décès - - conjoint collaborateur - formule: - valeur: assiette des cotisations - abattement: déduction tabac - -dirigeant . indépendant . contrats madelin: - titre: Contrats Madelin - question: Avez-vous souscrit à des contrats de complémentaire privée dits "contrats Madelin" - par défaut: non - références: - economie.gouv.fr: https://www.economie.gouv.fr/particuliers/reduction-impot-revenu-investissements-entreprise-pme-madelin - -dirigeant . indépendant . contrats madelin . montant: - titre: Somme des cotisations à contrats Madelin - formule: - somme: - - mutuelle - - retraite - - -dirigeant . indépendant . contrats madelin . part déductible fiscalement: - titre: Part de la cotisation à contrat Madelin qui est déductible fiscalement - formule: - somme: - - valeur: mutuelle - plafond: mutuelle . plafond - - valeur: retraite - plafond: retraite . plafond - -dirigeant . indépendant . contrats madelin . part non-déductible fiscalement: - titre: Part de la cotisation à contrat Madelin qui n'est pas déductible fiscalement - formule: montant - part déductible fiscalement - -dirigeant . indépendant . contrats madelin . mutuelle: - titre: Souscription à un contrat de mutuelle Madelin - question: Quel est le montant que vous versez à un contrat de mutuelle Madelin ? - description: | - Si vous cotisez au titre d'un contrat de mutuelle de type loi Madelin, - vous pouvez déduire une partie de ces cotisations des bénéfices - imposables que vous déclarez pour votre activité non salariée. - références: - Fiche impôts: https://www.impots.gouv.fr/portail/particulier/questions/je-cotise-un-contrat-madelin-quel-est-mon-avantage-fiscal - Bofip (contrats d'assurance de groupe): https://bofip.impots.gouv.fr/bofip/4639-PGP.html - Article de loi: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029042287&cidTexte=LEGITEXT000006069577&dateTexte=20140530&fastReqId=1900907951&nbResultRech=1 - par défaut: 50 €/mois - -dirigeant . indépendant . contrats madelin . mutuelle . plafond: - unité: €/an - formule: - somme: - - produit: - assiette: revenu professionnel - taux: 3.75% - - produit: - assiette: plafond sécurité sociale temps plein - taux: 7% - plafond: - produit: - assiette: 8 * plafond sécurité sociale temps plein - taux: 3% - références: - Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029042287&cidTexte=LEGITEXT000006069577&dateTexte=20140530 - Réassurez-moi: https://reassurez-moi.fr/guide/pro/tns/plafond#le_plafond_de_deduction_madelin_pour_une_mutuelle_santenbsp - note: | - Normalement c'est le résultat fiscal qui devrait être utilisé pour l'assiette du plafond, mais on utilise le revenu professionnel pour éviter un cycle. -dirigeant . indépendant . contrats madelin . retraite: - titre: Souscription à une retraite Madelin - question: Quel est le montant que vous versez à votre contrat Madelin retraite ? - description: | - Si vous cotisez au titre d'un contrat retraite de type loi Madelin, - vous pouvez déduire une partie de ces cotisations des bénéfices - imposables que vous déclarez pour votre activité non salariée. - références: - Fiche impôts: https://www.impots.gouv.fr/portail/particulier/questions/je-cotise-un-contrat-madelin-quel-est-mon-avantage-fiscal - Bofip (contrats d'assurance de groupe): https://bofip.impots.gouv.fr/bofip/4639-PGP.html - Article de loi: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029042287&cidTexte=LEGITEXT000006069577&dateTexte=20140530&fastReqId=1900907951&nbResultRech=1 - par défaut: 0 €/an - -dirigeant . indépendant . contrats madelin . retraite . plafond: - unité: €/an - formule: - le maximum de: - - barème: - assiette: revenu professionnel - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: 10% - plafond: 1 - - taux: 25% - plafond: 8 - - produit: - assiette: plafond sécurité sociale temps plein - taux: 10% - références: - Bofip: https://bofip.impots.gouv.fr/bofip/1124-PGP.html - LegiFiscal: https://www.legifiscal.fr/impots-personnels/impot-revenu/deduction-des-contrats-madelin-retraite.html - note: | - Normalement c'est le résultat fiscal qui devrait être utilisé pour l'assiette du plafond, mais on utilise le revenu professionnel pour éviter un cycle. - -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 - n’étant pas connus**, les cotisations et contributions des deux premières - années sont calculées sur une **base forfaitaire**. - - - Cette base s’élève à **19 % du plafond annuel de la Sécurité sociale** au titre de - la première et de la deuxième année d’activité (à l’exception de la cotisation Maladie - et indemnités journalières pour lesquelles l’assiette forfaitaire est égale à 40% du - plafond annuel de la Sécurité sociale). - - - 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**. - - - Ce simulateur calcule les cotisations dites définitives sur la base des revenus réels de votre - activité. Il vous permet donc de pouvoir anticiper le montant de cette régularisation et de - **planifier votre trésorerie** en conséquence. - - applicable si: entreprise . date de création >= 01/01/2020 - unité: €/an - recalcul: - règle: cotisations et contributions - avec: - assiette des cotisations: assiette forfaitaire - assiette des cotisations . sans plancher: assiette forfaitaire - situation personnelle . RSA: non - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/independant/mes-cotisations/les-etapes-de-calcul/le-mode-de-calcul/lajustement-et-la-regularisation.html - - -dirigeant . indépendant . cotisations et contributions . début activité . assiette forfaitaire: - produit: - assiette: PSS proratisé - taux: 19% - unité: €/an - arrondi: oui - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/independant/mes-cotisations/les-etapes-de-calcul/le-mode-de-calcul/les-cotisations-provisionnelles/debut-dactivite.html - - - -dirigeant . indépendant . cotisations et contributions . indemnités journalières maladie: - synonyme: maladie 2 - description: | - Cotisations pour les indemnités journalières des indépendants. Si l'état de - santé des artisans, commerçants, industriels et conjoints collaborateurs - nécessite un arrêt de travail, une part de leur ancien revenu leur sera - versé. - produit: - assiette: - valeur: assiette des cotisations - plancher: assiette minimale . maladie - plafond: 5 * plafond sécurité sociale temps plein - taux: - nom: taux - valeur: 0.85% - arrondi: oui - références: - Cotisation minimale: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/ - Taux de cotisations: https://www.secu-independants.fr/cotisations/calcul-cotisations/taux-de-cotisations/ - -dirigeant . indépendant . cotisations et contributions . maladie: - barème: - assiette: - valeur: assiette des cotisations - plancher: assiette minimale . maladie - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: taux progressif - plafond: 110% - - taux: 6.35% - plafond: 5 - - taux: 6.5% - arrondi: oui - références: - décret formule de calcul: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036342439&categorieLien=id - taux de cotisations: https://www.secu-independants.fr/cotisations/calcul-cotisations/taux-de-cotisations/ - note: | - On retrouve dans le décret ci-dessous la phrase suivante : - - > I.-Par dérogation au premier alinéa, le taux de la cotisation est fixé à 6,5 % lorsque le revenu d'activité est supérieur à cinq fois la valeur annuelle du plafond de la sécurité sociale déterminée conformément à l'article D. 613-2. - - Le terme "lorsque" laisse entendre qu'en cas de dépassement du seuil 5xPSS, tout le revenu est soumis à 6.5%. Il semblerait qu'une interprétation inverse soit à privilégier : seule la part supérieure à ce seuil est soumise à ce taux, et c'est cette implémentation que nous avons retenue. - - - -dirigeant . indépendant . cotisations et contributions . maladie . taux RSA: - # TODO: Il n'est pour l'instant pas possible de rendre non applicable une - # seule tranche d'un barème dans le mécanisme "taux progressif", ce qui - # éviterait de devoir créer 2 règles séparées avec 2 barèmes distincts. - applicable si: situation personnelle . RSA - remplace: taux progressif - unité: '%' - taux progressif: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - plafond: 40% - taux: 3.16% - - plafond: 110% - taux: 6.35% - note: | - Pour les indépendants au RSA, seule la réduction simple définie dans - le décret de calcul de la cotisation maladie est prise en compte. - La réduction renforcée en-dessous de 40% du plafond de la sécurité - sociale ne l'est pas, car il n'y a pas d'assiette minimale. - - -dirigeant . indépendant . cotisations et contributions . maladie . taux progressif: - taux progressif: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - plafond: 0% - taux: 0% - - plafond: 40% - taux: 3.16% - - plafond: 110% - taux: 6.35% - références: - Taux de cotisations: https://www.secu-independants.fr/cotisations/calcul-cotisations/taux-de-cotisations/ - décret formule de calcul: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036342439&categorieLien=id - -dirigeant . indépendant . cotisations et contributions . retraite de base: - barème: - assiette: - valeur: assiette des cotisations - plancher: assiette minimale . retraite - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: 17.75% - plafond: 1 - - taux: 0.6% - arrondi: oui - références: - Cotisation minimale: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/ - -dirigeant . indépendant . cotisations et contributions . retraite complémentaire: - formule: - barème: - assiette: assiette des cotisations - tranches: - - taux: 7% - plafond: - nom: plafond - acronyme: PRCI - titre: plafond retraite complémentaire des indépendants - valeur: 38493 €/an - - taux: 8% - plafond: 4 * plafond sécurité sociale temps plein - arrondi: oui - - -dirigeant . indépendant . cotisations et contributions . invalidité et décès: - formule: - produit: - assiette: - valeur: assiette des cotisations - plancher: assiette minimale . retraite - plafond: plafond sécurité sociale temps plein - taux: 1.3% - arrondi: oui - références: - Cotisation minimale: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/ - - -dirigeant . indépendant . cotisations et contributions . CSG et CRDS: - formule: - produit: - assiette: assiette - composantes: - - attributs: - nom: non déductible - arrondi: oui - composantes: - - taux: - nom: taux - valeur: 2.9% - - attributs: - nom: revenus de remplacement - assiette: dirigeant . indépendant . IJSS . total - taux: non déductible . taux - - attributs: - nom: déductible - arrondi: oui - composantes: - - taux: - nom: taux - valeur: 6.8% - - attributs: - nom: revenus de remplacement - assiette: dirigeant . indépendant . IJSS . total - taux: 3.8% - - références: - fiche Urssaf: https://www.urssaf.fr/portail/home/indépendant/mes-cotisations/quelles-cotisations/les-contributions-csg-crds/taux-de-la-csg-crds.html - IJSS (amelie.fr): https://www.ameli.fr/assure/remboursements/indemnites-journalieres/arret-maladie - IJSS (service-public.fr): https://www.service-public.fr/particuliers/vosdroits/F2971 - -dirigeant . indépendant . revenus étrangers: - description: | - Les revenus étrangers sont des revenus déclarés par les travailleurs indépendants pour des revenus perçus au titre de l’exercice d’une activité non salariée dans un autre Etat de l’UE, EEE ou en Suisse à l’étranger. - Ces revenus ne sont soumis qu’aux cotisations et sont intégrés à l’assiette sociale. Par contre, ces revenus sont identifiés spécifiquement afin de les déduire de l’assiette de la CSG/CRDS. - Pour savoir si ces revenus sont soumis à l'impôt sur le revenu, référez-vous à la notice explicative sur le site [impots.gouv.fr](https://www.impots.gouv.fr/portail/international-particulier/imposition-des-revenus-de-source-etrangere) - - question: Avez-vous perçu des revenus à l'étranger dans le cadre de votre activité ? - par défaut: non - -dirigeant . indépendant . revenus étrangers . montant: - titre: revenus perçu à l'étranger - question: Quel est leur montant ? - par défaut: 0 €/an - -dirigeant . indépendant . cotisations et contributions . CSG et CRDS . assiette: - note: >- - Seule la partie imposable des IJSS est retranchée de l'assiette de la CSG, - puisque la partie non imposable a déjà été retranchée du revenu net fiscal - fourni - valeur: - somme: - - assiette des cotisations . sans plancher - - cotisations - - conjoint collaborateur . cotisations - abattement: - somme: - - revenus étrangers . montant - - dirigeant . indépendant . IJSS . imposable - plancher: 0 €/mois - -dirigeant . indépendant . cotisations et contributions . formation professionnelle: - produit: - assiette: plafond sécurité sociale temps plein - taux: - variations: - - si: entreprise . activité = 'artisanale' - alors: 0.29% - - si: conjoint collaborateur - alors: 0.34% - - sinon: 0.25% - unité: €/an - arrondi: oui - note: Le taux n'est pas majoré pour les artisans avec conjoint collaborateur - - références: - fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23459 - fiche Urssaf: https://www.urssaf.fr/portail/home/indépendant/mes-cotisations/quelles-cotisations/la-contribution-a-la-formation-p/base-de-calcul-et-taux-de-la-con.html - brève Urssaf pour les artisans: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-indépendant/transfert-du-recouvrement-de-la.html - -dirigeant . indépendant . cotisations et contributions . allocations familiales: - formule: - produit: - assiette: assiette des cotisations - taux: - nom: taux - taux progressif: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - plafond: 110% - taux: 0% - - plafond: 140% - taux: 3.1% - arrondi: oui - -dirigeant . indépendant . cotisations et contributions . exonérations: - formule: - somme: - - ZFU - - ACRE - -dirigeant . indépendant . cotisations et contributions . exonérations . ZFU: - applicable si: établissement . ZFU - produit: - assiette: maladie - taux: taux - # TODO : Le plafond est proratisé en début / fin d'exonération - plafond: - recalcul: - avec: - revenu professionnel: 3042 heures/an * SMIC horaire - arrondi: oui - unité: €/an - -dirigeant . indépendant . cotisations et contributions . exonérations . âge: - question: Bénéficiez-vous du dispositif d'exonération "âge" - description: Ce dispositif a été arrêté en 2015, mais est toujours actif pour les personnes qui en bénéficiait avant son abbrogation. - par défaut: non - applicable si: entreprise . date de création < 01/2016 - rend non applicable: invalidité et décès - -dirigeant . indépendant . cotisations et contributions . exonérations . invalidité: - question: Êtes-vous titulaire d’une pension d’invalidité à titre de travailleur indépendant ? - description: Les personnes titulaires d’une pension d’invalidité versée par un régime des travailleurs non-salariés non agricoles bénéficient d’une exonération totale des cotisations maladie et retraite complémentaire. - par défaut: non - rend non applicable: - - exonérations . ZFU - - maladie - - indemnités journalières maladie - - retraite complémentaire - -dirigeant . indépendant . cotisations et contributions . exonérations . ZFU . taux: - titre: taux exonération ZFU - formule: - taux progressif: - assiette: établissement . ZFU . durée d'implantation en fin d'année - retourne seulement le taux: oui - variations: - - si: entreprise . effectif < 5 - alors: - tranches: - - plafond: 5 ans - taux: 100% - - plafond: 6 ans - taux: 60% - - plafond: 10 ans - taux: 60% - - plafond: 11 ans - taux: 40% - - plafond: 12 ans - taux: 40% - - plafond: 13 ans - taux: 20% - - plafond: 14 ans - taux: 20% - - plafond: 15 ans - taux: 0% - - sinon: - tranches: - - plafond: 5 ans - taux: 100% - - plafond: 6 ans - taux: 60% - - plafond: 7 ans - taux: 40% - - plafond: 8 ans - taux: 20% - - plafond: 9 ans - taux: 0% - -dirigeant . indépendant . cotisations et contributions . maladie domiciliation fiscale étranger: - applicable si: situation personnelle . domiciliation fiscale à l'étranger - titre: Maladie (domiciliation fiscale à l'étranger) - description: En contrepartie de l'exonération de CSG, les cotisants ont un taux maladie plus elevé. Contrairement aux autres assurés commerçants/artisans ils ne bénéficient pas de la réduction du taux de la cotisation maladie en fonction du revenu déclaré. - remplace: maladie - formule: - produit: - assiette: assiette des cotisations - taux: 14.5% - arrondi: oui - -dirigeant . indépendant . IJSS: - titre: indemnités journalières de sécurité sociale - description: >- - En cas de maladie, maternité, ou accident, y compris suite à un arrêt de travail ou un arrêt - pour garde d’enfant en lien avec l’épidémie du Covid-19, le régime général de Sécurité - sociale assure le versement de prestations « en espèces ». - - Ce sont les indemnités journalières de Sécurité sociale (IJSS). - - Les indemnités complémentaires aux indemnités journalières de la Sécurité - sociale versées dans le cadre d’un contrat de prévoyance ne constituent pas - des revenus de remplacement. - - Note: Les prestations d’invalidité versées par les régimes - d’invalidité-décès ne sont pas concernées - - question: Avez-vous perçu des indemnités journalières de maladie, maternité ou paternité au titre de votre activité indépendante ? - par défaut: non - -dirigeant . indépendant . IJSS . total: - titre: indemnités journalières - question: >- - Quel est le montant total brut de toutes vos indemnités journalières ? - description: >- - Indiquez uniquement le montant brut de vos revenus de remplacement, - imposables et non imposables qui figure sur le relevé de prestations fourni - pas votre caisse d'assurance maladie. - - > Les revenus de remplacement sont : l'allocation forfaitaire de repos - maternel, l'indemnité journalière forfaitaire d'interruption d'activité, - l'indemnité de remplacement pour maternité, paternité ou adoption et - l'indemnité journalière maladie. - par défaut: 0 €/an - -dirigeant . indépendant . IJSS . imposable: - titre: indemnités journalières imposable - résumé: Uniquement si vous ne relevez pas du régime micro-fiscal - question: Quel est le montant brut des indemnités journalières imposables perçues? - description: >- - Indiquez uniquement les revenus de remplacement imposables perçus, donc tous - les revenus de remplacement perçus **sauf les indemnités journalières en - lien avec une Affection de Longue Durée (ALD)**. - - - Ces revenus seront déduits de votre assiette des contributions, afin de ne - pas être soumis deux fois à la CSG-CRDS : - - > Les revenus de remplacement sont : l'allocation forfaitaire de repos - maternel, l'indemnité journalière forfaitaire d'interruption d'activité, - l’indemnité de remplacement pour maternité, paternité ou adoption et - l'indemnité journalière maladie. - par défaut: 0 €/an diff --git a/modele-social/règles/déclaration-revenu-indépendant.yaml b/modele-social/règles/déclaration-revenu-indépendant.yaml deleted file mode 100644 index 42c0e9f8f..000000000 --- a/modele-social/règles/déclaration-revenu-indépendant.yaml +++ /dev/null @@ -1,449 +0,0 @@ -# AIDE A LA DECLARATION DES INDEPENDANTS - -aide déclaration revenu indépendant 2020: - description: >- - Ces règles calculent les montants demandés dans la déclaration de revenu des - indépendants de 2021 sur les revenus 2020. - formule: non - remplace: - règle: entreprise . ACRE - par: ACRE - - -aide déclaration revenu indépendant 2020 . nature de l'activité: - remplace: entreprise . activité - question: Quelle est la nature de votre activité ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - artisanale - - commerciale ou industrielle - - libérale - références: - Vérifier la nature de son activité: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/verifier-nature-son-activite - Comment déterminer la nature de l'activité d'une entreprise ?: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32887 - - - -aide déclaration revenu indépendant 2020 . ACRE: - description: >- - L'aide à la création ou à la reprise d'une entreprise (Acre) consiste en une - exonération partielle de charges sociales, dite exonération de début - d'activité pendant 12 mois. - - - Elle est automatique pour les sociétés et les entreprises individuelles - (sous certaines conditions, comme par exemple ne pas en avoir bénéficié les - trois dernières années). - - - De plus, pour les travailleurs indépendants classique il est nécessaire de - respecter la condition d’être considéré comme créateur au sens de [l’article - R131-3 du Code de Sécurité Sociale](https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000034727582&cidTexte=LEGITEXT000006073189&dateTexte=20170511). - - - ### Détails - - - Cette exonération porte sur l’ensemble des cotisations à l’exception de la - cotisation retraite complémentaire et les contributions CSG/CRDS et CFP. - - - Cette exonération peut être totale, partielle ou nulle en fonction des revenus déclarés : - - - - Si le revenu est inférieur à 75% du PASS l’exonération est totale. - - - Si le revenu est compris entre 75% et 100% du PASS l’exonération est dégressive. - - - Si le revenu est supérieur à 100% du PASS l’exonération est nulle. - - question: Votre entreprise bénéficie-t-elle de l'ACRE ? - applicable si: entreprise . durée d'activité . en début d'année <= 1 an - par défaut: non - -aide déclaration revenu indépendant 2020 . nature de l'activité . libérale: - rend non applicable: dirigeant . indépendant . PL . CIPAV - remplace: entreprise . activité . libérale - formule: nature de l'activité = 'libérale' - titre: Libérale rattachée au régime général - description: | - Ce sont les professions "intellectuelles", qui ne sont rattachée à aucune - caisse spécifique de retraite. - - C'est le cas de toutes les professions libérale non réglementées depuis le - 1er janvier 2020. - - références: - fiche Wikipedia: https://fr.m.wikipedia.org/wiki/Profession_libérale - -aide déclaration revenu indépendant 2020 . nature de l'activité . commerciale ou industrielle: - remplace: entreprise . activité . commerciale ou industrielle - formule: nature de l'activité = 'commerciale ou industrielle' - description: | - ### Activité commerciale - - Achats de biens pour leur revente en l'état (commerce en gros ou de détail) - - Vente de prestations de services commerciales (location de matériel, transport, agence immobilière, hôtellerie-restauration, entreprise de spectacles, activité de sécurité privée, location, etc.) - - ### Activité industrielle - - Activité de production ou de transformation grâce à l'utilisation d'outils industriels, extraction, industries minières, manutention, magasinage et stockage - -aide déclaration revenu indépendant 2020 . nature de l'activité . artisanale: - formule: nature de l'activité = 'artisanale' - remplace: entreprise . activité . artisanale - description: | - C'est une activité de service, de production, de transformation, ou de réparation exercée par un professionnel qualifié, et qui nécessite des compétences et un savoir-faire spécifiques. - - > Par exemple : les travaux, les activités liées au bâtiment, la réparation de produits fournis par le client, les coiffeurs... - - - L'entreprise ne doit pas employer plus de 10 salariés (l'activité devient commerciale au-delà) - - Les activités artisanales sont répertoriées par un décret - références: - liste des activités artisanales: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/activites-artisanales-0 - -aide déclaration revenu indépendant 2020 . plafond sécurité sociale 2020: - remplace: plafond sécurité sociale temps plein - formule: 3428 €/mois - -aide déclaration revenu indépendant 2020 . SMIC 2020: - remplace: SMIC horaire - formule: 10.15 €/heure -aide déclaration revenu indépendant 2020 . PRCI: - remplace: dirigeant . indépendant . cotisations et contributions . retraite complémentaire . plafond - formule: 38340 €/an - -aide déclaration revenu indépendant 2020 . période: - formule: oui - remplace: - - règle: période . début d'année - par: 01/01/2020 - - règle: période . fin d'année - par: 31/12/2020 - -aide déclaration revenu indépendant 2020 . réduction covid: - titre: Réduction de cotisation Covid - remplace: - - règle: dirigeant . indépendant . cotisations et contributions . cotisations - par: - valeur: dirigeant . indépendant . cotisations et contributions . cotisations - abattement: réduction covid . part cotisations - - règle: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . non déductible - par: - valeur: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . non déductible - abattement: réduction covid . part CSG . non déductible - - règle: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . déductible - par: - valeur: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . déductible - abattement: réduction covid . part CSG . déductible - description: | - Dans le cadre de la crise sanitaire, le Gouvernement a mis en œuvre plusieurs mesures exceptionnelles concernant les cotisations et contributions sociales des travailleurs indépendants affectés par la crise du coronavirus avec : - - - un premier dispositif de réduction des cotisations et contributions sociales prévu par la 3ème loi de finance rectificative (LFR3) pour 2020(1) dans le cadre de la première période d’état d’urgence sanitaire du printemps 2020, - - un second dispositif de réduction prévu par la loi de financement de la sécurité sociale (LFSS) pour 2021 (2) dans le cadre de la seconde période d’état d’urgence sanitaire de l’automne 2020. - - références: - (1) Décret n° 2020-1103 du 1er septembre 2020: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000042297236/ - (2) Décret 2021-75 du 27 janvier 2021: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000043070140 - - question: Remplissez-vous les conditions pour bénéficier de la réduction de cotisations sociales liées à la crise du Covid-19 ? - par défaut: non - - -aide déclaration revenu indépendant 2020 . réduction covid . montant: - titre: Réduction Covid - formule: - somme: - - printemps 2020 - - automne 2020 -aide déclaration revenu indépendant 2020 . réduction covid . conjoint collaborateur: - applicable si: dirigeant . indépendant . conjoint collaborateur - remplace: - règle: dirigeant . indépendant . conjoint collaborateur . cotisations - par: - valeur: dirigeant . indépendant . conjoint collaborateur . cotisations - abattement: réduction covid . conjoint collaborateur - valeur: montant - -aide déclaration revenu indépendant 2020 . réduction covid . total: - titre: Réduction Covid - description: Intégrée dans le montant des cotisations affiché ci-dessus - somme: - - montant - - conjoint collaborateur - - - -aide déclaration revenu indépendant 2020 . réduction covid . part cotisations: - titre: Part réduction Covid sur cotisations (hors CSG/CRDS) - arrondi: oui - résoudre la référence circulaire: oui - produit: - assiette: montant - taux: pourcentage cotisations - -aide déclaration revenu indépendant 2020 . réduction covid . pourcentage cotisations: - unité: '%' - valeur: dirigeant . indépendant . cotisations et contributions . cotisations / (dirigeant . indépendant . cotisations et contributions . cotisations + dirigeant . indépendant . cotisations et contributions . CSG et CRDS) - - - -aide déclaration revenu indépendant 2020 . réduction covid . part CSG: - titre: Part réduction Covid sur CSG - valeur: montant - part cotisations - -aide déclaration revenu indépendant 2020 . réduction covid . part CSG . déductible: - titre: Part réduction Covid sur CSG/CRDS déductible - produit: - assiette: part CSG - taux: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . déductible . taux / taux CSG - arrondi: oui - -aide déclaration revenu indépendant 2020 . réduction covid . part CSG . non déductible: - titre: Part réduction Covid sur CSG/CRDS non déductible - valeur: part CSG - part CSG . déductible - -aide déclaration revenu indépendant 2020 . réduction covid . taux CSG: - unité: '%' - valeur: - somme: - - dirigeant . indépendant . cotisations et contributions . CSG et CRDS . non déductible . taux - - dirigeant . indépendant . cotisations et contributions . CSG et CRDS . déductible . taux - -aide déclaration revenu indépendant 2020 . réduction covid . montant . printemps 2020: - applicable si: éligible aide printemps 2020 - formule: - variations: - - si: secteur d'activité = 'S2' - alors: 1800 €/an - - sinon: 2400 €/an - -aide déclaration revenu indépendant 2020 . réduction covid . montant . automne 2020: - applicable si: éligible aide automne 2020 - formule: nombre de mois éligibles * 600 €/an/mois éligibles - -aide déclaration revenu indépendant 2020 . réduction covid . secteur d'activité: - question: | - De quel secteur votre activité principale relève-t'elle ? - # Pas d'inéligibilité explicite des PL réglementés à ce dispositif, mais il - # apparaît que les métiers concernés (médecins, avocats...) sont incompatibles - # avec les secteurs d'activités concernés par l'aide (hôtels, restaurants...) - description: | - Les conditions d’éligibilité aux aides « Covid » dépendent du secteur d’activité - dont relève l’activité principale. - - Les hôtels, restaurants, bars, etc. sont dans - la catégorie dite "S1" et ont le droit aux aides sans autres conditions. - - Les secteurs dont l'activité dépendent de celles du "secteur 1" peuvent - aussi bénéficier des aides à condition d'avoir eu une baisse de chiffre - d'affaires significative pendant le confinement. - - Enfin les secteurs dits "S2" sont ceux impliquant l'accueil du public, et - sont éligibles aux aides à condition d'avoir subi une fermeture - administrative. - - Les modalités sont précisées sur le site de l'Urssaf. - références: - Liste détaillée des secteurs (pdf): https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/liste-secteurs-pour-infographie.pdf - Présentation du dispositif: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-independant/dispositifs-de-reduction-des-cot.html - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - S1 - - S1-bis - - S2 - par défaut: non - -aide déclaration revenu indépendant 2020 . réduction covid . secteur d'activité . S1: - titre: Secteur dit S1 - description: | - Secteurs du tourisme, de l’hôtellerie, de la restauration, du sport, de la culture, du transport aérien et de l’événementiel. - - -aide déclaration revenu indépendant 2020 . réduction covid . secteur d'activité . S1-bis: - titre: Secteur dit S1 bis - description: | - Secteurs dont l’activité dépend de celle des secteurs 1 et qui ont subi une très forte baisse de leur chiffre d’affaires. - -aide déclaration revenu indépendant 2020 . réduction covid . secteur d'activité . S2: - titre: Secteur dit S2 - description: - Autres secteurs d’activité qui ont fait l’objet d’une interdiction affectant de manière prépondérante la poursuite de leur activité. - -aide déclaration revenu indépendant 2020 . réduction covid . éligible aide printemps 2020: - applicable si: secteur d'activité - question: Êtes-vous éligibles au dispositif de réduction prévu au titre de la première vague de la crise sanitaire du printemps 2020 ? - par défaut: non - description: | - Les conditions d’éligibilité à la réduction au titre de la première période d’état d’urgence sanitaire du printemps 2020 dépendent de votre secteur d’activité : - - ### Secteur S1 - - Activité principale exercée relevant des secteurs du tourisme, de l’hôtellerie, de la restauration, du sport, de la culture, du transport aérien et de l’événementiel (Voir liste détaillée des activités relevant du secteur S1). - - Votre activité doit avoir débuté avant le 1er juillet 2020. - - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 15 mars 2020. - - ### Secteur S1 bis - - Activité principale exercée dépendant de celles du secteur S1 (Voir liste détaillée des activités relevant du secteur S1 bis) et ayant subi une forte baisse du chiffre d’affaires à savoir : - - - Soit une baisse de chiffre d’affaires d’au moins 80% durant la période comprise entre le 15 mars et le 15 mai 2020 par rapport à la même période l’année précédente (cas 1) ; - ou, par rapport au chiffre d’affaires mensuel moyen de l’année 2019 ramené sur deux mois (cas 2) ; - ou, pour les entreprises créées après le 15 mars 2019 et avant le 10 mars 2020, par rapport au montant moyen calculé sur deux mois du chiffre d’affaires réalisé entre la date de création de l’entreprise et le 15 mars 2020 (cas 2 bis). - - - Soit à une baisse de chiffre d’affaires durant la période comprise entre le 15 mars et le 15 mai 2020 par rapport à la même période l’année précédente qui représente au moins 30 % du chiffre d’affaires de l’année 2019 (cas 3). - ou, pour les entreprises créées entre le 1er et le 14 mars 2019, une baisse du chiffre d’affaires réalisé entre la date de création de l’entreprise et le 31 décembre 2019 ramené sur 12 mois (cas 3 bis). - - Votre activité doit avoir débuté avant le 1er juillet 2020. - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 15 mars 2020. - - ### Secteur S2 - - Activité principale exercée dans un secteur autre que S1 et S1 bis impliquant l’accueil du public et interrompue en application du décret n° 2020-293 du 23 mars 2020, à l’exclusion des fermetures volontaires. - - Votre activité doit avoir débuté avant le 1er juin 2020. - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 15 mars 2020. - - -aide déclaration revenu indépendant 2020 . réduction covid . éligible aide automne 2020: - applicable si: secteur d'activité - question: Êtes-vous éligibles au dispositif de réduction prévu au titre de la première vague de la crise sanitaire de l'automne 2020 ? - par défaut: non - description: | - Les conditions d’éligibilité à la réduction au titre de la seconde période d’état d’urgence sanitaire de l’automne 2020 s’évaluent mois par mois et dépendent de votre secteur d’activité. - - Vous devez également préciser le nombre de mois où vous remplissez les conditions d’éligibilité. - - ### Secteur dit S1 - Activité principale exercée relevant des secteurs du tourisme, de l’hôtellerie, de la restauration, du sport, de la culture, du transport aérien et de l’événementiel - - Mois concerné | Conditions d'éligibilité - --------------|-------------------------- - Octobre 2020 | Activité exercée dans une zone d’application des mesures de couvre-feu Et avoir fait l’objet d’une mesure d’interdiction d’accueil du public **OU** Activité exercée dans une zone d’application des mesures de couvre-feu Et avoir subi une forte baisse du chiffre d’affaires mensuel(ca) - Novembre 2020 à Mars 2021 | Avoir fait l’objet d’une mesure d’interdiction d’accueil du public **OU** Avoir subi une forte baisse du chiffre d’affaires mensuel(ca) - - _(ca) Condition de baisse de chiffre d'affaire:_ - - _Vous devez avoir subi une baisse d’au moins 50% du chiffre d’affaires mensuel par rapport au même mois de l’année précédente, ou si vous le souhaitez par rapport au chiffre d’affaires mensuel moyen de l’année 2019, ou, pour les entreprises créées en 2020, par rapport au montant mensuel moyen du chiffre d’affaires réalisé entre la date de création de l’entreprise et le 31 août 2020._ - - _Cette condition est également satisfaite lorsque la baisse de chiffre d’affaires mensuel par rapport à la même période de l’année précédente représente au moins 15% du chiffre d’affaires de l’année 2019, ou, pour les entreprises créées en 2019, par rapport au chiffre d’affaires de l’année 2019 ramené sur 12 mois._ - - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 17 octobre 2020. - - Bon à savoir : Les activités de livraison, de retrait de commande ou de vente à emporter ne sont pas prises en compte pour apprécier le respect de la condition d’interdiction d’accueil du public. - - ### Secteur dit S1 bis - Activité principale exercée dépendant de celles du secteur S1 - - Mois concerné | Conditions d'éligibilité - --------------|-------------------------- - Octobre 2020 | Avoir fait l’objet d’une mesure d’interdiction d’accueil du public **OU** Avoir subi une forte baisse du chiffre d’affaires mensuel(ca) - - _(ca) Condition de baisse de chiffre d'affaire:_ - - _Vous devez avoir subi une baisse d’au moins 50% du chiffre d’affaires mensuel par rapport au même mois de l’année précédente, ou si vous le souhaitez par rapport au chiffre d’affaires mensuel moyen de l’année 2019, ou, pour les entreprises créées en 2020, par rapport au montant mensuel moyen du chiffre d’affaires réalisé entre la date de création de l’entreprise et le 31 août 2020._ - - _Cette condition est également satisfaite lorsque la baisse de chiffre d’affaires mensuel par rapport à la même période de l’année précédente représente au moins 15% du chiffre d’affaires de l’année 2019, ou, pour les entreprises créées en 2019, par rapport au chiffre d’affaires de l’année 2019 ramené sur 12 mois._ - - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 17 octobre 2020. - - Bon à savoir : Les activités de livraison, de retrait de commande ou de vente à emporter ne sont pas prises en compte pour apprécier le respect de la condition d’interdiction d’accueil du public. - - ### Secteur dit S2 - Activité principale exercée dans un secteur autre que S1 et S1 bis - - - **Mois concerné** : novembre 2020, février 2021, mars 2021 - - **Conditions d'éligibilité**: Avoir fait l’objet d’une mesure d’interdiction affectant de manière prépondérante la poursuite de l’activité, en application du décret n° 2020-1310 du 29 octobre 2020 - - Si vous avez cessé votre activité, la cessation doit être postérieure ou égale au 30 octobre 2020. - - Bon à savoir : Les activités de livraison, de retrait de commande ou de vente à emporter ne sont pas prises en compte pour apprécier le respect de la condition d’interdiction. - -aide déclaration revenu indépendant 2020 . réduction covid . nombre de mois éligibles: - formule: - somme: - - S1 et S1bis - - S2 - arrondi: oui - -aide déclaration revenu indépendant 2020 . réduction covid . nombre de mois éligibles . S1 et S1bis: - applicable si: - toutes ces conditions: - - éligible aide automne 2020 - - une de ces conditions: - - secteur d'activité = 'S1' - - secteur d'activité = 'S1-bis' - question: | - Précisez le nombre de mois entre octobre 2020* et mars 2021 durant lesquels vous avez subi une interdiction d’accueil du public ou une baisse de 50% de votre chiffre d’affaires - description: | - * Pour octobre 2020, votre activité devait également être située dans une zone d’application des mesures de couvre-feu - unité: mois éligibles - plafond: 6 mois éligibles - -aide déclaration revenu indépendant 2020 . réduction covid . nombre de mois éligibles . S2: - applicable si: - toutes ces conditions: - - éligible aide automne 2020 - - secteur d'activité = 'S2' - question: | - Précisez le nombre de mois (novembre 2020 et/ou février 2021 et/ou mars 2021) durant lesquels vous avez fait l’objet d’une mesure d’interdiction affectant de manière prépondérante la poursuite de votre activité - unité: mois éligibles - plafond: 3 mois éligibles - - -aide déclaration revenu indépendant 2020 . revenu net fiscal: - titre: revenu net fiscal - résumé: '[A]' - description: Résultat avant déduction des charges sociales et exonérations fiscales - valeur: dirigeant . rémunération . totale - -aide déclaration revenu indépendant 2020 . cotisations obligatoires: - titre: Cotisations sociales obligatoires déductibles - résumé: '[C]' - description: | - À reporter dans : - - **la case DSCA/DSCB** dans le formulaire de donnée complémentaire à la déclaration de revenus des indépendant (formulaire 2042) - - **régime réel simplifié :** la rubrique 326 du formulaire 2033-D-SD - - **régime réel normal :** la rubrique A5 du formulaire 2053-SD - - **déclaration contrôlée :** la rubrique BT du formulaire 2035-A-SD - unité: €/an - somme: - - dirigeant . indépendant . cotisations et contributions . cotisations - - dirigeant . indépendant . conjoint collaborateur . cotisations - références: - Notice impots.gouv.fr: https://www.impots.gouv.fr/portail/www2/fichiers/documentation/brochure/ir_2021/pdf_som/11-bis-decla_fusion_fisc_185a195.pdf - -aide déclaration revenu indépendant 2020 . CSG déductible: - titre: CSG déductible - résumé: '[B]' - description: "Montant de la CSG déductible à l'impôt sur le revenu" - valeur: dirigeant . indépendant . cotisations et contributions . CSG et CRDS . déductible - -aide déclaration revenu indépendant 2020 . CFP: - résumé: '[D]' - description: Contribution à la formation professionnelle - valeur: dirigeant . indépendant . cotisations et contributions . formation professionnelle - -aide déclaration revenu indépendant 2020 . total charges sociales déductible: - titre: charges sociales obligatoires déductibles fiscalement - résumé: '[B + C + D]' - somme: - - CSG déductible - - cotisations obligatoires - - CFP - description: | - À reporter dans : - - **régime réel simplifié :** la rubrique 252 du formulaire 2033-B-SD - - **régime réel normal :** la rubrique FZ du formulaire 2052-SD - - **déclaration contrôlée :** la rubrique BK du formulaire 2035-A-SD - - -aide déclaration revenu indépendant 2020 . assiette sociale: - résumé: '[A - (B + C + D)]' - description: Assiette utilisée pour le calcul des cotisations sociales - valeur: dirigeant . indépendant . assiette des cotisations diff --git a/modele-social/règles/entreprise-établissement.yaml b/modele-social/règles/entreprise-établissement.yaml deleted file mode 100644 index e376e6aef..000000000 --- a/modele-social/règles/entreprise-établissement.yaml +++ /dev/null @@ -1,937 +0,0 @@ -entreprise: - valeur: oui - description: | - Le contrat lie une entreprise, identifiée par un code SIREN, et un employé. - -entreprise . date de création: - question: Quelle est votre date de début d'activité ? - par défaut: 01/01/2020 - description: | - La date de début d'activité (ou date de création) est fixée lors de la - déclaration de votre entreprise. - - Vous pouvez [renseigner votre entreprise](/gérer), pour préremplir - automatiquement cette information. - - Si vous n'avez pas le jour exact, le mois suffit en général pour une bonne - approximation. - suggestions: - Début 2021: 01/01/2021 - Début 2020: 01/01/2020 - Fin 2017: 31/12/2017 - type: date - -entreprise . date de création . contrôle date future: - type: notification - sévérité: avertissement - formule: date de création > 01/2025 - description: Nous ne pouvons voir aussi loin dans le futur - -entreprise . date de création . contrôle date passée: - type: notification - sévérité: avertissement - formule: date de création < 01/1900 - description: Il s'agit d'une très vieille entreprise ! Êtes-vous sûr de ne pas vous être trompé dans la saisie ? - -entreprise . durée d'activité: - formule: - durée: - depuis: date de création - -entreprise . durée d'activité . en fin d'année: - titre: durée d'activité à la fin de l'année - formule: - somme: - - durée: - depuis: date de création - jusqu'à: période . fin d'année - - 1 jour # Le mécanisme durée n'inclue pas le dernier jour - -entreprise . durée d'activité . en début d'année: - titre: durée d'activité au début de l'année - formule: - durée: - depuis: date de création - jusqu'à: période . début d'année - -entreprise . chiffre d'affaires: - question: Quel est votre chiffre d'affaires envisagé ? - - résumé: Montant total des recettes brutes (hors taxe) - unité: €/an - somme: - - dirigeant . rémunération . nette après impôt - - dirigeant . rémunération . impôt - - dirigeant . rémunération . cotisations - - charges - - applicable si: entreprise . imposition . IS - somme: - - imposition . IS . résultat net - - imposition . IS . impôt sur les sociétés - plancher: 0€/an - arrondi: oui - identifiant court: CA - - -entreprise . chiffre d'affaires . vente restauration hébergement: - titre: Vente de biens, restauration, hébergement (BIC) - résumé: Chiffre d'affaires hors taxe - question: Quel est le chiffre d'affaires issus de la vente de bien, restauration ou hébergement ? - unité: €/an - variations: - - si: activité . mixte - alors: - produit: - assiette: chiffre d'affaires - taux: activité . mixte . proportions . vente restauration hébergement - - sinon: - applicable si: activité . service ou vente = 'vente' - valeur: chiffre d'affaires - arrondi: oui - plancher: 0€/an - description: | - ### Vente de biens - Il s’agit du chiffre d'affaires de toutes les opérations comportant - transfert de propriété d'un bien corporel, c'est-à-dire un bien ayant une - existence matérielle. - - ### Restauration et hébergement - Il s’agit du chiffre d'affaires de toutes les opérations de restauration - ou hébergement (hors meublé de tourisme classé) - - Ces revenus sont imposable au régime BIC - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32919 - définition vente de bien (impots.gouv): https://www.impots.gouv.fr/portail/professionnel/achatvente-de-biens - - -entreprise . chiffre d'affaires . service BIC: - unité: €/an - plancher: 0€/an - arrondi: oui - résumé: Chiffre d'affaires hors taxe - titre: Prestations de service commerciales ou artisanales (BIC) - question: Quel est le chiffre d'affaires issus de prestations de service commerciales ou artisanales ? - description: | - Il s’agit de toute opération ne comportant pas de transfert de propriété de - biens corporels (c'est-à-dire ayant une existence matérielle), dont - l'activité manuelle joue le principal rôle. - - Pour simplifier on pourrait dire que ce sont toutes les prestations de - services qui nécessite plus qu'un ordinateur pour être effectuées. - - **Exemples** : transports, service à la personne, réparation etc. - variations: - - si: activité . mixte - alors: - produit: - assiette: chiffre d'affaires - taux: activité . mixte . proportions . service BIC - - sinon: - applicable si: activité . service ou vente = 'service' - valeur: chiffre d'affaires - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32919 - - - -entreprise . chiffre d'affaires . service BNC: - titre: Autres prestations de service et activités libérales (BNC) - résumé: Chiffre d'affaires hors taxe - question: Quel est le chiffre d'affaires issus de prestations de service libérale ? - arrondi: oui - plancher: 0€/an - description: | - Ce sont toutes les opérations dont l'activité intellectuelle tient - un rôle essentiel. - - **Exemples** : conseil, accompagnement, traduction, développement, - formation, enseignement, sportif - - Les revenus tirés de ce chiffre d'affaires sont imposable au régime BNC (bénéfices non commerciaux) - variations: - - si: activité . mixte - alors: - produit: - assiette: chiffre d'affaires - taux: activité . mixte . proportions . service BNC - - sinon: - applicable si: activité = 'libérale' - valeur: chiffre d'affaires - - références: - liste des activités libérales: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/liste-professions-liberales - -entreprise . chiffre d'affaires . service: - titre: Chiffre d'affaires de prestation de service - description: | - Il s’agit de toute opération ne comportant pas de transfert de propriété de - biens corporels (c'est-à-dire ayant une existence matérielle) - unité: €/an - somme: - - service BIC - - service BNC - -entreprise . chiffre d'affaires . BIC: - description: | - Le chiffre d'affaires correspondant au revenus imposable au titre des bénéfice industriels et commerciaux (BIC ou micro-BIC). - unité: €/an - somme: - - service BIC - - vente restauration hébergement - -entreprise . chiffre d'affaires . franchise de TVA dépassée: - - description: | - La franchise de TVA est un dispositif qui exonère les entreprises de la - déclaration et du paiement de la TVA. Il s'applique en dessous d'un seuil de - chiffre d'affaire annuel dépendant de l'activité. - - Le professionnel qui relève de ce dispositif facture ses prestations ou ses - ventes en hors taxe, et ne peut pas déduire la TVA de ses achats. - une de ces conditions: - - chiffre d'affaires > seuil vente + seuil service - - vente restauration hébergement > seuil vente - - service > seuil service - note: > - On prend compte ici des seuils majorés (qui s'appliquent si le seuil - "minoré" n'a pas été dépassé en année `n - 2`) - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F21746 - -entreprise . chiffre d'affaires . franchise de TVA dépassée . seuil vente: - variations: - - si: établissement . localisation . outre-mer . Guadeloupe Réunion Martinique - alors: 110000 €/an - - sinon: 94300 €/an - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F21746 - -entreprise . chiffre d'affaires . franchise de TVA dépassée . seuil service: - variations: - - si: établissement . localisation . outre-mer . Guadeloupe Réunion Martinique - alors: 60000 €/an - - si: dirigeant . indépendant . PL . métier = 'avocat' - alors: 44500 € - - sinon: 36500 €/an - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F21746 - -entreprise . chiffre d'affaires . franchise de TVA dépassée . notification: - applicable si: chiffre d'affaires - type: notification - formule: oui - description: | - Le seuil annuel de chiffre d'affaires pour la franchise de TVA est dépassé. [En savoir plus](/documentation/entreprise/chiffre-d'affaires/franchise-de-TVA-dépassée) - - -entreprise . résultat fiscal: - unité: €/an - somme: - - chiffre d'affaires - - (- charges) - - (- charges . dirigeant) - - -entreprise . imposition: - question: À quel régime d'imposition l'entreprise est-elle soumise ? - description: | - Le créateur d'entreprise peut opter pour l'un des deux régimes d'imposition des bénéfices de son activité : - - l'impôt sur le revenu, où les bénéfices sont déclarés sur la déclaration de revenus personnelle et imposés au barème progressif - - l'impôt sur les sociétés, où les bénéfices sont déclarés au nom de la société - 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 . contrats madelin - question: Avez-vous opté pour le régime micro-fiscal ? - description: | - Avec le régime micro fiscal, les charges déductible sont estimées forfaitairement, comme un pourcentage du chiffre d'affaires. - Ce pourcentage dépend du type d'activité. - - Cette option permet de simplifier votre comptabilité, et peut-être avantageuse en terme de revenu 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). - valeur: chiffre d'affaires - 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 - une de ces conditions: - - entreprise . chiffre d'affaires > 176200 €/an - - entreprise . chiffre d'affaires . service > 72500 €/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 - par défaut: 01/01/2020 - -entreprise . exercice . fin: - type: date - par défaut: 31/12/2020 - -entreprise . exercice . durée: - titre: durée de l'exercice - formule: - durée: - depuis: début - jusqu'à: fin - -entreprise . exercice . date trop ancienne: - type: notification - sévérité: avertissement - formule: début < 01/01/2018 - description: La date saisie est trop ancienne. Le simulateur n'intègre pas les barèmes avant 2018. - -entreprise . exercice . date trop éloignée: - type: notification - sévérité: avertissement - formule: fin > 31/12/2022 - description: La date saisie est trop éloignée. Le simulateur n'intègre pas les barèmes au delà de 2022. - -entreprise . exercice . début après la fin: - type: notification - sévérité: avertissement - formule: début >= fin - description: La fin de l'exercice doit être postérieure à son début. - -entreprise . exercice . durée maximale: - type: notification - sévérité: avertissement - 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 - - charges de fonctionnement - titre: charges (hors rémunération dirigeant) - identifiant court: charges - résumé: Toutes les dépenses nécessaires à l'entreprise - question: Quelles sont les charges de l'entreprise ? - description: | - - Ce sont les dépenses de l'entreprise engagées dans l'intérêt de celle-ci, hors rémunération du dirigeant. Pour les sociétés et entreprises hors auto-entrepreneur, ces charges sont dites déductibles du résultat : l'entreprise ne paiera pas de cotisations ou impôt dessus. Pour l'auto-entrepreneur, elles ne sont pas déductibles du chiffre d'affaires encaissé. - - Nous ne traitons pas encore la TVA : les charges sont à renseigner hors taxe (excepté pour les auto-entrepreneurs en franchise de TVA) - - Par exemple, les charges peuvent être : - - - achat de matières premières pour une activité de production - - achat de produits en vue de leur revente, pour une activité commerciale - - frais de repas : le supplément par rapport au coût d'un repas à domicile - - Attention : l'achat d'un ordinateur à 1000€ n'est pas une charge, mais une immobilisation : c'est un bien qui va profiter à l'entreprise pendant plusieurs années. Chaque année, une partie de cette immobilisation est amortie, et cet amortissement déductible peut être intégré dans ce calcul, par exemple 200€ par an pendant 5 ans. - - A l'inverse, un téléphone portable à moins de 500€ peut être assimilé à une charge sans immobilisation. - - références: - Charges déductibles ou non du résultat fiscal d'une entreprise: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31973 - par défaut: 0 €/an - - - -entreprise . charges . dirigeant: - titre: Charges déductibles dirigeant - description: Les montants liés à la rémunération du dirigeant qui sont déductibles d'impôt. - variations: - - si: imposition . IS - alors: dirigeant . rémunération . totale - # Note : le cas de dirigeant AS à l'IR n'est pas géré - # - si: dirigeant . assimilé salarié - # alors: 0€/an - - sinon: # TNS dans entreprise à l'IR - valeur: dirigeant . indépendant . cotisations et contributions - abattement: dirigeant . indépendant . cotisations et contributions . non déductibles - -entreprise . ACRE: - description: >- - L'aide à la création ou à la reprise d'une entreprise (Acre) consiste en une - exonération partielle de charges sociales, dite exonération de début - d'activité pendant 12 mois. - - - Elle est **automatique** pour les **sociétés et les entreprises individuelles** - (sous certaines conditions, comme par exemple ne pas en avoir bénéficié les - trois dernières années). - - - Pour les **auto-entrepreneurs** en revanche, elle doit être demandée et est réservée aux - bénéficiaires suivants: - - - Les demandeurs d'emplois (indemnisés ou non indemnisés mais ayant au moins 6 mois d’inscription à Pôle Emploi au cours des 18 derniers mois). - - - Les bénéficiaires d'aides sociales (RSA, ASS, ATA) - - - Les jeunes entre 18 et 25 ans (jusqu'à 29 ans pour les personnes reconnues en situation de handicap) - - - Les personnes créant une micro-entreprise dans un quartier prioritaire de la ville (QPPV) - - - > *Historique*: - - - Pour les auto-entreprise créées à partir du 1er janvier 2020, l'exonération est - de nouveau soumise à condition. - - - Pour les entreprises créées entre le 1er janvier 2019 et le 31 décembre 2019, la réduction est généralisée à tous les créateurs, sauf si vous avez déjà obtenu l'ACCRE dans les trois années précédentes - - - Pour les entreprises créées avant le 1er janvier 2019, la l'exonération de cotisation s'appelait ACCRE était soumise à conditions et n'était pas automatique : il fallait en faire la demande. - question: Votre entreprise bénéficie-t-elle de l'ACRE ? - applicable si: - une de ces conditions: - - toutes ces conditions: - - dirigeant . auto-entrepreneur - - entreprise . durée d'activité < 3 ans - - entreprise . date de création < 04/2020 - - entreprise . durée d'activité . en début d'année < 1 an - par défaut: ACRE par défaut - note: Les auto-entreprises crées entre le 1er janvier et le 31 décembre 2019 bénéficient d'un dispositif plus favorable, actif pendant 3 années. - -entreprise . ACRE par défaut: - formule: - variations: - - si: - toutes ces conditions: - - dirigeant . auto-entrepreneur - - une de ces conditions: - # Pour l'année 2019, l'acre était automatiquement accordée aux auto-entrepreneur - - entreprise . date de création < 01/01/2019 - - entreprise . date de création > 31/12/2019 - alors: non - - sinon: oui - -entreprise . effectif: - unité: employé - formule: - variations: - - si: entreprise . effectif . seuil = 'moins de 5' - alors: 4 employés - - si: entreprise . effectif . seuil = 'moins de 11' - alors: 10 employés - - si: entreprise . effectif . seuil = 'moins de 20' - alors: 19 employés - - si: entreprise . effectif . seuil = 'moins de 50' - alors: 49 employés - - si: entreprise . effectif . seuil = 'moins de 150' - alors: 149 employés - - si: entreprise . effectif . seuil = 'moins de 250' - alors: 250 employés - - si: entreprise . effectif . seuil = 'plus de 250' - alors: 251 employés - -entreprise . effectif . seuil: - titre: seuil d'effectif - question: Quel est l'effectif de l'entreprise ? - description: > - De nombreuses cotisations patronales varient selon l'effectif de - l'entreprise. - - Le franchissement d'un seuil à la hausse n'est pris en compte que s'il est - atteint ou dépassé pendant 5 années civiles consécutives. - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - moins de 5 - - moins de 11 - - moins de 20 - - moins de 50 - - moins de 150 - - moins de 250 - - plus de 250 - par défaut: "'moins de 5'" - -entreprise . effectif . seuil . moins de 5: -entreprise . effectif . seuil . moins de 11: - titre: entre 5 et 10 -entreprise . effectif . seuil . moins de 20: - titre: entre 11 et 19 -entreprise . effectif . seuil . moins de 50: - titre: entre 20 et 49 -entreprise . effectif . seuil . moins de 150: - titre: entre 50 et 149 -entreprise . effectif . seuil . moins de 250: - titre: entre 150 et 250 -entreprise . effectif . seuil . plus de 250: - titre: 251 et plus - -entreprise . ratio alternants: - question: Quelle est la fraction de contrats d'alternance dans l'effectif moyen de l'entreprise ? - titre: Fraction d'alternants - description: | - Cette fraction détermine la contribution supplémentaire pour l'apprentissage pour les entreprises concernées. - suggestions: - 1%: 1% - 5%: 5% - par défaut: 0% - -entreprise . association non lucrative: - description: L'entreprise est une association non lucrative - question: S'agit-il d'une association à but non lucratif ? - par défaut: non - # L'association a but non lucratif ne paie pas d'IS de droit commun article 206 du Code général des impôts - # -> pas de taxe ni contribution d'apprentissage - rend non applicable: - - contrat salarié . taxe d'apprentissage - -entreprise . exonérée de TVA: - question: L'entreprise est-elle exonérée de TVA (hors franchise de base) ? - par défaut: non - description: | - Certains types d'entreprises ne sont pas assujetties à la TVA. - Ces dernières payent la taxe sur les salaires en contrepartie. - - C'est le cas par exemple des établissements bancaires, financiers ou d'assurance. - references: - Fiche economie.gouv.fr: https://www.economie.gouv.fr/entreprises/taxe-salaires - -entreprise . taxe sur les salaires . montant avant décote: - formule: - valeur: contrat salarié . taxe sur les salaires . barème / 1 employé * effectif - abattement: abattement associations - -entreprise . taxe sur les salaires . abattement associations: - applicable si: entreprise . association non lucrative - formule: 21044 €/an - -entreprise . taxe sur les salaires: - applicable si: - une de ces conditions: - - association non lucrative - - entreprise . exonérée de TVA - description: Lorsque le montant de la taxe sur les salaires est inférieur à 1200 €/an, il - n'y a pas besoin de faire de déclaration et la taxe n'est pas recouvré. - Entre 1200 €/an et 2400 €/an une décote s'applique. - formule: - variations: - - si: montant avant décote <= 1200 €/an - alors: 0 €/an - - si: montant avant décote <= 2400 €/an - alors: montant avant décote - (2400 €/an - montant avant décote * 75%) - - sinon: montant avant décote - références: - Fiche service-public: https://www.service-public.fr/professionnels-entreprises/vosdroits/F22576 - -entreprise . activité: - titre: nature de l'activité - question: Quelle est la nature de votre activité principale ? - description: Votre type d'activité va déterminer une grande partie des calculs de cotisations, contributions et impôt. - par défaut: "'commerciale ou industrielle'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - artisanale - - commerciale ou industrielle - - libérale - références: - Vérifier la nature de son activité: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/verifier-nature-son-activite - Comment déterminer la nature de l'activité d'une entreprise ?: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32887 - Spécifiquement pour les auto-entrepreneurs: https://www.shine.fr/blog/categorie-activite-auto-entrepreneur - -entreprise . activité . libérale: - description: | - Ce sont les professions "intellectuelles" : médecins, sage-femme, kiné, avocat, mais aussi consultant, développeur, designer... - - Selon la loi, ce sont des personnes exerçant à titre habituel, de manière indépendante et sous leur responsabilité, une activité : - - de nature généralement civile, - - ayant pour objet d'assurer des prestations principalement intellectuelles, techniques ou de soins, mises en œuvre au moyen de qualifications professionnelles appropriées et dans le respect de principes éthiques ou d'une déontologie professionnelle. - références: - fiche Wikipedia: https://fr.m.wikipedia.org/wiki/Profession_libérale - liste des professions libérales: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/liste-professions-liberales - -entreprise . activité . commerciale ou industrielle: - description: | - ### Activité commerciale - - Achats de biens pour leur revente en l'état (commerce en gros ou de détail) - - Vente de prestations de service commerciales (location de matériel, transport, agence immobilière, hôtellerie-restauration, entreprise de spectacles, activité de sécurité privée, location, etc.) - - ### Activité industrielle - - Activité de production ou de transformation grâce à l'utilisation d'outils industriels, extraction, industries minières, manutention, magasinage et stockage - -entreprise . activité . artisanale: - description: | - C'est une activité de service, de production, de transformation, ou de réparation exercée par un professionnel qualifié, et qui nécessite des compétences et un savoir-faire spécifiques. - - > Par exemple : les travaux, les activités liées au bâtiment, la réparation de produits fournis par le client, les coiffeurs... - - - L'entreprise ne doit pas employer plus de 10 salariés (l'activité devient commerciale au-delà) - - Les activités artisanales sont répertoriées par un décret - références: - liste des activités artisanales: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/activites-artisanales-0 - -entreprise . activité . service ou vente: - non applicable si: mixte - applicable si: - une de ces conditions: - - activité = 'artisanale' - - activité = 'commerciale ou industrielle' - question: Quelle est le type d'activité de l'entreprise ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - vente - - service - par défaut: "'vente'" - -entreprise . activité . service ou vente . vente: - titre: vente de biens, restauration ou hébergement - description: | - Il s’agit de toute opération comportant transfert de propriété d'un bien - corporel (c'est-à-dire un bien ayant une existence matérielle), ainsi que - toutes les activités de restauration et d'hébergement. - références: - page impots.gouv.fr: https://www.impots.gouv.fr/portail/professionnel/achatvente-de-biens - -entreprise . activité . service ou vente . service: - titre: prestation de service - description: | - Il s’agit de toute opération ne comportant pas de transfert de propriété de - biens corporels (c'est-à-dire ayant une existence matérielle). - - références: - page impots.gouv.fr: https://www.impots.gouv.fr/portail/professionnel/prestations-entre-assujettis - -entreprise . activité . mixte: - titre: Activités mixtes - question: Votre entreprise a-t-elle plusieurs types d'activités ? - par défaut: non - description: | - Il est possible d'avoir plusieurs activités avec des types de revenus - différents pour une même entreprise. - - Par exemple, une entreprise de plomberie qui facture l'achat et la pose d'un - robinet a une partie de son chiffre d'affaires en vente de materiel (le robinet) - et une partie en prestation de service (la pose) - - Il existe trois principales familles de revenus au yeux de l'administration - fiscale et sociale : - - - [Ventes de biens, restauration et hébergement (BIC)](/documentation/entreprise/chiffre-d'affaires/vente-restauration-hébergement) - - [Prestation de service commerciale ou artisanale (BIC)](/documentation/entreprise/chiffre-d'affaires/prestations-de-service-BIC) - - [Autres prestation de service et activité libérale (BNC)](/documentation/entreprise/chiffre-d'affaires/prestations-de-service-BNC) - - Si votre entreprise a des activités correspondants à plus d'un type de - revenus, répondez oui à cette question. - -entreprise . activité . mixte . proportions: - description: Part des différentes activités dans le chiffre d'affaires - titre: proportion activité - unité: '%' - somme: - - nom: service BIC - par défaut: - variations: - - si: activité = 'libérale' - alors: 0 - - sinon: 50% - - nom: service BNC - par défaut: - variations: - - si: activité = 'libérale' - alors: 2 / 3 - - sinon: 0 - - nom: vente restauration hébergement - par défaut: - variations: - - si: activité = 'libérale' - alors: 1 / 3 - - sinon: 50% - - -entreprise . activité . libérale réglementée: - question: Est-ce une activité libérale réglementée ? - par défaut: non - applicable si: activité = 'libérale' - description: | - Certaines professions libérales ont été classées dans le domaine libéral par la loi et leur titre est protégé. Leurs membres doivent respecter des règles déontologiques strictes et sont soumis au contrôle de leurs instances professionnelles (ordre, chambre, ou syndicat). - - > Exemples de professions règlementées : architecte, avocat, infirmier, médecin... - - Il s'agit des autres personnes qui pratiquent, une science ou un art et dont l'activité intellectuelle joue le principal rôle. Leurs recettes doivent représenter la rémunération d'un travail personnel, sans lien de subordination, tout en engageant leur responsabilité technique et morale. - - > Exemples de professions non-règlementées : développeur, historien, urbaniste... - références: - Liste des activités libérales: https://bpifrance-creation.fr/encyclopedie/trouver-proteger-tester-son-idee/verifiertester-son-idee/liste-professions-liberales - -entreprise . activité . débit de tabac: - applicable si: activité = 'commerciale ou industrielle' - question: Exercez-vous une activité de vente de tabac ? - par défaut: non - -établissement: - formule: oui - description: | - Le salarié travaille dans un établissement de l'entreprise, identifié par un code SIRET. - -établissement . localisation: - icônes: 🌍 - description: | - Lorsqu'une entreprise dispose de plusieurs établissements, certaines cotisations sont - calculées à l'échelle de l'établissement et sont fonction de règlementations locales. - question: Dans quelle commune l'établissement est-il implanté ? - API: commune - par défaut: - objet: - code: 29019 - nom: Non renseignée - departement: - nom: Non renseigné - taux du versement transport: 1.8 - -établissement . localisation . code commune: - formule: - synchronisation: - data: localisation - chemin: code - -établissement . localisation . commune: - description: | - Lorsqu'une entreprise dispose de plusieurs établissements, certaines cotisations sont - calculées à l'échelle de l'établissement et sont fonction de règlementations locales. - formule: - synchronisation: - data: localisation - chemin: nom - -établissement . taux du versement transport: - unité: "%" - formule: - synchronisation: - data: localisation - chemin: taux du versement transport - -établissement . localisation . département: - formule: - synchronisation: - data: localisation - chemin: departement . nom - -établissement . localisation . outre-mer: - applicable si: - une de ces conditions: - - département = 'Guadeloupe' - - département = 'Martinique' - - département = 'Guyane' - - département = 'La Réunion' - - département = 'Mayotte' - -établissement . localisation . outre-mer . Guadeloupe Réunion Martinique: - formule: - une de ces conditions: - - département = 'Guadeloupe' - - département = 'Martinique' - - département = 'La Réunion' - -établissement . ZFU: - 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ègles/impôt.yaml b/modele-social/règles/impôt.yaml deleted file mode 100644 index 643040ee9..000000000 --- a/modele-social/règles/impôt.yaml +++ /dev/null @@ -1,525 +0,0 @@ -impôt: - icônes: 🏛️ - description: Cet ensemble de formules est un modèle simplifié de l'impôt sur le revenu. - titre: impôt sur le revenu - somme: - - produit: - assiette: revenu imposable - taux: taux d'imposition - - dirigeant . auto-entrepreneur . impôt . versement libératoire . montant - arrondi: oui - unité: €/an - -impôt . taux d'imposition: - formule: - variations: - - si: méthode de calcul . taux neutre - alors: taux neutre d'impôt sur le revenu - - si: méthode de calcul . taux personnalisé - alors: taux personnalisé - - si: méthode de calcul . barème standard - alors: foyer fiscal . taux effectif - -impôt . méthode de calcul: - description: | - Nous avons implémenté trois façon de calculer l'impôt sur le revenu : - - *Le taux personnalisé* : indiqué sur votre avis d'imposition - - *Le taux neutre* : pour un célibataire sans enfants - - *Le barème standard * : la formule "officielle" utilisée par l'administration fiscale pour obtenir le taux d'imposition - - En remplissant votre taux personnalisé, vous serez au plus proche de votre situation réelle. Le taux neutre peut être intéressant dans le cas où vous n'avez pas transmis votre taux personnalisé à l'employeur et que vous souhaitez comparer les résultats du simulateur à votre fiche de paie. Le barème standard vous donne un résultat plus précis que le taux neutre pour un célibataire sans enfant. - question: Comment souhaitez-vous calculer l'impôt sur le revenu ? - # applicable si: revenu imposable > 0 - # bizarrement, cette condition ne semble pas marcher, on se résout donc à utiliser une version plus "hacky" et moins proche de la loi. Elle posera problème le jour où l'on aura a calculer l'impot avec plusieurs sources de revenu - non applicable si: dirigeant . auto-entrepreneur . impôt . versement libératoire - par défaut: - nom: par défaut - valeur: "'barème standard'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - taux neutre - - taux personnalisé - - barème standard - références: - différence taux neutre / personnalisé: https://www.impots.gouv.fr/portail/particulier/questions/quelles-sont-les-differences-entre-les-taux-de-prelevement-la-source-proposes - calcul du taux d'imposition: https://www.economie.gouv.fr/files/files/ESPACE-EVENEMENTIEL/PAS/Fiche_de_calcul_taux_simplifiee.pdf - -impôt . méthode de calcul . taux neutre: - titre: avec le taux neutre - description: Si vous ne connaissez pas votre taux personnalisé, ou si vous voulez connaître votre impôt à la source dans le cas où vous avez choisi de ne pas communiquer à votre taux à l'employeur, le calcul au taux neutre correspond à une imposition pour un célibataire sans enfants et sans autres revenus / charges. - formule: impôt . méthode de calcul = 'taux neutre' - -impôt . méthode de calcul . taux personnalisé: - titre: avec un taux personnalisé - description: Vous pouvez utiliser directement le taux personnalisé communiqué par l'administration fiscal pour calculer votre impôt. Pour le connaître, vous pouvez-vous rendre sur votre [espace fiscal personnel](https://impots.gouv.fr). - formule: impôt . méthode de calcul = 'taux personnalisé' - -impôt . méthode de calcul . barème standard: - titre: avec le barème standard - description: Le calcul "officiel" de l'impôt, celui sur lequel l'administration fiscal se base pour calculer votre taux d'imposition. - formule: impôt . méthode de calcul = 'barème standard' - -impôt . méthode de calcul . prélèvement à la source: - formule: - une de ces conditions: - - taux neutre - - taux personnalisé - -impôt . revenu imposable: - description: | - C'est le revenu à prendre en compte pour calculer l'impôt avec un taux moyen d'imposition (neutre ou personnalisé). - variations: - - si: dirigeant - alors: dirigeant . rémunération . imposable - - sinon: - valeur: contrat salarié . rémunération . net imposable - abattement: abattement contrat court - -impôt . revenu imposable . abattement contrat court: - description: Lorsque la durée d'un contrat de travail est inférieure à 2 mois, il est possible d'appliquer un abattement pour diminuer le montant du prélèvement à la source. - applicable si: - toutes ces conditions: - - méthode de calcul . taux neutre - - contrat salarié . CDD . durée contrat <= 2 mois - formule: - valeur: 50% * SMIC temps plein . net imposable * 1 mois/an - arrondi: oui - note: Cet abattement s'applique aussi pour les conventions de stage ou les contrats de mission (intérim) de moins de 2 mois. - références: - Bofip - dispositions spécifiques aux contrats courts: https://bofip.impots.gouv.fr/bofip/11252-PGP.html?identifiant=BOI-IR-PAS-20-20-30-10-20180515 - -impôt . taux neutre d'impôt sur le revenu . barème Guadeloupe Réunion Martinique: - icônes: 🇬🇵🇷🇪 🇲🇶 - formule: - grille: - assiette: revenu imposable - tranches: - - montant: 0% - plafond: 1626 €/mois - - montant: 0.5% - plafond: 1724 €/mois - - montant: 1.3% - plafond: 1900 €/mois - - montant: 2.1% - plafond: 2075 €/mois - - montant: 2.9% - plafond: 2292 €/mois - - montant: 3.5% - plafond: 2417 €/mois - - montant: 4.1% - plafond: 2500 €/mois - - montant: 5.3% - plafond: 2750 €/mois - - montant: 7.5% - plafond: 3400 €/mois - - montant: 9.9% - plafond: 4350 €/mois - - montant: 11.9% - plafond: 4942 €/mois - - montant: 13.8% - plafond: 5725 €/mois - - montant: 15.8% - plafond: 6858 €/mois - - montant: 17.9% - plafond: 7625 €/mois - - montant: 20% - plafond: 8667 €/mois - - montant: 24% - plafond: 11917 €/mois - - montant: 28% - plafond: 15833 €/mois - - montant: 33% - plafond: 24167 €/mois - - montant: 38% - plafond: 52825 €/mois - - montant: 43% - note: Ce barème n'a pas été revalorisé en 2021. - -impôt . taux neutre d'impôt sur le revenu . barème Guyane Mayotte: - icônes: 🇬🇾 🇾🇹 - formule: - grille: - assiette: revenu imposable - tranches: - - montant: 0% - plafond: 1741 €/mois - - montant: 0.5% - plafond: 1883 €/mois - - montant: 1.3% - plafond: 2100 €/mois - - montant: 2.1% - plafond: 2367 €/mois - - montant: 2.9% - plafond: 2458 €/mois - - montant: 3.5% - plafond: 2542 €/mois - - montant: 4.1% - plafond: 2625 €/mois - - montant: 5.3% - plafond: 2917 €/mois - - montant: 7.5% - plafond: 4025 €/mois - - montant: 9.9% - plafond: 5208 €/mois - - montant: 11.9% - plafond: 5875 €/mois - - montant: 13.8% - plafond: 6817 €/mois - - montant: 15.8% - plafond: 7500 €/mois - - montant: 17.9% - plafond: 8308 €/mois - - montant: 20% - plafond: 9642 €/mois - - montant: 24% - plafond: 12971 €/mois - - montant: 28% - plafond: 16500 €/mois - - montant: 33% - plafond: 26443 €/mois - - montant: 38% - plafond: 55815 €/mois - - montant: 43% - note: Ce barème n'a pas été revalorisé en 2021. - -impôt . taux neutre d'impôt sur le revenu: - description: > - C'est le barème à appliquer sur le salaire mensuel imposable pour obtenir l'impôt à payer mensuellement pour les salariés qui ne veulent pas révéler à leur entreprise leur taux d'imposition (ce taux peut révéler par exemple des revenus du patrimoine importants). - note: Attention, l'abattement de 10% est inclus implicitement dans ce barème. L'assiette est donc bien le salaire imposable, et non le salaire imposable abattu. - formule: - variations: - - si: établissement . localisation . outre-mer . Guadeloupe Réunion Martinique - alors: barème Guadeloupe Réunion Martinique - - - si: - une de ces conditions: - - établissement . localisation . département = 'Guyane' - - établissement . localisation . département = 'Mayotte' - alors: barème Guyane Mayotte - - sinon: - grille: - assiette: revenu imposable - tranches: - - montant: 0% - plafond: 1420 €/mois - - montant: 0.5% - plafond: 1475 €/mois - - montant: 1.3% - plafond: 1570 €/mois - - montant: 2.1% - plafond: 1676 €/mois - - montant: 2.9% - plafond: 1791 €/mois - - montant: 3.5% - plafond: 1887 €/mois - - montant: 4.1% - plafond: 2012 €/mois - - montant: 5.3% - plafond: 2381 €/mois - - montant: 7.5% - plafond: 2725 €/mois - - montant: 9.9% - plafond: 3104 €/mois - - montant: 11.9% - plafond: 3494 €/mois - - montant: 13.8% - plafond: 4077 €/mois - - montant: 15.8% - plafond: 4888 €/mois - - montant: 17.9% - plafond: 6116 €/mois - - montant: 20% - plafond: 7640 €/mois - - montant: 24% - plafond: 10604 €/mois - - montant: 28% - plafond: 14362 €/mois - - montant: 33% - plafond: 22545 €/mois - - montant: 38% - plafond: 48292 €/mois - - montant: 43% - références: - Explication de l'impôt neutre: https://www.economie.gouv.fr/prelevement-a-la-source/taux-prelevement#taux-non-personnalise - BOFIP: http://bofip.impots.gouv.fr/bofip/11255-PGP.html - -impôt . taux personnalisé: - question: Quel est votre taux de prélèvement à la source ? - description: | - Votre taux moyen d'imposition personnalisé, que vous pouvez retrouver sur : - - une fiche de paie - - un avis d'imposition - - votre espace personnel [impots.gouv.fr](https://impots.gouv.fr) - unité: '%' - - -# TODO: "foyer fiscal" should be in its own top level namespace, but we put it -# in the "impôt" namespace to have a better questions ordering -impôt . foyer fiscal: - icônes: 👨‍👩‍👧‍👦 - formule: oui - -impôt . foyer fiscal . situation de famille: - question: Quelle est votre situation familiale ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - célibataire - - couple - - veuf - par défaut: "'célibataire'" - -impôt . foyer fiscal . situation de famille . célibataire: - titre: Célibataire / Divorcé(e) / Union libre - -impôt . foyer fiscal . situation de famille . couple: - titre: Marié(e)s / Pacsé(e)s - -impôt . foyer fiscal . situation de famille . veuf: - titre: Veuf(ve) - -impôt . foyer fiscal . enfants à charge: - question: Combien d'enfants sont à charge du foyer fiscal ? - par défaut: 0 enfants - -impôt . foyer fiscal . nombre de parts: - unité: parts - formule: - somme: - - principales - - rattachées - - majoration personne seule avec enfant - - majoration personne veuve avec enfant - -impôt . foyer fiscal . nombre de parts . principales: - formule: - variations: - - si: situation de famille = 'couple' - alors: 2 parts - - sinon: 1 part - -impôt . foyer fiscal . nombre de parts . rattachées: - formule: - variations: - - si: enfants à charge <= 2 enfants - alors: 0.5 part/enfant * enfants à charge - - sinon: (1 part/enfant * enfants à charge) - 1 part - -impôt . foyer fiscal . nombre de parts . majoration personne seule avec enfant: - description: >- - Les contribuables célibataires, divorcés ou séparés, qui vivent seuls et - supportent effectivement la charge d’un ou plusieurs enfants bénéficient - d’une demie-part supplémentaire de quotient familial. - applicable si: - toutes ces conditions: - - situation de famille = 'célibataire' - - enfants à charge > 0 enfants - formule: 0.5 part - références: - Bofip: https://bofip.impots.gouv.fr/bofip/2028-PGP.html/identifiant=BOI-IR-LIQ-10-20-20-10-20140326#Majoration_pour_les_personn_22 - -impôt . foyer fiscal . nombre de parts . majoration personne veuve avec enfant: - description: >- - Une personne veuve avec des enfants à charge bénéficie d'une part - supplémentaire pour le calcul du quotient familial, ce qui correspond au - maintient de la part de la personne décédée. - - Une personne veuve sans enfant à charge ne bénéficie en revanche d'aucune - majoration. - applicable si: - toutes ces conditions: - - situation de famille = 'veuf' - - enfants à charge > 0 enfants - formule: 1 part - références: - Quotient familial d'une personne veuve: https://www.service-public.fr/particuliers/vosdroits/F35127 - -impôt . foyer fiscal . taux effectif: - unité: '%' - variations: - - si: impôt à payer = 0 - alors: 0% - - sinon: impôt à payer / revenu imposable - - -impôt . foyer fiscal . revenu imposable: - formule: - somme: - - revenu d'activité abattu - - applicable si: entreprise . imposition . IR - valeur: dirigeant . rémunération . imposable - - autres revenus imposables - -impôt . foyer fiscal . revenu imposable . revenu d'activité abattu: - description: | - Dans le cas général, l'impôt est calculé après l'application d'un abattement forfaitaire fixe. Chacun peut néanmoins opter pour la déclaration de ses *frais réels*, qui viendront remplacer ce forfait par défaut. - valeur: - nom: assiette - variations: - - si: dirigeant = non - alors: contrat salarié . rémunération . net imposable - - si: entreprise . imposition = 'IS' - alors: dirigeant . rémunération . imposable - abattement: - valeur: 10% * assiette - # A VÉRIFIER: calculé à la main en revalorisant le taux 2020 - # HISTORIQUE 2020: 12627€ - # 12627€ × (1 + 0,2%) - plafond: 12652 €/an - # HISTORIQUE 2020: 441€ - # 441€ × (1 + 0,2%) - plancher: 442 €/an - références: - Frais professionnels - forfait ou frais réels: https://www.service-public.fr/particuliers/vosdroits/F1989 - -impôt . foyer fiscal . revenu imposable . autres revenus imposables: - question: Quel est le montant total des autres revenus imposables du foyer fiscal ? - par défaut: 0 €/an - -impôt . foyer fiscal . revenu fiscal de référence: - description: >- - Le revenu fiscal de référence correspond au revenu abattu du foyer ajusté - avec un mécanisme de quotient et majoré d'un certains nombre d'exonérations. - Ces dernières sont réintégrées dans le calcul. - formule: - somme: - - revenu imposable - - contrat salarié . prime d'impatriation - références: - Article 1417 du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000034596743&cidTexte=LEGITEXT000006069577&categorieLien=id&dateTexte=20170505 - -impôt . foyer fiscal . impôt à payer: - formule: - somme: - - impôt sur le revenu - - CEHR - -impôt . foyer fiscal . impôt sur le revenu: - unité: €/an - formule: - valeur: impôt brut - abattement: décote - exemples: - - nom: Salaire d'un cadre - situation: - contrat salarié . rémunération . net imposable: 4000 - valeur attendue: 6977 - références: - Fiche service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F34328 - -impôt . foyer fiscal . impôt sur le revenu . décote: - description: Une décote est appliquée après le barème de l'impôt sur le revenu, pour réduire l'impôt des bas revenus. - formule: - variations: - - si: foyer fiscal . situation de famille = 'couple' - # HISTORIQUE-2020: 1286 - alors: 1289 €/an - 45.25% * impôt brut - # HISTORIQUE-2020: 777 - - sinon: 779€/an - 45.25% * impôt brut - plancher: 0 €/an - -impôt . foyer fiscal . impôt sur le revenu . quotient familial: - unité: €/part/an - formule: revenu imposable / nombre de parts - -impôt . foyer fiscal . impôt sur le revenu . quotient familial . plafond avantage: - formule: - somme: - - produit: - assiette: - variations: - - si: nombre de parts . majoration personne seule avec enfant - alors: nombre de parts . rattachées - 0.5 part - - sinon: nombre de parts . rattachées - # HISTORIQUE-2020: 1567 - facteur: 2 * 1570 €/part/an - - variations: - - si: nombre de parts . majoration personne seule avec enfant - # HISTORIQUE-2020: 3697 - alors: 3704 €/an - - sinon: 0 €/an - références: - Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000041464047&cidTexte=LEGITEXT000006069577&categorieLien=id&dateTexte=20190608 - Bofip: https://bofip.impots.gouv.fr/bofip/2494-PGP.html/identifiant=BOI-IR-LIQ-20-20-20-20200515#III._Niveau_du_plafonnement_12 - -impôt . foyer fiscal . impôt sur le revenu . impôt brut par part: - description: | - Voici le fameux barème de l'impôt sur le revenu. C'est un barème marginal à 5 tranches. - Une contribution sur les hauts revenus ajoute deux tranches supplémentaires. - - Attention : pour un revenu de 100 000€ annuels, le contribuable ne paiera 41 000€ d'impôt (le taux de la 4ème tranche est 41%) ! Ces 41% sont appliqués uniquement à la part de ses revenus supérieure à 72 617€. - formule: - barème: - assiette: quotient familial - tranches: - - taux: 0% - plafond: 10084 €/part/an - - taux: 11% - plafond: 25710 €/part/an - - taux: 30% - plafond: 73516 €/part/an - - taux: 41% - plafond: 158122 €/part/an - - taux: 45% - exemples: - - nom: Haut salaire de ~ 10 000€ mensuels - situation: - contrat salarié . rémunération . net imposable: 10000 - valeur attendue: 30227 - références: - Article 197 du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069577&idArticle=LEGIARTI000006308322 - -impôt . foyer fiscal . impôt sur le revenu . impôt brut: - formule: - le maximum de: - - impôt brut . sans plafonnement - - somme: - - recalcul: - règle: impôt brut . sans plafonnement - avec: - nombre de parts . rattachées: 0 part - - (- quotient familial . plafond avantage) - -impôt . foyer fiscal . impôt sur le revenu . impôt brut . sans plafonnement: - formule: - produit: - assiette: impôt brut par part - facteur: nombre de parts - -impôt . foyer fiscal . CEHR: - unité: €/an - formule: - variations: - - si: foyer fiscal . situation de famille = 'couple' - alors: - barème: - assiette: revenu imposable - tranches: - - taux: 0% - plafond: 500000 €/an - - taux: 3% - plafond: 1000000 €/an - - taux: 4% - - sinon: - barème: - assiette: revenu imposable - tranches: - - taux: 0% - plafond: 250000 €/an - - taux: 3% - plafond: 500000 €/an - - taux: 4% - références: - contribution exceptionnelle sur les hauts revenus: https://www.service-public.fr/particuliers/vosdroits/F31130 - Article 223 sexies du Code général des impôts: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000025049019&cidTexte=LEGITEXT000006069577 - Bofip.impots.gouv.fr: http://bofip.impots.gouv.fr/bofip/7804-PGP - -impôt . domiciliation étranger non implémentée: - formule: situation personnelle . domiciliation fiscale à l'étranger - type: notification - niveau: avertissement - description: | - La retenue à la source pour les non-résident n'est pas encore implémentée. Pour en savoir plus, se référer à la [documentation fiscale](https://www.impots.gouv.fr/portail/international-particulier/je-suis-non-resident-dois-je-declarer-des-revenus-et-payer-des-impots-en) diff --git a/modele-social/règles/profession-libérale.yaml b/modele-social/règles/profession-libérale.yaml deleted file mode 100644 index a5ff7ccb0..000000000 --- a/modele-social/règles/profession-libérale.yaml +++ /dev/null @@ -1,1168 +0,0 @@ -# TODO: rules have a lot to share with artiste-author (BNC-regime) and independant -dirigeant . indépendant . PL: - titre: Profession libérale - applicable si: entreprise . activité = 'libérale' - rend non applicable: entreprise . activité . mixte - formule: oui - -dirigeant . indépendant . PL . métier: - applicable si: entreprise . activité . libérale réglementée - par défaut: "'rattaché CIPAV'" - question: A quelle catégorie appartient votre profession ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - santé - - avocat - - expert-comptable - - rattaché CIPAV - -dirigeant . indépendant . PL . métier . rattaché CIPAV: - titre: Autre métier rattaché à la CIPAV - description: | - Vous exercez un métier réglementé rattaché à la CIPAV. La liste de ces métiers est : - - - Architecte (architecte, architecte d’intérieur, économiste de la construction, maître d’œuvre, géomètre expert) - - Guide-montage (moniteur de ski, guide de haute montagne, accompagnateur de moyenne montagne) - - Ostéopathe - - Psychologue - - Psychothérapeute - - Ergothérapeute - - Diététicien - - Chiropracteur - - Ingénieur conseil - - Guide-conférencier - -dirigeant . indépendant . PL . métier . santé: - titre: Praticien ou auxiliaire médical - question: Quel métier exercez-vous en tant que professionnel de santé ? - description: | - Si vous êtes praticien ou auxiliaire médical conventionné, vous relevez du - régime d'assurance maladie des praticiens et auxiliaires médicaux - conventionnés (PAMC). Le point sur les conditions à remplir pour bénéficier - de ce régime d'assurance maladie et sur les modalités de votre protection - sociale. - - > *Exceptions* : Les ostéopathe, psychologue, psychothérapeute, ergothérapeute, - diététicien et chiropracteur ne dépendent pas du régime PAMC mais de la - CIPAV pour leur retraite et invalidité. - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - médecin - - chirurgien-dentiste - - sage-femme - - auxiliaire médical - -dirigeant . indépendant . PL . métier . santé . auxiliaire médical: - description: | - Vous exercez un des métiers suivants : infirmier, masseur-kinésithérapeute, orthophoniste, orthoptiste ou pédicure-podologue. - -dirigeant . indépendant . PL . métier . santé . chirurgien-dentiste: -dirigeant . indépendant . PL . métier . santé . sage-femme: -# dirigeant . indépendant . PL . métier . santé . médecin remplaçant: -# description: | -# Les médecin remplaçant généraliste ou spécialiste (étudiant, salarié ou -# retraité) sont les honoraires rétrocédés ne dépassent pas 19 000 € par année -# civile peuvent bénéficier d'un dispositif particulier pour le paiement de -# leur cotisation de sécurité sociale. - -# Cela consiste en un taux unique de cotisations à 13,30% -# références: https://www.calcul.urssaf.fr/medecin_remplacant.html -dirigeant . indépendant . PL . métier . santé . médecin: -dirigeant . indépendant . PL . métier . secteur médecin: - applicable si: métier = 'santé . médecin' - question: Sur quel secteur êtes-vous conventionné ? - description: | - Les taux de cotisations et remboursement de la CPAM ne sont pas les même en - fonction du régime de tarification choisie par le praticien. - par défaut: "'S1'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - 'S1' - - 'S2' - - 'non conventionné' - -dirigeant . indépendant . PL . métier . secteur médecin . S1: - titre: Secteur 1 -dirigeant . indépendant . PL . métier . secteur médecin . S2: - titre: Secteur 2 -dirigeant . indépendant . PL . métier . secteur médecin . non conventionné: - -dirigeant . indépendant . PL . métier . avocat: - description: | - Les avocats cotisent auprès d'un organisme autonome pour la retraite et la - prévoyance. - -dirigeant . indépendant . PL . métier . expert-comptable: - description: | - Les experts comptables et les commissaires aux comptes cotisent auprès d'un - organisme autonome pour la retraite et la prévoyance. - -dirigeant . indépendant . PL . option régime général: - applicable si: - toutes ces conditions: - - entreprise . activité . libérale réglementée = non - - entreprise . date de création < 01/2019 - question: Avez-vous opté pour le rattachement au régime général des indépendants ? - description: | - Les personnes exerçant déjà une profession libérale non réglementée avant - 2019 peuvent opter entre 2019 et 2023 pour la Sécurité sociale pour les - indépendants, à condition d’être à jour dans le paiement de toutes leurs - cotisations à la CIPAV. - - Cette option leur permettra de bénéficier des mêmes droits que les artisans - et commerçants (indemnités journalières, retraite, invalidité, etc.). - - Ils auront nottament accès à des indemnités journalières en cas d'arrêt de - travail ou de maternité, ce qui n'est pas le cas à la CIPAV. - - La demande est à effectuer auprès de la CIPAV. Elle prendra effet au 1er - janvier de l’année suivante et sera définitive. - références: - fiche information droit d'option (PDF CIPAV): https://www.lacipav.fr/sites/default/files/2019-03/Fiche%20pratique%20droit%20d%27option.pdf - bpi-france: https://bpifrance-creation.fr/entrepreneur/actualites/nouvelle-liste-activites-liberales-non-reglementees-relevant-cipav - sécu-indépendant.fr: https://www.secu-independants.fr/creation-entreprise/professions-liberales/professions-de-sante/definir-son-concept/choisir-son-activite/ - - par défaut: non - -dirigeant . indépendant . PL . régime général: - description: | - Les professions libérales non règlementées affiliées au régime général - bénéficient de la même protection sociale que les artisans et commerçants. - - C'est le cas des professions libérales non règlementées crées avant le - 01/2019, ou celles ayant exercé leur [droit - d'option](/documentation/dirigeant/indépendant/PL/option-régime-général) - formule: - toutes ces conditions: - - CIPAV = non - - entreprise . activité . libérale réglementée = non - -dirigeant . indépendant . PL . régime général . taux spécifique retraite complémentaire: - titre: taux spécifique profession libérale non reglementée - question: Avez-vous opté pour des taux spécifiques de cotisation retraite complémentaire ? - par défaut: non - description: | - Les professions libérales non règlementées qui ont débuté leur activité à - compter du 1er janvier 2019 ou ceux qui ont débuté leur activité avant la - date du 1er janvier 2019  et ont opté pour le régime général des - travailleurs indépendants  ont la possibilité d’opter pour des taux - spécifique de la cotisation retraite complémentaire. - références: - Guide PL urssaf: https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/Diaporama_TI_statuts_hors_AE.pdf - -dirigeant . indépendant . PL . régime général . taux spécifique retraite complémentaire . montant: - titre: retraite complémentaire (taux PLNR) - remplace: cotisations et contributions . retraite complémentaire - formule: - barème: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: 0% - plafond: 1 - - taux: 14% - plafond: 4 - arrondi: oui - -dirigeant . indépendant . PL . maladie: - titre: maladie (taux PLR) - non applicable si: - une de ces conditions: - - régime général - - PAMC - remplace: cotisations et contributions . maladie - formule: - produit: - assiette: indépendant . assiette des cotisations - taux: - taux progressif: - assiette: indépendant . assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - plafond: 0% - taux: 1.5% - - plafond: 110% - taux: 6.5% - arrondi: oui - références: - secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/taux-de-cotisations - guide urssaf (pdf): https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/Diaporama_PL_statuts_hors_AE_et_PAM.pdf - note: | - Les professions libérales réglementée ne cotisent pour la part correspondante aux - indemnités journalières et n'ont donc pas le droit à ces indemnités en cas de - maladie. - -dirigeant . indépendant . PL . cotisations Urssaf: - description: | - Les cotisations recouvrées par l'Urssaf, qui servent au financement - de la sécurité sociale (assurance maladie, allocations familiales, - dépendance). - formule: - somme: - - cotisations et contributions . CSG et CRDS - - cotisations et contributions . maladie - - cotisations et contributions . allocations familiales - - cotisations et contributions . formation professionnelle - - PAMC . CURPS - arrondi: oui - -dirigeant . indépendant . PL . cotisations caisse de retraite: - description: | - Les cotisations recouvrée par la caisse de retraite autonome spécifique à la profession libérale effectuée. - formule: - somme: - - cotisations et contributions . retraite de base - - cotisations et contributions . retraite complémentaire - - cotisations et contributions . invalidité et décès - - cotisations et contributions . PCV - arrondi: oui - -dirigeant . indépendant . PL . CIPAV: - description: | - La CIPAV est la caisse de retraite autonomes des professions libérales réglementées. - rend non applicable: - - cotisations et contributions . indemnités journalières maladie - - conjoint collaborateur - références: - Site web: https://www.lacipav.fr/ - article de loi (chercher "travailleurs indépendants créant leur activité"): https://www.legifrance.gouv.fr/eli/loi/2017/12/30/CPAX1725580L/jo/texte#JORFARTI000036339157 - formule: - une de ces conditions: - - métier = 'rattaché CIPAV' - - toutes ces conditions: - - entreprise . date de création < 01/2019 - - option régime général = non - - entreprise . activité . libérale réglementée = non - -dirigeant . indépendant . PL . CIPAV . retraite complémentaire: - remplace: cotisations et contributions . retraite complémentaire - titre: retraite complémentaire (CIPAV) - unité: €/an - grille: - assiette: assiette des cotisations - tranches: - - montant: 1457 - plafond: 26581 €/an - - montant: 2913 - plafond: 49281 €/an - - montant: 4370 - plafond: 57851 €/an - - montant: 7283 - plafond: 66401 €/an - - montant: 10196 - plafond: 83061 €/an - - montant: 16023 - plafond: 103181 €/an - - montant: 17479 - plafond: 123301 €/an - - montant: 18936 - -dirigeant . indépendant . PL . CIPAV . invalidité et décès: - remplace: cotisations et contributions . invalidité et décès - titre: invalidité et décès (CIPAV) - formule: - variations: - - si: classe de cotisation = 'A' - alors: 76 €/an - - si: classe de cotisation = 'B' - alors: 228 €/an - - si: classe de cotisation = 'C' - alors: 380 €/an - -dirigeant . indépendant . PL . CIPAV . invalidité et décès . classe de cotisation: - question: Dans quelle classe cotisez-vous pour le régime invalidité-décès de la CIPAV ? - description: >- - La Cipav gère un régime de prévoyance versant une pension en cas - d'invalidité permanente et un capital décès ainsi qu’une rente pour les - conjoints et enfants survivants en cas de décès de l'assuré. Par défaut les - affiliés cotisent en « classe A » mais il est possible de cotiser en classe - B ou C afin de bénéficier d'une meilleure couverture invalidité-décès. - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - A - - B - - C - par défaut: "'A'" - -dirigeant . indépendant . PL . CIPAV . invalidité et décès . classe de cotisation . A: - titre: classe A -dirigeant . indépendant . PL . CIPAV . invalidité et décès . classe de cotisation . B: - titre: classe B -dirigeant . indépendant . PL . CIPAV . invalidité et décès . classe de cotisation . C: - titre: classe C - -dirigeant . indépendant . PL . retraite CNAVPL: - non applicable si: - une de ces conditions: - - régime général - - PL . CNBF - titre: retraite de base (CNAVPL) - description: | - Toutes les professions libérale (à l'exception des avocats) - ont les mêmes taux de cotisations pour leur retraite de base. - - La caisse nationale d'assurance vieillesse des professions - libérales est l'organisme qui fédère les différentes caisses - existantes (CIPAV, CARPIMKO, CARCDSF, CAVEC etc..) - - produit: - assiette: - valeur: assiette des cotisations - plancher: assiette minimale . retraite - composantes: - - attributs: - nom: tranche T1 - arrondi: oui - taux: 8.23% - plafond: plafond sécurité sociale temps plein - - attributs: - nom: tranche t2 - arrondi: oui - taux: 1.87% - plafond: 5 * plafond sécurité sociale temps plein - références: - cnavpl.fr: https://www.cnavpl.fr/preparer-sa-retraite/ - cotisation minimale (Guide CNAVPL): https://www.cnavpl.fr/wp-content/uploads/2020/10/guide-site-2020.pdf#page=21 - liste des caisses: https://www.cnavpl.fr/regimes-complementaires-et-prevoyance/ - Guide CNAVPL (PDF): https://www.cnavpl.fr/statuts-et-documents-de-reference/?wpdmdl=56215 - -#TODO: On ajoute une exception car la transitivité du remplacement ne fonctionne pas encore -dirigeant . indépendant . PL . retraite CNAVPL . remplace: - titre: retraite de base (CNAVPL) - non applicable si: CARMF . retraite CNAVPL - remplace: cotisations et contributions . retraite de base - formule: retraite CNAVPL - -dirigeant . indépendant . PL . PAMC: - applicable si: - une de ces conditions: - - toutes ces conditions: - - métier = 'santé . médecin' - - métier . secteur médecin != 'non conventionné' - - métier = 'santé . sage-femme' - - métier = 'santé . chirurgien-dentiste' - - métier = 'santé . auxiliaire médical' - - rend non applicable: - - cotisations et contributions . indemnités journalières maladie - - conjoint collaborateur - - entreprise . chiffre d'affaires . franchise de TVA dépassée - - dirigeant . indépendant . revenus étrangers - - dirigeant . indépendant . cotisations et contributions . maladie domiciliation fiscale étranger - formule: oui - -dirigeant . indépendant . PL . PAMC . proportion recette activité non conventionnée: - question: | - Quel est la part de votre chiffre d'affaires liée à une activité non - conventionnée (estimation) ? - par défaut: 0% - suggestions: - 10%: 10% - 30%: 30% - description: | - Les recettes non conventionnées sont toutes celles qui ne rentrent pas dans - les catégories suivantes : - - - Honoraires tirés des actes remboursables (y compris les - dépassements d’honoraires et les frais de déplacement figurant sur le relevé - SNIR) - - - Honoraires issus de rétrocessions concernant les actes remboursables - perçues en qualité de remplaçant - - - Toutes les rémunérations forfaitaires versées par l’assurance maladie - (aide à la télétransmission, indemnisation, indemnisation de la formation - continue, prime à l’installation, ...) - -dirigeant . indépendant . PL . PAMC . proportion recette activité non conventionnée . notification: - type: notification - sévérité: avertissement - formule: proportion recette activité non conventionnée > 100% - description: | - La proportion ne peut pas être supérieure à 100% - -dirigeant . indépendant . PL . PAMC . remplaçant: - question: Au 1er janvier 2020, exerciez-vous votre activité exclusivement en tant que remplaçant ? - description: | - Les practicien et auxiliaire médical exerçant une activité de remplacement ne sont pas redevables de la contribution aux unions régionales des professionnels de santé (CURPS) - par défaut: non - -dirigeant . indépendant . PL . PAMC . CURPS: - titre: Contribution aux unions régionales des professionnels de santé - remplace: cotisations et contributions . contributions spéciales - description: | - Les professions libérales de santé sont représentées par des unions - régionales des professionnels de santé qui contribuent à l’organisation et à - l’évolution de l’offre de santé au niveau régional, notamment à la - préparation du projet régional de santé et à sa mise en œuvre. - - Ces unions sont financées par une contribution recouvrée par l’Urssaf : la - contribution aux unions régionales des professionnels de santé (Curps). - note: | - Les remplaçants, quelle que soit leur activité, ne sont pas redevables de la - Curps. Si la Curps est présente sur leur échéancier de cotisations, ils sont - invités à se rapprocher de leur Urssaf pour que leur dossier cotisant soit - régularisé. Un nouvel échéancier de cotisations sera ensuite adressé. - acronyme: CURPS - applicable si: - toutes ces conditions: - - entreprise . date de création < 01/2020 - - revenu professionnel > 0 - non applicable si: - une de ces conditions: - - métier . secteur médecin = 'non conventionné' - - remplaçant - formule: - produit: - assiette: assiette des cotisations - taux: - variations: - - si: métier = 'santé . médecin' - alors: 0.5% - - si: métier = 'santé . chirurgien-dentiste' - alors: 0.3% - - sinon: 0.1% - plafond: 0.50% * plafond sécurité sociale temps plein - arrondi: oui - références: - Fiche Urssaf.fr: https://www.urssaf.fr/portail/home/independant/mes-cotisations/quelles-cotisations/la-contribution-aux-unions-regio/la-base-de-calcul-et-le-taux-de.html - -dirigeant . indépendant . PL . PAMC . maladie: - remplace: cotisations et contributions . maladie - titre: maladie (après participation CPAM) - formule: - somme: - - produit: - assiette: assiette des cotisations - taux: 6.50% - arrondi: oui - - contribution additionnelle - - (- participation CPAM) - -dirigeant . indépendant . PL . PAMC . dépassement d'honoraire moyen: - non applicable si: métier . secteur médecin = 'S1' - question: Quels est votre dépassement honoraires moyen (estimation) ? - par défaut: 0% - -dirigeant . indépendant . PL . PAMC . revenus activité conventionnée: - description: | - Les revenus conventionnés sont ceux correspondant aux recettes tirées des - honoraires et des rémunérations forfaitaires versées par la CPAM. - note: | - Pour éviter d'avoir à ventiler les charges entre celles issues de - l'activités conventionnées et celles qui ne le sont pas (ce qui aboutirait à - deux comptabilités distinct), on peut le calculer à partir du revenu - professionnel que l'on ajuste en fonction de la part du chiffre d'affaires - provenant des actes conventionnés. - formule: - produit: - assiette: assiette des cotisations - facteur: - valeur: 100% - proportion recette activité non conventionnée - plancher: 0% - -dirigeant . indépendant . PL . PAMC . assiette participation CPAM: - description: Aussi appelé revenu conventionnel, il s'agit du revenu des honoraires nets - de dépassement. - formule: revenus activité conventionnée / (100% + dépassement d'honoraire moyen) - note: | - La formule référencée dans les textes Urssaf est la suivante : - > (revenu de l’activité conventionnée) x (total des honoraires - total des dépassements d’honoraires) / montant total des honoraires. - - On peut simplififer cette formule en : - > (revenu de l’activité conventionnée) / (100% + dépassement d'honoraire moyen) - - ### Preuve - Si on prends les variables suivantes, - > `h+` : total des honoraires (avec dépassement) - `h` : honoraires sans dépassement - `d%`: pourcentage de dépassement d'honoraire moyen - - On a : - > - `h+ = h + h * d%` - `h+ = h * (100% + d%)` - - Si on remplace dans la formule de l'assiette participation CPAM, on a : - > 1. `(revenu de l’activité conventionnée) * h / h+` - > 2. `(revenu de l’activité conventionnée) * h / (h * (100% + d%)) - > 3. `(revenu de l’activité conventionnée) / (100% + d%)` - - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/praticien-et-auxiliaire-medical/mes-cotisations/le-calcul-de-mes-cotisations/la-participation-de-la-cpam-a-me/je-suis-medecin-du-secteur-1/assiette-de-participation-de-la.html - -dirigeant . indépendant . PL . PAMC . maladie . participation CPAM: - non applicable si: métier . secteur médecin = 'S2' - titre: Participation CPAM à la maladie - formule: - produit: - assiette: assiette participation CPAM - taux: 6.40% - arrondi: oui - -dirigeant . indépendant . PL . PAMC . maladie . contribution additionnelle: - formule: - produit: - assiette: (assiette des cotisations - assiette participation CPAM) - taux: 3.25% - arrondi: oui - -dirigeant . indépendant . PL . PAMC . allocations familiales: - applicable si: métier . secteur médecin = 'S1' - titre: allocations familiales (après participation CPAM) - remplace: - règle: cotisations et contributions . allocations familiales - sauf dans: participation CPAM - formule: - valeur: cotisations et contributions . allocations familiales - abattement: participation CPAM - - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/taux-et-baremes/taux-de-cotisations/les-praticiens-et-auxiliaires-me/taux-de-cotisations-medecin-sect.html -dirigeant . indépendant . PL . PAMC . allocations familiales . participation CPAM: - titre: Participation CPAM aux allocations familiales - formule: - produit: - assiette: cotisations et contributions . allocations familiales - taux: - grille: - assiette: assiette participation CPAM - multiplicateur: plafond sécurité sociale temps plein - tranches: - - montant: 100% - plafond: 140% - - montant: 75% - plafond: 250% - - montant: 60% - arrondi: oui - -dirigeant . indépendant . PL . PAMC . assiette participation chirurgien-dentiste: - applicable si: métier = 'santé . chirurgien-dentiste' - titre: assiette participation CPAM (chirurgien dentiste) - remplace: assiette participation CPAM - formule: - produit: - assiette: revenus activité conventionnée - taux: 1 - taux Urssaf / (1 + taux Urssaf) - référence: - Fiche Urssaf: https://www.urssaf.fr/portail/home/praticien-et-auxiliaire-medical/mes-cotisations/le-calcul-de-mes-cotisations/la-participation-de-la-cpam-a-me/je-suis-chirurgien-dentiste/assiette-de-participation-de-la.html - Texte de loi: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000020429271&categorieLien=id - -dirigeant . indépendant . PL . PAMC . assiette participation chirurgien-dentiste . taux Urssaf: - description: | - Le « taux Urssaf » (taux UR) permet de calculer la part de votre - cotisation d’assurance maladie-maternité prise en charge par la CPAM. - - Ce taux est pré-rempli sur votre déclaration de revenus professionnels. Il - est issu des données de votre Relevé individuel d’activité et de - prescriptions (RIAP). - - Plus le taux est faible, plus la participation CPAM est importante et donc - la part à la charge du praticien est faible. - - ## Calcul du taux - - La formule de calcul du taux de dépassement est la suivante : - > Taux Urssaf = (dépassements - montants remboursés forfaits CMU) / (montants remboursables actes + montants remboursés forfaits CMU) - question: Quel est votre "taux Urssaf" ? - unité: '' - par défaut: 1 - -dirigeant . indépendant . PL . PAMC . assiette participation chirurgien-dentiste . taux Urssaf . notification: - formule: taux Urssaf >= 100 - type: notification - sévérité: avertissement - description: Le taux Urssaf doit être inférieur à 100 - -dirigeant . indépendant . PL . PAMC . participation CPAM: - titre: Participation assurance maladie - description: | - Les professionnels de santé conventionnés bénéficient d'une prise en charge d'une partie de leur cotisations par l'Assurance Maladie. - formule: - somme: - - PAMC . allocations familiales . participation CPAM - - PAMC . maladie . participation CPAM - - CARMF . ASV . participation CPAM - - CARPIMKO . ASV . participation CPAM - - CARCDSF . chirurgien-dentiste . PCV . participation CPAM - - CARCDSF . sage-femme . PCV . participation CPAM - - CARMF . retraite CNAVPL . participation CPAM - arrondi: oui - références: - amelie.fr: https://www.ameli.fr/assure/droits-demarches/salaries-travailleurs-independants-et-personnes-sans-emploi/emploi-independant-non-salarie/praticien-auxiliaire-medical - rapport sécurtié sociale 2009: https://www.securite-sociale.fr/files/live/sites/SSFR/files/medias/CCSS/2009/RAPPORT/CCSS-RAPPORT-JUIN_2009-FICHE-LA_PRISE_EN_CHARGE_DES_COTISATIONS_DES_PRATICIENS_ET_AUXILIAIRES_MEDICAUX.pdf - -dirigeant . indépendant . PL . PAMC . IJSS: - remplace: indépendant . IJSS . imposable - rend non applicable: indépendant . IJSS - titre: indemnités journalières de sécurité sociale - description: >- - Les indemnités journalières de Sécurité sociale (IJSS) sont versées dans le cas de congé maternité/paternité/adoption. - - - La CSG-CRDS est automatiquement précomptée par l'Assurance maladie lors du versement. Leur montant est donc retranché à l'assiette pour le calcul de la CSG-CRDS restante dûe. - - - Les indemnités complémentaires aux indemnités journalières de la Sécurité - sociale versées dans le cadre d’un contrat de prévoyance ne constituent pas - des revenus de remplacement. - - Note: Les prestations d’invalidité versées par les régimes - d’invalidité-décès ne sont pas concernées. - - question: Quel est le montant des indemnités journalières de maternité ou paternité perçu au titre de votre activité libérale ? - par défaut: 0 €/an - -dirigeant . indépendant . PL . CARPIMKO: - description: La CARPIMKO est la caisse de retraite autonome des auxiliaires médicaux. - formule: oui - applicable si: métier = 'santé . auxiliaire médical' - références: - Site CARPIMKO: https://www.carpimko.com - -dirigeant . indépendant . PL . CARPIMKO . retraite complémentaire: - remplace: cotisations et contributions . retraite complémentaire - titre: retraite complémentaire (CARPIMKO) - formule: - somme: - - 1648 €/an - - barème: - assiette: assiette des cotisations - tranches: - - taux: 0% - plafond: 25246 €/an - - taux: 3% - plafond: 176413 €/an - arrondi: oui - références: - Site CARPIMKO: https://www.carpimko.com/cotisations/cotisations_cas_general - -dirigeant . indépendant . PL . CARPIMKO . invalidité et décès: - titre: invalidité et décès (CARPIMKO) - remplace: cotisations et contributions . invalidité et décès - formule: 678 €/an - références: - Site CARPIMKO: https://www.carpimko.com/cotisations/cotisations_cas_general - -dirigeant . indépendant . PL . CARPIMKO . ASV: - titre: Avantage social vieillesse (CARPIMKO) - remplace: cotisations et contributions . PCV - formule: - somme: - - forfaitaire - - proportionnelle - - (- participation CPAM) - arrondi: oui - références: - Taux 2020: http://www.carpimko.com/cotisations/cotisations_cas_general - -dirigeant . indépendant . PL . CARPIMKO . ASV . forfaitaire: - formule: 590 €/an -dirigeant . indépendant . PL . CARPIMKO . ASV . proportionnelle: - formule: - produit: - assiette: PAMC . assiette participation CPAM - taux: 0.40% - -dirigeant . indépendant . PL . CARPIMKO . ASV . participation CPAM: - titre: Participation CPAM à l'avantage social vieillesse - applicable si: PAMC - formule: - somme: - - produit: - assiette: forfaitaire - taux: 2 / 3 - arrondi: oui - - 60% * proportionnelle - - références: - Prise en charge CPAM: http://www.carpimko.com/cotisations/cotisations_cas_general - -dirigeant . indépendant . PL . CARMF: - formule: oui - description: | - La CARMF est la caisse de retraite autonome des médecins de France. - applicable si: métier = 'santé . médecin' - références: - Site CARMF: http://www.carmf.fr - note: | - L’affiliation est obligatoire pour les médecins titulaires du diplôme de - docteur en médecine, inscrits au conseil de l’Ordre et exerçant une activité - libérale (installation, remplacements, expertises pour les compagnies - d’assurance ou les laboratoires privés, secteur privé à l’hôpital, en - société d’exercice libéral ou toute autre activité rémunérée sous forme - d’honoraires, même s’il ne s’agit pas de la médecine de soins) en France - métropolitaine et dans les départements d’Outre-Mer ou à Monaco. - -dirigeant . indépendant . PL . CARMF . retraite CNAVPL: - titre: retraite de base CNAVPL (après participation CPAM) - applicable si: métier . secteur médecin = 'S1' - description: | - Pour compenser la hausse de la CSG, les médecins de secteur 1 bénéficient d'une participation de l'assurance maladie (avenant n°5 de la convention médicale) au financement de leurs cotisations du régime de base. - remplace: cotisations et contributions . retraite de base - formule: - valeur: PL . retraite CNAVPL - abattement: participation CPAM - références: - Avenant 5 à la convention médical: https://www.ameli.fr/sites/default/files/Documents/434342/document/avis_relatif_a_lavenant_ndeg_5_a_la_convention_nationale_organisant_les_rapports_entre_les_medecins_liberaux_et_lassurance_maladie.pdf - -dirigeant . indépendant . PL . CARMF . retraite CNAVPL . participation CPAM: - titre: participation CPAM à la retraite de base - formule: - produit: - assiette: assiette des cotisations - taux: - grille: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - montant: 2.15% - plafond: 140% - - montant: 1.51% - plafond: 250% - - montant: 1.12% - arrondi: oui - références: - Avenant 5 à la convention médical: https://www.ameli.fr/sites/default/files/Documents/434342/document/avis_relatif_a_lavenant_ndeg_5_a_la_convention_nationale_organisant_les_rapports_entre_les_medecins_liberaux_et_lassurance_maladie.pdf - -dirigeant . indépendant . PL . CARMF . retraite complémentaire: - remplace: cotisations et contributions . retraite complémentaire - description: >- - La CARMF gère le régime de retraite complémentaire. - - Le montant des cotisations est déterminé en fonction des revenus - nets d’activité indépendante de l’avant-dernière année. - - Les cotisations des deux premières années d’affiliation ne sont - pas dues, sauf si vous étes âgé de plus de 40 ans au début de votre - activité libérale. Dans ce cas, la cotisation est proportionnelle - aux revenus nets d'activité indépendante de 2018 plafonnés, sans - régularisation ultérieure, avec une cotisation maximale de 14 110 €. - titre: retraite complémentaire (CARMF) - arrondi: oui - variations: - - si: entreprise . date de création >= 01/01/2020 - alors: 0€/an - - sinon: - produit: - assiette: assiette des cotisations - plafond: 3.5 * plafond sécurité sociale temps plein - taux: 9.80% - - références: - Site CARMF: http://www.carmf.fr/page.php?page=cdrom/coti/coti-chiffre.htm - -dirigeant . indépendant . PL . CARMF . invalidité décès: - titre: invalidité et décès (CARMF) - remplace: cotisations et contributions . invalidité et décès - description: >- - La CARMF gère un régime de prévoyance versant une pension en cas - d'invalidité permanente et un capital décès ainsi qu’une rente pour les - conjoints et enfants survivants en cas de décès de l'assuré. - - La cotisation comporte trois classes forfaitaires dont le montant est - déterminé en fonction de vos revenus nets d'activité indépendante de - l’avant-dernière année. - - Sans communication des revenus professionnels non salariés et de l’avis - d’imposition de l’avant dernière année, le taux d’indemnisation ne peut être - fixé. Dans l’attente de la réception de ce document l’indemnisation sera - basée sur le taux prévu pour la classe A. - formule: - grille: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - montant: 631 €/an - plafond: 1 - - montant: 738 €/an - plafond: 3 - - montant: 863 €/an - références: - Montant des cotisations: http://www.carmf.fr/page.php?page=cdrom/coti/coti-cours.htm#base - Détails des couvertures: http://www.carmf.fr/page.php?page=cdrom/prev/prev-chiffre.htm - -dirigeant . indépendant . PL . CARMF . ASV: - titre: Allocations supplémentaires de vieillesse (CARMF) - remplace: cotisations et contributions . PCV - description: >- - Le régime des allocations supplémentaires de vieillesse (ASV) s'applique - pour les médecins conventionnés. - - Il fonctionne en points et comprend une part forfaitaire et une part - d’ajustement calculée sur le revenu conventionnel de N-2. - - Les deux tiers de la cotisation des médecins en secteur 1 sont financés - par les Caisses maladie. - non applicable si: métier . secteur médecin = 'non conventionné' - formule: - valeur [ref assiette]: - somme: - - 5325 €/an - - produit: - assiette: PAMC . revenus activité conventionnée - plafond: 5 * plafond sécurité sociale temps plein - taux: 3.80% - abattement: participation CPAM - arrondi: oui - références: - Taux 2021: http://www.carmf.fr/page.php?page=chiffrescles/stats/2021/taux2021.htm - -dirigeant . indépendant . PL . CARMF . ASV . participation CPAM: - titre: Participation CPAM aux allocations supplémentaires de vieillesse - applicable si: métier . secteur médecin = 'S1' - formule: - produit: - assiette: assiette - taux: 2 / 3 - -dirigeant . indépendant . PL . CARCDSF: - formule: oui - description: | - La CARCDSF est la caisse de retraite des chirurgiens dentiste et des sages femmes. - - applicable si: - une de ces conditions: - - métier = 'santé . chirurgien-dentiste' - - métier = 'santé . sage-femme' - références: - Site Web: http://www.carcdsf.fr - -dirigeant . indépendant . PL . CARCDSF . retraite complémentaire: - titre: retraite complémentaire (CARCDSF) - remplace: cotisations et contributions . retraite complémentaire - formule: - somme: - - cotisation forfaitaire - - cotisation proportionnelle - références: - Site CARCDSF: http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations - -dirigeant . indépendant . PL . CARCDSF . retraite complémentaire . cotisation proportionnelle: - formule: - barème: - assiette: assiette des cotisations - multiplicateur: plafond sécurité sociale temps plein - tranches: - - taux: 0% - plafond: 0.85 - - taux: 10.65% - plafond: 5 - arrondi: oui - -dirigeant . indépendant . PL . CARCDSF . retraite complémentaire . cotisation forfaitaire: - formule: - produit: - assiette: 2960.40 €/an - facteur: - variations: - - si: taux réduction - alors: taux réduction - - sinon: 100% - arrondi: oui - -dirigeant . indépendant . PL . CARCDSF . retraite complémentaire . cotisation forfaitaire . réduction applicable: - formule: assiette des cotisations < 85% * plafond sécurité sociale temps plein - description: | - Vous avez la possibilité de bénéficier d'une réduction de cotisation - pour la retraite complémentaire si vous en faites la demande. [En savoir - plus](/documentation/dirigeant/indépendant/PL/CARCDSF/retraite-complémentaire/cotisation-forfaitaire/taux-réduction) - type: notification - -dirigeant . indépendant . PL . CARCDSF . retraite complémentaire . cotisation forfaitaire . taux réduction: - applicable si: réduction applicable - description: | - Les affiliés dont les revenus professionnels nets sur l'année N-1 sont inférieurs à 85 - % du PASS en vigueur au 1er janvier de l’année considérée (34 966 € en 2020) - peuvent, sur demande, obtenir une réduction de la cotisation forfaitaire. - - Le coefficient de réduction appliqué est égal au rapport des revenus - professionnels non-salariés sur le seuil mentionné ci-dessus. - - La demande doit être adressée à la CARCDSF et être accompagnée d’une - photocopie de la déclaration d’impôt n° 2042 C ou 2035 ou 2065 et de leurs - annexes (2033 B et D ou 2053 et 2058 A) de l’année 2019. - unité: '%' - formule: assiette des cotisations / (85% * plafond sécurité sociale temps plein) - références: - Site CARCDSF: http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste: - applicable si: métier = 'santé . chirurgien-dentiste' - formule: oui - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . RID: - titre: invalidité et décès (CARCDSF chirurgien-dentiste) - remplace: cotisations et contributions . invalidité et décès - formule: 1078 €/an - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . PCV: - titre: Prestation complémentaire vieillesse (CARCDSF chirurgien-dentiste) - remplace: cotisations et contributions . PCV - non applicable si: exonération PCV - note: Une dispense peut être accordée lorsque les revenus professionnels 2019 sont - inférieurs ou égaux à 500 C (valeur au 1er janvier de l’année considérée), - soit 11 500 €. - - La demande doit être accompagnée d’une photocopie de la déclaration d’impôt - n° 2042 C ou 2035 ou 2065 et de leurs annexes (2033 B et D ou 2053 et 2058 - A) de l’année 2019. - - Cette dispense entraîne l’annulation des droits pour l’année et les points - non cotisés ne sont pas rachetables. - formule: - somme: - - forfaitaire - - proportionnelle - arrondi: oui - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . PCV . forfaitaire: - formule: 1440.60 €/an -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . PCV . proportionnelle: - formule: - produit: - assiette: assiette des cotisations - plafond: 5 * plafond sécurité sociale temps plein - taux: 0.725 % - références: - Site CARCDSF: http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . PCV . participation CPAM: - titre: Participation CPAM à la prestation complémentaire vieillesse - formule: - somme: - - 2 * forfaitaire - - proportionnelle - références: - Guide CARCDSF (PDF, page 6): http://www.carcdsf.fr/images/memento/0872-19_CARCDSF_MEMENTO_2020_CHIRURGIENS_DENTISTES-WEB.pdf - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . exonération PCV: - type: notification - formule: (assiette des cotisations / prix d'une consultation) <= 500 consultation/an - description: >- - Vous avez la possibilité de bénéficier d'une exonération totale de - cotisation pour la prestation complémentaire de vieillesse (PCV) si vous en - faites la demande. [En savoir - plus](http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations) - -dirigeant . indépendant . PL . CARCDSF . chirurgien-dentiste . prix d'une consultation: - formule: 23 €/consultation - -dirigeant . indépendant . PL . CARCDSF . sage-femme: - applicable si: métier = 'santé . sage-femme' - formule: oui - -dirigeant . indépendant . PL . CARCDSF . sage-femme . RID: - titre: invalidité et décès (CARCDSF sage-femme) - description: | - Il existe classes de cotisations aux choix, correspondant à des cotisations - et des degrés d'indemnisations différents. - - Le changement d'option pour une classe supérieure doit être demandé avant le - 1er juillet de l'année en cours, pour prendre effet au 1er janvier de - l'année suivante. - - Aucun changement de classe n'est autorisé après le 1er juillet du 56e anniversaire. - remplace: cotisations et contributions . invalidité et décès - formule: - variations: - - si: classe = 'A' - alors: 91 €/an - - si: classe = 'B' - alors: 182 €/an - - si: classe = 'C' - alors: 273 €/an - références: - Montant des cotisations: http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations - -dirigeant . indépendant . PL . CARCDSF . sage-femme . RID . classe: - titre: Classe de cotisation - question: Dans quelle classe cotisez-vous pour le régime invalidité-décès de la CARCDSF ? - description: | - Il existe classes de cotisations aux choix, correspondant à des cotisations - et des degrés d'indemnisations différents. - par défaut: "'A'" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - A - - B - - C -dirigeant . indépendant . PL . CARCDSF . sage-femme . RID . classe . A: - titre: classe A -dirigeant . indépendant . PL . CARCDSF . sage-femme . RID . classe . B: - titre: classe B -dirigeant . indépendant . PL . CARCDSF . sage-femme . RID . classe . C: - titre: classe C - -dirigeant . indépendant . PL . CARCDSF . sage-femme . PCV: - remplace: cotisations et contributions . PCV - non applicable si: exonération PCV - description: | - Pour 2020, le montant est fixé à 780 € dont un tiers, soit 260 € à votre - charge et 520 € à la charge des Caisses Primaires d’Assurance Maladie - (CPAM). - - formule: - valeur: 780 €/an - abattement [ref participation CPAM]: 520 €/an - références: - Site CARCDSF: http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations - note: | - Une dispense peut être accordée lorsque les revenus professionnels sont - inférieurs ou égaux à 3120 €. - - La demande doit être accompagnée d’une photocopie de la déclaration d’impôt - n° 2042 C ou 2035 ou 2065 et de leurs annexes (2033 B et D ou 2053 et 2058 - A). - - Cette dispense entraîne l’annulation des droits pour l’année et les points - non cotisés ne sont pas rachetables. - -dirigeant . indépendant . PL . CARCDSF . sage-femme . exonération PCV: - type: notification - formule: assiette des cotisations <= 3120 €/an - description: >- - Vous avez la possibilité de bénéficier d'une exonération totale de - cotisation pour la prestation complémentaire de vieillesse (PCV) si vous en - faites la demande. [En savoir - plus](http://www.carcdsf.fr/cotisations-du-praticien/montant-des-cotisations) - -dirigeant . indépendant . PL . CNBF: - formule: oui - applicable si: métier = 'avocat' - description: | - La Caisse Nationale des Barreaux Français (CNBF) est l’organisme de sécurité - sociale des avocats. - rend non applicable: - # Applicable mais pas encore supporté - - conjoint collaborateur - références: - Site CNBF: https://www.cnbf.fr - Barème 2020: https://www.cnbf.fr/wp-content/uploads/2020/08/CNBF-bareme-des-cotisations-et-prestations-2020.pdf - -dirigeant . indépendant . PL . CNBF . retraite de base: - remplace: cotisations et contributions . retraite de base - formule: - somme: - - cotisation forfaitaire - - cotisation proportionnelle - -dirigeant . indépendant . PL . CNBF . retraite de base . cotisation forfaitaire: - formule: - grille: - assiette: entreprise . durée d'activité . en fin d'année - tranches: - - montant: 290 €/an - plafond: 1 an - - montant: 581 €/an - plafond: 2 ans - - montant: 912 €/an - plafond: 3 ans - - montant: 1242 €/an - plafond: 5 ans - - montant: 1586 €/an - -dirigeant . indépendant . PL . CNBF . retraite de base . cotisation proportionnelle: - formule: - produit: - taux: 3.1% - assiette: assiette des cotisations - plafond: 291718 €/an - -dirigeant . indépendant . PL . CNBF . retraite complémentaire: - remplace: cotisations et contributions . retraite complémentaire - titre: retraite complémentaire (CNBF) - formule: - barème: - assiette: assiette des cotisations - multiplicateur: 41674 €/an - tranches: - - taux: 4% - plafond: 1 - - taux: 8% - plafond: 2 - - taux: 9.2% - plafond: 3 - - taux: 10.4% - plafond: 4 - - taux: 11.6% - plafond: 5 - arrondi: oui - note: | - Il existe plusieurs classes de cotisations, qui permettent de cotiser - d'avantage pour acquérir d'avantages de points. Seule la première classe est - implémentée pour l'instant. - -dirigeant . indépendant . PL . CNBF . RID: - titre: invalidité et décès (CNBF avocat) - remplace: cotisations et contributions . invalidité et décès - formule: - variations: - - si: entreprise . durée d'activité . en fin d'année < 5 ans - alors: 55 €/an - - sinon: 137 €/an - -dirigeant . indépendant . PL . CAVEC: - formule: oui - applicable si: métier = 'expert-comptable' - description: | - La CAVEC est l’organisme de sécurité sociale des experts-comptables et des - commissaires aux comptes. - rend non applicable: - # Applicable mais pas encore supporté - - conjoint collaborateur - références: - Site CAVEC: https://www.cavec.fr - -dirigeant . indépendant . PL . CAVEC . retraite complémentaire: - titre: retraite complémentaire (CAVEC) - remplace: cotisations et contributions . retraite complémentaire - formule: - grille: - assiette: assiette des cotisations - unité: €/an - tranches: - - montant: 648 - plafond: 16190 €/an - - montant: 2430 - plafond: 32350 €/an - - montant: 3834 - plafond: 44790 €/an - - montant: 5994 - plafond: 64560 €/an - - montant: 9558 - plafond: 79040 €/an - - montant: 14580 - plafond: 94850 €/an - - montant: 16200 - plafond: 132780 €/an - - montant: 20250 - références: - Site CAVEC: https://www.cavec.fr/fr/vos-cotisations-12/montant-des-cotisations-retraite-tns-59/montant-des-cotisations-62 - -dirigeant . indépendant . PL . CAVEC . invalidité et décès: - titre: invalidité et décès (CAVEC) - remplace: cotisations et contributions . invalidité et décès - formule: - grille: - assiette: assiette des cotisations - unité: €/an - tranches: - - montant: 288 - plafond: 16190 €/an - - montant: 396 - plafond: 44790 €/an - - montant: 612 - plafond: 79040 €/an - - montant: 828 - références: - Site CAVEC: https://www.cavec.fr/fr/vos-cotisations-12/montant-des-cotisations-retraite-tns-59/montant-des-cotisations-62 diff --git a/modele-social/règles/protection-sociale.yaml b/modele-social/règles/protection-sociale.yaml deleted file mode 100644 index a7eab0a66..000000000 --- a/modele-social/règles/protection-sociale.yaml +++ /dev/null @@ -1,436 +0,0 @@ -protection sociale: - description: > - La protection sociale est composée de 5 branches principales : maladie, famille, accidents - du travail et maladies professionnelles, retraite et chômage. A cela s'ajoutent - aussi les cotisations pour la formation professionnelle et le transport. - -protection sociale . retraite: - icônes: 👵 - type: branche - résumé: Garantit en moyenne 60 à 70 % du dernier revenu d'activité après 65 ans. - - description: | - Tous les travailleurs en France cotisent tout au long de leur vie professionnelle pour bénéficier d’un régime de retraite dès lors qu’ils ont l’âge de cesser leur activité. - - Le système des retraites est actuellement fondé sur le principe de la « répartition ». Cela veut dire que les cotisations des actifs financent les pensions des retraités. - - ## La retraite en France en quelques chiffres - - ** 2094 € / mois** : Niveau de vie moyen des plus de 65 ans (en comparaison du reste de la population, c'est le plus élevé de l'OCDE 🥇) - - **25 ans** : le nombre d'années passées en moyenne à la retraite (le plus élevé de l'OCDE 🥇) - - **75 %** : le taux de remplacement en pourcentage du salaire net à taux plein - - La retraite est la plus élevée des cotisations sociales. Elle peut être considérée comme un salaire différé, puisque vos cotisations vous assurerons un revenu futur. - - Simulez et gérez votre retraite sur [info-retraite.fr](https://www.info-retraite.fr/portail-info/home.html). - - références: - CNAV: https://www.lassuranceretraite.fr - OCDE: https://read.oecd-ilibrary.org/social-issues-migration-health/pensions-at-a-glance-2017_pension_glance-2017-en#page135 - INSEE: https://www.insee.fr/fr/statistiques/fichier/3549496/REVPMEN18_F1.21_niv-pauv-pers-agees.pdf - - formule: - somme: - - base - - complémentaire salarié - - complémentaire indépendants - - note: | - Il s'agit d'une estimation a but purement indicatif, afin de comparer la retraite des différents régimes. - On se limite notamment aux hypothèses suivantes : - - On considère que le travailleur a pris sa retraite à taux plein, en cotisant le nombre de trimestres requis (172), ou en partant à l'âge taux plein (67 ans) - - On considère que le travailleur a eu le même revenu tout au long de sa carrière - - On considère que le travailleur est resté dans le même régime tout au long de sa carrière - - On ne prend pas en compte les minorations / majorations - - On ne prend pas en compte les caisses de retraite des professions libérales réglementées (les 10 sections de la Cnavpl et la Cnbf) - - On ne calcule pas le nombre de trimestres validés par année - -protection sociale . retraite . plr: - applicable si: - toutes ces conditions: - - entreprise . activité . libérale réglementée - - dirigeant . indépendant . PL . option régime général = non - remplace: retraite - rend non applicable: complémentaire indépendants - titre: Retraite profession libérale réglementée - description: Nous n'avons pas implémenté les règles spécifiques aux professions libérales relementées. - valeur: "'Non implémenté'" - -protection sociale . retraite . base: - titre: pension de retraite de base - formule: - produit: - taux: taux de la pension - plafond: plafond sécurité sociale temps plein - assiette: revenu moyen - note: Les impatriés bénéficient d'une exonération de cotisation vieillesse. En contrepartie, ils n'acquièrent aucun droit pendant la durée d'exonération. - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F21552 - -protection sociale . retraite . base . taux de la pension: - description: Le taux appliqué, avec décote ou surcote en fonction du nombre de trimestres cotisés. - formule: - variations: - - si: trimestres validés = 0 - alors: 0% - - sinon: 50% - note: On ne prends pas en compte la décote du taux suite aux trimestres manquant. On considère donc que le cotisant part à taux plein, donc à 67 ans (ou avant si tous les trimestres sont validés). - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F19666 - -protection sociale . retraite . trimestres validés: - unité: trimestres validés/an - formule: - somme: - - trimestres salarié - - trimestres indépendant - - trimestres auto-entrepreneur - plafond: 4 - -protection sociale . retraite . trimestres validés . trimestres salarié: - unité: trimestres validés/an - applicable si: contrat salarié - formule: barème trimestres générique - -protection sociale . retraite . trimestres validés . trimestres indépendant: - unité: trimestres validés/an - applicable si: dirigeant . indépendant - formule: - variations: - - si: situation personnelle . RSA - alors: barème trimestres générique - - sinon: - valeur: barème trimestres générique - plancher: 3 - -protection sociale . retraite . trimestres validés . barème trimestres générique: - unité: trimestres validés/an - formule: - grille: - unité: trimestres validés/an - assiette: revenu moyen - multiplicateur: SMIC horaire - tranches: - - montant: 0 - plafond: 150 heures/an - - montant: 1 - plafond: 300 heures/an - - montant: 2 - plafond: 450 heures/an - - montant: 3 - plafond: 600 heures/an - - montant: 4 - références: - cnav.fr: https://www.legislation.cnav.fr/Pages/bareme.aspx?Nom=salaire_validant_un_trimestre_montant_bar - -protection sociale . retraite . trimestres validés . trimestres auto-entrepreneur: - applicable si: dirigeant . auto-entrepreneur - description: Les seuils de chiffre d'affaires minimum pour la validation des trimestres pour la retraite en auto-entrepreneur. En-dessous du montant minimum, vous n'aurez accès qu'à l'allocation de solidarité. - unité: trimestres validés/an - somme: - - grille: - assiette: entreprise . chiffre d'affaires . vente restauration hébergement - tranches: - - montant: 0 - plafond: 4137 €/an - - montant: 1 - plafond: 7286 €/an - - montant: 2 - plafond: 10426 €/an - - montant: 3 - plafond: 20740 €/an - - montant: 4 - - grille: - assiette: entreprise . chiffre d'affaires . service BNC - tranches: - - montant: 0 - plafond: 2880 €/an - - montant: 1 - plafond: 5062 €/an - - montant: 2 - plafond: 7266 €/an - - montant: 3 - plafond: 9675 €/an - - montant: 4 - - grille: - unité: trimestres validés/an - assiette: entreprise . chiffre d'affaires . service BIC - tranches: - - montant: 0 - plafond: 2412 €/an - - montant: 1 - plafond: 4239 €/an - - montant: 2 - plafond: 6071 €/an - - montant: 3 - plafond: 12030 €/an - - montant: 4 - références: - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23369 - -protection sociale . revenu moyen: - description: Le revenu utilisé pour le calcul du montant des pensions de retraite et des indemnités journalières de sécurité sociale lors d'un arrêt de travail. - notes: Normalement, on prend le revenu moyen des 25 meilleures années pour la retraite et des 3 derniers mois pour les indémnités. Vu qu'on intègre pas la notions de temporalité avec notre simulateur, on simplifie en prenant le même. - unité: €/an - - formule: - plancher: 0 €/mois - le maximum de: - - dirigeant . indépendant . revenu professionnel - - dirigeant . auto-entrepreneur . impôt . revenu imposable - - contrat salarié . rémunération . brut - -protection sociale . retraite . mois cotisés: - unité: mois - formule: 172 trimestres * 3 mois/trimestre - notes: On prend l'hypothèse d'une retraite à taux plein pour un travailleur né en 1973 ou après - -protection sociale . retraite . complémentaire salarié: - formule: points acquis * valeur du point - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F15396 - -protection sociale . retraite . complémentaire salarié . valeur du point: - formule: 1.2714 €/point/an - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F15396 - agirc-arrco: https://www.agirc-arrco.fr/ressources-documentaires/chiffres-cles/ - -protection sociale . retraite . complémentaire salarié . points acquis: - unité: points/mois - note: | - On se base sur une valeur constante du point, hors cette dernière change d'année en année, cette valeure est donc une grossière approximation - formule: mois cotisés * contrat salarié . retraite complémentaire / 17.3982 €/point - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F15396 - -protection sociale . retraite . complémentaire indépendants: - non applicable si: - toutes ces conditions: - - entreprise . activité = 'libérale' - - dirigeant . indépendant . PL . régime général = non - formule: total points acquis * valeur du point - références: - secu-independants.fr: https://www.secu-independants.fr/retraite/calcul-retraite/retraite-complementaire/ - -protection sociale . retraite . complémentaire indépendants . valeur du point: - formule: 1.191 €/point/an - références: - secu-independants.fr: https://www.secu-independants.fr/baremes/prestations-vieillesse-et-invalidite-deces - -protection sociale . retraite . complémentaire indépendants . total points acquis: - formule: points acquis * mois cotisés - -protection sociale . retraite . complémentaire indépendants . points acquis: - unité: points/an - valeur: dirigeant . indépendant . cotisations et contributions . retraite complémentaire / prix d'achat du point - -protection sociale . retraite . complémentaire indépendants . prix d'achat du point: - formule: 17.515 €/point - notes: il s'agit du prix d'achat 2018 (la valeur pour 2019 sur le site secu-independants.fr est marquée comme N.C) - références: - secu-independants.fr: https://www.secu-independants.fr/baremes/baremes-2018/baremesprestations-maladie-maternite/?reg=ile-de-france-centre&ae=oui - -protection sociale . santé: - icônes: 🏥 - type: branche - résumé: Couvre la plupart des soins de santé de la vie quotidienne et 100 % des maladies graves comme les séjours à l'hôpital. - description: | - L’Assurance Maladie protège durablement la santé de chacun dans sa vie personnelle ou professionnelle. - - Concrètement, elle accompagne 60 millions d’assurés tout au long de leur vie, en prenant en charge leurs soins quels que soient leurs ressources, leur situation ou leur état de santé. Elle garantit ainsi un accès universel aux droits et elle permet l’accès aux soins. - - Grâce à elle, vous êtes couvert sur la plupart des soins de santé. En cas de maladie grave ou de longue durée, 100 % des soins sont remboursés. - - ## L'assurance maladie en France en quelques chiffres - - **92 %** des dépenses de santé remboursées en moyenne par l'assurance maladie et la complémentaire - - **30 000 € / an / patient** : exemple de prise en charge complète pour une personne atteinte de mucoviscidose - - **1 468 € / mois** : indémnité versée par la sécurité sociale pour un congé maternité (salaire moyen) - - **82,4 ans** d’espérance de vie moyenne en france (dans le top 10 mondial 🏅) - - références: - ameli.fr: https://assurance-maladie.ameli.fr/sites/default/files/ra-2017_agir-ensemble-proteger-chacun.pdf - OCDE: https://read.oecd-ilibrary.org/social-issues-migration-health/health-at-a-glance-europe-2018_health_glance_eur-2018-en#page89 - -protection sociale . invalidité et décès: - icônes: 🦽 - type: branche - résumé: Garantit le versement d'une pension en cas d'invalidité et un capital à vos proches en cas de décès. - description: | - Vous pouvez être reconnu invalide si votre capacité de travail et de gain est réduite d'au moins 2/3 à la suite d'un accident ou d'une maladie d'origine non professionnelle. Vous pouvez obtenir le versement d'une pension d'invalidité afin de compenser la perte de revenus. - - Le capital décès est une indemnité qui garantit le versement d'un capital aux ayants droit d'un travailleur décédé, sous certaines conditions. Son montant est forfaitaire. - références: - capital décès (amelie.fr): https://www.ameli.fr/assure/remboursements/pensions-allocations-rentes/deces-proche-capital-deces - capital décès (salarié privé): https://www.service-public.fr/particuliers/vosdroits/F3005 - pension invalidité: https://www.service-public.fr/particuliers/vosdroits/F672 - -protection sociale . santé . indemnités journalières: - description: >- - Les indemnités journalières vous sont versées par l'Assurance Maladie pour compenser - votre revenu pendant un arrêt de travail. Elles sont calculées à partir de votre revenu - brut et versées tous les 14 jours en moyenne. - non applicable si: - toutes ces conditions: - - entreprise . activité . libérale réglementée - - dirigeant . indépendant . PL . régime général = non - unité: €/jour - note: | - Nous n'avons implémenté les indemnités des régimes particuliers des - professions libérales réglementées. Pour une liste exaustive des - indemnisation, consultez [ce - site](https://www.coover.fr/prevoyance/tns/arret-maladie-profession-liberale) - formule: - somme: - - indemnités journalières . auto-entrepreneur - - indemnités journalières . indépendant - - indemnités journalières . salarié - -protection sociale . santé . indemnités journalières . auto-entrepreneur: - applicable si: dirigeant . auto-entrepreneur - unité: €/jour - - formule: - variations: - - si: revenu moyen < 3919.20 €/an - alors: 0 €/jour - - sinon: - produit: - assiette: revenu moyen - taux: 50% - plafond: 55.51 €/jour - reférences: - - secu-independants.fr: https://www.secu-independants.fr/sante/indemnites-journalieres/montant-de-lindemnite - -protection sociale . santé . indemnités journalières . indépendant: - applicable si: dirigeant . indépendant - unité: €/jour - formule: - produit: - assiette: revenu moyen - taux: 50% - plancher: 21 €/jour - plafond: 55.51 €/jour - reférences: - - secu-independants.fr: https://www.secu-independants.fr/sante/indemnites-journalieres/montant-de-lindemnite - -protection sociale . santé . indemnités journalières . salarié: - unité: €/jour - - notes: Vu que le simulateur ne permet pas encore la conversion de période vers le jour, on multiplie le salaire moyen par 3 pour avoir le salaire trimestriel, puis on le divise par 91.25, conformément à la fiche service-public.fr - applicable si: contrat salarié - formule: - produit: - assiette: revenu moyen - taux: 50% - plafond: 1.8 * SMIC temps plein - reférences: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F3053 - -protection sociale . assurance chômage: - icônes: 💸 - type: assurance - résumé: Assure un revenu aux travailleurs à la recherche d'un nouvel emploi. - description: > - Depuis 1958, l’Assurance chômage protège tous les salariés du privé et certains du secteur public lorsqu’ils perdent leur emploi. Elle leur verse une allocation et favorise leur retour à l’emploi grâce à des aides. - - Fonctionnant comme une assurance, elle indemnise ceux qui ont cotisé, en fonction de leur ancien salaire. Mais elle est aussi solidaire, puisqu’elle mutualise les risques et compense mieux la perte d’un bas salaire que d’un haut revenu. - - Grâce à elle, tous ceux qui perdent leur emploi de façon involontaire peuvent toucher un revenu sous forme d’allocation à condition d’avoir cotisé suffisamment. - - ## L'assurance chômage en France en quelques chiffres - - **72 %** de l'ancien salaire net : pourcentage de l'allocation chômage en moyenne - - **2,7 millions** de chômeurs indemnisés chaque mois - - **1 020 €** : montant de l'allocation nette moyenne par mois - - **51 %** des allocataires cumulent allocation et salaire - - références: - Pôle-emploi: https://www.pole-emploi.fr/accueil - Unédic: https://www.unedic.org/a-propos/quest-ce-que-lassurance-chomage - -protection sociale . famille: - icônes: 👶 - type: branche - résumé: | - Assure des prestations en soutien aux familles : garde d'enfants, aide au logement... - description: | - Créée en 1945, la branche Famille est l’un des principaux acteurs de la politique familiale française. Actuellement, elle a deux missions prioritaires : - - Aider les familles dans leur vie quotidienne, faciliter, en particulier, la conciliation entre vie familiale et vie professionnelle - - Développer la solidarité envers les plus vulnérables, dont les personnes handicapées - - Pour remplir ces missions, elle s’appuie sur deux leviers : - - Le versement de prestations financières aux familles (prestations familiales et sociales, aides au logement et minima sociaux comme l’aide aux adultes handicapés et le revenu de solidarité active) - - L’accompagnement des familles et la mise en place ou le cofinancement de différents - services et équipements collectifs qui leur sont destinés (comme les crèches) - - ## Les allocations familiales en France en quelques chiffres - - **19 %** part des dépenses allouées à la petite enfance - - **860 € / mois** : montant de l'allocation aux adultes handicapés - - **75 %** des mères avec un enfant à charge travaillent (dont 70% à temps plein) - - références: - CAF: https://www.caf.fr/sites/default/files/plaquette branche famille francais.pdf - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F12242 - -protection sociale . accidents du travail et maladies professionnelles: - icônes: ☣️ - résumé: Offre une couverture complète des maladies ou accidents du travail. - description: | - L’assurance AT/MP (accident du travail et maladie professionnelle) est la plus ancienne branche de la Sécurité sociale : elle relève de principes qui remontent à l’année 1898 et qui ont été repris dans la loi du 31 décembre 1946. - - [🎞️ Voir la vidéo](https://www.youtube.com/watch?v=NaGI_deZJD8 ) - - La cotisation AT/MP couvre les risques accidents du travail, accidents de trajet et maladies professionnelles pour les salariés relevant du régime général. - - Cette cotisation est obligatoire et à la charge exclusive de l’employeur. - - Pour connaître les risques professionnels et mettre en place des actions de prévention, le [compte AT/MP](https://www.ameli.fr/paris/entreprise/cotisations/mp-tarification-calculs-baremes/compte-mp) est un service ouvert à toutes les entreprises du régime général de la Sécurité sociale. - - En cas d’AT/MP, les soins médicaux et chirurgicaux sont remboursés intégralement dans la limite des tarifs de la Sécurité sociale. - - unité: €/jour - - applicable si: contrat salarié - formule: - produit: - assiette: - valeur: revenu moyen - plafond: 83.4% * plafond sécurité sociale temps plein - taux: - nom: Pourcentage du salaire journalier de référence - valeur: 60% - note: | - Le taux est de 80% à partir du 29e jour d'arrêt. - références: - ameli.fr: https://www.ameli.fr/paris/entreprise/cotisations/mp-tarification-calculs-baremes/compte-mp - service-public.fr (AT): https://www.service-public.fr/particuliers/vosdroits/F31881 - service-public.fr (MP): https://www.service-public.fr/particuliers/vosdroits/F31880 - Calcul de l'indemnité: https://www.service-public.fr/particuliers/vosdroits/F32148 - Code de la Sécurité Sociale: https://www.legifrance.gouv.fr/codes/id/LEGISCTA000006156659/2020-12-10/ - -protection sociale . formation: - icônes: 👩‍🎓 - résumé: Finance la possibilité de suivre des formations professionnelles. - description: | - La formation professionnelle permet à chaque personne, indépendamment de son statut, d’acquérir et d’actualiser ses connaissances et ses compétences, d’accroître son niveau de qualification et de favoriser son évolution professionnelle. - - Pour avoir un compte-rendu personnalisé de vos droits à la formation, rendez-vous sur [www.moncompteactivite.gouv.fr](https://www.moncompteactivite.gouv.fr). - -protection sociale . autres: - icônes: 🔧 - résumé: Autres contributions au système social. - description: | - Toutes les contributions transverses au système social. - - On y retrouve par exemple la CRDS (contribution pour le remboursement de la dette sociale) qui est un impôt destiné à résorber l'endettement de la Sécurité sociale, et ainsi assurer la viabilité de la protection sociale pour vos enfants et petits enfants. - -protection sociale . transport: - icônes: 🚌 - résumé: Permet de maintenir le prix d'un billet de transport en commun à un bas prix - description: | - Cette contribution est reversée intégralement à l'[autorité organisatrice de la mobilité](https://fr.wikipedia.org/wiki/Autorit%C3%A9_organisatrice_de_la_mobilit%C3%A9) de la zone ou est implantée l'entreprise. Celle-ci peut ensuite l'utiliser pour subventionner les transports en commun existants ou pour développer de nouvelles infrastructures de transport (tramway, métro, bus...). - - ## Le versement transport en quelques chiffres - - **45% de réduction** sur le coût des transports en communs dans les 12 plus grandes agglomérations de France. - - **263 € / an / habitant** de gain de pouvoir d'achat pour les habitants d'Île-de-France - références: - wikipedia: https://fr.wikipedia.org/wiki/Versement_transport diff --git a/modele-social/règles/salarié.yaml b/modele-social/règles/salarié.yaml deleted file mode 100644 index 5e4e7160a..000000000 --- a/modele-social/règles/salarié.yaml +++ /dev/null @@ -1,3807 +0,0 @@ -contrat salarié: - icônes: 📄 - question: De quel type de contrat s'agit-il ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - CDI - - CDD - - apprentissage - - professionnalisation - - stage - par défaut: "'CDI'" - description: | - Le contrat qui lie une entreprise (via son établissement) à un individu, qui est alors son salarié. - - Le contrat n'est en fait pas nécessaire dans le droit français, il est possible d'employer quelqu'un sans contrat par exemple dans les cas suivants: - - Particuliers employeurs : Plus de 8 heures par semaine ou de plus de 4 semaines consécutives dans l'année. - - CDI : La signature d’un contrat de travail n’est pas obligatoire dans certains cas. C’est le cas du Contrat de travail à Durée Indéterminée, considéré comme la forme normale et générale de la relation de travail entre un salarié et un employeur (Art. L1221-2 du Code du travail). - -contrat salarié . CDI: - formule: contrat salarié = 'CDI' - -contrat salarié . ancienneté: - formule: - durée: - depuis: date d'embauche - -contrat salarié . ancienneté . date d'embauche: - question: Quelle est la date d'embauche du salarié ? - par défaut: 01/01/2021 - suggestions: - Début 2021: 01/01/2021 - Début 2020: 01/01/2020 - Fin 2017: 31/12/2017 - type: date - -contrat salarié . salarié majeur: - question: Le salarié est-il majeur ? - par défaut: oui - -contrat salarié . frais professionnels: - titre: remboursement de frais - unité: €/mois - description: >- - Les frais professionnels correspondent à des dépenses engagées par le - salarié pour les besoins de son activité professionnelle. Ces frais sont - ensuite remboursés par l’employeur. - - - Le dédommagement de ces frais peut prendre la forme : - - - d’un remboursement des dépenses réelles sur justificatifs ; - - - d’un versement d’allocations forfaitaires ; - - - de l’application d’une déduction forfaitaire spécifique sur le salaire soumis à cotisations ; cette possibilité n’étant ouverte qu’à certaines professions. - - - Les frais professionnels sont généralement exclus de la base de calcul des - cotisations de sécurité sociale et de la CSG-CRDS, sauf en cas de - dépassement de plafond pour les remboursements forfaitaires ( - frais de panier, titres-restaurant, forfait mobilités durables...). - formule: - somme: - - titres-restaurant . montant . employeur - - abonnement transports publics . prise en charge - - transports personnels . montant - -contrat salarié . frais professionnels . part déductible: - titre: Frais professionnels déductibles - description: >- - Part des frais déduite de l'assiette de cotisation sociale et pour le calcul - de l'impôt sur le revenu. - formule: - somme: - - titres-restaurant . part déductible - - abonnement transports publics . prise en charge - - transports personnels . part déductible - -contrat salarié . frais professionnels . titres-restaurant: - icônes: 🍽️ - description: >- - Le titre-restaurant est un titre spécial de paiement « des repas » remis par - l’employeur au salarié. - - - Le salarié ne peut utiliser les titres-restaurant en sa possession que pour - régler la consommation : - - - d’un repas, - - - de préparations alimentaires directement consommables, - - - de fruits et légumes. - - Ce titre peut être émis sur support papier ou sous forme dématérialisée. - question: Le salarié reçoit-il des titres-restaurant ? - par défaut: non - -contrat salarié . frais professionnels . titres-restaurant . montant: - titre: Titres-restaurant - formule: - produit: - assiette: montant unitaire - facteur: nombre - composantes: - - attributs: - nom: employeur - taux: taux participation employeur - - attributs: - nom: salarié - taux: 100% - taux participation employeur - -contrat salarié . frais professionnels . titres-restaurant . part déductible: - titre: Titres-restaurant (déductible) - formule: - valeur: montant . employeur - plafond: - produit: - assiette: nombre - facteur: 5.55 €/titres-restaurant - références: - urssaf.fr: https://www.urssaf.fr/portail/home/taux-et-baremes/frais-professionnels/les-titres-restaurant.html - -contrat salarié . frais professionnels . titres-restaurant . nombre: - question: Combien de titres-restaurant sont distribués au salarié ? - arrondi: oui - par défaut: - produit: - assiette: 19 titres-restaurant/mois - facteur: temps de travail . quotité de travail - suggestions: - 5 repas/semaines: 5 titres-restaurant/semaines * période . semaines par mois - 3 repas/semaine: 3 titres-restaurant/semaines * période . semaines par mois - - -contrat salarié . frais professionnels . titres-restaurant . montant unitaire: - question: Quelle est la valeur unitaire du titre-restaurant ? - description: >- - Il n'y a pas de valeur maximale ou minimale pour les titres-restaurant. En - revanche, pour bénéficier de l'exonération de cotisation, il ne faut pas - dépasser 11,08€ par titre en 2021. - par défaut: 8 €/titre-restaurant - suggestions: - faible: 6 €/titre-restaurant - moyenne: 8 €/titre-restaurant - max exonéré: 11.10 €/titre-restaurant - -contrat salarié . frais professionnels . titres-restaurant . taux participation employeur: - description: >- - Part du titre-restaurant payée par l'employeur. Doit être de 50% minimum et - de 60% maximum. - question: Quelle est la participation de l'employeur ? - par défaut: 50 % - suggestions: - 50%: 50 % - 60%: 60 % - -contrat salarié . frais professionnels . titres-restaurant . contrôle taux employeur min: - type: notification - sévérité: avertissement - formule: taux participation employeur < 50% - description: La part employeur du titre-restaurant doit être de 50% au minimum - -contrat salarié . frais professionnels . titres-restaurant . contrôle taux employeur max: - type: notification - sévérité: avertissement - formule: taux participation employeur > 60% - description: La part employeur du titre-restaurant doit être de 60% au maximum - -contrat salarié . frais professionnels . abonnement transports publics: - icônes: 🚍 - valeur: oui - -contrat salarié . frais professionnels . abonnement transports publics . montant: - titre: Abonnement aux transports publics - question: Quel montant le salarié dépense-t-il en abonnement aux transports publics chaque mois ? - unité: €/mois - par défaut: 0 €/mois - description: | - L'employeur doit prendre en charge 50% du montant dépensé par le salarié pour les transports publics lui permettant de se rendre sur son lieu de travail. - - Cette prise en charge (dans la limite des 50% du montant) est exonérée de cotisations sociales et d'impôt sur le revenu. - - Dans le cas d'un temps partiel, le taux de prise en charge sera le même pour un mi-temps ou plus. En dessous, le taux de prise en charge sera proportionnel. - références: - Articles R3261-1 à -10 du code du travail, version 01/01/2009: https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000020080272/2009-01-01 - Article 81 du code des impôts, version en vigueur au 31/12/2020: https://www.legifrance.gouv.fr/codes/id/LEGIARTI000042910732/2020-12-31/ - suggestions: - Navigo: 75 €/mois - Técély: 65 €/mois - RTM: 40 €/mois - Tisséo: 42.50 €/mois - TBM: 42.20 €/mois - - - -contrat salarié . frais professionnels . abonnement transports publics . taux de participation employeur: - valeur: 50% - -contrat salarié . frais professionnels . abonnement transports publics . taux de prise en charge: - titre: Taux de prise en charge - valeur: - produit: - assiette: - le minimum de: - - temps de travail . quotité de travail - - 50% - taux: 2 * taux de participation employeur - -contrat salarié . frais professionnels . abonnement transports publics . prise en charge: - titre: Abonnement transports publics, part prise en charge par l'employeur (déductible) - unité: €/mois - valeur: taux de prise en charge * montant - -contrat salarié . frais professionnels . transports personnels: - valeur: oui - non applicable si: déduction forfaitaire spécifique - références: - circ. DGT-DSS 2009-1 du 28 janvier 2009: https://www.legifrance.gouv.fr/download/file/pdf/cir_2423/CIRC - -contrat salarié . frais professionnels . transports personnels . montant: - titre: Transports personnels - valeur: - somme: - - carburant faible émission . montant - - forfait mobilités durables . montant - -contrat salarié . frais professionnels . transports personnels . part déductible: - valeur: - somme: - - carburant faible émission . part déductible - - forfait mobilités durables . part déductible - -contrat salarié . frais professionnels . transports personnels . proportion déduction: - titre: Facteur de proportion de la déductibilité - valeur: - produit: - assiette: - le minimum de: - - temps de travail . quotité de travail - - 50% - taux: 200% - références: - Article R3261-14 du code du travail, version 11/05/2020: https://www.legifrance.gouv.fr/codes/id/LEGIARTI000041865023/2020-05-11/ - -contrat salarié . frais professionnels . transports personnels . carburant faible émission: - valeur: oui - -contrat salarié . frais professionnels . transports personnels . carburant faible émission . montant: - titre: Prise en charge du carburant pour véhicule électrique, hybride rechargeable ou hydrogène - question: Quel montant l'employeur prend-il en charge des dépenses en carburant pour véhicule électrique, hybride rechargeable ou hydrogènes? - unité: €/an - par défaut: 0 €/an - description: | - L'employeur peut prendre en charge tout ou partie des frais de carburant dépensés par l'employé pour son véhicule électriques, hybrides rechargeables ou hydrogènes, sur présentation de justificatif. - - Cette prise en charge peut profiter d'une exonération des cotisations sociales et de l'impôt sur le revenu. Le montant maximal déductible est de 200€/an, mais attention - - - le plafond est partiellement réduit du montant de la prise en charge des frais d'abonnement aux transports publics - - - cette prise en charge de carburant entre dans la même assiette que la prise en charge du forfait mobilités durables. - - Dans le cas d'un temps partiel, l'avantage sera le même pour un mi-temps ou plus. En dessous, un facteur proportionnel sera appliqué. - - Pour verser une prime de salaire équivalente à 200€/an à son salarié sans ce dispositif, **l'employeur devrait débourser près de 500€ pour un salaire médian**. - références: - Articles R3261-11 à -13 du code du travail, version 11/05/2020: https://www.legifrance.gouv.fr/codes/section_lc/LEGITEXT000006072050/LEGISCTA000018487476/2020-05-11 - Article 81 du code des impôts, version en vigueur au 31/12/2020: https://www.legifrance.gouv.fr/codes/id/LEGIARTI000042910732/2020-12-31/ - -contrat salarié . frais professionnels . transports personnels . carburant faible émission . part déductible: - titre: Prise en charge du carburant pour véhicule électrique, hybride rechargeable ou hydrogène (part déductible) - unité: €/an - valeur: montant - plafond: - le minimum de: - - proportion déduction * 200€/an - - valeur: proportion déduction * 500€/an - abattement: abonnement transports publics . prise en charge - -contrat salarié . frais professionnels . transports personnels . forfait mobilités durables: - valeur: oui - -contrat salarié . frais professionnels . transports personnels . forfait mobilités durables . montant: - titre: Prise en charge des frais de transports forfait mobilités durables - question: Quel montant l'employeur prend-il en charge dans le cadre du forfait mobilités durables ? - unité: €/an - par défaut: 0 €/an - description: | - L'employeur peut prendre en charge tout ou partie des frais de déplacement liés à l'utilisation des véhicules entrant dans le cadre du forfait mobilités durables - - - le vélo et vélo à assistance électrique - - - le covoiturage (conducteur ou passager) - - - les engins de déplacement personnels en location ou en libre-service - - - l'autopartage avec des véhicules électriques, hybrides rechargeables ou hydrogènes - - - les transports en commun (hors abonnement). - - L'ancienne Indemnité Kilométrique Vélo entre maintenant dans ce dispositif. Elle peut être poursuivie mais son montant devra être imputé ici. - - L'employeur peut prendre en charge ces frais jusqu'à 500€/an de manière exonérée de cotisations sociales et d'impôt. Attention cependant - - - le plafond est réduit du montant de la prise en charge des frais d'abonnement aux transports publics - - - la prise en charge du carburant faible émission entre dans cette assiette également. - - Dans le cas d'un temps partiel, l'avantage sera le même pour un mi-temps ou plus. En dessous, un facteur proportionnel sera appliqué. - - Pour verser une prime de salaire équivalente à 500€/an à son salarié sans ce dispositif, **l'employeur devrait débourser près de 800€ pour un salaire médian**. - références: - Articles R3261-13-1 à -13-2 du code du travail, version 11/05/2020: https://www.legifrance.gouv.fr/codes/section_lc/LEGITEXT000006072050/LEGISCTA000018487476/2020-05-11 - Article 81 du code des impôts, version en vigueur au 31/12/2020: https://www.legifrance.gouv.fr/codes/id/LEGIARTI000042910732/2020-12-31/ - -contrat salarié . frais professionnels . transports personnels . forfait mobilités durables . part déductible: - titre: Prise en charge des frais de transports forfait mobilités durables (part déductible) - unité: €/an - valeur: montant - plafond: - valeur: proportion déduction * 500€/an - abattement: - somme: - - abonnement transports publics . prise en charge - - carburant faible émission . part déductible - -contrat salarié . activité partielle: - question: Le salarié est-il en chômage partiel ? - description: >- - À la suite de la crise du Coronavirus, le gouvernement a mis en place un - dispositif de chômage partiel étendu dans lequel l'État prend en charge - l'indemnisation des heures chômées jusqu’à 4,5 SMIC. - - La déclaration d'activité partielle est simplifiée et l'effet est - rétroactif. - par défaut: non - rend non applicable: - - temps de travail . heures supplémentaires - - temps de travail . heures complémentaires - références: - déclaration employeur: https://activitepartielle.emploi.gouv.fr/aparts/ - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23503 - economie.gouv.fr: https://www.economie.gouv.fr/entreprises/activite-partielle - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/reduire-ou-cesser-lactivite/lactivite-partielle.html - -contrat salarié . activité partielle . rémunération mensuelle minimale: - acronyme: RMM - description: >- - Les salariés à temps plein dont l’horaire de travail est réduit ont droit à - une rémunération mensuelle minimale qui peut donner lieu à un versement - complémentaire de l’employeur. - références: - Article L3232-3 du code du travail: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000006902847&cidTexte=LEGITEXT000006072050&dateTexte=20080501 - formule: - recalcul: - règle: contrat salarié . rémunération . net de cotisations - avec: - rémunération . brut de base: SMIC contractuel - activité partielle: non - temps de travail . heures supplémentaires: non - temps de travail . heures complémentaires: non - -contrat salarié . activité partielle . heures chômées: - unité: heures/mois - formule: - valeur: temps de travail . temps contractuel - heures travaillées - plancher: 0 - -contrat salarié . activité partielle . heures travaillées: - titre: heures travaillées restantes - question: Quelle est le nombre d'heures travaillées sur le mois ? - description: >- - Dans le cadre du chômage partiel, le nombre d'heure restantes travaillées. Doit être - inférieur au temps contractuel. - par défaut: 0 heures/mois - suggestions: - 30 h/semaine: 130 heures/mois - 20 h/semaine: 86.6666 heures/mois - 10 h/semaine: 43.3333 heures/mois - -contrat salarié . activité partielle . heures travaillées . contrôle temps de travail: - type: notification - sévérité: avertissement - formule: heures travaillées > temps de travail . temps contractuel - description: >- - Dans le cadre de l'activité partielle, le temps de travail doit être inférieur - à celui inscrit dans le contrat de travail. - -contrat salarié . activité partielle . indemnités: - titre: indemnités activité partielle - description: >- - La mise en chômage partiel ouvre droit non au paiement d’un salaire mais à - l’allocation spécifique. Pour chaque heure chômée indemnisable, le salarié - reçoit de l'entreprise une indemnité. L'entreprise obtient en contrepartie de - l’Etat une allocation d’activité partielle. - - Si après versement de l’indemnité d’activité partielle la rémunération du - salarié est inférieure à la rémunération mensuelle minimale (RMM garantie par - les articles L3232-1 et suivants du code du travail pour les salariés à temps - plein), l'employeur doit lui verser une allocation complémentaire qui est égale - à la différence entre la rémunération mensuelle minimale (ou Smic net) et la - somme initialement perçue par le salarié. - - formule: - somme: - - base - - complémentaire - - conventionnelle - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/reduire-ou-cesser-lactivite/lactivite-partielle.html - -contrat salarié . activité partielle . indemnités . base: - titre: indemnités d'activité partielle de base - formule: - multiplication: - assiette: retrait absence - taux: 70% - -contrat salarié . activité partielle . indemnités . complémentaire: - titre: indemnité complémentaire - description: >- - L'indemnité complémentaire de chômage partielle est une indemnité versée par - l'entreprise pour les salaires proches du SMIC permettant de s'assurer que - rémunération effectivement perçue ne soit jamais inférieure à celle - du SMIC net. - # La condition suivante assure que cette règle ne crée pas de boucle avec indemnités . conventionnelle . part soumise à cotisation - non applicable si: rémunération . brut de base > 3.15 * SMIC - formule: - valeur: rémunération mensuelle minimale - abattement: - somme: - - rémunération . net de cotisations - - indemnités . base - - indemnités . conventionnelle - -contrat salarié . activité partielle . indemnités . conventionnelle: - applicable si: convention syntec - formule: - produit: - assiette: retrait absence - taux: - grille: - assiette: rémunération . assiette congés payés - tranches: - - montant: 95% - 70% - plafond: 2000 €/mois - - montant: 80% - 70% - plafond: plafond sécurité sociale temps plein - - montant: 75% - 70% - références: - Legifrance: https://www.legifrance.gouv.fr/affichIDCCArticle.do?idArticle=KALIARTI000028465400&cidTexte=KALITEXT000028465378&dateTexte=29990101&categorieLien=id - Juritravail: https://www.juritravail.com/Actualite/Hygiene-securite-travail-employeur/Id/327284 - -contrat salarié . activité partielle . indemnités . conventionnelle . part soumise à cotisation: - applicable si: - toutes ces conditions: - - indemnités . conventionnelle > 0 - - indemnités . conventionnelle + indemnités . base > 3.15 * SMIC - remplace: contrat salarié . cotisations . assiette - rend non applicable: réduction générale - # règle: contrat salarié . cotisations . assiette - # par: contrat salarié . cotisations . assiette + part soumise à cotisation - formule: - somme: - - contrat salarié . cotisations . assiette - - valeur: activité partielle . indemnités - 3.15 * SMIC - plafond: activité partielle . indemnités . conventionnelle - références: - urssaf.fr: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-employeur/activite-partielle--nouveau-disp.html - Ordonnance du 22 avril 2020, article 5: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000041814597/#JORFARTI000041814602 - -contrat salarié . activité partielle . retrait absence: - titre: retrait activité partielle - formule: - multiplication: - assiette: rémunération . taux horaire - facteur: heures chômées - -contrat salarié . activité partielle . indemnisation entreprise: - titre: Remboursement de l'indemnité d'activité partielle - description: >- - Dans le cadre de la crise du Coronavirus, le gouvernement a annoncé que - l'indemnité de chômage partiel pour les commerces fermés sera prise à - 100% en charge par l'état. - formule: - multiplication: - assiette: retrait absence - taux: taux d'indemnisation - plancher: 8.11 €/heure * heures chômées - plafond: - recalcul: - avec: - rémunération . brut de base: 4.5 * SMIC - -contrat salarié . activité partielle . indemnisation entreprise . taux d'indemnisation: - description: >- - Depuis le 1er juin, le taux d'indemnisation de l'entreprise passe à 60%. - L'indemnité versée à l'employé reste inchangée et c'est donc l'entreprise - qui devra prendre en charge la différence. - - Cette mesure ne concerne pas les secteurs faisant l’objet de restrictions - législatives ou réglementaires particulières en raison de la crise sanitaire - formule: - variations: - - si: secteur d'activité restreint - alors: 70% - - sinon: 60% - -contrat salarié . activité partielle . secteur d'activité restreint: - question: >- - Le secteur d'activité de l'entreprise fait-il l'objet de restrictions - réglementaires ? (ex. : tourisme, restauration, culture, événementiel) - description: >- - Les entreprises accueillant du public qui connaissent une interruption partielle - ou totale de leur activité en raison de l'épidémie de Covid-19 percoivent un taux - d'allocation d'activité partielle majoré. - - Il concerne notamment les secteurs de l'hôtellerie-restauration, du sport, - de la culture et de l’événementiel. - - Les autres entreprises bénéficient d'un taux d'indemnité de droit commun. - références: - Liste des secteurs concernés: https://travail-emploi.gouv.fr/actualites/presse/communiques-de-presse/article/prise-en-charge-a-100-de-l-activite-partielle-par-l-etat-pour-les-entreprises - Actualité service-public.fr: https://www.service-public.fr/particuliers/actualites/A14386 - - par défaut: non - -# TODO : This should be merged with other convention collectives -contrat salarié . activité partielle . convention syntec: - question: La convention collective Syntec est-elle applicable à l'entreprise ? - description: >- - Convention Collective applicable aux salariés des Bureaux d'Études - Techniques, des Cabinets d'Ingénieurs-Conseils et des Sociétés de Conseils. - - Cette convention collective prévoit notamment une majoration de l'indemnité - de chômage partielle au dessus du minimum légal et à la charge de - l'entreprise. - par défaut: non - rend non applicable: - # TODO: this is not working, the question is still displayed. This should be - # fixed but is not critical - - profession spécifique - -contrat salarié . déduction forfaitaire spécifique: - description: >- - Pour une liste précise de professions, l'employeur peut pratiquer une - déduction forfaitaire spécifique pour frais professionnels sur la base de - calcul des cotisations sociales. - applicable si: - toutes ces conditions: - - application - - taux > 0% - titre: assiette avec DFS - remplace: - règle: cotisations . assiette - sauf dans: contrat salarié . CSG et CRDS - # TODO: ajouter pas d'abattement pour l'assurance chômage mais seulement - # pour les journalistes. Nécessite probablement de faire un re-remplacement - # inverse. - formule: - valeur: cotisations . assiette - abattement: - valeur: taux * cotisations . assiette - plafond: 7600 €/an - plancher: cotisations . assiette minimale - références: - Fiche Urssaf.fr: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-elements-a-prendre-en-compte/les-frais-professionnels/la-deduction-forfaitaire-specifi.html - -contrat salarié . déduction forfaitaire spécifique . taux: - formule: - variations: - - si: profession spécifique = 'journaliste' - alors: 20% - - si: profession spécifique = 'ouvrier du bâtiment' - alors: 10% - - si: profession spécifique = 'artiste musicien' - alors: 20% - - si: profession spécifique = 'pilote de ligne ou personnel navigant' - alors: 30% - - sinon: 0% - références: - Circulaire DSS: https://solidarites-sante.gouv.fr/fichiers/bo/2005/05-09/a0090046.htm - -contrat salarié . déduction forfaitaire spécifique . application: - description: >- - La déduction forfaitaire spécifique consiste en un abattement sur l'assiette - des cotisations sociales. L'employeur peut renoncer à appliquer cette - déduction afin d'accorder plus de droits au salarié, notamment en terme de - retraite et d'assurance chômage. - titre: application de la DFS - formule: oui - -contrat salarié . CDD . taxe forfaitaire sur les CDD d'usage: - description: | - À compter du 1er janvier 2020, l'employeur doit s'acquitter d'une taxe - forfaitaire pour chaque conclusion d'un CDD d'usage. L'objectif de cette - taxe est de décourager le recours excessif aux contrats courts. - - Certains secteurs d'activités définis dans le code du travail ne sont pas - concernés par cette taxe. - applicable si: motif = 'classique . usage' - # TODO: cette formule ne fonctionne pas pour des contrats dont la durée est - # inférieure à un mois - formule: 10 € / durée contrat - références: - Urssaf.fr: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-employeur/taxe-forfaitaire-sur-les-cdd-dus.html - -contrat salarié . CDD . CPF: - description: Contribution au financement du compte personnel de formation (CPF) spécifique aux CDD. - cotisation: - destinataire: OPCA - dû par: employeur - branche: formation - non applicable si: - une de ces conditions: - - événement . poursuite du CDD en CDI - - apprentissage - - contrat jeune vacances - - motif = 'classique . saisonnier' - - motif . contrat aidé - formule: - produit: - assiette: cotisations . assiette - taux: 1% - références: - Code du travail - Article L6322-37: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000022234996&cidTexte=LEGITEXT000006072050 - -contrat salarié . CDD . congés pris: - question: Combien de jours de congés seront pris sur la durée du CDD (en jours ouvrés) ? - description: | - Le contrat étant à durée déterminée, le salarié n'a pas forcément le temps de prendre tous les jours de congés qu'il a acquis comme tout salarié au cours du contrat. - Par exemple, pour un contrat de 3 mois, le salarié acquiert 2,08 jours de congés par mois (25 jours / 12 mois = 2,08), donc 6,25 sur la durée du contrat. Or il se peut que l'entreprise le contraigne à n'en prendre que 4, donc 2,25 jours ne seront pas pris. Ils seront payés par l'employeur à la fin du contrat. - unité: jours ouvrés - suggestions: - la totalité: congés dus sur la durée du contrat - la moitié: 50% * congés dus sur la durée du contrat - par défaut: 0 jours ouvrés - -contrat salarié . CDD . jours ouvrés sur la durée du contrat: - produit: - assiette: 253 jours ouvrés/an - facteur: durée contrat - -contrat salarié . CDD . congés dus sur la durée du contrat: - produit: - assiette: 25 jours ouvrés/an - facteur: durée contrat - arrondi: 2 unités - -contrat salarié . CDD . contrôle congés non pris max: - type: notification - sévérité: avertissement - formule: congés pris > congés dus sur la durée du contrat - description: Le nombre de jours de congés pris est supérieur à la totalité des jours de congés acquis sur la durée du contrat (par défaut 25 jours / an) - -contrat salarié . CDD . indemnité compensatrice de congés payés: - titre: indemnité de congés payés - indemnité: - destinataire: salarié - dû par: employeur - description: | - Le salarié en CDD bénéficie des mêmes droits à congés payés que le salarié - en CDI. Il acquiert et prend ses congés payés dans les mêmes conditions. - - Il est cependant courant que le salarié ne puisse pas prendre tous ses - congés avant le terme de son contrat, il bénéficie alors d'une indemnité - compensatrice de congés payés versée par l'employeur. - - Il existe deux méthodes pour calculer l'indemnité de congés non pris. - - ### Méthode "du dixième" - - Ce mode de calcul sera le plus souvent favorable au salarié lorsque celui-ci - a accompli des heures supplémentaires. Une indemnité égale au dixième de la - rémunération brute totale perçue par le salarié au cours de la période de - référence. - - ### Méthode "maintien du salaire" - - Cette méthode sera le plus souvent favorable au salarié lorsque celui-ci a - bénéficié d’une augmentation de salaire. - - Pour effectuer le calcul, l'employeur peut tenir compte soit : - - de l'horaire réel du mois, - - du nombre moyen de jours ouvrés (ou ouvrables), - - du nombre réel de jours ouvrés (ou ouvrables). - - unité: €/mois - non applicable si: événement . poursuite du CDD en CDI - le maximum de: - - nom: Méthode du dixième - produit: - assiette: rémunération . assiette congés payés - taux: 10% - abattement: - nom: proportion congés pris - unité: '%' - valeur: congés pris / congés dus sur la durée du contrat - plafond: 100% - - nom: Méthode du maintien de salaire - produit: - assiette: rémunération . assiette congés payés / jours ouvrés sur la durée du contrat - facteur: - nom: congés non pris - valeur: congés dus sur la durée du contrat - congés pris - - - note: | - L'indemnité est versée à la fin du contrat, sauf si le CDD se poursuit par un CDI. - À noter, la loi El Khomri modifie l'article L3141-12: - - avant : Les congés peuvent être pris dès l'ouverture des droits - - maintenant : Les congés peuvent être pris dès l’embauche - références: - Fiche service-public.gouv.fr: https://www.service-public.fr/particuliers/vosdroits/F2931 - Code du travail - Article L3141-24: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006072050&idArticle=LEGIARTI000006902661&dateTexte=&categorieLien=cid - Congés payés et contrat CDD: https://www.easycdd.com/LEGISLATION-CDD/L-embauche-le-suivi-du-contrat-CDD-les-incidents-frequents/Conges-payes-et-contrat-CDD - assiette de l'indemnité, circulaire DRT 18 du 30 octobre 1990: http://conseillerdusalarie.free.fr/Docs/TextesFrance/19901030Circulaire_DRT_90_18_du_30_octobre_1990_CDD_Travail_temporaire.htm - Méthode du maintien de salaire: https://www.service-public.fr/particuliers/vosdroits/F33359 - -contrat salarié . CDD . prime de fin de contrat: - indemnité: - destinataire: salarié - - alias: prime de précarité - description: Somme versée en fin de CDD comme compensation de précarité. - note: | - Attention : les exceptions sont légion. Conventions collectives... - - - Dans les faits, les CDD Senior perçoivent une indemnité d’un montant équivalent à l’indemnité de précarité : [line](https://www.easycdd.com/LEGISLATION-CDD/Fin-ou-rupture-du-contrat-CDD/La-prime-de-precarite/La-prime-de-precarite-n-est-pas-due-si) - - non applicable si: - une de ces conditions: - # Evènements particuliers - - événement . poursuite du CDD en CDI - - événement . refus CDI avantageux - - # Rupture TODO regrouper cela dans une nouvelle variante - - événement . rupture anticipée salarié - - événement . rupture pour faute grave ou force majeure - - événement . rupture pendant période essai - - - motif = 'classique . usage' - - motif = 'classique . saisonnier' - - motif . complément formation - - motif . contrat aidé - - - contrat jeune vacances - - # TODO Il faudrait pouvoir afficher les indemnités comme une somme de fin de contrat. - # Ici elle est étalée sur un mois moyen - formule: - produit: - assiette: - somme: - - rémunération . brut de base - - rémunération . avantages en nature . montant - - rémunération . primes - - rémunération . heures supplémentaires - taux: 10% - - références: - Code du travail - Article L1243-8: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000006189459&cidTexte=LEGITEXT000006072050 - Fiche Fin du CDD: https://www.service-public.fr/particuliers/vosdroits/F40 - Fiche La prime de précarité est-elle due: https://www.service-public.fr/particuliers/vosdroits/F803 - Le travail saisonnier: http://travail-emploi.gouv.fr/droit-du-travail/contrats-et-carriere/contrats-de-travail/article/le-travail-saisonnier - La prime de précarité n'est pas due si: https://www.easycdd.com/LEGISLATION-CDD/Fin-ou-rupture-du-contrat-CDD/La-prime-de-precarite/La-prime-de-precarite-n-est-pas-due-si - Poursuite de l'activité après la fin du CDD: https://www.easycdd.com/LEGISLATION-CDD/Fin-ou-rupture-du-contrat-CDD/Poursuite-de-l-activite-apres-la-fin-du-contrat-CDD - -contrat salarié . ATMP: - titre: Cotisation Accidents du Travail et Maladies Professionnelles - description: Cotisation due au titre des Accidents du Travail et Maladies Professionnelles. - cotisation: - dû par: employeur - branche: accidents du travail et maladies professionnelles - destinataire: Urssaf - responsable: CARSAT - formule: - produit: - assiette: cotisations . assiette - taux [ref]: - variations: - - si: taux réduit - alors: 0.8% - - si: taux connu - alors: taux personnalisé - - sinon: ATMP . taux collectif ATMP - références: - taux réduit 2020 (code 00.00B): https://www.legifrance.gouv.fr/loda/id/JORFTEXT000039684705 - -contrat salarié . ATMP . taux minimum: - description: >- - Le taux minimum existant pour la cotisation ATMP. Utilisé notamment pour le - calcul de la réduction générale de cotisations - formule: 0.70 % - références: - Article D241-2-4: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000041460928&cidTexte=LEGITEXT000006073189&dateTexte=20200101 - -contrat salarié . ATMP . taux moyen: - formule: 2.24% - références: - Arrêté tarification AT/MP 2020: https://www.legifrance.gouv.fr/loda/id/JORFTEXT000039684705 - -contrat salarié . ATMP . taux réduit: - titre: taux réduit pour activité sans risque - question: L'activité de l'établissement ou du salarié est-elle sans aucun risque ? - description: | - Ce taux correspond : - - aux petites entreprises dont l'activité n'est pas risquée, par exemple du conseil en informatique - - au nouveau "taux support" : pour les entreprises d'effectif inférieur à 150, les salariés des fonctions support (par exemple, secrétariat, comptabilité, ressources humaines) cotisent à un taux réduit. - applicable si: entreprise . effectif <= 150 - par défaut: non - références: - fiche ameli.fr: https://www.ameli.fr/employeur/actualites/evolution-de-la-tarification-de-lassurance-maladie-risques-professionnels-ce-qui-change - -contrat salarié . ATMP . taux connu: - question: Connaissez-vous le taux AT/MP applicable à votre entreprise ? - par défaut: non - description: | - Le taux de la cotisation Accident du Travail et Maladie Professionnel varie selon l'activité de l'entreprise, pour refleter le niveau de risque auxquels sont exposé leur salariés. - - Les entreprises de moins de 20 salariés sont assujetties à ce taux collectif commun à toute leur branche. - - A partir de 150 employé, le taux est individualisé en fonction des relevés réels des accidents et maladies professionnels de l'entreprise. - - Entre les deux, le taux est modulé. - - Ce taux n'a pas d'influence sur le net car il s'agit d'une cotisation à la charge de l'employeur. - références: - Description compte ATMP: https://www.ameli.fr/entreprise/votre-entreprise/compte-accidents-du-travail-et-maladies-professionnelles/mp/teleservice-compte-atmp - Accès compte ATMP (entreprise): https://www.net-entreprises.fr/declaration/compte-atmp/#lessentiel - -contrat salarié . ATMP . taux personnalisé: - question: Quel est le taux AT/MP de l'entreprise ? - description: > - Les entreprises de plus de 20 salariés ont un taux individualisé. L'entreprise peut consulter le taux qui la - par défaut: taux moyen - -contrat salarié . ATMP . taux collectif ATMP: - titre: Taux collectif ATMP - non applicable si: taux connu - question: De quel domaine d'activité dépend votre entreprise ? - description: | - Les entreprises de moins de 20 salariés sont assujetties à ce taux collectif. Pour les entreprises plus importantes, - ce taux est modulé (jusqu'à 150 salariés) voire individualisé (au-delà). - par défaut: taux moyen - references: - Arrêté tarification AT/MP 2020: https://www.legifrance.gouv.fr/loda/id/JORFTEXT000039684705 - -contrat salarié . CDD . événement: - titre: Événement de contrat - question: Pensez-vous être confronté à l'un de ces événements au cours du contrat ? - description: | - Certains événements impactent fortement les obligations du CDD. - - > Par exemple, dans l'hypothèse d'une poursuite du CDD en CDI, aucune majoration ou indemnité sur le CDD ne sera à verser. - - # TODO - # cette règle devrait n'être affichée que quand son espace, CDD, est valide - # CDD devrait être valide seulement si un motif de la liste des possibilités a été choisi - # elle apparaîtrait alors forcément _après_ la question du motif - formule: - une possibilité: - possibilités: - - poursuite du CDD en CDI - - refus CDI avantageux - - rupture anticipée salarié - - rupture pour faute grave ou force majeure - - rupture pendant période essai - par défaut: non - -contrat salarié . CDD . événement . poursuite du CDD en CDI: - titre: Poursuite du CDD en CDI - description: En fin de contrat, le CDD est reconduit en CDI sans interruption. - formule: contrat salarié . CDD . événement = 'poursuite du CDD en CDI' - - # TODO quand cette variable est appelée par une autre variable, - # on devrait pouvoir poser la question, puis proposer un bouton qui permette d'aider l'utilisateur à - # y répondre, en lui expliquant la formule suivante : - # - # formule: - # une possibilité: - # - embauche en CDI suivant le CDD - # - CDD requalifié en CDI # quand ça arrive ? - espace: contrat salarié . CDD . événement -contrat salarié . CDD . événement . refus CDI avantageux: - titre: Refus d'un CDI avantageux - description: Le salarié, au terme du CDD, refuse une reconduction en CDI pour un emploi similaire, et une rémunération au moins aussi avantageuse. - formule: contrat salarié . CDD . événement = 'refus CDI avantageux' - -contrat salarié . CDD . événement . rupture anticipée salarié: - titre: Rupture anticipée du salarié - description: Rupture anticipée du contrat à l'initiative du salarié. - formule: contrat salarié . CDD . événement = 'rupture anticipée salarié' - - # ces variables peuvent être attachées à un groupe ruptures pour plus de clarté -contrat salarié . CDD . événement . rupture pour faute grave ou force majeure: - titre: Rupture pour faute grave ou force majeure - formule: contrat salarié . CDD . événement = 'rupture pour faute grave ou force majeure' - - # ces variables peuvent être attachées à un groupe ruptures pour plus de clarté -contrat salarié . CDD . événement . rupture pendant période essai: - titre: Rupture pendant la période d'essai - formule: contrat salarié . CDD . événement = 'rupture pendant période essai' - # ces variables peuvent être attachées à un groupe ruptures pour plus de clarté -contrat salarié . CDD . motif: - titre: Motif de recours - question: Quel est le motif de recours au CDD ? - description: | - Le CDD est un contrat d'exception: son recours doit être autorisé par l'un des motifs spécifiés dans la loi. - formule: - une possibilité: - choix obligatoire: oui # cette contrainte devrait découler de la valeur CDD en amont, c'est un fix temporaire, qui devra être levé au passage à une simulation CDI / CDD - possibilités: - - classique - - contrat aidé - - complément formation - - issue d'apprentissage - # les CDD d'usage "concentrent la moitié des embauches en CDD" - par défaut: "'classique . usage'" - références: - Code du travail - Articles L1242-1 à 4: https://www.legifrance.gouv.fr/affichCode.do;jsessionid=E318966AA9DEB9E32465297F15B04D86.tpdila20v_1?idSectionTA=LEGISCTA000006195639&cidTexte=LEGITEXT000006072050&dateTexte=20170420 - le recours au CDD: http://www.entreprises.cci-paris-idf.fr/web/reglementation/developpement-entreprise/droit-social/le-recours-au-cdd - embaucher en CDD: https://www.service-public.fr/particuliers/vosdroits/F34 - les cas de recours au CDD: https://www.easycdd.com/LEGISLATION-CDD/Avant-de-rediger-un-contrat-CDD/Les-cas-de-recours-au-contrat-CDD - -contrat salarié . CDD . motif . classique: - titre: motifs classiques - formule: - une possibilité: - possibilités: - - remplacement - - accroissement activité - - saisonnier - - usage - - mission - références: - Code du travail - Article L1242-2: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=714D2E2B814371F4F1D5AA88472CD621.tpdila20v_1?idArticle=LEGIARTI000033024658&cidTexte=LEGITEXT000006072050&dateTexte=20170420 - par défaut: "'usage'" - -contrat salarié . CDD . motif . classique . saisonnier: - titre: Saisonnier - formule: contrat salarié . CDD . motif = 'classique . saisonnier' - description: Emplois à caractère saisonnier, dont les tâches sont appelées à se répéter chaque année selon une périodicité à peu près fixe, en fonction du rythme des saisons ou des modes de vie collectifs. - -contrat salarié . CDD . motif . classique . accroissement activité: - titre: Accroissement temporaire d'activité - formule: contrat salarié . CDD . motif = 'classique . accroissement activité' - description: Accroissement temporaire de l'activité de l'entreprise - -contrat salarié . CDD . motif . classique . remplacement: - titre: Contrat de remplacement - formule: contrat salarié . CDD . motif = 'classique . remplacement' - description: | - Nous regroupons dans cette catégorie les cas suivants. - - - Remplacement d'un salarié en cas : - - D'absence ; - - De passage provisoire à temps partiel, conclu par avenant à son contrat de travail ou par échange écrit entre ce salarié et son employeur ; - - De suspension de son contrat de travail ; - - De départ définitif précédant la suppression de son poste de travail après consultation du comité d'entreprise ou, à défaut, des délégués du personnel, s'il en existe ; - - D'attente de l'entrée en service effective du salarié recruté par contrat à durée indéterminée appelé à le remplacer ; - - - Remplacement d'un chef d'entreprise artisanale, industrielle ou commerciale, d'une personne exerçant une profession libérale, de son conjoint participant effectivement à l'activité de l'entreprise à titre professionnel et habituel ou d'un associé non salarié d'une société civile professionnelle, d'une société civile de moyens d'une société d'exercice libéral ou de toute autre personne morale exerçant une profession libérale ; - - - Remplacement du chef d'une exploitation agricole ou d'une entreprise mentionnée aux 1° à 4° de l'article L. 722-1 du code rural et de la pêche maritime, d'un aide familial, d'un associé d'exploitation, ou de leur conjoint mentionné à l'article L. 722-10 du même code dès lors qu'il participe effectivement à l'activité de l'exploitation agricole ou de l'entreprise ; - -contrat salarié . CDD . motif . classique . mission: - titre: Contrat de mission - formule: contrat salarié . CDD . motif = 'classique . mission' - description: | - > Aussi appelé contrat à objet défini. - - Recrutement d'ingénieurs et de cadres, au sens des conventions collectives, en vue de la réalisation d'un objet défini lorsqu'un accord de branche étendu ou, à défaut, un accord d'entreprise le prévoit et qu'il définit : - - - Les nécessités économiques auxquelles ces contrats sont susceptibles d'apporter une réponse adaptée ; - - Les conditions dans lesquelles les salariés sous contrat à durée déterminée à objet défini bénéficient de garanties relatives à l'aide au reclassement, à la validation des acquis de l'expérience, à la priorité de réembauche et à l'accès à la formation professionnelle continue et peuvent, au cours du délai de prévenance, mobiliser les moyens disponibles pour organiser la suite de leur parcours professionnel ; - - Les conditions dans lesquelles les salariés sous contrat à durée déterminée à objet défini ont priorité d'accès aux emplois en contrat à durée indéterminée dans l'entreprise. - -contrat salarié . CDD . motif . classique . usage: - titre: Contrat d'usage - alias: motif extra - formule: contrat salarié . CDD . motif = 'classique . usage' - description: Emplois pour lesquels, dans certains secteurs d'activité définis par décret ou par convention ou accord collectif de travail étendu, il est d'usage constant de ne pas recourir au contrat de travail à durée indéterminée en raison de la nature de l'activité exercée et du caractère par nature temporaire de ces emplois ; - références: - Embauche en contrat d'extra: https://www.service-public.fr/professionnels-entreprises/vosdroits/F33693 - - formule-futur: #TODO intégrer ça dans le formulaire. Comment ? - contrainte: - variable: entreprise . secteur activité - possibilités: - - Déménagement - - Services à la personne - - Hôtellerie, restauration - - Centre de loisirs et de vacances - - Activité foraine - - Sport professionnel - - Enseignement - - Spectacle - - Action culturelle - - Audiovisuel, production cinématographique, édition phonographique - - Exploitation forestière - - Réparation navale - - Information - - Enquêtes, sondages - - Entreposage et stockage de la viande - - Bâtiment et travaux publics pour les chantiers à l'étranger - - Coopération, assistance technique d'ingénierie et de recherche à l'étranger - - Recherche scientifique dans le cadre d'un accord international (convention, arrangement administratif) - - Assistance technique ou logistique dans les institutions internationales ou dans l'Union européenne prévu par les traités - -contrat salarié . CDD . motif . complément formation: - titre: Complément de formation professionnelle - formule: contrat salarié . CDD . motif = 'complément formation' - description: L'employeur s'engage, pour une durée et dans des conditions déterminées par décret, à assurer un complément de formation professionnelle au salarié. - références: - Code du travail - Article L1242-3: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=714D2E2B814371F4F1D5AA88472CD621.tpdila20v_1?idArticle=LEGIARTI000006901196&cidTexte=LEGITEXT000006072050&dateTexte=20170420 - Code du travail - Décret D1242-3: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000018537448&cidTexte=LEGITEXT000006072050 - -contrat salarié . CDD . motif . issue d'apprentissage: - titre: À l'issue d'un contrat d'apprentissage - formule: contrat salarié . CDD . motif = 'issue d'apprentissage' - description: | - A l'issue d'un contrat d'apprentissage, un contrat de travail à durée déterminée peut être conclu lorsque l'apprenti doit satisfaire aux obligations du service national dans un délai de moins d'un an après l'expiration du contrat d'apprentissage. - références: - Code du travail - Article L1242-4: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=714D2E2B814371F4F1D5AA88472CD621.tpdila20v_1?idArticle=LEGIARTI000028498598&cidTexte=LEGITEXT000006072050&dateTexte=20170420 - -contrat salarié . CDD . motif . contrat aidé: - titre: Contrat aidé (CUI, alternance, ...) - formule: contrat salarié . CDD . motif = 'contrat aidé' - références: - Code du travail - Article L1242-3: https://www.legifrance.gouv.fr/affichCodeArticle.do;jsessionid=714D2E2B814371F4F1D5AA88472CD621.tpdila20v_1?idArticle=LEGIARTI000006901196&cidTexte=LEGITEXT000006072050&dateTexte=20170420 -# TODO Attention : il faudrait peut-être prendre en compte les interdictions du CDD. -# https://www.legifrance.gouv.fr/affichCode.do;jsessionid=B74AE5D2E73ACE3A108B9ADF3BDC8C51.tpdila20v_1?idSectionTA=LEGISCTA000006195640&cidTexte=LEGITEXT000006072050&dateTexte=20170701 - -contrat salarié . CDD . durée contrat: - icônes: ⏳ - titre: durée du contrat - question: Quelle est la durée du contrat ? - description: | - [Cliquez ici](https://www.service-public.fr/professionnels-entreprises/vosdroits/F31211) pour connaître la durée maximale d'un CDD. - références: - Durée maximale d'un CDD (service-public.fr): https://www.service-public.fr/professionnels-entreprises/vosdroits/F31211 - suggestions: - 18 mois: 18 mois - 1 an: 12 mois - 6 mois: 6 mois - 3 mois: 3 mois - # 70% des contrats signés ont concerné, en 2015, des durées inférieures à un mois - par défaut: 1 mois - -contrat salarié . CDD . contrat jeune vacances: - titre: Contrat jeune vacances - question: Est-ce un contrat jeune vacances ? - description: Aussi appelé CDD vendanges. Contrat conclu avec un jeune pendant ses vacances scolaires ou universitaires. - note: Ce n'est pas un motif de CDD. - par défaut: non - -contrat salarié . CDD . indemnités salarié: - description: Cotisations employeur spécifiques au CDD - formule: - somme: - - prime de fin de contrat - - indemnité compensatrice de congés payés - -contrat salarié . apprentissage: - description: | - Le contrat d'apprentissage est un contrat de travail écrit à durée limitée (CDD) ou à durée indéterminée (CDI) entre un salarié et un employeur. Il permet à l'apprenti de suivre une formation en alternance en entreprise sous la responsabilité d'un maître d'apprentissage et en centre de formation des apprentis (CFA) pendant 1 à 3 ans. - formule: contrat salarié = 'apprentissage' - rend non applicable: - - CSG et CRDS - - statut cadre - - statut JEI - - régime des impatriés - - temps de travail . temps partiel - -contrat salarié . apprentissage . diplôme préparé: - question: Quel type de diplôme l'apprenti prépare-t-il ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - niveau bac ou moins - - niveau supérieur au bac - par défaut: "'niveau supérieur au bac'" - -contrat salarié . apprentissage . diplôme préparé . niveau bac ou moins: - titre: Diplôme d'un niveau inférieur ou égal au bac - formule: diplôme préparé = 'niveau bac ou moins' - description: Concerne les diplôme de niveau V (CAP, BEP, CTM...) et de niveau IV (Bac Pro, BP, BTM) - -contrat salarié . apprentissage . diplôme préparé . niveau supérieur au bac: - titre: Diplôme d'un niveau supérieur au bac - formule: diplôme préparé = 'niveau supérieur au bac' - description: Concerne les diplôme de niveau I (Master, Ingénieur, Grandes écoles...), de niveau II (License, BMS...), et de niveau III (BTS, SUT, BM, ...) - -contrat salarié . apprentissage . ancienneté: - question: Depuis combien de temps l'apprenti est-il employé ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - moins d'un an - - moins de deux ans - - moins de trois ans - - moins de quatre ans - par défaut: "'moins d'un an'" - -contrat salarié . apprentissage . ancienneté . moins d'un an: - formule: ancienneté = 'moins d'un an' - -contrat salarié . apprentissage . ancienneté . moins de deux ans: - formule: ancienneté = 'moins de deux ans' - -contrat salarié . apprentissage . ancienneté . moins de trois ans: - formule: ancienneté = 'moins de trois ans' - -contrat salarié . apprentissage . ancienneté . moins de quatre ans: - formule: ancienneté = 'moins de quatre ans' - type: notification - description: >- - La durée maximale du contrat peut être portée à 4 ans lorsque la qualité de - travailleur handicapé est reconnue à l'apprenti. - -contrat salarié . professionnalisation: - description: | - Le contrat de professionnalisation est un contrat de travail en alternance - réservé à un public prioritaire : jeunes de 16 à 25 ans dans le cadre de - leur formation initiale, demandeurs d'emplois, bénéficiaires du RSA, ASS ou - AAH, et les personnes ayant bénéficié d'un contrat unique d'insertion. - - Il peut prendre la forme d'un contrat à durée déterminée (CDD) ou - indéterminée (CDI), la période de professionnalisation proprement-dite - devant durer entre 6 et 12 mois. Dans certains cas cette période peut être - prolongée jusqu'à 36 mois. - formule: contrat salarié = 'professionnalisation' - rend non applicable: rémunération . contrôle smic - références: - Contrat de professionnalisation: https://www.service-public.fr/particuliers/vosdroits/F15478 - -contrat salarié . professionnalisation . jeune de moins de 30 ans: - question: Le salarié embauché a-t'il moins de 30 ans ? - par défaut: oui - -contrat salarié . professionnalisation . salarié de 45 ans et plus: - non applicable si: jeune de moins de 30 ans - question: Le salarié embauché a-t'il 45 ans ou plus ? - par défaut: non - -contrat salarié . stage: - description: | - Un employeur qui accueille un stagiaire doit lui verser une gratification minimale. Celle-ci est en partie exonérée de cotisations sociales. - formule: contrat salarié = 'stage' - rend non applicable: - - statut cadre - - statut JEI - - réduction générale - - allocations familiales . taux réduit - - maladie . taux employeur . taux réduit - - lodeom - - contribution d'équilibre général - - retraite complémentaire - - chômage - - AGS - - complémentaire santé - - contribution au dialogue social - - déduction forfaitaire spécifique - - temps de travail . temps partiel - - temps de travail . heures supplémentaires - - régime des impatriés - - rémunération . contrôle smic - - contrat salarié . activité partielle - -contrat salarié . stage . avertissement: - formule: oui - type: notification - sévérité: 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: - type: notification - sévérité: avertissement - formule: rémunération . brut de base < stage . gratification minimale - description: >- - La rémunération du stage est inférieure à la [gratification minimale](https://www.service-public.fr/professionnels-entreprises/vosdroits/F32131). - -contrat salarié . stage . gratification minimale: - formule: 15% * plafond sécurité sociale temps plein - références: - Gratification minimale: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32131 - -contrat salarié . exonération d'impôt des stagiaires et apprentis: - description: | - Les salaires versés aux apprentis ainsi que les gratifications de stages sont exonérés d'impôt sur le revenu dans la limite d'un SMIC annuel. - références: - Article 81 bis du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000029236245&cidTexte=LEGITEXT000006069577 - applicable si: - une de ces conditions: - - apprentissage - - stage - formule: SMIC - -contrat salarié . CDD: - formule: contrat salarié = 'CDD' - description: | - Par défaut, faire travailler quelqu'un en France établit automatiquement un CDI à temps plein. - Certaines situations exceptionnelles permettent aux employeurs de prévoir une date de fin. Le contrat, qui est alors nécessaire, mentionne cette date de fin. - -contrat salarié . CDD . information: - type: notification - formule: oui - description: >- - Rappelez-vous qu'un CDD doit toujours correspondre à un besoin temporaire de l'entreprise. - [Code du travail - Article L1242-1](https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000006901194&cidTexte=LEGITEXT000006072050) - -contrat salarié . cotisations . assiette: - titre: Assiette des cotisations sociales - description: | - L'assiette des cotisations sociales est la base de calcul d'un grand nombre de cotisations sur le travail salarié. Elle comprend notamment les rémunérations en espèces (salaire de base, indemnité, primes...) et les avantages en nature (logement, véhicule...). - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/la-base-de-calcul.html - formule: - valeur: rémunération . brut - abattement: - somme: - - frais professionnels . part déductible - - stage . gratification minimale - # TODO: La prévoyance et la retraite supplémentaire ne sont pour - # l'instant pas exposées sur les simulateurs. Je les désactive ici pour - # résoudre un problème de cycle avec la déduction forfaitaire - # spécifique. - # - # Il faudra soit supprimer complètement ces règles prévoyance / - # retraite supplémentaire (que j'avais ajoutées dans le cadre du POC - # Paie), soit les exposer dans un simulateur. - # - # - prévoyance . part déductible - # - retraite supplémentaire . part déductible - -contrat salarié . cotisations . assiette . salariale: - titre: Assiette des cotisations sociales - description: | - Les apprentis bénéficient d'une exonération de cotisations sociales jusqu'à 79% du SMIC. - références: - Urssaf: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-ou-aides-liees-a-la/le-contrat-dapprentissage/exonerations.html - formule: - variations: - - si: apprentissage - alors: - valeur: cotisations . assiette - abattement: 79% * SMIC - - sinon: cotisations . assiette - -contrat salarié . cotisations . assiette minimale: - formule: - recalcul: - règle: rémunération . assiette de vérification du SMIC - avec: - rémunération . brut de base: SMIC horaire * temps de travail . temps effectif - -contrat salarié . rémunération . brut de base: - titre: Salaire brut - identifiant court: salaire-brut - résumé: Brut de référence (sans les primes, indemnités ni majorations) - type: salaire - question: Quel est votre salaire brut ? - description: | - C'est le salaire *brut* régulier inscrit dans le contrat de travail. Il ne change jamais entre les mois et ne peut pas être modifié sans signature des deux parties. - - Il ne comprend pas les indemnités, avantages sociaux, avantages en nature et primes... - unité: €/mois - suggestions: - salaire médian: 2300 €/mois - SMIC: SMIC contractuel - formule: - inversion numérique: - question: Quel est le salaire ? - titre: salaire - avec: - - prix du travail - - rémunération . total - - rémunération . net - - rémunération . net après impôt - - équivalent temps plein - - dirigeant . rémunération . totale - - - références: - Le salaire. Fixation et paiement: http://travail-emploi.gouv.fr/droit-du-travail/remuneration-et-participation-financiere/remuneration/article/le-salaire-fixation-et-paiement - -contrat salarié . rémunération . contrôle smic: - type: notification - sévérité: avertissement - formule: assiette de vérification du SMIC < SMIC contractuel - description: Le salaire saisi est inférieur au SMIC. - -contrat salarié . rémunération . contrôle salaire élevé: - type: notification - formule: - toutes ces conditions: - - brut de base >= 10000 €/mois - - dirigeant = non - description: >- - Le salaire mensuel saisi est élevé. Ne vous êtes-vous pas trompé de période de calcul ? - -contrat salarié . rémunération . brut de base . équivalent temps plein: - applicable si: temps de travail . temps partiel - titre: Salaire brut équivalent temps plein - résumé: Le salaire si l'embauche se faisait à temps plein - question: Quel est le salaire en équivalent temps plein ? - unité: €/mois - formule: brut de base / temps de travail . quotité de travail - suggestions: - salaire médian: 2300 €/mois - SMIC: SMIC temps plein - -contrat salarié . rémunération . taux horaire: - unité: €/heure - formule: assiette de vérification du SMIC / temps de travail - -contrat salarié . rémunération . taux horaire . heures supplémentaires: - titre: taux horaire (heure supplémentaire) - description: > - Le taux horaire utilisé pour calculer la rémunération liée au heures - supplémentaires. Il intègre les avantages en nature et les primes - constituant la contrepartie d'un travail fourni. - unité: €/heure - formule: (assiette de vérification du SMIC + primes . fin d'année) / temps de travail . temps contractuel - références: - e-Paye (privé): https://e-paye.com/faq/les-heures-supplementaires-quelles-primes-inclure-dans-la-base-de-calcul-de-la-majoration-pour-heure-supplementaire/ - rfPaye (privé): https://rfpaye.grouperf.com/article/0168/ms/rfpayems0168_2027146.html - legisocial: https://www.legisocial.fr/actualites-sociales/1074-avantage-en-nature-et-heures-supplementaires-les-consequences-sur-le-bulletin-de-paie.html - -contrat salarié . rémunération . assiette de vérification du SMIC: - description: > - C'est le salaire pris en compte pour vérifier que le SMIC est atteint. - unité: €/mois - formule: - somme: - - brut de base - - avantages en nature . montant - - primes . activité - note: > - Les primes de fin d'année ou de 13ième mois sont prises en compte dans - l'assiette de vérification du SMIC mais seulement le mois où elles sont - payées (et non de manière lissée sur l'année), c'est pourquoi nous ne les - incluons pas dans cette formule. - -contrat salarié . rémunération . assiette congés payés: - titre: Assiette pour le calcul de l'indemnité de congés payés - description: >- - Pendant ses congés, le salarié ne perçoit pas son salaire. Il perçoit une - indemnité de congés payés. - - Toutes les sommes ayant le caractère de salaire sont prises en compte pour - déterminer l'indemnité de congés payés. Les autres sommes ne sont pas prise en - compte. - formule: - somme: - - brut de base - - heures supplémentaires - - heures complémentaires - - avantages en nature - - primes . ancienneté - - primes . activité - - CDD . prime de fin de contrat - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F33359 - -contrat salarié . rémunération . primes: - description: | - Les primes sont des compléments de salaire versés au salarié en vertu du - contrat de travail, de la convention collective, d'un usage d'entreprise, ou - bien à titre bénévole par l'employeur. - - Sauf exception, elles sont soumises aux cotisations sociales et à l'impôt - sur le revenu. - - formule: - somme: - - base - - activité - - primes . ancienneté - - fin d'année - -contrat salarié . rémunération . primes . base: - formule: 0€/mois - -contrat salarié . rémunération . primes . activité: - unité: €/mois - - titre: primes d'activité - description: > - Primes et gratifications versées en contrepartie, ou à l’occasion du travail, directement liées à l’exécution par le salarié de sa prestation de travail. Tel est le cas, par exemple, d’une prime de vente exclusivement basée sur les résultats du salarié. - - Ces primes sont prises en compte pour le calcul du salaire minimum - formule: - somme: - - activité . conventionnelles - - activité . base - -contrat salarié . rémunération . primes . activité . base: - titre: primes d'activité - question: Quel est le montant des primes liées à l'activité du salarié ? - par défaut: 0 €/mois - -contrat salarié . rémunération . primes . activité . conventionnelles: - formule: 0 €/mois - -contrat salarié . rémunération . primes . ancienneté: - formule: 0 €/mois - -contrat salarié . rémunération . primes . fin d'année: - titre: Prime de fin d'année ou de treizième mois - description: | - Cette prime est le plus souvent versée en une seule fois à la fin de - l'année. - - Les salariés à temps partiel ont dont à la prime de fin d'année dans les - mêmes conditions que les autres salariés en proportion de leur durée du - travail. - formule: - produit: - assiette: assiette de vérification du SMIC * temps de travail . quotité de travail / 1 an - facteur: prime de fin d'année en mois - -contrat salarié . rémunération . primes . fin d'année . prime de fin d'année en mois: - applicable si: treizième mois - formule: 13 mois - 1 an - note: > - Certaines entreprises proposent une prime de fin d'année sur une base de - 13,5 mois, 14 mois voire 15 mois. - -contrat salarié . rémunération . primes . fin d'année . treizième mois: - question: Le salarié bénéficie-t-il d'un treizième mois ? - description: > - La prime de treizième mois est un avantage accordé au salarié qui peut être - prévu par la convention collective ou le contrat de travail. Elle est - généralement versée en fin d'année. - par défaut: non - -contrat salarié . rémunération . brut: - description: Toutes les sommes versées au salarié sous forme monétaire en échange de son travail. - titre: Rémunération brute - # Pour les frais professionnels, et les cotisations de prévoyance facultative - # et de retraite complémentaire on ne ré-intègre ici que la part employeur - # (car la part salarié est déjà comptabilisé dans `rémunération . brut de - # base` dont elle vient en déduction). - somme: - - rémunération . brut de base - - avantages en nature . montant - - primes - - CDD . indemnités salarié - - heures supplémentaires - - heures complémentaires - - frais professionnels - - prévoyance . employeur - - retraite supplémentaire . employeur - abattement: activité partielle . retrait absence - -contrat salarié . rémunération . heures supplémentaires: - titre: rémunération heures supplémentaires - description: La rémunération relative aux heures supplémentaires - formule: - produit: - assiette: taux horaire . heures supplémentaires - facteur: - somme: - - temps de travail . heures supplémentaires - - temps de travail . heures supplémentaires . majoration - -contrat salarié . rémunération . heures complémentaires: - titre: rémunération heures complémentaires - description: La rémunération relative aux heures complémentaires - formule: - produit: - assiette: taux horaire . heures supplémentaires - facteur: - somme: - - temps de travail . heures complémentaires - - temps de travail . heures complémentaires . majoration - -contrat salarié . rémunération . revenus de remplacement: - description: >- - Les revenus de remplacement sont les revenus perçus en remplacement de la - rémunération du travail : allocations de chômage ou de chômage partiel, - indemnités maladie ou accident du travail, pension de retraite, revenu - d'intégration sociale, etc. - - Ces revenus sont imposables mais sont exonérés de cotisations sociales. Ils - sont soumis à la CSG/CRDS avec un taux spécifique. - formule: - somme: - - activité partielle . indemnités - note: >- - L'indemnité complémentaire n'est pas ajoutée ici car elle est - systématiquement exonérée de CSG du fait de l'écrêtement pour les bas - revenus. - - L'ajouter abouti à un calcul cyclique (vu qu'elle dépend du montant de la CSG) - -contrat salarié . avantages sociaux: - description: > - Ce sont les avantages sociaux payés par l'employeur. Ils sont spécifiques à l'entreprise, et fournis par des structures privées (mutuelle, assurance...). - Ils sont soumis à l'impôt sur le revenu. - unité: €/mois - formule: - somme: - - prévoyance . employeur - - retraite supplémentaire . employeur - - prévoyance obligatoire cadre - - complémentaire santé . employeur - -contrat salarié . rémunération . avantages en nature: - icônes: 🛏️🚗🥗📱 - titre: Avantages en nature - description: | - Les avantages en nature sont constitués par la fourniture par l’entreprise à ses travailleurs d’un bien ou service. La mise à disposition peut être gratuite ou moyennant une participation du bénéficiant inférieure à leur valeur réelle. - - - L’avantage en nature doit figurer sur le bulletin de paie. Il sera indiqué au niveau du salaire brut pour être soumis à cotisations. Après détermination du salaire net imposable, il sera déduit du salaire net à verser. - question: L'entreprise fournit-elle des avantages en nature (repas, véhicule, téléphone, réductions, logement...) ? - par défaut: non - -contrat salarié . rémunération . avantages en nature . montant: - titre: Avantages en nature - description: > - Les avantages en nature sont soumis aux cotisations et à l'impôt sur le revenu. Ils sont pris en compte pour vérifier que le salaire minimum est atteint. - formule: - somme: - - nourriture . montant - - ntic . montant - - autres . montant - -contrat salarié . rémunération . avantages en nature . ntic: - icônes: 💻📱 - description: | - L’usage privé des outils NTIC mis à disposition dans le cadre de l’activité professionnelle à titre permanent est constitutif d’un avantage en nature. - - - Cet avantage est inclus dans la base de calcul des cotisations de Sécurité sociale et d’assurance chômage. - - - La réalité de l’usage privé peut résulter soit d’un document écrit (contrat de travail, accord d’entreprise, règlement intérieur, courrier de la direction de l’entreprise autorisant le salarié à faire un usage privé des outils), soit de l’existence de factures détaillées permettant d’établir une utilisation privée. - question: > - L'entreprise fournit-elle gratuitement un outil issus des NTIC (ordinateur, téléphone, tablette, etc.) ? - par défaut: oui - -contrat salarié . rémunération . avantages en nature . autres: - question: > - Y a-t-il d'autres avantages en natures (logement, véhicule, réduction...) ? - par défaut: non - -contrat salarié . rémunération . avantages en nature . autres . montant: - titre: autres - question: > - Quel est le montant de ces autres avantages ? - par défaut: 0 €/mois - suggestions: - 🚗 véhicule: 260 €/mois - -contrat salarié . rémunération . avantages en nature . ntic . montant: - titre: outils NTIC - - description: | - Pour les avantages en nature de type NTIC (ordinateurs, smartphones, tablettes...), il y a une évaluation forfaitaire annuelle correspondant à 10% du prix d'achat. Par exemple, pour un téléphone acheté à 850€ TTC avec un abonnement de 30€ / mois, l'avantage en nature à reporter sur le bulletin de paie sera de : - - ``` - [10% x (850€ + (30€ x 12 mois)) ] / 12 mois - ``` - soit 10,08€ - formule: - produit: - assiette: - somme: - - coût appareils - - abonnements * 12 mois - taux: 10% /an - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-elements-a-prendre-en-compte/les-avantages-en-nature/les-outils-issus-des-nouvelles-t/dans-quel-cas-la-mise-a-disposit/levaluation-forfaitaire.html - -contrat salarié . rémunération . avantages en nature . ntic . coût appareils: - question: > - Quel est le coût total neuf des appareils mis à disposition ? - par défaut: 800 € - # TODO : vérifier et documenter les chiffres - suggestions: - 📱: 400 € - 📱✨ (haut de gamme): 850 € - 💻: 1200 € - 💻 + 📱✨: 2050 € - -contrat salarié . rémunération . avantages en nature . ntic . abonnements: - question: Quel est le coût de l'abonnement (forfait mobile, etc.) pris en charge par l'employeur ? - par défaut: 20 €/mois - suggestions: - aucun: 0 €/mois - standard: 20 €/mois - international: 40 €/mois - -contrat salarié . rémunération . avantages en nature . nourriture: - icônes: 🍝 - question: > - L'entreprise fournit-elle gratuitement des repas ? - par défaut: non - description: > - Les titres-restaurant ne sont pas considérés comme un avantage en nature mais comme un remboursement de frais. - -contrat salarié . rémunération . avantages en nature . nourriture . montant: - titre: nourriture - unité: €/mois - formule: - produit: - assiette [ref repas forfaitaire]: 4.85 €/repas - facteur: repas par mois - références: - urssaf.fr: https://www.urssaf.fr/portail/home/taux-et-baremes/avantages-en-nature/nourriture.html - -contrat salarié . rémunération . avantages en nature . nourriture . repas par mois: - question: > - Combien de repas par mois sont payés par l'entreprise ? - par défaut: 21 repas/mois - suggestions: - 1 par jour: 21 repas/mois - 2 par jour: 42 repas/mois - -contrat salarié . statut cadre: - question: Le salarié a-t-il le statut cadre ? - description: | - Un cadre d'entreprise est un employé ou dirigeant d'une entreprise - appartenant à la catégorie supérieure des salariés. Il s'agit d'un statut - reconnu par les conventions collectives, qui détermine l'appartenance à une - caisse de retraite spécifique, l'AGIRC, et quelques modalités spécifiques du - contrat de travail. - - - Reconnaissant initialement les compétences techniques et le rôle - d'encadrement du salarié, le statut s'est progressivement élargi à un - ensemble de postes de plus en plus nombreux, et a fini par recouvrir une - large population, mêlant managers, experts et dirigeants. - - - Il s'agit d'une notion mal définie désignant des concepts différents selon - le point de vue envisagé — que ce soit en termes de statut, de - représentation sociale, de rôle dans l'entreprise ou de culture. - par défaut: non - références: - wikipedia.fr: https://fr.wikipedia.org/wiki/Cadre_d%27entreprise - -contrat salarié . plafond sécurité sociale: - acronyme: PSS - unité: €/mois - formule: - valeur: plafond sécurité sociale temps plein * temps de travail . quotité de travail effective - # Note: le plafond de la sécurité sociale est pro-ratisé en fonction de la - # quotité de travail effective. Cela pose problème en cas de de chômage - # partiel à 100% car le plafond vaut alors 0€ et celui-ci est utilisé - # comme "multiplicateur" à plusieurs endroits, ce qui entraîne des - # divisions par zéro (si j'ai bien compris le problème, il est possible - # que le problème exact soit un peu différent). - plancher: 1 €/mois - -contrat salarié . plafond sécurité sociale . renonciation proratisation: - description: >- - D'un commun accord, l'employeur et l'employé peuvent renoncer à la réduction - du plafond de la sécurité sociale (applicable pour les salariés à temps - partiel), notamment afin d'augmenter le montant des cotisations vieillesse. - # TODO : Réactiver la règle (peut être ajouter des références et la déplacer dans l'espace de nom temps de travail) - valeur: non - applicable si: temps de travail . quotité de travail < 100% - remplace: - - règle: plafond sécurité sociale - par: plafond sécurité sociale temps plein - -contrat salarié . SMIC contractuel: - description: > - Valeur du SMIC pro-ratisé pour prendre en compte le temps partiel et utilisé pour la détermination du salaire minimum - formule: SMIC temps plein * temps de travail . quotité de travail - -contrat salarié . SMIC: - description: | - Plusieurs réductions de cotisations ([réduction générale](/documentation/contrat-salarié/réduction-générale), taux réduit d'[allocations familiales](/documentation/contrat-salarié/allocations-familiales/taux-réduit) et de [maladie](/documentation/contrat-salarié/maladie/taux-employeur/taux-réduit), réduction outre-mer) reposent sur un paramètre SMIC faisant l'objet de plusieurs ajustements pour prendre en compte le temps de travail effectif. - - Les heures supplémentaires et les heures complémentaires sont prises en - compte sans tenir compte de la majoration. - formule: temps de travail * SMIC horaire - références: - Détermination du SMIC: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/le-calcul-de-la-reduction/etape-1--determination-du-coeffi/determination-du-smic-a-prendre.html - -contrat salarié . cotisations . réductions de cotisations: - titre: Réductions de cotisations - formule: - somme: - - patronales . réductions de cotisations - - salariales . réductions de cotisations - -contrat salarié . cotisations . salariales: - titre: cotisations salariales - - somme: - - vieillesse . salarié - - maladie . salarié - - retraite complémentaire . salarié - - contribution d'équilibre général . salarié - - contribution d'équilibre technique . salarié - - chômage . salarié - - CSG et CRDS - - APEC . salarié - - complémentaire santé . salarié - - conventionnelles - abattement: réductions de cotisations - -contrat salarié . cotisations . patronales: - titre: cotisations patronales - somme: - - maladie . employeur - - ATMP - - prévoyance obligatoire cadre - - vieillesse . employeur - - retraite complémentaire . employeur - - complémentaire santé . employeur - - contribution d'équilibre général . employeur - - contribution d'équilibre technique . employeur - - allocations familiales - - chômage . employeur - - APEC . employeur - - AGS - - FNAL - - participation effort de construction - - contribution au dialogue social - - formation professionnelle - - versement transport - - taxe d'apprentissage - - CDD . taxe forfaitaire sur les CDD d'usage - - CDD . CPF - - forfait social - - conventionnelles - abattement: réductions de cotisations - -contrat salarié . rémunération: - formule: oui - description: La rémunération se distingue du salaire en incluant les avantages non monétaires versés en contrepartie du travail. Elle est donc plus large que les sommes d'argent versées au salarié. - -contrat salarié . rémunération . net de cotisations: - titre: Salaire net de cotisations - type: rémunération - formule: - somme: - - brut - - (- cotisations . salariales) - -contrat salarié . rémunération . net avec revenus de remplacement: - formule: - somme: - - net de cotisations - - revenus de remplacement - - (- CSG et CRDS . revenus de remplacement) - - (- cotisations . maladie sur les revenus de remplacement) - -contrat salarié . rémunération . net imposable: - titre: Salaire net imposable - type: salaire - description: | - C'est la base utilisée pour calculer l'impôt sur le revenu. - valeur: - nom: base - description: Le net imposable avant les exonérations et déductions - somme: - - net avec revenus de remplacement - - avantages sociaux - - CSG et CRDS . non déductible - abattement: - somme: - - frais professionnels . part déductible - - prime d'impatriation - - exonération d'impôt des stagiaires et apprentis - - heures supplémentaires et complémentaires défiscalisées - - retraite supplémentaire . exonération fiscale - - prévoyance . exonération fiscale - références: - DSN: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 - - - -contrat salarié . rémunération . net imposable . heures supplémentaires et complémentaires défiscalisées: - unité: €/mois - formule: - valeur: - somme: - - heures supplémentaires - - heures complémentaires - plafond: 5358 €/an - références: - DSN: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 - -contrat salarié . prime d'impatriation: - description: La prime d'impatriation est une partie de la rémunération exonérée d'impôt sur le revenu. - applicable si: régime des impatriés - formule: - produit: - assiette: rémunération . net imposable . base - taux: 30% - références: - Article 155B du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069577&idArticle=LEGIARTI000006307476&dateTexte=&categorieLien=cid - Bofip: https://bofip.impots.gouv.fr/bofip/5677-PGP - -contrat salarié . rémunération . net: - titre: Salaire net - identifiant court: salaire-net - unité: €/mois - type: salaire - question: Quel est votre salaire net ? - résumé: Salaire net avant impôt - description: >- - C'est le montant que le salarié toucherait à la fin du mois avant de payer - l'impôt sur le revenu. - - Aussi appelé salaire net à payer (c'était du moins le cas avant l'impôt à la - source). - - - Cette somme peut varier en fonction de décisions politiques (augmentation ou - diminution des cotisations) alors que le salaire brut est contractuel (pour - le changer, il faut signer un avenant au contrat). - - formule: - somme: - - net avec revenus de remplacement - - (- avantages en nature . montant) - - (- frais professionnels . titres-restaurant . montant) - -contrat salarié . rémunération . net après impôt: - titre: Salaire net après impôt - identifiant court: salaire-net-apres-impot - résumé: Versé sur le compte bancaire - question: Quel est le revenu net du salarié après impôt ? - type: salaire - unité: €/mois - description: | - Le 1er janvier 2019, l'impôt sur le revenu est prélevé à la source et apparaît donc sur la fiche de paie. - - Notre calcul retient le salaire net après déduction de l'impôt **neutre** (aussi appelé taux non personnalisé). - - C'est une bonne estimation du revenu net d'une personne en l'absence d'informations sur sa situation (c'est un cas par défaut : célibataire sans enfants ni patrimoine). - - Pour une simulation plus complète, rendez-vous sur [impots.gouv.fr](https://www3.impots.gouv.fr/simulateur/calcul_impot/2018/index.htm). - références: - Explication de l'impôt à la source: https://www.economie.gouv.fr/prelevement-a-la-source - - formule: net - impôt - -contrat salarié . prix du travail: - titre: Coût total - identifiant court: cout-embauche - résumé: Dépensé par l'entreprise - question: Quel est le coût total de cette embauche ? - description: | - Coût total d'embauche d'un salarié en incluant, en plus des éléments de rémunération, les aides différées et les coûts de medecine du travail - > C'est donc aussi une mesure de la valeur apportée par le salarié à l'entreprise : l'employeur est prêt à verser cette somme en contrepartie du travail fourni. - - À ce coût total, il ne faut pas oublier d'ajouter les dépenses spécifiques à votre entreprise : recherche du bon candidat, poste de travail, équipement, formation initiale, etc. - formule: - somme: - - rémunération . total - - (- aides employeur) - - taxe sur les salaires - - médecine du travail - unité: €/mois - -contrat salarié . rémunération . total: - titre: Total chargé - question: Quelle est la rémunération chargée ? - résumé: Dépensé par l'entreprise - type: salaire - unité: €/mois - description: | - C'est le total que l'employeur doit verser pour employer un salarié. - formule: - somme: - - brut - - cotisations . patronales - - activité partielle . indemnités - -contrat salarié . cotisations . salariales . réductions de cotisations: - titre: réductions salariales - formule: réduction heures supplémentaires - -contrat salarié . cotisations . patronales . réductions de cotisations: - titre: réductions patronales - description: >- - À l'exception de la déduction heure supplémentaire, les - dispositifs de réduction de cotisations patronales sont - mutuellement exclusif. - - Le formule ci dessous selectionne donc automatiquement - le plus avantageux pour l'employeur. - formule: - somme: - - déduction heures supplémentaires - - le maximum de: - - réduction générale - - lodeom . réduction outre-mer - - statut JEI . exonération de cotisations - - dirigeant . assimilé salarié . réduction ACRE - références: - urssaf.fr (cumul réduction générale): https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/les-regles-relatives-au-cumul.html - urssaf.fr (cumul JEI): https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-ou-aides-liees-au-s/jeunes-entreprises-innovantes/regles-de-cumul.html - -contrat salarié . cotisations . patronales . réductions de cotisations . déduction heures supplémentaires: - applicable si: entreprise . effectif < 20 - titre: déduction forfaitaire pour heures supplémentaires - formule: - produit: - assiette: temps de travail . heures supplémentaires - facteur: 1.50 €/heure - note: La déduction ne s’applique pas aux heures complémentaires - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-deduction-forfaitaire-patrona/employeurs-concernes.html - -contrat salarié . cotisations . salariales . réduction heures supplémentaires: - cotisation: - branche: retraite - dû par: salarié - aide: - type: réduction de cotisations - formule: rémunération . heures supplémentaires * taux des cotisations réduites - références: - Code de la sécurité sociale - Article D241-21: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000038056813&cidTexte=LEGITEXT000006073189 - -contrat salarié . cotisations . salariales . réduction heures supplémentaires . taux des cotisations réduites: - unité: '%' - description: le taux effectif des cotisations d'assurance vieillesse à la charge du salarié - formule: - valeur: - produit: - assiette: - somme: - - vieillesse . salarié - - retraite complémentaire . salarié - - contribution d'équilibre général . salarié - facteur: 1 / assiette - plafond: 11.31% - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-de-cotisations-sala/modalites-de-calcul-et-de-declar.html - Circulaire DSS/5B/2019/71: http://circulaire.legifrance.gouv.fr/pdf/2019/04/cir_44492.pdf - -contrat salarié . cotisations: - description: Total des cotisations patronales et salariales - formule: - somme: - - patronales - - salariales - -contrat salarié . cotisations . salariales . conventionnelles: - titre: cotisations salariales conventionnelles - description: Cotisations spécifiques à la convention collective - formule: 0 €/mois - -contrat salarié . cotisations . patronales . conventionnelles: - titre: cotisations patronales conventionnelles - description: Cotisations spécifiques à la convention collective - formule: 0 €/mois - -contrat salarié . cotisations . maladie sur les revenus de remplacement: - formule: - produit: - assiette: rémunération . revenus de remplacement - taux: - variations: - - si: établissement . localisation . département = 'Mayotte' - alors: 2.35% - - si: régime alsace moselle - alors: 1.5% - - sinon: 0% - -contrat salarié . aides employeur: - titre: aides employeur - résumé: Pour l'employeur, différées dans le temps - description: | - Ces aides sont appelées différées, car elles ne consistent pas en une simple réduction des cotisations mensuelles : elles interviendront a posteriori par exemple sous la forme d’un crédit d'impôt. - - Le simulateur n'intègre pas toutes les innombrables aides disponibles en France. Découvrez-les sur le [portail officiel](http://www.aides-entreprises.fr). - formule: - somme: - - aides à l'embauche - - activité partielle . indemnisation entreprise - -contrat salarié . aides employeur . aides à l'embauche: - description: | - L'État met en place des aides pour encourager l'embauche de certains publics prioritaires. Ces aides sont non cumulables entre elles. - formule: - le maximum de: - - aide à l'embauche d'apprentis - - aide exceptionnelle à l'embauche d'apprentis - - aide exceptionnelle à l'embauche des jeunes - - aide à l'embauche senior professionnalisation - - aide à l'embauche des travailleurs handicapés - - emploi franc - -contrat salarié . aides employeur . aide à l'embauche d'apprentis: - description: | - Depuis 2019 une aide à l'embauche unique remplace quatre précédents dispositifs. Le montant de l'aide dépend de l'ancienneté du contrat. - - Une fois les démarches d'enregistrement effectuées, l'aide est versée automatiquement tous les mois. - applicable si: - toutes ces conditions: - - entreprise . effectif < 250 - - apprentissage - - apprentissage . diplôme préparé . niveau bac ou moins - formule: - variations: - - si: apprentissage . ancienneté = 'moins d'un an' - alors: 4125 €/an - - si: apprentissage . ancienneté = 'moins de deux ans' - alors: 2000 €/an - - sinon: 1200 €/an - références: - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23556 - -contrat salarié . aides employeur . aide exceptionnelle à l'embauche d'apprentis: - description: >- - Dans le cadre du plan de relance de l'économie de la rentrée 2020, le - gouvernement met en place une aide exceptionnelle au recrutement des - apprentis. - - Cette aide est ouverte pour les contrats signés entre le 1er juillet 2020 et - le 28 février 2021. Elle se substitue à l’aide unique, dont bénéficient les - entreprises de moins de 250 salariés embauchant un apprenti de niveau CAP à - Bac. - applicable si: - toutes ces conditions: - - une de ces conditions: - - apprentissage - - professionnalisation . jeune de moins de 30 ans - - ancienneté . date d'embauche >= 01/07/2020 - - ancienneté . date d'embauche <= 31/12/2021 - - temps de travail . temps effectif > 0 heures/mois - formule: - variations: - - si: salarié majeur - alors: 8000 €/an - - sinon: 5000 €/an - rend non applicable: aide à l'embauche d'apprentis - références: - Plan \#1jeune1solution: https://travail-emploi.gouv.fr/formation-professionnelle/entreprise-et-alternance/aide-exceptionnelle-apprentissage - -contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes: - non applicable si: aides employeur . emploi franc - description: >- - Dans le cadre du plan de relance de l'économie de la rentrée 2020, le - gouvernement met en place une aide exceptionnelle au recrutement des - jeunes de moins de 26 ans. - - L’aide est de 4 000 euros sur un an pour un salarié à temps plein. Ce - montant est proratisé en fonction du temps de travail et de la durée du - contrat de travail. - applicable si: - toutes ces conditions: - - ancienneté . date d'embauche >= 01/08/2020 - - ancienneté . date d'embauche <= 31/05/2021 - - rémunération . brut de base <= 2 * SMIC - - une de ces conditions: - - CDI - - toutes ces conditions: - - CDD - - CDD . durée contrat >= 3 mois - - nom: jeune de moins de 26 ans - question: Le salarié a-t-il moins de 26 ans ? - par défaut: non - rend non applicable: - # Dispositifs moins généreux et non cumulables - - aide à l'embauche des travailleurs handicapés - formule: - produit: - assiette: 4000 €/an - facteur: temps de travail . quotité de travail effective - arrondi: oui - références: - Plan \#1jeune1solution: https://travail-emploi.gouv.fr/le-ministere-en-action/relance-activite/plan-1jeune-1solution/aide-embauche-jeunes - -contrat salarié . aides employeur . aide à l'embauche senior professionnalisation: - description: | - Les employeurs peuvent obtenir une aide de 2000 € pour l'embauche d'un - demandeur d'emploi de plus de 45 ans en contrat de professionnalisation. - applicable si: professionnalisation . salarié de 45 ans et plus - formule: - produit: - assiette: 2000 €/an - facteur: temps de travail . quotité de travail effective - arrondi: oui - références: - Ministère du travail: https://travail-emploi.gouv.fr/emploi/mesures-seniors/article/l-aide-a-l-embauche-d-un-demandeur-d-emploi-de-45-ans-et-plus-en-contrat-de - Pôle Emploi: https://www.pole-emploi.fr/employeur/aides-aux-recrutements/les-aides-a-lembauche/embauche-de-de-de-45-ans-et-plus.html - -contrat salarié . aides employeur . emploi franc: - description: | - Aide différée versée par Pôle emploi pour l'embauche d'un demandeur d'emploi - inscrit à Pôle Emploi et résidant dans un quartier prioritaire de la ville - (QPV). - - - *embauche en CDI* : 5000€/an pendant 3 ans, soit un total de 15 000€ - - *embauche en CDD d'au moins 6 mois* : 2 500€/an pendant 2 ans, soit 5 000€ au maximum - - [🗺 Vérifier l'éligibilité d'une adresse](https://sig.ville.gouv.fr/recherche-adresses-qp-polville) - applicable si: éligible - formule: - multiplication: - assiette: - variations: - - si: - toutes ces conditions: - - ancienneté . date d'embauche >= 15/10/2020 - - ancienneté . date d'embauche <= 31/05/2021 - - contrat salarié . aides employeur . aide exceptionnelle à l'embauche des jeunes . jeune de moins de 26 ans - alors: - nom: emploi franc plus - variations: - - si: CDD - alors: 5500 €/an - - sinon: 7000 €/an - - sinon: - variations: - - si: CDD - alors: 2500 €/an - - sinon: 5000 €/an - facteur: temps de travail . quotité de travail effective - arrondi: oui - rend non applicable: - # Dispositifs moins généreux et non cumulables - - aide à l'embauche des travailleurs handicapés - références: - Fiche emploi franc: https://travail-emploi.gouv.fr/emploi/emplois-francs/article/embaucher-une-personne-en-emploi-franc - -contrat salarié . aides employeur . emploi franc . éligible: - titre: éligibilité à l'aide emploi franc - applicable si: - une de ces conditions: - - CDI - - toutes ces conditions: - - CDD - - CDD . durée contrat >= 6 - question: Cette embauche est-elle éligible à l'aide emploi-franc ? - description: | - Conditions : - - Le salarié recruté est un demandeur d'emploi inscrit à Pôle Emploi et réside dans un quartier prioritaire de la ville (QPV) [vérifier l'éligibilité d'un quartier](https://sig.ville.gouv.fr/recherche-adresses-qp-polville) - - L'employeur est à jour de ses cotisations et n'a pas procédé à un licenciement économique pour le poste pourvu dans les 6 mois précédents le recrutement - - Le salarié recruté ne doit pas avoir appartenu à l'effectif de l'entreprise dans les 6 mois précédent l'embauche - par défaut: non - -contrat salarié . temps de travail: - unité: heures/mois - formule: - somme: - - temps contractuel - - heures supplémentaires - - heures complémentaires - description: En France, la base légale du travail est de 35h/semaine. Mais un grand nombre de dispositions existantes permettent de faire varier ce nombre. Vous pouvez les retrouver sur la page [service-public.fr](https://www.service-public.fr/particuliers/vosdroits/N458) dédiée. - -contrat salarié . aides employeur . aide à l'embauche des travailleurs handicapés: - non applicable si: aides employeur . emploi franc - description: >- - Dans le cadre du plan de relance, le gouvernement a décidé de créer une aide - à l’embauche visant à favoriser l’emploi des personnes en situation de - handicap quel que soit leur âge. - applicable si: - toutes ces conditions: - - nom: situation de handicap - question: Le salarié a-t'il la reconnaissance de travailleur handicapé (RQTH) ? - par défaut: non - - ancienneté . date d'embauche >= 01/09/2020 - - ancienneté . date d'embauche <= 30/06/2021 - - rémunération . brut de base <= 2 * SMIC - - une de ces conditions: - - CDI - - toutes ces conditions: - - CDD - - CDD . durée contrat >= 3 mois - formule: - produit: - assiette: 4000 €/an - facteur: temps de travail . quotité de travail effective - arrondi: oui - références: - Plan \#1jeune1solution: https://travail-emploi.gouv.fr/le-ministere-en-action/relance-activite/plan-1jeune-1solution/aide-embauche-jeunes - -contrat salarié . temps de travail . temps effectif: - formule: - somme: - - temps de travail - - (- activité partielle . heures chômées) - -contrat salarié . temps de travail . temps contractuel: - unité: heures/mois - formule: - produit: - assiette: temps hebdomadaire - facteur: période . semaines par mois - -contrat salarié . temps de travail . temps contractuel . temps hebdomadaire: - unité: heures/semaine - formule: - variations: - - si: temps partiel - alors: temps partiel . heures par semaine - - sinon: base légale - -contrat salarié . temps de travail . base légale: - formule: 35 heures/semaine - -contrat salarié . temps de travail . temps partiel: - question: Le contrat est-il à temps partiel ? - description: | - Deux contrats au même salaire, l'un à temps partiel, l'autre à temps complet, peuvent donner lieu à des montants de cotisation différents. - - Par exemple pour les cotisations plafonnées ou les exonérations dépendant du SMIC. - par défaut: non - -contrat salarié . temps de travail . temps partiel . heures par semaine: - par défaut: 32 heures/semaine - question: Quel est le nombre d'heures travaillées par semaine dans le cadre du temps partiel ? - suggestions: - 4 jours / semaine: base légale * 4 / 5 - mi-temps: base légale / 2 - -contrat salarié . temps de travail . temps partiel . contrôle temps min: - type: notification - sévérité: avertissement - formule: heures par semaine < 24 heures/semaine - description: Le nombre minimum d'heures par semaine est 24. Il est possible de descendre plus bas dans certains cas seulement. [Plus d'infos](https://www.service-public.fr/particuliers/vosdroits/F32428). - -contrat salarié . temps de travail . temps partiel . contrôle temps max: - type: notification - sévérité: avertissement - formule: heures par semaine >= base légale - description: Un temps partiel doit être en dessous de la durée de travail légale (35h) - -contrat salarié . temps de travail . quotité de travail: - description: Temps de travail en proportion du temps complet légal. - formule: - valeur: temps de travail / (base légale * période . semaines par mois) - plafond: 100% - unité: '%' - -contrat salarié . temps de travail . quotité de travail effective: - description: >- - Le plafond de la sécurité sociale doit être pro-ratisé en retirant les - absences ainsi que les jours passés au chômage partiel. - formule: temps de travail . temps effectif / (base légale * période . semaines par mois) - -contrat salarié . temps de travail . heures supplémentaires: - description: Toute heure de travail accomplie, à la demande de l'employeur, au-delà de la durée légale de 35 heures (ou de la durée équivalente) est une heure supplémentaire. Les heures supplémentaires ouvrent droit à une rémunération plus favorable (taux horaire majoré) au salarié. - titre: Nombre d'heures supplémentaires - non applicable si: temps partiel - question: Combien d'heures supplémentaires (non récupérées en repos) sont effectuées par mois ? - par défaut: 0 heure/mois - suggestions: - aucune: 0 heure/mois - 39h / semaine: 17.33 heures/mois - 42h / semaine: 30.33 heures/mois - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F2391 - -contrat salarié . temps de travail . contrôle 44h max: - type: notification - formule: - toutes ces conditions: - - heures supplémentaires > 9 heures/semaine * période . semaines par mois - - heures supplémentaires <= 13 heures/semaine * période . semaines par mois - description: La durée hebdomadaire moyenne de travail ne peut pas dépasser 44h - -contrat salarié . temps de travail . contrôle 48h max: - type: notification - sévérité: avertissement - formule: heures supplémentaires > 13 heures/semaine * période . semaines par mois - description: La durée hebdomadaire maximale de travail ne peut pas dépasser 48h - -contrat salarié . temps de travail . heures supplémentaires . majoration: - description: | - La rémunération des heures supplémentaires fait l'objet d'un ou plusieurs taux de majoration, fixés par convention ou accord collectif d'entreprise ou d'établissement (ou, à défaut, par convention ou accord de branche). Chaque taux est au minimum fixé à 10%. - - À défaut d'accord ou de convention, les taux de majoration horaire sont fixés à : - - 25 % pour les 8 premières heures supplémentaires travaillées dans la même semaine (de la 36e à la 43e heure), - - 50 % pour les heures suivantes. - titre: majoration heures supplémentaires - note: Pour l'instant, nous implémentons uniquement les taux standards et ceux de la convention HCR (Hôtel café restaurant). Si vous dépendez d'une convention avec des taux spécifiques, merci de nous le signaler à `contact@mon-entreprise.beta.gouv.fr` - unité: heure/mois - formule: - barème: - assiette: heures supplémentaires - multiplicateur: période . semaines par mois - tranches: - - taux: 25% - plafond: 8 heures/semaine - - taux: 50% - -contrat salarié . temps de travail . heures complémentaires: - description: > - Les heures complémentaires sont les heures effectuées par un salarié à temps - partiel au delà de son horaire contractuel. Les heures complémentaires ne - doivent pas amener le salarié à travailler pour une durée supérieur à la - durée légale ou conventionnelle du travail. - applicable si: temps partiel - question: Combien d'heures complémentaires (non récupérées en repos) sont effectuées par mois ? - par défaut: 0 heure/mois - -contrat salarié . temps de travail . contrôle heures complémentaires 10 pourcents: - type: notification - formule: heures complémentaires > heures complémentaires . seuil légal - description: Sauf disposition conventionnelle, le nombre d'heures complémentaires ne peut être supérieur à un dixième de la durée contractuelle du temps partiel. - -# TODO: Le système d'unité ne fait pas la conversion mois/semaines automatiquement donc nous devons ajouter un terme "semaines par mois" manuellement -contrat salarié . temps de travail . contrôle heures complémentaires max: - type: notification - sévérité: avertissement - formule: heures complémentaires + temps partiel . heures par semaine * période . semaines par mois >= base légale * période . semaines par mois - description: Les heures complémentaires ne doivent pas amener le salarié à travailler pour une durée supérieure ou égale à la durée légale du travail (35h) - -contrat salarié . temps de travail . heures complémentaires . majoration: - description: > - La rémunération des heures complémentaire fait l'objet d'un ou plusieurs - taux de majoration, fixés par convention ou accord collectif d'entreprise ou - d'établissement (ou, à défaut, par convention ou accord de branche). Chaque - taux est au minimum fixé à 10%. - - À défaut d'accord ou de convention, les taux de majoration horaire sont fixés à : - - 10 % pour les heures effectuées dans la limite d'un dixième de la durée contractuelle - - 25 % pour les heures suivantes. - titre: majoration heures complémentaires - note: Nous n'implémentons pas les taux conventionnels - formule: - barème: - assiette: heures complémentaires - mutliplicateur: - tranches: - - taux: 10% - plafond: seuil légal - - taux: 25% - -contrat salarié . temps de travail . heures complémentaires . seuil légal: - description: >- - Sauf disposition conventionnelle, le nombre d'heures complémentaires ne peut - être supérieur à un dixième de la durée contractuelle du temps partiel. - - Si la convention le permet, les heures complémentaire au delà de ce seuil - sont rémunérée avec une majoration de 25% - unité: heures/mois - formule: - produit: - assiette: temps partiel . heures par semaine - taux: 10% - facteur: période . semaines par mois - arrondi: 0 décimales - -contrat salarié . statut JEI: - titre: Statut JEI - question: >- - La personne bénéficie-t-elle de l'exonération Jeune Entreprise Innovante (JEI) ? - description: >- - Le statut de jeune entreprise innovante (JEI) a été créé par la loi de - finances pour 2004 et permet aux PME de moins de 8 ans consacrant 15% au - moins de leurs charges à de la Recherche et Développement de bénéficier - d'une exonération de cotisations sociales. - - - L’exonération peut s’appliquer sur les rémunérations versées : - - - aux salariés pour lesquels l’employeur est soumis à l’obligation - d’assurance chômage - - - aux mandataires sociaux qui participent, à titre principal, au projet - de recherche et de développement de l’entreprise - - - Par simplification, le bénéfice de l’exonération au titre d’un salarié sera - considéré comme acquis dès lors que la moitié de son temps de travail au - moins est consacrée à un ou des projets de recherche et de développement et - l’exonération ne pourra être remise en cause. - - par défaut: non - rend non applicable: - - réduction générale - - allocations familiales . taux réduit - - contrat salarié . maladie . taux employeur . taux réduit - - lodeom - -contrat salarié . statut JEI . exonération de cotisations: - titre: Exonération JEI - aide: - type: réduction de cotisations - démarches: non - description: | - Exonération pour les jeunes entreprises innovantes (JEI). - références: - description: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31188 - calcul: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-ou-aides-liees-au-s/jeunes-entreprises-innovantes/quelle-exoneration.html - cumuls: https://www.legisocial.fr/actualites-sociales/2068-comment-declarer-les-cotisations-dallocations-familiales-si-lentreprise-beneficie-du-regime-jei.html - unité: €/mois - - formule: - somme: - - allocations familiales - - maladie . employeur - - vieillesse . employeur - plafond: - recalcul: - avec: - rémunération . brut de base: 4.5 * SMIC - -contrat salarié . réduction générale: - description: | - Dans le cadre du pacte de responsabilité et de solidarité, le dispositif zéro cotisation Urssaf permet à l'employeur d'un salarié au Smic de ne plus payer aucune cotisation. Le montant de l'allègement est égal au produit de la rémunération annuelle brute par un coefficient. Il n'y a pas de formalité particulière à effectuer. - références: - description: https://www.service-public.fr/professionnels-entreprises/vosdroits/F24542 - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale.html - calcul: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/le-calcul-de-la-reduction.html - cumuls: https://www.legisocial.fr/actualites-sociales/2068-comment-declarer-les-cotisations-dallocations-familiales-si-lentreprise-beneficie-du-regime-jei.html - non applicable si: cotisations . assiette forfaitaire . montant - formule: - produit: - assiette: cotisations . assiette - facteur: coefficient - plafond: plafond avec application de la DFS - exemples: - # Formule de calcul algébrique : (0,2809÷0,6)×(1,6×(1 521,22÷1 530)−1)×1 530 - - nom: "Maximale dans le cas d'un SMIC" - situation: - rémunération . brut: 1521.22 - valeur attendue: 487.55 - - nom: 'Salaire proche du SMIC' - situation: - rémunération . brut: 1530 - valeur attendue: 490.37 - - nom: 'Résiduelle pour un salaire médian' - situation: - rémunération . brut: 2300 - valeur attendue: 87.10 - - nom: 'Nulle au-dessus du plafond' - situation: - rémunération . brut: 2464 - valeur attendue: 0 - -contrat salarié . réduction générale . coefficient: - formule: - produit: - assiette: SMIC / cotisations . assiette * 1.6 - 1 - facteur: T / 0.6 - plancher: 0% - plafond: T - arrondi: 4 décimales - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/le-calcul-de-la-reduction/etape-1--determination-du-coeffi.html - Code de la sécurité sociale: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000025103779&cidTexte=LEGITEXT000006073189 - -contrat salarié . réduction générale . T: - titre: Coefficient T - unité: '' - formule: - somme: - - T sécurité sociale et chômage - - valeur: retraite complémentaire . employeur . taux tranche 1 - plafond: 4.72% - - valeur: contribution d'équilibre général . employeur . taux tranche 1 - plafond: 1.29% - -contrat salarié . réduction générale . T sécurité sociale et chômage: - unité: '' - formule: - somme: - - maladie . taux employeur - - allocations familiales . taux - - vieillesse . employeur . déplafonnée . taux - - vieillesse . employeur . plafonnée . taux - - maladie . taux solidarité autonomie - - ATMP . taux minimum - - FNAL . taux - - chômage . employeur . taux - -contrat salarié . réduction générale . imputation sécurité sociale: - formule: - produit: - assiette: réduction générale - facteur: T sécurité sociale et chômage / T - -contrat salarié . réduction générale . imputation retraite complémentaire: - formule: réduction générale - imputation sécurité sociale - -contrat salarié . réduction générale . plafond avec application de la DFS: - applicable si: déduction forfaitaire spécifique > 0 - unité: €/mois - formule: - produit: - taux: 130% - assiette: - recalcul: - règle: réduction générale - avec: - déduction forfaitaire spécifique . application: non - -contrat salarié . contribution d'équilibre général: - description: >- - Cette cotisation créée en 2019 permet à la fois de compenser les - charges résultant des départs à la retraite avant 67 ans et d’honorer les - engagements retraite des personnes qui ont cotisé à la GMP, une ancienne - cotisation de compensation pour les cadres. - acronyme: CEG - cotisation: - branche: retraite - type de retraite: complémentaire - destinataire: AGIRC-ARRCO - formule: - barème: - assiette: cotisations . assiette - multiplicateur: plafond sécurité sociale - composantes: - - attributs: - nom: employeur - tranches: - - taux [ref taux tranche 1]: 1.29% - plafond: 1 - - taux: 1.62% - plafond: 8 - - attributs: - nom: salarié - assiette: cotisations . assiette . salariale - tranches: - - taux: 0.86% - plafond: 1 - - taux: 1.08% - plafond: 8 - - références: - calcul des cotisations: https://www.agirc-arrco.fr/ce-qui-change-au-1er-janvier-2019/vous-etes-une-entreprise-tiers-declarant/ - -contrat salarié . contribution d'équilibre technique: - acronyme: CET - cotisation: - branche: retraite - type de retraite: complémentaire - destinataire: AGIRC-ARRCO - applicable si: cotisations . assiette > plafond sécurité sociale - formule: - produit: - assiette: cotisations . assiette - plafond: 8 * plafond sécurité sociale - composantes: - - attributs: - nom: employeur - taux [ref]: 0.21% - - attributs: - nom: salarié - taux: 0.14% - références: - calcul des cotisations: https://www.agirc-arrco.fr/ce-qui-change-au-1er-janvier-2019/vous-etes-une-entreprise-tiers-declarant/ - -contrat salarié . retraite complémentaire: - cotisation: - branche: retraite - type de retraite: complémentaire - destinataire: AGIRC-ARRCO - description: | - Cotisations de retraite complémentaire. - formule: - barème: - assiette: cotisations . assiette - multiplicateur: plafond sécurité sociale - composantes: - - attributs: - nom: employeur - tranches: - - taux [ref taux tranche 1]: 4.72% - plafond: 1 - - taux [ref taux tranche 2]: 12.95% - plafond: 8 - - attributs: - nom: salarié - assiette: cotisations . assiette . salariale - tranches: - - taux [ref taux tranche 1]: 3.15% - plafond: 1 - - taux [ref taux tranche 2]: 8.64% - plafond: 8 - références: - calcul des cotisations: https://www.agirc-arrco.fr/ce-qui-change-au-1er-janvier-2019/vous-etes-une-entreprise-tiers-declarant/ - régime des impatriés: https://www.legifrance.gouv.fr/affichTexteArticle.do;jsessionid=D2C4F8F0A5E19693ADF9F440120B748A.tplgfr31s_2?idArticle=JORFARTI000038496272&cidTexte=JORFTEXT000038496102&dateTexte=29990101&categorieLien=id - -contrat salarié . retraite supplémentaire: - formule: - somme: - - nom: employeur - valeur: 0€/mois - - nom: salarié - valeur: 0€/mois - -contrat salarié . retraite supplémentaire . part déductible: - formule: - valeur: retraite supplémentaire . employeur - abattement: plafond d'exonération sociale employeur - -contrat salarié . retraite supplémentaire . plafond d'exonération sociale employeur: - formule: - valeur: 5% * rémunération . brut - plafond: 5% * plafond sécurité sociale - références: - Article D242-1: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000037456320&cidTexte=LEGITEXT000006073189&dateTexte=20180930 - -contrat salarié . retraite supplémentaire . exonération fiscale: - titre: retraite supplémentaire exonérée d'impôt - formule: - valeur: retraite supplémentaire - plafond: - produit: - assiette: rémunération . brut - plafond: 8 * plafond sécurité sociale temps plein - taux: 8% - références: - Bopfip § 120: https://bofip.impots.gouv.fr/bofip/5956-PGP.html - -contrat salarié . AGS: - description: Cotisation au Régime de Garantie des Salaires - cotisation: - dû par: employeur - branche: assurance chômage - references: - calcul: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31409 - formule: - produit: - assiette: cotisations . assiette - taux: 0.15% - plafond: 4 * plafond sécurité sociale - -contrat salarié . allocations familiales: - cotisation: - dû par: employeur - branche: famille - formule: - produit: - assiette: cotisations . assiette - taux [ref]: - variations: - - si: taux réduit - alors: 3.45% - - sinon: 5.25% - références: - calcul: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-cotisation-dallocations-famil.html - -contrat salarié . allocations familiales . taux réduit: - formule: cotisations . assiette < plafond de réduction - -contrat salarié . allocations familiales . taux réduit . plafond de réduction: - titre: Plafond de la réduction des allocations familiales - formule: SMIC * 3.5 - -contrat salarié . APEC: - cotisation: - branche: assurance chômage - type de retraite: complémentaire - destinataire: APEC - description: | - Cotisation chômage complémentaire cadre, pour le fonctionnement de l'APEC - (Association Pour l’Emploi des Cadres) - références: - chiffres clés: http://www.agirc-arrco.fr/l-agirc-et-larrco/chiffres-cles - - applicable si: statut cadre - - formule: - produit: - assiette: cotisations . assiette - plafond: 4 * plafond sécurité sociale - composantes: - - attributs: - nom: employeur - taux: 0.036% - - attributs: - nom: salarié - taux: 0.024% - -contrat salarié . chômage: - cotisation: - branche: assurance chômage - destinataire: Pôle emploi - dû par: employeur - description: Cotisation d’assurance chômage - références: - calcul: http://www.pole-emploi.fr/employeur/taux-des-contributions-de-l-assurance-chomage-et-cotisations-ags-@/article.jspz?id=61567 - urssaf: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/lassurance-chomage-et-lags/les-taux.html - changements 2017: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-employeur/contributions-patronales-dassura.html - formule: - produit: - assiette: cotisations . assiette - plafond: 4 * plafond sécurité sociale - composantes: - - attributs: - nom: salarié - taux [ref]: 0% - - attributs: - nom: employeur - taux [ref]: 4.05% - exemples: - - nom: SMIC - situation: - cotisations . assiette: 1500 - valeur attendue: 60.75 - - nom: Haut salaire - situation: - cotisations . assiette: 20000 - valeur attendue: 555.34 - -contrat salarié . complémentaire santé: - description: | - L'Assurance maladie (Sécurité sociale) ne rembourse pas complètement vos dépenses de santé. - - La complémentaire santé d'entreprise complète ces remboursements, en totalité ou en partie. - - En plus de la complémentaire santé, le dispositif collectif de l'entreprise peut proposer d'autres garanties (garanties décès, garantie dépendance, etc.). - cotisation: - branche: santé - références: - service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F20739 - formule: - produit: - assiette: forfait - composantes: - # Répartition arbitraire, en sachant que l'employeur doit prendre en charge au minimum 50% - - attributs: - nom: employeur - taux: part employeur - - attributs: - nom: salarié - taux: part salarié - exemples: - - nom: forfait à 40€ - situation: - forfait: 40 - valeur attendue: 40 - - nom: forfait à 100€ payé par l'employeur - situation: - forfait: 100 - part employeur: 100 - valeur attendue: 100 - -contrat salarié . complémentaire santé . part employeur: - description: Part de la complémentaire santé payée par l'employeur. Doit être de 50% minimum - question: Quelle est la part de la complémentaire santé payée par l'employeur ? - suggestions: - 50%: 50% - 100%: 100% - par défaut: 50% - -contrat salarié . complémentaire santé . part employeur min: - type: notification - sévérité: avertissement - formule: part employeur < 50% - description: La part employeur de la complémentaire santé doit être de 50% au minimum - -contrat salarié . complémentaire santé . part salarié: - description: Part de la complémentaire santé payée par l'employé. Ne peut pas être supérieure à 50% - formule: 100% - part employeur - -contrat salarié . complémentaire santé . forfait: - titre: Forfait de complémentaire santé entreprise - description: >- - L'employeur a l'obligation de proposer une offre de complémentaire santé. Il - doit prendre à sa charge au moins la moitié de son coût. - - Le montant peut varier, mais la prévoyance doit couvrir un panier minimum - légal de soins. - - note: >- - Pour des raisons historiques, la couverture sociale santé des salariés - d'Alsace-Moselle est plus forte. En conséquence, le prix des forfaits de - complémentaire santé qui leur sont proposés sont inférieurs. Une étude de - Meilleureassurance.com nous permet de supposer qu'il vaut en moyenne ~ 70% - du prix moyen en France. - - références: - les obligations de l'employeur: https://www.service-public.fr/professionnels-entreprises/vosdroits/F33754 - Alsace-moselle étude Meilleureassurance.com: http://www.lefigaro.fr/conjoncture/2018/10/16/20002-20181016ARTFIG00248-les-tarifs-des-complementaires-sante-font-le-grand-ecart-d-un-departement-a-l-autre.php - - question: Quel est le montant mensuel total (salarié et employeur) de la complémentaire santé entreprise ? - par défaut: 40 €/mois - suggestions: - basique: 40 €/mois - élevé: 100 €/mois - alsace moselle basique: 30 €/mois - -contrat salarié . complémentaire santé . contrôle min: - type: notification - sévérité: avertissement - formule: complémentaire santé . forfait < 15 €/mois - description: Vérifiez bien qu'une complémentaire santé si peu chère couvre le panier de soin minimal défini dans la loi. - -contrat salarié . régime alsace moselle: - titre: Régime Alsace-Moselle - description: | - Nous considérons qu'un salarié est affilié au régime Alsace-Moselle quand l'établissement dans lequel il travaille est situé dans ces départements. - - Attention : c'est une **simplification** : l'affiliation est plus compliquée que celà, voir les conditions exactes [sur le site du régime](http://regime-local.fr/salaries/). - formule: - une de ces conditions: - - établissement . localisation . département = 'Bas-Rhin' - - établissement . localisation . département = 'Haut-Rhin' - - établissement . localisation . département = 'Moselle' - -contrat salarié . contribution au dialogue social: - cotisation: - dû par: employeur - collecteur: Urssaf - description: | - Contribution patronale destinée à abonder un fonds paritaire dédié au financement des organisations syndicales et des organisations professionnelles d’employeurs. - - Anciennement 'contribution patronale au financement des organisations syndicales' - - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-contribution-patronale-au-dia.html - service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F33308 - - formule: - produit: - assiette: cotisations . assiette - taux: 0.016% - -contrat salarié . CSG et CRDS: - cotisation: - dû par: salarié - collecteur: Urssaf - description: >- - La CSG et la CRDS sont dues par tous les salariés remplissant les deux - conditions suivantes : - - - ils sont domiciliés fiscalement en France, - - ils sont à la charge d’un régime français d’assurance maladie obligatoire. - - Si l’un des deux critères n’est pas rempli, la CSG et la CRDS ne sont pas - dues, à la condition d’en apporter la preuve (justificatif fiscal ou carte - d’assuré social). - formule: - somme: - - CSG - - CRDS - -contrat salarié . CSG et CRDS . assiette de base: - références: - calcul: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-csg-crds/les-revenus-salariaux-soumis-a-l.html - abattement: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-csg-crds/abattement-et-deductions/les-revenus-exclus-de-labattemen.html - heures supplémentaires: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 - formule: - somme: - - assiette abattue totale - # TODO : ce sont les cotisations forfaitaires qui ne rentrent pas dans - # l'abattue, et non tous les avantages sociaux - - avantages sociaux - # - (- assiette revenu remplacements) - - (- assiette heures supplémentaires et complémentaires défiscalisées) - -contrat salarié . CSG et CRDS . assiette abattue totale: - formule: - barème: - assiette: - cotisations . assiette - # - rémunération . revenus de remplacement - multiplicateur: plafond sécurité sociale - # c'est en fait un abattement de 1,75% sur la partie en-dessous de 4 fois le plafond - tranches: - - taux: 98.25% - plafond: 4 - - taux: 100% - -contrat salarié . CSG et CRDS . assiette revenu remplacements: - formule: - produit: - taux: 98.25% - assiette: rémunération . revenus de remplacement - -contrat salarié . CSG et CRDS . assiette heures supplémentaires et complémentaires défiscalisées: - formule: - produit: - assiette: rémunération . net imposable . heures supplémentaires et complémentaires défiscalisées - taux: 98.25% - références: - DSN: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 - -contrat salarié . CSG et CRDS . non déductible: - titre: CSG non déductible et CRDS - formule: - somme: - - CSG . non déductible - - CRDS - - revenus de remplacement . CSG non déductible - - revenus de remplacement . CRDS - -contrat salarié . CSG et CRDS . CSG: - non applicable si: établissement . localisation . département = 'Mayotte' - description: >- - La contribution sociale généralisée (CSG) est un impôt destiné à participer - au financement de la protection sociale. - - A la différence des cotisations sociales qui ne portent que sur les revenus - d’activité, la CSG concerne, outre les revenus d’activité et de remplacement - (allocations chômage, indemnités journalières…), les revenus du patrimoine, - les produits de placement ou les sommes engagées ou redistribuées par les - jeux. - - Elle est prélevée à la source sur la plupart des revenus. Elle est recouvrée - par les Urssaf sur les revenus d’activité et par l’administration fiscale - sur les revenus du patrimoine. - - Le produit de la CSG est reversé à la Cnam et à la Cnaf, il finance - également le fonds de solidarité vieillesse - formule: - multiplication: - assiette: assiette de base - composantes: - - attributs: - nom: déductible - taux: - nom: taux - valeur: 6.8% - - attributs: - nom: non déductible - composantes: - - taux: - nom: taux - valeur: 2.4% - - attributs: - nom: heures supplémentaires et complémentaires défiscalisées - assiette: assiette heures supplémentaires et complémentaires défiscalisées - taux: déductible . taux + non déductible . taux - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/la-base-de-calcul/assiette-csg-crds.html - heures supplémentaires: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 - exemples: - - nom: 'CSG cadre' - situation: - cotisations . assiette: 1500 - complémentaire santé . forfait: 40 - statut cadre: oui - valeur attendue: 139.495 - - nom: 'CSG non cadre' - situation: - cotisations . assiette: 1500 - complémentaire santé . forfait: 40 - valeur attendue: 137.425 - -contrat salarié . CSG et CRDS . CRDS: - cotisation: - impôt: oui - dû par: salarié - description: Contribution pour le remboursement de la dette sociale - formule: - produit: - assiette: - somme: - - assiette de base - - assiette heures supplémentaires et complémentaires défiscalisées - taux: - nom: taux - valeur: 0.5% - -contrat salarié . CSG et CRDS . revenus de remplacement: - titre: CSG et CRDS revenus de remplacement - description: >- - La CSG et CRDS prélevées sur les revenus de remplacement. - note: >- - Le prélèvement de la CSG et de la CRDS ne peut pas avoir pour effet de - réduire le montant de la rémunération d’activité et des allocations de - chômage à un seuil inférieur au Smic brut. - formule: - somme: - - revenus de remplacement . CSG déductible - - revenus de remplacement . CSG non déductible - - revenus de remplacement . CRDS - -contrat salarié . CSG et CRDS . revenus de remplacement . CSG déductible: - titre: CSG déductible revenus de remplacement - produit: - assiette: CSG et CRDS . assiette revenu remplacements - taux: 3.8% - plafond [ref]: - somme: - - rémunération . net de cotisations - - rémunération . revenus de remplacement - - (- SMIC temps plein) - plancher: 0€/mois - note: - -contrat salarié . CSG et CRDS . revenus de remplacement . CSG non déductible: - titre: CSG non déductible revenus de remplacement - produit: - assiette: CSG et CRDS . assiette revenu remplacements - taux: CSG . non déductible . taux - plafond [ref]: - valeur: CSG déductible . plafond - CSG déductible - -contrat salarié . CSG et CRDS . revenus de remplacement . CRDS: - titre: CRDS revenus de remplacement - produit: - assiette: assiette revenu remplacements - taux: CRDS . taux - plafond: - valeur: CSG non déductible . plafond - CSG non déductible - -contrat salarié . FNAL: - titre: Contribution au Fonds National d’Aide au Logement - description: | - Le fonds national d’aide au logement (Fnal) est une contribution qui assure le financement de l’allocation logement. - cotisation: - dû par: employeur - destinataire: Urssaf - branche: famille - références: - calcul: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-contribution-au-fonds-nationa.html - formule: - produit: - assiette: cotisations . assiette - taux [ref]: - variations: - - si: éligible taux réduit - alors: 0.1% - - sinon: 0.5% - plafond: - applicable si: éligible taux réduit - valeur: plafond sécurité sociale - - exemples: - - nom: SMIC - situation: - cotisations . assiette: 1500 - entreprise . effectif: 10 - valeur attendue: 1.5 - -contrat salarié . FNAL . éligible taux réduit: - formule: entreprise . effectif < 50 - -contrat salarié . formation professionnelle: - cotisation: - dû par: employeur - collecteur: OPCO - branche: formation - # TODO majoration pour les entreprises de travail temporaire - # - description: Cette contribution obligatoire est collectée par l'OPCO (opérateurs de compétences) désigné par la branche conventionnelle de l'entreprise, ou à défaut à un OPCO interprofessionnel. - note: | - Une part supplémentaire peut-être obligatoire en fonction des accords collectifs d'une entreprise. - - > Par exemple pour la convention collective Syntec, un supplément de 0.025% est obligatoire. - - Le taux est porté à 1,3 % pour les entreprises de travail temporaire. Par ailleurs en cas de franchissement du seuil d'effectifs de 10 salariés, des taux spécifiques s'appliquent afin de limiter la hausse de la contribution à la formation professionnelle : - - - taux de **0,55 %** pour le franchissement en année **N, N+1 et N+2** - - taux de **0,70 %** pour le franchissement en année **N+3** (1,3 % pour les entreprises de travail temporaire) - - taux de **0,90 %** pour le franchissement en année **N+4** (1,3 % pour les entreprises de travail temporaire) - - taux de **1 %** pour le franchissement en année **N+5** (1,3 % pour les entreprises de travail temporaire) - non applicable si: - toutes ces conditions: - - entreprise . effectif < 11 - - apprentissage - - formule: - produit: - assiette: cotisations . assiette - variations: - - si: entreprise . effectif < 11 - alors: - taux: 0.55% - - sinon: - taux: 1% - références: - fiche Ministère du travail: https://travail-emploi.gouv.fr/formation-professionnelle/entreprises-et-formation/article/participation-financiere-des-entreprises-au-developpement-de-la-formation - Bercy infos: https://www.economie.gouv.fr/entreprises/contribution-formation-professionnelle - Taux réduit: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000037387044&cidTexte=LEGITEXT000006072050&dateTexte=20190101 - -contrat salarié . maladie: - cotisation: - branche: santé - description: Cotisations de la branche maladie - références: - fiche: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-cotisation-maladie---maternit.html - Décret n° 2017-1891 relatif au taux des cotisations d'assurance maladie: https://www.legifrance.gouv.fr/eli/decret/2017/12/30/CPAS1732212D/jo/texte - Réduction 2019: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-employeur/une-reduction-des-cotisations-pa.html - formule: - produit: - assiette: cotisations . assiette - composantes: - - attributs: - nom: employeur - composantes: - - attributs: - titre: maladie, maternité, invalidité, décès - nom: base - taux: taux employeur - - attributs: - nom: contribution solidarité autonomie - taux: taux solidarité autonomie - - attributs: - nom: salarié - titre: maladie, maternité, invalidité, décès salarié - taux: taux salarié - -contrat salarié . maladie . taux solidarité autonomie: - acronyme: CSA - formule: 0.3% - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-contribution-solidarite-auton.html - Fiche service-public: https://www.service-public.fr/professionnels-entreprises/vosdroits/F32872 - -contrat salarié . maladie . taux employeur: - formule: - variations: - - si: taux réduit - alors: 7% - - sinon: 13% - -contrat salarié . maladie . taux employeur . taux réduit: - formule: cotisations . assiette < plafond de réduction employeur - -contrat salarié . maladie . taux salarié: - formule: - variations: - - si: régime alsace moselle - alors: 1.5% - - sinon: 0% - -contrat salarié . maladie . plafond de réduction employeur: - formule: 2.5 * SMIC - -contrat salarié . médecine du travail: - alias: santé au travail - cotisation: - dû par: employeur - branche: santé - références: - fiche: http://travail-emploi.gouv.fr/emploi/maintien-dans-l-emploi/prevention-et-maintien-dans-l-emploi-10705/services-de-sante-au-travail-sst - question au sénat: http://www.senat.fr/questions/base/2005/qSEQ050919275.html - rapport officiel, page 6: http://www.ladocumentationfrançaise.fr/var/storage/rapports-publics/074000708.pdf - description: | - L'employeur a l'obligation d'organiser un service de santé au travail, en adhérant à un service interentreprises, ou en créant un service interne. - - Dans le cas de l'adhésion à un service, le montant de cette cotisation n'est pas défini par la loi, mais il doit être proportionnel au nombre d'employés. Nous avons choisi un montant indicatif (voir les références) ajusté avec l'inflation depuis 2007. - formule: 80 €/an - -contrat salarié . participation effort de construction: - titre: Participation à l'effort de construction - alias: Dispositif du 1% logement - acronyme: PEEC - description: Participation des employeurs à l'effort de construction - cotisation: - branche: logement - dû par: employeur - impôt: oui - références: - fiche: https://www.service-public.fr/professionnels-entreprises/vosdroits/F22583 - note: | - L'employeur a le choix entre verser cet impôt à un "organisme du 1% patronal" agréé, investir la somme dans le logement de ses salariés, ou accorder à eux et leur famille des prêts de construction à taux réduit. - - non applicable si: entreprise . effectif < 50 - formule: - produit: - assiette: cotisations . assiette - taux: 0.45% - -contrat salarié . prévoyance: - formule: - somme: - - nom: employeur - formule: 0 €/mois - - nom: salarié - formule: 0 €/mois - -contrat salarié . prévoyance . part déductible: - formule: - valeur: prévoyance . employeur - abattement: plafond exonération sociale employeur - -contrat salarié . prévoyance . plafond exonération sociale employeur: - formule: - somme: - - 6% * plafond sécurité sociale - - 1.5% * rémunération . brut - plafond: 12% * plafond sécurité sociale - -# TODO: À fusionner avec `contrat salarié . prévoyance`. Pour l'instant pas -# gênant d'avoir deux cotisations séparées car `contrat salarié . prévoyance` -# est toujours nulle (pas de question associée) -contrat salarié . prévoyance obligatoire cadre: - titre: Prévoyance obligatoire pour les cadres - cotisation: - dû par: employeur - branche: santé - références: - minimum: http://www.axios.fr/150-tranche-a-evitez-une-erreur-a-160-000-euros - applicable si: statut cadre - formule: - produit: - assiette: cotisations . assiette - plafond: plafond sécurité sociale - taux: 1.5% - # TODO attention : il semblerait que ce 1.5% englobe aussi la - # complémentaire santé ! La confusion serait entretenue par les organismes - # de prévoyance... - -contrat salarié . prévoyance . exonération fiscale: - titre: prévoyance exonérée d'impôt - formule: - valeur: prévoyance - plafond: - somme: - - 5% * plafond sécurité sociale temps plein - - 2% * rémunération . brut - plafond: 2% * 8 * plafond sécurité sociale temps plein - références: - Bopfip § 120: https://bofip.impots.gouv.fr/bofip/5956-PGP.html - -contrat salarié . taxe d'apprentissage: - cotisation: - destinataire: Opérateurs de compétences (OPCO) - branche: formation - dû par: employeur - description: La taxe d'apprentissage permet de financer par les entreprises les dépenses de l'apprentissage et des formations technologiques et professionnelles - applicable si: - une de ces conditions: - - entreprise . effectif > 10 - - apprentissage = non - références: - description: https://www.service-public.fr/professionnels-entreprises/vosdroits/F22574 - csa: http://www.opcalia.com/employeurs/financer-la-formation-et-lapprentissage/taxe-dapprentissage/contribution-supplementaire-a-lapprentissage-csa/ - - note: Taxe complexe, comportant notamment des exonérations non prises en compte ici. - formule: - somme: - - base - - contribution supplémentaire - -contrat salarié . taxe d'apprentissage . assiette: - titre: assiette de la taxe d'apprentissage - description: Le salaire des apprentis est partiellement exonéré dans la base de calcul de la taxe d'apprentissage. - formule: - variations: - - si: apprentissage - alors: - valeur: cotisations . assiette - abattement: - variations: - - si: établissement . localisation . outre-mer - alors: 20% * SMIC - - sinon: 11% * SMIC - - sinon: cotisations . assiette - -contrat salarié . taxe d'apprentissage . base: - titre: taxe d'apprentissage de base - formule: - produit: - assiette: assiette - taux: - variations: - - si: régime alsace moselle - alors: 0.44% - - sinon: 0.68% - -contrat salarié . taxe d'apprentissage . contribution supplémentaire: - applicable si: - toutes ces conditions: - - entreprise . effectif >= 250 - - entreprise . ratio alternants < 5% - - formule: - produit: - assiette: assiette - - # exception: - # si: régime géographique = Alsace-Moselle - # références: - # - http://circulaires.legifrance.gouv.fr/pdf/2012/03/cir_34909.pdf - # - http://bofip.impots.gouv.fr/bofip/6325-PGP.html - # # Toutefois, en application du IV de l’article 1609 quinvicies du CGI , dans les départements du Haut-Rhin, du Bas-Rhin et de la Moselle, les taux précités sont réduits à 52 % de leur montant, soit : - # facteur: 0.52 - - variations: - - si: taxe d'apprentissage . csa au taux majoré - alors: - taux: 0.6% - - si: entreprise . ratio alternants < 1% - alors: - taux: 0.4% - - si: entreprise . ratio alternants < 2% - alors: - taux: 0.2% - - si: entreprise . ratio alternants < 3% - alors: - taux: 0.1% - - si: entreprise . ratio alternants < 5% - alors: - taux: 0.05% - -contrat salarié . taxe d'apprentissage . csa au taux majoré: - titre: CSA au taux majoré - formule: - toutes ces conditions: - - entreprise . effectif >= 2000 - - entreprise . ratio alternants < 1% - -contrat salarié . taxe sur les salaires . assiette de base: - formule: - somme: - - cotisations . assiette - - avantages sociaux - références: - assiette: http://bofip.impots.gouv.fr/bofip/6690-PGP.html - -contrat salarié . taxe sur les salaires . assiette: - formule: - valeur: assiette de base - abattement: prime d'impatriation - références: - bofig: http://bofip.impots.gouv.fr/bofip/6691-PGP.html - impots.gouv.fr: https://www.impots.gouv.fr/portail/international-particulier/le-regime-des-impatries - -contrat salarié . taxe sur les salaires . barème: - unité: €/an - - formule: - barème: - assiette: assiette - tranches: - - taux: 4.25% - plafond: 8004 €/an - - taux: 8.5% - plafond: 15981 €/an - - taux: 13.6% - exemples: - - nom: salaire médian - situation: - contrat salarié . taxe sur les salaires: oui - assiette: 2300 - valeur attendue: 2598.40 - note: | - Nous n'implémentons pas les taux spécifiques pour l'outre-mer - références: - barème: https://www.service-public.fr/professionnels-entreprises/vosdroits/F22576 - -contrat salarié . profession spécifique: - question: Le salarié exerce t-il l'une des professions suivantes ? - par défaut: non - formule: - une possibilité: - possibilités: - - journaliste - - ouvrier du bâtiment - - artiste musicien - - pilote de ligne ou personnel navigant - -contrat salarié . profession spécifique . journaliste: - formule: contrat salarié . profession spécifique = 'journaliste' - icônes: ✒ - description: >- - Concerne les journalistes, rédacteurs, photographes, directeurs de journaux - Critiques dramatiques et musicaux. - -contrat salarié . profession spécifique . journaliste . réduction de taux: - applicable si: profession spécifique = 'journaliste' - remplace: - - règle: vieillesse . employeur . plafonnée . taux - par: vieillesse . employeur . plafonnée . taux * réduction de taux - - règle: vieillesse . employeur . déplafonnée . taux - par: vieillesse . employeur . déplafonnée . taux * réduction de taux - - règle: vieillesse . salarié . plafonnée . taux - par: vieillesse . salarié . plafonnée . taux * réduction de taux - - règle: vieillesse . salarié . déplafonnée . taux - par: vieillesse . salarié . déplafonnée . taux * réduction de taux - - - règle: allocations familiales . taux - par: allocations familiales . taux * réduction de taux - - règle: établissement . taux du versement transport - par: établissement . taux du versement transport * réduction de taux - - règle: ATMP . taux - par: ATMP . taux * réduction de taux - - règle: ATMP . taux minimum - par: ATMP . taux minimum * réduction de taux - formule: 80% - -contrat salarié . profession spécifique . journaliste . abattement fiscal: - applicable si: profession spécifique = 'journaliste' - remplace: rémunération . net imposable - titre: net imposable journaliste - formule: - valeur: rémunération . net imposable - abattement: 7650€/an - -contrat salarié . profession spécifique . ouvrier du bâtiment: - icônes: 👷‍♂️ - description: >- - Concerne les ouvriers du bâtiment visés aux paragraphes 1er et 2 de - l’article 1er du décret du 17 novembre 1936, à l’exclusion de ceux qui - travaillent en usine ou en atelier. - -contrat salarié . profession spécifique . artiste musicien: - icônes: 🎼 - description: >- - Concerne les artistes musiciens, choristes, chefs d’orchestre, régisseurs de - théâtre - -contrat salarié . profession spécifique . pilote de ligne ou personnel navigant: - icônes: ✈ - description: >- - Concerne les pilotes, radios, mécaniciens navigants des compagnies de - transports aériens ; pilotes et mécaniciens employés par les maisons de - construction d’avions et de moteurs pour l’essai de prototypes ; pilotes - moniteurs d’aéro-clubs et des écoles d’aviation civile - -contrat salarié . régime des impatriés: - question: Le salarié bénéficie-t-il du régime des impatriés ? - non applicable si: situation personnelle . domiciliation fiscale à l'étranger - par défaut: non - description: | - Si vous êtes salarié ou dirigeant fiscalement assimilé, et si vous avez été appelé par une entreprise étrangère à occuper un emploi dans une entreprise établie en France ayant un lien avec la première ou si vous avez été directement recruté à l’étranger par une entreprise établie en France, vous pouvez bénéficier du régime des impatriés. - - Vous devez en outre ne pas avoir été fiscalement domicilié en France les cinq années civiles précédant celle de la prise de fonctions et fixer en France votre domicile fiscal dès votre prise de fonctions. - - Les impatriés sont exonérés de cotisations retraite (régime de base et complémentaire) à condition de justifier d'une contribution minimale versée par ailleurs (par exemple dans une caisse de retraite ou un fond de pension étranger). Ils n’acquièrent aucun droit pendant la durée d’exonération. - - note: La durée d’application est fixée au maximum jusqu’au 31 décembre de la huitième année civile suivant la prise de fonctions dans l’entreprise d’accueil. - rend non applicable: - - vieillesse - - retraite complémentaire - - protection sociale . retraite . base - références: - impots.gouv.fr: https://www.impots.gouv.fr/portail/particulier/questions/puis-je-beneficier-du-regime-des-impatries - bofip: http://bofip.impots.gouv.fr/bofip/5694-PGP - Article 155B du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069577&idArticle=LEGIARTI000006307476&dateTexte=&categorieLien=cid - -contrat salarié . régime des impatriés . information: - type: notification - formule: oui - 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. - - [Lire le texte de loi](https://www.legifrance.gouv.fr/affichCode.do;jsessionid=F5CFB7C90D1D1F529A2CDC9FFD20BD6E.tplgfr34s_3?idSectionTA=LEGISCTA000038510929&cidTexte=LEGITEXT000006073189&dateTexte=20190626) - -contrat salarié . taxe sur les salaires: - unité: €/mois - taxe: - dû par: employeur - description: La taxe sur les salaires en France est un impôt progressif créé en 1948 que certains employeurs doivent acquitter sur les salaires qu'ils distribuent. - applicable si: entreprise . taxe sur les salaires - formule: entreprise . taxe sur les salaires * 1 employé / entreprise . effectif - note: | - Nous supposons ici que tous les salariés de l'entreprise ont la même rémunération - Cette implémentation de la taxe sur les salaires est spécifique aux associations à but non lucratif, elle est donc largement simplifiée. Plein d'autres organisations sont concernées, en fonction de la TVA qu'elles paient. Les associations y sont assujetties automatiquement. - exemples: - - nom: non applicable par défaut - situation: - rémunération . brut de base: 2300 - valeur attendue: false - - nom: association non lucrative unipersonnelle - situation: - entreprise . association non lucrative: oui - rémunération . brut de base: 2300 - entreprise . effectif: 1 - valeur attendue: 0 - - nom: association non lucrative - situation: - entreprise . association non lucrative: oui - rémunération . brut de base: 2300 - entreprise . effectif: 10 - complémentaire santé . forfait: 0 - valeur attendue: 41.17 - - références: - fiche: https://www.service-public.fr/professionnels-entreprises/vosdroits/F22576 - -contrat salarié . versement transport: - description: Contribution sur les salaires destinée au financement des transports publics. - applicable si: entreprise . effectif > 10 - cotisation: - branche: transport - dû par: employeur - formule: - produit: - assiette: cotisations . assiette - taux: établissement . taux du versement transport - - références: - wikipedia: https://fr.wikipedia.org/wiki/Versement_transport - -contrat salarié . vieillesse: - cotisation: - branche: retraite - collecteur: Urssaf - destinataire: CNAV - # CTP: 100 - description: Cotisation au régime de retraite de base des salariés. - formule: - produit: - assiette: cotisations . assiette - composantes: - - attributs: - nom: salarié - assiette: cotisations . assiette . salariale - composantes: - - attributs: - nom: déplafonnée - taux [ref]: 0.4% - - attributs: - nom: plafonnée - taux [ref]: 6.90% - plafond: plafond sécurité sociale - - attributs: - nom: employeur - composantes: - - attributs: - nom: déplafonnée - taux [ref]: 1.9% - - attributs: - nom: plafonnée - taux [ref]: 8.55% - plafond: plafond sécurité sociale - - exemples: - - nom: SMIC - situation: - cotisations . assiette: 1500 - valeur attendue: 266.25 - - nom: Salaire élevé - situation: - cotisations . assiette: 8000 - valeur attendue: 713.63 - références: - Article L727-2 du Code de la sécurité sociale: https://www.legifrance.gouv.fr/affichCode.do;jsessionid=F5CFB7C90D1D1F529A2CDC9FFD20BD6E.tplgfr34s_3?idSectionTA=LEGISCTA000038510929&cidTexte=LEGITEXT000006073189&dateTexte=20190626 - -contrat salarié . forfait social: - titre: Forfait social - description: | - Le forfait social est une contribution versée par l'employeur. Elle est prélevée sur les rémunérations ou gains non soumis aux cotisations et contributions sociales, mais assujettis à la CSG. - applicable si: entreprise . effectif > 10 - cotisation: - branche: retraite - collecteur: Urssaf - destinataire: CNAV - dû par: employeur - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/le-forfait-social.html - Fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31532 - Code du travail - Article L137-15: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000019950196&cidTexte=LEGITEXT000006073189 - formule: - produit: - assiette: avantages sociaux - taux: 8% - # Les cotisations au taux de 20% ne sont pas encore dans le modèle (intéressement, plans d'épargne, indemnités de rupture conventionnelles...) - -contrat salarié . maladie . taux domiciliation fiscale étranger: - titre: taux salarié (domiciliation fiscale à l'étranger) - applicable si: situation personnelle . domiciliation fiscale à l'étranger - remplace: taux salarié - formule: 5.50% - -contrat salarié . lodeom: - valeur: oui - description: | - Un ensemble assez complexe de réductions de cotisation est disponible pour les salariés d'outre-mer. - Leur fonctionnement est similaire à celui de la réduction générale sur les bas salaires : pour un certain salaire donné, 100% de réduction. - Pour un autre salaire plus élevé, 0% de réduction. Entre les deux, on trace une ligne droite. - -contrat salarié . lodeom . zone un: - titre: Zone géographique 1 (Guadeloupe, Martinique, La Réunion, Guyane) - références: - fiche Urssaf: https://www.urssaf.fr/portail/home/outre-mer/employeur/exoneration-de-cotisations-di-1/employeurs-situes-en-guadeloupe.html - formule: - une de ces conditions: - - établissement . localisation . département = 'Guadeloupe' - - établissement . localisation . département = 'La Réunion' - - établissement . localisation . département = 'Martinique' - - établissement . localisation . département = 'Guyane' - -# TODO : reformuler comme pour la réduction générale -contrat salarié . lodeom . réduction outre-mer: - aide: - type: réduction de cotisations - thème: aide bas salaires - démarches: non - - applicable si: - toutes ces conditions: - - cotisations . assiette <= plafond de l'assiette - - zone un - - une de ces conditions: - - éligible barème compétitivité - - éligible barème compétitivité renforcée - - éligible barème innovation et croissance - - formule: - somme: - - allocations familiales - - FNAL - - maladie . employeur - - vieillesse . employeur - - produit: - assiette: cotisations . assiette - taux: ATMP . taux minimum - - retraite complémentaire . employeur - - contribution d'équilibre général . employeur - - chômage . employeur - plafond: - variations: - - si: - toutes ces conditions: - - éligible barème innovation et croissance - - cotisations . assiette > borne inférieure * SMIC - - cotisations . assiette < 2.5 * SMIC - alors: 1.7 * paramètre T * SMIC - - si: - toutes ces conditions: - - éligible barème innovation et croissance - - cotisations . assiette > 2.5 * SMIC - alors: ((borne inférieure * paramètre T) / (borne supérieure - 2.5)) * écart au plafond de l'assiette - - sinon: multiplicateur * écart au plafond de l'assiette - note: Nous utilisons la méthode de calcul officielle de la sécurité sociale. Il serait préférable ici de réduire directement les cotisations concernées, ce qui éviterait au calcul de reposer sur les paramètres `T` publiés chaque année (ils dépendent directement des cotisaitons réduites). - références: - Estimateur Urssaf: https://www.urssaf.fr/portail/home/utile-et-pratique/estimateur-exoneration-lodeom.html?ut= - -contrat salarié . lodeom . plafond de l'assiette: - formule: borne supérieure * SMIC - -contrat salarié . lodeom . écart au plafond de l'assiette: - formule: plafond de l'assiette - cotisations . assiette - -contrat salarié . lodeom . éligible barème compétitivité: - titre: Eligibilité au barème de compétitivité - applicable si: - toutes ces conditions: - - zone un - - une de ces conditions: - - entreprise . effectif < 11 - - secteurs d'activité - rend non applicable: - - réduction générale - formule: oui - - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/outre-mer/employeur/exoneration-de-cotisations-di-1/employeurs-situes-en-guadeloupe/bareme-dit-de-competitivite.html - -contrat salarié . lodeom . secteurs d'activité: - applicable si: zone un - question: Votre entreprise appartient-elle à l'un des secteurs éligible LODEOM ? - description: | - Pour être éligible au 1er barème de l'exonération LODEOM, dit barème de compétitivité, votre entreprise doit appartenir à l'un des secteurs suivants : - - - ✈ transport aérien assurant les liaisons entre les départements et régions d’Outre-mer et entre la métropole et ces territoires, ainsi que les dessertes intérieures - - ⛵ dessertes maritimes, fluviales ou les liaisons entre départements et régions d’Outre-mer - - 🏗 bâtiment et travaux publics - - 📰 la presse - - 🎥 la production audiovisuelle - - les secteurs éligibles aux régimes de compétitivité renforcée (barème 2) ou d’innovation et de croissance (barème 3), qui ne respectent pas les conditions d’effectifs (moins de 250 salariés) ou de chiffres d’affaires annuel (moins de 50 millions d’euros). - par défaut: non - -contrat salarié . lodeom . éligible barème compétitivité renforcée: - applicable si: zone un - rend non applicable: - - réduction générale - - éligible barème compétitivité - question: Êtes-vous éligibles au barème compétitivité renforcée ? - description: | - - Chiffre d'affaires de moins de 50 millions d'euros - - Les employeurs relevant des secteurs de l’industrie, de la restauration, de l’environnement, de l’agro nutrition, des énergies renouvelables, des nouvelles technologies de l’information et de la communication et des centres d’appel, de la pêche, des cultures marines, de l’aquaculture, de l’agriculture, du tourisme y compris les activités de loisirs s’y rapportant, du nautisme, de l’hôtellerie, de la recherche et du développement ; - - Les entreprises bénéficiaires du régime de perfectionnement actif défini à l’article 256 du règlement (UE) n° 952/2013 du parlement européen et du conseil du 9 octobre 2013 établissant le code des douanes de l’Union - - En Guyane, les employeurs ayant une activité principale relevant de l’un des secteurs d’activité éligibles à la réduction d’impôt prévue à l’article 199 undecies B du code général des impôts, ou correspondant à l’une des activités suivantes : comptabilité, conseil aux entreprises, ingénierie ou études techniques. - par défaut: non - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/outre-mer/employeur/exoneration-de-cotisations-di-1/employeurs-situes-en-guadeloupe/bareme-dit-de-competitivite-renf.html - -contrat salarié . lodeom . éligible barème innovation et croissance: - applicable si: zone un - rend non applicable: - - réduction générale - - éligible barème compétitivité - - éligible barème compétitivité renforcée - question: Êtes-vous éligibles au barème innovation et croissance ? - description: | - - Sont éligibles à ce barème les employeurs occupant moins de 250 salariés et ayant réalisé un chiffre d’affaires annuel inférieur à 50 millions d’euros, au titre de la rémunération des salariés concourant essentiellement à la réalisation de projets innovants dans le domaine des technologies de l’information et de la communication. - - Les projets innovants se définissent comme des projets ayant pour but l’introduction d’un bien, d’un service, d’une méthode de production ou de distribution nouveau ou sensiblement amélioré sur le plan des caractéristiques et de l’usage auquel il est destiné. Ces projets doivent être réalisés dans les domaines suivants : - - 📱 télécommunication ; - - informatique, dont notamment programmation, conseil en systèmes et logiciels, tierce maintenance de systèmes et d’applications, gestion d‘installations, traitement des données, hébergement et activités connexes ; - - édition de portails internet et de logiciels; - - infographie, notamment conception de contenus visuels et numériques ; - - conception d’objets connectés. - - Si ces conditions sont réunies, l’exonération s’applique aux rémunérations versées aux salariés occupés principalement à la réalisation de projets innovants. - - Sont donc exclues les fonctions supports : tâches administratives financières, logistiques et de ressources humaines. - par défaut: non - références: - Fiche Urssaf: https://www.urssaf.fr/portail/home/outre-mer/employeur/exoneration-de-cotisations-di-1/employeurs-situes-en-guadeloupe/bareme-dit-innovation-et-croissa.html - -contrat salarié . lodeom . borne inférieure: - formule: - variations: - - si: éligible barème compétitivité - alors: 130% - - sinon: 170% - -contrat salarié . lodeom . borne supérieure: - unité: '' - formule: - variations: - - si: éligible barème compétitivité - alors: 220% - - si: éligible barème compétitivité renforcée - alors: 270% - - si: éligible barème innovation et croissance - alors: 350% - -contrat salarié . lodeom . multiplicateur: - note: pour le barème 1 le dénominateur vaut 0,9 - formule: (borne inférieure * paramètre T) / (borne supérieure - borne inférieure) - -contrat salarié . lodeom . paramètre T: - unité: '' - formule: - variations: - - si: - toutes ces conditions: - - zone un - - entreprise . effectif < 20 - alors: 0.3214 - - si: - toutes ces conditions: - - zone un - - entreprise . effectif >= 20 - alors: 0.3254 - note: La valeur du paramètre `T` dépend du taux FNAL. Une meilleur implémentation consiste à calculer ce paramètre comme une somme de taux. - -contrat salarié . cotisations . assiette forfaitaire: - formule: non - -contrat salarié . cotisations . assiette forfaitaire . montant: - titre: assiette forfaitaire de cotisations - non applicable si: rémunération réelle - remplace: - - règle: cotisations . assiette - sauf dans: - - chômage - - retraite complémentaire - - contribution d'équilibre général - - contribution d'équilibre technique - - convention collective - # Todo : Ce hack est dû à la façon dont est implémenté l'exonération salariale pour les apprentis - - assiette . salariale - - règle: assiette . salariale - dans: vieillesse - - règle: CSG et CRDS . assiette abattue totale - - règle: plafond sécurité sociale - par: plafond sécurité sociale temps plein - sauf dans: - - chômage - - retraite complémentaire - - contribution d'équilibre général - - contribution d'équilibre technique - - convention collective - # Todo : Ce hack est dû à la façon dont est implémenté l'exonération salariale pour les apprentis - - assiette . salariale - formule: - valeur: assiette forfaitaire - plancher: minimum - références: - exception agirc-arco (fiche 3): https://www.agirc-arrco.fr/fileadmin/agircarrco/documents/circulaires/agirc_arrco/2019/2019-1-DRJ_Reglementation__applicable_aux_entreprises.pdf - CSG et CRDS: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/la-base-de-calcul/assiette-csg-crds.html - plafond de sécurité sociale (urssaf.fr): https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/la-base-de-calcul/lassiette-maximale/salarie-a-temps-partiel.html#FilAriane - -contrat salarié . cotisations . assiette forfaitaire . minimum: - description: > - Il existe une règle générale d'encadrement des assiettes forfaitaires. Lorsque la rémunération est supérieure ou égale à - 1,5 fois le plafond de la sécurité sociale, l'assiette forfaitaire retenue ne peut être inférieure à 70% de la rémunération - applicable si: rémunération . brut >= 1.5 * plafond sécurité sociale temps plein - formule: 70% * rémunération . brut - -contrat salarié . cotisations . assiette forfaitaire . rémunération réelle: - question: Voulez-vous calculer les cotisations sur la rémunération réelle (au lieu de la base forfaitaire) ? - par défaut: non - -contrat salarié . convention collective: - par défaut: "'droit commun'" - question: "Quelle convention collective est applicable à l'entreprise ?" - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - droit commun - - HCR - - BTP - - sport - - SVP - - compta - - optique - -contrat salarié . convention collective . contrôle décharge: - type: notification - sévérité: avertissement - formule: convention collective != 'droit commun' - description: >- - Attention : l'implémentation des conventions collective est encore partielle - et non vérifiée. Néanmoins, cela permet d'obtenir une première estimation, - plus précise que le régime général. - -contrat salarié . convention collective . droit commun: - formule: convention collective = 'droit commun' diff --git a/modele-social/règles/situation-personnelle.yaml b/modele-social/règles/situation-personnelle.yaml deleted file mode 100644 index 06d9c4335..000000000 --- a/modele-social/règles/situation-personnelle.yaml +++ /dev/null @@ -1,18 +0,0 @@ -situation personnelle: oui - -situation personnelle . RSA: - titre: bénéficiaire RSA ou prime d'activité - question: >- - Etes-vous bénéficiaire du RSA ou de la prime d’activité ? - par défaut: non - -situation personnelle . domiciliation fiscale à l'étranger: - description: >- - Ces assurés ne sont pas redevables de la CSG/CRDS ni de l'impôt sur le revenu. - question: La résidence fiscale de la personne est-elle située à l'étranger ? - rend non applicable: - - dirigeant . indépendant . cotisations et contributions . CSG et CRDS - - contrat salarié . CSG et CRDS - par défaut: non - références: - urssaf.fr: https://www.urssaf.fr/portail/home/employeur/calculer-les-cotisations/les-taux-de-cotisations/la-csg-crds/qui-en-est-redevable.html diff --git a/modele-social/tsconfig.json b/modele-social/tsconfig.json deleted file mode 100644 index 49fc71a60..000000000 --- a/modele-social/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "noEmit": true, - "strict": true - }, - "include": ["build.js"] -} diff --git a/mon-entreprise/.babelrc.json b/mon-entreprise/.babelrc.json deleted file mode 100644 index b040ceca7..000000000 --- a/mon-entreprise/.babelrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plugins": [ - "react-hot-loader/babel", - ["webpack-alias", { "config": "./webpack.dev.js" }], - ["ramda", { "useES": true }] - ] -} diff --git a/mon-entreprise/.eslintrc.yaml b/mon-entreprise/.eslintrc.yaml deleted file mode 100644 index f0436fe75..000000000 --- a/mon-entreprise/.eslintrc.yaml +++ /dev/null @@ -1,5 +0,0 @@ -extends: '../.eslintrc' -overrides: - - files: ['*.test.js', 'cypress/integration/**/*.js'] - env: - mocha: true diff --git a/mon-entreprise/.gitignore b/mon-entreprise/.gitignore deleted file mode 100644 index d0deaa9b6..000000000 --- a/mon-entreprise/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.env -source/data/* -!source/data/versement-transport.json -cypress/videos -cypress/screenshots diff --git a/mon-entreprise/bundlesize.config.json b/mon-entreprise/bundlesize.config.json deleted file mode 100644 index 23d7de240..000000000 --- a/mon-entreprise/bundlesize.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files": [ - { - "path": "./dist/*.bundle.js", - "maxSize": "650 kB" - } - ] -} diff --git a/mon-entreprise/cypress.json b/mon-entreprise/cypress.json deleted file mode 100644 index e0ab57db9..000000000 --- a/mon-entreprise/cypress.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "projectId": "jxcngh", - "baseUrl": "http://localhost:8080/mon-entreprise", - "env": { - "language": "fr" - }, - "integrationFolder": "cypress/integration/mon-entreprise", - "chromeWebSecurity": false, - "retries": 1 -} diff --git a/mon-entreprise/cypress/.babelrc b/mon-entreprise/cypress/.babelrc deleted file mode 100644 index 0967ef424..000000000 --- a/mon-entreprise/cypress/.babelrc +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/mon-entreprise/cypress/.eslintrc.yaml b/mon-entreprise/cypress/.eslintrc.yaml deleted file mode 100644 index 538969434..000000000 --- a/mon-entreprise/cypress/.eslintrc.yaml +++ /dev/null @@ -1,6 +0,0 @@ -globals: - cy: false - Cypress: false - expect: false -env: - mocha: true diff --git a/mon-entreprise/cypress/downloads/contrat-salarié.html b/mon-entreprise/cypress/downloads/contrat-salarié.html deleted file mode 100644 index e53496707..000000000 --- a/mon-entreprise/cypress/downloads/contrat-salarié.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mon-entreprise.fr : L'assistant officiel du créateur d'entreprise - - - - - - - - - - -
- Un service de l'État français -
-
-
-
-
-
-
- - - - - -
- - - - - - - - - - - - diff --git a/mon-entreprise/cypress/downloads/demande-mobilité-internationale.pdf b/mon-entreprise/cypress/downloads/demande-mobilité-internationale.pdf deleted file mode 100644 index fa5343e67..000000000 Binary files a/mon-entreprise/cypress/downloads/demande-mobilité-internationale.pdf and /dev/null differ diff --git a/mon-entreprise/cypress/fixtures/example.json b/mon-entreprise/cypress/fixtures/example.json deleted file mode 100644 index da18d9352..000000000 --- a/mon-entreprise/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/mon-entreprise/cypress/integration/external/external-integrations.js b/mon-entreprise/cypress/integration/external/external-integrations.js deleted file mode 100644 index b5613e51e..000000000 --- a/mon-entreprise/cypress/integration/external/external-integrations.js +++ /dev/null @@ -1,18 +0,0 @@ -describe('Pole emploi', function () { - it('should display an iframe of the simulateur', function () { - cy.on('uncaught:exception', (err) => { - return !err.message.contains('Unexpected token <') - }) - cy.visit('https://entreprise.pole-emploi.fr/cout-salarie/') - cy.get('#simulateurEmbauche').iframe().contains('Salaire net') - }) -}) - -describe('Urssaf', function () { - it('should display an iframe of the simulateur', function () { - cy.visit( - 'https://www.urssaf.fr/portail/home/utile-et-pratique/estimateur-de-cotisations-2019.html' - ) - cy.get('#simulateurEmbauche').iframe().contains('Salaire net') - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/covid19.js b/mon-entreprise/cypress/integration/mon-entreprise/covid19.js deleted file mode 100644 index 2732c9d6d..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/covid19.js +++ /dev/null @@ -1,28 +0,0 @@ -const fr = Cypress.env('language') === 'fr' -const testText = (selector, text) => - cy.get(`[data-test-id=${selector}]`).should(($span) => { - const displayedText = $span.text().trim().replace(/[\s]/g, ' ') - console.log(displayedText, text) - expect(displayedText).to.eq(text) - }) - -describe('Page covid-19', function () { - if (!fr) { - return - } - before(() => cy.visit(encodeURI('/simulateurs/chômage-partiel'))) - it('should not crash', () => { - cy.contains('Salaire brut mensuel') - }) - it('should display 100% de prise en charge pour un SMIC', () => { - cy.get('input.currencyInput__input').click() - cy.contains('SMIC').click() - testText('comparaison-net', 'Soit 100 % du revenu net') - testText('comparaison-total', 'Soit 0 % du coût habituel') - }) - it('should display 85 % de prise en charge pour un salaire médian', () => { - cy.contains('salaire médian').click() - testText('comparaison-net', 'Soit 85 % du revenu net') - testText('comparaison-total', 'Soit 8 % du coût habituel') - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/demande-mobilité.js b/mon-entreprise/cypress/integration/mon-entreprise/demande-mobilité.js deleted file mode 100644 index f8ef40e8b..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/demande-mobilité.js +++ /dev/null @@ -1,112 +0,0 @@ -const fr = Cypress.env('language') === 'fr' - -describe( - 'Formulaire demande mobilité', - { - retries: { - runMode: 3, - openMode: 0, - }, - }, - function () { - if (!fr) { - return - } - before(() => cy.visit(encodeURI('/gérer/demande-mobilité'))) - it('should not crash', () => { - cy.contains('Demande de mobilité internationale') - }) - it('should allow to complete "coordonnées" section', () => { - cy.contains('SIRET').click() - cy.focused() - .type('684 064 0011') - .tab() - .type('Deaux') - .tab() - .type('Jean') - .tab() - .type('Française') - cy.contains('sécurité sociale').click() - cy.focused().type('1 91 07 468 054 75').tab().type('1991-07-25') - - cy.get( - "input[name='coordonnées assuré . commune de naissance . étranger'][value='non']" - ) - .next() - .click() - .wait(500) - - cy.focused().tab().type('Pouts').wait(1500).type('{enter}') - - cy.get( - "input[name='coordonnées assuré . domicile personnel . commune . étranger'][value='non']" - ) - .next() - .click() - - cy.focused() - .tab() - .type('3 rue de la Rhumerie') - .tab() - .type('Brest') - .wait(1500) - .type('{enter}') - .tab() - .type('jean.deaux@gmail.com') - .tab() - .type('06 85 69 78 54') - .tab() - }) - it('should allow to complete "activité en France" section', () => { - cy.focused() - .type('Deaux & Fils') - .tab() - .type('14 chemin des Docks') - .tab() - .type('Bre') - .wait(1500) - cy.contains('29240').click() - cy.contains('Organisme Urssaf').click() - cy.focused().type('Bretagne').tab().tab().type('Boulangerie') - }) - it('should allow to complete "votre demande" section', () => { - cy.get("input[name='demande . pays unique'][value='oui']").next().click() - cy.get("input[name='demande . infrastructure sauvegardée'][value='oui']") - .next() - .click() - cy.get("input[name='demande . activité semblable'][value='oui']") - .next() - .click() - cy.get("input[name='demande . date de fin connue'][value='oui']") - .next() - .click() - cy.get('label[for="détachement . pays"]').wait(1500).click() - cy.focused() - .select('Irlande') - .tab() - .type('2020-11-06') - .tab() - .type('2021-04-09') - .tab() - .tab() - .type('Fabrications de gateaux bretons') - cy.get("input[name='détachement . base fixe'][value='non']") - .next() - .click() - cy.get( - "input[name='commentaires additionnels . commentaires'][value='non']" - ) - .next() - .click() - }) - it.skip('should allow to download PDF', () => { - cy.contains( - 'Je certifie l’exactitude des informations communiquées ci-dessus' - ).click() - cy.contains('Fait à').click() - cy.focused().type('Plougastel') - cy.contains('Générer la demande').click() - cy.contains('Télécharger le fichier').click() - }) - } -) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/gérer.js b/mon-entreprise/cypress/integration/mon-entreprise/gérer.js deleted file mode 100644 index e1ec6f40d..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/gérer.js +++ /dev/null @@ -1,34 +0,0 @@ -describe('Manage page test', function () { - const fr = Cypress.env('language') === 'fr' - beforeEach(() => { - cy.visit(fr ? encodeURI('/gérer') : '/manage') - }) - it('should not crash', function () { - cy.contains(fr ? 'Gérer mon activité' : 'Manage my business') - }) - it.skip('should allow to retrieve company and show link corresponding to the legal status', function () { - cy.get('button.cta').click() - cy.get('input').first().type('menoz') - cy.contains('834364291').click() - cy.contains(fr ? 'simulateur SASU' : 'simulator for SASU').click() - cy.location().should((loc) => { - expect(loc.pathname).to.match(fr ? /dirigeant-sasu$/ : /sasu-chairman$/) - }) - }) - it.skip('should allow auto entrepreneur to access the corresponding income simulator', function () { - cy.get('button.cta').click() - cy.get('input').first().type('johan girod') - cy.contains('MONSIEUR').click() - // ask if auto-entrepreneur - cy.contains( - fr ? 'Êtes-vous auto-entrepreneur ?' : 'Are you auto-entrepreneur?' - ) - cy.contains(fr ? 'Oui' : 'Yes').click() - cy.contains( - fr ? 'simulateur auto-entrepreneur' : 'simulator for auto-entrepreneur' - ).click() - cy.location().should((loc) => { - expect(loc.pathname).to.match(/auto-entrepreneur$/) - }) - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/iframe.js b/mon-entreprise/cypress/integration/mon-entreprise/iframe.js deleted file mode 100644 index 86b694f5e..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/iframe.js +++ /dev/null @@ -1,11 +0,0 @@ -describe('Iframe integration test', function () { - if (Cypress.env('language') !== 'fr') { - return - } - it('should display an iframe of the simulateur', function () { - cy.visit('/dev/integration-test') - cy.contains('Visualiser').click() - cy.wait(1000) - cy.get('#simulateurEmbauche').iframe().contains('Salaire net') - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/landing.js b/mon-entreprise/cypress/integration/mon-entreprise/landing.js deleted file mode 100644 index aeec373db..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/landing.js +++ /dev/null @@ -1,17 +0,0 @@ -describe('Landing test', function () { - const fr = Cypress.env('language') === 'fr' - it('should not crash', function () { - cy.visit('/') - cy.get('img[alt="logo mon-entreprise.fr"]').should('be.visible') - }) - it('should display urssaf and marianne logo', function () { - cy.visit('/') - cy.get('img[alt="logo urssaf"]').should('be.visible') - cy.get('img[alt="logo marianne"]').should('be.visible') - }) - it('should display actionnable items', function () { - cy.visit('/') - cy.contains(fr ? 'Créer une entreprise' : 'Create a company') - cy.contains(fr ? 'Gérer mon activité' : 'Manage my business') - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/navigation.js b/mon-entreprise/cypress/integration/mon-entreprise/navigation.js deleted file mode 100644 index c98e1f2b2..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/navigation.js +++ /dev/null @@ -1,19 +0,0 @@ -describe('Navigation', function () { - const fr = Cypress.env('language') === 'fr' - it('should enable switching site language', () => { - cy.visit( - fr ? encodeURI('/créer/auto-entrepreneur') : '/create/auto-entrepreneur' - ) - cy.contains(fr ? 'Switch to English' : 'Passer en français').click() - cy.url().should( - 'include', - fr ? '/create/auto-entrepreneur' : encodeURI('/créer/auto-entrepreneur') - ) - }) - - it('should go back to home when clicking on logo', () => { - cy.visit(encodeURI('/documentation/contrat-salarié')) - cy.get('img[alt^="logo mon-entreprise"]').click() - cy.url().should('match', new RegExp(`${Cypress.config().baseUrl}/?`)) - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/simulateurs.js b/mon-entreprise/cypress/integration/mon-entreprise/simulateurs.js deleted file mode 100644 index 30fd7b186..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/simulateurs.js +++ /dev/null @@ -1,203 +0,0 @@ -const fr = Cypress.env('language') === 'fr' -const inputSelector = 'input.currencyInput__input:not([name$="charges"])' -const chargeInputSelector = 'input.currencyInput__input[name$="charges"]' -describe('Simulateurs', function () { - if (!fr) { - return - } - ;[ - 'indépendant', - 'dirigeant-sasu', - 'auto-entrepreneur', - 'salarié', - 'profession-liberale', - 'profession-liberale/medecin', - 'profession-liberale/sage-femme', - 'profession-liberale/auxiliaire-medical', - 'profession-liberale/chirurgien-dentiste', - ].forEach((simulateur) => - describe(simulateur, () => { - before(() => cy.visit(encodeURI(`/simulateurs/${simulateur}`))) - it('should not crash', function () { - cy.get(inputSelector) - }) - - it('should display a result when entering a value in any of the currency input', () => { - cy.contains('€/an').click() - if (['indépendant', 'profession-liberale'].includes(simulateur)) { - cy.get(chargeInputSelector).type(1000) - } - cy.get(inputSelector).each((testedInput, i) => { - cy.wrap(testedInput).type('{selectall}60111') - cy.wait(1500) - cy.contains('Cotisations') - cy.get(inputSelector).each(($input, j) => { - const val = $input.val().replace(/[\s,.]/g, '') - if (i != j) { - expect(val).not.to.be.eq('60111') - } - expect(val).to.match(/[1-9][\d]{3,6}$/) - }) - }) - }) - - it('should allow to change period', function () { - cy.contains('€/an').click() - cy.wait(200) - cy.get(inputSelector).first().type('{selectall}12000') - if (['indépendant', 'profession-liberale'].includes(simulateur)) { - cy.get(chargeInputSelector).type('{selectall}6000') - } - cy.wait(800) - cy.contains('€/mois').click() - cy.get(inputSelector) - .first() - .invoke('val') - .should('match', /1[\s]000/) - if (['indépendant', 'profession-liberale'].includes(simulateur)) { - cy.get(chargeInputSelector).first().invoke('val').should('eq', '500') - } - cy.contains('€/an').click() - }) - - it('should allow to navigate to a documentation page', function () { - cy.get(inputSelector).first().type('{selectall}2000') - cy.wait(700) - cy.contains('Cotisations').click() - cy.location().should((loc) => { - expect(loc.pathname).to.match(/\/documentation\/.*\/cotisations/) - }) - }) - - it('should allow to go back to the simulation', function () { - cy.contains('← ').click() - cy.get(inputSelector).first().invoke('val').should('eq', '2 000') - }) - }) - ) -}) -describe('Simulateur auto-entrepreneur', () => { - if (!fr) { - return - } - before(() => cy.visit('/simulateurs/auto-entrepreneur')) - - it('should allow to enter the date of creation', () => { - cy.get(inputSelector).first().type('{selectall}50000') - cy.contains('Passer').click() - cy.contains('Passer').click() - cy.contains('Début 2021').click() - cy.contains('ACRE') - }) - it('should not have negative value', () => { - cy.contains('€/mois').click() - cy.wait(100) - cy.get(inputSelector).first().type('{selectall}5000') - cy.wait(800) - cy.get(inputSelector).each(($input) => { - const val = +$input.val().replace(/[\s,.]/g, '') - expect(val).not.to.be.below(4000) - }) - }) -}) - -describe('Simulateur salarié mode partagé', () => { - const brutInputSelector = - 'input.currencyInput__input[name="contrat salarié . rémunération . brut de base"]' - const simulatorUrl = '/simulateurs/salaire-brut-net' - const searchParams = new URLSearchParams({ - 'contrat salarié': "'CDD'", - 'salaire-brut': '1539€/mois', - }) - searchParams.set('utm_source', 'sharing') - - const urlWithState = `${simulatorUrl}?${searchParams.toString()}` - if (!fr) { - return - } - it('should set input value from URL', function () { - cy.visit(urlWithState) - cy.wait(800) - cy.get(brutInputSelector).first().invoke('val').should('eq', '1 539') - - cy.contains('Voir mes paramètres').click() - cy.get('span.answerContent').first().contains('CDD') - }) - it('should set URL from input value', function () { - cy.visit(simulatorUrl) - cy.get(brutInputSelector).first().type('{selectall}1539') - cy.wait(1000) - cy.get('.step').find('input[value="\'CDD\'"]').click({ force: true }) - cy.wait(1000) - cy.contains('Générer un lien').click() - cy.get('.shareableLink') - .invoke('val') - .should('eq', Cypress.config().baseUrl + urlWithState) - }) -}) - -describe('Simulateur salarié', () => { - if (!fr) { - return - } - before(() => cy.visit(encodeURI('/simulateurs/salarié'))) - - it('should persist the current simulation (persistSimulation)', function () { - cy.get(inputSelector).first().type('{selectall}42') - cy.contains('Passer').click() - cy.contains('Passer').click() - cy.contains('Passer').click() - cy.wait(1600) - cy.visit('/simulateurs/auto-entrepreneur') - cy.get(inputSelector).first().type('{selectall}007') - cy.contains('Passer').click() - cy.contains('Passer').click() - cy.contains('Passer').click() - cy.wait(1600) - cy.visit(encodeURI('/simulateurs/salarié')) - cy.contains('Retrouver ma simulation').click() - cy.get(inputSelector).first().invoke('val').should('match', /42/) - }) - - it('should not crash when selecting localisation', function () { - cy.contains('Commune').click() - cy.get('fieldset input[type="search"]').type('Steenvoorde') - cy.contains('Steenvoorde (59114)').click() - cy.contains('Suivant').click() - cy.contains('Voir mes paramètres').click() - cy.contains('Steenvoorde') - }) - - describe('part time contract', () => { - before(() => { - cy.visit(encodeURI('/simulateurs/salarié')) - cy.get('input[name$="brut de base"]').click() - cy.get('button').contains('SMIC').click() - cy.contains('Voir mes paramètres').click() - cy.contains('Temps partiel').click() - cy.get('input[value="oui"]').parent().click() - cy.wait(100) - }) - - it('should permit selecting the smic before part-time contrat', () => { - cy.get('input[name$="brut de base"]').should(($input) => { - expect(+$input.val().replace(/[\s,.]/g, '')) - .to.be.above(1300) - .and.to.be.below(1500) - }) - }) - - it('should permit customizing the number of worked hours and clear the input value', () => { - cy.contains('Suivant').click() - cy.get('fieldset input[type="text"]').type(25) - cy.get('input[name$="net après impôt"]').should(($input) => { - expect(+$input.val().replace(/[\s,.]/g, '')).to.be.below(1000) - }) - - cy.get('fieldset input[type="text"]').clear() - cy.get('input[name$="net après impôt"]').should(($input) => { - expect(+$input.val().replace(/[\s,.]/g, '')).to.be.above(1000) - }) - }) - }) -}) diff --git a/mon-entreprise/cypress/integration/mon-entreprise/status.js b/mon-entreprise/cypress/integration/mon-entreprise/status.js deleted file mode 100644 index 9c5b0095c..000000000 --- a/mon-entreprise/cypress/integration/mon-entreprise/status.js +++ /dev/null @@ -1,28 +0,0 @@ -describe('Status guide', function () { - const fr = Cypress.env('language') === 'fr' - beforeEach(() => { - cy.visit(fr ? encodeURI('/créer') : '/create') - cy.get('.cta').click() - }) - - it('should allow to go back clicking on the previous answers', function () { - cy.get('.ui__.answer-group') - .contains(fr ? 'Seul' : 'Alone') - .click() - cy.contains(fr ? 'Un seul associé' : 'Only one partner').click() - cy.contains(fr ? 'Seul ou à plusieurs' : 'Number of partners') - }) - it('should guide thought the SASU status', function () { - cy.get('.ui__.answer-group') - .contains(fr ? 'Seul' : 'Alone') - .click() - cy.get('.ui__.answer-group') - .contains(fr ? 'Société' : 'Limited liability company') - .click() - // The click fails randomly and unexplicablly on CI - cy.wait(200) - cy.get('.answer-group button').contains('Assimilé').click() - cy.contains(fr ? 'Créer une SASU' : 'Create a SASU').click() - cy.url().should('match', /\/SASU$/) - }) -}) diff --git a/mon-entreprise/cypress/integration/publi.codes/index.js b/mon-entreprise/cypress/integration/publi.codes/index.js deleted file mode 100644 index ba756f0c4..000000000 --- a/mon-entreprise/cypress/integration/publi.codes/index.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('Navigation', function () { - it('landing should not crash', function () { - cy.visit('/') - }) - it('liste des mécanismes should not crash', function () { - cy.contains('Documentation').click() - cy.contains('Liste des mécanismes').click() - cy.contains('barème') - }) - it('bac à sable should work', function () { - cy.contains('Bac à sable').click() - cy.wait(5000) - cy.contains('Dépenses primeur') - cy.contains('11,50') - }) -}) diff --git a/mon-entreprise/cypress/jsconfig.json b/mon-entreprise/cypress/jsconfig.json deleted file mode 100644 index 3ce942d63..000000000 --- a/mon-entreprise/cypress/jsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -// Support IntelliSense for Cypress -{ - "include": ["../../node_modules/cypress", "./integration/**/*.js"] -} diff --git a/mon-entreprise/cypress/plugins/index.js b/mon-entreprise/cypress/plugins/index.js deleted file mode 100644 index f5a0a0d2e..000000000 --- a/mon-entreprise/cypress/plugins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -module.exports = (/* on, config */) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} diff --git a/mon-entreprise/cypress/support/commands.js b/mon-entreprise/cypress/support/commands.js deleted file mode 100644 index e25527a4f..000000000 --- a/mon-entreprise/cypress/support/commands.js +++ /dev/null @@ -1,30 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) -Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe) => { - return new Cypress.Promise((resolve) => { - setTimeout(() => resolve($iframe.contents().find('body')), 6000) - }) -}) diff --git a/mon-entreprise/cypress/support/index.js b/mon-entreprise/cypress/support/index.js deleted file mode 100644 index 46a58b9e4..000000000 --- a/mon-entreprise/cypress/support/index.js +++ /dev/null @@ -1,21 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import 'cypress-plugin-tab' -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/mon-entreprise/dev-server.js b/mon-entreprise/dev-server.js deleted file mode 100644 index 7ccacbfc5..000000000 --- a/mon-entreprise/dev-server.js +++ /dev/null @@ -1,45 +0,0 @@ -const express = require('express') -const webpack = require('webpack') -const webpackDevMiddleware = require('webpack-dev-middleware') - -const app = express() -const config = require('./webpack.dev.js') -const compiler = webpack(config) -const history = require('connect-history-api-fallback') - -const { watchDottedNames } = require('../modele-social/build') -watchDottedNames() - -const rewrite = (basename) => ({ - from: new RegExp(`^/${basename}/(.*)$|^/${basename}$`), - to: `/${basename}.html`, -}) - -app.get('/', function (req, res) { - res.send(``) -}) - -app.use( - history({ - rewrites: ['infrance', 'mon-entreprise', 'publicodes'].map(rewrite), - }) -) - -// Tell express to use the webpack-dev-middleware and use the webpack.config.js -// configuration file as a base. -app.use( - webpackDevMiddleware(compiler, { - publicPath: config.output.publicPath, - hot: true, - }) -) - -app.use(require('webpack-hot-middleware')(compiler)) - -app.listen(8080, function () { - // eslint-disable-next-line no-console - console.log('Mon-entreprise listening on port 8080!\n') -}) diff --git a/mon-entreprise/index.html b/mon-entreprise/index.html deleted file mode 100644 index 4606eccde..000000000 --- a/mon-entreprise/index.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <%= htmlWebpackPlugin.options.title %> - - - <% for (var css in htmlWebpackPlugin.files.css) { %> - - <% } %> - - - - - -
- Un service de l'État français -
-
-
-
-
-
-
- - - - - -
- - - - - - <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> - - <% } %> - - <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> - - <% } %> - - diff --git a/mon-entreprise/package.json b/mon-entreprise/package.json deleted file mode 100644 index bd0af5bad..000000000 --- a/mon-entreprise/package.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "name": "mon-entreprise", - "license": "MIT", - "version": "1.2.8", - "description": "Library to compute the french social security contributions. Also a website that explains the calculations, and a generic engine to build cool forms that asks the question needed to compute an objective.", - "repository": { - "type": "git", - "url": "https://github.com/betagouv/mon-entreprise.git", - "directory": "mon-entreprise" - }, - "private": true, - "engines": { - "node": ">=12.16.1" - }, - "browserslist": [ - "> 1% in FR", - "not ie < 11" - ], - "devDependencies": { - "@babel/core": "^7.9.0", - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.9.5", - "@babel/plugin-proposal-optional-chaining": "^7.9.0", - "@babel/preset-env": "^7.9.5", - "@babel/preset-react": "^7.9.4", - "@babel/preset-typescript": "^7.9.0", - "@types/cheerio": "^0.22.18", - "@types/js-yaml": "^3.12.2", - "@types/react": "^17.0.0", - "@types/react-color": "^3.0.1", - "@types/react-dom": "^17.0.0", - "@types/react-helmet": "^6.1.0", - "@types/react-redux": "^7.1.11", - "@types/react-router": "^5.1.2", - "@types/recharts": "^1.8.16", - "@types/redux-sentry-middleware": "^0.1.2", - "@types/styled-components": "^5.1.9", - "@types/webpack": "^4.41.10", - "@typescript-eslint/eslint-plugin": "^4.0.1", - "@typescript-eslint/parser": "^4.0.1", - "@wojtekmaj/enzyme-adapter-react-17": "^0.3.1", - "autoprefixer": "^9.7.6", - "babel-plugin-styled-components": "^1.10.7", - "copy-webpack-plugin": "^4.5.2", - "cypress-plugin-tab": "^1.0.5", - "eslint-plugin-react": "^7.12.4", - "html-webpack-plugin": "^3.2.0", - "i18next-parser": "^3.3.0", - "intl-locales-supported": "^1.0.0", - "mock-local-storage": "^1.0.5", - "monaco-editor-webpack-plugin": "^1.9.0", - "serve": "^11.1.0", - "terser-webpack-plugin": "^3.0.2", - "webpack": "^4.42.0", - "webpack-bundle-analyzer": "^3.7.0", - "webpack-cli": "^3.1.2", - "workbox-webpack-plugin": "^6.0.2", - "worker-loader": "^2.0.0" - }, - "dependencies": { - "@babel/runtime": "^7.3.4", - "@react-pdf/renderer": "^1.6.10", - "@rehooks/local-storage": "^2.1.1", - "@sentry/react": "^6.3.5", - "@sentry/tracing": "^6.3.5", - "classnames": "^2.2.5", - "color-convert": "^1.9.2", - "core-js": "^3.2.1", - "focus-trap-react": "^3.1.2", - "fuse.js": "5.2.1", - "iframe-resizer": "^4.1.1", - "modele-social": "^0.2.0", - "publicodes": "^1.0.0-beta.15", - "publicodes-react": "^1.0.0-beta.15", - "ramda": "^0.27.0", - "react": "^17.0.0", - "react-color": "^2.14.0", - "react-dom": "npm:@hot-loader/react-dom", - "react-easy-emoji": "^1.2.0", - "react-helmet": "^6.1.0", - "react-i18next": "^11.0.0", - "react-markdown": "^4.1.0", - "react-monaco-editor": "^0.40.0", - "react-number-format": "^4.3.1", - "react-redux": "^7.0.3", - "react-router-dom": "^5.1.1", - "react-router-hash-link": "^1.2.2", - "react-signature-pad-wrapper": "^1.2.11", - "react-spring": "=8.0.27", - "react-syntax-highlighter": "^10.1.1", - "react-transition-group": "^2.2.1", - "react-useportal": "^1.0.13", - "recharts": "^1.8.5", - "reduce-reducers": "^1.0.4", - "redux": "^4.0.4", - "regenerator-runtime": "^0.13.3", - "reselect": "^4.0.0", - "styled-components": "^5.1.0", - "swr": "^0.1.16", - "whatwg-fetch": "^3.0.0", - "yaml": "^1.9.2" - }, - "scripts": { - "prepare": "node scripts/prepare.js", - "build": "yarn run build:prod && yarn run build:legacy", - "build:prod": "yarn run webpack --config webpack.prod.js", - "build:legacy": "yarn run webpack --config webpack.prod.legacyBrowser.js", - "build:stats": "webpack --config webpack.prod.js --profile --json > stats.json", - "build:analyze-bundle": "ANALYZE_BUNDLE=1 yarn run build", - "build:dev": "FR_BASE_URL='http://localhost:5000${path}' EN_BASE_URL='http://localhost:5001${path}' yarn run build", - "clean": "rimraf dist node_modules 'source/data/!(versement-transport.json)'", - "test": "yarn test:file \"./{,!(node_modules)/**/}!(webpack).test.{js,ts}\"", - "test:file": "yarn mocha-webpack --webpack-config ./webpack.dev.js --include test/componentTestSetup.js --require mock-local-storage --require test/helpers/browser.js", - "test:bundlesize": "bundlesize", - "test:dev-e2e:publicodes": "cypress open --browser chromium --config baseUrl=http://localhost:8080/publicodes,integrationFolder=cypress/integration/publi.codes", - "test:dev-e2e:mon-entreprise": "cypress open --browser chromium", - "test:dev-e2e:mycompanyinfrance": "cypress open --browser chromium --config baseUrl=http://localhost:8080/infrance", - "i18n:rules:check": "node scripts/i18n/check-missing-rule-translation.js", - "i18n:rules:translate": "node scripts/i18n/translate-rules.js", - "i18n:ui:check": "yarn run i18next -c scripts/i18n/parser.config.js && node scripts/i18n/check-missing-UI-translation", - "i18n:ui:translate": "rm -rf source/locales/static-analysis-fr.json && yarn run i18next -c scripts/i18n/parser.config.js && node scripts/i18n/translate-ui.js", - "start": "node dev-server.js", - "serve:dev": "concurrently -k \"yarn run serve:dev:mon-entreprise\" \"yarn run serve:dev:mycompanyinfrance & yarn run serve:dev:publicodes\"", - "serve:dev:mon-entreprise": "PORT=5000 serve --config serve.mon-entreprise.json --no-clipboard", - "serve:dev:publicodes": "PORT=5002 serve --config serve.publicodes.json --no-clipboard", - "serve:dev:mycompanyinfrance": "PORT=5001 serve --config serve.infrance.json --no-clipboard" - } -} diff --git a/mon-entreprise/scripts/.eslintrc.yaml b/mon-entreprise/scripts/.eslintrc.yaml deleted file mode 100644 index bde82d6ae..000000000 --- a/mon-entreprise/scripts/.eslintrc.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# We overwrite the default ESLint configuration for NodeJS scripts -env: - node: true -rules: - no-console: 0 diff --git a/mon-entreprise/scripts/fetch-releases.js b/mon-entreprise/scripts/fetch-releases.js deleted file mode 100644 index d7eac7c5b..000000000 --- a/mon-entreprise/scripts/fetch-releases.js +++ /dev/null @@ -1,84 +0,0 @@ -// This script uses the GitHub API which requires an access token. -// https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line -// Once you have your access token you can put it in a `.env` file at the root -// of the project to enable it during development. For instance: -// -// GITHUB_API_SECRET=f4336c82cb1e494752d06e610614eab12b65f1d1 -// -// If you want to fetch unpublished "draft" release, you should check the -// "public repo" authorization when generating the access token. -require('dotenv').config() -require('isomorphic-fetch') -var { createDataDir, writeInDataDir } = require('./utils.js') - -// We use the GitHub API V4 in GraphQL to download the releases. A GraphQL -// explorer can be found here : https://developer.github.com/v4/explorer/ -const githubAuthToken = process.env.GITHUB_API_SECRET -const cursorOfV1Release = 'Y3Vyc29yOnYyOpHOARHb8g==' -const query = `query { - repository(owner:"betagouv", name:"mon-entreprise") { - releases(after:"${cursorOfV1Release}", last:100) { - nodes { - name - description - } - } - } -}` - -// In case we cannot fetch the release (the API is down or the Authorization -// token isn't valid) we fallback to some fake data -- it would be better to -// have a static ressource accessible without authentification. -const fakeData = [ - { - name: 'Fake release', - descriptionHTML: `You are seing this fake release because you - didn't configure your GitHub access token and we weren't - able to fetch the real releases from GitHub.

- See the script
fetch-releases.js
for more informations.`, - }, - { - name: 'Release 2', - descriptionHTML: 'blah blah blah', - }, - { - name: 'Release 3', - descriptionHTML: 'blah blah blah', - }, -] - -async function main() { - createDataDir() - const releases = await fetchReleases() - // The last release name is fetched on all pages (to display the banner) - // whereas the full release data is used only in the dedicated page, that why - // we deduplicate the releases data in two separated files that can be - // bundled/fetched separately. - writeInDataDir('releases.json', releases) - writeInDataDir('last-release.json', { name: releases[0].name }) -} - -async function fetchReleases() { - if (!githubAuthToken) { - return fakeData - } - try { - const response = await fetch('https://api.github.com/graphql', { - method: 'post', - headers: new Headers({ Authorization: `bearer ${githubAuthToken}` }), - body: JSON.stringify({ query }), - }) - const { - data: { - repository: { - releases: { nodes: releases }, - }, - }, - } = await response.json() - return releases.filter(Boolean).reverse() - } catch (e) { - return fakeData - } -} - -main() diff --git a/mon-entreprise/scripts/fetch-stats.js b/mon-entreprise/scripts/fetch-stats.js deleted file mode 100644 index 65b9fc61b..000000000 --- a/mon-entreprise/scripts/fetch-stats.js +++ /dev/null @@ -1,274 +0,0 @@ -require('dotenv').config() -require('isomorphic-fetch') - -const { map, filter, flatten, partition, pipe } = require('ramda') -const { compose } = require('redux') -const { createDataDir, writeInDataDir } = require('./utils.js') -const matomoSiteVisitsHistory = require('./matomoVisitHistory.json') -const fetchApi = async function (query) { - const response = await fetch('https://api.atinternet.io/v3/data/getData', { - method: 'POST', - headers: new Headers({ - 'x-api-key': `${process.env.ATINTERNET_API_ACCESS_KEY}_${process.env.ATINTERNET_API_SECRET_KEY}`, - 'Content-Type': 'application/json', - }), - body: JSON.stringify(query), - }) - if (!response.ok) { - const text = await response.text() - throw new Error(`Erreur de l'API (${text})`) - } - const data = await response.json() - return data.DataFeed.Rows.map((x) => x.Rows) -} - -const buildSimulateursQuery = (period, granularity) => ({ - columns: [ - 'page', - 'page_chapter1', - 'page_chapter2', - 'page_chapter3', - 'm_visits', - ], - space: { - s: [617190], - }, - period: { - p1: [period], - }, - evo: { - granularity, - top: { - 'page-num': 1, - 'max-results': 500, - sort: ['-m_visits'], - filter: { - property: { - page_chapter1: { - $in: ['gerer', 'simulateurs'], - }, - }, - }, - }, - }, - options: { - ignore_null_properties: true, - }, -}) - -const buildSatisfactionQuery = () => ({ - columns: [ - 'page_chapter1', - 'page_chapter2', - 'page_chapter3', - 'click', - 'm_events', - ], - space: { - s: [617190], - }, - period: { - p1: [last36Months], - }, - evo: { - granularity: 'M', - top: { - 'page-num': 1, - 'max-results': 500, - sort: ['-m_events'], - filter: { - property: { - $AND: [ - { - page_chapter1: { - $in: ['gerer', 'simulateurs'], - }, - }, - { - click_chapter1: { - $eq: 'satisfaction', - }, - }, - ], - }, - }, - }, - }, - options: { - ignore_null_properties: true, - }, -}) - -const buildSiteQuery = (period, granularity) => ({ - columns: ['m_visits'], - space: { - s: [617190], - }, - period: { - p1: [period], - }, - evo: { - granularity, - top: { - 'page-num': 1, - 'max-results': 500, - sort: ['-m_visits'], - }, - }, - options: { - ignore_null_properties: true, - }, -}) - -const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)) - .toISOString() - .slice(0, 10) -const last60days = { - type: 'D', - start: new Date( - Math.max( - new Date().setDate(new Date().getDate() - 60), - new Date('2021-02-27') - ) - ) - .toISOString() - .slice(0, 10), - end: yesterday, -} - -const last36Months = { - type: 'D', - start: - new Date( - Math.max( - new Date().setMonth(new Date().getMonth() - 36), - new Date('2021-03') - ) - ) - .toISOString() - .slice(0, 8) + '01', - end: yesterday, -} -const uniformiseData = pipe( - // For some reason, an artifact create ghost page with unlogical chapter metrics... - // It seems to only by one per month thought... This hacks resolves it - filter(({ m_visits }) => m_visits === undefined || m_visits > 2), - map(({ d_evo_day, d_evo_month, m_visits, m_events, ...data }) => ({ - date: d_evo_day != null ? d_evo_day : d_evo_month, - nombre: m_visits != null ? m_visits : m_events, - ...data, - })) -) -const flattenPage = compose( - flatten, - map(({ Rows, ...page }) => Rows.map((r) => ({ ...page, ...r }))), - filter((p) => p.page_chapter2 !== 'N/A') // Remove simulateur landing page -) -async function fetchDailyVisits() { - const pages = uniformiseData( - flattenPage(await fetchApi(buildSimulateursQuery(last60days, 'D'))) - ) - const site = uniformiseData( - (await fetchApi(buildSiteQuery(last60days, 'D')))[0].Rows - ) - - return { pages, site } -} - -async function fetchMonthlyVisits() { - const pages = uniformiseData( - flattenPage(await fetchApi(buildSimulateursQuery(last36Months, 'M'))) - ) - - const site = [ - ...matomoSiteVisitsHistory.map(({ date, visites }) => ({ - date: date + '-01', - nombre: visites, - })), - ...uniformiseData( - (await fetchApi(buildSiteQuery(last36Months, 'M')))[0].Rows - ), - ] - - return { pages, site } -} - -async function fetchUserFeedbackIssues() { - const tags = await fetch( - 'https://mon-entreprise.zammad.com/api/v1/tag_list', - { - headers: new Headers({ - Authorization: `Token token=${process.env.ZAMMAD_API_SECRET_KEY}`, - }), - } - ).then((r) => r.json()) - const sortedTags = tags - .sort((t1, t2) => t2.count - t1.count) - .filter(({ name }) => /#[\d]+/.exec(name)) - const query = `query { - repository(owner:"betagouv", name:"mon-entreprise") {${sortedTags - .map( - ({ name, count }, i) => - ` - issue${i}_${count}: issue(number: ${name.slice(1)}) { - title - closedAt - number - }` - ) - .join('\n')} - } - }` - const response = await fetch('https://api.github.com/graphql', { - method: 'post', - headers: new Headers({ - Authorization: `bearer ${process.env.GITHUB_API_SECRET}`, - }), - body: JSON.stringify({ - query, - }), - }) - const data = await response.json() - const issues = Object.entries(data.data.repository) - .filter(([, value]) => !!value) - .map(([k, value]) => ({ ...value, count: +/[\d]+$/.exec(k)[0] })) - const [closed, open] = partition((s) => s.closedAt, issues) - return { - open, - closed: closed.sort( - (i1, i2) => new Date(i2.closedAt) - new Date(i1.closedAt) - ), - } -} -async function main() { - createDataDir() - try { - const visitesJours = await fetchDailyVisits() - const visitesMois = await fetchMonthlyVisits() - const satisfaction = uniformiseData( - flattenPage(await fetchApi(buildSatisfactionQuery())) - ) - const retoursUtilisateurs = await fetchUserFeedbackIssues() - writeInDataDir('stats.json', { - visitesJours, - visitesMois, - satisfaction, - retoursUtilisateurs, - }) - } catch (e) { - console.error(e) - - // In case we cannot fetch the release (the API is down or the Authorization - // token isn't valid) we fallback to some fake data -- it would be better to - // have a static ressource accessible without authentification. - writeInDataDir('stats.json', { - visitesJours: [], - visitesMois: [], - satisfaction: [], - retoursUtilisateurs: [], - }) - } -} -main().catch((e) => { - throw new Error(e) -}) diff --git a/mon-entreprise/scripts/i18n/check-missing-UI-translation.js b/mon-entreprise/scripts/i18n/check-missing-UI-translation.js deleted file mode 100644 index 29525d534..000000000 --- a/mon-entreprise/scripts/i18n/check-missing-UI-translation.js +++ /dev/null @@ -1,12 +0,0 @@ -const { getUiMissingTranslations } = require('./utils') -const missingTranslationKeys = Object.keys(getUiMissingTranslations()) -if (missingTranslationKeys.length) { - throw new Error(`Il manque des traductions UI pour les clés suivantes : ${[ - '', - ...missingTranslationKeys, - ].join('\n\t- ')} -Utilisez la commande suivante pour traduire automatiquement les clés manquantes : - -\tyarn run i18n:ui:translate -`) -} diff --git a/mon-entreprise/scripts/i18n/check-missing-rule-translation.js b/mon-entreprise/scripts/i18n/check-missing-rule-translation.js deleted file mode 100644 index 2a3a1d778..000000000 --- a/mon-entreprise/scripts/i18n/check-missing-rule-translation.js +++ /dev/null @@ -1,13 +0,0 @@ -const { getRulesMissingTranslations } = require('./utils') - -const missingTranslations = getRulesMissingTranslations()[0] - -if (missingTranslations.length) { - throw new Error( - `Les traductions suivantes sont à corriger dans 'rules-en.yaml' : \n${missingTranslations - .map(([dottedName, attr]) => `\t- ${dottedName} [${attr}]\n`) - .join( - '' - )}\nUtilisez la commande suivante pour traduire automatiquement les clés manquantes :\n\n\tyarn run i18n:rules:translate\n` - ) -} diff --git a/mon-entreprise/scripts/i18n/parser.config.js b/mon-entreprise/scripts/i18n/parser.config.js deleted file mode 100644 index 504bf25da..000000000 --- a/mon-entreprise/scripts/i18n/parser.config.js +++ /dev/null @@ -1,75 +0,0 @@ -// i18next-parser.config.js - -module.exports = { - contextSeparator: '_', - // Key separator used in your translation keys - - createOldCatalogs: false, - // Save the \_old files - - defaultNamespace: 'translation', - // Default namespace used in your i18next config - - defaultValue: 'NO_TRANSLATION', - // Default value to give to empty keys - - indentation: 2, - // Indentation of the catalog files - - keepRemoved: false, - // Keep keys from the catalog that are no longer in code - - keySeparator: false, - // Key separator used in your translation keys - // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. - - // see below for more details - lexers: { - hbs: ['HandlebarsLexer'], - handlebars: ['HandlebarsLexer'], - - htm: ['HTMLLexer'], - html: ['HTMLLexer'], - - mjs: ['JavascriptLexer'], - js: ['JavascriptLexer'], // if you're writing jsx inside .js files, change this to JsxLexer - ts: ['JavascriptLexer'], - jsx: ['JsxLexer'], - tsx: ['JsxLexer'], - - default: ['JavascriptLexer'], - }, - - lineEnding: 'auto', - // Control the line ending. See options at https://github.com/ryanve/eol - - locales: ['fr'], - // An array of the locales in your applications - - namespaceSeparator: false, - // Namespace separator used in your translation keys - // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. - - output: 'source/locales/static-analysis-$LOCALE.json', - // Supports $LOCALE and $NAMESPACE injection - // Supports JSON (.json) and YAML (.yml) file formats - // Where to write the locale files relative to process.cwd() - - input: '../../source/**/*.{jsx,tsx,js,ts}', - // An array of globs that describe where to look for source files - // relative to the location of the configuration file - - reactNamespace: false, - // For react file, extract the defaultNamespace - https://react.i18next.com/components/translate-hoc.html - // Ignored when parsing a `.jsx` file and namespace is extracted from that file. - - sort: true, - // Whether or not to sort the catalog - - useKeysAsDefaultValue: false, - // Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World" - // The option `defaultValue` will not work if this is set to true - - verbose: false, - // Display info about the parsing including some stats -} diff --git a/mon-entreprise/scripts/i18n/translate-rules.js b/mon-entreprise/scripts/i18n/translate-rules.js deleted file mode 100644 index c2391ea4a..000000000 --- a/mon-entreprise/scripts/i18n/translate-rules.js +++ /dev/null @@ -1,39 +0,0 @@ -var { stringify } = require('yaml') -var fs = require('fs') -var prettier = require('prettier') - -const { - getRulesMissingTranslations, - rulesTranslationPath, - fetchTranslation, -} = require('./utils') - -const [missingTranslations, resolved] = getRulesMissingTranslations() - -fs.writeFileSync( - rulesTranslationPath, - stringify(resolved, { sortMapEntries: true }) -) -;(async function main() { - await Promise.all( - missingTranslations.map(async ([dottedName, attr, value]) => { - try { - const translation = await fetchTranslation(value) - resolved[dottedName][attr] = '[automatic] ' + translation - } catch (e) { - console.log(e) - } - }) - ) - - prettier.resolveConfig(rulesTranslationPath).then((options) => { - const formattedYaml = prettier.format( - stringify(resolved, { sortMapEntries: true }), - { - ...options, - parser: 'yaml', - } - ) - fs.writeFileSync(rulesTranslationPath, formattedYaml) - }) -})() diff --git a/mon-entreprise/scripts/i18n/translate-ui.js b/mon-entreprise/scripts/i18n/translate-ui.js deleted file mode 100644 index 721fba2af..000000000 --- a/mon-entreprise/scripts/i18n/translate-ui.js +++ /dev/null @@ -1,32 +0,0 @@ -var { stringify, parse } = require('yaml') -var R = require('ramda') -var fs = require('fs') - -const { - getUiMissingTranslations, - UiTranslationPath, - fetchTranslation, -} = require('./utils') - -const missingTranslations = getUiMissingTranslations() - -let translatedKeys = parse(fs.readFileSync(UiTranslationPath, 'utf-8')) - -Object.entries(missingTranslations) - .map(([key, value]) => [key, value === 'NO_TRANSLATION' ? key : value]) - .forEach(async ([key, value]) => { - try { - const translation = await fetchTranslation(value) - translatedKeys = R.assocPath( - key.split(/(?<=[A-zÀ-ü0-9])\.(?=[A-zÀ-ü0-9])/), - translation, - translatedKeys - ) - fs.writeFileSync( - UiTranslationPath, - stringify(translatedKeys, { sortMapEntries: true }) - ) - } catch (e) { - console.log(e) - } - }) diff --git a/mon-entreprise/scripts/i18n/utils.js b/mon-entreprise/scripts/i18n/utils.js deleted file mode 100644 index 64cc63cad..000000000 --- a/mon-entreprise/scripts/i18n/utils.js +++ /dev/null @@ -1,135 +0,0 @@ -require('dotenv').config() -require('isomorphic-fetch') -var fs = require('fs') -var path = require('path') -let R = require('ramda') -var querystring = require('querystring') -require('../../../modele-social/build') -let rules = require('../../../modele-social') -let { parse } = require('yaml') - -let rulesTranslationPath = path.resolve('source/locales/rules-en.yaml') -let UiTranslationPath = path.resolve('source/locales/ui-en.yaml') - -let attributesToTranslate = [ - 'titre', - 'description', - 'question', - 'résumé', - 'suggestions', - 'note', -] - -function getRulesMissingTranslations() { - let currentExternalization = parse( - fs.readFileSync(rulesTranslationPath, 'utf-8') - ) - - let missingTranslations = [] - let resolved = Object.entries(rules) - .map(([dottedName, rule]) => [ - dottedName, - !rule || !rule.titre // && utils.ruleWithDedicatedDocumentationPage(rule)) - ? { ...rule, titre: dottedName.split(' . ').slice(-1)[0] } - : rule, - ]) - .map(([dottedName, rule]) => ({ - [dottedName]: R.mergeAll( - R.toPairs(rule) - .filter(([, v]) => !!v) - .map(([k, v]) => { - let attrToTranslate = attributesToTranslate.find(R.equals(k)) - if (!attrToTranslate) return {} - let enTrad = attrToTranslate + '.en', - frTrad = attrToTranslate + '.fr' - - let currentTranslation = currentExternalization[dottedName] - - if ('suggestions' === attrToTranslate) { - return Object.keys(v).reduce((acc, suggestion) => { - const enTrad = `suggestions.${suggestion}.en` - const frTrad = `suggestions.${suggestion}.fr` - if ( - currentTranslation && - currentTranslation[enTrad] && - currentTranslation[frTrad] === suggestion - ) { - return { - ...acc, - [frTrad]: currentTranslation[frTrad], - [enTrad]: currentTranslation[enTrad], - } - } - missingTranslations.push([dottedName, enTrad, suggestion]) - return { - ...acc, - [frTrad]: suggestion, - } - }, {}) - } - - // Check if a human traduction exists already for this attribute and if - // it does need to be updated - if ( - currentTranslation && - currentTranslation[enTrad] && - currentTranslation[frTrad] === v - ) - return { - [enTrad]: currentTranslation[enTrad], - [frTrad]: v, - } - - missingTranslations.push([dottedName, enTrad, v]) - return { - [frTrad]: v, - } - }) - ), - })) - resolved = R.mergeAll(resolved) - return [missingTranslations, resolved] -} - -const getUiMissingTranslations = () => { - const staticKeys = require(path.resolve( - 'source/locales/static-analysis-fr.json' - )) - const translatedKeys = parse(fs.readFileSync(UiTranslationPath, 'utf-8')) - - const missingTranslations = Object.keys(staticKeys).filter((key) => { - if (key.match(/^\{.*\}$/)) { - return false - } - const keys = key.split(/(?<=[A-zÀ-ü0-9])\.(?=[A-zÀ-ü0-9])/) - return !R.path(keys, translatedKeys) - }, staticKeys) - return R.pick(missingTranslations, staticKeys) -} - -const fetchTranslation = async (text) => { - const response = await fetch( - `https://api.deepl.com/v2/translate?${querystring.stringify({ - text, - auth_key: process.env.DEEPL_API_SECRET, - tag_handling: 'xml', - source_lang: 'FR', - target_lang: 'EN', - })}` - ) - try { - const { translations } = await response.json() - console.log(`✅ Deepl translation succeeded for:\n\t${text}\n`) - return translations[0].text - } catch (e) { - console.warn(`❌ Deepl translation failed for:\n\t${text}\n`) - return '' - } -} -module.exports = { - fetchTranslation, - getRulesMissingTranslations, - getUiMissingTranslations, - rulesTranslationPath, - UiTranslationPath, -} diff --git a/mon-entreprise/scripts/matomoVisitHistory.json b/mon-entreprise/scripts/matomoVisitHistory.json deleted file mode 100644 index ba70c9f26..000000000 --- a/mon-entreprise/scripts/matomoVisitHistory.json +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - "date": "2019-01", - "visites": 119541 - }, - { - "date": "2019-02", - "visites": 99065 - }, - { - "date": "2019-03", - "visites": 122931 - }, - { - "date": "2019-04", - "visites": 113454 - }, - { - "date": "2019-05", - "visites": 118637 - }, - { - "date": "2019-06", - "visites": 152981 - }, - { - "date": "2019-07", - "visites": 141079 - }, - { - "date": "2019-08", - "visites": 127326 - }, - { - "date": "2019-09", - "visites": 178474 - }, - { - "date": "2019-10", - "visites": 198260 - }, - { - "date": "2019-11", - "visites": 174515 - }, - { - "date":"2019-12", - "visites":175128 - }, - { - "date":"2020-01", - "visites":238463 - }, - { - "date":"2020-02", - "visites":277987 - }, - { - "date":"2020-03", - "visites":344769 - }, - { - "date":"2020-04", - "visites":195817 - }, - { - "date":"2020-05", - "visites":221728 - }, - { - "date":"2020-06", - "visites":292412 - }, - { - "date":"2020-07", - "visites":267030 - }, - { - "date":"2020-08", - "visites":230344 - }, - { - "date":"2020-09", - "visites":315055 - }, - { - "date":"2020-10", - "visites":304903 - }, - { - "date":"2020-11", - "visites":281502 - }, - { - "date":"2020-12", - "visites":255363 - }, - { - "date":"2021-01", - "visites":338320 - }, - { - "date":"2021-02", - "visites":297955 - } -] diff --git a/mon-entreprise/scripts/prepare.js b/mon-entreprise/scripts/prepare.js deleted file mode 100644 index ebf6bf168..000000000 --- a/mon-entreprise/scripts/prepare.js +++ /dev/null @@ -1,2 +0,0 @@ -require('./fetch-releases.js') -require('./fetch-stats.js') diff --git a/mon-entreprise/scripts/utils.js b/mon-entreprise/scripts/utils.js deleted file mode 100644 index c65ac58ec..000000000 --- a/mon-entreprise/scripts/utils.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require('path') -const fs = require('fs') -const dataDir = path.resolve(__dirname, '../source/data/') - -exports.createDataDir = () => { - if (!fs.existsSync(dataDir)) { - fs.mkdirSync(dataDir) - } -} - -exports.writeInDataDir = (filename, data) => { - fs.writeFileSync(path.join(dataDir, filename), JSON.stringify(data, null, 2)) -} diff --git a/mon-entreprise/serve.infrance.json b/mon-entreprise/serve.infrance.json deleted file mode 100644 index c546aef83..000000000 --- a/mon-entreprise/serve.infrance.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rewrites": [{ "source": "**", "destination": "infrance.html" }], - "public": "dist" -} diff --git a/mon-entreprise/serve.mon-entreprise.json b/mon-entreprise/serve.mon-entreprise.json deleted file mode 100644 index a0077af85..000000000 --- a/mon-entreprise/serve.mon-entreprise.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rewrites": [{ "source": "**", "destination": "mon-entreprise.html" }], - "public": "dist" -} diff --git a/mon-entreprise/serve.publicodes.json b/mon-entreprise/serve.publicodes.json deleted file mode 100644 index 8ad7f32cc..000000000 --- a/mon-entreprise/serve.publicodes.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rewrites": [{ "source": "**", "destination": "publicodes.html" }], - "public": "dist" -} diff --git a/mon-entreprise/source/ATInternetTracking/.eslintrc.yaml b/mon-entreprise/source/ATInternetTracking/.eslintrc.yaml deleted file mode 100644 index 71cc8b93d..000000000 --- a/mon-entreprise/source/ATInternetTracking/.eslintrc.yaml +++ /dev/null @@ -1,2 +0,0 @@ -ignorePatterns: - - smarttag.js \ No newline at end of file diff --git a/mon-entreprise/source/ATInternetTracking/Tracker.ts b/mon-entreprise/source/ATInternetTracking/Tracker.ts deleted file mode 100644 index b9f42369d..000000000 --- a/mon-entreprise/source/ATInternetTracking/Tracker.ts +++ /dev/null @@ -1,128 +0,0 @@ -import './smarttag.js' - -// Ci-dessous les indicateurs personnalisés de site et de page -// https://developers.atinternet-solutions.com/javascript-fr/contenus-javascript-fr/indicateurs-de-site-et-de-page-javascript-fr/ -export const INDICATOR = { - SITE: { - LANGAGE: 1, - EMBARQUÉ: 2, - }, - PAGE: {}, -} as const - -type CustomSiteIndicator = { - [INDICATOR.SITE.LANGAGE]: '[fr]' | '[en]' // langage du site (mycompanyinfrance ou mon-entreprise) - [INDICATOR.SITE.EMBARQUÉ]: '1' | '0' // intégration externe -} -type CustomPageIndicator = Record -type CustomVars = { - site?: Partial - page?: Partial -} -type ATHit = { - name?: string - chapter1?: string - chapter2?: string - chapter3?: string - level2?: string - customVars?: CustomVars -} - -export interface ATTracker { - page: { - set(infos: ATHit): void - } - click: { - set( - infos: ATHit & { type: 'exit' | 'download' | 'action' | 'navigation' } - ): void - } - customVars: { - set(variables: CustomVars): void - } - privacy: { - setVisitorMode(authority: 'cnil', type: 'exempt'): void - setVisitorOptout(): void - getVisitorMode(): { name: 'exempt' | 'optout' } - } - dispatch(): void -} - -type ATTrackerClass = { new (options: { site: number }): ATTracker } - -declare global { - const ATInternet: { - Tracker: { Tag: ATTrackerClass } - } -} - -export function createTracker(siteId?: string, doNotTrack = false) { - const site = siteId ? +siteId : 0 - if (Number.isNaN(site)) { - throw new Error('expect string siteId to be of number form') - } - const BaseTracker: ATTrackerClass = siteId ? ATInternet.Tracker.Tag : Log - class Tag extends BaseTracker { - site: CustomSiteIndicator = { - [INDICATOR.SITE.LANGAGE]: '[fr]', - [INDICATOR.SITE.EMBARQUÉ]: document.location.pathname.includes( - '/iframes/' - ) - ? '1' - : '0', - } - constructor(options: { language: 'fr' | 'en' }) { - super({ site }) - this.site[ - INDICATOR.SITE.LANGAGE - ] = `[${options.language}]` as CustomSiteIndicator[1] - if (process.env.NODE_ENV === 'production' && doNotTrack) { - this.privacy.setVisitorOptout() - } else { - this.privacy.setVisitorMode('cnil', 'exempt') - } - } - - dispatch() { - this.customVars.set({ site: this.site }) - super.dispatch() - } - } - return Tag -} - -export class Log implements ATTracker { - constructor(options?: Record) { - console.debug('ATTracker::new', options) - } - page = { - set(infos: ATHit): void { - console.debug('ATTracker::page.set', infos) - }, - } - click = { - set(infos: ATHit): void { - console.debug('ATTracker::click.set', infos) - }, - } - customVars: ATTracker['customVars'] = { - set(variables) { - console.debug('ATTracker::customVars.set', variables) - }, - } - privacy: ATTracker['privacy'] = { - setVisitorMode(...args) { - console.debug('ATTracker::privacy.setVisitorMode', ...args) - }, - setVisitorOptout() { - console.debug('ATTracker::setVisitorOptout') - }, - getVisitorMode() { - console.debug('ATTracker::privacy.getVisitorMode') - return { name: 'exempt' } - }, - } - dispatch(): void { - console.debug('ATTracker::dispatch') - } -} diff --git a/mon-entreprise/source/ATInternetTracking/index.ts b/mon-entreprise/source/ATInternetTracking/index.ts deleted file mode 100644 index 23e1c6a0e..000000000 --- a/mon-entreprise/source/ATInternetTracking/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { createContext, useContext, useEffect } from 'react' -import { ATTracker, Log } from './Tracker' - -export const TrackingContext = createContext(new Log()) - -// From https://github.com/nclsmitchell/at-internet -function toAtString(string: string): string { - string = string.replace(/ /g, '_').toLowerCase() - string = string.replace(/[\300-\306]|[\340-\346]/g, 'a') - string = string.replace(/[\310-\313]|[\350-\353]/g, 'e') - string = string.replace(/[\314-\317]|[\354-\357]/g, 'i') - string = string.replace(/[\322-\330]|[\362-\370]/g, 'o') - string = string.replace(/[\331-\334]|[\371-\374]/g, 'u') - string = string.replace(/[\307\347]/g, 'c') - string = string.replace(/[\321\361]/g, 'n') - string = string.replace(/[^\w]/gi, '_') - return string -} - -// Chapter definition : https://www.atinternet.com/en/glossary/chapter/ -type Chapter1 = - | 'simulateurs' - | 'informations' - | 'creer' - | 'gerer' - | 'documentation' - | 'integration' - -let chapters: { - chapter1?: Chapter1 - chapter2?: string - chapter3?: string -} = {} -export function TrackChapter(props: { - chapter1?: Chapter1 - chapter2?: string - chapter3?: string -}) { - if (props.chapter1) { - chapters = { chapter2: '', chapter3: '', ...props } - return null - } - if (props.chapter2) { - chapters = { ...chapters, chapter3: '', ...props } - return null - } - - chapters = { ...chapters, ...props } - return null -} - -export function TrackPage(props: { - name?: string - chapter1?: Chapter1 - chapter2?: string - chapter3?: string -}) { - const tag = useContext(TrackingContext) - TrackChapter(props) - const propsFormatted = Object.fromEntries( - Object.entries({ ...chapters, name: props.name }).map(([k, v]) => [ - k, - v && toAtString(v), - ]) - ) - useEffect(() => { - tag.page.set(propsFormatted) - tag.dispatch() - }, [ - tag, - propsFormatted.name, - propsFormatted.chapter1, - propsFormatted.chapter2, - propsFormatted.chapter3, - ]) - return null -} diff --git a/mon-entreprise/source/ATInternetTracking/smarttag.js b/mon-entreprise/source/ATInternetTracking/smarttag.js deleted file mode 100644 index 74059e52f..000000000 --- a/mon-entreprise/source/ATInternetTracking/smarttag.js +++ /dev/null @@ -1,3688 +0,0 @@ -;(function () { - var dfltPluginCfg = {} - var dfltGlobalCfg = { - site: 617189, - log: '', - logSSL: '', - domain: 'xiti.com', - collectDomain: 'logc412.xiti.com', - collectDomainSSL: 'logs1412.xiti.com', - userIdOrigin: 'server', - pixelPath: '/hit.xiti', - disableCookie: false, - disableStorage: false, - cookieSecure: true, - cookieDomain: '', - preview: false, - plgs: [ - 'Campaigns', - 'Clicks', - 'ContextVariables', - 'Page', - 'ClientSideUserId', - 'Privacy', - ], - lazyLoadingPath: '', - documentLevel: 'document', - redirect: false, - activateCallbacks: true, - medium: '', - ignoreEmptyChapterValue: false, - base64Storage: false, - sendHitWhenOptOut: true, - forceHttp: false, - requestMethod: 'GET', - maxHitSize: 2000, - urlPropertyAuto: true, - urlPropertyQueryString: false, - } - ;(function (a) { - a.ATInternet = a.ATInternet || {} - a.ATInternet.Tracker = a.ATInternet.Tracker || {} - a.ATInternet.Tracker.Plugins = a.ATInternet.Tracker.Plugins || {} - })(window) - var Utils = function () { - function a(h) { - var b = typeof h - if ('object' !== b || null === h) - return 'string' === b && (h = '"' + h + '"'), String(h) - var e, - f, - d = [], - c = h.constructor === Array - for (e in h) - h.hasOwnProperty(e) && - ((f = h[e]), - (b = typeof f), - 'function' !== b && - 'undefined' !== b && - ('string' === b - ? (f = '"' + f.replace(/[^\\]"/g, '\\"') + '"') - : 'object' === b && null !== f && (f = a(f)), - d.push((c ? '' : '"' + e + '":') + String(f)))) - return (c ? '[' : '{') + String(d) + (c ? ']' : '}') - } - function g(a) { - return null === a ? '' : (a + '').replace(d, '') - } - function k(a) { - var l, - e = null - return (a = g(a + '')) && - !g( - a.replace(b, function (a, h, b, d) { - l && h && (e = 0) - if (0 === e) return a - l = b || h - e += !d - !b - return '' - }) - ) - ? Function('return ' + a)() - : null - } - var c = this, - b = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g, - d = RegExp( - '^[\\x20\\t\\r\\n\\f]+|((?:^|[^\\\\])(?:\\\\.)*)[\\x20\\t\\r\\n\\f]+$', - 'g' - ) - c.isLocalStorageAvailable = function () { - try { - var a = localStorage - a.setItem('__storage_test__', '__storage_test__') - a.removeItem('__storage_test__') - return !0 - } catch (b) { - return !1 - } - } - c.isBeaconMethodAvailable = function () { - return ( - window.navigator && 'function' === typeof window.navigator.sendBeacon - ) - } - c.Base64 = { - _keyStr: - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', - encode: function (a) { - var b = '', - e, - f, - d, - r, - q, - m, - p = 0 - for (a = c.Base64._utf8_encode(a); p < a.length; ) - (e = a.charCodeAt(p++)), - (f = a.charCodeAt(p++)), - (d = a.charCodeAt(p++)), - (r = e >> 2), - (e = ((e & 3) << 4) | (f >> 4)), - (q = ((f & 15) << 2) | (d >> 6)), - (m = d & 63), - isNaN(f) ? (q = m = 64) : isNaN(d) && (m = 64), - (b = - b + - this._keyStr.charAt(r) + - this._keyStr.charAt(e) + - this._keyStr.charAt(q) + - this._keyStr.charAt(m)) - return b - }, - decode: function (a) { - var b = '', - e, - f, - d, - r, - q, - m = 0 - for (a = a.replace(/[^A-Za-z0-9\+\/\=]/g, ''); m < a.length; ) - (e = this._keyStr.indexOf(a.charAt(m++))), - (f = this._keyStr.indexOf(a.charAt(m++))), - (r = this._keyStr.indexOf(a.charAt(m++))), - (q = this._keyStr.indexOf(a.charAt(m++))), - (e = (e << 2) | (f >> 4)), - (f = ((f & 15) << 4) | (r >> 2)), - (d = ((r & 3) << 6) | q), - (b += String.fromCharCode(e)), - 64 != r && (b += String.fromCharCode(f)), - 64 != q && (b += String.fromCharCode(d)) - return (b = c.Base64._utf8_decode(b)) - }, - _utf8_encode: function (a) { - a = a.replace(/\r\n/g, '\n') - for (var b = '', e = 0; e < a.length; e++) { - var f = a.charCodeAt(e) - 128 > f - ? (b += String.fromCharCode(f)) - : (127 < f && 2048 > f - ? (b += String.fromCharCode((f >> 6) | 192)) - : ((b += String.fromCharCode((f >> 12) | 224)), - (b += String.fromCharCode(((f >> 6) & 63) | 128))), - (b += String.fromCharCode((f & 63) | 128))) - } - return b - }, - _utf8_decode: function (a) { - for (var b = '', e = 0, f, d, c; e < a.length; ) - (f = a.charCodeAt(e)), - 128 > f - ? ((b += String.fromCharCode(f)), e++) - : 191 < f && 224 > f - ? ((d = a.charCodeAt(e + 1)), - (b += String.fromCharCode(((f & 31) << 6) | (d & 63))), - (e += 2)) - : ((d = a.charCodeAt(e + 1)), - (c = a.charCodeAt(e + 2)), - (b += String.fromCharCode( - ((f & 15) << 12) | ((d & 63) << 6) | (c & 63) - )), - (e += 3)) - return b - }, - } - c.loadScript = function (a, b) { - var e - b = b || function () {} - e = document.createElement('script') - e.type = 'text/javascript' - e.src = a.url - e.async = !1 - e.defer = !1 - e.onload = e.onreadystatechange = function (a) { - a = a || window.event - if ( - 'load' === a.type || - (/loaded|complete/.test(e.readyState) && - (!document.documentMode || 9 > document.documentMode)) - ) - (e.onload = e.onreadystatechange = e.onerror = null), b(null, a) - } - e.onerror = function (a) { - e.onload = e.onreadystatechange = e.onerror = null - b({ msg: 'script not loaded', event: a }) - } - var f = document.head || document.getElementsByTagName('head')[0] - f.insertBefore(e, f.lastChild) - } - c.cloneSimpleObject = function (a, b) { - if ('object' !== typeof a || null === a || a instanceof Date) return a - var e = new a.constructor(), - f - for (f in a) - a.hasOwnProperty(f) && - (void 0 === f || - (b && void 0 === a[f]) || - (e[f] = c.cloneSimpleObject(a[f]))) - return e - } - c.isEmptyObject = function (a) { - for (var b in a) if (a.hasOwnProperty(b)) return !1 - return !0 - } - c.isObject = function (a) { - return null !== a && 'object' === typeof a && !(a instanceof Array) - } - c.ATVALUE = '_ATVALUE' - c.ATPREFIX = '_ATPREFIX' - c.object2Flatten = function (a, b, e, f, d) { - var r = {}, - q = '', - m = '', - p = [], - g = '', - s = 0, - k - for (k in a) - if (a.hasOwnProperty(k)) - if ( - ((r = c.splitProtocolAndKey(k, d)), - (q = r.prefix || f || ''), - (m = (b ? b + '_' : '') + r.key), - c.isObject(a[k])) - ) - c.object2Flatten(a[k], m, e, q, d) - else { - p = m.split('_') - g = '' - for (s = 0; s < p.length; s++) - (r = c.splitProtocolAndKey(p[s], d)), - (q = r.prefix || q), - (g += r.key + (s < p.length - 1 ? '_' : '')) - m = g || m - e[m] = e[m] || {} - e[m][c.ATVALUE] = a[k] - e[m][c.ATPREFIX] = q - } - } - c.flatten2Object = function (a, b, e) { - b = b.split('_') - var f, d - for (d = 0; d < b.length - 1; d++) - (f = b[d]), a[f] || (a[f] = {}), (a = a[f]) - if (a.hasOwnProperty(c.ATVALUE)) { - f = a[c.ATVALUE] - var r = a[c.ATPREFIX] - delete a[c.ATVALUE] - delete a[c.ATPREFIX] - a.$ = {} - a.$[c.ATVALUE] = f - a.$[c.ATPREFIX] = r - } - e = c.cloneSimpleObject(e) - a[b[d]] ? (a[b[d]].$ = e) : (a[b[d]] = e) - } - c.getFormattedObject = function (a) { - var b = {}, - e, - f - for (f in a) - a.hasOwnProperty(f) && - (a[f].hasOwnProperty(c.ATVALUE) - ? ((e = a[f][c.ATPREFIX] ? a[f][c.ATPREFIX] + ':' + f : f), - (b[e] = a[f][c.ATVALUE])) - : (b[f] = c.getFormattedObject(a[f]))) - return b - } - c.completeFstLevelObj = function (a, b, e) { - if (a) { - if (b) - for (var f in b) !b.hasOwnProperty(f) || (a[f] && !e) || (a[f] = b[f]) - } else a = b - return a - } - c.getObjectKeys = function (a) { - var b = [], - e - for (e in a) a.hasOwnProperty(e) && b.push(e) - return b - } - c.objectToLowercase = function (a) { - var b = {}, - e - for (e in a) - a.hasOwnProperty(e) && - (c.isObject(a[e]) - ? (b[e.toLowerCase()] = c.objectToLowercase(a[e])) - : (b[e.toLowerCase()] = a[e])) - return b - } - c.splitProtocolAndKey = function (a, b) { - var e, f - 2 > a.length || ':' !== a[1] - ? ((e = ''), (f = a)) - : 4 > a.length || ':' !== a[3] - ? ((e = a.substring(0, 1)), (f = a.substring(2, a.length))) - : ((e = a.substring(0, 3)), (f = a.substring(4, a.length))) - b && ((e = e.toLowerCase()), (f = f.toLowerCase())) - return { prefix: e, key: f } - } - c.jsonSerialize = function (b) { - try { - return 'undefined' !== typeof JSON && JSON.stringify - ? JSON.stringify(b) - : a(b) - } catch (d) { - return null - } - } - c.jsonParse = function (a) { - try { - return 'undefined' !== typeof JSON && JSON.parse - ? JSON.parse(a + '') - : k(a) - } catch (b) { - return null - } - } - c.trim = function (a) { - try { - return String.prototype.trim - ? a.trim() - : a.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') - } catch (b) { - return a - } - } - c.arrayIndexOf = function (a, b) { - if (Array.prototype.indexOf) { - var e = -1 - 'undefined' !== typeof a.indexOf(b) && (e = a.indexOf(b)) - return e - } - return function (a) { - if (null == this) throw new TypeError() - var b = Object(this), - e = b.length >>> 0 - if (0 === e) return -1 - var h = 0 - 1 < arguments.length && - ((h = Number(arguments[1])), - h != h - ? (h = 0) - : 0 != h && - Infinity != h && - -Infinity != h && - (h = (0 < h || -1) * Math.floor(Math.abs(h)))) - if (h >= e) return -1 - for (h = 0 <= h ? h : Math.max(e - Math.abs(h), 0); h < e; h++) - if (h in b && b[h] === a) return h - return -1 - }.apply(a, [b]) - } - c.uuid = function () { - function a(f) { - var h = Math.random() - try { - e && (h = b.getRandomValues(new Uint32Array(1))[0] / Math.pow(2, 32)) - } catch (d) {} - return Math.floor((9 * h + 1) * Math.pow(10, f - 1)) - } - var b = window.crypto || window.msCrypto, - e = null !== b && 'object' === typeof b - return { - v4: function () { - try { - if (e) - return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace( - /[018]/g, - function (a) { - return ( - a ^ - (b.getRandomValues(new Uint32Array(1))[0] & (15 >> (a / 4))) - ).toString(16) - } - ) - } catch (a) {} - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( - /[xy]/g, - function (a) { - var b = (16 * Math.random()) | 0 - return ('x' === a ? b : (b & 3) | 8).toString(16) - } - ) - }, - num: function (b) { - var e = new Date(), - d = function (a) { - a -= 100 * Math.floor(a / 100) - return 10 > a ? '0' + a : String(a) - } - return ( - d(e.getHours()) + - '' + - d(e.getMinutes()) + - '' + - d(e.getSeconds()) + - '' + - a(b - 6) - ) - }, - } - } - c.isPreview = function () { - return window.navigator && 'preview' === window.navigator.loadPurpose - } - c.isPrerender = function (a) { - var b, - e = !1, - f = ['webkit', 'ms'] - if ('prerender' === document.visibilityState) b = 'visibilitychange' - else - for (var d = 0; d < f.length; d++) - 'prerender' === document[f[d] + 'VisibilityState'] && - (b = f[d] + 'visibilitychange') - if ('undefined' !== typeof b) { - var r = function (f) { - a(f) - c.removeEvtListener(document, b, r) - } - c.addEvtListener(document, b, r) - e = !0 - } - return e - } - c.addEvtListener = function (a, b, e) { - a.addEventListener - ? a.addEventListener(b, e, !1) - : a.attachEvent && a.attachEvent('on' + b, e) - } - c.removeEvtListener = function (a, b, e) { - a.removeEventListener - ? a.removeEventListener(b, e, !1) - : a.detachEvent && a.detachEvent('on' + b, e) - } - c.hashcode = function (a) { - var b = 0 - if (0 === a.length) return b - for (var e = 0; e < a.length; e++) - var f = a.charCodeAt(e), b = (b << 5) - b + f, b = b | 0 - return b - } - c.setLocation = function (a) { - var b = a.location - a = window[a.target] || window - b && (a.location.href = b) - } - c.dispatchCallbackEvent = function (a) { - var b - if ('function' === typeof window.Event) b = new Event('ATCallbackEvent') - else - try { - ;(b = document.createEvent('Event')), - b.initEvent && b.initEvent('ATCallbackEvent', !0, !0) - } catch (e) {} - b && - 'function' === typeof document.dispatchEvent && - ((b.name = a), document.dispatchEvent(b)) - } - c.addCallbackEvent = function (a) { - c.addEvtListener(document, 'ATCallbackEvent', a) - } - c.removeCallbackEvent = function (a) { - c.removeEvent('ATCallbackEvent', a) - } - ;(function () { - function a(b, e) { - e = e || { bubbles: !1, cancelable: !1, detail: void 0 } - var f - try { - ;(f = document.createEvent('CustomEvent')), - f.initCustomEvent(b, e.bubbles, e.cancelable, e.detail) - } catch (d) {} - return f - } - 'function' === typeof window.CustomEvent - ? (window.ATCustomEvent = window.CustomEvent) - : ('function' === typeof window.Event && - (a.prototype = window.Event.prototype), - (window.ATCustomEvent = a)) - })() - c.addEvent = function (a, b, e, f) { - c[a] = new ATCustomEvent(a, { detail: { name: b, id: e } }) - c.addEvtListener(document, a, f) - } - c.removeEvent = function (a, b) { - c.removeEvtListener(document, a, b) - } - c.dispatchEvent = function (a, b) { - c[a] = c[a] || new ATCustomEvent(a, { detail: { name: b, id: -1 } }) - try { - document.dispatchEvent(c[a]) - } catch (e) {} - } - c.privacy = new (function () { - function a(b, f) { - var e = [], - d, - h - d = {} - for (var l = 0; l < b.length; l++) { - d = {} - c.object2Flatten(b[l], null, d, null, !0) - for (var s in d) - d.hasOwnProperty(s) && -1 === c.arrayIndexOf(f, s) && delete d[s] - if (!c.isEmptyObject(d)) { - h = {} - for (var g in d) d.hasOwnProperty(g) && c.flatten2Object(h, g, d[g]) - d = c.getFormattedObject(h) - e.push(d) - } - } - return e - } - function b(a) { - for (var f = [], d = {}, e = 0; e < a.length; e++) - if ('string' === typeof a[e]) f.push(a[e]) - else - for (var h in a[e]) - a[e].hasOwnProperty(h) && - (f.push(h), (d[h] = (d[h] || []).concat(a[e][h]))) - return { keys: f, values: d } - } - var e = this, - f = { storageParams: null, bufferParams: null } - e.CONSENTNO = 'Consent-NO' - e.ALL = '*' - e.testStorageParam = function (a, b) { - var d - if (f.storageParams instanceof Array) { - for (var h, c = f.storageParams.length - 1; 0 <= c; c--) - if (((h = f.storageParams[c]), 'string' === typeof h)) { - if (h === a || h === e.ALL) return { toSetInStorage: !0 } - } else { - a: { - d = a - var l = b, - s = void 0, - g = void 0 - for (g in h) - if (h.hasOwnProperty(g) && d === g) { - if (!l) { - d = !0 - break a - } - s = [] - h[g] instanceof Array ? (s = h[g]) : s.push(h[g]) - for (var k = 0; k < s.length; k++) - if (s[k] === l) { - d = !0 - break a - } - } - d = !1 - } - if (d) return { toSetInStorage: !0 } - } - return { toSetInStorage: !1 } - } - return { toSetInStorage: !0 } - } - e.processStorageParams = function (a, d, h) { - if (h) { - h = h() - var m = b(f.storageParams) - if (m.keys[0] !== e.ALL) - for (var p in h) - if (h.hasOwnProperty(p)) - if (-1 === c.arrayIndexOf(m.keys, p)) a && a(p) - else if (c.isObject(h[p])) { - var g = p, - s = h[p].val, - k = m.values[p], - v = a, - w = d - if ('undefined' !== typeof k) { - var x = [] - k instanceof Array ? (x = k) : x.push(k) - k = void 0 - for (k in s) - s.hasOwnProperty(k) && - -1 === c.arrayIndexOf(x, k) && - v && - v([g, k]) - v && w && c.isEmptyObject(w(g)) && v(g) - } - } - } - } - e.testBufferParam = function (b, d) { - var l, m - if (f.bufferParams instanceof Array) { - for (var p, g = f.bufferParams.length - 1; 0 <= g; g--) - if (((p = f.bufferParams[g]), 'string' === typeof p)) { - if (p === b || p === e.ALL) return { toSetInBuffer: !0, value: d } - } else { - a: { - l = b - m = d - if (c.isObject(p)) { - var s = void 0, - k = [], - v = !1, - w = (s = s = void 0) - for (w in p) - if ( - p.hasOwnProperty(w) && - l === w && - ((s = m), - 'string' === typeof s && (s = c.jsonParse(s) || s), - 'object' === typeof s) - ) { - s instanceof Array ? ((k = s), (v = !0)) : k.push(s) - s = a(k, p[w]) - 0 === s.length - ? ((l = !1), (m = void 0)) - : ((s = v ? s : s[0]), - (l = !0), - (m = c.jsonSerialize(s))) - break a - } - } - l = !1 - m = void 0 - } - if (l) return { toSetInBuffer: !0, value: m } - } - return { toSetInBuffer: !1 } - } - return { toSetInBuffer: !0, value: d } - } - e.processBufferParams = function (d, g, q) { - if (g) { - g = g() - var m = b(f.bufferParams) - if (m.keys[0] !== e.ALL) - for (var p in g) - if (g.hasOwnProperty(p)) - if (-1 === c.arrayIndexOf(m.keys, p)) d && d(p) - else { - var k = p, - s = g[p], - t = m.values[p], - v = d, - w = q - if ('undefined' !== typeof t) { - var x = [], - y = s._value, - B = [], - A = !1, - C = void 0, - C = void 0 - t instanceof Array ? (x = t) : x.push(t) - 'string' === typeof y && (y = c.jsonParse(y) || y) - 'object' === typeof y && - (y instanceof Array ? ((B = y), (A = !0)) : B.push(y), - (C = a(B, x)), - 0 === C.length - ? v && v(k) - : ((C = A ? C : C[0]), - w && w(k, c.jsonSerialize(C), s._options))) - } - } - } - } - e.setParameters = function (a) { - f = a - } - e.getParameters = function () { - return f - } - e.resetParameters = function () { - f = { storageParams: null, bufferParams: null } - } - })() - c.optedOut = null - c.addOptOutEvent = function (a, b) { - c.addEvent('ATOptOutEvent', 'clientsideuserid', a, b) - } - c.removeOptOutEvent = function (a) { - c.removeEvent('ATOptOutEvent', a) - } - c.dispatchOptOutEvent = function (a) { - c.optedOut = a - c.dispatchEvent('ATOptOutEvent', 'clientsideuserid') - } - c.userOptedOut = function () { - c.dispatchOptOutEvent(!0) - } - c.userOptedIn = function () { - c.dispatchOptOutEvent(!1) - } - c.isOptedOut = function () { - if (null === c.optedOut) { - var a - a: { - a = null - c.isLocalStorageAvailable() && (a = localStorage.getItem('atuserid')) - if (null === a) { - var b = /(?:^| )atuserid=([^;]+)/.exec(document.cookie) - null !== b && (a = b[1]) - } - if (null !== a) - try { - a = decodeURIComponent(a) - } catch (d) {} - if ( - a && - ((a = c.jsonParse(a) || c.jsonParse(c.Base64.decode(a))), - null !== a) - ) { - a = 'OPT-OUT' === a.val - break a - } - a = !1 - } - c.optedOut = a - } - return !!c.optedOut - } - c.consentReceived = function (a) { - c.consent = !!a - } - c.consent = !0 - c.isTabOpeningAction = function (a) { - var b = !1 - a && - (a.ctrlKey || - a.shiftKey || - a.metaKey || - (a.button && 1 === a.button)) && - (b = !0) - return b - } - c.CLICKS_REDIRECTION = 'redirection' - c.CLICKS_FORM = 'form' - c.CLICKS_MAILTO = 'mailto' - } - ATInternet.Utils = new Utils() - var BuildManager = function (a) { - var g = this, - k = 0, - c = 0, - b = ['dz'], - d = '', - h = function (a, b, d, f, e, h, c) { - a = '&' + a + '=' - return { - param: a, - paramSize: a.length, - str: b, - strSize: b.length, - truncate: d, - multihit: f, - separator: e || '', - encode: h, - last: c, - } - }, - l = function (a, b) { - var d = '', - f = 0, - e = 0, - h = 0, - f = -1, - c = null, - l = null, - n - for (n in a) - a.hasOwnProperty(n) && - (c = a[n]) && - ((f = b - e), - c.last && null !== l - ? (l[n] = c) - : c.strSize + c.paramSize <= f - ? ((d += c.param + c.str), (e += c.paramSize + c.strSize)) - : ((l = l || {}), - (l[n] = c), - c.truncate && - ((h = f - c.paramSize), - c.separator && - ((f = c.str.substring(0, f)), - (f = c.encode - ? f.lastIndexOf(encodeURIComponent(c.separator)) - : f.lastIndexOf(c.separator)), - 0 < f && (h = f)), - (d += c.param + c.str.substring(0, h)), - (e += c.paramSize + c.str.substring(0, h).length), - (l[n].str = c.str.substring(h, c.strSize)), - (l[n].strSize = l[n].str.length)))) - return [d, l] - }, - e = function (d, f, e) { - var n = '', - g = function (f) { - if (f === {}) return [] - var d = [], - e - e = {} - var m = !1, - g = void 0, - p, - s, - r, - k, - q, - v, - t, - w, - D = '', - z - for (z in f) - if (f.hasOwnProperty(z)) - if ( - ((v = q = k = r = !1), - (p = f[z]._value), - (s = f[z]._options || {}), - 'boolean' === typeof s.encode && (r = s.encode), - 'function' === typeof p && (p = p()), - (p = - p instanceof Array - ? p.join(s.separator || ',') - : 'object' === typeof p - ? ATInternet.Utils.jsonSerialize(p) - : 'undefined' === typeof p - ? 'undefined' - : p.toString()), - r && (p = encodeURIComponent(p)), - -1 < ATInternet.Utils.arrayIndexOf(b, z) - ? (k = !0) - : 'boolean' === typeof s.truncate && (k = s.truncate), - 'boolean' === typeof s.multihit && (q = s.multihit), - 'boolean' === typeof s.last && (v = s.last), - (p = h(z, p, k, q, s.separator, r, v)), - q) - ) - (c -= p.paramSize + p.strSize), (D += p.param + p.str) - else if (v) - p.paramSize + p.strSize > c && - ((p.str = p.str.substring(0, c - p.paramSize)), - (p.strSize = p.str.length)), - (t = z), - (w = p) - else if ( - ((e[z] = p), - e[z].paramSize + e[z].strSize > c && !e[z].truncate) - ) { - a.emit('Tracker:Hit:Build:Error', { - lvl: 'ERROR', - msg: 'Too long parameter: "' + e[z].param + '"', - details: { value: e[z].str }, - }) - m = !0 - g = z - break - } - t && (e[t] = w) - e = [e, m, g, D] - f = e[0] - m = e[1] - n = e[3] - m && - ((e = e[2]), - (f = f[e]), - (f.str = f.str.substring(0, c - f.paramSize)), - (f.strSize = f.str.length), - (m = {}), - (m.mherr = h('mherr', '1', !1, !1, '', !1, !1)), - (m[e] = f), - (f = m)) - f = l(f, c) - if (null === f[1]) d = f[0] - else - for (d.push(f[0]); null !== f[1]; ) - (f = l(f[1], c)), d.push(f[0]) - return d - }, - k = '' - a.buffer.presentInFilters(f, 'hitType') || - (f = a.buffer.addInFilters(f, 'hitType', ['page'])) - f = a.buffer.addInFilters(f, 'hitType', ['all']) - var r, w - if (ATInternet.Utils.isObject(d)) { - f = a.buffer.addInFilters(f, 'permanent', !0) - f = a.buffer.get(f, !0) - for (r in d) - d.hasOwnProperty(r) && - ((k = {}), - d[r] && - 'object' === typeof d[r] && - d[r].hasOwnProperty('_value') - ? ((w = d[r]._value), - d[r].hasOwnProperty('_options') && (k = d[r]._options)) - : (w = d[r]), - (w = ATInternet.Utils.privacy.testBufferParam(r, w)), - w.toSetInBuffer && (f[r] = { _value: w.value, _options: k })) - k = g(f) - } else - for (r in ((f = a.buffer.get(f, !0)), (k = g(f)), f)) - f.hasOwnProperty(r) && - ((f[r]._options && f[r]._options.permanent) || a.buffer.del(r)) - e && e(k, n) - } - g.getCollectDomain = function () { - var b = '', - b = a.getConfig('logSSL') || a.getConfig('log'), - f = a.getConfig('domain') - return (b = - b && f - ? b + '.' + f - : a.getConfig('collectDomainSSL') || a.getConfig('collectDomain')) - } - var f = function (b) { - var f = '', - d = a.getConfig('baseURL') - if (d) f = d - else { - var d = g.getCollectDomain(), - e = a.getConfig('pixelPath'), - e = e || '/' - '/' !== e.charAt(0) && (e = '/' + e) - d && - (f = (a.getConfig('forceHttp') ? 'http://' : 'https://') + d + e) - } - d = a.getConfig('site') - f && d - ? b && b(null, f + '?s=' + d) - : b && b({ message: 'Config error' }) - }, - n = function (a, b, d) { - f(function (f, h) { - f - ? d && d(f) - : ((c = k - (h.length + 27)), - e(a, b, function (a, b) { - var f = [], - e = ATInternet.Utils.uuid().num(13) - if (a instanceof Array) - for (var c = 1; c <= a.length; c++) - f.push( - h + b + '&mh=' + c + '-' + a.length + '-' + e + a[c - 1] - ) - else f.push(h + b + a) - d && d(null, f) - })) - }) - }, - r = function (b, f, d, e, h, c, l) { - return (function () { - return function (n) { - a.emit(b, { - lvl: h, - details: { - hit: f, - method: d, - event: n, - isMultiHit: c, - elementType: l, - }, - }) - e && e() - } - })() - } - g.send = function (b, f, d, e, h) { - n(b, f, function (b, f) { - if (b) - a.emit('Tracker:Hit:Build:Error', { - lvl: 'ERROR', - msg: b.message, - details: {}, - }), - d && d() - else for (var c = 0; c < f.length; c++) g.sendUrl(f[c], d, e, h) - }) - } - k = Math.max(a.getConfig('maxHitSize') || 0, 2e3) - c = Math.max(a.getConfig('maxHitSize') || 0, 2e3) - d = a.getConfig('requestMethod') - g.sendUrl = function (b, f, e, h) { - var c = -1 < b.indexOf('&mh=') - e = e || d - ATInternet.Utils.isOptedOut() && !a.getConfig('sendHitWhenOptOut') - ? r('Tracker:Hit:Sent:NoTrack', b, e, f, 'INFO', c, h)() - : 'POST' === e && ATInternet.Utils.isBeaconMethodAvailable() - ? ((h = 'Tracker:Hit:Sent:Error'), - (e = 'ERROR'), - window.navigator.sendBeacon(b, null) && - ((h = 'Tracker:Hit:Sent:Ok'), (e = 'INFO')), - r(h, b, 'POST', f, e, c, '')()) - : ((e = new Image()), - (e.onload = r('Tracker:Hit:Sent:Ok', b, 'GET', f, 'INFO', c, h)), - (e.onerror = r( - 'Tracker:Hit:Sent:Error', - b, - 'GET', - f, - 'ERROR', - c, - h - )), - (e.src = b)) - } - }, - TriggersManager = function () { - function a(a, d, h) { - for (var c = [], e = 0; e < a.length; e++) - a[e].callback(d, h), a[e].singleUse || c.push(a[e]) - return c - } - function g(a, d, h, c) { - var e = a.shift() - if ('*' === e) - return ( - (d['*'] = d['*'] || []), - d['*'].push({ callback: h, singleUse: c }), - d['*'].length - 1 - ) - if (0 === a.length) return g([e, '*'], d, h, c) - d['*'] = d['*'] || [] - d[e] = d[e] || {} - return g(a, d[e], h, c) - } - function k(b, d, h, c) { - var e = d.shift() - '*' !== e && - (0 === d.length - ? k(b, [e, '*'], h, c) - : h[e] && ((h[e]['*'] = a(h[e]['*'], b, c)), k(b, d, h[e], c))) - } - var c = {} - this.on = function (a, d, h) { - h = h || !1 - return g(a.split(':'), c, d, h) - } - this.emit = function (b, d) { - c['*'] && (c['*'] = a(c['*'], b, d)) - k(b, b.split(':'), c, d) - } - }, - PluginsManager = function (a) { - var g = {}, - k = {}, - c = 0, - b = {}, - d = 0, - h = function (a) { - var b = !1 - g[a] && (b = !0) - return b - }, - l = (this.unload = function (b) { - h(b) - ? ((g[b] = void 0), - a.emit('Tracker:Plugin:Unload:' + b + ':Ok', { lvl: 'INFO' })) - : a.emit('Tracker:Plugin:Unload:' + b + ':Error', { - lvl: 'ERROR', - msg: 'not a known plugin', - }) - return a - }), - e = (this.load = function (b, f) { - 'function' === typeof f - ? 'undefined' === typeof a.getConfig.plgAllowed || - 0 === a.getConfig.plgAllowed.length || - -1 < a.getConfig.plgAllowed.indexOf(b) - ? ((g[b] = new f(a)), - k[b] && - h(b) && - ((k[b] = !1), - c--, - h(b + '_ll') && l(b + '_ll'), - 0 === c && - a.emit('Tracker:Plugin:Lazyload:File:Complete', { - lvl: 'INFO', - msg: 'LazyLoading triggers are finished', - })), - a.emit('Tracker:Plugin:Load:' + b + ':Ok', { lvl: 'INFO' })) - : a.emit('Tracker:Plugin:Load:' + b + ':Error', { - lvl: 'ERROR', - msg: 'Plugin not allowed', - details: {}, - }) - : a.emit('Tracker:Plugin:Load:' + b + ':Error', { - lvl: 'ERROR', - msg: 'not a function', - details: { obj: f }, - }) - return a - }), - f = (this.isLazyloading = function (a) { - return a ? !0 === k[a] : 0 !== c - }), - n = function (a) { - return !h(a) && !f(a) && h(a + '_ll') - }, - r = function (b) { - k[b] = !0 - c++ - ATInternet.Utils.loadScript({ - url: a.getConfig('lazyLoadingPath') + b + '.js', - }) - }, - q = function (a) { - return n(a) ? (r(a), !0) : !1 - }, - m = function (a) { - b[a] ? b[a]++ : (b[a] = 1) - d++ - }, - p = function (a, b, f, d) { - var e = null - b = b.split('.') - h(a) && - g[a][b[0]] && - (e = - 1 < b.length && g[a][b[0]][b[1]] - ? g[a][b[0]][b[1]].apply(g[a], f) - : g[a][b[0]].apply(g[a], f)) - d && d(e) - }, - u = function (f, e, h, c) { - m(f) - a.onTrigger( - 'Tracker:Plugin:Load:' + f + ':Ok', - function () { - p(f, e, h, function (e) { - b[f]-- - d-- - 0 === d && - a.emit('Tracker:Plugin:Lazyload:Exec:Complete', { - lvl: 'INFO', - msg: 'All exec waiting for lazyloading are done', - }) - c && c(e) - }) - }, - !0 - ) - }, - s = function (a) { - for (var b = { mcount: 0, plugins: {} }, f = 0; f < a.length; f++) - g.hasOwnProperty(a[f]) || (b.mcount++, (b.plugins[a[f]] = !0)) - return b - } - this.isExecWaitingLazyloading = function () { - return 0 !== d - } - a.exec = this.exec = function (a, b, d, e) { - n(a) ? (u(a, b, d, e), r(a)) : f(a) ? u(a, b, d, e) : p(a, b, d, e) - } - this.waitForDependencies = function (b, f) { - var d = s(b) - if (0 === d.mcount) - a.emit('Tracker:Plugin:Dependencies:Loaded', { - lvl: 'INFO', - details: { dependencies: b }, - }), - f() - else - for (var e in d.plugins) - d.plugins.hasOwnProperty(e) && - (a.emit('Tracker:Plugin:Dependencies:Error', { - lvl: 'WARNING', - msg: 'Missing plugin ' + e, - }), - a.onTrigger( - 'Tracker:Plugin:Load:' + e, - function (a, b) { - var e = a.split(':'), - h = e[3] - 'Ok' === e[4] && - ((d.plugins[h] = !1), d.mcount--, 0 === d.mcount && f()) - }, - !0 - ), - q(e)) - } - this.init = function () { - for (var a in ATInternet.Tracker.pluginProtos) - ATInternet.Tracker.pluginProtos.hasOwnProperty(a) && - e(a, ATInternet.Tracker.pluginProtos[a]) - } - }, - CallbacksManager = function (a) { - var g = this, - k = {}, - c = function (b) { - if (b.name) { - var d = !0, - h = a.getConfig('callbacks') - 'undefined' !== typeof h && - (h.include instanceof Array && - -1 === ATInternet.Utils.arrayIndexOf(h.include, b.name) && - (d = !1), - h.exclude instanceof Array && - -1 !== ATInternet.Utils.arrayIndexOf(h.exclude, b.name) && - (d = !1)) - ATInternet.Callbacks && - ATInternet.Callbacks.hasOwnProperty(b.name) && - ((h = {}), - (h[b.name] = { function: ATInternet.Callbacks[b.name] }), - d && g.load(b.name, h[b.name]['function']), - ATInternet.Tracker.callbackProtos[b.name] || - (ATInternet.Tracker.callbackProtos[b.name] = h[b.name])) - } - } - g.load = function (b, d) { - 'function' === typeof d - ? (new d(a), - a.emit('Tracker:Callback:Load:' + b + ':Ok', { - lvl: 'INFO', - details: { obj: d }, - })) - : a.emit('Tracker:Callback:Load:' + b + ':Error', { - lvl: 'ERROR', - msg: 'not a function', - details: { obj: d }, - }) - return a - } - g.init = function () { - if (a.getConfig('activateCallbacks')) { - var b = a.getConfig('callbacks') - if ('undefined' !== typeof b && b.include instanceof Array) - for (var d = 0; d < b.include.length; d++) - ATInternet.Callbacks && - ATInternet.Callbacks.hasOwnProperty(b.include[d]) && - ((k[b.include[d]] = { - function: ATInternet.Callbacks[b.include[d]], - }), - ATInternet.Tracker.callbackProtos[b.include[d]] || - (ATInternet.Tracker.callbackProtos[b.include[d]] = - k[b.include[d]])) - else - for (d in ATInternet.Callbacks) - ATInternet.Callbacks.hasOwnProperty(d) && - ((k[d] = { function: ATInternet.Callbacks[d] }), - ATInternet.Tracker.callbackProtos[d] || - (ATInternet.Tracker.callbackProtos[d] = k[d])) - if ('undefined' !== typeof b && b.exclude instanceof Array) - for (d = 0; d < b.exclude.length; d++) delete k[b.exclude[d]] - for (var h in k) - k.hasOwnProperty(h) && k[h] && g.load(h, k[h]['function']) - ATInternet.Utils.addCallbackEvent(c) - } - } - g.removeCallbackEvent = function () { - ATInternet.Utils.removeCallbackEvent(c) - } - }, - BufferManager = function (a) { - var g = this, - k = {} - g.set = function (a, b, c) { - b = ATInternet.Utils.privacy.testBufferParam(a, b) - b.toSetInBuffer && - ((c = c || {}), - (c.hitType = c.hitType || ['page']), - (k[a] = { _value: b.value, _options: c })) - } - var c = function (a, b, c) { - return (a = ATInternet.Utils.cloneSimpleObject(a[b])) && !c - ? a._value - : a - }, - b = function h(a, b) { - if (!(a && b instanceof Array && a instanceof Array)) return [] - if (0 === a.length) return b - var f = a[0], - c, - g = [], - q = ATInternet.Utils.cloneSimpleObject(a) - q.shift() - for (var m = 0; m < b.length; m++) - if ('object' !== typeof f[1]) - k[b[m]] && k[b[m]]._options[f[0]] === f[1] && g.push(b[m]) - else { - c = f[1].length - for (var p = 0; p < c; p++) - if ( - k[b[m]] && - k[b[m]]._options[f[0]] instanceof Array && - 0 <= - ATInternet.Utils.arrayIndexOf( - k[b[m]]._options[f[0]], - f[1][p] - ) - ) { - g.push(b[m]) - break - } - } - return h(q, g) - } - g.get = function (a, g) { - var e = {} - if ('string' === typeof a) e = c(k, a, g) - else - for ( - var f = b(a, ATInternet.Utils.getObjectKeys(k)), n = 0; - n < f.length; - n++ - ) - e[f[n]] = c(k, f[n], g) - return e - } - g.presentInFilters = function (a, b) { - return a && 0 !== a.length - ? a[0][0] === b - ? !0 - : g.presentInFilters(a.slice(1), b) - : !1 - } - g.addInFilters = function (a, b, e, f) { - if (!a || 0 === a.length) return f ? [] : [[b, e]] - var c = a[0][0], - r = a[0][1] - c === b && - (r instanceof Array && - -1 === ATInternet.Utils.arrayIndexOf(r, e[0]) && - r.push(e[0]), - (f = !0)) - return [[c, r]].concat(g.addInFilters(a.slice(1), b, e, f)) - } - g.del = function (a) { - k[a] = void 0 - } - g.clear = function () { - k = {} - } - }, - PropertiesManager = function (a) { - var g = this, - k = {} - g.setProp = function (a, b, d) { - 'undefined' !== typeof a && (k[a] = { value: b, persistent: !!d }) - } - g.setProps = function (a, b) { - if (ATInternet.Utils.isObject(a)) - for (var d in a) a.hasOwnProperty(d) && g.setProp(d, a[d], b) - } - g.delProp = function (c, b) { - 'undefined' !== typeof k[c] && delete k[c] - !b && a.delParam(c.toLowerCase()) - } - g.delProps = function (a) { - for (var b in k) k.hasOwnProperty(b) && g.delProp(b, a) - } - g.getProp = function (a) { - k = k || {} - return k[a] - } - g.getProps = function () { - return k - } - }, - Tag = function (a, g, k) { - g = g || {} - var c = this - c.version = '5.27.0' - var b = ATInternet.Utils.cloneSimpleObject(g) - c.triggers = new TriggersManager(c) - c.emit = c.triggers.emit - c.onTrigger = c.triggers.on - var d = ATInternet.Utils.cloneSimpleObject(dfltGlobalCfg) || {}, - h - for (h in a) a.hasOwnProperty(h) && (d[h] = a[h]) - c.getConfig = function (a) { - return d[a] - } - c.setConfig = function (a, b, h) { - ;(void 0 !== d[a] && h) || - (c.emit('Tracker:Config:Set:' + a, { - lvl: 'INFO', - details: { bef: d[a], aft: b }, - }), - (d[a] = b)) - } - c.configPlugin = function (a, b, h) { - d[a] = d[a] || {} - for (var g in b) - b.hasOwnProperty(g) && void 0 === d[a][g] && (d[a][g] = b[g]) - h && - (h(d[a]), - c.onTrigger('Tracker:Config:Set:' + a, function (a, b) { - h(b.details.aft) - })) - return d[a] - } - c.getAllContext = function () { - return b - } - c.getContext = function (a) { - return b[a] - } - c.setContext = function (a, f) { - c.emit('Tracker:Context:Set:' + a, { - lvl: 'INFO', - details: { bef: b[a], aft: f }, - }) - b[a] = f - } - c.delContext = function (a, f) { - c.emit('Tracker:Context:Deleted:' + a + ':' + f, { - lvl: 'INFO', - details: { key1: a, key2: f }, - }) - if (a) - b.hasOwnProperty(a) && - (f - ? b[a] && b[a].hasOwnProperty(f) && (b[a][f] = void 0) - : (b[a] = void 0)) - else if (f) - for (var d in b) - b.hasOwnProperty(d) && - b[d] && - b[d].hasOwnProperty(f) && - (b[d][f] = void 0) - } - c.plugins = new PluginsManager(c) - c.buffer = new BufferManager(c) - c.setParam = c.buffer.set - c.getParams = function (a) { - return c.buffer.get(a, !1) - } - c.getParam = c.buffer.get - c.delParam = c.buffer.del - c.builder = new BuildManager(c) - c.sendUrl = c.builder.sendUrl - c.callbacks = new CallbacksManager(c) - c.properties = new PropertiesManager(c) - c.setProp = c.properties.setProp - c.setProps = c.properties.setProps - c.delProp = c.properties.delProp - c.delProps = c.properties.delProps - c.getProp = c.properties.getProp - c.getProps = c.properties.getProps - c.sendHit = function (a, b, d, h, g) { - var m = c.getProps(), - p, - l - for (l in m) - m.hasOwnProperty(l) && - ((p = m[l].value), - m[l].persistent - ? c.setParam(l.toLowerCase(), p, { - permanent: !0, - hitType: ['all'], - encode: !0, - }) - : (ATInternet.Utils.isObject(a) - ? (a[l.toLowerCase()] = { - _value: p, - _options: { hitType: ['all'], encode: !0 }, - }) - : c.setParam(l.toLowerCase(), p, { - hitType: ['all'], - encode: !0, - }), - c.delProp(l, !0))) - c.builder.send(a, b, d, h, g) - } - ATInternet.Utils.privacy.resetParameters() - c.setParam( - 'ts', - function () { - return new Date().getTime() - }, - { permanent: !0, hitType: ['all'] } - ) - ;(c.getConfig('disableCookie') || c.getConfig('disableStorage')) && - c.setParam('idclient', ATInternet.Utils.privacy.CONSENTNO, { - permanent: !0, - hitType: ['all'], - }) - c.getConfig('medium') && - c.setParam('medium', c.getConfig('medium'), { - permanent: !0, - hitType: ['all'], - }) - if ( - c.getConfig('urlPropertyAuto') && - 'undefined' !== typeof window && - 'undefined' !== typeof window.location - ) { - h = (c.getConfig('urlPropertyQueryString') - ? window.location.href - : window.location.protocol + - '//' + - window.location.host + - window.location.pathname - ) - .replace(/[<>]/g, '') - .substring(0, 1600) - .replace(/&/g, '$') - var l = c.getContext('page') || {} - l.url = window.encodeURIComponent(h) - c.setContext('page', l) - c.setParam('page_url', h, { - permanent: !0, - hitType: 'page click publisher selfPromotion onSiteAdsClick onSiteAdsImpression InternalSearch mvtesting richmedia'.split( - ' ' - ), - }) - } - c.plugins.init() - c.callbacks.init() - c.emit('Tracker:Ready', { - lvl: 'INFO', - msg: 'Tracker initialized', - details: { tracker: c, args: { config: a, context: g, callback: k } }, - }) - k && k(c) - ATInternet.Tracker.instances.push(c) - } - ATInternet.Tracker.Tag = Tag - ATInternet.Tracker.instances = [] - ATInternet.Tracker.pluginProtos = {} - ATInternet.Tracker.addPlugin = function (a, g) { - g = g || ATInternet.Tracker.Plugins[a] - if (!ATInternet.Tracker.pluginProtos[a]) { - ATInternet.Tracker.pluginProtos[a] = g - for (var k = 0; k < ATInternet.Tracker.instances.length; k++) - ATInternet.Tracker.instances[k].plugins.load(a, g) - } - } - ATInternet.Tracker.delPlugin = function (a) { - if (ATInternet.Tracker.pluginProtos[a]) { - ATInternet.Tracker.pluginProtos[a] = void 0 - for (var g = 0; g < ATInternet.Tracker.instances.length; g++) - ATInternet.Tracker.instances[g].plugins.unload(a) - } - } - ATInternet.Tracker.callbackProtos = {} -}.call(window)) -;(function () { - var dfltPluginCfg = { - lifetime: 30, - lastPersistence: true, - domainAttribution: true, - enableUTMTracking: true, - UTMParameters: [ - 'utm_source', - 'utm_medium', - 'utm_campaign', - 'utm_term', - 'utm_content', - ], - querystringPrefix: 'at_', - } - var dfltGlobalCfg = { visitLifetime: 30, redirectionLifetime: 30 } - ATInternet.Tracker.Plugins.Campaigns = function (a) { - a.setConfig('visitLifetime', dfltGlobalCfg.visitLifetime, !0) - a.setConfig('redirectionLifetime', dfltGlobalCfg.redirectionLifetime, !0) - var g = {}, - k, - c - a.configPlugin('Campaigns', dfltPluginCfg || {}, function (a) { - g = a - }) - var b, - d, - h, - l, - e, - f, - n, - r, - q, - m, - p, - u, - s, - t = function () { - var b = function (a) { - var b = '' - a && - (b = - isNaN(a) && - -1 === a.search(/^\[(.*?)\]$/g) && - -1 === a.search(/^\d+\[(.*?)\]$/g) - ? '[' + a + ']' - : a) - return b - }, - f = function (a) { - var b = a - ;-1 !== a.search(/[-]/g) && - -1 === a.search(/^\[(.*?)\]$/g) && - (b = '[' + a + ']') - return b - }, - d = function (a) { - for (; '-' === a.charAt(a.length - 1); ) - a = a.substring(0, a.length - 1) - return a - } - this.SponsoredLinks = function () { - var e = { - google: 'goo', - yahoo: 'ysm', - miva: 'miv', - orange: 'wan', - msn: 'msn', - mirago: 'mir', - sklik: 'skl', - adfox: 'adf', - etarget: 'etg', - yandex: 'yan', - ebay: 'eba', - searchalliance: 'sal', - bing: 'bin', - naver: 'nav', - baidu: 'bdu', - qwant: 'qwt', - waze: 'waz', - amazon: 'amz', - }, - c = { search: 's', content: 'c' } - this.atMedium = 'sl' - this.atTerm = this.atNetwork = this.atVariant = this.atCreation = this.atPlatform = this.atCampaign = - '' - this.format = function () { - var a = 'sec', - h = b(this.atCampaign), - g = e[this.atPlatform] || f(this.atPlatform), - m = b(this.atCreation), - l = b(this.atVariant), - n = c[this.atNetwork] || f(this.atNetwork), - p = b(this.atTerm) - return d( - a + ('-' + h + '-' + g + '-' + m + '-' + l + '-' + n + '-' + p) - ) - } - this.setProperties = function (b) { - this.atCampaign = - a.utils.getQueryStringValue( - g.querystringPrefix + 'campaign', - b - ) || '' - this.atPlatform = - a.utils.getQueryStringValue( - g.querystringPrefix + 'platform', - b - ) || '' - this.atCreation = - a.utils.getQueryStringValue( - g.querystringPrefix + 'creation', - b - ) || '' - this.atVariant = - a.utils.getQueryStringValue(g.querystringPrefix + 'variant', b) || - '' - this.atNetwork = - a.utils.getQueryStringValue(g.querystringPrefix + 'network', b) || - '' - this.atTerm = - a.utils.getQueryStringValue(g.querystringPrefix + 'term', b) || '' - a.setContext('campaigns_events', { - $: this.atMedium, - campaign: this.atCampaign, - platform: this.atPlatform, - creation: this.atCreation, - variant: this.atVariant, - network: this.atNetwork, - term: this.atTerm, - }) - } - } - this.Email = function () { - var e = { acquisition: 'erec', retention: 'epr', promotion: 'es' } - this.atMedium = 'email' - this.atSendTime = this.atRecipientList = this.atRecipientId = this.atLink = this.atSendDate = this.atCreation = this.atCampaign = this.atEmailtype = - '' - this.format = function () { - var a = e[this.atEmailtype] || e.promotion, - c = b(this.atCampaign), - h = b(this.atCreation), - g = f(this.atSendDate), - m = b(this.atLink), - l = - f(this.atRecipientId) + - (this.atRecipientList ? '@' + f(this.atRecipientList) : ''), - n = f(this.atSendTime) - return d( - a + ('-' + c + '-' + h + '-' + g + '-' + m + '-' + l + '-' + n) - ) - } - this.setProperties = function (b) { - this.atEmailtype = - a.utils.getQueryStringValue( - g.querystringPrefix + 'emailtype', - b - ) || '' - this.atCampaign = - a.utils.getQueryStringValue( - g.querystringPrefix + 'campaign', - b - ) || '' - this.atCreation = - a.utils.getQueryStringValue( - g.querystringPrefix + 'creation', - b - ) || '' - this.atSendDate = - a.utils.getQueryStringValue( - g.querystringPrefix + 'send_date', - b - ) || '' - this.atLink = - a.utils.getQueryStringValue(g.querystringPrefix + 'link', b) || '' - this.atRecipientId = - a.utils.getQueryStringValue( - g.querystringPrefix + 'recipient_id', - b - ) || '' - this.atRecipientList = - a.utils.getQueryStringValue( - g.querystringPrefix + 'recipient_list', - b - ) || '' - this.atSendTime = - a.utils.getQueryStringValue( - g.querystringPrefix + 'send_time', - b - ) || '' - a.setContext('campaigns_events', { - $: this.atMedium, - emailtype: this.atEmailtype, - campaign: this.atCampaign, - creation: this.atCreation, - send_date: this.atSendDate, - link: this.atLink, - recipient_id: this.atRecipientId, - recipient_list: this.atRecipientList, - send_time: this.atSendTime, - }) - } - } - this.Affiliate = function () { - this.atMedium = 'affiliate' - this.atVariant = this.atCreation = this.atFormat = this.atIdentifier = this.atType = this.atCampaign = - '' - this.format = function () { - var a = 'al', - f = b(this.atCampaign), - e = b(this.atType), - c = b(this.atIdentifier), - h = b(this.atFormat), - g = b(this.atCreation), - m = b(this.atVariant) - return d( - a + ('-' + f + '-' + e + '-' + c + '-' + h + '-' + g + '-' + m) - ) - } - this.setProperties = function (b) { - this.atCampaign = - a.utils.getQueryStringValue( - g.querystringPrefix + 'campaign', - b - ) || '' - this.atType = - a.utils.getQueryStringValue(g.querystringPrefix + 'type', b) || '' - this.atIdentifier = - a.utils.getQueryStringValue( - g.querystringPrefix + 'identifier', - b - ) || '' - this.atFormat = - a.utils.getQueryStringValue(g.querystringPrefix + 'format', b) || - '' - this.atCreation = - a.utils.getQueryStringValue( - g.querystringPrefix + 'creation', - b - ) || '' - this.atVariant = - a.utils.getQueryStringValue(g.querystringPrefix + 'variant', b) || - '' - a.setContext('campaigns_events', { - $: this.atMedium, - campaign: this.atCampaign, - type: this.atType, - identifier: this.atIdentifier, - format: this.atFormat, - creation: this.atCreation, - variant: this.atVariant, - }) - } - } - this.Display = function () { - this.atMedium = 'display' - this.atDetailPlacement = this.atGeneralPlacement = this.atChannel = this.atFormat = this.atVariant = this.atCreation = this.atCampaign = - '' - this.format = function () { - var a = 'ad', - f = b(this.atCampaign), - e = b(this.atCreation), - c = b(this.atVariant), - h = b(this.atFormat), - g = b(this.atChannel), - m = b(this.atGeneralPlacement), - l = b(this.atDetailPlacement) - return d( - a + - ('-' + - f + - '-' + - e + - '-' + - c + - '-' + - h + - '-' + - g + - '-' + - m + - '-' + - l) - ) - } - this.setProperties = function (b) { - this.atCampaign = - a.utils.getQueryStringValue( - g.querystringPrefix + 'campaign', - b - ) || '' - this.atCreation = - a.utils.getQueryStringValue( - g.querystringPrefix + 'creation', - b - ) || '' - this.atVariant = - a.utils.getQueryStringValue(g.querystringPrefix + 'variant', b) || - '' - this.atFormat = - a.utils.getQueryStringValue(g.querystringPrefix + 'format', b) || - '' - this.atChannel = - a.utils.getQueryStringValue(g.querystringPrefix + 'channel', b) || - '' - this.atGeneralPlacement = - a.utils.getQueryStringValue( - g.querystringPrefix + 'general_placement', - b - ) || '' - this.atDetailPlacement = - a.utils.getQueryStringValue( - g.querystringPrefix + 'detail_placement', - b - ) || '' - a.setContext('campaigns_events', { - $: this.atMedium, - campaign: this.atCampaign, - creation: this.atCreation, - variant: this.atVariant, - format: this.atFormat, - channel: this.atChannel, - general_placement: this.atGeneralPlacement, - detail_placement: this.atDetailPlacement, - }) - } - } - this.Custom = function () { - this.atCustom4 = this.atCustom3 = this.atCustom2 = this.atCustom1 = this.atCampaign = this.atMedium = - '' - this.format = function () { - var a = '' - ;/\d+$/.test(this.atMedium) && (a = /\d+$/.exec(this.atMedium)[0]) - var a = 'cs' + a, - f = b(this.atCampaign), - e = b(this.atCustom1), - c = b(this.atCustom2), - h = b(this.atCustom3), - g = b(this.atCustom4) - return d(a + ('-' + f + '-' + e + '-' + c + '-' + h + '-' + g)) - } - this.setProperties = function (b) { - this.atMedium = - a.utils.getQueryStringValue(g.querystringPrefix + 'medium', b) || - '' - this.atCampaign = - a.utils.getQueryStringValue( - g.querystringPrefix + 'campaign', - b - ) || '' - this.atCustom1 = - a.utils.getQueryStringValue(g.querystringPrefix + 'custom1', b) || - '' - this.atCustom2 = - a.utils.getQueryStringValue(g.querystringPrefix + 'custom2', b) || - '' - this.atCustom3 = - a.utils.getQueryStringValue(g.querystringPrefix + 'custom3', b) || - '' - this.atCustom4 = - a.utils.getQueryStringValue(g.querystringPrefix + 'custom4', b) || - '' - a.setContext('campaigns_events', { - $: this.atMedium, - campaign: this.atCampaign, - custom1: this.atCustom1, - custom2: this.atCustom2, - custom3: this.atCustom3, - custom4: this.atCustom4, - }) - } - } - this.medium = { - sl: this.SponsoredLinks, - email: this.Email, - affiliate: this.Affiliate, - display: this.Display, - } - }, - v = function (b, f) { - var d = a.getContext('campaigns') || {} - d[b] = f - a.setContext('campaigns', d) - }, - w = function () { - var b = a.utils.getLocation(), - f = function () { - for (var f = g.UTMParameters, d, e = 0; e < f.length; e++) - (d = a.utils.getQueryStringValue(f[e], b)) && v(f[e], d) - } - ;(function () { - var f = a.utils.getQueryStringValue(g.querystringPrefix + 'medium', b) - if (f) { - var d = new t(), - f = - 'function' === typeof d.medium[f] - ? new d.medium[f]() - : new d.Custom() - f.setProperties(b) - n = f.format() - } else n = a.utils.getQueryStringValue('xtor', b) - r = a.utils.getQueryStringValue('xtdt', b) - q = a.utils.getQueryStringValue('xts', b) - })() - g.enableUTMTracking && f() - }, - x = function (b, f) { - var d = a.storage[c](b) - if (null !== d) return 'object' === typeof d && !(d instanceof Array) - a.storage[k](b, {}, f) - return !0 - } - ;(function () { - a.plugins.waitForDependencies(['Storage', 'Utils'], function () { - k = 'set' + (g.domainAttribution ? '' : 'Private') - c = 'get' + (g.domainAttribution ? '' : 'Private') - b = a.storage[c](['atredir', 'gopc']) - d = a.storage[c](['atredir', 'gopc_err']) - h = a.storage[c](['atredir', 'camp']) - a.storage.del(['atredir', 'gopc']) - a.storage.del(['atredir', 'gopc_err']) - a.storage.del(['atredir', 'camp']) - l = a.storage[c](['atsession', 'histo_camp']) - e = a.storage[c](['atreman', 'camp']) - f = a.storage[c](['atreman', 'date']) - w() - m = a.getContext('forcedCampaign') - p = !!a.getConfig('redirect') - u = !!(n && r && q) - s = !1 - if (u) { - var t = new Date().getTime() / 6e4 - s = - (!p && q !== a.getConfig('site')) || - 0 > t - r || - t - r >= a.getConfig('visitLifetime') - } - t = m || h || n - if ( - p && - t && - x('atredir', { path: '/', end: a.getConfig('redirectionLifetime') }) - ) { - a.storage[k](['atredir', 'camp'], t) - var B = (t = !1) - m || (h ? ((t = b), (B = d)) : ((t = u), (B = s))) - a.storage[k](['atredir', 'gopc'], t) - a.storage[k](['atredir', 'gopc_err'], B) - } - !p && - e && - (v('xtor', e), - (t = new Date().getTime() / 36e5), - (t = Math.floor(t - f)), - v('roinbh', 0 <= t ? t : 0)) - p || - ((t = null), - (t = h ? (b ? m || t : m || h) : u ? m : m || n || t), - l && l instanceof Array && -1 < l.indexOf(t) && (t = null), - t && v('xto', t)) - if (!p && !m) { - var A - h ? d && (A = h) : s && (A = n) - A && v('pgt', A) - } - if ( - !p && - (A = h ? m || h : m || n || null) && - !((!m && !h && u && s) || (!m && h && b && d)) - ) { - if ( - (!l || (l instanceof Array && 0 > l.indexOf(A))) && - x('atsession', { - path: '/', - session: 60 * a.getConfig('visitLifetime'), - }) - ) - a.storage[k](['atsession', 'histo_camp'], l && l.push(A) ? l : [A]) - ;(e && !g.lastPersistence) || - !x('atreman', { path: '/', session: 86400 * g.lifetime }) || - (a.storage[k](['atreman', 'camp'], A), - a.storage[k](['atreman', 'date'], new Date().getTime() / 36e5)) - } - a.emit('Campaigns:process:done', { lvl: 'INFO' }) - }) - })() - } - ATInternet.Tracker.addPlugin('Campaigns') -}.call(window)) -;(function () { - var dfltPluginCfg = {} - var dfltGlobalCfg = { storageMode: 'cookie' } - ATInternet.Tracker.Plugins.Storage = function (a) { - var g = this, - k = {}, - c = !1, - b = null - a.configPlugin('Storage', dfltPluginCfg || {}, function (a) { - k = a - 'localStorage' === k.storageMode && - (c = ATInternet.Utils.isLocalStorageAvailable()) - }) - var d = {}, - h = function (b) { - return a.getConfig('base64Storage') - ? ATInternet.Utils.Base64.encode(b) - : encodeURIComponent(b) - }, - l = function (b) { - return a.getConfig('base64Storage') - ? ATInternet.Utils.Base64.decode(b) - : decodeURIComponent(b) - }, - e = function () { - this.getData = function (a) { - var b = null - ;(a = - RegExp('(?:^| )' + a + '=([^;]+)').exec(document.cookie) || null) && - (b = l(a[1])) - return b - } - this.setData = function (b) { - var d = !1 - if (b.name && 'string' === typeof b.name) { - var c = b.options || {}, - f = c.end || {}, - e = c.domain || a.getConfig('cookieDomain'), - g = c.secure || a.getConfig('cookieSecure'), - k = ATInternet.Utils.jsonSerialize(b), - k = b.name + '=' + h(k), - k = - k + - (c.path && 'string' === typeof c.path ? ';path=' + c.path : ''), - k = - k + - (e && 'string' === typeof e ? ';domain=' + e : '') + - (g && 'boolean' === typeof g ? ';secure' : '') - 'function' === typeof f.toUTCString - ? (k += ';expires=' + f.toUTCString()) - : 'number' === typeof f && (k += ';max-age=' + f.toString()) - document.cookie = k - this.getData(b.name) && (d = !0) - } - return d - } - } - b = c - ? new (function () { - var a = function (a) { - var b = +new Date(), - c = !1, - d - a.options && - ('undefined' !== typeof a.options.expires - ? (d = a.options.expires) - : ((a = a.options.end || {}), - 'function' === typeof a.getTime - ? (d = a.getTime()) - : 'number' === typeof a && (d = b + 1e3 * a))) - 'number' === typeof d && b >= d && (c = !0) - return { itemToDelete: c, timestamp: d } - }, - b = function (a) { - var b = !1 - try { - localStorage.removeItem(a), (b = !0) - } catch (d) {} - return b - } - this.getData = function (d) { - var c = null, - f = localStorage.getItem(d) - if (f) { - var f = l(f), - e = ATInternet.Utils.jsonParse(f) - e && 'object' === typeof e - ? (a(e).itemToDelete && b(d)) || - (delete e.options.expires, - (c = ATInternet.Utils.jsonSerialize(e))) - : (c = f) - } - return c - } - this.setData = function (d) { - var c = !1 - if (d.name && 'string' === typeof d.name) { - var f = a(d) - 'number' === typeof f.timestamp && - (d.options.expires = f.timestamp) - var e = ATInternet.Utils.jsonSerialize(d) - if (f.itemToDelete) c = b(d.name) - else - try { - localStorage.setItem(d.name, h(e)), (c = !0) - } catch (g) {} - } - return c - } - })() - : new e() - var f = function (d, c) { - var f = !1 - d && - 'object' === typeof d && - (c || - (ATInternet.Utils.consent && - !a.getConfig('disableCookie') && - !a.getConfig('disableStorage'))) && - (f = b.setData(d)) - return f - }, - n = function (a, b, d) { - a = { name: a, val: b } - d && d.session && 'number' === typeof d.session && (d.end = d.session) - a.options = d || {} - return a - }, - r = function (d) { - var c = null, - f = null - a.getConfig('disableCookie') || - a.getConfig('disableStorage') || - !d || - 'string' !== typeof d || - (f = b.getData(d)) - ;(d = f) && (c = ATInternet.Utils.jsonParse(d)) - return c - }, - q = function (a, b) { - var d = ATInternet.Utils.cloneSimpleObject(a) - return f(d, b) - ? ATInternet.Utils.jsonParse(ATInternet.Utils.jsonSerialize(a)) - : null - }, - m = function (a, b, c) { - if (!c && d[a]) c = d[a] - else if ((c = r(a))) - (c.options = c.options || {}), - c.options.session && - 'number' === typeof c.options.session && - ((c.options.end = c.options.session), q(c, !1)), - (d[a] = c) - return c - ? b - ? ((a = null), - !c || - 'object' !== typeof c.val || - c.val instanceof Array || - void 0 === c.val[b] || - (a = c.val[b]), - a) - : c.val - : null - }, - p = function (a, b, c, f, e) { - if (b) { - if ((e = r(a))) - !e || 'object' !== typeof e.val || e.val instanceof Array - ? (e = null) - : 'undefined' === typeof c - ? delete e.val[b] - : (e.val[b] = c), - e && (e = q(e, f)) - } else (e = e || {}), (e = n(a, c, e)), (e = q(e, f)) - return e ? ((d[a] = e), e.val) : null - }, - u = function (a, b) { - if (b) p(a, b, void 0, !0, null) - else { - d[a] = void 0 - var c = n(a, '', { - end: new Date('Thu, 01 Jan 1970 00:00:00 UTC'), - path: '/', - }) - f(c, !0) - } - } - a.storage = {} - a.storage.getAll = function () { - return d - } - a.storage.get = g.get = function (a, b) { - b = !!b - return a instanceof Array ? m(a[0], a[1], b) : m(a, '', b) - } - a.storage.getPrivate = g.getPrivate = function (b, c) { - b instanceof Array - ? (b[0] += a.getConfig('site')) - : (b += a.getConfig('site')) - return g.get(b, c) - } - a.storage.set = g.set = function (a, b, c, d) { - var e - a instanceof Array - ? ((e = a[0]), (a = a[1]), (c = null)) - : ((e = a), (a = null)) - return ATInternet.Utils.privacy.testStorageParam(e, a).toSetInStorage || d - ? p(e, a, b, d, c) - : null - } - a.storage.setPrivate = g.setPrivate = function (b, c, d) { - b instanceof Array - ? (b[0] += a.getConfig('site')) - : (b += a.getConfig('site')) - return g.set(b, c, d) - } - a.storage.del = g.del = function (a) { - a instanceof Array ? u(a[0], a[1]) : u(a, '') - } - a.storage.delPrivate = g.delPrivate = function (b) { - b instanceof Array - ? (b[0] += a.getConfig('site')) - : (b += a.getConfig('site')) - g.del(b) - } - a.storage.cacheInvalidation = g.cacheInvalidation = function () { - d = {} - } - } - ATInternet.Tracker.addPlugin('Storage') -}.call(window)) -;(function () { - var dfltPluginCfg = {} - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.Utils = function (a) { - var g = this, - k = {} - a.utils = {} - a.utils.getQueryStringValue = g.getQueryStringValue = function (a, c) { - var g = ATInternet.Utils.hashcode(c).toString() - if (!k[g]) { - k[g] = {} - for ( - var l = RegExp('[&#?]{1}([^&=#?]*)=([^&#]*)?', 'g'), e = l.exec(c); - null !== e; - - ) - (k[g][e[1]] = e[2]), (e = l.exec(c)) - } - return k[g].hasOwnProperty(a) ? k[g][a] : null - } - a.utils.manageChapters = g.manageChapters = function (b, c, g) { - var k = '' - if (b) - for ( - var e = a.getConfig('ignoreEmptyChapterValue'), f = '', n = 1; - n < parseInt(g, 10) + 1; - n++ - ) - (f = b[c + n] || ''), - (k = e - ? k + (f ? f + '::' : '') - : k + (b.hasOwnProperty(c + n) ? f + '::' : '')) - return k - } - a.utils.getDocumentLevel = g.getDocumentLevel = function () { - var b = a.getConfig('documentLevel') - if (b) { - if (0 > b.indexOf('.')) return window[b] || document - b = b.split('.') - return window[b[0]][b[1]] || document - } - return document - } - a.utils.getLocation = g.getLocation = function () { - return g.getDocumentLevel().location.href - } - a.utils.getHostName = g.getHostName = function () { - return g.getDocumentLevel().location.hostname - } - a.dispatchIndex = {} - a.dispatchStack = [] - a.dispatchEventFor = {} - var c = 0 - a.dispatchSubscribe = function (b) { - return a.dispatchIndex[b] - ? !1 - : (a.dispatchStack.push(b), (a.dispatchIndex[b] = !0)) - } - a.dispatchSubscribed = function (b) { - return !0 === a.dispatchIndex[b] - } - a.addSpecificDispatchEventFor = function (b) { - return a.dispatchEventFor[b] - ? !1 - : ((a.dispatchEventFor[b] = !0), c++, !0) - } - a.processSpecificDispatchEventFor = function (b) { - a.dispatchEventFor[b] && - ((a.dispatchEventFor[b] = !1), - c--, - 0 === c && - ((a.dispatchEventFor = {}), - a.emit('Tracker:Plugin:SpecificEvent:Exec:Complete', { - lvl: 'INFO', - }))) - } - a.dispatch = function (b, d) { - var g = function () { - for (var c = '', f = null; 0 < a.dispatchStack.length; ) - (c = a.dispatchStack.pop()), - 0 === a.dispatchStack.length && (f = b), - a[c].onDispatch(f, d) - a.dispatchIndex = {} - a.delContext(void 0, 'customObject') - }, - k = function () { - if (a.plugins.isExecWaitingLazyloading()) - a.onTrigger( - 'Tracker:Plugin:Lazyload:Exec:Complete', - function () { - g() - }, - !0 - ) - else g() - } - if (0 === c) k() - else - a.onTrigger( - 'Tracker:Plugin:SpecificEvent:Exec:Complete', - function () { - k() - }, - !0 - ) - } - a.dispatchRedirect = function (b) { - var c = !0, - g = '', - k = null - b && - ((k = null), - b.hasOwnProperty('event') && (k = b.event || window.event), - !ATInternet.Utils.isTabOpeningAction(k) && - b.elem && - a.plugins.exec( - 'TechClicks', - 'manageClick', - [b.elem, k], - function (a) { - c = a.preservePropagation - g = a.elementType - } - ), - (k = b.callback)) - a.dispatch(k, g) - return c - } - a.manageSend = function (b) { - if (!ATInternet.Utils.isPreview() || a.getConfig('preview')) - ATInternet.Utils.isPrerender(function (a) { - b(a) - }) || b() - } - a.processTagObject = function (b, c, g) { - if ((b = a.getParam(b, !0)) && b._options.permanent) { - for (var k = !1, e = b._options.hitType || [], f = 0; f < e.length; f++) - if (-1 !== ATInternet.Utils.arrayIndexOf(c.concat('all'), e[f])) { - k = !0 - break - } - k && (g = ATInternet.Utils.completeFstLevelObj(b._value || {}, g, !0)) - } - return g - } - a.processContextObjectAndSendHit = function (b, c, g, k) { - var e = { - hitType: c.hitType, - encode: c.encode, - separator: c.separator, - truncate: c.truncate, - }, - f = a.getParam(b, !0) - if (f) { - for (var n = !1, r = f._options.hitType || [], q = 0; q < r.length; q++) - if ( - -1 !== ATInternet.Utils.arrayIndexOf(c.hitType.concat('all'), r[q]) - ) { - n = !0 - break - } - n - ? ((n = ATInternet.Utils.cloneSimpleObject(f)), - (n._value = ATInternet.Utils.completeFstLevelObj( - n._value || {}, - g, - !0 - )), - a.setParam(b, n._value, e), - a.manageSend(function () { - a.sendHit( - null, - [['hitType', c.hitType]], - k, - c.requestMethod, - c.elementType - ) - }), - f._options.permanent && a.setParam(b, f._value, f._options)) - : (a.setParam(b, g, e), - a.manageSend(function () { - a.sendHit( - null, - [['hitType', c.hitType]], - k, - c.requestMethod, - c.elementType - ) - }), - a.setParam(b, f._value, f._options)) - } else - a.setParam(b, g, e), - a.manageSend(function () { - a.sendHit( - null, - [['hitType', c.hitType]], - k, - c.requestMethod, - c.elementType - ) - }) - } - } - ATInternet.Tracker.addPlugin('Utils') -}.call(window)) -;(function () { - var dfltPluginCfg = { - clicksAutoManagementEnabled: true, - clicksAutoManagementTimeout: 500, - } - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.TechClicks = function (a) { - var g = this, - k = [ - 'Tracker:Hit:Sent:Ok', - 'Tracker:Hit:Sent:Error', - 'Tracker:Hit:Sent:NoTrack', - ], - c, - b, - d = !1 - a.configPlugin('TechClicks', dfltPluginCfg || {}, function (a) { - c = a.clicksAutoManagementEnabled - b = a.clicksAutoManagementTimeout - }) - var h = function (a) { - if (!d) - switch (((d = !0), a.target)) { - case '_top': - window.top.location.href = a.url - break - case '_parent': - window.parent.location.href = a.url - break - default: - window.location.href = a.url - } - }, - l = function (a) { - a.mailto - ? (g.timeout = setTimeout(function () { - window.location.href = a.mailto - }, a.timeout)) - : a.form - ? (g.timeout = setTimeout(function () { - a.form.submit() - }, a.timeout)) - : a.url && - (g.timeout = setTimeout(function () { - h({ url: a.url, target: a.target }) - }, a.timeout)) - }, - e = function (b) { - for (var c = 0; c < k.length; c++) - a.onTrigger(k[c], function (a, c) { - b && b(c) - }) - }, - f = function (a) { - for (var c, d = '_self'; a; ) { - if (a.href && 0 === a.href.indexOf('http')) { - c = a.href.split('"').join('\\"') - d = a.target ? a.target : d - break - } - a = a.parentNode - } - c && - (e(function (a) { - a.details.isMultiHit || - a.details.elementType !== ATInternet.Utils.CLICKS_REDIRECTION || - (g.timeout && clearTimeout(g.timeout), h({ url: c, target: d })) - }), - l({ url: c, target: d, timeout: b })) - }, - n = function (a) { - for (var c = a; c && 'FORM' !== c.nodeName; ) c = c.parentNode - c && - (e(function (a) { - a.details.isMultiHit || - a.details.elementType !== ATInternet.Utils.CLICKS_FORM || - (g.timeout && clearTimeout(g.timeout), c.submit()) - }), - l({ form: c, timeout: b })) - }, - r = function (a) { - for (var c = a; c && !(c.href && 0 <= c.href.indexOf('mailto:')); ) - c = c.parentNode - c && - (e(function (a) { - a.details.isMultiHit || - a.details.elementType !== ATInternet.Utils.CLICKS_MAILTO || - (g.timeout && clearTimeout(g.timeout), - (window.location.href = c.href)) - }), - l({ mailto: c.href, timeout: b })) - }, - q = function (a) { - for (var b = a; b; ) { - if (b.href) { - if (0 <= b.href.indexOf('mailto:')) - return ATInternet.Utils.CLICKS_MAILTO - if (0 === b.href.indexOf('http')) - return ATInternet.Utils.CLICKS_REDIRECTION - } else if ('FORM' === b.nodeName) { - var c = a - a = !1 - c && - ((b = c.tagName || ''), - (b = b.toLowerCase()), - 'form' === b - ? (a = !0) - : ((c = c.getAttribute('type') || ''), - (c = c.toLowerCase()), - 'button' === b - ? 'reset' !== c && 'button' !== c && (a = !0) - : 'input' === b && 'submit' === c && (a = !0))) - if (a) return ATInternet.Utils.CLICKS_FORM - break - } - b = b.parentNode - } - return '' - } - g.isFormSubmit = function (a) { - for (; a; ) { - if ('FORM' === a.nodeName) return !0 - a = a.parentNode - } - return !1 - } - a.techClicks = {} - a.techClicks.manageClick = g.manageClick = function (a, b) { - var d = !0, - e = '' - if (c && a) { - var g - a: { - for (e = a; e; ) { - if ( - 'function' === typeof e.getAttribute && - ('_blank' === e.getAttribute('target') || - 'no' === e.getAttribute('data-atclickmanagement')) - ) { - g = !0 - break a - } - e = e.parentNode - } - e = a - g = window.location.href - for (var h; e; ) { - if ( - (h = e.href) && - 0 <= h.indexOf('#') && - g.substring( - 0, - 0 <= g.indexOf('#') ? g.indexOf('#') : g.length - ) === h.substring(0, h.indexOf('#')) - ) { - g = !0 - break a - } - e = e.parentNode - } - g = !1 - } - e = q(a) - if (!g && e) { - switch (e) { - case ATInternet.Utils.CLICKS_MAILTO: - r(a) - d = !1 - break - case ATInternet.Utils.CLICKS_FORM: - n(a) - d = !1 - break - case ATInternet.Utils.CLICKS_REDIRECTION: - f(a), (d = !1) - } - b && - ((g = b.defaultPrevented), - 'function' === typeof b.isDefaultPrevented && - (g = b.isDefaultPrevented()), - g || (b.preventDefault && b.preventDefault())) - } - } - return { preservePropagation: d, elementType: e } - } - a.techClicks.deactivateAutoManagement = function () { - c = !1 - } - } - ATInternet.Tracker.addPlugin('TechClicks') -}.call(window)) -;(function () { - var dfltPluginCfg = { requestMethod: 'POST' } - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.Clicks = function (a) { - var g = {} - a.configPlugin('Clicks', dfltPluginCfg || {}, function (a) { - g = a - }) - var k = function (a) { - var b = '' - switch (a) { - case 'exit': - b = 'S' - break - case 'download': - b = 'T' - break - case 'action': - b = 'A' - break - case 'navigation': - b = 'N' - } - return b - }, - c = function (b) { - return a.utils.manageChapters(b, 'chapter', 3) + (b.name ? b.name : '') - }, - b = function (b, h) { - var l = { p: c(b), s2: b.level2 || '', click: k(b.type) || '' }, - e = ['click'], - f = a.getContext('page') || {} - l.pclick = c(f) - l.s2click = f.level2 || '' - if ((f = b.customObject)) - (f = a.processTagObject('stc', e, f)), - (l.stc = { - _value: ATInternet.Utils.jsonSerialize(f), - _options: { - hitType: e, - encode: !0, - separator: ',', - truncate: !0, - }, - }) - a.sendHit(l, [['hitType', e]], b.callback, g.requestMethod, h) - } - a.click = {} - a.clickListener = {} - a.click.send = function (d) { - d = d || {} - var c = !0, - l = '', - e = null - d.hasOwnProperty('event') && (e = d.event || window.event) - !d.elem || - ('POST' === g.requestMethod && - ATInternet.Utils.isBeaconMethodAvailable()) || - ATInternet.Utils.isTabOpeningAction(e) || - ((l = a.techClicks.manageClick(d.elem, e)), - (c = l.preservePropagation), - (l = l.elementType)) - b(d, l) - return c - } - a.clickListener.send = function (d) { - d = d || {} - if (d.elem) { - var c = 'click', - l = '' - a.plugins.exec('TechClicks', 'isFormSubmit', [d.elem], function (a) { - c = a ? 'submit' : 'click' - }) - ATInternet.Utils.addEvtListener(d.elem, c, function (e) { - ;('POST' === g.requestMethod && - ATInternet.Utils.isBeaconMethodAvailable()) || - ATInternet.Utils.isTabOpeningAction(e) || - (l = a.techClicks.manageClick(d.elem, e).elementType) - b(d, l) - }) - } - } - a.click.set = function (b) { - b = b || {} - a.dispatchSubscribe('click') - a.setContext('click', { - name: c(b), - level2: b.level2 || '', - customObject: b.customObject, - }) - a.setParam('click', k(b.type) || '', { hitType: ['click'] }) - } - a.click.onDispatch = function (b, h) { - var l = ['click'], - e = a.getContext('click') || {}, - f = a.getContext('page') || {} - a.setParam('pclick', c(f), { hitType: l }) - a.setParam('s2click', f.level2 || '', { hitType: l }) - a.setParam('p', e.name, { hitType: l }) - a.setParam('s2', e.level2, { hitType: l }) - ;(e = e.customObject) - ? a.processContextObjectAndSendHit( - 'stc', - { - hitType: l, - encode: !0, - separator: ',', - truncate: !0, - requestMethod: g.requestMethod, - elementType: h, - }, - e, - b - ) - : a.manageSend(function () { - a.sendHit(null, [['hitType', l]], b, g.requestMethod, h) - }) - a.delContext('click') - } - } - ATInternet.Tracker.addPlugin('Clicks') -}.call(window)) -;(function () { - var dfltPluginCfg = { domainAttribution: true } - var dfltGlobalCfg = { redirectionLifetime: 30 } - ATInternet.Tracker.Plugins.ContextVariables = function (a) { - var g = '', - k = null, - c, - b = '', - d = '', - h = {} - a.configPlugin('ContextVariables', dfltPluginCfg || {}, function (a) { - h = a - }) - a.setConfig('redirectionLifetime', dfltGlobalCfg.redirectionLifetime, !0) - var l = function (b, f) { - var d = null - a.plugins.exec('Storage', b, f, function (a) { - d = a - }) - return d - }, - e = function () { - a.setParam( - 'hl', - function () { - var a = new Date() - return a.getHours() + 'x' + a.getMinutes() + 'x' + a.getSeconds() - }, - { permanent: !0, hitType: ['all'] } - ) - }, - f = function (a) { - ;(a = c - ? c - : 'acc_dir' === g - ? '' - : null !== g - ? g - : 'acc_dir' === k - ? '' - : k - ? k - : a - ? a.referrer - : '') && - (a = a.replace(/[<>]/g, '').substring(0, 1600).replace(/&/g, '$')) - return a - } - a.plugins.waitForDependencies(['Storage', 'Utils'], function () { - b = 'set' + (h.domainAttribution ? '' : 'Private') - d = 'get' + (h.domainAttribution ? '' : 'Private') - var n = a.utils.getLocation() - g = a.utils.getQueryStringValue('xtref', n) - void 0 === g && (g = '') - c = a.getContext('forcedReferer') - if (a.getConfig('redirect')) { - var n = a.utils.getDocumentLevel(), - n = c ? c : null !== g ? g : n ? n.referrer : 'acc_dir', - r - if ((r = n)) { - r = { path: '/', end: a.getConfig('redirectionLifetime') } - var q = l(d, ['atredir']) - null !== q - ? (r = 'object' === typeof q && !(q instanceof Array)) - : (l(b, ['atredir', {}, r]), (r = !0)) - } - r && l(b, [['atredir', 'ref'], n]) - } else { - k = l(d, [['atredir', 'ref']]) - l('del', [['atredir', 'ref']]) - a.setParam('vtag', a.version, { permanent: !0, hitType: ['all'] }) - a.setParam('ptag', 'js', { permanent: !0, hitType: ['all'] }) - n = '' - try { - n += - window.screen.width + - 'x' + - window.screen.height + - 'x' + - window.screen.pixelDepth + - 'x' + - window.screen.colorDepth - } catch (m) {} - a.setParam('r', n, { permanent: !0, hitType: ['all'] }) - n = '' - window.innerWidth - ? (n += window.innerWidth + 'x' + window.innerHeight) - : document.body && - document.body.offsetWidth && - (n += document.body.offsetWidth + 'x' + document.body.offsetHeight) - a.setParam('re', n, { permanent: !0, hitType: ['all'] }) - e() - window.navigator && - a.setParam( - 'lng', - window.navigator.language || window.navigator.userLanguage, - { permanent: !0, hitType: ['all'] } - ) - n = ATInternet.Utils.uuid().num(13) - a.setParam('idp', n, { permanent: !0, hitType: ['page', 'clickzone'] }) - window.navigator && - a.setParam('jv', window.navigator.javaEnabled() ? '1' : '0', { - hitType: ['page'], - }) - n = a.utils.getDocumentLevel() - a.setParam('ref', f(n), { - permanent: !0, - last: !0, - hitType: ['page', 'ecommerce', 'avinsights', 'events'], - }) - } - a.emit('ContextVariables:Ready', { lvl: 'INFO' }) - }) - } - ATInternet.Tracker.addPlugin('ContextVariables') -}.call(window)) -;(function () { - var dfltPluginCfg = {} - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.Page = function (a) { - var g = ['pageId', 'chapterLabel', 'update'], - k = ['pid', 'pchap', 'pidt'], - c = ['page', 'site'], - b = ['f', 'x'], - d = function (b) { - return a.utils.manageChapters(b, 'chapter', 3) + (b.name ? b.name : '') - }, - h = function (a, b, d) { - b ? (a = b) : a || 'undefined' === typeof d || (a = d) - return a - }, - l = function (a, b, d) { - b.hasOwnProperty(d) && (a[d] = h(a[d], b[d], void 0)) - }, - e = function (d, f, e) { - if (f) - for (var g = 0; g < c.length; g++) - if (f.hasOwnProperty(c[g]) && f[c[g]]) - for (var h in f[c[g]]) - f[c[g]].hasOwnProperty(h) && - (e - ? (d[b[g] + h] = f[c[g]][h]) - : a.setParam(b[g] + h, f[c[g]][h])) - }, - f = function (b, d, f) { - if (d) { - var c = a.utils.manageChapters(d, 'chapter', 3) - c && (d.chapterLabel = c.replace(/::$/gi, '')) - for (c = 0; c < k.length; c++) - d.hasOwnProperty(g[c]) && - (f ? (b[k[c]] = d[g[c]]) : a.setParam(k[c], d[g[c]])) - } - }, - n = function (b, d, f) { - if (d && d.keywords instanceof Array) { - var c = d.keywords.length - if (0 < c) { - for (var e = '', g = 0; g < c; g++) - e += '[' + d.keywords[g] + ']' + (g < c - 1 ? '|' : '') - f ? (b.tag = e) : a.setParam('tag', e) - } - } - }, - r = function (b, d, f) { - if (d) { - var c, - e = function (a) { - return a ? a : '0' - } - c = '' + (e(d.category1) + '-') - c += e(d.category2) + '-' - c += e(d.category3) - f ? (b.ptype = c) : a.setParam('ptype', c) - } - }, - q = function (b, d, f) { - if (d) - for (var c in d) - d.hasOwnProperty(c) && - 'undefined' !== typeof d[c] && - (f ? (b[c] = d[c]) : a.setParam(c, d[c])) - } - a.customVars = {} - a.customVars.set = function (b) { - var d = a.getContext('page') || {}, - f = d.customVars - if (f) { - if (b) - for (var c in b) - b.hasOwnProperty(c) && - (f[c] = ATInternet.Utils.completeFstLevelObj(f[c], b[c], !0)) - } else f = b - d.customVars = f - a.setContext('page', d) - } - a.dynamicLabel = {} - a.dynamicLabel.set = function (b) { - var d = a.getContext('page') || {} - d.dynamicLabel = ATInternet.Utils.completeFstLevelObj( - d.dynamicLabel, - b, - !0 - ) - a.setContext('page', d) - } - a.tags = {} - a.tags.set = function (b) { - var d = a.getContext('page') || {} - d.tags = ATInternet.Utils.completeFstLevelObj(d.tags, b, !0) - a.setContext('page', d) - } - a.customTreeStructure = {} - a.customTreeStructure.set = function (b) { - var d = a.getContext('page') || {} - d.customTreeStructure = ATInternet.Utils.completeFstLevelObj( - d.customTreeStructure, - b, - !0 - ) - a.setContext('page', d) - } - a.page = {} - a.page.reset = function () { - a.delContext('page') - } - a.page.set = function (b) { - b = b || {} - a.dispatchSubscribe('page') - var d = a.getContext('page') || {} - d.name = h(d.name, b.name, '') - d.level2 = h(d.level2, b.level2, '') - l(d, b, 'chapter1') - l(d, b, 'chapter2') - l(d, b, 'chapter3') - d.customObject = ATInternet.Utils.completeFstLevelObj( - d.customObject, - b.customObject, - !0 - ) - a.setContext('page', d) - } - a.page.send = function (b) { - b = b || {} - var c = !0, - g = '', - k = { p: d(b), s2: b.level2 || '' }, - t = b.customObject - if (t) { - var v = ['page'], - t = a.processTagObject('stc', v, t) - k.stc = { - _value: ATInternet.Utils.jsonSerialize(t), - _options: { hitType: v, encode: !0, separator: ',', truncate: !0 }, - } - } - t = a.getContext('page') || {} - t.vrn && ((k.vrn = t.vrn), a.delContext('page', 'vrn')) - v = a.getContext('InternalSearch') || {} - 'undefined' !== typeof v.keyword && - ((k.mc = ATInternet.Utils.cloneSimpleObject(v.keyword)), - 'undefined' !== typeof v.resultPageNumber && - (k.np = ATInternet.Utils.cloneSimpleObject(v.resultPageNumber)), - a.delContext('InternalSearch')) - ATInternet.Utils.isPreview() && a.getConfig('preview') && (k.pvw = 1) - e(k, b.customVars, !0) - f(k, b.dynamicLabel, !0) - n(k, b.tags, !0) - r(k, b.customTreeStructure, !0) - v = a.getContext('campaigns') || {} - q(k, v, !0) - a.delContext('campaigns') - v = null - b && b.hasOwnProperty('event') && (v = b.event || window.event) - !ATInternet.Utils.isTabOpeningAction(v) && - b.elem && - ((v = a.techClicks.manageClick(b.elem, v)), - (c = v.preservePropagation), - (g = v.elementType)) - a.manageSend(function () { - a.sendHit(k, null, b.callback, null, g) - }) - t.name = h(t.name, b.name, '') - t.level2 = h(t.level2, b.level2, '') - l(t, b, 'chapter1') - l(t, b, 'chapter2') - l(t, b, 'chapter3') - a.setContext('page', t) - return c - } - a.page.onDispatch = function (b, c) { - var g = a.getContext('page') || {}, - h = a.getContext('InternalSearch') || {} - a.setParam('p', d(g)) - a.setParam('s2', g.level2 || '') - g.vrn && (a.setParam('vrn', g.vrn), a.delContext('page', 'vrn')) - 'undefined' !== typeof h.keyword && - (a.setParam('mc', ATInternet.Utils.cloneSimpleObject(h.keyword)), - 'undefined' !== typeof h.resultPageNumber && - a.setParam( - 'np', - ATInternet.Utils.cloneSimpleObject(h.resultPageNumber) - ), - a.delContext('InternalSearch')) - ATInternet.Utils.isPreview() && - a.getConfig('preview') && - a.setParam('pvw', 1) - e(null, g.customVars, !1) - f(null, g.dynamicLabel, !1) - n(null, g.tags, !1) - r(null, g.customTreeStructure, !1) - h = a.getContext('campaigns') || {} - q(null, h, !1) - a.delContext('campaigns') - var k = ['page'] - ;(g = g.customObject) - ? a.processContextObjectAndSendHit( - 'stc', - { - hitType: k, - encode: !0, - separator: ',', - truncate: !0, - elementType: c, - }, - g, - b - ) - : a.manageSend(function () { - a.sendHit(null, [['hitType', k]], b, null, c) - }) - } - } - ATInternet.Tracker.addPlugin('Page') -}.call(window)) -;(function () { - var dfltPluginCfg = { - clientSideMode: 'always', - userIdCookieDuration: 397, - userIdExpirationMode: 'fixed', - optOut: 'OPT-OUT', - userIdStorageName: 'atuserid', - userIdHitName: 'idclient', - itpCompliant: false, - baseDomain: '', - } - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.ClientSideUserId = function (a) { - var g = {}, - k = !1, - c = !1, - b = null, - d = -1 - a.configPlugin('ClientSideUserId', dfltPluginCfg || {}, function (a) { - g = a - }) - var h = function () { - var b = g.baseDomain - if (!b) { - var f = a.getConfig('cookieDomain') - f && ((b = f), '.' === b.charAt(0) && (b = b.substring(1, b.length))) - } - var f = a.builder.getCollectDomain(), - d = a.utils.getHostName() - return !!(b && f && d && -1 !== f.indexOf(b) && -1 !== d.indexOf(b)) - }, - l = function () { - b = { - contextUserId: void 0, - storageUserId: null, - finalUserId: null, - isFromTrackerContext: !1, - forceStorage: !1, - optout: { isOptedout: !1, fromStorage: !1 }, - } - }, - e = function () { - if ( - 'relative' === g.userIdExpirationMode || - ('fixed' === g.userIdExpirationMode && null === b.storageUserId) || - b.isFromTrackerContext - ) { - var f = new Date() - f.setTime(f.getTime() + 864e5 * g.userIdCookieDuration) - a.storage.set( - g.userIdStorageName, - b.finalUserId, - { end: f, path: '/' }, - b.forceStorage - ) - ATInternet.Utils.consent && - !b.isFromTrackerContext && - b.finalUserId !== a.storage.get(g.userIdStorageName, !0) && - a.setParam(g.userIdHitName, b.finalUserId + '-NO', { - multihit: !0, - permanent: !0, - hitType: ['all'], - }) - } - }, - f = function () { - a.setParam(g.userIdHitName, b.finalUserId, { - multihit: !0, - permanent: !0, - hitType: ['all'], - }) - e() - }, - n = function () { - l() - var d = !1 - null === ATInternet.Utils.optedOut - ? a.storage.get(g.userIdStorageName, !0) === g.optOut - ? (d = ATInternet.Utils.optedOut = !0) - : (ATInternet.Utils.optedOut = !1) - : !1 === ATInternet.Utils.optedOut && - (a.getParam(g.userIdHitName) === g.optOut && - a.delParam(g.userIdHitName), - a.storage.get(g.userIdStorageName, !0) === g.optOut && - a.storage.del(g.userIdStorageName)) - b.optout.isOptedout = ATInternet.Utils.optedOut - b.optout.fromStorage = d - b.contextUserId = a.getContext('userIdentifier') - b.storageUserId = a.storage.get('atuserid', !0) - d = !1 - if ('required' === g.clientSideMode) { - var e = '' - window.navigator && (e = window.navigator.userAgent) - if ( - (/Safari/.test(e) && !/Chrome/.test(e)) || - /iPhone|iPod|iPad/.test(e) - ) - d = !0 - } else 'always' === g.clientSideMode && (d = !0) - c = d - d = !1 - if ( - !a.getConfig('forceHttp') && - g.itpCompliant && - 'undefined' === typeof b.contextUserId && - !b.optout.isOptedout - ) - switch (g.clientSideMode) { - case 'never': - d = h() - break - case 'always': - case 'required': - ;(c && null !== b.storageUserId) || (d = h()) - } - ;(k = d) || - (!c && !b.optout.isOptedout && 'undefined' === typeof b.contextUserId) - ? a.setConfig('userIdOrigin', 'server') - : (a.setConfig('userIdOrigin', 'client'), - (b.isFromTrackerContext = !1), - (b.forceStorage = !1), - b.optout.isOptedout - ? ((b.finalUserId = g.optOut), - (b.isFromTrackerContext = !b.optout.fromStorage), - (b.forceStorage = !0)) - : a.getConfig('disableCookie') || a.getConfig('disableStorage') - ? ((b.finalUserId = a.getParam(g.userIdHitName)), - (b.isFromTrackerContext = !0)) - : 'undefined' !== typeof b.contextUserId - ? ((b.finalUserId = b.contextUserId), - (b.isFromTrackerContext = !0)) - : (b.finalUserId = - null !== b.storageUserId - ? b.storageUserId - : ATInternet.Utils.uuid().v4()), - f()) - }, - r = function (a) { - a && - (a = a.detail) && - 'clientsideuserid' === a.name && - a.id === d && - n() - } - ;(function () { - a.plugins.waitForDependencies(['Storage', 'Utils'], function () { - var a = ATInternet.Utils.uuid() - d = parseInt(a.num(8)) - ATInternet.Utils.removeOptOutEvent(r) - ATInternet.Utils.addOptOutEvent(d, r) - n() - }) - })() - a.clientSideUserId = {} - a.clientSideUserId.set = function (a) { - b.optout.isOptedout || - ((b.finalUserId = a), - (b.isFromTrackerContext = !0), - (b.forceStorage = !1), - f()) - } - a.clientSideUserId.store = function () { - b.finalUserId = a.getParam(g.userIdHitName) || b.finalUserId - null !== b.finalUserId && - b.finalUserId !== ATInternet.Utils.privacy.CONSENTNO && - b.finalUserId !== b.storageUserId && - ((b.isFromTrackerContext = !0), (b.forceStorage = !0), e()) - } - a.clientSideUserId.get = function () { - b.finalUserId = a.getParam(g.userIdHitName) || b.finalUserId - return b.finalUserId - } - a.clientSideUserId.clear = function () { - l() - a.delParam(g.userIdHitName) - a.storage.del(g.userIdStorageName) - } - } - ATInternet.Tracker.addPlugin('ClientSideUserId') -}.call(window)) -;(function () { - var dfltPluginCfg = { - authorityStorageName: 'atauthority', - authorities: { - default: { - name: 'default', - optin: { - name: 'optin', - storageDuration: 397, - trackerSettings: { disableStorage: false, disableCookie: false }, - add: { - buffer: { - visitorConsent: { param: 'vc', value: true }, - visitorMode: { param: 'vm', value: 'optin' }, - }, - }, - include: { storage: '*', buffer: '*' }, - }, - optout: { - name: 'optout', - storageDuration: 397, - trackerSettings: { disableStorage: false, disableCookie: false }, - add: { - buffer: { - visitorConsent: { param: 'vc', value: false }, - visitorMode: { param: 'vm', value: 'optout' }, - }, - }, - include: { - storage: ['atuserid', 'atauthority'], - buffer: [ - 's', - 'idclient', - 'ts', - 'vc', - 'vm', - 'click', - 'type', - 'olt', - 'cn', - 'mh', - ], - }, - }, - 'no-consent': { - name: 'no-consent', - storageDuration: 0, - trackerSettings: { disableStorage: true, disableCookie: true }, - add: { - buffer: { - visitorConsent: { param: 'vc', value: false }, - visitorMode: { param: 'vm', value: 'no-consent' }, - idclient: { param: 'idclient', value: 'Consent-NO' }, - }, - }, - include: { - storage: [], - buffer: [ - 's', - 'idclient', - 'ts', - 'vc', - 'vm', - 'click', - 'type', - 'olt', - 'cn', - 'mh', - ], - }, - }, - random: { - name: 'random', - storageDuration: 0, - trackerSettings: { disableStorage: false, disableCookie: false }, - add: { - buffer: { - visitorConsent: { param: 'vc', value: false }, - visitorMode: { param: 'vm', value: 'before-consent' }, - }, - }, - include: { - storage: [], - buffer: [ - 's', - 'idclient', - 'p', - 'vtag', - 'ptag', - 'ts', - 'vc', - 'vm', - 'ref', - 'xto', - 'click', - 'type', - 'olt', - 'cn', - 'mh', - ], - }, - }, - }, - cnil: { - name: 'cnil', - exempt: { - name: 'exempt', - storageDuration: 397, - trackerSettings: { disableStorage: false, disableCookie: false }, - add: { - buffer: { - visitorConsent: { param: 'vc', value: false }, - visitorMode: { param: 'vm', value: 'exempt' }, - }, - }, - include: { - storage: ['atuserid', 'atauthority'], - buffer: [ - 's', - 'idclient', - 'p', - 'vtag', - 'ptag', - 'ts', - 'vc', - 'vm', - 'click', - 'type', - 'olt', - 'cn', - 'mh', - ], - }, - }, - }, - }, - parametersToInclude: [ - 'click', - 's2', - 'p', - 'x1', - 'pclick', - 's2click', - 'f1', - 'x2', - 'f2', - 'stc', - 'ref', - 'page_url', - 'medium', - ], - } - var dfltGlobalCfg = {} - ATInternet.Tracker.Plugins.Privacy = function (a) { - var g = function (c) { - function b() { - if (r && q) { - var b = new Date() - b.setTime(b.getTime() + 864e5 * q.storageDuration) - a.storage.set( - p.authorityStorageName, - { authority_name: r.name, visitor_mode: q.name }, - { end: b, path: '/' } - ) - } - } - function d(d) { - q && - (0 < q.storageDuration - ? d - ? b() - : ((d = a.storage.get(p.authorityStorageName, !0)), - null === d - ? b() - : 'object' === typeof d && - ((d.authority_name === r.name && - d.visitor_mode === q.name) || - b())) - : a.storage.del(p.authorityStorageName)) - } - function g() { - ATInternet.Utils.privacy.processStorageParams( - function (b) { - a.storage.del(b) - a.storage.delPrivate(b) - }, - function (b) { - return a.storage.get(b, !0) || a.storage.getPrivate(b, !0) - }, - function (b) { - return a.storage.getAll(b, !0) || a.storage.getAllPrivate(b, !0) - } - ) - } - function k() { - ATInternet.Utils.privacy.processBufferParams( - function (b) { - a.delParam(b) - }, - function () { - return a.getParams([]) - }, - function (b, d, f) { - a.setParam(b, d, f) - } - ) - } - function e(a, b, d) { - if (q && q.include) { - q.include[b] instanceof Array - ? a instanceof Array - ? (q.include[b] = q.include[b].concat(a)) - : a && q.include[b].push(a) - : q.include[b] !== ATInternet.Utils.privacy.ALL && - (a instanceof Array - ? (q.include[b] = a) - : a && (q.include[b] = [a])) - a = m - var f = b + 'Params', - c = q.include[b] - b = [] - for (var e = {}, g = 0; g < c.length; g++) - if ('object' === typeof c[g]) - for (var h in c[g]) - c[g].hasOwnProperty(h) && - (e[h] = (e[h] || []).concat(c[g][h])) - else b.push(c[g]) - for (var k in e) - e.hasOwnProperty(k) && ((h = {}), (h[k] = e[k]), b.push(h)) - a[f] = b - ATInternet.Utils.privacy.setParameters(m) - d && d() - } - } - function f(b, f, c, e) { - if (p.authorities && p.authorities[b] && p.authorities[b][f]) { - c && a.clientSideUserId.clear() - e && n.setVisitorOptin() - r = p.authorities[b] - if ((q = r[f])) - for (var w in q.trackerSettings) - q.trackerSettings.hasOwnProperty(w) && - a.setConfig(w, q.trackerSettings[w]) - if (q && q.add) - for (var x in q.add.buffer) - q.add.buffer.hasOwnProperty(x) && - a.setParam(q.add.buffer[x].param, q.add.buffer[x].value, { - multihit: !0, - permanent: !0, - hitType: ['all'], - }) - if (q && q.include) { - for (var y in q.include) - q.include.hasOwnProperty(y) && - ((b = - q.include[y] instanceof Array - ? q.include[y] - : [q.include[y]]), - (m[y + 'Params'] = b)) - ATInternet.Utils.privacy.setParameters(m) - d(!1) - } - 0 < p.parametersToInclude.length && - (n.extendIncludeStorage(p.parametersToInclude), - n.extendIncludeBuffer(p.parametersToInclude)) - g() - k() - } - } - var n = this, - r = null, - q = null, - m = { storageParams: [], bufferParams: [] }, - p = c - n.setVisitorOptout = function () { - ATInternet.Utils.consentReceived(!0) - f('default', 'optout', !1, !1) - ATInternet.Utils.userOptedOut() - } - n.setVisitorOptin = function () { - ATInternet.Utils.consentReceived(!0) - a.clientSideUserId.store() - f('default', 'optin', !1, !1) - ATInternet.Utils.userOptedIn() - } - n.setVisitorRandomID = function () { - ATInternet.Utils.consentReceived(!1) - f('default', 'random', !1, !1) - ATInternet.Utils.userOptedIn() - } - n.setVisitorMode = function (a, b, d) { - var c = !0 - 'cnil' === a && 'exempt' === b - ? (c = !1) - : 'boolean' === typeof d && (c = d) - c && r && q && (c = a !== r.name || b !== q.name) - f(a, b, c, !0) - } - n.getAuthority = function () { - return r - } - n.getVisitorMode = function () { - return q - } - n.addAuthority = function (a) { - a && - 'object' === typeof a && - ((p.authorities = p.authorities || {}), (p.authorities[a.name] = a)) - } - n.extendIncludeStorage = function (a) { - e(a, 'storage', g) - } - n.extendIncludeBuffer = function (a) { - e(a, 'buffer', k) - } - n.updateStorageDuration = function (a) { - q && ((q.storageDuration = a), d(!0)) - } - ;(function () { - var b = a.storage.get([p.authorityStorageName, 'authority_name'], !0), - d = a.storage.get([p.authorityStorageName, 'visitor_mode'], !0) - b && - d && - ('default' === b - ? 'optin' === d - ? n.setVisitorOptin() - : 'optout' === d - ? n.setVisitorOptout() - : 'random' === d - ? n.setVisitorRandomID() - : n.setVisitorMode(b, d) - : n.setVisitorMode(b, d)) - })() - }, - k = null - a.privacy = { - setVisitorOptout: function () {}, - setVisitorOptin: function () {}, - setVisitorRandomID: function () {}, - setVisitorMode: function () {}, - getAuthority: function () {}, - getVisitorMode: function () {}, - addAuthority: function () {}, - extendIncludeStorage: function () {}, - extendIncludeBuffer: function () {}, - updateStorageDuration: function () {}, - } - ;(function () { - a.plugins.waitForDependencies( - ['Storage', 'Utils', 'ClientSideUserId'], - function () { - var c = null - a.configPlugin('Privacy', dfltPluginCfg || {}, function (a) { - c = ATInternet.Utils.cloneSimpleObject(a) - }) - null !== c && - ((k = new g(c)), - (a.privacy.setVisitorOptout = k.setVisitorOptout), - (a.privacy.setVisitorOptin = k.setVisitorOptin), - (a.privacy.setVisitorRandomID = k.setVisitorRandomID), - (a.privacy.setVisitorMode = k.setVisitorMode), - (a.privacy.getAuthority = k.getAuthority), - (a.privacy.getVisitorMode = k.getVisitorMode), - (a.privacy.addAuthority = k.addAuthority), - (a.privacy.extendIncludeStorage = k.extendIncludeStorage), - (a.privacy.extendIncludeBuffer = k.extendIncludeBuffer), - (a.privacy.updateStorageDuration = k.updateStorageDuration)) - } - ) - })() - } - ATInternet.Tracker.addPlugin('Privacy') -}.call(window)) -if (typeof window.ATInternet.onTrackerLoad === 'function') { - window.ATInternet.onTrackerLoad() -} diff --git a/mon-entreprise/source/App.css b/mon-entreprise/source/App.css deleted file mode 100644 index d2b1ef450..000000000 --- a/mon-entreprise/source/App.css +++ /dev/null @@ -1,13 +0,0 @@ -.app-container { - position: relative; - width: 100%; - flex: 1; - display: flex; - flex-direction: column; -} - -.app-content { - flex: 1; - display: flex; - flex-direction: column; -} diff --git a/mon-entreprise/source/App.tsx b/mon-entreprise/source/App.tsx deleted file mode 100644 index b0a4a42b2..000000000 --- a/mon-entreprise/source/App.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import Footer from 'Components/layout/Footer/Footer' -import Header from 'Components/layout/Header' -import Route404 from 'Components/Route404' -import 'Components/ui/index.css' -import { - engineFactory, - EngineProvider, - Rules, - SituationProvider, -} from 'Components/utils/EngineContext' -import { SitePathsContext } from 'Components/utils/SitePathsContext' -import 'iframe-resizer' -import { useContext, useMemo } from 'react' -import { Helmet } from 'react-helmet' -import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' -import { Redirect, Route, Switch } from 'react-router-dom' -import { - configSituationSelector, - situationSelector, -} from 'Selectors/simulationSelectors' -import './App.css' -import Accessibilité from './pages/Accessibilité' -import Budget from './pages/Budget/Budget' -import Créer from './pages/Créer' -import IntegrationTest from './pages/Dev/IntegrationTest' -import Personas from './pages/Dev/Personas' -import Sitemap from './pages/Dev/Sitemap' -import Documentation from './pages/Documentation' -import Gérer from './pages/Gérer' -import Iframes from './pages/Iframes' -import Integration from './pages/integration/index' -import Landing from './pages/Landing/Landing' -import Nouveautés from './pages/Nouveautés/Nouveautés' -import Simulateurs from './pages/Simulateurs' -import Stats from './pages/Stats/LazyStats' -import Provider, { ProviderProps } from './Provider' -import redirects from './redirects' -import { constructLocalizedSitePath } from './sitePaths' -import { - retrievePersistedInFranceApp, - setupInFranceAppPersistence, -} from './storage/persistInFranceApp' -import { setupSimulationPersistence } from './storage/persistSimulation' - -type RootProps = { - basename: ProviderProps['basename'] - rules: Rules -} - -export default function Root({ basename, rules }: RootProps) { - const { language } = useTranslation().i18n - const paths = constructLocalizedSitePath(language as 'fr' | 'en') - const engine = useMemo(() => engineFactory(rules), [rules]) - return ( - { - setupInFranceAppPersistence(store) - setupSimulationPersistence(store) - }} - initialStore={{ - inFranceApp: retrievePersistedInFranceApp(), - }} - > - - - - - ) -} - -const Router = () => { - const userSituation = useSelector(situationSelector) - const configSituation = useSelector(configSituationSelector) - const situation = useMemo( - () => ({ - ...configSituation, - ...userSituation, - }), - [configSituation, userSituation] - ) - return ( - - - - {/* Removes trailing slashes */} - ( - - )} - /> - - - - - ) -} - -const App = () => { - const { t } = useTranslation() - const sitePaths = useContext(SitePathsContext) - - return ( - <> -
-
- - {/* Passing location down to prevent update blocking */} -
- - {redirects} - - - - - - - - - - - - - - - -
-
-
- - ) -} diff --git a/mon-entreprise/source/Provider.tsx b/mon-entreprise/source/Provider.tsx deleted file mode 100644 index 2011229a8..000000000 --- a/mon-entreprise/source/Provider.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { ErrorBoundary } from '@sentry/react' -import { ThemeColorsProvider } from 'Components/utils/colors' -import { SitePathProvider, SitePaths } from 'Components/utils/SitePathsContext' -import { createBrowserHistory } from 'history' -import i18next from 'i18next' -import React, { createContext, useMemo } from 'react' -import { I18nextProvider } from 'react-i18next' -import { Provider as ReduxProvider } from 'react-redux' -import { Router } from 'react-router-dom' -import reducers, { RootState } from 'Reducers/rootReducer' -import { - applyMiddleware, - compose, - createStore, - Middleware, - PreloadedState, - Store, -} from 'redux' -// ATInternet Tracking -import { TrackingContext } from './ATInternetTracking' -import { createTracker } from './ATInternetTracking/Tracker' -import logo from './static/images/logo.svg' -import safeLocalStorage from './storage/safeLocalStorage' -import { inIframe } from './utils' - -const ATTracker = createTracker( - process.env.AT_INTERNET_SITE_ID, - safeLocalStorage.getItem('tracking:do_not_track') === '1' || - navigator.doNotTrack === '1' -) - -declare global { - interface Window { - __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any - } -} -if (process.env.REDUX_TRACE) { - console.log('going to trace') -} -const composeEnhancers = - (process.env.REDUX_TRACE - ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && - window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ - trace: true, - traceLimit: 25, - }) - : window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose - -if ( - process.env.NODE_ENV === 'production' && - 'serviceWorker' in navigator && - !inIframe() -) { - window.addEventListener('load', () => { - navigator.serviceWorker - .register('/sw.js') - .then((registration) => { - // eslint-disable-next-line no-console - console.log('SW registered: ', registration) - }) - .catch((registrationError) => { - // eslint-disable-next-line no-console - console.log('SW registration failed: ', registrationError) - }) - }) -} - -type SiteName = 'mon-entreprise' | 'infrance' | 'publicodes' - -export const SiteNameContext = createContext(null) - -export type ProviderProps = { - basename: SiteName - children: React.ReactNode - sitePaths?: SitePaths - initialStore?: PreloadedState - onStoreCreated?: (store: Store) => void - reduxMiddlewares?: Array -} - -export default function Provider({ - basename, - reduxMiddlewares = [], - initialStore, - onStoreCreated, - children, - sitePaths = {} as SitePaths, -}: ProviderProps) { - const history = useMemo( - () => - createBrowserHistory({ - basename: process.env.NODE_ENV === 'production' ? '' : basename, - }), - [] - ) - - const storeEnhancer = composeEnhancers(applyMiddleware(...reduxMiddlewares)) - - // Hack: useMemo is used to persist the store across hot reloads. - const store = useMemo(() => { - return createStore(reducers, initialStore, storeEnhancer) - }, []) - onStoreCreated?.(store) - - // Remove loader - const css = document.createElement('style') - css.type = 'text/css' - css.innerHTML = ` -#js { - animation: appear 0.5s; - opacity: 1; -} -#loading { - display: none !important; -}` - document.body.appendChild(css) - const iframeCouleur = - new URLSearchParams(document?.location.search.substring(1)).get( - 'couleur' - ) ?? undefined - - return ( - -
- -

Une erreur est survenue

-

- L'équipe technique de mon-entreprise.fr a été automatiquement - prévenue. Vous pouvez également nous contacter directement à - l'adresse{' '} - - contact@mon-entreprise.beta.gouv.fr - {' '} - si vous souhaitez partager une remarque. -

-

Veuillez nous excuser pour la gêne occasionnée.

-
- - } - > - - - - - - - - <>{children} - - - - - - - -
- ) -} diff --git a/mon-entreprise/source/actions/actions.ts b/mon-entreprise/source/actions/actions.ts deleted file mode 100644 index c5fa7736a..000000000 --- a/mon-entreprise/source/actions/actions.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { DottedName } from 'modele-social' -import Engine from 'publicodes' -import { SimulationConfig, Situation } from 'Reducers/rootReducer' -import { CompanyCreationAction } from './companyCreationChecklistActions' -import { CompanyStatusAction } from './companyStatusActions' -import { ActionExistingCompany } from './existingCompanyActions' -import { HiringChecklistAction } from './hiringChecklistAction' - -export type Action = - | ReturnType< - | typeof explainVariable - | typeof goToQuestion - | typeof hideNotification - | typeof loadPreviousSimulation - | typeof resetSimulation - | typeof setActiveTarget - | typeof setSimulationConfig - | typeof stepAction - | typeof updateSituation - | typeof updateSituation - | typeof updateUnit - | typeof batchUpdateSituation - > - | CompanyCreationAction - | CompanyStatusAction - | ActionExistingCompany - | HiringChecklistAction - -export const resetSimulation = () => - ({ - type: 'RESET_SIMULATION', - } as const) - -export const goToQuestion = (question: DottedName) => - ({ - type: 'STEP_ACTION', - name: 'unfold', - step: question, - } as const) - -export const stepAction = (step: DottedName, source?: string) => - ({ - type: 'STEP_ACTION', - name: 'fold', - step, - source, - } as const) - -export const setSimulationConfig = ( - config: SimulationConfig, - url: string, - initialSituation?: Situation -) => - ({ - type: 'SET_SIMULATION', - url, - config, - initialSituation, - } as const) - -export const setActiveTarget = (targetName: DottedName) => - ({ - type: 'SET_ACTIVE_TARGET_INPUT', - name: targetName, - } as const) - -export const updateSituation = (fieldName: DottedName, value: unknown) => - ({ - type: 'UPDATE_SITUATION', - fieldName, - value, - } as const) - -export const batchUpdateSituation = ( - situation: Parameters['setSituation']>[0] -) => - ({ - type: 'BATCH_UPDATE_SITUATION', - situation, - } as const) - -export const updateUnit = (targetUnit: string) => - ({ - type: 'UPDATE_TARGET_UNIT', - targetUnit, - } as const) - -export function loadPreviousSimulation() { - return { - type: 'LOAD_PREVIOUS_SIMULATION', - } as const -} - -export function hideNotification(id: string) { - return { type: 'HIDE_NOTIFICATION', id } as const -} - -export const explainVariable = (variableName: DottedName | null = null) => - ({ - type: 'EXPLAIN_VARIABLE', - variableName, - } as const) diff --git a/mon-entreprise/source/actions/companyCreationChecklistActions.ts b/mon-entreprise/source/actions/companyCreationChecklistActions.ts deleted file mode 100644 index 03b6a8457..000000000 --- a/mon-entreprise/source/actions/companyCreationChecklistActions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { LegalStatus } from 'Selectors/companyStatusSelectors' - -export type CompanyCreationAction = ReturnType< - typeof initializeCompanyCreationChecklist | typeof checkCompanyCreationItem -> - -export const initializeCompanyCreationChecklist = ( - statusName: LegalStatus, - checklistItems: Array -) => - ({ - type: 'INITIALIZE_COMPANY_CREATION_CHECKLIST', - checklistItems, - statusName, - } as const) - -export const checkCompanyCreationItem = (name: string, checked: boolean) => - ({ - type: 'CHECK_COMPANY_CREATION_ITEM', - name, - checked, - } as const) diff --git a/mon-entreprise/source/actions/companyStatusActions.ts b/mon-entreprise/source/actions/companyStatusActions.ts deleted file mode 100644 index eaad3363e..000000000 --- a/mon-entreprise/source/actions/companyStatusActions.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { fetchCompanyDetails } from '../api/sirene' -import { ApiCommuneJson } from 'Components/conversation/select/SelectCommune' -import { useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' -import { useHistory } from 'react-router' -import { useNextQuestionUrl } from 'Selectors/companyStatusSelectors' -import { Action } from './actions' -import { - addCommuneDetails, - setCompanyDetails, - setSiren, -} from './existingCompanyActions' - -export type CompanyStatusAction = ReturnType< - | typeof isSoleProprietorship - | typeof defineDirectorStatus - | typeof companyHasMultipleAssociates - | typeof isAutoentrepreneur - | typeof directorIsInAMinority - | typeof resetCompanyStatusChoice -> - -// This feels hacky, we should express this "dispatch and navigate" in another way -export const useDispatchAndGoToNextQuestion = () => { - const dispatch = useDispatch() - const history = useHistory() - const nextQuestion = useNextQuestionUrl() - const [dispatched, setDispatched] = useState(false) - useEffect(() => { - if (dispatched) { - history.push(nextQuestion) - } - }, [dispatched]) - return (action: Action) => { - dispatch(action) - setDispatched(true) - } -} - -export const isSoleProprietorship = (isSoleProprietorship?: boolean) => - ({ - type: 'COMPANY_IS_SOLE_PROPRIETORSHIP', - isSoleProprietorship, - } as const) - -type DirectorStatus = 'SALARIED' | 'SELF_EMPLOYED' - -export const defineDirectorStatus = (status: DirectorStatus) => - ({ - type: 'DEFINE_DIRECTOR_STATUS', - status, - } as const) - -export const companyHasMultipleAssociates = (multipleAssociates?: boolean) => - ({ - type: 'COMPANY_HAS_MULTIPLE_ASSOCIATES', - multipleAssociates, - } as const) - -export const isAutoentrepreneur = (autoEntrepreneur?: boolean) => - ({ - type: 'COMPANY_IS_MICROENTERPRISE', - autoEntrepreneur, - } as const) - -export const directorIsInAMinority = (minorityDirector?: boolean) => - ({ - type: 'SPECIFY_DIRECTORS_SHARE', - minorityDirector, - } as const) - -export const resetCompanyStatusChoice = (answersToReset?: string[]) => - ({ - type: 'RESET_COMPANY_STATUS_CHOICE', - answersToReset, - } as const) - -const fetchCommuneDetails = async function (codeCommune: string) { - const response = await fetch( - `https://geo.api.gouv.fr/communes/${codeCommune}?fields=departement,region` - ) - return await response.json() -} - -export const useSetEntreprise = () => { - const dispatch = useDispatch() - return async (siren: string) => { - const companyDetails = await fetchCompanyDetails(siren) - if (companyDetails === null) { - return - } - dispatch(setSiren(siren)) - dispatch( - setCompanyDetails( - companyDetails.categorie_juridique, - companyDetails.date_creation - ) - ) - if (companyDetails.etablissement_siege) { - const communeDetails: ApiCommuneJson = await fetchCommuneDetails( - companyDetails.etablissement_siege.code_commune - ) - dispatch(addCommuneDetails(communeDetails)) - } - } -} diff --git a/mon-entreprise/source/actions/existingCompanyActions.ts b/mon-entreprise/source/actions/existingCompanyActions.ts deleted file mode 100644 index f4147bbc1..000000000 --- a/mon-entreprise/source/actions/existingCompanyActions.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ApiCommuneJson } from 'Components/conversation/select/SelectCommune' - -export type ActionExistingCompany = ReturnType< - | typeof specifyIfAutoEntrepreneur - | typeof specifyIfDirigeantMajoritaire - | typeof resetEntreprise - | typeof setSiren - | typeof setCompanyDetails - | 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 setSiren = (siren: string) => - ({ - type: 'EXISTING_COMPANY::SET_SIREN', - siren, - } as const) - -export const setCompanyDetails = ( - catégorieJuridique: string, - dateDeCréation: string -) => - ({ - type: 'EXISTING_COMPANY::SET_DETAILS', - catégorieJuridique, - dateDeCréation, - } as const) - -export const addCommuneDetails = (details: ApiCommuneJson) => - ({ - type: 'EXISTING_COMPANY::ADD_COMMUNE_DETAILS', - details, - } as const) diff --git a/mon-entreprise/source/actions/hiringChecklistAction.ts b/mon-entreprise/source/actions/hiringChecklistAction.ts deleted file mode 100644 index ceb3ed523..000000000 --- a/mon-entreprise/source/actions/hiringChecklistAction.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type HiringChecklistAction = ReturnType< - typeof initializeHiringChecklist | typeof checkHiringItem -> - -export const initializeHiringChecklist = (checklistItems: Array) => - ({ - type: 'INITIALIZE_HIRING_CHECKLIST', - checklistItems, - } as const) - -export const checkHiringItem = (name: string, checked: boolean) => - ({ - type: 'CHECK_HIRING_ITEM', - name, - checked, - } as const) diff --git a/mon-entreprise/source/api/sirene.ts b/mon-entreprise/source/api/sirene.ts deleted file mode 100644 index f1fff6c40..000000000 --- a/mon-entreprise/source/api/sirene.ts +++ /dev/null @@ -1,70 +0,0 @@ -const isSIREN = (input: string) => /^[\s]*([\d][\s]*){9}$/.exec(input) -const isSIRET = (input: string) => /^[\s]*([\d][\s]*){14}$/.exec(input) - -export async function fetchCompanyDetails(siren: string) { - // Le paramètre `statut_diffusion` filtre les SIREN non diffusibles, cf. - // https://github.com/betagouv/mon-entreprise/issues/1399#issuecomment-770736525 - const response = await fetch( - `https://entreprise.data.gouv.fr/api/sirene/v3/unites_legales/${siren.replace( - /[\s]/g, - '' - )}?statut_diffusion=O` - ) - if (!response.ok) { - return null - } - const json = await response.json() - return json.unite_legale -} - -export async function searchDenominationOrSiren(value: string) { - if (isSIRET(value)) { - value = value.replace(/[\s]/g, '').slice(0, 9) - } - if (isSIREN(value)) { - return [{ siren: value }] - } - return searchFullText(value) -} - -type SireneData = { - etablissement: Array<{ - siren: string - is_siege: string - categorie_entreprise: string - activite_principale: string - l1_normalisee: string - }> -} - -export type Etablissement = { - siren: string - denomination?: string -} - -async function searchFullText( - text: string -): Promise | null> { - const response = await fetch( - `https://entreprise.data.gouv.fr/api/sirene/v1/full_text/${text}?per_page=5` - ) - if (!response.ok) { - return null - } - const json: SireneData = await response.json() - const etablissements = json.etablissement - .filter( - ({ is_siege, categorie_entreprise, activite_principale }) => - categorie_entreprise !== 'ETI' && - is_siege === '1' && - activite_principale !== '8411Z' - ) - .map(({ l1_normalisee, siren }) => ({ - denomination: l1_normalisee, - siren, - })) - if (!etablissements.length) { - return null - } - return etablissements -} diff --git a/mon-entreprise/source/components/Banner.css b/mon-entreprise/source/components/Banner.css deleted file mode 100644 index 038bd9211..000000000 --- a/mon-entreprise/source/components/Banner.css +++ /dev/null @@ -1,42 +0,0 @@ -.ui__.banner { - display: flex; - align-items: center; - margin: 1rem 0; -} - -.ui__.banner img { - margin-right: 0.6rem !important; - font-size: 1.4rem; - margin-bottom: 0.6rem !important; -} - -.ui__.banner.news { - justify-content: space-between; - background: var(--lightestColor); - font-size: 0.9em; - width: auto; - max-width: 450px; - margin: auto; - padding: 4px 15px 0px; - border-radius: 15px; -} - -.ui__.banner .banner-content { - flex-grow: 1; -} - -.ui__.banner.news img { - vertical-align: middle !important; - top: 4px; - position: relative; -} - -.ui__.close-button { - cursor: pointer; - font-size: 1.3em; - margin-left: 1rem; -} - -.ui__.close-button:hover { - color: black; -} diff --git a/mon-entreprise/source/components/Banner.tsx b/mon-entreprise/source/components/Banner.tsx deleted file mode 100644 index 029440679..000000000 --- a/mon-entreprise/source/components/Banner.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import emoji from 'react-easy-emoji' -import { useSelector } from 'react-redux' -import { firstStepCompletedSelector } from 'Selectors/simulationSelectors' -import Animate from 'Components/ui/animate' -import './Banner.css' - -type BannerProps = { - children: React.ReactNode - hidden?: boolean - hideAfterFirstStep?: boolean - icon?: string -} - -export default function Banner({ - children, - hidden: hiddenProp = false, - hideAfterFirstStep = true, - icon, -}: BannerProps) { - const hiddenState = useSelector(firstStepCompletedSelector) - - const hidden = hiddenProp || (hideAfterFirstStep && hiddenState) - return !hidden ? ( - -
- {icon && emoji(icon)} -
{children}
-
-
- ) : null -} diff --git a/mon-entreprise/source/components/BarChart.tsx b/mon-entreprise/source/components/BarChart.tsx deleted file mode 100644 index 308694138..000000000 --- a/mon-entreprise/source/components/BarChart.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useContext } from 'react' -import emoji from 'react-easy-emoji' -import { animated, config, useSpring } from 'react-spring' -import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting' -import { ThemeColorsContext } from 'Components/utils/colors' -import { formatValue } from 'publicodes' -import { useTranslation } from 'react-i18next' - -const ANIMATION_SPRING = config.gentle - -type ChartItemBarProps = { - numberToPlot: number - unit?: string - style: React.CSSProperties -} - -function ChartItemBar({ style, numberToPlot, unit }: ChartItemBarProps) { - const language = useTranslation().i18n.language - return ( -
- -
- {formatValue(numberToPlot, { - displayedUnit: unit, - precision: 0, - language, - })} -
-
- ) -} - -function BranchIcon({ icon }: { icon: string }) { - return ( -
- {emoji(icon)} -
- ) -} - -type BarChartBranchProps = { - value: number - title: React.ReactNode - icon?: string - maximum: number - description?: string - unit?: string -} - -export default function BarChartBranch({ - value, - title, - icon, - maximum, - description, - unit, -}: BarChartBranchProps) { - const [intersectionRef, brancheInViewport] = useDisplayOnIntersecting({ - threshold: 0.5, - }) - const { color } = useContext(ThemeColorsContext) - const numberToPlot = brancheInViewport ? value : 0 - const styles = useSpring({ - config: ANIMATION_SPRING, - to: { - flex: numberToPlot / maximum, - opacity: numberToPlot ? 1 : 0, - }, - }) as { flex: number; opacity: number } // TODO: problème avec les types de react-spring ? - - return ( - - {icon && } -
-

- {title} -
- {description && {description}} -

- -
-
- ) -} diff --git a/mon-entreprise/source/components/CompanyDetails.tsx b/mon-entreprise/source/components/CompanyDetails.tsx deleted file mode 100644 index 7d23b3a3d..000000000 --- a/mon-entreprise/source/components/CompanyDetails.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useEffect, useMemo, useState } from 'react' -import { Trans, useTranslation } from 'react-i18next' -import Skeleton from 'Components/ui/Skeleton' -import { Etablissement, fetchCompanyDetails } from '../api/sirene' - -type Company = { - denomination: string - prenom_usuel: string - nom: string - date_creation: string - etablissement_siege: { - libelle_commune: string - code_postal: string - } -} - -export default function CompanyDetails({ siren, denomination }: Etablissement) { - const { i18n } = useTranslation() - const DateFormatter = useMemo( - () => - new Intl.DateTimeFormat(i18n.language, { - month: 'short', - day: 'numeric', - year: 'numeric', - }), - [i18n.language] - ) - const [company, setCompany] = useState() - useEffect(() => { - fetchCompanyDetails(siren).then(setCompany) - }, [siren]) - - if (company === null) { - return ( -

- {siren} - est un SIREN non diffusable -

- ) - } - - return ( - <> -

- {denomination || company ? ( - <> - {denomination || - (company && - (company.denomination || - company.prenom_usuel + ' ' + company.nom))}{' '} - ({siren}) - - ) : ( - - )} -

- -

- Crée le{' '} - - {company ? ( - DateFormatter.format(new Date(company.date_creation)) - ) : ( - - )} - - ,  - {company ? ( - company.etablissement_siege ? ( - <> - domiciliée à{' '} - {company.etablissement_siege.libelle_commune} ( - {company.etablissement_siege.code_postal}) - - ) : ( - domiciliation inconnue - ) - ) : ( - - )} -

- - ) -} diff --git a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.css b/mon-entreprise/source/components/CurrencyInput/CurrencyInput.css deleted file mode 100644 index 253a288f6..000000000 --- a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.css +++ /dev/null @@ -1,29 +0,0 @@ -.currencyInput__container { - display: flex !important; - align-items: center; - justify-content: flex-end; -} - -.currencyInput__input:focus { - outline: none; -} -.currencyInput__input { - height: inherit; - max-height: inherit; - border: none; - text-align: inherit; - font-family: inherit; - padding: 0; - font-weight: inherit; - min-width: 0; - outline: none; - margin: 0; - width: inherit; - color: inherit; - background-color: transparent; - font-size: inherit; -} - -.currencyInput__input::-ms-clear { - display: none; -} diff --git a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.test.js b/mon-entreprise/source/components/CurrencyInput/CurrencyInput.test.js deleted file mode 100644 index 82d13f662..000000000 --- a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.test.js +++ /dev/null @@ -1,167 +0,0 @@ -import { expect } from 'chai' -import { mount, shallow } from 'enzyme' -import { match, spy, useFakeTimers } from 'sinon' -import CurrencyInput from './CurrencyInput' - -let getInput = (component) => mount(component).find('input') -describe('CurrencyInput', () => { - it('should render an input', () => { - expect(getInput()).to.have.length(1) - }) - - it('should accept , as decimal separator in french', () => { - const onChange = spy() - const input = getInput() - input.simulate('change', { target: { value: '12,1', focus: () => {} } }) - expect(onChange).to.have.been.calledWith( - match.hasNested('target.value', '12.1') - ) - }) - - it('should separate thousand groups', () => { - const input1 = getInput( - - ) - const input2 = getInput( - - ) - const input3 = getInput( - - ) - const input4 = getInput( - - ) - expect(input1.instance().value).to.equal('1 000') - expect(input2.instance().value).to.equal('1,000') - expect(input3.instance().value).to.equal('1,000.5') - expect(input4.instance().value).to.equal('1,000,000') - }) - - it('should handle decimal separator', () => { - const input = getInput() - expect(input.instance().value).to.equal('0,5') - }) - - it('should accept negative number', () => { - let onChange = spy() - const input = getInput() - input.simulate('change', { target: { value: '-12', focus: () => {} } }) - expect(onChange).to.have.been.calledWith( - match.hasNested('target.value', '-12') - ) - }) - - it('should not accept anything else than number', () => { - let onChange = spy() - const input = getInput() - input.simulate('change', { target: { value: '*1/2abc3', focus: () => {} } }) - expect(onChange).to.have.been.calledWith( - match.hasNested('target.value', '123') - ) - }) - - it('should pass other props to the input', () => { - const input = getInput() - expect(input.prop('autoFocus')).to.be.true - }) - - it('should not call onChange while the decimal part is being written', () => { - let onChange = spy() - const input = getInput( - - ) - input.simulate('change', { target: { value: '111,', focus: () => {} } }) - expect(onChange).not.to.have.been.called - }) - - it('should change the position of the currency symbol depending on the language', () => { - const inputFr = shallow() - expect(inputFr.children().last().text()).to.includes('€') - const inputEn = shallow() - expect(inputEn.children().first().text()).to.includes('€') - }) - - it('should debounce onChange call', () => { - const clock = useFakeTimers() - let onChange = spy() - const input = getInput( - - ) - input.simulate('change', { target: { value: '1', focus: () => {} } }) - expect(onChange).not.to.have.been.called - clock.tick(500) - input.simulate('change', { target: { value: '12', focus: () => {} } }) - clock.tick(600) - expect(onChange).not.to.have.been.called - clock.tick(400) - expect(onChange).to.have.been.calledWith( - match.hasNested('target.value', '12') - ) - clock.restore() - }) - - it('should initialize with value of the value prop', () => { - const input = getInput() - expect(input.instance().value).to.equal('1') - }) - - it('should update its value if the value prop changes', () => { - const component = mount() - component.setProps({ value: 2 }) - expect(component.find('input').instance().value).to.equal('2') - }) - - it('should not call onChange the value is the same as the current input value', () => { - let onChange = spy() - const wrapper = mount( - - ) - const input = wrapper.find('input') - input.simulate('change', { target: { value: '2000', focus: () => {} } }) - wrapper.setProps({ value: '2000' }) - expect(onChange).not.to.have.been.called - }) - - it('should adapt its size to its content', () => { - const wrapper = mount() - // It would be better to use `input.offsetWidth` but it's not supported by - // Enzyme/JSDOM - const getInlineWidth = () => - getComputedStyle( - wrapper.find('.currencyInput__container').getDOMNode() - ).getPropertyValue('width') - expect(getInlineWidth()).to.equal('') - wrapper.setProps({ value: '1000000' }) - expect(Number(getInlineWidth().replace(/em$/, ''))).to.be.greaterThan(5) - }) - - it('should not call onChange if the value is not a correct number', () => { - let onChange = spy() - mount() - .find('input') - .simulate('change', { - target: { value: '-', focus: () => {} }, - }) - mount() - .find('input') - .simulate('change', { - target: { value: ',', focus: () => {} }, - }) - mount() - .find('input') - .simulate('change', { - target: { value: ',5', focus: () => {} }, - }) - mount() - .find('input') - .simulate('change', { - target: { value: '8,', focus: () => {} }, - }) - expect(onChange).not.to.have.been.called - }) -}) diff --git a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.tsx b/mon-entreprise/source/components/CurrencyInput/CurrencyInput.tsx deleted file mode 100644 index 96c02b853..000000000 --- a/mon-entreprise/source/components/CurrencyInput/CurrencyInput.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import classnames from 'classnames' -import React, { useMemo, useRef, useState } from 'react' -import NumberFormat, { NumberFormatProps } from 'react-number-format' -import { currencyFormat, debounce } from '../../utils' -import './CurrencyInput.css' - -type CurrencyInputProps = NumberFormatProps & { - value?: string | number | null - debounce?: number - onChange: (event: React.ChangeEvent) => void - currencySymbol?: string - language: string -} - -export default function CurrencyInput({ - value, - debounce: debounceTimeout, - currencySymbol = '€', - onChange, - language, - missing, - className, - style, - dottedName, - ...forwardedProps -}: CurrencyInputProps) { - const valueProp = - typeof value === 'number' && Number.isNaN(value) ? '' : value ?? '' - - const [initialValue, setInitialValue] = useState(valueProp) - const [currentValue, setCurrentValue] = useState(valueProp) - - const onChangeDebounced = useMemo( - () => - debounceTimeout && onChange - ? debounce(debounceTimeout, onChange) - : onChange, - [onChange, debounceTimeout] - ) - // We need some mutable reference because the component doesn't provide - // the DOM `event` in its custom `onValueChange` handler - const nextValue = useRef('') - - const inputRef = useRef() - - // When the component is rendered with a new "value" prop, we reset our local - // state - if (valueProp !== initialValue) { - setCurrentValue(valueProp) - setInitialValue(valueProp) - } - - const handleChange = (event: React.ChangeEvent) => { - // Only trigger the `onChange` event if the value has changed -- and not - // only its formating, we don't want to call it when a dot is added in `12.` - // for instance - if (!nextValue.current || /(\.$)|(^\.)|(-$)/.exec(nextValue.current)) { - return - } - event.persist() - event.target = { - ...event.target, - value: nextValue.current, - } - nextValue.current = '' - onChangeDebounced?.(event) - } - - const { - isCurrencyPrefixed, - thousandSeparator, - decimalSeparator, - } = currencyFormat(language) - // Autogrow the input - const valueLength = currentValue.toString().length - const width = `${5 + (valueLength - 5) * 0.75}em` - return ( -
5 ? { width } : {}), ...style }} - onFocus={() => inputRef.current?.select()} - onClick={() => inputRef.current?.focus()} - > - {isCurrencyPrefixed && currentValue == '' && <>€ } - - { - setCurrentValue(value) - nextValue.current = value - .toString() - .replace(/^0+(.*)$/, '$1') - .replace(/^$/, '0') - }} - onChange={handleChange} - value={currentValue != null ? currentValue : ''} - autoComplete="off" - /> - {!isCurrencyPrefixed && <> €} -
- ) -} diff --git a/mon-entreprise/source/components/Distribution.css b/mon-entreprise/source/components/Distribution.css deleted file mode 100644 index 3107e8c64..000000000 --- a/mon-entreprise/source/components/Distribution.css +++ /dev/null @@ -1,116 +0,0 @@ -.distribution-chart__item { - display: flex; - margin: 1.5em 0; - min-height: 3.5em; - padding: 0; - width: 100%; - border: none; - align-items: center; - - position: relative; -} -.distribution-chart__bar-container { - display: flex; - align-items: center; - flex: 1; -} - -.distribution-chart__bar { - border-radius: 0.4em; - min-height: 1.5em; -} - -.distribution-chart__item-content { - flex: 1; -} - -.distribution-chart__counterparts { - width: 28em; -} - -@media screen and (max-width: 600px) { - .distribution-chart__item { - flex-direction: column; - align-items: stretch; - } - .distribution-chart__legend { - align-items: center !important; - justify-content: flex-start !important; - position: initial !important; - flex-direction: column !important; - padding-right: 0 !important; - width: auto !important; - height: initial !important; - } - .distribution-chart__counterparts { - display: flex; - flex-direction: column; - margin: auto; - width: auto; - margin-bottom: 1rem; - text-align: center; - align-items: center; - } - .distribution-chart__bar { - /* Enable printing of background color */ - min-height: 1.5em !important; - } - - .distribution-chart__branche-name { - font-size: inherit !important; - } -} -@media print { - .distribution-chart__bar { - box-shadow: inset 0 0 0 1000px var(--color); - } -} - -.distribution-chart__legend { - display: flex; - flex-direction: column; - align-items: center; - color: rgba(0, 0, 0, 0.7); - padding: 0.4em 0; - padding-right: 1em; - justify-content: space-evenly; - width: 6em; - flex-grow: 0; - flex-shrink: 0; -} - -.distribution-chart__icon { - font-size: 200%; - height: 1.2em; -} - -.distribution-chart__total { - border-top: 1px dotted rgba(0, 0, 0, 0.7); - color: black; - margin-top: 1em; - padding-top: 0.8em; -} -.distribution-chart__total { - display: grid; - grid-template-columns: 1fr auto auto; - grid-column-gap: 1em; - grid-row-gap: 0.4em; - align-items: flex-end; - text-align: right; - margin-bottom: 1em; -} - -.distribution-chart__total-border { - grid-column: 2 / span 2; - border-top: solid 1px rgba(0, 0, 0, 0.7); -} - -/* IE11 Special */ -@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - .distribution-chart__total > * { - margin: 8px; - } - .distribution-chart__total-border { - border: none; - } -} diff --git a/mon-entreprise/source/components/Distribution.tsx b/mon-entreprise/source/components/Distribution.tsx deleted file mode 100644 index 195d52f91..000000000 --- a/mon-entreprise/source/components/Distribution.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { EngineContext, useEngine } from 'Components/utils/EngineContext' -import { max } from 'ramda' -import { useContext } from 'react' -import { useSelector } from 'react-redux' -import { DottedName } from 'modele-social' -import { targetUnitSelector } from 'Selectors/simulationSelectors' -import BarChartBranch from './BarChart' -import './Distribution.css' -import './PaySlip' -import { getCotisationsBySection } from './PaySlip' -import RuleLink from './RuleLink' - -export default function Distribution() { - const targetUnit = useSelector(targetUnitSelector) - const engine = useContext(EngineContext) - const distribution = (getCotisationsBySection( - useEngine().getParsedRules() - ).map(([section, cotisations]) => [ - section, - cotisations - .map((c) => engine.evaluate({ valeur: c, unité: targetUnit })) - .reduce( - (acc, evaluation) => acc + ((evaluation?.nodeValue as number) || 0), - 0 - ), - ]) as Array<[DottedName, number]>) - .filter(([, value]) => value > 0) - .sort(([, a], [, b]) => b - a) - - const maximum = distribution.map(([, value]) => value).reduce(max, 0) - - return ( -
- {distribution.map(([sectionName, value]) => ( - - ))} -
- ) -} - -type DistributionBranchProps = { - dottedName: DottedName - value: number - maximum: number - - icon?: string -} - -export function DistributionBranch({ - dottedName, - value, - icon, - maximum, -}: DistributionBranchProps) { - const branche = useContext(EngineContext).getRule(dottedName) - - return ( - } - icon={icon ?? branche.rawNode.icônes} - description={branche.rawNode.résumé} - unit="€" - /> - ) -} diff --git a/mon-entreprise/source/components/EngineValue.tsx b/mon-entreprise/source/components/EngineValue.tsx deleted file mode 100644 index 6fd6223a6..000000000 --- a/mon-entreprise/source/components/EngineValue.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import Engine, { formatValue } from 'publicodes' -import React, { useContext } from 'react' -import { useTranslation } from 'react-i18next' -import { DottedName } from 'modele-social' -import { coerceArray } from '../utils' -import RuleLink from './RuleLink' -import { EngineContext, useEngine } from './utils/EngineContext' - -export type ValueProps = { - expression: string - unit?: string - engine?: Engine - displayedUnit?: string - precision?: number - linkToRule?: boolean -} & React.HTMLProps - -export default function Value({ - expression, - unit, - engine, - displayedUnit, - precision, - linkToRule = true, - ...props -}: ValueProps) { - const { language } = useTranslation().i18n - if (expression === null) { - throw new TypeError('expression cannot be null') - } - const e = engine ?? useEngine() - const isRule = expression in e.getParsedRules() - const evaluation = e.evaluate({ - valeur: expression, - ...(unit && { unité: unit }), - }) - const value = formatValue(evaluation, { - displayedUnit, - language, - precision, - }) - if (isRule && linkToRule) { - return ( - - {value} - - ) - } - return {value} -} - -type ConditionProps = { - expression: string | string[] - children: React.ReactNode -} -export function Condition({ expression, children }: ConditionProps) { - const engine = useContext(EngineContext) - if ( - !coerceArray(expression).every((expr) => engine.evaluate(expr).nodeValue) - ) { - return null - } - return <>{children} -} diff --git a/mon-entreprise/source/components/Feedback/Feedback.css b/mon-entreprise/source/components/Feedback/Feedback.css deleted file mode 100644 index c7d645842..000000000 --- a/mon-entreprise/source/components/Feedback/Feedback.css +++ /dev/null @@ -1,21 +0,0 @@ -.zammad-form > .form-group:nth-of-type(2) { - display: none; -} -.zammad-form > .btn[type='submit'] { - display: inline-block; - padding: 0.4rem 0.8rem; - color: inherit; - text-decoration: none; - border: 1px solid; - /* outline: none; */ - line-height: initial; - display: inline-block; - border-radius: 0.3rem; - transition: all 0.15s; - text-align: center; - text-transform: uppercase; - - font-family: 'Roboto', sans-serif; - font-weight: normal; - cursor: pointer; -} diff --git a/mon-entreprise/source/components/Feedback/FeedbackForm.tsx b/mon-entreprise/source/components/Feedback/FeedbackForm.tsx deleted file mode 100644 index 1d3df570a..000000000 --- a/mon-entreprise/source/components/Feedback/FeedbackForm.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { ScrollToElement } from 'Components/utils/Scroll' -import { useEffect } from 'react' -import { useTranslation } from 'react-i18next' -import { useLocation } from 'react-router' - -declare global { - const $: any -} - -// TODO: we could implement the form logic ourselves to avoid including -// https://mon-entreprise.zammad.com and https://code.jquery.com scripts -export default function FeedbackForm() { - // const tracker = useContext(TrackerContext) - const pathname = useLocation().pathname - const page = pathname.split('/').slice(-1)[0] - const isSimulateur = pathname.includes('simulateurs') - const lang = useTranslation().i18n.language - useEffect(() => { - const script = document.createElement('script') - script.src = 'https://code.jquery.com/jquery-2.1.4.min.js' - - document.body.appendChild(script) - setTimeout(() => { - const script = document.createElement('script') - script.id = 'zammad_form_script' - script.async = true - script.onload = () => { - $('#feedback-form').ZammadForm({ - messageTitle: `Remarque sur ${ - isSimulateur ? 'le simulateur' : 'la page' - } ${page}`, - messageSubmit: 'Envoyer', - messageThankYou: 'Merci de votre retour !', - lang, - attributes: [ - { - display: - "Que pouvons-nous améliorer afin de mieux répondre à vos attentes ? (ne pas mettre d'informations personnelles)", - name: 'body', - tag: 'textarea', - placeholder: 'Your Message...', - defaultValue: '', - rows: 7, - }, - { - display: 'Nom', - name: 'name', - tag: 'input', - type: 'text', - defaultValue: '-', - }, - { - display: 'Email (pour recevoir une réponse)', - name: 'email', - tag: 'input', - type: 'email', - placeholder: 'Your Email', - }, - ], - }) - } - script.src = 'https://mon-entreprise.zammad.com/assets/form/form.js' - document.body.appendChild(script) - }, 100) - }, []) - - return ( - -
-
- ) -} diff --git a/mon-entreprise/source/components/Feedback/index.tsx b/mon-entreprise/source/components/Feedback/index.tsx deleted file mode 100644 index ac04b6970..000000000 --- a/mon-entreprise/source/components/Feedback/index.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import React, { useCallback, useContext, useState } from 'react' -import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' -import { useLocation } from 'react-router-dom' -import styled from 'styled-components' -import { TrackingContext } from '../../ATInternetTracking' -import safeLocalStorage from '../../storage/safeLocalStorage' -import './Feedback.css' -import Form from './FeedbackForm' - -type PageFeedbackProps = { - blacklist?: Array - customMessage?: React.ReactNode - customEventName?: string -} - -const localStorageKey = (url: string) => `app::feedback::v3::${url}` -const setFeedbackGivenForUrl = (url: string) => { - safeLocalStorage.setItem( - localStorageKey(url), - JSON.stringify(new Date().toISOString()) - ) -} -// Ask for feedback again after 4 months -const askFeedback = (url: string) => { - const previousFeedbackDate = safeLocalStorage.getItem(localStorageKey(url)) - if (!previousFeedbackDate) { - return true - } - return ( - new Date(previousFeedbackDate) < - new Date(new Date().setMonth(new Date().getMonth() - 4)) - ) -} - -export default function PageFeedback({ customMessage }: PageFeedbackProps) { - const url = useLocation().pathname - const [display, setDisplay] = useState(askFeedback(url)) - const [state, setState] = useState({ - showForm: false, - showThanks: false, - }) - const ATTracker = useContext(TrackingContext) - - const handleFeedback = useCallback( - (rating: 'mauvais' | 'moyen' | 'bien' | 'très bien') => { - setFeedbackGivenForUrl(url) - ATTracker.click.set({ - chapter1: 'satisfaction', - type: 'action', - name: rating, - }) - ATTracker.dispatch() - const askDetails = ['mauvais', 'moyen'].includes(rating) - setState({ - showThanks: !askDetails, - showForm: askDetails, - }) - }, - [ATTracker, url] - ) - - const openSuggestionForm = useCallback(() => { - setState({ ...state, showForm: true }) - }, [state]) - - if (!display) { - return null - } - - return ( -
-
- {!state.showForm && !state.showThanks && ( - <> -

- {customMessage || ( - - Êtes-vous satisfait de cette page ? - - )}{' '} -

-
-
- handleFeedback('mauvais')}> - {emoji('🙁')} - - handleFeedback('moyen')}> - {emoji('😐')} - -
-
- handleFeedback('bien')}> - {emoji('🙂')} - - handleFeedback('très bien')}> - {emoji('😀')} - -
-
- - )} - {(state.showThanks || state.showForm) && ( - - )} - {state.showThanks && ( -
- Merci de votre retour ! -
- )} - {state.showForm ? ( -
- ) : ( - - )} -
-
- ) -} - -const EmojiButton = styled.button` - font-size: 1.5rem; - padding: 0.6rem; - transition: transform 0.05s; - will-change: transform; - :hover { - transform: scale(1.3); - } -` diff --git a/mon-entreprise/source/components/FindCompany.tsx b/mon-entreprise/source/components/FindCompany.tsx deleted file mode 100644 index 910d32dcc..000000000 --- a/mon-entreprise/source/components/FindCompany.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { useSetEntreprise } from 'Actions/companyStatusActions' -import CompanyDetails from 'Components/CompanyDetails' -import { useCallback, useMemo, useState } from 'react' -import { Trans } from 'react-i18next' -import { Etablissement, searchDenominationOrSiren } from '../api/sirene' -import { debounce } from '../utils' - -export default function Search() { - const [ - searchResults, - setSearchResults, - ] = useState | null>() - const [isLoading, setLoadingState] = useState(false) - - const handleSearch = useCallback( - function (value) { - searchDenominationOrSiren(value).then((results) => { - setLoadingState(false) - setSearchResults(results) - }) - }, - [setSearchResults, setLoadingState] - ) - const debouncedHandleSearch = useMemo(() => debounce(300, handleSearch), [ - handleSearch, - ]) - const setEntreprise = useSetEntreprise() - - return ( - <> -

- Retrouver mon entreprise -

-

- - Grâce à la base SIREN, les données publiques sur votre entreprise - seront automatiquement disponibles pour la suite du parcours sur le - site. - -

- -
- { - if (e.target.value.length < 2) { - setSearchResults(undefined) - return - } - setLoadingState(true) - debouncedHandleSearch(e.target.value) - }} - /> - {!isLoading && searchResults === null && ( -

- Aucun résultat -

- )} - - {searchResults && - searchResults.map(({ siren, denomination }) => ( - - ))} - - ) -} diff --git a/mon-entreprise/source/components/LangSwitcher.tsx b/mon-entreprise/source/components/LangSwitcher.tsx deleted file mode 100644 index c090901c5..000000000 --- a/mon-entreprise/source/components/LangSwitcher.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import emoji from 'react-easy-emoji' -import { useTranslation } from 'react-i18next' - -const languageCodeToEmoji = { - en: '🇬🇧', - fr: '🇫🇷', -} - -export default function LangSwitcher({ className }: { className: string }) { - const { i18n } = useTranslation() - const languageCode = i18n.language - const unusedLanguageCode = - !languageCode || languageCode === 'fr' ? 'en' : 'fr' - const changeLanguage = () => { - i18n.changeLanguage(unusedLanguageCode) - } - return ( - - ) -} diff --git a/mon-entreprise/source/components/LegalNotice.tsx b/mon-entreprise/source/components/LegalNotice.tsx deleted file mode 100644 index ea3380c78..000000000 --- a/mon-entreprise/source/components/LegalNotice.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Overlay from 'Components/Overlay' -import { useState } from 'react' -import { Trans } from 'react-i18next' - -export const LegalNoticeContent = () => ( - <> -

- Mentions légales -

-

- Editeur -

-

- Agence Centrale des Organismes de Sécurité Sociale (ACOSS) -
- 36 rue de Valmy - 93108 Montreuil Cedex -

-

- - Directeur de la publication - -

-

- - M. Yann-Gaël Amghar, Directeur de l'Acoss - -

-

- - Prestataire d'hébergement - -

-

- - Netlify -
- 610 22nd Street, Suite 315, -
- San Francisco, CA 94107
- Site web :  - - https://www.netlify.com - -
-

-

- Contact -

-

- - - contact@mon-entreprise.beta.gouv.fr - - -

- -) - -export default function LegalNotice() { - const [opened, setOpened] = useState(false) - const handleClose = () => { - setOpened(false) - } - const handleOpen = () => { - setOpened(true) - } - - return ( - <> - - {opened && ( - - - - )} - - ) -} diff --git a/mon-entreprise/source/components/MoreInfosOnUs.tsx b/mon-entreprise/source/components/MoreInfosOnUs.tsx deleted file mode 100644 index 2472bad18..000000000 --- a/mon-entreprise/source/components/MoreInfosOnUs.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useContext } from 'react' -import emoji from 'react-easy-emoji' -import { useTranslation } from 'react-i18next' -import { Link, useLocation } from 'react-router-dom' -import { icons } from './ui/SocialIcon' -import { SitePathsContext } from './utils/SitePathsContext' - -export default function MoreInfosOnUs() { - const { pathname } = useLocation() - const sitePaths = useContext(SitePathsContext) - const { language } = useTranslation().i18n - - if (language !== 'fr') { - return null - } - - return ( -
-

- Plus d'infos sur mon-entreprise.fr -

-
- {!pathname.startsWith(sitePaths.nouveautés) && ( - -
{emoji('✨')}
-

Les nouveautés

-

- Qu'avons-nous mis en production ces derniers mois ? -

-
Découvrir
- - )} - {!pathname.startsWith(sitePaths.stats) && ( - -
{emoji('📊')}
-

Les statistiques

-

Quel est notre impact ?

-
Découvrir
- - )} - {!pathname.startsWith(sitePaths.budget) && ( - -
{emoji('💶')}
-

Le budget

-

- Quelles sont nos ressources et comment sont-elles employées ? -

-
Découvrir
- - )} - -
- {' '} - - - - - -
-

Le code source

-

- Nos travaux sont ouverts et libres de droit, ça se passe sur GitHub -

-
Découvrir
-
-
-
- ) -} diff --git a/mon-entreprise/source/components/NewsletterRegister.tsx b/mon-entreprise/source/components/NewsletterRegister.tsx deleted file mode 100644 index 8d40d7bab..000000000 --- a/mon-entreprise/source/components/NewsletterRegister.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import * as animate from 'Components/ui/animate' -import { usePersistingState } from 'Components/utils/persistState' -import { useRef, useState } from 'react' -import emoji from 'react-easy-emoji' -import { Trans, useTranslation } from 'react-i18next' - -// We don't want to load the full sendinblue iframe, so we reimplement a simple -// logic to the a HTTP Post request to their URL with the email data. -const formInfos = { - method: 'post', - action: - 'https://b713d5c4.sibforms.com/serve/MUIEAEJui5ynU5AtcKQfxhATKzruDK8yC3YO4M56ltHV6LXHnZPESWHRjwK6Dtp3GgPqu49TljpGSa4Iy-BbaqLArDYDt5mEYtvOkrPD2b5siSNsthqi9kDz8wbljxKSDRbOQztsjyp3InDR1EUrTZJvyAk9YEkXtANb5upeHfN2VTF2m6lRiyOMF9B5VfD6jWGyxJaSNR8gsVQo?isAjax=1', -} - -export default function NewsletterRegister() { - const [userIsRegistered, setUserIsRegistered] = usePersistingState( - 'app::newsletter::registered', - false - ) - const formElement = useRef(null) - const [userJustRegistered, setUserJustRegistered] = useState(false) - const { i18n } = useTranslation() - - const onSubmit = (evt: React.FormEvent) => { - evt.preventDefault() - if (!formElement.current) { - return - } - fetch(formInfos.action, { - method: formInfos.method, - body: new FormData(formElement.current), - }).then(() => { - setUserIsRegistered(true) - setUserJustRegistered(true) - }) - } - - if (userJustRegistered) { - return ( - -
-

- {emoji('🎉')}{' '} - - Votre inscription est confirmée ! - -

-
-
- ) - } - - if (i18n.language !== 'fr' || userIsRegistered) { - return null - } - - return ( - <> -

- Restez au courant -

-
-

- - Inscrivez-vous à notre newsletter pour être informé des nouveautés - et accéder aux nouvelles fonctionnalités en avant-première. - -

- - -
- -
- - - -
-
- -
- - ) -} diff --git a/mon-entreprise/source/components/Notifications.css b/mon-entreprise/source/components/Notifications.css deleted file mode 100644 index 5a06662d7..000000000 --- a/mon-entreprise/source/components/Notifications.css +++ /dev/null @@ -1,52 +0,0 @@ -.blockingNotification { - text-align: center; - font-size: 150%; -} - -#notificationsBlock > ul { - list-style: none; - padding: 0; -} -#notificationsBlock .notification { - margin: 1em 0; - display: flex; - align-items: center; -} -#notificationsBlock .notificationText { - width: 100%; - padding: 1rem 2.6rem 1rem 1.6rem; - /*For the .hide element */ - position: relative; -} -#notificationsBlock .notificationText p:last-child { - margin: 0; -} - -.notificationText .hide { - position: absolute; - top: 0rem; - right: -1.4rem; - font-size: 200%; -} - -#notificationExplanation { -} -#notificationExplanation > div { - display: inline; -} - -/*Disable links visually */ -#notificationExplanation a { - color: inherit; - text-decoration: none; -} - -/* Display the values of the variables in the explanation of the failed control */ -#notificationsBlock .variable .situationValue { - display: inline-block; -} -#notificationsBlock img { - margin: 0 1em 0 !important; - width: 1.6em !important; - height: 1.6em !important; -} diff --git a/mon-entreprise/source/components/Notifications.tsx b/mon-entreprise/source/components/Notifications.tsx deleted file mode 100644 index bbcf00355..000000000 --- a/mon-entreprise/source/components/Notifications.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { hideNotification } from 'Actions/actions' -import animate from 'Components/ui/animate' -import { useEngine, useInversionFail } from 'Components/utils/EngineContext' -import Engine, { RuleNode } from 'publicodes' -import emoji from 'react-easy-emoji' -import { useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { RootState } from 'Reducers/rootReducer' -import './Notifications.css' -import { Markdown } from './utils/markdown' -import { ScrollToElement } from './utils/Scroll' - -// 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 -// the "sévérité" attribute. The notification will only be displayed if the -// publicodes rule is applicable. -type Notification = { - dottedName: RuleNode['dottedName'] - description: RuleNode['rawNode']['description'] - sévérité: 'avertissement' | 'information' -} - -export function getNotifications(engine: Engine) { - return Object.values(engine.getParsedRules()) - .filter( - (rule) => - rule.rawNode['type'] === 'notification' && - !!engine.evaluate(rule.dottedName).nodeValue - ) - .map(({ dottedName, rawNode: { sévérité, description } }) => ({ - dottedName, - sévérité, - description, - })) -} -export default function Notifications() { - const { t } = useTranslation() - const engine = useEngine() - const inversionFail = useInversionFail() - const hiddenNotifications = useSelector( - (state: RootState) => state.simulation?.hiddenNotifications - ) - const dispatch = useDispatch() - - const messages: Array = inversionFail - ? [ - { - dottedName: 'inversion fail', - description: t( - 'simulateurs.inversionFail', - 'Le montant saisi abouti à un résultat impossible. Cela est dû à un effet de seuil dans le calcul des cotisations.\n\nNous vous invitons à réessayer en modifiant légèrement le montant renseigné (quelques euros de plus par exemple).' - ), - sévérité: 'avertissement', - }, - ] - : (getNotifications(engine) as Array) - if (!messages?.length) return null - - return ( -
-
    - {messages.map(({ sévérité, dottedName, description }) => - hiddenNotifications?.includes(dottedName) ? null : ( - -
  • -
    - {emoji(sévérité == 'avertissement' ? '⚠️' : '💁🏻')} -
    - - -
    -
    -
  • - -
    - ) - )} -
-
- ) -} diff --git a/mon-entreprise/source/components/Overlay.tsx b/mon-entreprise/source/components/Overlay.tsx deleted file mode 100644 index 12b735f9d..000000000 --- a/mon-entreprise/source/components/Overlay.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import * as animate from 'Components/ui/animate' -import FocusTrap from 'focus-trap-react' -import { PageInfo } from 'iframe-resizer' -import React, { useEffect, useState } from 'react' -import styled, { css } from 'styled-components' - -type OverlayProps = React.HTMLAttributes & { - onClose?: () => void - xPosition?: number - children: React.ReactNode -} - -const useIFrameOffset = () => { - const [offsetTop, setOffset] = useState(null) - useEffect(() => { - if (!('parentIFrame' in window)) { - setOffset(0) - return - } - window.parentIFrame.getPageInfo(({ scrollTop, offsetTop }: PageInfo) => { - setOffset(scrollTop - offsetTop) - window.parentIFrame.getPageInfo(false) - }) - }, []) - return offsetTop -} - -export default function Overlay({ - onClose, - children, - className, - ...otherProps -}: OverlayProps) { - useEffect(() => { - const body = document.getElementsByTagName('body')[0] - body.classList.add('no-scroll') - return () => { - body.classList.remove('no-scroll') - } - }, []) - const offsetTop = useIFrameOffset() - - if (offsetTop === null) { - return null - } - return ( - -
- - -
- {children} - {onClose && ( - - )} -
-
-
-
-
- ) -} - -const StyledOverlayWrapper = styled.div<{ offsetTop: number | null }>` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - max-height: 100vh; - background: rgba(255, 255, 255, 0.9); - overflow: auto; - z-index: 2; - .overlayContent { - ${({ offsetTop }) => - offsetTop - ? css` - transform: translateY(${offsetTop}px); - ` - : css` - bottom: 0; - max-height: 80vh; - `} - position: absolute; - } - .overlayCloseButton { - position: fixed; - background-color: white; - border-top-left-radius: 100%; - text-decoration: none; - font-size: 2.5rem; - border-top: 0.5rem solid white; - border-left: 0.5rem solid white; - bottom: 0; - right: 0; - color: var(--lighterTextColor); - padding: 0 1rem; - text-decoration: none; - } - .ui__.card[aria-modal='true'] { - padding-bottom: 4rem; - display: flex; - flex-direction: column; - } - @media (max-width: 600px) { - .overlayContent { - width: 100%; - } - .overlayCloseButton { - position: fixed; - bottom: 0; - right: 0; - line-height: 1rem; - padding: 1.2rem; - padding-bottom: 1.5rem; - font-size: 3rem; - background: var(--lighterColor); - } - } - - @media (min-width: 600px) { - .overlayCloseButton { - position: absolute; - top: 0; - bottom: auto; - right: 0; - padding: 0 0.5rem; - font-size: 2rem; - } - .overlayContent { - transform: translateX(-50%) - translateY(calc(${({ offsetTop }) => offsetTop}px + 5rem)); - left: 50%; - width: 80%; - bottom: auto; - height: auto; - max-width: 40em; - min-height: 6em; - } - .ui__.card[aria-modal='true'] { - padding-bottom: 2rem; - margin-bottom: 2rem; - } - } -` diff --git a/mon-entreprise/source/components/PaySlip.css b/mon-entreprise/source/components/PaySlip.css deleted file mode 100644 index c8d370dfe..000000000 --- a/mon-entreprise/source/components/PaySlip.css +++ /dev/null @@ -1,87 +0,0 @@ -.payslip__salarySection { - display: grid; - grid-template-columns: auto 8.2em; - grid-gap: 0.3em 0em; -} -.payslip__salaryTitle { - grid-column-end: span 2; -} -.payslip__cotisationsSection { - display: grid; - grid-template-columns: auto auto auto; - grid-gap: 0.3em 0em; -} - -.payslip__container h4 { - border-bottom: 1px solid black; - margin: 0; - margin-top: 1.5em; - padding-bottom: 0.5em; - padding-left: 0.5em; - align-self: flex-end; - margin-bottom: 0.5em; -} -.payslip__container h4:first-child { - padding-left: 0; -} - -.payslip__total { - font-weight: bold; - margin-top: 1em; - background-color: transparent !important; -} - -.payslip__container h5 { - margin: 0; -} -.payslip__container h5 { - margin: 0; -} -.payslip__container h5:not(:first-of-type) { - margin-top: 0.6em; -} -.payslip__container *::first-letter { - text-transform: capitalize; -} - -.payslip__container span { - display: flex; - align-items: flex-end; - font-family: 'Courier New', Courier, monospace; - justify-content: flex-end; -} - -.payslip__cotisationsSection h4:not(:first-child) { - text-align: right; -} - -.payslip__cotisationTitle { - grid-column-end: span 3; -} - -.payslip__salaireNet { - font-weight: bold; -} - -/* IE */ -@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - .payslip__cotisationsSection > h4:nth-child(1), - .payslip__cotisationsSection > a, - div.payslip__total { - width: 50%; - display: inline-block; - } - .payslip__cotisationsSection > h4:not(:nth-child(1)) { - width: 25%; - display: inline-block; - } - .payslip__container .montant { - display: inline-block; - width: 25%; - text-align: right; - } - .payslip__salarySection > a { - width: 75%; - display: inline-block; - } -} diff --git a/mon-entreprise/source/components/PaySlip.tsx b/mon-entreprise/source/components/PaySlip.tsx deleted file mode 100644 index c6b11f8c4..000000000 --- a/mon-entreprise/source/components/PaySlip.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import Value from 'Components/EngineValue' -import RuleLink from 'Components/RuleLink' -import { EngineContext, useEngine } from 'Components/utils/EngineContext' -import { DottedName } from 'modele-social' -import { ASTNode, formatValue, ParsedRules, reduceAST } from 'publicodes' -import { RuleNode } from 'publicodes/dist/types/rule' -import { Fragment, useContext } from 'react' -import { Trans, useTranslation } from 'react-i18next' -import './PaySlip.css' -import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections' - -export const SECTION_ORDER = [ - 'protection sociale . santé', - 'protection sociale . accidents du travail et maladies professionnelles', - 'protection sociale . retraite', - 'protection sociale . famille', - 'protection sociale . assurance chômage', - 'protection sociale . formation', - 'protection sociale . transport', - 'protection sociale . autres', -] as const - -type Section = typeof SECTION_ORDER[number] - -function getSection(rule: RuleNode): Section { - const section = ('protection sociale . ' + - rule.rawNode.cotisation?.branche) as Section - if (SECTION_ORDER.includes(section)) { - return section - } - return 'protection sociale . autres' -} - -export function getCotisationsBySection( - parsedRules: ParsedRules -): Array<[Section, DottedName[]]> { - function findCotisations(dottedName: DottedName) { - return reduceAST>( - (acc, node) => { - if ( - node.nodeKind === 'reference' && - node.dottedName !== 'contrat salarié . cotisations' && - node.dottedName?.startsWith('contrat salarié . ') && - node.dottedName !== - 'contrat salarié . cotisations . patronales . réductions de cotisations' - ) { - return [...acc, node] - } - }, - [], - parsedRules[dottedName] - ) - } - - const cotisations = ([ - ...findCotisations('contrat salarié . cotisations . patronales'), - ...findCotisations('contrat salarié . cotisations . salariales'), - ] as Array) - .map((cotisation) => cotisation.dottedName) - .filter(Boolean) - .map( - (dottedName) => - dottedName.replace(/ . (salarié|employeur)$/, '') as DottedName - ) - .reduce((acc, cotisation: DottedName) => { - const sectionName = getSection(parsedRules[cotisation]) - return { - ...acc, - [sectionName]: (acc[sectionName] ?? new Set()).add(cotisation), - } - }, {} as Record>) - return Object.entries(cotisations) - .map(([section, dottedNames]) => [section, [...dottedNames.values()]]) - .sort( - ([a], [b]) => - SECTION_ORDER.indexOf(a as Section) - - SECTION_ORDER.indexOf(b as Section) - ) as Array<[Section, DottedName[]]> -} - -export default function PaySlip() { - const parsedRules = useEngine().getParsedRules() - const cotisationsBySection = getCotisationsBySection(parsedRules) - - return ( -
-
- - -
- - - {/* Section cotisations */} -
-

- Cotisations sociales -

-

- Part employeur -

-

- Part salarié -

- {cotisationsBySection.map(([sectionDottedName, cotisations]) => { - const section = parsedRules[sectionDottedName] - return ( - -
- -
- {cotisations.map((cotisation) => ( - - ))} -
- ) - })} - {/* Réductions */} -
- -
- - - {/* Total cotisation */} -

- Total des retenues -

- - - {/* Salaire chargé */} - - -
- {/* Section salaire net */} - -
- ) -} - -function findReferenceInNode( - dottedName: DottedName, - node: ASTNode -): string | undefined { - return reduceAST( - (acc, node) => { - if ( - node.nodeKind === 'reference' && - node.dottedName?.startsWith(dottedName) - ) { - return node.dottedName - } else if (node.nodeKind === 'reference') { - return acc - } - }, - undefined, - node - ) -} -function Cotisation({ dottedName }: { dottedName: DottedName }) { - const language = useTranslation().i18n.language - const engine = useContext(EngineContext) - const partSalariale = engine.evaluate( - findReferenceInNode( - dottedName, - engine.getRule('contrat salarié . cotisations . salariales') - ) ?? '0' - ) - const partPatronale = engine.evaluate( - findReferenceInNode( - dottedName, - engine.getRule('contrat salarié . cotisations . patronales') - ) ?? '0' - ) - - if (!partPatronale.nodeValue && !partSalariale.nodeValue) { - return null - } - return ( - <> - - - {partPatronale?.nodeValue - ? formatValue(partPatronale, { displayedUnit: '€', language }) - : '–'} - - - {partSalariale?.nodeValue - ? formatValue(partSalariale, { displayedUnit: '€', language }) - : '–'} - - - ) -} diff --git a/mon-entreprise/source/components/PaySlipSections.tsx b/mon-entreprise/source/components/PaySlipSections.tsx deleted file mode 100644 index a58167b72..000000000 --- a/mon-entreprise/source/components/PaySlipSections.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import Value, { Condition, ValueProps } from 'Components/EngineValue' -import RuleLink from 'Components/RuleLink' -import { DottedName } from 'modele-social' -import { Trans } from 'react-i18next' - -export const SalaireBrutSection = () => { - return ( -
-

- Salaire -

- - - - - - - - - - - - -
- ) -} - -export const SalaireNetSection = () => { - return ( -
-

- Salaire net -

- - - - - - - - - - - -
- ) -} - -type LineProps = { - rule: DottedName - negative?: boolean -} & Omit, 'expression'> - -export function Line({ - rule, - displayedUnit = '€', - negative = false, - className, - ...props -}: LineProps) { - return ( - - - - - ) -} diff --git a/mon-entreprise/source/components/PercentageField.css b/mon-entreprise/source/components/PercentageField.css deleted file mode 100644 index e51a19e08..000000000 --- a/mon-entreprise/source/components/PercentageField.css +++ /dev/null @@ -1,97 +0,0 @@ -.range { - -webkit-appearance: none; - vertical-align: middle; - outline: none; - border: none; - cursor: pointer; - padding: 0; - background: none; -} - -.range::-webkit-slider-runnable-track { - background-color: white; - height: 6px; - border-radius: 3px; - border: 1px solid transparent; -} - -.range[disabled]::-webkit-slider-runnable-track { - border: 1px solid white; - background-color: transparent; - opacity: 0.4; -} - -.range::-moz-range-track { - background-color: white; - height: 6px; - border-radius: 3px; - border: none; -} - -.range::-ms-track { - color: transparent; - border: none; - background: none; - height: 6px; -} - -.range::-ms-fill-lower { - background-color: white; - border-radius: 3px; -} - -.range::-ms-fill-upper { - background-color: white; - border-radius: 3px; -} - -.range::-ms-tooltip { - display: none; /* display and visibility only */ -} - -.range::-moz-range-thumb { - border-radius: 20px; - height: 18px; - width: 18px; - border: 2px solid white; - background: none; - background-color: var(--color); - cursor: pointer; -} - -.range:active::-moz-range-thumb { - outline: none; -} - -.range::-webkit-slider-thumb { - -webkit-appearance: none !important; - border-radius: 100%; - border: 2px solid white; - background-color: var(--color); - cursor: pointer; - height: 18px; - width: 18px; - margin-top: -7px; -} - -.range[disabled]::-webkit-slider-thumb { - background-color: transparent; - border: 1px solid white; -} - -.range:active::-webkit-slider-thumb { - outline: none; -} - -.range::-ms-thumb { - border-radius: 100%; - border: 2px solid white; - background-color: var(--color); - cursor: pointer; - height: 18px; - width: 18px; -} - -.range:active::-ms-thumb { - border: none; -} diff --git a/mon-entreprise/source/components/PercentageField.tsx b/mon-entreprise/source/components/PercentageField.tsx deleted file mode 100644 index c50ff386d..000000000 --- a/mon-entreprise/source/components/PercentageField.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { formatValue } from 'publicodes' -import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { debounce as debounceFn } from '../utils' -import { InputProps } from './conversation/RuleInput' -import './PercentageField.css' - -type PercentageFieldProps = InputProps & { - debounce: number -} - -export default function PercentageField({ - onChange, - value, - debounce = 0, -}: PercentageFieldProps) { - const [localValue, setLocalValue] = useState(value as number) - const debouncedOnChange = useCallback( - debounce ? debounceFn(debounce, onChange) : onChange, - [debounce, onChange] - ) - const language = useTranslation().i18n.language - - return ( -
- { - const value = e.target.value - setLocalValue(+value) - debouncedOnChange(value) - }} - type="range" - value={localValue} - name="volume" - min="0" - step="0.05" - max="1" - /> - - {formatValue(localValue, { - language, - displayedUnit: '%', - })} - -
- ) -} diff --git a/mon-entreprise/source/components/PeriodSwitch.css b/mon-entreprise/source/components/PeriodSwitch.css deleted file mode 100644 index ff71fd5ba..000000000 --- a/mon-entreprise/source/components/PeriodSwitch.css +++ /dev/null @@ -1,5 +0,0 @@ -#PeriodSwitch { - display: flex; - align-items: center; - justify-content: flex-end; -} diff --git a/mon-entreprise/source/components/PeriodSwitch.tsx b/mon-entreprise/source/components/PeriodSwitch.tsx deleted file mode 100644 index acb0c6ed1..000000000 --- a/mon-entreprise/source/components/PeriodSwitch.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { updateUnit } from 'Actions/actions' -import { Trans } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { targetUnitSelector } from 'Selectors/simulationSelectors' -import './PeriodSwitch.css' - -export default function PeriodSwitch() { - const dispatch = useDispatch() - const currentUnit = useSelector(targetUnitSelector) - - const units = ['€/mois', '€/an'] - return ( -
- - {units.map((unit) => ( - - ))} - -
- ) -} diff --git a/mon-entreprise/source/components/PreviousSimulationBanner.tsx b/mon-entreprise/source/components/PreviousSimulationBanner.tsx deleted file mode 100644 index 0a389846e..000000000 --- a/mon-entreprise/source/components/PreviousSimulationBanner.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { loadPreviousSimulation } from 'Actions/actions' -import { Trans } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { RootState } from 'Reducers/rootReducer' -import { LinkButton } from 'Components/ui/Button' -import Banner from './Banner' -import { firstStepCompletedSelector } from 'Selectors/simulationSelectors' - -export default function PreviousSimulationBanner() { - const previousSimulation = useSelector( - (state: RootState) => state.previousSimulation - ) - const newSimulationStarted = useSelector(firstStepCompletedSelector) - const dispatch = useDispatch() - - return ( - - ) -} diff --git a/mon-entreprise/source/components/QuickLinks.tsx b/mon-entreprise/source/components/QuickLinks.tsx deleted file mode 100644 index 526091eea..000000000 --- a/mon-entreprise/source/components/QuickLinks.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { goToQuestion } from 'Actions/actions' -import { DottedName } from 'modele-social' -import { contains, filter, pipe, reject, toPairs } from 'ramda' -import { Trans } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { RootState } from 'Reducers/rootReducer' -import { - answeredQuestionsSelector, - currentQuestionSelector, -} from 'Selectors/simulationSelectors' -import { useNextQuestions } from './utils/useNextQuestion' - -export default function QuickLinks() { - const currentQuestion = useSelector(currentQuestionSelector) - const nextSteps = useNextQuestions() - const quickLinks = useSelector( - (state: RootState) => state.simulation?.config.questions?.["à l'affiche"] - ) - const quickLinksToHide = useSelector(answeredQuestionsSelector) - const dispatch = useDispatch() - - if (!quickLinks) { - return null - } - const links = pipe( - reject((dottedName: DottedName) => contains(dottedName, quickLinksToHide)), - filter((dottedName: DottedName) => contains(dottedName, nextSteps)), - toPairs - )(quickLinks) - - if (links.length < 1) { - return null - } - - return ( - - Questions : - {links.map(([label, dottedName]) => ( - - ))}{' '} - {/* */} - - ) -} diff --git a/mon-entreprise/source/components/Route404.tsx b/mon-entreprise/source/components/Route404.tsx deleted file mode 100644 index 2e1f19596..000000000 --- a/mon-entreprise/source/components/Route404.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import image from 'Images/map-directions.png' -import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' -import { Link } from 'react-router-dom' - -export default () => ( -
-

- - Cette page n'existe pas ou n'existe plus - - {emoji(' 🙅')} -

- - {/* TODO: credits for the image to add: https://thenounproject.com/term/treasure-map/96666/ */} - - - Revenir en lieu sûr - - -
-) diff --git a/mon-entreprise/source/components/RuleLink.tsx b/mon-entreprise/source/components/RuleLink.tsx deleted file mode 100644 index ae174538b..000000000 --- a/mon-entreprise/source/components/RuleLink.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { RuleLink as EngineRuleLink } from 'publicodes-react' -import React, { useContext } from 'react' -import { Link } from 'react-router-dom' -import { DottedName } from 'modele-social' -import { EngineContext } from './utils/EngineContext' -import { SitePathsContext } from './utils/SitePathsContext' - -export default function RuleLink( - props: { - dottedName: DottedName - displayIcon?: boolean - } & Omit, 'to'> -) { - const sitePaths = useContext(SitePathsContext) - const engine = useContext(EngineContext) - return ( - - ) -} diff --git a/mon-entreprise/source/components/RulesList.tsx b/mon-entreprise/source/components/RulesList.tsx deleted file mode 100644 index 7be5822f2..000000000 --- a/mon-entreprise/source/components/RulesList.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import SearchBar from 'Components/SearchBar' -import { Trans } from 'react-i18next' -import './RulesList.css' - -export default function RulesList() { - return ( -
-

- Explorez notre documentation -

- -
- ) -} diff --git a/mon-entreprise/source/components/SchemeComparaison.css b/mon-entreprise/source/components/SchemeComparaison.css deleted file mode 100644 index 369a6bbaa..000000000 --- a/mon-entreprise/source/components/SchemeComparaison.css +++ /dev/null @@ -1,207 +0,0 @@ -.comparaison-grid { - display: grid; - justify-items: stretch; - justify-content: center; - grid-template-columns: - [row-legend] auto [assimilé-salarié] minmax(20%, 20rem) - [indépendant] minmax(20%, 20rem) [auto-entrepreneur] minmax(20%, 20rem) [end]; -} -.comparaison-grid.hideAutoEntrepreneur { - grid-template-columns: - [row-legend] auto [assimilé-salarié] minmax(20%, 20rem) - [indépendant] minmax(20%, 20rem) [auto-entrepreneur] 0 [end]; -} - -.comparaison-grid.hideAssimiléSalarié { - grid-template-columns: - [row-legend] auto [assimilé-salarié] 0 - [indépendant] minmax(20%, 20rem) [auto-entrepreneur] minmax(20%, 20rem) [end]; -} -.comparaison-grid > * { - border-bottom: 1px solid var(--lighterColor); - padding: 0.6rem 1.2rem; - border-right: 1px solid var(--lighterColor); - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-evenly; - text-align: center; - flex-wrap: wrap; -} - -.comparaison-grid > h2 { - margin: 0; - border: none; - align-self: stretch; -} - -.comparaison-grid > h2 img { - height: 1.6rem !important; - width: 1.6rem !important; -} -.comparaison-grid > .legend { - max-width: 15rem; - align-items: flex-end; - grid-column: row-legend; - text-align: right; -} - -.comparaison-grid > .AS-et-indep { - grid-column: assimilé-salarié / auto-entrepreneur; -} -.comparaison-grid > .AS { - grid-column: assimilé-salarié; - min-width: 11rem; -} -.comparaison-grid > .indep { - grid-column: indépendant; -} -.comparaison-grid > .auto { - grid-column: auto-entrepreneur; - border-right: none; - min-width: 14rem; -} -.comparaison-grid > .all { - border-right: none; - grid-column: row-legend / end; -} -.comparaison-grid > .all.colored { - background-color: var(--lightestColor); -} - -.comparaison-grid > .indep-et-auto { - grid-column: indépendant / end; - border-right: none; -} -.comparaison-grid > .AS-indep-et-auto { - grid-column: assimilé-salarié / end; - border-right: none; -} - -.comparaison-grid.hideAutoEntrepreneur > .auto { - display: none; -} -.comparaison-grid.hideAutoEntrepreneur > .indep-et-auto { - border-right: 1px solid var(--lighterColor); -} - -.comparaison-grid.hideAssimiléSalarié > .AS { - display: none; -} - -.comparaison-grid > .green { - font-weight: bold; - color: limegreen; -} - -.comparaison-grid > .red { - font-weight: bold; - color: red; -} - -.comparaison-grid > .no-border { - border: none; -} -.comparaison-grid .button { - align-self: stretch; -} - -@media (max-width: 800px) { - .comparaison-grid > * { - padding: 0.6rem; - } -} -@media (max-width: 600px) { - .comparaison-grid { - display: block; - padding: 0 0.6rem; - } - - .comparaison-grid h2 { - flex-direction: column; - } - .comparaison-grid small { - margin-left: 0.2rem; - } - - .comparaison-grid > *::before { - flex: 1; - text-align: left; - flex-shrink: 0; - white-space: nowrap; - user-select: text; - font-weight: normal; - } - .comparaison-grid > :not(.button)::before { - color: var(--darkestColor) !important; - opacity: 0.6; - } - .comparaison-grid > .AS::before { - content: 'Assimilé-salarié :'; - } - .comparaison-grid > .indep::before, - .comparaison-grid.hideAutoEntrepreneur > .indep-et-auto::before { - content: 'Indépendant :'; - } - - .comparaison-grid.hideAssimiléSalarié > .AS-et-indep::before, - .comparaison-grid.hideAssimiléSalarié > .indep::before { - content: 'Entreprise individuelle :'; - } - - .comparaison-grid > .auto::before { - content: 'Auto-entrepreneur :'; - } - .comparaison-grid > .indep-et-auto::before { - content: 'Indépendant / auto-entrepreneur :'; - } - .comparaison-grid > .AS-et-indep::before { - content: 'Assimilé salarié / indépendant '; - } - .comparaison-grid > h2::before { - display: none; - } - .comparaison-grid > h2.AS::after, - .comparaison-grid:not(.hideAutoEntrepreneur) > h2.indep::after { - display: block; - font-size: 1rem; - margin-top: 1rem; - content: 'vs'; - color: var(--lightColor); - } - .comparaison-grid > .legend { - justify-content: flex-start; - align-items: baseline; - text-align: left; - } - .comparaison-grid > * { - border: none; - max-width: inherit !important; - flex-direction: row; - text-align: right; - justify-content: right; - } - .comparaison-grid > :not(.all):not(.button) { - padding-left: 0; - } - .comparaison-grid > .all { - margin: 0 -0.6rem; - text-align: center; - justify-content: center; - margin-top: 2rem; - } - .comparaison-grid > .no-border > .button { - flex: 1; - } - .comparaison-grid > .no-border::before { - display: none; - } -} -@media (min-width: 600px) { - .comparaison-grid > h3 { - margin: 0; - font-weight: normal; - - font-size: 1rem; - } -} diff --git a/mon-entreprise/source/components/SchemeComparaison.tsx b/mon-entreprise/source/components/SchemeComparaison.tsx deleted file mode 100644 index a5162d915..000000000 --- a/mon-entreprise/source/components/SchemeComparaison.tsx +++ /dev/null @@ -1,611 +0,0 @@ -import { - defineDirectorStatus, - isAutoentrepreneur, - useDispatchAndGoToNextQuestion, -} from 'Actions/companyStatusActions' -import classnames from 'classnames' -import Conversation from 'Components/conversation/Conversation' -import Value from 'Components/EngineValue' -import * as Animate from 'Components/ui/animate' -import InfoBulle from 'Components/ui/InfoBulle' -import revenusSVG from 'Images/revenus.svg' -import { useCallback, useMemo, useState } from 'react' -import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' -import { useSelector } from 'react-redux' -import { situationSelector } from 'Selectors/simulationSelectors' -import dirigeantComparaison from '../pages/Simulateurs/configs/rémunération-dirigeant.yaml' -import SeeAnswersButton from './conversation/SeeAnswersButton' -import PeriodSwitch from './PeriodSwitch' -import './SchemeComparaison.css' -import { SimulationGoal, SimulationGoals } from './SimulationGoals' -import { useEngine } from './utils/EngineContext' -import useSimulationConfig from './utils/useSimulationConfig' - -type SchemeComparaisonProps = { - hideAutoEntrepreneur?: boolean - hideAssimiléSalarié?: boolean -} - -export default function SchemeComparaison({ - hideAutoEntrepreneur = false, - hideAssimiléSalarié = false, -}: SchemeComparaisonProps) { - useSimulationConfig(dirigeantComparaison) - const dispatch = useDispatchAndGoToNextQuestion() - const engine = useEngine() - - const [showMore, setShowMore] = useState(false) - const [conversationStarted, setConversationStarted] = useState( - !!Object.keys(useSelector(situationSelector)).length - ) - const startConversation = useCallback(() => setConversationStarted(true), [ - setConversationStarted, - ]) - - const situation = useSelector(situationSelector) - const displayResult = - useSelector(situationSelector)['dirigeant . rémunération . totale'] != - undefined - const assimiléEngine = useMemo( - () => - engine.shallowCopy().setSituation({ - ...situation, - dirigeant: "'assimilé salarié'", - }), - [situation] - ) - const autoEntrepreneurEngine = useMemo( - () => - engine.shallowCopy().setSituation({ - ...situation, - dirigeant: "'auto-entrepreneur'", - }), - [situation] - ) - const indépendantEngine = useMemo( - () => - engine.shallowCopy().setSituation({ - ...situation, - dirigeant: "'indépendant'", - }), - [situation] - ) - const plafondAutoEntrepreneurDépassé = - autoEntrepreneurEngine.evaluate( - "entreprise . chiffre d'affaires . seuil micro dépassé" - ).nodeValue === true - - return ( - <> -
-

- {emoji('☂')} Assimilé salarié - - - Le régime tout compris - - -

-

- {emoji('👩‍🔧')}{' '} - {hideAssimiléSalarié ? ( - Entreprise Individuelle - ) : ( - Indépendant - )} - - - La protection sociale à la carte - - -

-

- {emoji('🚶‍♂️')} Auto-entrepreneur - - - Pour commencer sans risques - - -

- -

- - Statuts juridiques possibles - -

-
-
- - SAS, SASU ou SARL avec gérant minoritaire - -
-
-
-
- {hideAssimiléSalarié ? ( - - EI ou EIRL - - ) : ( - - EI, EIRL, EURL ou SARL avec gérant majoritaire - - )} -
-
-
- - Auto-entreprise - -
- - -

Couverture accidents du travail

-
-
- - Oui - -
-
- Non -
- -

- Assurance maladie{' '} - (médicaments, soins, hospitalisations) -

-
Identique pour tous
-
- -

- Mutuelle santé - -

-
Obligatoire
-
Fortement conseillée
-
- - -

Indemnités journalières

-
-
++
-
++
-
+
- -

Retraite

-
-
+++
-
++
-
+
- - {showMore ? ( - <> - -

ACRE

-
- 1 an (automatique et inconditionnelle) -
-
- Entre 3 et 4 trimestres{' '} - (sous conditions d'éligibilité) -
-
- -

Déduction des charges

-
- Oui (régime fiscal du réel) -
-
- Non - - (mais abattement forfaitaire pour le calcul de l'impôt sur le - revenu) - -
-
- - -

Paiement des cotisations

-
Mensuel
-
- Provision mensuelle ou trimestrielle - - (avec régularisation après coup en fonction du revenu réel) - -
-
Mensuel ou trimestriel
-
- -

- Contrats prévoyance et retraite facultatives déductibles -

-
- Oui (sous certaines conditions) -
-
- Oui (Loi Madelin) -
-
-
- Non -
- -

Paiement de cotisations minimales

-
-
- Non -
-
- Oui -
-
- Non -
- -

- Revenu minimum pour l'ouverture des droits aux prestations -

-
Oui
-
- Non (cotisations minimales obligatoires) -
-
Oui
-
- {!hideAutoEntrepreneur && ( - -

Plafond de chiffre d'affaires

-
- Non -
-
- Oui - - (72 500 € en services / 176 200 € en vente de biens, - restauration ou hébergement) - -
-
- )} - -

- Gestion comptable, sociale, juridique... -

-
- Accompagnement fortement conseillé - - (expert comptable, comptable, centre de gestion agrée...) - -
- -
- Simplifiée{' '} - (peut être gérée par l'auto-entrepreneur) -
-
- - ) : ( - -
- -
-
- )} -
- {!conversationStarted ? ( - <> - -

- Comparer mes revenus, pension de retraite et indemnité maladie -

- - -
- - ) : ( -
- - - - - - {displayResult && ( - -
- -

- Vous pouvez consulter les différentes estimations - dans le tableau ci-dessous -

- - - - } - /> -
-
- )} -
- )} -
- {displayResult && ( - <> - -

- Revenu net de cotisations (avant impôts) -

-
-
- -
-
- -
-
- <> - {plafondAutoEntrepreneurDépassé && 'Plafond de CA dépassé'} - - -
- -

- - Pension de retraite - (avant impôts) - -

-
- {' '} - - - Pension calculée pour 172 trimestres cotisés au régime général - sans variations de revenus. - - -
-
- {' '} - - - Pension calculée à titre indicatif pour 172 trimestres cotisés - au régime des indépendants sans variations de revenus. - - -
-
- {plafondAutoEntrepreneurDépassé ? ( - '—' - ) : ( - <> - {' '} - - - Pension calculée pour 172 trimestres cotisés en - auto-entrepreneur sans variations de revenus. - - - - )} -
- -

- Nombre de trimestres validés (pour la retraite) -

-
-
- -
-
- -
-
- {plafondAutoEntrepreneurDépassé ? ( - '—' - ) : ( - - )} -
- -

- Indemnités journalières (en cas d'arrêt maladie) -

-
-
- - - - - ( - {' '} - - pour les accidents de trajet/travail et maladie pro - - ) - -
-
- -
-
- {plafondAutoEntrepreneurDépassé ? ( - '—' - ) : ( - - - - )} -
- - )} -
-
-
-

- - Créer mon entreprise en tant que : - -

-
- {!hideAssimiléSalarié && ( - - )} - - {!hideAutoEntrepreneur && ( - - )} -
-
- - ) -} diff --git a/mon-entreprise/source/components/SearchBar.css b/mon-entreprise/source/components/SearchBar.css deleted file mode 100644 index 0dc42725b..000000000 --- a/mon-entreprise/source/components/SearchBar.css +++ /dev/null @@ -1,8 +0,0 @@ -li.active { - background: var(--color); - color: var(--textColor); -} - -li.active a { - color: var(--textColor); -} diff --git a/mon-entreprise/source/components/SearchBar.tsx b/mon-entreprise/source/components/SearchBar.tsx deleted file mode 100644 index 63207facc..000000000 --- a/mon-entreprise/source/components/SearchBar.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react' -import { Trans, useTranslation } from 'react-i18next' -import { DottedName } from 'modele-social' -import Worker from 'worker-loader!./SearchBar.worker.js' // TODO: importing a worker this way doesn't work with babel transpilation https://github.com/betagouv/mon-entreprise/issues/1554 -import RuleLink from './RuleLink' -import './SearchBar.css' -import { useEngine } from './utils/EngineContext' -import { utils } from 'publicodes' - -const worker = new Worker() - -type SearchBarProps = { - showListByDefault?: boolean -} - -type SearchItem = { - title: string - dottedName: DottedName - espace: Array -} - -type Matches = Array<{ - key: string - value: string - indices: Array<[number, number]> -}> - -function highlightMatches(str: string, matches: Matches) { - if (!matches?.length) { - return str - } - const indices = matches[0].indices - .sort(([a], [b]) => a - b) - .map(([x, y]) => [x, y + 1]) - .reduce( - (acc, value) => - acc[acc.length - 1][1] <= value[0] ? [...acc, value] : acc, - [[0, 0]] - ) - .flat() - return [...indices, str.length].reduce( - ([highlight, prevIndice, acc], currentIndice, i) => { - const currentStr = str.slice(prevIndice, currentIndice) - return [ - !highlight, - currentIndice, - [ - ...acc, - - {currentStr} - , - ], - ] as [boolean, number, Array] - }, - [false, 0, []] as [boolean, number, Array] - )[2] -} -export default function SearchBar({ - showListByDefault = false, -}: SearchBarProps) { - const rules = useEngine().getParsedRules() - const [input, setInput] = useState('') - const [results, setResults] = useState< - Array<{ - item: SearchItem - matches: Matches - }> - >([]) - const { i18n } = useTranslation() - - const searchIndex: Array = useMemo( - () => - Object.values(rules) - .filter(utils.ruleWithDedicatedDocumentationPage) - .map((rule) => ({ - title: - rule.title + - (rule.rawNode.acronyme ? ` (${rule.rawNode.acronyme})` : ''), - dottedName: rule.dottedName, - espace: rule.dottedName.split(' . ').reverse(), - })), - [rules] - ) - - useEffect(() => { - worker.postMessage({ - rules: searchIndex, - }) - - worker.onmessage = ({ data: results }) => setResults(results) - return () => { - worker.onmessage = null - } - }, [searchIndex, setResults]) - - return ( - <> - { - const input = e.target.value - if (input.length > 0) worker.postMessage({ input }) - setInput(input) - }} - /> - {!!input.length && !results.length ? ( -

- - Aucun résultat ne correspond à cette recherche - -

- ) : ( -
    - {(showListByDefault && !results.length && !input.length - ? searchIndex - .filter((item) => item.espace.length === 2) - .map((item) => ({ item, matches: [] })) - : results - ) - .slice(0, showListByDefault ? 100 : 6) - .map(({ item, matches }) => ( -
  • - - - {item.espace - .slice(1) - .reverse() - .map((name) => ( - - {highlightMatches( - name, - matches.filter( - (m) => m.key === 'espace' && m.value === name - ) - )}{' '} - ›{' '} - - ))} -
    -
    - {highlightMatches( - item.title, - matches.filter((m) => m.key === 'title') - )} -
    -
  • - ))} -
- )} - - ) -} diff --git a/mon-entreprise/source/components/SearchBar.worker.js b/mon-entreprise/source/components/SearchBar.worker.js deleted file mode 100644 index a7f9df895..000000000 --- a/mon-entreprise/source/components/SearchBar.worker.js +++ /dev/null @@ -1,32 +0,0 @@ -import Fuse from 'fuse.js' - -let searchWeights = [ - { - name: 'espace', - weight: 0.6, - }, - { - name: 'title', - weight: 0.4, - }, -] - -let fuse = null -onmessage = function (event) { - if (event.data.rules) - fuse = new Fuse(event.data.rules, { - keys: searchWeights, - includeMatches: true, - minMatchCharLength: 2, - useExtendedSearch: true, - distance: 50, - threshold: 0.3, - }) - - if (event.data.input) { - let results = fuse - .search(event.data.input + '|' + event.data.input.replace(/ /g, '|')) - .slice() - postMessage(results) - } -} diff --git a/mon-entreprise/source/components/SearchButton.tsx b/mon-entreprise/source/components/SearchButton.tsx deleted file mode 100644 index 0a55202ae..000000000 --- a/mon-entreprise/source/components/SearchButton.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect, useState } from 'react' -import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' -import Overlay from './Overlay' -import SearchBar from './SearchBar' - -type SearchButtonProps = { - invisibleButton?: boolean -} - -export default function SearchButton({ invisibleButton }: SearchButtonProps) { - const [visible, setVisible] = useState(false) - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (!(e.ctrlKey && e.key === 'k')) return - setVisible(true) - - e.preventDefault() - return false - } - window.addEventListener('keydown', handleKeyDown) - return () => { - window.removeEventListener('keydown', handleKeyDown) - } - }, []) - - const close = () => setVisible(false) - - return visible ? ( - -

- Chercher dans la documentation -

- -
- ) : invisibleButton ? null : ( - - ) -} diff --git a/mon-entreprise/source/components/ShareSimulationBanner.tsx b/mon-entreprise/source/components/ShareSimulationBanner.tsx deleted file mode 100644 index 3f05cec91..000000000 --- a/mon-entreprise/source/components/ShareSimulationBanner.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import Animate from 'Components/ui/animate' -import { LinkButton } from 'Components/ui/Button' -import React, { useContext, useEffect, useState } from 'react' -import { Trans, useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' -import { situationSelector } from 'Selectors/simulationSelectors' -import { TrackingContext } from '../ATInternetTracking' -import Banner from './Banner' -import { useParamsFromSituation } from './utils/useSearchParamsSimulationSharing' - -export default function ShareSimulationBanner() { - const [opened, setOpened] = useState(false) - const { t } = useTranslation() - const tracker = useContext(TrackingContext) - const situation = useSelector(situationSelector) - const searchParams = useParamsFromSituation(situation) - searchParams.set('utm_source', 'sharing') - - const shareAPIAvailable = !!window?.navigator?.share - - const getUrl = () => - [ - window.location.origin, - window.location.pathname, - '?', - searchParams.toString(), - ].join('') - - const startSharing = () => { - if (shareAPIAvailable) { - try { - window.navigator.share({ - title: document.title, - text: t( - 'shareSimulation.navigatorShare', - 'Ma simulation Mon Entreprise' - ), - url: getUrl(), - }) - } catch { - setOpened(true) - } - } else { - setOpened(true) - } - } - - return ( - - {opened ? ( - -
- setOpened(false)} - > - × - -

- {t('shareSimulation.modal.title', 'Votre lien de partage')}{' '} -

-

- - Voici le lien que vous pouvez envoyer pour accéder à votre - simulation. - -

- -
-
- ) : ( - - Pour partager cette simulation :{' '} - { - tracker.click.set({ - chapter1: 'feature:partage', - type: 'action', - name: 'démarré', - }) - tracker.dispatch() - startSharing() - }} - > - Générer un lien dédié - - - )} -
- ) -} - -function ShareSimulationPopup({ url }: { url: string }) { - const inputRef: React.RefObject = React.createRef() - const { t } = useTranslation() - const [linkCopied, setLinkCopied] = useState(false) - const tracker = useContext(TrackingContext) - - const selectInput = () => inputRef.current?.select() - useEffect(selectInput, []) - useEffect(() => { - const handler = setTimeout(() => setLinkCopied(false), 5000) - return () => { - clearTimeout(handler) - } - }, [linkCopied]) - - return ( - <> - - {navigator.clipboard ? ( - - ) : ( -

- {t( - 'shareSimulation.modal.helpText', - 'Le lien est sélectionné, vous pouvez le copier/coller' - )} -

- )} - - ) -} diff --git a/mon-entreprise/source/components/SimulateurWarning.tsx b/mon-entreprise/source/components/SimulateurWarning.tsx deleted file mode 100644 index 3b3f990f4..000000000 --- a/mon-entreprise/source/components/SimulateurWarning.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import Warning from 'Components/ui/WarningBlock' -import { Trans } from 'react-i18next' -import { SitePaths } from './utils/SitePathsContext' - -type SimulateurWarningProps = { - simulateur: Exclude -} - -export default function SimulateurWarning({ - simulateur, -}: SimulateurWarningProps) { - return ( - -
    - {simulateur == 'auto-entrepreneur' && ( - <> -
  • - - Les auto-entrepreneurs bénéficient d’un régime très simplifié - avec un taux forfaitaire pour le calcul des cotisations et - contributions sociales appliqué sur le chiffre d’affaires. Selon - le choix de la modalité de paiement des impôts il est appliqué - un abattement forfaitaire au titre des frais professionnels. Il - n’est pas possible de déduire des charges réelles en plus. Votre - revenu net est donc le chiffre d’affaires moins toutes les - charges engagées pour l’entreprise. - -
  • -
  • - - Le simulateur n'intègre pas la cotisation foncière des - entreprise (CFE) qui est dûe dès la deuxième année d'exercice. - Son montant varie fortement en fonction du chiffre d'affaires et - de la domiciliation de l'entreprise.{' '} - - Plus d'infos. - - -
  • - - )} - {simulateur !== 'artiste-auteur' && ( -
  • - - Les calculs sont indicatifs. Ils ne se substituent pas aux - décomptes réels de l’Urssaf, du fisc ou autre. - -
  • - )} - - {simulateur == 'profession-libérale' && ( - -
  • - Ce simulateur est à destination des professions libérales en BNC. - Il ne prend pas en compte les sociétés d'exercice libéral. -
  • -
    - )} - {simulateur === 'SASU' && ( -
  • - - L'impôt sur les sociétés et la gestion des dividendes ne sont pas - encore implémentés. - -
  • - )} - {simulateur === 'artiste-auteur' && ( - <> -
  • - - Cette estimation est proposée à titre indicatif. Elle est faite - à partir des éléments réglementaires applicables et des éléments - que vous avez saisis, mais elle ne tient pas compte de - l'ensemble de votre situation. Le montant réel de vos - cotisations peut donc être différent. - -
  • -
  • - - Ce simulateur permet d'estimer le montant de vos cotisations à - partir de votre revenu projeté - -
  • - - )} - {['indépendant', 'profession-libérale'].includes(simulateur) && ( -
  • - - Le montant des cotisations est calculé pour un revenu sur l'année - 2020. - -
  • - )} - {['profession-libérale'].includes(simulateur) && ( -
  • - - Pour les professions réglementées, le simulateur ne calcule pas le - montant des cotisations à l'ordre. Elles doivent être ajoutées - manuellement dans la case « charges de fonctionnement ». - -
  • - )} -
-
- ) -} diff --git a/mon-entreprise/source/components/Simulation.tsx b/mon-entreprise/source/components/Simulation.tsx deleted file mode 100644 index 01e03ef79..000000000 --- a/mon-entreprise/source/components/Simulation.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import Conversation, { - ConversationProps, -} from 'Components/conversation/Conversation' -import PageFeedback from 'Components/Feedback' -import SearchButton from 'Components/SearchButton' -import ShareSimulationBanner from 'Components/ShareSimulationBanner' -import TargetSelection from 'Components/TargetSelection' -import * as Animate from 'Components/ui/animate' -import Progress from 'Components/ui/Progress' -import { useSimulationProgress } from 'Components/utils/useNextQuestion' -import React from 'react' -import { Trans } from 'react-i18next' -import { useSelector } from 'react-redux' -import { firstStepCompletedSelector } from 'Selectors/simulationSelectors' -import { TrackPage } from '../ATInternetTracking' -import SeeAnswersButton from './conversation/SeeAnswersButton' - -type SimulationProps = { - explanations?: React.ReactNode - results?: React.ReactNode - children?: React.ReactNode - customEndMessages?: ConversationProps['customEndMessages'] - showPeriodSwitch?: boolean -} - -export default function Simulation({ - explanations, - results, - children, - customEndMessages, - showPeriodSwitch, -}: SimulationProps) { - const firstStepCompleted = useSelector(firstStepCompletedSelector) - - const simulationBloc = children ?? ( - - ) - return ( - <> - {simulationBloc} - - - {!firstStepCompleted && } - {firstStepCompleted && ( - <> - - {results} - - -
-
-
- {explanations && ( - <> -
-
- {explanations} -
- - )} -
-
-
- - Êtes-vous satisfait de ce simulateur ? - - } - /> -
-
-
-
-
- - - )} - - ) -} - -export function Questions({ - customEndMessages, -}: { - customEndMessages?: ConversationProps['customEndMessages'] -}) { - const progress = useSimulationProgress() - - return ( - <> -
-
-
- {progress < 1 && ( -

- - - Améliorez votre simulation en répondant aux questions - - -

- )} - -
- - -
-
- {progress < 1 && ( - - )} - - ) -} diff --git a/mon-entreprise/source/components/SimulationGoals.tsx b/mon-entreprise/source/components/SimulationGoals.tsx deleted file mode 100644 index 3d4b475ba..000000000 --- a/mon-entreprise/source/components/SimulationGoals.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { updateSituation } from 'Actions/actions' -import classnames from 'classnames' -import Animate from 'Components/ui/animate' -import { DottedName } from 'modele-social' -import { formatValue, UNSAFE_isNotApplicable } from 'publicodes' -import { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { - firstStepCompletedSelector, - situationSelector, - targetUnitSelector, -} from 'Selectors/simulationSelectors' -import RuleInput, { InputProps } from './conversation/RuleInput' -import RuleLink from './RuleLink' -import AnimatedTargetValue from './ui/AnimatedTargetValue' -import { useEngine } from './utils/EngineContext' - -type SimulationGoalsProps = { - className?: string - children: React.ReactNode -} - -const InitialRenderContext = createContext(true) - -export function SimulationGoals({ - className = '', - children, -}: SimulationGoalsProps) { - const initialRender = useInitialRender() - - return ( - -
-
-
    {children}
-
-
-
- ) -} - -function useInitialRender() { - const [initialRender, setInitialRender] = useState(true) - useEffect(() => { - setInitialRender(false) - }, []) - return initialRender -} - -type SimulationGoalProps = { - dottedName: DottedName - labelWithQuestion?: boolean - small?: boolean - appear?: boolean - editable?: boolean - boolean?: boolean - alwaysShow?: boolean - onUpdateSituation?: ( - name: DottedName, - ...rest: Parameters - ) => void -} - -export function SimulationGoal({ - dottedName, - labelWithQuestion = false, - small = false, - onUpdateSituation, - appear = true, - alwaysShow = false, - editable = true, - boolean = false, //TODO : remove when type inference works in publicodes -}: SimulationGoalProps) { - const dispatch = useDispatch() - const engine = useEngine() - const currentUnit = useSelector(targetUnitSelector) - const situation = useSelector(situationSelector) - const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName) - const evaluation = engine.evaluate({ - valeur: dottedName, - ...(!boolean ? { unité: currentUnit, arrondi: 'oui' } : {}), - }) - const rule = engine.getRule(dottedName) - const initialRender = useContext(InitialRenderContext) - const [isFocused, setFocused] = useState(false) - const isFirstStepCompleted = useSelector(firstStepCompletedSelector) - const onChange = useCallback( - (x) => { - dispatch(updateSituation(dottedName, x)) - onUpdateSituation?.(dottedName, x) - }, - [dispatch, onUpdateSituation] - ) - if ( - !alwaysShow && - (isNotApplicable === true || - (!(dottedName in situation) && - evaluation.nodeValue === false && - !(dottedName in evaluation.missingVariables))) - ) { - return null - } - const displayAsInput = - !isFirstStepCompleted || isFocused || dottedName in situation - if ( - small && - !editable && - (evaluation.nodeValue === false || evaluation.nodeValue === null) - ) { - return null - } - return ( -
  • - -
    -
    - -
    - {small && } -
    - {editable ? ( - setFocused(true)} - onBlur={() => setFocused(false)} - onChange={onChange} - useSwitch - /> - ) : ( - - {formatValue(evaluation, { displayedUnit: '€' })} - - )} - {!isFocused && !small && ( - - - - )} -
    -
    -
    -
  • - ) -} diff --git a/mon-entreprise/source/components/StackedBarChart.test.js b/mon-entreprise/source/components/StackedBarChart.test.js deleted file mode 100644 index 207dfc9bb..000000000 --- a/mon-entreprise/source/components/StackedBarChart.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { expect } from 'chai' -import { roundedPercentages } from './StackedBarChart' - -describe('roundedPercentages', () => { - it('rounds correctly', () => { - expect(roundedPercentages([500, 250, 250])).to.deep.equal([50, 25, 25]) - expect(roundedPercentages([501, 251, 248])).to.deep.equal([50, 25, 25]) - expect(roundedPercentages([506, 257, 237])).to.deep.equal([50, 26, 24]) - expect(roundedPercentages([509, 259, 232])).to.deep.equal([51, 26, 23]) - expect(roundedPercentages([503, 253, 244])).to.deep.equal([50, 25, 25]) - }) -}) diff --git a/mon-entreprise/source/components/StackedBarChart.tsx b/mon-entreprise/source/components/StackedBarChart.tsx deleted file mode 100644 index 0e232ef16..000000000 --- a/mon-entreprise/source/components/StackedBarChart.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import RuleLink from 'Components/RuleLink' -import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting' -import { Names } from 'modele-social/dist/names' -import { EvaluatedNode } from 'publicodes' -import React from 'react' -import { useSelector } from 'react-redux' -import { animated, useSpring } from 'react-spring' -import { targetUnitSelector } from 'Selectors/simulationSelectors' -import styled from 'styled-components' -import { useEngine } from './utils/EngineContext' - -const BarStack = styled.div` - display: flex; - border-radius: 0.4em; - overflow: hidden; -` - -const BarItem = styled.div` - height: 26px; - border-right: 2px solid white; - transition: width 0.3s ease-out; - - &:last-child { - border-right: none; - } -` - -const BarStackLegend = styled.div` - display: flex; - margin-top: 10px; - flex-direction: column; - - @media (min-width: 800px) { - flex-direction: row; - text-align: center; - } -` - -const BarStackLegendItem = styled.div` - flex: 1 1 0px; - color: #555; - - strong { - display: inline-block; - color: #111; - margin-left: 8px; - } -` - -const SmallCircle = styled.span` - display: inline-block; - height: 11px; - width: 11px; - margin-right: 10px; - border-radius: 100%; -` - -function integerAndDecimalParts(value: number) { - const integer = Math.floor(value) - const decimal = value - integer - return { integer, decimal } -} - -// This function calculates rounded percentages so that the sum of all -// returned values is always 100. For instance: [60, 30, 10]. -export function roundedPercentages(values: Array) { - const sum = (a = 0, b: number) => a + b - const total = values.reduce(sum, 0) - const percentages = values.map((value) => - integerAndDecimalParts((value / total) * 100) - ) - const totalRoundedPercentage = percentages - .map((v) => v.integer) - .reduce(sum, 0) - const indexesToIncrement = percentages - .map((percentage, index) => ({ ...percentage, index })) - .sort((a, b) => b.decimal - a.decimal) - .map(({ index }) => index) - .splice(0, 100 - totalRoundedPercentage) - - return percentages.map( - ({ integer }, index) => - integer + (indexesToIncrement.includes(index) ? 1 : 0) - ) -} - -type StackedBarChartProps = { - data: Array<{ - color?: string - value: EvaluatedNode['nodeValue'] - legend: React.ReactNode - key: string - }> -} - -export function StackedBarChart({ data }: StackedBarChartProps) { - const [intersectionRef, displayChart] = useDisplayOnIntersecting({ - threshold: 0.5, - }) - const percentages = roundedPercentages( - data.map((d) => (typeof d.value === 'number' && d.value) || 0) - ) - const dataWithPercentage = data.map((data, index) => ({ - ...data, - percentage: percentages[index], - })) - - const styles = useSpring({ opacity: displayChart ? 1 : 0 }) - return ( - - - {dataWithPercentage - // has a border so we don't want to display empty bars - // (even with width 0). - .filter(({ percentage }) => percentage !== 0) - .map(({ key, color, percentage }) => ( - - ))} - - - {dataWithPercentage.map(({ key, percentage, color, legend }) => ( - - - {legend} - {percentage} % - - ))} - - - ) -} - -type StackedRulesChartProps = { - data: Array<{ color?: string; dottedName: Names; title?: string }> -} - -export default function StackedRulesChart({ data }: StackedRulesChartProps) { - const engine = useEngine() - const targetUnit = useSelector(targetUnitSelector) - return ( - ({ - key: dottedName, - value: engine.evaluate({ valeur: dottedName, unité: targetUnit }) - .nodeValue, - legend: {title}, - color, - }))} - /> - ) -} diff --git a/mon-entreprise/source/components/TargetSelection.css b/mon-entreprise/source/components/TargetSelection.css deleted file mode 100644 index 4cd362298..000000000 --- a/mon-entreprise/source/components/TargetSelection.css +++ /dev/null @@ -1,181 +0,0 @@ -#targetSelection h1 { - margin: 0.6em; - color: inherit; - text-align: center; - font-size: 180%; -} - -#targetSelection .targets { - list-style: none; - padding: 0; - margin: -1rem 0; -} - -#targetSelection .targets > li:first-child { - border-top: none; -} - -#targetSelection .targets li.small-target { - font-size: 85%; -} -#targetSelection .targets li.small-target .optionTitle a { - font-weight: normal; -} - -#targetSelection .targets li.small-target { - border-top: none; - padding: 0.4rem 1rem; -} - -#targetSelection .targets li.small-target .targetInput, -#targetSelection .targets li.small-target .editableTarget { - border-width: 1px; - border-radius: 0.2rem; - padding-top: 0; - padding-bottom: 0; -} - -#targetSelection .targets > li { - border-top: 1px dashed rgba(255, 255, 255, 0.6); - padding: 0.8rem 1rem; - margin-left: -1rem; - margin-right: -1rem; -} -.light #targetSelection .targets > li { - border-top: 1px dashed var(--darkColor); -} -.light #targetSelection .targets > li.small-target, -.light #targetSelection .targets > li:not(.small-target):first-of-type { - border-top: none; -} - -#targetSelection .targets > li .main { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; -} -#targetSelection .targets > li .guide-lecture { - flex: 1; - border-bottom: 1px dashed rgba(255, 255, 255, 0.25); - margin-left: 1rem; -} - -#targetSelection li .header { - display: flex; - align-items: center; -} -#targetSelection li:not(.small-target) .header { - flex: 1; -} -#targetSelection .targets > li.small-target .header p { - display: none; -} -#targetSelection .optionTitle + p { - margin-top: 0.2rem; - margin-bottom: 0; -} -#targetSelection .optionTitle a { - color: inherit; - font-weight: bold; - text-decoration: none; -} -#targetSelection .small-target .targetInputOrValue a { - text-decoration: none; -} -#targetSelection .optionTitle a:hover, -#targetSelection .small-target .targetInputOrValue a:hover, -#targetSelection .aidesGlimpse a:hover { - text-decoration: underline; -} - -@media (min-width: 660px) { - #targetSelection { - font-size: 115%; - } -} - -@media (hover: none) { - #targetSelection .optionTitle a, - #targetSelection .small-target .targetInputOrValue a { - text-decoration: underline; - } -} - -#targetSelection #labelNew { - background: #e2011c; /* Couleur de la marianne */ - border-radius: 0.3em; - padding: 0 0.3em; -} - -#targetSelection .targetInputOrValue { - font-size: 115%; - margin-left: 1rem; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -#targetSelection .aidesGlimpse { - font-size: 70%; - display: flex; -} -#targetSelection .aidesGlimpse a { - display: inline-block; - text-decoration: none; -} - -#targetSelection input { - margin: 2.7px 0; -} - -#targetSelection .targetInput { - width: 5.5em; - max-width: 7.5em; - text-align: right; - background: rgba(255, 255, 255, 0.2); - padding: 0.2rem 0.6rem; - border-radius: 0.3rem; - border: 1px solid; - font-size: inherit; -} - -#targetSelection .targetInput.focused { - box-shadow: 0 0 0px 2px white; -} - -.light #targetSelection .targetInput { - color: var(--darkColor); - border-color: var(--darkColor); - background: white; -} -.light #targetSelection .targetInput.focused { - box-shadow: 0 0 0px 2px var(--darkColor); -} -.light #targetSelection .optionTitle { - color: var(--darkColor); -} - -#targetSelection .editableTarget { - max-width: 7.5em; - width: 5.5em; - - display: inline-block; - transition: all 0.2s; - text-align: right; - padding: 0.2rem 0.6rem; - - background: transparent; - border: 1px dashed rgba(255, 255, 255, 0.5); - border-radius: 0.3rem; - - font-size: inherit; -} - -#targetSelection .unit { - margin-left: 0.4em; - font-size: 110%; - vertical-align: middle; -} - -/* Autre idée pour styler les checkboxes https://codepen.io/KenanYusuf/pen/PZKEKd */ diff --git a/mon-entreprise/source/components/TargetSelection.tsx b/mon-entreprise/source/components/TargetSelection.tsx deleted file mode 100644 index 5687a1b37..000000000 --- a/mon-entreprise/source/components/TargetSelection.tsx +++ /dev/null @@ -1,334 +0,0 @@ -import { updateSituation } from 'Actions/actions' -import classnames from 'classnames' -import Value, { Condition } from 'Components/EngineValue' -import PeriodSwitch from 'Components/PeriodSwitch' -import RuleLink from 'Components/RuleLink' -import Animate from 'Components/ui/animate' -import AnimatedTargetValue from 'Components/ui/AnimatedTargetValue' -import { ThemeColorsContext } from 'Components/utils/colors' -import { - EngineContext, - useEngine, - useInversionFail, -} from 'Components/utils/EngineContext' -import { SitePathsContext } from 'Components/utils/SitePathsContext' -import { DottedName } from 'modele-social' -import { Names } from 'modele-social/dist/names' -import { - ASTNode, - EvaluatedNode, - formatValue, - reduceAST, - RuleNode, -} from 'publicodes' -import { Fragment, useCallback, useContext, useState } from 'react' -import emoji from 'react-easy-emoji' -import { Trans, useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { useLocation } from 'react-router-dom' -import { RootState } from 'Reducers/rootReducer' -import { - situationSelector, - targetUnitSelector, -} from 'Selectors/simulationSelectors' -import InputSuggestions from './conversation/InputSuggestions' -import CurrencyInput from './CurrencyInput/CurrencyInput' -import './TargetSelection.css' - -export default function TargetSelection({ showPeriodSwitch = true }) { - const objectifs = useSelector( - (state: RootState) => state.simulation?.config.objectifs || [] - ) - const colors = useContext(ThemeColorsContext) - return ( -
    - {((typeof objectifs[0] === 'string' - ? [{ objectifs }] - : objectifs) as Array<{ - icône?: string - nom?: string - objectifs: Array - }>).map(({ icône, objectifs: targets, nom }, index: number) => ( - -
    -
    - {nom && ( -

    - {!!icône && emoji(icône)} {nom} -

    - )} -
    - {index === 0 && showPeriodSwitch && } -
    -
    -
      - {' '} - {targets.map((target) => ( - - ))} -
    -
    -
    - ))} -
    - ) -} - -type TargetProps = { - dottedName: DottedName -} -type TargetType = EvaluatedNode & - RuleNode['rawNode'] & - RuleNode & { dottedName: DottedName } - -const Target = ({ dottedName }: TargetProps) => { - const engine = useEngine() - const rule = engine.getRule(dottedName) - const evaluation = engine.evaluate({ - valeur: dottedName, - unité: useSelector(targetUnitSelector), - arrondi: 'oui', - }) - const target: TargetType = { ...evaluation, ...rule.rawNode, ...rule } - const isSmallTarget = - !rule.rawNode.question || - (dottedName in evaluation.missingVariables && - Object.keys(evaluation.missingVariables).length === 1) - if ( - target.nodeValue === false || - (isSmallTarget && !target.question && !target.nodeValue) - ) { - return null - } - return ( -
  • - -
    -
    -
    - {isSmallTarget && } - -
    -
    -
    -
  • - ) -} - -const Header = ({ target }: { target: TargetType }) => { - const sitePaths = useContext(SitePathsContext) - const { t } = useTranslation() - const { pathname } = useLocation() - // TODO : Super hacky, we want to amend one label in the covid simulator, but - // because the label is fetched from the global state we have to do a hack - // here based on the URL. - const hackyShowPeriod = pathname === sitePaths.simulateurs['chômage-partiel'] - return ( - - - - - {target.title} - {hackyShowPeriod && ' ' + t('mensuel')} - - -

    {target.résumé}

    -
    -
    - ) -} - -type TargetInputOrValueProps = { - target: TargetType - isSmallTarget: boolean -} - -function TargetInputOrValue({ - target, - isSmallTarget, -}: TargetInputOrValueProps) { - const { language } = useTranslation().i18n - const colors = useContext(ThemeColorsContext) - const dispatch = useDispatch() - const [isFocused, setFocused] = useState(false) - const targetUnit = useSelector(targetUnitSelector) - const engine = useContext(EngineContext) - const situation = useSelector(situationSelector) - const value = - (engine.evaluate({ - valeur: target.dottedName, - unité: targetUnit, - arrondi: 'oui', - }).nodeValue as number) ?? undefined - const blurValue = useInversionFail() && !isFocused - const onSuggestionClick = useCallback( - (value) => { - dispatch(updateSituation(target.dottedName, value)) - }, - [target.dottedName, dispatch] - ) - const isSituationEmpty = Object.keys(situation).length === 0 - const isActive = target.dottedName in situation - const onChange = useCallback( - (evt) => - dispatch( - updateSituation(target.dottedName, { - valeur: evt.target.value, - unité: targetUnit, - }) - ), - [targetUnit, target, dispatch] - ) - return ( - <> - - {target.question ? ( - <> - {!isFocused && } - { - setFocused(true) - }} - onBlur={() => setTimeout(() => setFocused(false), 200)} - language={language} - /> - - ) : ( - - {value && Number.isNaN(value) ? ( - '—' - ) : ( - - {formatValue(value, { displayedUnit: '€', language })} - - )} - - )} - {target.dottedName.includes('prix du travail') && } - {target.dottedName === 'contrat salarié . rémunération . net' && ( - - )} - - {(isActive || isFocused) && ( -
    - -
    - -
    -
    -
    - )} - - ) -} -function TitreRestaurant() { - const targetUnit = useSelector(targetUnitSelector) - const dottedName = - 'contrat salarié . frais professionnels . titres-restaurant . montant' - return ( - - -
    - - +{' '} - - - {' '} - en titres-restaurant {emoji(' 🍽')} - -
    -
    -
    - ) -} -function AidesGlimpse() { - const targetUnit = useSelector(targetUnitSelector) - const dottedName = 'contrat salarié . aides employeur' as Names - const engine = useEngine() - const aides = engine.getRule(dottedName) - // Dans le cas où il n'y a qu'une seule aide à l'embauche qui s'applique, nous - // faisons un lien direct vers cette aide, plutôt qu'un lien vers la liste qui - // est une somme des aides qui sont toutes nulle sauf l'aide active. - const aideLink = reduceAST( - (acc, node) => { - if (node.nodeKind === 'somme') { - const aidesNotNul = node.explanation - .map((n) => engine.evaluate(n)) - .filter(({ nodeValue }) => nodeValue !== false) - if (aidesNotNul.length === 1) { - return (aidesNotNul[0] as ASTNode & { nodeKind: 'reference' }) - .dottedName as DottedName - } else { - return acc - } - } - }, - dottedName, - aides - ) - return ( - - -
    - - en incluant{' '} - - - {' '} - d'aides {emoji(aides.rawNode.icônes ?? '')} - -
    -
    -
    - ) -} diff --git a/mon-entreprise/source/components/TypeFormEmbed.js b/mon-entreprise/source/components/TypeFormEmbed.js deleted file mode 100644 index e98b0c83c..000000000 --- a/mon-entreprise/source/components/TypeFormEmbed.js +++ /dev/null @@ -1,35 +0,0 @@ -import { Helmet } from 'react-helmet' - -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/mon-entreprise/source/components/conversation/Aide.css b/mon-entreprise/source/components/conversation/Aide.css deleted file mode 100644 index 48b4344f7..000000000 --- a/mon-entreprise/source/components/conversation/Aide.css +++ /dev/null @@ -1,37 +0,0 @@ -#helpWrapper { - margin: 2em auto 0; - display: none; -} - -#help { - border-radius: 0.4em; - padding: 0.1em 1em; - background: #e7f8e1; - border: 1px solid #9fbd94; - position: relative; -} - -#help blockquote { - font-style: italic; - font-size: 95%; - opacity: 0.95; - border-left: 4px solid #4b4b66; - margin-left: 0.3em; - padding-left: 0.6em; -} - -#helpWrapper.active { - display: block; -} - -#help #closeHelp { - font-size: 150%; - position: absolute; - top: 0.4em; - right: 0em; - cursor: pointer; -} - -#help h3 { - margin: 0.3em 0; -} diff --git a/mon-entreprise/source/components/conversation/Aide.tsx b/mon-entreprise/source/components/conversation/Aide.tsx deleted file mode 100644 index ee67c7d1b..000000000 --- a/mon-entreprise/source/components/conversation/Aide.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { explainVariable } from 'Actions/actions' -import Overlay from 'Components/Overlay' -import { Markdown } from 'Components/utils/markdown' -import { useContext } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { RootState } from 'Reducers/rootReducer' -import { References } from 'publicodes-react' -import { Trans } from 'react-i18next' -import './Aide.css' -import { EngineContext } from 'Components/utils/EngineContext' - -export default function Aide() { - const explained = useSelector((state: RootState) => state.explainedVariable) - const engine = useContext(EngineContext) - const dispatch = useDispatch() - - const stopExplaining = () => dispatch(explainVariable()) - - if (!explained) return null - - const rule = engine.getRule(explained), - text = rule.rawNode.description, - refs = rule.rawNode.références - - return ( - -
    -

    {rule.title}

    - - {refs && ( - <> -

    - En savoir plus -

    - - - )} -
    -
    - ) -} diff --git a/mon-entreprise/source/components/conversation/AnswerList.css b/mon-entreprise/source/components/conversation/AnswerList.css deleted file mode 100644 index 7f879137c..000000000 --- a/mon-entreprise/source/components/conversation/AnswerList.css +++ /dev/null @@ -1,24 +0,0 @@ -.answer-list table { - border-collapse: collapse; - width: 100%; -} -.answer-list tr:nth-child(2n + 1) { - background: none !important; -} - -.answer-list td { - padding: 0 0.8rem; -} - -.answer-list td:last-of-type { - text-align: start; -} - -.answer-list button.answer { -} -.answer-list .answerContent { -} - -.answer-list button.answer:hover { - opacity: 0.8; -} diff --git a/mon-entreprise/source/components/conversation/AnswerList.tsx b/mon-entreprise/source/components/conversation/AnswerList.tsx deleted file mode 100644 index c9385dad5..000000000 --- a/mon-entreprise/source/components/conversation/AnswerList.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { goToQuestion, resetSimulation } from 'Actions/actions' -import Overlay from 'Components/Overlay' -import { useEngine } from 'Components/utils/EngineContext' -import { useNextQuestions } from 'Components/utils/useNextQuestion' -import { EvaluatedNode, formatValue } from 'publicodes' -import emoji from 'react-easy-emoji' -import { Trans, useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { DottedName } from 'modele-social' -import { situationSelector } from 'Selectors/simulationSelectors' -import './AnswerList.css' - -type AnswerListProps = { - onClose: () => void -} - -export default function AnswerList({ onClose }: AnswerListProps) { - const dispatch = useDispatch() - const engine = useEngine() - const answeredQuestions = (Object.keys( - useSelector(situationSelector) - ) as Array).map((dottedName) => - engine.evaluate(engine.getRule(dottedName)) - ) - - const nextSteps = useNextQuestions().map((dottedName) => - engine.evaluate(engine.getRule(dottedName)) - ) - - return ( - - {!!answeredQuestions.length && ( - <> -

    - {emoji('📋 ')} - Mes réponses - - {emoji('🗑')}{' '} - - -

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

    - {emoji('🔮 ')} - Prochaines questions -

    - - - )} -
    - ) -} - -function StepsTable({ - rules, - onClose, -}: { - rules: Array - onClose: () => void -}) { - const dispatch = useDispatch() - const language = useTranslation().i18n.language - return ( - - - {rules.map((rule) => ( - - - - - ))} - -
    - - - span { - border-bottom-color: var(--textColorOnWhite); - padding: 0.05em 0em; - display: inline-block; - } - `} - > - - {formatValue(rule, { language })} - - {' '} -
    - ) -} diff --git a/mon-entreprise/source/components/conversation/Conversation.tsx b/mon-entreprise/source/components/conversation/Conversation.tsx deleted file mode 100644 index 9adc4f9b9..000000000 --- a/mon-entreprise/source/components/conversation/Conversation.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { goToQuestion, stepAction, updateSituation } from 'Actions/actions' -import RuleInput, { InputProps } from 'Components/conversation/RuleInput' -import Notifications from 'Components/Notifications' -import QuickLinks from 'Components/QuickLinks' -import * as Animate from 'Components/ui/animate' -import { EngineContext } from 'Components/utils/EngineContext' -import { useNextQuestions } from 'Components/utils/useNextQuestion' -import React, { useContext, useEffect } from 'react' -import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { - answeredQuestionsSelector, - situationSelector, -} from 'Selectors/simulationSelectors' -import { TrackPage } from '../../ATInternetTracking' -import Aide from './Aide' -import './conversation.css' -import { ExplicableRule } from './Explicable' - -export type ConversationProps = { - customEndMessages?: React.ReactNode -} - -export default function Conversation({ customEndMessages }: ConversationProps) { - const dispatch = useDispatch() - const engine = useContext(EngineContext) - const currentQuestion = useNextQuestions()[0] - const situation = useSelector(situationSelector) - const currentQuestionIsAnswered = situation[currentQuestion] != null - const previousAnswers = useSelector(answeredQuestionsSelector) - useEffect(() => { - if (currentQuestion) { - dispatch(goToQuestion(currentQuestion)) - } - }, [dispatch, currentQuestion]) - const setDefault = () => dispatch(stepAction(currentQuestion)) - - const goToPrevious = () => - dispatch(goToQuestion(previousAnswers.slice(-1)[0])) - - const submit = (source: string) => { - dispatch(stepAction(currentQuestion, source)) - } - - const onChange: InputProps['onChange'] = (value) => { - dispatch(updateSituation(currentQuestion, value)) - } - - const handleKeyDown = ({ key }: React.KeyboardEvent) => { - if (key === 'Escape') { - setDefault() - } else if (key === 'Enter') { - submit('enter') - } - } - - return currentQuestion ? ( - <> - {Object.keys(situation).length !== 0 && ( - - )} - -
    - -
    -

    - {engine.getRule(currentQuestion).rawNode.question} -   - -

    - -
    - -
    -
    -
    - -
    - {previousAnswers.length > 0 && ( - <> - - - )} - {currentQuestionIsAnswered ? ( - - ) : ( - - )} -
    - -
    - - - ) : ( -
    - -

    - {emoji('🌟')}{' '} - - Vous avez complété cette simulation - -

    -

    - {customEndMessages ? ( - customEndMessages - ) : ( - - Vous avez maintenant accès à l'estimation la plus précise possible. - - )} -

    -
    - ) -} diff --git a/mon-entreprise/source/components/conversation/DateInput.tsx b/mon-entreprise/source/components/conversation/DateInput.tsx deleted file mode 100644 index 91bf82325..000000000 --- a/mon-entreprise/source/components/conversation/DateInput.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { InputProps } from 'Components/conversation/RuleInput' -import { useCallback, useMemo } from 'react' -import styled from 'styled-components' -import InputSuggestions from './InputSuggestions' - -export default function DateInput({ - suggestions, - onChange, - missing, - id, - onSubmit, - required, - value, -}: InputProps) { - const dateValue = useMemo(() => { - if (!value || typeof value !== 'string') return undefined - const [day, month, year] = value.split('/') - return `${year}-${month}-${day}` - }, [value]) - // const [currentValue, setCurrentValue] = useState(dateValue) - const handleDateChange = useCallback( - (evt) => { - if (!evt.target.value) { - return onChange(undefined) - } - const [year, month, day] = evt.target.value.split('-') - if (+year < 1700) { - return - } - if (year.length > 4) { - return - } - if ([day, month, year].some((x) => Number.isNaN(+x))) { - return - } - onChange(`${day}/${month}/${year}`) - }, - [onChange] - ) - return ( -
    -
    - {suggestions && ( - { - onChange(value) - }} - onSecondClick={() => onSubmit?.('suggestion')} - /> - )} - -
    -
    - ) -} - -const DateStyledInput = styled.input` - font-family: 'Roboto', sans-serif; - text-transform: uppercase; - height: inherit; -` diff --git a/mon-entreprise/source/components/conversation/Explicable.css b/mon-entreprise/source/components/conversation/Explicable.css deleted file mode 100644 index 99449e3dd..000000000 --- a/mon-entreprise/source/components/conversation/Explicable.css +++ /dev/null @@ -1,23 +0,0 @@ -@media print { - .explicable { - display: none; - } -} -.explicable .icon { - display: inline-block; - padding: 0.15rem 0.6rem; - height: 100%; - margin-left: 0.2rem; - text-align: center; - cursor: pointer; - vertical-align: text-top; -} - -.explicable .icon:hover { - filter: brightness(90%); -} - -.variantLeaf .explicable .icon img { - width: 1.5em !important; - height: 1.5em !important; -} diff --git a/mon-entreprise/source/components/conversation/Explicable.tsx b/mon-entreprise/source/components/conversation/Explicable.tsx deleted file mode 100644 index 8b4a2bc98..000000000 --- a/mon-entreprise/source/components/conversation/Explicable.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { explainVariable } from 'Actions/actions' -import Overlay from 'Components/Overlay' -import { EngineContext } from 'Components/utils/EngineContext' -import { DottedName } from 'modele-social' -import React, { useContext, useState } from 'react' -import emoji from 'react-easy-emoji' -import { useDispatch } from 'react-redux' -import usePortal from 'react-useportal' -import './Explicable.css' - -export function ExplicableRule({ dottedName }: { dottedName: DottedName }) { - const engine = useContext(EngineContext) - const dispatch = useDispatch() - - // Rien à expliquer ici, ce n'est pas une règle - if (dottedName == null) return null - const rule = engine.getRule(dottedName) - - if (rule.rawNode.description == null) return null - - //TODO montrer les variables de type 'une possibilité' - - return ( - - ) -} - -export function Explicable({ children }: { children: React.ReactNode }) { - const { Portal } = usePortal({ - bindTo: document.getElementsByClassName('app-container')[0] as HTMLElement, - }) - const [isOpen, setIsOpen] = useState(false) - return ( - <> - {isOpen && ( - - setIsOpen(false)}>{children} - - )} - - - ) -} diff --git a/mon-entreprise/source/components/conversation/Input.tsx b/mon-entreprise/source/components/conversation/Input.tsx deleted file mode 100644 index 3194abac8..000000000 --- a/mon-entreprise/source/components/conversation/Input.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { formatValue, Unit } from 'publicodes' -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import NumberFormat from 'react-number-format' -import { currencyFormat, debounce } from '../../utils' -import InputSuggestions from './InputSuggestions' -import { InputProps } from './RuleInput' - -// TODO: fusionner Input.js et CurrencyInput.js -export default function Input({ - suggestions, - onChange, - onSubmit, - id, - value, - missing, - unit, - autoFocus, -}: InputProps & { - unit: Unit | undefined -}) { - const debouncedOnChange = useCallback(debounce(550, onChange), []) - const { language } = useTranslation().i18n - const unité = formatValue({ nodeValue: value ?? 0, unit }, { language }) - .replace(/[\d,.]/g, '') - .trim() - const { thousandSeparator, decimalSeparator } = currencyFormat(language) - // const [currentValue, setCurrentValue] = useState(value) - return ( -
    -
    - { - onChange(value) - }} - onSecondClick={() => onSubmit?.('suggestion')} - /> - { - if (floatValue !== value) { - debouncedOnChange( - floatValue != undefined ? { valeur: floatValue, unité } : {} - ) - } - }} - autoComplete="off" - {...{ [missing ? 'placeholder' : 'value']: value ?? '' }} - /> -  {unité} -
    -
    - ) -} diff --git a/mon-entreprise/source/components/conversation/InputSuggestions.tsx b/mon-entreprise/source/components/conversation/InputSuggestions.tsx deleted file mode 100644 index 085a3d667..000000000 --- a/mon-entreprise/source/components/conversation/InputSuggestions.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { ASTNode } from 'publicodes' -import { toPairs } from 'ramda' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' - -type InputSuggestionsProps = { - suggestions?: Record - onFirstClick: (val: ASTNode) => void - onSecondClick?: (val: ASTNode) => void -} - -export default function InputSuggestions({ - suggestions = {}, - onSecondClick = (x) => x, - onFirstClick, -}: InputSuggestionsProps) { - const [suggestion, setSuggestion] = useState() - const { t, i18n } = useTranslation() - - return ( -
    - {toPairs(suggestions).map(([text, value]: [string, ASTNode]) => { - return ( - - ) - })} -
    - ) -} diff --git a/mon-entreprise/source/components/conversation/ParagrapheInput.tsx b/mon-entreprise/source/components/conversation/ParagrapheInput.tsx deleted file mode 100644 index c200144c3..000000000 --- a/mon-entreprise/source/components/conversation/ParagrapheInput.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Evaluation } from 'publicodes/dist/types/AST/types' -import { useCallback } from 'react' -import { debounce } from '../../utils' -import { InputProps } from './RuleInput' - -export default function ParagrapheInput({ - onChange, - value, - id, - missing, - autoFocus, -}: InputProps & { value: Evaluation }) { - const debouncedOnChange = useCallback(debounce(1000, onChange), []) - - return ( -
    -