Created on 23 October 2024, 8 months ago

Problem/Motivation

It is important to be able to track which contacts belong to which Organization.

Steps to reproduce

Proposed resolution

Copy the features of CiviCRM Member

Remaining tasks

User interface changes

API changes

Data model changes

Feature request
Status

Active

Version

1.0

Component

Code

Created by

🇺🇸United States bluegeek9

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @bluegeek9
  • 🇺🇸United States jdleonard Austin, TX, USA

    I need to analyze this more fully, but some very quick questions/thoughts...

    1. CRM Membership -- Status -- Things to consider capturing: "Future", "Active", "Expired", "Suspended", "Cancelled/Voided/Revoked", "Grace period" (though the first three can be computed from the start/end date so perhaps the last three are the interesting ones)
    2. CRM Membership -- Visibility -- Consider a flag determining whether a membership is public (e.g. "show this membership on our list of members"). This could instead be a Contact-level preference, but I could foresee use cases where a member wants one membership public (e.g. "general member") and another non-public (e.g. "ultra rich benefactor"). I guess there's a higher-level question of how/where to track privacy/visibility of membership.
    3. CRM Membership Type -- Duration -- consider supporting all RFC 3339 durations ( https://www.drupal.org/project/duration_field uses its precursor). I've seen 90 minute memberships e.g. at a trampoline park. Maybe best to not unnecessarily restrict flexibility at the data model level (I could certainly see not exposing every option by default at the UI level).
    4. CRM Membership Type -- Fixed Period rollover -- how does this work?
    5. CRM Membership Log -- what is this for?
    6. How to handle a multi-person membership (that is not an entire household)? E.g. my local YMCA offers a membership including "two adults of the same household" (they also offer a true household membership). Or https://www.thedoseum.org/member offers "Family of 2", "Family of 3" etc. There seems to be a more general need for tracking the Contacts that a Household or Organization's Membership covers. Perhaps one Membership could be dependent on another e.g. YMCA example could be represented as a Household "Two-Person Membership" + two Memberships for the specific people in the Household? This approach would suggest (at least) a reference between (some) Memberships. Perhaps also specifying a Sub-Contact Type for a Membership Type.
    7. Building on the previous bullet, how to handle a membership model wherein an overall membership (e.g. "3-Year Family of 5") covers a different set of people over the course of the membership and not necessarily the maximum number of people. This is similar to seat licensing.
  • 🇺🇸United States bluegeek9
    1. Workflow: CiviCRM has a similair status. I think this will be needed eventually. I imagine it would depend on the workflow module.
    2. Do we want to have promote and sticky? Have them would like Nodes?
    3. Duration field: I looked at the Duration field. I do not know if it will work. It does not support Drupal 11. Memberships of less than a day will be harder to implement.
    4. Fixed or Rollover: Let us look at a monthly membership. Someone joins on the 15th of the month. With Fixed, the membership expires at the end of the month. With rollover, the membership expires on the 15th of next month.
    5. CRM Membership Log: The CRM Membership entity corresponds to a Contact and a membership type. Each time a membership is created or renewed a Membership Log is created. Handling payments is out of scope now. It is like a payment record. E.g. Contact A renewed monthly membership for 6 months.
    6. Multi-Person Membership: Not sure. Each Membership type defines which contact type can 'join'. This would allow households and organizations to join. How does that filter down, to the individual; I have no idea yet. My first thought was an unlimited entity reference field, with an item for each individual. That would work for households, but organizations will have issues; entity references do not scale. If memberships can have 'parent' memberships, that could also work.
    7. Note sure. If we used entity references, each membership type could have a different limit.
  • 🇺🇸United States jdleonard Austin, TX, USA

    Have you evaluated https://www.drupal.org/project/membership at all? Its project page description made it seem overly focused/limited, but perhaps there's an opportunity to contribute to / leverage its Membership entity.

  • 🇺🇸United States freelock Seattle

    I've built 3 sites using Membership module -- unfortunately none are active anymore. I did a fair amount of refactoring to make that more clear -- the current maintainer had different goals, but the overall structure can be part of the solution.

    The most recent site was for a company that was selling subscription access to its services, and its customers were businesses that typically bought 5 or more seats -- they had a couple different pricing tiers based on number of logins. It was essentially a domain-specific SaaS. I ended up using both Membership and Group to make this work. And it did work, with some glue code (this was before I was a big user of ECA).

    Membership provides two entity types:

    - "Membership" which was a permanent entity for a particular account -- might be pending, active, expired, or in some cooldown period -- you can set different state machines to allow grace periods for renewal. So this contains the current status, along with any number of members. You can have different bundles here for different levels of access.

    - "Membership Term" - this uses a special plugin so it can connect directly to external membership providers, at first. I created a general "membership_term" plugin so you could manage memberships entirely within Drupal. This entity represents the current active period, with the plugin defining details around how to handle fixed vs floating membership periods, handling pro-rating amounts for partial periods, triggering automatic renewals if connected to Commerce, and renewal notices at various points (I made a "Scheduled Message" module just for this reason, but none of our active sites still use it, we've replaced that with ECA). You make different bundles of this for different membership lengths.

    I had also created a Commerce Membership module, and we did have it working, but the client pulled the plug on the whole project before we had that wrapped up -- I can find and post that code.

    On this project, we used Group to enforce access. The custom code was simply keeping the various modules in sync -- sending people to buy more seats if they tried to add more users than they had in their agreement, pro-rating additional memberships so we could use the same membership term with a new quantity, making it easy to invite new users to your membership, etc. All the app's functionality was inside the group, so without group membership, authenticated users couldn't do anything other than purchase a membership and browse some help pages.

    I did find myself refactoring a lot of the membership term code, and sent a bunch of patches to the maintainer, most but not all are in the Membership module.

  • 🇬🇧United Kingdom robcarr Perthshire, Scotland

    The approach with Commerce License and Commerce Recurring is to create a [Drupal] User Role specific for a membership type (eg, Full member, Associate, Supporter). These roles (think of them as membership plan) can be purchased for a fixed or indefinite period using Commerce License. If such membership is to be renewed on a recurring (eg, annual) basis then Commerce Recurring can be used for repeated billing.

    So the architecture is:

    User - Subscription - Plan

    which seems to be comparable (but more abstracted) to the CiviCRM approach of 3 core entities

    Subscription:

    • Member (User reference)
    • Plan (reference)
    • Expiry date (date)

    Plan:

    • User role (reference)
    • Description
    • Fee
    • Duration

    I've not tried this approach for Membership plans that have no cost - Commerce may well take users through a checkout process even though there's nothing to pay. There's probably a way to tweak the checkout UX so this use case wouldn't look too weird though.

  • 🇺🇸United States freelock Seattle

    I have used Commerce Recurring for automatic renewals. I forget exactly why we didn't use Commerce License -- we had a reason at the time, but maybe whatever it was is no longer an issue. Could Commerce License be used to purchase a group membership, instead of a site role?

  • 🇺🇸United States freelock Seattle

    As for no-cost memberships, easiest was to set the membership term to "lifetime". I don't think that is correctly built in the membership module -- I think we set a date 20 years in the future as a short-term placeholder behavior -- I haven't had a customer yet that wanted a membership that didn't expire. (We did hit the Unix Y2K/2038 bug doing this!)

  • 🇺🇸United States jdleonard Austin, TX, USA

    I think there's significant value to keeping the representation of a "membership" very lightweight so that an ecosystem can form around it to serve many use cases, with various modules / recipes leveraging the key concept of membership (whether for an individual, household, or organization, or multiples of those types).

    It might indeed make sense to use Group for some Member Platform functionality, but I don't think we should necessarily require it simply to track memberships.

    Commerce License seems incompatible with the approach @bluegeek9 is taking here in CRM, which doesn't require a Contact/member to be a User.

    Contacts does require the use of Users (but removes the need for a User to be able to log in / have credentials) and thus would be more compatible with Commerce License. However, overloading the concept of a User to cover people who don't log in seems unwise and a Drupal anti-pattern.

    Looking more at the Membership module's model, I see that its Membership entity has a User as its subject/owner.

    My bias for Member Platform (and for a CRM in general) is toward the approach proposed here for CRM of tracking Contacts (and their Memberships) separate from Users and separate from Commerce. I would expect some module to offer the ability to link a User to a Contact and grant roles or Group membership or whatever. I would expect another module to offer integration with the Commerce ecosystem or whatever other payment system.

    I think there is lots to learn from the Membership module, but I'm not confident it's worth attempting to actually use the module. Anyone else have thoughts on this?

    @freelock Would be great to see the Commerce Membership code!

  • 🇺🇸United States freelock Seattle

    Membership entities do require an owner, but we have used it on sites where the owner is the only user, and additional members I think used Profile entities -- at some point on a D7 site we made some custom code to allow an existing profile to be associated with a new user... we've managed membership sites for families with children that do not have email addresses or logins but still needed to be distinct (a sports academy that had open field time as well as lessons).

    If Commerce License requires a user, that would be why we didn't use it...

    Architecture-wise, I fully agree -- only one of our sites used Group with memberships, and having different entities for Users, Profiles (or Contact), Memberships, Membership Terms, Payment subscriptions makes a lot of sense, especially with ECA now available to build actions to sync data to whatever entities your site needs.

  • 🇺🇸United States freelock Seattle

    Oh wow, looks like the code I wrote for commerce_membership (name on Drupal.org) is implemented in the site as "membership_commerce". It's not very big -- ~260 line .module file with several hooks, a service with some convenience functions, and two PriceResolver services.

    It also looks like these aren't written in a portable way -- field names are hard-coded instead of looked up from config. Went ahead and pushed what I have up to Commerce Membership .

  • 🇮🇪Ireland lostcarpark

    This is very interesting to me.

    As well as "fixed" or "rolling" membership, it would be great to support custom membership terms.

    For example, there's an organisation I'm part of where membership was originally yearly, but they produce a magazine that originally came out quarterly, but amateur magazines often struggle to follow strict schedules, so it became less regular, usually every four months or so. To keep things fair, they redefined the member as "four issues of the magazine", so when you join, you can't be sure when your membership will end.

    I'm not saying the module should handle this sort of exceptional case, but it should provide a way for organisations to implement it. Using a plugin to define membership term like the membership module as @freelock mentions in #6 sounds like a good approach to take.

    Perhaps we could provide "fixed" and "rolling" as two built in plugins, and allow organisation that need more complex rules, or integration with an external source, to implement their own plugin.

  • 🇺🇸United States freelock Seattle

    Fixed, rolling, and lifetime are the 3 basic models I've seen.

    Fixed is a specific term -- usually monthly or yearly, sometimes quarterly or weekly -- and all memberships expire at the same time. This model typically needs pro-rating if there's a payment involved.

    Rolling can start any time, and just repeats every week/month/quarter/year. Generally the main issue here are purchases on the 29th-31st of the month on a monthly basis, handling those dates when the month doesn't have that many days.

    Lifetime terms don't expire -- but this is really hard to get working in the same views with other membership terms -- needs a lot of special casing. When I've had to do these, I've punted on handling the hard stuff and just set the expiration out to a date 25 years in the future, sometimes a specific one so an alter hook can display it as "lifetime".

Production build 0.71.5 2024