morphx it, een inleiding tot axapta x++ en de morphx

363
Steen Andreasen MORPHX IT Een inleiding tot Axapta X++ en de MorphX ontwikkelomgeving

Upload: others

Post on 21-Dec-2021

0 views

Category:

Documents


0 download

TRANSCRIPT

Steen Andreasen

MORPHX IT

Een inleiding tot Axapta X++ en de MorphX ontwikkelomgeving

MORPHX IT Een inleiding tot Axapta X++ en de MorphX ontwikkelomgeving Copyright © 2006 Steen Andreasen, www.steenandreasen.com Editor: Steen Andreasen Layout: Steen Andreasen Omslag : Poul Cappelen en Ulla Bjulver Fotograaf: Ulla Bjulver Vertaald door : Linda Cornelis, Gert Cuppens, Jeroen Vangheluwe Onder redactie van : Gert Cuppens Denemarken 2007 ISBN: 87-991161-5-4 1e uitgave Alle rechten voorbehouden. De auteur heeft herbruikbare code in deze publicatie geschreven met het doel om door de lezers herbruikt te worden. U krijgt beperkte toestemming om de code in deze publicatie te herbruiken op voorwaarde dat de auteur wordt vermeld in de applicatie die deze code herbruikt en op voorwaarde dat de code zelf niet wordt verdeeld, online geplaatst, verkocht of commercieel gebruikt als een stand-alone product. Behalve deze specifieke uitzondering betreffende te herbruiken code, kan geen deel van deze publicatie op welke manier dan ook worden gebruikt of gereproduceerd zonder de voorafgaande schriftelijke toestemming van de auteursrechthouder behalve in het geval van korte citaten opgenomen in artikelen of overzichten. Elk ander gebruik zonder geschreven toestemming is verboden volgens de Deense auteursrechtwet. Als u enige onnauwkeurigheid ziet, gelieve dit te melden aan de auteur op het volgende e-mailadres: [email protected] Handelsmerken Alle begrippen die in dit boek worden vermeld en die gekend zijn als handelsmerk, zijn met hoofdletters geschreven. steenandreasen.com kan de nauwkeurigheid van deze informatie niet bevestigen. Het gebruik van een term in dit boek mag niet beschouwd worden als een uitspraak over de geldigheid van om het even welk handelsmerk. Waarschuwing en afwijzing van verantwoordelijkheid U mag nooit om het even welk voorbeeld in dit boek in een productieomgeving uitproberen. De informatie in dit boek wordt verstrekt zonder meer. De auteur of steenandreasen.com kan niet verantwoordelijk worden gesteld voor om het even welk verlies of schade dat het gevolg geweest zou zijn van de informatie die dit boek bevat.

“met dank aan mijn lieve vrouw Ulla, en mijn zoon Oliver, die met me opstonden en me steunden bij het schrijven van dit boek. ”

S. A.

Dankbetuiging

Ik dank iedereen die direct of indirect heeft bijgedragen tot de inhoud van dit boek door inspirerende commentaar en suggesties.

Een speciale bedanking is voor Lars Holm voor zijn bijdrage aan de Appendix Properties. Poul Cappelen en Ulla Bjulver www.photo-art.dk voor het ontwerp van de boekomslag. Jens Thrane www.addonax.dk, Christian Beck, Erik Pedersen, Lars Kjærsgaard, Jim Long, Hanne Paarup, Eric Fisher www.unitederp.com, Craig Brown www.edenbrook.co.uk, Daryl Spires www.avionsystems.co.uk, die het manuscript nagelezen, gecorrigeerd, geëditeerd hebben en , nog belangrijker, die me hebben aangemoedigd om vol te houden.

Aanbeveling van het boek

"Steen Andreasen is een uitstekend Axapta Programmeur en Technical Manager – en hij is een geduldig leraar. In dit boek heeft Steen Andreasen zeer hard gewerkt om u, de lezer, mee te nemen op een aangename reis door de wereld van de Axapta ontwikkeling. Ik beveel dit boek van harte aan als verplichte lectuur voor iedere ontwikkelaar, ervaren of beginner, die een carrière wil opbouwen in de Axapta Programmatie. Hartelijk bedankt, Steen Andreasen, voor al je inspanningen en gulheid om een zo goed gestructureerde waardevolle informatie open te stellen voor de gemeenschap. " Met vriendelijke groeten, Harish Mohanbabu Microsoft Dynamics Ax - MVP http://www.harishm.com/

Commentaar van de vertaler Vertalen vereist meer dan je eerst zou denken. Zeker als het gaat om een Engelse tekst die je naar het Nederlands wil vertalen. We zijn het nu eenmaal zo gewend om Engelse termen te gebruiken, en vooral als het gaat over informatica. Zinnen zoals “We hebben de rules overruled.” zijn geen uitzondering meer. Maar er is meer aan de hand. De laatste tijd hoor je mensen zelfs Engelse termen gebruiken in een context die met informatica niets van doen heeft : “Ik was compleet pissed”of “we zaten stuck op dat eiland”. Het Nederlands kan nochtans perfect uitdrukken dat je ergens vast zit of dat je je volledig van de pot gerukt voelt. Als je eenmaal begint te vertalen, moet je dat ook zo goed mogelijk doen. Ik heb Steen Andreasen nooit in levende lijve ontmoet. Onze enige contacten verliepen via het internet. Het was voor hem dan ook een kwestie van vertrouwen om me zijn volledig boek door te sturen nadat we nog maar één hoofdstuk vertaald hadden. En dat vertrouwen heb ik niet willen beschamen. De zin “we have overruled the rules” kon dus niet zomaar “we hebben de rules overruled” worden. Steen kent geen Nederlands maar kan wel half Engelse zinnen ontdekken. Halve vertalingen om een graantje mee te pikken, getuigt me van te weinig arbeidsethiek. Was een vertaling eigenlijk wel nodig ? Kennen we niet allemaal voldoende Engels ? Als ik een informaticus met jarenlange ervaring hoor zeggen dat hij pas na geruime tijd besefte dat desktop “bureaublad” betekent, dan kan je je afvragen of hij de betekenis van desktop wel had doorgrond. En roepen Engelse woorden wel hetzelfde op als onze eigen Nederlandse woorden ? Een kleine test kan je hiervan overtuigen : gebruik het woord “shit” maar eens aan tafel als je je vork hebt laten vallen. Gebruik daarna het Nederlandse equivalent, en je tafelgenoten zullen je aanwezigheid niet meer op prijs stellen. Eenmaal overtuigd van het nut van een Nederlandse vertaling, moet je daarna uitvissen welke woorden je best in het Engels laat en welke je beter kan vertalen. Ik heb me daarvoor soms laten inspireren door het Afrikaans. Maar wees gerust : een computer is geen rekenaar geworden. In bijlage vind je een overzicht van Nederlandse termen met hun Engelse vertaling erbij. Wie bijkomende informatie op het wereldwijde web zoekt, moet immers ook de Engelse termen kennen. Ik wens nog mijn metgezellen Linda en Jeroen te bedanken voor hun medewerking. Het werk is er alvast wat minder eenzaam door geworden. Gert Cuppens

© 2007 Steen Andreasen

7

Inhoudstafel

VOORWOORD............................................................................................... 15

INLEIDING ..................................................................................................... 17 Waarom dit boek belangrijk is .........................................................................18 Structuur van het boek ....................................................................................18

1 INLEIDING TOT MORPHX.................................................................. 19

1.1 AOT .................................................................................................................19 Lagen...............................................................................................................21 Eigenschappen ................................................................................................24 Add-ins.............................................................................................................25 Editor ...............................................................................................................25 Debugger .........................................................................................................28 Compilervenster...............................................................................................30 Import en Export ..............................................................................................32 Vergelijken van Objecten.................................................................................34 Code Upgrade .................................................................................................35 Zoeken.............................................................................................................36 Infolog ..............................................................................................................37 Prullenbak (Recycle Bin) .................................................................................39 Gebruikersinstellingen .....................................................................................39

1.2 Project.............................................................................................................43 Wijzigen van een project .................................................................................43 Projecttypes .....................................................................................................44

1.3 Samenvatting .................................................................................................44

2 INLEIDING TOT X++ ........................................................................... 45

2.1 Variabelen.......................................................................................................45

2.2 Operatoren .....................................................................................................50 Toewijzingsoperatoren.....................................................................................50 Relationele operatoren ....................................................................................51 Bitoperatoren ...................................................................................................52

2.3 Controlestructuurinstructies........................................................................53 Lussen .............................................................................................................53 Conditionele Uitdrukkingen..............................................................................55 Uitzonderingen.................................................................................................58 Varia ................................................................................................................59

2.4 Selectie-uitdrukkingen ..................................................................................61

2.5 Functies ..........................................................................................................67

2.6 Samenvatting .................................................................................................67

8

© 2007 Steen Andreasen

3 DATA DICTIONARY............................................................................ 69

3.1 Tabellen ..........................................................................................................69 Bedrijf...............................................................................................................69 Applicatietabellen.............................................................................................71 Systeemtabellen ..............................................................................................76 Velden..............................................................................................................77 Veldgroepen ....................................................................................................79 Indexen ............................................................................................................81 Relaties ............................................................................................................82 Verwijderacties ................................................................................................84 Methodes .........................................................................................................86

3.2 Koppelvlakken ...............................................................................................92

3.3 Views...............................................................................................................94

3.4 Uitgebreide Datatypes...................................................................................96 Uitgebreid datatype-array ................................................................................98

3.5 Basisenumeraties ..........................................................................................99

3.6 Kenmerksleutels ..........................................................................................101

3.7 Licentiecodes...............................................................................................101

3.8 Configuratiesleutels ....................................................................................102

3.9 Beveiligingsleutels ......................................................................................103

3.10 Tabelverzamelingen ....................................................................................105

3.11 Speciaal Tabelgebruik.................................................................................106 Gebruik van systeemklassen.........................................................................106 Externe databanken.......................................................................................107

3.12 Samenvatting ...............................................................................................109

4 MACRO’S.......................................................................................... 111

4.1 Macrocommando’s ......................................................................................111

4.2 Definitie van constanten .............................................................................113

4.3 Creatie van macro’s.....................................................................................114

4.4 Samenvatting ...............................................................................................115

5 KLASSEN.......................................................................................... 117

5.1 Klassefundamenten.....................................................................................117 Methodes .......................................................................................................118 Klassecomponenten ......................................................................................120

© 2007 Steen Andreasen

9

Modificeerders ...............................................................................................123 Het doorgeven van waardes..........................................................................129

5.2 AOS ...............................................................................................................133 Keuze van de rang.........................................................................................133 Optimalisatie van Objecten............................................................................135

5.3 Runbase Raamwerk.....................................................................................135 Het gebruik van het Runbase Raamwerk......................................................135 Dialoog...........................................................................................................140

5.4 Fundamentele klassen ................................................................................145 ClassFactory..................................................................................................145 Global ............................................................................................................145 Info.................................................................................................................146

5.5 Systeemklassen...........................................................................................146 Object ............................................................................................................146 Wijzigingen tijdens Runtime ..........................................................................147 Args................................................................................................................147 Funderingsklassen.........................................................................................148 Geoptimaliseerde Recordoperaties ...............................................................148 Bestandsafhandeling .....................................................................................149

5.6 Speciaal Gebruik van Klassen ...................................................................150 Het gebruik van COM ....................................................................................150 X++ Compiler.................................................................................................151

5.7 Samenvatting ...............................................................................................152

6 FORMULIEREN................................................................................. 153

6.1 Creatie van Formulieren .............................................................................154

6.2 Formulierquery ............................................................................................156 Het koppelen van Gegevensbronnen............................................................156 Instellen van toegang.....................................................................................160

6.3 Ontwerp ........................................................................................................162 Creatie van ontwerp.......................................................................................162 Formulierelementen in Ontwerp ....................................................................164 Display en Edit Modificeerders ......................................................................170

6.4 Methodes op een Formulier........................................................................174 Formuliermethodes........................................................................................174 Gegevensbronmethodes in een formulier .....................................................178 Gegevensbronveldmethodes in een formulier...............................................182 Formulierelementmethodes...........................................................................184 Gebruikelijke Formuliermethodes..................................................................184 Overschrijven van een Formulierquery..........................................................187 Wijzigen van gegevensbronnen vanuit X++ ..................................................189 Bouwen van opzoekingen .............................................................................191 Formulierdialoog ............................................................................................194

10

© 2007 Steen Andreasen

6.5 Speciale Formulieren ..................................................................................195 Oproepen van gebruikergedefinieerde methode...........................................195 Overladen van methodes ..............................................................................197 Algemene Formulierwijzigingen.....................................................................199 Kleuren ..........................................................................................................201

6.6 Samenvatting ...............................................................................................202

7 RAPPORTEN .................................................................................... 203

7.1 Report Wizard ..............................................................................................203

7.2 Het maken van rapporten ...........................................................................204

7.3 Rapportquery ...............................................................................................207

7.4 Sjablonen......................................................................................................210 Rapportsjabloon.............................................................................................210 Sectiesjabloon ...............................................................................................213

7.5 Ontwerpen ....................................................................................................213 Het ontwerp maken........................................................................................213 Auto-ontwerp .................................................................................................216 gegenereerd ontwerp.....................................................................................220 Rapportelementen in het ontwerp .................................................................220

7.6 Methodes in een Rapport............................................................................222 Report Runbase Raamwerk ..........................................................................226 Dynamische Rapporten .................................................................................231 Vaak gebruikte rapportmethoden ..................................................................234

7.7 Speciale Rapporten .....................................................................................239 Een rapport uitvoeren vanuit X++..................................................................239 Gebruik van tijdelijke tabellen........................................................................240 Gekleurde rijen ..............................................................................................242 Afdrukken met Microsoft Word ......................................................................244

7.8 Samenvatting ...............................................................................................247

8 QUERIES........................................................................................... 249

8.1 Bouwen van Queries ...................................................................................250 AOT Query.....................................................................................................250 X++ Query .....................................................................................................256

8.2 Queries in Formulieren en Rapporten .......................................................259

8.3 Samenvatting ...............................................................................................259

9 JOBS ................................................................................................. 261

9.1 Maken van jobs............................................................................................261

© 2007 Steen Andreasen

11

9.2 Samenvatting ...............................................................................................262

10 MENUOPTIES EN MENU’S .............................................................. 263

10.1 Menuopties...................................................................................................263

10.2 Menu’s ..........................................................................................................265 Het localiseren van het AOT-object vanuit het menu ....................................265

10.3 Samenvatting ...............................................................................................266

11 HULPBRONNEN............................................................................... 267

11.1 Het gebruik van hulpbronnen.....................................................................267

11.2 Samenvatting ...............................................................................................270

12 BIJLAGE EIGENSCHAPPEN ........................................................... 271

12.1 Data Dictionary Eigenschappen.................................................................271 Tabellen, Tabelkoppelvlakken en Tabel views..............................................271 Tabelveld, koppelvlakveld .............................................................................273 View Velden...................................................................................................275 Tabelveldgroep, koppelvlakveldgroep, View veldgroep ................................276 Tabelindex .....................................................................................................276 Tabelrelatie ....................................................................................................276 Tabelrelatieveld .............................................................................................277 Tabel Wisactie ...............................................................................................277 koppelvlakkoppeling ......................................................................................277 Koppelvlakveldtoewijzing...............................................................................277 Uitgebreid Datatype.......................................................................................278 Basisenumeratie ............................................................................................281 Basisenumeratie-element..............................................................................282 Licentiecodes.................................................................................................282 Configuratiesleutel, beveiligingsleutel ...........................................................283

12.2 Formuliereigenschappen............................................................................284 Formuliergegevensbron.................................................................................284 Formuliergegevensbronvelden ......................................................................285 Formulierontwerpgroepelementen.................................................................286 Formulierontwerp...........................................................................................292 Type formulierelementen...............................................................................294

12.3 Rapporteigenschappen...............................................................................314 Rapport ..........................................................................................................314 Rapportontwerp .............................................................................................314 Auto-ontwerp .................................................................................................316 Sectierapportelementen ................................................................................317 Sectiesjabloon ...............................................................................................320 Sectiegroep....................................................................................................320 Type rapportelementen .................................................................................321 Veldgroep ......................................................................................................332

12

© 2007 Steen Andreasen

12.4 Queryeigenschappen ..................................................................................332 Query .............................................................................................................332 Gegevensbronnen .........................................................................................333 Velden............................................................................................................334 Sorteervelden ................................................................................................334 Bereiken.........................................................................................................335

12.5 Menueigenschappen ...................................................................................335

12.6 Menuoptie-eigenschappen .........................................................................336

13 BIJLAGE MORPHX ONTWIKKELTOOLS........................................ 339

13.1 Kruisverwijzing ............................................................................................339

13.2 Applicatieobjecten.......................................................................................340 Applicatieobjectformulieren ...........................................................................340 Applicatiebeheer ............................................................................................341 Datagebruik ...................................................................................................341 Tellen van applicatieobjecten ........................................................................341 Vergrendelde applicatieobjecten ...................................................................341 Refresh tools..................................................................................................341 Herindexering ................................................................................................342

13.3 Systeemmonitoring .....................................................................................342 Databankopvolging ........................................................................................342 AOS-opvolging...............................................................................................342

13.4 Code Profiler ................................................................................................343

13.5 Applicatiehiërarchieboom ..........................................................................344

13.6 Visual MorphXplorer....................................................................................344

13.7 Code Explorer ..............................................................................................346

13.8 Tabeldefinities..............................................................................................346

13.9 Aantal Records ............................................................................................347

13.10 Helpteksten ..................................................................................................347

13.11 Versie Update ..............................................................................................347 Hernoemde applicatieobjecten ......................................................................348 Aanmaken van een upgrade project..............................................................348 Vergelijken van lagen ....................................................................................348

13.12 Wizards ..........................................................................................................348 Report Wizard................................................................................................349 Wizard Wizard ...............................................................................................349 Label File Wizard ...........................................................................................349 Class Wizard..................................................................................................349 COM Class Wrapper Wizard .........................................................................349

© 2007 Steen Andreasen

13

13.13 Label .............................................................................................................350 Vinden van een label .....................................................................................351 Label log ........................................................................................................351 Label file wizard .............................................................................................351 Label intervals................................................................................................351

14 BIJLAGE REPORT WIZARD ............................................................ 353

15 BIJLAGE WOORDENLIJST NEDERLANDS-ENGELS.................... 361

MORPHX IT Voorwoord

© 2007 Steen Andreasen

15

Voorwoord Succesvol programmeren is voornamelijk gebaseerd op het begrijpen van de gebruikersbehoeften en deze behoeften vertalen in een technisch werkende oplossing, een systeem. Het is bovendien essentieel dat de programmeur duidelijk begrijpt hoe dit systeem is aangepast zodat de wijzigingen gebruiksvriendelijk zijn en eenvoudig te behandelen bij een upgrade. Dit boek geeft een inleiding in de ontwikkelingsfase van Axapta. Het boek is niet alleen een oefening in de functionaliteit van Axapta maar omdat het gebaseerd is op mijn meer dan acht jaren van ervaring met Axapta, is het evenzeer een praktisch boek met zowel een hoop gecodeerde voorbeelden van productontwikkeling als klantgerichte oplossingen. De publicatie van dit boek staat al een hele tijd op mijn verlanglijstje. Tijdens mijn jarenlange werken met Axapta heb ik geleerd dat een praktisch en instructief boek voor Axapta niet beschikbaar is. Dit boek ,MORPHX IT, toont mijn professionele interesse in ERP systemen en Axapta in het bijzonder. Mijn reis als auteur door het publicatieproces was boeiend en het was aanmoedigend voor me om mail te krijgen van honderden mensen van de hele wereld. Ik kreeg commentaren en suggesties nadat ik een hoofdstuk van het boek voor download op het web had gezet. Dit verzekerde me dat er een sterke interesse en behoefte was aan het boek. Hopelijk geeft dit boek op zijn beurt inspiratie aan talloze mensen, de beginnelingen die hun eerste stappen zetten in Axapta, de lesgevers aan hogescholen en universiteiten waar het onderwijs van ERP systemen opgang maakt, en de meer ervaren Axapta professionelen in de bedrijfswereld. Veel programmeurs hebben ieder voor zich beetje bij beetje alle informatie moeten verzamelen, die ik in dit boek heb gepubliceerd. Het is mijn bedoeling om het werken in en met Axapta eenvoudiger te maken en te inspireren tot een voortdurende ontwikkeling. Steen Andreasen

MORPHX IT Inleiding

© 2007 Steen Andreasen

17

Inleiding Dit boek is een inleiding tot de ontwikkelomgeving van Axapta, ook bekend onder de naam MorphX. MORPHX IT is geschreven als een praktisch boek. Met praktisch bedoel ik dat je dit boek moet gebruiken terwijl je werkt met Axapta. Dit maakt van het boek ook een waardevolle dagelijkse metgezel gezien er een aanzienlijk aantal voorbeelden in staan. Ik geloof dat het direct gebruiken van het systeem de eenvoudigste en snelste manier is om een nieuwe ontwikkelingstaal te leren. Je moet beschikken over een geïnstalleerde Axapta applicatie en je moet een basiskennis hebben over hoe de Axapta user interface eruit ziet. Deze informatie kan je vinden in de handleidingen van het standaardpakket. Centraal in dit boek is het gezichtspunt van de ontwikkelaar. Je kan dit boek gebruiken zonder enige kennis over Axapta. Natuurlijk zal het eenvoudiger zijn als je je eerste stappen in de applicatie zelf hebt gezet. Je zal er je voordeel uithalen als je de voorbeelden uitprobeert terwijl je het boek leest. De voorbeelden in dit boek zitten in de zip file MORPHXIT_1ED_EXAMPLES.ZIP dat bij het boek hoort. Axapta 3.0 Service Pack 4 was geïnstalleerd bij het schrijven van dit boek. Als je een andere Service Pack van Axapta 3.0 gebruikt, kan je hier en daar kleine verschillen zien. Het boek is geschreven voor mensen die geen voorkennis hebben van programmatie in Axapta. Een achtergrond als programmeur is handig maar niet noodzakelijk. Het boek kan ook gelezen worden door personen met een technische achtergrond of door Axapta applicatieconsulenten die de ontwikkelomgeving willen gebruiken. Als je een beginner bent, raad ik je aan om de hoofdstukken vanaf het begin van het boek te lezen omdat ieder hoofdstuk meer en meer details van de omgeving bijbrengt. Mogelijk zijn er begrippen die je niet direct begrijpt. Niet alle begrippen in een hoofdstuk worden direct uitgelegd. Ik heb hiervoor gekozen om de inhoud zo eenvoudig mogelijk te houden. Alle begrippen ineens uitleggen zou dit boek te theoretisch maken om dagelijks te gebruiken. Vaak zijn er verwijzingen naar andere secties in hetzelfde hoofdstuk of naar andere hoofdstukken, waar je meer informatie van een bepaald begrip kan opzoeken. Ben je een meer ervaren Axapta gebruiker, dan kan je hoofdstukken van het boek uitkiezen om meer te weten te komen over een bepaald onderwerp.

MORPHX IT Inleiding

18

© 2007 Steen Andreasen

Waarom dit boek belangrijk is Gedurende de jaren dat ik met Axapta heb gewerkt, was ik me bewust van het gebrek aan documentatie over de Axapta ontwikkelomgeving. Beschouw dit boek als de nota’s die je zou willen hebben als je in Axapta begint te programmeren. MORPHXIT is het eerste Axapta programmatieboek en kan beschouwd worden als een alternatief voor het volgen van cursussen om te leren programmeren in Axapta. Met dit boek heb je een metgezel die je de ontwikkelomgeving van Axapta snel zal leren kennen. Een gids die je zal leren om je applicatie zo aan te passen dat je code eenvoudiger te onderhouden is en gebruikersvriendelijker voor de applicatiegebruikers. Structuur van het boek Bij het schrijven van het boek heb ik een keuze moeten maken van wat ik belangrijk vind voor een inleidend boek. Het web framework wordt niet in dit boek behandeld. Er zijn meerdere redenen waarom het web framework of raamwerk niet behandeld wordt. De focus lag op de kennis die een nieuwkomer in Axapta programmatie nodig heeft. Het zijn vaak ervaren Axapta programmeurs die met het web raamwerk starten. En voor de meeste klanten heb je de webonderdelen van Axapta niet nodig. Als ik een begrip in het boek behandel zoals een tool gebruikt in de ontwikkelingsomgeving, dan beschrijf ik niet alle velden, knoppen of kenmerken van de tool. Begrippen leg ik vaak uit zoals je dat doet als je iemand naast je hebt zitten. Dit gaat vooral op voor de Appendix MorphX Tools. Bij het lezen van dit boek zal je een heleboel praktische tips en aanbevelingen zien. Ik maak geen onderscheid tussen beide begrippen. Samen vormen ze mijn eigen set van regels die ik gebruik voor het programmeren of beheren van een ontwikkelproject in Axapta. De hoofdstukken in het boek volgen de drie voornaamste nodes in de Axapta ontwikkelomgeving, te beginnen met de inleidende hoofdstukken over de ontwikkel-omgeving en ontwikkeltaal. Ze worden gevolgd door de hoofdstukken over hoe je de ontwikkelomgeving moet gebruiken. Achteraan in het boek vind je appendixhoofdstukken die verdere informatie bevatten over sommigen begrippen van het boek.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

19

1 Inleiding tot MorphX De ontwikkelomgeving in Axapta wordt de MorphX Development Suite genoemd of kortweg MorphX. MorphX is een geïntegreerde ontwikkelomgeving die bestaat uit de Application Object Tree, de X++ programmeertaal en verschillende tools die een overzicht bieden op de applicatie. MorphX wordt gebruikt om Axapta applicatiemodules te bouwen. Indien je bij Microsoft licenties hebt gekocht op de broncode van Axapta, kan je elk object editeren dat hoort bij het standaardpakket. Deze omgeving omvat dezelfde tools die door Microsoft gebruikt worden voor het ontwikkelen van de software en staat je toe om de bestaande functionaliteit uit te breiden tot ze past binnen de vereisten van je organisatie. Hierdoor is Axapta veel eenvoudiger om aan te passen als andere ERP pakketten. Het versnelt het ontwikkelproces aangezien je kan bouwen op bestaande functionaliteit in plaats van je eigen code van nul af te schrijven. Zowel Microsoft SQL Server als Oracle databanken worden ondersteund. Maar eens je de databank en de applicatie hebt geïnstalleerd, moet je je geen zorgen meer maken over het type van databank dat je gebruikt. De Axapta kernel maskeert immers alle databankspecifieke zaken voor de ontwikkelaar. Het ontwikkelen van een eigentijdse gebruikersinterface kan een tijdrovende bezigheid zijn. In MorphX wordt de gebruikersinterface, zowel formulieren als rapporten, haast volledig gegenereerd door het systeem. Dit betekent dat je geen tijd moet besteden aan het positioneren en schikken van de velden in de ontwerpen. Dit wordt allemaal verder uitgelegd in de hoofdstukken over Formulieren en Rapporten.

1.1 AOT De Application Object Tree (AOT) is het ontwikkelmenu in Axapta. Alle objecten gebruikt in de applicatie, worden bewaard in de AOT en aan de ontwikkelaar getoond in een boomstructuur, gegroepeerd per objecttype. Axapta gebruikt een lagen-technologie om objecten te bewaren. Deze lagen helpen het onderscheid te maken tussen de diverse objecten in Axapta. Een typische Axapta installatie wordt immers niet alleen gebouwd op de standaardobjecten van Microsoft, maar ook op objecten van softwarebureau’s en op objecten die ontwikkeld zijn door de klant zelf. De boomstructuur van de AOT wordt aangegeven via knooppunten of nodes. Ieder knooppunt wordt gesymboliseerd door een plusteken (+) zoals hierboven getoond wordt. Als je een knooppunt in de AOT openklikt, worden de onderliggende knooppunten in het cachegeheugen opgeslagen. Dit zal enkele seconden duren als je een knooppunt de eerste keer opent. Het cachegeheugen bevat enkel gebruikte knooppunten. Als je een knooppunt openklikt, die je al eerder gebruikt hebt en dus in cache hebt gezet, zal je zien dat de AOT dit maal veel sneller reageert.

MORPHX IT Inleiding tot MorphX

20

© 2007 Steen Andreasen

De AOT roep je op via de toetsencombinatie ctrl+d of door te klikken op het icoon in het bovenste menu. Merk op dat je geen toegang hebt tot de AOT als je gebruik maakt van een demo installatie van Axapta. Je moet de licentiecodes voor MorphX en X++ geïnstalleerd hebben. Als je een nieuw object wil aanmaken, selecteert je een knooppunt in de AOT, klik met de rechtermuisknop en kies new. Een rode vertikale balk naast het objectknooppunt geeft aan dat het knooppunt nog niet is bewaard. Deze rode aanduiding wordt ook getoond als het knooppunt is gewijzigd. Alle gewijzigde of nieuw aangemaakte knooppunten kunnen bewaard worden door te klikken op het “bewaar alles”-icoon bovenaan het AOT venster. Objecten, velden en methodes kunnen gedupliceerd worden in de AOT. Methodes en velden kunnen ook gekopieerd worden. De copy en duplicate functies bereik je door het knooppunt te selecteren, met de rechtermuisknop te klikken en de gewenste functie te selecteren. Als je een knooppunt dupliceert, zal het systeem een kopie creëren van het geselecteerde knooppunt in de AOT met de prefix copyOf. Dit is vooral handig als je een wijziging wil testen en de vorige staat van het object wil bewaren als terugvalmogelijkheid voor het geval de wijziging niet naar behoren werkt. De AOT laat je ook toe om te verslepen en plaatsen (“drag and drop”). Dit is handig als je velden wil toevoegen aan formulieren of rapporten. De velden op formulieren of rapporten noemt men “controls” of formulier-/rapportelementen. Deze elementen zijn nog niet gekoppeld aan overeenkomstige databankvelden. Daarom is het aangeraden om de gewenste velden vanuit de data source te verslepen naar het formulier of rapport. Als je de velden versleept hebt naar het formulier of rapport, maakt MorphX het formulier- of rapportelement aan met de juiste eigenschappen. Ongeacht welk knooppunt je selecteert in de AOT, je zal altijd toegang hebben tot een contextgevoelig menu door het huidige knooppunt te selecteren en met de rechtermuisknop te klikken. Dit menu staat gekend als SysContextMenu in de AOT en kan je vinden onder het Menuknooppunt. Als je door het menu van de AOT bladert, worden alle menuopties beschikbaar in het contextmenu getoond. Afhankelijk van het geselecteerde knooppunt in de AOT, krijg je nog extra menuopties te zien om contextgevoelige activiteiten te starten, zoals de creatie van een nieuw object, het

Figuur 1: Application Object Tree

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

21

openen van een nieuw venster of het tonen van het tools submenu Add-Ins. Het openen van een nieuw venster toont het huidige geselecteerde knooppunt in de AOT als hoofdknooppunt in het nieuwe venster. Je kan net zoveel vensters openen als je zelf nodig acht. Dit is met name handig als je nieuwe objecten wil verslepen naar een formulier of rapport, of eenvoudigweg om cursors gepositioneerd te hebben op verschillende plaatsen in de AOT. Lagen De lagentechnologie in Axapta wordt gebruikt voor de organisatie van de objecten van het standaardpakket en de bijbehorende aanpassingen. Er zijn acht standaardlagen. Elk van deze standaardlagen heeft een overeenkomstige herstellaag (patch layer) wat neerkomt op een totaal van 16 lagen. De onderste vier lagen worden gebruikt voor het standaard pakket en zijn niet bereikbaar voor partners of klanten. Partners en klanten krijgen elk twee lagen met daarbovenop de bijbehorende herstellagen. Als je aanlogt, specifieer je de actieve laag in de client Axapta Configuratie. Alle lagen behalve de toplaag vragen een toegangscode. Als klanten een licentie kopen voor de software, krijgen ze een toegangscode voor de bovenste twee lagen en bijgevolg hebben ze geen mogelijkheid voor het wijzigen van de inhoud van de onderste zes lagen. Dit voorkomt dat een klant de kerncode wijzigt die door Microsoft of een business partner is geleverd. Dit verhindert evenwel niet dat je code kan wijzigen. Als je een standaardobject editeert, kopieert Axapta het object deels of geheel van één van de onderste lagen naar de actieve laag. Aangezien een object gewijzigd kan worden in meer dan één laag, start de zoektocht naar het object net onder de actieve laag en gaat door vanaf de hoogste laag naar de onderste laag. Eenmaal gevonden, wordt het object gekopieerd naar de actieve laag waar je het object dan kan wijzigen. Je wijzigingen worden bewaard in de actieve laag. Wijzigingen in de hogere laag zullen altijd de onderste laag overschrijven, in die zin dat een object zich gedraagt zoals voorgeschreven op de hoogste laag. Het object blijft natuurlijk bewaard in zijn oorspronkelijke vorm in de onderliggende lagen. Als je dus opnieuw wil beginnen, moet je enkel het object in de actieve laag wissen en je bent terug op je startpunt. De actieve laag wordt getoond in de statuslijn onderaan het Axapta venster. Voor een overzicht van de lagen kan je terecht bij figuur 2: Lagenoverzicht . Laag Toegangsrecht Omschrijving

SYS

Read Dit is de onderste laag. Alle objecten gemaakt door Microsoft worden hier bewaard.

SYP

Read Herstellaag voor de SYS laag. Wordt gebruikt voor Service Packs.

GLS

Read Solution Providers gebruiken deze laag. Hierin vind je zogenaamde global certified modules gemaakt door sub suppliers en gelicencieerd door Microsoft, zoals de CRM en HRM modules.

MORPHX IT Inleiding tot MorphX

22

© 2007 Steen Andreasen

Laag Toegangsrecht Omschrijving

GLP

Read Herstellaag voor de GLS laag. Wordt gebruikt voor Service Packs.

DIS

Read De laag wordt gebruikt voor landafhankelijke oplossingen. Er bestaan verschillende versies van de laag als deze laag moet beantwoorden aan de verschillende lokale behoeftes. Als een applicatie wordt gebruikt in landen met verschillende DIS lagen, moeten de lagen manueel gemengd worden.

DIP

Read Herstellaag voor de DIS laag. Wordt gebruikt voor Service Packs.

LOS

Read Deze laag wordt gebruikt voor locale oplossingen. Het gaat hier om modules zonder globaal certificaat zoals in de GLS laag. In Denemarken bevat deze laag de Loonbeheermodule.

LOP

Read Herstellaag voor de LOS laag. Wordt gebruikt voor Service Packs.

BUS

All Dit is de laagste laag waar de partners toegang tot hebben. Partners kunnen deze laag gebruiken voor hun eigen modules. De laag vereist een licentiecode.

BUP

All Herstellaag voor BUS. Partners kunnen deze laag gebruiken voor aanpassingen.

VAR

All Partners gebruiken deze laag voor klantspecifieke aanpassingen. De laag vereist een licentiecode.

VAP

All Herstellaag voor de VAR laag. Kan gebruikt worden voor aanpassingen.

CUS

All Deze laag wordt gebruikt voor aanpassingen door de klant zelf.

CUP

All Herstellaag voor de CUS laag. Kan gebruikt worden voor aanpassingen.

USR

All Dit is de hoogste laag. De laag kan gebruikt worden als een gebruiker zijn eigen aanpassingen beheert zoals de creatie van een rapport met behulp van report wizard. Deze laag wordt vaak gebruikt voor testdoeleinden

USP

All Herstellaag voor de USR laag. Bedoeld voor de aanpassingen van de klant.

Figuur 2: Lagenoverzicht

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

23

Als je inlogt op een Axapta client, wordt de actieve laag bepaald. Je kan niet van laag veranderen zonder de client te herstarten. Alle wijzigingen worden opgeslagen in de actieve laag. Afhankelijk van het gewijzigde object, worden verschillende onderdelen van het object bewaard in de aangepaste laag. Wijzigingen aan objecten zoals formulieren en rapporten vereisen dat het volledige object aangemaakt wordt in de actieve laag. Als een klasse of tabel is gewijzigd, wordt enkel de aangepaste methode of veld in de actieve laag aangemaakt. Elke laag wordt bewaard in een fysiek bestand met de benaming AX<laag>.AOD. De bestandsnaam voor de sys laag is AXSYS.AOD. De lagen worden geindexeerd in het bestand AXAPD.AOI. Indien dit bestand gewist wordt, wordt het indexbestand automatisch opgebouwd bij het starten van Axapta. Als een laagbestand wordt gewist of toegevoegd, wordt het indexbestand ook opnieuw opgebouwd. Dit kan handig zijn als je in een situatie verzeild geraakt waar je een object in de AOT niet kan vinden, of geconfronteerd wordt met eigenaardige bokkesprongen zoals het crashen van de AOT wanneer je een bepaald object wil beheren. Dan helpt het soms om het indexbestand te wissen en het terug op te bouwen bij het starten van Axapta. Om het indexbestand terug op te bouwen, moeten alle gebruikers afloggen en moet je de shutdown van de Application Object Server (AOS) starten. Start daarna één two-tier client op om de index terug op te bouwen. Opmerking : bij het wijzigen van een object worden aanpassingen in hogere lagen van hetzelfde object automatisch aangepast. Als een formulier is gewijzigd in de VAR laag, dan zullen wijzigingen van dit formulier in de USR laag automatisch worden aangepast met de wijzigingen van de VAR laag. Bij het importeren van objecten worden dezelfde objecten niet aangepast in hogere lagen. Als je de ontwikkeloptie hebt aangepast om alle lagen te tonen via Tools | Options, dan worden alle lagen voor een object getoond tussen haakjes na de objectnaam in de AOT. Dit geeft een snel overzicht van de lagen die aanpassingen bevatten voor een bepaald object. Als je een knooppunt selecteert die in meerdere lagen voorkomt en met de rechtermuisknop klikt, zal het knooppunt zich opsplitsen in verschillende lagen en iedere laag voor het object zal getoond worden als een apart knooppunt in de AOT. Je kan dan bladeren door de aanpassingen die in een enkele laag zijn gebeurd. Dit neemt niet weg dat je nog altijd maar kan wijzigen in de actieve laag. Klik nogmaals op de lagen om de laagopdeling terug te verwijderen.

MORPHX IT Inleiding tot MorphX

24

© 2007 Steen Andreasen

Eigenschappen Het eigenschappenblad of property sheet krijg je door een object in de AOT te selecteren, met de rechtermuisknop te klikken en kies vervolgens Properties of gebruik de toetsencombinatie alt+enter. Het is waarschijnlijk eenvoudiger als je het eigenschappenblad openlaat, omdat het iedere keer wordt aangepast telkens je een ander object selecteert in de AOT. Doorgaans wordt het eigenschappenblad aan de rechterzijde van het scherm geplaatst. Als je het eigenschappenblad zelf wil positioneren, klik je met de rechtermuisknop en kies No Docking. De eigenschappen worden voorgesteld op twee manieren. Het eerste tabblad lijst alle eigenschappen op en het tweede tabblad groepeert de eigenschappen. Als je werkt met een hoge resolutie, kan je in de meeste gevallen alle objecteigenschappen zien zonder door het scherm te moeten rollen. Wil je de eigenschappen alfabetisch zien, ga dan naar Tools | Options. Voor een overzicht van de eigenschappen in de AOT, verwijs ik naar het hoofdstuk Bijlage Eigenschappen. Opmerking : Het eigenschappenblad kan gebruikt worden om aantallen objecten te tellen. Alle knooppunten of nodes moeten in de AOT gecached worden om ze te tellen. Bij wijze van test kan je objecten met het prefix Sales selecteren en klik dan op alt+enter. Dit zal de geselecteerde knooppunten in het cachegeheugen plaatsen. Als je nu sommige knooppunten markeert, wordt het aantal knooppunten tussen haakjes getoond bovenaan het eigenschappenblad. Dit werkt alleen als alle geselecteerde objecten in cache zijn geplaatst. Alle objecteigenschappen hebben een set van standaard waardes. De standaard waardes zijn doorgaans die waardes die ten volle gebruik maken van de eigenschap van MorphX om het ontwikkelproces te automatiseren en programmaflexibiliteit opleveren. Dit betekent dat objecten zoals formulieren en rapporten een verzameling standaard waardes hebben waardoor de formulier- of rapportelementen automatisch gepositioneerd worden op scherm of papier. Als de standaard waarde van een eigenschap gewijzigd is, wordt de eigenschap in het vet getoond om zo snel wijzigingen in het eigenschappenblad te kunnen opsporen. Als je eenzelfde eigenschap van meerdere objecten wil wijzigen, selecteer alle knooppunten in de AOT die je wil wijzigen. Je kan eender welk type object in blok wijzigen, en alleen de eigenschappen die gemeenschappelijk zijn aan de geselecteerde objecten, zullen getoond worden in het eigenschappenblad. Als je een waarde van een eigenschap wijzigt, zijn er drie types pictogrammen die gebruikt worden in het eigenschappenblad. De pijl naar beneden wordt gebruikt om te kiezen tussen voorgedefinieerde waardes voor het positioneren of aanpassen van de formulier- of rapportelementen. Het vierkantje wordt getoond om te wisselen tussen voorgedefinieerde waardes en het ingeven van een vaste waarde.

Figuur 3: pictogram voor keuze van voorgedefinieerde waardes

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

25

Het pictogram met de puntjes wordt gebruikt als een nieuw formulier wordt geopend voor bijvoorbeeld het opgeven van labels of het kiezen van een lettertype.

Figuur 4: pictogram met drie puntjes Twee types eigenschappen zijn gekleurd. De AOT naam voor het object is lichtrood weergegeven als de eigenschap verplicht is. Eigenschappen waar je een label kan opgeven, worden geel weergegeven als er geen label is gekend. Dit betekent niet dat een label opgegeven moet worden, aangezien labels voor formulieren en rapporten worden bepaald op basis van de tabel of uitgebreid datatype. Als een label wordt gekozen uit het labelsysteem, verandert de gele kleur in wit. Add-ins Het submenu Add-Ins in de Contextmenu toont tools afhankelijk van het actieve knooppunt. Een standaardset van tools zoals Cross-reference en Check Best Practices kan je best via dit submenu oproepen. Add-ins is het enige submenu in het Contextmenu dat aangepast kan worden. De standaardelementen van het Add-Ins menu worden beheerd via MorphX. Je kan je eigen menupunten aanmaken via MorphX en ze toevoegen aan dit submenu. Het grootste deel van de Add-Ins menupunten kan ook opgeroepen worden via het menu Tools | Development tools. Editor De editor in MorphX wordt gebruikt voor de eigen programmeertaal van Axapta, namelijk X++. Om de editor te openen, kies je een methode in de AOT en dubbelklik of druk op enter. Selecteer een methodeknooppunt of het knooppunt van een object zoals een klasse of een formulier om een methodelijst te openen voor dat knooppunt. Het AOT pad naar het actieve knooppunt wordt getoond in de titelbalk van het editeervenster. Bovenaan het editeervenster, worden pictogrammen getoond voor algemene taken zoals het bewaren, compileren en het plaatsen van breekpunten. Het linkervenster toont de geselecteerde methodes. Als je wil wisselen tussen methodes, klik je op een methode in het linkervenster. Een asterisk (*) na de methodenaam in het linkervenster geeft aan dat de methode is gewijzigd maar de wijzigingen zijn nog niet bewaard. Als je de editor afsluit, zal je gevraagd worden of je de wijzigingen wil bewaren. Het handigste is dat je de auto save optie geactiveerd hebt via Tools | Options, omdat dan je wijzigingen automatisch bewaard worden op geregelde tijdstippen.

MORPHX IT Inleiding tot MorphX

26

© 2007 Steen Andreasen

Figuur 5: De editor met methodes voor de tabel CustTable Codevenster In het rechtervenster kan je de X++ code voor de geselecteerde methode editeren. Gereserveerde woorden worden in het blauw weergegeven. Commentaren zijn groen en text strings staan in het rood. Bij de uitvoering, wordt de opgegeven code gevalideerd en als een fout wordt gevonden, wordt die fout onderlijnd met een rode kartellijn. Bovendien wordt de methode met de compileerfout in het linkervenster eveneens onderlijnd. Voor een overzicht van de belangrijkste hotkeys of sneltoetsen in het codevenster, verwijs ik naar figuur 6: Algemene editeersneltoetsen. Een volledig overzicht van alle sneltoetsen kan je vinden in de Developers Guide bij Help | Microsoft Axapta Developer’s Guide. Als je in het codevenster met de rechtermuisknop klikt, zie je een menu waar je een overzicht van AOT objecten kan bekijken, informatie over je code kan opzoeken en editorscripts kan oproepen. Het oplijsten van AOT objecten zoals tabellen, klassen en uitgebreide datatypes voorkomt typwerk omdat je niet door de AOT moet rollen om de naam van je objecten te vinden. Je kiest gewoon de objectnaam van de lijst. Een alternatief voor het gebruik van de lijsten is het openen van een tweede AOT venster en het verslepen van de objectnaam van het tweede AOT venster naar het codevenster. Eigenlijk kan je eender welk knooppunt in de AOT verslepen naar het codevenster. De naam van het knooppunt wordt in het codevenster toegevoegd. Niettemin zal het verslepen van sommige knooppunten een aantal lijnen in de code toevoegen. Probeer eens een query te verslepen van de AOT naar het codevenster. Je krijgt alle code te zien die je geschreven hebt om de query uit te voeren. Het enige wat je nu nog moet doen, is de variabelen declareren.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

27

De menuopties van het opzoekmenu (lookup menu) worden gebruikt om naar de code van een methode te springen, om het parameterprofiel van een methode te tonen of om een label in het labelsysteem op te zoeken. Als je methode een compilatiefout geeft, geraak je via de opzoekmenuopties niet voorbij de plaats van de fout. Editorscripts zijn een verzameling scripts gemaakt met behulp van X++. Deze scripts worden gebruikt om vaak terugkerende taken uit te voeren zoals het toevoegen van commentaar of het formatteren van de code op een bepaalde manier. Je kan je eigen script toevoegen of een bestaand script wijzigen. De klasse EditorScripts heeft een methode voor elk van de bestaande scripts. Functie Sneltoets Omschrijving

New

ctrl+n Maak een nieuwe methode aan.

Save

ctrl+s Bewaart alle methodes getoond in het linkervenster.

Toggle breakpoint

F9 Plaats een breekpunt of schakel een actief breekpunt uit.

Enable/disable breakpoint

ctrl+F9 Gebruikt om een breekpunt over te slaan zonder het te verwijderen.

Remove all breakpoints

ctrl+shift+F9 Verwijder alle breekpunten geplaatst door de gebruiker.

List breakpoints

Shift+F9 Lijst alle breekpunten op.

Compile

F7 Compileer alle methodes in het linkervenster.

Lookup Properties/Methods

ctrl+space Toon een gele werktip. Afhankelijk van de code die werd aangeklikt, wordt het volgende getoond : basistype van een uitgebreid datatype, parameterprofiel voor een methode of de labeltekst van een label id.

Lookup Label/Text

ctrl+alt+space Als een label id is gemarkeerd, wordt de overeenkomstige label id en labeltekst getoond in het labelsysteem.

Lookup Definitions

ctrl+shift+space Opent de geselecteerde methode in een nieuw editeervenster. Je moet de methodenaam niet markeren. Als de methode niet is overschreven in de actieve laag, wordt er geen editeervenster geopend.

Script

Alt+m Opent het scriptmenu.

MORPHX IT Inleiding tot MorphX

28

© 2007 Steen Andreasen

Functie Sneltoets Omschrijving

List Tables

F2 Lijst alle tabellen op.

List Classes

F12 Lijst alle klassen op.

List Types

F4 Lijst alle uitgebreide datatypes op.

List Enums

F11 Lijst alle base enums op.

List Reserved Words

shift+F2 Lijst alle gereserveerde woorden op.

List Built-in Functions

shift+F4 Lijst alle functies op.

Figuur 6: Algemene editeersneltoetsen Debugger Als je een breekpunt zet op een codelijn, wordt de debugger geactiveerd als de codelijn gebruikt wordt tijdens de uitvoering van het programma. Bij debuggen in een three-tier omgeving, moet je nakijken dat de debugger geactiveerd is op de AOS, anders kan je alleen code debuggen die loopt op de client. Tijdens het debuggen wordt je Axapta client vergrendeld. Als je toegang wil tot de client tijdens het debuggen, kan je een andere Axapta client opstarten. Bedenk wel dat de debugger gekoppeld is aan de client waar de debugger werd geactiveerd. Breekpunten worden bewaard per gebruiker, en dus moet je je geen zorgen maken om andere gebruikers tijdens het debuggen. Wil je een lijst van de huidige breekpunten, druk dan op Shift+F9 op eender welke plaats in de AOT. Opmerking : Als je een error krijgt in de Infolog en je wil de error tracen met de debugger, dan kan je een breekpunt zetten net voordat de error wordt toegevoegd aan de Infolog. Ga naar de klassemethode Info.add() en plaats een breekpunt in de eerste lijn. De broncode die je debugt, wordt getoond in het bovenste venster. De code wordt voorgesteld net zoals in de code-editor. Een breekpunt kan je plaatsen zowel in de code-editor als in de debugger. De sneltoetsen voor het zetten van breekpunten zijn opgelijst in figuur 6: Algemene editeersneltoetsen. Breekpunten worden voorgesteld met een solide rode lijn in de editor. In de debugger worden breekpunten aangeduid met een rode cirkel in de linkermarge. Een gele pijl in de linkermarge geeft de cursor weer. Een toolbar of nutsbalk voor navigatie staat bovenaan het debuggervenster. Je hebt 4 verschillende vensters die je voor het monitoren kan gebruiken in de debugger. Deze vensters kunnen naar believen gepositioneerd en vergroot of verkleind worden.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

29

Opmerking : Verlaat je Axapta client niet tijdens het debuggen. Tijdens het debuggen vergrendel je de tabellen die in het bereik liggen van de code die je debugt. Denk ook aan transactievergrendeling die begint met ttsbgin. Dit zal je niet bepaald geliefd maken als een applicatiegebruiker probeert gegevens te plaatsen in een tabel die je vergrendeld hebt; de applicatiegebruiker gaat immers een databankfoutmelding te zien krijgen.

Figuur 7: de debugger Outputvenster De standaardplaats van het outputvenster is onderaan de debugger. Hier kan je de Infolog opvolgen evenals afdrukcommando’s in de code. Gebruik dit venster als je lijnen hebt toegevoegd aan de Infolog of als je in de code commentaar afdrukt om het verloop van het debuggen op te volgen. Variabelenvenster Dit venster vind je onder het debuggervenster, helemaal links. Het venster lijst alle variabelen op die in het bereik van het codevenster zijn. Alle soorten variabelen zoals tabelvelden, types en klassen worden getoond. Variabelen die gewijzigd zijn tussen twee breekpunten, worden opgelicht. Dit venster geeft je de mogelijkheid om de waarde van eender welke variabele te wijzigen tijdens het debuggen. Het opzoekveld

MORPHX IT Inleiding tot MorphX

30

© 2007 Steen Andreasen

bovenaan het variabelenvenster wordt gebruikt om een methode te selecteren in de call stack. Na selectie van de methode worden variabelen- en debuggervenster aangepast met de geselecteerde methode uit de stack. Call Stack venster Het call stack venster vind je in het midden, onder het debuggervenster. Dit venster geeft je een overzicht van de diverse methodes die werden opgeroepen. Je kan eender welke methode in de call stack of oproepstapel klikken om naar die methode over te stappen. Hierdoor wordt het debuggervenster en het variabelenvenster aangepast. Merk op dat je niet kan overstappen naar een niet-overschreven systeemmethode. Een gele pijl in de linkermarge verwijst naar de huidige methode die gedebugd wordt. Als je naar een andere methode overstapt, zal een groene driehoek de huidig geselecteerde methode aangeven. Observatievenster Dit venster vind je uiterst rechts onder het debuggervenster. Het observatievenster wordt gebruikt om manueel variabelen uit te kiezen die je wil opvolgen gedurende de debugsessie. Dit venster heeft drie gelijkaardige tabbladen die je helpen de informatie te organiseren als je een hoop variabelen aan het opvolgen bent. Wil je een variabele toevoegen, dan markeer je een variabele in het debuggervenster en je versleept de variabele naar het observatievenster. Gebruik de wistoets om een variabele te verwijderen. Net zoals in het variabelenvenster, kan je de waarde van een variabele veranderen die je hebt toegevoegd in het observatievenster. Zolang een variabele in het bereik van de code is, wordt de waarde ervan getoond. Val je buiten het bereik, dan wordt een foutmelding getoond als waarde voor de variabele. Wordt een variabele gewijzigd tussen twee breekpunten, dan wordt de waarde opgelicht. De variabelen in het observatievenster worden bewaard na het beëindigen van de debugsessie. Dit versnelt het debuggen omdat je niet telkens opnieuw van nul af moet starten als je dezelfde code verschillende malen moet debuggen. Compilervenster Als je X++ code compileert, hetzij vanuit de code-editor, hetzij vanuit een knooppunt in de AOT, wordt het compilervenster opgeroepen. Het compilervenster wordt normaal gepositioneerd onderaan het Axaptavenster. Dit neemt ruimte in omdat alle vensters geopend worden in de Axapta Integrated Development Environment (IDE). Je kan klikken met de rechtermuisknop in het compilervenster en No Docking selecteren als je verkiest dat het compilervenster als een standaardvenster reageert. Dit houdt meer van je werkruimte vrij, dat je vooral nodig hebt als je het eigenschappenblad nog wil zien. Je kan gebruik maken van het compilervenster of van het eenvoudiger berichtvenster voor het compileerresultaat. Met versie 3.0 werd het compilervenster ingevoerd. Het

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

31

berichtvenster was al in gebruik in vorige versies. Ga naar het compilervenster en klik op de Setup knop en kies de Compiler menuoptie om de compiler te configureren. De Output definieert het gebruik van het compilervenster of het observatievenster. Het observatievenster toont dezelfde informatie als het compilervenster met een eenvoudiger interface. Je kan een lijn in het observatievenster aanklikken om informatie te bekijken. Als je het observatievenster hebt gekozen voor de output, kan je het compilervenster herselecteren in het menu bij Tools | Options door de compilerknop te selecteren. Het niveau van compilatiecontroles kan gekozen worden in het compiler setup window. Het veld Diagnostic level definieert wat er in de controles hoort. Bij niveau 4 horen controles op de programmatierichtlijnen of de zogenaamde best practices. Kruisverwijzingen worden bijgewerkt indien aangevinkt. Merk wel op dat het bijwerken van kruisverwijzingen het compileren vertraagt. Voor informatie over het beheer van kruisverwijzingen verwijs ik naar de Bijlage MorphX Tools. Een compilatielog kan gemaakt worden tijdens de compilatie. Maar het kan ook eenvoudiger zijn om het resultaat van je compilatie vanuit het compilatievenster te exporteren omdat je dit resultaat weer kan importeren en dan de eigenschappen van het compilatievenster kan gebruiken voor het opzoeken van fouten en waarschuwingen. Het compilervenster bestaat uit 4 tabbladen. Het eerste tabblad geeft een overzicht van de compilatie. Afhankelijk van de instellingen, worden waarschuwingen, foutmeldingen, afwijkingen van de programmatierichtlijnen en taken getoond. Het is aangewezen om de hele applicatie te compileren vooraleer je aanpassingen in productie neemt. Gedurende een volledige compilatie, kan je mogelijk een groot aantal foutmeldingen en waarschuwingen krijgen. Paniek is overbodig want je krijgt dit doorgaans te zien. Een volledige compilatie bestaat uit drie iteraties en alle objecten worden pas herkend tijdens de laatste iteratie. Fouten en waarschuwingen van de compilatie worden getoond op het tweede tabblad. De lijst bevat fouten en waarschuwingen gevonden in methodes en eigenschappenbladen. Fouten worden aangegeven met een rood symbool en waarschuwingen met een geel symbool. Het compilervenster is een standaard Axaptaformulier en dus zijn de standaardmogelijkheden voor het sorteren en filteren van de output beschikbaar. Als je een fout of waarschuwing in de lijst selecteert en dubbelklikt, wordt de methode of het eigenschappenblad met de fout of waarschuwing geopend. Je kan dan de oorzaak van de fout of waarschuwing verbeteren, de wijzigingen aanpassen en het venster sluiten. Zijn de fouten en waarschuwingen verbeterd, dan worden ze van de lijst verwijderd. Als de best practice checks zijn aangevinkt, dan worden afwijkingen op de programmatievoorschriften opgelijst in het derde tabblad. Klik op de Setup knop en kies Best Practices in het compilervenster om de gewenste best practice checks te selecteren. Merk op dat de best practice checks beschouwd worden als richtlijnen. Gebruik je gezond verstand als je de output ervan doorneemt. Ontbrekende labels en het gebruik van basistypes in plaats van uitgebreide types zijn gemakkelijk op te zoeken. Bij de meer complexe programmatievoorschriften moet je niet ingaan op de suggesties tenzij je vertrouwd bent met het resultaat van de wijzigingen.

MORPHX IT Inleiding tot MorphX

32

© 2007 Steen Andreasen

Het laatste tabblad wordt gebruikt om je taken te kunnen opvolgen. Als je de tekst TODO in hoofdletters in een methode zet, dan zal de methode in het laatste tabblad verschijnen gedurende de compilatie. Dit is een aardigheidje, omdat het ons helpt herinneren aan plaatsen in de code die we moeten nakijken voor de wijzigingen in productie gaan. Opmerking : In het Add-ins menu vind je een menuoptie met de naam Compile forward. Deze menuoptie is beschikbaar op klassen, en zal alle klassen compileren die erven van de geselecteerde klasse. Dit is een snelle manier om te compileren voor je je wijzigingen uitprobeert. Import en Export Je hebt twee opties voor het overbrengen van wijzigingen van één systeem naar een ander. Ofwel kopieer je het bestand van een volledige laag, of je exporteert een selectie van objecten naar een tekstbestand. Welke optie je neemt, hangt af van geval tot geval. Het kopiëren van de laagbestanden wordt vaak gebruikt als je de installatie van een klant aanpast, en vaak is dit de enige optie als de klant geen licentie op MorphX gekocht heeft. Om een knooppunt in de AOT te exporteren, selecteer je dit knooppunt , klik met de rechtermuisknop en kies Export. Je kan de export vragen van objectknooppunten zoals tabellen, uitgebreide datatypes, formulieren of klassen. Methodes daarentegen kunnen niet apart geëxporteerd worden. Je kan verschillende objecten tegelijk exporteren door ze te markeren voor export. Plaats vervolgens de cursor op één van de gemarkeerde objecten en klik met de rechtermuisknop. Een dialoogvenster verschijnt als je de export selecteert. Geef hier de naam van het exportbestand op. De huidige laag wordt geëxporteerd tenzij anders gespecifieerd. Je hebt de optie om een andere laag te exporteren door de laag te selecteren in het dialoogvenster. Het is aanbevolen om altijd een laag te selecteren, ook al ben je in de laag die geëxporteerd moet worden, omdat je na expliciete selectie van de laag tenminste zeker bent dat de juiste laag wordt geëxporteerd. Labels die in je code worden gebruikt, kunnen ook geëxporteerd worden. Maar het importeren van geëxporteerde labels vereist kennis van het labelsysteem, dus is deze optie niet aangewezen indien het geëxporteerde bestand moet geïmporteerd worden door een persoon zonder de vereiste technische vaardigheden. Het exporteren van labels heeft wel het voordeel dat zowel code als labels verpakt zijn in één bestand. Dit is handig want niets is zo frustrerend als een bestand importeren en daarna moeten vaststellen dat de labels er niet bij zijn. Geëxporteerde objecten kunnen vergrendeld worden gedurende de export. De geëxporteerde objecten zullen gemarkeerd worden met een sleutelgatpictogram. Dit is mooi, maar verwacht er niet teveel van : vergrendelde objecten kunnen nog altijd gewijzigd worden en iedereen kan een object selecteren en met de rechtermuisknop het object vergrendelen of ontgrendelen. En vaak is het moeilijk om te bepalen wie wat heeft vergrendeld en waarom.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

33

Alle objectknooppunten zoals tabellen, uitgebreide datatypes en klassen, hebben een unieke identificatienummer (of id) in de AOT. Het identificatienummer is een volgnummer gebaseerd op de huidige laag. Dit identificatienummer kan in het geëxporteerde bestand toegevoegd worden. Deze optie wordt later gebruikt bij de import om de volgnummers in de verschillende omgevingen gesynchroniseerd te houden. Om een geëxporteerd bestand te importeren, klik je op het importicoon in de AOT tool bar. Geldige bestanden worden herkend aan de extensie XPO. Bestanden worden geïmporteerd in de huidige laag. Om de huidige laag te controleren, kijk je naar de rechterkant van de statuslijn onderaan het Axaptavenster. Wil je een overzicht van de geïmporteerde objecten, klik dan op details in de importdialoog. Een boomstructuur gelijkaardig aan de AOT wordt getoond met de objecten van het bestand. Bestaande objecten in de AOT worden in het vet gemarkeerd. Normaal worden alle objecten van het bestand geïmporteerd. Je kan objecten weglaten door ze af te vinken in de boomstructuur. Voor objecten die al bestaan in de AOT, kan je een vergelijking doen tussen het object in het bestand en het object met dezelfde naam in de AOT. Hiermee kan je een laatste controle doen van objecten die aangepast zullen worden. Een beschrijving van de vergelijkingstool vind je in de sectie Vergelijken van objecten. Als je de import van labels aanvraagt, worden drie extra tabbladen getoond in het detailvenster. Hier kan je aangeven voor welke talen de labels moeten geïmporteerd worden. De labels in het importbestand worden opgelijst. Je wordt ervan verwittigd indien het labelvolgnummer al gebruikt is in de applicatie. Voor ieder label kan je kiezen tussen een import of het aanmaken van een nieuw labelvolgnummer. Het standaard labelbestand dat gebruikt wordt voor het aanmaken van nieuwe labels wordt getoond in de rechterbovenhoek. Dit standaard labelbestand wordt bepaald in het labelsysteem. Voor meer informatie over het labelsysteem kan je terecht in de sectie Labelsysteem. Vooraleer je de optie kiest om tabellen en klasseonderdelen te wissen gedurende de import, moet je zeker zijn dat je dit concept begrijpt. Als een tabel of klasse wordt geëxporteerd, dan worden enkel gewijzigde onderdelen van tabellen en klassen bewaard in de gewijzigde laag, zoals indexen en methodes, niet de ganse tabel of klasse. Als je aangeeft dat je tabellen en klasseonderdelen wil wissen, dan wordt deze tabel of klasse gewist in de AOT vooraleer de import start van de tabel of klasse. Dit betekent dat alleen de methodes van een klasse die deel uitmaken van het importbestand zullen bestaan na de import. Dit geldt voor lagen waartoe je toegang hebt. Je kan nooit lagen wissen zoals SYS of GLS. Als het geïmporteerde bestand werd geëxporteerd met de id’s van de AOT, dan kan je de optie aanvinken om het object met identificatienummer te importeren. Dit verzekert je ervan dat je applicaties in verschillende systemen gesynchroniseerd blijven omdat de id’s of volgnummers in de AOT identiek zijn. Je zal een data recovery moeten uitvoeren als je importeert met id en voordien heb je een import gedaan van een nieuwe tabel zonder de id van de tabel te importeren. Je wordt verwittigd indien je de

MORPHX IT Inleiding tot MorphX

34

© 2007 Steen Andreasen

id van een tabel met data wijzigt, maar je kan de data recovery check aanvinken als je niet zeker bent. Vink de optie Overwrite locked objects aan als je onzeker bent dat een van de geïmporteerde objecten vergrendeld kan zijn. Vergelijken van Objecten Het vergelijken van objecten kan gebeuren ofwel bij het importeren van een XPO bestand ofwel door het selecteren van een object dat in meer dan een laag bestaat, vervolgens klik je met de rechtermuisknop en je selecteert Compare van de Add-Ins menu. Als je twee lagen vergelijkt, zal het systeem een vergelijkingsvenster tonen. Als je de lookup knop aanklikt om de lagen te kiezen die je wil vergelijken, zal je zien dat dezelfde lagen tweemaal worden opgelijst, met één laag aangeduid als de oude laag. De oude laag komt uit het lagenbestand dat in de oude folder van de applicatie bewaard wordt. Voor informatie over de bestandsstructuur van een Axapta installatie verwijs ik naar de handleidingen in het standaardpakket. Het vergelijken met de oude laag is nuttig als je een upgrade naar een nieuwe service pack van Axapta hebt uitgevoerd, en je wilt nagaan welke wijzigingen er zijn gebeurd op de oude laag van een object. Het vergelijkingsvenster is verdeeld in twee panelen. In het linkerpaneel zie je de boomstructuur van het object. Het rechterpaneel toont het resultaat van de vergelijking voor het knooppunt die in het linkerpaneel is geselecteerd. De vergelijkingsresultaten staan in rood of blauw. Deze kleuren worden gebruikt in het resultaat om het verschil tussen de twee lagen weer te geven. Alleen knooppunten waar er verschillen zijn, worden in de boomstructuur weergegeven. De verschillen in iedere laag worden aangeduid door een kleur. In de boomstructuur geeft een blauw of rood vinkvakje (“ checkbox”) aan dat het knooppunt enkel bestaat in de laag die voorgesteld wordt door de betreffende kleur. Een tweekleurig icoon geeft aan dat het knooppunt is gewijzigd in beide lagen. In figuur 8: Vergelijking van twee lagen van een object, worden twee lagen van het formulier CustTable vergeleken. In het vergelijkingsvenster, zie je lijnen code die enkel in één laag bestaan, weergegeven in de kleur die die laag voorstelt. Een linker- of rechterpijl wordt getoond aan het einde van een gekleurde lijn of gekleurde blok code. Als je klikt op de pijlen, kan je code toevoegen aan of verwijderen uit de huidige laag. Dit versnelt het proces van herinvoering van wijzigingen aan de applicatie gedurende een update, en wordt vaak gebruikt als je een service pack update wil invoeren. Deze tool vergelijkt niet alleen code, maar ook eigenschappenbladen. En net zoals met code, kan je wijzigingen in eigenschapwaardes tussen lagen migreren met behulp van de pijlen om de huidige laag aan te passen. Natuurlijk zijn er beperkingen aan de vergelijkingstool: als een hoop wijzigingen zijn aangebracht aan de twee lagen, ben je misschien beter af met het herschrijven van het object tijdens een upgrade in plaats van een verwarrende mengeling van blauwe en rode lijnen trachten op te lossen.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

35

Figuur 8: Vergelijking van twee lagen van een object Code Upgrade De Code Upgrade tool vind je ook in de Add-ins menu. Terwijl de Compare Object tool gebruikt kan worden voor het vergelijken van eender welk type object, is de code upgrade tool gespecializeerd in het vergelijken van methodes. Deze tool is heel handig voor het vergelijken van wijzigingen van een oude naar een nieuwe laag van een service pack of versie upgrade. Figuur 9: Code upgrade toont een klasse die gewijzigd is in verscheidene lagen. Je ziet de methodes aan de linkerkant. Methodes die gewijzigd zijn in zowel de oude als de nieuwe laag, worden gemarkeerd; je krijgt dus een markering als een methode die bestaat in de SYS laag van de oude versie, gewijzigd is in de nieuwe SYS laag. Als je klikt op een methode die in meer dan één laag is gewijzigd, dan zal Axapta het rechtervenster aanpassen met een tabblad die iedere gewijzigde laag voorstelt. Het eerste tabblad “Workspace” toont steeds de hoogste laag. In de Workspace kan je de methode editeren. Je kan het best de code openen in de editor via de Edit knop. In het submenu Suggestion, zijn de merge knoppen actief als de methode is gemarkeerd. Je kan de merge knoppen gebruiken om de code van alle lagen in de workspace of werkruimte te mengen. Dit helpt je misschien niet om de upgrade van een methode te vervolledigen, maar het zorgt er alvast voor dat je alle code in één plaats hebt. Een of twee lagen worden opgelijst in het submenu Suggestion. Dit zijn de lagen die de code upgrade tool voorstelt als startplaats voor de upgrade. Klik op de laagnaam om de laag in het Workspace tabblad te laden. De Compare knop wordt gebruikt om twee lagen van een methode te vergelijken. De vergeleken lagen worden getoond in een nieuw tabblad met de verschillen ingekleurd. Voor gemarkeerde methodes worden de

MORPHX IT Inleiding tot MorphX

36

© 2007 Steen Andreasen

tabbladen automatisch aangemaakt voor de lagen waar er verschillen zijn. Dit vereenvoudigt de vergelijking tussen de verschillende versies van de gemarkeerde methodes.

Figuur 9: Code upgrade Zoeken Het zoeken in de AOT objecten kan je doen door een knooppunt te selecteren en vervolgens ctrl+f in te toetsen. Een dialoogvenster verschijnt en standaard zal de zoekopdracht gebeuren op de methodes in het geselecteerde deel van de boomstructuur. Methodes die beantwoorden aan de zoekcriteria, worden opgelijst in een overzichtscherm met vermelding van het AOT pad waar het gevonden element zich bevindt. Met dubbelklikken open je de methode. Je kan de zoekopdracht wijzigen om ook in eigenschappen te zoeken. Wijzig de zoekopdracht om All Nodes te selecteren en je krijgt een eigenschaptabblad te zien. Het eigenschaptabblad lijst alle eigenschappen op die in de AOT worden gebruikt, gesorteerd op naam. Als je formulieren wil vinden met een specifieke eigenschapinstelling, selecteer je de eigenschap en geef de eigenschapwaarde op die je zoekt. Als je bijvoorbeeld alle formulieren wil zoeken met de eigenschap AlwaysOnTop, markeer je de eigenschap en je geeft Yes op als waarde van de eigenschap. Formulieren met deze eigenschap, worden dan opgelijst. Merk op dat je niet kan springen naar het eigenschappenblad via de rechtermuisknop. In plaats daarvan selecteer je de zoekoptie, klik je met de rechtermuisknop en je selecteert Properties van het Add-ins menu. Je kan in een methode zoeken via de toetscombinatie ctrl+f. De zoekfunctie gebruikt in methodes heeft een “zoek-en-vervang”-functie gelijkaardig aan die van Microsoft Word. Let wel, als je code wil zoeken en vervangen, zoals het wijzigen van een

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

37

variabele, kan je beter de cross-reference tool gebruiken omdat die je vertelt waar een object wordt gebruikt. Informatie over de cross-reference tool vind je in Bijlage MorphX Tools. Infolog De Infolog wordt gebruikt om te communiceren met de applicatiegebruikers. Je gebruikt de Infolog om gebruikers te informeren over validaties, foutmeldingen of om informatie te tonen bij het verwerken van een job. De Infolog wordt geopend in een apart venster en dit venster opent automatisch wanneer de Infolog wordt opgeroepen door een programma. Informatie in de Infolog wordt verwijderd zodra het Infologvenster sluit; als je deze informatie nog nodig hebt voor later gebruik, dan kan je de inhoud van het Infolog-venster afdrukken via de printmenuoptie in het hoofdmenu. De Infolog kan gestart worden zowel vanuit code als vanuit de kernel. Kernelinformatie gaat doorgaans over problemen rond systeemintegriteit, zoals informatie over verplichte databankvelden. Vanuit X++ kan je de informatie in de Infolog controleren. Er zijn drie informatietypes in de Infolog. Deze types, die aangeduid worden met een icoon, omvatten : info , waaschuwing en foutmelding . Informatieve tekst wordt doorgaans getoond om de gebruiker te informeren over acties uitgevoerd door het systeem. Waarschuwingen en foutmeldingen zijn meestal gekoppeld aan problemen waarvoor de gebruiker een actie moet uitvoeren, en mogelijk geeft dit aan dat een proces niet kan verdergaan. Als een helppagina of een actie gekoppeld is aan de informatie in the Infolog, dan wordt er bij de iconen aangegeven dat er extra informatie beschikbaar is. static void Intro_Infolog(Args _args) { int i; ; info("This is an info."); warning("This is a warning."); error("This is an error."); setprefix("prefix text"); for (i=1; i<=3; i++) { setprefix("1. for loop"); info("loop 1"); } for (i=1; i<=3; i++) { setprefix("2. for loop"); info("loop 2"); } info("Check customer parameters.", "", SysInfoAction_Formrun::newFormname(formStr(CustParameters), identifierStr(Customer_defaultCust), ""));

MORPHX IT Inleiding tot MorphX

38

© 2007 Steen Andreasen

info("Check the sales form help page.", "ApplDoc://Forms/SalesTable"); throw error("This error stop execution.", "");

} Dit is een voorbeeld van het gebruik van de Infolog vanuit X++. De eerste drie lijnen tonen hoe je eenvoudig informatie, een waarschuwing of een foutmelding kan toevoegen. Bij het gebruik van warning() en error() informatie, gebruik je normaal het throw commando zoals getoond wordt op de laatste lijn van het voorbeeld, omdat de throw iedere verdere actie ,zoals het aanpassen van een record, stopt. Als je veel informatie wil toevoegen aan de Infolog, kan je de setprefix() functie gebruiken om de informatie te organiseren en de presentatie gebruikersvriendelijker te maken. Het voorbeeld hierboven toont hoe je een paar geneste lussen kan structureren om de inhoud apart te groeperen in de Infolog. De prefix voor de eerste Infolog entry geeft de buitenste lus weer met aparte geïndenteerde prefixen die gecreëerd zijn voor ieder van de twee lussen die boodschappen weergeven. Een entry in de Infolog heeft drie parameters. De laatste twee parameters zijn opties gebruikt voor het koppelen aan een veld in een formulier of het koppelen aan een helppagina in de online help. De laatste twee info() lijnen tonen hoe je deze optionele parameters kan gebruiken. De eerste info() koppelt de Infolog entry aan het formulier van de customerparameters. Het systeem toont dit formulier als de gebruiker dubbelklikt op de boodschap in de Infolog. De laatste info() koppelt de Infolog entry aan de helppagina van het verkoopsformulier. Merk op dat de optionele parameters altijd apart worden gebruikt omdat je niet gelijktijdig aan een formulier en het helpsysteem kan koppelen. Het gebruik van koppelingen is een gebruiksvriendelijke manier om te communiceren met de gebruiker, zeker als er zich een fout voordoet. Je kan de gebruiker informatie geven over de plaats in het formulier om het probleem op te lossen of extra hulp geven over hoe de foutmelding verholpen kan worden. Jammer genoeg worden wijzigingen aan deze koppelingen niet in het ganse systeem doorgetrokken; na de wijziging van de naam van een helppagina of een formulierveld waarnaar verwezen wordt, moet je zelf manueel alle koppelingen aanpassen. De Infolog kan maximaal 10,000 boodschappen bevatten. De Infolog zou dus niet gebruikt mogen worden voor het rapporteren van gedetailleerde statusboodschappen die de normale activiteit weergeven van batchprogramma’s. je kan programmatorisch de maximale grootte van de Infolog aanpassen, maar besef wel dat de Infolog niet bedoeld is om grote hoeveelheden gegevens te verwerken. Het opbouwen van een Infolog met meerdere duizenden boodschappen vergt tijd en processorcapaciteit. Je bent dan beter af met het gebruik van een rapport.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

39

Een van de fundamentele klassen in Axapta is de Info klasse. De Info klasse bestuurt de Infolog. Voor meer informatie over fundamentele klassen kan je terecht in het hoofdstuk Klassen. Prullenbak (Recycle Bin) De AOT prullenbak of Recycle bin vind je in het hoofdmenu onder File | Open | AOT Recycle Bin. De prullenbak kan je gebruiken om objecten in hun oude versie te herstellen zoals tabellen, uitgebreide datatypes of formulieren. Onderdelen van objecten, zoals de velden van een tabel of methodes van een formulier kunnen niet hersteld worden. Je kan enkel objecten herstellen die tijdens de actieve sessie gewist werden, aangezien de prullenbak wordt geledigd wanneer de Axapta client wordt afgesloten. De gewiste objecten worden getoond in een lijst die gesorteerd is nadat de objecten werden gewist, met de laatst gewiste objecten het eerst vermeld. Als een object met dezelfde naam tweemaal werd gewist, dan verschijnt het object tweemaal in de lijst. Merk op dat een gewiste tabel wel kan hersteld worden maar dat deze tabel geen data meer bevat. Gebruikersinstellingen De gebruikersinstellingen die aan je gebruikersprofiel zijn gekoppeld, kan je beheren via het optieformulier dat je vindt in het hoofdmenu onder Tools | Options. Het gaat hier zowel om algemene instellingen als om instellingen die de ontwikkelomgeving beheren. Om de standaardinstellingen te herstellen, gebruik je de Default knop. Voor informatie over de Compiler knop, verwijs ik naar de sectie Compilervenster. In de onderstaande paragrafen hebben we de vertaling genomen zoals die verschijnt in de Nederlandstalige versie van Axapta. Tussen haakjes staat de Engelse versie. Algemeen (General) Het Algemeen tabblad wordt gebruikt om basisinstellingen te definiëren zoals naam en paswoord. Hier kan je ook de eenmalige aanmelding of single sign-ons beheren, zodat je netwerkgebruikersprofiel gebruikt kan worden om automatisch in Axapta in te loggen. Statusbalk (Status bar) Het Statusbalk tabblad definieert de informatie die getoond wordt in de statusbalk op de onderste lijn van het Axapta venster. Het veld laag van hulppr. weergeven ( Show util layer) is zeer belangrijk : als dit veld is aangevinkt, dan wordt de huidige laag voortdurend weergegeven. Het vinkvakje Waarschuwing bij overschakeling naar andere bedrijfsrekening (“ Warn company accounts change checkbox”) is ook belangrijk en is standaard aangevinkt. Indien het is aangevinkt, zal Axapta een waarschuwing geven als het huidige bedrijf wordt gewijzigd.

MORPHX IT Inleiding tot MorphX

40

© 2007 Steen Andreasen

Lettertypes (Fonts) Je kan de standaard lettertype en lettergrootte instellen in het Lettertypes tabblad. Normaal zijn de standaardinstellingen voldoende. Maar je kan de lettertypesinstellingen gebruiken als je een andere lettergrootte nodig hebt voor je rapporten. Dit is een globale instelling. Als je een bepaald lettertype nodig hebt voor een enkel formulier of rapport, kan je best dit bepaalde object wijzigen. Ontwikkeling (Development) Het zal geen verrassing zijn dat het Ontwikkeling tabblad bijzonder interessant is voor ontwikkelaars. Het wordt gebruikt om het gedrag van de ontwikkelomgeving aan te passen. In de Algemeen(General) veldgroep kan je het project selecteren dat opgeladen moet worden bij de opstart. Dit is handig als je aan hetzelfde project werkt gedurende een redelijk lange periode, omdat je niet telkens weer dit project zelf moet zoeken en openen bij de opstart van je client. Meer informatie over projecten vind je terug in de sectie Project. Het veld Toepassingsobjectlaag ( Application object layer) wordt gebruikt om in te stellen hoeveel informatie er getoond moet worden tussen haakjes na het object in de AOT. De optie Toon alle lagen (Show all layers) geeft je informatie over alle lagen waarin dit object werd gewijzigd en kan je een overzicht geven van de objecten die in iedere laag zijn opgeslagen. Als je het berichtvenster wil gebruiken voor informatie tijdens het tracen of compileren, wil je misschien de grenswaarde voor berichten instellen. Als het veld Waarschuwing over ontwikkeling (Development warnings) is aangevinkt, zal het systeem een groot aantal boodschappen genereren, waarvan een aanzienlijk aantal een twijfelachtige waarde hebben, zoals informatie over selects zonder een index. De Editor veldgroep heeft opties voor het inbrengen en toevoegen van tekst. Als IntelliSense is aangevinkt, worden methodes van het huidige object in de editor opgezocht wanneer er een punt wordt getypt na de objectnaam. Dit is bijzonder handig en zou altijd aangevinkt moeten zijn. Het spaart je tijd uit in je typwerk. Als je een klassenaam intikt gevolgd door een punt, worden alle methodes van de betreffende klasse opgezocht. IntelliSense toont je ook een gele werktip met informatie over basistypes en parameters wanneer de cursor wordt gepositioneerd over de variabelen en methodes in de editor. Het veld debug mode staat standaard op bij onderbrekingspunt (breakpoint). Dit betekent dat de debugger automatisch wordt gestart als je een onderbrekingspunt hebt gezet in je methode en de code die hieraan gekoppeld is, wordt uitgevoerd. De opties van de veldengroep “automatisch” worden gebruikt om automatisch gewijzigde objecten te bewaren. Als automatisch bijwerken (auto save) ingevuld is, worden objecten bewaard op een vast tijdsinterval. Als automatisch vernieuwen (Auto-refresh) aangevinkt is, worden objecten die aangemaakt en gewijzigd zijn door andere ontwikkelaars, automatisch bijgewerkt. Dit is handig als meerdere programmeurs werken aan eenzelfde applicatie en één van de programmeurs creëert een nieuwe tabel of klasse. Het nieuwe object wordt dan automatisch beschikbaar binnen een vastgelegd

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

41

tijdsinterval. Zonder het gebruik van auto refresh moet je je Axapta client herstarten om de wijzigingen van andere programmeurs in de AOT te zien. De limiet voor afvalophaling (garbage collection size) moet bepaald worden, aangezien het aantal overeenstemt met het maximum aantal objecten die buiten bereik zijn en toch in het geheugen worden bewaard. Een hoog aantal verbruikt meer geheugen maar ook minder CPU, omdat het systeem de afvalophalingroutine (garbage collection routine) minder vaak zal uitvoeren. Je kan traceringsopties instellen voor de databank, methodes, client/server en activeX calls. Het berichtvenster wordt gebruikt voor het weergeven van de traceringsopties. Let wel, het toestaan van een methodeoproep zal een aanzienlijk aantal lijnen in het berichtvenster genereren, omdat iedere methode wordt opgelijst. Als je het eigenschappenblad alfabetisch gesorteerd wil, kan je het veld alfabetisch sorteren (Sort alphabetically) aanvinken . Meer informatie over eigenschappen vind je onder de sectie Eigenschappen. SQL Databanktraceringsopties stel je in op het SQL tabblad. Vink het SQL trace veld aan om tracering toe te laten. Je kan bepalen of je het traceringsresultaat wil zien in het berichtvenster, de Infolog, de databank of weggeschreven in een bestand. Je zou enkel de traceringsopties mogen gebruiken als je je code wil optimaliseren. Het weergeven van de traceringsinformatie op scherm via het berichtvenster of de Infolog geeft je een snelle manier om naar de code door te gaan. Als je een traceringslijn dubbelklikt, wordt een formulier geopend met informatie over de getraceerde lijn; dit formulier staat je toe om de code te editeren die de getraceerde lijn heeft gegenereerd. Bevestiging (Confirmation) Bevestigingsopties zijn belangrijk voor applicatiegebruikers. De verschillende velden geven de diverse tabelgroepen weer. Meer informatie over de tabelgroepen vind je in het hoofdstuk Data Dictionary. De standaardinstellingen vereisen alleen dat de gebruiker het verwijderen van records bevestigt. Bij het uittesten van de applicatie is het aangewezen dat je dezelfde instellingen als de gebruikers hebt. Vooraf inlezen (Preload) Dit is een lijst tabellen waarvan alle records in het cachegeheugen worden geplaatst. Voor de instellingen van table caching kan je terecht in Data Dictionary. Tabellen met records waar weinig naar verwezen wordt, worden vaak aangeduid om vooraf ingelezen te worden. Als je een Axapta installatie hebt met veel records in één van de vooraf ingelezen tabellen, moet je nakijken of het niet beter is het vooraf inlezen van deze tabel terug af te zetten. De lijst is enkel een standaardinstelling en zou altijd nagekeken moeten worden voor iedere Axapta installatie.

MORPHX IT Inleiding tot MorphX

42

© 2007 Steen Andreasen

Gebruiksgegevens (Usage data) De knop Gebruiksgegevens ( Usage data) toont de instellingen voor objecten zoals formulieren, rapporten en uitvoerbare klassen. Als een gebruiker de opmaak van een formulier wijzigt, de query van een formulier verandert of waardes intikt in een rapportdialoog, worden deze instellingen bewaard. De gebruiksgegevens worden bewaard in de systeemtabel SysLastValue. Voor iedere gebruiker bestaat er slechts één record per object, omdat enkel de waarde van de laatste uitvoering wordt bewaard. Als je de gebruiksgegevens voor alle gebruikers wil zien, kan je het gebruiksgegevensformulier oproepen via Tools | Development Tools | Application Objects | Usage data. Het gebruiksgegevensformulier toont de inhoud van de systeemtabel SysLastValue voor de huidige gebruiker, onderverdeeld in tabbladen voor ieder type object. Het algemeen (general) tabblad heeft een knop om de inhoud te wissen in SysLastValue voor de huidige gebruiker. Dit is bijzonder handig als je je wijzigingen wil testen, omdat je dan kan testen met dezelfde instellingen die de applicatiegebruiker zal hebben de eerste maal dat het formulier of rapport wordt uitgevoerd. Het testen van wijzigingen met gebruiksgegevens opgeslagen voor de objecten is een vaak voorkomende bron van fouten, omdat een object anders kan handelen als er reeds gebruiksgegevens zijn, zoals wanneer een bereik (range) op de query is gedefinieerd. Het tabblad Alle gebruiksgegevens ( All usage data) lijst de objecten op van alle gebruiksgegevenstabbladen. Merk op dat dit tabblad ook de gebruiksgegevens voor klassen vermeldt. Best Practice of voorschriften Deze knop roept een formulier op waar de gebruiker de opties voor de voorschriftenvalidatie (best practice validation) kan instellen. Standaard zijn alle validaties ingeschakeld, maar je kan het formulier gebruiken om bepaalde validatiecontroles uit te schakelen. Het wijzigen van de voorschriftenopties kan ook gebruikt worden als je één enkele voorschriftcontrole wil doen op al je wijzigingen zoals de controle voor ontbrekende labels. Meer informatie over de voorschriften of best practice vind je in de sectie Compilervenster.

MORPHX IT Inleiding tot MorphX

© 2007 Steen Andreasen

43

1.2 Project Het AOT projectvenster wordt geopend door te klikken op het Projectpictogram in het hoofdmenu. Merk op dat AOT Project niets te maken heeft met de Project applicatiemodule. Om het onderscheid te maken spreken we van AOT Project. Een project wordt ook gebruikt om een subset van de AOT te groeperen. Objecten in een project gedragen zich hetzelfde als in de AOT, wat betekent dat je toegang hebt tot hetzelfde eigenschappenblad en dezelfde menus als je het object selecteert en met de rechtermuisknop klikt. Een object mag gelijktijdig voorkomen in een aantal projecten; de objecten zullen toch alleen maar bestaan in de AOT. Je moet projecten beschouwen als koppelingen naar een AOT object. Als een object dat is toegevoegd aan een project, wordt gewist of hernoemd in de AOT, zal de koppeling naar het AOT object nog steeds bestaan in het project en het pictogram voor het object in het project zal wijzigen. Projecten voorzien niet in versiecontrole, maar het groeperen van wijzigingen in projecten geeft je een beter overzicht van de objecten die gewijzigd zijn om nieuwe mogelijkheden aan te bieden. Door het onderbrengen van maatwerk voor de klant in projecten, vereenvoudig je ook het applicatie upgrade proces. Opmerking : als je de voorbeelden uit dit boek hebt geïmporteerd, zal je een lijst van projecten zien met het voorvoegsel MORPHXIT. Wijzigen van een project Het projectvenster toont alle projecten in een lijst. Om een project te openen, selecteer je het projectknooppunt en je dubbelklikt. Het geselecteerde project wordt geopend in een nieuw venster. Om een object aan een project toe te voegen, kan je een knooppunt in de AOT verslepen, of je kan met de rechtermuisknop klikken, kies daarna New en selecteer een AOT knooppunttype. De lijst van beschikbare knooppunttypes omvat een speciaal knooppunttype, Group, wat gebruikt wordt om objecten per type te groeperen zoals in de AOT. Als je een groepknooppunt creëert, moet je in de groepseigenschappen het type object aangeven dat in de groep bewaard wordt. Je kan een groepstype opgeven zoals tabellen (Tables) of formulieren (Forms). Het bepalen van het groepstype zal het icoon overnemen van dezelfde groep in de AOT. Eens het groepstype bepaald, kan je enkel objecten van dit bepaalde type toevoegen. Je hebt misschien al opgemerkt dat het groepknooppunt een eigenschap GroupMask heeft. Een groepsmasker wordt gebruikt voor het filteren met behulp van jokertekens (wildcards). Het definiëren van een groepsmasker doe je doorgaans bij het opzetten van een project, omdat een groepsmasker ervoor zorgt dat het systeem de inhoud van de groep vervangt met die objecten van de AOT die beantwoorden aan de opgegeven criteria. De iconenbalk bovenaan het projectvenster van een geselecteerd project is gelijkaardig aan de iconenbalk in de AOT. Hiermee kan je de inhoud van een project exporteren. Je hebt een extra filtericoon dat je niet in de iconenbalk van de AOT hebt. Het

MORPHX IT Inleiding tot MorphX

44

© 2007 Steen Andreasen

filtericoon wordt gebruikt om een project op te bouwen aan de hand van de gekozen filteropties. De projectobjecten kunnen gegroepeerd worden zoals in de AOT door AOT groepering te selecteren. Klik op de Select knop in de filterdialoog en je kan opgeven welke AOT objecten erbij moeten. De filteroptie doorzoekt de systeemtabel UtilElements, die informatie bevat over ieder object in de AOT. Als je bijvoorbeeld een project wil bouwen met alle objecten die in een bepaalde laag zijn gewijzigd, dan kan dit door het bereik te definiëren van het veld UtilElements.utilLevel. Projecttypes Er zijn twee groepen AOT projecten : private en gemeenschappelijke (“private and shared”). Private projecten zijn alleen zichtbaar voor de gebruiker die het project heeft aangemaakt. Gemeenschappelijke projecten zijn zichtbaar voor alle gebruikers. Je zou enkel private projecten mogen gebruiken voor testdoeleinden of gelijkaardige taken. Het gebruik van gemeenschappelijke projecten betekent dat meer dan één ontwikkelaar tegelijk aan het project kan werken. Een project aanmaken doe je door het private of gemeenschappelijke projectknooppunt te selecteren, klik met de rechtermuisknop en kies New. Behalve het onderscheid tussen privaat en gemeenschappelijk, zijn er ook nog drie projecttypes. Het eerste projecttype heet simpelweg project en is het standaard projecttype. Dit soort project maak je aan als je ctrl+n intikt op het projectknooppunt. De andere twee projecttypes, Web project en Help Book, maak je aan met behulp van X++. Door het uitbreiden van de systeemklasse ProjectNode kan je je eigen projecttypes aanmaken. Het icoon in de projectlijst geeft het projecttype weer. Bijkomende opties kunnen aan het projecttype toegevoegd worden door een menuoptie toe te voegen aan de Contextmenu

1.3 Samenvatting Aan het einde van dit hoofdstuk moet je weten hoe je toegang krijgt tot de ontwikkelomgeving en hoe je in de AOT moet navigeren. Je moet een basiskennis hebben van de structuur van een Axapta applicatie met behulp van de lagentechnologie en hoe deze lagen gebruikt worden in de AOT. Het volgende hoofdstuk gaat een stap verder en geeft je een inleiding tot de programmeertaal van MorphX, met name X++.

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

45

2 Inleiding tot X++ De programmeertaal in de MorphX Development Suite wordt X++ genoemd. Dit is een object-georiënteerde taal die gemaakt is om de logica voor de Axapta applicatie te schrijven. Je zult je misschien afvragen waarom ze weer een andere programmeertaal hebben gemaakt enkel voor Axapta. Dit is in feite de sleutel tot de flexibiliteit van Axapta, omdat X++ geoptimaliseerd is voor het maken en het aanpassen van bedrijfsobjecten. De taal is eenvoudig met een geïntegreerde SQL syntax, zodat er geen behoefte is om data source connecties op te zetten en te beheren. X++ is ook nauw geïntegreerd met de MorphX tools zoals de formuliergenerator en de rapportgenerator. Als je logica wil toevoegen aan je formulieren en rapporten, heb je verschillende standaardmethodes waar je je logica aan kan vasthangen. X++ is gemaakt met C++. De C++ kernel broncode is niet beschikbaar. Je hebt alleen een set systeemobjecten waarvan de parameterprofielen zichtbaar zijn. Alle X++ broncode is open source. Je kan je X++code niet verbergen en niets van de X++code gebruikt voor het standaardpakket is verborgen. Dit is een groot voordeel, omdat je snel zal leren het standaardpakket na te kijken vooraleer je eigen aanpassingen van nul op te bouwen. Vaak kan je bruikbare code vinden om je op het juiste spoor te zetten. De taal heeft een syntax die gelijkaardig is aan Java, gecombineerd met de mogelijkheid om SQL-achtige instructies te schrijven om data te manipuleren. Hoewel X++ een object-georiënteerde taal is, heb je niet de mogelijkheid om alle types van objecten over te erven zoals in C#. Klassen in X++ kunnen overgeërfd worden zoals in eender welke object-georiënteerde taal. Daarnaast kan je uitgebreide datatypes overerven en een verzameling van fundamentele klassen geeft de optie om algemene wijzigingen te maken voor de gebruikersinterface zoals het veranderen van het gedrag van alle formulieren. Een beschrijving van alle fundamentele klassen vind je in het hoofdstuk Klassen. Als je de basishandelingen van de X++taal wil uitproberen, kan je X++scripts maken, die het AOT-knooppunt Jobs gebruiken. Voor meer informatie over jobs verwijs ik naar het hoofdstuk Jobs.

2.1 Variabelen In X++ worden variabelen gedeclareerd aan de top van de editor voor de codelijnen. De variabelen zijn vaak gescheiden van de codelijnen door een lijn die alleen een puntkomma bevatten. Dit is te wijten aan de manier waarop de compiler de code vertaalt. Als je de puntkomma niet toevoegt, kan je een fout krijgen bij het compileren. De puntkomma is niet nodig voor alle gevallen, maar het is nu een aanvaarde Axapta standaard om een puntkomma te gebruiken in een methode op het einde van een gegevensdeclaratie. Om variabelen te declareren in X++ is het het beste om uitgebreide datatypes te gebruiken in plaats van basistypes. De reden hiervoor is dat uitgebreide datatypes informatie bevatten zoals een stringlengte en of een string links of rechts is uitgelijnd. Omdat variabelen kunnen gebruikt worden doorheen de applicatie, kan de ontwikkeling

MORPHX IT Inleiding tot X++

46

© 2007 Steen Andreasen

versneld worden als je het uitgebreide datatype verandert in plaats van de variabelendeclaraties te veranderen; meer informatie daarover vind je in het hoofdstuk Data Dictionary. Wanneer je variabelen benoemt, kan je best namen gebruiken die zin hebben in de context. Voor een variabele die de klanten telt, kan je best de naam noOfCustomers gebruiken in plaats van een éénlettervariabele zoals i. Als je een al gebruikt woord voor je variabele gebruikt, zal de compilatie mislukken. Gereserveerde woorden worden blauw gekleurd in de X++ editor. Je kan een compleet overzicht vinden van de gereserveerde woorden in de AOT onder systeemdocumentatie. De syntax om een variabele te declareren zie je hieronder : CustAccount MyCustAccount; Hier wordt een variabele van het uitgebreide datatype CustAccount gedeclareerd. CustAccount behoort tot het basistype string. Eerst moet je de naam van het type ingeven en vervolgens de naam van de variabele. Merk op dat de lijn wordt beëindigd met een puntkomma. Een puntkomma is nodig op het einde van elke instructie in X++. Je kan meer dan één variabele van hetzelfde type declareren in dezelfde instructie door de namen van de variabelen te scheiden door een komma. Alle basistypes worden beschreven in figuur 10: Basistypes in X++. Je kan meer dan één variabele op dezelfde lijn declareren als volgt:

CustAccount MyCustAccount1, MyCustAccount2, MyCustAccount3; Dit zal goed werken, maar je moet zo’n declaratie toch voorzichtig gebruiken. De compiler zal het type van de eerste variabele zonder enig probleem herkennen. Als één van de volgende variabelen MyCustAccount2 of MyCustAccount3 dezelfde naam als een tabel of een klasse hebben, zal de code gecompileerd worden met errors. Opmerking: X++ heeft niet de optie om variabelen te declareren als een constante . Als je een constante moet definiëren, moet je macro’s gebruiken. Informatie over macro’s vind je in het hoofdstuk Macro’s.

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

47

Basistype Omschrijving Str

Bevat alfanumerische karakters. Voor stringoperaties moet je kijken naar de functies met als prefix str.

Int

Numerische waarden. De functies minInt() en maxInt() kunnen gebruikt worden om de laagste en de hoogste waarde van de gehele getallen op te leveren.

Real

Decimale waarden.

Date

De datum begint te tellen vanaf de start van het jaar 1901. Als je de datum converteert naar een geheel getal, dan zal de waarde het aantal dagen zijn sinds begin 1901. De hoogste datum is 31 december 2155. De Global class heeft verschillende methodes met als prefix “date”, die je kan gebruiken voor datumoperaties. Om de volgende en de vorige datum voor een week, maand, kwartaal en een jaar te berekenen, kan je een beroep doen op de “date_” functies.

Timeofday Telt het aantal seconden sinds middernacht. Dit is in feite een systeemtype, in plaats van een basistype, maar Timeofday kan gekozen worden als één van de basistypes voor een uitgebreid datatype in de AOT.

Enum

Een enum is een enumeratie of vaste lijst van waarden. Om enums te declareren, moet je één van de enums gebruiken die in de AOT gemaakt zijn. Een van de meest gebruikte enums is Boolean, die de waarde juist of fout bevat. Enums kunnen maximaal 255 elementen bevatten.

Container

Een container is zoals een array, behalve dat een container verschillende basistypes kan bevatten. Containers worden typisch gebruikt voor het bewaren van de waarden van een query. X++ voorziet verschillende functies voor containers te handhaven. De naam van de functies begint met “con”.

Anytype

Anytype wordt vaak gebruikt in het parameterprofiel, aangezien anytype kan geïnitialiseerd worden met eender welk basistype. Anytype wordt op een basistype gezet wanneer hij geïnitialiseerd wordt. Een gebruikelijk voorbeeld van anytype is de globale methode Global::query Value().

Figuur 10: Basistypes in X++ Variabelen kunnen geïnitialiseerd worden in dezelfde lijn als de declaratie. Dit doe je door =<Startwaarde> na de variabelenaam in te tikken. Sommige basistypes staan een automatische conversie toe, zoals het omzetten van een geheel getal naar een reële waarde. De compiler zal je verwittigen wanneer je met typeconversies informatie zal verliezen. De compiler zal ook een fout opvangen als je een ongeldige initialisatie probeert, zoals het omzetten van een geheel getal naar een string.

MORPHX IT Inleiding tot X++

48

© 2007 Steen Andreasen

static void Intro_BaseTypes(Args _args) { Description myString = "A X++ string"; Counter myInteger = 100; Qty myReal = 12.25; TransDate myDate = str2Date('12-31-2005', 213); TimeHour24 myTime = str2Time('14:05'); NoYesId myEnum = NoYes::Yes; PackedQueryRun myContainer = ['12', 'test', 'tada']; AnyType myAnyType = systemdateget(); ; print myString; print myInteger; print myReal; print myDate; print myTime; print myEnum; print conPeek(myContainer, 1); print myAnyType; pause; } Dit is een voorbeeld van hoe je eenvoudige basistypes declareert. Hier worden alle variabelen geïnitialiseerd op hetzelfde moment en afgedrukt op het scherm. Als je het type van een variabele wil nakijken, kan je dit doen door op ctrl+spatiebalk in te drukken op de naam van de variabele. Het basistype van de variabele zal getoond worden als een gele werktip. Twee verschillende stringfuncties worden gebruikt voor het initialiseren van de datum en tijdsvariabelen. Hoe je deze functies gebruikt in X++, wordt later beschreven in dit hoofdstuk. Merk de notatie voor het initialiseren van een enum op. Eerst wordt de enum naam gespecifieerd, gevolgd door twee dubbele punten (::) en dan een element van de enum. Zo zien we hierboven dat de variabele myEnum een enumeratie is van het type NoYes en de waarde “Yes” bevat. De containervariabele wordt geïnitialiseerd met een lengte van 3. Een container kan eender welk basistype of zelfs een andere container bewaren. Je krijgt toegang tot de containers door de containerfuncties te gebruiken. Hier wordt het eerste element in de container afgedrukt. In dit voorbeeld zal de variabele van het type datum zijn, aangezien hij wordt geïnitialiseerd met een functie die de huidige datum teruggeeft. Merk op dat de tekst tussen aanhalingstekens wordt gezet. Je kan enkele en dubbele aanhalingstekens gebruiken om een string te definiëren in X++. Het maakt niets uit voor de compiler welke notitie je gebruikt. Ervaring zegt dat dubbele aanhalingstekens kunnen gebruikt worden voor tekst die getoond wordt aan de applicatiegebruikers en enkele aanhalingstekens voor tekst die alleen gebruikt wordt in code.

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

49

Basistypes kunnen ook gedeclareerd worden als arrays. Om een variabele te declareren als een dynamische array, voeg je vierkante haken [] toe na de variabele naam. Je kan twee parameters specifiëren tussen de haken voor de array. De eerste parameter wordt gebruikt om de lengte te bepalen van de array en de tweede parameter wordt gebruikt als je een lange array hebt en alleen maar een gedeelte van de array in het geheugen wilt bewaren. Merk op dat X++ alleen arrays met één dimensie aankan. static void Intro_Array(Args _args) { CustAccount myCustAccount[]; ; MyCustAccount[1] = "4000"; MyCustAccount[3] = "4001"; } In dit voorbeeld maak je een array van het uitgebreide datatype CustAccount. Het eerste en derde element van de array worden geïnitialiseerd. Wanneer je arrays gebruikt, ben je niet verplicht de elementen te initialiseren. Je zal misschien de systeemklasse Array hebben opgemerkt. Als je een array van het complexe type nodig hebt, bijvoorbeeld voor een array van objecten, zal je een systeemklasse Array nodig hebben. De Array klasse is één van de basisklassen en wordt beschreven in het hoofdstuk Klassen. De containervariabele en het declareren van een variabele als een array zijn allebei voorbeelden van complexe datatypes in X++. Wat verwarrend kan zijn, omdat de containervariabele in de lijst staat als één van de basistypes en een container in Morphx wordt behandeld als een basistype. Tabellen en klassen zijn de andere twee complexe datatypes in X++. Opmerking: in X++ moet je je geen zorgen maken over de toewijzing van het geheugen. Wanneer een object niet langer wordt gebruikt, zal de garbage collector automatisch het object verwijderen en het geheugen vrijmaken. De garbage collector kan je specifiek instellen per gebruiker in het menu Tool/Options.

MORPHX IT Inleiding tot X++

50

© 2007 Steen Andreasen

2.2 Operatoren X++ ondersteunt enkelvoudige en tweevoudige operatoren. Enkelvoudige operatoren hebben maar één operand nodig; tweevoudige of binaire operatoren hebben twee operands nodig. De drievoudige operator, een operator met drie operands, wordt beschreven in de paragraaf Controlestructuurinstructies. Operatoren hebben een voorgedefinieerde prioriteit die bepaalt in welke volgorde de operatoren worden uitgevoerd. Als een uitdrukking meer dan één operator gebruikt, wordt de voorgedefinieerde prioriteit gebruikt om de volgorde van verwerking van de operatoren te bepalen. x + y * z Vermenigvuldiging heeft een hogere prioriteit dan som, dus zal y*z verwerkt worden voor x + y. Je mag haken gebruiken om de prioriteit van de operatoren te wijzigen. (x + y) * z Het toevoegen van haken zorgt ervoor dat x+y eerst wordt verwerkt. Ook al ken je de prioriteiten van de operatoren, je kan best altijd haken gebruiken, omdat het de code leesbaarder maakt. Toewijzingsoperatoren Toewijzingsoperatoren worden gebruikt om een waarde te veranderen. Dit kan een variabele toewijzing zijn of het resultaat van een berekening. Operator Uitdrukking Omschrijving = x = y Noteer in de variabele x de waarde van y.

+ x + y Telt de waarde van y bij de waarde van x.

- x - y Trekt de waarde van y af van de waarde van x. De

operator kan ook gebruikt worden als een éénvoudige operator als prefix voor gehele of reële waarden.

*

x * y Vermenigvuldigt x met y.

/

x / y Deelt x door y. Indien y de waarde 0 heeft, zal de compiler de foutmelding geven “deling door 0”.

DIV

x DIV y Zal een gehele deling van x met y maken. Het resultaat wordt naar beneden afgerond.

MOD

x MOD y Zal een gehele deling van x met y maken en de rest als een geheel getal weergeven.

++

++x Verhoogt de waarde van x met 1. Dit is de korte schrijfwijze voor x=x+1. Dit is een enkelvoudige operator die slechts één operand behoeft. De operator kan gebruikt worden als voor- en navoegsel. Het

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

51

Operator Uitdrukking Omschrijving resultaat zal niet verschillen of je de operator als voor- of als achtervoegsel gebruikt.

--

--x; Verlaagt de waarde van x met 1. Dit is de korte schrijfwijze voor x=x-1. Is een enkelvoudige operator die slechts één operand behoeft. Hij werkt hetzelfde of je hem nu als voor- of als achtervoegsel gebruikt.

+=

x += y Telt bij x de waarde y op. Dit is de korte schrijfwijze voor x=x+y.

-=

x -= y Trekt bij x de waarde y af. Dit is de korte schrijfwijze voor x=x-y.

Figuur 11: Toewijzingsoperatoren Relationele operatoren Een relationele operator zal als resultaat juist of fout weergeven voor een uitdrukking. Dit wordt gebruikt bij een controlestructuurinstructie in een als-dan uitdrukking om het verdere verloop van het programma te beheren. Wanneer je data selecteert van een tabel, gebruik je relationele operatoren om het aantal opgehaalde records te beperken. Operator Uitdrukking Omschrijving >

x > y Groter dan: Waar als x groter is dan y.

>=

x >= y Groter dan of gelijk aan: Waar als x groter is dan of gelijk is aan y.

<

x < y Kleiner dan: Waar als x kleiner is dan y.

<=

x <= y Kleiner dan of gelijk aan: Waar als x kleiner dan of gelijk is aan y.

==

x == y Gelijk aan: Waar als x gelijk is aan y.

!=

x != y Niet gelijk aan: Waar als x niet gelijk is aan y.

like

x like y Gelijke: Waar als x gelijk is aan y. De operator like gebruikt de jokertekens * en ? om de uitdrukking te evalueren. Vaak wordt like gebruikt om die records op te halen waar een deel van het veld overeenkomt met een opgegeven waarde. Denk hierbij aan het gebruik van “*Peeters*” om alle klanten op te zoeken waarvan de naam “Peeters” bevat.

&&

x && y Logische en: Dit is één van de conditionele operatoren. Zal waar zijn als x en y allebei waar zijn. Vaak wordt deze operator gebruikt in combinatie met andere relationele operatoren, waar && gebruikt wordt om twee uitdrukkingen te valideren.

||

x || y Logische of: Waar als x of y waar zijn of als x en y allebei waar zijn. Dit is ook een conditionele operator, vaak gebruikt om twee uitdrukkingen te

MORPHX IT Inleiding tot X++

52

© 2007 Steen Andreasen

Operator Uitdrukking Omschrijving valideren.

!

!x Logische niet: Zal waar zijn als x fout is. Dit is de enige enkelvoudige relationele operator.

Figuur 12: Relationele operatoren Opmerking: Als je de uitdrukking x&&y gebruikt en x is fout, dan zal y niet geëvalueerd worden, omdat && alleen waar oplevert als beide operands waar zijn. Dit is nuttige informatie, omdat je je code kan optimaliseren door vertragende expressies aan de rechterkant te plaatsen. Denk hierbij aan expressies die toegang tot de databank nodig hebben. Als je deze expressies rechts plaatst, zal de databanktoegang pas nodig zijn als de linkerkant van de expressie alvast waar is. Bitoperatoren Zoals de naam het aangeeft, worden bitoperatoren gebruikt om uitdrukkingen te evalueren door het gebruik van rekenkundige en conditionele operatoren op bitniveau. Deze bitoperatoren kunnen alleen gebruikt worden met gehele getallen. Een situatie waarin je bitoperatoren gebruikt, is als je een alternatief wil voor het zetten van variabelen naar waar of fout terwijl je de toegangscontroles met verschillende niveaus moet controleren. Maar het gebruik van bitoperatoren maakt je code moeilijker leesbaar. Dit kan de reden zijn waarom deze operatoren niet veel worden gebruikt in de standaard applicatie. In feite zal je enkel bitoperatoren gebruiken, wanneer je een interface schrijft met externe applicaties zoals direct inwerken met Windows API of eender welke operatie van laag niveau op de databank. Om het resultaat van het gebruik van bitoperatoren te begrijpen, zal je de gehele getallen moeten vertalen naar binaire nummers. 13 & 10 // 1101 “bitwise and” 1010 De uitdrukking “13 & 10” zal 8 als resultaat geven. Dit is omdat 13 en 10 bit per bit worden vergeleken en waar de bits van beide getallen een “1” bevatten, noteren we opnieuw een “1”. Alleen de vierde bit is gelijk en daarom is het resultaat 8.

1 1 * 2 ^ 3 8 1 1 * 2 ^ 3 8 1 1 * 2 ^ 3 8 1 1 * 2 ^ 2 4 0 0 * 2 ^ 2 0 0 0 * 2 ^ 2 0 0 0 * 2 ^ 1 0 1 1 * 2 ^ 1 2 0 0 * 2 ^ 1 0 1 1 * 2 ^ 0 1 0 0 * 2 ^ 0 0 0 0 * 2 ^ 0 0 Totaal 13 totaal 10 totaal 8

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

53

Operator uitdrukking Omschrijving <<

x << y Shift links: De binaire waarde van x zal y-aantal posities opschuiven naar links. Dit zal de waarde van x verhogen.

>>

x >> y Shift rechts: De binaire waarde van x zal y-aantal posities opschuiven naar rechts. Dit zal de waarde van x verlagen.

&

x & y Binaire EN van x en y: De bits die op dezelfde positie staan voor x en y geven het resultaat.

|

x | y Binaire OF van x en y: Bits van x en y zullen gesommeerd worden.

^

x ^ y Binaire XOF van x en y: De bits die niet op dezelfde positie staan voor x en y zullen het resultaat weergeven.

~

~x Binaire NOT van x: Alle bits zullen omgekeerde worden.

Figuur 13: Bitoperatoren

2.3 Controlestructuurinstructies Code in X++ wordt sequentieel uitgevoerd. Vaak is het noodzakelijk om een blok code een aantal keer uit te voeren of alleen een deel van de code uit te voeren als er een voorwaarde vervuld is. Hiermee wordt als het ware de verhaalstroom van de applicatie bepaald. We spreken dan van control flow of controlestructuur. Controlestructuurinstructies worden in combinatie met relationele operatoren gebruikt om voorwaarden op te zetten die mee bepalen hoe een code wordt uitgevoerd. Lussen Een lus wordt gebruikt om hetzelfde codeblok te herhalen. Een uitdrukking kan voor de lus gezet worden om het aantal herhalingen van de lus te bepalen. static void Intro_While(Args _args) { Counter counter = 1; ; while (counter <= 10) { info(strfmt("while loop %1", counter)); counter++; } } Dit is een voorbeeld van een while lus. Een geheel getal dat gebruikt wordt om het aantal iteraties te controleren, wordt op de waarde 1 gezet. De while lus zal uitgevoerd

MORPHX IT Inleiding tot X++

54

© 2007 Steen Andreasen

worden zolang de teller kleiner is dan of gelijk is aan 10. Voor elke lus wordt er een lijn geprint op het infologboek en wordt de teller verhoogd met 1. De functie strfmt() wordt gebruikt om eender welk basistypevariabele in een string te veranderen. Aangezien de info() methode alleen een string aanvaardt als zijn eerste parameter, moet de teller het formaat krijgen van een string. Strfmt() gebruikt de notatie %<variabel getal> om variabelen in te voegen in de tekst. Merk op dat de while lus begint en eindigt met accolades {}. De accolades duiden het begin en het einde van een codeblok aan. Alleen code tussen de accolades zal herhaald worden. Het is niet verplicht om begin- en eindaccolades te plaatsen, maar als de accolades niet worden toegevoegd zal alleen de eerste lijn van de code na de while worden herhaald. Als de accolades in dit geval zouden worden overgeslagen, zou de lus nooit eindigen. Het wordt aangeraden om begin-en eindaccolades toe te voegen, zelfs al bevat de code één lijn, omdat het de code leesbaarder maakt. Als je dan later meer lijnen moet toevoegen, is het codeblok dat herhaald moet worden, al bepaald. In X++ worden while lussen vaak gebruikt om data op te halen van de databank. Een voorbeeld van het gebruik van lussen voor de selectie van data vind je in de paragraaf Selectie-uitdrukkingen. static void Intro_DoWhile(Args _args) { Counter counter = 1; ; do { info(strFmt("do-while loop %1", counter)); counter++; } while (counter <= 10); } Een do-while lus is gelijkaardig aan een while lus. Het grote verschil is dat een do-while lus altijd op zijn minst één maal zal uitgevoerd worden, aangezien de voorwaarde van de lus wordt verwerkt na elke iteratie. Een while lus, waar de voorwaarde wordt geëvalueerd aan het begin van elke iteratie, zal misschien niet uitgevoerd worden als niet aan alle voorwaarden van de lus voldaan werd. Het voorbeeld toont dezelfde code die gebruikt werd in de while lus. Merk op dat de while voorwaarde in een do-while lus moet eindigen met een puntkomma. static void Intro_For(Args _args) { Container names = ["Lay", "Kai", "Zbigniew", "Rolf", "Memed"]; Counter counter; ; for (counter=1; counter <= conlen(names); counter++) { info(strFmt("%1: Name: %2", counter, conpeek(names, counter))); } }

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

55

Een for lus functioneert zoals een while lus maar biedt een meer beknopte syntax. Een for lus heeft drie componenten tussen haken: een initialisatie voor de tellervariabele, een validatie-uitdrukking en een uitdrukking voor de vermeerdering. Voor elke iteratie van de lus, wordt de tellervariabele bijgewerkt op basis van de uitdrukking voor vermeerdering. Accolades bepalen wat er binnen de lus gebeurt. De validatie-uitdrukking wordt geëvalueerd aan het begin van elke lus. Wanneer de validatie-uitdrukking onwaar weergeeft, dan wordt de lus niet langer uitgevoerd en de controle wordt doorgegeven aan de uitdrukking onmiddellijk na de eindaccolade }. Het is belangrijk dat de code in de lus de geëvalueerde waarden van de validatie-uitdrukking aanpast opdat de lus zou eindigen. De for-lus van het voorbeeld doorkruist een verzameling van vijf elementen. De tellervariabele wordt geïnitialiseerd met de waarde 1 en wordt gebruikt om naar het eerste element van de verzameling te kijken. Het blok wordt herhaald zolang de teller kleiner is dan of gelijk is aan de lengte van de verzameling. Conditionele Uitdrukkingen Vaak moet je verschillende delen van de code uitvoeren afhankelijk van bepaalde voorwaarden en dit stuur je met conditionele uitdrukkingen. static void Intro_IfElse(Args _args) { NoYesId printToInfolog = true; ; if (printToInfolog) { info("Print to Infolog"); } else { print "Print to window"; pause; } } If uitdrukkingen worden het meest gebruikt als conditionele uitdrukking. In de meest eenvoudige vorm, kan je een enkelvoudige if-voorwaarde gebruiken, die bestaat uit een relationele uitdrukking tussen haken. Als de voorwaarde in de if-uitdrukking waar is, dan zal het codeblok in de if-uitdrukking uitgevoerd worden. Dit codeblok kan een enkelvoudige uitdrukking zijn of een reeks uitdrukkingen tussen accolades {}. Een if-uitdrukking kan je uitbreiden door een else voorwaarde toe te voegen. In dit geval zal de else-voorwaarde uitgevoerd worden als de voorwaarde in de if-uitdrukking onwaar is. Het bovenstaand voorbeeld illustreert het gebruik van een if-else-uitdrukking. Je kan een meer complexe logica uitvoeren door het nesten van if-uitdrukkingen. In het onderstaand voorbeeld is er een voorwaarde voor het codeblok gezet in de else-voorwaarde.

MORPHX IT Inleiding tot X++

56

© 2007 Steen Andreasen

static void Intro_IfElse(Args _args) { NoYesId printToInfolog = true; NoYesId printToWindow = true; ; if (printToInfolog) { info("Print to Infolog"); } else if (printToWindow) { print "Print to window"; pause; } } In bovenstaand voorbeeld kan je twee verschillende if uitdrukkingen gebruiken voor hetzelfde resultaat. Maar door het nesten van de tweede if onder de else, zal het tweede blok nooit worden gevalideerd als de eerste if-uitdrukking waar is, en op deze manier heb je een licht performantievoordeel. Je kan zoveel geneste if-uitdrukkingen toevoegen die je nodig hebt. Maar wees voorzichtig omdat hierdoor het programma moeilijk te lezen en te debuggen is. Vaak komt de noodzaak voor deze veelvoudige if-uitdrukkingen door de noodzaak om de uitvoering van het programma te bepalen, gebaseerd op de individuele waarde die in een variabele zit. Als het aantal waarden klein is, dan is een normale if-uitdrukking voldoende. Maar als de variabele meer dan twee of drie waarden kan hebben, wordt de lijst van if-uitdrukkingen snel onhandig. Gelukkig biedt X++ een conditionele uitdrukking die speciaal is ontworpen om deze situatie te behandelen, namelijk de switch-uitdrukking: static void Intro_Switch(Args _args) { SalesStatus salesStatus = SalesStatus::Invoiced; ; switch (salesStatus) { case SalesStatus::Delivered: info("Salesorder is delivered"); break; case SalesStatus::Invoiced: info("Salesorder is invoiced"); break; case SalesStatus::Canceled: info("Salesorder is cancelled"); break; default: info("Do nothing"); } } Als deze switch gemaakt was met if-else-uitdrukkingen, zou je verschillende niveau’s nodig gehad hebben, wat de code moeilijk leesbaar had gemaakt. In een switch uitdrukking worden alle voorwaarden toegevoegd op hetzelfde niveau, zodat de code

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

57

gemakkelijk leesbaar is en het gemakkelijk is om bijkomende voorwaarden toe te voegen. Bovenaan de switch-uitdrukking staat een expressie die de te evalueren variabele definieert. Na de switch-voorwaarde komen één of meerdere case-voorwaarden. Elke case-voorwaarde vermeldt één of meer waarden gescheiden door komma’s en beëindigd door een dubbele punt. X++ evalueert elke case-voorwaarde van de eerste tot de laatste. Als de waarde van de variabele vermeld in de switch-voorwaarde gelijk is aan één van de gespecifieerde waarden, dan wordt de code die volgt op de case-voorwaarde uitgevoerd. Als geen enkele gevalideerde case waar is, dan wordt de standaard case uitgevoerd. De standaard of default case is niet verplicht voor een switch. Als je de applicatiegebruikers wil verwittigen dat geen enkele case waar is, moet je een standaard toevoegen. Merk op dat er op het einde van elke switch een onderbreking (“break”) is toegevoegd. Wanneer X++ een onderbreking tegenkomt, dan geeft het de controle aan de eerste uitdrukking die volgt op de eindaccolade dat het bereik van de switch-uitdrukking bepaalt. Als het onderbrekingscommando wordt overgeslagen, dan worden alle cases achter een juiste case uitgevoerd. Dit is één van de meest voorkomende programmeerfouten wanneer je een switch-uitdrukking gebruikt. Als je een controle voor programmatievoorschriften uitvoert (te vinden onder “best practice check”), krijg je een overzicht van ontbrekende breaks in switch-uitdrukkingen. static void Intro_TernaryOperator(Args _args) { Boolean printCustomerName = false; ; print printCustomerName ? "Customer name" : "Customer account"; pause; } Voor eenvoudige uitdrukkingen is er een nuttig kort alternatief. Dit is een ternaire of drieledige operator, een operator met drie operands. De eerste operand bestaat uit een conditonele uitdrukking, zoals een if-uitdrukking, gevolgd door een vraagteken. De tweede en derde operand zijn elk een uitdrukking die uitgevoerd wordt als de eerste operand respectievelijk waar of onwaar is. Als de uitdrukking waar is, dan zal de eerste uitdrukking uitgevoerd worden; anders wordt de controle doorgegeven aan de tweede uitdrukking. Deze voorwaarden worden achter het vraagteken gezet en gescheiden door een dubbele punt. Merk op dat de uitdrukkingen van de tweede en derde operand (dus de voorwaarde is waar of onwaar) moeten verwijzen naar hetzelfde datatype. Aangezien de ternaire operator een conditionele uitdrukking toelaat, die beknopt op één lijn is geschreven, kunnen ze de leesbaarheid van het programma verhogen. Ze zijn het meest nuttig voor toewijzingsuitdrukkingen wanneer de waarde die wordt toegewezen maar één of twee mogelijke waardes kan zijn. Zo wordt de ternaire operator vaak gebruikt om de eigenschappen van formulierelementen zichtbaar of actief te zetten.

MORPHX IT Inleiding tot X++

58

© 2007 Steen Andreasen

Uitzonderingen Lussen en conditionele uitdrukkingen zijn beiden ontworpen om te controleren welk codeblok wordt uitgevoerd. Onverwachte voorwaarden zoals waarschuwingen en fouten moeten getoond worden aan de gebruiker als ze voorkomen. Uitzonderingsbeheer zou informatie in onverwachte situaties moeten onderscheiden van het gewone codeblok en actie ondernemen, gebaseerd op de informatie die de fout heeft doen ontstaan. Dit doe je met try-catch-uitdrukkingen. static void Intro_TryCatch(Args _args) { Counter counter; try { while (counter < 10) { counter++; if (counter MOD 7 == 0) throw error("Counter MOD 7 is zero"); if (counter MOD 3 == 0) throw warning("Counter MOD 3 is zero"); } } catch (Exception::Error) { print ( strfmt("An error appeared at loop %1", counter)); } catch (Exception::Warning) { print ( strfmt("A warning appeared at loop %1", counter)); retry; } pause; } Try-catch-uitdrukkingen worden gebruikt voor het behandelen van uitzonderingen in X++. Een try-catch-uitdrukking bestaat uit twee of meer gescheiden codeblokken : een try-blok dat een operatie probeert en één of meer catch-blokken waar de uitzonderingen opgeworpen vanuit de try-blok, worden opgevangen. Uitzonderingen mogen ofwel worden opgeworpen door de kernel of door het gebruik van het throw-commando vanuit de code in de try-blok. Het catch-gedeelte onderneemt actie op de uitzonderingen. Je kan meerdere catch-blokken hebben, elk ontworpen om een specifiek type van uitzondering op te vangen. Actie wordt alleen ondernomen op de types van uitzonderingen die je vermeldt in je code. Het getoonde try-catch-voorbeeld zal actie ondernemen op de uitzondering van het type fout en waarschuwing. Je kan een catch-blok toevoegen zonder een catch-type te specifiëren dat alle uitzonderingstypes behandelt, die niet gedefinieerd zijn voor eender welk ander catch-blok. Dus wat is zo belangrijk aan het gebruik van uitzonderingsbeheer, als je een gelijkaardige code kunt maken met het gebruik van controlestructuurinstructies zoals

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

59

lussen en if-else-uitdrukkingen ? Het voordeel van uitzonderingsbeheer is dat je de uitzonderingen kan opvangen voor ze worden bijgezet in de Infolog en geschikte actie kunt ondernemen. Ze zijn zeker nuttig wanneer je databankhandelingen doet zoals het bijwerken van records. Wanneer je een record bijwerkt, kan er een uitzondering optreden wegens databankvergrendelingen. Gewoonlijk is dit een tijdelijke situatie en als de handeling wordt overgedaan, zal deze lukken. Zonder uitzonderingsbeheer zal een onsuccesvolle aanpassing van het record worden doorgegeven aan de standaard foutenbehandeling die een fout in de infolog zal weergeven en onnodig het programma zal beëindigen. Wanneer een uitzondering wordt opgeworpen, kan je het retry-commando gebruiken om de try-blok terug uit te voeren en de gebruiker zal nooit hebben gemerkt dat er een uitzondering voordeed. Bij het coderen van een catch-blok, is het gewoonlijk nodig om logica toe te voegen die nakijkt of de toestand die de fout veroorzaakte, is gewijzigd. Zonder deze logica kan je programma in een lus komen omdat het voortdurend de code die de fout veroorzaakte, opnieuw wil uitvoeren. Varia X++ heeft commando’s die gebruikt kunnen worden om de uitvoering van een codeblok te herroepen. Vaak worden deze commando’s gebruikt in controlestructuurinstructies wanneer de resterende code in dat blok niet wordt uitgevoerd. static void Intro_Break(Args _args) { Counter counter; ; for (counter=1; counter <= 10; counter++) { print counter; if (counter == 5) { break; } } pause; } We hebben al eerder voorbeelden getoond over het gebruik van het break-commando in een switch-uitdrukking om een case te beëindigen. Je kan het break-commando in eender welk codeblok gebruiken. Als het gebruikt wordt in een controlestructuur-instructie, zal de uitdrukking voor de lus onderbroken worden. Hier wordt de afbreking gezet in een for-lus, zodat de lus eindigt na vijf iteraties. Als je een zwaar codeblok gaat uitvoeren, zoals een opzoeking in de databank, kan je de code versnellen door het toevoegen van een afbreking wanneer je al je gegevens hebt, in plaats van te wachten tot de lus volledig doorlopen is. Als je het return-commando gebruikt in plaats van een afbreking in een lus, zal dit hetzelfde resultaat geven als de afbreking, hoewel het break-commando meestal wordt

MORPHX IT Inleiding tot X++

60

© 2007 Steen Andreasen

verkozen. Return wordt gebruikt om een waarde weer te geven van een methode. Het gebruik van methodes wordt beschreven in het hoofdstuk Klassen. static void Intro_Continue(Args _args) { Counter counter; ; for (counter=1; counter <= 10; counter++) { if (counter MOD 3 == 0) { continue; } print counter; } pause; } Het verschil tussen een break en continue is dat break uit de lus zal stappen, terwijl continue de resterende code in de huidige iteratie zal overslaan. Je zal hetzelfde resultaat krijgen wanneer je een if- uitdrukking gebruikt in plaats van het continue-commando. Het is een optie en als je op het punt staat om een conditie toe te voegen aan een groot codeblok, kan je overwegen om continue te gebruiken, in plaats van een if-uitdrukking toe te voegen en veel lijnen code te indenteren.

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

61

2.4 Selectie-uitdrukkingen X++ gebruikt selectie-uitdrukkingen om data op te halen uit de databank. Selectie- uitdrukkingen kunnen geschreven worden zoals eender welke uitdrukking in X++. Om een selectie-uitdrukking te gebruiken moet je eerst variabelen declareren voor de tabellen waarnaar verwezen wordt. Een speciale vorm van de while-uitdrukking, while select, kan gebruikt worden om een lus te maken. In deze lus worden dan alle records opgehaald die voldoen aan specifieke criteria. De selectiecriteria worden gedefinieerd door het gebruik van uitdrukkingen gebaseerd op operatoren en variabelen. Merk op dat het basistype str niet kan gebruikt worden in uitdrukkingen tenzij de lengte van str wordt bepaald. Dit is weeral een reden om uitgebreide data types te gebruiken. static void Intro_Select(Args _args) { CustTable custTable; CustTrans custTrans; TransDate fromStartYear = systemdateget(); ; while select custTable join custTrans where custTrans.accountNum == custTable.accountNum && custTrans.transDate >= fromStartYear { info(strfmt("%1, %2, %3", custTable.accountNum, custTrans.transDate, custTrans.voucher)); } } Dit is een voorbeeld van hoe je selectie-uitdrukkingen in X++ gebruikt. We koppelen de tabellen CustTable en CustTrans aan elkaar via het sleutelwoord join ; daarna halen we de CustTrans records op met een transactiedatum gelijk aan of hoger dan de start van het jaar. We declareren een variabele die de systeemdatum weergeeft door de functie systemdateget(). Hoewel deze functie direct in de selectie-uitdrukking kan gebruikt worden, zou het de selectie vertragen omdat de functie systemdateget()berekend wordt voor elke iteratie van de lus. De selectie-uitdrukkingen in X++ volgen niet exact de SQL standaard. X++ implementeert ook niet alle standaard SQL-sleutelwoorden. Als je gewoon bent SQL-uitdrukkingen te schrijven, zal je je snel aanpassen aan de selectie-uitdrukkingen die in X++ worden gebruikt. Voor een overzicht van de sleutelwoorden beschikbaar in X++, gesorteerd op sleutelwoord kan je terecht in figuur 14: Overzicht van de select-sleutelwoorden.

MORPHX IT Inleiding tot X++

62

© 2007 Steen Andreasen

sleutelwoord omschrijving asc

Zet het sorteren op stijgende volgorde. Alle selecties halen doorgaans hun data op in stijgende volgorde. Wordt gebruikt in combinatie met order by of group by. Bijvoorbeeld select custTable order by accountNum asc;

avg

Een totaliserende functie gebruikt om de gemiddelde waarde van een veld van de opgehaalde records te berekenen. Selecties die totaliserende functies gebruiken, gaan maar één keer naar de databank om het resultaat te berekenen gebaseerd op meerdere records. Zie klassemethode KMKnowledgeCollectorStatisticsExecute.runQuery(). Bijvoorbeeld select avg(amountMST) from custTrans;

count

Een totaliserende functie gebruikt om het aantal opgehaalde records te tellen. Zie klassemethode KMKnowledgeCollectorStatisticsExecute.runQuery(). Bijvoorbeeld select count(recId) from custTrans;

delete_from

Verwijdert meerdere records met één oproep naar de databank. Dit is de snelste manier voor het verwijderen van veel records. Als je ervoor kiest om een gewone while-selectie te gebruiken, ga je voor ieder record opnieuw naar de databank. Zie klassemethode InventCostCleanUp.updateDelSettlement(). Bijvoorbeeld delete_from myTable where myTable.amountMST <='1000';

desc

Zet het sorteren op dalende volgorde. Wordt gebruikt in combinatie met order by of group by. Een dalende selectie kan zeer traag zijn, omdat indexen altijd stijgend zijn gesorteerd. Zie tabelmethode CustTable.lastPayment(). Bijvoorbeeld select custTable order by name desc;

exists join

Exists join wordt gebruikt om records op te halen waar op zijn minst één record in de tweede tabel overeenkomt met de join-uitdrukking. Merk op dat je geen records ophaalt uit de tweede tabel door het gebruik van exists join. Zie klassemethode InventAdj_Cancel.cancelInventSettlements(). Bijvoorbeeld while select custTable exists join custTrans where custTable.accountNum == custTrans.accountNum

firstfast

Geeft de instructie om het eerste record sneller te selecteren. Kan gebruikt worden in situaties waar alleen één record getoond wordt, zoals in een dialoog. Zie klassemethode ProjPeriodCreatePeriod.dialog(). Bijvoorbeeld select firstfast custTable order by accountNum;

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

63

sleutelwoord omschrijving firstonly

Alleen het eerste record zal worden geselecteerd. Firstonly mag alleen maar gebruikt worden, wanneer je geen while in de selecties gebruikt, zelfs als de selectie maar één enkel record oplevert. De methodes find() en exists() , die vaak gebruikt worden op tabellen, gebruiken firstonly.

forceliterals

Dwingt de kernel om data te selecteren zonder het gebruik van een “prepared select”. Het wordt gebruikt om zeker te zijn dat de databank een optimale index gebruikt gebaseerd op de waarden in de selectie-uitdrukking. Om de joins te verbeteren tussen grote tabellen zal de kernel standaard data ophalen door het gebruik van forceliterals. De kernel gebruikt hiervoor de eigenschap TableGroup. Data wordt opgehaald door het gebruik van forceliterals in joins waar op zijn minst twee tabellen niet behoren tot de Table Group Parameter of Group. Bijvoorbeeld select forceliterals custTrans order by voucher, transDate where custTrans.accountNum >= "4000";

forcenestedloop

Dwingt de kernel om voor het “join plan” de nested loop te kiezen in plaats van de merge join of hash join. Dit houdt in dat de eerste tabel wordt gelezen en voor ieder record van de eerste wordt een overeenkomstig record uit de tweede tabel opgehaald. Vaak gebruikt in combinatie met forceselectorder. Zie klassemethode ReqCalc.actionCalcDimTrans(). Bijvoorbeeld select forcenestedloop forceselectorder custTable join custTrans where custTable.accountNum == custTrans.accountNum && custTable.name >= "4000"; Doorgaans wordt nested loop gebruikt voor kleinere tabellen, hash join voor het grotere werk en de merge join zit daar tussenin.

forceplaceholders

Dwingt de kernel om de data te selecteren met een “prepared select”. Gebruikt om de standaard forceliterals in join op grote tabellen te herroepen. De kernel gebruikt standaard forceplaceholders wanneer je niet selecteert uit grote tabellen. Bijvoorbeeld select forceplaceholders custTable join custTrans where custTable.accountNum == custTrans.accountNum && custTable.name >= "4000"; Nog even over het verschil tussen forceliterals en forceplaceholders. Je vertrekt in beide gevallen van een SQL-instructie SELECT * FROM purchTable WHERE purchId == ‘EN00009’ ; die in SQL Server als volgt wordt vertaald :

• Forceliterals : WHERE (PURCHID="EN00009") • Forceplaceholders : WHERE (PURCHID=" @P1")

In het laatste geval spreken we van een “prepared statement” waarbij de waarde waarop geselecteerd wordt, als variabele (placeholder of plaatshouder) wordt doorgegeven. Als je werkt met een “prepared statement”, ga je 2 keer naar de server. De eerste keer stel je de “prepared statement”op en de tweede keer haal je de gegevens op. De “prepared statement” blijft echter beschikbaar zolang de

MORPHX IT Inleiding tot X++

64

© 2007 Steen Andreasen

sleutelwoord omschrijving connectie met de databank actief is. Als je meerdere malen een gelijkaardige SQL-instructie doorgeeft, zal je zo je tijdwinst halen omdat SQLServer de manier om gegevens op te halen, al heeft bepaald. Voor een eenmalige ophaling van gegevens tijdens de actieve sessie, werk je beter met forceliterals.

forceselectorder

Forceselectorder dwingt de kernel om de tabellen te benaderen in de volgorde waarin ze in de join vermeld staan. Vaak gebruikt in combinatie met forcenestedloop.

forupdate

Als records in een select worden bijgewerkt, moet de sleutel forupdate toegevoegd worden. Bijvoorbeeld while select forupdate custTable

from

Standaard worden alle velden van de tabel geselecteerd. From wordt gebruikt om alleen gespecifieerde velden te selecteren. Dit zal een select optimaliseren, zeker bij tabellen met veel velden. Gebruik het alleen voor optimalisatie, want het maakt de code meer complex. Bijvoorbeeld select accountNum, name from custTable;

group by

Groepeert alle rijen die een gemeenschappelijke waarde hebben in de velden vermeld achter “group by”. Alleen de velden die in de group by worden gespecifieerd, zullen opgehaald worden. Zie klassemethode InventStatisticsUS.calcTotals(). Bijvoorbeeld while select custTable group by custGroup;

index

Bepaalt de sorteervolgorde van de opgehaalde data. De kernel zal het sleutelwoord index vertalen in order by gevolgd door de velden van de index. Index mag alleen gebruikt worden als de opgehaalde data op een bepaalde manier moeten gesorteerd worden, aangezien de databank een gepaste index zal kiezen. Bijvoorbeeld while select custTable index accountIdx

index hint

Index hint zal de databank dwingen om een bepaalde index te gebruiken. De databank zal altijd proberen om de beste index te kiezen. Index hint kan alleen gebruikt worden als de databank geen gepaste index kiest. Bijvoorbeeld while select custTable index hint accountIdx

insert_recordset

Gebruikt om meerdere records in een tabel in te voegen. Insert_recordset is nuttig wanneer je data kopieert van de ene tabel naar de andere aangezien het maar één oproep naar de databank vraagt. Je kan dit enkel gebruiken als het aantal velden en het basistype van de velden uit beide tabellen overeenstemmen. Zie klassemethode ReleaseUpdateDB_V25toV30. updateASPEmail(). Bijvoorbeeld insert_recordset myTable (myNum,mySum) select myNum, sum(myValue) from anotherTable group by myNum where

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

65

sleutelwoord omschrijving myNum <= 100;

join

Join zal de data ophalen met een inner join. Records die voldoen aan de join zullen opgehaald worden uit beide tabellen. Zie table method SalesTable.LastConfirm(). Bijvoorbeeld while select custTable join custTrans where custTable.accountNum == custTrans.accountNum

maxof

Aggregatiesleutelwoord gebruikt om de hoogste veldwaarde op te halen. Zie klassemethode KMKnowledgeCollectorStatisticsExecute.runQuery(). Bijvoorbeeld select maxOf(amountMST) from custTrans;

minof

Aggregatiesleutelwoord gebruikt om de laagste veldwaarde op te halen. Zie klassemethode KMKnowledgeCollectorStatisticsExecute.runQuery(). Bijvoorbeeld select minOf(amountMST) from custTrans;

next

Gebruikt om naar het volgende record te gaan in een select. Je kan beter een while-selectie in de plaats gebruiken. Bijvoorbeeld select custTable; next custTable;

nofetch

Maakt een selectie zonder data op te halen. De cursor naar het resultaat van de selectie wordt dan doorgegeven naar een andere methode die dan verder werkt met de cursor. Kan nuttig zijn als je een ingewikkelde selectie-instructie wil doorgeven aan een query. Zie klassemethode PurchCalcTax_PurchOrder.initCursor(). Bijvoorbeeld select nofetch custTable;

notexists join

Is het tegenovergestelde van de exists join. Zal records ophalen van de eerste tabel, waar geen overeenkomstig record is in de tweede tabel volgens de join. Zie klassemethode InventConsistencyCheck_Trans.run(). Bijvoorbeeld while select custTable notexists join custTrans where custTable.accountNum == custTrans.accountNum

order by

Bepaalt de sorteervolgorde van de opgehaalde data. Wordt gebruikt om zeker te zijn dat de data op een exacte manier worden opgehaald. Als er geen index bestaat voor de volgorde van de velden, kan dit tijdrovend zijn. Zie table method CustTrans.transactionDate(). Bijvoorbeeld select custTrans order by accountNum, transdate desc

outer join Outer join zal records selecteren van beide tabellen ongeacht of er

MORPHX IT Inleiding tot X++

66

© 2007 Steen Andreasen

sleutelwoord omschrijving overeenkomstige records in de tweede table zijn volgens de join.

Zie klassemethode SysHelpStatistics.doTeams(). Bijvoorbeeld while select custTable outer join custTrans

reverse

Haalt de data op in omgekeerde volgorde. De sleutelwoorden asc en desc zijn gerelateerd met één enkel veld waar reverse gerelateerd is tot de tabel. Zie klassemethode InventUpd_Reservation.updateReserveLess(). Bijvoorbeeld while select reverse custTable

setting

Dit sleutelwoord wordt gebruikt in combinatie met update_recordset.

sum

Aggregatiesleutelwoord gebruikt om de som te maken van de opgehaalde data. Zie klassemethode KMKnowledgeCollectorStatisticsExecute.runQuery(). Bijvoorbeeld select sum(amountMST) from custTrans;

update_recordset

Update_recordset zal meerdere records bijwerken in één oproep naar de databank. Dit is nuttig om velden op een snelle manier te initialiseren. De bijgewerkte velden worden gespecifieerd na het sleutelwoord setting. Zie klassemethode ProdUpdHistoricalCost.postScrap(). Bijvoorbeeld update_recordset myTable setting field1 = myTable.field1 * 1.10;

Figuur 14: Overzicht selectie-sleutelwoorden In een vorige versie van Axapta was het de gewoonte om te bepalen welke index gebruikt werd in selectie-uitdrukkingen met de sleutelwoorden index en index hint. Moderne routines voor de optimalisering van de databank zijn enorm goed in het bepalen van de beste index om een query te ondersteunen zodat er geen noodzaak is om een index te bepalen in selectie-uitdrukkingen. Meer nog, als je de verkeerde index bepaalt, kan dit een averechts effect hebben op de performantie. Je moet alleen het gebruik van een index overwegen als je de zekerheid moet hebben dat de opgehaalde data wordt weergegeven in een bepaalde volgorde. De sleutelwoorden forceliterals, forcenestedloops, forceplaceholders, forceselectorder kunnen gebruikt worden om de performantie van de query te optimaliseren. Deze sleutelwoorden veranderen de standaardwijze waarop data wordt opgehaald uit de databank, ook het uitvoeringsplan genoemd. Wees voorzichtig hoe je dit gebruikt. Je kan een betere performantie krijgen op je testdata, maar slechtere performantie realiseren op productiegegevens. De impact van deze sleutelwoorden zijn afhankelijk van de samenstelling van de data. Gebruik ze niet tenzij je weet wat je doet. Wanneer je selectie-uitdrukkingen gebruikt om records op te halen die aangepast moeten worden zoals het verwijderen en bijwerken, moet je de Transaction Tracking

MORPHX IT Inleiding tot X++

© 2007 Steen Andreasen

67

System(TTS) gebruiken. De TTS laat je toe om de logische transactiegrenzen te bepalen. Een logische transactie kan het bijwerken van records in verschillende tabellen met zich meebrengen. Vaak is de integriteit van de databank afhankelijk van de relatie tussen de bijgewerkte records. De TTS verzekert dat als één bijwerking in de transactie faalt, de andere transacties in de groep teruggezet zullen worden naar de status voor de transactie zodat je de integriteit en de samenhang van de databank bewaart. Het systeem zal niet toestaan dat je de bestaande records bijwerkt zonder het gebruik van de TTS; doe je dit toch, krijg je een foutmelding. Meer informatie over de TTS vind je in het hoofdstuk Data Dictionary.

2.5 Functies X++ heeft een reeks systeemfuncties waarop je kan rekenen om verscheidene taken uit te voeren. Deze functies zijn geschreven in C++ en zijn deel van de Axapta kernel. Verschillende van deze functies kunnen gebruikt worden voor typeconversie zoals het converteren van een geheel getal naar een string. Er zijn ook een aantal nuttige functies die datum- en stringoperaties kunnen uitvoeren. Een overzicht van de systeemfuncties kan je vinden onder het knooppunt System Documentation in de AOT. Wanneer je functies gebruikt in X++, moet je de functienaam intypen en de parameters tussen haken ingeven. Gevorderde X++ programmeurs zullen opmerken dat deze functies op dezelfde manier worden aangesproken als Global klassemethodes. Vanuit X++ bekeken, lijken ze hetzelfde. Een manier om de oorsprong na te gaan is door te klikken met de rechtermuisknop om de functie of de methode op te zoeken.

2.6 Samenvatting De programmeertaal X++ die gebruikt wordt door MorphX, is geïntroduceerd in dit hoofdstuk. Je zou nu kennis moeten hebben over de syntax van X++, hoe je variabelen declareert, het gebruik van flow uitdrukkingen en hoe data worden geselecteerd uit de databank. Het leren van X++ is de eerste stap, wanneer je je eigen aanpassingen in Axapta wil maken. De volgende hoofdstukken tonen hoe je de individuele knooppunten in de AOT kan gebruiken en hoe je logica kan toevoegen aan de AOT-objecten door het gebruik van X++.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

69

3 Data Dictionary Het datamodel van de Axapta-applicatie is gedefinieerd in de Data Dictionary. Als je een Axapta-installatie wil wijzigen, ga je hier beginnen met het ontwerpen van je nieuwe datamodel. De gegevens van een Axapta-installatie worden bewaard in een relationele databank. Kortweg betekent dit dat de gegevens in aparte tabellen worden bewaard om redundante gegevens te voorkomen en relaties tussen de tabellen onderling maken het mogelijk om gerelateerde gegevens op te vragen. Een basiskennis van het creëren van een relationeel databankmodel en het normaliseren van gegevens is een groot voordeel bij het ontwerpen van wijzigingen aan de data dictionary van Axapta. Een uitleg over relationele databanken, hun ontwikkeling en verder onderhoud staat niet beschreven in dit boek. Er zijn massa’s boeken beschikbaar omtrent dit onderwerp. Daarnaast is er ook een enorme hoeveelheid aan kennis op het internet terug te vinden. Voor het beheer van de gegevens gebruikt een Axapta-installatie ofwel een Microsoft SQL Server ofwel een Oracle databank. Tabellen, views, velden en indexen worden met de databank gesynchroniseerd bij creatie, wijziging of verwijdering vanuit Axapta. Deze definitie van de data of meta-data is de enige informatie van de data dictionary die bewaard wordt in de databank, de daadwerkelijke data worden fysisch bewaard in de Microsoft SQL Server of Oracle databank. Informatie omtrent de actuele relaties tussen tabellen en beperkingen bij het verwijderen van records vind je terug in MorphX. Visual MorphXplorer kan gebruikt worden bij het bouwen van het actuele entiteit-relatie-diagramma (ERD).

3.1 Tabellen Er bestaan twee soorten categorieën binnen tabellen:

Applicatietabellen. Deze worden gebruikt bij het bepalen en bouwen van de applicatiemodules. Bij de initiële definitie van je Axapta-installatie zal je bepalen welke specifieke applicatietabellen je zal gebruiken. Dit hangt af van de specifieke configuratiesleutels die geactiveerd zijn en bij de opstart worden deze applicatietabellen met de databanksoftware gesynchroniseerd.

• Systeemtabellen. Deze tabellen bevatten informatie specifiek voor de werking van de Axapta-infrastructuur en zijn gecreëerd in de kernel van de applicatie. Systeemtabellen worden automatisch gesynchroniseerd met het databank besturingssysteem.

Bij synchronisatieproblemen, kan de SQL tool in het hoofdmenu onder Administration | Periodic | SQL Administration zeer handig zijn. Het starten van een Check/Synchronize zal de meeste synchronisatiefouten oplossen. Bedrijf Axapta biedt de mogelijkheid om dezelfde applicatie te gebruiken voor informatie van verschillende bedrijven. Alhoewel alle data bewaard wordt op dezelfde fysische databank, zal Axapta bepalen welke data gebruikt dient te worden voor ieder bedrijf en toch zal de applicatie identiek zijn inzake uitzicht, werking en functionaliteit.

MORPHX IT Data Dictionary

70

© 2007 Steen Andreasen

Dit is nuttig op een aantal gebieden:

• Je kunt testen met data voor gebruik in opleiding van nieuwe medewerkers op het huidige Axaptasysteem. Zij kunnen inloggen in Axapta in het ‘test’ bedrijf en jij bent er zeker van dat er geen kritische productiegegevens beschadigd worden.

• Je kan gebruik maken van een ‘planning’ of ‘budget’ bedrijf waar je toekomstige bedrijfsmodellen kan voorbereiden.

• Je kunt meerdere fysieke bedrijven hebben, die elk een compleet verschillende markt bespelen, maar dezelfde systeemfunctionaliteiten gebruiken. Je kan afzonderlijke informatie hebben ten dienste van ieder bedrijf: bijvoorbeeld, verschillende definities voor het Algemeen Grootboek, klant- en crediteurenspecificaties, voorraad- en inventarisbeheer en werknemerstructuren. Dergelijke informatie zal bewaard worden in een afzonderlijke bedrijfsidentificatie.

Axapta beheert dit door gebruik van het systeemveld dataAreaId om te bepalen tot welk bedrijf de gegevens behoren. Bij het toevoegen van een nieuw record, is dataAreaId automatisch geïnitialiseerd met het huidige bedrijfsidentificatienummer. Wanneer een gebruiker data opvraagt via formulieren en rapporten, of wanneer de applicatie data ophaalt door gebruik van een selectieopdracht, zal Axapta altijd data teruggeven van het huidige bedrijf, omdat de kernel automatisch een filter op dataAreaId toevoegt. static void DataDic_ChangeCompany(Args _args) { DataArea dataArea; CustTable custTable; ; while select dataArea { if (!dataArea.isVirtual) { info(strfmt("Company: %1", dataArea.id)); changeCompany(dataArea.id) { custTable = null; while select custTable { info(strfmt("%1, %2", custTable.dataAreaId, custTable.accountNum)); } } } } } Bovenstaand voorbeeld demonstreert hoe je gegevens kunt ophalen uit meer dan één bedrijf en klantnummers kunt afdrukken van alle niet-virtuele bedrijven. De changeCompany() functie gebruik je om van bedrijf te wisselen. Het wijzigen van het bedrijf gebeurt in de codeblok van changeCompany() en nadien zal je terugkeren naar

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

71

het bedrijf van waaruit je startte. Merk op dat de CustTable een null waarde meekrijgt. De tabelvariabele moet gereset worden anders zal je data van het standaard bedrijf selecteren. ChangeCompany() dien je te gebruiken met enige voorzichtigheid, want hiermee zou je wel eens data kunnen wijzigen in het verkeerde bedrijf. Virtuele bedrijven worden besproken in de sectie Tabelverzamelingen. Applicatietabellen Als je een nieuw veld of een nieuw formulier in Axapta wil, moeten de gegevens die de gebruiker zal opgeven in dit veld of formulier, bewaard worden in een bijbehorende tabel. Nieuwe velden kunnen toegevoegd worden aan een bestaande tabel, of aan een nieuwe tabel. Indien je ervoor kiest om nieuwe velden toe te voegen aan een bestaande tabel, zullen deze toegevoegd worden aan de huidige laag, ook al behoort de gewijzigde tabel tot een andere laag. Dit geldt voor alle objecten van een tabel, behalve voor verwijderacties. Bij een upgrade van de applicatie naar een nieuwe release is dit een voordeel, want je zal enkel manueel de gewijzigde code moeten samenvoegen die in meer dan één laag werd gewijzigd. De tabelknoop bevindt zich in de AOT onder Data Dictionary. Tabelbladeraar De tabelbladeraar kun je oproepen door een tabel te selecteren, klik vervolgens met de rechtermuisknop en kies Table Browser in het Add-Ins menu. De tabelbladeraar wordt opgebouwd via een standaard formulier genaamd SysTableBrowser. Je kunt de tabelbladeraar oproepen vanuit elke applicatietabel of systeemtabel. Je kunt de Tabelbladeraar ook selecteren in elke gegevensbron. Standaard zie je alle velden waarvan de eigenschap Visible de waarde “true” heeft. Om de meest gebruikte velden van een tabel te tonen, schakel je over naar “show Auto-report fields”. Je kunt de records in de tabelbladeraar filteren door een selectiecommando te schrijven, maar het is gemakkelijker om te filteren via het query icoon uit het bovenste menu. Wil je een overzicht van de gegevens in een tabel, dan is de tabelbladeraar handig omdat je niet moet weten waar in het menu de tabel wordt aangesproken. Voor testdoeleinden kun je de tabelbladeraar gebruiken om data te creëren of om bestaande data aan te passen. Niettemin is het uitermate belangrijk deze mogelijkheid enkel te gebruiken om te testen. Gegevens die je wijzigt met behulp van de tabelbladeraar, zullen gevalideerd worden aan de hand van de bedrijfslogica in de tabelscripts. Geef er toch de voorkeur aan om productiegegevens te wijzigen via een formulier omdat dit formulier bijkomende logica kan bevatten. Met de tabelbladeraar zou je data uit de productieomgeving kunnen beschadigen en daarom is deze tool niet bestemd voor de eindgebruiker. Creatie van tabellen Het is belangrijk om vooraf na te denken hoe je tabellen zullen gebruikt worden. Wanneer je tabellen geïnstalleerd worden in een productieomgeving, en de gebruikers hebben de eerste gegevens ingetikt, dan is het complexer om je tabellen en indexen nadien te herontwerpen, want je zal bestaande data moeten converteren.

MORPHX IT Data Dictionary

72

© 2007 Steen Andreasen

Performantie is ook een belangrijke kwestie. Indien je een nieuwe transactietabel aanmaakt, dien je de tabel te voorzien van een goede index, om data vlotjes te kunnen ophalen, zowel voor eenvoudige selecties, als bij het koppelen van de tabel naar andere tabellen. Als je bijkomende velden nodig hebt in een bestaande tabel, kun je misschien ervoor kiezen om een nieuwe tabel te maken voor deze velden. Dit kan een betere oplossing zijn dan de bestaande tabel uit te breiden. Hou wel in gedachte dat iedere keer je deze data nodig hebt, je een koppeling moet voorzien naar deze nieuwe tabel. Deze extra koppeling kan betekenen dat de gegevens trager worden opgehaald. Het belangrijkste is om bij het ontwerpen van tabellen steeds een goed overzicht te behouden van hoe je tabellen zullen geraadpleegd worden, alvorens je wijzigingen door te voeren. Voorbeeld 1: Creatie van een tabel Objecten gebruikt van het MORPHXIT_DataDictionary project

Table, MyTable In dit eerste voorbeeld maken we een tabel om klantinformatie te bewaren. Aanvankelijk zal deze slechts een beperkt aantal velden bevatten. In de loop van dit hoofdstuk zal deze uitgebreid worden met meer mogelijkheden. 1. Maak een nieuwe tabel aan via het Tables knooppunt in de AOT, kies via de

rechtermuisknop voor New Table. Een nieuwe tabel met naam "Table1" zal aangemaakt worden. Open het eigenschappenblad en hernoem deze tabel "MyTable".

2. Klap de nieuwe tabelknoop open, zodat de knooppunten op het volgende niveau

zichtbaar worden. Open een nieuwe instantie van de AOT en ga naar Data Dictionary/Extended Datatypes. Zoek het uitgebreid datatype AccountNum op en versleep het uitgebreid datatype AccountNum naar de Fields knoop van MyTable.

3. Zoek de uitgebreide datatypes CustName, CustGroupId en CurrencyCode op en

versleep deze naar de Fields knoop van MyTable. 4. Bewaar de tabel MyTable via de toetsencombinatie ctrl+s. De tabel is nu

gesynchroniseerd met de databank en je hebt nu je eerste tabel gemaakt! Wanneer de uitgebreide datatypes naar de Fields knoop van de tabel werden versleept, werd een nieuw veld gemaakt met dezelfde naam als het uitgebreide datatype. Dit is de gemakkelijkste manier om nieuwe velden aan te maken. Je kunt meerdere uitgebreide datatypes selecteren en deze in één keer verslepen. Daarna kun je voor ieder veld de eigenschappen ervan apart instellen. Via de tabelbladeraar kun je nu rijen aanmaken in MyTable. Als je een nieuwe rij aanmaakt, let er dan op dat de velden custGroupId en currencyCode allebei een opzoekknop hebben, waarmee je enkel waarden kan selecteren die vermeld zijn in de bijbehorende opzoektabel. Dit komt omdat de uitgebreide datatypes voor deze twee velden een relatie hebben met de tabellen CustGroup en Currency. Zonder enig programmeerwerk heb je een nieuwe tabel aangemaakt met relaties !

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

73

De tabeleigenschap TableGroup gebruikt men om tabellen te groeperen. En dit vergeet men vaak bij het aanmaken van nieuwe tabellen. Deze eigenschap wordt gebruikt om de inhoud van een tabel te specifiëren. Zo kan je bijvoorbeeld aangeven of de tabel een hoofdtabel is of een transactietabel, zoals respectievelijk de tabel met klanten en de tabel met klantentransacties. Doorblader even de bestaande tabellen; dit zal je een idee geven over het gebruik van de tabelgroepeigenschap. De standaard waarde van de eigenschap TableGroup is "Miscellaneous". Aangezien dataconfirmaties en data-exports gefilterd kunnen worden via de waarde van deze tabeleigenschap, moet ze een waarde hebben. Indien deze eigenschap geen correcte waarde heeft voor tabellen met een zeer grote hoeveelheid aan data, zoals transactietabellen, zullen gekoppelde selectieopdrachten niet performant werken aangezien de kernel deze tabelgroepeigenschap gebruikt om te bepalen of de selectie gebeurt via de optie forceLiterals. Meer informatie over selecties vind je in het hoofdstuk Inleiding tot X++. Er zijn nog meer eigenschappen die ingesteld kunnen worden, voornamelijk eigenschappen gerelateerd aan beveiliging en performantie. De meest gebruikelijke tabeleigenschappen zullen we in dit hoofdstuk bespreken. Een compleet overzicht van de eigenschappen van de data dictionary vind je in de Bijlage Eigenschappen. Tabelvariabelen Tabellen worden in X++ gedeclareerd net zoals elke andere variabele. Om een tabelvariabele van een waarde te voorzien, gebruikt men selectieopdrachten. Je kunt een tabelvariabele ook gelijk stellen aan een andere tabelvariabele op voorwaarde dat deze van hetzelfde type zijn. static void DataDic_OneCursor(Args _args) { CustTable custTable, newCustTable; ; select firstonly custTable; newCustTable = custTable; } In dit voorbeeld met de CustTable, is er slechts één cursorpositie indien een tabelvariabele gelijk wordt gesteld aan een andere. Indien één van de tabelvariabelen verwijst naar een nieuw record, zullen beide tabelvariabelen verwijzen naar dit nieuw record. static void DataDic_TwoCursors(Args _args) { CustTable custTable, newCustTable; ; select firstonly custTable; newCustTable.data(custTable); }

MORPHX IT Data Dictionary

74

© 2007 Steen Andreasen

Wil je twee aparte cursors hebben, dan moet je de data() methode gebruiken. Nu zullen de tabelvariabelen custTable en newCustTable elk hun eigen cursor hebben. Tijdelijke tabellen Een tijdelijke tabel bevat geen data en is niet gesynchroniseerd met de databank. Een tijdelijke tabel kan gebruikt worden zoals iedere gewone tabel in koppelingen en selecties. De tabeleigenschap Temporary bepaalt of de tabel tijdelijk is of niet. Iedere applicatietabel kan op “tijdelijk” ingesteld worden door de eigenschap Temporary op “Yes” te plaatsen. Wees hierbij voorzichtig: bij het activeren van deze eigenschap op een tabel die data bevat, zal de bestaande inhoud verwijderd worden! Om gemakkelijk tijdelijke tabellen in de AOT op te sporen, krijgen alle tijdelijke tabellen het voorvoegsel Tmp*. Tijdelijke tabellen worden heel vaak gebruikt om data te sorteren. Het kan nodig zijn om data te presenteren op een formulier of een rapport met een specifieke sortering, dat je niet kan verwezenlijken met een selectie-opdracht of een query. Je kunt dan gebruik maken van een tijdelijke tabel met de nodige velden en de gewenste sorteervolgorde. Data moet opgehaald worden uit de tabellen en ingevoegd in de tijdelijke tabel volgens de sorteercriteria. Zolang de tijdelijke tabel in gebruik is zal deze data bevatten. De inhoud van de tijdelijke tabel is bewaard in het bestandssysteem in een tijdelijk bestand. static void DataDic_TmpTable(Args _args) { CustTable custTable; CustTrans custTrans; TmpCustLedger tmpCustLedger; ; while select custTable { tmpCustLedger.accountNum = custTable.accountNum; tmpCustLedger.name = custTable.name; tmpCustLedger.insert(); } while select tmpCustLedger { info(strFmt("%1, %2", tmpCustLedger.accountNum, tmpCustLedger.name)); } } In dit vereenvoudigd voorbeeld, worden alle klantnummers en hun naam in een tijdelijke tabel TmpCustLedger genoteerd. De inhoud van TmpCustLedger wordt doorlopen en getoond in de Infolog. De data van tijdelijke tabellen zal meestal gefilterd en opgehaald worden uit één of meerdere tabellen. Het gebruik van tijdelijke tabellen veroorzaakt enige overlast, omdat de gegevens eerst moeten opgehaald worden en in de tijdelijke tabel opgeslagen. Vervolgens zal deze tijdelijke tabel doorlopen worden. Het gebruik van tijdelijke tabellen kan echter ook een oplossing zijn om de performantie te verbeteren. Indien je een formulier hebt met een complexe query waardoor het navigeren traag gebeurt, kan het gebruik van een tijdelijke tabel een betere oplossing zijn. Het zal aanvankelijk langer duren vooraleer het

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

75

formulier volledig opgeroepen is, maar zodra de gegevens zijn ingeladen, zal de applicatie zich performanter gedragen en dus gebruiksvriendelijker overkomen. Meer informatie omtrent het gebruik van formulieren vind je in het hoofdstuk Formulieren. static void DataDic_SetTmp(Args _args) { CustGroup custGroup; ; custGroup.setTmp(); custGroup.custGroup = "10"; custGroup.name = "Test customer 10"; custGroup.insert(); while select custGroup { info(strFmt("%1, %2", custGroup.custGroup, custGroup.name)); } } Een instantie van een tabel kan op “tijdelijk” ingesteld worden in X++. De gegevens worden dan niet verwijderd uit de databank, maar de tabelvariable zal leeg zijn. In het bovenstaand voorbeeld is de tabel CustGroup op “tijdelijk” ingesteld. Één enkele rij werd toegevoegd aan de tijdelijke instantie van CustGroup. Bij het overlopen van de tijdelijke tabel zal enkel deze ene rij die is toegevoegd, afgedrukt worden. Je moet voorzichtig zijn met het gebruik van bestaande tabellen als tijdelijke tabellen. Indien dit gebruikt wordt in een productieomgeving en iemand plaatst het stuk code, dat de tabel op tijdelijk plaatst, uit commentaar, dan zal het resultaat fataal zijn. Je kunt hiervan voorbeelden vinden in het standaard pakket. Hoedanook, het extra werk om een nieuwe tijdelijke tabel aan te maken kan de moeite lonen. Als je tijdelijke tabellen hebt met een groot aantal records, kun je overwegen om deze tijdelijke tabel aan te maken aan de kant van de server. Het eerste record dat wordt toegevoegd aan de tijdelijke tabel, bepaalt vanaf waar deze tijdelijke tabel beheerd wordt. Het kan een voordeel zijn om een klasse aan te maken om je tijdelijke tabel te beheren, want een volledige klasse kun je instellen om te lopen op de server. Meer informatie over het definiëren waar je code uitgevoerd wordt, vind je in het hoofdstuk Klassen.

MORPHX IT Data Dictionary

76

© 2007 Steen Andreasen

Systeemtabellen In tegenstelling tot applicatietabellen, zijn systeemtabellen niet wijzigbaar. Je kunt het datamodel van een systeemtabel niet wijzigen. Systeemtabellen worden gebruikt door de kernel om allerhande taken uit te voeren, zoals de databank gesynchroniseerd houden met de applicatie, het behandelen van licenties en gebruikersinformatie. De systeem-tabellen kun je terugvinden in de AOT onder de knoop System Documentation/Tables. Net zoals eender welke applicatietabel kan je systeemtabellen gebruiken in je wijzigingen. Hou bij het gebruik van systeemtabellen steeds in je achterhoofd dat de data die hierin bewaard wordt, vitaal kan zijn voor de applicatie. De systeemtabellen met het voorzetsel sql* bewaren informatie die de databank gesynchroniseerd houdt met de applicatie. Wijzig je de data in deze tabellen, dan kan je applicatie onstabiel maken. Bij het aanpassen van systeeminformatie, zoals gebruikers of licentiesleutels, worden de systeemtabellen aangepast door de applicatieobjecten. Nochtans beheren de kerneltaken het merendeel van de systeemtabellen. Er is één specifieke systeemtabel die de moeite loont om even apart te bespreken. De systeemtabel SysLastValue bewaart gebruikersgegevens. De tabel wordt regelmatig opgeroepen door de applicatie, want deze bewaart de laatste gebruikersinstellingen van een object. Het is mogelijk dat je SysLastValue nergens gedeclareerd zal zien in de code, want de tabel is verpakt in het runbase raamwerk of ze wordt gebruikt door de klasse xSysLastValue. Meer informatie omtrent gebruikersgegevens vind je in het hoofdstuk Inleiding tot MorphX. Systeemtabel Common De systeemtabel Common is de basis voor alle tabellen. De tabel bevat geen data en ze kan vergeleken worden met een tijdelijke applicatietabel. Common kun je vergelijken met het basistype anytype. Je kunt om het even welke tabel gelijkstellen aan de tabel Common. Dit kan nuttig zijn als je een tabel moet doorgeven als een parameter maar je kent de exacte tabelnaam niet vooraleer de code uitgevoerd wordt. static void DataDic_Common(Args _args) { Common common; CustTable custTable; ; common = custTable; if (common.tableId == tablenum(custTable)) { while select common { info(common.(fieldnum(custTable, name))); } } }

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

77

Het bovenstaande voorbeeld initialiseert Common met CustTable. Een controle werd ingebouwd om te garanderen dat Common gelijk is aan CustTable. Alle records van CustTable worden overlopen, en de klantnaam wordt afgedrukt in de InfoLog. Merk op dat de systeemfunctie fieldnum() gebruikt wordt om de naam van het veld af te drukken. Indien je de veldnaam niet kent bij uitvoering, kun je in de plaats het veldidentificatienummer tussen haakjes invullen. Util Tabellen De systeemtabellen met het voorzetsel Util* zijn eigenlijk geen echte tabellen. Je zal geen enkele van deze tabellen terugvinden in de databank. Ze worden bewaard in de applicatielagen, maar verschijnen in de AOT als systeemtabellen. De Util* tabellen bevatten informatie over eender welk object in de AOT, zelfs in de oude lagen indien je een upgrade van je applicatie hebt uitgevoerd, en bevatten een enorme hoeveelheid gegevens. Als dusdanig is het niet gebruikelijk deze tabellen te gebruiken in complexe selectie-opdrachten omdat koppelingen tussen twee Util* tabellen vrij lang zullen duren. Doe bij het gebruik van deze Util* tabellen ook een beroep op een efficiënte index. Je kunt de beschikbare indexen raadplegen via de Application Hierarchy Tree. De Util* tabellen worden vaak gebruikt door de hulpmiddelen in MorphX. Indien je je eigen hulpmiddelen voor MorphX wil ontwerpen, zul je deze tabellen bijzonder nuttig vinden. Meer informatie over hulpprogramma’s die gebruik maken van de Util* tabellen vind je in de Bijlage MorphX Tools. Velden Zowel uitgebreide datatypes als basisenums kunnen versleept worden om een nieuw veld aan te maken. Indien je hierin je gading niet vindt, moet je een nieuw uitgebreid datatype of base enum aanmaken alvorens nieuwe velden te creëren. Bij het selecteren van de uitgebreide datatypes of basisenums, moet je vooraf controleren of er een configuratiesleutel vermeld is in het eigenschappenblad, want deze velden zullen niet zichtbaar zijn voor de applicatiegebruikers als de bijbehorende configuratiesleutel gedesactiveerd is. Alle velden zouden in hun eigenschappen moeten verwijzen naar een uitgebreid datatype of een basistype. Je kan natuurlijk ook de velden manueel toevoegen aan een tabel, maar als je de gewoonte aanneemt om de gebruikte types te verslepen, zal je niet vergeten om de eigenschappen van het uitgebreid datatype of het basistype in te vullen. Het voordeel van het gebruik van uitgebreide datatypes of basisenums is dat het wijzigen van eigenschappen, zoals de veldlengte of de alignering, ook geldt voor alle velden die dit uitgebreid datatype of basisenum gebruiken. Wanneer een applicatiegebruiker in het Record info formulier een sleutelveld, zoals het klantnummer, selecteert en via de rechtermuisknop kiest voor Rename, zal het uitgebreid datatype van het klantnummer gebruikt worden om alle plaatsen op te sporen in de applicatie waar dit uitgebreid datatype gebruikt wordt. Dit is een zeer krachtige mogelijkheid van Axapta. Er zijn maar weinig applicaties op de markt die een dergelijk flexibel systeem aanbieden aan de applicatiegebruikers. Richtlijnen raden aan om alle velden te laten starten met een kleine letter. Aangezien alle uitgebreide datatypes en basisenums beginnen met een hoofdletter, zou je de velden manueel moeten wijzigen nadat ze versleept werden. Dit is een vrij tijdrovende regel,

MORPHX IT Data Dictionary

78

© 2007 Steen Andreasen

die in de praktijk weinig uithaalt. In de code zie je daarom dat sommige velden beginnen met een kleine letter en andere met een hoofdletter. Wellicht heb je bij het bekijken van de velden in de AOT al de icoontjes opgemerkt voorafgaand aan de veldnamen. Het icoon informeert je reeds over het basistype van een veld. Uitgebreide datatypes en basisenums maken ook gebruik van deze iconen. Dit is een grote hulp bij het selecteren van een veld of een type bij het gebruik van de X++ editor. Vaak gebruikte veldeigenschappen In het voorgaande voorbeeld van MyTable werd een tabel gemaakt en werden er velden aan toegevoegd. Sommige eigenschappen werden ook aangepast. Het veld AccountNum werd gedefinieerd als het sleutelveld voor MyTable. Bij sleutelvelden moet je de eigenschap Mandatory op de waarde “Yes” instellen, aangezien dit zal voorkomen dat een record bewaard wordt met een blanco waarde in het sleutelveld. In de tabelbladeraar of in een formulier zul je deze verplichte velden gemakkelijk herkennen aan het feit dat ze met een rode kleur onderlijnd worden. Het is gebruikelijk in Axapta om sleutelvelden enkel in te vullen bij het aanmaken van een nieuw record. De optie om te hernoemen wordt gebruikt om de waarde van een sleutelveld nadien te wijzigen. De eigenschap AllowEdit heeft de waarde “No” en de eigenschap AllowEditOnCreate heeft een standaardwaarde ‘Yes’. Hierdoor kan je een waarde ingeven bij het aanmaken van een nieuw record. De veldlabels die de applicatiegebruiker ziet, komen van het uitgebreid datatype of de basisenum. Enkel indien je deze waarde wil overschrijven, dien je een waarde op te geven bij de labeleigenschap van het veld. Indien je vaak het label van een uitgebreid datatype overschrijft, kun je overwegen om een nieuw uitgebreid datatype aan te maken. Indien je in een tabel velden hebt die niet nuttig zijn voor de eindgebruiker, zoals de hieronder beschreven containervelden, kun je de eigenschap Visible de waarde “No” geven. Dit zal het veld onzichtbaar maken voor de applicatiegebruiker. Merk op dat het veld niet getoond wordt in de tabelbladeraar. Containervelden Velden van het basistype container worden vaak gebruikt om binaire bestanden te bewaren, zoals bitmaps. Je moet voorzichtig zijn bij het toevoegen van dergelijke velden aan een bestaande tabel, aangezien dit type veld vaak groot is in vergelijking met andere velden. De tabel CompanyInfo is hier een voorbeeld van; als je een grote bitmap toevoegt voor het bedrijfslogo, kan de performantie van de applicatie sterk dalen. De tabel CompanyInfo is vaak gebruikt en omdat doorgaans alle velden van deze tabel geselecteerd worden, zal de bitmap altijd geladen worden wanneer een CompanyInfo record opgehaald wordt. In dit geval moet je dus overwegen om ofwel kleine bitmaps te gebruiken ofwel een gerelateerde tabel aan te maken voor je bitmap. De tabel CompanyImage is een generieke tabel met als doel het bewaren van bitmaps. Een alternatief is om je binaire bestanden toe te voegen aan de Resources knoop in de AOT. Meer informatie over resources vind je in het hoofdstuk Resources.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

79

Systeemvelden Alle tabellen hebben een reeks van systeemvelden, die automatisch toegevoegd worden bij het aanmaken van een nieuwe tabel. De systeemvelden worden niet opgelijst in de AOT. Je kunt een lijst krijgen van de systeemvelden door de velden van de systeemtabel Common te bekijken. De velden met het voorvoegsel modified* en created* bevatten geen waarde, tenzij je de eigenschappen van de bijbehorende veldnaam invult in het eigenschappenblad van de tabel. Standaard worden deze velden niet opgevuld, aangezien deze de performantie lichtjes doen dalen. RecId is een systeemveld dat als uniek identificatienummer van een record gebruikt wordt. Dit is een teller van het type integer of geheel getal, die gedeeld wordt door alle tabellen. De systeemtabel SystemSequences houdt deze teller bij. Telkens een nieuw record wordt toegevoegd aan een tabel, wordt deze teller met één verhoogd en krijgt het veld recId deze waarde. Het bestaan van recId is één van de hoofdredenen voor het gebruik van COM, als je records in Axapta toevoegt vanaf een extern systeem. Indien deze records vanaf het externe systeem direct worden toegevoegd in de databank, zal de echte waarde van recId niet aangepast worden. Een verklaring van het veld dataAreaId vind je in het begin van sectie Tabellen. Veldgroepen Velden die zichtbaar zijn voor de applicatiegebruikers, dienen op zijn minst tot één veldgroep te behoren. In MorphX kun je velden gebruiken die niet behoren tot een veldgroep. Hoedanook zal je enkel door gebruik van veldgroepen het voordeel van MorphX ten volle kunnen benutten. Veldgroepen worden vaak gebruikt bij het aanmaken van formulieren en rapporten. In plaats van velden één per één toe te voegen aan een ontwerp, moet je field groups toevoegen. Indien nadien een veldgroep wijzigt, bv. door het toevoegen van een nieuw veld aan de groep, zal dit nieuw veld automatisch beschikbaar zijn op alle plaatsen waar de specifieke veldgroep gebruikt wordt. De applicatiegebruikers kunnen alle velden van een tabel bekijken door in een formulier via de rechtermuisknop de optie Show all fields te selecteren van het Record info formulier. De velden worden gegroepeerd per veldgroep. Velden die niet behoren tot een veldgroep, worden allemaal getoond in een standaard veldgroep. Indien alle nieuwe velden worden toegevoegd aan een logische veldgroep, zal de applicatiegebruiker een logischer en gemakkelijker overzicht krijgen van de velden in die tabel. Voorbeeld 2: Toevoegen van veldgroepen Objecten gebruikt van MORPHXIT_DataDictionary project

Table, MyTable In het vorige voorbeeld werd de tabel MyTable aangemaakt en werden er velden aan toegevoegd. Nu zullen er veldgroepen worden toegevoegd aan de zonet aangemaakte tabel MyTable. 1. Zoek de veldgroepknoop AutoReport op. Selecteer de 3 velden en versleep deze

velden naar de AutoReport knoop.

MORPHX IT Data Dictionary

80

© 2007 Steen Andreasen

2. Ga naar de veldgroepknoop AutoLookup. Sleep de velden accountNum en custName naar de AutoLookup knoop.

3. Maak een nieuwe veldgroep met naam “Identification” door via de rechtermuisknop

op de knoop Field Groups te kiezen voor New Group. De naam van de veldgroep kan ingesteld worden via het eigenschappenblad. Voeg het veld accountNum toe aan deze veldgroep.

4. Maak een nieuwe veldgroep met naam “Overview” en voeg alle 4 velden toe aan

deze veldgroep. 5. Maak uiteindelijk een nieuwe veldgroep met naam “Details” en voeg er de velden

custName, custGroupId en currencyCode aan toe. 6. Bewaar de tabel. Je kan je afvragen waarom zoveel veldgroepen nodig zijn: AutoReport en AutoLookup zijn gereserveerde veldgroepen. De andere 3 veldgroepen worden gebruikt bij het groeperen van de velden op formulieren en rapporten. Bij het selecteren van het printericoon in een formulier, zullen de velden van de AutoReport-groep afgedrukt worden. Indien er geen velden werden toegevoegd, zal het rapport leeg zijn. Deze mogelijkheid heet auto-rapportering en wordt door de applicatie-gebruiker opgeroepen om eenvoudige rapporten af te drukken, rechtstreeks vanaf een formulier. De velden van de tabel die het vaakst gebruikt worden, zouden aan de veldgroep AutoReport moeten worden toegevoegd. AutoLookup kan gebruikt worden om te bepalen welke velden er getoond zullen worden bij het indrukken van de opzoekknop. Indien er geen velden werden toegevoegd aan de AutoLookup, zullen de tabeleigenschapvelden TitleField1 en TitleField2 gebruikt worden samen met het eerste veld van elke index. Formulieropzoekingen worden verder besproken in het hoofdstuk Formulieren. Het is aangeraden om steeds de twee veldgroepen Identification en Overview aan te maken. De veldgroep Identification zou alle sleutelvelden van een tabel moeten bevatten. Indien de tabel een unieke index heeft, zouden alle velden van deze unieke index ook aan deze veldgroep moeten worden toegevoegd. Beschouw de veldgroep Overview als een samenvatting of overzicht van de tabel. Deze veldgroep wordt gebruikt voor het tabblad Overview op de meeste formulieren. De meest gebruikte velden en op zijn minst alle verplichte velden zou je aan deze groep moeten toevoegen. Naast de Overview veldgroep zouden alle velden moeten behoren tot minstens één andere veldgroep om een logische groepering te maken van alle velden. De veldgroepen Identification en Details zijn de logische groeperingen voor de velden van MyTable. Alvorens de veldgroepen te gebruiken voor een formulier of een rapport, moeten er labels worden toegevoegd. Een label kan worden toegevoegd aan een veldgroep via het eigenschappenblad van de veldgroep. Je kan niet alleen velden toevoegen aan een veldgroep. Display en edit-methodes kunnen ook toegevoegd worden. Nochtans is de oplossing niet waterdicht want methoden toegevoegd aan een veldgroep, worden geïdentificeerd door een

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

81

opeenvolgende nummering. Indien een methode werd toegevoegd aan of verwijderd uit de tabel, worden de methodes hernummerd. Daarna moet je controleren of de veldgroepen nog altijd beschikken over de gewenste display- en edit-methodes. Het gebruik van methodes is beschreven in de sectie Methodes. Indexen Iedere tabel heeft op zijn minst één index nodig. Indien een tabel een unieke index heeft, kun je één enkel record ophalen. Dit is echter niet altijd mogelijk en niet altijd de beste oplossing. Transactietabellen zullen vaak verschillende records hebben met dezelfde waarden, waardoor het onmogelijk is om een unieke index te maken. Aangezien transactietabellen vaak worden aangesproken en een hoog aantal records bevatten, zal een unieke index zijn invloed hebben op de performantie. Belangrijk hier is dat de performantie van je applicatie in grote mate afhangt van de kwaliteit van je indexen. Hoofdtabellen en groeptabellen, zoals klanten- en klantengroeptabel, zouden unieke indexen moeten hebben, terwijl tabellen met een groot aantal records, zoals transactietabellen, beter geen unieke index hebben. Wees ook voorzichtig met het veelvuldig creëren van indexen op tabellen, want ook dit heeft zijn invloed op de performantie. Beheer je indexen dus met de nodige zorg en voorzichtigheid. Telkens wanneer de data in de tabel wijzigt, zal de databank de index(en) voor deze tabel bijwerken. Voorbeeld 3: Toevoegen van indexen Objecten gebruikt van MORPHXIT_DataDictionary project

Table, MyTable In de volgende stap voegen we indexen toe aan MyTable. In dit voorbeeld maken we twee indexen aan. 1. Zoek de knoop Indexes op in MyTable. Kies via de rechtermuisknop voor New

Index om een index aan te maken. Gebruik het eigenschappenblad om de index de naam “AccountIdx” te geven. Versleep het veld accountNum naar de index.

2. Ga naar het eigenschappenblad van AccountIdx en geef de eigenschap

AllowDuplicates de waarde “No”. 3. Maak een nieuwe index met als naam GroupIdx. Voeg er de velden custGroupId en

accountNum aan toe. 4. Bewaar de tabel. We hebben een unieke index aan MyTable toegevoegd door de eigenschap AllowDuplicates de waarde No te geven. Dit voorkomt dat er twee records kunnen ingegeven worden met dezelfde waarden voor de velden die behoren tot de unieke index. Indien een tabel data bevat en je wijzigt nadien een index van die tabel naar een unieke index, kun je een fout krijgen indien er dubbele records bestaan voor de index die je wil definiëren als uniek. Dit kan eigenaardig zijn als je geen data hebt in het actuele bedrijf, maar geen enkel bedrijf mag dubbele records bevatten voor de unieke

MORPHX IT Data Dictionary

82

© 2007 Steen Andreasen

index. Het is mogelijk om meer dan één unieke index te creëren voor een tabel, maar dit is geen goede keuze voor je databankontwerp. Daarnaast zal het hebben van meerdere unieke indexen het gebruik van de tabel onnodig complex maken. Tabellen met een unieke index moeten een waarde hebben voor de eigenschappen PrimaryIndex en ClusterIndex. Enkel unieke indexen kunnen geselecteerd worden als primaire index. De primaire index wordt gebruikt als de standaard sorteervolgorde wanneer de data van de tabel opgehaald wordt. Bij het cachen van de tabel wordt ook de primaire index gebruikt. Een unieke index wordt gespecifieerd als een ClusterIndex voor betere performantie. Geclusterde indexen bevatten zowel indexen als data, terwijl een gewone index enkel een gesorteerde lijst bevat van de indexvelden. Wanneer een record opgehaald wordt via een gewone index, moet de databank eerst de index doorzoeken en vervolgens het record opzoeken. Relaties Wanneer je klikt op de Transactions-knop op het klantenformulier, open je het klanttransactiesformulier. Probeer een andere klant te selecteren in het klantenformulier. Merk op dat het formulier met de klanttransacties automatisch vernieuwd wordt met de transacties voor de geselecteerde klant. Dit is één van de redenen voor het gebruik van relaties. Relaties zullen je tabellen met elkaar verbinden en MorphX tonen hoe het datamodel eruitziet. Gebruik van relaties zal je ook veel code besparen. Onderstaand voorbeeld met de klanttransacties werd gemaakt zonder ook maar één lijn code te schrijven. Voorbeeld 4: Relaties toevoegen Objecten gebruikt van het MORPHXIT_DataDictionary project

Table, CustTable Een relatie zal gelegd worden naar MyTable in CustTable, waardoor het mogelijk is om een klantnummer te koppelen aan een nummer in MyTable. 1. Zoek de tabel CustTable en ga naar de Fields knoop. Voeg een nieuw veld toe via

het uitgebreid datatype AccountNum. Hernoem het veld “altCustAccountNum”. 2. Ga naar de veldgroep Delivery in CustTable en voeg er het veld

altCustAccountNum aan toe. 3. Zoek de Relations knoop op in CustTable. Klik met de rechtermuisknop op de

knoop Relations en kies New Relation. Geef de nieuwe relatie de naam “MyTable” via het eigenschappenblad.

4. Selecteer de nieuwe relatie MyTable, klik met de rechtermuisknop en kies Normal.

Ga naar het eigenschappenblad en selecteer altCustAccountNum als Field en accountNum als RelatedField.

5. Bewaar de tabel.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

83

We hebben nu een nieuw veld aangemaakt in CustTable en gerelateerd met de tabel MyTable. Het nieuwe veld altCustAccountNum is deel van de veldgroep Delivery. Open het formulier CustTable en ga naar het tabblad Setup. In de veldgroep Delivery vind je het nieuwe veld met het label “Account number”. Merk op dat het veld een opzoekknop heeft. Doordat we in CustTable een relatie hebben gelegd naar MyTable, werd automatisch een opzoekknop toegevoegd. Je kunt nu een nummer kiezen van MyTable via de opzoekknop. Indien we in het nieuwe veld in CustTable een nummer intikken, dat niet bestaat in MyTable, zal er een fout optreden. Een relatie zal standaard garanderen dat alleen waarden uit de gerelateerde tabel geldig zijn. Indien nodig kun je deze validatie uitschakelen vanaf het eigenschappenblad. De relatie beschreven in CustTable werd aangemaakt als een normale relatie door het gebruik van één veld. Je kan eender welk aantal velden gebruiken om de relatie te bepalen. Aangezien MyTable slechts één veld heeft in de primaire index, is er slechts één veld nodig om de relatie te leggen. Alle velden die behoren tot de primaire index, moeten als gewone relatievelden toegevoegd worden. Er zijn drie soorten relaties mogelijk :

• Normaal : bepaalt relatievelden zonder bijkomende voorwaarden; • Veldklem (Field Fixed) : bepaalt relatievelden met filtering op de records in de

primaire tabel. Deze relatie geldt enkel voor numerische waarden. • Gerelateerde veldklem (Related Field Fixed) : bepaalt relatievelden met

filtering op records in de gerelateerde tabel. Deze relatie geldt enkel voor numerische waarden.

De laatste 2 relatietypes worden in feite toegevoegd aan de normale relatie. Hiermee kan je records filteren op basis van de waarde van een enumeratie, of je kan bepalen op welke tabel de relatie gelegd wordt. Neem als voorbeeld de tabel LedgerJournalTrans die manueel ingevoegde journaallijnen bevat. Wanneer de gebruiker de opzoekknop van het nummerveld indrukt, zal afhankelijk van het rekeningtype een andere tabel geselecteerd worden, zoals grootboek, klantentabel of leverancierstabel. Wanneer je de relaties met LedgerTable, CustTable en VendTable in de tabel LedgerJournalTrans naziet, zul je zien dat de relaties werden aangemaakt door gebruik van twee gewone relatievelden en één veldklemrelatie voor het nummertype. Dit voorbeeld gebruikt verschillende tabellen. Je zou ook de veldklemrelatie en gerelateerde veldklemrelatie kunnen gebruiken voor het filteren van data op dezelfde tabel, aangezien er meer dan één relatie kan gedefinieerd worden op dezelfde tabel. Stel je voor dat een kledingfabrikant in de herencataloog drie soorten kledij voorziet : vrijetijdskledij, maatpakken en sportkledij. In de MensCollection tabel is er een veld ClothingCategoryID, waar de waardes gedefinieerd zijn in de ClothingCategory enum. Het veld ClothingCategoryID zit ook in de verkooporderstabel. Als een verkoper een categorie intikt in het veld ClothingCategoryID van de verkooporderstabel, wordt er een opzoeklijst getoond van alle beschikbare kledingstukken. De kledingstukken die getoond worden, zijn gefilterd op basis van de waarde van de ClothingCategoryID. Dit maakt het opzoeken eenvoudiger omdat twee soorten herenkledingstukken niet voorkomen in de zoeklijst. Onthoud vooral dat de beide relaties (veldklem, gerelateerde veldklem) aan je expressie zullen worden toegevoegd. Het gebruik van opzoeklijsten zal verder besproken worden in het hoofdstuk Formulieren.

MORPHX IT Data Dictionary

84

© 2007 Steen Andreasen

Een uitgebreid datatype kan ook een relatie naar een tabel bevatten. Maar als een tabel relaties heeft op hetzelfde veld, zal de tabelrelatie de relatie van het uitgebreide datatype overschrijven. Als je relaties op twee plaatsen hebt, kan dit tot verwarring leiden bij het leren van MorphX. Het gebruik van de Visual MorphXplorer kan je hierbij helpen , want de relaties van zowel tabellen als uitgebreide datatypes worden getoond in de diagrammen. Verwijderacties Om de samenhang van de gegevens te verzekeren, maakt men gebruik van verwijder-acties. Wanneer je een tabel hebt aangemaakt om informatie van klanten in te bewaren, zal deze tabel gerelateerd zijn met de klantentabel. Indien je beslist om een klant te verwijderen uit de klantentabel, zal de data in je tabel bewaard blijven, maar je zal deze informatie niet meer nodig hebben. Dit zal inconsistentie veroorzaken in je data, zowel nu als in de toekomst: bijvoorbeeld, indien je nieuw gecreëerde tabel klanttransacties bevatte en je maakt een nieuwe klant aan met hetzelfde nummer, zal de tabel in de gerelateerde tabel opnieuw zichtbaar zijn, maar voor een andere klant. Om dergelijke situaties te vermijden, zullen verwijderacties ofwel data verwijderen uit de gerelateerde tabel indien een klant wordt verwijderd, of voorkomen dat een klant wordt verwijderd zolang er nog records zijn in de gerelateerde tabel. Het onderscheid tussen beide soorten acties hangt af van de soort informatie die je wil verwijderen. Stel je voor dat we over de volgende tabellen beschikken : een klantentabel met daaraan gekoppeld een verkoopsfacturentabel en een notatabel waar de verkoper nota’s bewaart per klant. Stel dat de gebruiker een klant wil verwijderen waarvoor er reeds verkoopsfacturen zijn genoteerd, dan wordt voorkomen dat deze klant kan verwijderd worden, omdat men enkel een klant mag wissen waarvoor er nog geen verkoopsfacturen genoteerd zijn. Indien de gebruiker een klant wil verwijderen waarvoor er enkel ook in de notatabel gegevens genoteerd zijn, dan mag de klant en de bijbehorende nota’s in de aparte notatabel verwijderd worden. Voorbeeld 5: Het toevoegen van verwijderacties Objecten gebruikt van het MORPHXIT_DataDictionary project

Table, CustTable In dit voorbeeld zullen verwijderacties worden toegevoegd op zowel CustTable als MyTable om dataconsistentie te verzekeren. 1. Ga naar de knoop DeleteActions in MyTable. Kies via de rechtermuisknop voor

New DeleteAction. Gebruik het eigenschappenblad van de nieuwe verwijderactie en stel deze in op CustTable. De verwijderactiemodus zet je op Restricted.

2. Zoek nu CustTable op en voeg een nieuwe verwijderactie toe aan de tabel MyTable.

Open het eigenschappenblad voor de nieuwe verwijderactie en kies MyTable. Geef de verwijderactiemodus de waarde Cascade.

3. Bewaar de tabel.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

85

Open het formulier CustTable, maak een nieuwe klant aan en selecteer een alternatief rekeningnummer van MyTable voor de nieuwe klant. Eenmaal klaar, probeer je via de tabelbladeraar het rekeningnummer te verwijderen van MyTable dat gerelateerd is met de nieuwe klant en je zal een foutmelding krijgen. De verwijderactie heeft de waarde “restricted” in MyTable en zal voorkomen dat records worden verwijderd die gebruikt worden in gerelateerde tabellen. Indien je probeert de nieuwe klant te verwijderen en daarna de tabel MyTable met de tabelbladeraar bekijkt, zul je merken dat het rekeningnummer dat gerelateerd was aan de klant, ook verwijderd is. Een verwijderactie op cascade plaatsen, zal alle gerelateerde records verwijderen. Verwijderacties gebruiken relaties om te bepalen of data van de gerelateerde tabel al dan niet moet verwijderd worden. Indien er geen relatie bestaat voor een tabel, gebruikt in een verwijderactie, zal de verwijderactie geen effect hebben. static void DataDic_DeleteActions(Args _args) { MyTable myTable; ; ttsbegin; select forupdate myTable where myTable.accountNum == "10"; if (myTable.validateDelete()) myTable.delete(); ttscommit; } Bij het verwijderen van records via X++ dienen sommige regels in acht genomen te worden om de verwijderacties te valideren. In dit voorbeeld haalt men één record op uit MyTable. Alvorens het record te verwijderen, wordt dit gevalideerd. Indien het record werd verwijderd zonder de methode validateDelete() op te roepen, zou de verwijderactie niet gevalideerd worden, wat zou kunnen leiden tot onsamenhangende data. Een verwijderactie kan op een derde modus ingesteld worden namelijk “Cascade + Restricted”. De keuze van de modus van de verwijderactie hangt af van de plaats waar ze wordt opgeroepen. In het ene geval (“cascade”) is er sprake van een domino-effect, waardoor het verwijderen van een record uit één tabel alle bijbehorende records uit gerelateerde tabellen eveneens verwijdert. In het andere geval (“restricted”) is er sprake van een beperking, met andere woorden, een record mag pas uit een tabel verwijderd worden indien er geen bijbehorende records in gerelateerde tabellen zijn. De verwijderactie zal zich beperken indien ze wordt opgeroepen vanaf de tabelbladeraar of vanaf een formulier. Bij het verwijderen van een record vanuit X++, zal deze modus een “cascade delete” uitvoeren in de gerelateerde tabel, zonder de validateDelete() methode op te roepen. Als je de bovenstaande code herbekijkt, zou dit hetzelfde betekenen als het verwijderen van een record in CustTable vanaf het klantformulier en vervolgens bijbehorende records verwijderen uit MyTable. Verwarrend ? De verwijderactiemodus Cascade + Restricted is nauwelijks gebruikt, en er is waarschijnlijk een goede reden om het niet te doen.

MORPHX IT Data Dictionary

86

© 2007 Steen Andreasen

Methodes Methodes worden gebruikt om X++ code aan je applicatie toe te voegen. Code in methodes wordt ook wel bedrijfslogica genoemd. Bij het wijzigen, toevoegen, of verwijderen van rijen uit een tabel, worden een aantal standaard methoden uitgevoerd. Je kunt je eigen methodes toevoegen en deze laten uitvoeren door één van de standaard methodes. Bij het wijzigen van een standaard methode, overschrijf je deze methode. Om een methode te overschrijven, ga je naar de Methods knoop van een tabel en kies via de rechtermuisknop voor Override Method. Een lijst van de standaard methoden komt tevoorschijn. Indien je de methode validateField() selecteert, zal onderstaande code getoond worden in de editor: public boolean validateField(fieldId _fieldIdToCheck) { boolean ret; ret = super(_fieldIdToCheck); return ret; } Bemerk het oproepen van super(). Dit voert de code uit van de overeenkomstige methode in de ouderklasse. Als je de standaard methode van een tabel overschrijft, erf je in feite over van een methode van een systeemklasse. Bijvoorbeeld, indien je één van de standaard tabelmethodes overschrijft, erf je een methode over van de systeemklasse XRecord. Tabelmethodes en klassen worden verkozen voor het toevoegen van logica aan de applicatie. Als je geen bedrijfslogica in de gebruikersinterface hebt, is dit gemakkelijker om nadien code te hergebruiken of deze van versie te verhogen. Je kunt niet voorkomen dat er eigen code op formulieren en rapporten komt, maar je zou er naar moeten streven dat er geen data wordt gewijzigd via code in formulieren of rapporten. Een voorbeeld van deze werkwijze vind je terug in het Model-View-Controller ontwerppatroon (MVC design patter). Je zou er moeten in slagen om je formulieren en rapporten te vervangen met een andere gebruikers-interface zoals een webinterface, zonder je bedrijfslogica te moeten herschrijven. Het gebruik van methodes werd hier slechts kort beschreven, om je toch een idee te geven van de mogelijkheden van methodes. In het hoofdstuk Klassen worden methodes dieper in detail besproken.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

87

Vaak gebruikte methodes Als je data wijzigt via het toevoegen, verwijderen of aanpassen van records, worden standaard methodes uitgevoerd. Wanneer je een nieuw record aanmaakt via de tabel-bladeraar of via een formulier, wordt de tabelmethode initValue() uitgevoerd. InitValue() wordt gebruikt voor het opvullen van velden met een standaard waarde. public void initValue() { super(); this.custGroupId = "10"; } Overschrijf initValue() in MyTable en voeg de bovenstaande lijn code toe. Via de tabelbladeraar open je de tabel MyTable en druk op de toetsen ctrl+n om een nieuw record aan te maken. Het veld custGroupId zal nu de standaard waarde 10 bevatten. Het sleutelwoord this in initValue() wordt gebruikt om te verwijzen naar het huidige object. Je kunt this niet vervangen met MyTable, aangezien de compiler zou verwachten dat MyTable een tabelvariabele is, gedeclareerd in de hoofding van de methode. Het gebruik van this voorkomt dat er verwarring ontstaat tussen de referentie naar het object en variabelen. Opmerking : Om een overzicht te krijgen van welke methoden uitgevoerd worden, kun je de methoden die je wil onderzoeken, overschrijven en een lijn toevoegen waarbij je een lijn tekst afdrukt in de InfoLog voor iedere overschreven methode. Als alternatief kun je een methode overschrijven en een breekpunt plaatsen bij de oproep van super(). Wanneer het breekpunt wordt uitgevoerd, kunnen de opgeroepen methoden opgevraagd worden in het stack trace venster in de debugger. Telkens een waarde van een veld wijzigt, wordt de methode modifiedField() opgeroepen. De methode is nuttig om waarden van andere velden te initialiseren indien de waarde van het huidige veld werd gewijzigd.. public void modifiedField(fieldId _fieldId) { switch (_fieldId) { case fieldnum(MyTable, custGroupId) : this.custCurrencyCode = ""; break; default : super(_fieldId); } } Zoals je kunt zien in bovenstaand voorbeeld, wordt de waarde van het veld custCurrencyCode uitgeblankt wanneer de waarde van het veld custGroupId wijzigt. ModifiedField() ontvangt het veldnummer van het actieve veld als parameter. Een switch-opdracht wordt gebruikt om te bepalen welk veld actief is. Indien geen enkel van de gecontroleerde velden actief zijn, wordt de super() methode opgeroepen. De switch bevat enkel een validatie voor één veld, dus hier hadden we ook een if-else opdracht kunnen gebruiken. Door een switch-opdracht te gebruiken kun je later de code gemakkelijker uitbreiden met bijkomende controles.

MORPHX IT Data Dictionary

88

© 2007 Steen Andreasen

Deze methode werd voor het eerst geïntroduceerd in versie 3.0. Daarvoor gebeurden de controles op veldwijzigingen in formulieren. Dit zorgde ervoor dat er veel code op formulieren werd geplaatst, die eigenlijk op tabelniveau thuis hoorden. Omwille hiervan zul je nog her en der veldwijzigingscontroles terugvinden in formulieren. De ideale oplossing is alle generieke controles plaatsen op tabelniveau. Maar indien de logica specifiek voor één enkel formulier van toepassing is, is het soms nodig om de controle in een formulier te plaatsen. print this.orig().custCurrencyCode; Een leuke eigenschap bij het wijzigen van een veldwaarde is dat je de vorige waarde opnieuw kunt oproepen. De waarde die behouden is, is de laatst geborgen waarde. De methode orig() wordt gebruikt om die opgeslagen waarde op te halen. Orig() zal een instantie van de huidige tabel maken. Als je het veld specifieert, kan je één enkele veldwaarde van orig() bijhouden. Wanneer het record wordt bewaard, zal orig() aangepast worden. De methode validateField() is gelijkaardig aan modifiedField(). Beide methodes ontvangen het actieve veld als parameter. Daar waar modifiedField() gebruikt wordt om de waarde van andere velden te initialiseren en geen waarde teruggeeft, gebruikt men validateField() enkel voor het valideren en zal deze een waarde true of false teruggeven. Indien validateField() de waarde false teruggeeft, zal de applicatie voorkomen dat de eindgebruiker de waarde van het veld effectief wijzigt. Het is belangrijk om de respectievelijke methode te gebruiken voor het initialiseren en het valideren, omdat dit het coderen eenvoudiger maakt. public boolean validateField(fieldId _fieldIdToCheck) { boolean ret; ret = super(_fieldIdToCheck); if (ret) { switch (_fieldIdToCheck) { case fieldnum(MyTable, custName) : if (strlen(this.custName) <= 3) ret = checkFailed("Customer name must be longer than 3 characters."); } } return ret; } De super() oproep controleert of het veld een geldige waarde heeft indien het veld een relatie heeft. Een voorbeeld hiervan is de klantengroeptabel. Indien de gebruiker in een record van de klantentabel een klantengroep intikt, zal de super() methode nakijken of de ingetikte waarde ook voorkomt in de klantengroeptabel. Een verplicht veld zal ook gecontroleerd worden. In het bovenstaande voorbeeld zal de validatie op het veld custName worden uitgevoerd, indien de controles in super() geldig zijn. In dit geval wordt een waarschuwing getoond in de Infolog indien de lengte van de

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

89

klantnaam kleiner is dan of gelijk is aan drie karakters. De globale methode checkFailed() wordt normaal gebruikt om validatieberichten naar de Infolog te sturen. Het bewaren van een record zal de validateWrite() methode oproepen. Daar waar validateField() een controle uitvoert op velden ingevuld door de eindgebruiker, zal validateWrite() enkel verplichte velden controleren. Controles uitgevoerd door validateWrite() zijn identiek als deze die opgeroepen worden via de super() oproep in validateField(). Indien een veld niet verplicht is, heeft de gebruiker mogelijk niet alle velden ingevuld als het record bewaard wordt. Als je voorwaarde niet gericht is op de waarde die door een gebruiker ingevuld wordt, moet je de validatie schrijven in de validateWrite() methode. De syntax van validateWrite() is gelijkaardig aan validateField(). Na de super() oproep moet je de teruggeefwaarde controleren alvorens je controles uit te voeren. Indien validateWrite() geldig is, zullen insert() of update() uitgevoerd worden. Indien het record vooraf nog niet bewaard werd, zal de insert() methode opgeroepen worden. Update() wordt opgeroepen indien het systeemveld recId een waarde bevat en het record eerder werd bewaard. Insert() en update() worden zelden overschreven. Op dit punt moet je reeds al je controles uitgevoerd hebben, en velden een waarde gegeven hebben. Indien je insert() of update() dient te overschrijven kan het zijn dat je op het verkeerde spoor bent. Als je er zeker van moet zijn dat een veld een bepaalde waarde heeft bij het invoegen van een record, kun je dit veld initialiseren voor het oproepen van super() in de insert() methode. Sommige speciale gevallen kunnen ook vereisen dat deze methoden overschreven worden, bijvoorbeeld, wanneer je de inhoud van een record dient te synchroniseren met een record in een andere tabel. Voorbeelden van het synchroniseren kun je terugvinden in de tabellen CustTable en EmplTable. Dit is geen aan te raden oplossing, maar het kan de beste keuze lijken uit je mogelijkheden. Het verwijderen van een record heeft een gelijkaardige werking. Bij het verwijderen van een record wordt eerst validateDelete()uitgevoerd. Indien de methode met succes is uitgevoerd, zal de methode delete() opgeroepen worden. Dezelfde regels voor toevoegen en aanpassen van records gelden bij het verwijderen. Dit betekent dat je eerst alles moet controleren alvorens delete() op te roepen. ValidateDelete() zal ook controleren of een verwijderactie toelaat om een record te verwijderen. Data toevoegen via X++ vereist wat meer dan wanneer men data toevoegt via de gebruikersinterface zoals via formulieren. Enkel de tabelmethodes die je oproept, zullen uitgevoerd worden. Indien een validatiemethode niet wordt opgeroepen, zal er ook niets gevalideerd worden bij het aanpassen, toevoegen of verwijderen. Bij het toevoegen of aanpassen van een record, gebeurt alleen de controle op de unieke indexen. static void DataDic_InsertRecord(Args _args) { MyTable myTable; ; ttsbegin; myTable.initValue(); myTable.accountNum = "100"; myTable.custName = "Alt. customer id 100"; myTable.custCurrencyCode = "USD"; if (myTable.validateWrite()) myTable.insert();

MORPHX IT Data Dictionary

90

© 2007 Steen Andreasen

ttscommit; } Bovenstaand voorbeeld toont hoe je tabelmethodes moet gebruiken bij het toevoegen van een record in MyTable. InitValue() wordt opgeroepen en zal een waarde geven aan het veld custGroupId. Het record zal enkel worden toegevoegd indien validateWrite() geldig is. Aangezien alle verplichte velden een waarde hebben, zal het record worden toegevoegd. In plaats van de methode insert() op te roepen, kon je ook de methode write() oproepen. Dit zal een bestaand record aanpassen, maar indien het record niet bestaat, wordt een nieuwe record aangemaakt. Het oproepen van validatiemethodes kost performantie en misschien wil je, afhankelijk van het geval, ervoor kiezen je data te valideren via andere methoden. Je kunt ook de code overslaan bij insert(), update() en delete() door het gebruik van de overeenkomstige tabelmethodes met voorvoegsel do*. Deze methodes kunnen niet overschreven worden en kan je enkel oproepen vanuit X++. Indien je alle logica overslaat bij de tabelmethodes om je code te optimaliseren, moet je er zeker van zijn dat je data niet corrupt zal zijn. De selectiesleutelwoorden delete_from, insert_recordset en update_recordset doen één enkele oproep naar de databank vanaf de client bij het verwerken van meerdere records. Het overschrijven van insert, update en delete zal de performantie van deze selectiesleutelwoorden beïnvloeden en ervoor zorgen dat het toevoegen, wijzigen of verwijderen record per record gebeurt. Op de meeste tabellen zul je de methodes find() en exist() terugvinden. Deze methodes zijn niet overschreven. De methodes werden meestal gemaakt om één enkel record op te halen volgens een unieke index. Het is aangewezen om deze twee methodes toe te voegen bij het creëren van een nieuwe tabel, aangezien je ze vroeg of laat toch nodig hebt. Alle velden die behoren tot de unieke index, moet je toevoegen als parameter. Find() wordt gebruikt om één enkel record te zoeken in de tabel. Dit kan zijn om een veldwaarde in te vullen in de tabelmethode initValue(). Exist() wordt gebruikt om na te gaan of een record bestaat dat voldoet aan de indexvelden. Een dergelijke controle past men meestal toe alvorens een nieuwe record toe te voegen aan de databank. Je zult veel voorbeelden terugvinden van deze methodes bij het doorbladeren van de tabellen in de AOT. Methodes, gebruikt voor gelijkaardige operaties, maar die geen unieke index bevatten, geef je niet zomaar de naam find() of exist(). Beschouw deze namen als gereserveerd en gebruik een navoegsel indien de methode geen unieke index gebruikt, zoals findVoucherDate(). Transactieopvolgingsysteem Zoals de naam het zegt, gebruikt men het Transactieopvolgingsysteem (Transaction Tracking System of TTS) om transacties te volgen. Het idee dat erachter schuilt, is om te garanderen dat de volledige transactie geborgen is in de databank. Dit is vitaal voor een relationele databank en nog meer voor een ERP systeem. Terwijl je een factuur boekt, moet je er zeker van zijn dat deze niet resulteert in corrupte data, wanneer dit proces faalt. TTS verzekert dat alle databankoperaties binnen een TTS lijn volledig weggeschreven worden of niet.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

91

Telkens je een insert, update of delete uitvoert, zou je een beroep moeten doen op TTS. In feite kun je zelfs geen record aanpassen zonder deze te plaatsen in een TTS-blok. Een TTS-blok wordt geïnitialiseerd door het sleutelwoord ttsbegin. Het sleutelwoord ttscommit zal alle dataoperaties binnen de TTS-blok wegschrijven naar de databank. Een blok kan afgebroken worden via het sleutelwoord ttsabort, wat de transactie zal terugdraaien naar zijn startpunt. Indien het systeem crasht, zal er automatisch een ttsabort opgeroepen worden door de databank. Een voorbeeld van het gebruik van TTS vind je in het bovenstaande codevoorbeeld bij het invoegen van een record in MyTable. Dit betekent niet dat je iedere databankoperatie in een TTS-blok moet plaatsen. Indien je code opgeroepen werd door een andere methode, waarin reeds een TTS-blok werd gestart, is een nieuw TTS-blok niet altijd noodzakelijk. Het starten van een extra TTS-blok binnen een TTS-blok zal een extra niveau toevoegen. Elk niveau dient bevestigd te worden met een ttscommit. Denk erom dat TTS je een foutmelding zal geven als de geneste TTS-blokken niet evenveel ttsbegin en ttscommit opdrachten bevatten. Dit wordt meestal veroorzaakt door een ontbrekende ttscommit. Je kunt een job uitvoeren die ttscommit oproept om de TTS fout te corrigeren. Je zal uiteraard het onevenwicht in de TTS-instructies moeten oplossen, maar het alternatief is de Axapta client herstarten. static void DataDic_UpdateRecord(Args _args) { MyTable myTable; ; ttsbegin; select forupdate firstonly myTable; myTable.custName = "Customer updated"; if (myTable.validateWrite()) myTable.update(); ttscommit; } Indien je een record dient aan te passen, moet je deze steeds ophalen met het sleutelwoord forupdate in de TTS-blok. Een vaak voorkomende fout bij het selecteren met forupdate is het ophalen van data om deze aan te passen alvorens ttsbegin op te roepen. Indien je dit doet, zal er een fout optreden. In het bovenstaande voorbeeld wordt het eerste record van MyTable opgehaald en het veld custName wordt gewijzigd alvorens de gegevens worden weggeschreven en geborgen via ttscommit.

MORPHX IT Data Dictionary

92

© 2007 Steen Andreasen

3.2 Koppelvlakken In Axapta zijn verschillende mogelijkheden in de Account Payable module en de Account Receivable module erg gelijkaardig, zoals de hoofdtabellen met klanten of leveranciers, transactie- en setuptabellen. De verschillende modules bieden je ook de mogelijkheid om journalen te starten, zoals journalen om manueel bonnen in te geven. Naast die gelijkaardige mogelijkheden in de modules is ook veel van de bedrijfslogica gelijkaardig, waardoor het mogelijk is om heel wat code te hergebruiken. Maar hoewel de tabellen en hun velden er identiek mogen uitzien in opbouw, verschillen de gebruikte namen vaak sterk van elkaar. Axapta biedt de mogelijkheid om te noteren welke tabellen en velden gelijkaardig zijn door ze met elkaar te koppelen of “mappen”. Hiervoor gebruikt men tabelkoppelvlakken of table maps. Men gebruikt vaak de term “table map” om het onderscheid te maken met de “foundation class map”. Het meest gebruikte koppelvlak in MorphX is AddressMap. Adresinformatie wordt gebruikt in verschillende tabellen en validaties van adresinformatie, zoals postcodes, zullen niet verschillen indien deze werd ingegeven voor een werknemer of een klant. Het koppelen van alle tabellen die gebruik maken van adressen aan hetzelfde koppelvlak zal het eenvoudiger maken om code van een koppelvlak te hergebruiken aangezien je je geen zorgen moet maken over de naamgeving van veldnamen in een specifieke tabel. Koppelvlakken hebben gelijkaardige knooppunten en eigenschappen als een tabel. In koppelvlakken zullen meestal enkel velden aangemaakt en gekoppeld worden, aangezien de eigenschappen al gespecifieerd zijn in de gekoppelde tabel. Je kunt koppelvlakken aanspreken vanuit X++ op dezelfde manier dat je een tabel aanspreekt of objecten zoals formulieren of rapporten. Enkel wanneer je een koppelvlak gebruikt op een formulier of een rapport, moet je overwegen veldgroepen aan te maken voor je koppelvlak. Bekijk nog even het koppelvlak AddressMap. Het eerste knooppunt “Fields” bevat de velden van het koppelvlak. Daarna zie je het knooppunt “Field groups”, waar de veldengroep voor autorapportering is gedefinieerd. En vervolgens zie je het knooppunt “mappings” waar je de diverse koppellijsten kan zien. Een koppellijst zorgt voor de koppeling van de velden van het koppelvlak met de overeenkomstige velden van een bepaalde tabel. Voorbeeld 6: Een koppelvlak aanmaken Objecten gebruikt van het MORPHXIT_DataDictionary project

Map, MyMap Een koppelvlak zal aangemaakt worden, waarbij gemeenschappelijke velden gekoppeld zullen worden voor de tabellen CustTable en MyTable. 1. Zoek de knoop Maps op, en selecteer via de rechtermuisknop New Map. Hernoem

het koppelvlak “MyMap” via het eigenschappenblad. 2. Sleep de uitgebreide datatypes AccountNum, CustName, CustGroupId en

CustCurrencyCode naar de Fields knoop van MyMap.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

93

3. Bewaar MyMap, klik met de rechtermuistoets op MyMap en selecteer Restore. 4. Ga naar de knoop Mappings, open het rechtermuisknopmenu en selecteer New

Mapping. Open het eigenschappenblad van deze nieuwe koppellijst en selecteer de tabel CustTable. Een lijn voor ieder veld in MyMap werd nu toegevoegd aan de nieuwe koppellijst. Gebruik het eigenschappenblad voor ieder koppelveld om het gerelateerd veld te specifiëren in de eigenschap MapFieldTo. De gerelateerde velden van CustTable zijn accountNum, name, currency en custGroup.

5. Herhaal stap 4 door het toevoegen van een koppelvlak voor de tabel MyTable. 6. Ga naar het knooppunt Methods end maak de volgende methode aan: void listRecords(MyMap _myMap) { ; while select _myMap { info(strFmt("%1, %2", _myMap.accountNum, _myMap.custName)); } } 7. Bewaar het koppelvlak. In het voorbeeld werd het koppelvlak hersteld nadat de velden werden toegevoegd. Telkens er een wijziging gebeurt aan de velden in een koppelvlak, dien je het koppelvlak te herstellen, anders zullen de wijzigingen niet getoond worden in de koppellijsten vooraleer de client herstart is. Een methode werd toegevoegd aan het koppelvlak waarmee we de koppelingen kunnen testen. Dit is een simpele methode die een waarde afdrukt van de records in het koppelvlak, en dus van de gekoppelde tabel. Maak een nieuwe job aan met de volgende inhoud: static void DataDic_TestMyMap(Args _args) { MyMap myMap; CustTable custTable; MyTable myTable; ; myMap.listRecords(myTable); } Merk op dat een koppelvlak gedeclareerd wordt net als een tabel. In feite toont enkel de naam dat het hier om een koppelvlak gaat. Het is aangeraden om het koppelvlak te voorzien met het voorvoegsel *Map waardoor de code gemakkelijker leesbaar wordt. De methode van het koppelvlak wordt opgeroepen met MyTable als parameter. Dit resulteert in een lijst van alle records in MyTable die afgedrukt worden in de Infolog. Een koppelvlak bevat geen records en kan beschouwd worden als een gespecialiseerde gemeenschappelijke tabel. Het koppelvlak kan geïnitialiseerd worden met eender welke tabel, gedeclareerd in het koppelvlak.

MORPHX IT Data Dictionary

94

© 2007 Steen Andreasen

De methoden van een koppelvlak zijn gelijkaardig aan die van een tabel. Je kunt velden initialiseren, gewijzigde velden controleren en valideren alvorens deze te bewaren of te verwijderen. Het oproepen van een standaard methode zoals insert() op een koppelvlak zal de betreffende insert() van de gekoppelde tabel uitvoeren. Je kunt dit gebruiken om je eigen methoden te koppelen. Zorg er echter wel voor dat de naam van de methode gelijk is aan deze voor alle gekoppelde tabellen. Een voorbeeld hiervan vind je in de koppelvlakmethode CustVendTrans.existInvoice(). Deze methode roept de bijhorende existInvoice() methode op voor de gekoppelde tabel, waardoor het mogelijk wordt om verschillende validaties te hebben voor verschillende tabellen in het koppelvlak.

3.3 Views In een relationele databank is data onderverdeeld in een aantal tabellen om overtollige data te vermijden en voor betere performantie. Dit is goed voor het beheren van je data, maar data ophalen voor rapporten wordt al complexer, aangezien je meestal data zal ophalen vanuit meerdere tabellen. Om extractie van data eenvoudiger te maken, kunnen views gebruikt worden. Een view is het resultaat van een inner join van twee of meerdere tabellen. Zoals de naam het aangeeft is een “view” enkel een zicht op gegevens en dus dienen views enkel om gegevens te lezen, al ondersteunen ze ook aggregatiefuncties op de opgehaalde data, waarmee je bijvoorbeeld de som, het gemiddelde van getallen kan berekenen. Je kunt views gebruiken als een alternatief voor gegevensbronnen in een rapport. Hoedanook blijft het aanleveren van data voor OLAP kubussen het hoofddoel van views. Views worden gesynchroniseerd met de databank. Dit maakt views nuttig indien je data dient te lezen van een Axaptatabel vanuit externe pakketten aangezien je data onmiddelijk kunt ophalen van de databank in de plaats van via de COM interface. OLAP kubussen in Axapta worden gebruikt in Microsoft SQL Server Analysis Services. Een beschrijving omtrent het gebruik van OLAP is niet besproken in dit boek. Je kunt meer informatie omtrent OLAP in Axapta terugvinden door de handleidingen uit het standaard pakket op te vragen. Voorbeeld 7: Creatie van een view Objecten gebruikt van het MORPHXIT_DataDictionary project

View, MyView In dit voorbeeld zal een view aangemaakt worden waarbij klantentransacties worden gesommeerd en afgedrukt. 1. Ga naar de knoop Views, selecteer via het rechtermuisknopmenu New View.

Hernoem de view "Myview" via het eigenschappenblad. 2. Zoek de knoop Metadata/Data Sources op, kies via de rechtermuisknop voor New

Data Source. Ga naar het eigenschappenblad van de nieuwe gegevensbron en kies er de tabel CustTable via de eigenschap Table.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

95

3. Vouw de knoop van de gegevensbron CustTable open en ga naar de knoop CustTable/Data Sources. Voeg een extra gegevensbron toe via de rechtermuisknop. Selecteer de tabel CustTrans als de tabel voor de gegevensbron.

4. Open het eigenschappenblad van CustTrans en geef de eigenschap Relations de

waarde Yes. Ontvouw de knoop Relations voor de CustTrans gegevensbron en controleer of er een relatie werd toegevoegd.

5. Selecteer de knoop Metadata, klik met de rechtermuisknop en kies Open New

Window. Dit zal een nieuw venster openen met de vertakking van metadata van MyView. Ga verder in het nieuw venster naar CustTable/Fields. Selecteer de velden AccountNum, Name en CustGroup en versleep de geselecteerde velden naar het andere venster tot aan de knoop Fields op het eerste niveau van de view.

6. Herhaal stap 5 door het veld AmountMST te slepen van CustTrans/Fields. Open het

eigenschappenblad voor het nieuwe veld van de view en geef de eigenschap Aggregation de waarde Sum.

7. Bewaar de nieuwe view. Bij het aanmaken van een view om data van een tabel op te halen, zal de view gedefinieerd worden op dezelfde manier als een standaard Axapta query. De gebruikte velden moeten geselecteerd worden van de gekozen tabellen. In dit voorbeeld werd één enkel veld van de klanttransacties gebruikt voor een aggregatiefunctie. Bij het gebruik van aggregatiefuncties, zullen de records uit de desbetreffende tabel gegroepeerd opgehaald worden. Het voorbeeld toont hoe klantentransacties gegroepeerd en gesommeerd werden per klant. Indien het veld TransDate van CustTrans werd toegevoegd zonder enige aggregatie, dan zou het veld AmountMST berekend en gegroepeerd worden per klant en transactiedatum. Bemerk dat geaggregeerde veldnamen in een view automatisch worden voorzien van een voorzetsel met de aggregatiefunctienaam. Je kunt het resultaat van MyView zien door de tabelbladeraar te openen. Alle klanten met transacties worden opgelijst en de balans is de sommatie van het veld AmountMST van CustTrans. Views kunnen gebruikt worden als om het even welke tabel, met een aantal beperkingen. Je kan views niet gebruiken in relaties, verwijderacties en tabelverzamelingen. Je kunt een view wel gebruiken als een alternatief voor een tabel in X++ of in een gegevensbron. Bij het aanmaken van een rapport kun je een view gebruiken zonder dat je moet weten hoe je de berekeningen moet uitvoeren voor je rapport. Dit kan echter wel een aantal beperkingen inhouden voor de eindgebruiker omdat die bij uitvoeringstijd zelf instellingen kan bepalen voor berekeningen.

MORPHX IT Data Dictionary

96

© 2007 Steen Andreasen

3.4 Uitgebreide Datatypes Uitgebreide datatypes zijn een centraal deel van MorphX. Of je nu velden toevoegt aan een tabel, of variabelen declareert in X++, je zou altijd gebruik moeten maken van uitgebreide datatypes in plaats van basistypes. De hoofdreden hiervan is dat je wijzigingen gemakkelijker te onderhouden zijn en je kan verzekeren dat een veld op dezelfde manier gepresenteerd wordt, waar het ook gebruikt wordt in de applicatie. Een uitgebreid datatype stamt af van een basistype of een ander uitgebreid datatype. Je kunt hierin zoveel niveaus hebben als je wil. Het verschil tussen een basistype en een uitgebreid datatype is dat een uitgebreid datatype een eigenschappenblad heeft waar informatie zoals labels, lengte en linker-of rechteralignering genoteerd zijn. Relaties kunnen ook worden toegevoegd aan een uitgebreid datatype. Alvorens je een nieuw uitgebreid datatype aanmaakt, moet je controleren of een reeds bestaand datatype voldoet. Indien je een nieuwe tabel aanmaakt en binnen die tabel item ids wil opzoeken van de tabel InventTable, dien je het uitgebreide datatype ItemId te gebruiken. Door het gebruiken van ItemId dien je geen labels of een relatie naar InventTable op te geven, aangezien deze al werd opgegeven in het uitgebreide datatype.

Figuur 15: Opzoeking van uitgebreid datatype in Application Hierarchy Tree Het is niet altijd even makkelijk om te beslissen welk uitgebreid datatype je zal gebruiken, vooral wanneer een uitgebreid datatype meerdere malen werd uitgebreid. De Application Hierarchy Tree kan gebruikt worden om een overzicht te krijgen. Je kunt de Application Hierarchy Tree oproepen vanaf het Add-Ins menu door te klikken met de rechtermuisknop op een uitgebreid datatype. Enkel het basistype en de uitgebreide niveaus voor het geselecteerde uitgebreide datatype zullen getoond worden. Sommige uitgebreide datatypes stammen af van uitgebreide systeemdatatypes. Uitgebreide systeemdatatypes staan in de AOT onder System Documentation/Types. Regionale instellingen zoals bedrag en types voor systeemvelden zijn allemaal aangemaakt als systeemvelden. Voorbeeld 8: Creatie van een uitgebreid datatype Objecten gebruikt van het MORPHXIT_DataDictionary project

Extended Data Type, DataDic_AltCustAccount In het volgende voorbeeld maken we een uitgebreid datatype aan dat afstamt van een ander uitgebreid datatype. Het nieuwe uitgebreid datatype zal de vorige aangemaakte tabelrelatie van CustTable overbodig maken.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

97

1. Ga naar de knoop Extended Datatypes, klik met de rechtermuisknop en maak

een nieuwe string. Geef het nieuwe uitgebreid datatype de naam "DataDic_AltCustAccount" door gebruik van het eigenschappenblad.

2. Geef het nieuwe uitgebreide datatype als label "Alt. customer" en als helptekst

"Identification for alternative customer account.". 3. Maak van DataDic_AltCustAccount een uitbreiding van AccountNum via de

eigenschap Extends. 4. Vouw de knooppunten van DataDic_AltCustAccount open en ga naar het

knooppunt Relations. Voeg een nieuwe relatie toe via de rechtermuisknop, kies New en selecteer Normal. Gebruik het eigenschappenblad om MyTable te selecteren en duid het veld accountNum als de relatie aan.

5. Bewaar het uitgebreid datatype en wacht tot de databank gesynchroniseerd is.

6. Ga naar de tabel CustTable en zoek het veld altCustAccountNum op. Open het

eigenschappenblad en wijzig de eigenschap ExtendedDataType naar DataDict_AltCustAccount.

7. Zoek de relaties in CustTable op en ga naar de relatie MyTable. Druk op de

delete knop om de relatie met MyTable te verwijderen. 8. Bewaar CustTable. 9. Wijzig het uitgebreid datatype van het veld accountNum in MyTable zoals reeds

uitvoerd in stap 6 en bewaar MyTable. Je hebt nu een nieuw uitgebreid datatype voor het alternatieve klantnummer aangemaakt. De verwijderde tabelrelatie in CustTable was overbodig, omdat het net aangemaakte uitgebreide datatype de relatie zal onderhouden. Het is flexibeler om de relaties op het niveau van het uitgebreide datatype te leggen aangezien je niet elke tabel moet wijzigen waar het uitgebreide datatype gebruikt wordt. Het uitgebreide datatype werd ook gewijzigd voor het veld accountNum in MyTable. Er zal geen opzoekknop worden toegevoegd aan accountNum omdat de gerelateerde tabel dezelfde is. Dit noemt men ook een zelf-relatie. Label en helptekst werden bepaald voor het nieuwe uitgebreide datatype. Indien er geen labels werden opgegeven voor het uitgebreide datatype, zouden de labels van AccountNum gebruikt worden. Indien je een uitgebreid datatype gevonden hebt dat aan je eisen voldoet, maar een ander label of helptekst is vereist, zou je een nieuw uitgebreid datatype moeten aanmaken. Gebruik geen bestaand uitgebreid datatype om het label van dit uitgebreid datatype te overschrijven door een label of helptekst op te geven bij de veldeigenschappen. Als je in X++ verwijst naar een veld, wordt het uitgebreid datatype gebruikt en is er geen informatie beschikbaar omtrent het veldlabel. Dit zal verder besproken worden wanneer display- en editmethodes besproken worden in het hoofdstuk Formulieren.

MORPHX IT Data Dictionary

98

© 2007 Steen Andreasen

Opmerking : De volledige databank zal gesynchroniseerd worden bij het bewaren van een uitgebreid datatype dat afstamt van een ander uitgebreid datatype. Als je veel uitgebreide datatypes aanmaakt, druk dan op ctrl+break om de synchronisatie te stoppen en synchroniseer bij het bewaren van het laatste uitgebreid datatype. Zowel uitgebreide datatypes als basisenumeraties kunnen gebruikt worden als een uitbreiding voor een veld. Waarom zou je dan een uitgebreid datatype aanmaken voor een basisenumeratie? Enkel uitgebreide datatypes kunnen gebruikt worden als je een veld wil toevoegen aan een dialoogvenster. Indien een veld in een dialoogvenster van het type enum is zoals NoYes, dan moet je het uitgebreide datatype NoYesId gebruiken dat op zijn beurt van de basisenumeratie NoYes afstamt. Een dialoogvenster wordt aangemaakt door middel van klassen en is beschreven in het hoofdstuk Klassen. Uitgebreid datatype-array Het definiëren van een uitgebreid datatype als een array is één van de krachtigste mogelijkheden van uitgebreide datatypes. Elk arrayelement van een uitgebreid datatype zal aangemaakt worden als een databankveld. Zowel in de AOT als in X++ zal een veld dat gebaseerd is op een uitgebreid datatype-array, er uitzien en gebruikt worden als elk ander veld.

Figuur 16: Het uitgebreid datatype Dimension De knoop Array Elements wordt gebruikt wanneer een uitgebreid datatype gedefinieerd wordt als een array. Het eerste element van de array is het element dat aangemaakt wordt bij een standaard uitgebreid datatype. De volgende arrayelementen worden aangemaakt onder de knoop Array Elements. Enkel label en helptekst worden gespecifieerd voor deze arrayelementen. Alle andere eigenschappen worden overgeërfd van het eerste element van de array. Net zoals bij het eerste element, kunnen aparte relaties worden gedefinieerd voor elk arrayelement. Het meest gebruikte uitgebreide datatype dat beroep doet op deze mogelijkheid, is Dimension, dat overal in de applicatie wordt gebruikt om data te groeperen. Dimension bestaat uit 3 arrayelementen, die elk hun eigen relatie hebben. Indien je een andere dimensie nodig hebt voor je applicatie, moet je enkel een arrayelement toevoegen aan het uitgebreid datatype Dimension. Alle objecten, zoals tabellen, formulieren en rapporten zullen de nieuwe dimensie tonen, zonder dat je één enkele lijn code moet schrijven.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

99

static void DataDic_EDTArray(Args _args) { CustTable custTable; ; select firstonly custTable; info(strfmt("%1, %2, %3", custTable.dimension[1], custTable.dimension[2], custTable.dimension[3])); } Je kan de arrayelementen van een uitgebreid datatype gebruiken zoals een variabele. In bovenstaand voorbeeld worden 3 dimensies van CustTable afgedrukt voor het eerste record. Bij het opzoeken van de velden van CustTable, zal het veld Dimension als array opgehaald worden. Je moet zelf opgeven welk arrayelement moet afgedrukt worden. Wanneer je direct naar arrayelementen verwijst, zoals in dit voorbeeld, zal een nieuw toegevoegd element niet worden afgedrukt. Een betere oplossing is het overlopen van alle arrayelementen, in plaats van de code aan te passen als je meer dan de eerste 3 arrayelementen wil afdrukken. Bekijken we een gegevensbron in een query, dan zien we net het tegenovergestelde. Een gegevensbron zal een veld tonen voor elk arrayelement. Als je Dimension gebruikt in formulieren of rapporten, wordt Dimension altijd toegevoegd aan een veldgroep, waardoor de dimensieveldgroep automatisch een nieuw arrayelement zal herkennen.

3.5 Basisenumeraties Om data op te delen in een categorie, heb je twee opties. Maak een gerelateerde tabel aan, zoals itemgroepen, die men gebruikt om artikelstukken te groeperen. Indien je daarentegen slechts een vast aantal categoriën nodig hebt of de eindgebruikers mogen zelf geen categorieën kunnen opgeven, dan kun je een basisenumeratie of base enum gebruiken. Item type, zoals gebruikt om het type van een artikelstuk te bepalen, is een goed voorbeeld. Een basisenumeratie kan maximaal 255 elementen bevatten. Enkele basisenumeraties hebben een hoop elementen, zoals de basisenumeratie LedgerTransTxt. Maar de meeste enumeraties hebben slechts een beperkt aantal elementen. De waarde van een basisenumeratie wordt bewaard in de databank als een gehele cijferwaarde (integer). Elementwaarden starten standaard vanaf nul en worden doorlopend genummerd. De eigenschap EnumValue toont het getal dat voor een element bewaard wordt in de databank. Bij het aanmaken van een nieuw element in een basisenumeratie van het standaard pakket, moet je een onderbreking maken in de nummering, door een hogere waarde op te geven in de eigenschap EnumValue. De basisenumeratie NumberseqModule werkt op deze manier. Een element dat toegevoegd is aan een basisenumeratie in de SYS-laag, zal aangemaakt worden in de huidige laag. Om te voorkomen dat jouw nummer gebruikt wordt bij een upgrade van de applicatie, dien je een aantal nummers over te slaan. In X++ worden steeds basisenumeratienamen gebruikt. Bij het gebruik van basisenumeraties in relaties, wordt daarentegen de gehele cijferwaarde gebruikt. Indien je de cijferwaarde van een element wijzigt gedurende een upgrade, dien je ook de

MORPHX IT Data Dictionary

100

© 2007 Steen Andreasen

relaties aan te passen die gebruik maken van de enumeratie. Het wijzigen van een nummer van een element zal de eigenschap UseEnumValues van de basisenumeratie op “Yes” instellen. Het wijzigen van deze eigenschap in “No” zal resulteren in een hernummering van alle elementen vanaf nul en doorlopend genummerd. static void DataDic_BaseEnum(Args _args) { InventTable inventTable; ; while select inventTable where inventTable.itemType == ItemType::BOM || inventTable.itemType == ItemType::Service { info(inventTable.itemId); } } Om te verwijzen naar een element van een basisenumeratie wordt de naam van de basisenumeratie gebruikt, gevolgd door een dubbele dubbelpunt. Door het ingeven van de tweede dubbelpunt, zal een lijst getoond worden met de beschikbare mogelijkheden. Hier worden alle stukken van het type BOM en Service opgehaald. Je kunt hier ook de nummers van de basisenumeraties gebruiken, maar het zou je code moeilijker leesbaar en onderhoudbaar maken. while select inventTable where inventTable.itemType >= ItemType::BOM In plaats van te controleren of het veld itemType gelijk is aan één van de opgegeven enumeratie-elementen, kan je met ‘groter dan’ of ‘gelijk aan’ het item type BOM ook hetzelfde resultaat bekomen. Dit is echter niet aangewezen, omdat het je code moeilijker te beheren maakt. Indien een nieuw element wordt aangemaakt voor de basisenumeratie ItemType, zou het nieuwe element ook opgehaald worden, wat mogelijk niet de bedoeling was. Opmerking : Het eerste element van een basisenumeratie heeft normaal de waarde nul – wat zal resulteren in “false” (0=onwaar; 1=waar) indien deze gebruikt wordt in een if-expressie. Dit is ook de reden waarom velden van het type enum niet verplicht zouden mogen zijn aangezien het eerste enumeratie-element als ongeldig zou worden beschouwd. Systeemenumeraties staan in de AOT onder System Documentation/Enums. Deze worden allemaal gebruikt voor selecties in eigenschappenbladen. Indien je eigenschappen wilt aanspreken via X++, moet je overwegen de systeemenumeraties te gebruiken. Een goed voorbeeld is de systeemenumeratie TableGroup; zij stelt de elementen van de tabeleigenschap TableGroup voor.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

101

3.6 Kenmerksleutels Voor versie 3.0 van Axapta werden kenmerksleutels gebruikt voor het instellen van de gebruikerstoegang, het bepalen van de tabellen die gesynchroniseerd moeten worden met de databank en het afhandelen van de licenties. Dit verliep niet zonder problemen bij de configuratie van Axapta en vroeg heel wat tijd en moeite. Vanaf versie 3.0 wordt de beveiliging afgehandeld door configuratie- en beveiligingsleutels en de licenties worden beheerd met licentiesleutels. Kenmerksleutels kan je niet gebruiken in versie 3.0. De reden dat we toch nog kenmerksleutels in versie 3.0 hebben is achterwaartse compatibiliteit. Als je een upgrade doet van versie 2.5 zal je de kenmerksleutels wel zien waardoor je manueel de kenmerksleutels kan vervangen door configuratie- en beveiligingsleutels.

3.7 Licentiecodes Als je Axapta aankoopt, moet je beslissen over systeeminstellingen zoals het aantal gebruikers, het aantal servers, toegang tot MorphX en X++. Je beslist ook welke applicatiemodules je gaat gebruiken. Voor iedere systeeminstelling en module zal je een licentiecode ontvangen. Alle licentiecodes worden gecompileerd in een codebrief. De codebrief is in feite zowel een document als een bestand dat in Axapta geïmporteerd kan worden. De licentiecodes vind je terug in het document en in gecompileerde vorm in het codebriefbestand. Deze licentiecodes worden gebruikt om na te gaan tot welk deel van Axapta je toegang krijgt. Enkel modules met een geldige licentiecode zullen beschikbaar zijn in het hoofdmenu. Als je een object zonder geldige licentie in de AOT wil oproepen, krijg je een foutmelding. Partners hebben doorgaans een codebrief met licentiecodes voor alle onderdelen van Axapta. Een klant kan een deel van de onderdelen kopen die horen bij zijn bedrijfsprocessen. Als je wijzigingen programmeert voor een klant, doe je dit waarschijnlijk met andere instellingen. Dit kan fouten veroorzaken omdat je wijzigingen wel zullen werken in je omgeving waar je beschikt over alle modules. Voor je je wijzigingen doorvoert in een productiesysteem, moet je de wijzigingen nog eens testen met dezelfde instellingen als de productieapplicatie. Je kan je eigen licentiecodes aanmaken en deze licentiecodes aan je wijzigingen koppelen. Maar om je eigen licentiecodes te gebruiken, moet je wel eerst contact opnemen met Microsoft omdat zij voor jou licentiecodes moeten genereren. Het aanmaken van nieuwe licentiecodes wordt gedaan door bedrijven die modules creëren voor de GLS-laag of door partners die hun eigen modules willen verkopen. Configuratiesleutels hebben een eigenschap LicenseCode, die gebruikt wordt om een licentiecode te koppelen aan je wijzigingen. Je hebt geen licentiecodes nodig voor gebruikerstoelatingen. Dit doe je met beveiligingsleutels. Licentiecodes worden enkel gebruikt als je een licentiecode wil zoals de meeste softwarepakketten hebben.

MORPHX IT Data Dictionary

102

© 2007 Steen Andreasen

3.8 Configuratiesleutels Je hebt twee niveau’s van beveiliging in Axapta. Configuratiesleutels zijn het hoogste niveau en beveiligingsleutels zijn het tweede niveau. Als een configuratiesleutel is gedesactiveerd, zullen de gerelateerde objecten niet verschijnen in menu’s, formulieren of rapporten en niemand zal toegang hebben tot deze gerelateerde objecten. Configuratiesleutels zijn gedefinieerd in een boomhiërarchie waar de hoogste configuratiesleutel gerelateerd is aan de licentiecode. Het formulier SysConfiguration toont de hiërarchie van de configuratiesleutels. Enkel configuratiesleutels waarvan de overeenkomstige licentiecode is ingetikt, kunnen geactiveerd worden en de configuratiesleutel op het hoogste niveau kan enkel gedesactiveerd worden door de licentiecode te verwijderen. Als je alle licentiecodes hebt ingetikt, betekent dit daarom niet dat alle configuratiesleutels zijn geactiveerd. Sommige configuratiesleutels zijn standaard gedesactiveerd. Dit geldt voor geavanceerde en landeigen kenmerken. Als je de instellingen van configuratiesleutels wijzigt, moet de databank gesynchronizeerd worden. Configuratiesleutels bepalen of een tabel al dan niet met de databank moet gesynchronizeerd worden. Je moet uiterst voorzichtig zijn bij het wijzigen van configuratiesleutels omdat gegevens uit de tabellen verwijderd worden als de configuratiesleutels gedesactiveerd worden. Je moet enkel een handvol configuratiesleutels aanmaken voor iedere module. Zo heb je een hoofdconfiguratiesleutel voor een module en een subconfiguratiesleutel voor iedere submodule. Doorgaans wordt er een configuratiesleutel gekoppeld aan iedere tabel, koppelvlak, view en menuoptie voor de wijzigingen. Dit zorgt ervoor dat een object waarvan de configuratiesleutel is gedesactiveerd, niet zichtbaar is in het menu. Bovendien wordt er een foutmelding gegenereerd indien een gebruiker het object in de AOT wil activeren omdat er geen toegang is tot de databank voor het betreffende object. De meeste objecten in de AOT hebben een eigenschap voor het definiëren van een configuratiesleutel. Je hebt misschien al gezien dat sommige uitgebreide datatypes en basisenums in het standaard pakket een overeenkomstige configuratiesleutel hebben. Het plaatsen van configuratiesleutels op alle niveau’s zou zeer moeilijk beheerbaar zijn. Maar het is zinnig om configuratiesleutels te plaatsen op sommige uitgebreide datatypes en basisenums. In de tabel SalesTable is er een veld ProjId. Het uitgebreid datatype voor ProjId heeft een configuratiesleutel. Als je een geldige licentie hebt voor de modules Account Receivable en Project, kan je een projectidentificatienummer intikken voor een verkooporder. Als je enkel een licentie hebt voor Account Receivable, zal je deze mogelijkheid niet hebben omdat het veld ProjId niet zichtbaar is. Het plaatsen van een configuratiesleutel op een element van een basisenum wordt soms gebruikt in het standaard pakket. Kijk even naar de basisenum FormTextType. Ieder van de elementen toegevoegd aan deze basisenum in de GLS laag heeft een configuratiesleutel. Als je niet beschikt over de gerelateerde modules, heeft het geen zin om de betreffende basisenumelementen te selecteren. Je kan dit ook voor je eigen wijzigingen gebruiken. Als je wil voorkomen dat de applicatiegebruiker een bepaald element van een basisenum selecteert, kan je je eigen configuratiesleutel gebruiken en dit aan het basisenumelement koppelen.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

103

static void DataDic_ConfigurationKey(Args _args) { SalesTable salesTable; ProjTable projTable; ; select firstonly salesTable; info(salesTable.salesId); info(salesTable.custAccount); if (isConfigurationKeyEnabled(configurationkeynum(ProjBasic))) { info(ProjTable::find(salesTable.projId).name); } } In X++ kan je controleren of een configuratiesleutel is geactiveerd. De globale methode isConfigurationKeyEnabled() voert de validatie van configuratiesleutels uit. Het heeft immers geen zin om code uit te voeren voor een onderdeel dat toch is gedesactiveerd. In het voorbeeld hierboven, wordt de projectnaam van het bijbehorende verkooporder enkel afgebeeld als de hoogste configuratiesleutel voor de projectmodule actief is. Als deze controle niet was voorzien, zou een blanco waarde zijn afgebeeld. Op zich niet zo erg, maar wat als deze code ook toeliet om een record te wijzigen of te verwijderen ? Zonder deze controle zou mogelijk het verkeerde record zijn aangepast. Opmerking : Verscheidene tabellen en velden hebben een naam voorafgegaan door DEL_. Het gaat hier om objecten die niet meer gebruikt worden en deze objecten blijven tot aan de uitgave van de volgende versie. De tabellen en velden worden hernoemd alsof ze zijn verwijderd. Al deze objecten hebben de configuratiesleutel SysDeletedObjects30. Als je een upgrade hebt uitgevoerd, kan je deze configuratiesleutel desactiveren.

3.9 Beveiligingsleutels Waar de configuratiesleutels de toegang instellen voor alle gebruikers, beheren de beveiligingsleutels de toegang voor een groep gebruikers of voor één enkele gebruiker. Normaal gebruik je de beveiligingsleutels voor een groep gebruikers omdat het veel te complex wordt als je de instellingen per gebruiker wil beheren. Net zoals de configuratiesleutels zijn de beveiligingsleutels hiërarchisch aan elkaar gekoppeld. Het standaard pakket gebruikt 9 beveiligingsleutels voor iedere module. Op het hoofdniveau is één beveiligingsleutel gerelateerd aan een configuratiesleutel, een andere beveiligingsleutel is gekoppeld aan tabellen en de laatste 7 beveiligingsleutels worden gebruikt voor het groeperen van de moduleobjecten zoals je ze ziet in het hoofdmenu. Je kan de beveiliginghiërarchie zien in het formulier SysUserGroupSecurity. Configuratiesleutels kunnen gerelateerd worden aan eender welke beveiligingsleutel. Maar het is aangewezen dat je enkel een configuratiesleutel definieert voor de beveiligingsleutels op het hoogste niveau. Als je een configuratiesleutel desactiveert, is de beveiligingsleutel niet meer beschikbaar en het desactiveren van een beveiligingsleutel op hoog niveau desactiveert op zijn beurt de beveiligingsleutels op een lager niveau.

MORPHX IT Data Dictionary

104

© 2007 Steen Andreasen

Beveiligingsleutels voeg je toe aan alle tabellen, koppelvlakken, views en menuopties. Als een van deze objecten geen beveiligingsleutel heeft, dan kan je de toegangsrechten voor dit object niet instellen en dus is dit object toegankelijk voor alle gebruikers. Configuratiesleutels kan je weglaten als je wijzigingen niet gebruikt worden in een gedistribueerde module; immers, wijzigingen gecreëerd voor één bepaalde installatie ga je natuurlijk niet desactiveren. Voor het opzetten van de gebruikersbeperkingen ga je niet alleen beveiligingsleutels activeren of desactiveren. Je kan daarenboven een toegangsniveau definiëren. De keuzes van toegangsniveau zijn no access, view, edit, add or delete (geen toegang, raadplegen, wijzigen, toevoegen of verwijderen). Deze toegangsniveau’s worden ingesteld op de data dictionary en de menuopties. Op tabellen, koppelvlakken en views gebruik je de eigenschap MaxAccessMode om het toegangsniveau in te stellen. Tabellen hebben doorgaans de eigenschap MaxAccessMode ingesteld op Delete, waardoor de gebruikers volledige toegang tot de tabel krijgen. Als een tabel wordt gebruikt voor transacties, dan moet je de eigenschap MaxAccessMode instellen op “View” omdat transacties niet mogen gewijzigd worden. Menuopties hebben een gelijkaardige eigenschap genaamd NeededAcccesLevel die wel het tegenovergestelde werkt als bij de tabellen : hier moet je het minimum vereiste toegangsniveau instellen voor de gebruiker de menuoptie kan gebruiken. De standaard waarde voor NeededAccessLevel is View. Je moet enkel het vereiste toegangsniveau wijzigen voor menuopties van het type “action”. Veel van deze actiemenuopties voeren operaties uit die niet mogen opgestart worden door de doorsnee applicatiegebruiker. En als je toch een menuoptie een hogere toegangsniveau wil geven, moet je er op zijn minst twee keer over nagedacht hebben. Het toevoegen van de beveiligingsleutels moet één van je laatste taken zijn bij het invoeren van wijzigingen. Je moet op zijn minst de beveiligingsleutels hebben toegevoegd voor je je laatste test uitvoert. Natuurlijk zal je niet alle combinaties van de beveiligingsleutels kunnen verifiëren. Maar je moet wel de beveiligingsleutels opzetten voor een applictiegebruiker zodat je een idee hebt van hoe de gebruiker je wijzigingen aan den lijve ondervindt.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

105

3.10 Tabelverzamelingen Als je het pakket gebruikt voor meer dan één bedrijf, is het nuttig om gegevens te delen van tabellen met algemene informatie. In het standaard pakket doe je dit bijvoorbeeld met tabellen die postcodes en landcodes bevatten. Als je de gegevens van een tabel tussen alle bedrijven wil delen, stel je de tabeleigenschap SaveDataPerCompany in op No. Dit zorgt ervoor dat de gegevens van alle bedrijven in één tabel terechtkomen en dat de gegevens ook toegankelijk zijn voor alle bedrijven. In de praktijk betekent dit dat de kernel het systeemveld dataAreaId van de tabel verwijdert. Tabelverzamelingen kunnen ook gebruikt worden om een tabel te delen tussen een aantal bedrijven. Het voordeel is dat je geen instellingen in de bestaande tabellen moet wijzigen. Een nieuwe tabelverzameling maak je door een nieuwe tabelverzamelingknoop te creëren en vervolgens de tabellen die je wil delen, te verslepen naar de nieuwe tabelverzameling. Een tabelverzameling is in feite enkel een sjabloon voor de tabellen die gemeenschappelijk zijn aan eender welk aantal bedrijven. In het hoofdmenu bepaal je dan welke bedrijven de tabellen van een tabelverzameling mogen delen. Het formulier SysDataAreaVirtual gebruik je om virtuele bedrijven op te zetten. Tabelverzamelingen worden gedeeld met behulp van een virtueel bedrijf. Je kan niet overstappen naar een virtueel bedrijf zoals naar een normaal bedrijf. Virtueel bedrijf is de term die gebruikt wordt voor het delen van een tabelverzameling tussen een aantal bedrijven. In het formulier kies je de bedrijven die een bepaalde tabelverzameling mogen delen. Voor je een virtueel bedrijf creëert, moet je eerst de gegevens exporteren van de tabellen die je gebruikt in de tabelverzameling. De reden hiervan is dat bestaande gegevens worden verwijderd uit de tabellen als je ze toewijst aan een virtueel bedrijf. Als je een tabelverzameling gebruikt voor definitietabellen zoals klantengroepen, is het opzetten van een virtueel bedrijf eenvoudig. Als je gegevens wil delen van hoofdtabellen zoals de inventory tabel, moet je eerst wat meer onderzoek doen omdat je niet alleen de tabel InventTable kan delen. Je moet alle tabellen delen die een relatie hebben met InventTable. Het delen van gegevens moet met de nodige omzichtigheid aangepakt worden. Voor je gegevens gaat delen, moet je dit eerst uitproberen in een testomgeving, en nakijken of je gaat werken met de eigenschap SaveDataPerCompany of met tabelverzamelingen. Je moet zeker zijn dat je wijzigingen geen fouten veroorzaken op objecten die de gemeenschappelijke tabellen gebruiken.

MORPHX IT Data Dictionary

106

© 2007 Steen Andreasen

3.11 Speciaal Tabelgebruik Het gebruik van de data dictionary is stap voor stap uitgelegd in dit hoofdstuk. Deze sectie toont voorbeelden over het gebruik van kenmerken van MorphX met betrekking tot tabellen die je in specifieke situaties nodig hebt. Gebruik van systeemklassen In de AOT onder System Documentation/Classes vind je een aantal klassen waarvan de naam begint met Dict*. Dit zijn klassen die gebruikt kunnen worden om te verwijzen naar eender welke knoop van de data dictionary of eender welk eigenschappenblad. Als je generische code schrijft waarbij je niet weet welke tabel je zal oproepen tot het programma effectief wordt uitgevoerd, kan je gebruik maken van deze systeemklassen. Zo kan je alle velden van een tabel doorlopen en de gebruiker een keuzescherm voorstellen waar hij een veld kan kiezen. Twee voorbeelden van het gebruik van systeemklassen voor de data dictionary vind je hieronder. Het eerste voorbeeld doorloopt alle velden van een tabel. In het tweede voorbeeld doorloop je alle methodes van een tabel. Elementen gebruikt van het MORPHXIT_DataDictionary project

Job, DataDic_SystemClassesFields Job, DataDic_SystemClassesMethods

static void DataDic_SystemClassesFields(Args _args) { SysDictTable dictTable; DictField dictField; Counter counter; ; dictTable = new SysDictTable(tableNum(MyTable)); for (counter=1; counter<=dictTable.fieldCnt(); counter++) { dictField = new DictField(dictTable.id(), dictTable.fieldCnt2Id(counter)); if (dictField.isSystem()) info(strfmt("System field: %1", dictField.label())); else info(strfmt("User field: %1", dictField.label())); } } Velden van de tabel MyTable worden afgedrukt. Een tekst geeft weer of het veld een normaal veld dan wel een systeemveld is. De klasse SysDictTable wordt gebruikt om de tabel te initializeren. Met deze klasse krijg je een hendel voor de tabeleigenschappen of de kindknopen. Let wel : SysDictTable is een applicatieklasse die erft van de systeemklasse DictTable. Verscheidene systeemklassen worden geërfd als applicatieklassen, en je moet aan deze klassen de voorkeur geven boven de basisklassen omdat de geërfde klassen over extra validaties beschikken.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

107

static void DataDic_SystemClassesMethods(Args _args) { SysDictTable dictTable; MethodInfo methodInfo; Counter counter; CustTable custTable; ; select firstonly custTable; dictTable = new SysDictTable(tableNum(CustTable)); for (counter=1; counter<=dictTable.objectMethodCnt(); counter++) { methodInfo = dictTable.objectMethodObject(counter); if (methodInfo.returnType() == Types::UserType) { info(strfmt("Method: %1, return value: %2", methodInfo.name(), dictTable.callObject(methodInfo.name(), custTable))); } } } Dit voorbeeld toont hoe je een hendel krijgt naar de methodes van een tabel en hoe je bepaalde methodes uitvoert. Dit zou je ook kunnen doen door direct de methodes van de CustTable variabele op te roepen, maar dat werkt niet als je de tabel pas kent op het moment van uitvoering en als je de systeemtabel Common als tabelvariabele gebruikt. De methodes van CustTable worden doorlopen. Het eerste record van CustTable wordt opgehaald om een cursor voor de opgeroepen methodes te hebben. Als een methode een uitgebreid datatype teruggeeft of een basisenumeratie, die de kernel beschouwt als gebruikerstypes, dan wordt de methode uitgevoerd en de teruggeefwaarde van de methode wordt afgedrukt samen met de methodenaam. Externe databanken Integratie met externe systemen waar je data naar wil overbrengen, is nooit een eenvoudige taak. Het probleem is dat wijzigingen voor het integreren van een extern systeem altijd op twee niveau’s moeten gebeuren. Om ervoor te zorgen dat alles goed werkt, moet je een gemeenschappelijk platform voor de integratie kiezen. Eén optie is het gebruik van de business connector, ook bekend als COM, omdat je de bedrijfslogica in Axapta kan oproepen. Beschouw de business connector als een Axapta client zonder gebruikersinterface. Dit vereist een connectie die altijd open is of op zijn minst altijd toegankelijk. Een andere optie is het gebruik van een externe databank die de gegevens voor uitwisseling beheert. Ieder systeem heeft dan toegang tot de gemeenschappelijke databank. In Axapta kan een batch job of stapeltaak geconfigureerd worden om op vaste tijdstippen de gegevens in de externe databank bij te werken. Op deze manier kan je twee systemen integreren zonder dat je over de juiste vaardigheden moet beschikken om direct te communiceren met het externe systeem.

MORPHX IT Data Dictionary

108

© 2007 Steen Andreasen

Elementen gebruikt van het MORPHXIT_DataDictionary project

Job, DataDic_ExternalDatabase static void DataDic_ExternalDatabase(Args _args) { LoginProperty loginProperty; ODBCConnection odbcConnection; Statement statement; ResultSet resultSet; ResultSetMetaData resultSetMetaData; Counter counter; ; loginProperty = new LoginProperty(); loginProperty.setDatabase("Northwind"); loginProperty.setDSN("AX30SP4"); // Datasource name for the Axapta database loginProperty.setUsername("sa"); // Database login name loginProperty.setPassword(""); // Database password odbcConnection = new ODBCConnection(loginProperty); statement = odbcConnection.createStatement(); resultSet = statement.executeQuery("select * from Employees"); resultSet.next(); resultSetMetaData = resultSet.getMetaData(); for (counter=1; counter <= resultSetMetaData.getColumnCount(); counter++) { switch (resultSetMetaData.getColumnType(counter)) { case 0,1 : info(resultSet.getString(counter)); break; case 3 : info(date2StrUsr(resultSet.getdate(counter))); break; } } } Dit voorbeeld werkt enkel met Microsoft SQL Server. We maken gebruik van de demo databank "Northwind" die bij de standaard installatie van Microsoft SQL Server hoort. Je moet de gegevensbron, loginnaam en wachtwoord voor je Microsoft SQL Server databank opgeven. Het eerste record van de tabel Employees van de Northwind databank wordt gelezen en ieder veld wordt afgedrukt in de InfoLog. Als je alle records van de tabel wil verwerken, kan je een WHILE lus toevoegen aan resultSet.next(). Eerst connecteren we met de externe databank en dan halen we de tabel op. Voor we een veld van de externe tabel afdrukken, kijken we eerst het type van dit veld na, omdat het type gekend moet zijn voor we de juiste methode kunnen kiezen om de veldwaarde om te zetten.

MORPHX IT Data Dictionary

© 2007 Steen Andreasen

109

3.12 Samenvatting In dit hoofdstuk hebben we de wijzigingen aan de data dictionary onder de loep genomen. Je moet nu weten hoe je gegevens van de tabellen definieert voor een optimale performantie en hoe je uitgebreide datatypes kan gebruiken om je wijzigingen eenvoudig onderhoudbaar te maken. We hebben gezien hoe je relaties en verwijderacties gebruikt om de samenhang van de gegevens te verzekeren en hoe je met behulp van de configuratie- en beveiligingsleutels de gebruikerstoelatingen kan regelen. We hebben de tabelbladeraar gebruikt om de gegevens in de databank na te kijken. Deze tool is normaal niet beschikbaar voor de applicatiegebruikers. In de volgende hoofdstukken zal je leren hoe je een gebruikersinterface kan maken voor de data dictionary.

MORPHX IT Macro’s

© 2007 Steen Andreasen

111

4 Macro’s In de voorganger van Axapta werden macro’s vaak gebruikt. De voorganger ondersteunde niet het gebruik van klassen, dus moest men wel aan de slag met macro’s. Dit is dan ook de reden dat macro’s nog steeds deel uitmaken van MorphX. In MorphX worden macro’s niet meer vaak gebruikt. Je vindt ze op enkele plaatsen zoals het bewaren van de lijst van velden bij het gebruik van dialoogschermen. Het wordt aanbevolen om macro’s alleen te gebruiken om constanten te definiëren. Macro’s worden niet aangeraden voor code, gezien hergebruik van code via macro’s niet zo flexibel is als via methodes. Macro’s kunnen aangemaakt worden onder de Macro knoop in de AOT, als een locale macro in een methode of als een lijn voor het definiëren van een constante. Het grote verschil tussen een macro en een methode is dat een macro geen variabelen declareert en dat de code in een macro niet wordt nagekeken op fouten vooraleer de code wordt opgeroepen door een methode. Dit is één van de hoofdredenen om geen code in de macro’s te plaatsen, omdat het de code moeilijker leesbaar maakt en meer aanleiding geeft tot fouten in de productieomgeving. Als je macro’s in je code gebruikt, moeten de macro’s gedeclareerd worden na de variabelendeclaratie. De beste plaats om de macrodefinitie te plaatsen is in de ClassDeclaration van een klasse, formulier of rapport. Dit maakt de macrodefinitie beschikbaar voor alle onderdelen van het object.

4.1 Macrocommando’s Om macro’s te schrijven beschik je over een set van eenvoudige macrocommando’s. Je hebt commando’s voor het bepalen van het begin en einde van een macro zoals in een methode. Een if-instructie kan gebruikt worden om het verloop van een macro te controleren. Macro if-instructies worden gebruikt om te valideren of een parameter is doorgegeven aan een macro of niet. Een lijst van macrocommando’s vind je terug in figuur 17: Macrocommando’s overzicht.

MORPHX IT Macro’s

112

© 2007 Steen Andreasen

Commando Omschrijving

#define

Gebruikt voor de definitie van een constante. Zie ook in macro HRMConstants. Voorbeeld #define.myConstant100('100')

#endif

Beëindigt een #if.empty of een #if.notempty instructie.

#endmacro

Beëindigt een #LOCALMACRO of een #GLOBALMACRO.

#globalmacro

Er is geen verschil of je het begin van een macro definieert met #localmacro of #globalmacro. #globalmacro wordt niet gebruikt in het standaard pakket, gebruik liever #localmacro.

#if.empty

Geeft de waarde true weer als de macro niet is opgeroepen met de parameter gevalideerd in de instructie. Voorbeeld #if.empty(%3) %3 = %2; #endif

#if.notempty

Geeft de waarde true weer als de macro is opgeroepen met de parameter gevalideerd in de instructie. Zie macro InventDimJoin. Voorbeeld #if.notempty(%3) print %3; #endif

#linenumber

Geeft de huidige lijnnummer van de macro weer. Kan gebruikt worden voor debugging.

#localmacro

Geeft de start aan van een locale macro. Zie in classDeclaration van de klasse SalesReport_Heading. Voorbeeld #localmacro.MyLocalMacro print %1; #endmacro

#macrolib

Gebruikt om een AOT macro in de code te laden. Zie klassemethode BOMHierarchy.searchDownBOM().

MORPHX IT Macro’s

© 2007 Steen Andreasen

113

Commando Omschrijving Voorbeeld #macrolib.MyMacro

#undef

Schrap de definitie van een constante die gedeclareerd is met #DEFINE. Een gedefinieerde constante kan niet gebruikt worden als #undef is opgeroepen met de naam van de betreffende constante. Voorbeeld #define.MyConstant(100) print #MyConstant; #undef.MyConstant print #MyConstant; // geeft foutmelding omdat #MyConstant niet is gedefinieerd.

Figuur 17: Macrocommando’s overzicht

4.2 Definitie van constanten Dit zijn de macro’s in hun eenvoudigste vorm. Als je tekst wil gebruiken in je code, wordt het sterk aangeraden om de tekst als constanten te definiëren. Je zal vaak een geheel getal of tekst nodig hebben om een waarde te bepalen. Als je de RGB kleur wil instellen, is het niet eenvoudig om het volgende te lezen : myStringColor(255, 255, 255) In plaats daarvan kan je een constante definiëren met een beschrijvende naam : myStringColor(#RBGColorWhite) Als je in de code een macro gebruikt als constante in plaats van de waarde, dan wordt je code eenvoudiger in onderhoud. Als je later de waarde van de tekst wil veranderen, volstaat het de constante te wijzigen. We raden aan om integer- en tekstwaardes in constanten te plaatsen voor je ze in de code gebruikt. Een praktische manier om de constanten te organiseren, is het maken van een macro in de AOT om al je constanten op één plaats te verzamelen. Neem even een kijkje in de macro’s in de AOT, en je zal zien dat sommige macro’s als prefix een modulenaam hebben gevolgd door het woord Constants. Als je een macro wil definiëren, gebruik je het macrocommando #define. De hierboven vermelde RGB waarde wordt als volgt gedefinieerd : #define.RBGColorWhite(255, 255, 255)

MORPHX IT Macro’s

114

© 2007 Steen Andreasen

4.3 Creatie van macro’s Als je door de klassen in de AOT bladert, zal je een macro zien met de naam CurrentList die vaak gebruikt wordt in de applicatie. Deze macro CurrentList wordt gebruikt voor de lijst van velden die bewaard worden in de dialoogschermen. #define.CurrentVersion(2) #localmacro.CurrentList FromDate, ToDate, Interest, CategoryA, CategoryB, CategoryC, Model #endmacro Dit is een stukje van de ClassDeclaration van de klasse InventReport_ABC. De constante CurrentVersion wordt gebruikt om het versienummer van de CurrentList macro bij te houden. Als de lijst van velden gewijzigd wordt in de macro CurrentList, wordt de constante CurrentVersion manueel opgehoogd. Dit is een deel van de interface waarmee je de waardes, opgegeven in dialoogschermen, kan bewaren. Meer uitleg hierover vind je in het hoofdstuk Klassen. static void Macros_LocalMacro(Args _args) { CustTable custTable; ; #localmacro.selectCustTable #ifnot.empty(%1) while select %1 #ifnot.empty(%3) order by %3 #endif { info(queryValue(%1.%2)); } #endif #if.empty(%1) info("No table specified."); #endif #endmacro #selectCustTable(CustTable, accountNum) #selectCustTable } Macro’s worden ofwel direct in de broncode geschreven, ofwel in een AOT macro geplaatst en deze AOT macro wordt dan in de code gedeclareerd. Het voorbeeld hierboven toont een #localmacro gecreëerd in de code. De macro selecteert records van een tabel en drukt een veld van de gekozen tabel af. De op te halen tabel en het af te drukken veld moeten opgegeven zijn in de parameters. Als er geen tabel is opgegeven op het moment dat de macro is opgeroepen, dan wordt een tekst afgedrukt in de Infolog. Omdat je geen variabelen kan declareren in een macro, gebruik je in de plaats daarvan integerwaardes voorafgegaan met een percentteken zoals %1. De integerwaardes

MORPHX IT Macro’s

© 2007 Steen Andreasen

115

worden oplopend genummerd en verwijzen naar de parameterpositie bij het oproepen van de macro. Hier zie je een macro met 3 parameters. De sorteervolgorde van de opgehaalde data is optioneel gezien de #ifnot.empty voorwaarde nakijkt of de derde parameter is gespecifieerd. Natuurlijk zouden er meer validaties moeten zijn, aangezien er meer kan misgaan, maar dit is een veelgebruikte handelwijze voor macro’s. De validaties plaats je voor de oproep van de macro. Merk op dat je een macro kan oproepen zonder aanhalingstekens na de macronaam. Opmerking: als je een AOT macro wijzigt, moet je alle AOT objecten hercompileren die de AOT macro gebruiken. Wijzigingen aan een AOT macro worden pas bij het compileren herkend door de objecten die de macro gebruiken. Als de macro gebruikt wordt op verschillende plaatsen, is het zinvol om de macro in de AOT te zetten omdat je dan de macro kan hergebruiken. Om een nieuwe macro in de AOT aan te maken, ontvouw je de Macro knoop, klik met de rechtermuisknop en kies New Macro. Een nieuwe lege macroknoop wordt dan gecreëerd. Je kan de nieuwe macro een naam geven door het eigenschappenblad te openen. Kopieer nu de code van de #localmacro in je nieuwe macro. static void Macros_MacroLib(Args _args) { CustTable custTable; ; #macrolib.Macros_MyMacro #selectCustTable(CustTable, accountNum) #selectCustTable } Een macro van de AOT gebruik je via het macrocommando #macrolib. In het voorbeeld is de naam van de AOT macro Macros_MyMacro. Merk op dat een AOT macro net zoveel #localmacro kan bevatten als nodig is. Je kan ook een definitie van een constante en een #localmacro aan dezelfde AOT macro toevoegen. Maar het is wel aangewezen om je constanten en de #localmacro in 2 macro’s op te splitsen omdat je code dan leesbaarder is. Opmerking : Als de naam van een AOT macro gelijk is aan de naam van een #localmacro in dezelfde AOT macro, dan kan de #localmacro aangesproken worden zonder de AOT macro te declareren met behulp van #macrolib. Dit werkt echter niet goed aangezien de parameters voor de #localmacro dan niet herkend worden.

4.4 Samenvatting In dit hoofdstuk is het gebruik van macro’s in MorphX uitgelegd. Je moet nu in staat zijn om constanten te definiëren met behulp van macrocommando’s en macro’s aan te maken hetzij rechtstreeks in je code hetzij in een AOT macro, dat we dan als macrobibliotheek gebruiken. Je moet nu ook weten waarvoor macro’s gebruikt kunnen worden en wanneer je ze gebruikt. Bovenal moet je weten waarom je code beter in methodes bewaart dan in macro’s.

MORPHX IT Klassen

© 2007 Steen Andreasen

117

5 Klassen X++ is een Object-Oriëntatie Programmeertaal, ook wel een OOP taal genoemd. Dit betekent dat code in objecten wordt ingekapseld. Een object kan met andere objecten communiceren door het gebruik van het parameterprofiel van deze objecten. Eén van de krachtige kenmerken van object-oriëntatie is het erven van code. Zo kan een subklasse code erven van de ouderklasse of superklasse. Dit maakt het eenvoudiger om wijzigingen te beheren omdat je bestaande functionaliteit kan uitbreiden door een subklasse voor je gewenste wijzigingen aan te maken. Als je meer wil lezen over Object-Oriëntatie Programmatie of OOP, kan je verschillende nuttige websites vinden op het internet. Basiskennis over OOP zal je helpen bij het ontwerpen van je klassen. Je hebt geen kennis over OOP nodig om dit hoofdstuk te lezen, maar het zou je wel helpen om bepaalde termen te begrijpen. Een klasse is een verzameling van methodes. In vergelijking met tabelmethodes is het herbruiken van code in een klasse veel eenvoudiger. De voornaamste reden hiervoor is dat klassen kunnen overerven en een klassemethode kan verwijzen naar andere klassemethodes. Hieruit moet je niet besluiten dat je code beter kan plaatsen in klassen, omdat tabelmethodes een specifiek doel hebben, net zoals klassemethodes. De standaard tabelmethodes kan je gebruiken om je eigen tabelmethodes uit te voeren. Als de code ook gebruikt kan worden voor andere objecten dan een tabel, kan je overwegen om de code in een klasse te plaatsen. Een andere optie is dat je een klasse declareert en de klassemethode oproept vanuit de standaard tabelmethode. Klassen behoren tot de complexe datatypes in MorphX. Een basisdatatype bewaart één enkele waarde, terwijl een klasseobject meerdere waardes kan bewaren. Net zoals basistypes gebruiken klassen variabelen om de waardes in op te slaan. Variabelen in een klasse kunnen enkel gebruikt worden door de methodes van de klasse. Deze klassemethodes zijn tevens de hendel waarmee je de klassevariabelen kan opvragen en wijzigen. Dus als je buiten deze klasse de klassevariabelen wil beheren, dan maak je gebruik van de klassemethodes. Dit is het principe van de inkapseling. Een klasse kan je beschouwen als de blauwdruk of gietvorm voor een reeks van objecten, waarbij de klassemethodes de handvatten zijn waarmee je de klasse kan behandelen. De klasse zelf bevat geen waardes omdat alleen een klasseobject variabelen kan bewaren. Zo kan een blauwdruk wel dienen om een reeks wagens van hetzelfde type te maken, maar enkel de wagen kan personen vervoeren. Als je een klasse declareert, wordt een object van een klasse gecreëerd. Dit is net zoals het declareren van een tabelvariabele gezien alleen een tabelvariabele een cursor kan bevatten.

5.1 Klassefundamenten Axapta heeft twee hoofdcategorieën van klassen, met name applicatieklassen en systeemklassen. Je gebruikt applicatieklassen voor het opbouwen van je applicatie. Enkel applicatieklassen kunnen gemaakt en gewijzigd worden. Systeemklassen worden voornamelijk gebruikt om tijdens de runtime wijzigingen aan de gebruikersinterface uit te voeren. Je vindt meer uitleg over systeemklassen verderop in dit hoofdstuk. Applicatieklassen vind je in de AOT onder het knooppunt Classes.

MORPHX IT Klassen

118

© 2007 Steen Andreasen

Methodes Een van de basisideeën in een OOP taal is het opsplitsen van code in kleine bouwstenen, waar iedere bouwsteen voorziet in het aanbieden van een bepaalde operatie. Een klasse is zo’n bouwsteen. Klassen worden onderverdeeld in methodes, waar iedere methode één enkele taak uitvoert. Om je code herbruikbaar te maken moet je dit in gedachte houden als je je methodes ontwerpt. Je kan het grootste gedeelte van de code in je klasse in één enkele methode schrijven, maar dit zou de klasse zinloos maken voor andere doeleinden. Sommigen hanteren als vuistregel dat het aantal lijnen van een methode niet een vooraf vastgelegd maximum mag overschrijden. Dit kan een goeie oplossing zijn, maar de beste stelregel is wel deze : je methodes moeten zo algemeen mogelijk zijn, zodat ze ook voor andere doeleinden gebruikt kunnen worden. Methodecomponenten De code in een methode bestaat uit 4 blokken : identificatie, variabelendeclaratie, codelijnen en een teruggeefwaarde of return value. Bovenaan de methode vind je de methode-identicifatie die de volgende syntax heeft :

< modificeerders> <teruggeeftype> <methodenaam>(parameterprofiel) Modificeerders zijn optionele sleutelwoorden die het gedrag van een methode bepalen. Een uitleg van deze sleutelwoorden vind je terug in de sectie Modificeerders. Het teruggeeftype moet aangegeven zijn. Dit kan eender welk basistype of complex type zijn. Als je niet wil dat een methode een waarde teruggeeft, moet je het sleutelwoord void als teruggeeftype opgeven. Void wordt ook gebruikt door sommige standaardmethodes zonder teruggeeftype, zoals je die bij tabellen en formulieren vindt. De naam van een methode start met een kleine letter. Gebruik een beschrijvende naam voor de methode die goed weergeeft wat de methode doet. Namen zoals calcInventQty() of isFormDatasource() geven beter weer wat deze methodes doen dan enkel maar calcQty() or formDatasource(). Na de methodenaam worden de parameters opgegeven. Parameters worden gedeclareerd zoals andere variabelen. Het wordt aangeraden om de parameternamen vooraf te laten gaan met een onderlijningsteken ("_") om zo het verschil te zien tussen parametervariabelen en variabelen die in de methode worden gebruikt. Vergeet niet dat je ook methodes zonder enige parameter kan schrijven. Verder kan je ook aangeven dat parameters optioneel zijn als de methode wordt opgeroepen. Dit doe je door de parametervariabele te initializeren. Merk op dat optionele parametervariabelen als laatste parameters moeten worden opgegeven in het parameterprofiel. Als je een methode overschrijft, zal de nieuwe versie van de methode hetzelfde parameterprofiel hebben als de originele methode. Het tweede blok is de variabelendeclaratie. Je kan meer informatie vinden over variabelendeclaraties in het hoofdstuk Inleiding tot X++.

MORPHX IT Klassen

© 2007 Steen Andreasen

119

Codelijnen worden toegevoegd na de variabelendeclaraties. Als de methode een teruggeeftype heeft, wordt het sleutelwoord return gebruikt om de waarde terug te geven. Het oproepen van return beëindigt de methode en geeft de waarde van het opgegeven type terug aan de oproepende methode. Je kan het sleutelwoord return een aantal keren gebruiken in de methode. Maar het wordt aangeraden om slechts één enkele return in de methode te gebruiken, omdat meerdere returns de code moeilijk leesbaar maken. Gebruik een variabele om de waarde die je wil teruggeven, in op te slaan en gebruik return op de laatste lijn van je methode. public AmountMst sumCustTrans(CustAccount _custAccount, TransDate _transDate = systemdateget()) { CustTrans custTrans; AmountMst sumAmountMst; ; if (!_custAccount) throw error("Customer account not specified."); sumAmountMst = (select sum(amountMst) from custTrans where custTrans.accountNum == _custAccount && custTrans.transDate >= _transDate).amountMst; return sumAmountMst; } Deze methode berekent de som van de klanttransacties en geeft de som terug via de variabele sumAmountMst. De methode heeft twee parameters waarvan één optioneel is. Als de eerste parameter customer account niet is opgegeven, wordt een foutmelding gegeven. Als de methode wordt opgeroepen met alleen maar de customer account, dan wordt de tweede parameter _transdate geïnitialiseerd met de huidige datum. Iconen Het icoon van een methode wordt bepaald door de modificeerders. Overschreven methodes hebben een icoon met een pijl , methodes gecreëerd in de huidige klasse hebben als icoon , beschermde methodes hebben een icoon met een sleutel , private methodes worden voorgesteld door een icoon met een grendelslot en statische methodes hebben een icoon met een kruis . De iconen maken het eenvoudig om een overzicht te krijgen van de methodes van een tabel of klasse. Kijk even naar de klasse CustBillExchangeClose. Je ziet snel het type van de methodes. Merk op dat overschreven methodes en instantiemethodes alfabetisch gesorteerd zijn en de statische methodes komen direct daarna. De reden voor een opsplitsing in deze sortering, is dat statische methodes op een andere manier gebruikt worden. Ieder icoon heeft een "verkeerslicht" aan de rechterkant van het icoon. De kleuren rood en geel geven aan of er foutmeldingen of waarschuwingen zijn gegeven over de methode. Een groen licht geeft aan dat de methode is gecompileerd zonder fouten.

MORPHX IT Klassen

120

© 2007 Steen Andreasen

Klassecomponenten Een applicatieklasse heeft 3 standaardknooppunten : ClassDeclaration, new en finalize. Zie ook in figuur 18: standaard applicatieklasseknooppunten . In ClassDeclaration worden globale variabelen voor de klasse gedefinieerd. Variabelen kunnen enkel gedeclareerd worden in ClassDeclaration, je kan de variabelen niet initialiseren. Alleen variabelen die je lokaal in methodes declareert, kan je tegelijk initialiseren. Macro’s die globaal voor de klasse dienen, worden ook gedeclareerd in ClassDeclaration.

Figuur 18: Standaard applicatieklasseknooppunten Als je een klasse declareert, dan declareer je tevens een klasseobject. Vooraleer je kan verwijzen naar het klasseobject, moet je dit klasseobject initiëren. De compiler geeft een foutmelding als je wil verwijzen naar een klasseobject dat niet geïnitieerd is. Het sleutelwoord new wordt gebruikt om de klasse te initiëren en dus een object te maken van de betreffende klasse. De syntax is new <Klassenaam>(). Het klasseobject en de klasse waarmee het klasseobject wordt geïnitieerd, moeten tot hetzelfde type behoren. Hierbij moeten we ook rekening houden met de klassehiërarchie. Anders gezegd, het klasseobject en de klasse gebruikt voor de initiatie, verwijzen naar dezelfde klasse of een ouderklasse van het klasseobject. MyClass myClassObject; ; myClassObject = new MyClass(); Het initiëren van een klasse gebeurt met de methode new(). Net zoals andere methodes, kan new() ook parameters hebben. Variabelen gedeclareerd in ClassDeclaration worden vaak geïnitialiseerd met de parameterwaardes van new(). Het oproepen van de methode new() roept de constructor van de klasse op. Een constructor dient om een object van de klasse aan te maken en te initialiseren. Verschillende programmeertalen hebben een methode, die dezelfde naam heeft als de betreffende klasse. Deze methode wordt de constructor genoemd. MorphX doet dit op een andere manier omdat we hier new() gebruiken als de constructor. Het is aangewezen in MorphX om een methode met de naam constructor() aan te maken als je je klasse wil initaliseren met behulp van verschillende subklassen. Normaal heeft de constructor() methode één enkele parameter die gebruikt wordt in constructor() om te bepalen welke subklasse er geïnitialiseerd moet worden via new(). De klasse NumberSeqReference die deel uitmaakt van het nummersequentiesysteem in Axapta, gebruikt zulke construct() methode. De methode finalize() wordt gebruikt om het klasseobject uit het geheugen te verwijderen. Nadat finalize() is opgeroepen, kan je niet meer verwijzen naar het betreffende klasseobject. Deze methode wordt niet automatisch opgeroepen aangezien de garbage collector automatisch objecten die niet meer gebruikt worden, uit het geheugen verwijdert. In de praktijk gebruikt men finalize() niet iedere keer als een

MORPHX IT Klassen

© 2007 Steen Andreasen

121

object niet meer gebruikt wordt. Zelfs indien je een lus hebt die een object aanmaakt bij iedere lusdoorgang, ga je finalize() niet oproepen vermits de garbage collector ingesteld is om het geheugen op te schonen als een bepaald aantal objecten niet meer in gebruik is. Het heeft zin om finalize() te gebruiken als je object niet meer nuttig is in de rest van de applicatie en als je wil voorkomen dat andere objecten dit klasseobject nog kunnen aanspreken. Voorbeeld 1: Creatie van een klasse Objecten gebruikt van het MORPHXIT_Classes project

Class, MyClass In dit voorbeeld wordt een klasse met één enkele methode aangemaakt. Dit eenvoudig voorbeeld toont hoe je een klasse moet aanmaken en gebruiken. 1. Ga naar het knooppunt Classes, klik met de rechtermuisknop en kies New Class.

Een nieuwe klasse met de naam "Class1" wordt aangemaakt. Open deze klasse door te dubbelklikken op de nieuwe klasse.

2. Kijk in het linkervenster om zeker te zijn dat ClassDeclaration is geselecteerd.

Hernoem de klasse door "Class1" te wijzigen in "MyClass". 3. In ClassDeclaration declareer je een variabele van het uitgebreide datatype ItemId.

ClassDeclaration ziet er nu als volgt uit : class MyClass { ItemId itemId; } 4. Voeg nu een nieuwe methode toe aan de klasse door ctrl+n in te drukken. De

nieuwe methode met naam "Method1" wordt automatisch geopend in de editor. Verander de naam van de methode in "parmItemId".

5. Voeg het uitgebreide datatype ItemId toe als parameter van de nieuwe methode

parmItemId(). De parameter moet geïnitialiseerd worden met de globale klassevariabele itemId. Stel daarom de parametervariabele _itemId gelijk aan de globale klassevariable itemId. De methode geeft itemId terug.

itemId parmItemId(ItemId _itemId = itemId) { ; itemId = _itemId; return itemId; } 6. Klik op het bewaaricoon in de editornutsbalk om alle wijzigingen van de klasse

te bewaren.

MORPHX IT Klassen

122

© 2007 Steen Andreasen

Aangezien klassevariabelen niet kunnen beheerd worden van buiten de klasse, moet je een methode creëren om de waarde van de klassevariabelen op te halen of in te stellen. De naam van deze methodes wordt vaak voorafgegaan met de prefix parm*. De klasse MyClass bevat één methode parmItemId() die de waarde van de globale klassevariabele ItemId teruggeeft indien de methode wordt opgeroepen zonder parameters. De optionele parameter in parmItemId() kan gebruikt worden om de waarde van ItemId in te stellen. Deze types van methodes worden vooral gebruikt in dialogen voor het overbrengen van de waarde van een dialoogveld naar een oproepend object. In andere talen, zoals Java en C#, maakt men wel het onderscheid tussen het ophalen of instellen van de waarde. De getter-methode haalt de waarde op en de setter-methode stel de waarde van een klassevariabele in. static void Classes_TestMyClass(Args _args) { MyClass myClass; ; myClass = new MyClass(); myClass.parmItemId("Item100"); info(myClass.parmItemId()); } De eenvoudigste manier om een klasse te testen is de creatie van een job. Maak een job aan zoals de job die hierboven wordt getoond. Deze job initieert MyClass en roept de methode parmItemId() op met een parameter. De methode parmItemId() wordt dan weer opgeroepen, maar ditmaal zonder parameter, waardoor je de waarde afdrukt in de Infolog, die je met de eerste parmItemId() hebt ingesteld. void methodWithFunction() { void methodWithFunction(CustAccount _custAccount) { ; info(_custAccount); } methodWithFunction("4000"); } In een methode kan je functies schrijven. Een functie kan enkel worden gebruikt door de methode en kan niet opgeroepen worden buiten de methode. De syntax van een functie in een methode is gelijkaardig aan die van een methode. Hierboven zie je een functie die gedeclareerd wordt met dezelfde naam als de methode. Dit is toegelaten zolang het parameterprofiel van de functie verschilt van het parameterprofiel van de methode. Een functie wordt opgeroepen zonder de objectnaam op te geven. Merk op dat er geen validatie is op het correct aantal parameters van de functie. Je moet het gebruik van functies in methodes beschouwen als een optie in X++. Dit wordt niet aangeraden omdat het de code niet leesbaarder maakt. In plaats daarvan kan je beter een andere methode creëren.

MORPHX IT Klassen

© 2007 Steen Andreasen

123

Modificeerders Je kan optioneel modificeerders voor een klasse of methode specifiëren. Modificeerders zijn nuttig als je beperkingen wil definiëren voor het gebruik van een methode of een volledige klasse en hoe een klasse kan geërfd worden. Toegangsmodificeerders Je kan een toegangsmodificeerder toevoegen aan een klasse of methode om het gebruik ervan te beperken. Zowel instantie- als statische methodes kunnen toegangsmodificeerders gebruiken en beperkingen kunnen gedefinieerd worden op verschillende niveau’s. Zo kan je een methode willen definiëren zodat ze enkel gebruikt kan worden in de klasse zelf of in de klassen en haar subklassen. Het standaardniveau is public dat volledige toegang geeft tot de klasse en alle methodes van de klasse. De toegangsmodificeerders worden getoond in figuur 19 : Overzicht van toegangsmodificeerders. MorphX kent toegangsmodificeerders voor methodes en klassen. Variabelen gedeclareerd in ClassDeclaration zullen altijd toegankelijk zijn voor subklassen. Sleutelwoord Beschrijving

Public

Standaardgedrag voor klassen en methodes.Een publieke klasse kan geërfd worden en klassemethodes kunnen overschreven worden in de subklassen en opgeroepen worden buiten de klasse.

Protected

Enkel methodes kunnen deze toegangsbeperking krijgen. Een beschermde methode kan gebruikt worden in de klasse en de subklassen. Ze kan ook overschreven worden in de subklassen.

Private

Zowel klassen als methodes kunnen de toegangsbeperking “private” krijgen. Dit heeft echter enkel invloed op de methodes. Een private methode kan enkel gebruikt worden binnen de klasse waarin ze is gedefinieerd. Overschrijven in de subklassen is niet mogelijk.

Figuur 19: Overzicht van toegangsmodificeerders Toegangsmodificeerders in MorphX worden vaak vergeten omdat de standaardtoegang public is. Dit houdt in dat ieder onderdeel van de klasse van eender waar opgeroepen kan worden. Geen beperking definiëren voor een klasse kan ook inhouden dat de klasse wordt gebruikt op een manier waar ze niet voor is bestemd. Hou er rekening mee dat toegangsmodificeerders er ook toe bijdragen dat het gebruik van je klasse eenvoudiger begrepen wordt. Een klasse heeft vaak verschillende methodes voor interne berekeningen in de klasse zelf. Zulke methodes zouden niet overschreven mogen worden door subklassen. Voorbeeld 2: Toegangsmodificeerders

MORPHX IT Klassen

124

© 2007 Steen Andreasen

Objecten gebruikt van MORPHXIT_Classes project

Class, MyClass_AccessModifiers Class, MyClass_AccessModierers_sub

We gaan een superklasse en subklasse aanmaken om het gebruik van de methodetoegangsmodificeerders aan te tonen. 1. Creëer een nieuwe klasse en noem deze klasse “MyClass_AccessModifiers”. 2. Voeg een methode met de naam publicMethod() toe aan de klasse. Stel de

toegangsmodificeerder in op public. Voeg een lijn toe om de toegangsmodificeerder af te drukken in de Infolog.

public void publicMethod() { ; info("public"); } 3. Herhaal stap 3 voor het toevoegen van gelijkaardige methodes voor de

toegangsmodificeerders protected en private. Denk eraan om de toegangs-modificeerder van iedere methode te definiëren.

4. Bewaar de klasse MyClass_AccessModifiers. 5. Creëer een subclasse met de naam MyClass_AccessModifiers_sub. De

ClassDeclaration van deze subklasse ziet er als volgt uit : class MyClass_AccessModifiers_sub extends MyClass_AccessModifiers { } 6. Selecteer het knooppunt MyClass_AccessModifiers_sub, klik met de

rechtermuisknop en kies Override Method. Selecteer de methode protectedMethod(). Hiermee maak je een nieuwe methode in de subklasse.

7. Bewaar de klasse MyClass_Modifiers_sub. De superklasse bevat 3 methodes, maar bij het overschrijven van methodes in de subklasse worden enkel de methodes met toegang “public” en “protected” getoond. MorphX kijkt de toegangsbeperkingen na en toont enkel die methodes die overschreven kunnen worden. Ook al is de standaardtoegang voor een methode public, toch heeft het zin om het sleutelwoord expliciet te schrijven. Methodes met een expliciete toegangsmodificeerder lopen niet het risico dat de toegangsmodificeerder worden gewijzigd in een subklasse. Merk op dat een overschreven methode enkel een oproep naar super() omvat. De oproep naar super() zal de code uitvoeren die in de superklasse is beschreven.

MORPHX IT Klassen

© 2007 Steen Andreasen

125

Opmerking : De tools Visual MorphXplorer en Application Hierarchy Tree beschikbaar vanuit het add-ins menu geven je een overzicht van de klassehiërarchie. Denk er wel aan om de kruisverwijzing tijdig bij te werken. Bij de aanmaak van een subklasse is het aan te raden om de subklasse als prefix de naam van de superklasse te geven gevolgd door "_". Als de superklasse SalesFormLetter is, dan kan de naam van een subklasse SalesFormLetter_Confirm zijn. static void Classes_Modifieres(Args _args) { MyClass_Modifiers_sub modifiers_sub; ; modifiers_sub = new MyClass_Modifiers_sub(); modifiers_sub.publicMethod(); } Deze job test de klasse MyClass_AccessModifiers_sub. Alleen de methode met toegangsmodificeerder public wordt uitgevoerd, gezien de methodes met toegang “private” en “protected” niet buiten de klasse kunnen worden uitgevoerd. Als je een punt intikt na de klassenaam, worden alle methodes van de superklasse en subklasse getoond. De compiler geeft echter een foutmelding als je een methode wil gebruiken met een beperkt toegangsniveau. Opmerking : Voor versie 3.0 werden toegangsmodificeerders niet gevalideerd door de compiler.Toegangsmodificeerders konden opgegeven worden maar hadden geen effect. Het gebruik van de toegangsmodificeerder “private” voor een klasse heeft geen effect. Als je wil beperken hoe een klasse wordt gedeclareerd, kan je best de methode new() als “private” definiëren. Dit voorkomt dat je een object van de klasse op de gewone manier via new() aanmaakt. In plaats daarvan kan je een constructor() methode voorzien om te controleren hoe een object van de klasse kan worden aangemaakt. Een constructor() methode wordt altijd gedefinieerd als statische methode; de toegang tot de constructor hangt af van de toegangsmodificeerder van deze methode. Toegangsmodificeerders kunnen ook gebruikt worden in formulieren en rapporten om te voorkomen dat methodes opgeroepen worden buiten het object. In feite kan je alle types modificeerders in formulieren en rapporten gebruiken hoewel het enkel zinvol is om toegangsmodificeerders te gebruiken.

MORPHX IT Klassen

126

© 2007 Steen Andreasen

Statische Modificeerder Normaal worden methodes beschouwd als instantiemethodes. Dit betekent dat je eerst een klasseobject moet declareren voor je de methode kan gebruiken. Als het sleutelwoord static wordt toegevoegd aan de methode, kan je de methode gebruiken zonder dat er eerst een klasseobject moet zijn aangemaakt. static EmplTable find(EmplId _emplId, boolean _forUpdate = false) { EmplTable emplTable; if (_emplId) { emplTable.selectForUpdate(_forUpdate); select firstonly emplTable index hint EmplIdx where emplTable.emplId == _emplId; } return emplTable; } De code hierboven toont de find() methode van EmplTable. Deze methode geeft een employee record terug gebaseerd op de employee id die als parameter wordt doorgegeven. Tabelmethodes zoals find() en exist() worden altijd als static gedefinieerd. Als je een methode gebruikt om een tabelrecord te vinden, is het niet zinnig om eerst een tabelvariabele te declareren omdat de methode nu net die tabelvariabele moet opzoeken. De onderstaande code toont hoe je de statische find() methode van EmplTable moet oproepen. Merk op dat een statische methode verschijnt na de dubbele dubbele punt (::). Deze syntax gebruik je ook om te verwijzen naar een enumeratie. static void Class_CallStaticMethod(Args _args) { ; info(EmplTable::find("AMO").name); } Statische modificeerders worden vaak gebruikt in klassen voor methodes die vaak gebruikt zullen worden. De klasse WinAPI is een goed voorbeeld voor het gebruik van statische methodes. WinAPI bevat een lijst van statische methodes zoals methodes voor de toegang tot het filesysteem voor bestandsbeheer. Zo voorziet WinAPI in een methode die nakijkt of een bepaald bestand bestaat. Methodes die een klasseobject declareren, gebruiken de static modificeerder. De meest gekende voorbeelden zijn de methodes main() en de constructor(). Merk op dat main() de enige klassemethode is die door de kernel wordt opgeroepen.

MORPHX IT Klassen

© 2007 Steen Andreasen

127

Finale Modificeerder Als je een klasse als finaal definieert, voorkom je dat de klasse wordt overschreven. De reden hiervoor kan zijn dat je wil dat de klasse gebruikt wordt zoals ze is. De klasse kan geschreven zijn voor gebruik door een andere klasse of kan gegevens doorgeven aan een andere klasse. De finale klasse InventOnhand is een voorbeeld hiervan. Een finale methode kan niet overschreven worden door een subklasse. Enkel instantiemethodes kunnen gedefinieerd worden als finaal, omdat een statische methode niet overschreven kan worden. Het definiëren van een methode als finaal voorkomt het overerven van deze methode. De methode heeft hetzelfde toegangsniveau als een publieke methode. Je kan echter het sleutelwoord final gebruiken samen met een van de toegangsmodificeerders zoals hieronder is aangegeven. final protected void protectedMethod() { ; info("protected"); } Display en Edit Modificeerders MorphX heeft twee speciale modificeerders, display en edit, die gebruikt worden in de gebruikersinterface. Zij worden gebruikt voor formulier- en rapportelementen die niet gekoppeld zijn aan een veld van een gegevensbron. Zowel display als edit kunnen gecombineerd worden met de sleutelwoorden static en final. Je vindt een gedetailleerde uitleg over de display en edit modificeerders in het hoofdstuk Formulieren. Abstract Modificeerder Een abstracte klasse of methode is het tegenovergestelde van een finale klasse of methode. Het gebruik van abstracte klassen is een manier voor het plannen van overerving, gezien je verplicht bent een subklasse te gebruiken. Je kan nooit een object initiëren die behoort tot de abstracte klasse, tenzij je een subklasse gebruikt. Dit systeem wordt vaak gebruikt in het standaardpakket voor superklassen om ervoor te zorgen dat een superklasse niet per ongeluk wordt gebruikt. De klasse SalesFormLetter die gebruikt wordt voor het aanmaken van documenten zoals verkoopsbevestigingen en verkoopsfacturen, gebruikt deze manier van werken. De klasse heeft een construct() methode die moet gebruikt worden, en om te voorkomen dat een object van deze klasse wordt aangemaakt via new(), wordt de klasse SalesFormLetter gedefinieerd als abstract. abstract CustAccount myAbstractMethod() { // The code in this method is never executed #if.never select firstonly custTable where custTable; #endif } Methodes kunnen gedeclareerd worden als abstract, maar alleen als de klasse zelf ook abstract is. Een abstracte methode moet overschreven worden aangezien een abstracte

MORPHX IT Klassen

128

© 2007 Steen Andreasen

methode geen code kan bevatten. Abstracte methodes bevatten enkel een parameterprofiel. Je kan niettemin de macro #if.never gebruiken om "code" toe te voegen aan een abstracte methode. Hierin kan je dan noteren waarom deze methode moet overschreven worden in een subklasse. Je vraagt je misschien af waarom je niet eenvoudigweg commentaar toevoegt in de abstracte methode. De reden hiervoor is dat de code in de macro getoond wordt in kleur net zoals andere code in de editor, waardoor je beter het verschil ziet tussen commentaar en code. Merk op dat er geen validatie van de code gebeurt die in de #if.never macro geschreven staat, dus je kan hier eender wat schrijven. Het is optioneel om methodes van een abstracte klasse als abstract te definiëren. Daarom kan je ook nog variabelen toevoegen aan de ClassDeclaration van een abstracte klasse. De toegangsmodificeerder protected kan gebruikt worden voor abstracte methodes. Een abstracte methode kan niet als static worden gedefinieerd, omdat de statische methodes enkel bestaan in de superklasse. Interface Klasse MorphX kan alleen enkelvoudige erfelijkheid aan, wat inhoudt dat je slechts één superklasse kan hebben. Dit is helemaal niet slecht, aangezien het in talen met meervoudige erfelijkheid moeilijk kan worden om een overzicht van de klassenhiërarchie te krijgen. Er is echter een manier om toch meervoudige erfelijkheid te benaderen. Hiervoor moet je interfaceklassen definiëren en deze interfaces implementeren in je subklasse. Net zoals abstracte klassen en methodes, kan je nooit een object aanmaken die behoort tot een interface klasse en de methodes van een interface klasse kunnen geen code bevatten. Wat is dan het verschil tussen een abstracte klasse en een interface? Een abstracte klasse wordt gebruikt om instructies te geven over de inhoud van een subclasse. Een interface moet geen deel uitmaken van de hiërarchie. Je kan meer dan één interface definiëren voor een klasse. Interfaces worden gebruikt voor algemene taken; denk hierbij aan de interface SysPackable die controleert dat de methodes voor het bewaren van de laatste waarden in een dialoogscherm, zijn gecreëerd. interface MyClass_Interface { } Een interface is in feite geen echte klasse. Als je een interface definieert, vervang je het sleutelwoord class door interface. Interfaces kunnen overerven van een andere interface, maar een klasse kan geen interface erven; een klasse implementeert een interface. public abstract class Runbase extends Object implements sysSaveable, sysRunable { } Als je een interface klasse in een klasse implementeert, gebruik je daarvoor het sleutelwoord implements. Hierboven wordt de klassehoofding van de klasse Runbase getoond. Merk op dat implements genoteerd wordt na de overgeërfde klasse.

MORPHX IT Klassen

© 2007 Steen Andreasen

129

Opmerking: Bij het oproepen van de globale methode pickInterface(true) in een job, worden alle interface klassen opgelijst in een scherm. De globale klasse heeft verschillende methodes die beginnen met pick* en die gebruikt kunnen worden voor het opzoeken in AOT knooppunten. Eender welke toegangsmodificeerder kan gebruikt worden in een interface klasse. Onthoud dat een interface klasse geen superklasse is, maar eerder een sjabloon voor je klasse. Het doorgeven van waardes Zoals we al gezien hebben, kan het parameterprofiel van een methode eender welk aantal parameters bevatten. Zowel basistypes als complexe types kunnen doorgegeven worden als parameter. Het hoofddoel van methodes is net het uitwisselen van variabelen via hun parameterprofiel en teruggeefwaarde. Het zorgt ervoor dat je methodes zich gedragen als bakstenen die je netjes in mekaar kan passen. Call by Het doorgeven van waardes aan een methode gebeurt via de parameters. In programmeertalen zoals C en C++ zijn er twee manieren om waardes door te geven aan een methode. Dit gebeurt via het doorgeven van een waardeparameter of een referentieparameter. In het Engels spreken we respectievelijk van “call by value” of “call by reference”. Bij het doorgeven van een waardeparameter (“call by value”) geeft men via de parameter de waarde van een variabele door, maar niet de variabele zelf. Men maakt als het ware een kopie van de oorspronkelijke variabele en werkt met die kopie verder. Bij het doorgeven van referentieparameter (“call by reference”) geeft men de variabele zelf door. Het verschil zie je als een methode de waarde van de parameter wijzigt. Als je een waardeparameter doorgeeft aan een methode, wordt de waarde van de variabele in de methode wel gewijzigd, maar de variabele zelf is ongewijzigd, omdat je met een kopie werkt. Als je een referentieparameter doorgeeft aan een methode, dan wordt de waarde van de variabele in de methode gewijzigd, omdat je nu met het origineel werkt, en niet met een kopie. In MorphX worden parameters doorgegeven via “call by value”. Dit betekent dat variabelen , doorgegeven aan een methode, niet gewijzigd worden als de waardeparameter wijzigt binnen de methode. Het gebruik van tijdelijke tabellen als parameter volgt deze regel niet gezien tijdelijke tabellen de regel van “call by reference” volgen. Als een tijdelijke tabel doorgegeven wordt aan een methode als parameter, en de tijdelijke tabelparameter wordt gewijzigd in de methode, dan zal de tijdelijke tabel zelf ook gewijzigd zijn. Het maakt niet uit welke type parameter je gebruikt; het wordt aangeraden om een lokale variabele te creëren voor de parametervariabele in plaats van de parametervariabele te wijzigen, aangezien dit je code leesbaarder maakt. Voorbeeld 3: Call by Objecten gebruikt van MORPHXIT_Classes project

MORPHX IT Klassen

130

© 2007 Steen Andreasen

Class, MyClass_PassingValues We gaan een klasse met twee methodes aanmaken. De bedoeling is om te tonen hoe MorphX reageert als variabelen worden doorgegeven aan een methode. Een parameter in de methode callByValue() wordt gewijzigd in de methode. Deze handelwijze wordt echter niet aangemoedigd. 1. Maak een nieuwe klasse aan met de naam MyClass_ParameterValues. 2. Voeg een methode toe met de naam callByValue() met één parameter van het

uitgebreide datatype Counter. Verhoog de waarde van Counter met 10 en laat de methode het resultaat teruggeven.

Counter callByValue(Counter _counter) { _counter += 10; return _counter; } 3. Voeg een methode toe met de naam callByReference() met de tijdelijke tabel

TmpAccountSum als parameter. De methode moet het laatste record van de tijdelijke tabel ophalen en een veld in de Infolog afdrukken.

void callByReference(TmpAccountSum _tmpAccountSum) { TmpAccountSum tmpAccountSum; ; tmpAccountSum = _tmpAccountSum; select firstonly tmpAccountSum order by accountNum desc; { info(tmpAccountSum.accountNum); } } 4. Bewaar de klasse. Via de onderstaande job kan je de klasse MyClass_PassingValues testen : Static void Classes_CallByValue(Args _args) { MyClass_PassingValues passingValues = new MyClass_PassingValues(); Counter counter = 100; ; info(strFmt("%1", counter)); info(strFmt("%1", passingValues.callByValue(counter))); info(strFmt("%1", counter)); } De job initializeert de counter variabele en drukt de waarde af in de Infolog voor de oproep van de methode callByValue(). De teruggegeven waarde van callByValue() wordt eveneens afgedrukt in de Infolog, en tot slot wordt de counter variabele weer

MORPHX IT Klassen

© 2007 Steen Andreasen

131

afgedrukt om te tonen dat callByValue() de beginwaarde van de counter variabele niet heeft gewijzigd. static void Classes_CallByReference(Args _args) { CustTable custTable; TmpAccountSum tmpAccountSum; MyClass_PassingValues passingValues = new MyClass_PassingValues(); Counter counter; ; while select custTable { counter++; if (counter > 5) break; tmpAccountSum.accountNum = custTable.accountNum; tmpAccountSum.insert(); } select tmpAccountSum; info(tmpAccountSum.accountNum); passingValues.callByReference(tmpAccountSum); info(tmpAccountSum.accountNum); } We doen een gelijkaardige test met de methode callByReference(). Eerst voegen we gegevens toe aan de tijdelijke tabel. De eerste 5 records van CustTable worden doorlopen en in de tijdelijke tabel geplaatst. Het eerste record van de tijdelijke tabel wordt afgedrukt in de Infolog. CallByReference() wordt opgeroepen met de tijdelijke tabel als parameter en drukt het laatste record van de tijdelijke tabel af. Als je na de oproep van CallByReference() de tijdelijke tabel opnieuw afdrukt, zal je zien dat het afgedrukte record gelijk is aan het record dat is opgehaald door CallByReference(). Het doorgeven van referentie of “call by reference” kan verwarrend zijn. Om te voorkomen dat je tijdelijke tabel wijzigt door de opgeroepen methode, zou je callByReference() als volgt moeten aanpassen. void callByReference(TmpAccountSum _tmpAccountSum) { TmpAccountSum tmpAccountSum; ; tmpAccountSum.setTmpData(_tmpAccountSum); select firstonly tmpAccountSum order by accountNum desc; { info(tmpAccountSum.accountNum); } } De methode setTmpData() wordt gebruikt om de lokale variabele TmpAccountSum te initializeren aangezien het gelijkstellen van TmpAccountSum aan de parametervariabele _tmpAccountSum nog altijd gelijk is aan een “call by reference”. Merk op dat enkel

MORPHX IT Klassen

132

© 2007 Steen Andreasen

tijdelijke tabellen doorgegeven worden als referentieparameter. Een normale tabel zoals CustTable wordt als waardeparameter doorgegeven. Recursieve oproepen Een recursieve methode is een methode die zichzelf oproept. Bij het schrijven van een recursieve methode, moet je erop letten dat je code niet leidt tot een eindeloze lus. Maar recursieve methodes hebben ook het voordeel dat ze je code vereenvoudigen. Voeg de volgende methode toe aan de klasse MyClass_PassingValues: void recursiveCall(ProjId _projId = "") { ProjTable projTable; ; while select projTable where projTable.parentId == _projId { setprefix(projTable.parentId); info(projTable.projId); this.recursiveCall(projTable.projId); } } De tabel ProjTable die de hoofdtabel is voor projecten, wordt doorlopen in de methode recursiveCall(). Projecten worden in een hiërarchie opgebouwd in Axapta, en deze methode sorteert de records volgens de boomstructuur. Hiervoor zou je een hoop code nodig hebben, of toch minstens twee methodes, ware het niet dat we een methode recursief kunnen gebruiken. Voor iedere iteratie in de methode recursiveCall() wordt een lijn afgedrukt in de Infolog. De methode setprefix() wordt gebruikt om het niveau van de projecten in de Infolog te tonen. Schrijf de onderstaande job om de methode te testen : static void Classes_RecursiveCall(Args _args) { MyClass_PassingValues passingValues = new MyClass_PassingValues(); ; passingValues.recursiveCall(); }

MORPHX IT Klassen

© 2007 Steen Andreasen

133

Teruggeven van waardes Een methode kan maar één waarde teruggeven, of beter één variabele. Als je return oproept, wordt de methode beëindigd. In sommige gevallen zou je willen dat je meer dan één variabele kan teruggeven. In dat geval moet je het ontwerp van je methode eens herbekijken, want mogelijk wil je te veel bereiken in je methode. Container returnContainer() { CustAccount custAccount; custName custName; return [custAccount, custName]; } Omdat het return type zowel een basistype als een complex type kan zijn, kan je ook een container gebruiken. Je moet geen containervariabele declareren in de methode, omdat je de waardes kan teruggeven, onderling gescheiden door een komma tussen vierkante haken [] zoals in het bovenstaande voorbeeld wordt getoond. MorphX heeft een verzameling van funderingsklassen die dit probleem ook kunnen oplossen. Een funderingsklasse kan een aantal variabelen bevatten. Je vindt meer informatie over dit soort klassen in de sectie Funderingsklassen.

5.2 AOS Een Axapta-installatie kan geconfigureerd worden als een tweerangs- of drierangsinstallatie (2-tier en 3-tier). Waar een tweerangsinstallatie bestaat uit een client en een server, beschikt een drierangsinstallatie ook over een Applicatie Object Server of AOS. Zonder een AOS is de werklast voor de client, wat zorgt voor meer verkeer tussen de databankserver en de client. Een AOS beperkt het verkeer tussen de databank en client omdat de AOS zich bekommert om de communicatie met de databank, waardoor de client zich enkel moet bekommeren om het opbouwen van de gebruikersinterface. Je kan een onbeperkt aantal AOS servers opzetten in een samenwerkende configuratie om de werklast te verdelen. Voor meer informatie over de AOS verwijs ik naar de handleidingen van het standaardpakket. Een uitleg over de AOS ligt buiten het bereik van dit boek. De focus ligt op de optimalisering van je code voor gebruik van de AOS. Opmerking : Het optimaliseren van je code voor de AOS is een optie. Je moet niet ieder deel van je code optimaliseren, omdat je mogelijk teveel tijd zal besteden aan de optimalisatie. Als je zware wijzigingen wil aanbrengen of je zit met een beperkte bandbreedte, dan krijg je zeker een veel betere performantie bij optimalisatie voor de AOS, en dan is het eerder een plicht in plaats van een optie. Keuze van de rang Als een tabelmethode of klassemethode wordt uitgevoerd, kan je bepalen of de code wordt uitgevoerd op de AOS of op de client. De standaard tabelmethodes die de databank benaderen voor het toevoegen, aanpassen of verwijderen van gegevens, kunnen niet aangepast worden. Deze methodes worden altijd op de server uitgevoerd. Formulieren zouden altijd op de client opgebouwd moeten worden. De standaardaanpak is dat een object wordt uitgevoerd in de rang waar het object wordt opgeroepen. De

MORPHX IT Klassen

134

© 2007 Steen Andreasen

enige reden om de rang te wijzigen waar een object wordt uitgevoerd, zou optimalisatie moeten zijn. Je code zal werken zowel in een tweerangs- als drierangsomgeving aangezien de instellingen om een object uit te voeren op een bepaalde rang, enkel gevalideerd worden in een drierangsomgeving. Dit betekent eveneens dat AOS optimalisatie enkel in een drierangsomgeving werkt. Als je een formulier wijzigt zodat het “server side” werkt , zal dit in tweerangsomgeving werken, maar het oproepen van het formulier in drierangsomgeving zorgt ervoor dat het formulier niet wordt getoond, aangezien het formulier aan de server is toegewezen. Om de rang voor het oproepen van het object aan te geven, gebruik je de eigenschap RunOn en de modificeerders client en server. Menuopties en klassen hebben deze eigenschap RunOn. De standaardinstelling voor menuopties is uitvoering op de client. Voor klassen kan je de eigenschap instellen op client, server of called from. De standaardinstelling is called from. Het instellen van RunOn op called from betekent dat het object uitgevoerd zal worden in de rang die gebruikt is door het oproepende object. De sleutelwoorden client en server worden gebruikt om de rang te specifiëren in methodes. Een methode wordt ingesteld op called from door zowel client als server op te geven als modificeerders van RunOn. Het wordt aanbevolen om de AOS modificeerders te plaatsen voor andere modificeerders. server AmountMST balancePerDate(TransDate _transactionDate = systemdateGet()) { return this.CustVendTable::balancePerDate(_transactionDate); } De eigenschap RunOn in het eigenschappenblad van een klasse wordt gebruikt om de rang te definiëren waarin de klasse wordt uitgevoerd. Alle instantiemethodes in de klasse zullen uitgevoerd worden in de rang gespecifieerd in de RunOn eigenschap. Enkel statische methodes van de klasse kunnen hun eigen AOS modificeerder hebben. De methode die je hierboven ziet, is gespecifieerd om uitgevoerd te worden op de AOS. Tabellen wijken hiervan af omdat zowel instantiemethodes als statische methodes in tabellen AOS modificeerders kunnen hebben. Als je de RunOn rang bepaalt voor een menuoptie die een klasse oproept, zal de klasse standaard lopen in dezelfde rang indien de eigenschap RunOn van de klasse ingesteld is op called from. De instelling van een menuoptie kan echter overschreven worden door de klasse. De rang wordt bepaald door waar de klasse is gedeclareerd, waar new() wordt opgeroepen. Dit betekent ook dat de RunOn eigenschap van een superklasse zal bepalen in welke rang de subklassen uitgevoerd zullen worden.

MORPHX IT Klassen

© 2007 Steen Andreasen

135

Optimalisatie van Objecten Welk object zou nu geoptimaliseerd moeten worden voor de AOS en wanneer zou je dit moeten doen ? Een vuistregel is dat tabelmethodes en klassen met veel databanktoegang kandidaten zijn voor objecten die op de AOS moeten lopen. Rapporten die gebruik maken van het runbase raamwerk kunnen ook ingesteld worden om op de server te lopen. Het lijkt misschien tegenstrijdig dat je een rapport laat lopen op de server, terwijl dit niet kan voor een formulier. Het runbase raamwerk handelt oproepen tussen de client en de AOS af, dus krijgt de gebruiker toch nog de rapportdialoog te zien ook al loopt het rapport op de AOS. De MorphX tools Systeem Monitoring en Code Profile kunnen gebruikt worden om oproepen tussen de rangen op te volgen. Je zou vertrouwd moeten zijn met deze tools vooraleer je je code gaat optimaliseren voor de AOS.

5.3 Runbase Raamwerk Een batch job of stapeltaak is een taak die ingesteld is om uitgevoerd te worden op een bepaalde tijd of herhaald te worden met een bepaald interval. De batch server die de batch jobs uitvoert, wordt doorgaans geplaatst op een gedediceerde server. Een Axapta client wordt opgestart om de batch server te runnen, en de batch server kan ook ingesteld worden om te runnen als een Microsoft Windows Service. Je vindt gedetailleerde informatie over de configuratie van batch servers en batch jobs in de standaardhandleidingen van het standaardpakket. Zware taken zoals complexe rapportering die een aanzienlijk aantal records moeten doorlopen, worden typisch ingesteld als stapeltaken. Dit kan een dagelijkse taak zijn zoals het overbrengen van verkoopsorders naar een extern systeem of het periodiek afdrukken van klantenbalansen. Stapeltaken hebben geen interactie met de applicatiegebruikers aangezien dit de stapeltaak “on hold” zou zetten. Als een klasse is ingesteld als stapeltaak, worden waardes op voorhand opgegeven door de applicatiegebruikers, gebruikt omdat de batch server elke dialoog zal overslaan. Het gebruik van het Runbase Raamwerk Het runbase raamwerk heeft twee primaire functies : het maken van een gelijkaardige opmaak voor dialoogvensters die getoond worden aan de applicatiegebruikers en de mogelijkheid bieden om een stapeltaak op vaste tijden op te starten. De klassen waarvan de naam begint met RunBase* maken deel uit van het runbase raamwerk. Verschillende van deze runbase klassen worden enkel opgeroepen door het raamwerk. In het dagelijks gebruik moet je alleen het doel van deze klassen kennen. Meer informatie vind je in figuur 20 : basisklassen van het runbase raamwerk.

MORPHX IT Klassen

136

© 2007 Steen Andreasen

Klassenaam Omschrijving

RunBase

De klasse RunBase wordt gebruikt voor taken die niet in batch moeten uitgevoerd worden.

RunBaseBatch

RunBaseBatch wordt gebruikt om de applicatiegebruiker de mogelijkheid te geven om een stapeltaak op een vast tijdstip te doen starten.

RunBaseReport

Wordt enkel gebruikt voor zware rapporten en is daarom een subklasse van RunBaseBatch.

Figuur 20: Basisklassen van het runbase raamwerk. Het deel van het runbase raamwerk dat gebruikt wordt voor rapporten, wordt meer in detail behandeld in het hoofdstuk Rapporten. Voorbeeld 4: het aanmaken van een klasse die in batch kan lopen Objecten gebruikt van het MORPHXIT_Classes project

Class, MyClass_RunBaseBatch Menu item action, MyClass_RunBaseBatch

In dit voorbeeld wordt een klasse aangemaakt die gebruik maakt van het runbase raamwerk. De klasse zal de mogelijkheid hebben om uitgevoerd te worden als stapeltaak. De focus ligt op het aanmaken van een klasse via het runbase raamwerk. De klasse zal alle klanten afdrukken waarvoor er klantentransacties genoteerd zijn na een op te geven datum. De laatst opgegeven “datum vanaf”, gebruikt voor het opzoeken van de klantentransacties, zal worden bewaard. Maak een nieuwe klasse aan met de naam “MyClass_RunBaseBatch”. class MyClass_RunBaseBatch extends RunBaseBatch { FromDate fromDate; DialogField dialogFromDate; #define.CurrentVersion(1) #localmacro.CurrentList fromDate #endmacro } De nieuwe klasse moet een subklasse zijn van de klasse RunBaseBatch. Een variabele van het uitgebreide datatype FromDate wordt gedefinieerd om hierin de laatst opgegeven “datum vanaf” te bewaren. De variabele dialogFromField is een variabele van de klasse DialogField waarmee je een veld toevoegt aan het dialoogvenster dat getoond wordt aan de applicatiegebruikers. De macroconstante CurrentVersion houdt de laatste versie van het dialoogvenster bij. CurrentList is een macro die de waarde van de variabelen van de laatste oproep van de klasse bewaart.

MORPHX IT Klassen

© 2007 Steen Andreasen

137

public container pack() { return [#CurrentVersion, #CurrentList]; } De methode Pack() wordt overschreven. De methode geeft de twee macro’s terug die je in de ClassDeclaration vindt. public boolean unpack(container packedClass) { container base; boolean ret; Integer version = conPeek(packedClass,1); switch (version) { case #CurrentVersion: [version, #CurrentList, base] = packedClass; ret = true; break; default: ret = false; } return ret; } Unpack() is ook een overschreven methode. De variabelen in de macro CurrentList worden geïnitialiseerd als unpack() wordt uitgevoerd. static void main(Args _args) { MyClass_RunBaseBatch runBaseBatch = new MyClass_RunBaseBatch(); ; if (runBaseBatch.prompt()) runBaseBatch.run(); } Dit is een statische methode die de klasse declareert. Main() wordt gebruikt om de klasse uit te voeren. Als de dialoog niet wordt gestopt via de cancel-knop door de applicatiegebruiker, zal de methode run() worden uitgevoerd. protected Object dialog() { DialogRunBase dialog = super(); ; dialog.addGroup("Date"); dialogFromDate = dialog.addFieldValue(typeId(FromDate), fromDate); return dialog; } In deze overschreven methode wordt de dialoog gedeclareerd. Er is een veld toegevoegd aan de dialoog voor het intikken van de “datum vanaf”.

MORPHX IT Klassen

138

© 2007 Steen Andreasen

public boolean getFromDialog() { boolean ret; ret = super(); if (ret) { fromDate = dialogFromDate.value(); } return ret; } GetFromDialog() wordt opgeroepen als de Ok knop van het dialoogvenster wordt ingedrukt. De waarde ingetikt in het veld “datum vanaf” wordt dan bewaard in de variabele fromDate. client server static ClassDescription description() { return "Testing batch able class"; } Alle klassen die erven van het runbase raamwerk zouden deze methode moeten hebben. Gezien de methode statisch is, kan je de methode van de superklasse niet overschrijven, en dus moet je description() manueel aanmaken. Deze methode voegt een titel toe aan het dialoogvenster. public void run() { CustTable custTable; CustTrans custTrans; Counter totalRecords; ; select count(recId) from custTable exists join custTrans where custTrans.accountNum == custTable.accountNum && custTrans.transDate >= fromDate; totalRecords = custTable.recId; startLengthyOperation(); this.progressInit("List customers with transactions", totalRecords, #AviSearch); while select custTable exists join custTrans where custTrans.accountNum == custTable.accountNum && custTrans.transDate >= fromDate { progress.incCount(); progress.setText(strfmt("%1, %2", custTable.accountNum, custTable.name)); sleep(500); } endLengthyOperation(); }

MORPHX IT Klassen

© 2007 Steen Andreasen

139

Run() voert de taak uit gebaseerd op de gegevens van het dialoogvenster. De eerste selectie-instructie telt het aantal klanten die verwerkt moeten worden via een geaggregeerde functie. Het aantal klanten wordt gebruikt om een voortgangsbalk te tekenen die aangeeft van welke klant de bijbehorende transacties worden doorlopen. De methodes startLengthyOperation() en endLenghthyOperation() worden gebruikt om de zandloper op het scherm weer te geven. Een macro-oproep wordt gebruikt als parameter bij de initialisatie van de voortgangsbalk. Dit is een macroconstante van de macrobibliotheek AviFiles die gedeclareerd is in het runbase raamwerk. In plaats van informatie naar de Infolog te sturen, wordt de voortgangsbalk aangepast telkens we de transacties van een volgende klant doorlopen. De functie sleep() wordt gezet om een vertraging van een halve seconde te krijgen waardoor je de voortgangsbalk ook te zien krijgt. Tot slot maak je een menuoptie voor de klasse door de klasse te verslepen naar het menuoptieknooppunt Output. Bij het uitvoeren van MyClass_Runable wordt een dialoogvenster getoond. Dit dialoogvenster heeft twee tabbladen. Het eerste tabblad heeft een veldgroep met een veld waar je de “datum vanaf” kan intikken. De waarde die je hier opgeeft, zal bewaard worden als de standaardwaarde voor de volgende keer dat de klasse wordt uitgevoerd. De prompt() methode in main() roept de dialog() methode op. De methode run() wordt opgeroepen als de OK-knop van het dialoogvenster wordt aangeklikt en de methode getFromDialog() geeft de waarde “true” terug, wat erop neerkomt dat de opgegeven waardes in het dialoogvenster zijn gevalideerd. Je moet altijd starten met het overschrijven van de methodes pack() en unpack() bij het creëren van een runbase klasse. Als je probeert om de statische main() methode te schrijven voor je deze methodes hebt overschreven, zal je een foutmelding krijgen omdat pack() en unpack() een deel zijn van de interface voorzien door het runbase raamwerk. Het tweede tabblad is voor de batchinstellingen. Zelfs als een klasse niet mag uitgevoerd worden als stapeltaak of batch job, kan je toch nog de klasse RunBaseBatch gebruiken. Als de methode canGoBatch() wordt overschreven en de waarde “false” teruggeeft, zal het batchtabblad niet getoond worden. Merk op dat je de methode validate() moet overschrijven, als je de waardes wil valideren die de applicatiegebruiker in het dialoogvenster heeft ingetikt. Het dialoogvenster kan niet gesloten worden met de OK-knop vooraleer de methode validate() de waarde “true” teruggeeft. Je kan de klasse MyClass_Runable direct uitvoeren door de klasse te selecteren, met de rechtermuisknop te klikken en vervolgens Open te kiezen. Omdat de klasse een statische methode main() met parameter Args heeft, kan de klasse uitgevoerd worden zonder enige codering. We spreken dan van een uitvoerbare klasse. Alle klassen met een statische main() methode zijn uitvoerbaar en kunnen opgeroepen worden via een menuoptie. Merk op dat je het runbase raamwerk niet nodig hebt om een uitvoerbare klasse te maken. Je kan best een klasse uitvoeren via een menuoptie. Het is aanbevolen om steeds een menuoptie te maken voor je uitvoerbare klasse omdat je dan je klasse test zoals de applicatiegebruikers de klasse zullen uitvoeren. Uitvoerbare klassen worden altijd gecreëerd als actiemenuopties, als ze geen formulier of rapport oproepen.

MORPHX IT Klassen

140

© 2007 Steen Andreasen

In dit voorbeeld hebben we gebruik gemaakt van een voortgangsbalk. Je kan best altijd een voortgangsbalk gebruiken voor een taak die meer dan enkele minuten duurt. Dit maakt je klasse meer gebruiksvriendelijk omdat de applicatiegebruiker kan zien wanneer de taak is beëindigd. Het runbase raamwerk voorziet reeds in voortgangsbalken en dus kan je eenvoudig een voortgangsbalk toevoegen aan je subklasse. Je kan trouwens voor eender welke codeblok een voortgangsbalk gebruiken. De klassen waarvan de naam begint met SysOperationProgress* gebruik je voor het opbouwen van voortgangsbalken. Het formulier Tutorial_Progress toont je een overzicht van de mogelijkheden bij het gebruik van voortgangsbalken. Een overzicht van beschikbare animaties in voortgangsbalken vind je terug in de klasse Tutorial_ShowAviFiles. De parameter Args in main() gebruik je om extra informatie door te geven aan het opgeroepen object. Hiervan wordt vaak gebruik gemaakt als je een uitvoerbare klasse oproept vanuit een formulier en het cursorrecord wil doorgeven of het oproepende object wil kenbaar maken. Het hoofdstuk Formulieren gaat dieper in op het gebruik van Args. Dialoog Een dialoog(venster) is de gebruikersinterface van een klasse. Als je een dialoogvenster voor je klasse nodig hebt, moet je het runbase raamwerk gebruiken omdat dialoogvensters een geïntegreerd deel zijn van het raamwerk. Voor het beheer van de dialogen maakt het raamwerk gebruik van klassen waarvan de naam begint met Dialog*. Eenvoudige formuliereigenschappen zoals het groeperen van velden, het toevoegen van opzoekvelden worden voorzien door de dialoogklassen. Je kan optionele knoppen toevoegen aan je dialoogvenster zoals voor het oproepen van een query. Als je klasse erft van de RunBaseReport klasse, worden je query- en printerinstellingen automatisch toegevoegd aan het dialoogvenster. Voorbeeld 4: klassedialoog Objecten gebruikt van MORPHXIT_Classes project

Class, MyClass_RunBaseDialog Menu item action, MyClass_RunBaseDialog

In dit voorbeeld worden sommige kenmerken van de klassedialoog getoond. Om de code te vereenvoudigen, hebben we de methodes description() en run() er met opzet uitgelaten. Om te beginnen, kopiëren we de klasse MyClass_RunBaseBatch en geven de kopie de naam “MyClass_RunBaseDialog”. Pack() en unpack() laten we ongewijzigd. De run() methode moet verwijderd worden. De andere methodes herschrijven we als volgt :

MORPHX IT Klassen

© 2007 Steen Andreasen

141

class MyClass_RunBaseDialog extends RunBaseBatch { NoYesId selectDate; FromDate fromDate; ToDate toDate; NoYesId selectCustGroup; CustGroupId custGroupId; DialogField dialogFromDate, dialogToDate, dialogSelectCustGroup, dialogCustGroup; DialogGroup dateGroup, countryGroup; #define.CurrentVersion(1) #localmacro.CurrentList fromDate, toDate, selectCustGroup, custGroupId #endmacro } We hebben nog wat bijkomende dialoogvelden gedeclareerd. Een variabele is aangemaakt om de waarde van elk dialoogveld te kunnen bewaren. De macro CurrentList is uitgebreid om de nieuwe variabelen te kunnen bevatten. protected Object dialog() { DialogRunbase dialog = super(); ; dialog.allowUpdateOnSelectCtrl(true); dateGroup = dialog.addGroup("Date"); dateGroup.frameOptionButton(FormFrameOptionButton::Check); dateGroup.columns(2); dialogFromDate = dialog.addFieldValue(typeId(FromDate), fromDate); dialogToDate = dialog.addFieldValue(typeId(ToDate), toDate); countryGroup = dialog.addGroup("Customer"); countryGroup.columns(2); dialogSelectCustGroup = dialog.addFieldValue(typeId(NoYesId), selectCustGroup, "Select customer group"); dialogCustGroup = dialog.addFieldValue(typeId(CustGroupId), custGroupId); return dialog; } De dialooggroep bevat de “datum vanaf” en de “datum tot”. Normaal worden alle dialoogvelden in één enkele kolom getoond. De methode columns() wordt gebruikt om de “datum vanaf” en de “datum tot “ op één lijn te plaatsen. De dialoogmethode frameOptionButton() zorgt ervoor dat er een vinkvakje in de veldgroephoofding staat. De andere veldgroep Country omvat twee velden. Als het veld dialogSelectCustGroup niet is aangevinkt, kan het veld dialogCustGroup niet geëditeerd worden. Dit kenmerk kan enkel gebruikt worden als de dialoogmethode allowUpdateOnSelectCtrl() op true gezet wordt.

MORPHX IT Klassen

142

© 2007 Steen Andreasen

public void dialogSelectCtrl() { ; if (dialogSelectCustGroup.value()) { dialogCustGroup.allowEdit(true); } else { dialogCustGroup.allowEdit(false); } } Telkens een dialoogmethode is opgegeven, wordt deze methode uitgevoerd indien de dialoogmethode allowUpdateOnSelectCtrl() de waarde “true” teruggeeft. public boolean getFromDialog() { boolean ret; ret = super(); if (ret) { fromDate = dialogFromDate.value() ? dialogFromDate.value() : systemdateget(); toDate = dialogToDate.value() ? dialogToDate.value() : systemdateget(); selectCustGroup = dialogSelectCustGroup.value(); custGroupId = dialogCustGroup.value(); } return ret; } De waarden worden uit de dialoogvelden opgehaald en bewaard in de variabelen die je in de ClassDeclaration vindt. Het vinkvakje in de hoofding van de dialooggroep “date” is met opzet niet toegevoegd. static void main(Args _args) { MyClass_RunBaseDialog runBaseDialog = new MyClass_RunBaseDialog(); ; if (runBaseDialog.prompt()) runBaseDialog.run(); } Main() is aangepast om de nieuwe klasse op te roepen. Nu moet je alleen nog een nieuwe menuoptie voor de klasse aanmaken. De klasse zal geen verwerkingen doen omdat run() niet als methode is toegevoegd. Gezien run() tot de superklasse behoort, levert dit geen foutmelding op. Het oproepen van de klasse laat het dialoogvenster zien zoals getoond in figuur 21: Dialoog-voorbeeld.

MORPHX IT Klassen

© 2007 Steen Andreasen

143

Figuur 21: Dialoogvoorbeeld Het dialoogvenster toont 2 manieren waarop je de waarde van een veld afhankelijk kan maken van een andere veldwaarde. Als je het vinkvak “date” in de veldgroephoofding uitvinkt, kunnen de velden “from date” en “to date” niet gewijzigd worden. Het uitvinken kan je gebruiken om aan te geven dat alle records doorlopen moeten worden zonder bijkomende selectie op datums. De klantenveldgroep gebruikt een gelijkaardige functionaliteit.Het veld Customer group kan enkel worden ingevuld als het vinkvak Select customer group is aangevinkt. Omdat de methode dialogSelectCtrl() enkel kan uitgevoerd worden met behulp van velden van het type DialogField, is dit een vaak gebruikte werkwijze. Maar dialogSelectCtrl() kan het dialoogvenster niet vernieuwen, en dus wordt het dialoogvenster niet aangepast met de nieuwe instellingen vooraleer de gebruiker het veld customer group verlaten heeft. Als je dialoogvensters bouwt, kan je als alternatief voor het gebruik van de dialoogklassen nadenken over het aanmaken van een formulier. Het runbase raamwerk ondersteunt de standaardformulieren als dialogen. Een formulier wordt op dezelfde wijze getoond als een dialoogvenster. Dit is vaak de betere oplossing voor complexe dialogen. Als je een complexe dialoog moet bouwen, kan het tijdswinst opleveren om daarvoor een formulier te gebruiken. Je krijgt een beter overzicht van je dialoog en het wordt eenvoudiger om de dialoog uit te breiden. Je vindt meer informatie over het gebruik van formulieren als dialoogvenster in het hoofdstuk Formulieren. In het dialoogvoorbeeld hadden we de dialoogvelden even goed kunnen toevoegen aan de query. Door het gebruik van de dialoogmethode addMenuItemButton() wordt een knop met de naam Select toegevoegd aan het dialoogvenster. De query die wordt opgeroepen als de select-knop wordt aangeklikt, moet opgegeven worden door het overschrijven van de methode queryRun(). Voeg de volgende code toe aan je dialoog om een knop te hebben voor de query die getoond wordt in het dialoogvenster : dialog.addMenuItemButton(MenuItemType::Display, menuItemDisplayStr(RunBaseQueryDialog), dialogMenuItemGroup::BottomGrp); Door het overschrijven van queryRun() met de code die hieronder staat, zal een query die verwijst naar de CustTable , gekoppeld met CustTrans, opgeroepen worden.

MORPHX IT Klassen

144

© 2007 Steen Andreasen

public QueryRun queryRun() { QueryRun ret; ret = new QueryRun(QueryStr(Cust)); return ret; } Details over het bouwen en ophalen van informatie via een query wordt uitgelegd in het hoofdstuk Queries. Een nuttig kenmerk van het gebruik van het runbase raamwerk is dat de laatst opgegeven waarde van een veld gemakkelijk bewaard kan worden voor de volgende run. Dit gebeurt voornamelijk voor het bewaren van de waardes ingetikt in de dialoogvelden, maar je kan eender welke variabelen bewaren. Hiervoor moet je de variabele alleen maar toevoegen aan de CurrentList macro. Als je de CurrentList van een bestaande klasse wijzigt, kan je best de macroconstante CurrentVersion met één ophogen, omdat je mogelijk foutmeldingen krijgt als je dezelfde versie gebruikt terwijl je de lijst van bewaarde variabelen aanpast. De laatste waardes worden bewaard in de systeemtabel SysLastValue per gebruiker en per bedrijf. Het ophogen van CurrentVersion zorgt ervoor dat er een nieuw record wordt aangemaakt in SysLastValue. Pack() en unpack() moeten altijd overschreven worden omdat ze deel uitmaken van de interface in het runbase raamwerk. En ook de twee macro’s in ClassDeclaration zijn verplicht. Je zal ook situaties hebben waarin de laatst bewaarde waardes niet nodig zijn. Om deze laatste waardes over te slaan, moet pack() een lege container zoals connull() teruggeven en unpack() moet de waarde “false” teruggeven. Als je enkel een dialoogvenster nodig hebt om de keuze te geven tussen verdergaan of stoppen, is er nog een eenvoudiger oplossing waarbij je geen gebruik moet maken van het runbase raamwerk. De class Box heeft een verzameling statische methodes die alle gewone combinaties ondersteunen. Je kan de oproep van een methode van de class box in een if statement plaatsen : static void Classes_Box(Args _args) { if (Box::yesNo("Continue", DialogButton::Yes,"Test of Box") == DialogButton::Yes) info("Here goes."); }

MORPHX IT Klassen

© 2007 Steen Andreasen

145

5.4 Fundamentele klassen De laatste klassen opgelijst in de AOT onder het knooppunt Classes zijn een speciaal type klassen. Men beschouwt deze klassen als fundamentele klassen en ze verschillen van de gewone klassen die je zou moeten overerven of wijzigen. Je kan deze fundamentele klassen herkennen aan het pictogram . Je zal deze fundamentele klassen gebruiken voor je wijzigingen ook al ben je je daar niet altijd van bewust, omdat deze klassen niet gedeclareerd worden als een normale klasse. De voornaamste fundamentele klassen worden gedeclareerd door de kernel bij het opstarten van de client. Een basiskennis van deze fundamentele klassen zal je zeker meer inzicht geven in de elementaire processen in MorphX. In de volgende paragrafen vind je meer uitleg over de voornaamste fundamentele klassen. ClassFactory Telkens een object van een gebruikersinterface wordt opgeroepen zoals een formulier, rapport of dialoogvenster, wordt de klasse ClassFactory geactiveerd. Maar ook bij het uitvoeren van een object wordt ClassFactory opgeroepen. Indien we vanuit X++ een formulier, rapport of query oproepen, dan wordt het object geïnitialiseerd via ClassFactory. Dit gebeurt automatisch als je een object oproept via een menuoptie of via het runbase raamwerk. Het doel van ClassFactory is eenzelfde startpunt te bieden. Hiermee krijg je dan een hendel voor het maken van algemene wijzigingen aan formulieren of rapporten zoals het toevoegen van hoofdingen of elementen aan formulieren tijdens runtime. Voorbeelden van het gebruik van ClassFactory voor formulieren en rapporten vind je in de hoofdstukken Formulieren en Rapporten. Merk op dat je in een drierangsomgeving of 3-tier twee instanties van ClassFactory hebt. Eén ervan is gedeclareerd op de AOS en de andere op de client. Als een object wordt uitgevoerd, wordt de ClassFactory instantie van de huidige rang gebruikt. Als je verwijst naar ClassFactory vanuit een formulier dat op de client loopt, kan het resultaat verschillen met wat je ziet als je verwijst naar de ClassFactory vanuit een klasse die op de AOS loopt, omdat de twee instanties apart handelen. Het doel van deze drierangsconstructie is het aantal oproepen tussen client en server verminderen. Global Alle methodes in de Global klasse zijn statische methodes. Je kan ook instantie-methodes in de Global klasse creëren. Maar voor het gebruik van zo’n instantiemethode zou je dan eerst een object van de klasse moeten declareren. Het idee achter de Global klasse is het aanbieden van een verzameling functies in X++ die gebruikt kunnen worden zonder dat je een klassenaam moet opgeven. Methodes van Global kunnen gebruikt worden op dezelfde manier als een systeemfunctie. Verschillende methodes zijn een supplement voor de functies gebruikt voor basistype- operaties zoals de Global methode date2StrUsr() die een variabele van het type date

MORPHX IT Klassen

146

© 2007 Steen Andreasen

omzet naar een string, geformatteerd door de standaarddatuminstellingen van de gebruiker. Je kan je eigen methodes toevoegen aan de Global klasse. Dit kan handig zijn als je een stukje code zeer vaak moet gebruiken. Maar voor je je eigen Global methode toevoegt, moet je wel eerst de bestaande Global methodes en de systeemfuncties nakijken die je onder System Documentation/Functions vindt, omdat je een functie kan vinden die al voldoet aan je wensen. Info Het Infologsysteem kan je gebruiken via de klasse Info. Een instantie met de naam infoLog van de Info klasse wordt gedeclareerd bij het opstarten van Axapta. Je gebruikt de Info klasse als je afdrukt naar de Infolog. Je kan beter nooit rechtstreeks verwijzen naar de Info klasse omdat je hiervoor al de globale methodes info(), warning() en error() hebt. De Info klasse heeft andere doeleinden omdat de klasse gebruikt wordt door het documentenbeheersysteem en voor het berekenen van de gebruikerslicenties. Je vindt meer informatie over het gebruik van de Infolog in het hoofdstuk Inleiding tot MorphX.

5.5 Systeemklassen Systeemklassen worden gebruikt zoals applicatieklassen. Een systeemklasse kan overgeërfd worden zoals eender welke applicatieklasse. Sommige applicatieklassen in het standaardpakket zijn trouwens subklassen van een systeemklasse. Aangezien je de systeemklassen niet kan wijzigen, geeft het overerven van een systeemklasse je wel de mogelijkheid om logica toe te voegen aan een systeemklasse. Klassen zoals SysDictTable en SysReportRun zijn voorbeelden van een subklasse van een systeemklasse. Enkele types van vaak gebruikte systeemklassen worden in de volgende paragrafen beschreven. Object De systeemklasse Object is de basis voor alle klassen in MorphX. Je kan Object vergelijken met de systeemtabel Common. Eender welke klasse kan gedeclareerd worden met behulp van de klasse Object. MorphX valideert de methodes van een object tijdens de compilatie via de klasse Object, en dit is bijzonder handig omdat het type van het object niet geweten moet zijn tot aan de runtime. Dit kan je dan gebruiken om je eigen methode voor een formulier of rapport uit te voeren, want de naam van het betreffende formulier of rapport ken je pas tijdens de runtime of uitvoertijd. Een voorbeeld van hoe je dit kan doen, vind je in het hoofdstuk Formulieren.

MORPHX IT Klassen

© 2007 Steen Andreasen

147

Wijzigingen tijdens Runtime Een van de voornaamste kenmerken van systeemklassen is de mogelijkheid om eender welk knooppunt in de AOT te benaderen. Dit is zeer handig, vooral voor formulieren en rapporten omdat je zo eender welke eigenschap of methode kan overschrijven tijdens de runtime. In dit boek vind je verschillende voorbeelden van het gebruik van systeemklassen voor het benaderen van eender welk object in de AOT om je code meer dynamisch te maken. Systeemklassen die gebruikt worden voor het benaderen van de AOT-knooppunten, hebben doorgaans een naam die begint met de betreffende AOT-knoop, net zoals alle systeemklassen voor formulieren de prefix Forms* hebben. Args Je zal al opgemerkt hebben dat de systeemklasse Args gebruikt wordt als parametervariable. Een uitvoerbare klasse moet Args als parameter in de main() methode hebben. Het doel van Args is het overbrengen van parameters tussen objecten. Als je een uitvoerbare klasse oproept vanuit een formulier, kan je Args gebruiken om een formRun object door te geven, waardoor je de gegevens op het formulier kan doorzoeken of zelfs methodes in het oproepende formulier uitvoeren. Voorbeeld 5: Args Objects gebruikt van MORPHXIT_Classes project

Class, MyClass_Args Menu item action, MyClass_Args Form, CustTable

We gaan een nieuwe menuoptie toevoegen aan het klantenformulier. De klasse opgeroepen door de nieuwe menuoptie, zal het geselecteerde record in het formulier afdrukken. 1. Creëer een nieuwe klasse en geef deze klasse de naam “MyClass_Args”. 2. De klasse heeft de main() methode waardoor ze uitvoerbaar wordt. Args wordt

gebruikt om te kijken of het oproepende object beschikt over de tabel CustTable. Het oproepende formulier kan eender welk formulier zijn zolang het maar gebruik maakt van de CustTable. Als dit zo is, wordt de tabelvariabele CustTable geïnitializeerd met het oproepende record en het resultaat wordt afgedrukt.

static void main(Args _args) { MyClass_Args myClassArgs = new MyClass_Args(); CustTable custTable; ; if (_args.dataset() == tablenum(CustTable)) { custTable = _args.record(); info(strfmt("Customer selected: %1", custTable.name)); } }

MORPHX IT Klassen

148

© 2007 Steen Andreasen

3. Bewaar de klasse en maak een menuoptie aan voor MyClass_Args. 4. Ga naar de Forms knoop in de AOT en zoek het formulier CustTable op. Open de

knoop van het formulier en ga door naar het ontwerp. Versleep de menuoptie voor MyClass_Args naar de knoop Design/ButtonGroup:ButtonGroup en bewaar het formulier.

Bij het uitvoeren van het formulier CustTable, zal je zien dat het formulier een knop heeft met dezelfde naam als de klasse. Er was geen label opgegeven voor de menuoptie, dus wordt de klassenaam getoond. Voer de klasse uit door te klikken op de knop, en de naam van de geselecteerde klant wordt afgedrukt in de Infolog. Dit was een eenvoudig voorbeeld van het gebruik van Args. Je kan Args gebruiken om een rapport op te roepen en filters toe te voegen aan je rapport om enkel geselecteerde records af te drukken. Args wordt vaak gebruikt voor het doorgeven van parameters aan de klasse die de zware taken moet uitvoeren. Omdat een formulier altijd op een client loopt, kan je hierdoor de werklast van een formulier op de AOS plaatsen. Blader door de AOT en je zal een hoop voorbeelden vinden van het gebruik van Args. Funderingsklassen MorphX heeft een groep systeemklassen die gebruikt kunnen worden als alternatief voor een complex datatype. Deze klassen worden funderingsklassen genoemd. Karakteristiek voor deze klassen is dat ze een dynamische lijst van variabelen kunnen beheren. Waarden worden in het geheugen opgeslagen zodat ze snel te gebruiken zijn. Vergeleken met een tijdelijke tabel die gegevens van en naar de harde schijf schrijft, is een funderingsklasse op vlak van performantie beduidend beter. Vijf verschillende funderingsklassen zijn geïmplementeerd in MorphX: Array, List, Map, Set en Struct. De funderingsklassen zijn ontworpen om eender welk type te gebruiken. Zo kan je een funderingsklasse als parameter gebruiken voor een methode of die methode een funderingsklasse als resultaat laten teruggeven. Je vindt nuttige voorbeelden over het gebruik van de funderingsklassen door te zoeken in de AOT en druk dan op F1 om de online helppagina’s te raadplegen. Geoptimaliseerde Recordoperaties Als je een taak hebt die dezelfde records meermaals ophaalt, moet je eens nadenken of je niet beter een recordsorteerlijst zou gebruiken voor betere performantie. Stel je even voor dat je een hoop records moet afdrukken, maar net voor het afdrukken zal een klasse deze records nog wijzigen. Normaal ga je de records eerst ophalen in je klasse, en als je je rapport oproept, gaat dit rapport de records een tweede keer ophalen. Om die tweede ophaling van dezelfde records te voorkomen, kan je gebruik maken van de systeemklasse RecordSortedList. Het samenstellen en afdrukken van verkoopsfacturen is een van de plaatsen in het standaardpakket dat gebruik maakt van RecordSortedList.

MORPHX IT Klassen

© 2007 Steen Andreasen

149

Als je je records niet moet sorteren, of als de opgehaalde gegevens al in de gewenste volgorde zijn, kan je beter de klasse RecordLinkList gebruiken. De systeemklasse RecordInsertList is een alternatief voor het selectiesleutelwoord insert_recordset. Voorbeelden van het gebruik van geoptimaliseerde recordoperaties vind je in de online help. Bestandsafhandeling Het importeren van gegevens vanuit een tekstbestand is heel eenvoudig omdat MorphX een reeks van systeemklassen heeft voor bestandsbeheer. De basisklasse voor bestandsbeheer is de systeemklasse Io. De systeemklassen AsciiIo en CommaIo zijn subklassen van Io voor het werken met tekstbestanden. static void Classes_CommaIo(Args _args) { CommaIo fileOut; FileName fileName = "c:\\Customers.csv"; CustTable custTable; ; #File fileOut = new CommaIo(filename, #io_write); if (fileOut) { while select custTable { fileOut.write(custTable.accountNum, custTable.name, custTable.custGroup, custTable.currency); } } } Het voorbeeld toont het gebruik van CommaIo. CustTable wordt doorlopen en weggeschreven naar een bestand waarvan de waardes door een komma gescheiden zijn (csv staat voor comma separated value). Een macroconstante van de macrobibliotheek File wordt gebruikt voor het bepalen van de schrijftoegang bij het declareren van de CommaIo file. De methode write() in de CommaIo klasse neemt eender welk aantal velden aan als parameters. Merk op dat je een dubbele backslash (\\) moet gebruiken als je verwijst naar het pad in het bestandssysteem. In MorphX moet je het bestand niet sluiten, want dit wordt automatisch gedaan zodra de methode die het bestand heeft geïnitialiseerd, beëindigd wordt. Opmerking: als je complexe datatypes moet exporteren, zoals containers, kan je dit doen met behulp van de systeemklasse BinaryIo. Een alternatief is het exporteren van de gegevens in XML-formaat.

MORPHX IT Klassen

150

© 2007 Steen Andreasen

5.6 Speciaal Gebruik van Klassen In deze sectie vind je voorbeelden van het gebruik van klassen voor speciale gevallen. Verschillende hoofdstukken in dit boek hebben gelijkaardige voorbeelden, zoals voor het integreren met externe producten. Sommige van deze klassen hebben een zeer specifiek gebruik. Het gebruik van COM De Business Connector die ook bekend staat als de COM connector, kan gebruikt worden om Axapta te integreren met een extern product. In dit voorbeeld wordt er contact gemaakt met Microsoft Outlook vanuit Axapta met behulp van COM. Om dit voorbeeld uit te testen, moet je minstens een licentie voor een COM-gebruiker hebben. Elementen gebruikt van MorphxIt_Classes project

Job, Classes_ReadFromOutlook static void Classes_ReadFromOutlook(Args _args) { SysOutlookApplication sysOutlookApplication; SysOutlook_NameSpace sysOutlookNameSpace; SysOutlookMapiFolder sysOutlookMapiFolder; SysOutlook_Folders sysOutlookFolders; SysOutlook_Items collection; COM message; Notes messagebody; ; #sysOutLookComDef sysOutlookApplication = new sysOutlookApplication(); sysOutlookNameSpace = sysOutlookApplication.getNameSpace("MAPI"); sysOutlookNameSpace.logon(); sysOutlookFolders = sysOutlookNameSpace.Folders(); sysOutlookMapiFolder = sysOutlookNameSpace.getDefaultFolder(#OlDefaultFolders_olFolderInbox); collection = sysOutlookMapiFolder.Items(); message = collection.GetFirst(); while (message) { info(message.subject()); message = collection.GetNext(); } } Applicatieklassen waarvan de naam begint met SysOutlook* worden gebruikt om toegang te krijgen tot Micosoft Outlook. De klasse SysOutlookApplication opent een COM connectie en SysOutlookNamesSpace meldt zich aan bij Microsoft Outlook. Je krijgt een waarschuwing te zien in Axapta en je moet de toegang tot je mail client toestaan. De macrobibliotheek SysOutlookComDef bevat een lijst van macroconstantes

MORPHX IT Klassen

© 2007 Steen Andreasen

151

voor het werken met Microsoft Outlook. De standaardfolder Inbox wordt geselecteerd in Microsoft Outlook en het onderwerp van alle mails in de Inbox worden afgedrukt in de Infolog. Dit voorbeeld leest alleen maar in Microsoft Outlook. Je kan natuurlijk ook naar je mail client schrijven of de client synchroniseren met gegevens uit Axapta. Het formulier HRMInterviewTable maakt gebruik van Microsoft Outlook voor het aanmaken van afspraken in de kalender, gebaseerd op gegevens die in Axapta zijn ingetoetst. Opmerking : als je Axapta gaat koppelen aan een extern systeem, kan je gebruik maken van de COM Class Wrapper Wizard om een COM wrapper aan te maken voor je extern systeem. De SysOutlook* klasse werd gemaakt dankzij deze wizard. X++ Compiler MorphX heeft een systeemklasse XppCompiler die gebruikt wordt voor het compileren van X++ code, die als tekstbestand wordt aangeboden. Je vraagt je zeker af wat het nut ervan is omdat je alles in MorphX geïntegreerd hebt, dus wie maalt erom ? Dit kan handig zijn als je je systeembeheerders de optie wil geven om hun eigen validaties in X++ code te schrijven zonder dat ze gebruik moeten maken van de AOT. In een gecontroleerde omgeving kan je je wijzigingen flexibeler doorvoeren door de gebruiker toe te laten kleine wijzigingen aan te brengen. Er zijn plaatsen in Axapta waar de gebruiker enkele hardgecodeerde variabelen heeft die gebruikt kunnen worden in een tekstveld zoals in het formulier TransactionsTexts. En wat als je nu die hardgecodeerde variabelen niet wil gebruiken, maar je eigen variabelen, geschreven in X++, wil opgeven ? Dit kan heel handig zijn en met behulp van de klasse XppCompiler kan je dit ook doen. Elementen gebruikt van MorphxIt_Classes project

Class, Classes_XppCompiler Menu item output, Classes_XppCompiler

We maken een klasse aan met twee methodes. De klasse moet uitvoerbaar zijn. Notes buildFunction() { TextBuffer textBuffer = new TextBuffer(); ; textBuffer.appendText("static void test(Counter _counter, CustTable _custTable)"); textBuffer.appendText("{"); textBuffer.appendText("while select _custTable"); textBuffer.appendText("{"); textBuffer.appendText("info(_custTable.name);"); textBuffer.appendText("_counter++;"); textBuffer.appendText("}"); textBuffer.appendText("info(strfmt(\"Customers printed: %1\", _counter));"); textBuffer.appendText("}"); return textBuffer.getText(); }

MORPHX IT Klassen

152

© 2007 Steen Andreasen

Eerst bouwen we een methode die de functiecode genereert die we aan de klasse XppCompiler gaan aanbieden. Deze functie verwacht twee parameters, een teller en de tabel CustTable. Alle records in CustTable worden doorlopen. De klantnaam en het totaal aantal klanten wordt afgedrukt naar de Infolog. De systeemklasse TextBuffer wordt gebruikt om de string te bouwen die de functiecode bevat. Het toevoegen van strings met het + teken is heel traag. Je moet altijd gebruik maken van TextBuffer als je verschillende strings aan mekaar voegt. static void main(Args _args) { MyClass_XppCompiler myClassXppCompiler = new MyClass_XppCompiler(); xppCompiler compiler = new xppCompiler(); Notes codeString; CustTable custTable; ; codeString = strfmt(myClassXppCompiler.buildFunction()); if (compiler.compile(codeString)) { runbuf(codeString, 0, custTable); } else { info(compiler.errorText()); } } De main() methode declareert de compilerklasse XppCompiler. Als de code in buildFunction() zonder fouten wordt gecompileerd, zal de functie runbuf() de code uitvoeren. De eerste parameter van runbuf() is de codestring en de volgende parameters zijn de parameters die je nodig hebt voor de code vermeld in buildFunction(). Alle variabelen in de code vermeld in buildFunction(), moeten gespecifieerd worden als parameters omdat je geen enkele variabele kan declareren.

5.7 Samenvatting Klassen zijn fundamenteel in MorphX. De applicatieklassen worden gebruikt voor de opbouw van de bedrijfslogica. Systeemklassen koppelen de kernel aan de applicatie. Dit hoofdstuk handelde over de verschillen en het gebruik van de diverse klassen. Nu zou je de kennis moeten hebben over de diverse mogelijkheden van het gebruik van klassen in MorphX. In het volgende hoofdstuk zal je zien hoe je klassen kan gebruiken voor het ontwerpen van de gebruikersinterface.

MORPHX IT Formulieren

© 2007 Steen Andreasen

153

6 Formulieren Formulieren zijn het belangrijkste gedeelte van de gebruikersinterface omdat een formulier de schakel is tussen de applicatiegebruiker en de databank. Als een applicatiegebruiker gegevens toevoegt, wijzigt of verwijdert, is dat door het gebruik van formulieren. De databank kan benaderd worden via de tabelbladeraar, maar je zou dit nooit als een optie mogen beschouwen omdat je zo je gegevens enorm kan verknoeien. Een formulier zal altijd extra validaties doen die de tabelbladeraar niet doet. Bovendien brengt een formulier structuur in de gegevens zodat de gebruiker hier gemakkelijk mee kan omgaan. Een formulier heeft een hoop eigenschappen die handig zijn voor de eindgebruiker zoals het sorteren en filteren van gegevens en het personaliseren van het formulieruitzicht. Sommige van deze eigenschappen die toegankelijk zijn voor de eindgebruiker, zullen in dit hoofdstuk behandeld worden. Vergeet echter niet dat we onze aandacht toespitsen op het gebruik van MorphX voor het bouwen van formulieren.

Figuur 22: Formulieroverzicht

MORPHX IT Formulieren

154

© 2007 Steen Andreasen

6.1 Creatie van Formulieren Een Axaptaformulier wordt aangemaakt in de AOT met behulp van het knooppunt Forms. Een formulier bestaat uit twee delen, met name de query die de gegevens ophaalt en het ontwerp dat de opmaak van het formulier definieert. Het ontwerp wordt gebruikt om de tabellen die in de query vermeld zijn, te tonen. Voorbeeld 1: Mijn eerste formulier Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm Menu item display, MyForm

Om wat ervaring op te doen met het gebruik van formulieren, gaan we een formulier aanmaken zoals getoond wordt in Figuur 22: Formulieroverzicht. Dit is een eenvoudig formulier dat je de basisstappen toont bij het aanmaken van een formulier. In de volgende voorbeelden zullen we aandacht geven aan bepaalde details en later zullen we eigenschappen toevoegen aan het formulier. Voor de eenvoud gebruiken we geen labels in dit voorbeeld, maar onthou wel dat je voor je eigen wijzigingen niet zonder deze labels kan. 1. Selecteer de AOT knoop Forms, klik met de rechtermuisknop en selecteer New

Form. Geef het formulier de naam "MyForm" via het eigenschappenblad. 2. Klik het knooppunt van het nieuwe formulier open zodat je de knooppunten Data

Sources en Designs kan zien. Open een nieuw venster voor de AOT, zoek de tabel SalesTable op en versleep SalesTable naar de formulierknoop Data Sources.

3. Ga naar het knooppunt Designs/Design en open het eigenschappenblad. In de

eigenschap Caption tik je de tekst "Sales orders". Selecteer de gegevensbron SalesTable in de eigenschap TitleDatasource.

4. Selecteer de knoop Designs/Design, klik met de rechtermuisknop en selecteer New

Control/Tab. Ga naar het eigenschappenblad van de tab; in Width selecteer je de waarde "Column width" en in Height selecteer je de waarde "Column height".

5. Voeg een nieuw tabblad toe door met de rechtermuisknop te klikken op de knoop

Designs/Design/[Tab:Tab] en selecteer New Control/TabPage. Geef het nieuwe tabblad de naam "Overview" via het eigenschappenblad. Geef het tabblad een titel door "Overzicht" te tikken in de eigenschap Caption.

6. Voeg een raster toe aan het tabblad door met de rechtermuisknop te klikken op

Designs/Design/[Tab:Tab]/[TabPage:Overview] en selecteer New Control/Grid. 7. Ga naar het nieuwe raster en stel de eigenschappen Width en Height in op "Column

width" en "Column height".

MORPHX IT Formulieren

© 2007 Steen Andreasen

155

8. Klik de gegevensbron SalesTable open en selecteer de velden SalesId, CustAccount, CurrencyCode en SalesStatus. Versleep de velden naar het raster in de vermelde volgorde. Dit kan je het beste doen door met de rechtermuisknop te klikken op de gegevensbron SalesTable, selecteer Open New Window en versleep de velden vanuit het nieuwe AOT-venster.

9. Voeg een tweede tabblad toe door de knoop [Tab:Tab] te selecteren en dan met de

rechtermuisknop te klikken. Geef het nieuwe tabblad de naam “General” en zet de eigenschap Caption in op "Algemeen".

10. Ga naar de gegevensbron SalesTable en zoek het knooppunt Fields van de

gegevensbron op. De veldgroepen die in de data dictionary voor SalesTable zijn opgenomen, staan vlak na de velden vermeld. Selecteer de veldgroepen Identification, Currency, Customer en Status. Versleep de veldgroepen in de vermelde volgorde naar de ontwerpknoop Designs/Design/[Tab:Tab]/[TabPage:General].

11. Bewaar het formulier. Je hebt nu je eerste formulier aangemaakt ! 12. Maak een menuoptie voor het formulier door het formulier MyForm te verslepen

naar het knooppunt Menu Items/Display. Het formulier dat je zojuist hebt gemaakt, is een voorbeeld van een doorsneeformulier in Axapta. Je hebt geen dataconnectie moeten configureren. Kies simpelweg de tabellen die je nodig hebt en versleep de tabellen naar het querydeel van het formulier. Het opstellen van de formulieropmaak is uiterst eenvoudig omdat MorphX zelf de formulierelementen zal positioneren. Versleep je een veld naar de opmaak, dan wordt er automatisch een formulierelement aangemaakt en dit formulierelement wordt geplaatst rekening houdend met de reeds bestaande formulierelementen. Het beste aan deze oefening is wel dat je niet één enkele lijn code hebt moeten schrijven. Je hebt enkel een paar eigenschappen bepaald. Je zal nog vaak genoeg code moeten toevoegen aan je formulieren, maar het voorbeeld toont hoe eenvoudig het is om de tabelgegevens op scherm te laten zien. Ontwerpen worden altijd via automatische positionering bepaald, zelfs voor complexe formulieren, en dit levert je heel wat tijdswinst op. De eigenschappen die we in de opmaak van dit voorbeeld hebben bepaald, worden altijd voor een standaardformulier gedefinieerd. In het knooppunt Design hebben we de tekst voor de titelbalk, bovenaan het formulier, opgegeven. De eigenschap Caption specifieert de formuliernaam en de eigenschap TitleDatasource toont de waarde van de velden opgegeven in de tabeleigenschappen TitleField1 en TitleField2 die na de titel verschijnen. Het instellen van de eigenschappen Width en Height op de waardes “Column width” en “Column height” laat de applicatiegebruikers toe om de grootte van het formulier zelf te bepalen. Width en Height zijn ook ingesteld voor het tabblad en het raster zodat ook het raster kan vergroot of verkleind worden.

MORPHX IT Formulieren

156

© 2007 Steen Andreasen

Je kan velden best altijd toevoegen aan formulieren door gebruik te maken van de veldgroepen, omdat het eenvoudiger wordt te bepalen welke velden op formulieren verschijnen door gebruik te maken van de data dictionary. Een formulierveldgroep heeft de eigenschap AutoDataGroup. Wijzigingen aan de veldgroepen in de data dictionary zullen automatisch doorgetrokken worden naar de formulier- en rapportveldgroepen op voorwaarde dat de eigenschap AutoDataGroup ingesteld is op Yes. Hierdoor kan je heel eenvoudig een nieuw veld toevoegen aan een bestaande veldgroep omdat je enkel de data dictionary moet wijzigen. Voor de eenvoud van het voorbeeld hebben we geen veldgroepen toegevoegd aan het raster. Je kan veldgroepen verslepen naar een raster of je kan een veldgroep opgeven in de eigenschap DataGroup van het raster. Een tabel zal doorgaans een veldgroep Overview hebben waarin alle velden vermeld staan die in een raster getoond moeten worden. Opmerking : Als je meer formuliereigenschappen wil nakijken, moet je eens een kijkje nemen in de formulieren waarvan de naam begint met tutorial*.

6.2 Formulierquery Een query bevat de tabellen die gebruikt worden door een formulier. Het is niet verplicht om een query te definiëren voor je formulier. Je kan de gegevens ook ophalen via selectie-instructies. Maar een query zou toch altijd je eerste keuze moeten zijn voor objecten die gebruikt worden door de applicatiegebruikers omdat een query verschillende mogelijkheden aanbiedt aan deze gebruikers, zoals het filteren, sorteren en afdrukken van gegevens. De structuur van een formulierquery, zoals je die zit in de knooppunten in de AOT, verschilt enigszins van de structuur van queries die je op andere plaatsen in de AOT bouwt zoals rapportqueries. Een rapportquery plaatst de tabellen op hun respectievelijke koppelingsniveau (join level) in de boomstructuur. Een formulierquery plaatst alle tabellen op hetzelfde niveau onder de knoop Data Sources. Als je een tabel toevoegt aan de data source knoop van een formulier, dan voeg je in feite een formulierquery-gegevensbron toe. Dit kan verwarrend overkomen als je afwisselt tussen het ontwikkelen van formulieren en rapporten. Je zal echter snel over de verschillen kunnen heen kijken omdat de eigenschappen dezelfde zijn. Vanuit X++ worden queries op dezelfde manier aangesproken ongeacht het soort object dat deze queries gebruikt. Meer informatie over queries vind je terug in het hoofdstuk Queries. Het koppelen van Gegevensbronnen De meest gebruikte manier om records te filteren alvorens je ze oplijst in een formulier, is door het koppelen van data sources of gegevensbronnen. Dit kan je doen door binnen één formulier de gegevensbronnen met elkaar te koppelen. Wordt een formulier opgeroepen door een gerelateerd formulier, dan kan je de gegevensbronnen koppelen met de gegevensbronnen van het oproepend formulier.

MORPHX IT Formulieren

© 2007 Steen Andreasen

157

Voorbeeld 2: Outer joined formulier Elementen gebruikt van het MORPHXIT_Forms project

Form, MyForm_OuterJoin Menu item display, MyForm_OuterJoin

In dit voorbeeld gaan we het formulier aangemaakt in voorbeeld 1, uitbreiden zodat ook gegevens van de werknemerstabel worden getoond. In dit voorbeeld worden er geen labels aangemaakt. 1. Kopieer het formulier MyForm en geef het de naam “MyForm_OuterJoin”. 2. Klik de Data Sources knoop van het nieuwe formulier open, open een ander

venster van de AOT en zoek de tabel EmplTable. Versleep EmplTable naar de Data Sources knoop.

3. Open het eigenschappenblad van Data Sources/EmplTable. Kies SalesTable in de

eigenschap JoinSource. Stel de eigenschap LinkType in op OuterJoin. 4. Selecteer het veld SalesTaker van de SalesTable en versleep het veld naar

Designs/Design/[Tab:Tab]/[TabPage:Overview]/[Grid:Grid]. 5. Herhaal stap 4 om het veld Name van EmplTable aan het raster toe te voegen. 6. Selecteer de veldgroep Name van EmplTable en versleep de veldgroep naar het

tabblad General. 7. Ga naar het tabblad General en stel de eigenschap Columns in op 2. 8. Bewaar het formulier en maak een menuoptie aan voor je nieuwe formulier. Het formulier MyForm_OuterJoin heeft twee bijkomende velden in het raster waarin je de werknemersnummer en werknemersnaam ziet met de bijbehorende verkoops-ontvangsten. De werknemersnaam wordt opgehaald uit de werknemerstabel. Je ziet nog altijd hetzelfde aantal records, omdat de gegevensbronnen aan elkaar gekoppeld zijn via een outer join. Als we een inner join hadden gebruikt, zouden we enkel die verkoopsorders zien waarvoor ook verkoopsontvangsten genoteerd waren. Wijzig de eigenschap LinkType van de gegevensbron EmplTable om te zien hoe de gegevens worden opgehaald. Als je gegevensbronnen aan je formulier toevoegt door een tabel van de data dictionary te verslepen, zal de naam van de gegevensbron gelijk zijn aan de tabelnaam. Je kan niettemin de naam van de gegevensbron wijzigen. Als je eenzelfde tabel meer dan eens in de query vermeldt, moeten beide gegevensbronnen natuurlijk een andere naam hebben. Dit is hetzelfde als het declareren van twee tabelvariabelen van dezelfde tabel in X++.

MORPHX IT Formulieren

158

© 2007 Steen Andreasen

De eigenschap Columns die we gewijzigd hebben voor het tabblad General, verdeelt de veldgroepen in 2 kolommen. Normaal worden alle veldgroepen getoond in één kolom, en dus kan je best het aantal kolommen aanpassen als je formulier meerdere veldgroepen bevat. Opmerking : Als je niet zeker bent hoe twee tabellen aan elkaar gerelateerd zijn, kan je een AOT query maken met de twee tabellen aan elkaar gekoppeld. In de AOT query worden de relaties automatisch getoond door de eigenschap Relations van de gegevensbron op “true” in te stellen. Het koppelen van tabellen om gegevens uit meerdere tabellen op te halen, is heel performantievriendelijk. Deze werkwijze is aangewezen als er een 1-op-1-relatie bestaat. De applicatiegebruikers zullen niet zien of de getoonde gegevens uit één of meerdere tabellen komen. En meermaals zal de gebruiker zich er ook niet van bewust zijn uit hoeveel tabellen de gegevens komen. In het voorbeeld tonen we gegevens uit de SalesTable en EmplTable in het raster, en in dit geval is de koppelwijze ofwel inner join of outer join. Er zijn nog 2 andere types van join : de exist join en de not exist join. Een exist join toont enkel gegevens uit tabel A die ook een overeenkomstig record in tabel B hebben. De not exist join is het tegenovergestelde : die join toont enkel gegevens uit tabel A die geen overeenkomstig record in tabel B hebben. Maar beide joins hebben wel één ding gemeenschappelijk : ze tonen geen gegevens uit de gekoppelde tabel. Formulierlinktypes De koppelwijze of join mode van een formulier wordt ingesteld via de eigenschap van de gegevensbron met de naam LinkType. Buiten de standaardkoppelwijzes hebben formulieren nog 3 bijkomende koppelwijzes om formulieren meer gebruiksvriendelijk te maken : Passive, Delay en Active. Bij het gebruik van een inner join of een outer join worden alle records van de gekoppelde gegevensbron altijd onmiddellijk opgehaald. Dit is nodig als je meerdere gegevensbronnen in een raster samenbrengt. Als gegevens van de gekoppelde gegevensbron niet in hetzelfde raster voorkomen, kan je beter een van de speciale koppelwijzes gebruiken. De standaardwaarde van LinkType is Delayed. Dit is de meest voorkomende koppelwijze wanneer gekoppelde gegevensbronnen niet in hetzelfde raster worden opgelijst. Met delayed stellen we een vertraging in voor er gegevens uit de gekoppelde gegevensbron worden opgehaald. Dit is bijzonder nuttig en versnelt het rollen doorheen de records van een raster, waardoor het formulier gebruiksvriendelijker wordt. We spreken in feite over milliseconden en de applicatiegebruiker zal nauwelijks weten dat de gegevens met vertraging worden opgehaald. Het formulier SalesTable maakt gebruik van deze techniek. Opmerking : Het gebruik van join modes Delayed, Active en Passive zorgt ervoor dat de gegevensbronnen gekoppeld worden als een outer join. Alle records van de gekoppelde gegevensbron worden opgehaald. Het LinkType Active is gelijkaardig aan Delayed. Het enige verschil is dat Active geen vertraging kent bij het ophalen van de gegevens van de gekoppelde gegevensbron. Active wordt niet vaak gebruikt omdat het de applicatiegebruikers niet dezelfde flexibiliteit geeft als Delayed. Passive is het tegenovergestelde van Active. Waar Active

MORPHX IT Formulieren

© 2007 Steen Andreasen

159

altijd gegevens ophaalt van de gekoppelde gegevensbron, zal Passive geen gegevens van de gekoppelde gegevensbron ophalen. Passive kan je gebruiken als je via X++ wilt controleren wanneer gegevens worden opgehaald. Het koppelen van formulieren Relaties gedefinieerd in de data dictionary worden automatisch gebruikt voor het koppelen van formulieren. Stel dat je een nieuw formulier aanmaakt dat opgeroepen moet worden vanuit het klantenformulier om de klantgegevens te tonen. Als de tabel voor je nieuwe formulier een veld heeft dat gebruik maakt van het uitgebreide datatype CustAccount, dan zal je nieuw formulier automatisch gekoppeld worden aan het klantenformulier. Het uitgebreide datatype CustAccount heeft een relatie met de tabel CustTable dat gebruikt wordt door het klantenformulier. Als je je nieuwe formulier oproept vanuit het klantenformulier, dan wordt het uitgebreide datatype gebruikt door MorphX om een koppeling te maken tussen de twee formulieren. Dit soort koppelingen noemen we dynalinks. Open het klantenformulier CustTable en open daarna het transactieformulier door te klikken op de knop Transactions in het klantenformulier. Als je doorheen de klantenrecords van het klantenformulier bladert, haalt het transactieformulier automatisch de bijbehorende transacties op van de betreffende klant. Dit gebeurt zonder dat je enige code moet schrijven en is het standaardgedrag als je formulieren creëert die verwijzen naar gerelateerde tabellen. In sommige gevallen zal je afwillen van de dynalinks. Je moet misschien alle records tonen in het gekoppelde formulier of je wilt een alternatieve koppeling tussen de twee formulieren. void init() { super(); this.query().dataSourceNo(1).clearDynalinks(); criteriaOpen = this.query().dataSourceNo(1).addRange(fieldnum(CustTrans,Closed)); custTransDetails = new CustTransDetails(custTrans); } Als je de dynalinks wil inactiveren voor het formulier van de klantentransacties, zoek je het formulier CustTrans in de AOT op. Wijzig de methode init() die je vindt onder Data Sources/CustTrans. Na de oproep van super() in init(), voeg je een lijn toe om de dynalinks uit te schakelen zoals je kan zien in de code hierboven. Als je nu het transactieformulier opent vanuit het klantenformulier, is er geen filtering meer voorzien en worden alle transactierecords getoond. De reden om een dynalink uit te schakelen, kan zijn dat je alle records wil tonen in het gekoppelde formulier omdat je dit formulier wil gebruiken voor zoekopdrachten. Dit formulier kan je dan plaatsen achter een lookup-knop.

MORPHX IT Formulieren

160

© 2007 Steen Andreasen

Maar je kan ook je eigen dynalink maken in plaats van de dynalink te gebruiken die gebaseerd is op de relaties in de data dictionary. Wijzig de init() methode zoals hieronder staat aangegeven : void init() { super(); this.query().dataSourceNo(1).clearDynalinks(); this.query().dataSourceTable(tablenum(CustTrans)).addDynalink( fieldnum(CustTrans, CurrencyCode), element.args().record(), fieldnum(CustTable, Currency)); criteriaOpen = this.query().dataSourceNo(1).addRange(fieldnum(CustTrans,Closed)); custTransDetails = new CustTransDetails(custTrans); } Na de oproep van clearDynaLinks() wordt een nieuwe dynalink opgebouwd via de addDynalink() methode. Je definieert het veld in de opgeroepen gegevensbron dat je wil koppelen aan de oproepende gegevensbron en het oproepende record. Je maakt gebruik van element.args().record() om het oproepende record door te geven. Door het toevoegen van deze dynalink worden de klantentransacties nu gefilterd op basis van de klantenvalutacode in plaats van het klantnummer. Dit heeft natuurlijk niet veel zin in een echte productieomgeving. Maar het geeft wel aan hoe je de standaard dynalinks kan overschrijven. Opmerking: Als je niet zeker weet welke velden de dynalink gebruikt voor het filteren, kijk dan eens naar de titelbalk van het formulier. De dynalink velden staan opgelijst als het laatste deel van de titel na de formuliernaam en de velden title1 en title2. Instellen van toegang Algemene toegangsbeperkingen voor tabellen en velden worden gedefinieerd in de data dictionary. Beperkingen die specifiek zijn voor een formulier moet je in de gegevensbron van dit formulier vastleggen. Je kan de instelling van de data dictionary niet overschrijven, maar je kan wel bijkomende toegangsbeperkingen opleggen. Toegangsbeperkingen kan je instellen zowel op het niveau van de gegevensbron als op veldniveau binnen de gegevensbron. Voor een tabel kan je de maximale toegangstoelating definiëren met behulp van de eigenschappen AllowEdit, AllowCreate en AllowDelete voor de gegevensbron. Binnen de gegevensbron kan je op veldniveau beperkingen opleggen om aan te geven wanneer een veld zichtbaar of editeerbaar moet zijn. Beperkingen kan je ook koppelen aan formulierelementen binnen een formulierontwerp. Maar het is aangewezen om de veldbeperkingen binnen een gegevensbron te regelen, omdat je één enkel veld aan meerdere formulierelementen in hetzelfde formulier kan koppelen. We geven er ook de voorkeur aan om geen code in het ontwerp te plaatsen en geen standaardinstellingen in het ontwerp te wijzigen. Programmeren in MorphX houdt trouwens in dat je best geen code in formulieren plaatst, waardoor je eenvoudiger de gebruikersinterface kan wijzigen. Toegangsbeperkingen plaatsen op een formulierelement wordt dan ook afgeraden

MORPHX IT Formulieren

© 2007 Steen Andreasen

161

temeer omdat deze beperkingen makkelijk in een ander formulier omzeild kunnen worden. Sommige toegangsbeperkingen worden ingesteld via de eigenschappen. Een formulier als CustTrans dat klantentransacties toont, zou niet editeerbaar mogen zijn. Je zal vaak beperkingen moeten opleggen afhankelijk van het object dat het formulier oproept of afhankelijk van de waarde van bepaalde records in het formulier. Voorbeeld 3: definiëren van toegangsbeperking Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_SettingAccess Menu item display, MyForm_SettingAccess

Dit voorbeeld toont hoe je toegangsbeperking kan opleggen voor de gegevensbron van een formulier met behulp van X++. Beperkingen opleggen in X++ is normaal gebaseerd op een voorwaarde. Om de code zo eenvoudig mogelijk te houden, hebben we hieronder geen voorwaardes toegevoegd. Kopieer het formulier MyForm en geef het nieuwe formulier de naam “MyForm_SettingAccess”. Overschrijf de init() methode en voeg het volgende toe : public void init() { super(); salesTable_ds.allowCreate(false); salesTable_ds.object(fieldnum(salesTable, CurrencyCode)).allowEdit(false); salesTable_ds.object(fieldnum(salesTable, SalesStatus)).visible(false); } Bewaar het formulier en maak een menuoptie voor dit formulier aan. Bij het oproepen van het formulier, zal je geen nieuwe records kunnen toevoegen. Het veld CurrencyCode kan niet gewijzigd worden en het veld SalesStatus wordt niet getoond in het ontwerp. Eigenschappen en methodes van een gegevensbron kunnen aangesproken worden vanuit X++ door het gebruik van de naam van de gegevensbron gevolgd door de letters _ds. Als je verwijst naar de gegevensbronvelden, moet je de gegevensbronmethode object() gebruiken. Gebruik het tabelveldidentificatienummer als parameter voor object() en je zal naar de gegevensbronvelden kunnen verwijzen. In het standaardpakket vind je verschillende voorbeelden waar toegangsbeperkingen ingesteld worden met behulp van formulierelementen in het ontwerp in plaats van gegevensbronvelden. Dit is zeker niet optimaal, maar de mogelijkheid om gegevensbronvelden te gebruiken is pas in één van de laatste versies van Axapta geïntroduceerd. En dus zal je hier en daar nog de oude werkwijze met formulierelementen vinden.

MORPHX IT Formulieren

162

© 2007 Steen Andreasen

6.3 Ontwerp Sommige ontwikkelomgevingen laten je veel tijd verliezen met het opmaken van formulieren, omdat je alle formulierelementen manueel moet toevoegen. Dit is niet het geval met MorphX. Het opstellen van een formulierontwerp in MorphX is eerder rechttoe rechtaan. De ontwikkelstandaarden raden je aan om geen code toe te voegen aan je ontwerp en indien mogelijk te werken met het automatisch positioneren van de formulierelementen. Dit versnelt de ontwikkeling. Als je het querydeel van het formulier hebt gemaakt, kan je het ontwerp grotendeels klaarmaken door het verslepen van veldgroepen van de formuliergegevensbronnen naar de opmaak van het ontwerp. Natuurlijk zal je ook gevallen hebben waarbij je toch code nodig hebt in je ontwerp. En soms moet je de eigenschappen van de formulierelementen enigszins aanpassen. Dit kan je doen met enkele instellingen. Het automatisch positioneren van de formulierelementen in het ontwerp is op verschillende vlakken handig. Als je een veld of veldgroep toevoegt in het midden van een rij velden, zullen de nakomende formulierelementen netjes herschikt worden. Als een veld wordt verwijderd of toegevoegd aan een veldgroep in een tabel, dan wordt de overeenkomstige veldgroep in het ontwerp automatisch aangepast. Stel je een veld in het ontwerp in als onzichtbaar, dan worden de daaropvolgende velden ook herschikt. En als geen enkel veld van een veldgroep getoond moet worden, dan wordt de veldgroep ook automatisch onzichtbaar. Het gebruik van automatische instellingen maakt het natuurlijk ook moeilijker om een speciaal formulierontwerp te creëren. Je kan deze automatische instellingen ook uitschakelen, en ieder formulierelement zelf in het ontwerp plaatsen. Het idee achter de automatische instellingen is dat je eenvoudig en snel formulieren moet kunnen ontwerpen, en dat je een reeks formulieren moet kunnen maken met eenzelfde uitzicht. Creatie van ontwerp Je kan best een aantal basisregels volgen om je formulieren een gelijkaardig uitzicht te geven. Een typisch formulier zie je in figuur 23 : Standaardformulier. Een formulier wordt doorgaans verdeeld in tabbladen, waar het eerste tabblad (vaak Overview genoemd), het overzicht biedt van de beschikbare records. De velden getoond op dit eerste tabblad, kunnen best verzameld zijn in een tabelveldgroep. Als een veld verplicht ingevuld moet worden, wat dus inhoudt dat het altijd een waarde moet bevatten, dan zou dit veld ook op het eerste tabblad getoond moeten worden. Op de volgende tabbladen kan je de overige velden tonen, gegroepeerd per tabblad met een logische naam. Velden die je niet op een samenhangende manier kan groeperen, worden vaak getoond op het tweede tabblad met de naam General of Algemeen. Als je later velden gaat toevoegen, is het aangewezen ze toe te voegen via de tabelveldgroepen. Het is een ontwikkelstandaard om de velden van het eerste tabblad, ook op één van de volgende tabbladen te tonen. Het eerste tabblad is vaak een lijst van de belangrijkste velden van een tabel, en gezien deze velden ook deel uitmaken van een andere veldgroep, is het logisch dat deze velden tweemaal op het formulier verschijnen. Zelfs als je een eenvoudig formulier met een handjevol velden aanmaakt, kan je best de bovenstaande standaarden volgen. Je spaart een beetje tijd uit door alle velden van de

MORPHX IT Formulieren

© 2007 Steen Andreasen

163

gegevensbron op één enkel tabblad te tonen. Maar je formulier ziet er wel heel anders uit en breekt met de standaardopmaak van de overgrote meerderheid van formulieren. Knoppen worden doorgaans aan de rechterzijde van het formulier toegevoegd. Bij voorkeur koppel je menuopties aan de knoppen omdat menuopties automatisch worden gevalideerd door de instellingen van de veiligheid- en configuratiesleutels. Meer informatie over het aanmaken van menuopties vind je in het hoofdstuk Menus en Menuopties.

Figuur 23: Standaardformulier Het ontwerp van een formulier wordt aangemaakt onder het knooppunt Designs. Het meervoud in de naam van dit knooppunt (“designs”) mag echter niet tot verwarring leiden : een formulier heeft slechts één enkel ontwerp. Je kan het formulieronwerp bekijken door de design-knoop te selecteren en dan dubbel te klikken. Dit opent het formulier in ontwerpmode zoals je kan zien in figuur 24 : Formulierediteermode. De formulierediteermode wordt gebruikt voor het aanmaken en wijzigen van een formulierontwerp. Maar gebruik dit enkel om een grafisch overzicht te krijgen van je ontwerp, want de tool is nogal rudimentair. Gebruik liever de formulierknooppunten voor het maken en wijzigen van je formulierontwerp.

MORPHX IT Formulieren

164

© 2007 Steen Andreasen

Figuur 24: Formulierediteermode Als je het formulierontwerp opent in editeermode, wordt een nutsbalk of toolbar geopend naast je formulier. Je kan deze nutsbalk gebruiken om een formulierelement te selecteren en vervolgens op je formulier te klikken om het betreffende element te positioneren. Wil je een formulierelement van het ontwerp verwijderen, klik dan op het element en druk op de delete-toets. Het bekijken van een formulierontwerp in editeermode is handig om de diverse onderdelen van het ontwerp te leren kennen, omdat je in het ontwerp kan navigeren. En het is zeker nuttig om een overzicht te krijgen van een formulier met een hele hoop formulierelementen, zoals het SalesTable-formulier. Klik je op een formulierelement in het ontwerp, dan activeer je het eigenschappenblad van het geselecteerde element. Dit kan een snelle manier zijn om de eigenschappen van een element op te roepen, tot je vertrouwd bent met de diverse formulierelementen. Formulierelementen in Ontwerp Het aanmaken van een ontwerp doe je door formulierelementen aan het ontwerp toe te voegen, hetzij door de gegevensbronvelden en -veldgroepen te verslepen naar het ontwerp, hetzij door manueel een formulierelement toe te voegen. Als je vanuit de gegevensbronnen versleept, zal een formulierelement automatisch worden gecreëerd met de eigenschappen die overeenstemmen met de betreffende gegevensbron. Dit is de snelste weg om je ontwerp op te bouwen en het garandeert je dat je formulierelementen correct zijn aangemaakt. Formulierelementen kunnen ook manueel worden aangemaakt. Dit doe je doorgaans voor elementen die niet gebonden zijn aan een gegevensbron. Merk op dat velden en veldgroepen niet versleept kunnen worden vanuit een tabel zoals in een rapportontwerp.

MORPHX IT Formulieren

© 2007 Steen Andreasen

165

Zorg altijd voor een goede benaming van je formulierelementen. Het manueel aanmaken van een element zorgt ervoor dat de naam van het element bestaat uit het elementtype gevolgd door een volgnummer. Het aanmaken van een ontwerp waar de formulierelementen hun standaardnaam hebben, zorgt ervoor dat het ontwerp moeilijker te begrijpen is, omdat je alle knooppunten moet openen om een overzicht te krijgen. Bekijk even figuur 25: Slechte benaming van formulierelementen. En hier gaat het dan nog om een eenvoudig formulier.

Figuur 25: Slechte benaming van formulierelementen Labels gebruikt voor velden en veldgroepen worden gespecifieerd in de uitgebreide datatypes, basisenums of in de tabel. Je moet enkel labels voorzien voor formulierelementen die velden en veldgroepen organiseren zoals tabbladen en rasters. Als je een ander label nodig hebt voor een veld of veldgroep, kan je het standaardlabel overschrijven door een ander label voor dit formulierelement op te geven. Dit wordt echter niet aangeraden omdat het de automatische instellingen inactiveert en het onderhoud van je formulier meer tijdrovend maakt. Een overzicht van de beschikbare elementen voor een formulierontwerp vind je terug in figuur 26: Formulierelementen. Naam Omschrijving ActiveX

Gebruikt om een ActiveX element in je formulier te integreren. Een voorbeeld zie je in het formulier KMKnowledgeAnalogMeter.

Animate

Gebruikt voor het afspelen van filmfragmenten.

Button

Een eenvoudige knop. Gebruik de MenuItemButton waar mogelijk, omdat de methode clicked() van dit formulierelement overschreven moet worden om een taak uit te voeren.

ButtonGroup

Een buttonGroup wordt gebruikt voor het groeperen van eender welke soort formulierknoppen.

CheckBox

Geeft een true of false waarde terug. Wordt vaak gebruikt om de waarde in te stellen van een NoYes enum.

ComboBox

Het meest gebruikte formulierelement om de waardes van een enum te tonen. Toont de enum met de beschikbare waardes in

MORPHX IT Formulieren

166

© 2007 Steen Andreasen

Naam Omschrijving een neerklaplijst.

CommandButton

Een speciaal type van knop. Dit formulierelement heeft een eigenschap genoemd Command, waar de actie van de knop wordt gedefinieerd. Normaal wordt dit element gebruikt voor het toevoegen van Ok, Cancel en Apply knoppen aan een formulier. Zie verder in het formulier ReqTransPoCreate.

DateEdit

Gebruikt voor het tonen van datums. De datums worden geformatteerd volgens de Windows regionale instellingen.

Grid

Gebruikt om de beschikbare records in het formulier op te lijsten. Een raster wordt doorgaans toegevoegd aan het eerste tabblad. De eigenschappen Width en Height zouden altijd ingesteld moeten worden op Column width en Column Height waardoor het mogelijk wordt om het raster te herschalen.

Group

Een Group wordt gebruikt voor het tonen van tabelveldgroepen. Je kan een Group ook gebruiken om andere formulierelementen van het type Group in kolommen te schikken.

HTML

Gebruikt om HTML inhoud aan een formulier toe te voegen. Een voorbeeld zie je in het formulier ForecastItemAllocationDefaultDataWizard.

IntEdit

Gebruikt voor gehele getallen.

ListBox

Een eenvoudige versie van de ListView. Kan gebruikt worden voor enums. Dit element wordt zelden gebruikt.

ListView

Vaak gebruikt als een vast aantal waardes geselecteerd kan worden. De ene ListView bevat alle mogelijke waardes en de andere ListView bevat de gekozen waardes. De gegevens moeten manueel worden ingevuld in een ListView. Zie formulier KMActionType.

MenuButton

Een MenuButton wordt gebruikt voor het organiseren van knoppen in submenus. Validaties voor het activeren van de knoppen in een submenu worden vaak geplaatst in de MenuButton clicked() methode.

MenuItemButton

Gebruikt voor het aanmaken van een knop voor een menuoptie. MenuItemButton is het verkozen knoptype omdat je geen code nodig hebt voor het oproepen van de bijbehorende menuoptie. De eigenschappen MenuItemType en MenuItemName geven het object aan dat uitgevoerd moet worden. Wordt aangemaakt door het verslepen van een menuoptie naar het formulierontwerp.

Progress

Voortgangsbalken worden normaal aangemaakt via de applicatieklasse SysOperationProgress. Dit formulierelement kan gebruikt worden om een voortgangsbalk te integreren in het formulier. Gebruik de Progress methode pos() om de teller bij te werken

MORPHX IT Formulieren

© 2007 Steen Andreasen

167

Naam Omschrijving

RadioButton

Alternatief voor de ComboBox voor het tonen van enums. Elke waarde van de enum wordt getoond als een selecteerbare knop.

RealEdit

Gebruikt voor decimale getallen.

Separator

Een formulierelement gebruikt door de menuButton. Scheidt de knoppen in een submenu door een horizontale lijn toe te voegen.

StaticEdit

Toont een niet-editeerbare tekst. Het element kan gekoppeld worden aan een gegevensbronveld. Normaal wordt de tekst voor dit element ingesteld in het eigenschappenblad of via X++. Merk op dat dit formulierelement niet gebruikt kan worden in een raster.

StringEdit

Gebruikt voor tekstwaardes. Indien gebruikt voor memovelden, kan men de eigenschappen Width en Height best gelijkstellen aan Column width en Column height. Hierdoor kan het memoveld herschaald worden.

Tab Dit element staat op het hoogste niveau van een standaardformulierontwerp. Een tab wordt gebruikt voor het toevoegen van tabbladen. De eigenschappen Width en Height kunnen best altijd gelijkgesteld worden aan Column width en Column Height waardoor de gebruiker de tabbladen van een tab kan herschalen.

Table

Meestal niet gebruikt. Kan meerdere records tonen in rastervorm. De gegevens voor dit formulierelement moeten manueel worden ingevuld.

TabPage

Tabbladen worden toegevoegd aan een tab. Een tabblad wordt gebruikt voor het bewaren van formulierelementen die gegevens bevatten. Het formulierontwerp zal doorgaans één of meerdere tabbladen omvatten.

TimeEdit

Wordt gebruikt voor het tonen van de tijd. De tijd wordt geformatteerd volgens de Windows regionale instellingen.

Tree

Gebruikt voor het tonen van records in een boomstructuur. De logica voor het beheren van de boom zit in een applicatieklasse. Zie formulier ProjTable.

Window

Wordt gebruikt voor het toevoegen van een bitmap aan een formulier. Zie formulier KMAction.

Figuur 26: Formulierelementen

MORPHX IT Formulieren

168

© 2007 Steen Andreasen

Raster Het raster heeft verschillende kenmerken waarmee je het ontwerp gebruiksvriendelijker kan maken. Je kan een aantal records in een raster markeren op dezelfde manier dat je bestanden in het Windows file system kan markeren. Dit is handig als je een bepaalde taak wil uitvoeren op een aantal records zoals het oproepen van een klasse voor het bijwerken van de geselecteerde records. Een vinkvakje wordt vaak toegevoegd aan het raster voor het beheren van de gemarkeerde records. Als je geen informatie wil bijhouden over de records die gemarkeerd zijn, kan je beter de markeringsoptie gebruiken. Voorbeeld 4: Multimark Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_MultiMark Menu item display. MyForm_MultiMark

Dit voorbeeld toont hoe je gemarkeerde records in een raster kan doorlopen. 1. Kopieer het formulier MyForm en geef het de naam “MyForm_MultiMark”. 2. Ga naar het knooppunt Designs/Design, klik met de rechtermuisknop en selecteer

New Control/ButtonGroup. 3. Voeg een knop toe aan de ButtonGroup door te klikken met de rechtermuisknop

op ButtonGroup en selecteer New Control/Button. Ga naar het eigenschappenblad van de nieuwe knop en verander de naam van het formulierelement door “MultiMarkTestButton” in de eigenschap Name in te tikken. Stel de eigenschap MultiMark in op Yes. In de eigenschap Text tik je “Show marked” in.

4. Overschrijf de methode clicked() van de knop

[ButtonGroup:ButtonGroup]/Button:MultiMarkTextButton/Methods en voeg de volgende code toe :

void clicked() { salesTable salesTableMarked; ; super(); if (salesTable_ds.anyMarked()) { salesTableMarked = salesTable_ds.getFirst(1,false); while (salesTableMarked) { info(salesTableMarked.salesId); salesTableMarked = salesTable_ds.getNext(); } } }

MORPHX IT Formulieren

© 2007 Steen Andreasen

169

5. Bewaar het formulier en creëer een menuoptie. Open het nieuwe formulier MyForm_MultiMark. Markeer enkele records in het raster en klik op de knop Show marked. De waarde van het verkoopsnummer voor de geselecteerde records zal afgedrukt worden in de Infolog. In een productieomgeving zou je via een menuoptieknop een run base klasse oproepen om de records te bewerken. Enkel knoppen met de eigenschap MultiSelect ingesteld op Yes kunnen gebruikt worden als je meerdere records markeert. Dit is handig omdat je niet voor iedere menuoptie moet nagaan of het met meerdere records wordt opgeroepen. Het formulier SalesTable in het standaardpakket maakt gebruik van multimarkering. Opmerking : Een raster laadt enkel delen van het record die beschikbaar zijn in het formulier. Dit betekent dat de vertikale rolbalk gepositioneerd is afhankelijk van het aantal records in het cache-geheugen. Dit geeft een betere performantie. Een ander aardigheidje van een raster is dat je gemarkeerde records kan kopiëren en in Microsoft Excel kan plakken. Maar enkel records die gekoppeld zijn aan een gegevensbronveld kunnen op deze manier gekopieerd worden. Zowel records als velden kunnen gekleurd worden weergegeven in het raster. Het gebruik van kleuren maakt je formulieren gebruiksvriendelijker omdat je zo het opvolgen van de waardes van bepaalde velden vereenvoudigt. Voorbeelden van het gebruik van kleuren in formulieren vind je terug in de sectie Speciale Formulieren. Boomstructuur Als records in je gegevensbron hiërarchisch gerelateerd zijn, moet je eens denken over het gebruik van een boomstructuur. Dit geeft de applicatiegebruiker een logisch zicht op de records. Formulieren zoals ProjTable en HRMOrganization maken gebruik van een boomstructuur. Het inbouwen van een boomstructuur in een formulier kan wel eens lastig zijn, zeker omdat het gebruik ervan vereist dat je code toevoegt om het te doen werken. Het is veel eenvoudiger om een bestaand formulier met boomstructuur te kopiëren dan er zelf een te bouwen. Eén klasse wordt gebruikt om de gegevens in de boom te beheren. Je vindt twee versies van deze klasse in het standaard pakket. De eenvoudigste klasse genaamd FormTreeDatasource wordt gebruikt in het formulier ProjTable. Het formulier HRMOrganization gebruikt daarentegen een meer geavanceerde klasse CCFormTreeDatasource. Deze geavanceerde versie ondersteunt onderandere het gebruik van verslepen en plaatsen (“drag and drop”). Een formulier met boomstructuur is vaak zeer groot en als de applicatiegebruiker de grootte van het formulier wijzigt, kan het gebeuren dat de boomstructuur niet meer naar behoren werkt. Dit kan je opvangen door een scheidslijn te voorzien tussen de boomstructuur en de andere formulierelementen. Je kan dan op de scheidslijn klikken en ofwel de boomstructuur of de andere formulierelementen herschalen. Een scheidslijn of splitter is geen gewoon formulierelement omdat het gebouwd wordt met behulp van een klasse. Wil je zo’n scheidslijn in je formulier, kopieer dan code van een formulier dat al zo’n scheidslijn heeft. In figuur 27 : Gebruik van een scheidslijn zie je waar een

MORPHX IT Formulieren

170

© 2007 Steen Andreasen

scheidslijn is toegevoegd aan het formulier HRMOrganization. Je gebruikt een veldgroep voor het aanmaken van de scheidslijn en overschrijft de methodes mouseUp(), mouseMove() en mouseDown(). Kijk wel alle eigenschappen na van de veldgroep SplitControl omdat je enkele eigenschappen zal moeten wijzigen.

Figuur 27: Gebruik van een scheidslijn Display en Edit Modificeerders MorphX heeft twee types van speciale methodemodificeerders die gebruikt worden door de gebruikersinterface. Deze worden gebruikt als je een waarde van een veld wil tonen dat geen deel uitmaakt van de query. Denk hierbij aan de situatie waarin je een complexe query moet bouwen om te kunnen koppelen met een bepaald tabelveld, of waarin er geen enkele relatie bestaat met het tabelveld. Om de waarde van dit veld op een eenvoudige manier op te halen, worden de modificeerders display en edit gebruikt. Deze twee methodetypes worden normaal omschreven als displaymethodes en editmethodes. Display- en editmethodes kunnen steeds gebruikt worden om de waarde van een type terug te geven. De modificeerder display en edit geven aan dat de methode bedoeld is om gebruikt te worden door de gebruikersinterface. Voor je gebruik wil maken van een display- of editmethode, moet je wel weten dat je de gegevens in een raster niet kan sorteren op basis van formulierelementen die gebruik maken van een display- of editmethode. Applicatiegebruikers kunnen records in een raster sorteren door te klikken op de labelnaam van het veld waarop men wil sorteren. Maar een applicatiegebruiker zal natuurlijk niet kunnen weten of een formulierelement gekoppeld is aan een gegevensbron of gebaseerd op een display- of editmethode.

MORPHX IT Formulieren

© 2007 Steen Andreasen

171

Voorbeeld 5: Display en edit Elementen gebruikt van het MORPHXIT_Forms project

Table, SalesTable Form, MyForm_DisplayEdit Menu item display, MyForm_DisplayEdit

Dit voorbeeld toont hoe je moet omgaan met de gebruikersinterfacemodificeerders, display en edit. MyForm wordt uitgebreid zodat het ook beschikt over formulierelementen die zowel display- als editmodificeerders gebruiken. 1. Kopieer het formulier MyForm en geef de kopie de naam

“MyForm_DisplayEdit”. 2. Open een ander venster van de AOT en ga naar de methodes van de tabel

SalesTable. 3. Versleep de methode customerName() naar de tabelveldgroep Customer. 4. Creëer een nieuwe methode in SalesTable met de volgende code: display LineAmount salesTotal(SalesTable _salesTable) { return (select sum(lineAmount) from salesLine where salesLine.salesId == _salesTable.salesId).lineAmount; } 5. Zorg ervoor dat je de wijzigingen aan SalesTable bewaart en versleep de nieuwe

methode salesTotal() naar het formulier MyForm_DisplayEdit in het ontwerp op Designs/Design/[Tab:Tab]/[TabPage:Overview]/[Grid:Grid]. Positioneer het formulierelement dat het laatste in het raster staat. Open het eigenschappenblad van het nieuwe formulierelement en selecteer SalesTable als waarde voor de eigenschap DataSource.

6. Ga terug naar de tabel SalesTable en voeg een nieuwe methode toe met de

volgende code: edit CustName editCustomerName(boolean _set, CustName _name) { CustName name = _name; CustTable custTable; if (_set) { if (name) { ttsbegin; custTable = CustTable::find(this.custAccount, true); custTable.name = name; custTable.update();

MORPHX IT Formulieren

172

© 2007 Steen Andreasen

ttscommit; } } else { name = CustTable::find(this.custAccount).name; } return name; } 7. Bewaar de wijzigingen aan SalesTable en versleep de nieuwe methode naar het

formulierontwerp op Designs/Design/[Tab:Tab]/[TabPage:Overview]/[Grid:Grid]. Positioneer het nieuwe formulierelement in het raster rechtsachter het formulierelement SalesTable_CustAccount. Open het eigenschappenblad voor het nieuwe element en selecteer SalesTable als DataSource.

8. Ga naar het knooppunt Data Sources/SalesTable/Methods en overschrijf de init()

methode met de volgende code: public void init() { super(); this.cacheAddMethod(tablemethodstr(SalesTable, salesTotal)); } 9. Bewaar het formulier en creëer een menuoptie voor dit formulier. Twee verschillende displaymethodes werden toegevoegd aan het formulier. De SalesTablemethode customerName() is een displaymethode die de klantnaam weergeeft die hoort bij het geselecteerde verkoopsorder. Zowel displaymethodes als editmethodes kunnen toegevoegd worden aan de tabelveldgroepen. Dit is handig omdat je nu niet het formulier moet wijzigen om de klantnaam te tonen. Het gebruik van display- en editmethodes in tabelveldgroepen heeft echter ook een nadeel. Als je een tabelmethode toevoegt of verwijdert, worden de unieke nummers van de methodes, ook wel method ids genoemd, hernummerd. Dit betekent dat je de tabelveldgroepen die gebruik maken van display- en editmethodes moet nakijken, telkens als het aantal tabelmethodes is gewijzigd, omdat de veldgroepen mogelijk een verkeerde display- of editmethode gebruiken. Opmerking : Als je displaymethode op het formulier getoond wordt zonder label of waarde, ben je waarschijnlijk vergeten de naam van de gegevensbron voor het formulierelement in te stellen. Als je een display- of editmethode versleept vanuit de tabelmethodes, moet dit manueel gebeuren. De tweede displaymethode in het formulier, salesTotals(), berekent het totaal van het verkoopsorder. Als je een displaymethode in een raster gebruikt, moet je het huidige record als parameter doorgeven. En om dit huidige record als parameter door te krijgen, moet de displaymethode gecreëerd worden als een tabelmethode of als een gegevensbronmethode.

MORPHX IT Formulieren

© 2007 Steen Andreasen

173

Display- en editmethodes veroorzaken een vertraging, zeker als ze gebruikt worden in een rasterelement, omdat displaymethodes meermaals uitgevoerd worden bij het opbouwen van het formulier. Om de performantie te verhogen, kunnen displaymethodes in het cachegeheugen bewaard worden. Als je de init() methode van de gegevensbron overschrijft, kan je de gegevensbronmethode cacheAddMethod() toevoegen om de tabeldisplaymethodes in het cachegeheugen te bewaren. Enkel tabelmethodes kunnen geborgen worden in het cachegeheugen en deze berging moet gebeuren na super() in de init() methode van de gegevensbron. Displaymethodes worden doorgaans gecreëerd als tabelmethodes, dus is dit normaal geen probleem. Editmethodes kunnen niet in het cachegeheugen bewaard worden, maar editmethodes zijn niet zo talrijk als displaymethodes, dus het cachen van displaymethodes zal de performantie zeker ten goede komen. In het voorbeeld hebben we enkel de displaymethode gebruikt in het raster, in de cache geborgen. Er valt niet veel winst te halen in het cachen van de displaymethode die de klantnaam toont, omdat dit als neveneffect heeft dat de klantnaam zal worden opgezocht voor alle records. Een editmethode is een uitbreiding van displaymethodes. Displaymethodes worden normaal gebruikt voor het weergeven van bijkomende informatie of om berekeningen te doen, terwijl editmethodes gebruikt worden in gevallen waar een join niet gelegd kan worden en een waarde in een gerelateerde tabel editeerbaar moet zijn. Editmethodes hebben twee parameters, de eerste geeft de waarde “true” terug indien de waarde van het formulierelement is gewijzigd, en de tweede parameter bevat de waarde van dit formulierelement. Je moet code toevoegen aan de editmethode om de waarde in de gerelateerde tabel aan te passen. Zoals je al wel kan raden, komt deze mogelijkheid met een extra kost, en je moet daarom altijd overwegen of je ontwerp wel een editmethode kan gebruiken. Het uitgebreide datatype gebruikt als teruggeefwaarde in display- en editmethodes, kan je gebruiken voor het instellen van de labels en het formatteren van het formulierelement. Als je een ander label wenst voor je formulierelement, kan je overwegen om een ander uitgebreid datatype te creëren, eerder dan de labels in het ontwerp te wijzigen. Opmerking : Als je één enkel record wil afbeelden, is een alternatief voor editmethodes het specifiëren van een uitgebreid datatype in het eigenschappenblad van het formulierelement en het instellen van de eigenschap AutoDeclaration. Je hebt dan de dubbele mogelijkheid om de waarde van het formulierelement op te halen en in te stellen. Het formulier KMAction maakt van deze werkwijze gebruik om een filter te voorzien bovenaan het formulier.

MORPHX IT Formulieren

174

© 2007 Steen Andreasen

6.4 Methodes op een Formulier Basisformulieren kunnen gebouwd worden zonder één enkele lijn code toe te moeten voegen. Zelfs ingewikkelde formulieren kan je maken zonder eigenhandig code te voorzien. In feite zou je moeten proberen om code uit de formulieren te houden en de logica toe te voegen aan tabellen of klassen, omdat het dan eenvoudiger is de gebruikersinterface te wijzigen. Gebruik je een extern systeem waarbij je logica aanbiedt via COM, dan zal je formuliercode moeten schrijven omdat formulieren niet uitgevoerd kunnen worden door COM. Je zal niet altijd code in je formulieren kunnen vermijden. Maar denk bij het bouwen van je formulieren eraan dat je best je code elders kan plaatsen indien mogelijk. En hierbij moet je altijd een zo centraal mogelijke plaats kiezen. Stel dat je bepaalde taken moet uitvoeren als de waarde van een formulierelement is gewijzigd. Als het formulierelement is gekoppeld aan een gegevensbronveld, is het beter om de code toe te voegen aan de tabelmethode modifiedField() dan aan de gegevensbronveldmethode modified(). In de volgende secties vind je een overzicht van methodes die overschreven kunnen worden in formulieren. Formuliermethodes Bij het uitvoeren van een formulier, wordt een instantie van de systeemklasse FormRun geïnitieerd. Ga naar het knooppunt MyForm/Methods en je vindt er de methodes van het FormRun object die je kan overschrijven. Men beschouwt deze methodes als de formuliermethodes. Als je klikt met de rechtermuisknop op het knooppunt van de formuliermethodes, en je selecteert vervolgens Override Method, zal je zien dat niet alle methodes van de FormRun klasse getoond worden. Enkel de methodes die overschreven kunnen worden, worden getoond en dus zal je methodes die voorzien zijn van het sleutelwoord final niet in de lijst zien. Je kan verwijzen naar formuliermethodes met de sleutelwoorden this en element. Het sleutelwoord this kan enkel gebruikt worden binnen een andere formuliermethode. Daarentegen ligt het sleutelwoord element in het bereik van het gehele formulierobject en kan dus gebruik worden om te verwijzen naar methodes van gegevensbronnen of formulierelementen.

Naam Parameters Omschrijving activate

Opgeroepen telkens een formulier wordt geactiveerd. Als je wisselt tussen twee formulieren, wordt de methode opgeroepen als je een formulier selecteert om ze te activeren. Zie formulier PBATable.

canClose

Opgeroepen door de methodes closeCancel() of closeOk(). Vaak gebruikt om te valideren of het formulier gesloten mag worden. Zie formulier InventTransPick.

close Deze methode wordt als laatste uitgevoerd

MORPHX IT Formulieren

© 2007 Steen Andreasen

175

Naam Parameters Omschrijving wanneer een formulier wordt gesloten. Zie

formulier ProdParameters.

closeCancel

Als je klikt op een CommandButton met de Command eigenschap ingesteld op Cancel, dan wordt de methode closeCancel() opgeroepen. Als closeCancel() de waarde true teruggeeft, wordt de methode canClose() opgeroepen. Zie formulier AssetChangeGroup.

closed

Closed() geeft de waarde true terug nadat de super() methode in close() is opgeroepen. Kan gebruikt worden om na te gaan of close() werd opgeroepen. Wordt niet vaak gebruikt.

closedCancel

Geef de waarde true terug als closeCancel() is opgeroepen. Vaak gebruikt door de methode close() om te controleren of het formulier met een cancel of ok knop werd afgesloten. Zie formulier CustParameters.

closedOk

Geeft de waarde true terug als closeOk() is opgeroepen. De teruggeefwaarde kan gebruikt worden om na te gaan of de gegevensbronmethode write() uitgevoerd moet worden. Zie formulier SalesCreateOrder.

closeOk

Als je klikt op een CommandButton met de Command eigenschap ingesteld op OK, wordt de methode closeOk() opgeroepen. Als closeOk() de waarde true teruggeeft, wordt de methode canClose() opgeroepen. Zie formulier AssetChangeGroup.

closeSelect

str _selectString Kan gebruikt worden om de waarde te overschrijven die door het opzoekformulier is teruggegeven. Meestal wordt de methode gebruikt om code toe te voegen die uitgevoerd moet worden voor het opzoekformulier wordt gesloten. De FormRunmethode selectMode() zal een formulier in opzoekmode instellen en het formulierelement specifiëren dat gebruikt wordt als teruggeefwaarde. Zie formulier KMGamePlanLookup.

controlMethodOverloadObject

Object _val Als je deze methode overschrijft, kan je de methodes voor formulierelementen toegevoegd aan het formulier tijdens runtime, creëren in een klasse in plaats van in het formulier. Zie klasse BOMChangeItem.

copy Uitgevoerd als de applicatiegebruiker de

MORPHX IT Formulieren

176

© 2007 Steen Andreasen

Naam Parameters Omschrijving toetsencombinatie ctrl+c gebruikt om de

inhoud van een formulierelement of een volledig record te kopiëren.

cut

Uitgevoerd als de applicatiegebruiker de toetsencombinatie ctrl+x gebruikt om de inhoud van een formulierelement uit te knippen.

doApply

Als je klikt op een CommandButton met de Command eigenschap ingesteld op Apply, wordt de method doApply() opgeroepen. Gebruikt om de wijzigingen aangebracht in het formulier, te bevestigen. Zie formulier ProdSetupRelease.

docCursor

Documentenbeheer moet documenten koppelen aan de huidig actieve gegevensbrontabel. De methode docCursor() kan gebruikt worden om te bepalen welke tabel hoort bij de betreffende documenten, indien een formulier meer dan één gegevensbron gebruikt. Zie formulier InventTable.

Finalize

Opgeroepen wanneer het formulier wordt gesloten. Verwijdert het formulierobject van het geheugen. Deze methode wordt normaal niet overschreven.

firstField

int _flags = 1 Zet de focus op het eerste veld in het formulierontwerp. Wordt overschreven indien de focus gezet moet worden op een ander formulierelement. Zie formulier CustOpenTrans.

init Dit is de eerste methode die wordt opgeroepen en die kan overschreven worden. De methode initialiseert het formulier. Entiteiten gebruikt in het formulier worden hier doorgaans geïnitialiseerd. Ook de controle of het formulier opgeroepen mag worden door een ander formulier gebeurt in init(). Zie formulier CustOpenBalance.

lastField

int _flags = 1 Deze methode heeft geen effect. Zou de focus moeten leggen op het laatste veld in het ontwerp.

loadUserSettings

De applicatiegebruiker kan zijn eigen gebruikersinstellingen beheren, waarmee bijvoorbeeld een formulier wordt aangepast naar de smaak en behoefte van de gebruiker. Deze gebruikersintellingen worden opgehaald door loadUserSettings().

MORPHX IT Formulieren

© 2007 Steen Andreasen

177

Naam Parameters Omschrijving new

Args _args = NULL New() is de eerste methode die wordt opgeroepen bij het uitvoeren van een formulier. Deze methode zou nooit overschreven mogen worden omdat dit het formulier doet crashen bij het openen.

nextField

Uitgevoerd als de gebruiker naar het volgende veld gaat door op tab of enter te drukken.

nextGroup

Uitgevoerd als de gebruiker naar de volgende veldgroep gaat door de toetscombinatie ctrl+pgdn in te drukken

paste

Uitgevoerd als de gebruiker de combinatie ctrl+v indrukt om een waarde in een formulierelement te plakken.

prevField

Uitgevoerd als de gebruiker naar het vorige veld gaat door de combinatie shift+tab in te drukken.

prevGroup

Uitgevoerd als de gebruiker naar de vorige veldgroep gaat door de toetscombinatie ctrl+pgup in te drukken.

print

Uitgevoerd bij het afdrukken van een formulier. Drukt de velden van de tabelveldgroep AutoReport af. Zie formulier ReqItemJournalSafetyStock.

printPreview

PrintPreview() wordt opgeroepen als File | Print Preview wordt geselecteerd in het topmenu. Zie formulier SysLicenseAgreement.

reload

Args _args = NULL Niet gebruikt.

resize

int _width, int _height Als de grootte van het formulier wordt gewijzigd, wordt de methode resize() opgeroepen.

run

Run() laadt het formulier op. De super() oproep in de methode roept de gegevensbronquery van het formulier op. Indien mogelijk, zou de initialisatie van het formulier moeten gebeuren in de init() methode van het formulier en de init() methodes van de gegevensbronnen, in plaats van dit via run() te regelen. Zie formulier ProdTable.

saveUserSettings

Eigen aanpassingen die de applicatiegebruiker aanbrengt aan het formulier, worden bewaard door saveUserSetttings.

MORPHX IT Formulieren

178

© 2007 Steen Andreasen

Naam Parameters Omschrijving selectControl

FormControl _control Opgeroepen als de focus verspringt naar een ander formulierelement. Zie formulier LedgerTransSettlement.

send

Send() is opgeroepen als File | Send is geselecteerd in het topmenu.

setApply

Object _object, Object _parm Kan gebruikt worden in combinatie met een commandoknop als het formulier wordt opgeroepen vanuit een klasse. Zie klasse DocuActionTrans.

task

Opgeroepen als de applicatiegebruiker een toets indrukt. Kan gebruikt worden om sneltoetsen te definiëren. Zie formulier JmgSelectJob.

Figuur 28: Formuliermethodes Gegevensbronmethodes in een formulier De gegevensbronmethodes van een formulier worden vaak overschreven als je wijzigingen aanbrengt aan de formulierquery of als je records van een gegevensbron met behulp van X++ wil toevoegen, wijzigen of verwijderen. Een formuliergegevensbron is een instantie van de systeemklasse FormDataSource. Naam Parameters Omschrijving Active Active() wordt opgeroepen telkens een

nieuw record wordt toegevoegd. De methode wordt vaak gebruikt voor het instellen van toegangscontrole. Zie formulier SalesTable.

create

boolean _append = false Bij gebruik van de toetscombinatie ctrl+n wordt de methode create() opgeroepen. Initialiseren van veldwaardes zou moeten gebeuren via initValue(). Zie formulier SalesTable.

cursorNotify

int _event Opgeroepen als de cursor geplaatst wordt bij een ander record. De methode wordt ook opgeroepen als de cache van de gegevensbron gewijzigd wordt. Kan gebruikt worden voor geavanceerd gebruik van cache. Zie formulier projPeriodEmpl.

defaultMark

int _value Geeft de waarde 1 terug als alle records in het raster gemarkeerd zijn met ctrl+a. Zie klasse ReqTransFormPO.

Delete

Als de applicatiegebruiker alt+F9 intikt, wordt de methode delete() opgeroepen. Delete() roept eerst validateDelete() op

MORPHX IT Formulieren

© 2007 Steen Andreasen

179

Naam Parameters Omschrijving voor de methode het record probeert te verwijderen. Zie formulier ProjJournalTable.

deleteMarked

De methode wordt opgeroepen als de applicatiegebruiker meer dan één record heeft gemarkeerd en dan alt+F9 intikt. DeleteMarked() roept dan delete() op voor ieder record dat verwijderd moet worden.

displayOption

Gebruikt voor het inkleuren van kolommen of rijen. Zie formulier EmplTable.

executeQuery

Haalt gegevens op gebaseerd op de instellingen in de formulierquery. De methode is vaak overschreven om de waarde van de bereiken van de query in te stellen via X++. Zie formulier CustTrans.

filter

fieldId _field, str _value Als je een veld selecteert, met de rechtermuisknop klikt en vervolgens Filter kiest, wordt de methode filter() opgeroepen. Niet vaak overschreven omdat men voor het overschrijven meestal executeQuery() gebruikt.

findRecord

Common _record Kan gebruikt worden om een cursor te plaatsen bij een specifiek record met behulp van een Common object. Let wel, dit is een zeer trage manier om de cursor te positioneren. Zie formulier SalesTable.

findValue

fieldId _field, str _value Kan gebruikt worden om de cursor te plaatsen bij een record waarvan de waarde van een opgegeven veld overeenstemt met de opgegeven waarde. Dit is een zeer trage manier om de cursor te positioneren. Zie formulier LedgerJournalTrans.

first

Opgeroepen bij gebruik van de toetscombinatie ctrl+home om het eerste record te selecteren in een gegevensbron. Zie formulier ContactPerson.

forceWrite

boolean _value Markeert een record als “bevuild” (lees : gewijzigd ten opzichte van het record in de databank ). Indien de waarde op “true” staat, wordt het huidige record bewaard alvorens verder te gaan. Moet opgeroepen worden vanuit X++. Zie formulier LedgerJournalTransApprove.

MORPHX IT Formulieren

180

© 2007 Steen Andreasen

Naam Parameters Omschrijving

init

Init() is de eerste gegevensbron-methode die wordt opgeroepen. Wijzigingen aan de query tijdens runtime worden doorgaans hier toegevoegd. Zie formulier CustTrans.

initValue

Als je een nieuw record in een formulier aanmaakt, wordt initValue() opgeroepen. Deze methode wordt gebruikt voor het initialiseren van veldwaardes. Gebruik liever de overeenkomstige tabelmethode, tenzij de instellingen specifiek zijn voor het formulier. Zie formulier BankAccountStatement.

last

Opgeroepen bij gebruik van de toetsencombinatie ctrl+end voor het selecteren van het laatste record in een gegevensbron. Zie formulier HRMApplicantTable.

leave

De methode wordt opgeroepen als de cursor het huidig geselecteerde record verlaat.

leaveRecord

Kan opgeroepen worden vanuit X++ om na te gaan of de cursor verplaatst mag worden naar een ander record. Zie formulier InventPosting.

linkActive

Als een gerelateerd subformulier is geactiveerd, dan wordt linkActive() opgeroepen telkens er een nieuw record geselecteerd wordt in het hoofdformulier. LinkActive() roept op zijn beurt executeQuery() op. Zie formulier MarkupTrans.

mark

int _value Geeft de waarde 1 terug als het huidige record gemarkeerd is. Kan gebruikt worden om een record te markeren als de methode wordt opgeroepen met de waarde 1.

next

Opgeroepen als het volgende record wordt geselecteerd. Zie formulier EmplTable.

nextPage

int _pageSize Als de applicatiegebruiker de page- down-toets in een raster indrukt, wordt nextPage() opgeroepen. De parameter _pageSize bevat het aantal records dat in het raster getoond wordt.

prev Opgeroepen als het vorige record wordt

MORPHX IT Formulieren

© 2007 Steen Andreasen

181

Naam Parameters Omschrijving geselecteerd.

Zie formulier EmplTable.

prevPage

int _pageSize Als de applicatiegebruiker de page- up-toets indrukt in een raster, wordt prevPage() opgeroepen. De parameter _pageSize bevat het aantal records dat in het raster getoond wordt.

print

PrintMedium _target Opgeroepen als ctrl+p wordt ingedrukt voor het afdrukken van het auto report. Kan gebruikt worden om het gedrag te wijzigen van een afgedrukt auto report zoals het wijzigen van de opmaak tijdens runtime. Zie formulier CCCount.

prompt

Geactiveerd als de applicatiegebruiker de formulier-querydialoog oproept. Prompt() wordt enkel opgeroepen voor de huidige gegevensbron. Kan gebruikt worden om te voorkomen dat de querydialoog opgeroepen wordt voor formulieren die gebruik maken van een boomstructuur. Zie formulier HRMOrganization.

refresh

Vaak gebruikt in combinatie met reread().Waar reread() de gegevensbron van het formulier aanpast voor het huidige record met de waarden van de tabel, wijzigt refresh() de formulieropmaak voor het huidige record met de waardes van de gegevensbron van het formulier. Zie formulier WMSShipment.

refreshEx

anytype _pos Uitgebreide versie van refresh(). Als de methode wordt opgeroepen met de parameter -1, worden alle records van de gegevensbron aangepast. Zie formulier CustOpenTransReverse.

removeFilter

Opgeroepen als een ingestelde filter wordt verwijderd. Wordt doorgaans overschreven bij gegevensbronnen die slechts één record bevatten zoals bij parameterformulieren. Zie formulier InventParameters.

reread

Moet opgeroepen worden vanuit X++. Haalt het huidige record uit de gegevensbrontabel. Zie formulier SalesCreateOrderForm.

research

Moet opgeroepen worden vanuit X++. Haalt de records op gespecifieerd door de query. Gelijkaardig aan het

MORPHX IT Formulieren

182

© 2007 Steen Andreasen

Naam Parameters Omschrijving oproepen van executeQuery(), behalve dat research() de instellingen bewaart die in de query zijn gewijzigd. Zie formulier LedgerTable.

validateDelete

Delete() roept validateDelete() op om te controleren of het record verwijderd mag worden. Zie formulier ProdJournalTable.

validateWrite

ValidateWrite wordt opgeroepen door write(). Geeft de waarde true weer als het record toegevoegd of gewijzigd mag worden. Zie formulier InventTable.

write

Opgeroepen bij het indrukken van de toetscombinatie ctrl+s. Roept insert() op bij een nieuw record, anders wordt update() opgeroepen. Validaties moeten indien mogelijk voorzien worden in validateWrite(). Zie formulier CustOpenTrans.

Figuur 29: Formuliergegevensbronmethodes Gegevensbronveldmethodes in een formulier Alle gegevensbronvelden in het formulier hebben dezelfde set methodes omdat ze allemaal gedeclareerd zijn met behulp van de systeemklasse FormDataObject. Naam Parameters Omschrijving context Opgeroepen als de applicatiegebruiker

op een veld met de rechtermuisknop klikt.

filter

str _filterStr, NoYes _clearPrev Opgeroepen als de applicatiegebruiker op een veld met de rechtermuisknop klikt en Filter selecteert. Merk op dat de oproep van super() in filter() niet zal werken als de methode is overschreven.

find

Opgeroepen als de applicatiegebruiker op een veld met de rechtermuisknop klikt en Find selecteert.

helpField

Toont de helptekst voor het huidige veld onderaan het scherm. De getoonde helptekst komt van het huidige formulierelement, het gerelateerde tabelveld, uitgebreide datatype of basisenum.

jumpRef

Wordt opgeroepen bij de toetsencombinatie ctrl+alt+F4 om naar

MORPHX IT Formulieren

© 2007 Steen Andreasen

183

Naam Parameters Omschrijving de hoofdtabel te gaan. De methode kan overschreven worden om naar een ander formulier te gaan als er geen relatie bestaat. Zie formulier InventItemGroup.

lookup

FormControl _formControl, str _filterStr

Gebruikt om een aangepast opzoekformulier te bouwen voor formulierelementen die geen opzoekfunctie hebben. Zie formulier InventTable.

modified

Als men de waarde wijzigt van een veld gekoppeld aan een gegevensbronveld, dan wordt modified() opgeroepen. Indien validate() de waarde true teruggeeft, wordt modified() opgeroepen. Wordt doorgaans overschreven indien tegelijk de waarde van een ander veld ingesteld moet worden. Zie formulier PriceDiscAdm.

performFormLookup

FormRun _form, FormControl _formControl

Kan gebruikt worden om een bestaand opzoekformulier te overschrijven. Zie formulier HRMApplication.

restore

Deze methode wordt blijkbaar niet gebruikt.

toolTip

Gebruikt om de gele werktip te tonen wanneer je de cursor positioneert op een formulierelement. Kan overschreven worden om de standaardwerktip te wijzigen. Zie formulier BOMDesigner.

validate

Deze methode wordt opgeroepen als de waarde wijzigt van een formulierelement dat gekoppeld is aan een gegevensbronveld. Gebruikt om te controleren of het formulierelement moet gewijzigd worden. Zie formulier InventTable.

Figuur 30: Formuliergegevensbronveldmethodes Validaties specifiek voor één enkel formulier gebeuren aan de hand van de formulier- gegevensbronveldmethodes. Als de validaties van een formulierelement, gekoppeld aan een gegevensbronveld, algemeen gebruikt mogen worden, kan je beter de tabelmethodes validateField() en modifiedField() gebruiken in plaats van de gegevensbron-veldmethodes validate() en modified(). Dit kadert in het Model-View-Controller ontwerppatroon dat in diverse programmeertalen wordt gebruikt.

MORPHX IT Formulieren

184

© 2007 Steen Andreasen

Formulierelementmethodes Sommige methodes van een formulierelement zijn gelijkaardig aan de methodes van een formuliergegevensbronveld. Je hebt vaak alternatieven voor het overschrijven van een formulierelement, omdat je de keuze hebt tussen het gebruik van de methodes op niveau van de gegevensbron of het gebruik van automatische declaratie van een formulierelement. Het automatisch declareren van een formulierelement gebeurt door het instellen van de elementeigenschap AutoDeclaration op Yes. Deze eigenschap vind je terug in alle formulierelementen. Het automatisch declareren creëert een instantie van de overeenkomstige systeemklasse voor het formulierelement. Zo creëert de automatische declaratie van een stringelement een instantie van de systeemklasse FormStringControl die gebruikt kan worden door alle methodes op een formulier. Gebruikelijke Formuliermethodes Enkel een handvol formuliermethodes heb je dagelijks nodig. Een basiskennis van de uitvoervolgorde van deze methodes zal je verder helpen als je je wijzigingen begint te maken. De volgende methodes worden uitgevoerd in de vermelde volgorde wanneer een formulier wordt geopend en gesloten :

init() ► ds init() ► run() ► ds executeQuery() ► canClose() ► close()

1. FormRun.init() is de eerste opgeroepen methode. De oproep van super() in FormRun.init() roept de methode FormDataSource.init() op voor iedere gegevensbron of data source gebruikt in de formulierquery.

2. De oproep van super() in FormRun.run() roept FormDataSource.executeQuery() op voor iedere gegevensbron.

3. Bij het afsluiten van het formulier, zal FormRun.canClose() nagaan of het formulier gesloten mag worden. Is alles in orde, dan wordt FormRun.close() uitgevoerd.

Dit zijn de belangrijkste methodes die uitgevoerd worden als een formulier wordt geopend en gesloten. Je kan nog andere methodes oproepen tijdens het openen en sluiten van formulieren, zoals het opladen van de applicatiegebruikersinstellingen. Maar doorgaans moet je enkel de hier opgelijste methodes overschrijven. Opmerking : Om de uitvoeringsvolgorde van de methodes na te gaan, kan je een breekpunt zetten in de super() oproep van een overschreven methode en vervolgens de stack trace in de debugger nagaan. Een andere optie is het afdrukken van een lijn naar de Infolog vanuit iedere overschreven methode. Voor iedere gegevensbron in je formulierquery, wordt de volgende sequentie opgestart door executeQuery() als het formulier wordt geopend:

ds executeQuery() ► refresh() ► active()

1. De gegevensbronmethode executeQuery()haalt alle records op voor de

huidige gegevensbron en roept daarna refresh() op. 2. Refresh() zorgt voor een synchronisatie van de opgehaalde records met de

databank en wordt normaal niet overschreven. Als je records toevoegt,

MORPHX IT Formulieren

© 2007 Steen Andreasen

185

aanpast of verwijdert, die ook in een formulier getoond worden, kan refresh() opgeroepen worden om de gewijzigde data te tonen.

3. Bij het openen van het formulier, wordt active() uitgevoerd voor het huidige record. Je kan active() gebruiken voor het instellen van de toegangsbeperkingen van de formulierelementen. Active() wordt opgeroepen telkens een record wordt geselecteerd.

Voorbeeld 6: Active Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_Active Menu item display. MyForm_Active

Het voorbeeld toont hoe de gegevensbronmethode active() gebruikt kan worden om de toegang te controleren. 1. Kopieer MyForm en geef het nieuwe formulier de naam “MyForm_Active”. 2. Klik het knooppunt van het formulier open en ga naar Data

Sources/SalesTable/Methods. Klik met de rechtermuisknop om de methode active() te overschrijven. Voeg het volgende toe aan active():

public int active() { int ret; ret = super(); salesTable_ds.allowEdit(salesTable.salesStatus == SalesStatus::Invoiced ? false : true); return ret; } 3. Bewaar het formulier en maak een menuoptie aan. Roep het formulier MyForm_Active op. Je zal geen verkoopsorders kunnen wijzigen die als status “gefactureerd” hebben. Als je naar het tweede tabblad van het formulier gaat, zal je zien dat de formulierelementen zijn uitgegrijsd om aan te geven dat je deze niet meer mag editeren. Formulierelementen in een raster veranderen niet van opmaak. Als je een element in een raster wil wijzigen, kan je dit doen door de rijen te kleuren. Het gebruik van kleuren in rijen wordt uitgelegd in de sectie Speciale Formulieren.

MORPHX IT Formulieren

186

© 2007 Steen Andreasen

Gegevensbronmethodes Het toevoegen van een nieuw record in een formulier start de volgende sequentie van gegevensbronmethodes :

create() ► initValue() ► refresh() ► active() ► refresh()

1. De methode create() wordt opgeroepen bij de toetscombinatie ctrl+n om een nieuw record aan te maken.

2. De gegevensbronmethode initValue() wordt opgeroepen door create(). Als je velden wil initialiseren met specifieke waardes, moet je dit doen in initValue(). De tabelmethode initValue() wordt opgeroepen door super() in de gegevensbronmethode initValue(). Als de geïnitialiseerde waardes niet specifiek zijn voor het formulier, kan je best de overeenkomstige tabelmethode gebruiken.

3. Na de initialisatie wordt refresh() opgeroepen, en als je de cursor naar een bepaald record verplaatst, wordt active() opgeroepen. Refresh wordt dan weer opgeroepen door active().

Bewaar je een record, dan roep je hiermee de volgende methodes op :

validateWrite() ► write() ► refresh()

1. ValidateWrite() roept de tabelmethode validateWrite() op. We willen er nogmaals op drukken dat je best altijd de overeenkomstige tabelmethodes gebruikt indien mogelijk.

2. De gegevensbronmethode write() roept insert() of update() van de tabel op, afhankelijk van het feit of het record al dan niet reeds werd aangemaakt. Validaties van waardes zouden al voor dit punt moeten gebeurd zijn. Als je voorwaardes wil opleggen, moet je dit doen in validateWrite().

3. Refresh() wordt opgeroepen om de formulierelementen te synchroniseren met de databankgegevens.

Het verwijderen van een record heeft een gelijkaardig verloop :

validateDelete() ► delete() ► active()

1. ValidateDelete() roept de overeenkomstige tabelmethode op. Op dit punt moeten de voorwaardes voor verwijdering van een record al nagekeken zijn, hetzij vanuit de gegevensbron hetzij vanuit de tabel.

2. De gegevensbronmethode delete() roept de tabelmethode delete() op. 3. Active() wordt opgeroepen als de cursor verspringt naar een ander record

nadat het huidige record is verwijderd.

MORPHX IT Formulieren

© 2007 Steen Andreasen

187

Formulierelementmethodes Alle formulierelementen hebben methodes die overschreven kunnen worden. Je zou enkel in speciale gevallen deze methodes moeten overschrijven. Als een formulierelement niet gekoppeld is aan een gegevensbron, moet je de methodes van dat element gebruiken. Voor gekoppelde formulierelementen is het aangewezen om de methodes van het overeenkomstige gegevensbronveld te gebruiken. Bepaalde speciale formulierelementen zoals de boomstructuur en de lijst vereisen net dat bepaalde van hun formulierelementmethodes worden overschreven. Voor formulierelementen die overeenstemmen met basistypes zal je zelden de formulierelementmethode moeten gebruiken. Overschrijven van een Formulierquery Je zal vaak situaties hebben waarin je gegevens op een formulier wil filteren. Formulieren maken doorgaans gebruik van een query voor het ophalen van gegevens en dus volstaat het om bereiken toe te voegen aan de formulierquery. Als je gebruik maakt van een AOT-query of een rapportquery, zal je de bereiken kunnen toevoegen met behulp van de knooppunten in de AOT. Een formulierquery heeft niet dezelfde opties voor het filteren via de AOT-knooppunten. In plaats daarvan moet je X++ gebruiken voor het toevoegen van de bereiken. Voorbeeld 7: toevoegen van een bereik Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_QueryFilter Menu item display. MyForm_QueryFilter

Dit voorbeeld toont hoe je een filter kan toevoegen aan een formulierquery met behulp van een formulierelement. 1. Maak een kopie van MyForm en hernoem het formulier “MyForm_Query”. 2. Open het eigenschappenblad van het knooppunt Designs/Design en stel de

eigenschap Columns in op 1. 3. Klik met de rechtermuisknop op het knooppunt Designs/Design en selecteer New

Control/Group. Positioneer het nieuwe formulierelement boven de tab en hernoem het nieuwe formulierelement “rangeGroup”. Stel de eigenschap Caption voor het nieuwe element in op “Filter”.

4. Klik met de rechtermuisknop op het formulierelement “rangeGroup” en selecteer

een formulierelement van het type StringEdit. Geef het element de naam “filterCurrencyCode”. Open het eigenschappenblad van dit nieuwe element en stel AutoDeclaration in op “Yes” en ExtendedDataType op “CurrencyCode”.

5. Ga naar de ClassDeclaration van het formulier en tik het volgende in : public class FormRun extends ObjectRun { QueryBuildRange rangeCurrencyCode; }

MORPHX IT Formulieren

188

© 2007 Steen Andreasen

6. Ga vervolgens naar Data Sources/SalesTable/Methods en overschrijf init(): public void init() { QueryBuildDatasource salesTableDS; ; super(); salesTableDS = SalesTable_ds.query().dataSourceTable(tablenum(SalesTable)); rangeCurrencyCode = salesTableDS.addRange(fieldNum(SalesTable, currencyCode)); } 7. Overschrijf de executeQuery() methode van de gegevensbron SalesTable: public void executeQuery() { rangeCurrencyCode.value(queryValue(filterCurrencyCode.valueStr())); super(); } 8. Zoek het formulierelement filterCurrencyCode op en overschrijf de modified()

methode : public boolean modified() { boolean ret; ret = super(); SalesTable_ds.executeQuery(); return ret; } 9. Bewaar het formulier en maak een menuoptie aan voor dit formulier. Het formulier MyForm_QueryFilter heeft een formulierelement boven het raster. Dit element kan gebruikt worden om enkel verkoopsorders te tonen die een bepaalde munteenheid hebben. Het filterformulierelement is niet gekoppeld aan een gegevensbron. Het uitgebreide gegevenstype CurrencyCode gekoppeld aan het element bepaalt de labels en voegt een opmaak toe. Omdat het formulierelement niet gekoppeld is aan een gegevensbron, worden de ingetikte waardes niet gevalideerd en kan je dus ook een munteenheid intikken die niet in de gegevensbron te vinden is. Je zou dus best een validatie voorzien die nagaat of een geldige munteenheid werd ingetikt. De validatie is hier weggelaten om het voorbeeld zo eenvoudig mogelijk te maken. In de init() methode van de gegevensbron SalesTable is een nieuw querybereik gedefinieerd. Bereiken zouden altijd gedefinieerd moeten worden in de init() methode omdat deze methode slechts eenmaal wordt uitgevoerd. De waarde gebruikt door het bereik, is gedefinieerd in de executeQuery() methode omdat deze methode wordt opgeroepen telkens de formulierquery is gewijzigd. Een formulierquery kan opnieuw

MORPHX IT Formulieren

© 2007 Steen Andreasen

189

uitgevoerd worden als de applicatiegebruiker de querydialoog oproept door te klikken op het querypictogram en de waardes van de query wijzigt. In het voorbeeld wordt de executeQuery() methode gestart zodra de waarde van het formulierelement Currency is gewijzigd. Merk op dat de bereiken van de munteenheidcode gewijzigd kunnen worden, als de applicatiegebruiker op het querypictogram klikt. Om dit te voorkomen, moet de volgende lijn worden toegevoegd aan de init() methode van de query : rangeCurrencyCode.status(RangeStatus::Locked); Het toevoegen van filterformulierelementen aan de formulieropmaak wordt vaak gebruikt in Axapta. De applicatiegebruiker had hetzelfde resultaat kunnen bereiken door direct naar de querydialoog te gaan. Dit voorbeeld was eenvoudig en dus had de gebruiker niet veel moeite gehad om de querydialoog hiervoor te gebruiken. Als je daarentegen meerdere filterformulierelementen zou gehad hebben in combinatie met meerdere gegevensbronnen in je formulierquery, dan zou het geen eenvoudige opgave geweest zijn voor de applicatiegebruiker. Wijzigen van gegevensbronnen vanuit X++ Het is aangewezen om records nooit toe te voegen, te wijzigen of te verwijderen met behulp van formuliermethodes. Zulke operaties moet je altijd doen via een tabel- of klassemethode om gegevenshandelingen uit de formulieren te houden. Wil je in een tabel een record toevoegen, wijzigen of verwijderen, dan moet je het formulier aan de tabel koppelen met behulp van een gegevensbron. Het is een vaak voorkomende fout om rechtstreeks te verwijzen naar de tabel in plaats van de gegevensbron te gebruiken. Stel dat je een uitvoerbare klasse hebt voor het toevoegen van een nieuw record in een tabel en dat je deze klasse in je formulier gebruikt als gegevensbron. Om het nieuwe record in je formulier te tonen, gebruik je de methode executeQuery(). Je nieuwe record wordt zeker getoond, maar de cursor zal gepositioneerd worden op het eerste record in het formulier.Dit is in sommige gevallen wat je hebben wilt, maar vaak is het een vereiste dat de cursor gepositioneerd wordt op het nieuwe record en dus niet noodzakelijk het eerste record. Een gegevensbron heeft een methode findRecord() die gebruikt kan worden om de cursor te positioneren. Het wordt niet aanbevolen findRecord() te gebruiken omdat dit tot resultaat heeft dat de meeste records in je formulier worden doorlopen om de cursor te positioneren. In plaats daarvan kan je best dezelfde gegevensbronmethodes gebruiken die het formulier gebruikt om nieuwe records toe te voegen. Voorbeeld 8: Toevoegen van records Elementen gebruikt van MORPHXIT_Forms project

Class, MyForm_NewRecord Form, VendGroup Menu item action, MyForm_NewRecord

MORPHX IT Formulieren

190

© 2007 Steen Andreasen

In dit voorbeeld zal je leren hoe je een nieuw record toevoegt aan een formuliergegevensbron via X++ met behulp van dezelfde methodes die je gebruikt om records aan te maken vanuit de formuliergebruikersinterface. 1. Maak een nieuwe klasse aan, ga naar de ClassDeclaration en hernoem de klasse

“MyForm_CreateRecord”. 2. Maak de klasse uitvoerbaar door een static main() methode toe te voegen: static void main(Args _args) { MyForm_NewRecord newRecord; ; newRecord = new MyForm_NewRecord(); newRecord.run(_args.record()); } 3. Maak een methode genaamd run() en voeg de volgende code toe : void run(VendGroup _vendGroup) { VendGroup vendGroup; VendGroup callerVendGroup = _vendGroup; FormDataSource formDataSource; ; formDataSource = callerVendGroup.dataSource(); vendGroup.data(callerVendGroup); formDataSource.create(true); callerVendGroup.data(vendGroup); callerVendGroup.recId = 0; callerVendGroup.vendGroup = "G1"; callerVendGroup.name = "Group 1"; callerVendGroup.write(); formDataSource.reread(); formDataSource.refresh(); } 4. Bewaar de klasse en maak een menuoptie voor deze klasse. Ga naar het

eigenschappenblad voor de nieuwe menuoptie en stel label in op “New record”. 5. Zoek het formulier VendGroup en klik het formulierknooppunt open tot aan

Designs/Design/[ButtonGroup:ButtonGroup]. Versleep de menuoptie voor de nieuwe klasse naar de button group.

6. Bewaar het formulier VendGroup. Als je het formulier VendGroup opent, zal je een nieuwe knop zien met de naam “New Record”. Als je op de knop klikt, wordt een nieuw record toegevoegd aan de tabel VendGroup en de cursor wordt gepositioneerd op dit nieuwe record. Het record is een

MORPHX IT Formulieren

© 2007 Steen Andreasen

191

kopie van het huidige geselecteerde record. Alleen de velden “account number” en “name” zijn gewijzigd. In de klasse is een hendel gemaakt voor de VendGroup gegevensbron. Het huidig geselecteerde record wordt doorgegeven als parameter in de main() methode. Een tabelvariabele heeft een methode datasource() die geïnitialiseerd wordt als ze wordt opgeroepen vanuit een formulier. Deze methode wordt gebruikt om de gegevensbronmethodes create() en write() op te roepen voor het aanmaken en wegschrijven van een nieuw record. Als het huidige record is gekopieerd, wordt het systeemveld recId op 0 gezet om zo het nieuwe record aan de tabel toe te voegen. De oproep van de gegevensbronmethodes refresh() en reread() is heel belangrijk omdat ze het record in het formulier hernieuwen en het nieuwe record uit de tabel lezen. Als je deze methodes niet oproept en je gebruikt de AOS, dan zal je foutmeldingen krijgen omdat het cachegeheugen niet wordt aangepast aan de nieuwe situatie. Het gebruik van de formuliergegevensbronmethodes voor het toevoegen, wijzigen en verwijderen van records maakt je formulier gebruiksvriendelijker. Deze werkwijze heeft de voorkeur als je enkele records toevoegt. Maar als je een hoop records in een formulier wil toevoegen, kan je beter de tabelmethodes gebruiken in plaats van de formuliergegevensbronmethode omdat die zeer traag is in het geval je een groot aantal records wil toevoegen. De systeemklasse Args werd in dit voorbeeld gebruikt om een hendel te krijgen voor het oproepende object. Args wordt vaak gebruikt voor het valideren van het oproepende object. Sommige formulieren vereisen dat ze opgeroepen worden door een bepaald formulier of klasse. In dit geval wordt Args gebruikt voor de validatie, en afhankelijk van het oproepende object kunnen verschillende bereiken worden toegevoegd voor de selectie van de gegevens op het formulier en bepaalde restricties op formulierelementen kunnen worden gezet zodat de formulierelementen uitgegrijsd zijn of niet getoond worden. Je vindt tig voorbeelden van het gebruik van Args als je de formulieren in het standaardpakket eens nakijkt. Bouwen van opzoekingen Het toevoegen van een relatie aan een uitgebreid datatype zorgt ervoor dat er automatisch een opzoekknop wordt toegevoegd aan formulierelementen die gebruik maken van dit uitgebreid datatype. Vaak loopt dit heel goed. Maar er zijn gevallen waarin er geen relaties bestaan en je de gebruiker wil toelaten om de bijbehorende gegevens op te zoeken. Of je wil de mogelijkheid geven om de records van het opzoekformulier te filteren, of je wil een complexere schermopmaak bouwen zoals het voorzien van diverse tabbladen in het opzoekformulier. Een opzoekformulier kan overschreven worden door een nieuwe opzoeking in X++ te bouwen of door het standaard opzoekformulier te vervangen door een maatwerkformulier.

MORPHX IT Formulieren

192

© 2007 Steen Andreasen

Kijk even naar de volgende code die komt uit het formulier HRMApplication. Dit is de methode performFormLookup() overschreven voor het gegevensbronveld hrmRecruitingId: public void performFormLookup(FormRun _p1, FormControl _formControl) { super(hrmApplication::hrmRecruitingIdLookup(_p1), _formControl); } Om een bestaande opzoeking in X++ te overschrijven, kan je gebruik maken van de methode performFormLookup() van het gegevensbronveld. Zowel gegevensbronvelden als formulierelementen hebben een methode genaamd lookup(). Deze moet je enkel gebruiken indien het formulierelement nog geen opzoeking heeft, of als je een opzoeking wil aanbieden voor een formulierelement dat niet gekoppeld is aan een gegevensbronveld. Omdat de methode performFormLookup() pas geïntroduceerd is vanaf Axapta 3.0, ga je waarschijnlijk niet veel voorbeelden vinden van het gebruik van deze methode in het standaardpakket. PerformFormLookup() heeft twee parameters : het FormRun object gebruikt door de opzoeking is de eerste parameter en het formulierelement dat gebruikt wordt als opzoekveld is de tweede parameter. Hier wordt de methode hrmRecruitingIdLookup() van de tabel HRMApplication gebruikt om het FormRun object te overschrijven dat doorgegeven wordt aan de super() oproep. static formRun hrmRecruitingIdLookup(FormRun lookupFormRun) { formDataSource formDataSource; query formquery; ; formDataSource = lookupFormRun.objectSet(); formQuery = formDataSource.query(); formQuery.dataSourceNo(1).addRange(fieldNum(HRMRecruitingTable, status)).value(queryValue(HRMRecruitingStatus::Started)); return lookupFormRun; } Als je de tabelmethode hrmRecruitingIdLookup() opzoekt, zal je zien dat het FormRun object gebruikt wordt om een hendel te krijgen op het standaard opzoekformulier. Op die manier krijg je toegang tot de query die gebruikt wordt door de opzoeking. Hier wordt een nieuw bereik toegevoegd om de records te filteren die in de opzoeklijst wordt getoond. De query gebruikt in de opzoeking is een gewone query, die gekoppeld kan worden aan andere gegevensbronnen. Als je een opzoeking wil maken voor een formulierelement dat geen opzoeking heeft, zal je de methode performFormLookup niet kunnen gebruiken. In plaats daarvan wordt de klasse SysTableLookup gebruikt om een query te bouwen voor je opzoeking. Het formulier InventTable maakt gebruik van deze klasse in diverse methodes in de tabel InventTable. De naam van de opzoekmethodes van de tabel InventTable wordt voorafgegaan door lookup*. Een formulier aangemaakt in de AOT kan ook gebruikt worden als opzoekformulier. Formulieren die dienst doen als opzoekformulier, worden snel herkend in de AOT

MORPHX IT Formulieren

© 2007 Steen Andreasen

193

omdat hun naam eindigt op *lookup. Als je een formulier dezelfde opmaak wil geven als een standaard opzoekformulier gegenereerd door de kernel, moet je het formulier aanmaken zonder tabbladen. Voeg enkel een raster toe die de velden bevat die opgelijst moeten worden. Als je de eigenschap Frame van het knooppunt Design op “Border” instelt, zal het formulier er als een standaard opzoekformulier uitzien. De init() methode van het opzoekformulier zou het formulierelement moeten teruggeven dat geselecteerd moet worden : void init() { super(); element.selectMode(CustTable_AccountNum); } De code hierboven komt uit het formulier CustTableLookup. De formuliermethode selectMode() zet het formulier in ‘opzoekmodus’. Als de applicatiegebruiker op enter drukt of op een record klikt, wordt het formulier gesloten en de waarde van het formulierelement gespecifieerd als parameter, wordt teruggegeven. client static void lookupCustomer(FormStringControl _formStringControl, CompanyId _companyId = curExt()) { Args args; FormRun formRun; if (! CompanyTmpCheck::exist(_companyId)) { throw error(strFmt("@SYS10666", _companyId)); } changecompany(_companyId) { args = new Args(); args.name(formstr(CustTableLookup)); formRun = classFactory.formRunClass(args); formRun.init(); _formStringControl.performFormLookup(formRun); } } Hier wordt het formulier CustTableLookup opgeroepen door de methode lookupCustomer van de tabel CustTable. Merk op dat de gegevensbronmethode performFormLookup gebruikt wordt om het opzoekformulier te koppelen aan het oproepende formulier. In plaats van je opzoekmethode direct vanuit X++ op te roepen, is het beter je opzoekformulier te koppelen aan een uitgebreid datatype. Hiervoor moet je enkel de formuliernaam opgeven bij de eigenschap FormHelp van het uitgebreid datatype en je opzoekformulier wordt automatisch gebruikt als je het betreffende uitgebreide datatype in een formulier gebruikt. Dit is best wel handig, omdat je geen enkele lijn code moet schrijven in het oproepend formulier. Indien nodig, kan je beperkingen opleggen aan je opzoekformulier om het gedrag van het formulier te sturen, rekening houdend met de oproeper. Het uitgebreid datatype ProjId gebruikt een aangepast opzoekformulier. Een voorbeeld van uitgebreide datatypes die aangepaste opzoekformulieren gebruiken, vind je in het uitgebreide datatype TransDate.

MORPHX IT Formulieren

194

© 2007 Steen Andreasen

Opmerking : Als je een opzoeking wil maken op AOT objecten zoals tabellen en klassen, neem dan eens een kijkje in de Global klassen waarvan de naam begint met pick*. Kijk het formulier sysDatabaseLogTableSetup na als voorbeeld van het gebruik van de Global klassemethode pickTable(). Formulierdialoog Een gebruikelijke manier om een dialoog te creëren voor een runbase klasse is met behulp van de Dialog* classes. Een dialoog is vaak zeer eenvoudig en je moet enkel een paar formulierelementen toevoegen en ze categoriseren in veldgroepen. Maar als je een complexe dialoog hebt gemaakt met diverse formulierelementen waar deze elementen geactiveerd worden op basis van runtime instellingen, zal je je al snel realiseren dat het onderhoud van deze dialoog tijdverslindend wordt. Voor je het zover laat komen, moet je nagaan of je niet beter een formulier als dialoog kan gebruiken. Het runbase raamwerk ondersteunt het gebruik van een gewoon formulier als dialoog. Je moet enkel een paar regels respecteren bij het opbouwen van het dialoogformulier.

Figuur 31: Formulierdialoog Bekijk figuur 31: Formulierdialoog. Het formulier is een voorbeeld van een dialoogformulier gebruikt door een rapport in de Projectmodule. Het runbase raamwerk kijkt na of bepaalde formulierelementen worden aangemaakt en juist benoemd in de opmaak. Deze verplichte formulierelementen worden gebruikt door het runbase raamwerk om de verschillende onderdelen in de rapportopmaak te localiseren. Als je eenmaal beslist hebt om een formulierdialoog te gebruiken, zal je toch nog in staat zijn om bijkomende formulierelementen toe te voegen aan je formulierdialoog, gebruik makende van de runbase klasse en de dialoogklassen. De verplichte formulierelementen in de formulierdialoog worden gebruikt om de elementen die je toevoegt vanuit de klasse, te positioneren. Als je een formulierdialoog maakt, is het veel sneller om een bestaande formulier-dialoog te kopiëren dan je eigen dialoog van nul af te bouwen. Als je een formulierdialoog gebruikt zoals hierboven is getoond, ben je zeker dat je alle vereiste formulierelementen hebt. Vervang daarna de elementen in de veldgroep genaamd

MORPHX IT Formulieren

© 2007 Steen Andreasen

195

GroupLeft met de elementen die je nodig hebt voor je dialoog. Alle formulierelementen die gebruikt worden voor het intikken van gegevens, moeten gecreëerd worden met behulp van editmethodes. Dit maakt het eenvoudiger om de waardes naar de runbase klasse over te brengen. De formulierdialoog hierboven vermeld, wordt opgeroepen uit de klasse ProjReport_EstimateCheck. De dialog() methode in de klasse ziet er als volgt uit : public Object dialog() { DialogRunbase dialog = Dialog::newFormnameRunbase(formstr(ProjEstimatelistDialog),this); ; dialog = super(dialog); // Add extra fields below return dialog; } Merk op dat bijkomende dialoogvelden ook worden toegevoegd in de dialog() methode. Een parametermethode wordt geschreven voor ieder veld in de dialoog om waardes voor de dialoog in te stellen en op te halen (de zogenaamde “getter-and-setter methodes”). De methode getFromDialog() wordt enkel gebruikt als je bijkomende velden toevoegt aan je formulierdialoog vanuit je klasse.

6.5 Speciale Formulieren Deze sectie bevat voorbeelden van meer geavanceerde formulieren. De voorbeelden moeten je een idee geven van de mogelijkheden die Axapta-formulieren je bieden. Ook al zijn de formulieren gestandaardizeerd en werkt de opmaak met autopositionering, dan heb je nog verschillende opties om je formulieren gebruiksvriendelijker te maken zonder dat daarom je code complexer moet worden. Oproepen van gebruikergedefinieerde methode Als je een formulier oproept vanuit X++, heb je waarschijnlijk al gemerkt dat je niet je eigen methodes kan oproepen die je op het formulier hebt aangemaakt. Als je een nieuwe methode in het formulier hebt geschreven, zal je methode niet gekend zijn bij het declareren van een FormRun object buiten het formulier omdat alleen onderdelen van het FormRun object gekend zijn. Soms kan het handig zijn om je eigen methodes buiten het formulier op te roepen. Dit kan je doen met behulp van de superklasse voor de systeemklasse FormRun. Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_CallingUserMethod Menu item display, MyForm_CallingUserMethod Job, Forms_CallingUserMethod

MORPHX IT Formulieren

196

© 2007 Steen Andreasen

Kopieer MyForm, hernoem het formulier “MyForm_CallingUserMethod” en maak een menuoptie voor het nieuwe formulier. void setReadOnly() { salesTable_ds.allowEdit(false); salesTable_ds.allowCreate(false); salesTable_ds.allowDelete(false); salesTable_ds.insertAtEnd(false); salesTable_ds.insertIfEmpty(false); element.design().caption(strfmt("READ ONLY - %1", element.design().caption())); } Voeg de bovenstaande methode toe aan MyForm_CallingUserMethod. Het oproepen van deze methode voorkomt dat de gegevens in het formulier gewijzigd worden en breidt de titelbalk uit met een “read only” boodschap. Deze methode wordt gebruikt om te tonen hoe je een nieuwe formuliermethode buiten het formulier kan oproepen. static void Forms_CallingUserMethod(Args _args) { Args args; Object formRun; ; args = new Args(); formRun = new menuFunction(menuItemDisplayStr(MyForm_CallingUserMethod), MenuItemType::Display).create(args); formRun.init(); formRun.setReadOnly(); formRun.run(); } Maak een nieuwe job met de bovenstaande code. Het formulier MyForm_CallingUserMethod wordt opgeroepen bij het uitvoeren van deze job. In plaats van de systeemklasse FormRun te gebruiken, wordt de systeemklasse Object gebruikt voor het declareren van een FormRun object. Object valideert de FormRun methodes niet. Dit geeft de mogelijkheid om nieuwe methodes, toegevoegd aan het formulier, op te roepen. Je moet dan wel nakijken of de methode bestaat, omdat er geen validatie is voor runtime en er wordt evenmin opzoekinformatie getoond als je een punt intikt na een instantie van de klasse Object. De methode toegevoegd aan het formulier MyForm_CallingUserMethod heeft geen enkele functionaliteit die niet kan opgeroepen worden door te refereren naar formulier- of formuliergegevensbronmethodes. Toch bewijst het gebruik van zo’n methode zijn nut omdat je enkel één lijn code moet toevoegen aan de job die het formulier oproept. En gezien het formulier vanop verschillende plaatsen kan opgeroepen worden, kan je nu de code in slechts één plaats beheren.

MORPHX IT Formulieren

© 2007 Steen Andreasen

197

Overladen van methodes Als je formulierelementen bij runtime hebt toegevoegd aan je formulieren, krijg je mogelijk situaties waarin het nuttig kan zijn om de methodes van het runtime formulier-element te overschrijven. Het overschrijven van methodes van een dynamisch formulierelement is mogelijk, als je dit kenmerk op het formulier activeert en een methode maakt op het formulier voor iedere methode die je wil overschrijven. Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_OverloadMethod Menu item display, MyForm_OverloadMethod Job, Forms_OverloadMethod

Kopieer MyForm, hernoem het formulier “MyForm_OverloadMethod” en maak een menuoptie voor het nieuwe formulier. public void init() { super(); element.controlMethodOverload(true); } In de init() methode van een nieuw formulier moet je de optie activeren voor het overschrijven van methodes van bij runtime toegevoegde formulierelementen. De code voor de overschreven methodes moet toegevoegd worden aan het formulier. De syntax is: <formulierelementnaam>_<formulierelementmethode te overschrijven>. Dit betekent dat je de naam van het toe te voegen formulierelement voor runtime moet kennen. In dit geval zal de modified() methode van een nieuw formulierelement genaamd showOnlyOpenOrders worden overschreven : Boolean showOnlyOpenOrders_modified() { FormCheckBoxControl checkBoxControl = this.controlCallingMethod(); QueryBuildRange rangeSalesStatus; Boolean ret; ; ret = checkBoxControl.modified(); rangeSalesStatus = SalesTable_ds.query().dataSourceTable(tablenum(SalesTable)).findRange(fieldnum(SalesTable, salesStatus)); if (!rangeSalesStatus) { rangeSalesStatus = SalesTable_ds.query().dataSourceTable(tablenum(SalesTable)).addRange(fieldnum(SalesTable, salesStatus)); } if (checkBoxControl.value() == NoYes::Yes) { rangeSalesStatus.enabled(true); rangeSalesStatus.value(queryValue(SalesStatus::Backorder)); }

MORPHX IT Formulieren

198

© 2007 Steen Andreasen

else { rangeSalesStatus.enabled(false); } SalesTable_ds.executeQuery(); return ret; } Een nieuw vinkvakje wordt toegevoegd aan het formulier. Dit vinkvakje wordt gebruikt om enkel de open verkoopsorders te tonen. De code in deze methode wordt opgeroepen wanneer de waarde van het vinkvakje wordt gewijzigd. Met de oproep van this.controlCallingMethod() wordt een hendel naar het nieuwe vinkvakje gemaakt. Dit is belangrijk omdat je de super() oproep van de gewijzigde methode manueel moet doen. Je methode moet tevens dezelfde teruggeefwaarde hebben als de overschreven methode. Opmerking: Je kan de methodes voor formulierelementen toegevoegd bij runtime, plaatsen in een klasse met behulp van de FormRun methode controlMethodOverloadObject(). In de methode wordt een nieuw bereik toegevoegd om te filteren op de verkoopsorderstatus. Omdat deze methode meermaals kan worden opgeroepen, wordt het rangeSalesStatus object eerst geïnitialiseerd door te zoeken in de query naar een bestaand bereik op het veld salesStatus. Als er geen bereik is gevonden, wordt de methode voor de eerste keer uitgevoerd, en een nieuw bereik wordt toegevoegd. Indien het vinkvakje wordt aangevinkt, wordt het bereik geactiveerd en er wordt gefilterd op open verkoopsorders. Als het vinkvakje wordt uitgevinkt, wordt het bereik gedesactiveerd. static void Forms_OverloadMethod(Args _args) { Args args; FormRun formRun; FormCheckBoxControl ctrlShowOnlyOpenOrders; ; args = new Args(); formRun = new menuFunction(menuItemDisplayStr(MyForm_OverloadMethod), MenuItemType::Display).create(args); formRun.init(); formRun.design().columns(1); ctrlShowOnlyOpenOrders = formRun.design().addControl(FormControlType::CheckBox, "showOnlyOpenOrders"); ctrlShowOnlyOpenOrders.label("Only open orders"); formRun.design().moveControl(ctrlShowOnlyOpenOrders.id()); formRun.run(); } Het formulier wordt opgeroepen met behulp van een job. Na het initialiseren van het formulier en het activeren van de optie om de methodes te overschrijven, wordt het

MORPHX IT Formulieren

© 2007 Steen Andreasen

199

vinkvakje toegevoegd. Let erop dat de naam van het nieuwe formulierelement moet overeenstemmen met de prefix van de formuliermethode gebruikt om modified() te overschrijven. Een label wordt toegevoegd aan het formulierelement. Om dit voorbeeld eenvoudig te houden maken we geen gebruik van het labelsysteem. Om het nieuwe vinkvakje bovenaan het formulier te plaatsen, moet het kolomnummer op de formulieropmaak de waarde 1 krijgen. Omdat een nieuw formulierelement altijd als laatste element wordt toegevoegd, wordt de opmaakmethode moveControl() gebruikt om het vinkvakje als eerste formulierelement te plaatsen. MoveControl() heeft twee parameters, en de tweede parameter geeft aan achter welk formulierelement het betreffende element moet geplaatst worden. Omdat het vinkvakje het allereerste formulierelement moet zijn, wordt de tweede parameter niet ingevuld. Het overschrijven van methodes van een dynamisch formulierelement is niet optimaal omdat je dan een methode op het formulier moet creëren voor iedere methode die moet overschreven worden. Bij het toevoegen van dynamische formulierelementen weet je niet altijd het effectieve aantal toe te voegen elementen voor runtime; soms moet je een aantal formulierelementen toevoegen aan meerdere formulieren. Een alternatief is het toevoegen van een menuoptie bij runtime. Het toevoegen van een menuoptie die een uitvoerbare klasse oproept, stelt je in staat om al je logica in de oproepende klasse te verzamelen. Dit geeft je niet dezelfde mogelijkheden als het overschrijven van methodes van een formulierelement, maar in de meeste gevallen zal het beantwoorden aan je vereisten. Algemene Formulierwijzigingen Telkens een formulier wordt uitgevoerd, wordt de klasse SysSetupFormRun opgeroepen door de klasse ClassFactory. SysSetupFormRun is een subklasse van de systeemklasse FormRun. Dit betekent dat je de mogelijkheid hebt om een hendel te maken op FormRun wanneer een formulier wordt uitgevoerd. Als je SysSetupFormRun wijzigt, kan je tijdens de runtime wijzigingen aanbrengen aan eender welk formulier zonder dat je hiervoor het formulier moet aanpassen. Dit is een bijzonder handige optie die veel codering kan voorkomen. Onthou wel dat zulke wijzigingen extra vertraging met zich meebrengen omdat de code wordt opgeroepen telkens een formulier wordt opgeladen. Je moet dus voorzichtig zijn met je wijzigingen van SysSetupFormRun. Probeer altijd je wijzigingen uit in een lokale installatie van Axapta. Als je fouten in je code schrijft, kan je de hele applicatie op de knieën krijgen, en dit wordt niet bepaald gewaardeerd in een productieomgeving. En niet alleen de normale applicatieformulieren krijgen hiervan de weerbots. Alle MorphX tools gemaakt door de AOT zoals het compileerformulier en het zoekformulier delen dan in de klappen. Elementen gebruikt van MORPHXIT_Forms project

Class, SysSetupFormRun Voeg een nieuwe methode toe met de naam showDatasourceInfo aan de klasse SysSetupFormRun:

MORPHX IT Formulieren

200

© 2007 Steen Andreasen

void showDatasourceInfo() { FormDatasource formDatasource; FormGroupControl formGroupControl; FormStaticTextControl formStaticTextControl; Counter counter; Counter noOfDatasources; ; noOfDatasources = this.form().dataSourceCount(); if (noOfDatasources) { formGroupControl = this.form().design().addControl(FormControlType::Group, "formGroupControl"); formGroupControl.caption("Tables used by form"); formGroupControl.frameType(3); } for (counter=1; counter <= noOfDatasources; counter++) { formStaticTextControl = formGroupControl.addControl(FormControlType::StaticText, "formStaticTextControl" + int2str(counter)); formStaticTextControl.text(tableid2name(this.form().dataSource(counter).table())); } } De methode drukt de namen van tabellen af die het formulier als gegevensbron gebruikt. Een veldgroep wordt toegevoegd aan het formulier, en voor iedere gegevensbron zal een formulierelement van het type “statische tekst” de gegevensbrontabelnamen tonen. De veldgroep wordt enkel toegevoegd indien het formulier een gegevensbron heeft. void new(Args args) { KMActionMenuButtonAuto KMActionMenuButtonAuto; CCDatalinkMenuButtonAuto CCDatalinkMenuButtonAuto; KMKnowledgeCollectorMenuButtonAuto KMKnowledgeCollectorMenuButtonAuto; ; super(Args); // CC Start if (this.name() != formStr(SysLicenseCode)) { KMActionMenuButtonAuto = new KMActionMenuButtonAuto(this); if (KMActionMenuButtonAuto.check()) KMActionMenuButtonAuto.create(); CCDatalinkMenuButtonAuto= new CCDatalinkMenuButtonAuto(this); if (CCDatalinkMenuButtonAuto.check()) CCDatalinkMenuButtonAuto.create(); KMKnowledgeCollectorMenuButtonAuto= new KMKnowledgeCollectorMenuButtonAuto(this); if (KMKnowledgeCollectorMenuButtonAuto.check()) KMKnowledgeCollectorMenuButtonAuto.create(); } // CC End this.showDatasourceInfo(); }

MORPHX IT Formulieren

© 2007 Steen Andreasen

201

De methode wordt opgeroepen vanuit de new() methode in de klasse SysSetupFormRun. Het uitvoeren van een formulier voegt automatisch een veldgroep met kader toe waarin de tabellen vermeld worden die het formulier gebruikt. Dit is een eenvoudig voorbeeld van hoe je algemene wijzigingen aan formulieren kunt aanbrengen. Neem een kijkje bij de andere code in de new() methode. Deze code wordt gebruikt in de HRM modules om automatisch knoppen toe te voegen aan formulieren. De klasse CCDataLinkMenuButtonAuto wordt gebruikt om knoppen aan een formulier toe te voegen tijdens runtime. Dit HRM kenmerk noemen we Data links en wordt gebruikt om een Axapta record te koppelen aan een externe databank. Als een formulier gebruik maakt van een gegevensbron, geconfigureerd als data link , dan wordt bij runtime een knop toegevoegd aan het formulier. Met deze knop wordt dan een ander formulier opgeroepen die de gerelateerde records van de externe databank toont. Kleuren Een typisch formulier in Axapta gebruikt een standaardset van formulierkleuren. Enkel op een beperkt aantal plaatsen in het standaardpakket zijn de formulierelementen in andere kleuren weergegeven. Het wijzigen van de standaardkleuren van een formulier maakt het meer gebruiksvriendelijk. Het kleuren van een formulierelement is een alternatief voor het sorteren of filteren van gegevens omdat het eenvoudiger wordt een element met een bepaalde waarde op te sporen. Elementen gebruikt van MORPHXIT_Forms project

Form, MyForm_ColorRow Form, MyForm_ColorColumn Menu item display, MyForm_ColorRow Menu item display, MyForm_ColorColumn

Maak twee kopieën van MyForm genaamd “MyForm_ColorRow” en “MyForm_ColorColumn”. Ga naar het formulier MyForm_ColorRow en overschrijf de SalesTable gegevensbronmethode displayOption() met de volgende code: public void displayOption(Common _record, FormRowDisplayOption _options) { SalesTable salesTableLocal = _record; ; if (salesTableLocal.currencyCode == CompanyInfo::find().currencyCode) { _options.backColor(WinApi::RGB2Int(255, 255 , 255)); // White } else { _options.backColor(WinApi::RGB2Int(255, 0, 0)); // Red } super(_record, _options); }

MORPHX IT Formulieren

202

© 2007 Steen Andreasen

De methode displayOption wordt uitgevoerd voor ieder record in het formulier bij het laden van dit formulier. Je gebruikt de methode om de standaardkleuren van een formulierelement te wijzigen. In dit voorbeeld kleuren records rood als de munteenheid van het verkoopsorder verschilt van de bedrijfsmunteenheid. Merk op dat je de kleuren moet instellen voor alle records en niet alleen de records die rood moeten kleuren. Het voorbeeld hierboven kleurde alle formulierelementen van een record. Wijzig de displayOption() methode in het formulier MyForm_ColorRow als volgt : public void displayOption(Common _record, FormRowDisplayOption _options) { SalesTable salesTableLocal = _record; ; if (salesTableLocal.currencyCode == CompanyInfo::find().currencyCode) { _options.backColor(WinApi::RGB2Int(255, 255 , 255)); // White } else { _options.backColor(WinApi::RGB2Int(255, 0, 0)); // Red _options.affectedElementsByControl(SalesTable_SalesId.id(), SalesTable_CurrencyCode.id()); } super(_record, _options); } In vergelijking met het voorgaande voorbeeld zie je dat de methode affectedElementsByControl() wordt opgeroepen. Deze methode zorgt ervoor dat enkel de formulierelementen doorgegeven als parameters aan affectedElementsByControl(), van kleur veranderen. Je kan eender welk aantal formulierelementen doorgeven om te kleuren. Maar onthoud dat de formulierelementen als auto declared moeten gedefinieerd zijn. In de voorbeelden werden de criteria en de kleuren hard gecodeerd. In een productieomgeving zou je dit soort opties open moeten laten voor de applicatiegebruikers. In de HRM modules maakt het formulier EmplTable gebruik van gekleurde rijen. De kleuren gebruikt in EmplTable, zijn gedefinieerd door de applicatiegebruiker in het parameterformulier HRMParameters.

6.6 Samenvatting Dit hoofdstuk toonde je het gebruik van de diverse onderdelen van een formulier zoals de gegevensbronnen en de formulierelementen in een formulieropmaak. Je zou nu moeten weten hoe je gegevens uit de databank ophaalt en de gegevens beheert in een formulier. Je moet nu ook kennis hebben van het maken van formulieren met een gestandaardiseerd uitzicht. Omdat formulieren het hoofddeel zijn van de gebruikersinterface, is een gestandaardiseerde uitzicht en werking van je formulieren essentieel omdat dit gebruiksvriendelijker is.

MORPHX IT Rapporten

© 2007 Steen Andreasen

203

7 Rapporten Rapporten in Axapta zijn gebaseerd op een query en een ontwerp die beheerd worden in de Application Object Tree (AOT). Een rapport is enkel een definitie, het bevat geen gegevens en bij uitvoer zal het de gegevens die het nodig heeft, ophalen uit de databank. Als je een rapport uitvoert, heb je de optie om het rapport direct af te drukken, dan wel een batch job te definiëren om het rapport later uit te voeren op een afzonderlijke batch server. Stapelverwerking of batch processing wordt gebruikt voor lange rapporten zoals het maandelijks afdrukken van de klantenbalanslijsten. Axapta rapporten zijn zeer flexibel omdat MorphX tools voorziet om de rapportdefinitie te overschrijven zonder dat je hiervoor complexe programmatie moet doen. Door het gebruik van de MorphX omgeving, kan je data filteren of zelfs de opmaak van de printopdracht bij runtime wijzigen. Er zijn twee manieren om rapporten aan te maken in Axapta. Je kan de ingebouwde report wizard gebruiken, of je gebruikt de rapportgenerator, die je kan vinden in de AOT onder Reports. Dit hoofdstuk spitst zich toe op de technische aspecten van het aanmaken van rapporten en is niet bedoeld om de verschillende opties van het rapportdialoogvenster te behandelen. Hoewel het natuurlijk handig is als je de gebruikersinterface van Axapta begrijpt, moet je de report user interface niet begrijpen om je voordeel te halen uit de informatie in dit hoofdstuk. Als je niet vertrouwd bent met de eindgebruikersinterface voor rapporten, krijg je meer gedetailleerde informatie door de handleidingen in het standaardpakket na te lezen.

7.1 Report Wizard De Report wizard is een hulpmiddel ontworpen om niet-technische personen toe te laten rapporten aan te maken. De wizard kan gestart worden via de toolbar menu Tools | Development tools | Wizards | Report Wizard. Dit is een goed startpunt om te leren werken met Axapta rapporten. De Report wizard is een eindgebruikerstool die je door de verschillende stappen gidst om een rapport aan te maken. Je hebt de mogelijkheid om een rapport, aangemaakt door de wizard, te bewaren in de AOT. Nieuwe Axapta-programmeurs kunnen best deze objecten, aangemaakt door de wizard, nakijken om zo vertrouwd te geraken met de standaard elementen in een rapport. Maar ook voor ervaren Axaptaprogrammeurs is de Report Wizard een goed hulpmiddel : ze kunnen eerst de report wizard gebruiken om de basisstructuur van het rapport te maken en daarna de rapportgenerator voor de laatste wijzigingen. Voor een stap-per-stapgids over de werking van de Report wizard verwijs ik naar de Bijlage Report wizard.

MORPHX IT Rapporten

204

© 2007 Steen Andreasen

7.2 Het maken van rapporten Als je de output bekijkt die door de report wizard wordt gegenereerd, kan je zien wat er vereist is om rapporten vanaf nul te bouwen met behulp van de rapportgenerator. Je kan best starten bij enkele standaard rapporten in de AOT om te zien of je een bestaand rapport kan vinden dat al aan sommige vereisten voldoet. Kopieer dit rapport en wijzig de kopie waar nodig. Als je wil weten hoe je een rapport op een menu kan terugvinden in de AOT, kijk dan in het hoofdstuk Menu’s en menuopties. Als je start met de rapportgenerator, kunnen de handleidingrapporten of tutorial reports in het standaardpakket je ook verder helpen. Zoek in de AOT naar rapporten waarvan de naam begint met tutorial_. Axaptarapporten bestaan uit twee onderdelen. Ieder van die onderdelen wordt in de AOT voorgesteld als een knoop of node. De Data Sources knoop definieert welke gegevens er opgehaald moeten worden en de Designs knoop bepaalt de opmaak en presentatie van het rapport. Figuur 32 rapportoverzicht toont een overzicht van een doorsneerapport.

Figuur 32: Rapportoverzicht

MORPHX IT Rapporten

© 2007 Steen Andreasen

205

Voorbeeld 1: mijn eerste rapport Elementen gebruikt van het MORPHXIT_Reports project

Report, MyReport Menu item output, MyReport

Als eerste oefening ga je een rapport maken zoals getoond wordt in Figuur 32 rapportoverzicht. Het rapport drukt klantentransacties af, gegroepeerd per klant. Het voorbeeld wordt eenvoudig gehouden omdat het de bedoeling is de basisstappen te leren die nodig zijn om een rapport aan te maken. Verderop in dit hoofdstuk worden de details uitgelegd en meer elementen worden toegevoegd aan het voorbeeld. 1. Om een nieuw rapport aan te maken, selecteer je de Reports knoop in de AOT , klik

met de rechtermuisknop en selecteer New Report. Een nieuw rapport met de naam "Report1" wordt aangemaakt. Open het eigenschappenblad en geef het rapport de naam "MyReport".

2. Voor dit rapport haal je je informatie uit de Customer hoofdtabel samen met de

bijbehorende transactiegegevens van iedere klant. Dit bereik je door twee niveaus van gegevensbronnen voor de query te definiëren. Voor dit voorbeeld is de klantentabel op het eerste niveau en de klantentransactietabel is op het tweede niveau. Het ontwikkelen in Morphx gebeurt vaak via het verslepen van elementen (“drag and drop”). In dit geval ga je tabellen van de ene sectie van de AOT verslepen naar de gegevensbron van het rapport waar je mee bezig bent. Om de taak van het verslepen zo eenvoudig mogelijk te maken, laat Axapta je toe om verschillende zichten van de AOT openen. In dit geval open je een nieuw zicht van de AOT, en klik door tot de Data Dictionary/Tables knoop. Selecteer de tabel CustTable. Open de Data Sources/Query knoop in je rapport en versleep de CustTable naar de Data Sources/Query/Data Sources knoop. Nu heb je het eerste niveau van de query toegevoegd. Klik de CustTable data sources knoop in je rapport open. Versleep de tabel CustTrans naar de CustTable/Data Sources knooppunt.

3. De twee gegevensbronnen van de query moeten gekoppeld worden om te vermijden

dat alle transacties van de Customer Transaction tabel voor iedere klant worden afgedrukt. Ga naar de CustTrans knoop in de Query en zet de eigenschap Relations op “Yes.” De knoop CustTrans/Relations bevat nu een element dat beide tabellen aan elkaar koppelt. Het rapport drukt nu enkel de transacties af die behoren bij de klant die door het rapport behandeld wordt.

4. De query is nu klaar om gegevens op te halen voor het rapport; nu moeten we de

presentatie van de gegevens aanpakken. Ga naar de Designs knoop, klik met de rechtermuisknop en kies New Report Design. Een nieuw ontwerp met de naam "ReportDesign1" wordt aangemaakt. Navigeer naar de ReportDesign1 knoop en selecteer de Caption eigenschap; geef hier de tekst "Customer transactions list" in.

5. Ga naar de ReportDesign1/AutoDesignSpecs knoop. Selecteer de knoop, klik met de

rechtermuisknop en selecteer Generate Specs From Query. Je ontwerp omvat nu twee secties, één voor elke tabel in de query.

MORPHX IT Rapporten

206

© 2007 Steen Andreasen

6. De laatste stap is de selectie van de velden die afgedrukt moeten worden. Selecteer

de Query knoop van je rapport, klik met de rechtermuisknop en kies Open New Window. Dit vereenvoudigt het verslepen van de af te drukken velden naar het ontwerp. Kies de velden AccountNum en Name van de CustTable gegevensbron en versleep de velden één voor een naar de sectieknoop CustTable_Body. Ga naar de CustTrans gegevensbron en versleep de velden Voucher, TransDate en AmountMST naar de sectie CustTrans_Body.

7. Je hebt nu een rapport zoals getoond in Figuur 32. Om het rapport uit te voeren,

selecteer je de rapportnaam, klik met de rechtermuisknop en kies Open. Axapta toont een dialoogvenster voor het filteren, sorteren en andere afdrukopties. Klik op OK. Het volgende dialoogvenster is de printerdialoog. Kijk na of de printout verwijst naar “Screen” en klik op OK. Je rapport wordt nu afgedrukt naar het scherm.

8. Zoals je wellicht hebt opgemerkt, laat de opmaak te wensen over. Veel van de

formatteringstaken kan je overlaten aan MorphX door aan te geven dat het rapport gebruik moet maken van een voorgedefinieerde rapportsjabloon. Sjablonen geven MorphX de instructie om standaardrapportkenmerken aan te maken zoals hoofdingen. Om een sjabloon toe te voegen aan je rapport, ga je naar de Designs/reportDesign1 knoop en zoek naar de eigenschap ReportTemplate. Klik op de pijl en kies de sjabloon InternalList.

9. Voer het rapport opnieuw uit door stap 7 te volgen. Het rapport heeft nu

hoofdinginformatie zoals de rapportnaam, paginanummer, datum en tijd. Je hebt je eerste rapport gemaakt !

In het voorbeeld MyReport heb je niet één enkele lijn code moeten schrijven. Als je rapporten maakt in Axapta, moet je geen code schrijven voor dataconnecties en het positioneren van rapportelementen in de opmaak. MorphX doet dit allemaal voor je. Maak een query en selecteer de kolomvolgorde van je rapportelementen in het ontwerp. Je moet enkel gebruik maken van X++ als je meer complexe rapporten maakt waar het ophalen van gegevens te moeilijk is voor een query of als je een speciale opmaak nodig hebt. Als je het rapport uitvoerde, werden er twee dialoogvensters getoond. Probeer een menupunt te maken voor MyReport door MyReport te verslepen naar de Menu Items/Output knoop. Je kan nu je rapport uitvoeren door het menupunt te selecteren, met de rechtermuisknop te klikken en vervolgens Open te kiezen. De informatie die voordien in twee verschillende dialoogvensters werd getoond, zie je nu in één enkel venster. Het uitvoeren van een rapport via een menupunt activeert automatisch een meer verfijnde rapportraamwerk bij uitvoering. Dit is de dialoog die de gebruikers zullen zien. Meer details worden later in dit hoofdstuk behandeld.

MORPHX IT Rapporten

© 2007 Steen Andreasen

207

7.3 Rapportquery Er zijn situaties waarin een query niet voldoende is voor je vragen en je de gegevens moet ophalen met behulp van X++. Maar meestal haal je de gegevens voor een rapport op met een query die de gegevensbronnen voor het rapport definieert en aangeeft hoe deze met elkaar verbonden zijn. De rapportgenerator gebruikt een standaard Axapta query. Voor meer informatie over de opbouw van een query verwijs ik naar het hoofdstuk Queries. Voor je de query opbouwt, moet je beslissen welke tabellen je nodig hebt. Het is vaak het geval, zoals in het voorbeeld van MyReport, dat je gegevens moet afdrukken uit een enkel formulier of een reeks van aanverwante formulieren. In dit geval moet je de formulieren zelf nakijken om de namen van de benodigde tabellen op te zoeken. Voor uitleg over het opzoeken van tabellen en velden op formulieren, verwijs ik naar het hoofdstuk Menuopties en menu’s. Nadat je de vereiste tabellen hebt bepaald, moet je nagaan hoe de gegevens gesorteerd moeten worden. Vervolgens moet je de gegevens filteren om overbodige records van het rapport uit te sluiten. De beslissing die je hier maakt, kan een grote invloed hebben op de systeemperformantie. Wat bijkomende planning op dit moment kan de uitvoertijd van het rapport aanzienlijk beperken. Zo zijn de selectiecriteria meer efficiënt als je ze plaatst op de tabel op het hoogste niveau in de gegevensbron. Je kan als algemene regel hanteren dat je het gebruik van de selectiecriteria moet beperken tot de eerste twee niveau’s van de gegevensbron. Als je rapport gegevens filtert op het derde niveau van de query, moet je je ontwerp herbekijken en proberen een meer doeltreffende manier te vinden. Krijg je hetzelfde resultaat door het gebruik van twee aparte rapporten ? Indien niet, kan je dan gebruik maken van een tijdelijke tabel om het gewenste resultaat te bekomen ? Voor de meeste rapporten zal je alle vereiste tabellen toevoegen aan de Query knoop van het rapport. Als gegevens van gerelateerde tabellen worden gebruikt, moeten de tabellen gekoppeld worden in de query, zoals je dat hebt gedaan met CustTable en CustTrans in het MyReport voorbeeld. Soms is het nodig om gegevens op te halen uit twee tabellen zonder gemeenschappelijk veld. In dit geval heb je geen relatie die kan dienen als koppeling. Dan moet je zoeken naar een andere tabel die een relatie heeft met beide tabellen waarop je je rapport wil baseren. Een voorbeeld hiervan is de afdruk van verkooporderlijnen gegroepeerd per klant. Er is geen directe relatie tussen de Customer tabel en de Sales Invoice Lines tabel. Daarom moet je de Customer Invoice Journal tabel gebruiken om het rapport te creëren. Zie Figuur 33 : Relatie tussen CustTable en CustInvoiceTrans. Je kan de drie tabellen toevoegen aan de query, of je kan enkel de tabel van het eerste niveau CustTable toevoegen als een gegevensbron en X++ gebruiken om de twee andere tabellen op te halen. Men geeft er doorgaans de voorkeur aan om een query te gebruiken in plaats van X++ aangezien deze werkwijze de gebruiker de voordelen biedt van de rapportdialoogvensters.

MORPHX IT Rapporten

208

© 2007 Steen Andreasen

C ustTable

C ustomers

77 dat

InvoiceAccount C ustInvoiceJour

Customer invoice journal

62 dat

+CustInvoiceJour ����������� ��

�������������� ��

64 dat Figuur 33: Relatie tussen CustTable en CustInvoiceTrans Het koppelen van gegevensbronnen kan op twee manieren gebeuren. Als de gegevensbronnen al een relatie hebben, dan moet de eigenschap Relation van de gegevensbron op het lagere niveau de waarde True hebben, zoals in MyReport. De relatie zal dan zichtbaar zijn onder de Relations knoop voor de gekoppelde gegevensbron. Als er geen relatie getoond wordt, moet je de relatie zelf aanmaken onder de Relations knoop en de eigenschap Relation moet de waarde False hebben. Het is aangewezen om een reeds bestaande relatie te gebruiken, eerder dan je eigen relatie aan te maken, omdat wijzigingen aan de data dictionary automatisch meegenomen worden in het rapport. De standaardkoppeling voor gegevensbronnen is de inner join, maar de join mode kan gewijzigd worden in de eigenschappen van de gekoppelde gegevensbron. Inner joins worden vaak gebruikt in bedrijfsrapportering waar je gegevens hebt in een hoofdtabel en je wil alle bijbehorende transacties afdrukken. Als je echter alle records van de hoofdtabel wil afdrukken, ook als er geen bijbehorende transacties bestaan, dan moet je join mode wijzigen naar OuterJoin op de transactietabel. Als je de Fields knoop selecteert en met de rechtermuisknop klikt, kan je een veld of een aggregatiefunctie toevoegen. Normaal worden alle velden van de huidige tabel onder deze knoop opgelijst en dus heeft het geen zin om bijkomende velden van de tabel toe te voegen. Als je een aggregatiefunctie toevoegt, worden alle velden verwijderd omdat je ze niet allebei kan gebruiken. Om de aggregatiefuncties te verwijderen, en de veldlijst te herstellen, verander je de Dynamic eigenschap van de Fields knoop in Yes. De aggregatiefuncties kunnen gebruikt worden als je het aantal klanten per klantgroep wil tellen. Je moet een tabel selecteren, een aggregatiefunctie kiezen en de gewenste velden. Voorbeeld 2: Aggregatiefunctie Elementen gebruikt van het MORPHXIT_Reports project

Report, MyReport_aggregate Menu item output, MyReport_aggregate

Het volgende voorbeeld telt het aantal klanten per klantgroep. Het ontwerp is vereenvoudigd om de aandacht toe te spitsen op de aggregatiefuncties. 1. Voeg de Customer tabel toe aan de report query. Navigeer dan naar de Fields knoop,

klik met de rechtermuisknop en selecteer de aggregatiefunctie Count. De waarde van het count field moet AccountNum zijn.

2. Zet de eigenschap OrderMode van de CustTable data source op Group by. Als

laatste stap moet je aangeven hoe de informatie gesorteerd moet worden. Ga naar de Sorting knoop en voeg het veld CustGroup toe. Je hebt nu een query zoals getoond in Figuur 34: Aggregatiefunctie.

MORPHX IT Rapporten

© 2007 Steen Andreasen

209

Figuur 34: Aggregatiefunctie

3. De volgende stap is het maken van een ontwerp om het resultaat af te drukken.

Creëer een auto design en kies voor Generate Specs From Query zoals dat gebeurd is in het MyReport voorbeeld. Je hebt nu een sectie voor CustTable met een rapportelement voor het afdrukken van het veld CustGroup. Voeg het veld AccountNum toe.

4. Voer het rapport uit. Een rij zal gedrukt worden voor iedere klantengroep. Het veld

AccountNum telt het aantal klanten in iedere klantengroep . Bij het gebruik van aggregatiefuncties moeten gegevens geselecteerd worden waarbij de “group by” parameter de waarde OrderMode heeft. De compiler zal een foutmelding geven als je de “order by” wil selecteren. Dit is logisch als je bedenkt dat informatie record per record wordt opgehaald bij het gebruik van “order by”. Als je OrderMode gebruikt als waarde voor de “group by” parameter, zal MorphX een enkel record ophalen voor iedere groep gebaseerd op de sorteervelden. Dit betekent dat alleen de velden aangeduid als sorteervelden een waarde zullen bevatten bij het gebruik van “group by”. Je kan zoveel aggregatiefuncties toevoegen als je nodig hebt. Zo kan je een transactielijst afdrukken met een aggregatie voor het minimum, maximum en gemiddelde bedrag. De Sorting knoop onder de Data Sources knoop wordt gebruikt om aan te geven hoe de output van het rapport gesorteerd moet worden. Hierbij kan je gebruik maken van indexen of velden. Je moet minstens één index of sorteerveld aanduiden. Bij het uitvoeren van het rapport kan de gebruiker de sorteervelden wijzigen. Bedenk dat het gebruik van een veld voor sorteren in plaats van een index de uitvoering van je rapport kan doen vertragen. De sorteervelden hebben een eigenschap met de naam AutoSum. Deze eigenschap wordt gebruikt als je wil dat MorphX subtotalen afdrukt bij het wijzigen van de waarde van het sorteerveld. Auto Sums worden meer gedetailleerd uitgelegd als we Auto design behandelen. Bereiken worden gebruikt om de records te filteren. De standaard bereiken worden gespecifieerd in de Range knoop onder de data source knoop. Bij uitvoer van het rapport krijgt de gebruiker de kans om nog extra bereiken toe te voegen of de standaard bereiken te verwijderen, afhankelijk van de waarden van de eigenschap van het bereik. Zo kan je voor een bereik een standaardwaarde opgeven en aanduiden of de gebruiker deze opgegeven waardes nog mag wijzigen. De eigenschappen kunnen ook aangeven of het bereik vergrendeld of verborgen moet zijn. Als er geen bereik wordt opgegeven, zal het eerste element van iedere index voor de tabel gebruikt worden als standaardbereik bij het uitvoeren van het rapport. Bij wijze van test kan je het rapport MyReport

MORPHX IT Rapporten

210

© 2007 Steen Andreasen

uitvoeren. Je zal zien dat een standaardset van bereiken is toegevoegd. Voeg nu het veld AccountNum toe aan de Data sources/CustTable/Ranges knoop. Als je MyReport nu uitvoert, zal alleen het bereik AccountNum getoond worden.

7.4 Sjablonen In Axapta heb je twee verschillende types sjablonen : de rapportsjablonen en de sectiesjablonen. De sjablonen verschijnen als de eerste twee elementen onder de Report knoop in de AOT. Rapportsjabloon Rapportsjablonen worden gebruikt om de basisformattering van een rapport te definiëren, zoals de hoofding en voetregel. Je kan sjablonen maken voor complexere gevallen zoals het gebruiken van gegevens van specifieke tabellen. Maar hiermee beperk je natuurlijk ook het aantal gevallen waarin je dit sjabloon kan gebruiken. Rapportsjablonen worden doorgaans gebruikt voor informatie die geen betrekking heeft op een specifieke tabel, zoals een titel, paginanummering en lijnen. De sjabloon InternalList gebruikt in het MyReport voorbeeld, is een algemeen gebruikt rapportsjabloon. Dit sjabloon formatteert de rapportnaam, bedrijfsnaam, paginanummer, datum en tijd. Om de sjabloon te bekijken, zoek je ze op in de AOT, selecteer de sjabloonknoop, klik met de rechtermuisknop en kies Edit om de visuele editor te openen. Als je een rapportsjabloon aanmaakt met controls van een specifieke tabel, dan zal ieder rapport, gebaseerd op dit sjabloon, toch nog expliciet de tabel moeten declareren en de vereiste records ophalen. Voorbeeld 3: Rapportsjabloon Elementen gebruikt van MORPHXIT_Reports project

Report template, MyInternalList Report, MyReport_MyInternalList

In dit voorbeeld maak je een nieuw sjabloon, gebaseerd op de InternalList sjabloon. InternalList bevat de basisopmaak van een hoofding. Je gaat een proloog- en een epiloogsectie toevoegen aan de nieuwe sjabloon. Het MyReport voorbeeld wordt uitgebreid met de nieuwe rapportsjabloon. 1. Maak eerst een kopie van MyReport en hernoem het nieuwe rapport

“MyReport_MyInternalList”. 2. Ga naar de AOT en zoek het rapportsjabloon InternalList. Klik met je

rechtermuisknop op de rapportsjabloon en kies Duplicate. Hernoem de nieuwe rapportsjabloon “MyInternalList”.

3. Klik met de rechtermuisknop op de naam van de rapportsjabloon; kies New en

selecteer de rapportsectie Prolog. De proloog zal een tekst en een nieuw paginabegin bevatten. Eerst moet de tekst die je gaat printen, worden gedefinieerd.

MORPHX IT Rapporten

© 2007 Steen Andreasen

211

Ga naar Prolog/Methods; klik hierop met je rechtermuisknop en kies New Method. Open de nieuwe methode en typ het volgende in:

display description prologDescription() { return strfmt("Start of report: %1", element.design().lookupCaption()); }

Deze methode zal een “Start of report” string opleveren die de waarde van de titel van het rapportontwerp bevat. Deze methode moet dan aangeroepen worden in het proloogontwerp. Sluit de editor en sleep de methode naar de Prolog knoop. Axapta zal een string maken, die de teruggeefwaarde van de displaymethode afdrukt.

4. Klik met je rechtermuisknop op de naam van de rapportsjabloon, kies New en selecteer de rapportsectie Epilog. Creëer nu de volgende methode en sleep de methode naar de Epilog knoop.

display description epilogDescription() { return strfmt("End of report: %1", element.design().lookupCaption()); }

5. Als je de proloog- en epiloogsecties wil afdrukken op nieuwe pagina’s, moet je een

nieuw paginabegin toevoegen. Ga naar Prolog/Methods; klik met je rechtermuisknop en kies Override Method en selecteer executeSection(). Merk op dat de oproep van de newPage methode geplaatst is na de oproep van super(). Hierdoor zal een nieuwe pagina beginnen nadat de proloogsectie afgedrukt is.

public void executeSection() { super(); element.newPage(); }

6. Voeg een nieuw paginabegin toe aan de epiloogsectie. Het nieuw paginabegin moet

uitgevoerd worden voor de super() methode in de epiloogsectie, omdat de epiloog moet geprint worden op een nieuwe pagina. Je moet nu een rapportsjabloon hebben zoals afgebeeld in Figuur 35 : rapportsjabloon.

MORPHX IT Rapporten

212

© 2007 Steen Andreasen

Figuur 35: Rapportsjabloon

7. De volgende stap is de nieuwe rapportsjabloon gebruiken in

MyReport_MyInternalList. Ga naar de knoop Designs/ReportDesign1; open het eigenschappenblad en selecteer MyInternalTemplate als de rapportsjabloon.

8. Creëer een nieuwe menuoptie voor het rapport. Wanneer het rapport wordt

uitgevoerd, zal er een pagina voor de proloog voor het rapport en een pagina voor de epiloog na het rapport worden afgedrukt.

De MyInternalList sjabloon gebruikt displaymethodes om de waarden voor de rapportelementen terug te geven. Net zoals in het geval van formulieren, gebruik je regelmatig displaymethodes als je een rapport maakt of aanpast. Dit is één van de manieren om gegevens af te drukken die je niet gemakkelijk kan ophalen met een query. In dit voorbeeld gebruik je de displaymethode om een string terug te geven, in andere gevallen kan het een resultaat van een berekening zijn. Je maakt gewoon de displaymethode en sleept de methode naar het ontwerp. Je moet je geen zorgen maken over het veldtype om de juiste waarde weer te geven. MorphX doet dit voor jou door het teruggeeftype van de methode na te kijken. Je moet overwegen of je één of twee rapportsjablonen gaat gebruiken voor de meeste van je rapporten. Het voordeel van het gebruiken van één rapportsjabloon is dat het je toelaat om de basisopmaak van je rapporten te standaardiseren. Als je later besluit om een rapportsjabloon aan te passen, zullen alle rapporten met auto-ontwerp die dit sjabloon gebruiken, automatisch mee veranderen. Opmerking: Het is vaak nodig om zowel de paginanummer af te drukken als het totaal aantal pagina’s. Het sjabloon InternalList gebruikt element.page() om de huidige paginanummer af te drukken. De methode element.pagesTotal() geeft het totaal aantal af te drukken pagina’s weer. Element.pagesTotal() kan alleen gebruikt worden als teruggeefwaarde voor een displaymethode met teruggeeftype integer. Het totaal aantal pagina’s wordt berekend tijdens het uitvoeren van het rapport dus kan je de methode niet gebruiken voor validaties. Om <page> of <page total> af te drukken, zal je gebruik moeten maken van 3 rapportelementen.

MORPHX IT Rapporten

© 2007 Steen Andreasen

213

Sectiesjabloon De sectiesjablonen zijn pas geïntroduceerd in versie 3.0. Dit is de reden waarom je geen voorbeelden van hun gebruik zal vinden in het standaardpakket. Een sectiesjabloon moet gebaseerd zijn op een tabelkoppelvlak. Tabelkoppelvlakken worden uitgelegd in het hoofdstuk Data Dictionary. Velden van het koppelvlak kunnen toegevoegd worden als rapportelementen. In het geval dat je rapporten met gelijkaardige sectieblokken hebt, kan je dankzij een sectiesjabloon hetzelfde stuk code herbruiken in plaats van dezelfde sectieblok telkens weer op te bouwen in verschillende rapporten. Niettemin kan het in praktijk gemakkelijker zijn om één rapport te maken en X++ te gebruiken om de output aan te passen in plaats van twee rapporten te gebruiken met een sectiesjabloon. Het SalesInvoice rapport is hiervan een uitstekend voorbeeld.

7.5 Ontwerpen Het plaatsen van tabelvelden en rapportelementen in je ontwerp wordt gedaan door MorphX. Alle rapportelementen zullen automatisch naar de standaard worden aangepast. Dit betekent dat de rapportelementen ingesteld worden op automatisch positioneren. Het lettertype en de lettergrootte worden bepaald door de gebruikersopties in de nutsbalkmenu Tools/Options in het tabblad Fonts. Wanneer je de volgorde van de rijen kiest, zal MorphX de rapportelementen positioneren, gebaseerd op de uitgebreide datatypes. Dit kan zeer handig zijn : als je ooit een rapportelement moet toevoegen in het midden van een rij of je wil een rapportelement verbergen, dan worden de volgende rapportelementen overeenkomstig geherpositioneerd. Voor de meeste rapporten moet je Morphx toelaten om de rapportelementen zelf te plaatsen. In situaties waar je rapportelementen altijd een vaste positie moeten hebben, kan je de standaard instellingen overschrijven. Dit heeft wel één nadeel : als je één enkel rapportelement op een vaste positie in een rij zet, dan moet je vaste posities definiëren voor alle rapportelementen. Dit is meestal niet aan te raden, tenzij je rapportelementen in de opmaak van een voorgedrukt document moeten passen of moeten beantwoorden aan specificaties van klanten, verkopers of de overheid. Als je rapportelementen moet afdrukken die onder elkaar zijn gepositioneerd in dezelfde kolom, kan je de eigenschap ModelFieldname gebruiken (alle rapportelementen hebben deze eigenschap). De positie van het huidige rapportelement zal zich aanpassen aan de positie van het rapportelement bepaald in ModelFieldName, als de positionering van het huidige rapportelement op automatisch wordt gezet. Het ontwerp maken Een rapport kan meer dan één ontwerp hebben. Onder de Design knoop kan je zoveel ontwerpen maken als je nodig hebt. Dit kan zeer nuttig zijn als je een document hebt dat je wil afdrukken met een verschillende opmaak voor elke taal of voor elke klantengroep. Verschillende ontwerpen in één rapport worden niet vaak gebruikt in het standaardpakket. In plaats van te werken met verschillende ontwerpen, kan je de nood aan verschillende layout oplossen met X++. Een voorbeeld hiervan is het Sales Invoice

MORPHX IT Rapporten

214

© 2007 Steen Andreasen

rapport. In het SalesInvoice rapport zal de methode element.changeDesign() ervoor zorgen dat er wel of niet een rapportelement zal afgedrukt worden. Het vraagt dikwijls meer tijd om verschillen in meerdere ontwerpen na te kijken dan één ontwerp te manipuleren met X++. Het onderhoud van de hoofdingsecties in verschillende ontwerpen is vervelend werk, omdat het tijd vraagt om na te gaan of je veranderingen hetzelfde zijn in alle ontwerpen. Opmerking : Als je een rapport maakt zoals een document dat moet passen in een voorgedrukte layout, kan het nodig zijn om de laatste aanpassing te doen met gebruik van de specifieke printer driver die gebruikt zal worden om voor de productieoutput te zorgen. Variaties in printers kunnen ervoor zorgen dat velden op een andere plaats worden afgedrukt. Vaak zal de opmaak aangepast worden naar gelang de gebruikte printer driver. Ontwerpen kunnen gemaakt worden als auto-ontwerp of gegenereerd ontwerp. Een ontwerp kan ook zowel een auto-ontwerp als een gegenereerd ontwerp bevatten. In dit geval wordt alleen het gegenereerd ontwerp gebruikt. Het grote verschil tussen de twee is dat auto-ontwerpen alle mogelijkheden van MorphX gebruiken : ze laten dynamische sjablonen toe, auto-hoofdingen en auto-sommen gebaseerd op de criteria die je in de query hebt vastgelegd. Gegenereerde ontwerpen zijn statisch en zullen zich niet automatisch aanpassen aan de veranderingen die je maakt in de query of de rapportsjabloon. Het wordt aangeraden om auto-ontwerpen te gebruiken. Je kan overwegen gebruik te maken van gegenereerde ontwerpen maar dan enkel in speciale gevallen waar een vaste layout vereist is. Gegenereerde ontwerpen zijn doorgaans enkel nodig wanneer de opmaak vastgelegd is door een contract of statuut , of wanneer je gebruik moet maken van voorgedrukte formulieren zoals cheques en aankooporders. Gegenereerde ontwerpen hebben extra secties om paginahoofdingen en –voetregels toe te voegen aan de middensectie. Voor de rest gebruiken auto-ontwerp en gegenereerd ontwerp dezelfde soort secties. Zie figuur 36 Rapportontwerpsecties voor een overzicht van de secties in rapportontwikkeling Type Omschrijving Proloog (Prolog )

Dit is de eerste sectie die afgedrukt wordt. De proloog wordt meestal gebruikt om een logo of een titel op de eerste pagina af te drukken.

Paginahoofding (Page Header) De paginahoofding wordt bovenaan op elke pagina

afgedrukt. Een rapport kan meer dan één paginahoofding hebben.

Midden (Body) De middensectie wordt afgedrukt na de

paginahoofding. Dit is de gegevenssectie. Het rapport zal normaal gezien een middensectie bevatten voor elke gegevensbron.

Paginavoetregel (Page Footer) De voetregel wordt onderaan elke pagina

afgedrukt. Een rapport kan meer dan één paginavoetregel hebben.

Epiloog (Epilog) Dit is de laatste pagina die afgedrukt wordt.

Programmeerbare sectie (Programmable Section) Programmeerbare secties worden uitgevoerd op

basis van code. Dit type van secties kan gebruikt

MORPHX IT Rapporten

© 2007 Steen Andreasen

215

Type Omschrijving worden in gevallen , waar je gegevens moet afdrukken die geen deel zijn van de query.

Sectiesjabloon (section template) Sectiesjablonen worden gebruikt om veelvuldig

gebruikte gegevens te bepalen; het wordt meestal gebruikt in middensecties. Een sectie is gebaseerd op een koppelvlak (Map).

Hoofding (Header) Hoofding wordt gebruikt in gegenereerde

ontwerpen als hoofding van de middensectie.

Sectiegroep (Section Group) In gegenereerde ontwerpen wordt de middensectie toegevoegd aan een sectiegroep.

Voetregel (Footer) Voetregel wordt gebruikt in gegenereerde

ontwerpen als voetregel voor een middensectie.

Figuur 36: Rapportontwerpsecties Je kan bij benadering een beeld van het rapport zien als je view selecteert. De view optie kan bijna hetzelfde zijn als het resultaat op papier. Maar als een rapport een complex ontwerp heeft zoals het SalesInvoice rapport, kan het moeilijk zijn om uit te puzzelen hoe het resultaat eruit zal zien als het afgedrukt is. Je hebt twee opties om rapportelementen toe te voegen aan je ontwerp, ofwel door het gebruik van de knopen van de rapportboom, die je hebt gezien in voorgaande voorbeelden, ofwel door de visuele editor te gebruiken. De visuele editor geeft de optie om de rapportelementen in je ontwerp te zien of aan te passen. Om een rapport te editeren met de rapportboom, moet je dubbelklikken op de ontwerpknoop; als je de visuele editor wil gebruiken, klik je met je rechtermuisknop op de ontwerpknoop en kies edit. Zoals de view optie in het rapport, kan de visuele editor moeilijk zijn om te gebruiken in complexe rapporten, maar het kan gebruikt worden wanneer je rapporten maakt met een relatief eenvoudige opmaak. Om een ontwerp te maken, biedt de visuele editor dezelfde hoofdkenmerken als de AOT. Via de visuele editor kan je de eigenschappen van een element in een rapport veranderen en rapportelementen toevoegen en verwijderen. Wil je een rapport aanpassen via de visuele editor, dan positioneer je de cursor en klik je met je rechtermuisknop op de menu om een element aan te passen, te verwijderen of toe te voegen. Wil je de eenheid van de liniaal veranderen, dan klik je erop met je rechtermuisknop en kies tussen centimeters, inches en chars.

MORPHX IT Rapporten

216

© 2007 Steen Andreasen

Figuur 37: De visuele editor In de praktijk is de visuele editor het best om een overzicht te krijgen van je ontwerp of om de eigenschappen van een specifiek rapportelement op te vragen en te veranderen. De visuele editor is relatief traag en de meeste dingen kunnen sneller gedaan worden wanneer je werkt met een rapportboom. Auto-ontwerp De meest gebruikelijke weg om de opmaak voor je rapporten te maken is door het gebruiken van auto-ontwerp. Wanneer je auto-ontwerp gebruikt, moet je alleen een rapportsjabloon kiezen en kiezen welke velden van de query moeten afgedrukt worden. MorphX zal voor de formattering van je opmaak zorgen. Als je rapport gehele of reële getallen bevat, zal de gebruiker de optie hebben om een optelling tijdens de uitvoering te kiezen. Voor een snelle start met auto-ontwerp klik je met je rechtermuisknop op de auto design knoop en kies Generate Specs From Query. Een middensectie zal dan gemaakt worden voor elke gegevensbron in de query en de sorteervelden zullen toegevoegd worden als rapportelementen aan het ontwerp. Om het visueel voor te stellen, klik met je rechtermuisknop op de auto design knoop en selecteer view. Open MyReport in de visuele editor. Merk op dat de visuele editor de secties toont van de rapportsjabloon, ook al zijn de sjabloonsecties geen deel van de rapportknopen. Dit geeft een nuttig overzicht van het rapport. Om het rapport aan te passen, klik je met je rechtermuisknop en kies edit. In editeermode zullen alleen de knopen die deel zijn van het rapport, toegankelijk zijn. Autosommen zijn een nuttig kenmerk van auto-ontwerpen : het staat de gebruiker toe, bij het uitvoeren van het rapport, om aan te geven waar er subtotalen moeten berekend worden. Als je bijvoorbeeld een overzicht wil van de verkoopstransacties van het voorbije kwartaal, kan je de records uit de verkoopstransactietabel sorteren op klantrekeningnummer en transactiedatum. Het rapport kan dan een totaal per klant per transactiedatum berekenen, een totaal per klant en een algemeen totaal. Door het

MORPHX IT Rapporten

© 2007 Steen Andreasen

217

sorteren van de records krijg je dus drie niveau’s waarop je totalen kan berekenen. Deze niveau’s noemen we de sorteerbreekpunten. In de rapportdialoog kan je zowel sorteerbreekpunten instellen voor subtotalen voor elk veld van een middensectie als het totaal vragen voor het gehele rapport. Vanuit het standpunt van de applicatiegebruiker kan het de belangrijkste reden zijn om auto-ontwerp te gebruiken. Het maakt je rapport meer flexibel en elimineert de meeste inspanningen van het programmeren die anders noodzakelijk zijn om deze sommen te coderen. Voorbeeld 4: Autosom Elementen gebruikt van het MORPHXIT_Reports project

Report, MyReport_Sums Menu item output, MyReport_Sums

Breid MyReport uit met totalen voor transactiebedragen. Een subtotaal voor elke klant en een totaal voor alle klanten zal toegevoegd worden. 1. Start met het kopiëren van MyReport en geef het nieuwe rapport de naam

“MyReport_Sums”. 2. Omdat je een index niet kan gebruiken om sorteerbreekpunten in te stellen als de

waarde van een indexveld wijzigt, kan je best de AccountIdx verwijderen uit de sorteerknoop van de CustTable gegevensbron. In plaats daarvan specifieer je het AccountNum veld. Zet de eigenschap Autosum op Yes voor het veld AccountNum. Je hebt nu bepaald dat een subtotaal zal afgedrukt worden telkens de waarde van een klantenrekening verandert.

3. Je moet nu nog opgeven welke velden je wil optellen. In dit voorbeeld zal alleen het veld AmountMST van de tabel CustTrans gebruikt worden. Zoek het rapportelement dat AmountMST afdrukt in de CustTrans middensectie; open het eigenschappenblad en zet SumAll op Yes.

4. Maak een nieuw menuoptie voor het rapport en laat het rapport lopen. Voor elke

klant zal een subtotaal geprint worden. 5. In dit geval wordt er alleen een subtotaal afgedrukt. Om een totaal voor het hele

rapport toe te voegen, sluit je het rapport en ga terug naar de AOT. Ga naar de middensectie van de CustTable en zet de eigenschap GrandTotal op Yes . De middensectie CustTrans heeft deze eigenschap niet, alleen de CustTable wel omdat deze de primaire gegevensbron is.

Stap 2 specifieerde het rapportelement dat bepaalt wanneer er subtotalen in het rapport worden afgedrukt, maar niet welke velden worden opgeteld. Het ontwerp specifieert welke velden moeten opgeteld worden,en hier is dat AmountMST. Dit zijn de enige vereiste instellingen. De gebruiker zal de rest kunnen doen tijdens de uitvoering. Het sorteerveld, gebruikt om te bepalen wanneer er een subtotaal berekend wordt, en de instellingen voor het algemeen totaal bepalen alleen de standaardinstellingen van het rapport en kunnen veranderd worden tijdens de uitvoering van het rapport.

MORPHX IT Rapporten

218

© 2007 Steen Andreasen

De knoop AutoDesignSpecs heeft een eigenschap GrandTotal. Deze eigenschap zal een totaal afdrukken voor het rapport met als standaard label “SuperGrandTotal”, als de eigenschap op Yes wordt gezet. SuperGrandTotal en het algemeen totaal, ingesteld via de rapportdialoog of de middensectie, zullen altijd hetzelfde resultaat geven. Beiden zullen een totaal voor het hele rapport afdrukken. Dus als de gebruiker de optie heeft om het volledige totaal in stellen via de rapportdialoog, moet je de super grand total niet gebruiken. De waarde van een autosom kan beheerd worden via element.sumControl(). Om het rapportelement “autosom” voor CustTrans.AmounMST in het voorgaande voorbeeld te beheren, moet je code er als volgt uitzien :

element.sumControl(identifierstr(CustTrans_AmountMST), element.indent()); Element.sumControl geeft de gesommeerde waarde terug. De eerste parameter is de naam van het gesommeerde veld. De systeemfunctie identifierstr() wordt gebruikt om te verhinderen dat de “best practice check” een waarschuwing geeft. Gebruik steeds element.indent() als tweede parameter om het juiste indentatieniveau te bepalen. Zoals je misschien hebt opgemerkt, is er een eigenschap auto-hoofding. Dit wordt op dezelfde manier gebruikt als auto-som. In plaats van totalen af te drukken, zal een hoofding worden afgedrukt elke keer als een sorteerveld van waarde verspringt. De gebruiker kan de auto-hoofding controleren tijdens de uitvoering , maar als het nodig is, kan je instellen dat de autohoofdingen standaard zichtbaar moeten zijn. Autosommen en autohoofdingen zijn kenmerken die alleen beschikbaar zijn in auto-ontwerpen. Alle secties die tot nu toe vermeld zijn, starten ofwel door het rapportraamwerk ofwel door de rapportquery. Je zal situaties hebben waar je een rapportsectie manueel zal moeten starten. In die situatie maak je gebruik van programmeerbare secties die in X++ opgeroepen worden. Voorbeeld 5: Programmeerbare sectie Elementen gebruikt van MORPHXIT_Reports project

Report, MyReport_ProgSec Menu item output, MyReport_ProgSec

Je gaat een programmeerbare sectie toevoegen aan MyReport. Voor de eenvoud zal het rapport alleen een tekstueel rapportelement afdrukken. 1. Start met het kopiëren van MyReport en hernoem het nieuwe rapport

“MyReport_ProgSec”. Ga naar de knoop AutoDesignSpecs, klik met je rechtermuisknop op de knoop en kies een nieuwe ProgrammableSection.

2. Open het eigenschappenblad voor de nieuwe programmeerbare sectie en zoek de

eigenschap ControlNumber. Het ControlNumber wordt in X++ gebruikt als referentie voor de sectie. Stel het ControlNumber in op 10.

MORPHX IT Rapporten

© 2007 Steen Andreasen

219

3. Voeg nu een rapportelement toe aan de programmeerbare sectie. Klik met je rechtermuisknop op de programmeerbare sectie en kies New Control om een tekstueel rapportelement of text control toe te voegen. Ga naar het nieuwe rapportelement, zoek de eigenschap Text en typ “Hoofding voor klanten” in.

4. Definieer nu de uitvoering van de programmeerbare sectie. Ga naar

MyReport_ProgSec/Methods, klik met je rechtermuisknop en kies Override Method en selecteer init(). De init() methode moet er als volgt uitzien:

Public void init() { super(); element.execute(10); } 5. Maak een menupunt voor het rapport MyReport_ProgSec. Wanneer

MyReport_ProgSec wordt uitgevoerd, zal de tekst “Hoofding voor klanten” afgedrukt worden vooraleer de query start.

In het voorbeeld heb je de nummer van de programmeerbare sectie in 10 gewijzigd. Het is aangewezen dat je openingen laat in de volgorde van de nummers die je toekent aan de programmeerbare secties. Op deze manier kan je later de logische volgorde bewaren als je een nieuwe programmeerbare sectie moet toevoegen. De uitvoering van een programmeerbare sectie kan opgeroepen worden vanuit X++ waar nodig. Maar als je het gebruikt in combinatie met autosommen, zijn er dingen waarvoor je moet opletten: stel dat je een programmeerbare sectie wil afdrukken voor de middensectie wordt geprint, dan is de logische plaats om je code toe te voegen in de executeSection() methode juist voor super() in de middensectie. Dit zal je programmeerbare sectie afdrukken voor de middensectie wordt afgedrukt, maar de programmeerbare sectie zal ook uitgevoerd worden voor elke autosom. Tijdens de uitvoering zal MorphX een autosom als een voetregel behandelen en dit zorgt ervoor dat de middensectie opnieuw zal uitgevoerd worden. De oplossing is om in dit geval de rapportmethodes header() of footer() te gebruiken. Hier kan je controleren welke middensectie zal worden uitgevoerd. Wanneer een middensectie wordt uitgevoerd, zullen de parameters _tableId en _fieldId een waarde bevatten. public void header(ReportSection _headerSection, tableId _tableId, fieldId _fieldId) { if (_tableId == tableNum(custTable) && _fieldId) element.execute(10);

super(_headerSection, _tableId, _fieldId); } Hier wordt de header() methode gebruikt. Er wordt nagekeken of het de middensectie is van de klantentabel die afgedrukt wordt. Is dit het geval, dan wordt de programmeerbare sectie uitgevoerd. Een andere manier om de header() en footer() methode te gebruiken, is het toevoegen van een nieuwe pagina na de som als je een groep verkopers- of klantentransacties wil afdrukken.

MORPHX IT Rapporten

220

© 2007 Steen Andreasen

Opmerking : Programmeerbare secties worden vaak gebruikt om scheidingen in te voegen zoals blanco rijen of lijnen. Dit kan je doen door de eigenschappen van de programmeerbare sectie in te stellen. Je zal een “dummy” of leeg rapportelement moeten toevoegen aan je programmeerbare sectie, omdat de sectie niet zal afgedrukt worden als je programmeerbare sectie geen rapportelementen heeft. gegenereerd ontwerp Het gebruik van gegenereerde ontwerpen kan op het eerste zicht eenvoudiger lijken dan auto-ontwerpen: je hebt immers meer secties die je kan gebruiken om je rapport te maken en alle secties zijn zichtbaar. Maar gegenereerde ontwerpen zijn meer statisch, het ontwerp is gebaseerd op de instellingen van de query en de eigenschapinstellingen van de ontwerpknoop. Het nadeel hiervan is het volgende : als je een rapportsjabloon hebt gekozen voor je rapport en later beslis je het rapportsjabloon te wijzigen of een ander sjabloon te kiezen, dan zal je ontwerp niet automatisch aangepast worden. Bovendien heeft de gebruiker niet de mogelijkheid om te kiezen voor het berekenen van totalen of subtotalen bij het uitvoeren van het rapport. Om het gegenereerd ontwerp van naderbij te bekijken, gebruiken we MyReport voor het aanmaken van een gegenereerd ontwerp. Klik met de rechtermuisknop op de Designs/AutoDesign1 knoop in MyReport en kies Generate Design. Een nieuwe knoop met de naam Generated Design is nu aangemaakt onder Designs/AutoDesign1. Als je het gegenereerde ontwerp openvouwt, zal je zien dat een ontwerp gelijkaardig aan het auto-ontwerp is aangemaakt. Het verschil is dat de secties van het rapportsjabloon en de secties voor het berekenen van totalen, zijn toegevoegd. Als je een overzicht wil van je rapport bij het gebruik van auto-ontwerpen, kan je handig gebruik maken van de optie voor de creatie van een gegenereerd ontwerp gebaseerd op je auto-ontwerp. Alle secties gebaseerd op je sjablonen en de auto-sommen worden immers ingevuld. Rapportelementen in het ontwerp De meest eenvoudige manier om rapportelementen toe te voegen aan je ontwerp, is het verslepen van velden of displaymethodes vanaf een gegevensbron of rechtstreeks van een tabel. Als je een basistype veld of displaymethode versleept zoals string, enum, integer, real, date, time, zal MorphX automatisch een rapportelement van hetzelfde type aanmaken. Rapportelementen zoals prompt, shape, sum en field group worden gebruikt voor meer speciale doeleinden en moeten manueel worden toegevoegd. Je kan natuurlijk alle types van rapportelementen manueel toevoegen, maar het werkt sneller door de elementen te verslepen omdat MorphX het element automatisch positioneert en de eigenschappen invult met een referentie naar het betreffende veld of displaymethode. Auto-ontwerpen en gegenereerde ontwerpen hebben dezelfde rapportelementen. Voor een overzicht van de beschikbare elementen verwijzen we naar Figuur 38 : rapportelementen.

MORPHX IT Rapporten

© 2007 Steen Andreasen

221

Naam Omschrijving String

Gebruikt voor alfanumerieke waardes. Bij het afdrukken van memovelden, zal de eigenschap DynamicHeight de hoogte van het rapportelement aanpassen aan het aantal lijnen dat wordt afgedrukt.

Enum

Gebruikt voor het afdrukken van basisenums.

Integer

Gebruikt voor gehele getallen.

Real Gebruikt voor reële getallen.

Date Gebruikt voor het afdrukken van datums. Datums worden geformatteerd op basis van de Windows regionale instellingen.

Time Gebruikt voor het afdrukken van Tijd. Tijd wordt

geformatteerd op basis van de Windows regionale instellingen.

Text Gebruikt voor het afdrukken van vastetekstwaarden. Als de

tekst een dynamische waarde moet bevatten, worden displaymethodes die een string teruggeven, gebruikt.

Prompt Prompt voegt tekst toe gevolgd door puntjes en een dubbele punt.

Shape Tekent een rechthoek. Grootte en positie worden bepaald door

de eigenschappen. Kan gebruikt worden voor het opbouwen van de opmaak van een formule.

Bitmap Gebruikt voor grafische gegevens. Geef het pad op van de

bitmap, verwijs naar een container of gebruik resources. Een voorbeeld van het gebruik van een resource als bitmap vind je in het rapport HRMApplicantStatus.

Sum Gebruikt voor het afdrukken van sommen in gegenereerde

ontwerpen. Met auto-ontwerpen kunnen sommen gebruikt worden voor het afdrukken van een som in een programmeerbare sectie. Een voorbeeld van het gebruik in auto-ontwerp vind je in het rapport SalesLinesExtended.

Field group Gebruikt voor het toevoegen van een field group van de data

dictionary. De field group of veldengroep op het rapport wordt automatisch aangepast als de field group in de data dictionary wordt gewijzigd.

Figuur 38: Rapportelementen Bitmap rapportelementen zijn een beetje lastig in het gebruik. Er zijn verschillende manieren om een bitmap te configureren. Je kan pictogrammen gebruiken uit de Axapta resources. Ofwel geef je de resource id in het eigenschappenblad in of je maakt een displaymethode die de resource id teruggeeft. Voor een overzicht van de resources kan je gebruik maken van het rapport tutorial_Resources. Dit rapport drukt de resource id af en het bijbehorende pictogram. Je kan ook het pad van de bitmap opgeven of verwijzen

MORPHX IT Rapporten

222

© 2007 Steen Andreasen

naar een bitmap die is opgeslagen in een container. Als je het pad opgeeft, moet je een dubbele backslash gebruiken. Het rapportelementen voor sommatie in gegenereerde ontwerpen wordt gebruikt in voetregelsecties. Bij gebruik in auto-ontwerpen moet dit sommatie-element in een programmeerbare sectie geplaatst worden. Opmerking: Bij het afdrukken van een rapport kan de infolog melden dat de schaal van het rapport is gewijzigd om op één pagina te passen. Dit wordt veroorzaakt door teveel kolommen in je ontwerp. Wijzig de eigenschap FitToPage in No op de design knoop om herschaling te voorkomen. Bij het toevoegen van rapportelementen aan je ontwerp, moet je erop toezien dat de elementen die verwijzen naar een gegevensbron, opgehaald worden op het moment dat de sectie wordt afgedrukt. In het MyReport voorbeeld kan je geen rapportelement van de middensecties CustTable en CustTrans in een paginahoofdingsectie afdrukken : bij het verwerken van de paginahoofding, heeft MorphX immers de informatie geassocieerd met de middensecties, nog niet opgehaald. Hetzelfde geldt voor het toevoegen van een rapportelement aan de middensectie CustTable met verwijzing naar een veld van CustTrans. Dit zal eveneens een foutmelding geven omdat CustTable wordt opgehaald vóór CustTrans.

7.6 Methodes in een Rapport Je kan eenvoudige rapporten maken, zoals een lijst van voorraadartikels, zonder dat je X++ code moet schrijven. Hiervoor moet je enkel gebruik maken van de mogelijkheden van de rapportgenerator. Voor moeilijker rapporten die gegevens filteren gebaseerd op een parameter van een oproepend programma of die een bepaalde sortering voorzien, moet je de rapportmethodes overschrijven met je eigen X++ code. Een overzicht van de methodes in een rapport vind je terug in Figuur 39: Rapportmethodes. De querymethodes worden beschreven in het hoofdstuk Queries. De rapportsysteemklassen worden vaak gebruikt voor het wijzigen van rapporten tijdens de uitvoering. Deze klassen zijn de basisrapportcomponenten en zij staan de programmeur toe om alle aspecten van een rapport tijdens de uitvoering te herdefiniëren. Je kan een rapport van nul af opbouwen met behulp van de systeemklassen. Meer informatie over deze systeemklassen vind je terug in het hoofdstuk Klassen. Naam Parameters Omschrijving CallMenuFunction

MenuFunction _menuFunction Webmethode.

Caption

str _reportSpelling, str _reportName, str _designCaption, str _designName

Gebruikt voor het bepalen van de titel van het rapport. De parameters _reportSpelling en _designCaption bepalen de titels in het “Print to Screen” venster.

CreateProgressForm

Overschrijft het standaard

voortgangsformulier dat getoond wordt als de rapportpagina’s worden

MORPHX IT Rapporten

© 2007 Steen Andreasen

223

Naam Parameters Omschrijving aangemaakt. De methode voorziet in de mogelijkheid om je eigen voortgangsformulier te maken.

Dialog

Object _dialog Dialog() wordt gebruikt als je velden

toevoegt aan het rapportdialoogvenster. Het report runbase raamwerk zal het dialoogvenster oproepen als het rapport wordt uitgevoerd. Zie ook bij report KMAction.

Fetch

Deze methode is de motor van het

rapport. Fetch() opent de dialoog met de gebruiker, selecteert de records van de databank door het verwerken van de query en stuurt de records naar de printer. Deze methode wordt meestal overschreven als een expressie niet in de query kan worden gespecifieerd. Een voorbeeld hiervan vind je terug in het rapport HRMCourseSkills bij het afdrukken van de detailinformatie.

Footer

ReportSection _footerSection, tableId _tableId, fielded _fieldId

Deze methode wordt opgeroepen telkens als een sectie in het ontwerp wordt uitgevoerd. Gezien auto-som geen deel is van het ontwerp, geeft dit de mogelijkheid om code uit te voeren voor of na het afdrukken van de auto-som.

GetTarget

Geeft het geselecteerde printmedium terug.

Header

ReportSection _headerSection, tableId _tableId, fieldId _fieldId

Deze methode wordt opgeroepen telkens als een sectie in het ontwerp wordt uitgevoerd. Gezien auto-som geen deel is van het ontwerp, geeft dit de mogelijkheid om code uit te voeren voor of na het afdrukken van de auto-som.

Init

Deze methode wordt als eerste

opgeroepen om het rapport te initializeren. Entiteiten die in het rapport worden gebruikt, worden hier geïnitialiseerd. Een voorbeeld daarvan vind je terug in het rapport salesFreightSlip.

New

anytype _argsOrReportOrContainer, str _designName, boolean _isWebReport=FALSE

Gebruikt om een reportRun object te initializeren. Dit wordt normaal niet gedaan door de rapportgenerator. Meestal gebruik je deze methode als een rapport wordt geïnitialiseerd

MORPHX IT Rapporten

224

© 2007 Steen Andreasen

Naam Parameters Omschrijving vanuit X++.

Pack

Deze methode wordt gebruikt voor het

opslaan van de laatste gebruikerwaardes. Ze wordt gebruikt in samenhang met unpack(), die de laatst opgeslagen waarde ophaalt. Maar unpack() is geen basismethode. Als een nieuw dialoogveld wordt toegevoegd, wordt pack() overschreven om de waardes van het dialoogvenster te bewaren. Een voorbeeld vind je terug in het rapport KMAction.

PageFormatting

Deze methode wordt niet meer

gebruikt. Vanaf versie 3.0 wordt de method PrintJobSetttings.PageFormatting() in plaats daarvan gebruikt.

Print

Print() geeft het aantal af te drukken

bladzijden weer. Deze method kan gebruikt worden om na te gaan of er bladzijden afgedrukt moeten worden. Zie in het rapport projTimeSheetEmpl.

PrinterSettings

int _showWhat=-1 Gebruikt om de verschillende delen

van de printerdialoog te activeren. Deze methode wordt alleen opgeroepen als het report runbase raamwerk niet actief is. In feite wordt deze methode opgeroepen vanuit de prompt() methode.

ProgressInfo

int _pageNo, int _lineNo

ProgressInfo wordt opgeroepen voor iedere lijn die afgedrukt wordt.

Prompt

boolean _enableCopy=TRUE, boolean _enablePages=TRUE, boolean _enableDevice=TRUE, boolean _enableProperties=TRUE, boolean _enablePrintTo=TRUE

Voor versie 3.0 handelde prompt() de rapportdialoog af. Voortaan wordt de dialog() methode gebruikt. De methode kan niet gebruikt worden in combinatie met het report runbase raamwerk aangezien het raamwerk alle instellingen zal overschrijven. In plaats daarvan moet je de klasse PrintJobSettings gebruiken.

Run

Run() wordt opgeroepen als OK knop

wordt aangeklikt in het dialoogvenster. Run() voert de volgende stappen uit :

• Als er geen gegenereerd ontwerp bestaat, wordt er een ontwerp aangemaakt op basis van auto design.

• Call fetch() • Call print()

MORPHX IT Rapporten

© 2007 Steen Andreasen

225

Naam Parameters Omschrijving De methode kan gebruikt worden voor het toevoegen van bereiken aan de query na de “Based On settings” in de dialoog. Zie verder in het rapport ReqPO.

Send

Send() is aanverwant aan fetch().

Fetch() gaat door de query records, en send() stuurt de records naar het ontwerp. De methode kan overschreven worden om te bepalen of een record al dan niet afgedrukt moet worden. Een voorbeeld vind je terug in het rapport CustTransList.

SetTarget

PrintMedium _target Stelt het doelmedium van het rapport

in.

ShowMenuFunction

MenuFunction _menuFunction Webmethode.

ShowMenuReference

WebMenu _menuReference Webmethode.

Title

str _title="" Wordt niet vaak meer gebruikt. Het kan de titelbalk van een schermafdruk overschrijven als het wordt uitgevoerd door fetch().

ExecuteSection

Iedere sectie in het ontwerp heeft de

executeSection methode, die gebruikt wordt om de sectie af te drukken. De methode kan gebruikt worden om te bepalen of de sectie al dan niet moet afgedrukt worden. Een voorbeeld vind je terug in het rapport CustCollectionJour.

Figuur 39: Rapportmethodes De vorige onderdelen van dit hoofdstuk legden de nadruk op de verschillende onderdelen die een rapport uitmaken. Het wordt nu tijd om na te kijken hoe je X++ kan gebruiken om je rapporten te wijzigen.

MORPHX IT Rapporten

226

© 2007 Steen Andreasen

Report Runbase Raamwerk Je hebt je misschien afgevraagd waarom je soms verschillende dialoogvensters krijgt bij het uitvoeren van rapporten binnen Axapta. Als een rapport wordt uitgevoerd vanuit de AOT, krijg je eerst de querydialoog en daarna de printerdialoog. Als een rapport wordt uitgevoerd vanaf een menuoptie, krijg je maar één dialoogvenster te zien. In versie 3.0 van Axapta is de nieuwe runbase klasse runbaseReportStd ingevoerd. Als een rapport wordt opgeroepen vanaf een menuoptie, dan wordt RunbaseReportStd opgeroepen vanaf de klasse SysReportRun. Dit Report Runbase Raamwerk is vaak een bron van verwarring bij nieuwe Axapta- programmeurs. Het is belangrijk te begrijpen dat een rapport kan opgeroepen worden op vier manieren :

1. direct vanuit de rapportknoop in de AOT; 2. via een menuoptie hetzij op een gebruikersmenu hetzij in de AOT; 3. opgeroepen door een klasse die erft van runBaseReport; 4. opgeroepen vanuit X++.

De klasse RunbaseReportStd wordt alleen opgeroepen door het Report Runbase Raamwerk als je rapport niet wordt opgeroepen door een klasse die erft van de RunBaseReport klasse. Maar als een rapport wordt uitgevoerd vanuit de Reports knoop, wordt het Runbase Report Raamwerk niet uitgevoerd, en worden de twee dialoogvensters getoond. Opmerking: Rapporten worden vaak gebruikt om de gegevensintegriteit van het systeem te controleren. Hoewel het nuttig kan zijn om rapporten de gegevens te laten aanpassen, is het toch aangewezen om rapporten niet naar de databank te laten schrijven. Als je rapport records moet wijzigen of toevoegen, kan je dit best doen door een klasse te schrijven die de gegevensmanipulatie uitvoert. Deze klasse moet dan opgeroepen worden door een klasse die erft van RunBaseReport . Het is aangewezen om altijd een menuoptie te maken dat je rapport uitvoert; zo krijg je hetzelfde dialoogvenster te zien dat getoond wordt aan de gebruikers als zij een rapport lanceren vanuit één van hun menu’s. Onthoud dat een rapport uitgevoerd door een klasse, altijd moet erven van de klasse RunBaseReport. De klasse RunbaseReportStd wordt enkel gebruikt door het runbase raamwerk. Meer informatie over de runbase klassen vind je terug in het Klassen hoofdstuk.

Figuur 40 : Runbase rapportklassen Bij de creatie van een rapport in vorige versies van Axapta, was het de gewoonte dat een rapport moest opgeroepen worden vanuit een geërfde RunBaseReport klasse. Dit werd gedaan om de twee dialoogvensters, die hierboven vermeld werden, op te vangen. Zodoende was het mogelijk om het rapport in stapelverwerking (batch) uit te voeren. Dit zorgde ook voor een betere performantie omdat de klasse uitgevoerd kon worden op de server. Met de invoering van de RunBaseReportStd klasse, zouden enkel rappporten met een zware databankbelasting mogen erven van de RunBaseReport klasse.

MORPHX IT Rapporten

© 2007 Steen Andreasen

227

Voorbeeld 6: Report runbase Elementen gebruikt van het MORPHXIT_Reports project

Class, SalesReport_DailyEntries Report, SalesDailyEntries Menu item output, SalesReport_DailyEntries

Om het Report Runbase raamwerk te verkennen, navigeer je naar de Classes knoop en zoek de klasse SalesReport_DailyEntries op. Deze klasse erft van de RunBaseReport klasse en wordt daarom gebruikt om een rapport op te roepen. Dit is een gebruikelijke rapportklasse met in de naam een prefix die verwijst naar de module. Je zal veel gelijkaardige rapportklassen zien wanneer je de toepassingsklassen doorloopt. SalesReport_DailyEntries roept het rapport SalesDailyEntries op. In feite is deze klasse niet nodig sinds de introductie van Axapta 3.0, aangezien de logica van de klasse afgehandeld kan worden door de klasse RunBaseReportStd. Niettemin is de klasse SalesReport_DailyEntries een goed voorbeeld van hoe je een rapportklasse moet bouwen. De klasse heeft de volgende methodes: public identifiername lastValueElementName() { return reportstr(SalesDailyEntries); } Hier wordt de naam van het rapport gespecifieerd zodat de method overschreven moet worden. De functie reportstr() garandeert dat de rapportnaam die je ingeeft een geldige rapportnaam is. client server public static ClassDescription description() { return "@SYS77491"; } Deze optionele methode definieert de titelnaam van de rapportdialoog.

static void main(Args args) { SalesReport_DailyEntries salesReport_DailyEntries; ; salesReport_DailyEntries = new salesReport_DailyEntries(); if (salesReport_DailyEntries.prompt()) { salesReport_DailyEntries.run(); } } De main() methode is een statische methode die de klasse initialiseert. Hierdoor kan je de klasse oproepen via een menuoptie. Het systeem kijkt na of het dialoogvenster wordt opgeroepen. Als OK wordt aangeklikt in het dialoogvenster, wordt het rapport uitgevoerd.

MORPHX IT Rapporten

228

© 2007 Steen Andreasen

Om de klasse in het rapport te kunnen gebruiken, moet een variabele van de SalesReport_DailyEntries klasse gedeclareerd worden in de klassedeclaratie voor het SalesDailyEntries rapport. public class ReportRun extends ObjectRun { SalesReport_DailyEntries salesReport_DailyEntries; } public void init() { super();

salesReport_DailyEntries = element.args().caller();

if (!salesReport_DailyEntries) { throw error(Error::missingRecord(funcName())); } } Een object van de SalesReport_DailyEntries klasse wordt doorgegeven via args().caller() aan het rapport. Het systeem kijkt na of het rapport wordt aangeroepen door een klasse. Dit wordt gedaan om te voorkomen dat het rapport rechtstreeks wordt uitgevoerd vanuit X++ of de AOT. In dit voorbeeld zou het niet uitmaken op welke manier het rapport wordt aangeroepen, maar in sommige gevallen kan de klasse de af te drukken data filteren. Het rechtstreeks aanroepen van dit rapport leidt dan tot onverwachte resultaten. Zoals eerder vermeld, is de klasse SalesReport_DailyEntries niet nodig gezien de interne klasse RunBaseReportStd de logica zal afhandelen die nodig is om de query/print dialoog weer te geven en automatisch de stapelverwerking of batch te vergemakkelijken. Om het rapport te veranderen zodat het niet de SalesReport_DailyEntries klasse gebruikt, moet je de init() methode van het rapport aanpassen. In dit geval kan je eenvoudig init() verwijderen. De menuoptie verwijst nog altijd naar de klasse, dus moet je naar de output menuoptie SalesReport_DailyEntries gaan en de eigenschappen veranderen voor de menuoptie zodat het niet meer de klasse maar het rapport oproept. Wanneer het rapport rechtsstreeks wordt uitgevoerd vanuit de AOT, zal je hetzelfde resultaat krijgen alsof je de klasse SalesReport_DailyEntries gebruikt. Als je het rapport uitvoert via een menu , zal je het geconsolideerd dialoogscherm zien. Voorbeeld 7: Rapportdialoog Elementen gebruikt van MorphxIt_Report project

Report, SalesDailyEntries Menu item output, SalesDailyEntries_Without_Class

Nu is het tijd om meer kenmerken toe te voegen aan het SalesDailyEntries rapport. We beginnen met een dialoogveld dat aangeeft of de details moeten afgedrukt worden. De waarde van het nieuwe veld, ingevuld door de gebruiker, wordt opgeslagen. Hierdoor wordt de laatst gekende gebruikerswaarde opgeladen wanneer het rapport wordt

MORPHX IT Rapporten

© 2007 Steen Andreasen

229

uitgevoerd. Het volgende moet toegevoegd worden aan de klassedeclaratie van het rapport: public class ReportRun extends ObjectRun { DialogField dialogPrintDetails; Boolean printDetails;

#DEFINE.CurrentVersion(1) #LOCALMACRO.CurrentList printDetails #ENDMACRO } DialogPrintDetails is een variabele van de klasse DialogField. Dit wordt gebruikt voor het nieuw dialoogveld dat de gebruiker ziet wanneer het rapport wordt uitgevoerd. De variabele printDetails bewaart de waarde van het dialoogveld. De macro CurrentList is een lijst van variabelen die bewaard worden. De lijst zal doorgaans een variabele voor elk veld in het dialoogscherm bevatten. In dit voorbeeld bevat CurrentList maar één variabele. Om er meer toe te voegen, moet je de variabelen scheiden door een komma. CurrentVersion houdt een spoor bij van de opgeslagen versie van CurrentList. Axapta laat toe dat je de parameters van batchjobs en rapporten bewaart van elke uitvoering. De waarden van de parameters die de gebruiker het laatst heeft opgegeven om deze job te laten lopen, worden vooraf door het systeem opgehaald. Als er veranderingen worden gemaakt aan de CurrentList, dan moet CurrentVersion met één stijgen. Je kan ook de gebruiksgegevens resetten. Hierdoor worden de waarden van vorige jobuitvoeringen verwijderd, samen met de selectiebereiken die door de gebruiker werden opgegeven. public Object dialog(DialogRunbase _dialog = null) { DialogRunBase dialog; ;

dialog = super(_dialog); dialogPrintDetails = dialog.addFieldValue(typeId(NoYesId), printDetails, "Print details", "Print additional information for the transactions.");

return dialog; } De dialoog wordt geïnitieerd door super() en bevat de standaard dialoog voor het rapport. Het enige wat je moet doen, is een nieuw veld toevoegen voor de afdrukdetails. Het nieuwe veld zal automatisch in een standaard veldgroep met de naam Parameters gezet worden.

MORPHX IT Rapporten

230

© 2007 Steen Andreasen

public boolean getFromDialog() { boolean ret;

printDetails = dialogPrintDetails.value(); ret = true;

return ret; } Wanneer je OK klikt in het dialoogvenster, roept het systeem de methode getFromDialog methode op. De waarde van het nieuwe dialoogveld wordt bewaard in de printDetails variabele.

public container pack() { return [#CurrentVersion, #CurrentList]; }

Zoals hier getoond, wordt de laatste waarde van het nieuwe dialoogveld bewaard. Pack() bewaart de huidige waarde van CurrentVersion en CurrentList gespecifieerd in de ClassDeclaration. public boolean unpack(container packedClass) { boolean ret; Integer version = conPeek(packedClass,1);

switch (version) { case #CurrentVersion: [version, #CurrentList] = packedClass; ret = true; break; default: ret = false; } return ret; } Deze methode haalt de laatst bewaarde waarde van CurrentVersion en CurrentList op. public void run() { if (printDetails) { SalesLine_Name.visible(true); } else { SalesLine_Name.visible(false); }

super(); }

MORPHX IT Rapporten

© 2007 Steen Andreasen

231

Tot slot moeten we nakijken of details al dan niet afgedrukt moeten worden. In dit voorbeeld bepaal je zo of de naam SalesLine afgedrukt wordt of niet. Alle rapportelementen moeten gedeclareerd worden voor je ze kan gebruiken in X++. Je kan de eigenschappen veranderen tijdens de uitvoering. Een rapportelement wordt gedeclareerd door de eigenschap AutoDeclaration op Yes te zetten. In bovenstaand voorbeeld moet je eerst de AutoDeclaration eigenschap van het SalesLine_Name element op Yes zetten. In het rapportdialoog voorbeeld hebben we de methodes pack() en unpack() gebruikt. Deze methodes worden gebruikt om de laatste waarde van een dialoogvenster te bewaren en om de gebruikersparameters, opgegeven in de dialoog, over te brengen van client naar server, wanneer het rapport wordt uitgevoerd in batch mode. Als je nood hebt aan deze functionaliteit voor je dialoog, moet je de twee methodes van een bestaande klasse kopiëren en CurrentVersion en CurrentList toevoegen aan de ClassDeclaration. Dynamische Rapporten De mogelijkheid om veranderingen te maken tijdens de uitvoering is erg nuttig, omdat het de optie voorziet om eigenschappen te veranderen of nieuwe elementen toe te voegen als je het rapport uitvoert. Hierdoor kan je één rapport maken in plaats van verschillende rapporten met gelijkaardige ontwerpen. Het SalesInvoice rapport is hiervan een voorbeeld. Afhankelijk van de verkoopsparameters wordt SalesInvoice afgedrukt met verschillende rapportelementen. Omdat Axapta een meertalig systeem is, moet het mogelijk zijn om rapporten in verschillende talen af te drukken. In de meeste gevallen zal Axapta dit correct behandelen zonder de noodzaak voor extra programmatie. Standaard zal het rapport afgedrukt worden in de taal van de Axaptagebruiker. Natuurlijk moet je uitzonderingen kunnen toestaan in bepaalde omstandigheden. Stel je voor dat een verkoopsfacturen-rapport moet afgedrukt worden in de voorkeurstaal van de klant. Dit doe je door de Language eigenschap op de ReportDesign knoop in te stellen. Je kan de eigenschap een vaste waarde geven, maar de beste manier is de taal instellen vanuit X++. Hier wordt myTable.languageId gebruikt om de taal in te stellen voor het ontwerp: public void init() { super();

element.design().laguageId(myTable.languageId); } Het sleutelwoord element wordt gebruikt in het rapport als referentie voor alle rapportobjecten. Onder rapportobject verstaan we ieder onderdeel van een rapport en dit gaat dus verder dan de rapportelementen die je op een rapport positioneert. Hier verwijst het element naar de methode languageId() in het objectontwerp. Op deze manier krijg je greep op elk onderdeel van het rapport. Als je element gebruikt als

MORPHX IT Rapporten

232

© 2007 Steen Andreasen

verwijzing naar een rapportobject, kan het resultaat een lang pad opleveren, zoals je hieronder ziet : element.design().sectionGroup(tablenum(CustInterestTrans)).section(ReportBlockType::BODY).controlName('custInterestTrans_custInterestJourInterestAmount'); De bovenstaande lijn komt uit de init() methode in het rapport CustInterestNote. Een betere manier om dit soort problemen af te handelen is de eigenschap AutoDeclaration te gebruiken. Wanneer je de eigenschap AutoDeclaration gebruikt, declareer je eigenlijk een instantie van een systeemklasse. SalesLine_Name in het rapportdialoogvoorbeeld is een instantie van de systeemklasse ReportStringControl. Opmerking : Wanneer je door de standaardrapporten in de AOT bladert, kan je rapporten zien waar systeemklassen voor secties en rapportelementen in de code gedeclareerd zijn, en niet via de eigenschap AutoDeclaration. Dit is omdat de AutoDeclaration eigenschap voor rapportelementen pas is toegevoegd in v3.0. van Axapta. De systeemklassen zijn bedoeld om gebruikt te worden wanneer je nood hebt aan onderdelen zoals secties en rapportelementen in een rapport tijdens de uitvoering zelf. Zo kan je bijvoorbeeld beslissen om bijkomend gedetailleerde informatie af te drukken of zelfs een extra sectie van een andere tabel af te drukken, afhankelijk van waar je rapport wordt opgeroepen, In plaats van systeemklassen te gebruiken om rapportelementen tijdens de uitvoering te creëren, kan je ook alle secties en rapportelementen toevoegen die nodig zijn voor de verschillende combinaties en dan de Visible eigenschap gebruiken om te bepalen of het rapportelement al dan niet moet getoond worden. In sommige gevallen krijgt het gebruik van de systeemklassen de voorkeur. Als je de systeemklassen gebruikt voor het maken van rapportelementen , kan je wachten tot de uitvoering om te beslissen welk type element nodig is. Dit is zeker handig als de rapportelementen die je moet toevoegen, afhankelijk zijn van de gebruikersinstellingen of parameters. Voorbeeld 8: Rapportsysteemklassen Elementen gebruikt van MorphxIt_Report project

Report, MyReport_SystemClasses Menu item output, MyReport_SystemClasses

Om de rapportsysteemklassen uit te proberen, maak je een nieuw rapport zoals getoond wordt in figuur 41: test van rapportsysteemklassen. Tijdens de uitvoering zal het rapport een middensectie maken voor de CustTable. De middensectie zal 10 rapport-elementen hebben, die de waarde van de eerste 10 velden van de CustTable zullen printen. De tabel CustTable wordt toegevoegd als databron en de knoop voor het auto-ontwerp is gemaakt.

MORPHX IT Rapporten

© 2007 Steen Andreasen

233

Figuur 41: Test van rapportsysteemklassen Overschrijf de init() methode van het rapport zoals hieronder getoond. Andere code is niet nodig. public void init() { ReportSection reportSection; DictTable dictTable; DictField dictField; Counter fieldCounter;

super();

reportSection = element.design().autoDesignSpecs().addSection(ReportBlockType::Body); reportSection.table(tableNum(custTable));

dictTable = new DictTable(tableNum(custTable));

while (fieldCounter < 10) { fieldCounter++; dictField = new DictField(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter)); reportSection.addControl(dictTable.id(), dictTable.fieldCnt2Id(fieldCounter)); } } De instantie van de systeemklassen reportSection en reportControl wordt gebruikt om de middensectie en zijn bijbehorende rapportelementen aan te maken. Een nieuwe rapportsectie van het type Body Section wordt toegevoegd aan de AutoDesignSpecs knoop en er wordt bepaald om de tabel CustTable te gebruiken. DictTable is ook een instantie van een systeemklasse. DictTable gebruikt men vaak wanneer men een verwijzing nodig heeft naar een tabel of veldeigenschappen. Zo is ook DictField een systeemklasse die een verwijzing voorziet naar een specifiek veld voor een specifieke tabel. In dit voorbeeld gebruiken we dictField en DictTable om de eerste 10 velden van de tabel CustTable te overlopen. Voor ieder veld zal een rapportelement aan de middensectie toegevoegd worden. MorphX zorgt ervoor dat de rapportelementen van het gewenste type toegevoegd worden en positioneert deze automatisch. Bij het openen van het rapport worden de eerste 10 velden van de CustTable op één lijn afgedrukt. Wanneer je een module schrijft waarbij de gebruiker de optie heeft om zijn/haar eigen rapportopmaak te bepalen, dan bieden systeemklassen hier een antwoord op.

MORPHX IT Rapporten

234

© 2007 Steen Andreasen

Vaak gebruikte rapportmethoden Bij het wijzigen van een rapport, overschrijf je bestaande methodes, of voeg je nieuwe methodes toe die opgeroepen worden vanuit de overschreven methodes. De volgende methodes worden in onderstaande volgorde uitgevoerd wanneer een rapport geladen wordt:

init() ► dialog() ► run() ► fetch() ► send() ► print()

Init() en dialog() worden opgeroepen bij het laden van het rapport. Run() wordt opgeroepen bij het klikken op de OK knop van het dialoogvenster. Fetch() doorloopt het resultaat van de query en voor ieder gevonden record

wordt de send() methode uitgevoerd. Uiteindelijk wordt de print() methode opgeroepen.

Deze methodes zijn de belangrijkste van een rapport en zijn diegene die je het meest zal overschrijven. Typische aanpassingen zijn het toevoegen van formulierelementen aan het dialoogvenster, het manipuleren van de query output alvorens deze af te drukken, of het aanpassen van de output bestemd voor de middensectie van het rapport door een programmeerbare sectie uit te voeren. De bovenvermelde volgorde van uitvoering wordt gebruikt wanneer het report runbase raamwerk actief is. Wanneer je het rapport rechtstreeks vanuit de AOT oproept, zonder gebruik te maken van een menuoptie , dan wordt een enigszins andere volgorde van uitvoering gehanteerd, zoals hieronder afgebeeld:

init() ► run() ► prompt() ► fetch() ► send() ► print() Merk op dat dialog() niet zal opgeroepen worden. RunBaseReportStd beheert de dialoogvensters van het rapport en wanneer het “runbase raamwerk niet actief is” wordt prompt()gebruikt. Voorbeeld 9: fetch() overschrijven Elementen gebruikt van het MORPHXIT_Reports project

Rapport, MyReport_Fetch Menu item output, MyReport_Fetch

Een nieuw rapport wordt aangemaakt voor het afdrukken van transacties voor iedere klant, gefilterd van een aantal dagen geleden tot de huidige systeemdatum. Het rapport heeft een dialoogvenster waarop het aantal dagen door de gebruiker kan opgegeven worden. Start met het voorbeeld MyReport te dupliceren. Het resultaat zal eruit zien als in Figuur 42 : Rapport om fetch() te overschrijven. Het voorbeeld legt de nadruk op het overschrijven van methodes.

MORPHX IT Rapporten

© 2007 Steen Andreasen

235

Figuur 42: Rapport om fetch() te overschrijven Nu je de query en het ontwerp van het rapport hebt aangemaakt, kun je starten met het creëren van methodes voor het rapport. public class ReportRun extends ObjectRun { DialogField dialogDaysBack; NumberOf daysBack; } De variabele dialogDaysBack is nodig voor het dialoogvenster. De waarde zal toegekend worden aan de variabele daysBack. public Object dialog(Object _dialog) { DialogRunBase dialog; ;

dialog = super(_dialog); dialogDaysBack = dialog.addFieldValue(typeId(NumberOf), daysBack, "Number of days", "Number of days back to be printed.");

return dialog; } Een veld wordt toegevoegd aan het dialoogvenster om het aantal dagen in te geven.

MORPHX IT Rapporten

236

© 2007 Steen Andreasen

public boolean getFromDialog() { boolean ret;

daysBack = dialogDaysBack.value(); ret = true;

return ret; } De variabele daysBack wordt gebruikt om de waarde van het dialoogvenster te bewaren. public boolean fetch() { QueryRun qr; QueryBuildRange rangeTransDate; Boolean ret;

qr = new QueryRun(element);

rangeTransDate = element.query().dataSourceTable(tablenum(CustTrans)).addRange(fieldnum(CustTrans, transDate)); rangeTransDate.value(queryRange(systemdateGet()-daysBack, systemDateGet())); rangeTransDate.status(RangeStatus::LOCKED);

element.design().caption(strFmt("%1, %2", element.design().caption(), rangeTransDate.value()));

if (qr.prompt() && element.prompt()) { while (qr.next()) { custTable = qr.get(tableNum(CustTable)); custTrans = qr.get(tableNum(CustTrans));

if (!custTable) { ret = false; break; }

if (qr.changed(tableNum(custTable))) element.send(custTable, 1);

if (qr.changed(tableNum(custTrans))) element.send(custTrans, 2); } ret = true; } else ret = false;

return ret; } De variabele daysBack bevat de waarde die door de gebruiker werd ingegeven. Je moet een bereik toevoegen aan de query om te filteren op de transactiedatums tussen n-1

MORPHX IT Rapporten

© 2007 Steen Andreasen

237

dagen en de systeemdatum. Een QueryRun object, qr genoemd, wordt geïnitialiseerd met de actieve query van het rapport, waarna een bereik wordt toegevoegd aan de query voor de transactiedatums. Het bereik is geblokkeerd, waardoor de gebruiker deze niet kan wijzigen. Het bereik van de transactiedatums wordt toegevoegd aan de titel van het rapport. Op dit punt wordt de query doorlopen. De standaardlus van de query en de afdruk van het record wordt afgehandeld door de oproep van de super() methode in de fetch() methode. Vooraleer de query te doorlopen, wordt gecontroleerd of de dialoogvensters van de query en het rapport werden opgeroepen. Dit zijn de twee dialoogvensters die meekomen met RunBaseReportStd. Binnen iedere lus worden de tabellen CustTable en CustTrans geïnitialiseerd. Wanneer geen records gevonden worden, stopt de lus en wordt het rapport beëindigd. Wanneer een gegevensbron werd gewijzigd, en een record is gevonden, zal het rapport worden afgedrukt via de send() methode. Bemerk de tweede parameter in de send() methode. De tweede parameter definieert het niveau van het record. CustTable is op het eerste niveau van de query en CustTrans is op het tweede niveau. Dit is belangrijk, omdat autosommen niet afgedrukt worden, indien de niveau’s niet correct worden ingesteld. In het fetch voorbeeld werd de query van het rapport doorlopen. Dit kon ook via het WHILE commando, SELECT commando, of een combinatie van beide. Voor ieder record doorlopen in de query, wil je misschien een bijbehorend record selecteren van een tabel die niet in de query zit, of een tijdelijke tabel opbouwen om af te drukken. Voor iedere record dat moet afgedrukt worden, moet je enkel de send() methode oproepen. Wanneer je de af te drukken records wil valideren, dien je de send() methode te overschrijven in plaats van de fetch() methode: public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean _newPageBeforeBody=FALSE) { boolean ret; CustTrans custTrans;

if (_cursor.tableId == custTrans.tableId) { custTrans = _cursor; }

if (custTrans.transDate == systemDateGet()) { ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody); }

return ret; } De send() methode krijgt het af te drukken record door als een parameter. Het enige wat je moet doen, is de betreffende tabel initialiseren. In dit geval is de tabel CustTrans geinitialiseerd als de cursor een CustTrans record is. Enkel klanttransacties waarvan de transactiedatum gelijk is aan de systeemdatum, zullen afgedrukt worden.

MORPHX IT Rapporten

238

© 2007 Steen Andreasen

Het toevoegen van querywaarden kan vaak door simpelweg de init() methode te overschrijven, wat stukken gemakkelijker is omdat je slechts een beperkt aantal lijnen code nodig hebt. Het bereik toegevoegd aan de query in het fetch voorbeeld was afhankelijk van de gebruikerinteractie. De wijziging moest gebeuren na het sluiten van het dialoogvenster en dus moest de code genoteerd worden in de fetch() methode. Alvorens een bereik aan de query toe te voegen, moet je nakijken of de query niet al een bereik voor het veld heeft, door gebruik te maken van de QueryBuildRange.findRange() methode. Indien je twee bereiken voor hetzelfde veld aanmaakt, werden gecreërd, zullen de bereiken in een EN-relatie worden verbonden, wat onverwachte resultaten kan opleveren. De gebruiker heeft de mogelijkheid in het rapportdialoogvenster om de bereiken toegepast in de query af te drukken. Zowel bereiken toegevoegd aan de querygegevensbron, als bereiken toegevoegd met X++ code zullen afgedrukt worden. Maar als de fetch() methode is overschreven, zal deze optie uitgeschakeld zijn.

MORPHX IT Rapporten

© 2007 Steen Andreasen

239

7.7 Speciale Rapporten Tot nog toe richtte dit hoofdstuk zich op de basisstappen voor het creëren van rapporten. Om een idee te krijgen van de mogelijkheden binnen de MorphX taal, zal dit gedeelte via een aantal voorbeelden aantonen hoe je kan omgaan met speciale rapporten en hoe je die rapporten gebruiksvriendelijker kan maken. Een rapport uitvoeren vanuit X++ Rapporten worden normaal opgestart vanuit een menuoptie, opgeroepen via het hoofdmenu of via een formulier. Soms is het nodig om een rapport direct uit X++ code op te roepen, omdat de gebruiker het rapport niet rechtstreeks via een menuoptie mag oproepen. Elementen gebruikt van MORPHXIT_Reports project

Job, Reports_ExecuteReport Job, Reports_ExecuteReportSilent

static void Reports_ExecuteReport(Args _args) { Args args; SysReportRun reportRun; ;

args = new Args();

reportRun = new menuFunction(menuItemOutputStr(MyReport), MenuItemType::Output).create(args); reportRun.run(); } De job toont hoe MyReport opgeroepen wordt vanuit X++. Merk op dat de applicatieklasse SysReportRun gebruikt wordt. SysReportRun erft over van de systeemklasse ReportRun. Het voordeel van het gebruik van de applicatieklasse is de mogelijkheid om de code in de SysReportRun klasse te overschrijven. Je kan ook je eigen klasse maken die overerft van de SysReportRun klasse. Dit is handig als je voor een bepaalde reeks rapporten een aantal controles moet uitvoeren bij het afdrukken, in plaats van die voor ieder rapport apart te voorzien. MyReport wordt opgeroepen door gebruik te maken van een menuoptie, die het runbase rapport raamwerk oproept en het correcte dialoogvenster laadt voor het rapport. Indien je geen gebruik wil maken van het runbase raamwerk of je wil het rapport afdrukken zonder gebruikersinteractie, dien je het rapport op te roepen zonder gebruik te maken van het runbase rapport raamwerk. static void ExecuteReportSilent(Args _args) { Args args; SysReportRun reportRun; ; args = new Args(); args.name(reportstr(MyReport));

MORPHX IT Rapporten

240

© 2007 Steen Andreasen

reportRun = classFactory.reportRunClass(args); reportRun.query().interactive(false); reportRun.report().interactive(false); reportRun.setTarget(PrintMedium::Printer); reportRun.run(); } In deze job voeren we het rapport uit, zonder gebruik te maken van de menuoptie en dus wordt het runbase rapport raamwerk niet gebruikt. Het voorbeeld drukt het rapport onmiddelijk af op de standaardprinter zonder interactie van de gebruiker, omdat zowel de query als het rapportdialoogvenster op inactief geplaatst zijn. Wanneer de methode dialog() in je rapport overschreven wordt, moet je er zeker van zijn dat dit geen problemen met zich meebrengt, omdat dialog()niet uitgevoerd zal worden. Indien je rapport uit meer dan één ontwerp bestaat, moet je aanduiden welk ontwerp je wenst te gebruiken. Indien deze niet werd aangeduid, of indien een foutieve waarde werd ingesteld, zal het eerste ontwerp op het rapport toegepast worden. reportRun.design("MyDesign"); reportRun.run(); Gebruik van tijdelijke tabellen Indien een speciale sorteermethode nodig is, of je wenst data vanuit meerdere tabellen te selecteren die niet met elkaar gekoppeld kunnen worden, dan kan het gebruik van tijdelijke tabellen hier een oplossing voor bieden. Het gebruik van tijdelijke tabellen is vrij simpel. De tijdelijke tabel moet opgevuld en doorgegeven worden naar het rapport. Er kunnen performantieproblemen optreden bij het gebruik van tijdelijke tabellen, gezien het rapport 2 maal zal uitgevoerd worden. De eerste maal zal de tijdelijke tabel opgebouwd worden. Vervolgens zal deze overlopen worden in het rapport. Het gebruik van tijdelijke tabellen zou niet je eerste keuze mogen zijn. Het is beter om het ontwerp van je rapport opnieuw te analyseren. Meer informatie over tijdelijke tabellen vind je in het hoofdstuk : Data Dictionary. Elementen gebruikt van MORPHXIT_Reports project

Class, Reports_TempTable Report, Reports_TempTable

Bij het gebruik van tijdelijke tabellen, dient het rapport opgeroepen te worden vanaf een klasse. Dit biedt de mogelijkheid om de tijdelijke tabel te bouwen op de server. Het volgende voorbeeld toont hoe je een rapport kunt maken met gebruik van een tijdelijke tabel. Voor de eenvoud voegt het voorbeeld 10 records toe aan de tijdelijke tabel en drukt het resultaat af. class Reports_TempTable extends runBaseReport { } De klasse erft over van runBaseReport.

MORPHX IT Rapporten

© 2007 Steen Andreasen

241

public identifiername lastValueElementName() { return reportstr(Reports_TempTable); } De naam voor het rapport wordt gespecificeerd. tmpAccountSum tempTable() { CustTrans custTrans; TmpAccountSum tmpAccountSum; Counter counter; ;

while select custTrans { counter++;

if (counter == 10) { break; }

tmpAccountSum.accountNum = custTrans.accountNum; tmpAccountSum.currencyCode = custTrans.currencyCode; tmpAccountSum.balance01 = custTrans.amountMST; tmpAccountSum.insert(); }

return tmpAccountSum; } De tijdelijke tabel tmpAccountSum wordt gebruikt. De eerste tien records van de CustTrans tabel worden aan de tabel tmpAccountSum toegevoegd. Het rapport gebruikt deze methode om de buffer van de tijdelijke tabel door te geven aan het rapport. static void main(Args args) { Reports_TempTable reports_TempTable = new reports_TempTable(); if (reports_TempTable.prompt()) { reports_TempTable.run(); } } De klasse wordt geïnitialiseerd en het rapport wordt uitgevoerd. De laatste stap is het creëren van het rapport. We gaan een rapport aanmaken met de tijdelijke tabel TmpAccountSum als gegevensbron. De drie velden opgevuld met de waarden van CustTrans zullen afgedrukt worden. Je rapport moet eruitzien als Figuur 43 : Rapport met tijdelijke tabel.

MORPHX IT Rapporten

242

© 2007 Steen Andreasen

Figuur 43: Rapport met tijdelijke tabel Init() moet overschreven worden. De runbase klasse wordt geïnitialiseerd en de query wordt voorzien van een buffer naar de tijdelijke tabel. Merk op dat je een referentie dient te maken naar de buffer. De query zal hierdoor het volledige bereik hebben van de tijdelijke tabel en alle records van de tijdelijke tabel overlopen. public void init() { Reports_TempTable reports_tempTable; ; super();

reports_TempTable = element.args().caller();

if (!reports_TempTable) { throw error(Error::missingRecord(funcName())); }

reports_TempTable.queryRun().setRecord(reports_TempTable.tempTable()); } Gekleurde rijen Het gebruik van kleuren vind je niet vaak in de rapporten van het standaardpakket. Je zal een beetje moeten spelen met het rapport om het gewenste resultaat te krijgen. Nochtans kan het gebruik van kleuren de kers op de taart zijn voor je rapport en de afdruk gemakkelijker maken om te lezen. Elementen gebruikt van het MORPHXIT_Reports project

Rapport, MyReport_Color Menu item output, MyReport_Color

Dit voorbeeld toont hoe één enkele kolom een bepaalde kleur kan krijgen afhankelijk van een bepaalde conditie. Het rapport zal de achtergrondkleur wijzigen van het veld

MORPHX IT Rapporten

© 2007 Steen Andreasen

243

CustTrans.amountMST. Om de code te vereenvoudigen, gebeurt de conditiecontrole vanuit X++. In een echte applicatie zullen de voorwaarden opgegeven worden via een dialoogvenster of gebaseerd zijn op de data in een formulier. Het resultaat zal eruit zien zoals in Figuur 44 : Rapport gekleurde rijen.

Figuur 44: Rapport gekleurde rijen 1. Start met het dupliceren van het rapport MyReport en geef het de naam

“MyReport_Color”. 2. Het doel is om het element CustTrans_AmountMST een bepaalde kleur te geven.

Door gebruik te maken van het ongewijzigde MyReport voorbeeld, zal het label van de paginahoofding ook van kleur wijzigen. In plaats van de standaard paginahoofding voor de middensectie CustTrans_Body, moet je een nieuwe hoofding aanmaken. Om de standaard paginahoofding over te slaan, geef je de eigenschap NoOfHeadingLines de waarde “0” in de middensectie CustTrans_Body.

3. Maak een nieuwe paginahoofding door een programmeerbare sectie toe te voegen

en een prompt voor elk van de drie velden in de middensectie. De eigenschap ModelFieldName van iedere prompt moet ingevuld worden met de naam van het overeenkomstige rapportelement uit de middensectie. Voeg een label toe voor iedere prompt.

4. Declareer een variabele om bij te houden wanneer de programmeerbare sectie,

gebruikt om de paginahoofding af te drukken, moet uitgevoerd worden. public class ReportRun extends ObjectRun { Boolean printCustTransHeader; }

MORPHX IT Rapporten

244

© 2007 Steen Andreasen

5. Overschrijf de methode send(). Als het huidige record een CustTrans record is, dan

zal de paginahoofding afgedrukt worden voor de eerste CustTrans in een rij. Er werd een conditie toegevoegd voor het veld CustTrans.AmountMST. Indien een bedrag hoger dan 500 wordt afgedrukt, zal de achtergrondkleur wijzigen in het geel. Zoniet, zal de achtergrondkleur ongewijzigd blijven in een neutrale kleur.

public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean _newPageBeforeBody=FALSE) { boolean ret; ;

if (_cursor.tableId == tableNum(custTable)) printCustTransHeader = true;

if (_cursor.tableId == tableNum(custTrans)) { if (printCustTransHeader) { element.execute(10); printCustTransHeader = false; }

if (custTrans.amountMST > 500) { CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 0)); } else { CustTrans_AmountMST.backgroundColor(Winapi::RGB2int(255, 255, 255)); } }

ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody);

return ret; } Afdrukken met Microsoft Word Rapporten aanmaken in Axapta met een complex ontwerp zoals formules, tabellen en figuren kan een hele uitdaging zijn. Door gebruik te maken van de COM interface kun je connecteren naar externe applicaties zoals Microsoft Word. Om Microsoft Word te gebruiken om data af te drukken, moet je eerst een Microsoft Word sjabloon aanmaken met de nodige markeringen (bookmarks). De markeringen worden gebruikt om de data uit Axapta te positioneren. Let wel, je moet een licentiecode hebben voor minstens één COM client om de COM interface te kunnen gebruiken. Opmerking: De documentafhandeling in het standaardpakket gebruikt de COM interface om Microsoft Excel en Microsoft Word bestanden te koppelen. De klassen gebruikt door documentafhandeling hebben een naam beginnend met DocuActionCOM.

MORPHX IT Rapporten

© 2007 Steen Andreasen

245

Elementen gebruikt van het MORPHXIT_Reports project

Class, PrintUsingWord Job, Reports_PrintUsingWord

Bijhorend

Wordsjabloon , Reports_WordTemplate.dot Dit voorbeeld toont hoe je een connectie kan maken met Microsoft Word en een nieuw document kan maken dat data van de InventTable tabel zal afdrukken. De eerste 10 rijen van de InventTable zullen afgedrukt worden. Er zullen labels worden afgedrukt voor de rapport- en kolomhoofdingen. Je zal een Microsoft Word sjabloon moeten aanmaken met de volgende markeringen : label_header, label_itemid, label_itemname en label_itemdesc. Label_header zal een teksthoofding afdrukken voor de kolommen. Creëer een tabel en voeg de 3 resterende markeringen toe als hoofdingen voor de tabel. De volgende stap is het creëren van de volgende klasse: void run() { COM COMAppl, COMDocuments, COMDocument; ;

COMAppl = new COM('Word.Application'); COMDocuments = COMAppl.documents();

// enter path to the template Reports_wordtemplate.dot COMDocument = COMdocuments.add('d:\\Reports_WordTemplate.dot');

if (COMDocument) { this.setLabels(COMDocument); this.sendInventTable(COMDocument); this.showDocument(COMAppl); } } Run() zal de COM connectie initialiseren, en een nieuw Microsoft Word document openen, gebaseerd op de sjabloon Reports_WordTemplate.dot. Vergeet niet te controleren of het pad naar de sjabloon wel degelijk correct is. Bij de creatie van het document zullen de labels en data toegevoegd worden. Uiteindelijk zal het document getoond worden. Onthou dat het getoonde document niet bewaard wordt. Indien je het document wenst te bewaren, moet je de volgende instructie toevoegen : COMdocument.saveAs(<filename>,0,false,'',false); void setLabels(COM _COMDocument) { COM COMBookmarks, COMBookmark, COMrange; DictField dictField; Label label; ;

COMBookmarks = _COMDocument.bookmarks();

if (COMbookmarks.exists('label_header')) {

MORPHX IT Rapporten

246

© 2007 Steen Andreasen

COMbookmark = COMbookmarks.item('label_header'); COMrange = COMbookmark.range(); COMRange.InsertAfter("Inventory list"); }

if (COMbookmarks.exists('label_itemId')) { COMbookmark = COMbookmarks.item('label_itemId'); COMrange = COMbookmark.range(); DictField = new dictField(tableNum(inventTable), fieldNum(inventTable, itemId)); COMRange.InsertAfter(dictField.label()); }

if (COMbookmarks.exists('label_itemName')) { COMbookmark = COMbookmarks.item('label_itemName'); COMrange = COMbookmark.range(); DictField = new dictField(tableNum(inventTable), fieldNum(inventTable, itemName)); COMRange.insertAfter(dictField.label()); }

if (COMbookmarks.exists('label_itemDesc')) { COMbookmark = COMbookmarks.item('label_itemDesc'); COMrange = COMbookmark.range(); label = new Label(CompanyInfo::languageId()); COMRange.InsertAfter(label.extractString(literalstr("@SYS58702"))); } } Er wordt gekeken of de markering gevonden kan worden. Indien deze gevonden wordt, worden de labels opgevuld. Het label voor de hoofding krijgt de statische tekst “Inventory list”. De labels voor de markeringen label_itemId en label_itemName krijgen de waarde van het label van de overeenkomstige tabelvelden. De label voor de markering label_itemDesc krijgt een waarde via de methode label.extractString() die het label ophaalt voor de standaardbedrijfstaal. void sendInventTable(COM _COMDocument) { COM COMTable, COMRows, COMRow; COM COMCells, COMCell, COMRange; InventTable inventTable; Counter counter; ;

//init tabel COMTable = COMDocument.Tables(); COMTable = COMTable.Item(1); COMRows = COMTable.Rows(); while select inventTable { counter++; if (counter == 10) { break; }

MORPHX IT Rapporten

© 2007 Steen Andreasen

247

// add new row COMRow = COMRows.Add(); COMCells = COMRow.Cells();

// item id COMCell = COMCells.Item(1); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemId);

// item name COMCell = COMCells.Item(2); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemName);

// item description COMCell = COMCells.Item(3); COMRange = COMCell.Range(); COMRange.InsertAfter(inventTable.itemDescription()); } } De code doorloopt de inventTable. De tabel in Microsoft Word wordt eerst geïnitieerd. Voor iedere iteratie wordt een rij toegevoegd aan de tabel in Microsoft Word. De velden itemId, itemName en de displaymethode itemDescription() van de InventTable worden toegevoegd aan de drie rijen in de Microsoft Word tabel. Merk op dat er geen markeringen (bookmarks) nodig zijn. void showDocument(COM _COMAppl) { _COMAppl.visible(TRUE); } Het aangemaakte Microsoft Word document wordt getoond. static void main(Args _args) { PrintUsingWord printUsingWord = new PrintUsingWord(); ; PrintUsingWord.run(); } Deze code initieert en voert deze klasse uit.

7.8 Samenvatting Dit hoofdstuk gaf je een inleiding op rapporten in Axapta. Het omvat de basis voor het creëren van rapporten. Nu zou je reeds vertrouwd moeten zijn met de verschillende rapportonderdelen, zoals gegevensbronnen, ontwerpen, secties en rapportelementen in ontwerpen en de vaak gebruikte methoden gebruikt bij het aanmaken van een rapport. Je zou ook kennis moeten verworven hebben omtrent het aanmaken van rapporten met de rapportgenerator. Tot slot wilden we je ook inzicht geven in de kracht van Axaptarapporten en de MorphX ontwikkelingsomgeving.

MORPHX IT Queries

© 2007 Steen Andreasen

249

8 Queries Selectie-instructies en queries zijn twee opties om data op te halen uit de databank wanneer je MorphX gebruikt. Een selectie-instructie is een statische uitdrukking geschreven in X++. Een query biedt meer mogelijkheden : je kan een query schrijven in X++ of maken met behulp van de AOT-knoop Queries. De applicatiegebruiker kan een query tijdens de uitvoering wijzigen aan de hand van het query-dialoogvenster. In X++ kan je de query aanpassen aan de hand van de query-systeemklassen. Dit maakt queries handig wanneer je werkt met objecten zoals formulieren en rapporten omdat je gegevens kan filteren en de sorteervolgorde kan wijzigen via een query-dialoog of in X++. Instellingen die de applicatiegebruiker in de query-dialoog opgeeft, worden bewaard per gebruiker per bedrijf. Deze instellingen kent men ook als laatste waarden of “last values”. Het bewaren van de laatste waarden maakt het werk gemakkelijker voor de gebruiker aangezien de favoriete instellingen alleen ingesteld worden bij een eerste gebruik. Het gebruik van een query of een selectie-instructie heeft geen invloed op de performantie. Selecties en queries zijn beiden deel van MorphX en worden allebei uitgevoerd door de kernel. Je keuze voor een selectie of een query hangt af van wat je nodig hebt. Queries zijn aangewezen als je wil dat de applicatiegebruiker data kan filteren of als je veranderingen wil aanbrengen tijdens de uitvoering van de query zoals het filteren van gegevens in een formulier of rapport afhankelijk van het oproepende object. Selectie-instructies worden doorgaans gebruikt als de opgehaalde data worden gebruikt zonder tussenkomst van de gebruiker. Dit hoofdstuk zal zich toespitsen op het maken en gebruiken van queries van MorphX. Je kan de informatie over de gebruikersinterface van queries vinden in de handleidingen in het standaardpakket.

MORPHX IT Queries

250

© 2007 Steen Andreasen

Figuur 45: Query opgeslagen in AOT

8.1 Bouwen van Queries Complexe queries en queries waarbij de standaard methodes worden overschreven, kan je best bouwen met behulp van de Queries knoop. Een query die wordt opgeslagen als een knoop in de AOT, wordt weergegeven als een boomstructuur zodat je gemakkelijk een overzicht hebt van de enkelvoudige componenten van de query. Dit is vooral handig als je leert werken met queries, omdat je je geen zorgen moet maken over welke systeemklassen je moet gebruiken en hoe je ze moet gebruiken. AOT Query Queries die opgeslagen zijn in de AOT, kan je gebruiken in elk deel van je code. Een AOT-query kan niet gedeclareerd worden als een type of een tabel. Je zal een systeemklasse moeten gebruiken om je query uit te voeren. Toch is een AOT-query zeer flexibel omdat je maar enkele lijnen code nodig hebt om je query te integreren in eender welk stuk code. Als je later beslist om je AOT-query te veranderen, om pakweg je filter aan te passen, zullen je veranderingen op alle plaatsen waar de query gebruikt wordt, effect hebben. Het nadeel is dat het onduidelijk kan zijn welke query je moet gebruiken. Als je een query vindt in de AOT die voldoet aan je noden, zal je niet weten waar de query wordt gebruikt tenzij je het kruisverwijzingsysteem gebruikt. Veranderingen aanbrengen in zo’n query kan fatale gevolgen hebben. En dus zal je er waarschijnlijk voor kiezen om je eigen query te maken in de AOT.

MORPHX IT Queries

© 2007 Steen Andreasen

251

Basiscomponenten De eerste stap in het maken van een query is het opzoeken van de nodige tabellen. Een tabel die gebruikt wordt in een query, noemt men een data source of gegevensbron. Je kan een gegevensbron vergelijken met een tabelvariabele. Normaal heeft de gegevensbron dezelfde naam als de gerelateerde tabel. Alleen als je een tabel nodig hebt, die je meer dan éénmaal gebruikt in de query , moet je eens nadenken over het veranderen van de naam van de gegevensbron. De naam van de gegevensbron verwijst in X++ naar een tabelvariabele. Tabellen, tabelkoppelvlakken en views kunnen gebruikt worden als gegevensbronnen. In X++ worden ze allemaal op dezelfde manier gebruikt. Alleen wanneer je tijdelijke tabellen gebruikt, moet je een beetje code toevoegen. Opmerking: Als je onzeker bent over het resultaat van je query, moet je proberen je query in een rapport te maken. Het rapport zal het resultaat van je query printen. In het hoofdstuk Rapporten kan je lezen hoe je de rapport query gebruikt. Als een query data ophaalt uit meer dan één tabel, moet je zoals in een selectie-uitdrukking de koppelingsvolgorde van de tabellen bepalen. Elk niveau in de queryboom bepaalt een koppeling tussen twee tabellen. Voorbeeld 1: Maken van AOT query Elementen gebruikt van MORPHXIT_Queries project

Query, MyQuery Dit voorbeeld toont je hoe je een AOT query moet maken terwijl je twee tabellen koppelt en de standaard bereiken voor filters bepaalt. Je moet eindigen met een query zoals die getoond wordt in figuur 46: MyQuery.

Figuur 46: MyQuery

MORPHX IT Queries

252

© 2007 Steen Andreasen

1. Ga naar de Queries knoop in de AOT, klik met je rechtermuisknop en kies New Query. Hernoem de query naar “MyQuery” en open de nieuwe queryknoop.

2. Open een ander venster in de AOT, zoek de tabel CustInvoiceJour en sleep de tabel

naar de queryknoop Data Sources. De gegevensbron zal de suffix “_1”krijgen. Dit is om unieke namen te krijgen. Verwijder de suffix.

3. Klik de nieuwe gegevensbronknoop open, versleep de tabel CustInvoiceTrans naar

de knoop Data Sources/CustInvoiceJour/Data Sources en verwijder de suffix. 4. Ga naar het eigenschappenblad voor de gegevensbron CustInvoiceTrans en geef de

eigenschap Relations de waarde “Yes”. Ontvouw de knoop van de nieuwe gegevensbron en kijk na of de relatievelden zijn opgelijst onder CustInvoiceTrans/Relations.

5. Ga naar de Ranges knoop die je vindt onder de gegevensbron CustInvoiceJour, klik

met je rechtermuisknop en kies New Range. Hiermee voeg je een nieuw bereik toe met het eerste veld van de gegevensbrontabel. Verander het veld door het eigenschappenblad van het nieuw bereik te openen en kies in de eigenschap Field het veld InvoiceAccount.

6. Herhaal stap 5 door een bereik toe te voegen voor het veld CustGroup. 7. Ga naar de gegevensbron CustInvoiceTrans en voeg een bereik toe voor het veld

ItemId. De query die gemaakt wordt in dit voorbeeld, koppelt de tabellen CustInvoiceTabel en CustInvoiceTrans dat de journalen van de klantenfacturen bevat. De tabellen zijn aan elkaar gekoppeld door de standaard koppeling “inner join”. Je kan de koppelingswijze van twee gegevensbronnen veranderen door de eigenschap JoinMode van de lagere gegevensbron in te stellen. De eigenschap Relations van de gegevensbron op het lagere niveau wordt gebruikt om de relatievelden van de koppeling te definiëren. Dit zijn de relatievelden gespecifieerd in de data dictionary. Je kan relatievelden manueel specifiëren, maar het is beter om de instellingen van de data dictionary te gebruiken omdat dit het onderhoud gemakkelijker maakt. Als een relatieveld wordt veranderd in de data dictionary, dan zullen alle queries die deze gegevensbron gebruiken automatisch worden aangepast. Schrijf nu een job om de query te testen: static void Queries_TestMyQuery(Args _args) { SysQueryRun queryRun = new SysQueryRun(querystr(MyQuery)); CustInvoiceJour custInvoiceJour; CustInvoiceTrans custInvoiceTrans; ; if (queryRun.prompt()) { while (queryRun.next()) {

MORPHX IT Queries

© 2007 Steen Andreasen

253

custInvoiceJour = queryRun.get(tableNum(CustInvoiceJour)); custInvoiceTrans = queryRun.get(tableNum(CustInvoiceTrans)); if (queryRun.changed(tableNum(CustInvoiceJour))) { info(strfmt("Account: %1", custInvoiceJour.invoiceAccount)); } if (queryRun.changed(tableNum(CustInvoiceTrans))) { info(strfmt("Item: %1, Qty: %2, ",custInvoiceTrans.itemId, custInvoiceTrans.qty)); } } } } De subklasse SysQueryRun van de systeemklasse QueryRun wordt gebruikt om een query uit te voeren. De naam van de geïnitialiseerde query wordt gespecificieerd door de functie querystr(). Je kan de naam van de query ingeven als tekst, maar querystr() zal controleren of de query bestaat in de AOT. QueryRunprompt() zal de querydialoog oproepen en als je op OK drukt in de querydialoog , dan worden de records opgehaald die overeenkomen met het bereik opgegeven in de query. Een tabelvariabele voor elke gegevensbron moet geïnitialiseerd worden om de recordwaarden te verkrijgen van de query. Wanneer een query de records afloopt, moet je nakijken van welke gegevensbron het huidige record wordt opgehaald. Dit gebeurt met behulp van queryRun.changed(). Opmerking: Door het verslepen van een AOT-query naar de editor kan je de basisstructuur maken voor het doorlopen van de query. Wanneer MyQuery wordt uitgevoerd, verschijnt de querydialoog. De 3 velden gespecifieerd onder de Ranges knoop in de query, zijn standaard velden die je kan gebruiken als filter. De applicatiegebruiker kan restricties aan het standaard bereik toevoegen of verwijderen. Je kan wel beperkingen op een bereik zetten door het gebruik van de bereikeigenschap Status. Normaal worden bereiken aangemaakt met de status “open”. Je kan een standaard waarde voor een bereik ingeven door de eigenschap Value. Als de statuseigenschap op “Lock” wordt gezet, dan mag de applicatie-gebruiker het bereik niet veranderen. Het vergrendelen van een bereik kan nuttig zijn als je gegevens in een formulier wil filteren en je de applicatiegebruiker informeert over de verplichte filter. Je kan de bereikeigenschap Status op “Hidden” zetten als je niet wil dat de verplichte filter zichtbaar is voor de applicatiegebruiker. De knoop Sorting wordt gebruikt om een index of velden op te geven voor het sorteren. Het opgeven van velden voor de sortering zorgt ervoor dat de opgehaalde gegevens gesorteerd of gegroepeerd worden op een bepaalde manier. In het MyQuery voorbeeld was geen sortering gedefinieerd. Zoals in selecties wordt sortering niet gespecifieerd tenzij je zeker wil zijn van de sorteervolgorde. Sortering wordt doorgaans gebruikt voor queries in rapporten. Een rapportquery heeft bijkomende eigenschappen voor het sorteren , zodat je sommaties kan maken gebaseerd op de sorteervelden. Meer informatie over het gebruik van queries in rapporten vind je in het hoofdstuk Rapporten.

MORPHX IT Queries

254

© 2007 Steen Andreasen

static void Queries_MyQueryAsSelect(Args _args) { CustInvoiceJour custInvoiceJour; CustInvoiceTrans custInvoiceTrans; ; while select custInvoiceJour join custInvoiceTrans where custInvoiceJour.salesId == custInvoiceTrans.salesId && custInvoiceJour.invoiceId == custInvoiceTrans.invoiceId && custInvoiceJour.invoiceDate == custInvoiceTrans.invoiceDate && custInvoiceJour.numberSequenceGroup == custInvoiceTrans.numberSequenceGroup { // print fetch data } } Als MyQuery zou geschreven worden als selectie-uitdrukking, dan zou de code eruit zien zoals hierboven. Merk op dat alle velden in de where voorwaarde via AND zijn gekoppeld. Een query zal voorwaarden altijd met AND aan elkaar koppelen. Alleen een selectie-instructie kan zowel AND als OR gebruiken. Niettemin zijn er truukjes om op dezelfde manier te werken in een query. Dit wordt uitgelegd in de volgende paragrafen. Aggregatiefuncties Standaard haalt een query alle velden van een record op, net zoals een selectie. Als je de eigenschap Dynamic op de Fields knoop op “No” instelt, zorg je ervoor dat de query alleen waarden van de opgegeven velden ophaalt. Het is aanbevolen dat je deze instelling enkel gebruikt als je de query moet optimaliseren omdat je deze optie mogelijk niet bewust gebruikt als je de query aanspreekt in X++. De eigenschap Dynamic heeft ook andere doeleinden. Wanneer je met je rechtermuisknop klikt op de Fields knoop, kan je een van de aggregatiefuncties kiezen : AVG, SUM, COUNT, MIN of MAX. Het kiezen van een aggregatiefunctie zal de eigenschap Dynamic op “No” zetten, omdat alleen de geaggregeerde velden een waarde zullen bevatten. Een aggregatiefunctie wordt gebruikt om een berekening te maken op de opgehaalde data. Het meest voorkomende gebruik van een aggregatiefunctie is het sommeren van een rapport. Voorbeeld 2: Aggregatiefunctie Elementen gebruikt van MorphxIt_Queries project

Query, MyQuery_Aggregate Dit voorbeeld toont hoe je de aggregatiefunctie SUM gebruikt om verkochte hoeveelheden per artikel te sommeren aan de hand van het klantenfactuurjournaal. 1. Maak een nieuwe query en geef de query de naam "MyQuery_Aggregate". 2. Versleep de tabel CustInvoiceTrans naar data source en verwijder de suffix.

MORPHX IT Queries

© 2007 Steen Andreasen

255

3. Ontvouw de knoop van de gegevensbron CustInvoiceTrans, klik met de rechtermuisknop op de knoop Fields en selecteer New/SUM. Open het eigenschappenblad voor het sommatieveld en kies het veld Qty.

4. Ga naar de Sorting knoop, klik met je rechtermuisknop en selecteer New/Field.

Gebruik het eigenschappenblad om ItemId te selecteren als sorteerveld. 5. De laatste stap is het instellen van de sorteervolgorde. Ga naar de gegevensbron

CustInvoiceTrans en stel de eigenschap OrderMode in op “Group by”. Je kan om het even hoeveel aggregatiefuncties toevoegen aan een query. Om het voorbeeld te vereenvoudigen is er één aggregatiefunctie gebruikt. Wanneer je aggregatiefuncties gebruikt, gebruik je sorteervelden om het groeperen van gegevens voor de functie te bepalen. Behalve de geselecteerde aggregatiefuncties zullen alleen de sorteervelden een waarde hebben. De ordermodus moet altijd “group by” zijn wanneer je aggregatiefuncties gebruikt. Het zou geen zin hebben om “order by” te gebruiken, omdat dan alle records zouden geprint worden en in dit voorbeeld zou je niets kunnen sommeren. static void Queries_TestMyQuery_Aggregate(Args _args) { SysQueryRun queryRun = new SysQueryRun(querystr(MyQuery_Aggregate)); CustInvoiceTrans custInvoiceTrans; ; if (queryRun.prompt()) { while (queryRun.next()) { custInvoiceTrans = queryRun.get(tableNum(CustInvoiceTrans)); if (queryRun.changed(tableNum(CustInvoiceTrans))) { info(strfmt("Item: %1, Qty: %2, ",custInvoiceTrans.itemId, custInvoiceTrans.qty)); } } } } Deze job kan gebruikt worden om de query te testen. Wanneer je hem uitvoert, zal je zien dat het sorteerveld is toegevoegd aan de querydialoog op het sorteertabblad. Aangezien de data worden opgehaald met een group by, kan je de sorteervelden niet veranderen. Gevorderde bereiken Een beperking in het gebruik van queries is dat bereikvoorwaardes via AND aan elkaar zijn gekoppeld. Een record wordt daarom pas geselecteerd als het aan alle voorwaardes voldoet. Laten we veronderstellen dat je transacties in het klantenfactuurjournaal wil zoeken die behoren tot de klantengroep “40” of die de valutacode “USD” hebben. Als je een query gebruikt, vind je enkel records die aan beide voorwaarden voldoen. Er bestaat een truukje om dit te omzeilen. Open het formulier CustInvoiceJournal en klik op het query-icoon . Geef nu het volgende bereik in : ((custgroup == "20") ||

MORPHX IT Queries

256

© 2007 Steen Andreasen

(currency == "USD")). Het symbool || staat hier voor OR. De ingegeven code wordt niet vertaald als een waarde voor een specifiek queryveld. Wanneer je op OK klikt in de querydialoog, worden de records in het formulier klantenfactuurjournaal opgehaald met een OR uitdrukking. Opmerking: Jokertekens zoals ‘*’ en ‘..’ kunnen ook gebruikt worden in X++ wanneer je het bereik van een query bepaalt. In de online help vind je een lijst van alle jokeropties voor de querydialoog. De haken maken het mogelijk om een booleaanse uitdrukking in een querybereik te schrijven door het gebruik van gegevensbronvelden. Er zijn echter geen validaties als je zo’n uitdrukking intikt. En daarom ben je op jezelf aangewezen om een foutenloze uitdrukking te schrijven. Om deze reden moet je altijd zo’n uitdrukkingen toevoegen aan je query met behulp van X++. Het uittesten van een booleaanse uitdrukking is veel gemakkelijker als je de uitdrukking in de querydialoog uitprobeert en daarna de tekst van het querybereik in X++ kopieert. Je zal je misschien afvragen waarom je al die moeite zou doen als dit allemaal kan met een selectie-uitdrukking. Het kan zijn dat je een bestaand object moet aanpassen , zoals een klasse of een formulier, dat gebruik maakt van een query. Als je een bestaande query moet vervangen door een selectie, moet je misschien veel code veranderen. Bovendien is het altijd te verkiezen om queries te gebruiken voor objecten die door de gebruikers-interface aangesproken worden. Methodes op een Query Methodes op een AOT-query worden haast nooit overschreven. Wijzigingen noteer je altijd op de methodes van de gegevensbron. Alleen formulieren hebben AOT-knopen voor het overschrijven van de gegevensbronmethodes. In het hoofdstuk Formulieren lees je hoe je de formuliergegevensbronmethodes moet gebruiken. X++ Query Zoals je hebt gezien, kan een query uitgevoerd worden vanuit X++, maar je kan X++ ook gebruiken om een query te bouwen en vervolgens uit te voeren met behulp van de systeemklassen met prefix Query*. Deze systeemklassen gebruik je in X++ doorgaans om eenvoudige queries te bouwen of queries die voor een specifiek doel worden gebruikt. Opmerking: Sommige querysysteemklassen worden overgeërfd als applicatieklassen. Je moet de applicatiesubklassen gebruiken aangezien ze toegevoegde logica bevatten. Een subklasse van een systeemklasse heeft als prefix Sys* zoals SysQuery. Wanneer je de kern van een query kent, zal je vrij gemakkelijk queries kunnen bouwen met behulp van X++. static void Queries_SystemClasses(Args _args) { SysQuery query; SysQueryRun queryRun; QueryBuildDataSource custInvoiceJourDS, custInvoiceTransDS;

MORPHX IT Queries

© 2007 Steen Andreasen

257

; query = new Query(); custInvoiceJourDS = query.addDataSource(tablenum(CustInvoiceJour)); custInvoiceJourDS.addRange(fieldnum(CustInvoiceJour, InvoiceAccount)); custInvoiceJourDS.addRange(fieldnum(CustInvoiceJour, CurrencyCode)); custInvoiceTransDS = custInvoiceJourDS.addDataSource(tablenum(CustInvoiceTrans)); custInvoiceTransDS.addRange(fieldnum(CustInvoiceTrans, ItemId)); custInvoiceTransDS.relations(true); queryRun = new SysQueryRun(query); queryRun.prompt(); } De query in dit MyQuery voorbeeld is gebouwd aan de hand van querysysteemklassen. Dit is even rechttoe rechtaan als de AOT gebruiken. Eerst moet je een nieuwe query declareren en een gegevensbron toevoegen aan de queryknoop. Met de klasse QueryBuildDataSource krijg je een hendel voor de gegevensbron CustInvoiceJour waarmee je de gegevensbron kan toevoegen op het volgend niveau. Net zoals in de AOT, worden de queryrelaties op “true” ingesteld, met behulp van de relaties gedefinieerd in de data dictionary. De standaard bereikvelden worden toegevoegd via de addRange()methode. Om een overzicht te krijgen van de query, wordt de prompt() methode aangeroepen wanneer je de job uitvoert. Het tonen van het querydialoogvenster helpt je om snel een overzicht te krijgen van de gebouwde query. Als je een query gebruikt voor het overlopen van een tijdelijke tabel, dan moet je de volgende lijn toevoegen na het initialiseren van je QueryRun object. De lijn eruit laten is een vaak voorkomende fout. Zonder deze lijn ga je je afvragen waarom je geen output krijgt van je tijdelijke tabel en uiteindelijk zou je er nog voor kiezen om de query achterwege te laten en over te stappen naar het gebruik van een select-instructie. queryRun.setRecord(MyTable); Het toevoegen van bereiken aan een query wordt vaak gedaan in X++. Een typisch geval is het filteren van gegevens opgehaald door een query met behulp van variabelen. static void Queries_SystemClassesRanges(Args _args) { SysQuery query; SysQueryRun queryRun; QueryBuildDataSource custInvoiceJourDS; QueryBuildRange rangeInvoiceAccount, rangeInvoiceDate, rangeDimensionDepartment; ; query = new Query(); custInvoiceJourDS = query.addDataSource(tablenum(CustInvoiceJour)); rangeInvoiceAccount = custInvoiceJourDS.addRange(fieldnum(CustInvoiceJour, InvoiceAccount)); rangeInvoiceAccount.value(queryValue("4000")); rangeInvoiceDate = custInvoiceJourDS.addRange(fieldnum(CustInvoiceJour, InvoiceDate)); rangeInvoiceDate.value(queryRange(datenull(), systemdateget())); rangeDimensionDepartment = custInvoiceJourDS.addRange(fieldId2Ext( fieldnum(CustInvoiceJour, Dimension), 1));

MORPHX IT Queries

258

© 2007 Steen Andreasen

rangeDimensionDepartment.value(queryValue("Sales")); queryRun = new SysQueryRun(query); queryRun.prompt(); } In bovenstaand voorbeeld worden 3 bereiken toegevoegd aan de query die maar 1 gegevensbron bevat. De methode addRange() geeft een instantie van de klasse QueryBuildRange terug die gebruikt wordt om de waarde in te stellen voor elk van de bereiken. Let op de globale methodes queryValue() en queryRange(). Je kan het gebruik van deze methodes overwegen wanneer je een waarde toevoegt aan een query om twee redenen : beide methodes aanvaarden de variabele anytype als parameter en ze formatteren de waarde die getoond wordt in het bereik. Zoals de naam al aangeeft, wordt QueryRange() gebruikt om een bereik in te geven (waarde van tot ) terwijl queryValue() wordt gebruikt voor één enkele waarde. Als je meer waarden nodig hebt om het bereik te bepalen, kan je in plaats daarvan de functie strfmt() gebruiken om je uitdrukking te formatteren. Denk er wel altijd aan dat de waarden van het bereik moeten gescheiden worden door komma’s. Het gebruik van arrayvelden om je querybereik te definiëren, vereist wat toegevoegde code. Dimension is het meest gebruikelijke arrayveld. Wanneer je Dimension toevoegt als een bereik in de AOT-query, heb je de keuze. Ofwel voeg je het veld Dimension toe waardoor ieder element van de array wordt toegevoegd aan het bereik. Ofwel voeg je ieder element van de array toe door het betreffende veld uit de veldenlijst te kiezen. Dit verschilt enigszins van X++ aangezien je niet zomaar naar het eerste arrayelement kan verwijzen met Dimension[1]. In plaats daarvan wordt de methode fieldId2Ext() gebruikt. De eerste parameter is het veldidentificatienummer en de tweede parameter is het nummer van het gewenste arrayelement. In het bovenstaande voorbeeld wordt het eerste element van de array dimension van de CustInvoiceJour toegevoegd aan het bereik. Een querybereik heeft een beperkte grootte. Niettemin is de maximum bereikgrootte relatief groot en je moet al verschillende honderden bereiken concateneren om de limiet te bereiken. Dit zal voor de meeste gevallen ruimschoots volstaan. Als het bereik van je query niet volstaat voor het bepalen van je criteria om de gegevens op te halen, gebruik je beter een selectie om een string samen te stellen die de waardes voor het querybereik bewaart. Dit kan een lijst van artikelnummers zijn die je uit de voorraadtabel wil ophalen. Dit wordt echter niet als een goed ontwerp beschouwd aangezien je niet kan weten of je de limiet gaat overschrijden. Het toevoegen van een bereik voor artikelnummers als hierop al een bereik is opgegeven, zal ertoe leiden dat de twee bereiken via AND aan elkaar worden gekoppeld. Als je de limiet bereikt, zal dit resulteren in een foutmelding van de databank. Door het gebruik van het uitgebreide datatype bereik, kan je verder gaan. Het uitgebreide datatype Range heeft een stringlengte van 250 karakters. Je kan de lengte veranderen naar 1000 karakters wat de maximum lengte is van een string. Vooraleer je dit doet, kan je in de plaats daarvan overwegen een tijdelijke tabel of een lijst met gesorteerde records te gebruiken. Je zal querybereiken zeer veel gebruiken bij het bouwen van formulieren en rapporten. Bereiken worden doorgaans gebruikt in X++ om op basis van het aanroepend formulier

MORPHX IT Queries

© 2007 Steen Andreasen

259

de opgehaalde gegevens te filteren vooraleer ze op een formulier of een rapport getoond worden.

8.2 Queries in Formulieren en Rapporten Zowel formulieren als rapporten maken gebruik van queries voor het ophalen van data. Aangezien formulieren en rapporten hun eigen knopen hebben voor het bouwen van een query, maken ze geen gebruik van de AOT-queries. Vergeleken met een AOT-query, hebben formulieren en rapporten bijkomende eigenschappen. Als je X++ gebruikt, kan je een andere query specifiëren of zelfs de volledige query bouwen in X++ om die te gebruiken voor een formulier of rapport. In het normale geval zou je de ingebouwde knopen in de AOT gebruiken. Een selectie-instructie kan dezelfde taak doen als een query in een formulier of een rapport maar het gebruik van selecties in een formulier of rapport is minder gebruiksvriendelijk. In formulieren worden de opties voor het filteren en sorteren van data ook gebruikt voor het afdrukken van automatische rapporten, gebaseerd op de velden opgegeven in de tabelveldgroep AutoReport. Formulieren zonder query hebben deze optie niet. Rapporten zonder een query zullen geen opties hebben voor het filteren of automatisch afdrukken van totalen. Als je een selectie in een rapport gebruikt, zullen de filteropties manueel in de code moeten gebouwd worden.

8.3 Samenvatting Queries hebben een centrale rol in MorphX. Het is belangrijk om het concept van queries te begrijpen omdat je queries veel zal gebruiken. Als je weet hoe een query wordt opgebouwd en hoe je de querysysteemklassen gebruikt, zal het gemakkelijker voor je zijn om objecten aan te passen met een query en zo je aanpassingen meer gebruiksvriendelijk te maken. Je moet nu de basiskennis hebben over het bouwen en het gebruiken van queries. Als je meer leest over queries in de hoofdstukken Formulieren en Rapporten, moet je het volledige beeld krijgen hoe MorphX queries gebruikt.

MORPHX IT Jobs

© 2007 Steen Andreasen

261

9 Jobs Je zal al wel gemerkt hebben dat heel wat voorbeelden in dit boek gebruik maken van jobs. Het schrijven van testscripts is dan ook het hoofddoel van jobs. Jobs moet je gebruiken om je wijzigingen te testen, zoals na het schrijven van een complexe codeblok, of om na te gaan wat een specifieke systeemfunctie doet, of om eenmalig gegevens aan te passen. Een job kan niet geërfd worden of opgeroepen worden vanuit code op dezelfde manier als methodes.

9.1 Maken van jobs Een job wordt gedefinieerd als static om het uitvoerbaar te maken. Zonder het sleutelwoord static zou de compiler de job beschouwen als eender welke methode. Bij het creëren van een nieuwe job wordt de optionele parameter Args automatisch toegevoegd aan het parameterprofiel. Het profiel van een job is identiek aan een uitvoerbare klassemethode. Jobs kunnen geen waarde teruggeven zoals een methode. Daarom wordt een job gedefinieerd met het void sleutelwoord. static void Jobs_MyJob(Args _args) { ; info("Test of job."); } Jobs kunnen opgeroepen worden door een menuoptie waardoor het mogelijk wordt een job uit te voeren via een menu. Dit is handig als je een job hebt geschreven voor datawijziging die moet uitgevoerd worden in een Axaptainstallatie zonder licentiecode voor MorphX. Een job kan niet versleept worden naar een menuoptie zoals formulieren, rapporten en uitvoerbare klassen. In plaats daarvan moet je zelf de menuoptie creëren. Vanwaar deze omslachtige manier van werken ? Wel, jobs zijn niet echt bedoeld om op een menu te plaatsen, en misschien wil men je zo dwingen wat langer na te denken voor je een job toevoegt aan een menu. Je kan daarentegen een job wel verslepen naar het knooppunt Classes. Hiermee creëer je een uitvoerbare klasse met de code van je job. Dit is bijzonder handig. Het enige wat je nu nog moet doen, is een menuoptie voor de klasse creëren. Als je een job op een menu plaatst, moet je wel zeker zijn dat de gegevens niet gecorrumpeerd worden als de job tweemaal zou uitgevoerd worden. static void Jobs_ExecutingJob(Args _args) { Args args; ; args = new Args(); args.name(identifierStr(Jobs_MyJob)); new menuFunction(menuItemActionStr(Jobs_MyJob), MenuItemType::Action).run(args); } Als je een menuoptie voor een job maakt, kan je de menuoptie gebruiken om je job uit te voeren vanuit code. Het voorbeeld toont hoe de job Jobs_MyJob wordt uitgevoerd. Merk op dat de naam van de job in args.name() gespecifieerd wordt via de algemene

MORPHX IT Jobs

262

© 2007 Steen Andreasen

functie identifierStr() aangezien er geen specifieke functie voor jobs is. Args kan ook gebruikt worden om een record of parameter door te geven aan de opgeroepen jobs. Dit is nuttig als je job wordt opgeroepen vanaf een formulier en je wil de job het cursor record in het formulier doorgeven. Meer uitleg over het doorgeven van een cursor record aan een opgeroepen menuoptie vind je in het hoofdstuk Formulieren.

9.2 Samenvatting Je moet nu een idee hebben van het gebruik van jobs en wanneer je je code beter in methodes kan plaatsen.

MORPHX IT Menuopties en Menu’s

© 2007 Steen Andreasen

263

10 Menuopties en Menu’s Objecten zoals formulieren, rapporten en uitvoerbare klassen worden beschikbaar gesteld voor applicatiegebruikers door het toevoegen van deze objecten aan menu’s. Vaak zullen applicatiegebruikers geen toegang hebben tot de AOT, en dus krijg je hiermee een mogelijkheid om te controleren welke functionaliteiten aan de applicatiegebruikers worden aangeboden. Anders gezegd, je zal enkel een object aan een menu toevoegen als het klaar is voor gebruik. Om een object op te roepen vanuit een menu, moet je twee stappen nemen. Allereerst moet je een menuoptie aanmaken, en ten tweede moet deze menuoptie opgeroepen worden vanuit een menu. De reden van deze twee stappen is dat je meer dan één menuoptie kan maken voor een object. Je kan verschillende eigenschappen instellen voor de menuopties en hiermee kan je het gedrag van het object laten afhangen van de oproepende menuoptie.

10.1 Menuopties Menuopties worden gegroepeerd in 3 verschillende knooppunten. De groepering is enkel een logische groepering, waardoor een overeenkomstige pictogram voor het object in het menu wordt getoond. Niettemin kan je je best aan de volgende regels houden : het display knooppunt is voorbehouden voor formulieren, het output knooppunt voor rapporten en het action knooppunt voor uitvoerbare klassen. Als je een klasse gebruikt voor het oproepen van een formulier of rapport, kan je toch best de knooppunten display of output gebruiken. De menuoptie en dus het pictogram dat in het menu wordt getoond, verwijst best naar het objecttype dat de gebruiker zal zien als de menuoptie wordt geactiveerd. De eenvoudigste manier om een nieuwe menuoptie aan te maken, is door het verslepen van je object naar het gewenste menuoptietype. Hierdoor wordt een nieuw menuoptie aangemaakt met dezelfde naam als je object. Daarna moet je enkel nog een label toevoegen en de gebruikerstoelatingen instellen voor de menuoptie. Als er geen label is opgegeven voor een menuoptie, wordt in het menu deze menuoptie getoond met de naam van de menuoptie voorafgegaan door de asterisk (*). Opmerking : Je zou altijd je wijzigingen moeten testen door de objecten op te roepen via de menuopties in plaats van via de AOT. Hierdoor krijg je een zicht hoe je wijzigingen zich gedragen als de objecten worden opgeroepen door de applicatiegebruikers. Menuopties behoren tot een van de hoofddomeinen om gebruikerstoelatingen te beheren. De eigenschappen ConfigurationKey en SecurityKey worden gebruikt om te bepalen welke gebruiker en gebruikersgroep een menuoptie mogen gebruiken. Als een gebruiker geen toelating heeft tot een menuoptie, zal deze menuoptie niet zichtbaar zijn voor de gebruiker. Deze handige eigenschappen zorgen ervoor dat enkel toegankelijke menu’s worden getoond. De gebruiker zal dus een beperkt menu zien, waarop enkel die applicatieonderdelen te zien zijn waartoe de gebruiker wordt toegelaten. Het tweede hoofddomein voor het beheren van gebruikerstoelatingen zijn de tabellen. Meer

MORPHX IT Menuopties en Menu’s

264

© 2007 Steen Andreasen

informatie over configuratie- en veiligheidsleutels vind je terug in het hoofdstuk Data Dictionary. Opmerking : MorphX zal automatisch een sneltoets voor je menuopties selecteren. Om de standaard sneltoets te wijzigen, moet je een ampersand (‘&’) voor de letter plaatsen die je als sneltoets wenst : Transacti&ons. Dit werkt niet als je het label van de menuoptie wijzigt. Hiervoor moet je de eigenschap Text van de MenuItemButton wijzigen. Je kan een menuoptie gebruiken om een object op te roepen vanuit een menu, vanuit een formulier of vanuit code. Objecten gebruikt voor de gebruikersinterface zoals formulieren en rapporten zou je altijd via menuopties moeten oproepen. Je kan natuurlijk formulieren en rapporten vanuit code oproepen zonder een menuoptie, maar het gebruik van een menuoptie garandeert je dat de gebruikerstoelatingen automatisch worden geverifieerd. static void MenuItems_ExecuteObject(Args _args) { ; new menuFunction(menuItemDisplayStr(CustTable), MenuItemType::Display).run(); } Hier zie je hoe het formulier CustTable wordt opgeroepen met behulp van de display menuoptie CustTable. Het formulier wordt enkel geladen indien de gebruiker toegang heeft gekregen tot het CustTable formulier. De methode run() die de menuoptie uitvoert, heeft de klasse Args als parameter. Je kan Args bijvoorbeeld gebruiken om een parameter door te geven aan het opgeroepen object om gegevens te filteren. Meer informatie over de klasse Args vind je in het hoofdstuk Klassen. Een andere manier om een waarde door te geven aan het opgeroepen object is door gebruik te maken van de parametereigenschappen van de menuoptie. De eigenschappen EnumTypeParameter en EnumParameter kunnen gebruikt worden om een enum te definiëren en een element voor de gekozen enumeratie. Het instellen van een enum parameter voor een menuoptie maakt het eenvoudig om een object te hergebruiken voor diverse doeleinden. Zo kan je één formulier aanmaken en op verschillende manieren aansturen in plaats van diverse gelijkaardige formulieren te maken. Deze techniek wordt gebruikt in Axapta voor het oproepen van de journaals. Neem een kijkje bij de display menuopties waarvan de naam begint met LedgerJournalTable*. Je zal zien dat deze menuopties hetzelfde formulier LedgerJournalTable oproepen met verschillende onderdelen van de enum LedgerJournalType. In het formulier LedgerJournalTable wordt de enum gebruikt om het gedrag van het formulier te bepalen. Dit is een handige manier om je code te schikken, omdat het eenvoudig is als je een nieuw journaaltype wil aanmaken. Je moet enkel een nieuw onderdeel maken voor de enumeratie LedgerJournalType, een nieuwe menuoptie maken die gebruik maakt van dit enumeratie-element en een controle voorzien in het formulier LedgerJournalTable voor het nieuwe enumelement. Een overzicht van alle eigenschappen van menuopties vind je in de Appendix Eigenschappen.

MORPHX IT Menuopties en Menu’s

© 2007 Steen Andreasen

265

10.2 Menu’s Om een nieuw menu aan te maken, kan je eenvoudig menuopties naar je menu verslepen. Scheidingstekens en submenu’s kunnen aangemaakt worden door te klikken met de rechtermuisknop op het menuknooppunt en kies vervolgens new. Maar in plaats van submenu’s aan te maken en vervolgens menuopties toe te voegen aan dit submenu, kan je eens kijken naar het alternatief van een menuverwijzing. Een menuverwijzing is een koppeling naar een ander menu. Als je een nieuw object toevoegt aan een menu, kan je kiezen voor het toevoegen van een menuverwijzing. Je zal een venster zien verschijnen waar je een bestaand menu kan kiezen en verslepen naar je menu als menuverwijzing. Let wel : als je een ander menu direct van de AOT versleept naar je menu, dan zal het gekozen menu aangemaakt worden als submenu en niet als menuverwijzing. Als je een nieuwe menuoptie toevoegt aan een menu, ben je in feite een bestaand menu aan het wijzigen. Om het menubeheer eenvoudiger te maken, zou je moeten nagaan of het niet beter is een nieuw menu te maken voor de nieuwe menuopties en dit nieuwe menu toe te voegen als menuverwijzing aan het bestaande menu. Dit heeft twee doeleinden. Ten eerste houd je je wijzigingen gescheiden van de standaardmenu’s en ten tweede kan je je menu eenvoudig gebruiken in een ander menu. Configuratiesleutels en veiligheidsleutels kunnen ingesteld worden voor een volledig menu via de menueigenschappen. Maar deze werkwijze wordt niet aanbevolen. Je kan beter de gebruikerstoelatingen op menuopties definiëren, omdat je zo zeker bent dat de gebruikerstoelatingen altijd geverifieerd worden, ongeacht vanwaar de menuoptie wordt opgeroepen. Bovendien is het instellen van een gebruikerstoelating eenvoudiger te beheren omdat je de instelling op één plaats hebt gezet. Opmerking : Het is aanbevolen dat ieder menu opgeroepen vanuit het hoofdmenu maximaal 5 menuopties heeft bovenaan het menu. De overige menuopties zouden gegroepeerd moeten worden in submenu’s genaamd: Journals, Inquiries, Reports, Periodic en Setup. Het localiseren van het AOT-object vanuit het menu Een van de eerste taken die je moet doen als je een object in de AOT wijzigt, is het opzoeken van de naam van het object. Je weet waarschijnlijk in welk menu het object kan opgeroepen worden. Nu heb je twee opties om de AOT naam van het object te krijgen en doorgaans gaat het om een formulier, een rapport of een klasse. Omdat je het menupad van het object kent, kan je het overeenkomstige menu in de AOT opzoeken en het menuknooppunt in de AOT openklappen om de betreffende menuoptie te vinden. Kijk het eigenschappenblad van de menuoptie na en de eigenschappen Class en Object geven je dan de naam van het AOT object. De tweede optie is te klikken met de rechtermuisknop op een open formulier of dialoog en daarna Setup te kiezen. Dit opent het formulier User setup zoals getoond in figuur 47 : User setup formulier. Ga naar het tabblad Information. De eerste drie velden tonen je de AOT naam van het opgeroepen formulier of klasse en de menuoptie die het object oproept. Klik op de Edit knop om het object in de AOT te openen. Merk op dat deze optie enkel beschikbaar is voor formulieren en uitvoerbare klassen en ze kan niet gebruikt worden voor queries en rapporten.

MORPHX IT Menuopties en Menu’s

266

© 2007 Steen Andreasen

Figuur 47: User setup formulier Zoals je kan zien, gaat het hier niet om snelle manieren om een AOT objectnaam op te zoeken. Jammer genoeg zijn er niet meer opties als je enkel het menupad kent. Maar MorphX is open source en geeft dus zijn eigen broncode prijs. Dit houdt in dat je het systeem kan wijzigen met behulp van de systeemklassen waardoor je gemakkelijker aan de AOT informatie kan geraken.

10.3 Samenvatting Je zou nu moeten weten hoe je een object oproept met behulp van een menuoptie. Je moet ook het belang van de menuopties kennen bij het beheer van gebruikerstoelatingen. Als je menuopties toevoegt aan een menu, zou je moeten weten hoe je je menu’s indeelt om het hergebruik en beheer eenvoudiger te maken.

MORPHX IT Hulpbronnen

© 2007 Steen Andreasen

267

11 Hulpbronnen Hulpbronnen of resources worden gebruikt om eender welk type bestand te bewaren. In plaats van bitmaps of een ander type bestand te gebruiken voor het beheren van gegevens via het bestandssysteem, kan je de bestanden toevoegen aan het knooppunt Resources. Dit knooppunt Resources is niet op dezelfde manier geïntegreerd zoals objecten van het type formulier of rapport. Bij het gebruik van de resource knooppunten, doe je een beroep op de algemene systeemklassen voor het doorkruisen van de AOT knooppunten. In sommige gevallen zal je tijdelijk een resource knooppunt moeten exporteren voor je het bestand kan gebruiken. De resource knooppunten werden voor de eerste maal ingevoerd in versie 3.0 van Axapta. Dit is de reden dat er nog maar een handvol methodes beschikbaar is voor het gebruik van de hulpbronknopen. In ieder geval zijn hulpbronnen nuttig, omdat je nu geen paden van het bestandssysteem nodig hebt, en bovendien verzamel je zo alle bestanden die nodig zijn binnen je applicatie, op één plaats. Hulpbronnen bewaard onder de Resources knoop in de AOT mag je niet verwarren met de hulpbronnen beheerd in de kernel file. Bitmaps gebruikt voor AOT knopen worden allen bewaard in de kernel file en je verwijst ernaar met behulp van een resource id. Een overzicht van de resources in de kernel file met inbegrip van de bijbehorende resource id vind je in het formulier Tutorial_Resources.

11.1 Het gebruik van hulpbronnen Een hulpbron voeg je toe door de Resources knoop te selecteren, klik vervolgens met de rechtermuisknop en kies Create from file. Blader door het bestandssysteem met behulp van het dialoogvenster dat verschijnt, en kies een bestand. Als je op de Open knop klikt, zal het gekozen bestand opgeslagen worden in de AOT. Informatie over de toegevoegde hulpbron kan je bekijken via een klik op de rechtermuisknop en kies daarna Open. Het hulpbrontype zie je in het voorschouwvenster. Als de hulpbron een bitmap is, wordt deze bitmap ook getoond in het voorschouwvenster. Als je een bitmap toevoegt aan een formulier of rapport, kan je de bitmap op verschillende plaatsen halen : je kan kiezen tussen kernel resources, een bitmap bewaard in een tabel, een bestandspad naar een bitmap of je kan verwijzen naar een bitmap onder de resources knoop. Afhankelijk van het geval is de ene oplossing al beter dan de andere. Als je een bitmap met je wijzigingen in productie wilt nemen, of je wilt zeker zijn dat die bepaalde bitmap wordt gebruikt, moet je nadenken over de optie om de bitmap op te slaan onder de resources knoop. Voorbeeld 1: het laden van een bitmap Objecten gebruikt van MORPHXIT_Resources project

Resource, MORPHXIT Form, Resources_LoadBitmap

MORPHX IT Hulpbronnen

268

© 2007 Steen Andreasen

In dit voorbeeld wordt een bitmap, bewaard onder de resources knoop, geladen en getoond in een formulier. Als je het voorbeeld wil uitvoeren, moet je eerst een resource van het type image met de naam MORPHXIT aanmaken. 1. Maak een nieuw formulier en geef het de naam “Resources_LoadBitmap”. 2. Ga naar de knoop Design en stel de eigenschappen Width en Height in op Column

width en Column height. 3. Voeg een vensterelement toe aan het ontwerp. Dit vensterelement zal gebruikt

worden om de hulpbronknoop MORPHXIT te tonen. Stel de eigenschap AutoDeclaration in op Yes.

4. Maak een nieuwe formuliermethode genaamd showResource(). De methode zal

gebruikt worden om de hulpbron te laden. De methode moet er als volgt uitzien :

public void showResource() { ResourceNode resourceNode; Container imageContainer; Image image; ; resourceNode = SysResource::getResourceNode(resourceStr(MORPHXIT)); resourceNode.AOTload(); imageContainer = SysResource::getResourceNodeData(resourceNode); image = new Image(imageContainer); imageCtrl.widthValue(image.width()); imageCtrl.heightValue(image.height()); imageCtrl.image(image); } 5. In de laatste stap moet je de nieuwe methode showResource() vanuit run() oproepen.

Overschrijf run() door het volgende toe te voegen :

public void run() { super(); element.showResource(); } De klasse SysResource en de systeemklasse ResourceNode worden allebei gebruikt voor het manipuleren van de hulpbronnen. SysResource omvat verschillende handige statische methodes die zowel voor het laden als het bewaren van hulpbronnen gebruikt kunnen worden. SysResource wordt doorgaans gebruikt om de resource knoop op te zoeken en de gevonden knoop door te geven aan ResourceNode. Als je de methodes in de klasse SysResource van dichterbij bekijkt, zal je zien dat de klassemethodes gebruik maken van de systeemklasse TreeNode. TreeNode is een basisklasse die gebruikt kan worden om door eender welk AOT knooppunt te navigeren.

MORPHX IT Hulpbronnen

© 2007 Steen Andreasen

269

Let erop dat je na de initialisatie van de ResourceNode in showResource(), de methode AOTLoad() oproept. Als je deze methode niet oproept, zal de bitmap niet getoond worden in het formulier. Het vensterelement toegevoegd om de bitmap te tonen, wordt geïnitialiseerd met behulp van de systeemklasse Image. Zowel de hoogte als de breedte van het vensterelement worden zo ingesteld dat het vensterelement overeenstemt met de grootte van de bitmap. In dit voorbeeld werd een bitmap van het type JPG gebruikt. Maar niet alle types bitmaps worden ondersteund : zo kan je bijvoorbeeld geen gebruik maken van GIF. Je zal een foutmelding krijgen indien je toch een niet ondersteunde bitmap zoals GIF wil gebruiken. Als je een module hebt gemaakt of een add-on oplossing, kan je gebruik maken van hulpbronnen om bestanden aan je pakket toe te voegen of zelfs gegevens. Demonstratiegegevens of standaard gegevens worden vaak verscheept met het pakket om de opstart voor de gebruiker eenvoudiger te maken. Je moet dan een export maken van de nodige gegevens en resource knooppunten maken voor de gegevensbestanden. Deze werkwijze werd ook gebruikt in het standaardpakket voor het opzetten van het bedrijfsportaal en het importeren van de rollen voor de gebruikers van de Enterprise Portal. Voorbeeld 2: standaard data Objecten gebruikt van MORPHXIT_Resources project

Resource, CustGroup_Data Resource, CustGroup_Def Job, Resources_DefaultData

Dit voorbeeld toont aan hoe je gebruik kan maken van gegevensexportbestanden die zijn toegevoegd als hulpbronnen. De gegevens zullen automatisch worden geïmporteerd. Je krijgt een dialoogscherm te zien als er al gegevens bestaan in de tabel waarin de gegevens worden geïmporteerd. Voor de eenvoud voegen we enkel gegevens toe aan de tabel CustGroup. 1. Start het exporteren van de inhoud van de tabel CustGroup via de export data

menuoptie die je in het hoofdmenu vindt onder Administration | Periodic | Data export/import | Export.

2. Maak resource knooppunten respectievelijk voor het gegevensbestand en het

definitiebestand. De resource knooppunten noem je CustGroup_Data en CustGroup_Def.

3. Maak een job aan met de naam Resources_DefaultData en schrijf hierin de volgende

code:

static void Resources_DefaultData(Args _args) { FilePath tempPath; ResourceNode resourceDefinitionFile; ResourceNode resourceDataFile; SysDataImport sysDataImport; ;

MORPHX IT Hulpbronnen

270

© 2007 Steen Andreasen

tempPath = xinfo::directory(directoryType::Temp); resourceDefinitionFile = sysResource::getResourceNode(resourceStr(CustGroup_Definition)); resourceDataFile = sysResource::getResourceNode(resourceStr(CustGroup_Data)); sysResource::exportResource(resourceDefinitionFile, tempPath); sysResource::exportResource(resourceDataFile, tempPath); sysDataImport = sysDataImport::newFilename(tempPath + resourceDefinitionFile.filename()); sysDataImport.parmLoadAll(true); sysDataImport.run(); } De import tool die je kan oproepen vanuit het hoofdmenu, wordt gebruikt voor het importeren van de hulpbrongegevens. Voor je de gegevensbestanden, bewaard als hulpbronnen, kan importeren, moet je de bestanden eerst exporteren. De gegevens-bestanden worden tijdelijk geëxporteerd naar het temp pad voor de Axapta installatie. Dit doe je met behulp van de klasse SysResource voor het opzoeken van de resource knooppunten en het exporteren van de bestanden. De exportbestanden kan je nu importeren via de data import tool. Als je de import tool vanop het hoofdmenu oproept, krijg je een dialoogscherm te zien waar je bestandsnaam en instellingen kan opgeven. De rest van de dialoog kan je overslaan omdat er geen verdere informatie nodig is. Dat is alles. Dit is echt een gebruiksvriendelijke manier om gegevens voor je wijzigingen te installeren, en zo moet de applicatiegebruiker geen moeite doen om handmatig de gegevens op te sporen en te importeren.

11.2 Samenvatting Het gebruik van hulpbronnen is niet wijd verspreid in MorphX. Dit is waarschijnlijk de reden dat hulpbronnen vaak vergeten worden en dat alternatieve oplossingen worden uitgedacht. Dit hoofdstuk heeft hopelijk wat meer inzicht gebracht in het gebruik van hulpbronnen en hoe je door hun gebruik de bestanden in het bestandssysteem niet meer nodig hebt.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

271

12 Bijlage eigenschappen In dit hoofdstuk vind je een overzicht van alle eigenschappen die vanuit de AOT bereikbaar zijn. De eigenschappen worden in iedere sectie alfabetisch gesorteerd. Objecten met gelijkaardige eigenschappen worden gegroepeerd om een meer compact overzicht te hebben. Sommige secties hebben een derde kolom Type, dat gebruikt wordt om de objecten op te lijsten die over de betreffende eigenschap beschikken. Waar je all ziet staan in de kolom Type, geldt de eigenschap voor alle objecten in de sectie.

12.1 Data Dictionary Eigenschappen De eigenschappen van de metadata views vind je terug in de sectie Query Eigenschappen. Tabellen, Tabelkoppelvlakken en Tabel views In de Engelstalige Axapta-omgeving gebruikt men de termen Tables, Table Maps en Table Views. Eigenschap Omschrijving

CacheLookup Gebruikt om het caching algorithme te specifiëren dat gebruikt wordt als een specifiek record geselecteerd wordt door een WHERE statement.

ChangedBy De gebruiker die het laatste de tabel heeft gewijzigd.

ChangedDate De datum waarop de tabel het laatst is gewijzigd.

ChangedTime Het tijdstip waarop de tabel het laatst is gewijzigd.

ClusterIndex Geeft aan welke index gebruikt moet worden als cluster index. Enkel unieke indexen kunnen hier opgegeven worden.

ConfigurationKey Gebruikt om de Configuratiesleutel voor de tabel op te geven.

CreatedBy De gebruiker die de tabel heeft aangemaakt. Indien ingesteld, wordt het systeemveld createdBy aangepast wanneer er een record wordt toegevoegd.

CreatedDate De datum waarop het record is aangemaakt. Indien ingesteld, wordt het systeemveld CreatedDate aangepast wanneer er een record wordt toegevoegd.

CreatedTime Het tijdstip waarop het record is aangemaakt. Indien ingesteld, wordt het systeemveld createdTime aangepast wanneer er een record wordt toegevoegd.

MORPHX IT Bijlage eigenschappen

272

© 2007 Steen Andreasen

Eigenschap Omschrijving

CreatedTransactionId Indien ingesteld, wordt de transactie-identificatienummer van de transactie die het record heeft aangemaakt, opgeslagen.

CreateRecIdIndex Indien ingesteld, wordt een index genaamd RecId aangemaakt. Dit veld bevat het recordidentificatienummer.

CreationDate De datum waarop de tabel is aangemaakt.

FormRef De naam van een display-menuoptie die gebruikt moet worden als je naar de hoofdtabel wil gaan.

ID Het identificerend nummer van de tabel.

Label Het label van de tabel. Dit is het label dat de applicatiegebruiker te zien krijgt.

LockedBy Bevat de naam van de gebruiker die momenteel de tabel vergrendelt.

MaxAccessMode Definieert de toegangsmodus van de tabel.

ModifiedBy Indien ingesteld, wordt het gebruikersprofiel van de gebruiker die het record heeft gewijzigd, hier bewaard.

ModifiedDate Indien ingesteld, wordt de datum van recordwijziging bewaard.

ModifiedTime Indien ingesteld, wordt het tijdstip van recordwijziging bewaard.

ModifiedTransactionId Indien ingesteld, wordt het identificerend nummer van de transactie die het record wijzigde, hier bewaard.

Name De naam van de tabel, tabelmap of tabel view.

PrimaryIndex Geeft aan welke index gebruikt moet worden als primaire index. Enkel unieke indexen kunnen opgegeven worden als primaire index.

SaveDataPerCompany Geeft aan of gegevens per bedrijf moeten bewaard worden.

SecurityKey Gebruikt om een veiligheidsleutel voor de tabel op te geven.

Systemtable Indien ingesteld, wordt de tabel beschouwd als een systeemtabel.

TableContents Geeft aan welk type data de tabel bevat.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

273

Eigenschap Omschrijving

TableGroup Geeft aan tot welke tabelgroep de tabel behoort. Deze eigenschap wordt gebruikt door de kernel om het uitvoeringsplan te bepalen voor selecties die gebruik maken van joins.

Temporary Ingesteld voor tijdelijke tabellen.

TitleField1 Het eerste veld dat gebruikt wordt in de formuliertitel.

TitleField2 Het tweede veld dat gebruikt wordt in de formuliertitel.

Tabelveld, koppelvlakveld In de Engelstalige Axapta-omgeving gebruikt men de termen Table field, Map field. Eigenschap Type Omschrijving

Adjustment String Specifieert de horizontale schikking indien een uitgebreid datatype niet is opgegeven.

AliasFor All Geeft aan voor welk veld dit veld een alias is.

AllowEdit All Indien ingesteld is het veld editeerbaar.

AllowEditOnCreate All Indien ingesteld is het veld editeerbaar bij de creatie van het record.

ConfigurationKey All Wordt gebruikt om een configuratiesleutel voor het veld te specifiëren.

EnumType Enum Geeft aan welke enumeratie gebruikt wordt voor het tabelveld.

ExtendedDataType All Geeft aan welk uitgebreid datatype gebruikt wordt voor het tabelveld.

FieldUpdate Integer Real

Standaardinstelling is Absolute dat aanduidt dat de huidige waarde wordt overschreven als het veld wordt gewijzigd. Relative staat toe dat meerdere applicatiegebruikers naar hetzelfde record

MORPHX IT Bijlage eigenschappen

274

© 2007 Steen Andreasen

Eigenschap Type Omschrijving schrijven waarna de waardes ingetikt door de gebruikers, gesommeerd worden.

GroupPrompt All Geeft aan welk veldlabel gebruikt moet worden bij gebruik in een veldgroep.

HelpText All Helptekst die afgebeeld wordt in de statusbalk.

ID All Het unieke identificatienummer van het veld.

Label All Gebruikt om het label van het uitgebreid DataType te overschrijven, indien zo’n datatype was opgegeven.

Mandatory All Indien ingesteld, moet een waarde ingevuld worden door de applicatiegebruiker.

Name All De veldnaam.

SaveContents All Indien ingesteld, wordt de waarde van het tabelveld bewaard in de databank.

StringSize String Gebruikt om de veldlengte in karakters op te geven indien er geen uitgebreide datatype is opgegeven.

Type All Toont het basisveldtype.

Visible All Indien ingesteld, is het veld zichtbaar op formulieren.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

275

View Velden In de Engelstalige Axapta-omgeving gebruikt men de term View fields. Eigenschap Type Omschrijving

Aggregation All Geeft de aggregatie aan die op het veld wordt uitgevoerd.

ConfigurationKey All Gebruikt om een configuratiesleutel op te geven voor het veld.

DataField All Selecteert een veld van de geselecteerde gegevensbron.

DataSource All Gegevensbron die gebruikt wordt in het formulierelement. Gegevens worden opgehaald via deze gegevensbron.

EnumType Enum Toont de enumeratie gebruikt voor het tabelveld.

ExtendedDataType All Toont het uitgebreide datatype gebruikt voor het tabelveld.

GroupPrompt All Het veldlabel dat getoond wordt bij gebruik in een veldgroep.

HelpText All Helptekst die getoond wordt in de helptekstsectie van Axapta als het veld wordt geselecteerd.

ID All Het identificatienummer van het view veld.

Label All Gebruikt om het label van het uitgebreide datatype te overschrijven.

Name All De veldnaam.

StringSize String Bepaalt de tekenreeksgrootte van het tabelveld.

Type All Toont het basisveldtype.

MORPHX IT Bijlage eigenschappen

276

© 2007 Steen Andreasen

Tabelveldgroep, koppelvlakveldgroep, View veldgroep In de Engelstalige Axapta-omgeving gebruikt men de termen Table field group, Map field group, View field group. Eigenschap Omschrijving

Name Groepsnaam

Label Label van de groepsnaam.

Tabelindex In de Engelstalige Axapta-omgeving gebruikt men de term Table index. Eigenschap Omschrijving

Name Naam van de index.

AllowDuplicates Indien ingesteld, kan de index twee of meer records met dezelfde indexsleutel bevatten.

ConfigurationKey Gebruikt om een configuratiesleutel voor de index op te geven.

Enabled Indien ingesteld, is de index actief.

ID Het identificatienummer van de index.

Tabelrelatie In de Engelstalige Axapta-omgeving gebruikt men de term Table relation. Eigenschap Omschrijving

Name Naam van de tabelrelatie.

Table Tabel waaraan gerelateerd wordt.

Validate Bepaalt of relaties gevalideerd moeten worden. Indien dit niet ingesteld is, zal de tabelrelatie niet gebruikt worden in objecten zoals formulieren en rapporten.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

277

Tabelrelatieveld In de Engelstalige Axapta-omgeving gebruikt men de term Table relation field. Eigenschap Type Omschrijving

Field Normal Field fixed

Eén van de velden dat deel uitmaakt van de veldrelatie.

RelatedField Normal Related field fixed

Eén van de velden dat deel uitmaakt van de veldrelatie.

Value Field fixed Related field fixed

Geeft een vaste waarde aan voor een relatie. Normaal gebruikt voor het instellen van de waarde van een enumeratie.

Tabel Wisactie In de Engelstalige Axapta-omgeving gebruikt men de term Table delete action. Eigenschap Omschrijving

DeleteAction Actie uit te voeren bij het wissen van een record.

Table Tabel gebruikt voor de wisactie.

koppelvlakkoppeling In de Engelstalige Axapta-omgeving gebruikt men de termen Map mapping. Eigenschap Omschrijving

MappingTable Geeft de tabel aan waaraan het koppelvlak moet gekoppeld worden.

Koppelvlakveldtoewijzing In de Engelstalige Axapta-omgeving gebruikt men de term Map field mapping. Eigenschap Omschrijving

MapField Het veld in het koppelvlak.

MapFieldTo Geeft het veld aan waaraan het koppelvlak moet gekoppeld worden.

MORPHX IT Bijlage eigenschappen

278

© 2007 Steen Andreasen

Uitgebreid Datatype In de Engelstalige Axapta-omgeving gebruikt men de term Extended Data Type. Eigenschap Type Omschrijving

Adjustment String Specifieert de horizontale positionering, indien het uitgebreide datatype niet erft van een ander uitgebreid datatype.

Alignment All Bepaalt de alignering van de waarde.

AllowNegative Integer Real

Indien uitgeschakeld kunnen enkel positieve waardes worden ingetikt.

ArrayLength All Geeft aan uit hoeveel array-elementen het uitgebreide datatype bestaat.

AutoInsSeparator Real Dit wordt door MorphX gebruikt om een decimaal scheidingsteken te zetten.

ButtonImage All Geeft aan welke afbeelding er getoond wordt aan de rechterkant van het formulierelement indien de gebruiker de opzoekfunctie kan gebruiken.

ChangeCase String Gebruikt om de gegevens van kleine letters naar hoofdletters om te zetten.

ConfigurationKey All Gebruikt om een configuratiesleutel op te geven voor het uitgebreide datatype.

DateDay Date Bepaalt hoe de dag wordt afgebeeld. De Windows regionale instellingen zijn de standaardinstelling.

DateFormat Date Bepaalt het datumformaat. De Windows regionale instellingen zijn de standaardinstelling.

DateMonth Date Bepaalt hoe de maand wordt getoond. De Windows regionale instellingen zijn de standaardinstelling.

DateSeparator Date Stelt het datumscheidingsteken in. De Windows regionale instellingen zijn de standaardinstelling.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

279

Eigenschap Type Omschrijving

DateYear Date Bepaalt hoe het jaar wordt getoond. De Windows regionale instellingen zijn de standaardinstelling.

DecimalSeparator Real Bepaalt het decimale scheidingsteken. De Windows regionale instellingen zijn de standaardinstelling.

DisplayNegative Real Past de positie van de afgebeelde negatieve waardes aan.

DisplayHeight String Stelt het maximale aantal lijnen in die tegelijk worden getoond in het formulierelement.

DisplayLength All Stelt het maximale aantal karakters in die tegelijk worden getoond in het formulierelement.

EnumType Enum Geeft op welke enumeratie er gebruikt wordt.

Extends All Indien ingesteld, worden bepaalde eigenschappen overgeërfd van het vermelde uitgebreide datatype, zoals Alignment en StringSize.

FormatMST Real Formatteer de waarde volgens de instellingen van de standaardbedrijfsmunteenheid.

FormHelp All Formulier dat gebruikt wordt bij het opstarten van een opzoekfunctie in het formulierelement.

HelpText All Helptekst die afgebeeld wordt in de statusbalk.

Label All Het label dat gebruikt wordt.

Name All Naam van het uitgebreide datatype.

NoOfDecimals Real Stelt het aantal getallen in dat na het decimale scheidingsteken getoond moet worden.

MORPHX IT Bijlage eigenschappen

280

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

RotateSign Integer Real

Gebruikt om negatieve waardes te inverteren.

ShowZero Integer Real

Geeft aan of nulwaardes getoond moeten worden.

SignDisplay Integer Real

Geeft aan hoe negatieve waardes getoond moeten worden.

StringSize String Geeft de lengte in karakters weer.

Style Enum Bepaalt de grafische weergave van het uitgebreide datatype.

ThousandSeparator Real Bepaalt het scheidingsteken voor duizendtallen. De Windows regionale instellingen zijn de standaardinstelling.

TimeFormat Time Bepaalt het tijdsformaat. De Windows regionale instellingen zijn de standaardinstelling.

TimeHours Time Geeft aan of de uren getoond moeten worden. De Windows regionale instellingen zijn de standaardinstelling.

TimeMinute Time Geeft aan of de minuten getoond moeten worden. De Windows regionale instellingen zijn de standaardinstelling.

TimeSeconds Time Geeft aan of de seconden getoond moeten worden. De Windows regionale instellingen zijn de standaardinstelling.

TimeSeparator Time Stelt het scheidingsteken van de tijdsformattering in. De Windows regionale instellingen zijn de standaardinstelling.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

281

Basisenumeratie In de Engelstalige Axapta-omgeving gebruikt men de termen Base enum. Eigenschap Omschrijving

ChangedBy De gebruiker die het laatst de basisenumeratie heeft gewijzigd.

ChangedDate De datum waarop de basisenumeratie het laatst is gewijzigd.

ChangedTime Het tijdstip waarop de basisenumeratie het laatst is gewijzigd.

ConfigurationKey Gebruikt om een Configuratiesleutel op te geven voor de basisenumeratie.

CreatedBy De gebruiker die de basisenumeratie heeft aangemaakt.

CreatedTime Het tijdstip waarop de basisenumeratie is aangemaakt.

CreationDate De datum waarop de basisenumeratie is aangemaakt.

DisplayLength Stelt het maximaal aantal karakters in dat tegelijk getoond wordt in een formulierelement.

Help Helptekst die getoond wordt in de statusbalk.

ID Het identificatienummer van de basisenumeratie.

Label Het label van de basisenumeratie. Met dit label zal de gebruiker de basisenumeratie identificeren.

LockedBy De gebruiker die momenteel de basisenumeratie vergrendelt.

Name De naam van de basisenumeratie.

Style Bepaalt de grafische voorstelling van de basisenumeratie.

UsedEnumValue Indien gedesactiveerd, zal Axapta de basisenumeratie-elementen nummeren en de eigenschap EnumValue van de basisenumeratie-elementen wordt niet gebruikt.

MORPHX IT Bijlage eigenschappen

282

© 2007 Steen Andreasen

Basisenumeratie-element In de Engelstalige Axapta-omgeving gebruikt men de term base enum entry. Eigenschap Omschrijving

ConfigurationKey Gebruikt om een configuratiesleutel te definiëren voor het Basisenumeratie-element.

EnumValue Dit is de Integer waarde die bewaard wordt in de databank.

Label Naam die getoond wordt aan de gebruiker.

Name AOT naam van het Basisenumeratie-element.

Licentiecodes In de Engelstalige Axapta-omgeving gebruikt men de term Licence codes. Eigenschap Omschrijving

ChangedBy De gebruiker die laatst de licentiecode heeft gewijzigd.

ChangedDate De datum waarop de licentiecode het laatst is gewijzigd.

ChangedTime Het tijdstip waarop de licentiecode het laatst is gewijzigd.

CreatedBy De gebruiker die de licentiecode heeft aangemaakt.

CreatedTime Het tijdstip waarop de licentiecode is aangemaakt.

CreationDate De datum waarop de licentiecode is aangemaakt.

Group Geeft aan van welke licentiecodegroep de licentiecode deel uitmaakt.

ID Het identificatienummer van de licentiecode.

Label Het label van de licentiecode. Dit is het label waarmee de gebruiker de licentiecode identificeert.

LockedBy De gebruiker die momenteel de licentiecode vergrendelt.

Type De meeste licentiecodes zijn booleaanse waardes. Dit wordt enkel gewijzigd indien het aantal licenties geteld wordt zoals gebruikers of COM-gebruikers.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

283

Eigenschap Omschrijving

Configuratiesleutel, beveiligingsleutel In de Engelstalige Axapta-omgeving gebruikt men de termen Configuration key, Security key. Eigenschap Type Omschrijving

ChangedBy Both De gebruiker die de sleutel het laatst heeft gewijzigd.

ChangedDate Both De datum waarop de sleutel het laatst is gewijzigd.

ChangedTime Both Het tijdstip waarop de sleutel het laatst is gewijzigd.

ConfigurationKey Security Key Gebruikt om een configuratiesleutel voor de beveiligingsleutel op te geven.

CreatedBy Both De gebruiker die de sleutel heeft aangemaakt.

CreatedTime Both Het tijdstip waarop de sleutel is aangemaakt.

CreationDate Both De datum waarop de sleutel is aangemaakt.

ID Both Het identificatienummer van de sleutel.

Label Both Het label van de sleutel. Dit is het label waarmee de gebruiker de sleutel kan identificeren.

LicenseCode Configuration Key Licentiecode gebruikt om de configuratiesleutel te activeren.

LockedBy Both De gebruiker die momenteel de sleutel vergrendelt.

Name Both De sleutelnaam.

ParentKey Both Oudersleutel voor deze sleutel; indien de oudersleutel geïnactiveerd is, zal dit deze

MORPHX IT Bijlage eigenschappen

284

© 2007 Steen Andreasen

Eigenschap Type Omschrijving sleutel eveneens beïnvloeden.

12.2 Formuliereigenschappen Formuliergegevensbron In de Engelstalige Axapta-omgeving gebruikt men de term Form data source. Eigenschap Omschrijving

AllowCheck Indien ingesteld, worden configuratiesleutels en beveiligingsleutels gevalideerd bij runtime.

AllowCreate Staat de gegevensbron toe om een nieuw record toe te voegen.

AllowDelete Staat de gegevensbron toe om tabelrecords te verwijderen.

AllowEdit Staat de gegevensbron toe om tabelrecords te editeren.

AutoNotify Zou gedesactiveerd moeten worden indien de formulierquery niet wordt gebruikt. Wordt gebruikt door de fomulierquery, maar heeft blijkbaar geen invloed indien deze eigenschap gedesactiveerd wordt.

AutoQuery Indien gedesactiveerd, heeft de gebruiker niet de mogelijkheid om de querydialoog, filter- en zoekopties te gebruiken voor de gegevensbron.

AutoSearch Geeft aan of de records automatisch moeten worden opgehaald bij het opstarten.

Company Indien opgegeven, worden de records van dit bedrijf opgehaald.

CounterField Gebruikt om een teller te definiëren voor de records toegevoegd met behulp van de gegevensbron. CounterField kan gebruikt worden als records gesorteerd moeten worden in de volgorde dat de gebruikers ze hebben toegevoegd. Een veld van het type real moet aangemaakt worden in de tabel gebruikt door de gegevensbron. Dit veld moet geselecteerd worden voor de eigenschap CounterField. MorphX zal automatisch de tellerwaarde ophogen als een record wordt toegevoegd. Het formulier SalesTable maakt gebruik van de CounterField eigenschap bij het toevoegen van verkoopsorderlijnen.

DelayActive Indien ingesteld, zal het uitvoeren van de code en het ophalen van de gegevens uit de tabel(len) uitgesteld worden als je door een lijst van records rolt. Dit verbetert de performantie.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

285

Eigenschap Omschrijving

Index Index gebruikt voor het sorteren en ophalen van records.

InsertAtEnd Indien ingesteld, worden nieuwe records aan het einde toegevoegd.

InsertIfEmpty Een nieuw record wordt toegevoegd indien de gegevensbron geen enkel record vindt.

JoinSource Verwijst naar de formuliergegevensbron die hiermee gekoppeld is.

LinkType Deze eigenschap wordt gebruikt in combinatie met JoinSource. LinkType definieert de join mode gebruikt bij het koppelen van twee gegevensbronnen.

Name De naam van de formuliergegevensbron.

OnlyFetchActive Dit zorgt ervoor dat de query van de formuliergegevensbron enkel de waardes van de velden ophaalt die in het formulier getoond worden.

StartPosition Geeft aan of de formuliergegevensbron het eerste of het laatste record moet tonen.

Table De tabel gebruikt in de formuliergegevensbron.

Formuliergegevensbronvelden In de Engelstalige Axapta-omgeving gebruikt men de term Form data source fields. Eigenschap Omschrijving

AllowAdd Staat de gebruiker toe om dit veld toe te voegen aan de gebruikersinstellingen.

AllowEdit Laat het editeren van de waarde van het formulierelement toe.

Enabled Geeft aan of het formulierelement geactiveerd moet zijn.

Mandatory Indien ingesteld, moet de gebruiker verplicht een waarde in dit veld invullen.

Skip Geeft aan of het formulierelement overgeslagen moet worden als de tab-toets wordt ingedrukt.

Visible Gebruikt om het formulierelement te verbergen. Indien de volgende formulierelementen automatisch gepositioneerd zijn, worden deze

MORPHX IT Bijlage eigenschappen

286

© 2007 Steen Andreasen

Eigenschap Omschrijving elementen herschikt.

Formulierontwerpgroepelementen In de Engelstalige Axapta-omgeving gebruikt men de term Form design group controls. Deze sectie geeft een overzicht van de eigenschappen van de formulierelementen ButtonGroup, Group, Tab en TabPage. Eigenschap Type Omschrijving

AlignChild All Geeft aan of dit formulierelement op dezelfde manier moet gealigneerd worden als de groep waartoe het behoort.

AlignChildren All Indien ingesteld, zullen de formulierelementen die omvat zijn in dit formulierelement op dezelfde manier gealigneerd worden.

AlignControl All Deze instelling schikt de formulierelementen afhankelijk van de langste label.

AllowEdit All Laat het editeren van de waarde in een formulierelement toe.

AllowUserSetup All Laat gebruikersinstellingen voor dit formulierelement toe.

ArrangeMethod All Stelt de oriëntatie in voor de geschikte formulierelementen.

ArrangeWhen All Geeft aan wanneer de formulierelementen in het ontwerp geschikt moeten worden.

AutoDataGroup Group Indien ingesteld, kan het formulierelement enkel velden omvatten van de groep opgegeven in de eigenschap DataGroup.

AutoDeclaration All Indien ingesteld op Yes, kan er in X++ verwezen worden naar de formulierontwerpknoop met behulp van de sectienaam.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

287

Eigenschap Type Omschrijving

BackgroundColor All RGB waarde of naam van het Windows kleurenschema.

BackStyle All Stelt de achtergrond voor het formulierelement in op doorzichtig. Wordt gebruikt indien de achtergrondkleur of bitmaps niet getoond moeten worden, of om de kleur van de achtergrond overeen te laten stemmen met de kleur ingesteld met de BackGroundColor.

Bold ButtonGroup Group

Zorgt ervoor dat de gegevens getoond in het formulierelement vet worden afgedrukt.

BottomMargin All Stelt de marge in onder het formulierelement.

Caption ButtonGroup Group TabPage

Titel van het formulierelement.

ColorScheme All Geeft aan of RGB kleuren gebruikt worden dan wel het Windows kleurenschema.

Columns All Aantal kolommen in het formulierelement. De ingebedde formulierelementen zullen geschikt worden afhankelijk van het aantal kolommen.

Columnspace All Stelt de spatie in tussen de kolommen.

ConfigurationKey All Gebruikt om een configuratiesleutel te specifiëren voor het formulierelement.

DataGroup Group Veldgroepnaam.

DataSource All Gegevensbron die gebruikt wordt in het formulierelement. Gegevens worden opgehaald met behulp van deze gegevensbron.

MORPHX IT Bijlage eigenschappen

288

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

DragDrop All Staat het gebruik toe van verslepen en plaatsen in het formulierelement.

Enabled All Geeft aan of het formulierelement geactiveerd moet zijn.

Font ButtonGroup Group

Het lettertype te gebruiken voor het ontwerp. Indien niet opgegeven, wordt het standaard lettertype gebruikt.

FontSize ButtonGroup Group

De lettergrootte te gebruiken voor het ontwerp. Indien niet opgegeven, wordt de standaard lettergrootte gebruikt.

FrameOptionButton Group Geeft aan of de kader een knop moet bevatten.

FramePosition ButtonGroup Group

Specifieert de plaatsing van de kader.

FrameType ButtonGroup Group

Bepaalt welk type kader rond het formulierelement geplaatst wordt.

Height All Stelt een vaste hoogte in voor het formulierelement.

HelpText All Helptekst die getoond wordt in de statusbalk. Deze tekst overschrijft de helptekst opgegeven voor het veld of een uitgebreid datatype.

HideIfEmpty All Verbergt het formulierelement als het leeg is.

Italic ButtonGroup Group

Gebruikt een cursief lettertype.

LabelBold Group Toont het label in vet lettertype.

LabelFont Group Stelt het lettertype van het label in. Indien niet opgegeven, wordt het standaard lettertype gebruikt.

LabelFontSize Group Stelt de lettergrootte van het label in. Indien niet opgegeven, wordt het

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

289

Eigenschap Type Omschrijving standaardlettertype gebruikt.

LabelItalic Group Toont het label in cursief lettertype.

LabelUnderline Group Onderlijnt de labeltekst. De eigenschap LabelLineBelow wordt gebruikt om een lijn te trekken over de volle breedte van het label.

Left All Plaatst het formulierelement op een vaste positie berekend vanaf links. Als formulierelementen horizontaal gealigneerd zijn en één formulierelement is op een vaste positie gezet, dan moeten alle formulierelementen op een vaste positie worden gezet.

LeftMargin All Stelt een linkermarge in voor het formulierelement.

Name All Naam van het formulierelement.

NeededAccessLevel All Vereiste toegangsniveau om dit formulierelement te activeren.

OptionValue Group Waarde te gebruiken met de eigenschap FrameOptionButton. Gebruikt om de standaard waarde in te stellen als FrameOptionButton ingesteld is op Check of Radio.

RightMargin All Stelt een rechtermarge in voor het formulierelement.

SecurityKey All Gebruikt om een beveiligingsleutel in te stellen voor het formulierelement.

SelectControl Tab Geeft aan of het eerste formulierelement geactiveerd moet worden als de gebruiker van tabblad verandert.

ShowTabs Tab Indien gedesactiveerd, worden de tabbladen verborgen.

SizeHeight ButtonGroup Geeft aan of alle knoppen in een knopgroep dezelfde hoogte moeten

MORPHX IT Bijlage eigenschappen

290

© 2007 Steen Andreasen

Eigenschap Type Omschrijving hebben.

SizeWidth ButtonGroup Geeft aan of alle knoppen in een knopgroep dezelfde breedte moeten hebben.

Skip All Geeft aan of het formulierelement overgeslagen moet worden als de gebruiker de tabtoets indrukt.

Tab Tab Actief tabblad bij het openen van het formulier.

TabAppearance Tab TabPage

Bepaalt hoe de tabbladen worden getoond.

TabAutoChange Tab TabPage

Werkt niet. Zou ervoor moeten zorgen dat je de tabtoets kan gebruiken om naar het volgende tabblad te gaan.

TabLayout Tab Geeft aan hoe de tabbladen geschikt worden. De optie Tunnel kan niet gebruikt worden voor formulieren omdat dit behoort tot de kenmerken van het webraamwerk.

TabPlacement Tab Bepaalt waar de tabbladen worden geplaatst.

Top All Stelt een vaste positie in voor het formulierelement berekend vanaf de top van het vorige formulierelement.

TopMargin All Stelt de marge in boven het formulierelement.

Underline ButtonGroup Group

Zorgt ervoor dat het label wordt onderlijnd.

VerticalSpacing All Spatie boven en onder het formulierelement.

Visible All Gebruikt om het formulierelement te verbergen. Als de volgende formulierelementen geautopositioneerd worden, worden

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

291

Eigenschap Type Omschrijving deze elementen herschikt.

Width All Stelt een vaste breedte in voor het formulierelement. Indien ingesteld op standaard, bepalen de uitgebreide datatypes de breedte.

MORPHX IT Bijlage eigenschappen

292

© 2007 Steen Andreasen

Formulierontwerp In de Engelstalige Axapta-omgeving gebruikt men de term Form design. Eigenschap Omschrijving

AlignChild Geeft aan of dit formulierelement de schikking van de groep moet volgen waartoe het behoort.

AlignChildren Indien ingesteld, zullen de formulierelementen die behoren tot dit element, geschikt worden afhankelijk van elkaars positie.

AllowDocking Geeft aan of het venster gedokt kan worden.

AllowUserSetup Laat gebruikersinstellingen toe voor dit formulierelement.

AlwaysOnTop Geeft aan of het formulier altijd in het voorste scherm getoond moet worden.

ArrangeMethod Stelt de oriëntatie in voor de schikking van de formulierelementen.

ArrangeWhen Geeft aan wanneer de schikking van de formulierelementen in het ontwerp moeten gebeuren.

BackgroundColor RGB waarde of naam van het Windows kleurenschema.

BottomMargin Stelt de marge in beneden het formulierelement.

Caption Titel van het formulier. De titel verschijnt in de titelbalk van het formulier.

ColorScheme Geeft aan of de RGB kleuren of het Windows kleurenschema gebruikt moet worden.

Columns Aantal kolommen in het formulierelement. De formulierelementen die in dit element zitten, worden dan geschikt volgens dit aantal kolommen.

Columnspace Stelt de spatie in tussen de kolommen.

DataSource Gegevensbron gebruikt voor het formulierelement. Gegevens worden opgehaald via deze gegevensbron.

Font Het lettertype gebruikt voor het ontwerp. Indien niet gespecifieerd, wordt het standaard lettertype gebruikt.

Frame Deze eigenschap bepaalt het uiterlijk van de kader rond het formulier.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

293

Eigenschap Omschrijving

Height Stelt een vaste hoogte in voor het formulierontwerp.

HideIfEmpty Verbergt het ontwerp en toont enkel de titelbalk, indien het formulierelement leeg is.

HideToolbar Verbergt de nutsbalk die de knoppen bevat voor het navigeren en het bekijken van de documenten van het record.

Imagemode Bepaalt hoe de achtergrondafbeelding getoond moet worden. Zie verder bij de eigenschap ImageName.

ImageName Naam van de afbeelding te gebruiken als achtergrond in het formulier.

ImageResource Naam van de hulpbron te gebruiken als achtergrond in het formulier.

LabelFont Stelt het lettertype in van het label van de formulierelementen. Indien niet opgegeven, wordt het standaard lettertype gebruikt.

Left Stelt een vaste horizontale positie in voor het formulier.

LeftMargin Stelt de linkermarge in voor het ontwerp.

Mode Heeft blijkbaar geen effect. Deze eigenschap werd gebruikt in de voorganger van Axapta voor het instellen van schrijfrechten op formulieren.

NeededAccessLevel Heeft blijkbaar geen effect. NeededAccessLevel wordt normaal gedefinieerd met behulp van menuopties, zoals de helptekst van deze eigenschappen vermeldt.

RightMargin Stelt de rechtermarge in voor het ontwerp.

SaveSize Bewaart de grootte van het formulierontwerp en gebruikt die de volgende keer dat het formulier getoond wordt.

SetCompany Geeft aan of je het bedrijf wil instellen als het formulier wordt geactiveerd.

TitleDatasource Indien ingesteld, worden de titelvelden van de gegevensbron getoond in de titel van het formulier.

Top Stelt een vaste verticale positie in voor het formulier.

TopMargin Stelt de bovenste marge van het ontwerp in.

MORPHX IT Bijlage eigenschappen

294

© 2007 Steen Andreasen

Eigenschap Omschrijving

Visible Gebruikt om het volledige ontwerp te verbergen.

Width Stelt een vaste breedte in voor het formulier.

WindowResize Schakelt de mogelijkheid tot herschalen van het venster in of uit.

WindowType Gebruikt om een formulier te wijzigen in een opspringformulier. Een opspring- of popup formulier kan niet herschaald worden.

Type formulierelementen In de Engelstalige Axapta-omgeving gebruikt men de termen Type controls. Deze sectie geeft een overzicht van formulierelementen van het basistype en andere elementen voor het weergeven van data zoals een raster en knoppen. Eigenschap Type Omschrijving

ActiveBackColor Grid RGB waarde of naam van het Windows kleurenschemaelement.

ActiveForeColor Grid RGB waarde of naam van het Windows kleurenschemaelement.

AlignChild Design

Geeft aan of dit formulierelement de schikking van de groep moet volgen waartoe het behoort.

AlignChildren Design Indien ingesteld, zullen de formulierelementen die behoren tot dit element, geschikt worden afhankelijk van elkaars positie.

AlignControl All Deze instelling schikt de formulierelementen afhankelijk van de lengte van het label.

Alignment DateEdit IntEdit RealEdit StaticText StringEdit TimeEdit

Schikt de gegevens van het formulierelement. Kan gebruikt worden om de elementgegevens links te aligneren, als de formulierelementen vertikaal worden gepositioneerd.

AllowEdit All Staat toe dat de waarde van het

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

295

Eigenschap Type Omschrijving formulierelement wordt geëditeerd.

AllowNegative IntEdit RealEdit

De eigenschap wordt gebruikt om te voorkomen dat negatieve waardes door de gebruiker worden ingetikt.

AnimateFile Animate De naam van het .avi bestand dat afgespeeld moet worden in het formulierelement.

AppendNew ComboBox Indien ingesteld op Yes, kan de gebruiker handmatig nieuwe elementen toevoegen.

ArrayIndex ComboBox DateEdit IntEdit Listbox RadioButton RealEdit StringEdit TimeEdit

Indien het betreffende veld, formulier- of rapportelement een array is, kan er opgegeven worden dat één enkel element van de array gebruikt mag worden.

AutoArrange ListView Geeft aan of pictogrammen automatisch geschikt moeten worden.

AutoDataGroup Grid Indien ingesteld, kan het formulierelement enkel velden bevatten van de groep die is opgegeven in de eigenschap DataGroup.

AutoDeclaration All Indien ingesteld op Yes, kan X++ verwijzen naar de eigenschappen van het formulierelement via de naam van dit formulierelement.

AutoInsSeparator RealEdit Heeft blijkbaar geen effect. Zou het automatisch toevoegen van decimalen moeten afblokken.

AutoPlay Animate Start automatisch het afspelen van het videobestand.

BackgroundColor Button CheckBox ComboBox CommandButton DateEdit

RGB waarde of naam van het Windows kleurenschemaelement.

MORPHX IT Bijlage eigenschappen

296

© 2007 Steen Andreasen

Eigenschap Type Omschrijving Grid IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit Table TimeEdit Tree Window

BackStyle Button CheckBox ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit TimeEdit Tree Window

Stelt de achtergrond van het formulier-element in op doorzichtig. Gebruikt indien de achtergrondkleur of de bitmaps niet getoond mogen worden, of om de achtergrondkleur van de elementgegevens in te stellen met de kleur vermeld in de eigenschap BackGroundColor.

Bold Button ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText

Gebruikt om de gegevens van het formulierelement vet af te beelden.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

297

Eigenschap Type Omschrijving StringEdit TimeEdit Tree

Border Animate Button ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RealEdit StringEdit TimeEdit Tree

Typografie van het kader dat bij het formulierelement behoort.

BottomMargin Grid MenuButton RadioButton Table

Stelt de marge in onder de formulier-elementgegevens.

ButtonDisplay Button CommandButton MenuButton MenuItemButton

Bepaalt of een tekst, een afbeelding of beide afgebeeld moeten worden en tevens de plaats van tekst of afbeelding.

CanScroll ListView Tree

Laat het rollen door een venster toe.

Caption ActiveX HTML RadioButton

Titel van het formulierelement.

Center Animate Indien ingesteld op Yes, wordt de videoclip gecentreerd in het formulierelement.

ChangeCase StringEdit Gebruikt om de gegevens in het formulierelement in kleine letters of hoofdletters te tonen.

CheckBox ListView Indien ingesteld, wordt een vinkvakje

MORPHX IT Bijlage eigenschappen

298

© 2007 Steen Andreasen

Eigenschap Type Omschrijving Tree getoond voor iedere rij van de

formulierelementgegevens.

ClassName ActiveX HTML

De naam of GUID van de klasse of het object dat gebruikt moet worden.

ColorScheme Button CheckBox ComboBox CommandButton DateEdit Grid IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit Table TimeEdit Tree Window

Geeft aan of er gebruik wordt gemaakt van RGB kleuren of van het Windows kleurenschema.

Column Table Stelt de actieve kolom in.

ColumnHeader ListView Bepaalt of een hoofding wordt getoond voor de kolommen.

ColumnHeaderButton ListView Indien ingesteld, werken de kolomhoofdingen als een knop. Enkel als Viewtype is ingesteld op Report.

ColumnImages ListView Indien ingesteld, kan ieder object in een kolom een afbeelding bevatten.

Columns RadioButton Table

Aantal kolommen in het formulier-element. Bij het schikken van de ingebedde formulierelementen wordt er rekening gehouden met het aantal kolommen.

ComboType ComboBox Het type combobox.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

299

Eigenschap Type Omschrijving

Command CommandButton Uit te voeren commando als je op de knop klikt.

ConfigurationKey All Gebruikt om een configuratiesleutel te specifiëren voor het formulierelement.

Custom ActiveX Html

Deze eigenschap moet gebruikt worden als een custom ActiveX property editor gebruikt moet worden.

DataField CheckBox ComboBox DateEdit IntEdit Listbox RadioButton RealEdit StaticText StringEdit TimeEdit Window

Selecteer een veld van de geselecteerde gegevensbron. In plaats van een veld te selecteren, kan een displaymethode opgegeven worden in de eigenschap DataMethod.

DataGroup Grid Veldgroepnaam.

DataMethod CheckBox ComboBox DateEdit IntEdit Listbox RadioButton RealEdit StaticText StringEdit TimeEdit Window

Selecteer een display- of editeermethode die gebruikt moet worden. Als de methode bij een tabel hoort, moet de gegevensbron opgegeven worden in de eigenschap Table en de methode moet bestaan hetzij in de gegevensbron die gekoppeld is aan de tabel hetzij in de tabel zelf.

DataSource CheckBox ComboBox DateEdit Grid IntEdit Listbox MenuItemButton RadioButton

Gegevensbron die gebruikt wordt in het formulierelement. Gegevens worden opgehaald via deze gegevensbron.

MORPHX IT Bijlage eigenschappen

300

© 2007 Steen Andreasen

Eigenschap Type Omschrijving RealEdit StaticText StringEdit TimeEdit Window

DateDay DateEdit Bepaalt hoe de dag wordt getoond. Windows regionale instellingen worden gebruikt als standaard waarde.

DateFormat DateEdit Bepaalt het datumformaat. Windows regionale instellingen worden gebruikt als standaard waarde.

DateMonth DateEdit Bepaalt hoe de maand wordt getoond. Windows regionale instellingen worden gebruikt als standaard waarde.

DateSeparator DateEdit Bepaalt het scheidingsteken gebruikt in de datum. Windows regionale instellingen worden gebruikt als standaard waarde.

DateValue DateEdit Indien opgegeven, wordt deze datum gebruikt als standaard waarde in het formulierelement.

DateYear DateEdit Bepaalt hoe het jaar wordt getoond. Windows regionale instellingen worden gebruikt als standaard waarde.

DecimalSeparator ReaLEdit Bepaalt het decimale scheidingsteken. Windows regionale instellingen worden gebruikt als standaard waarde.

DefaultButton Button CommandButton MenuButton MenuItemButton

Indien ingesteld, is dit de standaardknop.

Direction Progress Stelt horizontale of verticale richting in.

DisabledImage Button CommandButton MenuButton MenuItemButton

Toont de bitmap op het opgegeven pad als de knop gedesactiveerd is. De eigenschap ButtonText moet ingesteld zijn om afbeeldingen te tonen.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

301

Eigenschap Type Omschrijving

DisabledResource Button CommandButton MenuButton MenuItemButton

Toont de opgegeven resource bitmap als de knop gedesactiveerd is. De eigenschap ButtonText moet ingesteld zijn om afbeeldingen te tonen.

DisplaceNegative IntEdit RealEdit

Schikt de positie van negatieve waardes bij het afdrukken.

DisplayHeight DateEdit IntEdit RealEdit StaticText StringEdit TimeEdit

Bepaalt het maximale aantal lijnen dat tegelijk zichtbaar is in het formulierelement.

DisplayLength ComboBox DateEdit IntEdit Listbox RadioButton RealEdit StaticText StringEdit TimeEdit

Bepaalt het maximale aantal karakters dat tegelijk zichtbaar is in het formulierelement.

DragDrop All Laat verslepen en plaatsen toe in het formulierelement.

EditLabels ListView Tree

Laat de gebruiker toe om de labels in het formulierelement te editeren.

Enabled All Bepaalt of het formulierelement geactiveerd is.

EnumType ComboBox Listbox RadioButton

Bepaalt welke enumeratie gebruikt moet worden in het formulierelement als een veld of methode niet is opgegeven.

ExtendedDataType ComboBox DateEdit IntEdit Listbox RadioButton RealEdit StringEdit

Bepaalt welke uitgebreid datatype gebruikt moet worden in het formulierelement als een veld of methode niet is opgegeven.

MORPHX IT Bijlage eigenschappen

302

© 2007 Steen Andreasen

Eigenschap Type Omschrijving TimeEdit

Font Button ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit TimeEdit Tree

Het lettertype dat gebruikt moet worden voor de gegevens in het formulierelement. Indien niet gespecifieerd, wordt het standaard lettertype gebruikt.

FontSize Button ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit TimeEdit Tree

De lettergrootte die gebruikt moet worden voor de gegevens in het formulierelement. Indien niet gespecifieerd, wordt de standaard lettergrootte gebruikt.

ForegroundColor Button CheckBox ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton

RGB waarde of naam van het Windows kleurenschema.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

303

Eigenschap Type Omschrijving RealEdit StaticText StringEdit TimeEdit Tree Window

FormatMST RealEdit Formatteert de waarde met behulp van de instellingen voor de standaard bedrijfsmunteenheid.

FramePosition RadioButton Bepaalt de positie van de kader.

FrameType RadioButton Bepaalt het kadertype dat rond het formulierelement getoond wordt.

GridLines Grid ListView Table

Toont lijnen in het raster. Enkel geldig bij het formulierelementtype ListView als de eigenschap ViewType ingesteld is op Report.

HasButtons Tree Indien ingesteld, wordt een + of – getoond indien de formulierelementknoop verder opengeklikt kan worden.

HasLines Tree Dit tekent lijnen voor iedere rij in de gegevens van het formulierelement.

Headerdragdrop ListView Laat verslepen en plaatsen toe in de hoofding van het formulierelement.

Height All Stelt een vaste hoogte in voor het formulierelement.

HelpText All Helptekst die getoond wordt in de statusbalk. Deze waarde overschrijft het label opgegeven bij het tabelveld of het uitgebreide datatype.

HideFirstEntry ComboBox Listbox RadioButton

Verbergt het eerste element. Stel dat je een enumeratie hebt met de waardes “blanco”, “mannelijk”, “vrouwelijk”, dan kan je in de radiobutton het eerste element “blanco” verbergen, en de gebruiker enkel de keuze laten tussen “mannelijk” en “vrouwelijk”.

MORPHX IT Bijlage eigenschappen

304

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

HighlightActive Grid Indien ingesteld, wordt de geselecteerde lijn gemarkeerd met een kleur.

Imagemode Window Bepaalt hoe de achtergrondafbeelding getoond wordt. Zie verder bij de eigenschap ImageName.

ImageName Window Naam van de afbeelding te gebruiken als achtergrond van het formulierelement.

ImageResource Window Bepaalt de afbeeldinghulpbron die getoond moet worden.

Italic ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit TimeEdit Tree

Stelt het lettertype op cursief in voor de formulierelementgegevens.

Item ComboBox Listbox RadioButton

Heeft blijkbaar geen effect. De eigenschap Selection wordt gebruikt om de standaard ingave te bepalen. Is gedesactiveerd indien een enumeratie is opgegeven voor het formulierelement.

ItemAlign ListView Heeft blijkbaar geen effect. Zou de elementen in een lijst moeten aligneren naar de bovenkant of de linkerkant.

Items ComboBox Listbox RadioButton

Is gedesactiveerd indien een enumeratie is opgegeven voor het formulierelement. Kan gebruikt worden om het aantal opties bij een kiesknop of radio button in te stellen.

Label CheckBox ComboBox

Gebruikt om het standaard label van het veld of het uitgebreide datatype te overschrijven. Het label wordt niet

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

305

Eigenschap Type Omschrijving DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

getoond als de eigenschap ShowLabel op false is ingesteld.

LabelAlignment CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Aligneert het label van het formulierelement.

LabelBold CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Stelt het label in op vet afdrukken.

LabelFont CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Stelt het lettertype van het label in. Indien niet gespecifieerd, wordt het standaard lettertype gebruikt.

LabelFontSize CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit

Stelt de lettergrootte van het label in. Indien niet gespecifieerd, wordt de standaard lettergrootte gebruikt.

MORPHX IT Bijlage eigenschappen

306

© 2007 Steen Andreasen

Eigenschap Type Omschrijving TimeEdit Window

LabelForegroundColor CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

RGB waarde of naam van het Windows kleurenschema te gebruiken voor het label van het formulierelement.

LabelHeight CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Deze eigenschap functioneert niet. Zou gebruikt moeten worden om de hoogte van een label in te stellen.

LabelItalic CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Stelt het label in op cursief.

LabelPosition CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Positioneert het label naar boven of links toe.

LabelUnderline CheckBox Onderlijnt de labeltekst.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

307

Eigenschap Type Omschrijving ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

LabelWidth CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Gebruikt als de eigenschap LabelPosition ingesteld is op Left. Stelt een vaste breedte in voor het label.

Left All Zet het formulierelement op een vaste positie berekend vanaf links.

LeftMargin Grid MenuButton RadioButton Table

Stelt een linkermarge in voor het formulierelement.

LimitText DateEdit IntEdit RealEdit StringEdit TimeEdit

Het maximaal aantal karakters dat de gebruiker kan intikken in het formulierelement.

LinesAtRoot Tree Dit trekt lijnen voor de stamrij in de gegevens van het formulierelement.

LookupButton DateEdit IntEdit RealEdit StringEdit TimeEdit

Geeft aan wanneer het formulierelement een opzoekknop zou moeten hebben.

Loops Animate Het aantal keren dat een filmfragment moet afgespeeld worden. Indien ingesteld op 0, heeft dit tot gevolg dat het

MORPHX IT Bijlage eigenschappen

308

© 2007 Steen Andreasen

Eigenschap Type Omschrijving filmfragment herhaald wordt.

Mandatory DateEdit IntEdit RealEdit StringEdit TimeEdit

Indien ingesteld, moet het veld verplicht ingevuld worden.

MenuItemName MenuItemButton Naam van de menuoptie. Enkel menuopties van het type opgegeven in de eigenschap MenuItemType kunnen geselecteerd worden.

MenuItemType MenuItemButton Type van de menuoptie.

MultiLine StringEdit Geeft aan of de gegevens in het formulierelement over verschillende lijnen moeten beschikken.

MultiSelect Button CommandButton Grid MenuButton MenuItemButton

In een raster betekent dit dat meerdere rijen tegelijk geselecteerd kunnen worden. In andere types van formulierelementen, wordt het formulierelement gedesactiveerd indien verschillende rijen zijn geselecteerd en indien deze eigenschap is gedesactiveerd.

Name All De naam van de sectie. Dit is de naam gebruikt door X++ om te verwijzen naar de sectie indien de eigenschap AutoDeclaration is ingesteld.

NeededAccessLevel Button CommandButton MenuButton MenuItemButton

Vereiste toegangsniveau om dit formulierelement te activeren.

NoOfDecimals RealEdit Stelt het aantal decimale cijfers in dat getoond moet worden.

NormalImage Button CommandButton MenuButton MenuItemButton

Toont de opgegeven resource bitmap indien de knop is geactiveerd. De eigenschap ButtonText moet ingesteld zijn zodat afbeeldingen getoond kunnen worden.

NormalResource Button Toont de opgegeven afbeelding indien de

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

309

Eigenschap Type Omschrijving CommandButton MenuButton MenuItemButton

knop is geactiveerd. De eigenschap ButtonText moet ingesteld zijn zodat afbeeldingen getoond kunnen worden.

OneClickActivate ListView Activeer met één enkele klik.

PasswordStyle StringEdit Indien ingesteld wordt de waarde in het formulierelement getoond met asterisken (*).

Pos Progress Startpositie voor de voortgangsbalk.

ProgressType Progress Heeft blijkbaar geen effect.

RangeHi Progress Maximumwaarde voor de voortgangsbalk.

RangeLo Progress Minimumwaarde voor de voortgangsbalk.

RealValue RealEdit Stelt de standaard waarde in voor het formulierelement.

ReplaceOnLookup DateEdit IntEdit RealEdit StringEdit TimeEdit

Geeft aan of de waarde in het formulierelement vervangen moet worden indien een nieuwe waarde is geselecteerd na een opzoeking.

RightMargin Grid MenuButton RadioButton Table

Stelt een rechtermarge in voor het formulierelement.

RotateSign IntEdit RealEdit

Gebruikt om negatieve waardes te inverteren.

Row Table Actieve rij.

Rows Table Aantal rijen in het formulierelement.

RowSelect ListView Tree

Geeft aan of rijen geselecteerd kunnen worden.

SaveRecord Button Geeft aan of het record bewaard moet

MORPHX IT Bijlage eigenschappen

310

© 2007 Steen Andreasen

Eigenschap Type Omschrijving CommandButton MenuButton MenuItemButton

worden door Axapta indien de gebruiker dit formulierelement activeert.

SearchMode DateEdit IntEdit RealEdit StringEdit TimeEdit

Bepaalt de manier waarop records gezocht worden bij het intikken van gegevens.

SecurityKey All Gebruikt om een beveiligingsleutel in te stellen voor het formulierelement.

Selection ComboBox Listbox RadioButton

Stelt het initiële geselecteerde element in.

ShowColLabels Grid Table

Geeft aan of kolomlabels getoond moeten worden.

ShowLabel CheckBox ComboBox DateEdit IntEdit Listbox RealEdit StringEdit TimeEdit Window

Indien ingesteld op No, wordt het label niet getoond.

ShowRowLabels Grid Table

Geeft aan of rijlabels getoond moeten worden.

ShowSelAlways ListView Tree

Geeft aan of de selectie van een object behouden moet blijven indien de focus wordt gewijzigd.

ShowShortCut Button CommandButton MenuButton MenuItemButton

Indien ingesteld, wordt een snelkoppeling getoond in het label van het formulierelement.

ShowZero IntEdit RealEdit

Bepaalt of nulwaardes getoond moeten worden.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

311

Eigenschap Type Omschrijving

SignDisplay IntEdit RealEdit

Bepaalt hoe negatieve waardes moeten getoond worden.

SingleSelection ListView Tree

Staat toe dat meer dan één object tegelijk wordt geselecteerd.

Skip All Geeft aan of het formulierelement overgeslagen moet worden als de tabtoets wordt ingedrukt.

Sort ListView Bepaalt hoe het sorteren van de elementen wordt uitgevoerd.

Step Progress Aantal stappen in iedere iteratie.

Text Button ComboBox CommandButton Listbox MenuButton MenuItemButton RadioButton StaticText StringEdit

Tik hier de tekst in die getoond moet worden.

ThousandSeparator RealEdit Bepaalt het scheidingsteken voor duizendtallen. De Windows regionale instellingen worden gebruikt als standaard.

TimeFormat TimeEdit Bepaalt de tijdsformattering. De Windows regionale instellingen worden gebruikt als standaard.

TimeHours TimeEdit Geeft aan of de uren getoond moeten worden. De Windows regionale instellingen worden gebruikt als standaard.

TimeMinute TimeEdit Geeft aan of minuten getoond moeten worden. De Windows regionale instellingen worden gebruikt als standaard.

TimeSeconds TimeEdit Geeft aan of seconden getoond moeten worden. De Windows regionale instellingen worden gebruikt als

MORPHX IT Bijlage eigenschappen

312

© 2007 Steen Andreasen

Eigenschap Type Omschrijving standaard.

TimeSeparator TimeEdit Stelt het scheidingsteken voor tijd in. De Windows regionale instellingen worden gebruikt als standaard.

Top All Stelt een vaste positie in voor het formulierelement, berekend vanaf de bovenkant van het voorgaande formulierelement.

TopMargin Grid MenuButton RadioButton Table

Stelt de marge in boven het formulierelement.

TrackSelect ListView Tree

Geeft aan of het object geselecteerd moet worden als de cursor over het formulierelement gaat.

Transparent Animate Bepaalt of een doorzichtige achtergrond gebruikt moet worden.

TwoClickActivate ListView Vereist activering met een dubbele klik.

Underline Button ComboBox CommandButton DateEdit IntEdit Listbox ListView MenuButton MenuItemButton RadioButton RealEdit StaticText StringEdit TimeEdit Tree

Onderlijnt de gegevens in het formulierelement.

Value CheckBox IntEdit MenuItemButton TimeEdit

Bepaalt de initiële waarde.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

313

Eigenschap Type Omschrijving

VerticalSpacing All Voorziet ruimte boven en onder het formulierelement.

ViewType ListView Bepaalt hoe de objecten visueel worden voorgesteld.

Visible All Gebruikt om het formulierelement te verbergen. Indien de daaropvolgende formulierelementen via autopositionering worden geplaatst, zullen deze formulierelementen worden herschikt.

VisibleCols Grid Bepaalt het aantal kolommen dat zichtbaar is.

VisibleRows Grid Bepaalt het aantal rijen dat zichtbaar is.

Width All Stelt een vaste breedte in voor het formulierelement. Indien ingesteld op de standaard waarde, zal het uitgebreide datatype de breedte instellen.

MORPHX IT Bijlage eigenschappen

314

© 2007 Steen Andreasen

12.3 Rapporteigenschappen De eigenschappen van de rapportquery kan je vinden in de sectie Queryeigenschappen. Rapport In de Engelstalige Axapta-omgeving gebruikt men de term Report. Eigenschap Omschrijving

Name AOTnaam van het rapport.

AllowCheck Indien ingesteld, worden de configuratiesleutels en beveiligingsleutels gevalideerd bij uitvoering.

Autojoin Koppelt de rapportquery met de oproeper. Als het rapport wordt opgeroepen door een formulier, wordt het rapport gekoppeld met het actieve record, met andere woorden het record waar de cursor opstaat.

Interactive Bepaalt of een dialoogvenster getoond moet worden aan de gebruiker bij uitvoering.

Rapportontwerp In de Engelstalige Axapta-omgeving gebruikt men de term Report design. Eigenschap Omschrijving

Name

Naam van het ontwerp. Gebruikt om het ontwerp te identificeren, als het rapport over meer dan één ontwerp beschikt.

AutoDeclaration

Indien ingesteld op , kan X++ verwijzen naar de rapportontwerpknoop met behulp van de sectienaam.

Caption

Titel van het rapport. Als de standaard rapportsjabloon InternalList wordt gebruikt, wordt caption gebruikt om de naam van het rapport in de hoofding af te drukken.

Description

Wordt afgedrukt in de titelbalk van het “afdruk naar scherm” venster. Indien niet opgegeven, wordt de Caption text afgebeeld.

JobType

Kan gebruikt worden om het ontwerp te identificeren. JobType wordt niet afgedrukt.

EmptyReportPrompt

Bevat een tekst die getoond wordt in de Infolog, als het rapport leeg is. Indien niet ingevuld, wordt de standaard tekst getoond.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

315

Eigenschap Omschrijving

ArrangeWhen

Bepaalt wanneer de formulierelementen in het ontwerp geschikt moeten worden.

ColorScheme

Bepaalt of de RGB kleuren of het Windows kleurenschema gebruikt wordt.

ForegroundColor

RGB waarde of naam van het Windows kleurenschema.

ResolutionX

Heeft blijkbaar geen effect.

ResolutionY

Heeft blijkbaar geen effect.

Ruler

Gebruikt om de meeteenheden voor de visuele ontwerper in te stellen.

ReportTemplate

Kies een rapportsjabloon dat je gebruikt voor het ontwerp.

TopMargin

Stelt de bovenste marge in voor het ontwerp.

BottomMargin

Stelt de onderste marge in voor het ontwerp.

LeftMargin

Stelt de linkermarge in voor het ontwerp.

RightMargin

Stelt de rechtermarge in voor het ontwerp.

Language

Stelt een vaste taal in voor het ontwerp. Indien niet ingesteld, wordt de standaardtaal van de gebruiker gebruikt.

Font

Het lettertype te gebruiken voor het ontwerp. Indien niet ingesteld wordt het standaard lettertype gebruikt.

FontSize

De lettergrootte te gebruiken voor het ontwerp. Indien niet ingesteld wordt de standaard lettergrootte gebruikt.

Italic Stelt het ontwerp in op cursief.

Underline Onderlijnt tekst in het ontwerp.

Bold Stelt het ontwerp in op vet.

PrintFormName

Het applicatieformulier gebruikt voor de printerdialoog. Als het rapport wordt opgeroepen via het report runbase raamwerk, wordt dit formulier niet gebruikt.

MORPHX IT Bijlage eigenschappen

316

© 2007 Steen Andreasen

Eigenschap Omschrijving

HideBorder

Slaat het afdrukken van labels en lijnen rond de formulierelementen over.

Orientation

Stelt de afdrukoriëntatie in op portrait of landscape (rechtop of dwars). Een ontwerp zal standaard dwars worden afgedrukt, als er teveel formulierelementen zijn om rechtop af te drukken zonder te herschalen.

FitToPage

Bepaalt wanneer het rapport moet herschaald worden, indien niet alle formulierelementen passen op de breedte van het rapport.

RemoveRepeatedHeaders

Sla hoofdingen over als er geen records beschikbaar zijn voor de hoofding.

RemoveRepeatedFooters

Sla voetregels over als er geen records beschikbaar zijn voor de voetregel.

RemoveRedundantFooters

Sla het afdrukken van sommatievoetregels over als er maar één record is dat gesommeerd kan worden.

Auto-ontwerp In de Engelstalige Axapta-omgeving gebruikt men de term Auto design. Eigenschap Omschrijving

GrandHeader Bepaalt of een hoofdingtekst afgedrukt moet worden bij sorteerbreekpunten. Deze hoofdingtekst wordt beschouwd als de algemene grote hoofding (super grand header) en wordt afgedrukt voor de grote hoofding (grand header) die ingesteld kan worden op de middensectie.

GrandTotal Drukt een algemeen totaal af voor formulierelementen waarvan de eigenschap SumAll, SumPos of SumNeg is ingesteld.

HeaderText Deze tekst wordt afgedrukt als hoofdingtekst bij sorteerbreekpunten indien GrandHeader is ingesteld op Yes. Als hier geen tekst is opgegeven, wordt de standaard tekst afgedrukt.

TotalText Als GrandTotal is ingesteld op Yes, wordt deze tekst afgedrukt als tekst bij het algemeen totaal, in plaats van de standaard tekst.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

317

Sectierapportelementen In de Engelstalige Axapta-omgeving gebruikt men de termen Section controls. Deze sectie is een overzicht van de diverse types rapportelementen van de rapportsecties. Eigenschap Secties Omschrijving

ArrangeMethod All Stelt de oriëntatie in voor het schikken van de rapportelementen.

ArrangeWhen All Bepaalt wanneer de rapportelementen in de sectie geschikt moeten worden.

AutoDeclaration ProgrammableSection Body PageFooter

Indien ingesteld op Yes, kan X++ verwijzen naar deze sectie met behulp van de sectienaam.

Bold All Stelt het vetniveau in voor de hoofdingen en de gegevens in deze sectie.

Bottom ProgrammableSection Prolog Body Epilog

Stelt een vaste positie in voor de sectie berekend vanaf de onderkant van de bladzijde.

BottomMargin All Stelt de marge in onder de sectiegegevens.

ColorScheme All Bepaalt of RGB kleuren of het Windows kleurenschema gebruikt wordt.

ColumnHeadingsStrategy ProgrammableSection Body PageFooter

Bepaalt het afdrukken van labels en of regelafbreking hiervoor gebruikt mag worden. Indien ingesteld op DisplacedLines, zullen de hoofdinglabels afgedrukt worden, verspreid over twee lijnen indien de labels niet op één enkele lijn passen.

Columns All Deze eigenschap heeft geen functie. Gebruikt op formulieren om het aantal kolommen te bepalen voor het schikken van formulierelementen.

Columnspace All Stelt de spatie in tussen de kolommen.

ControlNumber

ProgrammableSection De identificatie van een programmeerbare sectie. Gebruikt om de sectie uit te voeren vanuit X++.

MORPHX IT Bijlage eigenschappen

318

© 2007 Steen Andreasen

Eigenschap Secties Omschrijving

Font All Het lettertype te gebruiken voor de sectie. Indien niet ingesteld wordt het standaard lettertype gebruikt.

FontSize All De lettergrootte te gebruiken voor de sectie. Indien niet ingesteld wordt de standaard lettergrootte gebruikt.

FooterText Body Enkel beschikbaar in auto-ontwerpen. Deze tekst wordt afgedrukt als tekst bij het algemeen totaal, in plaats van de standaard tekst.

ForegroundColor All RGB waarde of naam van het Windows kleurenschema.

GrandHeader Body Enkel beschikbaar in auto-ontwerpen. Bepaalt of een hoofdingtekst moet afgedrukt worden bij sorteerbreekpunten.

GrandTotal Body Enkel beschikbaar in auto-ontwerpen. Drukt een totaal af voor formulierelementen waarvan de eigenschap SumAll, SumPos of SumNeg is ingesteld.

HeaderText Body Enkel beschikbaar in auto-ontwerpen. Deze tekst wordt afgedrukt als hoofdingtekst bij sorteerbreekpunten. Indien er geen tekst is opgegeven, wordt de standaard tekst afgedrukt.

Height All Stelt een vaste hoogte in voor de sectie.

Italic All Stelt het lettertype in op cursief voor hoofdingen en gegevens in de sectie.

LabelBottomMargin ProgrammableSection Body PageFooter

Stelt de marge in onder de hoofdinglabels en voor de sectiegegevens.

LabelTopMargin ProgrammableSection Body PageFooter

Stelt de marge in boven de hoofdinglabels.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

319

Eigenschap Secties Omschrijving

LeftMargin All Linkermarge voor de sectie.

LineAbove All Voeg een lijn toe boven de sectiegegevens.

LineBelow All Voeg een lijn toe onder de sectiegegevens.

LineLeft All Voeg een lijn toe links van de sectiegegevens.

LineRight All Voeg een lijn toe rechts van de sectiegegevens.

Name All De naam van de sectie. Dit is de naam die X++ gebruikt om te verwijzen naar de sectie, indien de eigenschap AutoDeclaration is ingesteld.

NoOfHeadingLines ProgrammableSection Body PageFooter

Labels worden afgedrukt als hoofdinglijnen. Gebruikt om NoOfHeadingLines in te stellen op een vaste waarde. Ingesteld op nul indien er geen hoofding afgedrukt moet worden.

ResolutionX All Heeft blijkbaar geen effect.

ResolutionY All Heeft blijkbaar geen effect.

RightMargin All Rechtermarge van de sectie.

Ruler All Gebruikt om de meeteenheid van de huidige sectie in te stellen voor de visuele ontwerper.

Table Body Gebruikt door element.send() om te bepalen welke middensecties afgedrukt moeten worden.

Thickness All Bepaalt de dikte van de lijnen toegevoegd voor de sectiegegevens.

Top ProgrammableSection Prolog Body Epilog

Stelt een vaste positie in voor de sectie berekend vanaf de bovenkant van het blad.

MORPHX IT Bijlage eigenschappen

320

© 2007 Steen Andreasen

Eigenschap Secties Omschrijving

TopMargin All Stelt een marge in onder de sectiegegevens.

Underline All Onderlijnt hoofdingen en gegevens in de sectie.

Sectiesjabloon In de Engelstalige Axapta-omgeving gebruikt men de termen Section template. De sectiesjabloon wordt enkel gebruikt door auto-ontwerpen van een rapport. Eigenschap Omschrijving

SectionTemplate

Naam van de sectiesjabloon.

Table

De tabel gebruikt voor de sectiesjabloon. De tabel moet een onderdeel zijn van het koppelvlak gebruikt voor de sectiesjabloon.

Sectiegroep In de Engelstalige Axapta-omgeving gebruikt men de term Section group. De sectiegroep wordt enkel gebruikt door gegenereerd rapportontwerp. Eigenschap Omschrijving

DataField

Kan gebruikt worden om een sectiegroep te identificeren indien een rapport twee sectiegroepen bevat die dezelfde tabel gebruiken.

Name

Naam van de sectiegroep.

Table

De tabel gebruikt in de sectiegroep. Gebruikt door element.send() om te bepalen wanneer de sectiegroep moet afgedrukt worden.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

321

Type rapportelementen In de Engelstalige Axapta-omgeving gebruikt men de termen Type controls. Deze sectie beschrijft alle types van rapportelementen die gebruikt worden door een sectierapportelement (section control). Eigenschap Type Omschrijving

Alignment

String Real Integer Enum Date Time Sum Bitmap Prompt

Aligneert de elementgegevens. Kan gebruikt worden om de gegevens links te aligneren, als de rapportelementen vertikaal zijn gepositioneerd.

AllowNegative

Real Integer Sum

Deze eigenschap wordt niet gebruikt in rapporten. Ze wordt gebruikt in formulieren om te voorkomen dat de eindgebruiker negatieve waardes intikt.

ArrayIndex

String Real Integer Enum Date Time Sum Bitmap Prompt

Als het geselecteerd veld of rapportelement een array is, kan opgegeven worden dat één enkel element van deze array afgedrukt moet worden.

AutoDeclaration All Indien ingesteld op Yes, kan X++ verwijzen naar de eigenschappen van het rapportelement met behulp van de rapportelementnaam.

AutoInsSeparator

Real Sum

Zorgt ervoor dat MorphX een decimaal scheidingsteken gebruikt.

BackgroundColor

String Text Real Integer Enum Date Time Sum

RGB waarde of naam van het Windows kleurenschema.

MORPHX IT Bijlage eigenschappen

322

© 2007 Steen Andreasen

Eigenschap Type Omschrijving Prompt

BackStyle

String Text Real Integer Enum Date Time Sum Bitmap Prompt

Stelt de achtergrond van het rapportelement in op doorzichtig. Wordt gebruikt indien de achtergrondkleur of achtergrondbitmaps niet getoond moeten worden, of om de achtergrondkleur gelijk te stellen aan de kleur ingesteld met de eigenschap BackGroundColor.

Bold

String Text Real Integer Enum Date Time Sum Prompt

Stelt het vetniveau in van de rapportelementgegevens.

BottomMargin

All Stelt de marge in onder de rapportelementgegevens.

ChangeCase

String Text Enum Prompt

Gebruikt om de rapportelementgegevens weer te geven met kleine letters of hoofdletters.

ChangeLabelCase

String Real Integer Enum Date Time Sum Bitmap Shape

Gebruikt om het label weer te geven met kleine letters of hoofdletters.

ColorScheme

All Geeft aan of de RGB kleuren gebruikt moeten worden of het Windows kleurenschema.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

323

Eigenschap Type Omschrijving

ConfigurationKey

All Gebruikt om een configuratiesleutel op te geven voor het rapportelement.

CssClass

All Webeigenschap.

DataField

String Real Integer Enum Date Time Sum Bitmap Prompt

Selecteert een veld van de gekozen gegevensbron. In plaats van een veld te selecteren, kan je ook een displaymethode opgeven in de eigenschap DataMethod.

DataFieldName

Sum Naam van het rapportelement dat gesommeerd moet worden.

DataMethod

String Real Integer Enum Date Time Bitmap

Selecteert een displaymethode die gebruikt wordt bij het afdrukken. Indien de displaymethode bij een tabel hoort, moet de gegevensbron worden opgegeven in de eigenschap Table.

DateDay

Date Bepaalt hoe een dag wordt getoond. Windows regionale instellingen worden gebruikt als standaard.

DateFormat

Date Bepaalt het datumformaat. Windows regionale instellingen worden gebruikt als standaard.

DateMonth

Date Bepaalt hoe de maand wordt getoond. Windows regionale instellingen worden gebruikt als standaard.

DateSeparator

Date Bepaalt het datumscheidingsteken. Windows regionale instellingen worden gebruikt als standaard.

DateYear

Date Bepaalt hoe een jaar wordt getoond. Windows regionale instellingen worden gebruikt als standaard.

MORPHX IT Bijlage eigenschappen

324

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

DecimalSeparator

Real Sum

Bepaalt het decimale scheidingsteken. Windows regionale instellingen worden gebruikt als standaard.

DisplaceNegative

Real Integer Sum

Bepaalt de schikking van negatieve waardes bij het afdrukken.

DynamicHeight

String Indien ingesteld op Yes, wordt de hoogte bepaald afhankelijk van de tekst.

ExtendedDataType

String Text Real Integer Enum Date Time Prompt

Specifieert een uitgebreid datatype voor het rapportelement indien een DataField of een DataMethod niet is opgegeven.

ExtraSumWidth

Real Integer Sum

Bepaalt extra spatie voor de gesommeerde waarde. Handig voor munteenheden met een aanzienlijk aantal cijfers.

Font

String Text Real Integer Enum Date Time Sum Prompt

Het lettertype gebruikt voor de rapportelementgegevens. Indien niet opgegeven, wordt het standaard lettertype gebruikt.

FontSize

String Text Real Integer Enum Date Time Sum Prompt

De lettergrootte gebruikt voor de rapportelementgegevens. Indien niet opgegeven, wordt de standaard lettergrootte gebruikt.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

325

Eigenschap Type Omschrijving

ForegroundColor

All RGB waarde of naam van het Windows kleurenschema.

FormatMST

Real Sum

Formatteert de waarde aan de hand van de instellingen voor de standaard bedrijfsmunteenheid.

Height

All Stelt een vaste hoogte in voor het rapportelement.

ImageName

Bitmap Pad en bestandsnaam voor de bitmap.

ImageResource

Bitmap Gebruikt om een resource id op te geven voor de bitmap. Gebruik het rapport Tutorial_Resources om een overzicht te krijgen van de resource id's.

Italic

String Text Real Integer Enum Date Time Sum Prompt

Stelt het lettertype in op cursief voor de rapportelementgegevens.

Label

All Gebruikt om het standaard label te overschrijven indien er geen DataField of ExtendDataType is opgegeven. Het label wordt niet afgedrukt, indien de eigenschap ShowLabel is ingesteld op false.

LabelBold

String Real Integer Enum Date Time Sum Bitmap Shape

Stelt het label in op vet.

LabelCssClass

All Webeigenschap.

LabelFont String Stelt het lettertype in voor het label. Indien

MORPHX IT Bijlage eigenschappen

326

© 2007 Steen Andreasen

Eigenschap Type Omschrijving Real

Integer Enum Date Time Sum Bitmap Shape

niet opgegeven, wordt het standaard lettertype gebruikt.

LabelFontSize

String Real Integer Enum Date Time Sum Bitmap Shape

Stelt de lettergrootte in voor het label. Indien niet opgegeven, wordt de standaard lettergrootte gebruikt.

LabelItalic

String Real Integer Enum Date Time Sum Bitmap Shape

Stelt het label in op cursief.

LabelLineBelow

String Real Integer Enum Date Time Sum Bitmap Shape

Drukt een lijn af met de breedte van het rapportelement onder het label.

LabelLineThickness

String Real Integer Enum Date

Bepaalt de dikte van de lijn onder het label.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

327

Eigenschap Type Omschrijving Time Sum Bitmap Shape

LabelPosition

String Real Integer Enum Date Time Sum Bitmap Shape

Positioneert het label naar boven of naar links.

LabelTabLeader

String Real Integer Enum Date Time Sum Bitmap Shape

Gebruikt als de eigenschap LabelPosition is ingesteld op Left. Plaatst tabs of punten gevolgd door een dubbele punt na het label.

LabelUnderline

String Real Integer Enum Date Time Sum Bitmap Shape

Onderlijnt de labeltekst. De eigenschap LabelLineBelow wordt gebruikt om een lijn te plaatsen onder de volle breedte van het label.

LabelWidth

String Real Integer Enum Date Time Sum Bitmap Shape

Gebruikt als de eigenschap LabelPosition is ingesteld op Left. Bepaalt een vaste breedte voor het label.

MORPHX IT Bijlage eigenschappen

328

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

Left All Plaatst het rapportelement op een vaste positie berekend vanaf links. Als de rapportelementen horizontaal gealigneerd zijn en één element is op een vaste positie, dan moeten alle elementen op een vaste positie worden geplaatst.

LeftMargin

All Bepaalt de linkermarge voor het rapportelement.

Line

Shape Bepaalt het lijntype voor het rapportelement van type shape.

LineAbove

All Voegt een lijn toe boven de rapportelementgegevens.

LineBelow

All Voegt een lijn toe onder de rapportelementgegevens.

LineLeft

All Voegt een lijn toe links van de rapportelementgegevens.

LineRight

All Voegt een lijn toe rechts van de rapportelementgegevens.

MenuItemName

All Webeigenschap.

MenuItemType

All Webeigenschap.

ModelFieldName

All Geef hier de naam van het rapportelement op ten opzichte waarvan het betreffende rapportelement zich moet positioneren. Wordt vaak gebruikt om prompt- en sommatie-elementen te positioneren ten opzichte van gegevens.

Name All Naam van het rapportelement.

NoOfDecimals

Real Sum

Bepaalt het aantal decimalen dat afgedrukt moet worden.

ResizeBitmap

Bitmap Gebruikt om de bitmap te herschalen indien een vaste breedte en hoogte is opgegeven voor de bitmap.

RightMargin All Stelt een rechtermarge in voor het

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

329

Eigenschap Type Omschrijving rapportelement.

RotateSign

Real Integer Sum

Gebruikt om negatieve waardes te inverteren.

SecurityKey

All Gebruikt om een beveiligingsleutel op te geven voor het rapportelement.

ShowLabel

String Real Integer Enum Date Time Sum Bitmap Shape

Indien ingesteld op No, wordt het label niet afgedrukt.

ShowPicAsText

Bitmap Drukt niet de bitmap af maar het pad waar de bitmap zich bevindt of de resource id.

ShowZero

Real Integer Sum

Bepaalt of nulwaardes getoond moeten worden.

SignDisplay

Real Integer Sum

Bepaalt hoe negatieve waardes getoond moeten worden.

SumAll

Real Integer

Sommeert alle waardes. Gebruikt door sommatie-elementen om te bepalen welke rapportelementen gesommeerd moeten worden. Indien ingesteld in auto-ontwerp, zal het rapportelement gesommeerd worden indien de gebruiker een groepstotaal toevoegt in het dialoogvenster.

SumNeg

Real Integer

Ingesteld als enkel negatieve waardes gesommeerd moeten worden.

SumPos

Real Integer

Ingesteld als enkel positieve waardes gesommeerd moeten worden.

MORPHX IT Bijlage eigenschappen

330

© 2007 Steen Andreasen

Eigenschap Type Omschrijving

SumType Sum

Ingesteld om uitsluitend positieve of negatieve waardes te sommeren.

Table

String Real Integer Enum Date Time Sum Bitmap Prompt

Selecteer een gegevensbron te gebruiken voor het rapportelement.

Text

Text Geef hier de tekst op die afgedrukt moet worden.

Thickness

All Bepaalt de dikte van de lijnen toegevoegd aan de rapportelementgegevens.

ThousandSeparator

Real Sum

Bepaalt het scheidingsteken voor duizendtallen. Windows regionale instellingen worden gebruikt als standaard.

TimeFormat

Time Bepaalt het tijdsformaat. Windows regionale instellingen worden gebruikt als standaard.

TimeHours

Time Bepaalt of uren getoond moeten worden. Windows regionale instellingen worden gebruikt als standaard.

TimeMinute

Time Bepaalt of minuten getoond moeten worden. Windows regionale instellingen worden gebruikt als standaard.

TimeSeconds

Time Bepaalt of seconden getoond moeten worden. Windows regionale instellingen worden gebruikt als standaard.

TimeSeparator

Time Stelt het scheidingsteken voor de tijd in. Windows regionale instellingen worden gebruikt als standaard.

Top

All Stelt een vaste positie in voor het rapportelement berekend vanaf de top van de sectie. Indien rapportelementen horizontaal gealigneerd zijn en één

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

331

Eigenschap Type Omschrijving element is op een vaste positie gezet, dan moeten alle elementen op een vaste positie gezet worden.

TopMargin

All Stelt de marge in boven de rapportelementgegevens.

Type

Shape Stelt het type van shape of vorm in om af te drukken.

TypeHeaderPrompt

Text Prompt

Bepaalt of een tekst gevolgd moet worden door punten en een dubbele punt.

Underline

String Text Real Integer Enum Date Time Sum Prompt

Onderlijnt de rapportelementgegevens.

Visible

All Gebruikt om het rapportelement te verbergen. Indien de volgende rapportelementen geautopositioneerd zijn, zullen deze elementen herschikt worden.

WarnIfMissing

Bitmap Drukt een waarschuwing af in de Infolog als de bitmap niet gevonden kan worden aan de hand van ImageName, ImageResource of DataMethod.

WebTarget

All Webeigenschap.

Width

All Stelt een vaste breedte in voor het rapportelement. Indien ingesteld op default, zal het uitgebreide datatype de breedte bepalen.

MORPHX IT Bijlage eigenschappen

332

© 2007 Steen Andreasen

Veldgroep In de Engelstalige Axapta-omgeving gebruikt men de term Field group. Eigenschap Omschrijving

AutoFieldGroupOrder

Bepaalt of eigenschappen en veldvolgorde bewaard moeten worden. De veldgroep zal automatisch aangepast worden indien er wijzigingen zijn aangebracht aan de veldgroep van de data dictionary.

DataGroup

Veldgroepnaam.

Table

Tabel waaruit de veldgroep is genomen.

12.4 Queryeigenschappen Query Eigenschap Omschrijving

AllowCheck Indien ingesteld, worden de configuratiesleutels en beveiligingsleutels gevalideerd bij uitvoering van de query.

Form Bepaalt het Axaptaformulier gebruikt voor de querydialoog. Dit wordt normaal niet gewijzigd.

Interactive Bepaalt of de querydialoog getoond wordt aan de gebruiker.

Literals Kies voor default, forceliterals of forceplaceholders. Alle instructies die frequent worden uitgevoerd, zouden gebruik moeten maken van forceplaceholders. Het gebruik van forceliterals behoud je best voor gegevens die je niet vaak moet ophalen.

Name AOT naam van de query.

Title Titel voor de query.

UserUpdate Bepaalt of de gebruikersinstellingen voor de query worden bewaard.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

333

Gegevensbronnen In de Engelstalige Axapta-omgeving gebruikt men de term Data sources. Eigenschap Omschrijving

AllowAdd Indien gedesactiveerd, kan de gebruiker geen tabelbereiken toevoegen of verwijderen. Hetzelfde geldt voor sorteervelden.

Company Haal gegevens op van het opgegeven bedrijf. Dit wordt doorgaans niet gebruikt, gezien de gebruiker verwacht dat de gegevens van het actieve bedrijf worden opgezocht.

Enabled Bepaalt of de gegevensbron geactiveerd of gedesactiveerd moet worden. Indien gedesactiveerd, wordt de gegevensbron niet gebruikt in de sql-instructie.

FetchMode Enkel gekoppelde gegevensbronnen hebben deze eigenschap. Kan gebruikt worden om een één-op-veel-relatie te wijzigen in een één-op-één-relatie. Deze eigenschap wordt niet vaak gebruikt. In de meeste gevallen volstaat de eigenschap JoinMode.

FirstFast Bepaalt of het eerste record sneller moet worden opgehaald dan de overige records.

FirstOnly Selecteer enkel het eerste record.

JoinMode Bepaalt hoe de gegevensbronnen gekoppeld moeten worden.

Name AOT-naam van de gegevensbron.

OrderMode Kies voor order by of group by. Dit bepaalt de manier waarop de gegevens van de databank worden gesorteerd of gegroepeerd.

Relations Indien geactiveerd, zullen relaties tussen deze gegevensbron en de gegevensbron van één niveau hoger toegevoegd worden aan de hand van het actuele datamodel.

Update Indien ingesteld op yes, wordt het record opgehaald “forupdate” waardoor het record aangepast kan worden.

MORPHX IT Bijlage eigenschappen

334

© 2007 Steen Andreasen

Velden Eigenschap Omschrijving

Dynamic Toont doorgaans alle velden van de tabel. Indien de eigenschap order mode van het rapport de waarde “group by” heeft, en deze eigenschap is ingesteld op “no”, dan kunnen enkel geaggregeerde waardes gekozen worden voor het Fields knooppunt.

Sorteervelden Eigenschap Omschrijving

AutoHeader Geeft aan of een groepstitel afgedrukt moet worden telkens een waarde in dit veld wijzigt. Enkel gebruikt door rapportqueries.

AutoSum Geeft aan of een som afgedrukt moet worden telkens een waarde in dit veld wijzigt. Enkel gebruikt door rapportqueries.

HeaderDetailLevel Gebruikt in rapportqueries door de eigenschap AutoHeader. Normaal wordt een hoofding afgedrukt als de waarde van het sorteerveld wijzigt. De eigenschap kan gebruikt worden om te bepalen voor welk gedeelte van een veld een sorteerbreekpunt moet voorzien worden. Velden die gebruikt maken van de lettertekens punt (“.”), spatie (“ “), streepje (“-“),voorwaartse schuine streep (“/”),achterwaartse schuine streep (“\”) bestaan uit verschillende subvelden. Indien de waarde van de klantrekening gelijk is aan “CUST100.10 “, bestaat de veldwaarde uit twee subvelden. Als je de waarde van HeaderDetailLevel instelt op 2, voorzie je een sorteerbreekpunt, als de waarde na de punt is gewijzigd.

Ordering Stelt de sorteervolgorde in op oplopend of aflopend. .

SumDetailLevel Gebruikt in rapportqueries door de eigenschap AutoSum. Normaal wordt een som afgedrukt indien de waarde van het sorteerveld wijzigt. De eigenschap kan gebruikt worden om te bepalen voor welk gedeelte van een veld een sorteerbreekpunt moet voorzien worden. Velden die gebruik maken van de lettertekens punt (“.”), spatie (“ “), streepje (“-“), voorwaartse schuine streep (“/”), achterwaartse schuine streep (“\”) bestaan uit verschillende subvelden. Indien de waarde van de klantrekening gelijk is aan “CUST100.10 “, bestaat de veldwaarde uit twee subvelden. Als je de waarde van SumDetailLevel instelt op 2, voorzie je een sorteerbreekpunt, als de waarde na de punt is gewijzigd.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

335

Bereiken In de Engelstalige Axapta-omgeving gebruikt men de term Ranges. Eigenschap Omschrijving

Enabled Indien gedesactiveerd, wordt het bereik genegeerd.

Label Dit is het label van het bereikveld.

Name AOT naam van het bereik.

Status Kies voor open, locked of hide. Bij open, kan het bereik gewijzigd en verwijderd worden. Bij locked, kan het bereik noch gewijzigd noch verwijderd worden. Bij hidden, wordt het bereik niet getoond in de querydialoog.

Value Vaste waarde voor het bereik.

12.5 Menueigenschappen Eigenschap Omschrijving

ChangedBy De gebruiker die het laatste het menu heeft gewijzigd.

ChangedDate De datum waarop het menu het laatst is gewijzigd.

ChangedTime Het tijdstip waarop het menu het laatst is gewijzigd.

ConfigurationKey Gebruikt om een configuratiesleutel op te geven voor het menu.

CreatedBy De gebruiker die het menu heeft aangemaakt.

CreatedTime Het tijdstip waarop het menu is aangemaakt.

CreationDate De datum waarop het menu is aangemaakt.

HelpText Helptekst die wordt afgebeeld in de statusbalk.

Label Het label van het menu.

LockedBy De gebruiker die momenteel het menu heeft vergrendeld.

MORPHX IT Bijlage eigenschappen

336

© 2007 Steen Andreasen

Eigenschap Omschrijving

Name De menunaam.

NeededAccessLevel Vereiste toegangsniveau om dit menu te activeren.

SecurityKey Gebruikt om een beveiligingsleutel op te geven voor het menu.

SetCompany Bepaalt of je het bedrijf wil instellen als het menu is geactiveerd.

12.6 Menuoptie-eigenschappen Eigenschap Omschrijving

ChangedBy De gebruiker die het laatste de menuoptie heeft gewijzigd.

ChangedDate De datum waarop de menuoptie het laatst is gewijzigd.

ChangedTime Het tijdstip waarop de menuoptie het laatst is gewijzigd.

Class Het objecttype dat wordt uitgevoerd als de menuoptie wordt geactiveerd.

ConfigurationKey Gebruikt om een configuratiesleutel op te geven voor de menuoptie.

CountryConfigurationKey Gebruikt om een landspecifieke configuratiesleutel op te geven.

CreatedBy De gebruiker die de menuoptie heeft aangemaakt.

CreatedTime Het tijdstip waarop de menuoptie is aangemaakt.

CreationDate De datum waarop de menuoptie is aangemaakt.

EnumParameter Stelt een basisenumeratie-element in, zie verder bij de parameter EnumTypeParameter.

EnumTypeParameter Selecteert een basisenumeratie die doorgegeven wordt aan args.parmEnum() in het opgeroepen object. Zie verder bij de eigenschappen Class en Object.

HelpText Helptekst die afgebeeld wordt in de statusbalk.

MORPHX IT Bijlage eigenschappen

© 2007 Steen Andreasen

337

Eigenschap Omschrijving

Label Het label van de menuoptie.

LockedBy De gebruiker die momenteel de menuoptie vergrendelt.

MultiSelect Kan deze menuoptie worden uitgevoerd als meerdere records geselecteerd zijn in het formulier.

Name De naam van de menuoptie.

NeededAccessLevel Vereiste toegangsniveau om deze menuoptie te activeren.

Object Het object dat wordt uitgevoerd bij het activeren van de menuoptie.

Parameters Geef hier een stringwaarde op die doorgegeven wordt aan de args.parm() in het opgeroepen object. Zie verder bij de eigenschappen Class en Object.

RunOn Deze eigenschap geeft aan of het object uitgevoerd wordt op de AOS of niet.

SecurityKey Gebruikt om een beveiligingsleutel te specifiëren.

Web Webeigenschap.

WebConfigurationKey Webeigenschap.

WebPage Webeigenschap.

WebSecureTransaction Webeigenschap.

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

339

13 Bijlage MorphX ontwikkeltools Iedere ontwikkelomgeving die naam waardig voorziet de ontwikkelaar van een gereedschapskist om sneller te ontwikkelen. In het hoofdmenu bij Tools | Development tools vind je dit gereedschap of tools gemaakt met behulp van MorphX. Deze gereedschapskist geeft je een overzicht van de applicatieobjecten. Het merendeel van de ontwikkeltools kan je oproepen vanuit de AOT of vanuit de editor via de rechtermuisknop. De debugger kan je ook oproepen via het development tools menu, maar dit heeft niet veel zin : daarom hebben we de uitleg over de debugger geplaatst in het hoofdstuk Inleiding tot MorphX. Als je een kijkje wil nemen naar de ontwikkeltools vanuit de AOT, zoek dan in het menu DevelopmentTools in de AOT.

13.1 Kruisverwijzing De kruisverwijzing kan je vergelijken met een telefoonboek van de diverse applicatie-elementen : het geeft een overzicht van welk element waar wordt gebruikt. MorphX voorziet in een Cross Reference tool waarmee je kan opzoeken waar in de applicatie objecten, variabelen en labels gebruikt worden. Als je op het punt staat om de naam te wijzigen van een object of een variabele, of een label van het labelsysteem te verwijderen, helpt de cross reference tool je om een overzicht te krijgen van de gevolgen van deze aanpassing. Voor je gebruik kan maken van de kruisverwijzing, moet je een volledige hercompilatie uitvoeren met de kruisverwijzing geactiveerd. Je kan de opbouw van de kruisverwijzing activeren in het compileeropzetformulier. Dit verhoogt wel de compilatietijd aanzienlijk. Eén enkele opbouw van kruisverwijzing kan je doen via het topmenu door de optie Tools | Development tools | Cross-reference | Periodic | Update. Selecteer Update all om de kruisverwijzing op te bouwen. Het opbouwen van de kruisverwijzing neemt uren in beslag en vraagt veel geheugen. De opbouw kan ook niet in een oogopslag gebeuren aangezien de kruisverwijzing vereist dat alle objecten in de AOT geïndexeerd worden. Bovendien is de kruisverwijzing pas nuttig als je het geregeld bijwerkt. Dit kan je doen door een stapeltaak op te zetten die op vrije uren de kruisverwijzing opbouwt. De kruisverwijzing kan opgeroepen worden via het topmenu of door een object in de AOT te selecteren, met de rechtermuisknop te klikken en vervolgens Cross-reference te kiezen in de Add-ins menu. Het resultaat van de kruisverwijzing wordt bewaard in tabellen waarvan de naam begint met xRef. Je kan dit resultaat vergelijken met een bevolkingsregister : van iedere persoon worden een aantal gegevens genoteerd, zoals adres en contactgegevens. Alleen worden in de kruisverwijzing geen personen, maar objecten ingeschreven. Opmerking: Als je een nieuwe standaardinstallatie van Axapta hebt gedaan, kan het een goed idee zijn om de kruisverwijzing op te bouwen en de kruisverwijzingstabellen te exporteren. Als je daarna de kruisverwijzing nodig hebt voor een nieuwe installatie, kan je de kruisverwijzingsgegevens importeren in plaats van weer de kruisverwijzing van nul af op te bouwen. De formulieren Names en Path, opgeroepen door de kruisverwijzing in het topmenu, lijst alle inschrijvingen van de kruisverwijzing op. Het formulier Names lijst de

MORPHX IT Bijlage MorphX ontwikkeltools

340

© 2007 Steen Andreasen

inschrijvingen per objecttype op en het Pathformulier toont het AOT-pad voor iedere inschrijving. Als je records wil filteren in de kruisverwijzing, of je zoekt specifieke objecten in de AOT, dan zijn deze formulieren zeer handig. Als je daarentegen al het AOT-pad van het object kent, is het veel sneller om in de AOT-boom je weg te zoeken tot aan het object, met de rechtermuisknop te klikken en te kiezen voor cross-reference in het Add-ins menu. Het Path-formulier toont voor een AOT-object enkel het AOT-pad naar de locaties waar het betreffende object wordt gebruikt. In het kruisverwijzings-formulier geeft een object nog bijkomende informatie prijs, zoals welke andere objecten gebruikt worden door het betreffende object, en waar in de applicatie het bewuste object gebruikt wordt. Alle kruisverwijzingsformulieren hebben de menuoptie Edit, waarmee je de X++ code van een methode kan opzoeken. Indien het trefwoord in de kruisverwijzing een eigenschap betreft, kan het eigenschappenblad opgezocht worden door het Add-ins menu op te roepen en daarna Properties te selecteren.

Figuur 48: kruisverwijzing toont waar het uitgebreide datatype CustAccount wordt gebruikt.

13.2 Applicatieobjecten Het submenu Applicatieobjecten kan je vinden in de AOT knoop Menus onder de naam ApplicationObjects. Deze tools zijn niet direct het meest vooruitstrevende dat je in je gereedschapskist vindt en vaak zal gebruiken; je kan deze tools eerder beschouwen als “aardige hebbedingetjes”. Omdat het menu is aangemaakt in de AOT, kan je je eigen tools toevoegen. Applicatieobjectformulieren De formulieren Application objects en Old application objects lijsten de twee systeemtabellen UtilElements en UtilElementsOld op ; hierin vind je respectievelijk informatie over alle objecten in de AOT en alle objecten in de oude lagen. Vanuit het Add-ins submenu in de AOT kan je het formulier Application objects oproepen, waarbij de actieve filter gebaseerd is op de geselecteerde knoop in de AOT. Je zal geduldig moeten zijn bij het gebruik van het formulier Old application objects, omdat het formulier tergend traag opgeladen wordt. Het formulier koppelt de tabellen UtilElements en UtilElementsOld met elkaar om de verschillen te tonen. Dit zou handig kunnen zijn om een versie te vergelijken met de voorgaande versie, maar het formulier

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

341

is zo traag dat het in feite nutteloos wordt. Meer informatie over systeemtabellen waarvan de naam begint met Util* vind je in het hoofdstuk Data Dictionary. Applicatiebeheer Je kan deze tool gebruiken om mensen aan te stellen voor het beheer van de applicatieobjecten. Het applicatiebeheerformulier geeft je een overzicht van welke persoon verantwoordelijk is voor welk object, en van de groep waartoe deze persoon behoort. In de bijbehorende formulieren kan je personen en groepen definiëren. Het applicatiebeheer is waarschijnlijk gemaakt voor intern gebruik binnen de Axapta ontwikkelteams. Het is een eenvoudige tool en het is mogelijk niet ten gronde getest. Neem even een kijkje in het overzicht van het applicatiebeheerformulier. De selectie van het applicatieobjecttype gebeurt in het tweede veld maar moet wel eerst worden ingevuld, voor je de naam van het applicatieobject kan intikken. Je kan natuurlijk de tool wijzigen naar je eigen behoeften. Alle objecten hebben een naam die begint met SysUtilMangement*. Datagebruik Het datagebruikformulier toont de inhoud van de systeemtabel SysLastValue voor alle gebruikers. Meer informatie over datagebruik vind je in de sectie Gebruikers-instellingen. Tellen van applicatieobjecten Het formulier wordt gebruikt om een overzicht te krijgen van het aantal objecten, zoals tabellen en formulieren gebruikt in iedere module. Enkel de modules van het standaardpakket worden geteld. Als je je eigen modules wil tellen, moet je de klasse SysUtilCount aanpassen. De tabel SysCountTable bevat de resultaten van de telling. Vergrendelde applicatieobjecten Indien je werkt met het vergrendelen van applicatieobjecten in de AOT, dan zal je dit formulier zeer handig vinden. Het formulier toont een lijst van applicatieobjecten vergrendeld door alle gebruikers. Als je een lijn in het formulier verwijdert, wordt het vermelde object ontgrendeld. Let wel, in de AOT zal het object nog steeds als vergrendeld getoond worden, omdat de wijzigingen genoteerd in het overzichtsformulier van vergrendelde applicatieobjecten, pas in de AOT worden weergegeven, zodra de Axapta client is herstart. Refresh tools De drie menuopties, Refresh Dictionary, Refresh Data en Refresh AOD, worden gebruikt bij webontwikkeling. Met deze opties worden objecten en gegevens doorgegeven aan de webinterface zodat deze interface werkt met de meest recente wijzigingen.

MORPHX IT Bijlage MorphX ontwikkeltools

342

© 2007 Steen Andreasen

Herindexering Herindexering bouwt het indexbestand van de applicatieobjectlagen opnieuw op. Je mag nooit de applicatieobjectlagen herindexeren als er andere gebruikers in de applicatie zijn, omdat dit het indexbestand zal corrumperen. Als je de lagen wil herindexeren, kan je best het indexbestand AXAPD.AOI verwijderen. Het bestand kan gewist worden als er geen clients zijn ingelogd, en als alle AOS servers en de COM connector zijn gestopt. Als je de Axapta client start en je logt in, wordt het indexbestand automatisch terug opgebouwd.

13.3 Systeemmonitoring De tool voor systeemmonitoring kan je oproepen via het topmenu en door te dubbelklikken op het computerpictogram in de statusbalk. Het formulier System Monitoring toont databankoproepen en de grootte van die oproepen. Als je een AOS server gebruikt, krijg je bijkomende informatie voor client en server calls en een extra tabblad met informatie over de wachttijden. Dit is nuttige informatie als je het verkeer tussen client, server en de databank wil optimaliseren. Databankopvolging Als je een sequentie wil beginnen opvolgen, druk je op de Continue knop en je probeert je wijzigingen uit, zoals het openen van het verkooporderformulier en het aanmaken van een verkooporder. Klik op de Pause knop om de opvolging te stoppen. Dit telt het aantal verschillende types van databankoproepen. In veel gevallen is het beheer van het aantal selectinstructies de beste manier voor het optimaliseren van een applicatie. AOS-opvolging Als je een Axapta Object Server (AOS) gebruikt, worden objecten uitgevoerd op de client of op de AOS. De oproepen tussen client en server zullen geteld worden door het systeemmonitoringformulier. Je kan het resultaat van de AOS-oproepen gebruiken om de oproepen naar clients te verminderen. Objecten met gebruikersinteractie, zoals formulieren en dialogen, zullen altijd uitgevoerd worden op de client. Maar het verdient de voorkeur om de oproepen naar klassen en databank uit te voeren op de server. Meer informatie over het instellen van een klasse voor uitvoering op de server vind je in het hoofdstuk Klassen. In het tabblad Remote connection kan je de AOS wachttijd testen. Om een juist idee van de wachttijd te krijgen, moet je dit wel een aantal keren testen. Als je applicatie gebruikt wordt door clients met een beperkte bandbreedte, kan je de gebruikerservaring nabootsen door hun verbinding te simuleren. Stel de bandbreedte en wachttijd in, en klik op de knop Set as current. Dit staat je toe de performantie van de applicatie te testen onder verschillende communicatiescenario’s en geeft je zo een goede terugkoppeling over hoe je je code kan optimaliseren.

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

343

13.4 Code Profiler De code profiler wordt gebruikt om de uitvoertijd en de databanktijd van de code te berekenen. Net zoals de systeemmonitoring tools, wordt de code profiler geactiveerd door een startknop. Als je de kenmerken van het na te kijken menu voldoende hebt geprofileerd, klik je op de stopknop. De optie voor traceerdiepte gebruik je enkel als je een specifiek aantal niveau’s wil opvolgen. Na het profileren vraagt het systeem om een naam waaronder het de verzamelde gegevens kan bewaren. Ieder profileerverslag wordt bewaard in de databank en kan bekeken worden via de knop Profiler runs. Het formulier Profiler runs lijst de samengestelde profileringen op. Je kan doorklikken in een profilering en iedere uitgevoerde lijn code zien en zelfs de code editeren. Een beter overzicht van de codelijnen krijg je door de oproepen als boomstructuur te bekijken. De uitvoertijd voor iedere lijn code wordt berekend, en als je dit wenst, wordt ook de totale tijd van herhaalde oproepen van een lijn code berekend. Deze totale tijdswaarden zijn belangrijk als je code wil evalueren die herhaaldelijk in een lus wordt uitgevoerd. static void Intro_CodeProfiler(Args _args) { CustTable custTable; Counter counter; ; #profileBegin("Test of profiler") while select custTable { info(strFmt("Customer name: %1", custTable.name)); counter++; if (counter >= 10) break; } #profileEnd } Een andere manier om de code profiler te gebruiken is met behulp van macro’s in de code die de code profiler oproepen. Het start- en stoppunt van het profileren moeten opgegeven worden in de code zoals je in het voorbeeld hierboven kan zien. Het voorzien van profilering in de code is nuttig als je enkel een bepaald deel van je code wil controleren. Als je de code profiler start vanop het menu, ga je mogelijk meer code traceren dan echt nodig is. Let wel, je moet achteraf de totalen zelf berekenen aan de hand van de resultaten op het Profiler runs formulier. Als je een AOS gebruikt, wordt ook de AOS-tijd berekend. In het overzicht van het Profiler runs formulier, worden de profileringen opgelijst of de profilering is opgeroepen vanuit een AOS client of niet. Je moet het voorbeeld hierboven eens uitvoeren zowel op een tweerangs als een drierangs client. Merk op dat er minder lijnen worden gegenereerd als de code wordt uitgevoerd op een AOS client. De reden hiervoor is dat de code wordt uitgevoerd op de AOS met minder oproepen voor de client. Het gebruik van de code profiler is een goede oefening omdat je een zicht krijgt op de uitvoertijd van je code, en dus weet je ook waar je kan optimaliseren. Merk op dat het enkel de uitvoertijd is en niet de algemene tijd die wordt berekend. De code profiler

MORPHX IT Bijlage MorphX ontwikkeltools

344

© 2007 Steen Andreasen

genereert een groot aantal lijnen voor het openen van formulieren en het oproepen van een aantal taken via het formulier. Als de code profiler gestart is, kan het zijn dat je applicatie traag reageert. En als je de code profiler een grote stapeltaak voor de kiezen geeft, kan het profileren eindeloos lijken. Als je echt de code van een stapeltaak wil uitpluizen, beperk je best het aantal keren dat je een lus doorloopt, of je kan macro’s gebruiken om zo de code die geprofileerd moet worden, te beperken.

13.5 Applicatiehiërarchieboom Voor je gebruik maakt van de Application Hierarchy Tree, moet je de kruisverwijzing voor de typehiërarchie opbouwen. Ga naar Tools | Development Tools | Cross-reference | Periodic | Update in het topmenu en kies Update type hierarchy. Het is aan te raden om in Axapta gebruik te maken van uitgebreide datatypes in plaats van de basistypes. Na verloop van tijd zal je vaststellen dat je een hoop uitgebreide datatypes hebt gemaakt, omdat deze uitgebreide datatypes informatie bevatten over labels, formattering en relaties. De applicatiehiërarchieboom is bijzonder handig als je een overzicht wil van de uitgebreide datatypes en hoe deze datatypes van elkaar erven. Als je een nieuwe tabel aanmaakt en je moet uitzoeken of je een nieuw uitgebreid datatype moet maken dan wel een reeds bestaand datatype kan hergebruiken, dan kan je beroep doen op deze tool. De namen gebruikt voor de basistypes zijn niet helemaal dezelfde als de namen vermeld in de AOT. Dit is in het begin verwarrend, maar als je al weet dat het basistype varstring hetzelfde is als het basistype memo, raak je al een eind ver. Tabellen en klassen worden ook opgelijst in de applicatiehiërarchieboom. De tabellen vind je onder de wortelknoop Common. Alle tabellen zitten op hetzelfde niveau omdat tabellen niet van elkaar kunnen erven. Je kan de tabellijst gebruiken om op te zoeken welke methodes, velden en indexen een tabel bevat. Zowel applicatietabellen als systeemtabellen worden getoond. Je ziet ook methodes en indexen van de systeemtabellen, wat je trouwens niet kan zien in de AOT. De Object knoop toont een boom van klassen en hoe die klassen van elkaar erven. Voor iedere klasse vind je informatie over de methodes en de kindklassen. Je ziet heel eenvoudig tot welke klasse een methode behoort.

13.6 Visual MorphXplorer De Visual MorphXplorer wordt gebruikt om entiteit-relatie-diagrammen te tekenen waarop je de tabelrelaties kan zien en de erfelijkheid tussen klassen. Diagrammen worden gebouwd in het eerste tabblad. De titel van het diagram bepaal je op het tabblad “algemeen”. Het kleurentabblad dient om de standaardkleuren voor het diagram te wijzigen. Je kan de diagrammen bewaren en afdrukken. Heel handig voor het afdrukken van grote diagrammen is dat je het aantal af te drukken bladzijden in de breedte en de hoogte kan bepalen. Om een object toe te voegen, ga je naar het diagramtabblad, klik met de rechtermuisknop en kies nieuwe tabel of klasse. Zodra je je keuze gemaakt hebt,

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

345

verschijnt er een lijst waar je een object kan selecteren. Versleep het object naar het diagramtabblad om het object te positioneren. Eens het object gepositioneerd is, klik je met de rechtermuisknop om gerelateerde objecten te zien en toe te voegen aan het diagram. Merk op dat je geen tabellen en klassen kan combineren in één diagram. Je kan Visual MorphXplorer gebruiken om een diagram te tekenen waar je ofwel relaties tussen tabellen kan raadplegen of de hiërarchie van klassen. Als je in Visual MorphXplorer de gerelateerde objecten niet ziet, denk er dan aan om de kruisverwijzing voor het datamodel op te bouwen. Ga naar Tools | Development tools | Cross-reference | Periodic | Update en kies Update Data Model.

Figuur 49: Visual MorphXplorer Gerelateerde tabellen worden onderverdeeld in 1-n en n-1 relaties. Op dezelfde manier stelt een Axapta query gerelateerde tabellen voor. Als een tabel onderdeel is van een koppelvlak, zal je ook in staat zijn om een koppelvlak te selecteren. Een overzicht van de symbolen in het diagram vind je in figuur 50: Symbolen gebruikt in Visual MorphXplorer. Als je een gerelateerde tabel toevoegt, wordt het veld dat de relatie legt, afgebeeld. Als de relatie bestaat uit meer dan één veld, worden de relatievelden echter niet afgebeeld. In plaats daarvan wordt de tabelnaam afgebeeld voorafgegaan door een +. Dit is een beperking van de Visual MorphXplorer, omdat het handiger is om alle relatievelden te zien, eventueel in een helptekst die verschijnt als je de cursor over de relatie beweegt. Als je een diagram bouwt voor een klassehiërarchie, kan je de super- en subklassen kiezen. Als je de kruisverwijzing hebt opgebouwd, kan je ook de klassen kiezen die gebruik maken van de betreffende klasse of die door deze klasse gebruikt worden.

MORPHX IT Bijlage MorphX ontwikkeltools

346

© 2007 Steen Andreasen

Symbool Object Omschrijving

Tabel Geeft aan dat er 0-n records horen bij deze relatie.

Tabel Precies 1 record voor de relatie.

Tabel Klasse

Bij tabellen geeft het aan dat de gerelateerde tabel een koppelvlak is. Bij klassen geeft het aan dat het om een super- of subklasse gaat.

Klasse Klasse die gebruik maakt van of gebruikt wordt door de gerelateerde klasse.

Figuur 50: Symbolen gebruikt in Visual MorphXplorer

Visual MorphXplorer kan geactiveerd worden via het Add-ins menu in de AOT. De menuoptie voor de Visual MorphXplorer zal enkel beschikbaar zijn indien je een enkele tabel of klasse hebt gekozen. Als je daarentegen in de AOT het project MORPHXIT_VisualMorphXplorer importeert, zal je meerdere tabellen in de AOT kunnen selecteren en het bijbehorende diagram met relaties opbouwen in de Visual MorphXplorer. Als er meer dan één relatieveld is tussen twee tabellen, zoals het geval is bij de tabellen CustTable en CustTrans, dan worden beide relaties getekend. De uitbreiding die je in het vermelde project vindt, geeft je een snel overzicht van het datamodel. Je kan met deze uitbreiding ook het diagram van een submodule opbouwen, maar dit heeft zijn beperkingen. Het meervoudig selecteren geldt enkel voor tabellen, en als je teveel tabellen selecteert, zal je client crashen. Het formulier van Visual MorphXplorer maakt gebruik van de VarChart component om het diagram te tonen. De VarChart component wordt gebruikt op verschillende plaatsen in het standaardpakket, maar het is niet goed gedocumenteerd en je zal maar weinig informatie vinden over deze component. Daarom is het Visual MorphXplorer formulier een goede plaats om bepaalde kenmerken VarChart te onderzoeken.

13.7 Code Explorer De Code Explorer wordt gebruikt om te bladeren door de AOT in HTML-stijl. Je kan door de verschillende knopen klikken en informatie opvragen over lagen, eigenschappen en de code van methodes bekijken. Kruisverwijzingsinformatie wordt getoond indien de kruisverwijzing recent is bijgewerkt. De Code Explorer gebruikt het helpsysteem om de informatie te tonen. Als je een voorbeeld wilt van hoe je in code het helpsysteem kan gebruiken, moet je een kijkje nemen in de klassen waarvan de naam begint met SysCodeExplorer*.

13.8 Tabeldefinities Deze menuoptie drukt de inhoud van tabellen af zoals ze is opgeslagen in de UtilElements tabel. Het rapport, dat de tabeldefinities afdrukt, is georganiseerd zodat je alle belangrijke informatie van tabellen te zien krijgt, zoals de velden, eigenschappen en relaties. Geef een bereik op voor de inhoud die je wil opvragen via de query; anders ga je de printer wel een tijdje zoet houden, gezien de volledige inhoud van alle tabellen

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

347

meer dan 2000 bladzijden bedraagt. Gebruik dit rapport enkel als je informatie wil afdrukken van een aantal tabellen of een submodule.

13.9 Aantal Records Het Number of records formulier wordt gebruikt om de records te tellen in het huidige bedrijf voor iedere tabel. Het formulier lijst ook tijdelijke tabellen en koppelvlakken op, maar enkel de tabellen worden gesommeerd. Als je begint met het cachen van tabellen, zijn de recordtellingen nuttig. Je hebt misschien een volledige table cache opgezet voor een tabel, maar als de betreffende tabel een aanzienlijk aantal records bevat, is het aangewezen om het cachen van deze tabel terug te herbekijken. Meer informatie over de caching opties voor tabellen vind je terug in het hoofdstuk Bijlage eigenschappen .

13.10 Helpteksten De online help wordt getoond in een HTML-stijl. Als je op F1 drukt op een formulier of een AOT knoop, wordt de online help voor de knoop getoond. De inhoud van het helpsysteem is opgelijst in het formulier Help Texts. Er zijn drie verschillende soorten online help : systeemdocumentatie, applicatieontwikkelaardocumentatie en applicatiedocumentatie. In de AOT vind je de online help onder de laatste drie knopen. Systeemdocumentatie is de bron van informatie over kernelobjecten zoals klassen en tabellen. De applicatieontwikkelaarknoop wordt gebruikt voor het creëren van online help voor je eigen tabellen en klassen. Als een nieuwe tabel of klasse wordt aangemaakt, wordt een nieuw trefwoord toegevoegd aan de applicatieontwikkelaarknoop. Applicatiedocumentatie behelst de online help die getoond wordt aan de applicatiegebruikers. Het editeren van de online help gebeurt aan de hand van het formulier Help texts of met behulp van de drie AOT knopen. Opmerking : Weinig tabellen en klassen beschikken over online help. Dit zal waarschijnlijk verbeterd worden bij de release van versie 4.0.

13.11 Versie Update Als er nieuwe versies of service packs worden uitgebracht, zal je je applicatie moeten upgraden. De lagentechnologie in Axapta vereenvoudigt dit proces omdat wijzigingen gescheiden worden van de basiscode die Microsoft levert. Niettemin ga je toch nog een hele hoop code manueel moeten nakijken. Als je een formulier in de VAR-laag hebt gewijzigd, en hetzelfde formulier is aangepast in de SYP-laag of een nieuwe service pack, dan moet je dit formulier zelf nakijken. Hiervoor kan je een beroep doen op de vergelijkingstool, beschreven in de sectie Vergelijken van objecten. Voor je de vergelijkingstool gebruikt, kan je een overzicht krijgen van de wijzigingen in de nieuwe release aan de hand van de Versie Update tools.

MORPHX IT Bijlage MorphX ontwikkeltools

348

© 2007 Steen Andreasen

Hernoemde applicatieobjecten Objecten die hernoemd zijn in een nieuwe service pack of een nieuwe versie worden opgelijst in het formulier Renamed application objects. De inhoud van het formulier moet manueel worden opgebouwd. Om deze lijst op te bouwen, start je de applicatie die je wil upgraden, en klik op de Update knop. Hiermee maak je een tekstbestand dat de lijst van hernoemde objecten bevat. Het tekstbestand kan je dan importeren in de nieuwe release door op de Update knop te klikken. Het overzicht van hernoemde objecten is nuttig, omdat je nu enkel objecten moet vergelijken waarvan de naam niet is gewijzigd. Je hebt misschien wijzigingen aangebracht aan een SYS object, dat hernoemd is in de nieuwe release. Door de hernoeming heb je dit object enkel nog in je eigen laag. De lijst met hernoemde objecten helpt je om de wijzigingen op te sporen, wanneer een object is hernoemd of verwijderd. Aanmaken van een upgrade project Als je de applicatie wil upgraden, moet je als eerste stap een upgrade project aanmaken. Hiermee creëer je een AOT-project dat alle objecten bevat die in de huidige laag zijn gewijzigd. Je kan een optie aanvinken om wijzigingen van de huidige laag te verwijderen, als je ze ook terugvindt in een lagerliggende laag. Als je een hot fix in de huidige laag hebt geïmporteerd, kan je deze optie overwegen, omdat hot fixen normaal opgenomen zijn in de uitgebrachte service packs of versie updates. Zodra je het upgrade project hebt aangemaakt, zal je de objecten kunnen vergelijken met behulp van de vergelijkingstool. Vergelijken van lagen Als je een volledige vergelijking wil maken van de verschillen tussen twee lagen, kan je een beroep doen op de Compare layers. Hiermee maak je een AOT-project dat bestaat uit de objecten die enkel bestaan in de gekozen bronlaag of waar de bronlaag en referentielaag van elkaar verschillen. Als je een upgrade naar een beta versie hebt gedaan, of je hebt een service pack geïnstalleerd, dan zal de compare layers tools je een snel overzicht geven van de wijzigingen die gebeurd zijn tussen de verschillende versies.

13.12 Wizards De wizards in het Wizardsmenu helpen je bij het aanmaken van objecten zoals rapporten en klassen. Starten met een wizard kan een goede oefening zijn, omdat je zo inzicht verwerft in de basisstructuur van een object; je leert welke eigenschappen doorgaans worden ingesteld en welke formulierelementen meestal worden toegevoegd. De report wizard is zeer handig, en zal je meer helpen bij het gebruik van bepaalde eigenschappen dan je zou leren bij het bladeren door de rapportknopen in de AOT. Als je doorklikt in de menuknoop in de AOT, vind je het menu Wizards. Hier kan je je eigen wizards of hulpprogramma’s toevoegen.

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

349

Report Wizard Als je wil leren hoe de rapportgenerator werkt, gebruik dan de report wizard. De wizard bouwt de basis van je rapport. Als je een eenvoudig rapport hebt, moet je niet eens naar de AOT gaan. Een volledig overzicht van de report wizard vind je terug in de Bijlage Report Wizard. Wizard Wizard Tot Axapta 2.5 was er een richtlijn om de applicatiegebruikers te voorzien van een wizard om records aan te maken in complexe formulieren. Dit veranderde met versie 3.0 en de invoering van recordsjablonen. De Wizard Wizard werd ingevoerd om de ontwikkeling van deze wizards te stroomlijnen. Ook al wordt het niet meer aangeprezen om wizards te gebruiken om gegevens toe te voegen, dan nog zijn er gevallen waarin een wizard nuttig kan zijn, zeker als de applicatiegebruiker gegevens moet invullen in een zelden gebruikt formulier. Label File Wizard Dit is de enige wizard die niet gebruikt wordt om AOT-objecten aan te maken. Deze wizard dient om een nieuw labelbestand te creëren. Als je een labelbestand aanmaakt met behulp van de wizard, moet je starten op een 2-rangs client terwijl er geen andere gebruikers zijn ingelogd. Zodra je het traject van de wizard hebt doorlopen, herstart je de client vooraleer andere gebruikers kunnen inloggen in de applicatie. Het nieuwe labelbestand zal dan klaar zijn voor gebruik. Merk op dat het labelbestand eerst wordt aangepast zodra de laatste gebruiker van de applicatie uitlogt. Daarom is het aangewezen dat je labelbestanden aanmaakt zonder dat er andere gebruikers zijn ingelogd in Axapta. Meer informatie over het labelsysteem vind je terug in de sectie Label. Class Wizard Dit is een zeer eenvoudige wizard. De class wizard creëert een klasse in de AOT en voegt methodes van de interfaceklassen toe. Je kan in de wizard slechts één enkel sjabloon kiezen. Niettemin kan je de wizard uitbreiden door je eigen sjablonen eraan toe te voegen. Anders is de wizard enkel nuttig voor trainingdoeleinden. COM Class Wrapper Wizard Als je wil communiceren met een ActiveX component met behulp van de COM interface, dan is deze wizard de ideale startplek. Deze wizard lijst alle componentbibliotheken op die op je computer zijn geïnstalleerd. Selecteer een bibliotheek en je start de creatie van Axaptaklassen die een omhulsel vormen voor de klassen en methodes in deze bibliotheek. Dit is uitermate handig en versnelt het werken met een COM interface.

MORPHX IT Bijlage MorphX ontwikkeltools

350

© 2007 Steen Andreasen

13.13 Label Het labelsysteem is een van de krachtige kenmerken van Axapta om de meertaligheid van de applicatie op een eenvoudige manier te voorzien. In plaats van in de code tekst in te tikken voor een veld of helptekst, wordt een labelidentificatienummer opgegeven. Dit label id wordt toegevoegd aan het labelsysteem dat informatie bevat over de bijbehorende tekst voor het label in iedere taal. Het labelidentificatienummer heeft de syntax: @<labelbestand id><labelnummer>, zoals @SYS1002. Het labelbestand id is een identificatienummer van 3 karakters. Het labelnummer is een doorlopend nummer, dat opgehoogd wordt als een label wordt toegevoegd. In de Axapta applicatiemap, bestaat een reeks labelbestanden voor iedere labelbestand id. De labelbestanden worden genoemd volgens de structuur AX<labelbestand id><taal id>.<extensie> zoals AXSYSEN-US.ALD. Een overzicht van de labelbestanden vind je in figuur 51 : Labelbestanden. Bestandsextensie Omschrijving *.ALD

Een tekstbestand dat alle labels bevat voor het labelbestand id.

*.ALC

Commentaren toegevoegd in het labelsysteem worden opgeslagen in dit bestand.

*.ALI

Indexbestand voor het labelbestand id. Als dit bestand ontbreekt, wordt het bestand automatisch aangemaakt zodra het labelsysteem wordt opgeroepen.

Figuur 51: Labelbestanden De labelvelden worden aangepast als de laatste gebruiker uit de applicatie uitlogt. Indien voor één of andere reden de laatste gebruiker niet correct is uitgelogd, dan worden de labelbestanden niet opnieuw opgebouwd. Aangezien de labelbestanden een onderdeel zijn van de wijzigingen, samen met de wijzigingen in de AOT, is het van cruciaal belang dat je zeker bent dat de labelbestanden zijn aangepast met de laatst toegevoegde labels. Kijk het *.ALD bestand na om zeker te zijn dat de laatste labels ook effectief zijn toegevoegd. Als een label ontbreekt in het bestand, zal de applicatiegebruiker het label id zien in plaats van de labeltekst. Opmerking : De reden van het gebruik van labels is de applicatiegebruikers toelaten om de applicatie te gebruiken in hun taal van voorkeur. Zelfs als je je wijzigingen niet wil vertalen in een andere taal, moet je toch nog het gebruik van labels aanhouden, om zeker te zijn van een consistente benaming in je applicatie. Als je een term wil wijzigen, moet je slechts één enkel label aanpassen, in plaats van de betreffende tekst door heel je code opzoeken en wijzigen. Het standaard pakket heeft een labelbestand voor iedere laag met een naam die verwijst naar de betreffende laag. Het is aanbevolen om deze labelbestanden niet te wijzigen omdat deze labelbestanden aangepast kunnen zijn bij de uitgave van een service pack of versie. In plaats daarvan moet je een nieuw labelbestand maken voor je wijzigingen.

MORPHX IT Bijlage MorphX ontwikkeltools

© 2007 Steen Andreasen

351

Vinden van een label Dit formulier wordt gebruikt om te zoeken in het labelsysteem. Dit is hetzelfde formulier dat geopend wordt als je een label opzoekt vanuit het eigenschappenblad of de code editor. Het zoeken naar labels gebeurt in de taal die je bovenaan hebt geselecteerd. Als je wilt zoeken naar een label in het Engels, kan je hetzelfde label laten zien in andere talen door naar het tabblad “advanced” te gaan en daar de gewenste talen te selecteren. Het zoeken gebeurt dan nog steeds in het Engels, maar bovenop krijg je de geselecteerde talen onderaan het formulier te zien. Dit kan handig zijn als je zeker wil zijn dat je de labels hebt toegevoegd in alle talen die je gebruikt voor formules zoals in de verkoopsfacturen. Als je naar een label zoekt, kan het gebruik van < and > je zoekresultaat beperken. Als je het label 'Customer' wil opzoeken, is de zoektocht performanter als je <Customer> intikt, omdat je dan enkel labels met de exacte tekst ‘Customer' opvraagt. Als je alle labels wil zien die beginnen met 'Customer', tik je enkel <Customer in. Normaal wordt het labelsysteem opgeroepen vanuit het eigenschappenblad of de code editor, waar je een tekst opzoekt om het bijbehorende label te vinden. Als het label is gevonden, klik je op de Paste knop om het label id te krijgen. Als er geen label is gevonden, kan je een nieuw label toevoegen gebaseerd op de zoektekst door te klikken op de New knop. Het nieuwe label wordt aangemaakt in het standaard label file id opgegeven in het “advanced” tabblad. Label log Alle wijzigingen van labels in het standaard pakket, worden geregistreerd en in dit formulier getoond. Het toevoegen, verwijderen en wijzigen van labels wordt gelogd. Als je per ongeluk een label hebt gewist, kan het label opnieuw worden aangemaakt met behulp van het formulier “Label log”. Label file wizard Een beschrijving van de label file wizard vind je in de sectie Wizards. Label intervals Het formulier Label intervals kan je gebruiken voor het beheer van de label id's in je eigen labelbestanden. Als je werkt in een omgeving waar er aanpassingen gebeuren in meer dan één applicatie, en je gebruikt hetzelfde labelbestand, dan kan je dit formulier gebruiken om label intervals te definiëren voor iedere applicatie. Geef eerst het label file id op. Intervalstatus moet beschikbaar zijn voor de labelidentificatienummers. In iedere applicatie geeft je een label interval op, en je noteert het laatst gebruikte label-identificatienummer van het interval. De volgende maal dat een label wordt gemaakt, zal het labelidentificatienummer genomen worden uit het interval, en het laatste gebruikte label wordt opgehoogd in het formulier.

MORPHX IT Bijlage MorphX ontwikkeltools

352

© 2007 Steen Andreasen

Het is natuurlijk altijd de beste oplossing om je labels in één applicatie te creëren, maar deze werkwijze kan dienen als handigheidje. Je moet natuurlijk zelf het beheer doen van je labelbestanden voorafgegaan door *.ALD.

MORPHX IT Bijlage Report Wizard

© 2007 Steen Andreasen

353

14 Bijlage Report Wizard Deze bijlage bevat een stappengids die aangeeft hoe je een rapport kan maken met de report wizard. De report wizard is een tool ontworpen om gebruikers zonder technische vaardigheden en kennis toe te laten eenvoudige rapporten te schrijven in Axapta. Maar het is ook een nuttige tool om te leren hoe je rapporten maakt. Zodra je door alle stappen van de wizard bent gegaan, heb je de keuze : ofwel voer je het rapport uit, of je bewaart het in de AOT. Daar kan je het rapport achteraf altijd weer opvragen. Het maak ook geen verschil uit of je een rapport maakt via de wizard of de rapportgenerator, een rapport kan je altijd bewaren in de AOT. Tot je vertrouwd bent met de rapportgenerator, kan de wizard je begeleiden door de basisstappen die je moet doorlopen om de structuur van je rapport op te zetten. Stap 1 Geef op of je systeemnamen of labelnamen wil gebruiken. Systeemnamen zijn de namen gebruikt in de AOT. Het kan voordelig zijn om systeemnamen te gebruiken omdat deze namen uniek zijn.

MORPHX IT Bijlage Report Wizard

354

© 2007 Steen Andreasen

Stap 2 Geef de naam van het rapport op. Deze naam wordt gebruikt bij het bewaren van het rapport in de AOT. Caption is de afdruknaam van het rapport. Stap 3 Selecteer de tabellen waarvan je gegevens wil ophalen. Selecteer CustTable als tabel op het eerste niveau en CustTrans als tabel op het tweede niveau. Als het venster van de gerelateerde tabellen (linksonder) leeg is na selectie van de tabel CustTable, moet je de kruisverwijzigingen van het datamodel bijwerken. Dit moet je doen na de installatie van Axapta, en na iedere wijziging van het datamodel. Het datamodel bijwerken doe je via Tools | Development tools | Cross-reference | Periodic | Update. Selecteer de optie Update Data Model.

MORPHX IT Bijlage Report Wizard

© 2007 Steen Andreasen

355

Stap 4 Selecteer de af te drukken velden van iedere gegevensbron. Normaal wordt de veldgroep AutoReport geselecteerd voor iedere gegevensbron. Het is mogelijk om veldgroepen, tabelvelden en displaymethodes te selecteren. De Up en Down knoppen worden gebruikt om de afdrukvolgorde van de velden te wijzigen. Stap 5 Geef aan welke velden van het basistype integer en real gesommeerd moeten worden. Standaard worden alle gehele en decimale getallen geselecteerd voor sommatie.

MORPHX IT Bijlage Report Wizard

356

© 2007 Steen Andreasen

Stap 6 Bepaal de sorteervolgorde van de indexvelden. Standaard duidt de report wizard alle velden aan die deel uitmaken van een index. De Up en Down knoppen worden gebruikt om de sorteervolgorde van de velden te wijzigen. Stap 7 Geef aan of er een nieuwe subhoofding moet afge-drukt worden telkens de waarde in één van de opgegeven sorteervelden wijzigt. In het voorbeeld wordt er een nieuwe sub-hoofding afgedrukt telkens de rekeningnummer in de klantentabel wijzigt.

MORPHX IT Bijlage Report Wizard

© 2007 Steen Andreasen

357

Stap 8 Geef aan of er groepstotalen afgedrukt moeten worden telkens de waarde in één van de opgegeven sorteervelden gewijzigd is. In het voorbeeld worden groepstotalen afgedrukt telkens de rekening-nummer in de klantentabel wijzigt en als er een nieuwe transactiedatum in de klantentransactietabel begint. Stap 9 Selecteer de querybereiken. De report wizard selecteert normaal alle velden die gebruikt worden in de indexen van de gebruikte tabellen. De querybereiken gebruik je om de records te filteren.

MORPHX IT Bijlage Report Wizard

358

© 2007 Steen Andreasen

Stap 10 Bepaal de rapportopmaak en geef aan of er een sjabloon gebruikt mag worden. Rapportsjablonen vind je in de AOT onder Reports. Stap 11 Geef aan of je het rapport wil toevoegen aan een menu. De Design knop zal het rapport in de AOT openen in opmaakzicht. De Print preview knop kan gebruikt worden om een voorafblik te krijgen van het gedefinieerde rapport.

MORPHX IT Bijlage Report Wizard

© 2007 Steen Andreasen

359

Stap 12 Selecteer een menu voor het rapport. Als je een rapport toevoegt aan een menu, wordt een nieuw menuoptie van het type Output aangemaakt. Stap 13 Je hebt nu alle stappen doorlopen. Klik op Finish om het rapport in de AOT te bewaren.

MORPHX IT Bijlage woordenlijst Nederlands-Engels

© 2007 Steen Andreasen

361

15 Bijlage woordenlijst Nederlands-Engels In deze vertaling hebben we zoveel mogelijk Engelse woorden willen vermijden waar mogelijk. De opdracht was immers een Nederlandse vertaling te maken, geen “dunglish” te schrijven. Maar wie extra informatie wil opzoeken op het internet, komt al heel snel een Engelstalige webstek tegen. Daarom voegen we hier nog een woordenlijst aan toe met de Nederlandse termen die we gebruikt hebben, en hun Engelse tegenhanger. Dat moet het opzoeken op het wereldwijde web alvast wat eenvoudiger maken. De lijst is gesorteerd op het Nederlandse woord in de middenste kolom. Engels Nederlands alternatief current layer actieve laag aggregate function aggregatiefunctie base enum basisenumeratie Range bereik message window berichtvenster file system bestandssysteem commitment borging compiler setup form compileeropzetformulier control flow controlestructuur database lock databankvergrendeling drill down doorklikken landscape dwars single sign-on eenmalige aanmelding property setting eigenschapinstelling property sheet eigenschappenblad entry for an enum enumeratie-element form formulier control formulierelement rapportelement foundation class funderingsklasse user permission gebruikerstoelating data link gegevenskoppeling join select statement gekoppelde selectieopdracht tools gereedschap hulpmiddelen related field fixed gerelateerde veldklem to resize herschalen patch layer herstellaag main area hoofddomein header hoofding resource hulpbron wizard hulpprogramma

MORPHX IT Bijlage woordenlijst Nederlands-Engels

362

© 2007 Steen Andreasen

Engels Nederlands alternatief if statement if -instructie icon ikoon pictogram entry inschrijving trefwoord statement instructie wild card jokerteken feature kenmerk radio button kiesknop node knooppunt knoop join koppeling join level koppelingsniveau koppelniveau join order koppelingsvolgorde mapping koppellijst map koppelvlak join mode koppelwijze cross reference kruisverwijzing layer laag last committed value laatst geborgde waarde font lettertype garbage collection size limiet voor afvalophaling customisation maatwerk paswerk bookmark markering menu item menuoptie menupunt menu reference menuverwijzing body section middensectie drop down list neerklaplijst toolbar nutsbalk watch window observatievenster design view opmaakzicht call stack oproepstapel popup window opspringvenster tracing opvolging lookup form opzoekformulier lookup opzoeking lookup mode opzoekmodus override overschrijven live data productiegegevens best practices programmatievoorschriften richtlijnen framework raamwerk kaderwerk grid raster portrait rechtop

MORPHX IT Bijlage woordenlijst Nederlands-Engels

© 2007 Steen Andreasen

363

Engels Nederlands alternatief call by reference referentieparameter record rij scroll bar rolbalk separator scheidingsteken splitter scheidslijn select statement selectie-instructie selectieopdracht template sjabloon keyword sleutelwoord shortcut snelkoppeling kortpad hotkey sneltoets shortcut key sneltoets break level sorteerbreekpunt sorting break sorteerbreekpunt batch job stapeltaak batch stapelverwerking return type teruggeeftype return value teruggeefwaarde caption titel caption bar titelbalk tracing tracering 2-tier tweerangs release uitgave extended data type uitgebreid datatype runable uitvoerbaar runtime uitvoertijd field fixed veldklem window control vensterelement locking vergrendeling flow verloop drag and drop verslepen en plaatsen check box vinkvak(je) footer voetregel preview voorschouw progress bar voortgangsbalk rule of thumb vuistregel call by value waardeparameter latency wachttijd tooltip werktip top node wortelknoop to log in zich aanmelden