Businesses experiencing serious growth need serious support. We know business and we know technology. We've got you!

E-ttach Development Guide


Code is highlighted below - the process flow outlines which object methods are called as part of the e-ttach process flow.

Extensible examples are real-world examples of how to add your own custom extensions .


E-ttach Object process flow 

Record Selected On Form

	​ TagGenerator_E2::InitializeCursorDocTags(record);

Check if “Dynamic Tag Generation” is enabled in the parameters, if disabled then stop process. If enabled perform the following:

Create any missing TagRef_E2 records (where docuRef exists but TagRef_E2 does not).

	TagGenerator_E2.createRelations();

 Create TagRef_E2 records from related records where they do not already exist.

     TagGenerator_E2.copyTagRelations();

 

Attachment Dragged to Record

	DragDropControl_E2.attachDocument(Stream, filename, controlname, uniqueid)     

Create Docuref record (extensible): 

	​DragDropControl_E2.createDocuRefFromCommon(record, filename);

To add tags to existing docuRef records you can do the following:

	​TagDefinition_E2 tagDefinition = TagDefinition_E2::createTagFromRecord(tagcursor);
	​TagRef_E2::LinkTagRefToRecord(tagDefinition.RecId, docuRef, true);

Where tagCursor = the related record you want to link the docuRef record to (e.g InventTable::find(“item001”)). DocuRef is the originating docuRefRecord. 

Note: this Is automatically done via the TagGenerator_E2 class on any record an attachment is added to.


Code Examples

DragDropControl_E2 class methods


class DragDropControl_E2 
{
​public DocuRef createDocuRefFromCommon(Common record, str _fileName = "")
    {
        DocuRef docuRef, existDefault;   
 
        this.initDocuType();
       docuRef.clear();
        docuRef.RefTableId = record.TableId;
        docuRef.RefRecId = record.RecId;
        docuRef.RefCompanyId = record.dataAreaId;
        docuRef.Name = _fileName;

        select RecId from existDefault
            where existDefault.RefTableId == docuRef.reftableid &&
            existDefault.RefRecId == docuRef.refRecId &&
            existDefault.RefCompanyId == docuRef.RefCompanyId &&
      ​ existDefault.DefaultAttachment;
        if(!existDefault)
            docuRef.DefaultAttachment = NoYes::Yes;
        docuRef.TypeId = docuType;
        // Find document type by table / module
        DocuTypeRelation_E2 docuRelation = DocuTypeRelation_E2::findFromCursor(record);
​if(docuRelation && docuRelation.DocuTypeId)
​        docuRef.TypeId = docuRelation.DocuTypeId;
       
​ docuRef.TypeId = this.overrideDocuType(docuRef.TypeId);             
        docuRef.insert(); 
        return docuRef;
​}
​ public docuTypeId overrideDocuType(DocuTypeId _docuTypeId)
​{
return _docuTypeId;
​}
}

 

TagGenerator_E2 Class methods


class TagGenerator_E2
{
  public void createRelations()
    {
        this.createBaseRecordRelation(tagDefinition.getCursor());
    }
  private void createBaseRecordRelation(Common _cursor)
    {
        // Do not execute if disabled
        if(DragDropHelper_E2::isdisabled())
            return; 
        str tagName = TagDefinition_E2::getfieldIdentifier1(_cursor);
        TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_cursor,tagName);  
        TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
    } 
  public void createCustTableRelations(CustTable _custTable)
   {
       TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_custTable);
 
       TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
   }
 
   public void createVendTableRelations(VendTable _vendTable)
   {
       TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_vendTable);
 
       TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
   }
   public void createProjTableRelations(ProjTable _projTable)
   {
       TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_projTable);
 
       TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
   }
  public void createInventTableRelations(InventTable _inventTable)
   {
       TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_inventTable);
 
       TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
   }
 
   public void createEcoResProductRelations(EcoResProduct _ecoresProduct)
   {
       TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_ecoresProduct);
 
       TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);   
}
  // This Method can be extended to add tag propagation rules 
    public static boolean shouldPropagateTag(Common _from, Common _to, boolean _docuCopy)
    {
       // If feature setup disables table string then do not copy ->
       if(_from.TableId && !DragDropHelper_E2::checkFeatureStr(tableId2Name(_from.TableId)))
           return false;           
       if(_to.TableId && !DragDropHelper_E2::checkFeatureStr(tableId2Name(_to.TableId)))
           return false;
       // <- If feature setup disables table string then do not copy 
       return !_docuCopy;
    } }

When an attachment is added it calls the “createRelations” method and you can check the table as is shown below.

TagGenerator_Sales_E2_Extension class

This is the e-ttach sales order class – where it creates attachment tags from salesLine attachments to the salesTable record, and then on to the custTable record:

[ExtensionOf(classStr(TagGenerator_E2))]
final class TagGenerator_Sales_E2_Extension
{
    public void createRelations()
    {
        next createRelations();        
        switch(tagDefinition.refTableId)
        {       
            case tableNum(SalesLine) :
                this.createSalesLineRelations(tagDefinition.getCursor());
                break;
            case tableNum(SalesTable) :
                this.createSalesTableRelations(tagDefinition.getCursor());
                break;
        }
    } 
    public void copyTagRelations()
    {
        next copyTagRelations();
 
        switch(cursor.tableid)
        {                  
            case tableNum(CustTrans) :
                this.copyToCustTrans(cursor);
                break;
            case tableNum(salesTable) :
                break;
        }      
    } 
    public void createSalesLineRelations(SalesLine _salesLine)
    {
        SalesTable salesTable = salesTable::find(_salesLine.SalesId);
       
        if(TagGenerator_E2::shouldPropagateTag(_salesLine,salesTable,isDocuCopy))
            this.createSalesTableRelations(salesTable); 
    }
    public void createSalesTableRelations(SalesTable _salesTable)
    {
        TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_salesTable);
 
        TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor); 

        if(TagGenerator_E2::shouldPropagateTag(_salesTable,_salesTable.custTable_CustAccount(),isDocuCopy))
            this.createCustTableRelations(_salesTable.custTable_CustAccount());
        if(TagGenerator_E2::shouldPropagateTag(_salesTable,_salesTable.custTable_InvoiceAccount(),isDocuCopy))
            this.createCustTableRelations(_salesTable.custTable_InvoiceAccount());
    } 
    public void copyToCustTrans(CustTrans _custTrans)
    {
        CustInvoiceJour cij = _custTrans.custInvoiceJour_RU(); 

        TagRef_E2::copyTags(cij,_custTrans); 
    } 
}

Note: the copyTagRelations method is called when a record is selected on a form, it then checks to see if it should create any missing tags from related records (in this case customer invoice attachments Tags are copied to the customer transaction record if they are missing.)


TagGenerator_LedgerJournal_E2_Extension class   

This is the LedgerJournal e-ttach class, which propagates the Tags from the journal Line to the Journal Header and to the related account / offset account (Customer, Vendor and Project):

[ExtensionOf(classStr(TagGenerator_E2))]
final class TagGenerator_LedgerJournal_E2_Extension
{
    public void createRelations()
    {
        next createRelations();
        
        switch(tagDefinition.refTableId)
       {       
            case tableNum(LedgerJournalTrans) :
                this.createJournalLineRelations(tagDefinition.getCursor());
                break;
            case tableNum(LedgerJournalTable) :
                this.createJournalTableRelations(tagDefinition.getCursor());
                break;
        }
    } 
    public void copyTagRelations()
    {
        next copyTagRelations();      
    } 
    public void createJournalLineRelations(LedgerJournalTrans _journalTrans)
    {
        LedgerJournalTable journalTable = _journalTrans.ledgerJournalTable();
        if(TagGenerator_E2::shouldPropagateTag(_journalTrans,journalTable,isDocuCopy))
            this.createJournalTableRelations(journalTable); 
        this.createJournalAccountRelation(_journalTrans.AccountType,_journalTrans.parmAccount(), _journalTrans);
        this.createJournalAccountRelation(_journalTrans.OffsetAccountType, _journalTrans.parmOffsetAccount(),_journalTrans);
​} 
    public void createJournalAccountRelation(LedgerJournalACType _accountType, LedgerJournalAC _accountNum, ledgerJournalTrans _ljt)
    {       
        switch(_accountType)
        {
            case LedgerJournalACType::Cust:
                CustTable custTable = CustTable::find(_accountNum);
                if(TagGenerator_E2::shouldPropagateTag(_ljt,custTable,isDocuCopy))
                    this.createCustTableRelations(custTable);
                break;
            case LedgerJournalACType::Vend :
                vendTable vendTable = vendTable::find(_accountNum);
                if(TagGenerator_E2::shouldPropagateTag(_ljt,vendTable,isDocuCopy))
                    this.createVendTableRelations(vendTable);
                break;
            case LedgerJournalACType::Project :
                ProjTable projTable = ProjTable::find(_accountNum);
                LedgerJournalTrans_Project ljtProject = LedgerJournalTrans_Project::find(_ljt.recid);
                if(ljtProject)
                {
                    ProjTable = ProjTable::find(ljtProject.projId);                  
                }
                if(TagGenerator_E2::shouldPropagateTag(_ljt,projTable,isDocuCopy))
                    this.createProjTableRelations(ProjTable);
                break;
        }
    } 
    public void createJournalTableRelations(LedgerJournalTable _journalTable)
    {
        TagDefinition_E2 locTagDef = TagDefinition_E2::createTagFromRecord(_journalTable);
 
        TagRef_E2::LinkTagRefToRecord(locTagDef.recid,cursor);
    } 
}

Note how the method shouldPropagateTag is called for each table, this enables custom logic to be added to check the from and to record to determine if the tag should be copied.

 

Extending Objects

Even though the code is not available in Visual studio for the runtime package/model. You can still extend our objects.

From the AOT you can right click on the form or class and create an extension.

Then to create a class extension you will need to manually create a class.

e.g this is an extension class for the DragDropFormPart_E2 form:

[ExtensionOf(formStr(DragDropFormPart_E2))]
final class DragDropFormPart_E2_ettach_Extension
{
    public DocuTypeId selecteddocuType;
    public edit DocuTypeId editDocuType(boolean _set, DocuTypeId _docuType)
    {
        if(_set)
        {           
​ selecteddocuType = _docuType;
        }
        return selecteddocuType;
    }    
​[FormControlEventHandler(formControlStr(DragDropFormPart_E2, DocumentType), FormControlEventType::Modified)]
    public void DocumentType_OnModified(FormControl sender, FormControlEventArgs e)
    {       
​FormStringControl fsc = sender as FormStringControl;       
​this.selecteddocuType = fsc.text();
    } 
    public docuTypeId getDocuType()
    {
        DocuTypeId _curType = selecteddocuType;
        return selecteddocuType;
    } 
    public void init()
    {
        next init(); 
           
​if(DragDropParameters_E2::find().DocumentTypeSelection &&
​ SysUserInfo::find().DocumentTypeSelection_E2 != DragDropExpanded_E2::No)           
​{               
​FormControl fc =  this.design().controlName("DocumentType");               
​if(fc)                   
​ fc.visible(true);               

​Common_ds.allowEdit(true);           
​}       
    } 
}

The form has had an extension created and a new field added called “DocumentType” which uses an edit method (editDocuType):

So in the class above in the init method it sets the field visible based on the parameter setup and when that field is modified it stores the selecteddocutype which is then called by another extension class which overrides the docutype on creation of the docuRef Record:

[ExtensionOf(classStr(DragDropControl_E2))]
final class DragDropControl_E2_ettach_Extension
{
    public edit DocuTypeId editDocuType(boolean _set, DocuTypeId _docuType)
    {
        if(_set)
        {           
​ this.docuType = _docuType;
        }
        return this.docuType;
    } 
    public DocuRef createDocuRefFromCommon(Common record, str _fileName)
    {
        FormRun curFormRun = this.formRun();       

        if(curFormRun.name() == formStr(DragDropFormPart_E2))
        {           
​ Object caller = curFormRun;
           this.docuType = caller.getDocuType();
        }
        DocuRef DocuRef = next createDocuRefFromCommon(record,_fileName);
        return DocuRef;
    } 
    public docuTypeId overrideDocuType(DocuTypeId _docuTypeId)
    { 
        DocuTypeId docuTypeId = next overrideDocuType(_docuTypeId); 
        FormRun curFormRun = this.formRun();       
        if(curFormRun.name() == formStr(DragDropFormPart_E2))
        {           
​ Object caller = curFormRun;
           this.docuType = caller.getDocuType();            
​ if(this.docuType)               
​ docuTypeId = this.docuType;
        }
        return docuTypeId;
    } 
}

For the Record notes form, there is an extensible method that allows development to populate additional fields on docuRef. This method is on the RecordNotesFormPart_E2 form that you can use in an extension class like the above called “initDocuRefFromForm”.

You can create a class extension like this:

[ExtensionOf(formStr(RecordNotesFormPart_E2))]
final class RecordNotesFormPart_E2_Elysian2ettach_Extension
{
    public str documentToAddTo; 
    public str editDocument(boolean _set, str _documentName)
    {
        if(_set)           
​documentToAddTo = _documentName; 
        return documentToAddTo;
    }
    public void initDocuRefFromForm(DocuRef _docuRef, Common _selectedRecord)
    { 
        // Set your docuref record values here.       
​ _docuRef.documentToAdd = documentToAddTo;
        // CoC next call
        next initDocuRefFromForm(_docuRef,_selectedRecord);               
    }
}

That will populate a new field called documentToAdd on docuRef with the value stored in the documentToAddTo variable.