۱. مقدمه و صورت مسئله (Executive Summary)

در سازمان‌های در حال رشد و تکنولوژی‌محور، فرایند ورود نیروهای جدید (Onboarding) و آموزش اولیه (Training) شامل تعاملات پیچیده بین چندین واحد سازمانی از جمله تیم جذب (TA)، reources انسانی (HR)، زیرساخت و فناوری اطلاعات (IT)، اداری و پشتیبانی (Admin) و امور مالی (Finance) است.عدم وجود یک سامانه یکپارچه برای مدیریت این چرخه، منجر به چالش‌های زیر می‌شد:

  • تاخیر در اعطای دسترسی‌ها و تحویل تجهیزات تا روزهای پس از شروع به کار فرد.

  • ناهمگامی واحدهای IT و پشتیبانی در ساخت اکانت‌های Active Directory، ایمیل سازمانی و پیام‌رسان‌های داخلی.

  • خطاهای انسانی در ثبت اطلاعات مالی (شماره شبا، کد ملی، مراکز هزینه) و تخصیص سیم‌کارت یا مزایای پرسنلی.

  • عدم وجود شفافیت و قابلیت پیگیری (Traceability) برای مدیران ارشد و تیم HRBP.

برای حل این چالش‌ها، پروژه ارکستراسیون و اتوماسیون فرآیند آنبوردینگ بر روی بستر Jira Service Management (JSM) با استفاده از قابلیت‌های ScriptRunner for Jira، اتوماسیون‌های native، یکپارچه‌سازی با Active Directory / LDAP و سامانه‌های ارسال پیامک طراحی و پیاده‌سازی گردید.


۲. معماری کلان راهکار (Architectural Blueprint)

طراحی بر پایه یک مدل ترکیبی ارکستراسیون مرکزی (Hybrid Orchestration Model) انجام شده است. در این مدل، یک Main Issue در پروژه جذب (TA) ایجاد شده و چرخه حیات درخواست را کنترل می‌کند. متناسب با وضعیت فرآیند و پاسخ به سوالات شرطی فرم‌ها، Issueهای وابسته (Linked Issues) در پروژه‌های متناظر به صورت خودکار ساخته شده و با لینک‌های JQL blocking به یکدیگر متصل می‌شوند.

                  ┌─────────────────────────────────────────┐
                  │          [TA] Onboarding Request        │
                  │              (Main Orchestrator)        │
                  └────────────────────┬────────────────────┘
                                       │
            ┌──────────────────────────┴──────────────────────────┐
            ▼                                                     ▼
   ┌─────────────────┐                                   ┌─────────────────┐
   │     Tier A      │                                   │     Tier B      │
   │ (Pre-Day 1 Gate)│                                   │  (Day 1 Actions)│
   └────────┬────────┘                                   └────────┬────────┘
            │                                                     │
 ┌──────────┼──────────┬──────────┐            ┌──────────────────┼──────────────────┐
 ▼          ▼          ▼          ▼            ▼                  ▼                  ▼
[ADM]      [IT]       [IT]      [HR]         [ADM]               [IT]               [IT]
Contract   Accounts   Equip.    Pack/Lunch   Perks               Metabase           CRM/BackOffice
Setup      (AD/Mail)  Config    Prep         Activation          Access             Access

ساختار پروژه‌ها و تفکیک وظایف (Project Mapping)

کلید پروژه

نام پروژه Jira

مسئولیت اصلی

Issue Typeهای مرتبط

TA

Talent Acquisition

ارکستراسیون کل فرآیند، فرم آموزش، چک‌لیست نهایی

Onboarding Request, Training Form, Final Checklist

HR

Human Resources

ارزیابی قرارداد، تجهیزات، سیم‌کارت، بودجه آموزش

Contract Setup, Equipment Prep, SIM Provisioning

IT

Information Technology

ساخت اکانت Active Directory، سرویس‌های CRM، Metabase، مرکز تلفن

Account Creation, IT Revocation, Access Grant

ADM

Admin & Facilities

خدمات رفاهی، ناهار، بیمه تکمیلی، هماهنگی امضا

Benefits Activation, Lunch Prep, Insurance Cutoff

FIN

Finance

محاسبات مالی، مرکز هزینه، تسویه‌حساب

Settlement Calculation, Payment Issue


۳. جزئیات پیاده‌سازی تکنیکال (Deep-Dive Technical Implementation)

۳.۱ اتوماسیون ساخت کاربر در Active Directory با Groovy ScriptRunner

یکی از کلیدی‌ترین بخش‌های تکنیکال این فرآیند، ساخت خودکار اکانت کاربر در Active Directory / LDAP بلافاصله پس از تایید مراحل اولیه است. این اقدام از طریق یک Groovy Post-Function در ScriptRunner روی Transition مربوطه اجرا می‌شود.

کدهای ساخت کاربر در Active Directory (نمونه کد اصلاح‌شده و امن):

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.comments.CommentManager
import javax.naming.directory.*
import javax.naming.Context
import java.util.Hashtable

// ۱. تنظیمات اتصال امن به LDAP / Active Directory
Hashtable<String, String> env = new Hashtable<>()
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
env.put(Context.PROVIDER_URL, "ldaps://ad.company.local:636")
env.put(Context.SECURITY_AUTHENTICATION, "simple")
env.put(Context.SECURITY_PRINCIPAL, "CN=Jira ServiceAccount,OU=ServiceAccounts,DC=company,DC=local")
env.put(Context.SECURITY_CREDENTIALS, "SecureServiceAccountPassword123!")

CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
MutableIssue mutableIssue = (MutableIssue) issue
CommentManager commentManager = ComponentAccessor.getCommentManager()

// ۲. دریافت مقادیر Custom Fieldها از تیکت جیرا
CustomField firstNameField = customFieldManager.getCustomFieldObjectByName("نام انگلیسی")
CustomField lastNameField = customFieldManager.getCustomFieldObjectByName("نام خانوادگی انگلیسی")
CustomField emailField = customFieldManager.getCustomFieldObjectByName("ایمیل شرکتی")
CustomField passwordField = customFieldManager.getCustomFieldObjectByName("account-password")
CustomField ouField = customFieldManager.getCustomFieldObjectByName("واحد مربوطه")

def firstName = ((firstNameField?.getValue(issue) as String) ?: "").toLowerCase().trim()
def lastName = ((lastNameField?.getValue(issue) as String) ?: "").toLowerCase().trim()
def userPrincipalName = (emailField?.getValue(issue) as String) ?: ""
def rawPassword = (passwordField?.getValue(issue) as String) ?: ""
def targetOU = (ouField?.getValue(issue) as String) ?: "Employees"

DirContext ctx = null

try {
    ctx = new InitialDirContext(env)
    
    String userCn = "${firstName} ${lastName}"
    String userLogin = "${firstName}.${lastName}"
    String userDn = "CN=${userCn},OU=${targetOU},OU=EnterpriseUsers,DC=company,DC=local"
    
    Attributes userAttributes = new BasicAttributes(true)
    Attribute objectClass = new BasicAttribute("objectClass")
    objectClass.add("top")
    objectClass.add("person")
    objectClass.add("organizationalPerson")
    objectClass.add("user")
    userAttributes.put(objectClass)

    // کدگذاری کلمه عبور به فرمت UTF-16LE برای Active Directory
    String quotedPassword = "\"" + rawPassword + "\""
    byte[] utf16Password = quotedPassword.getBytes("UTF-16LE")

    userAttributes.put(new BasicAttribute("givenName", firstName))
    userAttributes.put(new BasicAttribute("sn", lastName))
    userAttributes.put(new BasicAttribute("cn", userCn))
    userAttributes.put(new BasicAttribute("displayName", userCn))
    userAttributes.put(new BasicAttribute("sAMAccountName", userLogin))
    userAttributes.put(new BasicAttribute("userPrincipalName", userPrincipalName))
    userAttributes.put(new BasicAttribute("mail", userPrincipalName))
    userAttributes.put(new BasicAttribute("unicodePwd", utf16Password))
    
    // تنظیم بیت‌های UAC: ترکیب ۵۱۲ (Normal Account) و ۶۵۵۳۶ (Password Never Expires) = ۶۶۰۴۸
    userAttributes.put(new BasicAttribute("userAccountControl", Integer.toString(66048)))
    userAttributes.put(new BasicAttribute("pwdLastSet", "-1"))

    // ایجاد کاربر در LDAP
    ctx.createSubcontext(userDn, userAttributes)
    
    String successMsg = "✅ اکانت Active Directory با موفقیت ایجاد شد: ${userDn}"
    log.info(successMsg)
    commentManager.create(mutableIssue, ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), successMsg, false)

} catch (Exception e) {
    String errorMsg = "❌ خطا در ساخت کاربر در Active Directory: " + e.getMessage()
    commentManager.create(mutableIssue, ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), errorMsg, false)
    log.error(errorMsg, e)
} finally {
    if (ctx != null) {
        try { ctx.close() } catch (Exception e) { log.error("خطا در بستن LDAP Context", e) }
    }
}

۳.۲ استعلام خودکار اطلاعات پرسنلی و مالی از پایگاه‌داده (Database Integration)

جهت جلوگیری از دوباره‌کاری و ورود دستی داده‌ها، اسکریپت زیر با اتصال به پایگاه‌داده بانک اطلاعاتی پرسنلی (Personnel_Info) بر اساس ایمیل ثبت‌کننده، فیلدهای شماره شبا، کد ملی، کد مرکز هزینه (CostCenter) و شناسه Threads را استخراج کرده و Custom Fieldهای تیکت جیرا را به‌روزرسانی می‌کند.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.onresolve.scriptrunner.db.DatabaseUtil
import org.apache.log4j.Logger

def log = Logger.getLogger("com.enterprise.PersonnelFieldUpdater")
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def issueManager = ComponentAccessor.getIssueManager()
def changeHolder = new DefaultIssueChangeHolder()

def issue = issueManager.getIssueObject(transientVars["issue"].id ?: transientVars["issue"].getKey())
if (!issue) return

def reporterEmail = issue.getReporter()?.getEmailAddress()
if (!reporterEmail) return

// شناسه Custom Fieldهای هدف در جیرا
final String shebaFieldId = 'customfield_10508'
final String costCenterFieldId = 'customfield_21501'
final String nationalIdFieldId = 'customfield_12310'

DatabaseUtil.withSql('Personnel_DB_Connection') { sql ->
    def query = "SELECT NationalID, SHEBA, CostCenter FROM Personnel WHERE Email = ?"
    def row = sql.firstRow(query, reporterEmail)
    
    if (row) {
        def shebaVal = row['SHEBA'] ?: "-"
        def costVal = row['CostCenter']?.toString()?.isNumber() ? Double.parseDouble(row['CostCenter'].toString()) : null
        def nationalVal = row['NationalID']?.toString()?.isNumber() ? Double.parseDouble(row['NationalID'].toString()) : null

        CustomField shebaField = customFieldManager.getCustomFieldObject(shebaFieldId)
        CustomField costField = customFieldManager.getCustomFieldObject(costCenterFieldId)
        CustomField nationalField = customFieldManager.getCustomFieldObject(nationalIdFieldId)

        if (shebaVal && shebaField) {
            shebaField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(shebaField), shebaVal), changeHolder)
        }
        if (costVal && costField) {
            costField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(costField), costVal), changeHolder)
        }
        if (nationalVal && nationalField) {
            nationalField.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(nationalField), nationalVal), changeHolder)
        }
    }
}

۳.۳ یکپارچه‌سازی با سامانه پیامک (Kavenegar REST API Webhook)

پس از ایجاد اکانت یا تعیین کد داخلی (Extension Code) در مرکز تلفن (MicroSIP)، اطلاعات دسترسی از طریق Web Request در Jira Automation به API وب‌سرویس پیامک ارسال می‌گردد:

  • Endpoint: https://api.kavenegar.com/v1/{API_KEY}/verify/lookup.json

  • Method: POST

  • Payload Params:

    • receptor: {{issue.customfield_mobile}}

    • template: OnboardingCredentials

    • token: {{issue.customfield_extension_code}}


۴. چرخه حیات و معماری فازهای اجرایی (Workflow Phases)

فرآیند ورود نیرو شامل دو فاز اصلی قبل از شروع به کار (Tier A) و اقدامات روز اول (Tier B) است:

[تکمیل فرم توسط TA]
        │
        ▼
[امضای قرارداد و NDA] (Gate 1)
        │
        ├─────────────────────────┬─────────────────────────┐
        ▼                         ▼                         ▼
[ایجاد اکانت IT]          [تحویل و کانفیگ سیستم]     [پک آنبوردینگ & ناهار]
(Mail & Mattermost)       (Stock vs Purchase)        (بر اساس ساختمان)
        │                         │                         │
        └─────────────────────────┼─────────────────────────┘
                                  │
                                  ▼
                     [روز اول حضور (Start Date)] (Gate 2)
                                  │
        ├─────────────────────────┼─────────────────────────┐
        ▼                         ▼                         ▼
[فعال‌سازی مزایا]        [دسترسی Metabase]         [دسترسی CRM & VoIP]
(Baino, Cubi, Snapp)     (بر اساس Collection)      (Extension Code)
                                  │
                                  ▼
                     [چک‌لیست نهایی & بستن تیکت]

۱. فاز پیش‌نیازهای روز اول (Tier A - Pre-Day 1)

  1. تایید امضای قرارداد و NDA: گیت اولیه فرآیند. تا زمانی که این مرحله انجام نشود، سایر تیکت‌ها صادر نمی‌شوند.

  2. ساخت اکانت‌های سازمانی: تولید ایمیل سازمانی و اکانت Mattermost ظرف ۲۴ ساعت کاری.

  3. تامین و کانفیگ تجهیزات: بررسی موجودی انبار توسط تیم اموال. در صورت عدم موجودی، صادر شدن خودکار تیکت خرید به مسئول مربوطه و تحویل جهت کانفیگ فنی به IT.

  4. هماهنگی لوجستیک و ناهار: ارسال اطلاع‌رسانی به مدیر فضای کار هر ساختمان (نکست، آفتاب، کانون، اردیبهشت) بر اساس فیلد ساختمان محل کار.

۲. فاز اقدامات روز اول (Tier B - Day 1 Actions)

  1. فعال‌سازی حساب‌های رفاهی: اعطای اعتبار سامانه‌های سازمانی.

  2. اعطای دسترسی‌های تخصصی: بر اساس شرط‌های فرم اولیه، تیکت‌های دسترسی به Metabase، CRM شرکا، Back-Office و سیستم تلفنی ParsLogic صادر شده و کد داخلی ثبت می‌شود.

  3. چک‌لیست نهایی TA: پس از تکمیل تمام تیکت‌های موازی، یک چک‌لیست جمع‌بندی برای کارشناس جذب صادر شده و پس از تایید نهایی، تیکت اصلی به وضعیت Closed تغییر می‌یابد.


۵. دستاوردها و نتایج کلیدی (Business Impact & KPIs)

پیاده‌سازی این سناریو روی Jira Service Management دستاوردهای زیر را به همراه داشت:

  • کاهش ۸۰ درصدی زمان آماده‌سازی دسترسی‌ها: زمان اعطای دسترسی‌ها و ساخت اکانت از ۳ روز کاری به کمتر از ۴ ساعت کاهش یافت.

  • حذف کامل خطاهای انسانی در اعطای دسترسی: به لطف اسکریپت‌های اتوماتیک ساخت کاربر در Active Directory و استعلام پایگاه‌داده.

  • شفافیت کامل در گزارش‌گیری (SLA Tracking): امکان سنجش عملکرد واحدهای پشتیبانی (IT، اداری، مالی) با Dashboardهای اختصاصی JSM.

  • تجربه کاربری عالی برای نیوجوینرها (NPS بالا): دریافت اطلاعات ورود، کد داخلی و خوش‌آمدگویی از طریق پیامک دقیقاً در روز اول حضور.