cub-e.net

just coding...

Veri Turleri Hakkinda

Microsoft Dynamics CRM 2011 ve Microsoft Dynamics CRM Online'da programlama modeli .NET'in temel türlerini kullanacak şekilde değiştirildi.

Bu tabloda beni en çok şaşırtan ise Customer, Lookup, Owner nesnelerinin artık EntityReference türünden sadece bir değer almaları. CRM'i yeni öğrenler için işler gerçekten kolaylaştırılmış. Artık kod yazarken CRM ile başlayan nesnelerimiz yok.

Aşağıdaki tablo bize Microsoft Dynamics CRM 4.0 ile CRM 2011, 2013 ve 2015 arasındaki tür dönüşümünü göstermektedir.

Özellik Adı

Microsoft Dynamics CRM 2011, 2013, 2015 Türü

Microsoft Dynamics CRM 4.0 Türü

AttributeTypeCode.Boolean

bool ya da System.Boolean

CrmBoolean

AttributeType.CalendarRules

EntityCollection

DynamicEntity[] or calendarrule[]

AttributeType.Customer

EntityReference

Customer

AttributeType.DateTime

System.DateTime

CrmDateTime

AttributeType.Decimal

decimal ya da System.Decimal

CrmDecimal

AttributeType.Double

double ya da System.Double

CrmFloat

AttributeType.Integer

int ya da System.Integer

CrmNumber

AttributeType.Internal

System.Object

Kayıtlarda Kullanılmaz

Kayıtlarda Kullanılmaz.

AttributeType.Lookup

EntityReference

Lookup

AttributeType.Memo

string ya da System.String

System.String

AttributeType.Money

Money

CrmMoney

AttributeType.Owner

EntityReference

Owner

AttributeType.PartyList

EntityCollection or ActivityParty[]

activityparty[] or DynamicEntity []

AttributeType.Picklist

OptionSetValue

Picklist

AttributeType.PrimaryKey

System.Guid

Key

AttributeType.String

System.String

System.String

AttributeType.State

OptionSetValue yada oluşturulan enumeration kullanılmalı

EntityNameStateInfo

AttributeType.Status

OptionSetValue ya da int

Status

AttributeType.Uniqueidentifier

System.Guid

UniqueIdentifier

AttributeType.Virtual

System.Object

Kayıtlarda Kullanılmaz

Kayıtlarda Kullanılmaz

Eski Tür

Yeni Tür

CrmAttributeType Class (MetadataService)

Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode

Moniker Class (CrmService)

Microsoft.Xrm.Sdk.EntityReference

SecurityPrincipal Class (CrmService)

Microsoft.Xrm.Sdk.EntityReference

 

OptionSetValue

OptionSetValue’a değer atamak için ilk önce OptionSetValue türünden bir nesne oluşturmanız gerekmektedir.  Burada dikkat çekmek istediğim konu ise eğer state alanı ile çalışacaksanız (yani firma için aktif/pasif, teklif için açık/kazanıldı/kaybedildi gibi) early-bound sınıflarda bunlar için mutlaka bir enumaration oluşturulmakta. Ama late bound sınıflarda bu durumu programcı yönetmektedir.

Örnek olarak adres üzerindeki bir optionset alana değer atama aşağıdaki şekilde olmaktadır;

OptionSetValue osv = new OptionSetValue(1);

contact.Attributes["address1_freighttermscode"] = osv;

 

EntityReference

CRM sisteminde iki entity’yi birbirine bağlamak için lookup nesnesini kullanmak zorundayız. Lookup’lar üzerinde programatik işlem yapabilmek için EntityReference nesnesini kullanmaktayız. Bu nesneye Lookup alana reference vermek istemiz nesnenin türü ve Id’sini vermemiz gerekmektedir.

Aşağıdaki örnekte parentAccountId atama yapılacak nesnenin guid cinsinden Id’si olmalı;

EntityReference parentaccountid = new EntityReference("account", parentAccountId));

accountEntity.Attributes["parentaccountid"] = parentaccountid;

ioService.Update(accountEntity);

Null Değer Atama

CRM 4.0’dan farklı olarak .Net Type türleri kullanıldığı için null değer atama işlemi artık sadece alana değer vermekten ibaret oldu. İşte birkaç örnek;

EntityAdi.IndustryCode = null;

EntityAdi.AccountId = Guid.Empty;

EntityAdi.AccountNumber ="";

EntityAdi.Address1_Country = String.Empty;

Dynamics CRM 2015 Plug-in Mimarisi

Sizlere bugun Dynamics CRM icerisindeki Plug-in Mimarisinden soz etmek isitiyorum. Plug-in’ler IPlugin arayuzunden turetilmis kod parcaciklaridir ve CRM’in icerisinde belli bir sira icerisinde calisirlar. Kabaca tariff edersek bunlar birer .dll dosyalaridir ve CRM’e bu dosyaya ne zaman bakmasi gerektigini biz soyleriz. Plug-in’ler olay bazli olarak calisirlar. Yani herhangi bir kayit olusturuldugunda, guncellendiginde, silindiginde vb.. olaylar oldukca biz ilgili ayari yapmissak calisirlar.

Plug-in’lerin en güzel yani pre ve post olarak calismaya ayarlanabilmeleridir. Plug-in’ler olay bazli calisirlar demiştim iste bu olay olmadan önceki kaydin son hali üzerinden ve olay olduktan sonraki hali üzerinden işlem yapmaniza olanak sağlarlar.

Plug-in’lerin calismasi icin Microsoft.Xrm.Sdk.dll ve Microsoft.Crm.Sdk.Proxy.dll dosyalarinin referanslara eklenmis olmasi gerekmektedir. Tam yeri gelmisken bahsedeyim eger siz ucuncu parti bir .dll kullaniyorsaniz (yani kendi yazdiginiz siniflarin oldugu ya da diger uygulamalardan aldiginiz) bu .dll’lerin ilgili serverin assembly klasöründe olduğundan emin olun yoksa plug-in calismayabilir.

public class MyPlugin: IPlugin

{

    public void Execute(IServiceProvider serviceProvider)

    {}

}

 

Detaya inecek olursak IPlugin arayuzunden turerilmis bir sinif içerisinde Execute metodu yer almalidir. Bu metod parametre olarak IServiceProvider arayuzunden türetilmiş bir bilgi yiginini içerir. Yani CRM kod içerisinde yapacagimiz işlemlerde bize CRM içerisinde olan olaylardan bize bilgi tasir ki biz de bu bilgileri kodun içerisinde kullanalim. Ne gibi veriler içinde tasimakta derseniz cok fazla detay verebilirim mesela su anda hangi kullanicinin işlem yaptigi, tasidigi nesnenin turunu, eger pre-plugin ise değerlerin değişmeden önceki halini vb… bir cok veri içermekte.

Simdi sirasiyla gelen veri yiginlarini inceleyelim.

Plug-in Execution Context

Calisma zamaninda oluşan veriler bu yapi içerisinde yer almaktadır. Bunlara kodun calisma hiyerarşisi ve entity bilgileri de dahildir.

IPluginExecutionContext context = (IPluginExecutionContext)

    serviceProvider.GetService(typeof(IPluginExecutionContext));

Bir olay olduğunda kayit edilmiş bir plug-in’e bu veriler aktarılır aslinda o anda calisan butun plug-in’lere bu veriler aktarılır ama execution pipeline denen sralamaya uyarak aktarılır once pre sonra post pluginlere veri aktarılır. Hatta siz pre-plugin ile bir veriyi değiştirirseniz post-plugin’e o veri aktarılır.

Tabii burada yeri gelmişken bahsedeyim burada sozu edilen kodlarin sonsuz döngüye girmemeleri için sistem içerisinde Depth denen bir anahtar yer almaktadır. Varsayilanda bu bir plugin’i arda arda 8 kere calistirir ve durdurur. Boylece sistemin bir kod yanlisigi ile çökmesi engellenmiştir. Bu değer değiştirebilir bir değerdir.

Sistemin calismasi da aslinda su mantiga dayanmakta;



Yani Event Execution Pipeline’a bir mesaj girdiginde Pre-Event -> Platform Islemleri (Yani CRM’in kendi ic isleyisi) -> Post-Event seklinde islenmekte. Bu dongu senkron ve asenkron yapilar için böyle ilerlemekte.

Organization Servise Erisme

CRM içerisinde işlem yapabilmek her zaman bir servis nesnesine ihtiyaç duymaktayız. Iste kullanicinin hareketi neticesinde acilmiş bu servisi bize kullanmamiz için geçirmekte serviceProvider nesnesi.

IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

Notification Servise Erisme

Senkron olarak işaretlenmiş plug-in’ler Microsoft Azure Service Bus’a veri mesaj gondebilirler. IServiceEndpointNotificationService turunden olan bu bilgi Azure Service Bus’a gönderilir. Bu sayede Azure Service Bus içerisinde endpoint oluşturulmuş ve endpoint’i dinleyen 3. Parti bir servis ile iletişime geçebilmektedir.

Input ve Output Parametreleri

InputParameters nesnesi su anda yapilan hareketin yani su anda tetiklenmiş olayin bilgisini ve su anda üzerinde işlem görülen entity’nin bilgisini içerir. Bu bilgiye erişmek için “Target” nesnesine bakmamiz gerekmektedir ve bu nesneyi alip Entity class’ina çevirebiliriz. Input nesneleri Request message yapisindadir.

if (context.InputParameters.Contains("Target") &&

    context.InputParameters["Target"] is Entity)

{

    // Obtain the target entity from the input parameters.

    Entity entity = (Entity)context.InputParameters["Target"];

}

Fakat unutmaniz gereken bir nokta var her mesaj Entity nesnesini içermeyebilir. Ornegin DeleteRequest; Entity değil EntityReference dondurur.

if (context.InputParameters.Contains("Target") &&    context.InputParameters["Target"] is EntityReference)

{

    // Obtain the target entity from the input parameters.

    EntityReference entity = (EntityReference)context.InputParameters["Target"];

}

Benzer sekilde OutputParameters da Response message içerir. Ama sunu unutmayin ki senkron post-event ve asenkron plug-in’ler OutputParameters turunden nesneler içerirler.

Pre ve Post Entity Imajlari

Bu konuyu okurken sakin Ingilizce Images kelimesinin resim anlamiyla karistirmayin buradaki anlami verinin  o anki goruntusu seklinde ifade etmek daha doğru olur. Aslinda tam Ingilizce tabiriyle snapshot. PreEntityImages ve PostEntityImages verileri sistem tarafından size gönderilir ama siz ozellikle beklediğiniz alanlari plug-in’in kayit işlemi sirasinda sisteme soyleyebilrsiniz.

Burada tabii ki bir mantik çerçevesi olduğunu da unutmayin Create aninda bir nesnenin preImage’i olamayacagi gibi Delete işleminden sonra da bir postImage beklemeyin.

Simdi bu bilgileri verdikten sonra butun bunlari birleştirerek bir plug-in temel goruntusune bakalim.

Aciklamalar kodun içinde.

using System;

using System.ServiceModel;

using Microsoft.Xrm.Sdk;

 

public class MyPlugin: IPlugin

{

    public void Execute(IServiceProvider serviceProvider)

    {

        // Sandbox içerisinde calisan plug-in’ler TracingService’den yararlanabilirler.

        ITracingService tracingService =

            (ITracingService)serviceProvider.GetService(typeof(ITracingService));

 

        //Context’i elde ediyoruz.

        IPluginExecutionContext context = (IPluginExecutionContext)

            serviceProvider.GetService(typeof(IPluginExecutionContext));

 

        // InputParameters’dan gelen verileri aliyoruz

        if (context.InputParameters.Contains("Target") &&

            context.InputParameters["Target"] is Entity)

        {

            // Target ile entity’e erisiyoruz.

            Entity entity = (Entity)context.InputParameters["Target"];

 

            // Beklediginiz entity geldi mi diye kontrol ediyoruz.

            if (entity.LogicalName != "account")

                return;

 

            // CRM Servisi elde ediyoruz

            IOrganizationServiceFactory serviceFactory =

                (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

 

            try

            {

                / Iste buradan sonrasi size kalmis istediğiniz kodu yazabilirsiniz.

            }

 

            catch (FaultException<OrganizationServiceFault> ex)

            {

                throw new InvalidPluginExecutionException("An error occurred in MyPlug-in.", ex);

            }

 

            catch (Exception ex)

            {

                tracingService.Trace("MyPlugin: {0}", ex.ToString());

                throw;

            }

        }

    }

}

Execute Metodu

Dynamics CRM plug-in mimarisi isimli makalede InputParameters ve Output Parameters konularindan bahsederken Request ve Response konularina değinmeden geçtim konu havada kalmasin diye ayri bir baslik altinda incelemenin daha doğru olacagini duşundum. (Ilgili makaleye buradan ulasabilirsiniz)

CRM Servisi içerisindeki Execute Metodu Request ve Response yani Talep ve Yanit seklinde calismaktadir. Sistem üzerinde yapacaginiz butun hareketleri bu sekilde yaopabilirsiniz ki buna ayri metodlari bulunan Create, Update, Delete bile dahildir.

Konu aslinda basit olduğundan çokça açıklanacak bir tarafi da yok gibi ama calisma yapisina bakacak olursak siz bir talepte bulunursunuz o da size yanit verir diye kisaca özetleyebiliriz.



Execute metodunun alabileceği Request’lere makalenin sonunda yer vereceğim. Ama oncelikle sunu da belirteyim ki Request nesnesi opsiyonel parametrelere de sahiptir. Bu parametreler sunlardir.


Parameter

Description

Messages

SolutionUniqueName

Islemin yapilacagi Cozumun Adi

AddPrivilegesRoleRequest

CreateRequest

DeleteRequest

MakeAvailableToOrganizationTemplateRequest

UpdateRequest

SuppressDuplicateDetection

Eslenen kayitlar bulunsun mu bulunmasin mi

CreateRequest

UpdateRequest

 

Kullanima dair ornek kod ise su sekilde;

Account target = new Account();

target.Name = "Fabrikam";

CreateRequest req = new CreateRequest();

req.Target = target;

req["SuppressDuplicateDetection"] = true;

req["SolutionUniqueName"] = "MySolutionName";

CreateResponse response = (CreateResponse)_service.Execute(req);


Eger isterseniz Execute Metodunu asenkron olarka da calistirabilirsiniz. Bu ekranda donmalari ve kullanicilarin ekrandan işlem bitmeden cikmalarini önleyecektir. Bunun için yapmaniz gereken ExecuteAsyncRequest mesajini geçmek olacaktır.

ExecuteMultipleRequest ile de toplu daha aktarimlarinda kullanabileceğiniz mesajdir.

xRM Mesajlarinin tamamina bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/en-us/library/gg334698.aspx

CRM mesajlarina da bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/en-us/library/gg309482.aspx

Dynamics CRM'e saatinizden erisin :)

Akilli saatler de akilli telefonlar kadar hizli bir sekilde hayatimiza girdi. Su anda sektördeki buyuk firmalar ve hatta kickstarter grisimleri bile akilli saat projeleriyle karsimiza cikmakta. Ben de bir Pebble kullanicisiyim ve ozellikle 1 hafta dayanan pili ile diğer akilli saatlerden ayrilmakta.

Bugun Dynamics CRM ile akilli saatlerin nasil birleştirileceğini inceleyeceğiz. Akilli saatler genel itibariyle telefon üzerindeki notificationlari gösterebilme yeteneğine sahipler. Telefon üzerinde notification oluşturmak için ben bugun Pushover sistemini kullanacagim.

Soyle bir senaryomuz olsun; sistem üzerinde oluşturduğumuz bir is akisi ile CRM uzerinde servis talepleri oluştuğu anda push notification isimli bir entity üzerinde bir kayit oluşturuyoruz. Bu kayit varsayilanda gönderilmeyi bekliyor durumunda olacak. Uygulamamiz bu kayitlari okuyacak ve Push Notification’in servisine gönderecek. Boylece notification telefonumuza orada da saatimize gelecek.

Sizin oluşturduğunuz sistemlerde farkli is akislari ile yine push notification isimli entity’e kayit oluşturabilir böylece degisik senaryolar içinde bu sistemi kullanabilirsiniz. Ornegin sistem üzerinde değeri 50.000 tl üzerinde bir firsat oluşturulduğunda bu sistemi tetikleyebilirsiniz.

Sisteme uye olmak için telefonunuza Pushover uygulamasini indirmeniz gerekmekte. Daha sonrasinda ise https://pushover.net sitesinden kendinize bir hesap oluşturuyorsunuz.

Bu sistem üzerinden notification gönderebilmek için 2 bilgiye ihtiyamiz bulunmakta

1.       Sizin User Key’iniz. Her bir kullanicinin bir key’i var sistemde.



2.       Create New Application linkine tıklayarak olusturacaginiz Application Key’i.



Bu sekilde elde ettiğimiz key’ler ile sistem üzerinden notification gönderebilir hale geldik. Bu bilgilerin CRM’de saklanacagi en uygun yer bence Kullanici Kartiydi ve bende bu nedenle kullanici nesnesi üzerine PushOverToken ve PushOverUserKey isimli iki alan actim ve bilgileri buraya girdim.



Daha sonra Push Notification isimli aktivite turunde bir entity oluşturdum ve asagidaki alanlari actim. Buradaki önemli nokta böyle bir entity’i nasil kullanacagimiz. Yazinin basinda da belirttiğim gibi cok farkli senaryolar için bu nesne üzerinde kayitlar oluşturabilirsiniz.


Olusturdugum alanlari incelersek;

Subject : Ekran görüntülenecek baslik bilgisi

To : Mesajlarin kimlere gönderileceği bilgisi

Regarding : Kaydin bagli bulunduğu ana entity. Burada bu bilgiyi telefon üzerinden kullanici bu kayda ulaşmak isterse tek tiklama ile bu bilgiye ulassin diye tutuyorum.

Status Reason : Varsayilanda “Waiting To Send” yani bekliyor konumunda oluşturuluyor kayitlar. Gondeme işlemi tamamlandiktan sonra bu alani “Sent” yani gönderildi olarak değiştiriyorum.

Description : Detayli bilgi vermek için bu alani kullaniyorum.

 

static string URL = "https://xxx.contoso.com";

        private static IOrganizationService ServiseBaglan()

        {

            try

            {

                Uri organizationUri = new Uri(String.Format("{0}/XRMServices/2011/Organization.svc", URL));

                ClientCredentials clientcred = new ClientCredentials();

                clientcred.UserName.UserName = "kullanici adi";

                clientcred.UserName.Password = "sifre";

                OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(organizationUri, null, clientcred, null);

                IOrganizationService _service = (IOrganizationService)_serviceproxy;

                return _service; ;

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                return null;

            }

        }

 

        private static void changeStatus(IOrganizationService service, Guid Id)

        {

            // Create the Request Object

            SetStateRequest state = new SetStateRequest();

 

            // Set the Request Object's Properties

            state.State = new OptionSetValue(0);

            state.Status = new OptionSetValue(100000000);

 

            // Point the Request to the case whose state is being changed

            state.EntityMoniker = new EntityReference("new_pushnotification", Id);

 

            // Execute the Request

            SetStateResponse stateSet = (SetStateResponse)service.Execute(state);

        }

        static void Main(string[] args)

        {

            try

            {

                string fetchXml = @"<fetch mapping='logical'>

                                 <entity name='new_pushnotification'>

                                    <attribute name='activityid' />

                                    <attribute name='subject' />

                                    <attribute name='description' /> 

                                     <filter type='and'>

                                        <condition attribute='statuscode' operator='eq' value='1'/>

                                     </filter>

                                    <link-entity name='activityparty' from='activityid' to='activityid' alias='ac'>

                                        <link-entity name='systemuser' from='systemuserid' to='partyid' alias='ad'>

                                            <attribute name='new_pushovertoken' />

                                            <attribute name='new_pushoveruserkey' />

                                        </link-entity>

                                    </link-entity>

                                 </entity>

                             </fetch>";

 

                IOrganizationService service = ServiseBaglan();

               

                if (service != null)

                {

                    Console.WriteLine("Servise Baglanildi");

 

                    EntityCollection entCollection = (EntityCollection)service.RetrieveMultiple(new FetchExpression(fetchXml));

                    foreach (var item in entCollection.Entities)

                    {

                        Console.WriteLine("Aktarilacak kayitlar bulundu");

 

                        if (item.Contains("ad.new_pushovertoken") && item.Contains("ad.new_pushoveruserkey") && item.Contains("subject") && item.Contains("description"))

                        {

                            string PushOverToken = ((AliasedValue)item["ad.new_pushovertoken"]).Value.ToString();

                            string PushOverUserKey = ((AliasedValue)item["ad.new_pushoveruserkey"]).Value.ToString();

                            string subject = item["subject"].ToString();

                            string description = item["description"].ToString();

                            Guid activityid = new Guid(item["activityid"].ToString());

                            string url = String.Format("{0}/main.aspx?etn=new_pushnotification&pagetype=entityrecord&id=%7B{1}%7D", URL, activityid.ToString());

 

                            dynamic Parameters = new NameValueCollection() {

                        {

                            "token",

                            PushOverToken

                        },

                        {

                            "user",

                            PushOverUserKey

                        },

                        {

                            "message",

                            description

                        },

                        {

                            "url",

                            url

                        },

                        {

                            "url_title",

                            "Open Record"

                        },

                        {

                            "title",

                            subject

                        }

                        };

 

                            WebClient client = new WebClient();

                            Console.WriteLine("Kayitlar gonderiliyor");

                            client.UploadValues("https://api.pushover.net/1/messages.json", Parameters);

                            Console.WriteLine("CRM guncelleniyor");

                            changeStatus(service, activityid);

                        }

                    }

                }

 

                Console.WriteLine("Tamamlandi");

                Console.ReadLine();

}

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                return null;

            }

        }

 

Kodda da görebileceğiniz uzere Ilk olarak CRM Servisine baglaniyoruz. Ben IFD enabled bir organizasyon kullandigim için benim servis baglantim bu sekilde Office 365 ya da on premise sistemlerde farkli sekillerde CRM servise bağlanma yöntemleri mevcut. Bu konuda sorun yasarsaniz benimle iletişime geçebilirsiniz.

 

Daha sonra ise fetchXML sorgumuz ile sistem üzerinde bekleyen notification’larin bilgisini aliyoruz ama bu işlemi yaparken ayni zamanda systemuser nesnesine de dallanarak ihtiyacim olan pushovertoken ve pushoveruserkey bilgilerini de aliyorum. Boylece her bir kullanici için teker teker veri sorgulayarak servisi meşgul etmiyorum.

 

@"<fetch mapping='logical'>

                                 <entity name='new_pushnotification'>

                                    <attribute name='activityid' />

                                    <attribute name='subject' />

                                    <attribute name='description' /> 

                                     <filter type='and'>

                                        <condition attribute='statuscode' operator='eq' value='1'/>

                                     </filter>

                                    <link-entity name='activityparty' from='activityid' to='activityid' alias='ac'>

                                        <link-entity name='systemuser' from='systemuserid' to='partyid' alias='ad'>

                                            <attribute name='new_pushovertoken' />

                                            <attribute name='new_pushoveruserkey' />

                                        </link-entity>

                                    </link-entity>

                                 </entity>

                             </fetch>";

 

 

Verileri aldiktan sonra ise bunlari istediğim gibi düzenleyip string veriler haline getiriyorum. Sonunda ise asagidaki bolum ile servise Push Notification servisine gönderiyorum;

 

                            dynamic Parameters = new NameValueCollection() {

                        {

                            "token",

                            PushOverToken

                        },

                        {

                            "user",

                            PushOverUserKey

                        },

                        {

                            "message",

                            description

                        },

                        {

                            "url",

                            url

                        },

                        {

                            "url_title",

                            "Open Record"

                        },

                        {

                            "title",

                            subject

                        }

                        };

 

                            WebClient client = new WebClient();

                            Console.WriteLine("Kayitlar gonderiliyor");

                            client.UploadValues("https://api.pushover.net/1/messages.json", Parameters);

                            Console.WriteLine("CRM guncelleniyor");

                            changeStatus(service, activityid);

 

 

Burada dikkat edilmesi gerekn nokta URL kisminda gerçekten sistemdeki nesnenin acilacagi URL’i buraya getiriyorum ki telefondan kullanici direkt kayda ulassin.

Gonderme işleminden sonra ise kayitlari gönderildi diye güncelliyorum ki tekrar tekrar ayni kayitlari göndermeyelim.

 

Sonuc olarak once telefonda sonra ise saatimde gelen verilere ulaşıyorum :)

PushOver'in ana ekraninda gelen notification.




Notification'i acinca detaylara ulasiyorum. 



Open Record isimli linke tiklayinca da direkt ilgili kayda ulasiyorum.



Ve sonuc Pebble ekraninda CRM'den gelen Notification'i goruyorum :) iste mutlu son...

Microsoft Dynamics CRM 2015 Coklu Arama (Multi-Entity Quick Find)

Bugune kadar CRM ile ilgilenen pek cok kisi ve firma projeler icerisinde (ki bunlara bende dahilim ozellikle cagri merkezi projelerinde) CRM icin coklu arama ozelligi uygulamasi yazdi. Yani bir tane kutuya CRM icerisinde aramak istediginiz kisi, kurum ya da herhangi birsey yaziyorsunuz ve bu otomatik olarak daha onceden belirlediginiz nesneler icerisinde araniyor ve ekrana getiriliyor. Bugun Dynamics CRM 2015 ile birlikte bu ozellik CRM icersinde gomulu olarak geliyor ve sistem menusunde her zaman ulasilabilir bir yerde duruyor.


Bu ekrani acabilmek icin yukaridaki resimde de godugunuz uzere buyutec simgesine tiklamak gerekiyor. Cikan ekrandaki metin kutusuna birseyler yazip enter'a bastiginizda ise yazdiginiz kelime daha onceden ayarlarlar bolumunde ayarlanan butun nesneler icerisinde aranmakta ve sonuclar karsiniza gelmekte. 

Dilerseniz bu ekrana gecmeden once ayarlar bolumunde nasil ayarlar yapabilecegimize bakalim. Bu ekran ile ilgili ayarlari acmak icin Ayarlar>Yonetim bolumu altinda bulunan Sistem Ayarlari dugmesine tiklamamiz gerekmekte. Acilan ayarlar ekraninda asagiya dogru ilerledigimizde "Hizli Bulmayi Ayarla" yazisi altindaki 2 secenek iste aradigimiz ayarlar.


Hizli bul kayit sinirlarini etkinlestir: Bu ayar arama performansini arttirmak için arama kayitlarini 10.000 kayitla sinirli tutan ozellik.
Arama icin varliklari secin: Bu dugmeye tikladiginizda ise bir pencere acilacak ve bu arama ozelliginin hangi varliklar uzerinde etkili olacagini ayarlamanizi saglayacaktir.


Burada sitediginiz nesneleri ekleyebilir ve cikartabilirsiniz ama unutmamamiz gereken bir sinirimiz mevcut o da max. 10 tane nesne secebiliyoruz. 10'dan fazla nesne sectiginizde su sekilde bir uyari karsiniza cikacaktir.


Iste bu ekran vasitasiyla ayarlamalarimizi yaptiktan sonra gecek arama ekranimiza donebiliriz. Bu ekran uzerinde istedigimizi yazip entrer'a bastigimizda sonuclar ekrana gelecektir.


Bu ekranda bazi onemli ozellikler bulunmakta;
  • her bir nesne uzerindeki + dugmesine tiklayarak yeni kayit ekleyebilirsiniz.
  • herhangi bir kayda tikladiginizda o kayit acilir.
  • arama yapilacak alanlari arama hizli gorunumu uzerinden ayarlayabilirsiniz.
  • O nesne icin arama hizli gorunumunde ayarlanmis ilk uc alan goruntulenir.
  • ekranin sag tarafinda bulunan filtre olcutunu kullanarak kayitlari filtreleyebilirsiniz.
Bu sayede CRM 2015'de yillardir soze edilen ve beklenen bir ozelligi de Dynamics CRM platformu icerisinde gormus olduk.

Satışlarınız Düşmesin Yükselsin

Acikakademi.com sponsorlugunda gerceklestirecegimiz bu online etkinlikte Dynamics CRM 2013 SP1 uzerinde bir satis senaryosunun nasil gerceklestirilecegi anlatilacaktir. 09.10.2014 tarihinde Turkiye saatiyle 20:00 da etkinlik baslayacaktir. Internet uzerinden online olarak izleyebileceginiz etkinlik hakkinda daha fazla bilgiye buraya tiklayarak ulasabilirsiniz.

Sektörünün önde gelen firmalarından Contoso, satışlarını artıramamaktan yakınıyor, firma sahibi Ahmet Bey ise sorunun nereden kaynaklandığını tam teşhis edemiyordu. Satış ekipleri mi mevcut potansiyelin farkında olamıyor, yoksa potansiyellerini bildikleri halde işleri rakiplerine mi kaptırıyorlardı? Ahmet Bey’in bu sorunu çözebilmesi için daha ayrıntılı bilgiye sahip olması gerekiyor, ancak satış ekiplerinden daha fazla bilgi toplamak için girişimde bulunduğunda, tepki alıyordu. Dışa dönük ve satışa odaklanmış bu ekipler, sistemle uğraşmaya ve bilgi girişi yapmaya direniyorlar; tüm zamanlarını satışa ayırmak istiyorlardı. Satış ekiplerinden her görüşmelerini, her teklifi, teklif sonrası her takip adımını sisteme girmelerini istemek hiç de kolay değildi.

Pazarlama bölümü ise ürünlerini etkin tanıtamıyordu. Doğru müşteriye doğru iletişim kanalı ile ulaşmak ve bunu yaparken ekibinin aktivitelerini de denetleyerek, kampanyanın maliyetini düşürmek istiyorlardı. Tabii ki ürün ve pazarlama performanslarının etkin bir şekilde raporlanması da gerekiyordu.

İşte firmasındaki bütün bu sorunlara cevap arayan Ahmet Bey, araştırmasında Microsoft’un Dynamics CRM İş Platformunun, esnek ve genişleyebilir yapısı ile çok iyi bir ürün olduğunu gördü. Satış ve pazarlama bölümündeki sorunları giderebildiği gibi, servis talepleri ile çağrıları yönetebileceğini, servis aktiviteleri ile sahadaki personelin iş yükünü yönetebileceğini öğrendi. Üstelik iş akışları ile sistem otomatik olarak kendisine verilmiş işleri yapmaktaydı. İş akışları sayesinde bir olay ya da değişiklik olduğunda sistem bunu algılayıp gerekli işlemleri yerine getirebilmekteydi. Ayrıca bu sisteme tümleşik olan Web Portalı sayesinde kullanıcılar kendi servis taleplerini açmanın yanında, kendisiyle ilgilenen teknik servis personelinin boş olduğu saatlere randevu da alabiliyordu. Sahadaki teknik servis personeli sistemle entegre tablet ve akıllı cep telefonu arabirimi sayesinde şirket sistemine bağlanabilmekte, böylece müşteri bilgilerine ulaşabilmekte ve üzerlerindeki işleri, servis taleplerini çevrimiçi olarak görebilmekteydiler. Üstelik esnek raporlama arayüzü ile herkes kendi anlayabildiği şekilde raporlar alarak sistemi en yüksek verimle kullanabilmekteydi. 

Dynamics CRM 2013 and Microsoft Dynamics CRM Online nedir?

Microsoft Dynamics CRM 2013 ve Microsoft Dynamics CRM Online güçlü ve gelişmiş bir iş platformu deneyimini kullanıcılarına sunmaktadır. Dynamics CRM içerisine entegre edilmiş gelişmiş uygulama altyapısı sayesinde sadece uygulamayı kullanmakla kalmıyor aynı zamanda onun istediğimiz yerlerini işimize uygun halde yeniden programlayarak hem kendi içinde hem de dış uygulamalarla konuşabilen kendi iş çözümümüzü oluşturabiliyoruz. Bu şekilde genişlemeye biz xRM adını vermekteyiz. Yani herşeyin yönetilebildiği bir platform.


Dynamics CRM Microsoft tarafından çeşitli zamanlarda çıkartılan çeşitli service pack ve rollup paketleriyle genişlemekte ve yeni özellikler kazandırılmaktadır.

Dynamics CRM mevcutta üç farklı çeşitte paketlenmektedir;

  • Microsoft Dynamics CRM 2013 on-premises deployment
  • Microsoft Dynamics CRM 2013 Internet-facing deployment (IFD)
  • Microsoft Dynamics CRM Online

Bu üç çeşitte de aşağıdaki şekillerde son kullanıcı arabirimi sunmaktadır;

  • Microsoft Dynamics CRM Web client
  • Microsoft Dynamics CRM for Microsoft Office Outlook
  • Microsoft Dynamics CRM for Microsoft Office Outlook with Offline Access
  • Microsoft Dynamics CRM for phones and tablets

Yani siz Dynamics CRM’i şirketiniz için kullanmaya başladıktan sonra isterseniz Internet Explorer, Chrome, Safari gibi browserlardan sisteme girebilir isterseniz de cep telefonu ya da Outlook üzerinden de kullanabilirsiniz.

Oluşturduğunuz uygulamaları Dynamics CRM ile Web Servisleri aracılığıyla entegre edebilirsiniz. Bu web servisleri arka tarafta Dynamics CRM’in kalbi olan xRM platform katmanı ile iletişim halinde bulunmaktadır.

Bu katmanda güvenlik gibi genel kısımları içermektedir ve bir uygulamanın çalışması için gerekli olan yapı taşlarını içerir fakat kendisi ilgili nesnelerinin bir koleksiyonun başka bir şey değildir. Nesneler arasındaki iletişimi sağlar ve tekliften siparişe, siparişten faturaya gibi fiyatlandırma mantığı gibi daha genişletilebilir bir mantığı uygulamak için kullanılır.

XRM platformu ayrıca, güvenlik yoluyla verilere ve veritabanına erişimi denetler. Ayrıca iş akışı ve özel iş mantığı uygulamaları (plugins) için süreçlerinin tetiklenmesini denetler. Bu katman Microsoft Exchange Server üzerinden gelen ve giden e-posta işleme alır.

Microsoft Dynamics CRM 2013 'de FetchXML ile Aggregate(Toplama Sorguları) İşlemleri

Bir önceki makalede FetchXML ile neler yapabileceğimize değindik. (Buraya tıklayarak o makaleye ulaşabilirsiniz) Eğer istersek FetchXML ile grouping ve aggregate işlemlerini de yapabilmekteyiz. FetchXML ile aşağıdaki işlemler desteklenmektedir;

·         sum

·         avg

·         min

·         max

·         count(*)

·         count(attribute name)

 

Aggregate yapabileceğimiz sorgu örneği standart olarak bildiğimiz fetchXML kurgusundan farklı değil burada sadece attribute kısmında aggregate ile ilgili keyword yer almakta. Aggregate=true diyerek bu şekilde bir sorgulama yapacağımızı sisteme söylüyoruz. Sonrasında ise sırasıyla hangi alan üzerinden işlem yapılacağını, alias yani etiketin ne olacağını ve aggregate türümüzün ne olacağını sisteme söylüyoruz.

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_count' aggregate='count'/>

  </entity>

</fetch>

Bu şekilde oluşturduğumuz bir sorgu ile aslında şunu demek istiyoruz : “select count('name') as opportunity_count from opportunity” yani standart bir count işleminden fazlası değil yaptığımız.

 

Bu fetchXML’i çalıştırmak için ise aşağıdaki gibi bir kod hazırladım;

 

string opportunity_count = @" <fetch distinct='false' mapping='logical' aggregate='true'>

                                                <entity name='opportunity'>

                                                    <attribute name='name' alias='opportunity_count' aggregate='count'/>

                                                </entity>

                                              </fetch>";

 

                EntityCollection opportunity_count_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_count));

 

                foreach (var c in opportunity_count_result.Entities)

                {

                    Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate2);

                }

 

Burada dikkat edilmesi gereken nokta geriye EntityCollection döndürmesi. Bir önceki makalemden hatırlarsanız normalde bu tarz sorgulama ile biz geriye account, contact gibi sitem içerisindeki nesnelerden oluşmuş bir dizi alıyoruz. FetchXML yapısı gereği tek bir metod kullanıyoruz ve bu metod sonucunda yukarıdaki örnekte olduğu gibi tek bir satır dahi dönecek olsa bile yine sonucu bir collection içerisinde almaktayız. Bu nedenle mecbur foreach içerisinde dönüyoruz ya da collection’un 0. üyesini alıyoruz. Ben örneğimde foreach ile collection içinde döndüm ve gördüğünüz gibi değeri aldım.

 

Yine bir önceki makalemde ileri düzey sorgular bölümünde anlattığım gibi eğer nesne üzerinden direkt bir değer döndürmeyeceksek sonuç her zaman AliasedValue olarak gelmekte. işte bu nedenden dolayı şu şekilde bir convert işlemi yapıyoruz;

 

Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value

 

Bu işlem sonucunda bu koddan aşağıdaki gibi çıktımızı alabiliyoruz.

 

 

Eğer distinct yapmak istersek de fetchXML içerisine yerleştireceğimiz bir distinct=”true” ifadesiyle ile sisteme bunu beliretebiliriz.

Aşağıda ise distinct yapılmış bir kod örneği bulunmakta;

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

  </entity>

</fetch>

 

Bu FetxhXML’i de aşağıdaki kodla çalıştırabiliriz.

 

                string opportunity_distcount = @"

                                           <fetch distinct='false' mapping='logical' aggregate='true'>

                                               <entity name='opportunity'>

                                                  <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

                                               </entity>

                                           </fetch>";

 

                EntityCollection opportunity_distcount_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_distcount));

 

                foreach (var c in opportunity_distcount_result.Entities)

                {

                    Int32 aggregate4 = (Int32)((AliasedValue)c["opportunity_distcount"]).Value;

                    System.Console.WriteLine("Distinct name count of all opportunities: " + aggregate4);

                }

 

                Console.ReadLine();

 

Bu kod sonucunda da aşağıdaki gibi bir cevap alabiliriz sistemden;


 

Gelin olayı biraz renklendirelim. Bu sefer de bir sorgu içerisinde birden fazla aggregate seçeneğinin cevabını isteyelim. Böyle bir işlem için standart fetchXML’in içerisinde sırasıyla hangi özelliklerin bize geri döneceğini belirtiyoruz tabii unutulmaması gereken nokta ise hepsi için ayrı birer alias olması gerektiğidir.

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

  </entity>

</fetch>

 

Bu sorgu için ise aşağıdaki kodu hazırladım;

 

                string estimatedvalue_avg2 = @"

                                    <fetch distinct='false' mapping='logical' aggregate='true'>

                                        <entity name='opportunity'>

                                           <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

                                        </entity>

                                    </fetch>";

 

                EntityCollection estimatedvalue_avg2_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(estimatedvalue_avg2));

 

                foreach (var c in estimatedvalue_avg2_result.Entities)

                {

                    Int32 aggregate8a = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate8a);

                    decimal aggregate8b = ((Money)((AliasedValue)c["estimatedvalue_sum"]).Value).Value;

                    System.Console.WriteLine("Sum of estimated value of all opportunities: " + aggregate8b);

                    decimal aggregate8c = ((Money)((AliasedValue)c["estimatedvalue_avg"]).Value).Value;

                    System.Console.WriteLine("Average of estimated value of all opportunities: " + aggregate8c);

 

                }

 

                Console.ReadLine();

 

İşte bu şekilde aynı anda count, sum ve avg işlemlerini yapabilmekteyim. Sonuç ise aşağıdaki gibi;

 

 

Bu iki makalede FetchXML’ler üzerinde konuştuk. Umarım faydalı olmuştur.