Thursday, June 23, 2016

I started to work on #988. We want a new table parameter and a new virtual field on every partners table.

This ticket is not only useful in Lino Voga, also in Lino Così, Lino Welfare and Lino Presto. And for all future applications with accounting functionality.

So it should be defined in lino_cosi.lib.contacts.models.

Discovered a design flaw

Every application has its own version of contacts plugin:

  • lino_voga.lib.contacts.models

  • lino_presto.lib.contacts.models

  • lino_welfare.lib.contacts.models

These extensions until now inherited from lino.modlib.contacts.models and not from lino_cosi.lib.contacts.models:

from lino.modlib.contacts import Plugin   # __init__.py
from lino.modlilib.contacts.models import *  # models.py

This was a design flaw which just didn’t disturb until now because lino_cosi.lib.contacts.models just defined some detail layouts which anyway weren’t being reused by the applications.

But actually they must inherit from lino_cosi.lib.contacts

from lino_cosi.lib.contacts import Plugin from lino_cosi.lib.contacts.models import *

Redefining the detail_layout

Note: why do we need:

@dd.receiver(dd.post_analyze)
def customize_contacts2(sender, **kw):
    site = sender
    site.modules.contacts.Persons.set_detail_layout(PersonDetail())
    site.modules.contacts.Companies.set_detail_layout(CompanyDetail())
    site.modules.contacts.Partners.set_detail_layout(PartnerDetail())

and cannot just do:

Persons.detail_layout = PersonDetail()
Companies.detail_layout = CompanyDetail()
Partners.detail_layout = PartnerDetail()

One reason is: set_detail_layout has the advantage of doing some validation. (TODO: explain more).

It seems that the following is a compromise which works:

Persons.set_detail_layout(PersonDetail())
Companies.set_detail_layout(CompanyDetail())
Partners.set_detail_layout(PartnerDetail())

As I wrote for #526, “it seems that the general rule is: if you want your own detail_layout, then you should write your own plugin.”

In other words, if you write your own plugins, then you don’t need to put your set_detail_layout calls into a post_analyze handler.

Pluggable accounting functionality

In Lino Welfare we have a challenge: we would like to have the accounting functionality “pluggable”. For example in chatelet it is disabled, they don’t want any ledger, journals, movements & Co. And we want that choice independently of the choice “eupen” or “chatelet”.

We solve this using the two classes lino_xl.lib.vat.mixins.PartnerDetailMixin and lino_xl.lib.vatless.mixins.PartnerDetailMixin. They both define a ledger tab panel, and they both are designed to “do nothing” when ledger is not installed. Or more precisely they define a DummyPanel named ledger in that case. Partner details who use them, must make sure that their main contains ledger.

A pitfall

It took me some time to realize that, when I write a lino_cosi.lib.contacts.models.Partner class, then I must also write Person Company classes.

Note that in lino_xl.lib.households we don’t write:

from lino_xl.lib.contacts.models import Partner

class Household(Partner):
    ...

But:

contacts = dd.resolve_app('contacts')

class Household(contacts.Partner):
    ...

The advantage is that we don’t need to create a lino_cosi.lib.households plugin which would avoid this trick for households:

from lino_xl.lib.households.models import * from lino_cosi.lib.contacts.models import Partner

class Household(Partner, Household):

I started a new specs document Partners in Lino Voga.