viernes, 3 de noviembre de 2017

D365FO- IMPORT FILE TO TABLE


This post shows you how to import data from a fixed width text file and insert records in a table.

From the previous post [D365 - UPLOAD FILE], we obtain the text file to be treated. Once we have access to the text file, we can include your data in a table, as we show below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    void importFile2Table(FileUploadTemporaryStorageResult _result)
    {
        
        str fileUrl;

        if (_result && _result.getUploadStatus())
        {
            fileUrl = _result.getDownloadUrl();

            using(System.IO.MemoryStream stream = _result.openResult() as System.IO.MemoryStream)
            using(System.IO.StreamReader sreader = new System.IO.StreamReader(stream, System.Text.Encoding::UTF8, true))
            {
               
               this.readAndInsertFile(sreader, _result.getFileName());
                
            }
            
        }
        
        
        info(strFmt("@SPBLabels:PWFIN_0618_028", _result.getFileName()));
    }

    void readAndInsertFile(System.IO.StreamReader _sreader, fileName _fileName)
    {
       
        str  lineData;
        int  lineNum;

        while (!_sreader.EndOfStream)
        {
           
            lineData =  _sreader.ReadLine();
            lineNum++;
                      
            this.insertLines(lineData);
        }
            
      
    }

    void insertLines(str _lineData)
    {
        SPBTestImportTable            spbTestImportTable;
        
        spbTestImportTable.DueDate         = str2Date(subStr(_lineData, 7,8), 321);
        spbTestImportTable.TransDate       = str2Date(subStr(_lineData, 24,8), 321);
        spbTestImportTable.Time            = str2Time(subStr(_lineData, 32,4));
        spbTestImportTable.Transmitter     = subStr(_lineData, 4,13);
        spbTestImportTable.SDueDate      = str2Date(subStr(_lineData, 17,8), 321);
        spbTestImportTable.Transmitter2    = subStr(_lineData, 34,13);
        spbTestImportTable.Observations    = subStr(_lineData, 47,82);
        spbTestImportTable.Transmitter  = subStr(_lineData, 4,13);
        
        spbTestImportTable.insert();

        
    }

lunes, 30 de octubre de 2017

D365 - UPLOAD FILE

In D365FO the file import utilities through the methods of the WINAPI classes have been deprecated.


In this new version based on Cloud, we can use temporary URLS to help us perform these loads.

An example of import is the following: 

I worked from a RunBase class, in which I used its "Dialog" method to offer the user an interface in which to upload the file to be treated:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class SPBImportFile extends RunBaseBatch
{
    private DialogRunbase       myDialog;
    private DueDate             dueDate;
    private DialogField         dialogDueDate;
    
        
    private const str FileUploadName = 'FileUpload';
    private const str OkButtonName = 'OkButton';

    public Object dialog()
    {
        VendParameters vendParameters = VendParameters::find();

        eMDdialog = new DialogRunbase("@SPBLabels:EMDImportation", this);
        var dialogGroupParameters = myDialog.addGroup("Parametros");
        dialogGroupParameters.columns(2);

        dialogDueDate = myDialog.addFieldValue(extendedTypeStr(DueDate), dueDate);
       
        var dialogGroup = myDialog.addGroup();
        FormBuildControl formBuildControl = myDialog.formBuildDesign().control(dialogGroup.name());
        
        FileUploadBuild  dialogFileUpload = formBuildControl.addControlEx(classstr(FileUpload), FileUploadName);
        dialogFileUpload.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
        dialogFileUpload.fileNameLabel("@SYS308842");
        
        return myDialog;
    }
}

I used the 'DialogPostRun' method to manage the controls and enable the accept button in the dialog


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
   public void dialogPostRun(DialogRunbase _dialog)
    {
        super(_dialog);
        FileUpload fileUpload = this.getFormControl(_dialog, FileUploadName);
        fileUpload.notifyUploadCompleted += eventhandler(this.uploadCompleted);
        this.setDialogOkButtonEnabled(_dialog, false);
    }

    protected FormControl getFormControl(DialogRunbase _dialog, str _controlName)
    {
        return _dialog.formRun().control(_dialog.formRun().controlId( _controlName));
    }

    private void setDialogOkButtonEnabled(DialogRunbase _dialog, boolean _isEnabled)
    {
        FormControl okButtonControl = this.getFormControl(_dialog, OkButtonName);

        if (okButtonControl)
        {
            okButtonControl.enabled(_isEnabled);
        }
    }

And in the run method we perform the procedure to process the imported data. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public void run()
    {
        
        try
        {
            ttsbegin;
            FileUpload fileUploadControl = this.getFormControl(eMDdialog, FileUploadName);
            FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult() as FileUploadTemporaryStorageResult;

            if (fileUploadResult != null && fileUploadResult.getUploadStatus())
            {
               //DO THE ACTION
            
            
            }
            ttscommit;
        }
        catch (Exception::Deadlock)
        {
            retry;
        }
    }

}

miércoles, 25 de octubre de 2017

D365 - QUERIES: COPY RANGES AND FILTERS


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public static void copyRangeFilters(Query _originQuery, Query _destinyQuery, QueryBuildDataSource _originDataSource, QueryBuildDataSource _destinyDataSource, fieldName _ledgerDimensionFieldName = '')
    
{
        
    QueryBuildRange     queryBuildRangeOrigin;
    QueryFilter         originQueryFilter;

    QueryBuildRange     queryBuildRangeAdded;
    QueryFilter         newFilter;
        
    container          dimensionCriteria;
    List               dimensionRanges;
    ListEnumerator     listEnumerator;

    int                 i;
        
    DimensionProvider dimensionProvider = new DimensionProvider();
        
    if (!_originDataSource || !_destinyDataSource || !_originQuery || !_destinyQuery ||  (_originDataSource.file() != _destinyDataSource.file()))
        
    {
        return;
            
    }
        
    // Ranges
    for (i = 1; i <= _originDataSource.rangeCount(); i++)
        
    {
        queryBuildRangeOrigin = _originDataSource.range(i);
        queryBuildRangeAdded  = _destinyDataSource.addRange(queryBuildRangeOrigin.field());
        queryBuildRangeAdded.status(queryBuildRangeOrigin.status());
        queryBuildRangeAdded.value(queryBuildRangeOrigin.value());
            
    }
        
    // Filters
    for (i = 1; i <= _originQuery.queryFilterCount(); i++)
        
    {
        originQueryFilter = _originQuery.queryFilter(i);
            
        if ((originQueryFilter.DataSource().file() == _destinyDataSource.file()) && (originQueryFilter.value() != ''))
            
        {
            newFilter = _destinyQuery.addQueryFilter(_destinyDataSource, originQueryFilter.field());
            newFilter.value(originQueryFilter.value());
                
        }
            
    }
        
    if (_ledgerDimensionFieldName)
        
    {
        // dimension ranges
        dimensionRanges = dimensionProvider.getDimensionRangesFromQuery(_originQuery);
            
        if (!dimensionRanges.empty())
            
        {
            listEnumerator = dimensionRanges.getEnumerator();
                
            while (listEnumerator.moveNext())
                
            {
                dimensionCriteria = listEnumerator.current();
                    
                if (strContains(conpeek(dimensionCriteria, DataSourceNamePosition), _originDataSource.name()))
                    
                {
                    dimensionProvider.addAttributeRangeToQueryFromRangeCon( _destinyQuery,
                                                                            _destinyDataSource.name(),
                                                                            _ledgerDimensionFieldName,
                                                                            dimensionCriteria);
                        
                }
                    
            }
                
        }
            
    }
        
}

lunes, 23 de octubre de 2017

D365 - IMAGE ON A GRID (and list of symbols)



We will use an extended data type 'ImageReference' to reference the image. And we will build it as follows: 

1
2
3
ImageReference image;

image = imageReference::constructForSymbol(ImageReferenceSymbol::Accept);

'ImageReferenceSymbol:: ' Accepts many symbols. You can find a list of symbols on this link.

We will need to return the image in a container, constructing a packaging of the class:

1
2
3
4
Container imageContainer;

imageContainer = image.pack();
return imageContainer;

 Now we need to use a display method in the same way as in the previous entry [DISPLAY METHOD ON TABLE EXTENSION], we proceed as follows:


Create a new class that contains the display method, with the following source code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static class NewVendTrans_Extension
{
    [SysClientCacheDataMethodAttribute(true)]
    Public static display container displayTransMarkedByOtherSpecConciled(VendTrans _vendTrans)

    {
        vendTRansOpen       vendTransOpen;
        ImageReference      image;
        container           imageContainer;
       
        if(!vendTransOpen::findRefId(_vendTrans.recId).recId )
        {
            image          = ImageReference::constructForSymbol(ImageReferenceSymbol::Error);
            imageContainer = image.pack();
                
        }
        else
        {
            image = ImageReference::constructForSymbol(ImageReferenceSymbol::Assign);
            imageContainer = image.pack();
               
        }
        
        
        return  return imageContainer;
    }
}

Insert in the Grid an image type reference and assign the new generated display:


You can find a list of symbols on this link.

(Thanks Ivan for the images link). 

D365 - DISPLAY METHOD ON TABLE EXTENSION

Create a new class that contains the display method: 


      [Using 'SysClientCacheDataMethodAttribute' attribute in the display method declaration, the display method will be automatically cached on any form where it’s used]  

    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static class VendTransOpen_Extension
{
   
    [SysClientCacheDataMethodAttribute(true)]
    Public static display amountMst displayAmountMstReverse(VendTransOpen _vendTransOpen)

    {
       Return _vendTRansOpen.amountMst * -1; 
    }
}


On the form extension, create a new field with ‘Data Method’ property like: VendTransOpen_Extension: displayAmountMstReverse.



Set on the field the property ‘Cache Data Method’: yes if you don’t want to use the Attribute above


viernes, 20 de octubre de 2017

D365 - DEBUG IN D365FO



  • Start Visual Studio (Run as administrator).
  • From the toolbar: go to ‘Debug’/ ‘Attach to process...’




  • Mark ‘Show processes from all users’  In the ‘Attach to process’ screen, and then select
     ‘w3wp.exe’ process


D365 - EVENT HANDLER TABLE WITH ARGS


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 class CustInvoiceJour_LedgerExtension_TableHandler
{
    
    [PostHandlerFor(tableStr(CustInvoiceJour), tableMethodStr(CustInvoiceJour, initFromCustInvoiceTable))]
    public static void CustInvoiceJour_Post_initFromCustInvoiceTable(XppPrePostArgs args)
    {
        CustInvoiceJour     custInvoiceJour     = args.getThis() as CustInvoiceJour;
        CustInvoiceTable    custInvoiceTable    = args.getArg('_custInvoiceTable') as CustInvoiceTable ;

        custInvoiceJour.Factoring = custInvoiceTable.Factoring;
    }

}

D365 - LOOKUP ON DIALOG

In the ‘DIALOG’ method, we include a new field of the desired type on which we will do the lookup and we override its lookup method as follows:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public Object dialog()
{
   DialogRunbase   dialog = super();

  
   dlgOperationalPoint = dialog.addFieldValue(extendedTypeStr(PhysicalOperationalPoint),operationalPoint);
   dlgOperationalPoint.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(MyUIBuilderClass, OperationalPointLookup), this);
        
   return dialog;
}


In a new class or an existing class, we create a new method that references the Lookup and performs the necessary search:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 private void OperationalPointLookup(FormControl _formControl)
 {
        
    SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(table), _formControl);
       
    Query query = new Query();
    QueryBuildDataSource queryBuildDataSource = query.addDataSource(tableNum(table));


    queryBuildDataSource.addRange(fieldNum(table, fieldNum)).value(this.parmFieldValue());
    queryBuildDataSource.addRange(fieldNum(table, VendAccount)).value(this.parmVendAccount());

    queryBuildDataSource.addOrderByField(fieldNum(table, PhysicalOperationalPoint));

    queryBuildDataSource.orderMode(OrderMode::GroupBy);

    sysTableLookup.addLookupfield(fieldNum(table, PhysicalOperationalPoint));

    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();
 }

miércoles, 18 de octubre de 2017

D365 -DATA EVENTHANDLER ON TABLE VALIDATIONS


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class FactoringManagement_LedgerExtension_TableHander
{
    [DataEventHandler(tableStr(FactoringManagement), DataEventType::ValidatedField)]
    public static void FactoringManagement_onValidatedField(Common sender, DataEventArgs e)
    {
        ValidateFieldEventArgs   validateFieldEventArgs   = e as ValidateFieldEventArgs;
        FactoringManagement      factoringManagement      = sender as FactoringManagement;
 
        boolean result; 
       
        switch (validateFieldEventArgs.parmFieldId())
        {
            case fieldNum(FactoringManagement, PercentMax) :

                if (pwFactoringManagement.PercentMax > 100)
                {
                    result = validateFieldEventArgs.parmValidateResult();
                    result = checkFailed(strfmt("Factoring percentage cannot be greater than 100"));                    
                    validateFieldEventArgs.parmValidateResult(result);
                }
            
             break;
        }
       
    }

}

D365 -CHANGE CURRENCY


1
CurrencyExchangeHelper::curAmount2CurAmount(Amount, CompanyInfoHelper::standardCurrency(),  CurrencyCodeDestiny, SystemDateGet());