Gnucash Lots?

Derek Atkins warlord@MIT.EDU
23 May 2002 15:28:19 -0400


linas@linas.org (Linas Vepstas) writes:

> > 2) Splits want to be tied to an account, but I don't want to touch any
> >    accounts until you post the invoice.  This means you would need to
> >    store the account information in a KVP until you actually post.
> 
> Hmm. So if I understand correctly, you can start creating an invoice
> one one day, stop, save, shut down gnucash, come back another day,
> continue editing the invoice, and post sometime later?  

Yes.

> This kind of feals like a "shopping cart", where nothing is 'posted'
> until the shopper "checks out" ... right?
> 
> What's really the real-world example of needing this feature?

Shop orders, lazy entering, or real-time data entry that is reconciled
later.  I can certainly see a consultant entering data into invoices
in real-time, but only posting the invoice when the job is done, or at
the end of the month, or whatever.

Another real-world example: my mechanic invoices me at the end of
every "shop order", however, as they find problems with the vehicle
they work on entering the data in real-time.  However, only when the
job is done do they post the invoice to my account and print out the
invoice and send to me.  Orthogonal to the invoicing, they send me a
monthly account statement that shows all the invoices (shop orders)
and all the payments, with the balance due as of the end of the month.

A shopping cart is related..  You can place an order for items, but
you only get invoiced for the items that actually ship.  In other
words, You can place an order for 5 widgets, but only 2 widgets get
sent.  You don't want to post the 5 widgets, you only want to post the
2.  However, the underlying data structures are related (indeed, they
are connected).

> > When you wanted to search for
> > an Entry, it would make it much harder if you had to search by KVP.
> 
> Ah-ha!  Searching is hard!
> 
> Do we need to generalize query?

Well, to some extent, yes.  The major problem is that searching by KVP
is much less flexible than searching by any other "core data type".
I've been working on re-implementing Queries, I understand how hard it
is.

> Would the following routine be useful?
> 
> Split * find_split_where_kvp_value_equals (GList *splits, 
>                           char * key, kvp_value * match_value)
> {
>    for (n=splits; n; n=n->next)
>    {
>      kvp_frame = ((Split *)n->data)->kvp_data;
>      val = kvp_get_value (kvp_frame, key);
>      if (kvp_equal (match_value, val) return ((Split *) n->data);
>    }
> }
>
> Or at least a query-generalized version of the above?

No, it wouldn't help.  Besides the fact that this routine already
exists, it is insufficient.  The problem is that you cannot perform
full-scale queries on KVP values.  For example, you cannot search for
Split->KVP{string-key} matches regex{xxx} and Split->KVP{date-key}
occurs between {date1} and {date2}.  These types of robust query
operations just aren't possible with KVPs now.

Could it be made to work?  Sure.  In fact the new Query framework I've
created is a good deal of the way to doing it.  The only problem is
that it really isn't general _enough_.  You cannot add new types to
the KVP.  For example, I don't just want to be able to store a GUID, I
want to be able to store a TYPED-GUID (e.g., this is a Customer GUID
vs. a Vendor GUID).  This is only one example off the top of my head.

> I also don't understand why you did this; it seems to be causing you to 
> reinvent engine features, and reinvent the ledger display ...

I don't feel like I've had to re-invent a lot of stuff, not really.
I've certainly had to re-invent the ledger layout, model, etc.  OTOH,
I would have had to code probably 75% of this anyways because there is
no general means of doing the work I would have needed to do.  All the
work with the backends is just that I've tried to keep the business
functions "separate" from the engine.  That was a personal choice, and
in the end I believe it's made the engine a much more flexible beast
in the end.  Re-implementing the Query is also something that would
have had to happen.  Same thing for the Search dialog.

Most of the work that I've been doing has absolutely nothing to do
with actual financial bits.  In fact, this whole piece is maybe 25% of
the work that I've done.

> Naively, I would have thought that it would have been enough to just
> make all of the transfers be ordinary transactions, working between
> accounts in a 'pending orders' account subtree.
> Days or months later, when the customer commits, you'd have a transfer
> from 'pending' to 'actual' (either by creating a new transaction between
> 'pending' and 'actual', or by reparenting the splits from the 'pending'
> subtree to the 'actual' subtree.  If the customer cancels, you could 
> void out the pending order or delete it outright.  None of this would 
> require a reinvention of the core objects (and maybe thereby avoided 
> at least some of your storage problems?)

Nah, I'd still have storage problems.  I'd still have to store
Customer and Vendor information, Job and PO information, all of which
are outside everything else.

Could I have used Splits?  Sure.  Would it have been easier?  Maybe.
I'm not convinced.  However most of the work I've done has really been
on other parts, generalizing the engine and making most of the
functions plug-and-play instead of fixed-list.

> > No, there is too much information in the Invoice.  The Invoice really
> > is tied to the transaction that is posted, not the Lot.
> 
> Well, you still have to have the invoice point at the lot.  A
> transaction might be associated with multiple lots.

Fair enough; I didn't really think about that.  However, the Invoice
already points to the Transaction and the Posted Account, which should
be enough to determine the Lot:

        GNCLot * gnc_lot_from_transaction (Transaction *txn, Account *acc)
        {
                GList *node;
                for (node = xaccTransGetSplits (txn); node; node = node->next) {
                        Split *split = node->data;
                        if (xaccSplitGetAccount (split) == acc && split->lot)
                                return split->lot;
                }
                return NULL;
        }

Looking at the API, we probably need/want an xaccSplitGetLot() function
(or gnc_lot_get_lot_from_split())

> I presume that someday, you'll be able to manage inventory, in which
> case the splits in the transaction will be parts of different lots.
> In the paint example, they're will be a a split that belongs to the 
> 'paint' lot, that debits the amount of paint in the warehouse. 
> You don't want to confuse that lot with the lot that encapsulates 
> the charge for that paint (which would be in the same transaction).

Fair enough.

-derek

-- 
       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL: http://web.mit.edu/warlord/    PP-ASEL-IA     N1NWH
       warlord@MIT.EDU                        PGP key available