dmitry-merzlyakov/nledger

Core double entry components in nLedger

Opened this issue · 5 comments

Thanks for the work, I apreciate it.

I am attempting to use nLedger in a bit unconventional way. I need to incorporate it into a portfolio tracking system for complex derivatives trades.

The entries will actually be in a database instead of a text file. I could certainly export it, but it would make more sense to just work directly with that data.

I also need to run micro ledgers for trade analysis. These would just be a regular ledger but only focus on the entries related to a specific trade.

My question is what classes should I be focusing on as the entry point for doing this. Basically where would I start if I just wanted to use the Double Entry Accounting functionality of the code?

Anything you can share would be very helpful, but don't feel like you need to do my homework. I am just looking for basic guidance.

Thanks

Hi Doug,

Thank you for interest to this product. At this moment, I see two general strategies to integrate NLedger with external applications:

  1. Deal with it as with a black box that consumes a text stream and produces a formatted output;
  2. Manually populate Journal entries (Xacts, Posts) and Accounts and run the reporter;

In my opinion, the first way is preferable (at least, at this moment) because it is easier and more stable.
I understand we all like to work with domain objects, but in case of NLedger it might be too complicated. The drawbacks are:

  • you will need to make significant changes to inject your code and manage execution flow on your own;
  • there are many peculiarities in the original domain objects; you will need to make an effort to produce journal objects in the same manner as the rest of code wants;
  • your code injections are not covered by the original unit tests.

Of course, everything is possible and if you decide to follow the second option - do not hesitate to ask any questions about the code.

If you decide to follow the first option, I suggest to look at either Program.cs or TestRunner.cs - they both have a code to run NLedger as a black box:

  • you can collect data from your database and compose an input text stream (formatted as an Ledger journal). I understand it sounds ugly, but, actually, it might be not so bad idea - you need to get your data into memory anyway; composing a text stream is a fast operation, so it may work quite efficient;

  • you can get the output stream and handle as you need. You can consider writing an own AnsiTextWriter in case you want to colorize the output on your own way (e.g. for Web). Another possible option is to specify own formatting templates (please, take a look at the original Ledger documentation; there are some examples - all they are supported). It might help you to adopt the output stream to be handled by your code in more convenient way. You can also look at the MainApplicationContext.Current and observe parsed data (Journal entries, accounts etc). Unfortunately, report data is not accessible.

Further (in about three +/- months) I want to complete and publish a much more convenient integration way. My plan is to implement the way how Ledger is integrated with Python - but make it more generalized. In two words:

  • there will be added a "connector" assembly with a simple public API;
  • there will be a conversion component that will convert internal NLedger object into public DTO;
  • this conversion component will be used for integration with scripting engines and with external applications;
  • API methods will provide all basic Ledger verbs and will deal with simple public DTO.

So, people will be able to communicate with NLedger in the same way as the original Ledger is called from Python environment. Unfortunately, this feature is currently in development so it is only a plan.

Thank you!
Dmitry

@dmitry-merzlyakov

I have a question regarding "black box" usage:

I have a console app that has a consumer listening to a queue, and it consumes messages which identify the specific ledger file and query to run like so:

public class NLedgerRequestConsumer : IConsumer<NLedgerRequest>
{
    private static readonly object lockObject = new object();

    public Task Consume(ConsumeContext<NLedgerRequest> context)
    {
        context.Respond(new NLedgerResponse
        {
            Result = QueryLedger(context.Message.LedgerFile, context.Message.LedgerQuery)
        });

        return Task.CompletedTask;
    }

    public string QueryLedger(string ledgerFile, string query)
    {
        lock (lockObject)
        {
            // Capture Output
            var       sb = new StringBuilder();
            using var sw = new StringWriter(sb);
            Console.SetOut(sw);

            // Initialize NLedger
            var main = new Main();
            new NLedgerConfiguration().ConfigureConsole(MainApplicationContext.Current);

            // Execute Query
            return main.Execute($@"-f {ledgerFile} {query}") == 0
                ? sb.ToString()
                : nameof(Exception);
        }
    }
}

Since you are very intimate with the inner workings of the code, my question is:

Do you see any issues that could arise from using the NLedger library like this as a black box implementation? or is more needed to isolate the statics being used internally?

Hi @VagyokC4,

Yes, it should work. Basically, I would recommend to check TestRunner class; it mostly does the same work. See how you can extract error messages, add post-processing for the output etc.

In regards to multi-threading: though I did not make special tests, I would tell that everything inside your QueryLeadger method should be thread-safe. All static variables are [ThreadStatis] and managed by MainApplicationContext class. So, you should be able to run QueryLedger in parallel without a global lock.

And, just a comment - you can consider implementing your own providers to simplify interop with NLedger (check "Abstract Application Services" in MainApplicationContext). E.g. input files might be kept somewhere in DB, not in the file system.

If you have any further questions - please, ask.

Thanks,
Dmitry

@dmitry-merzlyakov Perfect. Thank-you!

I found your road map to version 1.0. How is that coming along?

Hi @VagyokC4

The road map is more or less still on track; the goals are not changed. Basically, I would tell that my current priorities are:

  • Keep NLedger code up-to-date. I recently updated next-dev with the latest changes in the original ledger, so this point is covered;
  • Add Standard 2.0 compilation for NLedger dll; create and publish a NuGet package with all binaries. It should be done soon (in a month I guess);
  • Add a "normal" API that would let other application seamlessly host NLedger. Very initial progress;
  • Enable script integration (for Python first of all). Good progress here; I have a prototype for Python (based on Python.Net) and Powershell (based on Management.Automation), so I am confident it will be published in several months.

I am going to include this stuff into 0.8.

Thanks,
Dmitry