using Sage.CRM.WebObject

advertisement
Sage CRM Developers Course
Using the .NET API
(2)
Looking ahead to the classes
DP01: Introduction to the Development
Partner Program
DP02: Entities and the Data Model (Part 1 of
2)
DP03: Entities and the Data Model (Part 2 of
2)
DP04: Implementing Screen Based Rules
(Part 1 of 2)
DP05: Implementing Screen Based Rules
(Part 2 of 2)
DP06: Screen and User Independent
Business Rules
DP07: Workflow (Part 1 of 2)
DP08: Workflow (Part 2 of 2)
DP09: Using the API Objects in ASP Pages
(Part 1 of 2)
DP10 : Using the API Objects in ASP Pages
(Part 2 of 2)
DP11: Using the Component Manager
DP12: Programming for the Advanced Email
Manager
DP13: Using the Web Services API
DP14: Using the Web Services API (Part 2 of
2)
DP15: Coding the Web Self Service COM API
(Part 1 of 2)
DP16: Coding the Web Self Service COM API
(Part 2 of 2)
DP17: Using the .NET API (Part 1 of 2)
DP18: Using the .NET API (Part 2 of 2)
Agenda
The Specialised Classes
ListPage
DataPage
DataPageEdit
DataPageDelete
DataPageNew
SearchPage
Workflow using the .Net API
Customizing the Specialised Classes
Programmatic Control of Screens
Building Complex Pages
Controlling Screen Positioning
The Specialised
Classes
ListPage
ListPage
using System;
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
namespace crm72a
{
public class OppoListPage : ListPage
{
public OppoListPage()
: base("opportunity", "opportunitylist", "opportunityfilterbox")
{
FilterByField = "oppo_assigneduserID";
FilterByContextId = (int)Sage.KeyList.UserId;
}
}
}
DataPage
DataPage
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
using Sage.CRM.Data;
namespace crm72a
{
public class myDataPage : DataPage
{
public myDataPage()
: base("company", "comp_companyid", "companyboxshort")
{
this.UseEntityTabs = true;
this.UseWorkflow = true;
this.EditMethod = "RunDataPageEdit";
this.ContinueMethod = "RunListPage";
}
}
}
Allows complex screens to be built using simple DataPage class
DataPageEdit
DataPageEdit
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
namespace myProject
{
public class myClass : DataPageEdit
{
/* Constructor needs EntityName, IdField and ScreenName
*/
public myClass()
: base("xxxxTableName", "xxxx_fieldname", "xxxxScreenName")
{
this.UseEntityTabs = true;
this.UseWorkflow = true;
this.CancelMethod = "RunListPage";
this.DeleteMethod = "RunDataPageDelete";
this.SaveMethod = "RunDataPage";
}
}
}
DataPageDelete
DataPageDelete
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
namespace myProject
{
public class myClass : DataPageDelete
{
/* If inherited from DataPageDelete then just need to set the EntityName, IdField and ScreenName
*/
public myClass()
: base("xxxxTableName", "xxxx_fieldname", "xxxxScreenName")
{
this.CancelMethod = "RunListPage";
this.DeleteMethod = "RunDataPage";
}
}
}
DataPageNew
DataPageNew
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
using Sage.CRM.Controls;
using Sage.CRM.Data;
namespace myProject
{
public class myClass : DataPageNew
{
public myClass()
: base("xxxxTableName", "xxxx_fieldname", "xxxxScreenName")
{
this.SaveMethod = "RunDataPage";
this.UseWorkflow = true;
this.WorkflowName = "Entity Workflow";
this.WorkflowState = "NewRecord";
}
public override void AfterSave(EntryGroup screen)
{
/*Record recNewOppo = screen.getRecord;
*recNewOppo.SetField("xxxxx_companyid", int.Parse(GetContextInfo("company", "comp_companyid")));
*recNewOppo.SetField("xxxxx_personid", int.Parse(GetContextInfo("company", "comp_primarypersonid")));
*recNewOppo.SaveChanges();
*/
base.AfterSave(screen);
}
}
}
SearchPage
SearchPage
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
namespace myProject
{
public class myClass : SearchPage
{
/*if descended from SearchPage then you just need to set the
* appropriate ListName and SearchBoxName
*/
public myClass()
: base("xxxxSearchBoxName", "xxxxListName")
{
this.SavedSearch = true;
/* *************
* Other objects available include:
*
* SearchScreen
* ResultsGrid
*/
}
}
}
Search Screen Additional Components
Saved Search available
Keyword Search available
Clear Button automatic in class
Create New Group
Requires additional custom coding by developer
Actions
Requires additional custom coding by developer
Workflow using the
.Net API
Workflow in DataPages
Controlled by properties of Specialised
Classes
DataPageBase
UseWorkflow
DataPage
isWorkflowPostBack
DataPageEdit
WorkflowScreen
DataPageNew
WorkflowName
WorkflowState
Calling Custom ASP and .NET Workflow
Behaviour
ASP pages and .NET assemblies may only be called by user
driven rules (Primary, Transitional, Global) and may not be
invoked by Escalation rules.
Can either call ASP page or .NET assembly
Workflow actions would be ignored
Jscript conditions maybe used to control availability of rule
Customizing the
Specialised Classes
Programmatic Control of Screens
Adding Conditional Behaviour to a screen
Controlling the display of data elements
Adding and removing default buttons
Adding Custom controls to screens
– Buttons and Hyperlinks (Save, Cancel, Help, Clear etc)
Adding/Removing Fields from screens and lists
Interacting with existing CRM information (TopContent and (recent) menus)
Sage.CRM.Controls
The Sage.CRM.Controls namespace provides access to screen
components to allow programmatic update of properties
Entry
– Fields within a Meta Data defined screen
EntryGroup
– Control of HTML returned and interaction with component Fields (Entry)
GridCol
– Columns within a Meta Data defined list
List
– Control of the HTML returned and interaction with component Columns (GridCol)
Interacting with the Screens
When you call AddEntryGroup (c.f.
DataPage) it stores up all the names
of the entry groups that you have
added in the array called
EntryGroups
Contains EntryGroup objects for each
screen.
Can reference EntryGroups array in
BuildContents
In ListPage can access
ResultsGrid
Screen Fields
EntryGroup mygroup = EntryGroups[1];
Entry newE =
mygroup.CreateEntry("oppo_createddate");
base.BuildContents();
List Columns
for (int i = 0; i < ResultsGrid.Count; i++)
{
if (ResultsGrid[i].Name ==
"oppo_stage")
{
ResultsGrid[i].ShowHeading =
false;
break;
}
}
Linking GridCols to ASP and .NET
To link to an ASP page
GridCol colOppoStatus = this.ResultsGrid[0];
colOppoStatus.JumpAction = 430;
colOppoStatus.CustomActionFile = "CRMtest.asp";
colOppoStatus.CustomIdField = "oppo_opportunityid";
To call a CustomDotNet dll
GridCol colOppoStatus = this.ResultsGrid[0];
colOppoStatus.JumpAction = 432;
colOppoStatus.CustomActionFile = "CRMtest&dotnetfunc=RunOppoSummary";
colOppoStatus.CustomIdField = "oppo_opportunityid";
User Screen TopContent
Custom Entity
GetCustomEntityTopFrame("Project");
User Screen control
HTMLString strFieldText = new HTMLString("<span class=TOPCAPTION>Custom Entity
Information</span>");
VerticalPanel panelFields = new VerticalPanel();
panelFields.Add(strFieldText);
ImageObject myEntityImage = new ImageObject(CurrentUser.VirtualImgPath() +
"Icons/company.png");
this.AddTopContent(new HorizontalPanel(myEntityImage, panelFields));
Admin Screen TopContent
//change the breadcrumb trail as needed;
string strCrumbTrail = "<A CLASS=TOPBC TARGET = EWARE_MID HREF=" + Url("1650") +
"&MenuName=Admin&BC=Admin,Admin>Administration</A> ->";
strCrumbTrail += " <A CLASS=TOPBC TARGET = EWARE_MID HREF=" + Url("1650") +
"&MenuName=AdminSystem&BC=Admin,Admin,AdminSystem,System>System</A> ->";
strCrumbTrail += " <A CLASS=TOPBC TARGET = EWARE_MID HREF=" +
UrlDotNet(ThisDotNetDll, ThisDotNetFunction) +
"&MenuName=Admin&BC=Admin,Admin,AdminSystem>My Admin</A> ->";
HTMLString strFieldText = new HTMLString(strCrumbTrail);
VerticalPanel panelFields = new VerticalPanel();
panelFields.Add(strFieldText);
ImageObject myEntityImage = new ImageObject(CurrentUser.VirtualImgPath() + "Icons/admin.png");
this.AddTopContent(new HorizontalPanel(myEntityImage, panelFields));
Button Types
Main Default Buttons
AddEditButton
AddHelpButton
AddDeleteButton
AddContinueButton
AddSaveButton
AddCancelButton
Custom Buttons
AddConfirmButton("CaptionCode","Image.gif","Question");
AddConfirmButton("CaptionCode","Image.gif","Question","fieldname","fieldvalue");
AddSubmitButton("CaptionCode","Image.gif","javascript")
AddSubmitButton("CaptionCode","Image.gif","fieldname","fieldvalue");
AddUrlButton("CaptionCode","Image.gif","URL");
string test = "/Main Menu/wwhelp/wwhimpl/js/html/wwhelp.htm?href=AddingInformation.html";
AddHelpButton(test);
Special Help Method
Add the help button
AddHelpButton("help.htm");
In CRM - turn on inline translation mode and go to your dot net dll page with the help
button on it.
Click the help button and the list of available help files will be displayed.
Select the desired help file and save.
Turn off inline translation mode and your help button on your .net dll page will
launch the help file you selected previously.
Overriding Button Behaviour
Examples
AddEditButton
public override void AddEditButton()
{
string sUrl = UrlDotNet(ThisDotNetDll, "RunEditOpportunity");
AddUrlButton("Edit", "Edit.gif", sUrl);
}
AddContinueButton
public override void AddContinueButton()
{
//base.AddContinueButton();
//string sUrl = UrlDotNet(ThisDotNetDll, "RunDeleteOpportunity");
string sUrl = Url("184"); ;
AddUrlButton("Continue", "Continue.gif", sUrl);
}
Tip:
Ensure Base.AddCancelButton is commented out to stop default behaviour being
implemented
Dealing with Security Policy
For example adding Custom Button to a Screen
Delete, Edit
if (CurrentUser.IsInRange(Sage.PermissionType.Delete, "EntityName",
TerritoryId,AssignedToUserId, AssignedToTeamId, CreatedByUserId))
{
string sUrl = UrlDotNet(ThisDotNetDll, "RunCustomAction");
AddUrlButton("Continue", "Continue.gif", sUrl);
}
New Button
CurrentUser.HasRights(Sage.PermissionType.Insert, "Entity");
Custom Validation
Can override the Validate method to add your
own validation
Must always call the base validation method as
this checks for required fields.
public override bool Validate()
{
//Always call the base validation method as it
checks for required fields
Boolean ok = base.Validate();
if (ok)
{
//do my validation here!
//Check values on screen with
Dispatch.ContentField("fieldname");
string name =
Dispatch.ContentField("core_name");
if (name == "aaa")
{
ok = false;
AddError("The name cannot be aaa");
}
}
return ok;
}
Overriding the AfterSave()
public override void AfterSave(EntryGroup screen)
{
Record recNewOppo = EntryGroups[0].getRecord;
recNewOppo.SetField("oppo_primarycompanyid",
int.Parse(GetContextInfo("company","comp_companyid")));
recNewOppo.SetField("oppo_primarypersonid",
int.Parse(GetContextInfo("company", "comp_primarypersonid")));
recNewOppo.SaveChanges();
base.AfterSave(screen);
}
Intercepting the SaveChanges()
Override of the EntryGroups_FillRecordFromContent method
public override void EntryGroups_FillRecordFromContent(Sage.CRM.Data.Record
info)
{
base.EntryGroups_FillRecordFromContent(info);
info.SetField("core_name", "Override the core name");
info.SetField("core_Status", "Logged");
}
Building Complex
Pages
Typical Web Page Tasks
Building Pages without Reference to Rebuild of Company Quicklook
showing inclusion of custom entity
Meta Data
Demonstration of dll called via workflow
Data Tasks
Cloning and copying data
– E.g. Copying a Case
– Cloning a Report or Group
Custom Complex Pages
Multi-Entity Edit or Entry Screens
Multiple Lists on Page
Interaction with 3rd Party Apps via COM or
web services API including screen scraping
from legacy systems
e.g. Opportunity Workflow
Rebuild of the Company Summary
Screen
Editable grid example
e.g. quick editing/entry of contacts in company
Custom Import example
e.g. Importing folder contents to create library
entries
List with check boxes (to allow batch
tasks)
Listing and Notes with a Custom Entity
Compound Entry Screen
quick entry of company with oppo/case details
Sage.CRM.Data
Specialised Web Classes referencing Meta Data screens and lists automatically
imply Data Interaction
Explicit control of Data Interaction provided by Record and Entity classes within
Sage.CRM.Data namespace
Record
– Allows individual records and data sets to be handled for Create, Read and Update and
Delete
Entity
– Allows compound entities to be handled in a unified manner
– E.g. Insert of Company together with Person, Address, Phone, Email and intersection
table records
Record Handling
FindCurrentRecord(tablename)
gets current record
FindRecord(tablename, whereclause)
gets the record specified by where clause (like the existing COM)
Data Handling Changes
EntryGroup screenPersonBoxShort = new
EntryGroup(“CompanyBoxShort");
compGroup.Fill (compRec);
QuerySelect PhonQuery = GetQuery();
PhonQuery.SQLCommand=querySQL;
PhonQuery.ExecuteReader();
//PhonQuery.ExecuteNonQuery();
Usage
Record object
FindCurrentRecord(“Tablename”)
FindCurrentEntity(“EntityName”)
FindRecord(“TableName”,”WhereClause”)
– record set maybe returned
Record NewUserConRec = new
Record("UserContacts");
Specialised Classes
CreateAppointment()
CreateTask()
Record OppoRecords =
FindRecord("opportunity",
"oppo_assigneduserid=" +
CurrentUser.UserId);
while (!OppoRecords.Eof())
{
AddContent(OppoRecords.GetFieldA
sString("oppo_description"));
OppoRecords.GoToNext();
}
Sage.CRM.HTML
Building forms, fields and tables with out reference to meta data
UI control
Creating Lists and Screens
List
List CommList = new List("CommunicationList");
Screen
EntryGroup compGroup = new EntryGroup("companyboxlong");
Filtering of Lists
AddContent(HTML.Form());
AddContent(HTML.StartTable());
GetTabs();
List CommList = new List("CommunicationList");
CommList.RowsPerScreen = 4;
CommList.ShowNavigationButtons = false;
CommList.Filter = "cmli_comm_companyid =" + Dispatch.EitherField("key1");
AddContent(HTML.GridData(CommList.ToHtml()));
AddContent(HTML.EndTable());
Controlling
Positioning
Using objects in the
Sage.CRM.UI
namespace
Use of Vertical Panel
using System;
using System.Collections.Generic;
using System.Text;
using Sage.CRM.WebObject;
using Sage.CRM.UI;
using Sage.CRM.Data;
using Sage.CRM.Controls;
namespace CRM62a
{
class OpportunityQuotes : Web
{
public override void BuildContents()
{
GetTabs();
VerticalPanel vp = new VerticalPanel();
vp.AddAttribute("width", "100%");
Record oppRecord = FindCurrentRecord("Opportunity");
EntryGroup oppScreen = new EntryGroup("OpportunityDetailBox");
oppScreen.Title = "Opportunity Detail";
oppScreen.Fill(oppRecord);
vp.Add(oppScreen);
List quotesGrid = new List("QuotesGrid");
quotesGrid.SelectSql = "Select * From quotes";
quotesGrid.Filter = "Quot_Opportunityid = " + oppRecord.GetFieldAsString("Oppo_OpportunityId");
vp.Add(quotesGrid);
AddContent(vp);
}
}
}
Compounding Pages
When using Panels must convert to UI object
HorizontalPanel
VerticalPanel
VerticalPanel myPipeandList = new VerticalPanel();
HTMLString myPipeLine = new HTMLString();
myPipeLine.Html = myPipe.Execute();
myPipeandList.Add(myPipeLine);
HorizontalPanel myListandFilter = new HorizontalPanel();
EntryGroup oppoFilter = new EntryGroup("OpportunityFilterBox");
HTMLString FilterBoxHTML = new HTMLString();
FilterBoxHTML.Html = oppoFilter.GetHtmlInEditMode();
myListandFilter.Add(FilterBoxHTML);
Q&A
Looking ahead to the classes
DP01: Introduction to the Development
Partner Program
DP02: Entities and the Data Model (Part 1 of
2)
DP03: Entities and the Data Model (Part 2 of
2)
DP04: Implementing Screen Based Rules
(Part 1 of 2)
DP05: Implementing Screen Based Rules
(Part 2 of 2)
DP06: Screen and User Independent
Business Rules
DP07: Workflow (Part 1 of 2)
DP08: Workflow (Part 2 of 2)
DP09: Using the API Objects in ASP Pages
(Part 1 of 2)
DP10 : Using the API Objects in ASP Pages
(Part 2 of 2)
DP11: Using the Component Manager
DP12: Programming for the Advanced Email
Manager
DP13: Using the Web Services API
DP14: Using the Web Services API (Part 2 of
2)
DP15: Coding the Web Self Service COM API
(Part 1 of 2)
DP16: Coding the Web Self Service COM API
(Part 2 of 2)
DP17: Using the .NET API (Part 1 of 2)
DP18: Using the .NET API (Part 2 of 2)
Download