piątek, 30 marca 2012

Inny sposób dostępu do zewnętrznego źródła danych

Oto inny sposób dostępu do zewnętrznego źródła danych z poziomu systemu Dynamics AX.

W przypadku uruchamiania przykładu, proszę o sprawdzenia
  1. connection string do bazy danych
  2. uprawnienia do zewnętrznego źródła danych

  
static void SBR_TestSQLPrice(Args _args)
{
    System.Data.SqlClient.SqlConnection             sqlConn;
    System.Data.SqlClient.SqlDataReader             sqlDataReader;
    System.Data.SqlClient.SqlCommand                sqlCommand;
    System.Data.SqlClient.SqlParameterCollection    sqlParm;

    AccountNum                              accountNum;
    Name                                    name;
    CustGroupId                             custGroupId;
    ;
    try
    {
        sqlConn = new System.Data.SqlClient.SqlConnection('Server=TEST2-AXAPTA2009;Trusted_Connection=YES;Database=OPT_AX2009_DEV_LIVE;Connection timeout=5');
        sqlConn.Open();

        sqlCommand = new System.Data.SqlClient.SqlCommand('Select AccountNum, Name, CustGroup from CustTable Where DataAreaId = @Parm', sqlConn);

        sqlParm = sqlCommand.get_Parameters();
        sqlParm.AddWithValue('@Parm', curExt() );

        sqlDataReader = sqlCommand.ExecuteReader();

        while (sqlDataReader.Read())
        {
            accountNum  = sqlDataReader.GetString( sqlDataReader.GetOrdinal('AccountNum'));
            name        = sqlDataReader.GetString( sqlDataReader.GetOrdinal('Name'));
            custGroupId = sqlDataReader.GetString( sqlDataReader.GetOrdinal('CustGroup'));

            info(strFmt('%1 - %2 / %3', accountNum, name, custGroupId ) );
        }

        sqlDataReader.Close();
        sqlConn.Close();
    }
    catch (Exception::CLRError)
    {
        error(AifUtil::getClrErrorMessage());
    }
}

wtorek, 20 marca 2012

Logowanie wyjątków .NET do bazy danych

Aby w bardzo prosty sposób przechwycić wyjątek powstały w zewnętrznej bibliotece .NET można użyć metody AifUtil::getClrErrorMessage();.
Kod tej metody jest bardzo prosty i pokazuje jak obsługiwać wyjątki .NET po stronie systemu Dynamics AX.

Przykład użycia ww. metody:
  
static void SBR_AifInfoLog_Example_NET(Args _args)
{
    SysExceptionLog         exceptionLog;
    System.Uri              uri;
    System.Net.WebRequest   webRequest;
    System.Net.WebResponse  webResponse;
    str                     clrError;
    ;
    
    try
    {
        // kod który zwraca wyjątek
        // - pod podanym adresem nie ma serwera WWW
        // - nastąpi TimeOut po 5 sekundach
        uri = new System.Uri('http://192.168.1.100/');
        webRequest = System.Net.WebRequest::Create(uri);
        webRequest.set_Timeout(5000);
        webResponse = webRequest.GetResponse();
        webResponse.Close();
    }
    catch (Exception::CLRError)
    {
        // zapisujemy wyjątek do bazy danych
        clrError = AifUtil::getClrErrorMessage();
        exceptionLog = new SysExceptionLog();
        exceptionLog.writeEntry(Exception::Error, clrError, 'NET_DEMO');

        // wyświetlamy błąd na ekranie
        error(clrError);
    }
}

Aby zobaczyć zalogowane wyjątki uruchamiamy menu item \Menu Items\Display\Exceptions i uruchamiamy formatkę gdzie mamy listę przechwyconych wyjątków.

Logowanie wyjątków do bazy danych

Bardzo często użytkownik zgłasza problem w stylu:
"Panie Sebastianie, wyskoczył błąd i się nie zaksięgowało, proszę o pomoc."
Oczywiście nie ma komunikatu błędu i większość czasu trzeba teraz poświęcić na to co było robione a nie na poprawę mechanizmu/danych.

Dobrym rozwiązaniem jest zapisywanie pewnych wyjątków w bazie danych, aby później można było je przeglądać.
Oczywiście można pisać swoje różne mechanizmy, ale pokażę jak to zrobić bardzo prosto (ale wymagana jest licencja na AIF).
Moduł AIF (Aplication Integration Framework) poza swoją funkcjonalnością udostępnia kilka ciekawych klas.
W tym poście pokażę jak wykorzystać dwie z nich AifInfoLog oraz SysExceptionLog.


Mamy kawałek kodu (w naszym przypadku potwierdzenie listy pobrania) i chcemy zalogować wyjątek jeśli wystąpi:

  
static void SBR_AifInfoLog_Example(Args _args)
{
    AifInfoLog      aifInfoLog;
    SysExceptionLog exceptionLog;
    ;
    aifInfoLog = new AifInfoLog();

    try
    {
        // resetujmy zapamiętane komunikaty
        aifInfoLog.reset();

        // kod który zwraca wyjątek
        WMSOrder::find('00000081_068').finish();
    }
    catch
    {
        // zapisujemy wyjątek do bazy danych
        exceptionLog = new SysExceptionLog();
        exceptionLog.writeInfoLogData('SBR_TEST', aifInfoLog.getInfoLogData());
    }
}

Aby zobaczyć zalogowane wyjątki uruchamiamy menu item \Menu Items\Display\Exceptions i uruchamiamy formatkę gdzie mamy listę przechwyconych wyjątków.

czwartek, 15 marca 2012

Drukowanie na raporcie bitmap pobieranych z internetu

Jest to moja metoda drukowania zdjęć na raportach które znajdują się w sieci (dostęp przez protokół HTTP).
Nie jest ona zbyt elegancka (przesyłanie przez plik) ale daje radę.

Dodajemy nową metodę do raportu:
void loadImage(ReportBitmapControl _imageControl, str _url)
{
    Str                     fileName;
    System.Net.WebRequest   webRequest;
    System.Net.WebResponse  webResponse;
    System.Uri              uri;
    System.IO.Stream        stream;
    System.Drawing.Image    img;
    ;

    fileName = System.IO.Path::GetTempFileName();

    uri = new System.Uri(_url);

    webRequest = System.Net.WebRequest::Create(uri);
    webRequest.set_UseDefaultCredentials(true);

    webResponse = webRequest.GetResponse();

    stream = webResponse.GetResponseStream();

    img = System.Drawing.Image::FromStream(stream);
    img.Save(fileName);

    stream.Close();
    webResponse.Close();

    _imageControl.imageName(fileName);
}

i dodajemy jej wywołanie:
public void executeSection()
{
    ;
    this.loadImage(imageControl, 'http://www.likeyou.me/wp-content/uploads/2012/01/spongeBob.jpg');
    super();
}

środa, 7 marca 2012

Wydajne tworzenie plików Excel w .Net

Aby szybko i wydajnie stworzyć plik XLSX z kodu polecam użycie biblioteki EEPlus.
Wykorzystałem ją aby z poziomu serwisu WCF tworzyć wiele (kilka tysięcy) plików Excel jednocześnie.


Przykład kodu generującego prosty plik XLSX:
            
            FileInfo newFile = new FileInfo(@"c:\temp\sample1.xlsx");
            
            if (newFile.Exists)
            {
                newFile.Delete();  // ensures we create a new workbook
                newFile = new FileInfo(@"c:\temp\sample1.xlsx");
            }
            
            using (ExcelPackage package = new ExcelPackage(newFile))
            {
                // add a new worksheet to the empty workbook
                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Inventory");
                //Add the headers
                worksheet.Cells[1, 1].Value = "ID";
                worksheet.Cells[1, 2].Value = "Product";
                worksheet.Cells[1, 3].Value = "Quantity";
                worksheet.Cells[1, 4].Value = "Price";
                worksheet.Cells[1, 5].Value = "Value";

                //Add some items...
                worksheet.Cells["A2"].Value = 12001;
                worksheet.Cells["B2"].Value = "Nails";
                worksheet.Cells["C2"].Value = 37;
                worksheet.Cells["D2"].Value = 3.99;

                worksheet.Cells["A3"].Value = 12002;
                worksheet.Cells["B3"].Value = "Hammer";
                worksheet.Cells["C3"].Value = 5;
                worksheet.Cells["D3"].Value = 12.10;

                worksheet.Cells["A4"].Value = 12003;
                worksheet.Cells["B4"].Value = "Saw";
                worksheet.Cells["C4"].Value = 12;
                worksheet.Cells["D4"].Value = 15.37;

                //Add a formula for the value-column
                worksheet.Cells["E2:E4"].Formula = "C2*D2";

                //Ok now format the values;
                using (var range = worksheet.Cells[1, 1, 1, 5])
                {
                    range.Style.Font.Bold = true;
                    range.Style.Fill.PatternType = ExcelFillStyle.Solid;
                    range.Style.Fill.BackgroundColor.SetColor(Color.DarkBlue);
                    range.Style.Font.Color.SetColor(Color.White);
                }

                worksheet.Cells["A5:E5"].Style.Border.Top.Style = ExcelBorderStyle.Thin;
                worksheet.Cells["A5:E5"].Style.Font.Bold = true;

                worksheet.Cells[5, 3, 5, 5].Formula = string.Format("SUBTOTAL(9,{0})", new ExcelAddress(2, 3, 4, 3).Address);
                worksheet.Cells["C2:C5"].Style.Numberformat.Format = "#,##0";
                worksheet.Cells["D2:E5"].Style.Numberformat.Format = "#,##0.00";

                //Create an autofilter for the range
                worksheet.Cells["A1:E4"].AutoFilter = true;

                worksheet.Cells["A2:A4"].Style.Numberformat.Format = "@";   //Format as text
                worksheet.Cells.AutoFitColumns(0);  //Autofit columns for all cells

                // lets set the header text 
                worksheet.HeaderFooter.OddHeader.CenteredText = "&24&U&\"Arial,Regular Bold\" Inventory";

                // add the page number to the footer plus the total number of pages
                worksheet.HeaderFooter.OddFooter.RightAlignedText =
                    string.Format("Page {0} of {1}", ExcelHeaderFooter.PageNumber, ExcelHeaderFooter.NumberOfPages);
                
                // add the sheet name to the footer
                worksheet.HeaderFooter.OddFooter.CenteredText = ExcelHeaderFooter.SheetName;
                
                // add the file path to the footer
                worksheet.HeaderFooter.OddFooter.LeftAlignedText = ExcelHeaderFooter.FilePath + ExcelHeaderFooter.FileName;

                worksheet.PrinterSettings.RepeatRows = worksheet.Cells["1:2"];
                worksheet.PrinterSettings.RepeatColumns = worksheet.Cells["A:G"];

                // Change the sheet view to show it in page layout mode
                worksheet.View.PageLayoutView = true;

                // set some document properties
                package.Workbook.Properties.Title = "Invertory";
                package.Workbook.Properties.Author = "Jan Källman";
                package.Workbook.Properties.Comments = "This sample demonstrates how to create an Excel 2007 workbook using EPPlus";

                // set some extended property values
                package.Workbook.Properties.Company = "AdventureWorks Inc.";

                // set some custom property values
                package.Workbook.Properties.SetCustomPropertyValue("Checked by", "Jan Källman");
                package.Workbook.Properties.SetCustomPropertyValue("AssemblyName", "EPPlus");

                // save our new workbook and we are done!
                package.Save();
            }


I wynik działania kodu: