Poniżej podaję sposób na uniwersalny eksport dowolnej tabeli (pliku DBF) do pliku tekstowego. Niestety jego mankamentem jest zdecydowanie mniejsza wydajność w stosunku do natywnego copy.
procedure Export parameters p_FName, p_Sprtr && nazwa pliku, separator private all like l_* l_cnt=fcount() && ustalamy liczbę kolumn tabeli l_EOLn=Chr(13)+Chr(10) && zmienna przechowująca znaki końca wiersza i przejścia do nowej linii set textmerge on to (p_FName) noshow && włączenie łączenia tekstu do pliku scan for l_=1 to l_cnt-1 && przetwórz wszystkie kolumny oprócz ostatniej \\<<Eval(Field(l_))>><<p_Sprtr>> endfor && teraz jeszcze ostatnia kolumna i wstawienie końca wiersza \\<Eval(Field(l_cnt))>><<l_EOLn>> endscan set textmerge to && wyłączenie łączenia tekstu do pliku return
Jeśli procedura zakończy swoje działanie prawidłowo, zwraca wartość .T., w przeciwnym wypadku .F.
procedure Convert parameters p_FName, p_SrcCP, p_DstCP private all like l_* && jeśli plik nie istnieje lub typy parametrów nie są zgodne if not file(p_FName) or type('p_DstCP')<>type('p_SrcCP') return .F. && zakończ informując o niepowodzeniu endif. l_native=type('p_SrcCP')='N' && czy konwersja ma się odbyć za pomocą CPConvert? && jeśli parametry konwersji zawierając ciągi znaków porównaj ich długości if not l_native and len(p_SrcCP)<>len(p_DstCP) return .F. && zakończ informując o niepowodzeniu endif l_file=FOpen(p_FName,2) && otwórz plik w trybie do odczytu i zapisu do while .T. l_buf=fread(l_file,2048) && odczytaj 2kB z pliku l_len=len(l_buf) if l_len=0 && jeśli bufor jest pusty - nie ma już co konwertować exit && opuść pętle endif =fseek(l_file,-l_len,1) && cofnij się o długość bufora if l_native l_buf=CPConvert(p_SrcCP,p_DstCP,l_buf) && dokonaj konwersji natywną funkcją FoxPro else l_buf=ChrTran(l_buf,p_SrcCP,p_DstCP) && dokona konwersji wg ciągów znaków endif =fwrite(l_file,l_buf) && zapisz zmodyfikowany bufor enddo =fclose(l_file) return .T.
Prezentowane tutaj rozwiązanie demonstruje jak doprowadzić do
współpracy programów Subiekt i Word, w celu uzyskania wydruku
etykiet wysokiej jakości.
Jeśli dodatkowo Subiekt uruchamiany jest pod kontrolą Windows
(w oknie MS-Dos - także w trybie pełnoekranowym) przekaże on
automatycznie sterowanie do programu Word.
Aby współpraca obu programów była poprawna, na komputerze należy
zainstalować Worda co najmniej w wersji 97 SR-1
(patrz menu Worda: Pomoc -> Microsoft Word - informacje),
wraz ze sterownikiem ODBC dla plików tekstowych.
W przeciwnym wypadku Word podczas otwierania dokumentu METKI.DOC
będzie komunikował o niemożliwości odnalezienia źródła danych
korespondecji seryjnej.
Przykładowe etykiety zawierają następujące informacje o towarze: nazwa towaru, cena (zgodna z ceną ustaloną dla kasy fiskalnej z poziomu Administrator -> ECR/POS -> Różne -> Cena sprzedaży), kod kreskowy, kod dostawcy oraz przykładowe logo (logo witryny Subiektyw). Wygląd etykiet zawarty jest w pliku METKI.DOC, który jest dokumentem korespondencji seryjnej edytora Word (szablonem). Zmianę wyglądu etykiem można uzyskać poprzez modyfikację jego zawartości.
Poniżej przedstawiony jest kod źródłowy programu przygotowującej dane dla programu Word. Jego działanie polega na utworzeniu pliku tekstowego ETYKIETY.TXT - źródła korespondencji seryjnej dokumentu METKI.DOC, zawierającego wspomniane wyżej informacje o towarze, oddzielone od siebie średnikiem i umieszczone w kolejnych liniach pliku oprócz pierwszej, która zawiera informacje nagłówkowe tj. nazwy pól, wg których METKI.DOC umieszcza dane na etykietach.
Po utworzeniu pliku ETYKIETY.TXT i umieszczeniu go w katalogu SUBIEKT4\SYSTEM program wywołuje program Word:
start winword metki /mDoDok
i przekazuje sterowanie do niego. Katalogiem roboczym Worda jest
SUBIEKT4\SYSTEM i tam dokument korespondencji seryjnej
szuka źródła danych (ETYKIETY.TXT). Umieszczone w METKI.DOC
makro DoDok tworzy dodatkowy dokument programu Word,
który można albo zapisać albo wydrukować.
METKI.DOC korzysta z zewnętrznego pliku LOGO.JPG zawierającego
obrazek, który jest drukowany na każdej etykiecie. Musi być on
umieszczony w tym samym katalogu. Zatem w przypadku chęci zapisania
wygenerowanego dokumentu na dysk należy pamiętać, by do tego samego
folderu został także skopiowany plik LOGO.JPG.
Oczywiście METKI.DOC można dowolnie modyfikować, lub zastąpić go
własnym (należy stworzyć w nim makro DoDok lub przekopiować je
z METKI.DOC).
Uwaga !!! W trakcie uruchamiania programu Word może pojawić się następujący komunikat (o ile mamy włączoną opcję ostrzeganie o makrach w dokumentach Worda, co gorąco polecam):
Nie należy się jednak obawiać, dokument nie zawiera wirusa, a jedynie
makro generujące dokument z etykietami gotowymi do wydruku.
Można więc śmiało użyć przycisku Włącz makra.
Nieufni mogą (równie dobrze) kazać wyłączyć makra, z tym że później
będą musieli użyć przycisków korespondencji seryjnej, by uzyskać wydruk.
Jeśli z jakiś przyczyn nie jest możliwe uruchamianie Subiekta pod kontrolą Windows, można uzyskać wydruk w inny sposób. Podczas pracy z Subiektem należy jedynie wybrać produkty i po uzyskaniu komunikatu o przygotowaniu etykiet do wydruku zakończyć pracę z nim pracę. Następnie należy uruchomić system Windows, na Pulpicie utworzyć skrót do programu Word, który uruchomi go z dwoma parametrami: metki.doc /mDoDok. Katalogiem roboczym skrótu powinien być: SUBIEKT4\SYSTEM. Powyższy skrót pozwoli uzyskać przygotowany wcześniej wydruk.
Aby móc skorzystać z opisywanego rozwiązania, jak również mieć
możliwość modyfikacji dokumentu korespondencji seryjnej METKI.DOC
zapoznaj się z warunkami użytkowania.
Pliki: METKI.DOC oraz LOGO.JPG można pobrać z
Ładowni
Poniżej źródło modułu:
procedure Etykiety private all like l_* if not (s4open('towary') and s4open('ecr_sys')) do s4message with 'Nie można otworzyć bazy danych' return endif store '' to l_wyb, l_dane if s4rpprms(" Wybierz towary do etykiet ","T:'','l_wyb','l_dane'") select ecr_sys go top if wykonaj(l_wyb, l_dane,; 'tw_cena'+iif(between(es_cena,1,3),Str(es_cena,1),'2')+'b') do s4wait with 1 run 'start winword metki /mDoDok' do s4wait do s4message with "Towary zostały przygotowane do wydruku etykiet." else do s4message with "Nie wybrano żadnych towarów" endif endif =s4close('ecr_sys') return procedure Wykonaj parameters p_wyb, p_dane, p_pole private all like l_* do s4wait with 3 do case case p_wyb <> 3 select StrTran(tw_nazwa,'.','. ') as nazwa, tw_kodpask as barkod,; tw_dostkod as symbol, &p_pole as cena; from towary; where (p_wyb = 1 OR (p_wyb = 2 AND tw_grupa = p_dane)); and tw_status=' '; order by tw_nazwa; into cursor C1 otherwise if reccount(p_dane)>0 select StrTran(tw_nazwa,'.','. ') as nazwa, tw_kodpask as barkod,; tw_dostkod as symbol, &p_pole as cena; from towary; where tw_id in (select tw_id from &p_dane); order by tw_nazwa; into cursor C1 endif endcase l_druk=_tally>0 if l_druk do Zapisz with 'ETYKIETY.TXT' endif use in (p_dane) use in C1 do s4wait return l_druk procedure Zapisz parameters p_FName set textmerge on to (p_FName) noshow \\Nazwa;Barkod;Symbol;Cena scan \"<<AllTrim(nazwa)>>";"<<AllTrim(barkod)>>";"<<AllTrim(symbol)>>";<<cena>> endscan set textmerge to return
Mniej powszechna jest natomiast wiedza, że liczba owych - dostępnych kolumn może być dowolnie zwiększana. Aby tego dokonać należy dysponować jakimkolwiek edytorem baz danych, który pozwala dodawać rekordy do tabel DBF oraz dokumentacją techniczną opisującą format tabel używanych przez Subiekta (przede wszystkim tabeli TOWARY.DBF, w której przechowywane są informacje o towarach). Dokumetacja ta znajduje się na stronach producenta Subiekta - firmy Insert (www.insert.com.pl))
Następnie wystarczy zmodyfikować tabelę TFLDS umieszczoną w katalogu SUBIEKT4\SYSTEM, dodając na jej końcu pusty rekord i wypełnić przynajmniej dwa jego pola: TF_OPIS i TF_NAME. Pierwsze definiuje nazwę kolumny (jej nagłówek), drugie jej zawartość. Poniżej podaję przykład na uzyskanie dodatkowej kolumny o nazwie Narzut 1 zawierającej wartość narzutu dla pierwszej ceny towaru (domyślnie cena hurtowa).
TF_OPIS: Narzut 1
Jak się okazuje zawartość pola TF_NAME jest traktowana przez Subiekta
w sposób szczególny tzn. tak jakby to był fragment jego własnego kodu
(a właściwie wyrażenia matematycznego). Mamy zatem de facto możliwość
zmodyfikowania zachowania się Subiekta. Najprostszą konsekwencją tego
faktu jest możliwość uzyskania podwójnej kolumny np. zawierającej cenę
i stan towaru (dzięki czemu pozostałe trzy mogą zawierać np. nazwę towaru,
kod towaru i kod dostawcy). W ten sposób uzyskamy jak gdyby 5 kolumn na
liście towarów.
A oto sposób na uzyskanie takiego efektu:
Zawarty w polu TF_NAME fragment kodu realizuje przetworzenie na tekst liczby zawartej w polu TW_CENA1B tabeli TOWARY (będzie ona zajmować 8 znaków z czego 2 znaki zajmie wartość groszy) oraz przetworzenie na tekst wartości zapasu towaru przechowywanej w tabeli STAN (ponieważ domyślną tabelą jest TOWARY, to pole ST_STAN musi być poprzedzone nazwą tabeli, która go zawiera). Obydwa teksty są następnie łączone ze sobą, a zapas towaru jest dodatkowo otaczany nawiasami. Wszystko to przyporządkowywane jest zmiennej CenaStan, co jest niezbędne do prawidłowego zaprezentowania wartości wyrażenia (tylko zmienne mogą być wyświetlane, w poprzednim przykładzie TW_NARZUT1 też było zmienną). Oczywiście użycie pola z innej tabeli jest możliwe tylko dlatego, że Subiekt podczas wyświetlania listy towarów wiąże ze sobą te dwie tabele (dzięki temu danemu towarowi odpowiada jego stan).
Wiedząc, że TF_NAME zawiera kod wyrażenia, możemy pójść jeszcze dalej i wykorzystać np. funkcję FoxPro:
iif(warunek, ta_wartość_gdy_warunek_prawdziwy, ta_wartość_gdy_warunek_fałszywy)
która pozwoli nam np. wyświetlić na liście towarów cenę zależną od aktualnie wybranego magazynu. Jeśli będzie to magazyn pierwszy, wówczas będzie to pierwsza cena, w przeciwnym wypadku cena druga.
TF_OPIS: CenaWgMagponieważ pierwszy parametr funkcji iff to także wyrażenie, możemy więc wstawić tam również funkcję. Oto sposób na uzyskanie dwóch różnych cen w zależności od stanu klawisza Num Lock, jeśli jest włączony wówczas widoczna jest cena pierwsza, gdy wyłączony cena 2. Efekt jest widoczny od razu na liście, pod warunkiem, że przewiniemy ją w dół lub w górę.
TF_OPIS: CenaWgMagZwieńczeniem powyższych rozważań jest użycie jako wyrażenia, funkcji napisanej w FoxPro. Niech będzie to funkcja pozwalająca dowolnie zmieniać zawartość kolumny, wypełniając ją np. wybraną ceną sprzedaży. Tym razem pola tabeli TFLDS.DBF będą miały następującą postać:
TF_OPIS: CenaFunkcja Pole ma następującą postać:
function Pole if Empty(On('key','F12')) && jeśli nie zaprogramowano dotąd klawisza F12 do Ini && dokonaj tego i zainicjuj wybór zawartości kolumny endif return Eval(ps_Pole) && zwróć domyślną zawartość kolumny procedure Wybor private all like l_* push key clear && zapamiętaj przyporządkowania klawiszy i wyczyść je define popup WyborM from 0,65 shadow margin define bar 1 of WyborM prompt 'Cena 1 N' && przykładowa lista define bar 2 of WyborM prompt 'Cena 2 N' && pól do wyboru define bar 3 of WyborM prompt 'Cena 3 N' define bar 4 of WyborM prompt 'Cena 1 B' define bar 5 of WyborM prompt 'Cena 2 B' define bar 6 of WyborM prompt 'Cena 3 B' define bar 7 of WyborM prompt 'Narzut 1' define bar 8 of WyborM prompt 'Narzut 2' define bar 9 of WyborM prompt 'Narzut 3' on selection popup WyborM do Zmien && w przypadku wyboru uruchom Zmien activate popup WyborM && wyświetl menu release popup WyborM && i usuń je po opuszczeniu pop key && przywróć poprzednie przyporządkowanie klawiszy return procedure Zmien && po wybraniu opcji w menu ps_Pole='tw_'+ChrTran(prompt(),' ','') && ustal nową zawartość kolumny keyboard '{ESC}{Ctrl+Q}' && wyjdź z menu i zmień ustawienia browse return && powyższe działa tylko, gdy set s4buforkl=2 (co najmniej) procedure Ini && Inicjowanie mechanizmu wyboru zawartości kolumny on key label F12 do Wybor in Pole && gdy F12 wykonaj Wybor z POLE.FXP if type('ps_Pole')='U' && jeśli do tej pory nie zdefiniowano zmiennej public ps_Pole && uczyń ją zmienną publiczną ps_Pole='tw_cena1b' && i zainicjuj domyślną wartością endif return
Powyższą funkcję po skompilowaniu do postaci POLE.FXP (docelowo poleceniem compile pole nodebug - zajmuje mniej pamięci) należy umieścić w katalogu SUBIEKT4\SYSTEM, tam bowiem będzie jej szukał Subiekt.
Aby skorzystać z powyższego rozwiązania zapoznaj się z warunkami użytkowania.
Gotowa do użycia funkcja POLE.FXP znajduje się w Ładowni
Czy jest to jednak jedyne miejsce ? Na pierwszy rzut oka może się wydawać, że tak, albowiem nigdzie indziej firma Insert nie udostępniła takiej możliwości. Jednakże każdy w miarę zorientowany programista FoxPro szybko znajdzie zastosowanie dla polecenia on key label i po przyporządkowaniu do dowolnego klawisza swojej procedury, będzie mógł ją wywoływać ze zdecydowanie większej ilości miejsc programu (choć nie wszędzie, co wynika z idącego w parze z powyższym poleceniem, polecenia push key clear).
Niestety aby móc przyporządkować procedurę pod dany klawisz niezbędne jest wydanie polecenia on key label. Sprowadza się to zatem do uruchomienia jakiegoś zestawienia własnego, a to na dłuższą metę mija się z celem.
Najlepiej, gdyby Subiekt sam wykonywał za nas to polecenie (lub grupę poleceń). Ujmując rzecz inaczej, wspaniale byłoby, gdyby posiadał coś na kształ pliku AUTOEXEC.BAT z systemu Dos lub folderu AutoStart systemu Windows. Niestety autorzy programu z naturalnych względów nie przewidzieli takiej potrzeby.
Czy oznacza to, że nie ma szans na uzyskanie funkcjonalności AutoExec w Subiekcie ? Okazuje się, że jest - muszę jednak przyznać, iż jej odnalezienie zajęło mi pół roku.
Pierwszą myślą jaka przyszła mi do głowy było to, że Subiekt jako program napisany w FoxPro powinien korzystać z pliku CONFIG.FP - być może w nim istnieje jakaś furtka pozwalająca zrealizować funkcję AutoExec. Subiekt rzeczywiście korzysta z tego pliku, zaś w systemie pomocy FoxPro znajdowała się informacja, że umieszczenie w CONFIG.FP linii z komendą COMMAND=ZrobTo.APP uruchomi program ZrobTo.APP. Okazało się jednak, że ta funkcjonalność FoxPro jest dostępna tylko podczas pracy w jego środowisku. Programy wykonywane poza nim ignorują powyższy wpis. To samo dotyczyło komendy SHELL. Wyglądało na to, że AutoExec w Subiekcie jest niemożliwy do uzyskania.
W związku z tym odłożyłem ten temat na później i co jakiś czas wracałem
do niego próbując różnych rozwiązań.
Właśnie podczas jednej z takich prób, kiedy testowałem wewnętrzne
zmienne FoxPro (te zaczynające się od podkreślenia) poprzez ich ustawianie
w CONFIG.FP, z systemu pomocy wyczytałem, że istnieje grupa czterech
zmiennych _GEN* umożliwiająca zmianę pewnych funkcjonalności
FoxPro (np. programu do generowania menu). Właśnie jedna z tych zmiennych
o nazwie _GENPD okazała się długo szukaną furtką. Wystarczyło tylko
umieścić ją w CONFIG.FP:
_GENPD=AUTOEXEC.FXP
aby uzyskać zamierzony efekt.
Jak się okazuje zmienna ta służy do określania nazwy sterownika drukarki,
którego FoxPro ma używać do wykonywania wydruków. Jest ona o tyle specyficzna,
że nadanie jej wartości (nazwy pliku sterownika), powoduje dodatkowo jego
uruchomienie w celach inicjalizacji. Jest to proces, który zachodzi zarówno
w środowisku FoxPro, jak również w programach zewnętrznych (a więc
i Subiekcie).
Ponieważ FoxPro traktuje AutoExec.FXP, jak sterownik drukarki, będzie go zatem
próbował wykorzystywać podczas drukowania. Dlatego zaraz po uruchomieniu,
procedura AutoExec powinna wyczyścić zmienną _GENPD:
procedure AutoExec parameters p_1,p_2 && wg dokumentacji dwa parametry _GENPD='' && anulowanie wpisu z CONFIG.FP - nas tu nie było&& tutaj dowolne dalsze operacje return
Wg dokumentacji, procedura sterownika powinna posiadać dwa parametry, stąd
choć nie są one potrzebne procedurze AutoExec muszą zostać zadeklarowane.
Należy zdawać sobie sprawę, że _GENPD jest wykonywane jeszcze w czasie prologu
FoxPro, w momencie, kiedy się ono inicjuje. Oznacza to, że Subiekt nie został
jeszcze w ogóle "zauważony", dopiero za chwilę, kiedy skończą się
czynności inicjalizacyjne zacznie być wykonywany kod Subiekta. W związku z
tym, próby wywoływania procedur Subiekta dostępnych w zestawieniach własnych,
mogą się nie powieść. Nic nie stoi natomiast na przeszkodzie, aby uruchomić
wspomnianą wyżej komendę on key label lub ustawić swoją własną zmienną.
Przy umiejętnym stosowaniu i znajomości kilku sztuczek można nawet
zmodyfikować menu Subiekta dodając do niego własną opcję.
Aby skorzystać z powyższego rozwiązania zapoznaj się z warunkami użytkowania.