Ask a Dev
Extension > How do I place a person in a tree in custom code?
# Question
How do I place a person in a tree in custom Extension code?
# Answer
Sometimes in code, you'll need to place a person in a tree. The Extension makes that possible quite easily, but the syntax isn't readily clear.
### A word of caution:
> Trees are important to the business, so making mistakes here can result in a lot of work. Please make sure you're comfortable with tree structure, tree types, and that you know how to test and check the results of moves in Corp Admin.
The concept of a **node** is used. This is a spot in the tree, and it "knows" about itself: who its parent is, and who it contains.
The "ValidatePlacement" call you see is to make sure you don't create circular references.
Here, we're placing in the "Enroller Tree". For Unilevel and Enroller tree, you need to specify the LegName as "LegName.Empty", not just "null", or you'll get a null reference exception. For Binary or Matrix trees, you can use the appropriate LegNames in that enumeration.
```csharp
var _nodeId = new DirectScale.Disco.Extension.NodeId(_associate.AssociateId);
var enrollernodeDetail = _treeService.GetNodeDetail(_nodeId, TreeType.Enrollment);
try
{
_treeService.ValidatePlacements(
new Placement[] {
new Placement() {
Tree = TreeType.Unilevel,
NodeDetail = new NodeDetail() { NodeId = _nodeId, UplineId = enrollernodeDetail.UplineId, UplineLeg = LegName.Empty }
}
});
_treeService.Place(new Placement[] {
new Placement() {
Tree = TreeType.Unilevel,
NodeDetail = new NodeDetail() { NodeId = _nodeId, UplineId = enrollernodeDetail.UplineId, UplineLeg = LegName.Empty }
};
});
}
```
Posted by Russell Kloepfer over 3 years ago
Extension >Reward Point Loyalty Program Solution
Justin was asked for a solution on a loyalty program, where an associate would receive reward points based on the length of time someone remained on Autoship. This is a solution Justin provided to the development team:
#Notes:
- Reward Points will be distributed to associate types 1 & 3 on all autoship orders based on the rules outlined below.
- The first successful autoship processed is month 1 and will receive 5% of the order subtotal as reward points. Months 1-3 receive 5%, months 4-12 receive 10%, and 13+ months receive 15%.
- The points will not be tied to a specific autoship ID but if the user has an active autoship order every month. We will use the Associate Custom fields to keep track of successful autoship orders as well as the length of consecutive successful autoship orders.
- If the user cancels their autoship and does not have another active autoship (not an annual renewal) their points balance will be removed.
- If the user fails to pay for an autoship order for a month, their reward points will remain on the account and the accumulated months will remain the same until the autoship is cancelled. Autoship retry rules will be reevaluated to account for this use case.
- Once the autoship is paid for after failing for more than one month, the count of months on autoship will continue where it left off. I.e. If the autoship processes successfully in July on month 3 then fails in August and September, when it processes successfully in October it will increase to month 4.
- If there is more than 1 successful autoship processed in the month, each order will earn reward points but the percentage increase will only happen monthly.
- To keep track of the months as well as the last successful autoship order, the Associate Custom fields will be used.
1. CRM_CustomFields Field1 will be the last successful autoship month (mm/yyyy)
2. CRM_CustomFields Field2 will be the number of months on autoship
>A query will need to run to update all existing Associates (Type 1 or 3) with the correct data in CRM_CustomFields Field1 and Field 2 before rolling live. The query will need to be written to match the criteria below to update all existing associates who have been on autoship with the correct last processed month (Field1) and consecutive months on autoship (Field2).
##The developer will need to program the following:
###In the FinalizeAcceptedOrder Hook:
1. Check for OrderType=2 and AutoShipType is 0 or 1
2. Check for AssociateType 1 or 3
3. Get CRM_CustomFields
4. Check Field1 to see if value is any month prior to the current month, if it is
Update Field2 +1
else if Field1 != current month,
Update Field2 = 1
>// Note: if Field1 = current month, no changes to Field2
###Update CRM_CustomFields Field1
- Field1= current month (mm/yyyy)
- Distribute the points based on the rules
- CRM_CustomFields Field2 contains the number of months
1. 1-3 = 5% of Order SubTotal
2. 4-12 = 10% of Order SubTotal
3. 13+ = 15% of Order SubTotal
- Use IRewardPointsService from DirectScale NuGet package to add points
###In the CancelAutoship Hook:
- Check to see if there are other valid autoships (only AutoShipType 0 or 1). If there are no other valid autoships,
- Get RemainingBalance total from CRM_RewardPoints
- Create an entry in that is the negative of the total RemainingBalance
- Use IRewardPointsService from DirectScale NuGet package to add negative points
- Update CRM_CustomFields 1 & 2 back to blank
- CRM_CustomFields Field1 = ""
- CRM_CustomFields Field2 = ""
Posted by Russell Kloepfer over 3 years ago
Extension > Calling Custom Functions from Retail or Web Office code
From Russell:
Calling custom-created Extension functions from Retail and Web Office can be tricky, but it's not bad when you know what's happening. This interaction I think will help clear things up, and I've added a couple of notes below.
Please note that there are two proxy pathways used in the Retail/Web Office API space, "Proxy" (for logged-in users), and "AnonymousProxy" (for anonymous users).
The "AnonymousProxy" you were calling required a status code of "0" (worked with non-Extension API calls from our previous Client DLL). There's a new one we added recently that allows you to call Extension APIs without requiring weird data structures. (the only difference is calling the "AnonymousProxySimple").
var request = {
Region: "us",
};
var baseUrl =
"/AnonymousProxySimple/demo/get-enrollment-activation-kits?timeout=30000"; // "demo" is the client ID. Put yours here.
$RestService
.V3Post(baseUrl, request, true, true)
.then(function (result) {
var response = JSON.parse(result.data.ResponseBody);
console.log(response);
})
.catch(function (err) {
console.log(err);
});
**Note to widget developers**: This same rule applies in the Back Office code. "ProxySimple" and "Proxy" behave differently. You can get rid of that "Status" ID and any other structures and just work directly with results if you use "Simple"
**There are extensive notes on the topic. This is from a document in the DS internal help site**:
# Authenticated Customers
There are two proxy endpoints that authenticated customer accounts can use: Simple and Standard. These are called by custom content pages and widgets in the back office and retail site.
### Simple Endpoint
The simple endpoint is recommended over the standard endpoint because the requirements are less stringent for the Disco client API that is called. Let's walk through what happens when a call is made to the simple endpoint.
1. Suppose the following is posted to the simple API endpoint:
Request URL: https://api.directscale.com/ProxySimple/Associates/GetEyeColor
Request Body: { associateId: 4567 }
Notes:
A request to the proxy endpoint must be a POST. Even if you are not sending a request body to the proxy endpoint, you must still use a POST.
The authentication token of the logged in customer account is required in the request header in the same manner as all other customer API endpoints.
2. The API proxy sends the following request to Disco:
Request URL: https://{clientname}.corpadmin.directscale.com/Command/ClientAPI/Associates/GetEyeColor
Request Body: { associateId: 4567, authenticatedAssociateId: 1234 }
Notes:
The proxy API will determine the correct clientname to use on the domain of the request to Disco by looking at the passed user auth token. It has embedded in it the client the user belongs to.
You should not pass an authenticatedAssociateId parameter on the post to the proxy. The proxy will add the authenticatedAssociateId parameter when it prepares to call Disco. If a call to the proxy includes this parameter, it will be overwritten by the API proxy with the logged in user Id. If a Disco API does not need this parameter, it is ignored.
All Disco custom client API endpoints begin with the root level path /Command/ClientAPI. This is automatically prepended to the passed path from the proxy call. So, do not add /Command/ClientAPI to your proxy call or it will be sent to Disco as /Command/ClientAPI/Command/ClientAPI.
Note on Timeout:
By default, the request to Disco will timeout after 10 seconds. If you need to adjust that timeout, an optional timeout query parameter timeout may be passed on the call to the proxy. The value is the number of seconds to use for the request timeout on the call to Disco.
3. Disco returns some kind of message back to the proxy like this:
Response Status Code: 200
Response Body: { Color: 'Green' }
Notes:
Custom client Disco API endpoints should make use of the authenticatedAssociateId parameter when they need the logged in user Id. In the case of the example above, the Disco API should check that the associateId passed is in the downline of the authenticatedAssociateId or perform whatever other security checks are prudent for the endpoint.
4. The proxy returns the data from Disco like this:
Response Body: { ResponseBody: { Color : 'Green' }, ResponseStatus: 200 }
Notes:
The ResponseStatus is set to whatever HTTP response status code the call to the Disco endpoint returned.
The ResponseBody is set to whatever response body the Disco endpoint returned.
If the call to the Disco API timed out or had a TCP error, the proxy will return a 500 HTTP response with an empty response body. The Intranet logs time outs and TCP errors on the Exception Logs page.
### Standard Endpoint
The standard endpoint is the first version of the proxy. It expects Disco endpoint responses in a certain format. Let's walk through what happens when a call is made to the standard endpoint.
Notes from the simple endpoint apply here too except where otherwise noted.
1. Suppose the following is posted to the standard API endpoint:
Request URL: https://api.directscale.com/Proxy/Associates/GetEyeColor
Request Body: {associateId: 4567 }
2. The API proxy sends the following request to Disco:
Request URL: https://{clientname}.corpadmin.directscale.com/Command/ClientAPI/Associates/GetEyeColor
Request Body: { associateId: 4567, authenticatedAssociateId: 1234 }
3. Disco returns some kind of message back to the proxy like this:
Response Status Code: 200
Response Body: { Status: 0, Message: null, Data: { Color: 'Green' } }
Notes:
The API proxy is expecting that the Disco endpoint will return a JSON object in the response body with the properties Status, Data and Message.
4. The proxy returns the data from Disco like this:
Response Body: { ErrorMessage: null, Response: { Color: 'Green' } }
Notes:
The response JSON object of the API1 proxy includes two properties: ErrorMessage and Response.
ErrorMessage contains the value of the Message property returned in the Disco response when the Status property is set to a non-zero value. Otherwise it will be null.
Response contains the value of the Data property returned in the Disco response when the Status property is set to zero. Otherwise it will be null.
If the Disco call returned a non-zero value of Status, the Intranet will log the error and the proxy will return an empty 500 HTTP response in the same manner as the simple endpoint error handling.
# Unauthenticated Customers
The unauthenticated proxy endpoints are called by enrollment or other sites that do not have a logged in user auth token to pass.
The endpoints behave in the same manner as the authenticated customer endpoints except:
No authenticatedAssociateId parameter is added to the requests to Disco because the user is not logged in.
The paths of the endpoints require the name of the client be passed.
Simple Endpoint: https://api.directscale.com/AnoymousProxySimple/{client}/{the rest of the path}
Standard Endpoint: https://api.directscale.com/AnonymousProxy/{client}/{the rest of the path}
# Authenticated Admin Users
There is only one authenticated admin user proxy endpoint. It is called by custom content admin pages.
The admin proxy endpoint behaves in the same manner as the authenticated customer simple endpoint except:
The authenticatedAssociateId that is passed to Disco by the proxy is, of course, the Id of the logged in admin user not the Id of a logged in customer.
The path of the admin endpoint is: https://api.directscale.com/Admin/Proxy/{the rest of the path}
Read more: [Client API Proxy: Corporate Office (Disco)](https://developers.directscale.com/docs/disco-client-api-proxy)
Posted by Learning Experience Team over 3 years ago
Extension > Creating Custom Coupons on the Fly
From Russell:
**Q**: Do we need to call `SaveCoupon` from `couponService` or not while creating a custom coupon and applying it on the order on the fly? Also, does adding a newly created coupon mean that only its couponCode should be added to the `string[]` of the `orderCoupons` object?
**A**: When using the **ProcessCouponHook**. You have two options:
1. Add an existing coupon code to the `string[] CouponCodes`, then call the base functionality `func(request)`. If you use this method, the coupon code has already been set up in the system and will determine its discount amount. You are just adding to the order automatically.
2. Build your own Discount. Sometimes you want to build a discount that is different for everyone. In this case, you will run the base functionality and develop your `OrderCoupon` object. Using this method, you can create the discount amount very custom per person and order. This object asks for a `couponID`, which is looking at coupons that already exist, but you do not have to give a valid `couponID` if you don't want to. If you give it a valid `couponID,` the only thing that it will do is show that a coupon was used on this order in the UI.
Posted by Learning Experience Team over 3 years ago
In the Client DLL, where does ServiceLocator.Instance.Logger log to?
Because the Client DLL is not part of the new Extension, `ServiceLocator.Instance.Logger` logs to Azure. Unfortunately, we can't provide Azure access as you would be able to access all client logs, not just your own.
The new Extension logs to the ExtensionLog table. Read more: [Extension Log](https://developers.directscale.com/docs/extension-log)
Posted by Learning Experience Team over 3 years ago
Extension > Getting Client ID and Environment on Running Application
From Russell:
Sometimes, it's essential to know whether you're running in a Sandbox or the Live environment and to verify the Client ID of the instance you're running on. This can be done in the extension using the following:
ExtensionContext is part of the SettingsService (ISettingsService), which can be injected.
Once you've got the handle to SettingsService, do this:
```
string clientId = _settingsService.ExtensionContext().ClientId; // IE, "demo"
DirectScale.Disco.Extension.EnvironmentType envType = _settingsService.ExtensionContext().EnvironmentType;
// EnvironmentType is an enum, and can be either Sandbox or Live
```
Read more: [Verifying Extension Environment and Client ID](https://developers.directscale.com/docs/verifying-extension-environment-and-client-id)
Posted by Learning Experience Team over 3 years ago