Search This Blog

Friday, March 30, 2012

AX2012 Import HCMWorker or Employee from CSV

This is the sample code for creating the Employee and if employee exists then updating the employee.
First build the class to read from CSV file and call this methods for creating/updateing the worker.
Please change the container column number values from your csv positions.

we can use HCMWorkerImportService for create, but this service did'nt provide the update functionality.
I will update the the next post using service to create/update the employee.

private void run()
{

 if (!HcmWorker::findByPersonnelNumber(conPeek(_c, #EmployeeNumber)).RecId)
        {//Update employee
            this.employeeCreate(_c);
        }
        else
        {//Insert employee
            this.employeeUpdate(_c);
        }

}


static public Description createDescription(DirPersonName _dirPersonName)
{
    #Define.Space(" ")
    return _dirPersonName.FirstName
        + (_dirPersonName.MiddleName ? #Space + _dirPersonName.MiddleName : "")
        + (_dirPersonName.LastName   ? #Space + _dirPersonName.LastName   : "");
}


public Static HcmPersonGender convGender(str _gender)
{
    HcmPersonGender ret;
    #Define.Female("Kvinde")
    #Define.Male("Mand")
    ;
    switch (_gender)
    {
        case #Female:
            ret = HcmPersonGender::Female;
            break;

        case #Male:
            ret = HcmPersonGender::Male;
            break;

        default:
            ret = HcmPersonGender::None;
    }

    return ret;
}


public Static str defaultCountryRegionId()
{
    str ret;
    LogisticsPostalAddress  logisticsPostalAddress_Default;
    ;

    logisticsPostalAddress_Default.initValue();

    ret = logisticsPostalAddress_Default.CountryRegionId;

    return ret;
}





public static date convDate(str _date)
{
    Date ret;
    ;

    ret = str2Date(_date, 123);

    return ret;
}


This method is used to create the employee.
Create
private void employeeCreate(container _c)
{
    boolean                                 ret = true;
    CompanyInfo                             companyInfo;
    HcmEmploymentRecId                      newEmploymentRecId;
    ValidFromDateTime                       employmentStartDateTime;
    ValidToDateTime                         employmentEndDateTime;
    HcmWorker                               newHcmWorker;
    DirPerson                               dirPerson;
    DirPersonName                           dirPersonName;
    HcmEmploymentType                       hcmEmploymentType = HcmEmploymentType::Employee;
    NumberSeq                               numberSeqPersonnelNum;
    HcmPersonPrivateDetails                 HcmPersonPrivateDetails;
    AxLogisticsPostalAddress                axLogisticsPostalAddress = new AxLogisticsPostalAddress();
    AxLogisticsLocation                     axLogisticsLocation;
    ;

    companyInfo = companyInfo::find();

    employmentStartDateTime = datetobeginUtcDateTime(HrmImportEmployeeMasterdata::convDate(conpeek(_c, #DateOfHiring)), DateTimeUtil::getUserPreferredTimeZone());
    employmentEndDateTime   = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::maxValue(), DateTimeUtil::getUserPreferredTimeZone());

    dirPersonName.FirstName     = conpeek(_c, #FirstName);
    dirPersonName.MiddleName    = conpeek(_c, #MiddleName);
    dirPersonName.LastName      = conpeek(_c, #LastName);
    newHcmWorker = HcmWorker::find(HcmWorkerTransition::newCreateHcmWorker(dirPersonName
                                                                           , conpeek(_c, #EmployeeNumber)
                                                                           , companyInfo.RecId
                                                                           , hcmEmploymentType
                                                                           , employmentStartDateTime
                                                                           , employmentEndDateTime));

    if (newHcmWorker.RecId == 0)
    {
        ret = false;
    }

    if (newHcmWorker.RecId == 0)
    {
        // Updating an existing worker

        // If there is no active employment for the worker
        newEmploymentRecId = HcmWorkerTransition::newCreateHcmEmployment(newHcmWorker.RecId, companyInfo.RecId,
                hcmEmploymentType, employmentStartDateTime, employmentEndDateTime);


        if (newEmploymentRecId == 0)
        {
            ret = false;
        }
    }

    if (ret)
    {
        if(numberSeqPersonnelNum)
        {
            // mark number sequence based ID consumed
            numberSeqPersonnelNum.used();
        }
    }
    else
    {
        if(numberSeqPersonnelNum)
        {
            numberSeqPersonnelNum.abort();
        }

        dirPerson.clear();
        dirPersonName.Person = 0;
    }

    hcmPersonPrivateDetails.initValue();
    hcmPersonPrivateDetails.Person      = dirPersonName.Person;
    hcmPersonPrivateDetails.BirthDate   = HrmImportEmployeeMasterdata::convDate(conpeek(_c, #BirthDate));
    hcmPersonPrivateDetails.Gender      = HrmImportEmployeeMasterdata::convGender(conpeek(_c, #Gender));
    hcmPersonPrivateDetails.insert();

    dirPerson                   = dirPerson::find(dirPersonName.Person, true);
    dirPerson.Initials          = conpeek(_c, #Initials);
    dirPerson.ProfessionalTitle = conpeek(_c, #Position);
    dirPerson.update();

    //Create address
    axLogisticsLocation = new AxLogisticsLocation();
    axLogisticsLocation.validateInput(true);
    axLogisticsLocation.parmIsPostalAddress(NoYes::Yes);
    axLogisticsLocation.parmDescription(HrmImportEmployeeMasterdata::createDescription(dirPersonName));
    axLogisticsLocation.save();

    axLogisticsPostalAddress    = new AxLogisticsPostalAddress();
    axLogisticsPostalAddress.parmLocation(axLogisticsLocation.parmRecId());
    axLogisticsPostalAddress.validateInput(true);
    axLogisticsPostalAddress.parmCountryRegionId(HrmImportEmployeeMasterdata::defaultCountryRegionId());
    axLogisticsPostalAddress.parmZipCode(conpeek(_c, #PostalCode));
    axLogisticsPostalAddress.parmZipCodeRecId(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).RecId);
    axLogisticsPostalAddress.parmStreet(conpeek(_c, #Address));
    axLogisticsPostalAddress.parmCity(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).City);
    axLogisticsPostalAddress.parmCityRecId(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).CityRecId);
    axLogisticsPostalAddress.save();

    DirParty::addLocation(dirPersonName.Person, axLogisticsLocation.parmRecId(), true, true, true);
}


This method is used to update the employee

Update
private void employeeUpdate(container _c)
{
    boolean                                 ret = true;
    CompanyInfo                             companyInfo;
    ValidFromDateTime                       employmentStartDateTime;
    ValidToDateTime                         employmentEndDateTime;
    HcmWorker                               hcmWorker;
    DirPerson                               dirPerson;
    DirPersonName                           dirPersonName;
    HcmEmploymentType                       hcmEmploymentType = HcmEmploymentType::Employee;
    HcmPersonPrivateDetails                 HcmPersonPrivateDetails;
    AxLogisticsPostalAddress                axLogisticsPostalAddress = new AxLogisticsPostalAddress();
    AxLogisticsLocation                     axLogisticsLocation;
    Description                             description_Old;
    HcmEmployment                           hcmEmployment;
    LogisticsLocation                       logisticsLocation;
    LogisticsPostalAddress                  logisticsPostalAddress;
    DirPartyLocation                        dirPartyLocation;
    ;

    companyInfo = companyInfo::find();
    hcmWorker   = hcmWorker::findByPersonnelNumber(conpeek(_c, #EmployeeNumber));

    //Update dirPersonName
    dirPersonName   = dirPersonName::find(hcmWorker.Person, true);
    description_Old = HrmImportEmployeeMasterdata::createDescription(dirPersonName);
    if (   dirPersonName.FirstName  != conpeek(_c, #FirstName)
        || dirPersonName.MiddleName != conpeek(_c, #MiddleName)
        || dirPersonName.LastName   != conpeek(_c, #LastName))
    {
        dirPersonName.validTimeStateUpdateMode(ValidTimeStateUpdate::Correction);
        dirPersonName.FirstName     = conpeek(_c, #FirstName);
        dirPersonName.MiddleName    = conpeek(_c, #MiddleName);
        dirPersonName.LastName      = conpeek(_c, #LastName);
        dirPersonName.update();
    }

    //Update hiring
    employmentStartDateTime = datetobeginUtcDateTime(HrmImportEmployeeMasterdata::convDate(conpeek(_c, #DateOfHiring)), DateTimeUtil::getUserPreferredTimeZone());
    employmentEndDateTime   = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::maxValue(), DateTimeUtil::getUserPreferredTimeZone());

    hcmWorker       = HcmWorker::findByPersonnelNumber(conpeek(_c, #EmployeeNumber));
    hcmEmployment   = hcmEmployment::getActiveEmploymentsByWorker(hcmWorker.RecId, employmentStartDateTime, employmentEndDateTime, true);
    if (hcmEmployment.RecId)
    {
        HcmWorkerTransition::newUpdateHcmEmployment(hcmEmployment, employmentStartDateTime, employmentEndDateTime);
    }
    else
    {
        hcmWorker = HcmWorker::find(HcmWorkerTransition::newCreateHcmWorker(dirPersonName
                                                                           , conpeek(_c, #EmployeeNumber)
                                                                           , companyInfo.RecId
                                                                           , hcmEmploymentType
                                                                           , employmentStartDateTime
                                                                           , employmentEndDateTime));
    }

    hcmPersonPrivateDetails = HcmPersonPrivateDetails::findByPerson(hcmWorker.Person, true);
    if (hcmPersonPrivateDetails)
    {
        if (   hcmPersonPrivateDetails.BirthDate    != HrmImportEmployeeMasterdata::convDate(conpeek(_c, #BirthDate))
            || hcmPersonPrivateDetails.Gender       != HrmImportEmployeeMasterdata::convGender(conpeek(_c, #Gender)))
        {
            hcmPersonPrivateDetails.BirthDate   = HrmImportEmployeeMasterdata::convDate(conpeek(_c, #BirthDate));
            hcmPersonPrivateDetails.Gender      = HrmImportEmployeeMasterdata::convGender(conpeek(_c, #Gender));
            hcmPersonPrivateDetails.update();
        }
    }
    else
    {
        hcmPersonPrivateDetails.initValue();
        hcmPersonPrivateDetails.Person      = dirPersonName.Person;
        hcmPersonPrivateDetails.BirthDate   = HrmImportEmployeeMasterdata::convDate(conpeek(_c, #BirthDate));
        hcmPersonPrivateDetails.Gender      = HrmImportEmployeeMasterdata::convGender(conpeek(_c, #Gender));
        hcmPersonPrivateDetails.insert();
    }

    dirPerson                   = dirPerson::find(dirPersonName.Person, true);
    if (   dirPerson.Initials               != conpeek(_c, #Initials)
        || dirPerson.ProfessionalTitle      != conpeek(_c, #Position))
    {
        dirPerson.Initials          = conpeek(_c, #Initials);
        dirPerson.ProfessionalTitle = conpeek(_c, #Position);
        dirPerson.update();
    }

    //Update or Create address
    select firstOnly forUpdate logisticsLocation
        where logisticsLocation.Description == description_Old
        join dirPartyLocation
            where dirPartyLocation.Location == logisticsLocation.RecId
               && dirPartyLocation.Party    == dirPerson.RecId;

    if (logisticsLocation)
    {
        axLogisticsLocation = AxInternalBase::construct(logisticsLocation);
    }
    else
    {
        axLogisticsLocation = new AxLogisticsLocation();
    }

    axLogisticsLocation.validateInput(true);
    axLogisticsLocation.parmIsPostalAddress(NoYes::Yes);
    axLogisticsLocation.parmDescription(HrmImportEmployeeMasterdata::createDescription(dirPersonName));
    axLogisticsLocation.save();


    //Update or Create postaladdress
    select firstOnly forUpdate logisticsPostalAddress
        where logisticsPostalAddress.Location == axLogisticsLocation.parmRecId();

    if (logisticsPostalAddress)
    {
        axLogisticsPostalAddress = AxInternalBase::construct(logisticsPostalAddress);
    }
    else
    {
        axLogisticsPostalAddress = new AxLogisticsPostalAddress();
    }

    axLogisticsPostalAddress.parmLocation(axLogisticsLocation.parmRecId());
    axLogisticsPostalAddress.validateInput(true);
    axLogisticsPostalAddress.parmCountryRegionId(HrmImportEmployeeMasterdata::defaultCountryRegionId());
    axLogisticsPostalAddress.parmZipCode(conpeek(_c, #PostalCode));
    axLogisticsPostalAddress.parmZipCodeRecId(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).RecId);
    axLogisticsPostalAddress.parmStreet(conpeek(_c, #Address));
    axLogisticsPostalAddress.parmCity(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).City);
    axLogisticsPostalAddress.parmCityRecId(LogisticsAddressZipCode::find(conpeek(_c, #PostalCode)).CityRecId);
    axLogisticsPostalAddress.save();

    DirParty::addLocation(dirPersonName.Person, axLogisticsLocation.parmRecId(), true, true, true);
}

17 comments:

  1. Krishna, thanks for posting this information. I tried your code, but it gave me an error saying that Field 'Name' and 'Worked' must be filled in.

    I'm now sure where in the code is the name of the party beind created or populated.

    regards,

    Francis

    ReplyDelete
  2. Krishna, I got it to work. My system is on RTM. Once I moved to the CU2 environment, it worked fine. Thanks very much. This was very helpful!!!!

    ReplyDelete
  3. Dear Krishna,

    I want to Import the Employees in AX2012 but i am bot able to find the HCMWorkerImportservices,
    what is RTM and CU2 from where i can find.

    ReplyDelete
    Replies
    1. Hi,
      May be your hcmworker service was not deployed and activated. so go to servicegroups and check that hcmworker service and deploy the service.

      hope this will solves ur issue.

      Delete
  4. Hi Krishna,

    Where can I find the class HrmImportEmployeeMasterdata?

    Thanks!

    ReplyDelete
    Replies
    1. Hi,
      This is the class where I created all the above code to import the employee.
      So you can write your own class and change accordingly...I updated the remaining methods that I used in the code as well.

      Thanks,
      Krishna.

      Delete
  5. Hello Krishna,


    This blog is very helpful for me for Employee Creation and Employee Updation on Enterprises Portail in Dynamics Ax 2012.Thanks very much.

    ReplyDelete
    Replies
    1. Hi Harsh,
      Thank's for your message.I will do my level best for supporting the people.

      Regards,
      krishna.

      Delete
  6. Hello Harsh,
    Thank's for you post here, is is very helpfull,
    Now I must try to create / update the phone and email address within this peace of code. than'k's again for this on.

    Regards
    Alex

    ReplyDelete
    Replies
    1. Hi Alex,
      Thanks, if you have any issues,let me know.

      Regards,
      krishna.
      krishna.dynamics@gmail.com

      Delete
    2. Hi Krishna, I have used your code, but found that for changing (update) the addres, there is a safer way .
      I have uses the next code

      logisticsLocation = DirParty::primaryPostalAddressLocationTable(dirPerson.RecId);
      logisticsLocation.selectForUpdate(true);
      to find the location for the address, and logisticsPostalAddress = DirParty::primaryPostalAddress(dirPerson.RecId);
      logisticsPostalAddress.selectForUpdate(true);
      for the address record.

      I have found a way to also create and update the email and phone.

      Delete
    3. Hi Alex,
      In My case If not found it will create otherwise it will update.
      The above code you mentioned was right,it was used to update the address directly....thanks for your message...with the updated code.

      Regards,
      krishna.

      Delete
  7. Great Post Krishna, Would be ultimate if you can also share your HrmImportEmployeeMasterdata Class for End to End Employee Creation/Updation.

    -Kuldeep

    ReplyDelete
  8. Hi Reddy, just got a little piece of your Import code using CSV file for Employee Data in AX 2012. Could u pls help with the code or procedure to build the class to read the csv file and the the methods. Could u help with the CSV file format too, though I was able to come up with one from the code.
    Thanks.
    Tunji.

    ReplyDelete
  9. Hi Krishna,
    Thanks for sharing your code.

    I'm little bit confused with this line:

    employmentEndDateTime = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::maxValue(), DateTimeUtil::getUserPreferredTimeZone());

    Now, as far as I understand the goal is to set employmentEndDateTime to infinity which indicates that the employee is still employed. The thing that I don't understand is why time zone offset is applied?

    I did some test and the result seems not to be correct (at least to me), here it is:


    static void TestDateTimeMaxValue(Args _args)
    {
    utcdatetime maxDateTime;
    Timezone tz;
    ;

    tz = DateTimeUtil::getUserPreferredTimeZone();
    // tz = GMTPLUS0200HELSINKI_KYIV_RIGA_VILNIUS; <- use this if your preferred timezone offset is <= 0
    info(strfmt("The timezone is: %1", tz));

    maxDateTime = DateTimeUtil::maxValue();
    info(strfmt("The maxDateTime is: %1", maxDateTime));

    maxDateTime = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::maxValue(), tz);
    info(strfmt("The maxDateTime after offset is: %1", maxDateTime));

    }


    *** The timezone is: (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius <--- positive offset +2
    *** The maxDateTime is: 12/31/2154 23:59:59
    *** The maxDateTime after offset is: 1/1/2155 01:59:59 <--- maxDateTime > DateTimeUtil::maxValue() ???

    So for negative time zone offsets this:

    employmentEndDateTime = DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::maxValue(), DateTimeUtil::getUserPreferredTimeZone());
    newUpdateHcmEmployment(hcmEmployment, employmentStartDateTime, employmentEndDateTime);

    might work (in practice), but for positive offsets I’m not so sure (at least period overlap check in updateHcmEmployment will fail).

    Is it really necessary to adjust time zone offset when employmentEndDateTime == maxValue()?

    Thanks.

    ReplyDelete
    Replies
    1. Yes Keshav you are right. when using MaxDateTime you must not use offset, as it will add depends on time zone it will add hours for that.I changed in my environement but not in blog sorry for that.

      Delete
  10. Hi krishna i am working on ax2012r2 and i used the above method to create new employee but found an error stating that cannot create records in hcmemployment as end date is not greater than start date. What might be the reason for this?

    ReplyDelete

Thanks for visiting my blog,
I will reply for your comment within 48 hours.

Thanks,
krishna.