jueves, 17 de mayo de 2018

DATE TIME NULL



There is a function in D365FO that returns a data type date and time null. (UTCDateTime type)

·        utcdatetimenull() 

The value that returns this function also corresponds to executing the following expression: 

·         DateTimeUtil::minValue().

The return  value is '1900-01-01T00:00:00'. 

lunes, 8 de enero de 2018

DEPLOY REPORTS - SSRS REPORTS



·         Open windows POWERSHELL in administrator mode.

·         Set-ExecutionsPolicy Unrestricted: YES

·         Local environment:
C:\Packages\Plugins\AxReportStartVmRoleStartupTask\DeployAllReportsToSSRS.ps1

·         Azure environment:
C:\AosService\PackagesLocalDirectory\Plugins\AxReportVmRoleStartupTask\DeployAllReportsToSSRS.ps1 -PackageInstallLocation "C:\AosService\PackagesLocalDirectory"





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