Hi guys,

I'd like to add a couple of features (so the work is not directly on you,) one of which is a forced matrix. I suppose you heard of those where you have a limited number of people that can be added directly below you.

I'd like something where an affiliate can get up to n people in his downline, then any other person becomes an affiliate of someone further down in the tree. I will call those "auto-affiliates". It is important since the tree needs to be built as fairly as possible.

For instance, the tree is defined as 5,3,10 (at this time, the tree depth is just 5, I'd add the support for the two other numbers: maximum number of auto-affiliates and maximum number of affiliates under someone at all time.)

  • User U registers an account
  • User U finds user P1 who registers an account, P1 is right below U
  • User U finds user P2 who registers an account, P2 is right below U
  •     etc. up to P10 (last number in the tree depth definition: 5,3,10)
  • User U continues to find other users... P11 cannot be put right under U because U already has 10 affiliates (assuming no one else left.) User P11 is added under P1 instead.
  • User U finds P12 who is now added under P2.
  •     etc. up to P20 who is added under P10.
  • Then we repeat that with P21 to P30...
  • Then we repeat that with P31 to P40...
  • At that point we reached the maximum auto-affiliates under P1 to P10 (30 people total, 3 per sub-affiliate.) So we want to add new affiliates found by U under P11 to P40 instead (90 more people at that level)
  • Once all the possible auto-affiliates under the 5 levels tree were added, restart at the top of the tree and add new users under affiliates who have the least sub-affiliates, if multiple are at the same level, add the new affiliate to the oldest sub-affiliate
  • In this example, once U has 111,110 affiliates, he reached his limit for that account... He loses new affiliates to someone who is under his tree in case another user leaves the tree and thus makes those extra affiliates will become his sub-affiliates!

I hope this makes sense...

It may sound a little complicated, it is not really that bad. 8-) Of course, since a sub-affiliate could himself/herself find other sub-affiliates it skews the search for adding a new affiliate... We may want to offer different options too (i.e. randomize at a given level instead of using the oldest affiliate first, count the maximum of 3 as the maximum of sub-affiliate total instead of auto-affiliates...) And the worst case is when someone leaves the tree! (especially if you give them 30 days to change their mind... hmmm... that one will be for later! 8-) )

So, now the real question: Would you agree in integrating such a feature in your module? If not directly in your module (and if possible) would you mind adding the necessary hooks so I can create a separate module that offers this extra capability to your module?

Thank you.
Alexis Wilke

Comments

bojanz’s picture

I'm not sure I see the benefit of adding this to the module, since it's quite a bit more complicated than the average use case.

However, I can integrate the patches and hooks (planning to use conditional actions?) you need to make it happen.

bojanz’s picture

Status: Active » Closed (fixed)
AlexisWilke’s picture

Status: Closed (fixed) » Active

Hey, I'm not done yet!

Sorry if I was a little long but I will post patches here so my new module works. I'm starting now. I should have something within a few days.

Thank you for keeping this active!
Alexis

AlexisWilke’s picture

Hi bojanz,

Existing module

Okay, I'm trying to figure out how to program my system and I have a few questions about the existing module:

1) What happens if I delete a user?

At this point it looks like you do nothing at all. This means you will continue to generate commissions to that user, although it works because you never access the "users" table in that code, I would think that could look suspicious to an admin to have to pay someone he previously deleted...

This is not a trivial delete since all the affiliates below that user should probably get that user affiliate as their previous level affiliate. Hmmm... Let's give an example: User A is the affiliate of user B; and User B is the affiliate of user C. At this time, A receives commissions on sales to B and C. User B receives commissions on user C. The administrator decides to delete B. I think A should continue to receive commissions on C as if C was in his direct downline. At this point, you do not modify the uc_affiliate2_users table at all.

I know you also have "product affiliates". So I'm not too sure how much work is involved right at this point...

2) uc_order_comment_save() with wrong user?

It looks like you are adding a comment to an order with the current user (global $user; although in one case $user is not even defined.)

That probably needs to be fixed using either the order user or the affiliate (I think the latter is what was meant to be, but I may be wrong.)

3) Cumulative commissions

From what I understand, the commissions are added to each user's account by the uc_affiliate2_apply_commission() function. This happens each time there is an update on the order. I did not test yet, but my orders go from Payment Received to Completed and in both cases I tell you to add the commissions. It looks like it could very well be added twice in that case... At least, I did not see anything that would prevent that from happening.

I think that a simple solution to that problem would be to do a SELECT with the affiliate ID and order ID. If we find an entry, skip the save altogether, it was already processed.

Forced Matrix module

Otherwise, from what I can see so far, and I may have missed something, but anyway... all I would need is receive some sort of an event when the uc_affiliate2_apply_commission() function is entered. I need the Order and the Affiliate identifier. With those two parameters, from what I can tell at this point, I can force the matrix as required by my needs.

Just right now, I'm not too sure what would be best, a ca_pull_trigger() call should work since we can modify the parameters in the callback. My idea is pretty simple. The Forced Matrix module will manage its own tree and thus be capable of knowing where a new customer is to be added. If the call is not about a new customer, leave everything in place (i.e. case when the Admin edits the order.)

If the Forced Matrix module kicks in and determines that the current user is "complete," then make changes to the tables and return with a new Affiliate identifier. The order will not be modified (unless we have to change the product affiliates too, but I'm not too sure how that works right now... I may have to forfeit that feature to start with.)

More soon.
Thank you.
Alexis Wilke

bojanz’s picture

Regarding #1:
We do a user_load when registering clicks. So, if a user has been deleted, he can't get no clicks.

However, nothing is stopping him from getting a commission if the user already had his uid in session (or a cookie), so an additional check needs to be made when awarding commissions.

Also, we do nothing on handling the downline of the affiliate user, appropriate code needs to be added, so that when a user is deleted, his downline is examined and modified.

Regarding #2:
I've just made a CVS commit that should make it good.

We add an order comment in two cases:
1) The admin (or someone with equal permissions) is removing a commission. In this case, we use his uid, so that it can be seen who did it (we were missing a "global $user" here, thanks)
2) A commission is being added. This could happen at the end of a user checkout (and not by admin just changing the order status), so using the current user uid is a mistake, I changed it to 0 (like in the example in the docs)

Regarding #3:
The commission is awarded only if it goes from a "don't award" to a "award" status.
If it goes from an "award" status to another "award" status, nothing will happen.

Still, I do agree that an additional check should be made.

Doud’s picture

This feature seems nice!

I know a lot of networks that have forced matrices.
It sounds like a good feature to add, some systems even have several matrices generating commissions at different rates and levels.
For example a basic matrix where users can have unlimited number of direct affiliates, generating low commissions, and another matrix, cohexist with the first one but that only allow two direct affiliates, and would spread the new registered users under those ones, generating different rates of commission.

So its in fact 2 separate functionalities we're talking about :
- hability to create unlimited matrices
- per matrix settings : free or forced matrix

And the question about the module behavement facing user delete is a good one, but also quite complex, different rules could be applied.

+Following

bojanz’s picture

+1 on using conditional actions and having the forced matrix as a separate module.
Should be much easier to maintain that way.

AlexisWilke’s picture

Doud,

Using different roles, you will be able to allow one (free) or the other (forced) on the same website.

So, you could have two roles such as: "Affiliates" and "Forced matrix". And make people part of one or the other (the Forced matrix would have priority if you are part of both, so any product that allow for a downline would use the forced matrix.)

What is your take about Deleted users? At this point, my idea is to handle the tree as a very simple tree. Say you have users A, B, C, and D. A found B, and B found C and D. B leaves, A inherits C and D. But at that point A may have reached his limit (i.e. we can replace B with C, but D is "too much".) In that case I place D as if it were a new affiliate of A, using the same rules as you would otherwise use for new affiliates.

Would that sound like what you'd do? I guess C could be places under A as a new affiliate too and end up in place of B automatically.

Bojanz,

Yes. And I was thinking that the conditional actions could make use of additional conditions such as a minimum amount spent, the type of product, whether the product as the Recurring feature attached, etc.

Thank you.
Alexis

Doud’s picture

Hi AlexisWilke,

Using different roles definitly seems to be the solution.

In my affiliate site, I use a role based commission architecture. Depending on the user's role, different commission rates are applied per depth. When cumulating roles with 'act as affiliate' permission, uc_affiliate2 return only one commission structure, and not necessarly the one you're looking for.
I made trigger based rules to change user roles from one to another depending on their amount of revenues.

In order to keep functionning like this, I would need to create new roles (one "free matrix" and one "forced matrix") that do not have the 'act as affiliate' permission.

Did you built it in this way ? Maybe a 'receive commissions' permission would resolve this.
User that would be affiliate but that do not have the 'receive commissions' permission would be affiliates, but would not receive any comission.

Your solution for deleted users seems to be the good one, its based on the most common affiliation systems rules so should cover many needs.

And what do you think about having the possibility of creating several matrices with you tree depth calcultation system ?
Each one would create an associated role. And depending on the site settings, if the user is part of several, we can either select the matrix to use by a weight system, or we can choose to cumulate the two matrices.

----------

Bojanz,

I don't think this should be a separate module. A forced matrix automatically position people following certain rules.
The tree calculation rule proposed by AlexisWilke (5,3,10) can be very flexible.

With those settings and if we consider 0 as 'unlimited', a tree structure like (5,0,0) represent a free matrix.

My opinion it that adding this functionality would enhance this module and cover more needs, making it a standard solution for Affiliation Systems.

Let me know if you need a hand on this.

Cheers,

Ed

AlexisWilke’s picture

Status: Active » Needs review
StatusFileSize
new1.6 KB

@Doud,

Okay, I have a starting point here:

UC Matrix

I does not do anything yet, but it has all the settings and the trigger/capture support that is necessary to transform the tree appropriately.

In regard to having it in a separate module, it probably isn't a bad idea, although new versions of the uc_affiliate2 module may break the uc_matrix module... We'll see how that works. Another way would have been for me to take the existing uc_affiliate2 as it was and not depend on this module. Just rename it uc_matrix and make that work instead.

Anyway... duplicate effort is an annoyance too. 8-)

As mentioned in an earlier post, we'll need some extra trigger. Cool thing since you could add all sorts of reasons why the trigger should not be taken in account (i.e. user not in right group, order amount too small or too large, etc.)

Bojanz,

Let me know if you see any problem with that patch. I may need to have the returned value tested to bypass the default behavior. I hope not, we'll see.

Thank you.
Alexis Wilke

bojanz’s picture

Having it as a separate module is the best option.
Plus, I don't think there will be much breakage in the future concerning this (even in the soon to be started 3.x branch).

You know very well what a nightmare it would be to maintain a fork of uc_affiliate2 (having to port over and apply patches, most not even concerning you...).

Patch looks okay. Test those triggers, test your module, ensure you have in that patch everything you need, and when that happens, I will commit it.

AlexisWilke’s picture

StatusFileSize
new74.37 KB

Bojanz,

As I'm trying to figure out how to count things, I noticed what feels like a bug. I have to test to make sure though... 8-)

It looks like in two cases the linking between affiliate and user does not occur.

The following graph is a somewhat simplified graph showing what happens in the affiliate module. The two red lines should be followed but they are not at this point. (see attached.)

Thank you.
Alexis

drumnjo’s picture

subscribe

bojanz’s picture

How's the forced matrix coming along?
Did you make any more changes, or is the current patch the one I should commit?

Also, did you manage to reproduce the bugs mentioned in the graph?
I've taken a look at the graph and your first red line seems to be invalid, can't seem to find anything wrong there, but then again I think better in code than in pictures.

AlexisWilke’s picture

bojanz,

There is only one case when you link aid and uid together. That's on a user registration (when user_save() is called and the user did not exist before.) And that happens either when you purchase something or actually register. But really only if you did not have an account on the affiliate enabled website.

Maybe if you had case 'update' as well... and add the link between aid/uid if it does not exist yet?

/**
 * Implementation of hook_user()
 */
function uc_affiliate2_user($op, &$edit, &$account, $category = NULL) {
  global $user;
  switch ($op) {
    case 'insert':
      // update user count for affiliate and associate user with affiliate
      $aff = $_SESSION['affiliate'];
      if ($aff) {
        db_query('INSERT INTO {uc_affiliate2_users} (aid, uid) VALUES (%d, %d)', $aff, $account->uid);

        $message = t('User !name referred by user id !affiliate', array('!name' => theme('username', $account),
                                                                        '!affiliate' => theme('placeholder', $aff)
                                                                        )
                     );

        watchdog('user', $message);
      }
      break;

A case that does not work going something like this:

1. User A comes to my website
2. User A becomes affiliate
3. User A put a link on his website

4. User B comes to my website
5. User B creates an account
6. User B leaves... (B is not an affiliate nor did he click any link to become one.)

7. User B finds User A website and clicks on his affiliate link
8. B is still logged on my website
9. B is never linked to A
10. B purchase something, A receives nothing

Now some people may consider that since B found the website before clicking on A's link that we owe nothing to A...

I did not have much time to look in UC Matrix lately... But the only thing left, really, is counting the number of paying children. (well... we'd have to make the Delete work properly too! Especially if a child is not considered a child anymore once he/she stops paying a recurring fee! One thing at a time, though.)

Thank you.
Alexis

bojanz’s picture

Okay, I now understand what you're saying.

Frankly, I'm doing Affiliate-NG/Commerce Affiliate without multilevel affiliating, and avoiding dilemmas like this one. The fact is that people rarely use it, and those who don't never come and explain their user case, making it hard to do it right. Maybe it's just that the current way of doing it is good enough for most who want multilevel affiliates, so they don't speak up.

AlexisWilke’s picture

bojanz,

The two "broken" cases may not be considered broken to some people since the user would have come to your website before clicking an affiliate link. However, in this case, it is not even a question of whether you have parent/child. In my example B is not an affiliate and he purchases something after he clicked on the link offered by A. A could complain that he should be assigned the commission. That would fail in all cases, with a parent/child or just a random customer.

Thank you.
Alexis

bojanz’s picture

Are you sure?

The click handling code doesn't take into account if the user is logged-in or not, if B followed A's link, A should get a commission regardless of what B did earlier. So what you're describing should be a non-issue.

Maybe I'm misunderstanding it...

AlexisWilke’s picture

if B followed A's link, A should get a commission regardless of what B did earlier.

Try the steps in #15 and see the A does not receive the commission.

Thank you.
Alexis