oracle-samples / clara-rules

Forward-chaining rules in Clojure(Script)
http://www.clara-rules.org
Apache License 2.0
1.2k stars 115 forks source link

Using accumulator with a custom function #503

Open adeojo opened 3 months ago

adeojo commented 3 months ago

I have the following custom function

(defn calculate-trading-dates
  [effective-date terminate-date billing-dates ]
  (let [effective-date-to-use (if (.isBefore effective-date (.getStartOfMonth billing-dates))
                                (.getStartOfMonth billing-dates)
                                effective-date)
        terminate-date-to-use (if (nil? terminate-date)
                                (.getEndOfMonth billing-dates)
                                terminate-date)
        numerator (double (.getNumberOfTradingDates billing-dates effective-date-to-use terminate-date-to-use true))
        denominator (double (.getNumberOfTradingDates billing-dates))
        final-value (/ numerator denominator)]
    final-value))

The above function is used in a rule as below

(defrule my-rule
    "Sample Rule"

    [?billingDates <- BillingDates]
    [?sumCalcTradingDates <- (acc/sum #((calculate-trading-dates (.getEffectiveDate %) (.getTerminateDate %) ?billingDates ) % )) :from [DlpFirm
                                                                                                                               (= mpid ?accountId)
                                                                                                                               (= portId "PrimaryDLP")
                                                                                                                               (= ?effectiveDate effectiveDate )
                                                                                                                               (= ?terminateDate terminateDate )]]

    [:test (>= (.doubleValue ?sumCalcTradingDates) 25) ]

    =>
    (when (true? (get (.getFlags ?item) "baseChargeApplied"))
      (insert! (Map/of "$transactionalBillingChargeWriter" (TransactionalBillingCharge. ^EquityTransactionalBillableItem ?item (.getChargeTypeDTO ?charge-types "ABC100"))))
 ))     

When I test the above rule, I get the error:

    Unable to resolve symbol: ?billingDates in this context

FYI :The DLPFirm POJO does not have BillingDates

Any ideas how I can use the the function with an accumulator ?

EthanEChristian commented 3 months ago

The compilation of the accumulator would occur prior to the execution of rules and outside of that context, which is likely the source of the error:

Unable to resolve symbol: ?billingDates in this context

In the sense that at session creation time, its trying to construct the field accessor function:

(acc/sum #((calculate-trading-dates (.getEffectiveDate %) (.getTerminateDate %) ?billingDates) % ))

which attempts to reference a binding scoped to an execution time variable.

Off the cuff, i'd probably defer that sort of logic to the RHS for calculation rather than the accumulator if possible.

EthanEChristian commented 3 months ago

Other contributors mentioned(off-thread) an alternative of introducing intermediate facts to house this sort of data for easier accumulation.

Something akin to:

(defrecord DlpFirmWithTradingDates [trading-dates dlp-firm])

(r/defrule my-rule
  "Sample Rule"
  [?billingDates <- BillingDates]
  [?dlpFirm <- DlpFirm
    (= portId "PrimaryDLP")
    (= ?tradingDates (calculate-trading-dates effectiveDate terminateDate ?billingDates))]
  =>
  (r/insert! (->DlpFirmWithTradingDates ?tradingDates ?dlpFirm)))

then a subsequent rule to accumulate the intermediate facts.

adeojo commented 3 months ago

Will give this a try. thx