Custom duration datafield

If you use the duration datafield, you can only make a choice between days, weeks or years. However, in some contracts, you want it to be a concept, like “Workday” for instance.

It could be solved by using a number datafield combined with a condition that results in either Workday or Workdays, but then you’ll have issues with using the same datafield in different contracts, because in some contracts you want it to be a number datafield, and in others a duration datafield.

Hi Bernd,

The problem with “Working Days”, for a platform like ClauseBase, is that this concept requires a lot of “world knowledge”. After all, a working day means something different for someone in the Netherlands vs. someone in Poland, and even something different between an employee in the French banking sector vs. an employee in the French schooling system, due to the differing holiday calendars. Accordingly, it is almost impossible for a generic system to start doing calculations with working days — unlike the regular days, which can easily be used for calculations.

What you could do, is to create a data expression in the same concept, that converts the number-datafield into a certain number of days using the @construct-duration special function — e.g. @construct-duration(#concept^working-days-as-number, "days"). The user would then input the number of working days using as a regular number-field, and the function would convert this number into a duration with the equivalent number of regular days. You can then use that data-expression as a regular data-field.

Does this sound like something that would solve your problem?

I’ve been experimenting with this, but I didn’t find a solution yet.

The problem with the data expression is that it will still say “days” instead of “Workdays”.

Let’s say that I have this duration datafield: #confidential-information^return-after-termination. However, in some contracts, I should be x Workdays instead of x days.

I understand that CB cannot make determine what should be a workday, but you could make it a defined term and include a definition (in my use case: “Workday” : all calendar days except for Saturdays, Sundays and official Belgian holidays.).

What I did: I created the following snippet { @has-autonomous-definition( #workday ) and @definitionlist-present : #workday | else: calendar days } under the snippet @#workdays-or-calendardays.

#confidential-information^return-after-termination @#workdays-or-calendardays obviously does not give the desired result :slight_smile:

What I could do to solve my problem is to convert the #confidential-information^return-after-termination into a number datafield, but then CB would not understand that these numbers are actually days, correct? It would also mean that I need to change existing clauses.

Or I can create a new similar number datafield @confidential-information^return-after-termination-as-number, but then I would end up with two very similar datafields which would make things more complex than needed.

P.S.: is there any major difference between using a data expression and using a snippet? If I’m not mistaken, you could obtain the same result, but in a slightly different way?

  1. There are no major differences between data-expressions and external snippets now that snippets can also result in data other than text; the major difference is organisation.

Data-expressions are cleaner from an organisational perspective, because they can be stored at the level of the concept. Other clause authors will have an easier time figuring out how everything works when the datafields and the data-expressions are located in the same concept.

Where possible, I would therefore prefer data-expressions.

2. Why not create a number-type data-expression @confidential-information^return-after-termination-as-number? This data-expression would then extract the number of days using @days-in(#confidential-information^return-after-termination).

Be aware that users may end up inserting a certain number of weeks/months/years/quarters instead of days into #confidential-information^return-after-termination. In such cases, there will be some rounding errors — e.g., @days-in(1 month) happens to always result in 30, because the software does not know whether you are talking about March (31 days) or February (usually 28 days). For this reason, the cleaner approach would have been to go for a number-field from the very beginning.

Thank you Maarten for the advice.

I’ve been experimenting and I ended up with the following clause in an empty test document (#1224408):

{@has-autonomous-definition(#workday) and @definitionlist-present: #concept3^return-after-termination-as-number @if(#concept3^return-after-termination-as-number = 1, #-workday, @plural(#-workday)) | else: #concept3^return-after-termination} (see #1224227 and #1224226)

  • #concept3^return-after-termination = duration datafield
  • #concept3^return-after-termination-as-number = number-type data-expression using @days-in(#confidential-information^return-after-termination)

This seems to work as intended, but:

  • The styling doesn’t work with the @days-in data-expression. The result is either “2 Workdays” or “two (2) days”, whereas I would also prefer “two (2) Workdays”
  • The term #workday doesn’t show up in the terms panel (unless I add #workday to the clause to make it display in the terms panel)

Hi Bernd,

  1. You can wrap any number (including output such as @days-in) into a call to @dynamic-fullnr. (This is actually what happens under the hood when a duration field is outputted.)

  2. When looking at your sample clause, it seems to me that (even after removing the blunt #workday in your clause) things work as intended.

Background: a defined term will only show up in the terms panel when it actually gets used in the text. In your example, the “path” taken by the software throughout your clause, does not encounter concept #workday.

For example, if you would insert {false: #concept-x} in a clause (which will result in nothing, because the condition “false” is, well, always false), and this is the only clause where #concept-x gets used, then #concept-x will not show up in the terms panel, because the software does not actually “encounter” #concept-x when evaluating the clause.

By the way, the same idea applies for the Data Dashboard: by default, only datafields that are actually encountered, will get shown. However, in the Data Dashboard, you can force the software to show all datafields in a clause — even those that are never “encountered” by the software when calculating the currently shown clauses — by enabling “show unused”.

Such option does not currently exist for the terms panel (but could be added, if users would be interested in it.)


Hi Maarten,

The dynamic-fullnr works!

I think the “show unused” function would be in particular interesting for the terms panel too, because if you delete the definition of “Workday”, the software does not encounter the term anymore and you don’t get the chance to add a definition, because the term is completely optional:

{@has-autonomous-definition(#workday) and @definitionlist-present: @dynamic-fullnr(#concept3^return-after-termination-as-number) @one-else(#concept3^return-after-termination, #-workday, @plural(#-workday)) | else: #concept3^return-after-termination}

Also, if the user selects months (or years) instead of days, the result is “thirty (30) Workday”. Of couse, because the input is “1”. So the calculation is right, but the result isn’t. Is there any solution to this problem? For instance, would it be possible to forbid the user to select anything else than days? I tried to solve it by using the @if … = 1 OR = 30 but this doesn’t work in the @if function and it also doesn’t work by using a data expression in the @if special function. But still, this would not be a solution because the software doesn’t know whether the “30” are days or months. So even if it would work, then “1 month” would result in “30 Workdays” (plural), but “30 days” would still result in “30 Workday” (singular).

You cannot currently forbid the user to select weeks/months/years/quarters instead of days.

However, as for the singular/plural solution, the solution is easy: use your (number-based) data-expression instead of your (duration-based) datafield, so

@one-else(#concept3^return-after-termination-as-number, #-workday, @plural(#-workday)

instead of

@one-else(#concept3^return-after-termination, #-workday, @plural(#-workday)

The reason is subtle, but as follows.

In principle, @one-else expects a number as its first argument. When you pass a duration instead (i.e., your duration-based datafield) as the first parameter, then you are offering apples while oranges were kind of expected. The software tries to do something useful here (instead of just returning an error), and simply extracts the number currently present in the duration.

This works fine in your particular situation as long as your users work with days, but obviously breaks down when they switch to weeks/months/years/quarters. You are therefore advised to perform a manual conversion, as you did within the data-expression (where you explicitly convert from duration to days).

In general, you should always check what data type is expected by some particular function, otherwise you will encounter other surprises. You can always check the data type on, or by pressing Ctrl-space when your cursor is within the parentheses of a special function and looking at the popup window. Below you can see the suggestions for @one-else.


Thank you Maarten. That works.

I think it would be best to make a snippet. I cannot put all this in a data-expression I suppose? Seems to be too complex for one data-expression.

Do you also think that a “show unused” option for the terms panel would be needed in my use case? Once you delete the definition of Workday, there is no way to add it again.

Also, thinking one step further with Q&A’s in mind: there is a possibility to insert, change or delete clauses via change sets, but if I’m not mistaken there is no option to insert, change or delete definitions in the same way as for clauses?

1. You could make a data expression of type “text”, and then return something like @one-else(#concept3^return-after-termination, #-workday, @plural(#-workday). But in general, whenever you ultimately want to insert a piece of text into a clause (instead of some other data type), an (external) snippet is probably a better idea.

2. I’ll add the “show unused” option.

3. It is indeed not possible to insert or delete definitions through Q&A change sets, because definitions are automatically inserted or removed when the need arises (e.g., when the base text does not use concept A, then the definition A will not show up; if due to some change set or change in a datafield concept A does get use, then its definition will automatically show up; and it will also disappear automatically if needed).

So far, we have never seen the need to further refine this system in the Q&A — if you need to dynamically alter the contents of a definition, you can easily do so through conditions within the body of the definition.

1. Ok. What I did is the following:

  • external snippet: {#workday^enabled = true: {@has-autonomous-definition(#workday) and @definitionlist-present: @dynamic-fullnr(?WORKDAYS) @one-else(?WORKDAYS, #-workday, @plural(#-workday))} | else: ?CALENDARDAYS}
  • clause: @#workdays-or-calendardays(?WORKDAYS := #concept3^return-after-termination-as-number, ?CALENDARDAYS := #concept3^return-after-termination)
  • I added a true/false datafield to enable or disable using Q&A.
  • Because of the parameters, I can reuse the external snippet in other contracts with other concepts

2. Thanks!

3. Thanks for the clarification, then there is no need to have change sets for this indeed. I was not yet aware that unused definitions automatically disappear, which is very convenient.

Hi Bernd, the option you suggested is now available in the “…” menu of the terms panel.

Thanks for the inspiration!


Excellent Maarten. Works like a charm :slight_smile: