Cortiveo i GEngine - prirucnik

↑ Nazad

Nalaz 24. septembra 2026, čitanjem generator, generator2 i generator3.

Zaključak pre objašnjenja

Mehanizam već postoji. Štampa ne treba da se menja. Ono što danas drži „plitku štampu" je jedna linija u koraku stvaranja redova, ne u koraku štampanja.

1. Jedan red H tabele = jedna štampana strana

generator3/DataHPrintBase.java:86 i generator/ObrazacDataHPrintBase.java:143:

dbtH = getRow(pageIndex);          // pageIndex JE indeks reda u H tabeli
return printObrazacData(pGraphics, pageIndex);

Dakle broj strana nije podešavanje štampe — to je broj redova koje je create korak napravio. Ako klijent ima 3 reda, ima 3 strane. Već sada.

2. Dva različita broja strane (ovo je srce stvari)

Svaki H red nosi dva broja, i oni nisu isto:

kolonaznačenjekoristi
ClientStranaRb / ClientStranaCountkoja je ovo strana klijenta i koliko ih imakoverte, OMR, izveštaj, težina
ObrazacStranaRbkoja šablonska strana se crtaizbor elemenata za štampu

Uslov koji izabira šta se crta (generator3/DataHPrint.java:373):

// preskoci definicije koje se ne odnose na tekucu stranu
if (mPrintItem.get(i).piStrana != getRow(pageIndex).getInt("ObrazacStranaRb"))
    continue;

Poređenje ide sa ObrazacStranaRb, a ne sa ClientStranaRb. Zato se šablonska strana može ponavljati: klijent sa 7 strana može da ima redove ObrazacStranaRb = 1, 2, 2, 2, 2, 2, 3 — prva strana, pet puta sredina, pa zadnja. Štampa to već ume.

3. Šta danas zaključava broj strana

generator/ObrazacDataHCreate.java:296 (isto i u generator3):

int brojstrana = getDefinition().getInt("BrojStrana");   // KONSTANTA po obrascu
...
for (int strana = 0; strana < brojstrana; strana++) {
    dbt.set("ClientStranaRb", strana + 1);
    dbt.set("ObrazacStranaCount", brojstrana);
    dbt.set("ClientStranaCount", brojstrana);
}

Svi dobijaju isto, jer broj dolazi iz definicije. To je jedino mesto koje treba da postane dinamično.

4. Kako bi izgledalo dinamično

int potrebno = strana1 + srednje(klijent) + zadnja;   // izracunato iz podataka
for (int strana = 0; strana < potrebno; strana++) {
    dbt.set("ClientStranaRb",    strana + 1);
    dbt.set("ClientStranaCount", potrebno);
    dbt.set("ObrazacStranaRb",   sablonZa(strana, potrebno)); // 1 / 2 / 3
    dbt.set("Rb",                rbStrane);                   // kljuc za detalje te strane
}

gde je srednje(klijent) = ceil(brojStavki / stavkiPoStrani).

Sve ostalo prati samo od sebe, jer je izvedeno iz ClientStranaCount:

5. Tabele koje se pune po stranama

Za to je model u prvom generatoru: generator/DetailMemDataSource.java

public class DetailMemDataSource extends MemDbtObject implements GDetailParametersSettable
{
    private GFilterItem mfiRB = new GFilterItem("Rb", null);

    public boolean setParameters(GDataSource dbt) {
        mDefinition = (ObrazacDefinition) dbt;
        mfiRB.setValue(mDefinition.getInt("Rb"));   // veze detalj za tekuci master red
        setFilter(mfRB);
        setMaster(mDefinition, "Rb");
        return true;
    }

    void transferData() {                            // prepisi samo redove ovog Rb
        for (int i = 0; i < mMasterDataSource.getRowCount(); i++)
            if (mMasterDataSource.getRow(i).getInt("Rb") == mDefinition.getInt("Rb")) {
                clear(); copy(mMasterDataSource.getRow(i)); insert();
            }
    }
}

Prevod: memorijska detalj-tabela filtrirana po Rb matičnog reda. Ako svaka strana dobije svoj Rb, a stavke se rasporede po stranama, onda svaka strana automatski „vidi" samo svoje stavke — i tabela na strani se puni sama. Isti obrazac važi i kad podaci dolaze iz SQL-a, jer je MemDbtObject ista tabela kao DB tabela (vidi arhitekturu).

6. Učitavanje iz sopstvene tabele

Ulaz je već pluggable — generator3/DataHCreate.java:191:

switch (InputTip) {
    case ListInputType.PRN   : readDataPrn(this, fileNo); break;
    case ListInputType.CSV   : readDataCsv(this, fileNo, ","); break;
    case ListInputType.SSV   : readDataCsv(this, fileNo, ";"); break;
    case ListInputType.EXCEL : readDataExcel(this, fileNo); break;
    ...
}

Dodavanje SQL/custom ulaza = nova vrednost u ListInputType + jedna readDataSql(...) grana. Nema drugih izmena: ostatak lanca (create → H redovi → štampa) ne zna odakle su podaci došli.

7. Plan izmene, po riziku

#korakrizik
1readDataSql kao nov InputTip — učitavanje iz sopstvene tabelemali, aditivno
2potrebnoStrana(klijent) + ObrazacStranaRb šablon (prva / sredina / zadnja) u kopiji create klasesrednji, ne dirati postojeću
3detalj-tabela po Rb strane, po uzoru na DetailMemDataSourcesrednji
4provera: printIzvestaj histogram mora da pokaže različite brojeve strana—

Pravilo: ne menjati generator3 u mestu. generator2 je već kopija-rezerva prvobitnog generator3; nova varijanta ide kao generator4 ili kao potklasa create koraka, pa se dokaže na test podacima, tek onda se prebacuje.

Zašto je prvi generator vredan — definicije poslova su XML fajlovi

Ovo je ispravka: prvo sam istakao DetailMemDataSource, a glavna razlika je drugde. Korisnik je upozorio, i bio je u pravu.

generator1 pamti posao u XML fajlu (.obr), a ne u bazi. generator/ObrazacDefinition.java:

// UČITAVANJE: svaki .obr fajl iz foldera je jedan obrazac
detailList[0] = this;                                   // master = definicija obrasca
for (int i = 0; i < mDetailMemDataSource.size(); i++) {
    mDetailMemDataSource.get(i).setMaster(this, "Rb");
    detailList[i + 1] = mDetailMemDataSource.get(i);    // + sve njegove detalj-tabele
}
GFile[] file = GFolder.getFilesByExtension(Main.EXTENSION_OBRAZAC);   // "obr"
for (int i = 0; i < file.length; i++)
    (new GGXml(file[i].getName())).importData(detailList);

// SNIMANJE: samo ono sto je promenjeno, pri izlasku iz opcije
if (!getBoolean("Promenjen")) continue;
(new GGXml(getString("Opis").trim() + "." + Main.EXTENSION_OBRAZAC))
    .exportDetailData(this, detailDataSource);

gjava/io/GGXml.java je opšti mehanizam engine-a, ne nešto napisano za ovaj generator:

public boolean exportData(GDataSource2d... pDataSource)
public boolean exportDetailData(GDataSource pMaster, GDataSource2d[] pDataSource)
public boolean importData(GDataSource2d... pDataSource)
private int findDataSource(String pName, GDataSource2d... pDataSource)  // spaja po IMENU tabele

Dakle bilo koji skup tabela (master + detalji) može da se snimi u XML i pročita nazad, a povezivanje ide po imenu tabele. Fajlovi sa ekstenzijom .obr u radnom folderu štampe su tačno to — snimljene definicije obrazaca.

Šta se time dobija (i šta je generator3 izgubio)

generator1 (XML .obr)generator3 (SQL tabele)
gde je definicijafajl po obrascuredovi u definition* tabelama
prenos na drugu mašinukopiraj fajlizvoz/uvoz redova, ili ručno
verzionisanje, diffda, običan tekstne bez posla
15 bazajedan fajl za svedefinicija se replicira po bazi
uređivanje u ekranuda (pa Promenjen → snimi na izlasku)da, direktno u bazi

Zato je ovo važno za zadatak „15 baza odjednom": definicija kao fajl ne mora da se replicira po bazama. GGXml već ume i izvoz i uvoz, pa je „prenesi obrazac iz baze A u bazu B" postojeći mehanizam, ne nov razvoj.

Druga vredna stvar u generatoru1

DetailMemDataSource (master-detail u memoriji, filter po Rb) i ObrazacDataHCreate sa tri različita puta stvaranja redova (541 linija). generator3 je uredniji i data-driven, ali je usput izgubio i XML pamćenje posla i detalj-mehanizam.

Dinamično listanje je spajanje tih dvaju: urednost trojke + XML definicije i detalji jedinice.

Izvor: 07-dinamicno-listanje.md