Meningkatkan Akurasi Data: Membangun Plugin Dynamics 365 untuk Generate Running Number Harian (YYMMDDXXXX)

1. JUDUL: Meningkatkan Akurasi Data: Membangun Plugin Dynamics 365 untuk Generate Ruing Number Harian (YYMMDDXXXX)

2. KONTEN:

Pendahuluan

Dalam banyak skenario bisnis, kebutuhan akaomor unik yang berurutan untuk setiap record merupakan hal yang krusial, mulai dari nomor faktur, nomor tiket dukungan, hingga identifikasi internal. Dynamics 365 menyediakan banyak fleksibilitas, namun skema penomoran yang lebih kompleks, seperti ruing number harian dengan format spesifik (contoh: YYMMDDXXXX), seringkali memerlukan solusi kustom. Artikel ini akan memandu Anda secara teknis dalam membangun plugin Dynamics 365 untuk mengimplementasikan skema penomoran harian format YYMMDDXXXX, memastikan setiap record baru mendapatkaomor unik yang direset setiap hari.

Memahami Kebutuhan Ruing Number Harian

Format YYMMDDXXXX berarti:

  • YY: Dua digit terakhir tahun (misalnya, ’24’ untuk tahun 2024).
  • MM: Dua digit bulan (misalnya, ’01’ untuk Januari).
  • DD: Dua digit hari (misalnya, ’15’ untuk tanggal 15).
  • XXXX: Nomor urut harian yang akan di-reset setiap hari ke-1, dan akan bertambah secara sekuensial (misalnya, ‘0001’, ‘0002’, dst.) hingga mencapai ‘9999’.

Implementasi ini memerlukan mekanisme untuk menyimpan dan mengelola nomor urut harian, serta memastikan bahwa proses penomoran berjalan secara atomik dan konsisten, terutama dalam lingkungan multi-pengguna dengan potensi race condition.

Arsitektur Solusi: Entitas Kustom untuk Counter

Untuk menyimpan dan mengelola nomor urut harian secara efektif, kita akan membuat entitas kustom di Dynamics 365 yang berfungsi sebagai “daily counter”. Entitas ini akan menyimpaomor urut terakhir untuk setiap tanggal.

Konfigurasi Entitas Kustom ‘Daily Number Counter’

Buat entitas kustom baru, misalnya dengaama tampilan “Daily Number Counter” daama skema cr_dailynumbercounter. Entitas ini harus memiliki field-field berikut:

  • Primary Field: Biarkan sebagai default (misalnya, cr_name). Ini bisa diatur otomatis atau diisi dengan tanggal untuk kemudahan identifikasi.
  • Tanggal Counter (cr_date): Tipe data ‘Date Only’. Field ini akan menyimpan tanggal spesifik untuk counter. Pastikan field ini memiliki Unique Constraint (Index Duplicate Detection) untuk menghindari duplikasi record counter untuk tanggal yang sama. Ini sangat penting untuk mencegah race condition.
  • Current Sequence (cr_currentsequence): Tipe data ‘Whole Number’. Field ini akan menyimpaomor urut terakhir yang telah digunakan untuk tanggal tersebut.

Membangun Plugin: Logika dan Implementasi

Plugin akan berjalan pada event pembuatan (Create) record dari entitas target (misalnya, entitas ‘Kasus’ atau ‘Lead’) dan akan mengambil nomor urut harian dari entitas cr_dailynumbercounter, lalu mengaplikasikaya ke field pada entitas target.

Detail Plugin

  • Event: Create
  • Entity: Entitas target (misalnya, incident, lead, atau entitas kustom Anda)
  • Stage: Pre-Operation (Synchronous). Ini memastikaomor urut diatur sebelum record disimpan ke database.

Struktur Kode Plugin (C#)

Berikut adalah contoh struktur kode plugin:


using System;
using System.Linq;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client; // For FaultException
using System.ServiceModel; // For FaultExceptioamespace YourNamespace.Plugins
{
    public class GenerateDailyRuingNumber : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { Entity entity = (Entity)context.InputParameters["Target"];

// Pastikan plugin hanya berjalan untuk entitas yang relevan if (entity.LogicalName != "cr_yourtargetentityname") // Ganti dengan logical name entitas target Anda { return; }

// Ambil tanggal hari ini dalam format UTC DateTime todayUtc = DateTime.UtcNow.Date; // Hanya tanggal, tanpa waktu

try { // 1. Ambil atau buat record daily counter untuk hari ini Entity dailyCounterRecord = GetOrCreateDailyCounter(service, todayUtc, tracingService);

// 2. Ambil nomor urut saat ini, increment, lalu update counter int currentSequence = dailyCounterRecord.Contains("cr_currentsequence") ? (int)dailyCounterRecord["cr_currentsequence"] : 0; currentSequence++;

dailyCounterRecord["cr_currentsequence"] = currentSequence; service.Update(dailyCounterRecord); // Update counter di database

// 3. Generate ruing number dalam format YYMMDDXXXX string yyMmDd = todayUtc.ToString("yyMMdd"); string ruingNumber = $"{yyMmDd}{currentSequence:D4}"; // D4 untuk padding 0 hingga 4 digit

// 4. Set nilai ruing number ke field pada entitas target // Ganti "cr_ruingnumberfield" dengan logical name field di entitas target Anda entity["cr_ruingnumberfield"] = ruingNumber;

tracingService.Trace($"Generated ruing number: {ruingNumber}"); } catch (FaultException ex) { tracingService.Trace($"Plugin error: {ex.Message}"); throw new InvalidPluginExecutionException("Error generating daily ruing number: " + ex.Message, ex); } catch (Exception ex) { tracingService.Trace($"Plugin error: {ex.ToString()}"); throw new InvalidPluginExecutionException("An unexpected error occurred: " + ex.Message, ex); } } }

private Entity GetOrCreateDailyCounter(IOrganizationService service, DateTime date, ITracingService tracingService) { // Coba ambil record counter yang sudah ada untuk tanggal ini QueryExpression query = new QueryExpression("cr_dailynumbercounter") { ColumnSet = new ColumnSet("cr_currentsequence"), Criteria = new FilterExpression(LogicalOperator.And) }; // Perhatikan bahwa untuk field DateOnly, perbandingan dengan DateTime.Date akan cocok query.Criteria.AddCondition("cr_date", ConditionOperator.Equal, date);

EntityCollection existingCounters = service.RetrieveMultiple(query);

if (existingCounters.Entities.Any()) { tracingService.Trace($"Existing counter found for {date.ToShortDateString()}."); return existingCounters.Entities.First(); } else { // Jika belum ada, coba buat record counter baru Entity newCounter = new Entity("cr_dailynumbercounter"); newCounter["cr_date"] = date; newCounter["cr_currentsequence"] = 0; // Akan di-increment menjadi 1 oleh logika utama plugin

try { tracingService.Trace($"Creating new counter for {date.ToShortDateString()}."); newCounter.Id = service.Create(newCounter); returewCounter; } catch (FaultException ex) { // Tangani potensi race condition: instance plugin lain mungkin sudah membuat record // ErrorCode -2147220937 adalah untuk "A record with these values already exists." (duplicate key) if (ex.Detail.ErrorCode == -2147220937) { tracingService.Trace("Race condition detected: another plugin instance created the counter. Re-retrieving."); // Coba ambil lagi, karena record sekarang seharusnya sudah ada existingCounters = service.RetrieveMultiple(query); if (existingCounters.Entities.Any()) { return existingCounters.Entities.First(); } else { // Ini seharusnya tidak terjadi, tapi jika ya, berarti ada masalah throw new InvalidPluginExecutionException("Failed to retrieve counter after race condition handling.", ex); } } throw; // Lempar kembali error lain } } } } }

Penjelasan Penting: Penanganan Concurrency

Bagian kunci dari solusi ini adalah penanganan race condition. Ketika beberapa pengguna mencoba membuat record target secara bersamaan pada hari yang sama, ada potensi dua atau lebih instance plugin mencoba membuat record cr_dailynumbercounter baru secara bersamaan. Dengan menambahkan Unique Constraint pada field cr_date di entitas cr_dailynumbercounter, hanya satu operasi Create yang akan berhasil. Operasi laiya akan menghasilkan FaultException dengan ErrorCode -2147220937 (Duplicate Record). Logika try-catch dalam fungsi GetOrCreateDailyCounter menangkap error ini dan kemudian mencoba Retrieve kembali record counter, memastikan semua instance plugin bekerja dengan record counter yang sama yang berhasil dibuat.

Langkah-langkah Setelah Pengembangan

  1. Build Proyek Plugin: Bangun proyek C# Anda dan pastikan outputnya adalah assembly (DLL).
  2. Daftarkan Plugin: Gunakan Plugin Registration Tool untuk mendaftarkan assembly plugin.
  3. Daftarkan Step Plugin: Daftarkan step baru untuk plugin Anda dengan detail berikut:
    • Message: Create
    • Primary Entity: Entitas target Anda (misalnya, cr_yourtargetentityname)
    • Event Pipeline Stage of Execution: Pre-Operation
    • Execution Mode: Synchronous
  4. Uji Coba: Buat beberapa record baru pada entitas target untuk memastikaomor urut harian dihasilkan dengan benar dan direset pada hari berikutnya. Uji juga skenario pembuatan record secara bersamaan jika memungkinkan untuk memverifikasi penanganan race condition.

Kesimpulan

Dengan mengikuti panduan ini, Anda telah berhasil membangun solusi yang kuat dan skalabel untuk menghasilkan ruing number harian dengan format YYMMDDXXXX di Dynamics 365. Penggunaan entitas kustom sebagai counter dan penanganan race condition yang cermat memastikan integritas data dan kinerja yang andal. Pendekatan ini tidak hanya memenuhi persyaratan bisnis spesifik, tetapi juga menunjukkan kekuatan dan fleksibilitas platform Dynamics 365 dalam mengimplementasikan logika bisnis yang kompleks melalui plugin kustom.

3. GAMBAR:
A conceptual illustration showing a flow from a Dynamics 365 form (e.g., a “New Case” form) to a cloud icon representing the Dynamics 365 environment, then to a code editor displaying C# plugin code snippet, and finally back to the Dynamics 365 form with a field populated with a ruing number like “2401150001”. The image should have a clean, modern, technical aesthetic, perhaps with subtle lines coecting the components, emphasizing data flow and custom logic. A small icon of a calendar with a “reset” symbol could be subtly included to represent the daily reset.

4. TAGS: Dynamics 365, Power Platform, CRM Plugin, Ruing Number, Custom Development, C#, Technical Guide, Data Generation, Business Logic

Leave a Comment