Dynamics AX TAP Knowledge Transfer Wave 2 Application Integration Framework (AIF) Document/Interface Development Søren Vagn Andersen Program Manager Microsoft Dynamics AX Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics Supported XML Documents Trade Price List [out] Price/Discount Agreement [in] Purchase Order [out] Sales Order [in] Picking List [out] Packing Slip [in] Advance Ship Notice [out] Sales Invoice [out] Purchase Invoice [in] Free Text Invoice [in] Finance Exchange Rates [out,in] GL Chart of Accounts [out,in] GL Dimensions [out,in] Inventory Inventory Counting Journal [in] Inventory Transfer Journal [in] Inventory Balances [out] Inventory Transactions [out] Inventory Profit/Loss Journal [in] XML Document Development Model driven approach Focus on data and business logic Customizations handled (almost) automatically Generalized framework Tools support Wizard for generating class skeletons Best practice check to detect data model changes Model Driven Approach Axd<Document> query defines XML structure and content SalesTable SalesLine Axd<Document> class performs serialization and de-serialization XML HOW ? ... ... DB Ax<Table> classes manage data access Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics XML Schema Structure <?xml version="1.0" encoding="utf-16"?> <SalesOrder xmlns="http://schemas.microsoft.com/dynamics/"> <DocPurpose>Original</DocPurpose> <SenderId>dmo</SenderId> Empty Hidden <SalesTable class="entity"> <CaseTagging>No</CaseTagging> <CovStatus>1</CovStatus> <CurrencyCode>EUR</CurrencyCode> <CustAccount>4020</CustAccount> Parm<Property>() methods on Axd<Document> class <SalesLine class="entity"> <Blocked>No</Blocked> <CaseTagging>No</CaseTagging> <Complete>No</Complete> <CurrencyCode>EUR</CurrencyCode> ... <InventDim class="entity">…</InventDim> </SalesLine> <DocuRefHeader class="entity">…</DocuRefHeader> <DocuRefHeader class="entity">…</DocuRefHeader> </SalesTable> </SalesOrder> Serialization Strategy Serialize parm<Property>() methods on the Axd<Document> class Read data using Ax<Table> class, if present Read directly from table, if no Ax<Table> class Field list on Axd<Document> query controls what fields are serialized If Ax<Table> exists, only fields with parm<Field>() method on Ax<Table> class Calculated fields (display fields) are always serialized Mapping Simple Datatypes X++ Datatype XSD Type String xsd:string, maxLength=StringSize (maxLength=max(StringSize,50) in case of value mapping) xsd:int xsd:time, ISO format: HH:MM:SS (Time stamp if global::isTypeTime is true) xsd:long xsd:decimal, fractionDigits=NoOfDecimals xsd:date, ISO format: YYYY-MM-DD xsd:string, enumeration=<EnumName>, ... xsd:string, pattern=’[0-9A-Fa-f]{8}-...’ xsd:string, Base64 encoded Integer Integer Int64 Real Date Enum GUID BLOB Arrays All elements are serialized <Dimension> <element>Department</element> <element>Cost Center</element> <element>Purpose</element> </Dimension> Null Values Outbound Null values are not serialized Arrays may contain empty elements Inbound Empty string elements interpreted as zero-length string, not null Use <element xsi:nil=”true”/> to specify null value Security Respects Microsoft Dynamics AX security Security keys Record level security MaxAccessMode property AllowEditOnCreate property Visible property No access to system tables SystemTable=Yes TableType≠Table Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics Axd<Document> Query Describes structure of XML document Controls amount of data retrieved from Microsoft Dynamics AX Disabled fields and data sources are excluded from Schema Only one (1) ”root” data source allowed Same name as Axd class Work Tables and Journals Posting XML PurchTable VendPurchOrderJour Read from journals Insert into work tables PurchLine VendPurchOrderTrans XML Documents With Header/Lines Header = ”Root” data source SalesTable SalesLine Lines Notes at both header and lines Axd<Document> Classes Service classes Implement AifServiceable interface Expose document actions Validation of entire document, e.g. Cross-table dependencies Document life-cycle constraints May invoke business logic Posting Ax<Table> Classes 1-to-1 relationship with tables Name derived from table, e.g. AxSalesLine parm<Field>() method for each field Defaulting of fields with no values Validation of fields with respect to, e.g. Referential integrity Number sequences Business logic Value mapping of specific fields Also used by other modules Enterprise Portal ... Class Hierarchy AifServiceable AxdBase Axd<Document> Axd<Document> +getActionList() ? Action AxInternalBase Axd<Document> Method SendDocument Table Ax<Table>XML read(entityKey, ...) ReceiveDocument entityKey create(XML, ...) ... ... Action Axd<Document> Method SendDocument XML read(entityKey, ...) Generate XML document identified by entity key SendDocumentList XML readList(entityKeyList, ...) Generate list of XML documents each identified by entity key QueryDocuments XML findList(queryCriteria, ...) Generate list of XML documents matching query criteria QueryEntityKeys Generate list of entity keys for XML documents matching query criteria ReceiveDocument entityKeyList findEntityKeyList(queryCriteria, ...) entityKey create(XML, ...) Parse XML document and write data to database ReceiveDocumentList Parse list of XML documents and write data to database entityKeyList createList(XML, ...) Entity Keys Identify specific document instance Table (Field, Value) pairs for primary keys Use entity keys returned by AIF Table Must be ”root” data source in query Use ID in X++ Not needed in XML / Web Client Field Use ID in X++ Use Field Name in XML / Web Client Using Entity Keys XML <?xml version="1.0" encoding="utf-8" ?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2005/12/documents"> <Header> … <Action>readInventSum</action> Action </Header> <Body> <EntityKey> <KeyData> Entity Key <KeyField> <Field>ItemId</Field> <Value>B-R12</Value> </KeyField> <KeyField> <Field>InventDimId</Field> <Value>00001_060</Value> </KeyField> </KeyData> </EntityKey> </Body> </Envelope> Using Entity Keys Web Services Create entity key object //Set the Enity Key EntityKey entityKey = new EntityKey(); entityKey.KeyData = new KeyField[2] { new KeyField(), new KeyField() }; entityKey.KeyData[0].Field = "ItemId"; entityKey.KeyData[0].Value = "B-R12"; entityKey.KeyData[1].Field = "InventDimId"; entityKey.KeyData[1].Value = "00001_060"; Populate entity key Query Criteria Similar to entity keys Criteria for identifying range of documents List of Table Field Operator Operand1 Operand2 (optional; operator dependent) Table must be ”root” data source or inner-joined with ”root” data source Query must not contain hidden or locked range for <Table>.<Field> Valid Operators X++ Operator == != > >= < <= .. XML Enumeration # of Operands Equals 1 NotEquals 1 GreaterThan 1 GreaterThanEqual 1 s LessThan 1 LessThanEquals 1 Range 2 Using Query Criteria <?xml version="1.0" encoding="utf-8" ?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2005/12/documents"> <Header> … <Action>findListSalesOrder</action> Action </Header> <Body> Query <QueryCriteria> <CriteriaElement> criteria <DataSourceName>SalesTable</DataSourceName> <FieldName>CustAccount</FieldName> <Operator>Range</Operator> <Value1>4000</Value1> <Value2>4020</Value2> </CriteriaElement> <CriteriaElement> <DataSourceName>SalesTable</DataSourceName> <FieldName>CurrencyCode</FieldName> <Operator>Equals</Operator> <Value1>USD</Value1> </CriteriaElement> </QueryCriteria> </Body> </Envelope> Constraints Enables the AIF to make decisions based on document contents Routing Security (to prevent spoofing) Constraint types Customer Vendor Warehouse Constraint Based Routing EndpointA Microsoft Dynamics AX EndpointB PurchaseOrder1 [3001,3004] [3001] PurchaseOrder2 [3004] XML [3001] EndpointC [3000,3001] EndpointD [3001,3002,3004] EndpointE [No Constraints] Constraint Based Security EndpointA [4000] Microsoft Dynamics AX SalesOrder1 [4000] SalesOrder2 [4002] XML EndpointB [4000,4002] EndpointC [4000,4012] EndpointD [4000,4002,4012] EndpointE [No Constraints] Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics Axd<Document> TO DO’s ... in code generated by the Axd Wizard getLabel() getActionList() initQueryFromEntityKey(), initQueryFromQuery() Validate entity key contents Update query based on configuration settings prepareForSave() Validate data across tables Provide defaults for document getConstraintList() Identify document constraints Update Query Example: AxdSalesInvoice public void initQueryFromEntityKey(AifEntityKey _aifEntityKey = null, boolean _validateEntityKey = false) { QueryBuildDataSource qbdsFormLetterRem; ... //Disable FormLetterRemarks if no remarks for sales invoice. if (!(select formLetterRemarks where formLetterRemarks.FormLetter == FormTextType::SalesInvoice).RecId) { qbdsFormLetterRem = query.dataSourceTable(tablenum(formLetterRemarks),1); qbdsFormLetterRem.enabled(false); qbdsFormLetterRem = query.dataSourceTable(tablenum(formLetterRemarks),2); if (qbdsFormLetterRem) { qbdsFormLetterRem.enabled(false); } } Disable data sources not needed } Document Validation/Defaulting Example: AxdSalesOrder public boolean prepareForSave(AxdStack _axdStack, str _dataSourceName) { AxSalesTable axSalesTable; ... Verify ; switch (classidget(_axdStack.top())) { case classnum(AxSalesTable) : axSalesTable = _axdStack.top(); this.checkSalesTable(axSalesTable); that elements required by document are provided axSalesTable.parmSalesType(this.salesType()); ... } ... } Set default order type Document Validation Logic Example: AxdSalesOrder private void checkSalesTable(AxSalesTable _axSalesTable) { ; if (!_axSalesTable.parmDeliveryDate()) { error(strfmt("@SYS88971")); } if (!_axSalesTable.parmCustAccount()) { error(strfmt("@SYS88972")); } if (!_axSalesTable.parmPurchOrderFormNum()) { error(strfmt("@SYS88973")); } } Verify that values are provide Document Defaults As Parameters Example: AxdSalesOrder protected SalesType salesType() { ; switch (AxdDocumentParameters::find().SalesType) { case AxdSalesType::Journal : return SalesType::Journal; case AxdSalesType::Sales : return SalesType::Sales; } return SalesType::Journal; } Identifying Constraints Example: AxdSalesOrder public void getConstraintList(Common _curRec, AifConstraintList _constraintList) { AifConstraint aifConstraint = new AifConstraint(); SalesTable salesTable; ; if (_curRec.TableId != tablenum(SalesTable)) { throw error(strfmt("@SYS23396",funcname())); } Constraint = Customer ID salesTable = _curRec ; aifConstraint.parmId(salesTable.CustAccount); aifConstraint.parmType(AIFConstraintType::Customer); _constraintList.addConstraint(aifConstraint) ; } Add constraint to list Ax<Table> TO DO’s ... in code generated by the Axd Wizard Implement defaulting logic Add to set<Field>() methods Add parm<Field>() methods for calculated fields (display fields) Implement caching as appropriate Use cacheObject() as skeleton for objects Use cacheRecordRecord() as skeleton for records Resolving Defaulting Logic Example: SalesTable.CashDisc void initInvoiceAccount() { CustTable custTable; ; custTable = this.custTable_InvoiceAccount(); this.NumberSequenceGroup this.Payment this.initFromPayment(); if (custTable.PaymSched) { this.PaymentSched } this.PaymMode this.PaymSpec this.CashDisc this.setListCode(); } = custTable.NumberSequenceGroup; = custTable.PaymTermId; = custTable.PaymSched; = custTable.PaymMode; = custTable.PaymSpec; = custTable.CashDisc; CashDisc is defaulted from CustTable Ax<Table> Defaulting Logic Example: AxSalesTable.setCashDisc() protected void setCashDisc() { if (this.isMethodExecuted(funcname(), fieldnum(SalesTable, CashDisc))) { return; Make sure the invoice account is set } this.setInvoiceAccount(); Default CashDisc from (cached) custome record associated with the invoice accou if (this.isFieldSet(fieldnum(SalesTable, InvoiceAccount))) { this.parmCashDisc(this.invoiceAccount_CustTableRecord().CashDisc); } } Calculated (Display) Fields Typically method on table parm<Field>() method on Ax<Table> class Always included in XML schema Always serialized Read-only Can not be mandatory Ax<Table> Class Performance Caching Frequently Used Data Avoid duplicate database lookups Cache table variables Cache Ax<Table> classes Axd Wizard generates code templates Copy for each table variable/Ax<Table> class Search/replace table variable/class name Use cache for data lookup Caching Table Variables Example: AxSalesLine public InventTable inventTableRecord(InventTable _inventTable = null) { InventTable inventTable; Insert in cache if (!inventTableIdx) { inventTableIdx = this.nextCacheIdx(); } if (!prmisdefault(_inventTable)) { this.tableCacheInsert(inventTableIdx, _inventTable); inventTable = _inventTable; } else { if (this.tableCacheExist(inventTableIdx)) { inventTable = this.tableCacheLookup(inventTableIdx); } else { this.setInventTableRecordFields(); inventTable = InventTable::find(this.parmItemId()); this.tableCacheInsert(inventTableIdx, inventTable); } } return inventTable; Lookup record in cache } Lookup record and insert in cache Using Cached Table Variables Example: AxSalesLine protected void setSalesUnit() { if (this.isMethodExecuted(funcname(), fieldnum(SalesLine, SalesUnit))) { return; } this.setInventTableRecordFields(); if (this.isInventTableRecordFieldsSet()) { this.parmSalesUnit(this.inventTableRecord().salesUnitId()); } } Lookup inventTable in cache Best Practices Don’t override AxdBase.getName() Be careful with inner joins Use FetchMode=1:n for outer joins Watch out for display fields Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics Enabling The Application Send Electronically Calling the AIF Send Service Using the Send Framework Integrating with exising business logic, e.g. posting routines ”Send Electronically” Button Example: VendPurchOrderJour form void clicked() { VendPurchOrderJour vendPurchOrderJourLocal; ; for (vendPurchOrderJourLocal = vendPurchOrderJour_ds.getFirst(true) ? vendPurchOrderJour_ds.getFirst(true) : vendPurchOrderJour; vendPurchOrderJourLocal; vendPurchOrderJourLocal = vendPurchOrderJour_ds.getNext()) { vendPurchOrderJourLocal.sendElectronically(XMLDocPurpose::Original); } super(); } Original Calling The AIF Send Service void sendElectronically(XMLDocPurpose _xMLDocPurpose, AifSendMode _aifSendMode = AifSendMode::Async) { AxdSendContext axdSendContext = AxdSendContext::construct(); AifEntityKey aifEntityKey = AifEntityKey::construct(); Map keyData; AifConstraintList aifConstraintList = new AifConstraintList(); AifConstraint aifConstraint = new AifConstraint(); ; keyData = SysDictTable::getKeyData(this); Create entity aifEntityKey.parmTableId(this.TableId); key aifEntityKey.parmRecId(this.RecId); Establish aifEntityKey.parmKeyDataMap(keyData); document axdSendContext.parmXMLDocPurpose(_xMLDocPurpose); context axdSendContext.parmSecurity(false); aifConstraint.parmType(AifConstraintType::Vendor) ; aifConstraint.parmId(this.OrderAccount) ; aifConstraintList.addConstraint(aifConstraint) ; AifSendService::submitDefault( classnum(AxdPurchaseRequisition), aifEntityKey, aifConstraintList, _aifSendMode, axdSendContext.pack()); } Identify constraint s Call AIF Send Service (”send and forget”) Send Framework Example: General Ledger > Chart of Accounts Select target endpoints Select which documents (here: accounts) to send Using the Send Framework Example: AxdSendChartofAccounts class AxdSendChartofAccounts extends AxdSend { } static public void main(Args args) { AxdSendChartofAccounts axdSendChartofAccounts ; AifConstraintList aifConstraintList; AifConstraint aifConstraint; ; axdSendChartofAccounts aifConstraintList aifConstraint = new AxdSendChartofAccounts(); = new AifConstraintList(); = new AifConstraint(); aifConstraint.parmType(AifConstraintType::NoConstraint); aifConstraintList.addConstraint(aifConstraint); axdSendChartofAccounts.parmShowDocPurpose(true) ; Configure form axdSendChartofAccounts.sendMultipleDocuments(classnum(AxdChartOfAccounts), AifSendMode::Async, aifConstraintList); } Invoke Send Sending As Part Of Posting Example: PurchFormLetter_PurchOrder void printJournal() { if (journalList.len() > 0) { if (printFormletter) { this.sendAsXML(); Send document before printing vendPurchOrderJour.printJournal(this, journalList); } if (this.proforma()) { this.removeProforma(); } } } Sending As Part Of Posting Example: PurchFormLetter_PurchOrder protected void sendAsXML() { boolean newLine; } if (new PrintJobSettings(printerSettingsFormletter, true).getTarget() != PrintMedium::Screen && !this.proforma()) { newLine = journalList.first(vendPurchOrderJour); while (newLine) { vendPurchOrderJour.sendElectronically(XMLDocPurpose::Original); newLine = journalList.next(vendPurchOrderJour); } } Same method as invoked from ”Send Electronically” Button Agenda Extensibility Model XML Document Structure Key Concepts Implementing New Documents Sending Documents Advanced Topics Mandatory Fields ... or Required Elements? Mandatory Field Field that must contain a value when a record is inserted into the Microsoft Dynamics AX database, e.g. SalesTable.SalesId Required Element Element required to be present in the XML document to satisfy the schema, e.g. <DeliveryDate> on a Sales Order Required Elements An XML element is required when Element is required by Axd<Document> class OR Field is mandatory on table AND Field value can not be defaulted Mandatory Fields Example: Sales Order SalesTable CurrencyCode CustAccount CustGroup InvoiceAccount LanguageId SalesId ShippingDateRequested SalesLine CurrencyCode CustAccount CustGroup ItemId ShippingDateRequested Defaulting Mandatory Fields Example: AxSalesTable protected void initMandatoryFieldsExemptionList() { Register field as not required super(); Override this.setParmMethodAsNotMandatory(methodstr(AxSalesTable, parmSalesId)); this.setParmMethodAsNotMandatory(methodstr(AxSalesTable, parmInvoiceAccount)); this.setParmMethodAsNotMandatory(methodstr(AxSalesTable, parmCustGroup)); this.setParmMethodAsNotMandatory(methodstr(AxSalesTable, parmLanguageId)); this.setParmMethodAsNotMandatory(methodstr(AxSalesTable, parmShippingDateRequested)); } Required By Axd<Document> Class Example: AxdSalesOrder protected void initMandatoryFieldsMap() { Register field as required super(); Override this.setParmMethodAsMandatory(classnum(AxSalesLine), methodstr(AxSalesLine,parmSalesQty)); this.setParmMethodAsMandatory(classnum(AxSalesLine), methodstr(AxSalesLine,parmSalesUnit)); this.setParmMethodAsMandatory(classnum(AxSalesTable), methodstr(AxSalesTable,parmPurchOrderFormNum)); this.setParmMethodAsMandatory(classnum(AxSalesTable), methodstr(AxSalesTable,parmDeliveryDate)); } Inventory Dimensions <SalesLine class=”entity”> ... <ItemId>CL-100B</ItemId> ... <InventDim class="entity"> <inventColorId>Gold</inventColorId> <inventSizeId>30</inventSizeId> </InventDim> ... </SalesLine> InventDim.inventDimId = 00003_060 AxInventDim_<ParentTable> Example: AxInventDim_SalesLine class AxInventDim_SalesLine extends AxInventDim { Class is specific AxSalesLine axSalesLine; SalesLine } to public static AxInventDim_SalesLine newAxSalesLine(AxSalesLine _axSalesLine) { AxInventDim_SalesLine axInventDim_SalesLine; ; axInventDim_SalesLine = new AxInventDim_SalesLine(); axInventDim_SalesLine.axSalesLine(_axSalesLine); return axInventDim_SalesLine; Register parent } SalesLine public void save() Update SalesLine instead { of saving InventDim ; record axSalesLine.updateFromInventDim(); } AxInventDim_<ParentTable> Example: AxSalesLine.updateFromInventDim public void updateFromInventDim() { ttsbegin; runState = AxBCRunState::Save; fieldModified = new Set(Types::Integer); this.initRecord(); this.inputStatus(InternalExternal::Internal); Invoke defaulting logic this.setInventDimId(); inventDimIdDirtySaved = false; SalesLine.InventDimId this.validateFields(); this.validateWrite(); Re-validate data Update SalesLine this.write(); SalesLine.InventDimId this.resetInternalValues(); ttscommit; } for Document References (Notes) Accounts Receivable > Setup > Forms > Form Setup Control whether document references are included in XML document Configuring Document References Example: AxdSalesOrder Disable data sources depending on value of ”Include documents on sheet” Select configured document type Allow only ”external” notes public void initQueryFromEntityKey(AifEntityKey _aifEntityKey, boolean _validateEntityKey) { super(_aifEntityKey, _validateEntityKey); this.docuRefEnabling(); } Disabling DocuRef Data Sources Example: AxdSalesOrder protected void docuRefEnabling() { CustFormLetterDocument custFormLetterDocument; void disableDocuRef(tableId _tableId) { ... } ; custFormLetterDocument = CustFormLetterDocument::find(); switch (custFormLetterDocument.DocuOnConfirm) { case DocuOnFormular::Head: disableDocuRef(tablenum(SalesLine)); break; case DocuOnFormular::Line: disableDocuRef(tablenum(SalesTable)); break; case DocuOnFormular::None: disableDocuRef(tablenum(SalesTable)); disableDocuRef(tablenum(SalesLine)); } ... } Selecting Correct DocuRef Type Example: AxdSalesOrder protected void docuRefEnabling() { CustFormLetterDocument custFormLetterDocument; void disableDocuRef(tableId _tableId) { ... } ; custFormLetterDocument = CustFormLetterDocument::find(); switch (custFormLetterDocument.DocuOnConfirm) { ... } query.dataSourceTable(tablenum(DocuRef),1). findRange(fieldnum(DocuRef,TypeId)). value(custFormLetterDocument.DocuTypeConfirm); query.dataSourceTable(tablenum(DocuRef),2). findRange(fieldnum(DocuRef,TypeId)). value(custFormLetterDocument.DocuTypeConfirm); } Allow Only External DocuRef Notes Example: AxdSalesOrder Only external notes Place restriction on data source in query Make sure the range is locked! Multiple Constraints EndpointA Microsoft Dynamics AX EndpointB PickingList1 [WH1,WH2] [WH1,WH2] PickingList2 [WH1] XML [WH1] EndpointC [WH2,WH3] EndpointD [WH1,WH2,WH4] EndpointE [No Constraints] Multiple Constraints Example: AxdPickingList class AxdPickingList extends AxdBase { AifConstraintList aifConstraintList; } public void getConstraintList(Common _common, AifConstraintList _constraintList) { int i ; int noOfConstraints ; ; noOfConstraints = aifConstraintList.getConstraintCount() ; for(i=1 ;i <= noOfConstraints; i++) { _constraintList.addConstraint(aifConstraintList.getConstraint(i); } Copy constraints identified } while processing document Multiple Constraints Example: AxdPickingList public void processingRecord(Common common) { AIFConstraint AIFConstraint ; Called for every data InventDim inventDim ; source ; if (common.TableId == tablenum(InventDim)) { inventDim = common ; AIFConstraint = new AIFConstraint(); if (inventDim.InventLocationId) { AIFConstraint.parmType(AifConstraintType::Warehouse) ; AIFConstraint.parmId(inventDim.InventLocationId) ; } else Identify constraint { AIFConstraint.parmType(AifConstraintType::NotSet ) ; } aifConstraintList.addConstraint(AIFConstraint) ; } Add constraint to list } Exposing Calculated Data Problem: The data to be read is not readily available in Microsoft Dynamics AX Solution Create Axd<Document> query with temporary table as data source Make Axd<Document> class generate data in temporary table Generating Data In Temporary Table Example: AxdPriceList class AxdPricelist extends AxdBase { ... PriceDiscTmpPrintout priceDiscTmpPrintout; } void prepareForQuery(QueryRun _queryRun) { ; Run before seriallization and after de-serialization select firstonly priceDiscTmpPrintout; if(!priceDiscTmpPrintout) this.insertPriceDiscTmpPrintout(); _queryRun.setCursor(priceDiscTmpPrintout); } Generate data ”Connect” query to temporary table Invoking Business Logic Problem Want to invoke business logic, such as a posting routine without reading or creating data Solution Create Axd<Document> class with no query Add properties to Axd<Document> class for holding results Add parm<Property>() method per property Override read() method to invoke business logic before serializing data Axd<Document> Class With No Query class AxdOperation extends AxdBase { str result; Property } will be serialized public Str parmResult(str _result = result) { ; result = _result; return result; } public QueryName getQueryName() { return ''; No query! } Invoking Business Logic public AifDocumentXml read(AifEntityKey _entityKey, AifSchemaInfo _xsdInfo, AifEndpointActionPolicyInfo _actionPolicyInfo, AifConstraintList _constraintList, AifPropertyBag _aifPropertyBag) { AifDocumentXml xml; ; // Call business logic here using data in _entityKey as // parameters. Save result in ’result’ property. xml = super(_entityKey, _xsdInfo, _actionPolicyInfo, _constraintList, _aifPropertyBag); return xml; } questions? © 2006 Microsoft Corporation. All rights reserved. This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.