Skip to content

MLM Admin Applet

Overview

The MLM Admin applet is where you run a member-gets-member referral programme. You create a program (a referral campaign with a start and end date), enrol members from Membership Admin as participants, and the applet keeps the lineage — who introduced whom, one upline-to-downline link at a time, up to ten levels deep. New downlines arrive by invitation (an e-mail, SMS or WhatsApp message sent through a contact-centre channel, accepted on a registration page) or by scanning a participant’s QR link, which drops them onto your storefront’s sign-up page with the inviter’s key attached.

The applet records the tree. It pays nobody: the money is an MLM-type scheme in Commission Scheme, and the Sales Commission posting run walks the lineage up from the buyer and writes one commission line per upline level.

Registry code mlm-admin-applet (TNT-USER, ACTIVE, created 2022-12-28; the registry’s documentation_url still points at a Confluence page). Master-data applet: no document, no journal, no stock. There are no screenshots on this page — the repository ships none and static/images/ holds none.

Where it fits

DirectionDocument / appletHow it connects
UpstreamMembership AdminEvery participant, inviter and downline is a membership card (bl_crm_membership_hdr); the member’s own MLM tab shows uplines and downlines read from this applet’s /uplines and /downlines endpoints
UpstreamCustomer Maintenance, EntityParticipants carry the member’s customer entity; entity contacts are named people under an entity who are not yet members
UpstreamUnified Contact CenterA program’s communication endpoint links name the e-mail / SMS / WhatsApp endpoints and message templates the invitation processor sends through
UpstreamCommission SchemeA program may name a scheme; the commission run is driven by MLM-type schemes, not by that field (see Lifecycle)
UpstreamCP Commerce AdminThe program’s Website and URL Forward point at a storefront; a storefront sign-up carrying an inviter’s key creates the downline’s entity, membership, participant and lineage
DownstreamSales CommissionThe posting run reads bl_mlm_lineage_hdr for MLM schemes and writes one intermediate commission line per upline level; the commission report shows MLM Program and MLM Member columns

Screens and menus

Route root: applets/wavelet/erp/mlm-admin-applet/. Opening the applet lands on Invitations.

Menu itemRouteWhat it shows
Entity ContactscontactsPeople under a customer entity who can be invited before they are members: Name, Parent Entity, Email, Phone, Status; create and edit
InvitationsinvitationAll invitations across programs: Inviter, Invitee, Program, Invite Method, Invite Status, Date Created, Expiry Date; + opens Invitation Create
ProgramsprogramPrograms: Program Code, Program Name, Commission Scheme, Website, Start Date, End Date; + opens Program Create
PayoutgenericNo such route exists — the entry opens the applet’s 404 page. A Commission Report entry above it is commented out

Program Create has a single Details tab. Program Edit has five tabs and a Delete button:

TabContent
DetailsThe program header (Commission Scheme is shown at create only; the select is commented out on edit)
Communication EndpointsListing of the contact-centre endpoints linked to the program: Virtual Contact, Endpoint Name, Endpoint ID, Priority, Channel Name. The listing reads correctly; create, update and delete on this tab are not wired (see Troubleshooting)
ParticipantsParticipant, Object Type, Referral Source, Date Created, Date Effective; create picks a membership card on a Participant tab; edit has QR Code and Details tabs and Delete
InvitationsThe same invitation screens as the top-level menu, filtered to the program; edit adds the Invitee field and Delete
LineageUpline, Downline, Level, Invite Method (the referral-source channel name), Invite Status, Date Created; create picks an upline and a downline from the program’s participants; edit allows Delete

The participant’s QR Code tab renders core2/tnt/dm/mlm/programs/{tenantCode}/participant/invite-by-guid/{participantGuid} as a QR image, a copyable link and a PNG download — the self-service invitation link described under Lifecycle.

Settings and Personalization menus

EntryRoutePurpose
Field Settingssettings/field-settingsEight slide-toggles copied from the document-applet template (Unit Discount, SST/VAT/GST, WHT, Blanket Order, Segment, G/L Dimension, Profit Center, Project); bound to nothing, Save has no handler
Default Selectionsettings/default-selectionDefault Branch and Default Location (see Configuration — the save path is broken)
Webhook, Feature Visibility, permission listingssettings/…Shared platform screens; the settings menu’s default lands on Feature Visibility
Personalization → Default Selectionpersonalization/personal-default-selectionPer-user branch and location, same broken save path

Configuration

Before you can use it

PrerequisiteWhereWhy
Members with a card numberMembership AdminA participant is a membership card; the participant picker lists membership cards and nothing else (obj_type is always MEMBERSHIP in the UI)
Customer entitiesCustomer MaintenanceA participant is unique per program + entity + member; entity contacts hang under a parent entity
Contact-centre endpoints and message templates for e-mail, SMS or WhatsAppUnified Contact CenterThe invitation processor sends through the program’s endpoint links in priority order; with none, invitations stay CREATED
A storefront website with post-registration configured, and a landing URL containing <URL-KEY>CP Commerce AdminThe QR link redirects to the program’s URL Forward with the key substituted; the storefront sign-up links the new user to the inviter
An MLM-type commission scheme and a commission cycleCommission Scheme, Sales CommissionWithout them the lineage earns nothing
The two queue processors scheduled: MLM_INVITATION_LINK_PROCESSOR and INVITATION_INSERTION_INTO_LINEAGE_PROCESSORSchedulerInvitations are sent and accepted invitations become lineage rows only when these run
API permissions TNT_API_DM_MLM_{PROGRAM_HDR, PROGRAM_PARTICIPANT_HDR, ENTITY_CONTACT_HDR, LINEAGE_HDR, INVITATION_HDR, PROGRAM_CC_ENDPOINT_LINK}_{READ, CREATE, UPDATE, DELETE}Settings → permission listingsEach listing, create and edit screen

Applet settings

Settings live in an applet-local settings container (not the shared field-configuration component); there is no applet-settings model file in this project. Tenant administrators can open the screens; nothing they save is read.

SettingWhereDeclaredRenderedPersistedConsumedEffect
DEFAULT_BRANCHDefault Selectionbuilt inline into the applet container’s extension rowsYes, branch drop-downBroken — the subscription that loads the applet container is commented out (default-settings.component.ts L32–40), so the save at L43 dereferences an unset object and throwsNo consumer outside the settings screensNone
DEFAULT_LOCATIONDefault Selectionas aboveYesBroken, as aboveNoneNone
Personal DEFAULT_BRANCH / DEFAULT_LOCATIONPersonalization → Default Selectionas aboveYesBroken — same commented subscriptionNoneNone
Eight Field Settings togglesField SettingsNo modelYesNo — Save has no handler; the component class is emptyNoNone (document-template residue)

Model-only keys: none — the project has no settings model. Keys read at runtime without a declaration: none found.

Document behaviour settings

Not applicable — no documents.

Feature visibility / permissions

API permissions are the TNT_API_DM_MLM_* family in MlmPermissions.java (L14–95: OWNER, ADMIN, MEMBER, CREATE, UPDATE, DELETE, READ for each of the six tables) plus TNT_API_DM_MLM_REGISTRATION_{OWNER, ADMIN} (L99–102, marked in source as still to be revised). The acceptance and QR-redirect endpoints are anonymous by design (they are used by people who are not yet users).

Route guards: none — app.routing.ts declares no canActivate. The applet’s own PermissionResolver only maps target tables to services for the shared permission screens. No client-side permission definitions are registered for this applet (0 rows in bl_applet_client_side_perm_dfn for mlm-admin-applet, queried 2026-09-15).

Fields

Program (bl_mlm_program_hdr)

FieldMeaningRequiredNotes / validation
Program NameDisplay nameYes (UI, ≤255)Backend does not check uniqueness of code or name — MLMErrors declares MLM_PROGRAM_HDR_OBJECT_CODE_ALREADY_EXISTS but the validator never raises it; DDL has no unique constraint
Program CodeBusiness keyYes (UI, ≤255)As above
Start Date, End DateThe campaign windowYesBackend rejects null date_start / date_end (MlmProgramHdrDataConsistencyObject L77–87); DDL date_end NOT NULL. Nothing else reads the window — an expired program still accepts participants and invitations
Commission SchemeA scheme from Commission SchemeNoMust exist if given (L49–74); set at create only; not read by the commission run (see Lifecycle)
WebsiteA storefront (bl_cms_website_hdr)NoMust exist if given
URL ForwardThe public landing URL the QR link redirects toNoMust contain the literal <URL-KEY>; otherwise the redirect endpoint answers 404
StatusACTIVE / INACTIVEYesDefault ACTIVE

Participant (bl_mlm_program_participant_hdr)

FieldMeaningRequiredNotes / validation
ProgramThe program being editedYes (read-only)
Object TypeWhat kind of record participatesYesUI offers MEMBERSHIP only
ParticipantThe membership card, picked on the Participant tabYesUnique per program + entity + memberMLM_PROGRAM_PARTICIPANT_HDR_OBJECT_VIOLATE_UNIQUE_CONSTRAINTS_ON_PROGRAM_HDR_MEMBER_GUID_ENTITY_HDR… (L56–69); program, entity and member guids must exist
Date EffectiveFrom when the participant countsYes (backend, L118–130)
Referral SourceThe channel (bl_msg_channel_hdr) that brought the participantNo
StatusACTIVE / INACTIVEYes
Invite URL keyRandom key generated on create (MlmProgramParticipantHdrService L34–37)Not shown as a field; unique in DDL; the storefront looks the inviter up by guid or this key

Lineage (bl_mlm_lineage_hdr)

FieldMeaningRequiredNotes / validation
UplineThe parent participantYesParent and child participant, entity and member guids must all exist (MlmLineageHdrDataConsistencyObject L51–127); parent ≠ child (L211–218)
DownlineThe child participantYesUnique per program + child (L180–194) — a downline has exactly one upline in a program
Referral SourceChannel that made the linkOptional in the UI, required by the backend (L130–136)Leaving it empty fails the save with …REFERRAL_SOURCE_IS_NULL_OR_EMPTY
Lineage TypeKind of linkYesUI offers membership only
StatusACTIVE / INACTIVEYes
LevelDepth under the rootComputed, not entered: parent’s level + 1, or 1 when the parent has no lineage row (MlmLineageHdrService.getLevel L114–130); must stay within 0…10 (MAX_LEVEL = 10, L29–30, L221–227), also checked on delete (L460–468)

Invitation (bl_mlm_invitation_hdr)

FieldMeaningRequiredNotes / validation
ProgramWhich program the invitee joinsYesMust exist
Lineage TypeWhere the invitee comes from: MEMBERSHIP, ENTITY, ENTITY_CONTACTYesReveals a tab — Invite from Members / Invite from Entity / Invite from Entity Contacts — a multi-select; one invitation row is created per selected invitee (mlm-invitation-create.component.ts L193–273)
InviterThe parent participantYesMust exist
Expiry DateWhen the invitation lapsesYes (UI)Not enforced — nothing sets EXPIRED, and the send processor picks every CREATED invitation regardless of the date
Invite TypePARENT (sent by an inviter) or SELF_CLICKED (came in through a QR link)No
Invite StatusCREATED, SENT, ACCEPTED, REJECTED, EXPIRED, LINEAGE_UPDATED (MLMConstants.InvitationStatus)NoNormally driven by the processors; the form lets an administrator set any value by hand
StatusACTIVE / INACTIVEYes
Invitee (edit only)The child member / entity / contactExistence checks only if present (MlmInvitationHdrDataConsistencyObject L32–139); no e-mail or phone format check

Entity Contact (bl_mlm_entity_contact_hdr)

FieldMeaningRequiredNotes / validation
NameThe personYes (≤255)Backend rejects empty
Parent EntityThe customer entity they belong toNo (UI)Must exist if given (MlmEntityContactHdrDataConsistencyObject L49–58)
Email, PhoneHow to reach themAt least one (L59–65, …DOES_NOT_CONTAIN_ANY_CONTACT_INFORMATION)Format validated (L66–77); duplicate e-mail / phone error codes exist in MLMErrors but are not checked
StatusACTIVE / INACTIVEYes

Communication Endpoint Link (bl_mlm_program_cc_endpoint_link)

FieldMeaningRequiredNotes / validation
NameLabelYes
EndpointA contact-centre endpoint (e-mail, SMS, WhatsApp)YesProgram and endpoint must exist; unique per program + endpoint (L87–95)
PrioritySend order, lowest firstOptional in the UI, required by the backend (L78–84; DDL NOT NULL)Empty fails with …PRIORITY_IS_NULL_OR_EMPTY — but see Troubleshooting: the create screen cannot reach the backend at all
StatusACTIVE / INACTIVEYesThe send processor uses ACTIVE links only

Lifecycle and effects

Posting proof: nothing posts, nothing moves. ServerDocTypes.java, JournalPostingTypeHandler.java and GenericDocumentTypeHandler.java contain no entry matching MLM, LINEAGE or REFERRAL (grep at backend commit 45edc03526: zero hits in each). No record in this applet is a generic document; there is no amount or quantity signum, no Dr/Cr equation, no stock processor, nothing for VOID to reverse. Every table carries only status ACTIVE / INACTIVE.

How a downline joins — two paths

    stateDiagram-v2
    [*] --> CREATED : invitation saved in the applet
    CREATED --> SENT : MLM_INVITATION_LINK_PROCESSOR sends via the program's endpoint links
    SENT --> ACCEPTED : invitee registers through mlm/invitation-acceptance
    [*] --> ACCEPTED : QR / storefront sign-up with the inviter's key (SELF_CLICKED)
    ACCEPTED --> LINEAGE_UPDATED : INVITATION_INSERTION_INTO_LINEAGE_PROCESSOR writes the lineage row
    LINEAGE_UPDATED --> [*]
    note right of CREATED
        REJECTED and EXPIRED exist in the enum
        but nothing sets them automatically
    end note
  

Path 1 — an invitation sent by the applet. You create an invitation for one or more invitees. MLM_INVITATION_LINK_PROCESSOR (MlmInvitationLinkProcessor.java L45; registered in JobProcessorClassName.java L272) picks every ACTIVE invitation whose status is CREATED (L154–163), loads the program’s ACTIVE endpoint links in priority order (L142–151), builds the message from a contact-centre template — the template’s <URL-KEY> is replaced by the program’s URL Forward resolved for the inviter, and the inviter’s entity e-mail is copied in (EmailEndpointService.java L53–90) — and sends by SMS (needs an invitee phone), e-mail (needs an invitee e-mail) or WhatsApp (L118–132). On a 2xx reply the status becomes SENT (L176–179); on failure it tries the next link, up to three attempts, then throws (L102–114). The invitee opens the link and posts to POST core2/tnt/dm/mlm/invitation-acceptance (anonymous; MlmRegistrationController.java L29) with the inviter participant guid, first and last name, e-mail or phone, a password and a referral source (MlmRegistrationRequest.java L7–13). If no confirmed login exists for that e-mail or phone, the backend creates a login subject, a registration record that expires in eight hours, and e-mails or texts a six-digit code and registration key (MlmRegistrationService.java L84–114, L270–313, L392–441); the caller then finishes with POST …/invitation-acceptance/password-creation (code, key, password; L477–560). With or without that step, the backend finds or creates the customer entity, finds or creates the membership card, creates the participant, and sets the matching invitation — same inviter and program, same e-mail or phone — to ACCEPTED with the child member and entity filled in (L349–367). No match raises NO INVITATION CONTAINER COULD BE FOUND.

Path 2 — a QR or shared link. Each participant’s QR tab exposes GET core2/tnt/dm/mlm/programs/{tenantCode}/participant/invite-by-guid/{participantGuid} (anonymous; MlmProgramHdrController.java L197–230). It answers with a 302 to the program’s URL Forward with <URL-KEY> replaced by the participant guid, or 404 when the URL lacks the placeholder or is not a valid URL. When the visitor registers on the storefront with that key as invited_by, post-registration (PostRegistrationService.java L95–96) calls TenantUserConfigurationService.stepLinkUserToMLM (L995–1021): it looks the inviter up by guid or invite key (MlmProgramParticipantHdrUow.getByInviteKeyOrGuid L332–341), creates the new user’s entity, membership card and participant with referral source INVITE (L1043–1072), and writes an invitation that is already ACCEPTED, type SELF_CLICKED, lineage type MEMBERSHIP (L1075–1103). A website whose post-registration request sets mlm_enforce_for_all turns a missing key into the hard error INVITE_URL_KEY_NOT_FOUND_BUT_THE_MLM_ENFORCE_FOR_ALL_SET_TO_TRUE (L1016–1018).

Both paths end the same way. INVITATION_INSERTION_INTO_LINEAGE_PROCESSOR (InvitationInsertionIntoLineageProcessor.java L39; registered L265) takes every ACTIVE invitation at ACCEPTED that carries a child member and entity and a parent participant, member and entity (L78–95), requires the child to already be a participant (NO_PARTICIPANT_FOUND otherwise, L141–156), creates the lineage row through MlmLineageHdrService.create — which computes the level — and sets the invitation to LINEAGE_UPDATED (L106–111).

Lineage maintenance. Changing a row’s upline re-levels its whole subtree (MlmLineageHdrService L42–50, MlmLineageHdrUow.updateForChildrenLevels L530–580); deleting a row re-levels the descendants (L90–112). GET …/mlm/lineages/uplines/{participantGuid} and /downlines/{participantGuid} (MlmLineageHdrController.java L249, L271) return the chain each way; Membership Admin’s MLM tab reads them.

Where the money is. The sales-commission posting run (CommissionPostingServices.java) reads the lineage only for a scheme whose Commission Type is MLM (L205): it loads the buyer entity’s participants (L209–210), walks the lineage upward level by level (getMlmUplineMembers L731–752 — level 0 is the buyer, then each parent), and writes one bl_fi_sale_commission_intermediate_processing_txn_line per upline per sales line with the program guid, level, member and upline entity (L273–287), skipping levels whose treatment is zero. The scheme applied is the one the commission cycle selects; the program’s own Commission Scheme field is not consultedCommissionReportUow.java L460–463 joins the program only to print its name on the report. The member’s referral_code on the membership card is a separate mechanism that no MLM code reads.

Endpoints (all under core2/tnt/dm/mlm/): programs, program-participants (plus POST …/login-ep for a logged-in user to enrol themselves and GET …/login-entity-ep/query for their own participations), lineages (plus /uplines/…, /downlines/…), invitation, entity-contacts, mlm-program-cc-ep-link — each with POST, PUT, DELETE /{guid}, GET, GET /{guid}, GET /query — and the two anonymous acceptance endpoints above.

Related applets

  • Membership Admin — participants are its membership cards; its member edit screen’s MLM and Referrals tabs show the tree this applet builds.
  • Commission Scheme — the MLM commission type and its per-level treatments (up to ten uplines) are defined there.
  • Sales Commission — the posting run that walks the lineage and the report that shows MLM Program and MLM Member per line.
  • Customer Maintenance and Entity — the entities behind participants and entity contacts; acceptance creates a customer entity when none exists.
  • Unified Contact Center — the e-mail, SMS and WhatsApp endpoints and message templates the invitation processor sends through.
  • CP Commerce Admin — the storefront whose sign-up completes the QR path and whose post-registration settings can make the inviter key mandatory.

Troubleshooting

SymptomCauseFix
Nothing happens when you save a Communication Endpoint link; invitations never leave CREATEDThe endpoint-link effects file is entirely commented out, so the create action has no handler; the edit screen is a copy of Entity Contact edit and its Save and Delete dispatch entity-contact actionsInsert the link rows through POST core2/tnt/dm/mlm/mlm-program-cc-ep-link; do not use the edit screen’s Delete, which targets an entity contact
Payout opens a “page not found” screenThe menu entry points at a route (generic) that does not existNone in the applet; commission payouts are in Sales Commission
Changing Default Branch or Location throws an error and nothing is savedThe settings screen never loads the applet container it writes intoNothing reads these values; leave the screen alone
Lineage save fails with …REFERRAL_SOURCE_IS_NULL_OR_EMPTYReferral Source is optional on the form but required by the validatorPick a referral source
Endpoint-link save fails with …PRIORITY_IS_NULL_OR_EMPTY (via API)Priority is NOT NULL in the table and checked by the validatorSend a priority
…VIOLATE_UNIQUE_CONSTRAINTS_ON_PROGRAM_HDR_MEMBER_GUID_ENTITY_HDR… on participant createThe member is already a participant of this programOpen the existing participant
MLM_LINEAGE_HDR_OBJECT_PARENT_AND_CHILD_PARTICIPANT_GUID_IS_THE_SAME / …VIOLATE_UNIQUE_CONSTRAINTSUpline equals downline, or the downline already has an upline in this programCorrect the pair; delete the old link first to move a downline
MLM_LINEAGE_HDR_OBJECT_MAX_OR_MIN_LEVEL_IS_BREACHEDThe new link would put someone deeper than level 10Attach the downline higher in the tree
An invitation past its Expiry Date is still sent, or still acceptedExpiry is stored but never enforced; EXPIRED and REJECTED are never set automaticallySet the invitation INACTIVE (or its status to EXPIRED by hand) to stop it
An accepted invitation never becomes a lineage rowThe child is not yet a participant, or the invitation lacks child member / entity — the lineage processor skips itEnrol the child as a participant; if the status was set to ACCEPTED by hand, fill in the invitee
Acceptance fails with NO INVITATION CONTAINER COULD BE FOUNDThe e-mail or phone entered on the acceptance page differs from the one on the invitation, or the inviter / program does not matchAccept with the invited address, or create a matching invitation
The QR link answers 404 or “Invalid URL”The program’s URL Forward is empty, is not a valid URL, or lacks <URL-KEY>Set URL Forward to the storefront landing page with <URL-KEY> in it
Storefront sign-up rejected with INVITE_URL_KEY_NOT_FOUND_BUT_THE_MLM_ENFORCE_FOR_ALL_SET_TO_TRUEThe website enforces an inviter key on every registration and the visitor arrived without oneRegister through a participant’s link, or turn off enforcement on the website’s post-registration settings
E-mail invitations arrive formatted like the SMS templateThe send processor passes the phone message-type code to the e-mail branch (MlmInvitationLinkProcessor.java L122–125)Make the phone template usable by e-mail until the processor is corrected
Two programs with the same codeNo code or name uniqueness check server-sideKeep codes unique yourself
Uplines are paid under a scheme other than the one on the programThe commission run uses the cycle’s scheme; the program’s Commission Scheme field is informationalConfigure the intended MLM scheme in the commission cycle

Related documentation

Last updated on