@@ -49,7 +49,17 @@ def _skel_repr(skel: SkeletonInstance_T | None) -> str:
4949 return f'<SkeletonInstance of { skel .skeletonCls .__name__ } with key={ skel ["key" ]} and name={ skel ["name" ]} >'
5050
5151
52- class DiscountConditionScope :
52+ class DiscountConditionScope (abc .ABC ):
53+ """
54+ Validator that validates a specific discount condition scope.
55+
56+ The scope is usually defined by one bone in the :class:`DiscountConditionSkel`.
57+
58+ A scope is fulfilled if the precondition and the main condition are fulfilled.
59+ It is not fulfilled if the precondition is fulfilled but the main condition is not.
60+ If the precondition is not fulfilled, the scope is ignored (regardless of the main condition).
61+ """
62+
5363 _is_applicable = None
5464 _is_fulfilled = None
5565
@@ -71,6 +81,12 @@ def __init__(
7181 self .context = context
7282
7383 def precondition (self ) -> bool :
84+ """
85+ Validates the precondition for this scope.
86+
87+ Usually this means the related bone in the :class:`DiscountConditionSkel` has a value defined.
88+ Unfulfilled preconditions make :class:``DiscountConditionScope`` no longer necessary.
89+ """
7490 return True
7591
7692 allowed_contexts : t .Final [list [DiscountValidationContext ]] = [
@@ -81,28 +97,46 @@ def precondition(self) -> bool:
8197
8298 @abc .abstractmethod
8399 def __call__ (self ) -> bool :
100+ """
101+ The (main) condition of this scope.
102+
103+ This check could be, for example, that:
104+
105+ - A bone of an ``ArticleAbstractSkel`` has the value from the
106+ value range of a scope bone in the :class:`DiscountConditionSkel`.
107+
108+ - The context (e.g. language) matches the value range of a scope bone.
109+ """
84110 ...
85111
86112 @property
87113 def is_applicable (self ) -> bool :
114+ """Cached and evaluated precondition"""
88115 if self ._is_applicable is None :
89116 self ._is_applicable = self .precondition ()
90117 return self ._is_applicable
91118
92119 @property
93120 def is_fulfilled (self ) -> bool :
121+ """Cached and evaluated condition"""
94122 if self ._is_fulfilled is None and self .is_applicable :
95123 self ._is_fulfilled = self ()
96124 return self ._is_fulfilled
97125
98126 def __repr__ (self ) -> str :
127+ """Represent the scope as a string"""
99128 return (
100129 f'<{ self .__class__ .__name__ } with '
101130 f'{ self .is_applicable = } and { self .is_fulfilled = } >'
102131 )
103132
104133
105134class ConditionValidator :
135+ """
136+ Validator that validates a specific discount condition (with many scopes).
137+
138+ It validates a complete :class:`DiscountConditionSkel`.
139+ """
106140 scopes : list [t .Type [DiscountConditionScope ]] = []
107141
108142 def __init__ (self ):
@@ -173,7 +207,11 @@ def register(cls, scope: t.Type[DiscountConditionScope]):
173207
174208
175209class DiscountValidator :
210+ """
211+ Validator that validates a specific discount (with many conditions).
176212
213+ It validates a complete :class:`DiscountSkel`.
214+ """
177215 def __init__ (self ):
178216 super ().__init__ ()
179217 self ._is_fulfilled = None
0 commit comments