mon-entreprise/publicodes/source/evaluateRule.ts

153 lines
4.1 KiB
TypeScript

import { map, pick, pipe } from 'ramda'
import { evaluationFunction } from '.'
import { typeWarning } from './error'
import { bonus, mergeAllMissing, mergeMissing } from './evaluation'
import { convertNodeToUnit } from './nodeUnits'
export const evaluateApplicability: evaluationFunction = function(node: any) {
const cacheKey = `${node.dottedName} [applicability]`
if (node.dottedName && this.cache[cacheKey]) {
return this.cache[cacheKey]
}
const evaluatedAttributes = pipe(
pick(['non applicable si', 'applicable si', 'rendu non applicable']) as (
x: any
) => any,
map(value => this.evaluateNode(value))
)(node) as any,
{
'non applicable si': notApplicable,
'applicable si': applicable,
'rendu non applicable': disabled
} = evaluatedAttributes,
parentDependencies = node.parentDependencies.map(parent =>
this.evaluateNode(parent)
)
const anyDisabledParent = parentDependencies.find(
parent => parent?.nodeValue === false
)
const { nodeValue, missingVariables = {} } = anyDisabledParent
? anyDisabledParent
: notApplicable?.nodeValue === true
? {
nodeValue: false,
missingVariables: notApplicable.missingVariables
}
: disabled?.nodeValue === true
? { nodeValue: false }
: applicable?.nodeValue === false
? { nodeValue: false, missingVariables: applicable.missingVariables }
: {
nodeValue: [notApplicable, applicable, ...parentDependencies].some(
n => n?.nodeValue === null
)
? null
: !notApplicable?.nodeValue &&
(applicable?.nodeValue == undefined || !!applicable?.nodeValue),
missingVariables: mergeAllMissing(
[...parentDependencies, notApplicable, disabled, applicable].filter(
Boolean
)
)
}
const res = {
...node,
nodeValue,
isApplicable: nodeValue,
missingVariables,
parentDependencies,
...evaluatedAttributes
}
if (node.dottedName) {
this.cache[cacheKey] = res
}
return res
}
export const evaluateFormula: evaluationFunction = function(node) {
const explanation = this.evaluateNode(node.explanation)
const { nodeValue, unit, missingVariables, temporalValue } = explanation
return {
...node,
nodeValue,
unit,
missingVariables,
explanation,
temporalValue
}
}
export const evaluateRule: evaluationFunction = function(node: any) {
this.cache._meta.contextRule.push(node.dottedName)
const applicabilityEvaluation = evaluateApplicability.call(this, node)
const {
missingVariables: condMissing,
nodeValue: isApplicable
} = applicabilityEvaluation
// evaluate the formula lazily, only if the applicability is known and true
let evaluatedFormula =
isApplicable && node.formule
? this.evaluateNode(node.formule)
: node.formule
if (node.unit) {
try {
evaluatedFormula = convertNodeToUnit(node.unit, evaluatedFormula)
} catch (e) {
typeWarning(
node.dottedName,
"L'unité de la règle est incompatible avec celle de sa formule",
e
)
}
}
const missingVariables = mergeMissing(
bonus(condMissing, !!Object.keys(condMissing).length),
evaluatedFormula.missingVariables
)
const temporalValue = evaluatedFormula.temporalValue
this.cache._meta.contextRule.pop()
return {
...node,
...applicabilityEvaluation,
...(node.formule && { formule: evaluatedFormula }),
nodeValue: evaluatedFormula.nodeValue,
unit: node.unit ?? evaluatedFormula.unit,
temporalValue,
isApplicable,
missingVariables
}
}
export const evaluateDisabledBy: evaluationFunction = function(node) {
const isDisabledBy = node.explanation.isDisabledBy.map(disablerNode =>
this.evaluateNode(disablerNode)
)
const nodeValue = isDisabledBy.some(
x => x.nodeValue !== false && x.nodeValue !== null
)
const explanation = { ...node.explanation, isDisabledBy }
return {
...node,
explanation,
nodeValue,
missingVariables: mergeAllMissing(isDisabledBy)
}
}
export const evaluateCondition: evaluationFunction = function(node) {
const explanation = this.evaluateNode(node.explanation)
const nodeValue = explanation.nodeValue
const missingVariables = explanation.missingVariables
return { ...node, nodeValue, explanation, missingVariables }
}