Nalaz 24. septembra 2026, čitanjem generator, generator2 i generator3.
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.
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.
Svaki H red nosi dva broja, i oni nisu isto:
| kolona | značenje | koristi |
|---|---|---|
ClientStranaRb / ClientStranaCount | koja je ovo strana klijenta i koliko ih ima | koverte, OMR, izveštaj, težina |
ObrazacStranaRb | koja šablonska strana se crta | izbor 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.
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.
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:
Obrazac.printIzvestaj() — histogram i podela koverata do/preko 20 ggetCountCoverat() — broj koverata, pa i AA brojgenerator3/DataHCreate.java:209-222) — marka na prvoj strani svakog koverta i SystemPrintOMREndOfClient na zadnjoj; sve po ClientStranaRb/CountAutoprint i podela „6+ strana" — čita isti ClientStranaCountZa 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).
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.
| # | korak | rizik |
|---|---|---|
| 1 | readDataSql kao nov InputTip — učitavanje iz sopstvene tabele | mali, aditivno |
| 2 | potrebnoStrana(klijent) + ObrazacStranaRb šablon (prva / sredina / zadnja) u kopiji create klase | srednji, ne dirati postojeću |
| 3 | detalj-tabela po Rb strane, po uzoru na DetailMemDataSource | srednji |
| 4 | provera: 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.
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 tabeleDakle 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.
generator1 (XML .obr) | generator3 (SQL tabele) | |
|---|---|---|
| gde je definicija | fajl po obrascu | redovi u definition* tabelama |
| prenos na drugu mašinu | kopiraj fajl | izvoz/uvoz redova, ili ručno |
verzionisanje, diff | da, običan tekst | ne bez posla |
| 15 baza | jedan fajl za sve | definicija se replicira po bazi |
| uređivanje u ekranu | da (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.
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.