Association module: workflow states

Following up the discussion about the association blueprint and the merge-request for the assiciation module, I’d like to bring up the topic “workflow states” to a broader audience. At the Unconference I had some person-to-person discussions (esp. thanks to @timitos and @dave) but there was no workshop on this topic.

These are the proposed workflow states and some reasoning — based on recent discussions:

  • Draft — same as anywhere in Tryton
  • Rejected: not accepted as a member, end-state of the workflow
  • Accepted (by the board/whoever-is-in-charge): the party is allowed to become a member, but some other (technical, administrative) preconditions might be required to become “Active”
    I added this to the proposal for several reasons:
    • allowing separation of duties between the board and the operative personal
    • allowing for checking preconditions (e.g. paying entrence-fees, which a future member will only pay if accepted)
    • we have fine-grained workflow-steps on other modules, too
    • it’s much easier to customize skipping a workflow-state than adding one
  • Running (is member) → rename to „Member“
  • Ended: some kind of „normal” termination which allows the party to become a member again (forgot to renew membership / cancelled membership / died / defaulted on their payment)
  • Expelled: association has decided to terminate the membership and is unlikely to want the party to be a member again

Please comment, so we can finish the implementation.

Sorry if I could not attend the conference (I was on sick leave).

But my 2 cents (I hope it can help):

  • The ended and expelled seams a bit redundant, at least for the base module (doesn’t mean it could be have more sense later). For the base module seams a bit to limitative to limit the rejoin if you’re in the expelled state. And you permitt to rejoin the expelled people, there is not distinction between the two states (IMHO).

So, I propose:

  • Draft
  • Pending - the approving workflow has started (vote, waiting payment, ecc…)
  • Active - the member is part of the association
  • Expelled - the member is no longer part of the association (or never was)

If you want register that a member was not accepted by the association, You can expell it (if tlyou want to keep record of it). Otherwise you can push to draft, or delete the record (this I think depends on the association/club management).

I will prefer to keep it simpe for the base module. So I agree with:

The limitation can be always added by customization code if really needed.

I’ve just had a look at the current merge request, and I can see that there is a join_date and leave_date, so I’m not so sure that (my previous suggestion) of Active for one of the states is correct. In fact, with those dates, you may only need:

  • Draft
  • Approved

The dates indicate whether the membership is Active or not (and could work with an active field in a similar way to, for example, the party relationships).

As a suggestion, you could then have a

  • Rejected

state, to mirror the Approved state and which could also be used, along with the join_date / leave_date, to indicate that the member was expelled (for example, if there is no join_date then the membership was rejected, but with a join_date the membership was terminated early because the member was expelled)?

It took me quite some time to come back to this.

Yes @dave you are absolutely right: The join_date and the leave_date is what determines whether the membership is active. This is esp. true since whether the membership is active depends on the reporting day as well (see Reporting date - passing date to search in list-view).

Thus the states are workflow-states only. These will allow to hide some fields and add some constraints depending on workflow state.

No I wonder whether it is a good idea to make the date fields read-only depending in state, as this will inhibit correcting erroneous dates.

Once a member is approved its join date can not be updated anymore.
If you want to allow correcting errors, it shouldbe possible to move from approved to draft, then update the date and then move again to approved.

1 Like

@dave: To implement workflow (esp. not showing termination date field prior to being a member), I now end up with these states:

  • draft
  • rejected
  • approved
  • quitted
  • expelled

Your idea of using a single state “rejected” sounded alluring. Anyhow for a workflow, it is a difference, esp. since one might want to go back to the previous state (esp. from “quitted” and “expelled” to “accepted”, e.g. if the notice of termination was voided).

A quite different solution with less workflow states would be to unify “quitted” and “expelled” and have some “reason for termination” field. For now such a reason would be just “quitted”, “died”, “expelled”. Anyhow some assosiations might add more reasons and even depend the termination period on it.

What do you think?

I will prefer to keep it simple and have a single terminated (or another name if you think better) state and that’s it.

More states or even the “reason for termination” can be added by third party modules.

For me the variety on states is a strong indication, that the membership states should be a many2one to a table of the individual states of the association:
The state has a translatable required name.
A boolean field “Is Member” on the states informs about the legal state of the membership.

So a boolean is_member is the simplest.

This sounds inconvenient. If the states of associations are usually individual it would be nice to get the module run without customisation or extension.

Also the history of membership states of one person is IMHO important and should be available. There are associations which have memberships for just one day, e.g. to participate an event or to fulfil regulatory demands…

This depends on how you handle the book. This is the flow I use:

  • I receive a new membership request
  • I check if the member is active, if not I create a new member ID
  • I approve or reject the member
  • The membership ends (for some reason like: death, request or by the board)

By doing so I have a line for each request (with the request form attached), and if i need i can check all the requests done by the same person, filtering by party.

By design I permitted to reuse the same member ID by put the member in draft state, but doing so (without using the history model) you’ll lose some information. But doing so you cannot map 1:1 a membership request to his flow.

This model is based on Italian law, that treat the membership book like a physical journal.

I permitted (in my design) to use a laxed approach by make so the member ID can be reused by put the member in a draft state from a terminal one (expelled, ecc…). But if you want keep the history i think it can be handled in a separate module (but this is just my opinion)


Sorry if I add some noise, but this is very important to say:
A member is part of the association. You have voting right, and you acceptance must be approved by the board. A similar position is a partner in a private company.
So what you’re describing is more a fund-raising activity, where you can do activities to non-member people for raising some cash. In due time this will be another module too :smiley:

My opinion is aligned with @pokoli. A small set of states (that I view more like “gates” for data input) and a “reason” selection field for recording the cause of the expulsion.

In the generated user interface you can still have multiple tab that use the reason field as a filter (If I remember correctly).

Hmm, good question.

Personally, I think I quite like the idea of a unified terminated state, and an optional termination_reason, but I’m not sure if this should be a selection, many2one, or char, although I’m inclined to think many2one with a selection widget? I think this field would probably be useful for any association, so in my opinion should be in this module.

I think the is_member should be calculated from the context, dates, and state.

I think that using the above design, both of these would be doable if you created a new membership record for each non-concurrent membership request from a party, and the membership ID would be the number from the membership record?

If correctly designed, I would have thought that this module would be able to handle both of those scenarios?

While I agree on having few states only, having a single end-state only is not possible AFAIU: It makes a difference whether ever one was a member (was in state “approved”) or not.

If one was a member, there might be reasons to revive the membership, thus there needs to be a transition from “terminated” back to “approved”. While a “rejected” workflow must only go back to “draft”.

If we can make buttons a) set another fields value and transition and b) transition the state based on some field value, we could use less end-states. Example code is welcome. For now “Terminating” reasons would be: rejected, quitted, expelled, death, other.

Regarding moving “reason for termination” into third party modules: I’m strictly against doing so, since it is important to know whether was expelled or not - as discussed in length elsewhere.

Whether a party is a member depends on the reporting date, as described in the design document and discussed in Reporting date - passing date to search in list-view - #4 by htgoebel . I there is a solution taking this into account, I’d be happy to see an example code.

And historization of the “member” record does not solve this issue, since today I enter the termination date at the end of the year. So *tomorrow it will still be a member, but the record is unchanged.