Showing posts with label Excel. Show all posts
Showing posts with label Excel. Show all posts

Thursday, April 20, 2023

X++ to run XIRR

I created a small X++ wrapper to call XIRR function from the C# project provided by https://github.com/klearlending/XIRR (Thank you!) 

You can test XIRR function in Excel, btw.

using tmxExcelFinance;
/// <summary>
/// Implements various financial functions
/// </summary>
public final class myFinanceFunction
{
    /// <summary>
    /// Calculates XIRR value
    /// </summary>
    /// <param name = "_cashFlows">List of containers kind [cashflow amount, its date]</param>
    /// <param name = "_decimals">how many decimals to calculate</param>
    /// <param name = "_maxRate">Maximum rate</param>
    /// <returns>XIRR value</returns>
    public static real calculateXIRR(List _cashFlows, int _decimals, real _maxRate = 1000000)
    { 
         
        var                                 cashFlows       = new System.Collections.Generic.List<CashFlowDates>();
        System.Collections.IEnumerable      cashFlowsI;
        ListEnumerator                      leC             = _cashFlows.getEnumerator();
        System.Double                       dbl;
        System.DateTime                     dt;

        while(leC.moveNext())
        {
            [dbl, dt] = leC.current();
            cashFlows.Add(new CashFlowDates(dbl, dt));
        }
        // convert to iterable
        cashFlowsI = cashFlows;

        return XIRR::Calc(cashFlowsI, _decimals, _maxRate);
    }

}

This is how we can call the wrapper

 public static void calculateXIRRFromInvestmentDetailsLineTmp(RefRecId _projTableRecId, myInvestmentDetailsLineTmp _myInvestmentDetailsLineTmp)
    {
        List listCashFlowsDates = new List(Types::Container);

        myInvestmentDetailsLineTmp myInvestmentDetailsLineTmpLocal;

        myInvestmentDetailsLineTmpLocal.linkPhysicalTableInstance(_myInvestmentDetailsLineTmp);

        while select myInvestmentDetailsLineTmpLocal
        {
            listCashFlowsDates.addEnd([myInvestmentDetailsLineTmpLocal.TransactionCurrencyAmount, DateTimeUtil::newDateTime(myInvestmentDetailsLineTmpLocal.LineDate, 0)]);
        }
        myProjXIRRTable.XIRRValue = myFinanceFunction::calculateXIRR(listCashFlowsDates, 4);
...

Or

 cashFlows1 = new List (Types::Container);
        
        cashFlows1.addEnd([-10000,str2Date("01/01/2008", 123)]);
        cashFlows1.addEnd([2750, str2Date("01/03/2008", 123)]);
        cashFlows1.addEnd([4250, str2Date("30/10/2008", 123)]);
        cashFlows1.addEnd([3250, str2Date("15/02/2009", 123)]);
        cashFlows1.addEnd([2750, str2Date("01/04/2009", 123)]);

res = myFinanceFunction::calculateXIRR(cashFlows1, 10);

Notes about the solution creation.



D365FO project must be <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>


Last remark. I tried to implemented the same algorithm in X++, but I bumped into two limitations.

First, there is a limit for recursion depth - 400 levels; we can flatten it, though. 

Second, real loses required decimals and ends up with division by zero.

Wednesday, March 6, 2019

Extended version of Universal Field Changer for Microsoft Dynamics AX2012

Oh yeah! The Field changer is still on the road! Now equipped with two updating options:


Feel free to use this surgeon's tool at your own risk! Grab it from here.

Saturday, December 9, 2017

EDT and tables wizard for AX 2012

AX 2012 Wizard allows to create new Extended data types and new tables with relations, delete actions, indexes and find methods, based on a simple Excel file. Two sample Excel files are included in the zip-package.

Just import two classes (ADO class is to support Excel import).

You can set up new types (if needed) and new tables with all bells and whistles, then run the Wizard and can go fishing until it does its job.

Happy fishing!




It creates:
first
- all new types based on basic types; (ignores if exists)
- all new labels for US-EN and FR-CA or finds existing one for US-EN;
second
- new tables;
- new fields;
- new field group with all new fields and the same label as for table;
- new index based on the first field and set is as cluster index;
- normal relation to the main table if given (one only);
- cascade delete action for all new relations from subordinated tables;
- method find based on the first field.


If it fails to open Excel file, check your currently installed Excel version and adapt the connection string.




Wednesday, October 7, 2015

DIXF setup notes


File name
That was a bad idea to name an entity with characters forbidden by Windows for filenames...


It raises an error while trying to Generate source file.


 Excel version

To work well with Excel we need to have ODBC Excel 64 bit driver installed on the server


cmd (as administrator)

AccessDatabaseEngine_x64 /passive

Excel sheet name

And not to forget to add $ at the end of lookup sheet name.




Yes, $$$ rule as usual…

Tuesday, March 20, 2012

COMVariant type to work with Excel in AX

To avoid eventual issues while working with Excel objects in X++, do not forget about COMVariant type in AX!

Lets say we output some information directly to an Excel file and want to change the current row height based on the number of strings on notes.


...
COM             rng;
COMVariant      rowHeightVariant;
real            rowHeight;
...

rng = wks.Range(strFmt("A%1", curRow));   cell.Value2(_route.OprNum);
// to keep the current row height
rowHeightVariant = rng.RowHeight();
rowHeight = rowHeightVariant.double();
...
//to adjust the row height accordingly to the number of strings in notes
rng.RowHeight(rowHeight*this.countLines(notes));
...


Unfortunately, I had spent so much time trying to understand the reason of errors until I found this posting. Thank you Max!

Tuesday, January 17, 2012

Universal Field Changer new version for Microsoft Dynamics AX2012

So, I took my old project from the case just to add a new feature: this time I would like to get all the table fields with their labels in the user's language.



Frankly seaking I was going to get it in a grid in order to export to Excel; but unfortunately I did not find a fast way to show my temporary table. Finally, I just use InfoLog in the comma separated format that can be used lately to open in Excel.



Enjoy, anyway!

Description:

Universal Field Changer class for Microsoft Dynamics AX2012:

- collects all the fields from all the tables in AOT in temporary tables;
- makes possible to change any values using filtres by table and field names and existing values;
- provides access to SQL query string;
- prints the field lists with labels in user's language;
- creates dynamically all the form controls and uses method overloading and can be used as a tutorial;

Tuesday, March 16, 2010

All Lables from given Layer in given Languages

Sometimes I need to see a label translations into different languages. Sometimes it is good to see all the labels that were created during the project on a given layer - like those on USR.

This job exports all labels from a given layer in given languages to an Excel file.

Here I used the two following code examples from the AXForum.info:

Firstly, the job calculates the number of labels; then, it shows progress window; finally, it opens an Excel file saved under the name like "Labels from "+#LayerId+ " in "+#English+", "+#French+", "+#Russian+".xls".

In this particular job are SYS layer and three mentioned languages used; you can easily change them to your needs.



static void SisPrintAllLabelsOnGivenLanguages(Args _args)
{
    // needed layer
    #define.LayerId("SIS")
    // all needed languages
    #define.English("en-us")
    #define.French("fr-ca")
    #define.Russian("ru")
    // label classes
    Label   lEn = new Label(#English);
    Label   lFr = new Label(#French);
    Label   lRu = new Label(#Russian);
    // start looking every label
    str 250 lId;
    int totalLabels;
    RunbaseProgress         progress;
    #macrolib.AviFiles
    //Excel variables section
    COM rstAxa = new COM('ADODB.Recordset');    // ADO: Recordset
    COM flds   = rstAxa.Fields();
    COM fld;
    #define.LabelId("LabelId")
    COM xlApp;            
    COM wbks, wbk;        
    COM wkss, wks;        
    COM rng, cell, rngCR; 
    COM font;             
    COM entCol;           
    COM actWin;           
    int i, iMax;
    ;
    print("@SYS34745");
    // start looking every label
    lId = lEn.searchFirst('');
    while (lId)
    {
        if (lEn.moduleId(lId) == #LayerId) // The particular label file
        {
            // count labels
            totalLabels++;
        }
        lId = lEn.searchNext();
    }
    print(strfmt("%1 = %2", "@SYS54695", totalLabels));
    print("@SYS76178");
    if (totalLabels<=0)
        return;

    progress = RunbaseProgress::construct(1,null);

    progress.setCaption("@SYS76178");
    progress.setTotal(totalLabels);
    progress.setAnimation(#AviPrint);

    // <--- create excel fields in a worksheet
    flds.Append(#LabelId, 8);
    flds.Append(#English, 8);
    flds.Append(#French, 8);
    flds.Append(#Russian, 8);
    rstAxa.Open();

    xlApp = new COM('Excel.Application');
    xlApp.Visible(false);
    wbks = xlApp.Workbooks();
    wbk  = wbks.Add();
    wkss = wbk.Worksheets();
    wks  = wkss.Item(1);
    wks.Name("Labels");
    rng  = wks.Range('A1');
    flds = rstAxa.Fields();
    iMax = flds.Count() - 1;
    for (i = 0; i <= iMax; i += 1)
    {
        fld = flds.Item(i);
        cell = rng.Offset(0, i);
        cell.Value2(fld.Name());
    }
    rngCR = rng.CurrentRegion();
    font = rngCR.Font();
    font.Bold(true);
    cell = rng.Offset(1, 0);
    // <--- end of creating of excel fields in a worksheet

    lId = lEn.searchFirst('');
    while (lId)
    {
        if (lEn.moduleId(lId) == #LayerId) // The particular label file
        {
            // print to excel
            progress.incCount();
            rstAxa.AddNew();
            fld = flds.Item(#LabelId);  fld.Value(lId);
            fld = flds.Item(#English);  fld.Value(lEn.extractString(lId));
            fld = flds.Item(#French);   fld.Value(lFr.extractString(lId));
            fld = flds.Item(#Russian);  fld.Value(lRu.extractString(lId));
            rstAxa.Update();
        }
        lId = lEn.searchNext();
    }
    cell.CopyFromRecordset(rstAxa);
    progress.kill();
    // format excel worksheet
    rngCR = rng.CurrentRegion();
    entCol = rngCR.EntireColumn();
    entCol.AutoFit();
    cell.Select();
    actWin = xlApp.ActiveWindow();
    actWin.FreezePanes(true);
    rstAxa.Close();
    xlApp.Visible(true);
    wbk.SaveAs("Labels from "+#LayerId+ " in "+#English+", "+#French+", "+#Russian+".xls");
}