środa, 4 grudnia 2013

Znaki specjalne, a wersja 2013.X

Jakiś czas po konwersji do wersji 2013 zauważyłem, iż niektóre towary przestały spełniać warunki pewnej filtracji, co do tej pory normalnie działało. Jak się okazało wersja 2013 ma nową funkcjonalność polegającą na wklejaniu w kod towaru znaku ASCII [US], czyli separatora.
Dla przykładu:
SELECT 'TEST1' AS [String1], 'TEST' + CHAR(31) + '2'  AS [String2]

da nam wynik 


co na pierwszy rzut oka wydaje się być poprawne i tak jest, tak długo jak zrobiliśmy to świadomie. W przypadku wprowadzania przez użytkowników kodów w kartotekach towarowych ten warunek niekoniecznie jest spełniony.

Dla pewności napisałem więc funkcję, która zamienia znaki specjalne na coś bardziej czytelniejszego:

CREATE FUNCTION [CDN].[PokazUkryteZnaki] (@tekst VARCHAR(8000))
RETURNS varchar(8000)
AS
BEGIN
   DECLARE @Wynik VARCHAR(8000);
   SET @Wynik = @tekst

   SET @Wynik = REPLACE( @Wynik, CHAR(1),  '[SOH]')
   SET @Wynik = REPLACE( @Wynik, CHAR(2),  '[STX]')
   SET @Wynik = REPLACE( @Wynik, CHAR(3),  '[ETX]')
   SET @Wynik = REPLACE( @Wynik, CHAR(4),  '[EOT]')
   SET @Wynik = REPLACE( @Wynik, CHAR(5),  '[ENQ]')
   SET @Wynik = REPLACE( @Wynik, CHAR(6),  '[ACK]')
   SET @Wynik = REPLACE( @Wynik, CHAR(7),  '[BEL]')
   SET @Wynik = REPLACE( @Wynik, CHAR(8),  '[BS]')
   SET @Wynik = REPLACE( @Wynik, CHAR(9),  '[HT]')
   SET @Wynik = REPLACE( @Wynik, CHAR(10), '[LF]')
   SET @Wynik = REPLACE( @Wynik, CHAR(11), '[VT]')
   SET @Wynik = REPLACE( @Wynik, CHAR(12), '[FF]')
   SET @Wynik = REPLACE( @Wynik, CHAR(13), '[CR]')
   SET @Wynik = REPLACE( @Wynik, CHAR(14), '[SO]')
   SET @Wynik = REPLACE( @Wynik, CHAR(15), '[SI]')
   SET @Wynik = REPLACE( @Wynik, CHAR(16), '[DLE]') 
   SET @Wynik = REPLACE( @Wynik, CHAR(17), '[DC1]')
   SET @Wynik = REPLACE( @Wynik, CHAR(18), '[DC2]')
   SET @Wynik = REPLACE( @Wynik, CHAR(19), '[DC3]')
   SET @Wynik = REPLACE( @Wynik, CHAR(20), '[DC4]')
   SET @Wynik = REPLACE( @Wynik, CHAR(21), '[NAK]')
   SET @Wynik = REPLACE( @Wynik, CHAR(22), '[SYN]')
   SET @Wynik = REPLACE( @Wynik, CHAR(23), '[ETB]')
   SET @Wynik = REPLACE( @Wynik, CHAR(24), '[CAN]')
   SET @Wynik = REPLACE( @Wynik, CHAR(25), '[EM]')
   SET @Wynik = REPLACE( @Wynik, CHAR(26), '[SUB]')
   SET @Wynik = REPLACE( @Wynik, CHAR(27), '[ESC]')
   SET @Wynik = REPLACE( @Wynik, CHAR(28), '[FS]')
   SET @Wynik = REPLACE( @Wynik, CHAR(29), '[GS]')
   SET @Wynik = REPLACE( @Wynik, CHAR(30), '[RS]')
   SET @Wynik = REPLACE( @Wynik, CHAR(31), '[US]')
   --SET @Wynik = REPLACE( @Wynik, CHAR(32), '[SP]')   -- SPACJA

   RETURN(@Wynik)
END
GO

W efekcie jej wywołania, nasze kody wyglądają już trochę inaczej:

Dla towarów można napisać coś takiego:

SELECT CDN.PokazUkryteZnaki(Twr_Kod) AS KodPelny,Twr_Kod AS Kod
FROM CDN.TwrKarty
WHERE Twr_Archiwalny = 0
AND CDN.PokazUkryteZnaki(Twr_Kod) <> Twr_Kod

Powyższe zapytanie spowoduje wyświetlenie listy towarów których kod zawiera niepożądany znak specjalny. Warto zauważyć, że w ciele funkcji wykomentowany jest fragment zamieniający "spację" na [SP], ponieważ w moim przypadku znak spacji jest dopuszczalny w kodzie towaru i nie było potrzeby wyszukiwania towarów, które ją zawierają. W bazie którą sprawdzałem zapytanie zwróciło skromne 70 kartotek, a wszystkie zostały zmodyfikowane po wgraniu wersji 2013.5 (nowy interface). Wychodzi więc na to, że nowa wersja XL-a nie ignoruje znaków specjalnych i użytkownik może wprowadzić co tylko wymyśli :)

Pozdrawiam,
Mateusz

czwartek, 28 lutego 2013

DataGridView Performance

Jak zapewne wiadomo, korzystanie ze standardowej kontrolki DataGridView dostępnej w .NET często wiąże się z problemami z jej wydajnością. Pomijając już kwestię zaawansowanej optymalizacji, czy też wyłączania efektów wizualnych (w końcu nie chodzi o to żeby coś było brzydkie) najprościej stworzyć klasę:
public static class ExtensionMethods
    {
        public static void DoubleBuffered(this DataGridView dgv, bool setting)
        {
            Type type = dgv.GetType();
            PropertyInfo pi = type.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
            pi.SetValue(dgv, setting, null);
        }
    }

Następnie w konstruktorze, po inicjalizacji kontrolek, umieszczamy taki kawałek kodu:
ExtensionMethods.DoubleBuffered(MyGridName, true);

Jak można się domyślać po "DoubleBuffered", nasz DataGridView powinien teraz działać znacznie szybciej :)

Pozdrawiam

środa, 30 stycznia 2013

Reaktywacja

Minęło sporo czasu od ostatniego wpisu, jako że po prostu zaniedbałem to co niegdyś zacząłem. Niemniej jednak postaram się ożywić ten blog i mam nadzieje, że poruszę kilka interesujących tematów.

Dzisiejszy wpis to coś niezbyt trudnego, związanego z problemem z którym borykał się jeden ze znajomych użytkowników XL-a. Idea była taka, aby podczas wywoływania wydruku faktury pokazywał się parametr "opis" z domyślną wartością wziętą z opisu tego konkretnego dokumentu FS. Problemem jest fakt, że w momencie wywołania wydruku nie ma jak się dobrać do GID-u faktury na której stoimy (pomijam doklejanie do filtra obowiązkowego czegoś na zasadzie 1=1, gdzie jedynki to GID-y, które potem można powycinać). W tym przypadku z pomocą może nam przyjść pole TrN_Aktywny, dzięki któremu możemy stworzyć parametr na zasadzie poniższego przykładu:

@PAR ?@S255|Opis|&Opis:{GetSQL('SELECT TnO_Opis FROM CDN.TraNag INNER JOIN CDN.TrNOpisy ON CDN.TraNag.TrN_GIDNumer=CDN.TrNOpisy.TnO_TrnNumer INNER JOIN CDN.Sesje ON CDN.TraNag.TrN_Aktywny=CDN.Sesje.SES_SesjaID AND SES_OpeIdent = '''& XLOpeIdent() &'''')} @? PAR@

Coś takiego spowoduje, że na oknie z parametrami, ten zdefiniowany powyżej będzie mieć domyślną wartość pobraną z opisu faktury, na której właśnie stoimy (Warto zaznaczyć, że w przypadku otwarcia kilku faktur jednocześnie przez jednego usera, warto się zabezpieczyć jakąś funkcją agregującą i pamiętać o tym, iż będzie to tylko ochrona przed błędem, a nie zwracanie dobrej wartości). Sposób ten jest o tyle fajny, że pozwala nam w parametrach wstawiać domyślne wartości pobrane z dowolnego miejsca w bazie które jest powiązane z fakturą na której użytkownik się znajduje (np. wartość atrybutu, który jest wypełniony na tej fakturze).

Dziękuje za zainteresowanie i do "poczytania" wkrótce :)