Simulateur d'impôt sur les sociétés (#1230)

pull/1353/head
Maxime Quandalle 2021-01-08 14:34:37 +01:00 committed by GitHub
parent 938291eb4f
commit 9cbbac6861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 696 additions and 164 deletions

View File

@ -112,25 +112,156 @@ entreprise . imposition . IS . notification IS non intégré:
le calcul de l'impôt sur le revenu.
entreprise . bénéfice:
titre: Bénéfice de l'exercice
résumé: Imposable à l'impôt sur les sociétés
formule: chiffre d'affaires - charges dont rémunération dirigeant
entreprise . bénéfice . information sur le report de déficit:
type: notification
formule: bénéfice < 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).
[Plus d'infos sur service-public.fr](https://www.service-public.fr/professionnels-entreprises/vosdroits/F23628)
entreprise . résultat net:
résumé: Ce qu'il reste après impôt sur les sociétés
formule: bénéfice - impôt sur les sociétés
entreprise . exercice:
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 . impôt sur les sociétés:
unité: €/an
formule:
barème:
assiette: bénéfice
tranches:
- taux: 15%
plafond: 38120 €/an
- taux: 28%
plafond: 500000 €/an
- taux: 33.3%
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 service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23575
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 . impôt sur les sociétés . plafond taux réduit 1:
applicable si: éligible taux réduit
valeur: 38120 €/an
entreprise . impôt sur les sociétés . plafond taux réduit 2:
applicable si: éligible taux réduit
valeur: 500000 €/an
entreprise . 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 . impôt sur les sociétés . prorata temporis:
description: |
Lorsque la durée de lexercice 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 dexercice 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 . 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 limpôt sur les sociétés. Son montant nest pas déductible des résultats.
Lassiette bénéficie dun abattement important, et seules les entreprises réalisant plus de 2,3 millions deuros de bénéfices sont concernées par cette contribution.
formule:
produit:
taux: 3.3%
assiette:
allègement:
assiette: 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 dont rémunération dirigeant:
formule: charges + dirigeant . rémunération totale

View File

@ -0,0 +1,92 @@
import { updateSituation } from 'Actions/actions'
import { DottedName } from 'modele-social'
import { UNSAFE_isNotApplicable } from 'publicodes'
import { useDispatch, useSelector } from 'react-redux'
import { situationSelector } from 'Selectors/simulationSelectors'
import RuleInput, { RuleInputProps } from './conversation/RuleInput'
import { useEngine } from './utils/EngineContext'
import Animate from 'Components/ui/animate'
import { useState, useEffect, createContext, useContext } from 'react'
type SimulationGoalsProps = {
className?: string
children: React.ReactNode
}
const InitialRenderContext = createContext(false)
export function SimulationGoals({
className = '',
children,
}: SimulationGoalsProps) {
const initialRender = useInitialRender()
return (
<InitialRenderContext.Provider value={initialRender}>
<section className={`ui__ card ${className}`}>
<div id="targetSelection">
<ul className="targets">{children}</ul>
</div>
</section>
</InitialRenderContext.Provider>
)
}
function useInitialRender() {
const [initialRender, setInitialRender] = useState(true)
useEffect(() => {
setInitialRender(false)
}, [])
return initialRender
}
type SimulationGoalProps = {
dottedName: DottedName
} & Omit<RuleInputProps, 'onChange'>
export function SimulationGoal({
dottedName,
...otherProps
}: SimulationGoalProps) {
const dispatch = useDispatch()
const engine = useEngine()
const situation = useSelector(situationSelector)
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
const evaluation = engine.evaluate(dottedName)
const rule = engine.getRule(dottedName)
const initialRender = useContext(InitialRenderContext)
if (
isNotApplicable === true ||
(!(dottedName in situation) &&
evaluation.nodeValue === false &&
!(dottedName in evaluation.missingVariables))
) {
return null
}
return (
<li>
<Animate.appear unless={initialRender}>
<div className="main">
<div className="header">
<label htmlFor={dottedName}>
<span className="optionTitle">
{rule.rawNode.question || rule.title}
</span>
<p className="ui__ notice">{rule.rawNode.résumé}</p>
</label>
</div>
<div className="targetInputOrValue">
<RuleInput
className="targetInput"
isTarget
dottedName={dottedName}
onChange={(x) => dispatch(updateSituation(dottedName, x))}
useSwitch
/>
</div>
</div>
</Animate.appear>
</li>
)
}

View File

@ -13,6 +13,7 @@ type DateInputProps = {
onSubmit: RuleInputProps['onSubmit']
value: InputCommonProps['value']
suggestions: RuleNode['suggestions']
required: RuleInputProps['required']
}
export default function DateInput({
@ -20,6 +21,7 @@ export default function DateInput({
onChange,
id,
onSubmit,
required,
value,
}: DateInputProps) {
const dateValue = useMemo(() => {
@ -64,6 +66,7 @@ export default function DateInput({
id={id}
type="date"
value={dateValue}
required={required}
onChange={handleDateChange}
/>
</div>

View File

@ -24,6 +24,7 @@ export type RuleInputProps<Name extends string = DottedName> = {
useSwitch?: boolean
isTarget?: boolean
autoFocus?: boolean
required?: boolean
id?: string
className?: string
onSubmit?: (source: string) => void
@ -58,6 +59,7 @@ export default function RuleInput<Name extends string = DottedName>({
id,
isTarget = false,
autoFocus = false,
required = true,
className,
onSubmit = () => null,
}: RuleInputProps<Name>) {
@ -74,11 +76,11 @@ export default function RuleInput<Name extends string = DottedName>({
onChange,
autoFocus,
className,
required,
title: rule.title,
id: id ?? dottedName,
question: rule.rawNode.question,
suggestions: rule.suggestions,
required: true,
}
if (getVariant(engine.getRule(dottedName))) {
return (

View File

@ -15,6 +15,9 @@ export const engineOptions = {
.replace(/_plural$/, '')
return key || unit
},
formatUnit(unit: string, count: number): string {
return i18n?.t(`units:${unit}`, { count })
},
}
export function useEngine(): Engine<DottedName> {

View File

@ -1,5 +1,5 @@
import convert from 'color-convert'
import React, { createContext, useEffect, useRef } from 'react'
import React, { createContext, useContext, useEffect, useRef } from 'react'
/*
Hex to RGB conversion:
@ -55,9 +55,9 @@ const deriveAnalogousPalettes = (hex: string) => {
]
}
const generateTheme = (themeColor?: string) => {
export const generateTheme = (themeColor: string) => {
const // Use the default theme color if the host page hasn't made a choice
color = themeColor || '#2975D1',
color = themeColor,
lightColor = lightenColor(color, 10),
darkColor = lightenColor(color, -20),
lighterColor = lightenColor(color, 45),
@ -90,9 +90,13 @@ const generateTheme = (themeColor?: string) => {
}
}
const defaultColor = '#2975D1'
export type ThemeColors = ReturnType<typeof generateTheme>
export const ThemeColorsContext = createContext<ThemeColors>(generateTheme())
export const ThemeColorsContext = createContext<ThemeColors>(
generateTheme(defaultColor)
)
type ProviderProps = {
color?: string
@ -100,7 +104,7 @@ type ProviderProps = {
}
export function ThemeColorsProvider({ color, children }: ProviderProps) {
const colors = generateTheme(color)
const colors = generateTheme(color ?? useContext(ThemeColorsContext).color)
const divRef = useRef<HTMLDivElement>(null)
useEffect(() => {
Object.entries(colors).forEach(([key, value]) => {

View File

@ -2580,8 +2580,6 @@ contrat salarié . prix du travail:
résumé.fr: Dépensé par l'entreprise
titre.en: labor cost
titre.fr: Coût total
identifiant court.en: employee-laborcost
identifiant court.fr: salarie-coutembauche
contrat salarié . profession spécifique:
question.en: '[automatic] Does the employee work in one of the following professions?'
question.fr: Le salarié exerce t-il l'une des professions suivantes ?
@ -3046,8 +3044,6 @@ contrat salarié . rémunération . brut de base:
suggestions.salaire médian.fr: salaire médian
titre.en: Gross salary
titre.fr: Salaire brut
identifiant court.en: employee-brut
identifiant court.fr: salarie-brut
contrat salarié . rémunération . brut de base . équivalent temps plein:
question.en: What is the full-time equivalent salary?
question.fr: Quel est le salaire en équivalent temps plein ?
@ -3105,8 +3101,6 @@ contrat salarié . rémunération . net:
résumé.fr: Salaire net avant impôt
titre.en: Net salary
titre.fr: Salaire net
identifiant court.en: employee-net
identifiant court.fr: salarie-net
contrat salarié . rémunération . net après impôt:
description.en: >-
Net salary after deduction of the **neutral** income tax (also called
@ -3132,8 +3126,6 @@ contrat salarié . rémunération . net après impôt:
résumé.fr: Versé sur le compte bancaire
titre.en: Net salary after income tax
titre.fr: Salaire net après impôt
identifiant court.en: employee-netaftertax
identifiant court.fr: salarie-netapresimpot
contrat salarié . rémunération . net avec revenus de remplacement:
titre.en: '[automatic] net with replacement income'
titre.fr: net avec revenus de remplacement
@ -3292,8 +3284,6 @@ contrat salarié . rémunération . total:
résumé.fr: Dépensé par l'entreprise
titre.en: Total salary
titre.fr: Total chargé
identifiant court.fr: salarie-total
identifiant court.en: employee-total
contrat salarié . salarié majeur:
question.en: Is the employee over 18 (age of majority)?
question.fr: Le salarié est-il majeur ?
@ -3989,8 +3979,6 @@ dirigeant . auto-entrepreneur . net après impôt:
résumé.fr: Avant déduction des dépenses liées à l'activité
titre.en: net income after tax
titre.fr: revenu net après impôt
identifiant court.en: auto-entrepreneur-netaftertax
identifiant court.fr: auto-entrepreneur-netapresimpot
dirigeant . auto-entrepreneur . net de cotisations:
description.en:
This is the income net of contributions and expenses, before the
@ -4003,8 +3991,6 @@ dirigeant . auto-entrepreneur . net de cotisations:
résumé.fr: Avant impôt
titre.en: Net contribution income
titre.fr: Revenu net de cotisations
identifiant court.en: auto-entrepreneur-net
identifiant court.fr: auto-entrepreneur-net
dirigeant . auto-entrepreneur . notification calcul ACRE annuel:
description.en: >
[automatic] The ACRE rate used is the one corresponding to the current
@ -5776,8 +5762,6 @@ dirigeant . indépendant . revenu net de cotisations:
résumé.fr: Avant déduction de l'impôt sur le revenu
titre.en: net contribution income
titre.fr: revenu net de cotisations
identifiant court.en: independant-net
identifiant court.fr: independant-net
dirigeant . indépendant . revenu professionnel:
description.en: >
This is the income net of deductible contributions of the self-employed
@ -5858,8 +5842,6 @@ dirigeant . rémunération totale:
résumé.fr: Dépensé par l'entreprise
titre.en: Director total income
titre.fr: rémunération totale
identifiant court.en: director-total
identifiant court.fr: dirigeant-total
entreprise:
description.en: The contract binds a company and an employee
description.fr: |
@ -5951,8 +5933,27 @@ entreprise . auto entreprise impossible:
titre.en: auto enterprise impossible
titre.fr: auto entreprise impossible
entreprise . bénéfice:
titre.en: profit
titre.fr: bénéfice
résumé.en: '[automatic] Taxable for corporate income tax purposes'
résumé.fr: Imposable à l'impôt sur les sociétés
titre.en: '[automatic] Profit for the year'
titre.fr: Bénéfice de l'exercice
entreprise . bénéfice . information sur le report de déficit:
description.en: >
[automatic] Deficits incurred in one year can be carried forward to
subsequent years (carry-forward), or carried back to the previous year only
(carry-back).
[More info on service-public.fr](https://www.service-public.fr/professionnels-entreprises/vosdroits/F23628)
description.fr: >
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).
[Plus d'infos sur service-public.fr](https://www.service-public.fr/professionnels-entreprises/vosdroits/F23628)
titre.en: '[automatic] deficit carry forward information'
titre.fr: information sur le report de déficit
entreprise . catégorie d'activité:
description.en: Your activity category will determine a large part of the
calculations of contribution, contribution and tax.
@ -6164,8 +6165,6 @@ entreprise . charges:
résumé.fr: Toutes les dépenses nécessaires à l'entreprise
titre.en: expenses
titre.fr: charges de fonctionnement
identifiant court.en: company-expenses
identifiant court.fr: entreprise-charges
entreprise . charges dont rémunération dirigeant:
titre.en: expenses of which executive compensation
titre.fr: charges dont rémunération dirigeant
@ -6176,8 +6175,6 @@ entreprise . chiffre d'affaires:
résumé.fr: Montant total des recettes brutes (hors taxe)
titre.en: '[automatic] revenues'
titre.fr: chiffre d'affaires
identifiant court.en: company-turnover
identifiant court.fr: entreprise-ca
entreprise . chiffre d'affaires de société:
titre.en: company turnover
titre.fr: chiffre d'affaires de société
@ -6191,8 +6188,6 @@ entreprise . chiffre d'affaires minimum:
question.fr: Quel est votre chiffre d'affaires minimum envisagé ?
titre.en: Minimum turnover
titre.fr: chiffre d'affaires minimum
identifiant court.en: company-turnover-min
identifiant court.fr: entreprise-ca-min
entreprise . date de création:
description.en: >
[automatic] The activity start date (or creation date) is set at the time of
@ -6286,6 +6281,44 @@ entreprise . effectif . seuil . moins de 50:
entreprise . effectif . seuil . plus de 250:
titre.en: '[automatic] 251 and more'
titre.fr: 251 et plus
entreprise . exercice:
titre.en: '[automatic] exercise'
titre.fr: exercice
entreprise . exercice . date trop ancienne:
description.en:
'[automatic] The date entered is too old. The simulator does not
integrate the scales until 2018.'
description.fr: La date saisie est trop ancienne. Le simulateur n'intègre pas
les barèmes avant 2018.
titre.en: '[automatic] too old date'
titre.fr: date trop ancienne
entreprise . exercice . date trop éloignée:
description.en: '[automatic] The date entered is too far in the future. The
simulator does not integrate scales beyond 2022.'
description.fr: La date saisie est trop éloignée. Le simulateur n'intègre pas
les barèmes au delà de 2022.
titre.en: '[automatic] date too far in the future'
titre.fr: date trop éloignée
entreprise . exercice . durée:
titre.en: '[automatic] fiscal year'
titre.fr: durée de l'exercice
entreprise . exercice . durée maximale:
description.en: '[automatic] The maximum length of a fiscal year is 24 months.'
description.fr: La durée maximale d'un exercice comptable est de 24 mois.
titre.en: '[automatic] maximal duration'
titre.fr: durée maximale
entreprise . exercice . début:
titre.en: '[automatic] start'
titre.fr: début
entreprise . exercice . début après la fin:
description.en: '[automatic] The end of the fiscal year must be after the
beginning of the fiscal year.'
description.fr: La fin de l'exercice doit être postérieure à son début.
titre.en: '[automatic] start after completion'
titre.fr: début après la fin
entreprise . exercice . fin:
titre.en: '[automatic] end'
titre.fr: fin
entreprise . franchise de TVA:
description.en: |
[automatic] The VAT exemption is a device that exempts businesses from the
@ -6351,6 +6384,41 @@ entreprise . imposition . IS . notification IS non intégré:
entreprise . impôt sur les sociétés:
titre.en: corporate income tax
titre.fr: impôt sur les sociétés
entreprise . impôt sur les sociétés . contribution sociale:
description.en: >
[automatic] The social contribution on profits is a tax separate from the
corporate tax. Its amount is not deductible from profits.
The tax base benefits from a significant reduction, and only companies making more than EUR 2,3 million in profits are concerned by this contribution.
description.fr: >
La contribution sociale sur les bénéfices est un impôt distinct de limpôt
sur les sociétés. Son montant nest pas déductible des résultats.
Lassiette bénéficie dun abattement important, et seules les entreprises réalisant plus de 2,3 millions deuros de bénéfices sont concernées par cette contribution.
titre.en: '[automatic] social contribution'
titre.fr: contribution sociale
entreprise . impôt sur les sociétés . plafond taux réduit 1:
titre.en: '[automatic] ceiling reduced rate 1'
titre.fr: plafond taux réduit 1
entreprise . impôt sur les sociétés . plafond taux réduit 2:
titre.en: '[automatic] ceiling reduced rate 2'
titre.fr: plafond taux réduit 2
entreprise . impôt sur les sociétés . prorata temporis:
description.en: >
[automatic] When the duration of the fiscal year is not equal to one year,
we pro-rate the
ceilings used in the corporate tax scale.
description.fr: |
Lorsque la durée de lexercice 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.
titre.en: '[automatic] prorata temporis'
titre.fr: prorata temporis
entreprise . impôt sur les sociétés . éligible taux réduit:
titre.en: '[automatic] eligible reduced rate'
titre.fr: éligible taux réduit
entreprise . non assujettie à TVA:
description.en: >
[automatic] Some businesses are not subject to VAT.
@ -7264,8 +7332,6 @@ revenu net après impôt:
résumé.fr: Disponible sur votre compte en banque
titre.en: Net income after tax
titre.fr: revenu net après impôt
identifiant court.en: netaftertax
identifiant court.fr: netapresimpot
revenus net de cotisations:
description.en: |
l'impôt sur le revenu.

View File

@ -93,6 +93,7 @@ Modifier mes réponses: Change my answers
Mon entreprise: My company
Mon revenu: My income
Montant: Amount
Montant de l'impôt sur les sociétés: Amount of corporate income tax
Montant des cotisations: Amount of contributions
"Nom de l'entreprise ou SIREN ": Company name or SIREN code
Non: No
@ -714,6 +715,8 @@ gérer:
<0>Estimate the amount spent for hiring</0>
<1>Calculate how much your company will have to spend to pay your next employee</1>
is: <0>Estimate the amount of corporate income tax</0><1>Calculate the amount of
corporation tax based on your gross profit.</1>
revenus: >
<0>Calculate my net income</0>
@ -747,6 +750,10 @@ gérer:
titre: Manage my business
heure: hour
heures: hours
impotSociété:
exerciceDates: Exercise from <2></2> to <6></6>
warning: "This simulator is aimed at <2>“TPE”</2>: it takes into account the
reduced corporate tax rates."
imprimer: Print
impôt: tax
impôt sur le revenu: income tax
@ -1106,6 +1113,26 @@ pages:
title: "Independent: Urssaf income simulator"
shortname: Independent
title: Income simulator for the self-employed
is:
meta:
title: Corporate Tax Simulator
seo: <0>How is corporate tax calculated?</0><1>Corporate income tax applies to
the profits made by corporations (SA, SAS, SASU, SARL, etc.) and on an
optional basis for certain other kind of companies (EIRL, EURL, SNC,
etc.).</1><2>It is calculated on the basis of the profits made in France
during the financial year. The duration of a financial year is normally
12 months but it may be shorter or longer (in particular at the beginning
of activity or on the dissolution of the company). In this case, the tax
scale is scaled according to the length of the fiscal year, which is
taken into account in the simulator by changing the start and end dates
of the fiscal year.</2><3>Reduced rate and specific regimes</3><4>“TPE”
with a turnover of less than €7.63 million and 75% of their capital
owned by individuals benefit from a reduced corporate tax rate. This
rate is taken into account on the simulator and it is currently not
possible to simulate ineligibility for the reduced rates.</4><5>Finally,
there are specific tax regimes with dedicated rates for certain types of
capital gains (transfer of securities, transfer of patents). These
regimes are not included in the simulator.</5>
médecin:
shortname: Doctor
title: Income simulator for private practitioners
@ -1314,8 +1341,7 @@ selectionRégime:
urssaf: The figures are indicative and do not replace the actual accounts of the
Urssaf, impots.gouv.fr, etc
shareSimulation:
banner: "You can share your simulation: <2 onClick={onClick}>Share
link</2>"
banner: "You can share your simulation: <2 onClick={onClick}>Share link</2>"
modal:
button: Copy link
helpText: The link is already selected, you can just hit "copy".

View File

@ -49,15 +49,16 @@ type QuestionsKind =
| 'liste noire'
export type SimulationConfig = {
objectifs:
objectifs?:
| Array<DottedName>
| Array<{ icône: string; nom: string; objectifs: Array<DottedName> }>
'objectifs cachés': Array<DottedName>
'objectifs cachés'?: Array<DottedName>
situation: Simulation['situation']
bloquant?: Array<DottedName>
questions?: Partial<Record<QuestionsKind, Array<DottedName>>>
branches?: Array<{ nom: string; situation: SimulationConfig['situation'] }>
'unité par défaut': string
color?: string
}
export type Situation = Partial<Record<DottedName, any>>

View File

@ -5,17 +5,21 @@ import { useEngine } from 'Components/utils/EngineContext'
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import { Documentation, getDocumentationSiteMap } from 'publicodes-react'
import { useCallback, useContext, useMemo } from 'react'
import { useCallback, useContext, useMemo, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { Redirect, useLocation } from 'react-router-dom'
import { RootState } from 'Reducers/rootReducer'
import SearchBar from 'Components/SearchBar'
import { ThemeColorsProvider } from 'Components/utils/colors'
export default function RulePage() {
const currentSimulation = useSelector(
(state: RootState) => !!state.simulation?.url
)
const documentationColor = useSelector(
(state: RootState) => state.simulation?.config.color
)
const engine = useEngine()
const documentationPath = useContext(SitePathsContext).documentation.index
const { pathname } = useLocation()
@ -34,23 +38,25 @@ export default function RulePage() {
return (
<Animate.fromBottom>
<ScrollToTop key={pathname} />
<div
css={`
display: flex;
margin-top: 2rem;
justify-content: space-between;
`}
>
{currentSimulation ? <BackToSimulation /> : <span />}
<SearchButton key={pathname} />
</div>
<Documentation
language={i18n.language as 'fr' | 'en'}
engine={engine}
documentationPath={documentationPath}
referenceImages={referencesImages}
/>
{/* <button>Voir l</button> */}
<ThemeColorsProvider color={documentationColor}>
<div
css={`
display: flex;
margin-top: 2rem;
justify-content: space-between;
`}
>
{currentSimulation ? <BackToSimulation /> : <span />}
<SearchButton key={pathname} />
</div>
<Documentation
language={i18n.language as 'fr' | 'en'}
engine={engine}
documentationPath={documentationPath}
referenceImages={referencesImages}
/>
{/* <button>Voir l</button> */}
</ThemeColorsProvider>
</Animate.fromBottom>
)
}

View File

@ -171,6 +171,27 @@ export default function SocialSecurity() {
<Trans>Commencer</Trans>
</div>
</Link>
<Link
className="ui__ interactive card box light-border"
to={{
pathname: sitePaths.simulateurs.is,
state: {
fromGérer: true,
},
}}
>
<div className="ui__ big box-icon">{emoji('🗓')}</div>
<Trans i18nKey="gérer.choix.is">
<h3>Estimer le montant de limpôt sur les sociétés</h3>
<p className="ui__ notice">
Calculez le montant de l'impôt sur les sociétés à partir
de votre bénéfice.
</p>
</Trans>
<div className="ui__ small simple button hide-mobile">
<Trans>Commencer</Trans>
</div>
</Link>
</>
)}
</div>

View File

@ -1,109 +1,44 @@
import { setSimulationConfig, updateSituation } from 'Actions/actions'
import RuleInput from 'Components/conversation/RuleInput'
import { setSimulationConfig } from 'Actions/actions'
import { DistributionBranch } from 'Components/Distribution'
import Value, { Condition } from 'Components/EngineValue'
import SimulateurWarning from 'Components/SimulateurWarning'
import AidesCovid from 'Components/simulationExplanation/AidesCovid'
import { SimulationGoal, SimulationGoals } from 'Components/SimulationGoals'
import 'Components/TargetSelection.css'
import Animate from 'Components/ui/animate'
import { EngineContext, useEngine } from 'Components/utils/EngineContext'
import { EngineContext } from 'Components/utils/EngineContext'
import { DottedName } from 'modele-social'
import { UNSAFE_isNotApplicable } from 'publicodes'
import { createContext, useContext, useEffect, useState } from 'react'
import { useContext, useEffect, useState } from 'react'
import { Trans } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { situationSelector } from 'Selectors/simulationSelectors'
import styled from 'styled-components'
import config from './configs/artiste-auteur.yaml'
const InitialRenderContext = createContext(false)
function useInitialRender() {
const [initialRender, setInitialRender] = useState(true)
useEffect(() => {
setInitialRender(false)
}, [])
return initialRender
}
export default function ArtisteAuteur() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setSimulationConfig(config))
}, [])
const initialRender = useInitialRender()
return (
<>
<SimulateurWarning simulateur="artiste-auteur" />
<section className="ui__ light card">
<div id="targetSelection">
<ul className="targets">
<InitialRenderContext.Provider value={initialRender}>
<SimpleField dottedName="artiste-auteur . revenus . traitements et salaires" />
<SimpleField dottedName="artiste-auteur . revenus . BNC . recettes" />
<SimpleField dottedName="artiste-auteur . revenus . BNC . micro-bnc" />
<Warning dottedName="artiste-auteur . revenus . BNC . contrôle micro-bnc" />
<Condition expression="artiste-auteur . revenus . BNC . micro-bnc = non">
<SimpleField dottedName="artiste-auteur . revenus . BNC . frais réels" />
</Condition>
<SimpleField dottedName="artiste-auteur . cotisations . option surcotisation" />
</InitialRenderContext.Provider>
</ul>
</div>
</section>
<SimulationGoals className="light">
<SimulationGoal dottedName="artiste-auteur . revenus . traitements et salaires" />
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . recettes" />
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . micro-bnc" />
<Warning dottedName="artiste-auteur . revenus . BNC . contrôle micro-bnc" />
<Condition expression="artiste-auteur . revenus . BNC . micro-bnc = non">
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . frais réels" />
</Condition>
<SimulationGoal dottedName="artiste-auteur . cotisations . option surcotisation" />
</SimulationGoals>
<CotisationsResult />
</>
)
}
type SimpleFieldProps = {
dottedName: DottedName
}
function SimpleField({ dottedName }: SimpleFieldProps) {
const dispatch = useDispatch()
const engine = useEngine()
const situation = useSelector(situationSelector)
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
const evaluation = engine.evaluate(dottedName)
const rule = engine.getRule(dottedName)
const initialRender = useContext(InitialRenderContext)
if (
isNotApplicable === true ||
(!(dottedName in situation) &&
evaluation.nodeValue === false &&
!(dottedName in evaluation.missingVariables))
) {
return null
}
return (
<li>
<Animate.appear unless={initialRender}>
<div className="main">
<div className="header">
<label htmlFor={dottedName}>
<span className="optionTitle">
{rule.rawNode.question || rule.title}
</span>
<p className="ui__ notice">{rule.rawNode.résumé}</p>
</label>
</div>
<div className="targetInputOrValue">
<RuleInput
className="targetInput"
isTarget
dottedName={dottedName}
onChange={(x) => dispatch(updateSituation(dottedName, x))}
useSwitch
/>
</div>
</div>
</Animate.appear>
</li>
)
}
type WarningProps = {
dottedName: DottedName
}

View File

@ -78,6 +78,7 @@ export default function Simulateurs() {
<Trans>Autres outils</Trans>
</HeadingWithAnchorLink>
<div className="ui__ box-container">
<SimulateurCard {...simulators['is']} />
{language === 'fr' && (
<SimulateurCard {...simulators['demande-mobilité']} />
)}

View File

@ -0,0 +1,128 @@
import { setSimulationConfig, updateSituation } from 'Actions/actions'
import RuleInput from 'Components/conversation/RuleInput'
import Value from 'Components/EngineValue'
import Notifications from 'Components/Notifications'
import { SimulationGoal, SimulationGoals } from 'Components/SimulationGoals'
import Animate from 'Components/ui/animate'
import Warning from 'Components/ui/WarningBlock'
import { ThemeColorsContext } from 'Components/utils/colors'
import { useContext, useEffect } from 'react'
import emoji from 'react-easy-emoji'
import { Trans } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { situationSelector } from 'Selectors/simulationSelectors'
const config = {
color: '',
'unité par défaut': '€/an',
situation: {},
}
export default function ISSimulation() {
const dispatch = useDispatch()
const { color } = useContext(ThemeColorsContext)
useEffect(() => {
// HACK The config is mutated to avoid reseting the situation everytime the
// component is loaded. `setSimulationConfig` relies on config object
// equality. The `setSimulationConfig` design should be improved.
config.color = color
dispatch(setSimulationConfig(config))
}, [])
return (
<>
<Warning
localStorageKey={'app::simulateurs:warning-folded:v1:impôt-societé'}
>
<Trans i18nKey="impotSociété.warning">
Ce simulateur sadresse aux{' '}
<abbr title="Très Petite Entreprises">TPE</abbr> : il prend en compte
les taux réduits de limpôt sur les sociétés.
</Trans>
</Warning>
<ExerciceDate />
<Notifications />
<SimulationGoals className="plain">
<SimulationGoal dottedName="entreprise . bénéfice" autoFocus={true} />
</SimulationGoals>
<Explanations />
</>
)
}
function ExerciceDate() {
const dispatch = useDispatch()
return (
<p
css={`
display: flex;
justify-content: flex-end;
align-items: center;
opacity: 0.85;
font-style: italic;
input {
border: none;
border-radius: 0;
padding: 0;
margin: 0 10px 6px 10px;
border-bottom: 2px dotted var(--color);
}
`}
>
{emoji('📆')}&nbsp;
<Trans i18nKey="impotSociété.exerciceDates">
Exercice du{' '}
<RuleInput
dottedName={'entreprise . exercice . début'}
onChange={(x) =>
dispatch(updateSituation('entreprise . exercice . début', x))
}
/>{' '}
au{' '}
<RuleInput
dottedName={'entreprise . exercice . fin'}
onChange={(x) =>
dispatch(updateSituation('entreprise . exercice . fin', x))
}
/>
</Trans>
</p>
)
}
function Explanations() {
const situation = useSelector(situationSelector)
const showResult = situation['entreprise . bénéfice']
if (!showResult) {
return null
}
return (
<Animate.fromTop>
<p
className="ui__ lead card light-bg"
css={`
width: fit-content;
text-align: center;
margin: 2rem auto;
padding: 1rem 4rem;
strong {
font-size: 1.3em;
}
`}
>
<strong>
<Value
expression="entreprise . impôt sur les sociétés"
displayedUnit="€"
className="payslip__total"
/>
</strong>
<br />
<span className="ui__ notice">
<Trans>Montant de l'impôt sur les sociétés</Trans>
</span>
</p>
</Animate.fromTop>
)
}

View File

@ -1,4 +1,5 @@
import { setSimulationConfig } from 'Actions/actions'
import { ThemeColorsProvider } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import Meta from 'Components/utils/Meta'
import { default as React, useContext, useEffect } from 'react'
@ -41,8 +42,11 @@ export default function SimulateurPage({
)}
</>
)}
<Component />
{seoExplanations && !inIframe && seoExplanations}
<ThemeColorsProvider color={inIframe ? undefined : meta?.color}>
<Component />
{seoExplanations && !inIframe && seoExplanations}
</ThemeColorsProvider>
</>
)
}

View File

@ -32,6 +32,7 @@ import IndépendantSimulation, {
IndépendantPLSimulation,
} from './IndépendantSimulation'
import SalariéSimulation from './SalariéSimulation'
import ISSimulation from './ISSimulation'
import SchemeComparaisonPage from './SchemeComparaison'
import ÉconomieCollaborative from './ÉconomieCollaborative'
@ -54,6 +55,7 @@ const simulateurs = [
'avocat',
'expert-comptable',
'pamc',
'is',
] as const
export type SimulatorData = Record<
@ -65,6 +67,7 @@ export type SimulatorData = Record<
ogTitle?: string
ogDescription?: string
ogImage?: string
color?: string
}
icône: string
shortName: string
@ -742,6 +745,58 @@ export function getSimulatorsData({
shortName: t('pages.simulateurs.pamc.shortname', 'PAMC'),
component: PAMCHome,
},
is: {
icône: '🗓',
path: sitePaths.simulateurs.is,
iframe: 'impot-societe',
meta: {
title: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
description: t(
'pages.simulateurs.pamc.meta.description',
'Calculez votre impôt sur les sociétés'
),
color: '#E71D66',
},
shortName: t('pages.simulateurs.is.meta.title', 'Impôt sur les sociétés'),
title: t(
'pages.simulateurs.is.meta.title',
"Simulateur d'impôt sur les sociétés"
),
component: ISSimulation,
seoExplanations: (
<Trans i18nKey="pages.simulateurs.is.seo">
<h2>Comment est calculé limpôt sur les sociétés ?</h2>
<p>
Limpôt sur les sociétés sapplique aux bénéfices réalisés par les
sociétés de capitaux (SA, SAS, SASU, SARL, etc.) et sur option
facultative pour certaines autres sociétés (EIRL, EURL, SNC, etc.).
</p>
<p>
Il est calculé sur la base des bénéfices réalisés en France au cours
de lexercice comptable. La durée dun exercice est normalement dun
an mais il peut être plus court ou plus long (notamment en début
dactivité ou à la dissolution de lentreprise). Dans ce cas le
barème de limpôt est pro-ratisé en fonction de la durée de
lexercice, ce qui est pris en compte dans le simulateur en
modifiant les dates de début et de fin de lexercice.
</p>
<h2>Taux réduit et régimes spécifiques</h2>
<p>
Les PME réalisant moins de 7,63 millions deuros de chiffre
daffaire et dont le capital est détenu à 75% par des personnes
physiques bénéficient dun taux réduit dimpôt sur les sociétés. Ce
taux est pris en compte sur le simulateur et il nest pour linstant
pas possible de simuler linéligibilité aux taux réduits.
</p>
<p>
Enfin il existe des régimes dimpositions spécifiques avec des taux
dédiés pour certains types de plus-values (cession de titres,
cession de brevets). Ces régimes ne sont pas intégrés dans le
simulateur.
</p>
</Trans>
),
},
}
}

View File

@ -69,6 +69,7 @@ const sitePathsFr = {
index: '/économie-collaborative',
votreSituation: '/votre-situation',
},
is: '/impot-societe',
},
nouveautés: '/nouveautés',
stats: '/stats',
@ -128,6 +129,7 @@ const sitePathsEn = {
index: '/sharing-economy',
votreSituation: '/your-situation',
},
is: '/corporate-tax',
},
nouveautés: '/news',
integration: {

View File

@ -147,6 +147,29 @@ exports[`calculate simulations-auto-entrepreneur: échelle de revenus 10`] = `
Notifications affichées : dirigeant . auto-entrepreneur . contrôle seuil de CA dépassé, entreprise . seuil de franchise de TVA dépassé"
`;
exports[`calculate simulations-impot-société: bénéfices 1`] = `
"[0,0]
Notifications affichées : entreprise . bénéfice . information sur le report de déficit"
`;
exports[`calculate simulations-impot-société: bénéfices 2`] = `"[0,0]"`;
exports[`calculate simulations-impot-société: bénéfices 3`] = `"[300,0]"`;
exports[`calculate simulations-impot-société: bénéfices 4`] = `"[3000,0]"`;
exports[`calculate simulations-impot-société: bénéfices 5`] = `"[51044,0]"`;
exports[`calculate simulations-impot-société: bénéfices 6`] = `"[555044,0]"`;
exports[`calculate simulations-impot-société: bénéfices 7`] = `"[5595044,159457]"`;
exports[`calculate simulations-impot-société: prorata temporis 1`] = `"[275044,0]"`;
exports[`calculate simulations-impot-société: prorata temporis 2`] = `"[277936,0]"`;
exports[`calculate simulations-impot-société: prorata temporis 3`] = `"[272981,0]"`;
exports[`calculate simulations-indépendant: acre 1`] = `"[73025,23025,50000,51980,8213,41787,0,73025]"`;
exports[`calculate simulations-indépendant: activité 1`] = `"[26840,6840,20000,20726,601,19399,0,26840]"`;

View File

@ -0,0 +1,17 @@
bénéfices:
- entreprise . bénéfice: -2000 €/an
- entreprise . bénéfice: 0 €/an
- entreprise . bénéfice: 2000 €/an
- entreprise . bénéfice: 20000 €/an
- entreprise . bénéfice: 200000 €/an
- entreprise . bénéfice: 2000000 €/an
- entreprise . bénéfice: 20000000 €/an
prorata temporis:
- entreprise . bénéfice: 1000000 €/an
- entreprise . bénéfice: 1000000 €/an
entreprise . exercice . début: 01/01/2020
entreprise . exercice . fin: 01/06/2020
- entreprise . bénéfice: 1000000 €/an
entreprise . exercice . début: 01/01/2020
entreprise . exercice . fin: 01/06/2021

View File

@ -21,6 +21,7 @@ import autoEntrepreneurSituations from './simulations-auto-entrepreneur.yaml'
import independentSituations from './simulations-indépendant.yaml'
import professionsLibéralesSituations from './simulations-professions-libérales.yaml'
import remunerationDirigeantSituations from './simulations-rémunération-dirigeant.yaml'
import impotSocieteSituations from './simulations-impôt-société.yaml'
import employeeSituations from './simulations-salarié.yaml'
import aideDéclarationIndépendantsSituations from './aide-déclaration-indépendants.yaml'
@ -150,3 +151,10 @@ it('calculate simulations-professions-libérales', () => {
}
)
})
it('calculate simulations-impot-société', () => {
runSimulations(impotSocieteSituations, [
'entreprise . impôt sur les sociétés',
'entreprise . impôt sur les sociétés . contribution sociale',
])
})

View File

@ -11,7 +11,7 @@ import {
} from './replacement'
import { Rule, RuleNode } from './rule'
import * as utils from './ruleUtils'
import { getUnitKey } from './units'
import { formatUnit, getUnitKey } from './units'
const emptyCache = () => ({
_meta: { ruleStack: [] },
@ -60,6 +60,7 @@ export type Logger = {
type Options = {
logger: Logger
getUnitKey?: getUnitKey
formatUnit?: formatUnit
}
export type EvaluationFunction<Kind extends NodeKind = NodeKind> = (
this: Engine,

View File

@ -51,25 +51,28 @@ type NodeValuePointerProps = {
unit: Unit | undefined
}
export const NodeValuePointer = ({ data, unit }: NodeValuePointerProps) => (
<small
className="nodeValue"
style={{
background: 'white',
borderBottom: '0 !important',
margin: '0 0.2rem',
padding: '0 0.2rem',
textDecoration: 'none !important',
boxShadow: '0px 1px 2px 1px #d9d9d9, 0 0 0 1px #d9d9d9',
lineHeight: '1.6em',
borderRadius: '0.2rem',
}}
>
{formatValue(simplifyNodeUnit({ nodeValue: data, unit }), {
language: 'fr',
})}
</small>
)
export const NodeValuePointer = ({ data, unit }: NodeValuePointerProps) => {
const engine = useContext(EngineContext)
return (
<small
className="nodeValue"
style={{
background: 'white',
borderBottom: '0 !important',
margin: '0 0.2rem',
padding: '0 0.2rem',
textDecoration: 'none !important',
boxShadow: '0px 1px 2px 1px #d9d9d9, 0 0 0 1px #d9d9d9',
lineHeight: '1.6em',
borderRadius: '0.2rem',
}}
>
{formatValue(simplifyNodeUnit({ nodeValue: data, unit }), {
formatUnit: engine?.options?.formatUnit,
})}
</small>
)
}
// Un élément du graphe de calcul qui a une valeur interprétée (à afficher)
type NodeProps = {