Merge pull request #77 from sgmap/associations

Ajout du type d'entreprise association non lucrative
pull/84/head
Mael 2017-10-04 17:51:22 +02:00 committed by GitHub
commit 37037eb6f4
17 changed files with 322 additions and 178 deletions

View File

@ -62,7 +62,7 @@
"css-loader": "^0.28.1",
"csv-loader": "^2.1.1",
"daggy": "^1.1.0",
"eslint": "^4.4.1",
"eslint": "^4.8.0",
"eslint-plugin-react": "^7.0.1",
"express": "^4.15.3",
"fantasy-combinators": "0.0.1",

View File

@ -10,13 +10,28 @@
calcul: https://www.service-public.fr/professionnels-entreprises/vosdroits/F31326
# TODO - exlure stage, apprentissage
non applicable si: salaire brut > plafond cice
non applicable si:
une de ces conditions:
- assiette cotisations sociales > plafond cice
- entreprise . association non lucrative
formule:
multiplication:
assiette: assiette cotisations sociales
taux: 7%
exemples:
- nom: SMIC
situation:
assiette cotisations sociales: 2300
entreprise . association non lucrative: non
valeur attendue: 161
- nom: Non applicable si association
situation:
assiette cotisations sociales: 2300
entreprise . association non lucrative: oui
valeur attendue: 0
- espace: contrat salarié
nom: plafond cice
# TODO: calcul du smic proratisé

View File

@ -0,0 +1,42 @@
- espace: contrat salarié
nom: CITS
titre: Crédit d'impôt de taxe sur les salaire
aide:
type: avantage fiscal
thème: aide bas salaires
démarches: non
description: |
La loi de finances pour 2017 instaure, au bénéfice des associations et organismes sans but lucratif (OSBL),
un dispositif de crédit d'impôt de taxe sur les salaires (CITS).
références:
fiche: https://www.service-public.fr/associations/actualites/A11012
non applicable si:
une de ces conditions:
- assiette cotisations sociales > plafond CITS
- ≠ entreprise . association non lucrative
formule:
multiplication:
assiette: assiette cotisations sociales
taux: 4%
exemples:
- nom: SMIC
situation:
assiette cotisations sociales: 2300
entreprise . association non lucrative: oui
valeur attendue: 92
- nom: Non applicable si organisme lucratif
situation:
assiette cotisations sociales: 2300
entreprise . association non lucrative: non
valeur attendue: 0
- espace: contrat salarié
nom: plafond CITS
formule: multiplicateur CITS * smic mensuel
- espace: contrat salarié
nom: multiplicateur CITS
formule: 2.5

View File

@ -23,14 +23,30 @@
Code du travail - Article L6322-37 : https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000022234996&cidTexte=LEGITEXT000006072050
exemples:
- nom: Non applicable si CDI
situation:
type de contrat: CDI
assiette cotisations sociales: 1480
valeur attendue: 0
- nom: SMIC
situation:
type de contrat: CDD
événement: aucun
motif: accroissement activité
contrat jeune vacances: non
assiette cotisations sociales: 1480
valeur attendue: 14.8
- nom: salaire médian
situation:
contrat salarié . CDD . motif: classique . usage
type de contrat: CDD
événement: aucun
motif: accroissement activité
contrat jeune vacances: non
assiette cotisations sociales: 2300
valeur attendue: 23

View File

@ -10,7 +10,9 @@
notes: Taxe complexe, comportant notamment des exonérations non prises en compte ici.
# non applicable si: Entreprise . association non lucrative
non applicable si: Entreprise . association non lucrative
# 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
formule:
somme:
- taxe d'apprentissage de base

View File

@ -4,6 +4,8 @@
description: |
Sont assujetties les associations à but non lucratif et les entreprises non soumises à la TVA ou payant la TVA sur moins de 10% de leur chiffre. Les particuliers employeurs, les employeurs agricoles, les établissements d'enseignement supérieur, les auto-entrepreneurs ne sont pas concernés.
question: L'entreprise est-elle assujettie à la taxe sur les salaires ?
# variable non utilisée pour l'instant, comme dans le simulateur v1
# à ajouter quand nous aurons des mécanismes logiques plus évolués (notamment 'applicable si')
- espace: contrat salarié
nom: taxe sur les salaires annuelle
@ -26,6 +28,13 @@
taux: 13.6%
- au-dessus de: 152279
taux: 20%
exemples:
- nom: salaire médian
situation:
assiette taxe sur les salaires: 2300
valeur attendue: 2639.16 # calcul annuel : 7721×4.25%+(154177721)×8.5%+(2760015417)×13.6%
- espace: contrat salarié
nom: assiette taxe sur les salaires
@ -33,5 +42,18 @@
- espace: contrat salarié
nom: taxe sur les salaires
non applicable si: ≠ assujettie à la taxe sur les salaires
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.
non applicable si: ≠ Entreprise . association non lucrative
formule: taxe sur les salaires annuelle / 12
exemples:
- nom: non applicable par défaut
situation:
salaire brut: 2300
valeur attendue: 0
- nom: association non lucrative
# Ce test ne sert qu'à tester la condition "association non lucrative", tant que nous faisons face à la limitation des calculs temporels
situation:
Entreprise . association non lucrative: oui
taxe sur les salaires annuelle: 2639.16
valeur attendue: 219.93

View File

@ -196,6 +196,7 @@
formule:
somme:
- CICE
- CITS
- réduction générale
- exonération JEI
@ -237,9 +238,6 @@

View File

@ -28,3 +28,8 @@
suggestions:
1: 1
5: 5
- espace: entreprise
nom: association non lucrative
description: L'entreprise est une association non lucrative
question: S'agit-il d'une association à but non lucratif ?

View File

@ -78,6 +78,7 @@
#results ul {
display: inline-flex;
justify-content: space-around;
@ -89,78 +90,6 @@
}
#results li {
margin: 0 1em 0;
text-align: center;
width: 25%;
}
#results li a {
text-decoration: none;
}
#results .rule-type {
color: white;
border: none;
font-size: 85%;
line-height: 2em;
font-weight: 600;
margin: .6em 0 .1em;
}
#results .rule-box {
padding: .6em 1em;
color: #333350;
background: white;
border-radius: 3px;
white-space: nowrap;
color: #333350;
height: 6em;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
#results .rule-name {
font-size: 175%;
font-weight: 600;
}
#results li p {
margin: 0;
padding: 0 0;
font-size: 120%;
color: inherit;
width: 100%;
}
#results li.number p {
color: #4A89DC;
font-weight: bold;
}
#results li.irrelevant .rule-type {
color: rgba(255, 255, 255, 0.35);
}
#results li.irrelevant .rule-name {
text-decoration: line-through;
}
#results li.unsatisfied p {
font-style: italic;
}
#results li.irrelevant p {
font-weight: 600;
}
#results li p .figure {
font-size: 250%;
}
@media (max-width: 1280px) {
@ -201,12 +130,6 @@
width: 100%;
font-size: 90%;
}
#results ul li .rule-box p {
padding: 0.6em;
}
#results .rule-name {
font-size: 150%;
}
}

View File

@ -4,16 +4,12 @@ import classNames from 'classnames'
import {Link} from 'react-router-dom'
import {connect} from 'react-redux'
import { withRouter } from 'react-router'
import {formValueSelector} from 'redux-form'
import './Results.css'
import {capitalise0} from '../utils'
import {computeRuleValue, clearDict} from 'Engine/traverse'
import {clearDict} from 'Engine/traverse'
import {encodeRuleName} from 'Engine/rules'
import {getObjectives} from 'Engine/generateQuestions'
let fmt = new Intl.NumberFormat('fr-FR').format
let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits))
import RuleValueVignette from './rule/RuleValueVignette'
@withRouter
@connect(
@ -49,54 +45,17 @@ export default class Results extends Component {
<i className="fa fa-arrow-circle-left" aria-hidden="true"></i>Reprendre la simulation
</Link>
</div>
: <div id="results-titles">
: <div id="results-titles">
<h2>Vos résultats <i className="fa fa-hand-o-right" aria-hidden="true"></i></h2>
{do {let text = R.path(['simulateur', 'résultats'])(analysedSituation.root)
text &&
<p id="resultText">{text}</p>
}}
<p id="understandTip"><i className="fa fa-lightbulb-o" aria-hidden="true"></i><em>Cliquez pour comprendre chaque calcul</em></p>
</div>
</div>
}
<ul>
{explanation.map(
({name, dottedName, type, 'non applicable si': nonApplicable, formule: {nodeValue: formuleValue}}) =>
do {
//TODO quel bordel, à revoir
let
ruleValue = computeRuleValue(formuleValue, nonApplicable && nonApplicable.nodeValue),
unsatisfied = ruleValue == null,
nonApplicableValue = nonApplicable ? nonApplicable.nodeValue : false,
irrelevant = nonApplicableValue === true || formuleValue == 0,
number = nonApplicableValue == false && formuleValue != null
;<li key={name} className={classNames({unsatisfied, irrelevant, number})}>
<Link to={"/regle/" + encodeRuleName(name)} >
<div className="rule-type">
{type}
</div>
<div className="rule-box">
<div className="rule-name">
{capitalise0(name)}
</div>
<p>
{conversationStarted && (
irrelevant ?
"Vous n'êtes pas concerné"
: unsatisfied ?
'En attente de vos réponses...'
: <span className="figure">{humanFigure(2)(formuleValue) + '€'}</span>
)}
</p>
</div>
</Link>
{/* <div className="pointer">•</div> */}
</li>
}
)}
{explanation.map( rule => <RuleValueVignette key={rule.nom} {...rule} conversationStarted={conversationStarted} />)}
</ul>
</section>
)

View File

@ -65,12 +65,3 @@
#examples .example.ok i {
color: #2ecc71
}
#reportError {
background: #c0392b;
color: white;
border: none;
font-size: 100%;
padding: .3em .6em;
margin-top: 3em;
}

View File

@ -26,14 +26,11 @@ export default class Examples extends Component {
)(ex.situation)
let runExemple = analyseSituation(rules, rule.name)(v => exempleSituation[v]),
exempleCalculatedValue = runExemple["non applicable si"] &&
runExemple["non applicable si"].nodeValue
? null
: runExemple.formule.nodeValue
exempleValue = runExemple.nodeValue
return {
...ex,
ok: Math.abs( ex['valeur attendue'] - exempleCalculatedValue ) < .1, //TODO on peut sûrement faire mieux...
ok: Math.abs( ex['valeur attendue'] - exempleValue ) < .1, //TODO on peut sûrement faire mieux...
rule: runExemple
}
})
@ -61,11 +58,11 @@ export default class Examples extends Component {
: <i className="fa fa-times" aria-hidden="true"></i>
}</span>
<span className="name">{nom}</span>
{!ok &&
{!ok && focusedExample == nom &&
<div className="ko">
Ce test ne passe pas
{showValues && <span>
: la valeur attendue était {' '}
: le résultat attendu était {' '}
<span className="expected">{expected}</span>
</span>}
</div>
@ -83,12 +80,6 @@ export default class Examples extends Component {
</button>
</div>
}
<button
id="reportError">
<a href={"mailto:contact@embauche.beta.gouv.fr?subject=Erreur dans une règle " + name}>
<i className="fa fa-exclamation-circle" aria-hidden="true" style={{marginRight: '.6em'}}></i>Signaler une erreur
</a>
</button>
</div>
)
}

View File

@ -96,6 +96,16 @@
text-align: center;
}
#ruleValue {
margin-left: 3em;
font-weight: 600;
}
/*
Règles CSS d'affichage des algorithmes
*/
#rule-rules {
display: inline-flex;
justify-content: start;
@ -114,7 +124,6 @@
}
#algorithm {
margin-top: 3em;
margin-right: 10em;
}
.dictionaryPanelWrapper {
@ -348,3 +357,14 @@
justify-content: space-between;
align-items: baseline;
}
#reportError {
background: #c0392b;
color: white;
border: none;
font-size: 100%;
padding: .3em .6em;
margin: 3em auto 0;
display: block;
}

View File

@ -14,6 +14,7 @@ import References from './References'
import Algorithm from './Algorithm'
import Examples from './Examples'
import Helmet from 'react-helmet'
import {humanFigure} from './RuleValueVignette'
@connect(
state => ({
@ -54,12 +55,15 @@ export default class Rule extends Component {
// }
let
situationExists = !R.isEmpty(this.props.form)
conversationStarted = !R.isEmpty(this.props.form),
situationExists = conversationStarted || this.state.example != null
let
{type, name, description} = this.rule,
destinataire = R.path([type, 'destinataire'])(this.rule),
destinataireData = possiblesDestinataires[destinataire]
destinataireData = possiblesDestinataires[destinataire],
situationOrExampleRule = R.path(['example', 'rule'])(this.state) || this.rule,
ruleValue = situationOrExampleRule.nodeValue
return (
<div id="rule">
@ -80,17 +84,18 @@ export default class Rule extends Component {
<div id="destinataire">
<h2>Destinataire</h2>
{!destinataireData ?
<p>Non renseigné</p>
: <div>
<a href={destinataireData.lien} target="_blank">
{destinataireData.image &&
<img src={require('Règles/ressources/destinataires/' + destinataireData.image)} /> }
{!destinataireData.image &&
<div id="calligraphy">{destinataire}</div>
}
</a>
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
</div>
<p>Non renseigné</p>
:
<div>
<a href={destinataireData.lien} target="_blank">
{destinataireData.image &&
<img src={require('Règles/ressources/destinataires/' + destinataireData.image)} /> }
{!destinataireData.image &&
<div id="calligraphy">{destinataire}</div>
}
</a>
{destinataireData.nom && <div id="destinataireName">{destinataireData.nom}</div>}
</div>
}
</div>
@ -99,15 +104,32 @@ export default class Rule extends Component {
{this.renderReferences(this.rule)}
</div>
</section>
<div id="ruleValue" style={{visibility: situationExists ? 'visible' : 'hidden'}}>
<h2>Résultat</h2>
<p>
{ruleValue == 0
? 'Règle non applicable'
: ruleValue == null
? 'Situation incomplète'
: humanFigure(2)(ruleValue) + ' €'}
</p>
</div>
<section id="rule-calc">
<Algorithm {...{traversedRule: R.path(['example', 'rule'])(this.state) || this.rule, showValues: situationExists || this.state.example != null}}/>
<Algorithm traversedRule={situationOrExampleRule} showValues={situationExists} />
<Examples
situationExists={situationExists}
situationExists={conversationStarted}
rule={this.rule}
focusedExample={this.state.example}
showValues={this.state.showValues}
inject={example => this.setState({example, showValues: true})}/>
</section>
<button
id="reportError">
<a href={"mailto:contact@embauche.beta.gouv.fr?subject=Erreur dans une règle " + name}>
<i className="fa fa-exclamation-circle" aria-hidden="true" style={{marginRight: '.6em'}}></i>Signaler une erreur
</a>
</button>
</div>
)
}

View File

@ -0,0 +1,94 @@
.RuleValueVignette {
margin: 0 1em 0;
text-align: center;
width: 25%;
}
.RuleValueVignette li a {
text-decoration: none;
}
.RuleValueVignette .rule-type {
color: white;
border: none;
font-size: 85%;
line-height: 2em;
font-weight: 600;
margin: .6em 0 .1em;
}
.RuleValueVignette .rule-box {
padding: .6em 1em;
color: #333350;
background: white;
border-radius: 3px;
white-space: nowrap;
height: 6em;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.RuleValueVignette .rule-name {
font-size: 175%;
font-weight: 600;
}
.RuleValueVignette p {
margin: 0;
padding: 0 0;
font-size: 120%;
color: inherit;
width: 100%;
}
.RuleValueVignette.number p {
color: #4A89DC;
font-weight: bold;
}
.RuleValueVignette.unsatisfied p {
font-style: italic;
}
.RuleValueVignette.irrelevant p {
font-weight: 600;
}
.RuleValueVignette p .figure {
font-size: 250%;
}
.RuleValueVignette:not(.unsatisfied):not(.irrelevant) .rule-box {
border-bottom: .8em solid #4A89DC;
}
.RuleValueVignette:hover .rule-box {
background: #ddd;
}
.RuleValueVignette.irrelevant .rule-box {
background: rgba(255, 255, 255, 0.35);
}
.RuleValueVignette.irrelevant .rule-type {
color: rgba(255, 255, 255, 0.35);
}
.RuleValueVignette.irrelevant .rule-name {
text-decoration: line-through;
}
@media (max-width: 1280px) {
.RuleValueVignette .rule-box p {
padding: 0.6em;
}
.RuleValueVignette .rule-name {
font-size: 150%;
}
}

View File

@ -0,0 +1,47 @@
import React from "react"
import {Link} from 'react-router-dom'
import {encodeRuleName} from 'Engine/rules'
import classNames from 'classnames'
import {capitalise0} from '../../utils'
let fmt = new Intl.NumberFormat('fr-FR').format
export let humanFigure = decimalDigits => value => fmt(value.toFixed(decimalDigits))
import './RuleValueVignette.css'
export default ({
name,
type,
conversationStarted,
nodeValue: ruleValue
}) =>
do {
let
unsatisfied = ruleValue == null,
irrelevant = ruleValue == 0,
number = typeof ruleValue == 'number' && ruleValue > 0
;<span
key={name}
className={classNames('RuleValueVignette', { unsatisfied, irrelevant, number })}
>
<Link to={"/regle/" + encodeRuleName(name)}>
<div className="rule-type">
{type}
</div>
<div className="rule-box">
<div className="rule-name">
{capitalise0(name)}
</div>
<p>
{conversationStarted &&
(irrelevant
? "Vous n'êtes pas concerné"
: unsatisfied
? "En attente de vos réponses..."
: <span className="figure">
{humanFigure(2)(ruleValue) + "€"}
</span>)}
</p>
</div>
</Link>
</span>
}

View File

@ -328,20 +328,17 @@ let treat = (rules, rule) => rawNode => {
let parsedNode = onNodeType(rawNode)
return parsedNode.evaluate ? parsedNode :
{...parsedNode, evaluate: defaultEvaluate}
{...parsedNode, evaluate: defaultEvaluate}
}
//TODO c'est moche :
export let computeRuleValue = (formuleValue, condValue) =>
condValue === undefined
? formuleValue
: formuleValue === 0
? 0
: condValue === null
? null
: condValue === true
? formuleValue
: formuleValue === 0
? 0
: formuleValue
: condValue === null ? null : condValue === true ? 0 : formuleValue
export let treatRuleRoot = (rules, rule) => {
let evaluate = (situationGate, parsedRules, r) => {