EP 2: Integration ร่วมกับ Microsoft Dynamics 365 for Finance and Operations

สำหรับนักพัฒนาแล้ว คงหนีไม่พ้นต้องเจอการทำ Integration Apps แนะนำการ integrate หรือสร้าง api สำหรับเชื่อมต่อกับ Dynamics 365 for Finance and Operations
blog cover2

สำหรับนักพัฒนาแล้ว คงหนีไม่พ้นต้องเจอการทำ Integration Apps บล็อกนี้จึงมาแนะนำการ integrate หรือสร้าง api สำหรับเชื่อมต่อกับ Microsoft Dynamics 365 for Finance and Operations (ซึ่ง 2 วิธีการที่จะแนะนำในวันนี้ ใช้บ่อยมาก)

Quick Suggest

1. OData Service OData

เป็นโปรโตคอลมาตรฐานสำหรับการดึงข้อมูล ที่สามารถเข้าถึงได้ด้วยเทคโนโลยี HTTP และ JSON, OData service จะถูก public และใช้งานได้ทันที เมื่อเรา build application ผ่านเงื่อนไข Data Entities ที่ IsPublic = Yes

วิธีการใช้งานคือ http://[baseURI]/data , รายการที่แสดงขึ้นมาทั้งหมด คือ data entities ที่เราสามารถเรียกใช้งานได้ เช่น หากเราต้องการข้อมูลลูกค้า ก็ / ต่อท้ายได้เลย http://[baseURI]/data/Customers

Class service3
OData Service

รองรับคำสั่งตาม OData standards เช่น

  • ดูข้อมูลทุก Company ในระบบ
				
					http://[baseURI]/data/Customers?cross-company=true
				
			
  • ดูข้อมูลเฉพาะ Company ที่เราสนใจ
				
					http://[baseURI]/data/Customers?$filter=dataAreaId eq ‘thmf’&cross-company=true
				
			
  • กรองข้อมูลฟิลอื่นๆก็ได้เหมือนกัน (กลุ่มลูกค้ารหัส 20)
				
					http://[baseURI]/data/Customers?$filter=dataAreaId eq ‘thmf’ and CustomerGroupId eq ‘20’&cross-company=true
				
			

และเชื่อมต่อกับตัวดึงข้อมูล OData ใน Power BI Desktop ได้อีกด้วย สะดวกมาก ๆ

OData

เกือบลืมไปอย่าง แวะมาดูการยืนยันตัวตนในการร้องขอใช้ API กันซักนิด

  • Authentication

    OData Services, JSON-based Custom Service, and REST Metadata Service รองรับการยืนยันตัวตนแบบ OAuth 2.0 ดังนั้นเราต้องมาเตรียมตัวกันก่อน

    OAuth — Authorization Code Grant Flow

Authorization Code Grant Flow 1

จาก Flow เราต้องทำการ Register Application ที่ Microsoft Azure Active Directory (AAD) ก่อน เพื่อให้ได้ Authorization token มา (Link)

Authorization 1

การ request ทุกครั้งจำเป็นต้อง call 2 ครั้ง ครั้งแรกนำ Authorization token ร้องขอไปยัง Azure AD เพื่อให้ได้ Access token มา (ซึ่งมีเวลาหมดอายุ), ครั้งที่สอง นำ Access token ที่ได้มาแนบไปกับการ request API ที่เราต้องการใช้งาน

  • ลองทดสอบโดยใช้ Postman

Postman

Request 1 : Access Token

  • POST
				
					https://login.microsoftonline.com/:tenant_id/oauth2/token
				
			
  • Params
				
					tenant_id:c4dc70b8–32c4–4443-afde-530521xxxxxx (ได้จาก AAD)
				
			
  • Body
				
					grant_type:client_credentials
client_id:c8907ca6–09a6–40a6–83ce-e67a4xxxxx (ได้จาก AAD)
client_secret:.Ew2C94l?2elh/rLDszhHFFxxxxxxx (ได้จาก AAD)
resource:[baseURI]
				
			
Access Token

Request 2 : Odata

  • GET:
				
					https://[baseURI]/data/Customers?$filter=dataAreaId eq ‘thmf’&$select=CustomerAccount,Name,AddressDescription,FullPrimaryAddress&cross-company=true
				
			
  • Headers
				
					Authorization:Bearer xxxxxxxxx (จาก access_token Request 1) Content-Type:application/json
				
			
  • Tests
				
					var json = JSON.parse(responseBody); tests[“Get customer info”] = !json.error && responseBody !== ‘’ && responseBody !== ‘{}’;
				
			
Access Token1

ข้อจำกัดของ api คือ URL ที่อยู่ใน VM จะไม่สามารถทดสอบได้ ต้องเป็น URL on-cloud หรือ on premises เท่านั้น

2. JSON-based custom service

เป็น custom service ที่เราสามารถสร้างเองได้จาก X++ classes หรือการเขียนโปรแกรมนั่นเอง มีการรับค่าและรีเทิร์นค่าแบบ JSON (JavaScript Object Notation), ทุก service ที่ถูก custom ขึ้นมา จะถูก deploy เป็น 2 endpoint เสมอ

  • SOAP
				
					endpoint https:///soap/services/UserSessionService?wsdl
				
			
  • JSON endpoint
				
					https:///api/services/UserSessionService/AifUserSessionService/GetUserSessionInfo
				
			

ตัวอย่างในวันนี้จะเป็นการสร้าง api อย่างง่ายในการเปิดใบสั่งขาย หรือ sales order มาเริ่มกันเลย

ส่วนประกอบของ Object

JSON based custom service

Class contract สำหรับดูแลเรื่อง parameter ซึ่งในที่นี้เราให้เป็น List ของรายการสินค้าและจำนวน

				
					[DataContractAttribute]
public class QERP_SalesOrderContract
{
   ItemId itemId;
   Qty qty;
   [DataMemberAttribute]
   public ItemId itemId(ItemId _itemId = itemId)
   {
      itemId = _itemId;
      return itemId;
   }
   [DataMemberAttribute]
   public Qty qty(Qty _qty = qty)
   {
      qty = _qty;
      return qty;
   }
}
				
			

Class info สำหรับ return สถานะและรายละเอียด

				
					[DataContractAttribute]
public class QERP_SalesOrderInfo
{
   boolean success;
   str message;
   [DataMemberAttribute]
   public boolean success(boolean _success = success)
   {
      success = _success;
      return success;
   }
   [DataMemberAttribute]
   public str message(str _message = message)
   {
      message = _message;
      return message;
   }
}
				
			

Class service สำหรับสร้าง sales order

				
					public class QERP_SalesOrderService
{
[AifCollectionTypeAttribute('salesLineList', Types::Class, classStr(QERP_SalesOrderContract)), SysEntryPointAttribute(true)]
public QERP_SalesOrderInfo CreateSalesOrder(DataAreaId dataArea, CustAccount customer, List salesLineList)
{
   QERP_SalesOrderInfo messageInfo = new QERP_SalesOrderInfo();
   QERP_SalesOrderContract salesLineData;
   SalesTable salesTable;
   SalesLine salesLine;
   NumberSeq numberSeq;
   ListIterator literatorset;
   ttsbegin;
   changecompany(dataArea)
   {
   numberSeq = NumberSeq::newGetNum(SalesParameters::numRefSalesId());
   salesTable.SalesId = numberSeq.num();
   salesTable.initValue();
   salesTable.CustAccount = customer;
   salesTable.initFromCustTable();
   salesTable.insert();
   literatorset = new ListIterator(salesLineList);
   while (literatorset.more())
   {
      salesLineData = literatorset.value();
      salesLine.clear();
      salesLine.SalesId = salesTable.SalesId;
      salesLine.ItemId = salesLineData.itemId();
      salesLine.SalesQty = salesLineData.qty();
      salesLine.createLine(NoYes::Yes, // Validate
            NoYes::Yes, // initFromSalesTable
            NoYes::Yes, // initFromInventTable
            NoYes::Yes, // calcInventQty
            NoYes::No, // searchMarkup
          NoYes::No); // searchPrice
      literatorset.next();
   }
   }
   ttscommit;
   messageInfo.success(true);
   messageInfo.message(strFmt("Sales order '%1' has been created.", salesTable.SalesId));
   return messageInfo;
   }
}
				
			

เมื่อ Build application เราก็จะได้ API เรียบร้อย

Class service1

ทดสอบ request

Class service2
Class service4

ใช้ tools ประเภท SoapUI ก็ได้เหมือนกันนะ

Class service

ก้าวเข้าสู่ Digital Business

ดูผลิตภัณฑ์ที่เกี่ยวข้องได้ที่นี่

Table of Content

🧑‍💻 Workshop สร้าง AI-Chatbot ผู้ช่วยอัจฉริยะปฏิวัติองค์กรดิจิทัล

สร้าง AI-Chatbot อัจฉริยะด้วย Copilot Studio! เชื่อมระบบในองค์กรได้ทันที ยกระดับ Productivity ด้วยการสอนที่เข้าใจง่ายและลงมือทำได้จริง