Przejdź do głównej treści

Własne moduły


System Ready_™ umożliwia tworzenie własnych modułów, które są oparte na definicji XML, widoku (raport) oraz przyciskach akcji (toolbar).

Aby moduł był poprawny, musi zawierać przynajmniej jedną zakładkę.

W niniejszej dokumentacji wykorzystany zostanie przykład plik o nazwie EmployeeBriefcase.xml w folderze edokumenty/var/cfg/CustomModules/, gdzie nazwa pliku (EmployeeBriefcase) to identyfikator modułu.

Następnie do pliku wklejamy poniższy kod.

Wygląd
custom-module-with-one-tab
custom-module-with-one-tab

Wskazówka

Pierwszy poziom zakładek (jeżeli jest ich więcej niż jedna) /modules/module/tabs/tab/tabs/tab zostaną wyświetlane w pionie.

Jest to zalecany przez guideline sposób prezentacji zakładek dla aplikacji i custom modułów.

Parametry definicji modułu

  • label - tytuł zakładki. Sugeruje się ograniczenie do 1 słowa.

  • rep_id - (zastępowane przez phpRender) id raportu, który ma się wyświetlić. Więcej informacji w raportach.

  • phpRender - (zastępowane przez rep_id) przyjmuje nazwę klasy z widokiem. Informacje o phpRender.

  • grp_id - identyfikatory grup użytkowników, dla których zakładka będzie widoczna, np. grp_id="2,5,10".

  • def_tab - znacznik wskazujący, czy zakładka domyślnie ma się pokazywać na pasku zakładek, czy też ma być widoczna dopiero po kliknięciu linku więcej.

    Uwaga!

    Atrybut def_tab dla zakładki działa wyłącznie dla własnych modułów. Nie działa on dla dodatkowych zakładek na kartotekach.

  • forceOpen - wymusza otwarcie zakładki jako pierwszej. Przyjmuje wartość 0 lub 1. Atrybut ten można ustawić tylko dla 1 zakładki.

  • prior - ustawienie na odpowiedniej kolejności zakładek. Parametr przyjmuje wartość od 0 do n gdzie n <= liczbie zakładek. Atrybut ten w połączeniu z forceOpen umożliwia nam dodanie nowej domyślnie otwartej zakładki na pierwszej pozycji w kartotece.

  • showFilters - zwijanie panelu z filtrami, domyślnie panel jest zwinięty.
    Przyjmuje wartość 0 lub 1.

  • iframe - url do strony poza systemem, którą chcemy wyświetlić w zakładce. alwaysVisible - czy zakładka ma być zawsze widoczna. Przyjmuje wartość 0 lub 1.

  • right - prawa systemowe do wyświetlenia zakładki. Informacje jak dodać swoje prawa znajdziesz tutaj

  • filtersMode - za pomocą tego atrybutu można zmieniać położenie panela filtrów. Standardowo panel filtrów widoczny jest po lewej stronie (wartość atrybutu filtersMode = "left"). Dla wartości "right_abs" panel filtrów pojawi się z prawej strony i będzie on nachodził na listę umieszczoną w module.

  • visible - wskazuje, czy dany moduł ma być widoczny. Nie w każdym przypadku moduł powinien być widoczny. Czasami celem istnienia modułu jest bycie uruchomionym poprzez kliknięcie z innego miejsca w aplikacji, np. z Ustawień Aplikacji.

    Uwaga!

    Atrybut visible dla zakładki działa wyłącznie dla własnych modułów. Nie działa on dla dodatkowych zakładek na kartotekach.

    Przykład ukrytego modułu:

    <module id="ERPExchangeConfigurationTokens" name="Tokeny definicji" label="Tokeny definicji" icon="service.svg" color="#327DE6" visible="false" right="">...</module>
    

    lub inaczej:

    <module id="ERPExchangeConfigurationTokens" name="Tokeny definicji" label="Tokeny definicji" icon="service.svg" color="#327DE6" visible="0" right="">...</module>
    

    Obie postaci są prawidłowe.

    W przypadku, gdy widoczność zakładki modułu ma być uzależniona od ustawienia globalnego (w tabeli global_sys_conf), można wykorzystać funkcję getGlobalSystemConfig, która pobierze wartość logiczną (0,1). Zmienna wykorzystywana w tej funkcji musi mieć wartości 0 (FAŁSZ) lub 1 (PRAWDA) Parametrami tej funkcji są pola z ww. tabeli:

    • objnam - symbol aplikacji w global_sys_conf, np #FK#
    • varnam - nazwa zmiennej global_sys_conf, np #SHOW.OLD.COST.ANALYSIS.TAB#
<tab id="cost_analysis" visible="{getGlobalSystemConfig(#FK#,#SHOW.OLD.COST.ANALYSIS.TAB#)}" label="{translate(#ANALIZA KOSZTÓW#)}" licenceTypeAccessRule="Allow:REGULAR" rep_id="1802"  groupName="{translate(#ANALIZY#)}" showFilters="0">
  • groupName - pozwala na grupowanie zakładek. Przykładowy kod takiego atrybutu (z funkcją tłumaczenia):
<tab label="{translate(#Szyna#)} Azure" showFilters="0" groupName="{translate(#ADMINISTRACJA#)}"
     phpRender="ReadyApp\KSEF\View\Module\EventLog" right="bswfms.settings.level2">...</tab>
  • licenceTypeAccessRule - wskazuje, jakiego rodzaju użytkownicy mają możliwość zobaczenia zakładki modułu. Wyróżnia się następujące rodzaje:

    • REGULAR - Użytkownicy standardowi systemu,
    • PORTAL - Użytkownicy portalu

    Przykład definicji typu licencji:

    <tab label="{translate(#Moje#)}" rep_id="2042" licenceTypeAccessRule="Allow:REGULAR,PORTAL">...</tab>
    

Dodanie prawa systemowego dostępu do modułu

Kolejnym etapem jest dodanie prawa systemowego.

Prawa systemowe

Dostęp do pewnej części aplikacji lub wykonanie jakieś akcji możemy ograniczyć użytkownikowi, za pomocą prawa systemowego.

Przykład tworzenia prawa dla własnego modułu

Aby utworzyć nowe prawo:

  1. Wchodzimy w Panel Sterowania > Definicje uprawnień.
Panel sterowania
Panel sterowania
  1. Zaznaczamy element drzewa CustomModules i klikamy przycisk Nowe.
Definicja uprawnień systemowych
Definicja uprawnień systemowych
  1. Wypełniamy formatkę. Należy zwrócić uwagę na pole identyfikator, które powinno być zapisane jednym ciągiem bez białych znaków.
Definicja uprawnienia systemowego
Definicja uprawnienia systemowego

Po kliknięciu przycisku zapisz mamy do dyspozycji nowe prawo bswfms.custom_modules.employee_briefcase, które następnie wpisujemy jako wartość atrybutu right w tagu module naszego pliku konfiguracyjnego customowego modułu.

...    
<module ... right="bswfms.custom_modules.employee_briefcase">
...

Bez ustawionego prawa moduł nie będzie widoczny w systemie. Nowo utworzone prawo należy wprowadzić jako wartość atrybutu right tagu module, np. right="bswfms.custom_modules.employee_briefcase"

Generowanie widoku modułu.

W celu wygenerowania widoku modułu istnieją dwie możliwości:

  • podpięcie raportu,
  • użycie phpRender-a do generowania treści.

Poniżej opisano każdą z tych opcji:

Użycie raportu jako treści zakładki modułu

Następnie należy wprowadzić klucz raportu. Jeżeli nie jest znany identyfikator raportu, który odpowiada za listę na poniższym przykładzie, należy przejść do definicji raportu.

Natomiast jeżeli już znany jest identyfikator raportu id, to należy wpisać go jako wartość atrybutu rep_id w tagu tab.

Filtrowanie danych w zakłądce modułu

W przypadku wykorzystania raportu do generowania treści można wykorzystać ten sam raport jako źródło dla więcej niż jednej zakładki modułu. Aby każda z zakładek nie pokazywała tych samych danych, należy dodać filtrowanie. Filtrowanie można dodać w definicji XML zakładki modułu np. (definicje z aplikacji Wnioski zakupowe):

<modules><module id="Request" name="{translate(#Wnioski zakupowe#)}" label="{translate(#Wnioski zakupowe#)}" icon="Shopping_Cart.svg" color="#e67e22" licenceTypeAccessRule="Allow:REGULAR,PORTAL" right="bswfms.apps.request">
        <tabs>
            <tab label="Wnioski">
                <tabs compactView="1">
                    <tab label="{translate(#Moje#)}" rep_id="2042" licenceTypeAccessRule="Allow:REGULAR,PORTAL">
                        <filters>
                            <filter name="FiltrZakładki" type="StaticFilter" token="{FILTER_TAB}" >
                                (d.adduid = {LOGGED_USR_ID}
                                OR
                                {LOGGED_USR_ID} IN (SELECT DISTINCT (o3.usr_id)
                                                    FROM stages s
                                                    INNER JOIN procedures pr2 USING (procid)
                                                    LEFT JOIN orgtree_view o3 ON o3.orunid = ANY (s.orgarr)
                                                    WHERE (QQ
                                                            (s.is_act AND NOT s.is_fix)
                                                        OR
                                                            (s.ptsttp = 'END' AND s.is_fix IS TRUE)
                                                    ) AND (s.ptsttp != 'SUBPROCESS') AND (pr2.rootpr = d.procid))
                                )
                            </filter>
                        </filters>
                        <buttons>
                            <button>
                                <custom_widget>1243</custom_widget>
                            </button>
                            <button>
                                <id>xls</id>
                                <label/>
                                <dscrpt>XLS</dscrpt>
                                <onclick>{EXPORT_TO_XLS}</onclick>
                                <icon>xls.png</icon>
                            </button>
                            <button>
                                <id>refresh1</id>
                                <label>{translate(#Odśwież#)}</label>
                                <dscrpt>{translate(#Odśwież#)}</dscrpt>
                                <onclick>
                                    {AFTER_SUBMIT}
                                </onclick>
                                <icon>refresh.png</icon>
                            </button>
                        </buttons>
                    </tab>
                    <tab label="{translate(#Zakończone#)}" rep_id="2042" licenceTypeAccessRule="Allow:REGULAR,PORTAL">
                        <filters>
                            <filter name="FiltrZakładki" type="StaticFilter" token="{FILTER_TAB}">
                                ((etap IS NULL ) AND state_ > 6 )
                            </filter>
                        </filters>
                        <buttons>
                            <button>
                                <id>xls</id>
                                <label/>
                                <dscrpt>XLS</dscrpt>
                                <onclick>{EXPORT_TO_XLS}</onclick>
                                <icon>xls.png</icon>
                            </button>
                            <button>
                                <id>refresh2</id>
                                <label>{translate(#Odśwież#)}</label>
                                <dscrpt>{translate(#Odśwież#)}</dscrpt>
                                <onclick>
                                    {AFTER_SUBMIT}
                                </onclick>
                                <icon>refresh.png</icon>
                            </button>
                        </buttons>
                    </tab>
                    <tab label="{translate(#Wszystkie#)}" rep_id="2042" licenceTypeAccessRule="Allow:REGULAR,PORTAL">
                        <filters>
                            <filter name="FiltrZakładki" type="StaticFilter" token="{FILTER_TAB}">
                                true
                            </filter>
                        </filters>
                        <buttons>
                            <button>
                                <id>xls</id>
                                <label/>
                                <dscrpt>XLS</dscrpt>
                                <onclick>{EXPORT_TO_XLS}</onclick>
                                <icon>xls.png</icon>
                            </button>
                            <button>
                                <id>refresh3</id>
                                <label>{translate(#Odśwież#)}</label>
                                <dscrpt>{translate(#Odśwież#)}</dscrpt>
                                <onclick>
                                    {AFTER_SUBMIT}
                                </onclick>
                                <icon>refresh.png</icon>
                            </button>
                        </buttons>
                    </tab>
                </tabs>
            </tab>
        </tabs>
    </module></modules>

W powyższym przykładzie pierwsza zakładka opisuje "Moje wnioski", następna "Zakończone", a ostatnia wszystkie. Istotą tego filtrowania jest to, że zawartość taga <filter> musi być prawidłowym warunkiem WHERE głównej kwerendy raportu. W kwerendzie zaś musi być użyty tag {FILTER_STRING}

Wykorzystanie phpRender-a do generowania treści modułu

PhpRender

PhpRender jest używany w definicji zakładek, w systemowych plikach xml-owych, custom modules.

Implementuje ICustomView gdzie musimy utworzyć metode toHtml, w której utworzymy Panel z listą.

Kod
class ExampleView implements ICustomView {

    /**
     * @var string
     */
    private $name;
    /**
     * @var string
     */
    private $refreshToken = 'return false;';

    public function __construct(string $name, ?Panel $parent = NULL) {
        $this->name = $name;
        $this->refreshToken = 'App.'.$this->name.'asyncRefresh();';
    }

    public function toHtml(): string {

        $html = '<div class="custom-projects gbs sbg b-s-b" style="overflow-y: scroll; height: 100%">  
                    <div class="ready-bootstrap">
                        <div class="row" style="margin-left: 5px; margin-right: 0;">
                                <button class="rbos-btn rbos-btn-secondary no-print" title="'.Translator::translate('Odśwież').'"
                                onclick="'.$this->refreshToken.' return false;">
                            <i style="vertical-align: middle; font-size: 14px; line-height: 30px; margin-right: 3px;"
                               class="eic eic-refresh"></i>'.Translator::translate('Odśwież').'
                        </button>      
                        </div>
                    </div>
                 </div>';
        return $html;
    }

    public function setParams(array $params): void {

    }

}

W przykładzie prezentowany jest widok z przyciskiem, który potrafi odświeżyć sam siebie poprzez wywołanie funkcji asynchronicznej z atrybutu onClick przycisku "odśwież"

Analogicznie została dodana obsługa na zakładkach spraw poprzez dodanie atrybutu phpRender="{klasa widoku z namespace}" do elementu tab.

Przykład definiowania zakładki
<tab label="Przykład widoku ICustomView" phpRender="ReadyApp\ExampleApp\View\ExampleView"></tab>

Uwagi końcowe.

Aby zobaczyć nowo utworzony moduł, należy wylogować się z systemu, a następnie zalogować się. Moduł powinien być widoczny w menu aplikacji.