Welcome | Get started | Dive into Lino | Contribute | Reference

roger -- A customized Lino Voga site

A Lino Voga site with a few local customizations.

Used in Lino Voga specs.

The lino_book.projects.roger demo project illustrates some local customizations.

This is a tested document. The following instructions are used for initialization:

>>> from lino import startup
>>> startup('lino_book.projects.roger.settings.doctests')
>>> from lino.api.doctest import *

A customized management of membership fees

The lino_voga.lib.roger.courses plugin demonstrates the following rules for handling memberships:

  • Membership costs 15€ per year.

  • Members get a discount on enrolments to courses.

  • Customers can freely decide whether they want to be members or not.

  • They become member by paying the membership fee.

To handle these rules, we have an additional field member_until on each pupil.

There is a custom data checker lino_voga.lib.roger.courses.MemberChecker

>>> dd.demo_date()
datetime.date(2015, 5, 22)
>>> rt.show(courses.Pupils)
... 
======================================== ================================= ================== ============ ===== ===== ======== ==============
 Name                                     Address                           Participant type   Section      LFV   CKK   Raviva   Mitglied bis
---------------------------------------- --------------------------------- ------------------ ------------ ----- ----- -------- --------------
 Hans Altenberg (MEL)                     Aachener Straße, 4700 Eupen       Member                          Yes   No    No       31/12/2015
 Annette Arens (MEC)                      Alter Malmedyer Weg, 4700 Eupen   Helper                          No    Yes   No       31/12/2015
 Laurent Bastiaensen (ME)                 Am Berg, 4700 Eupen               Non-member                      No    No    No       31/12/2015
 Bernd Brecht (ME)                        Aachen, Germany                   Member                          No    No    No       31/12/2015
 Ulrike Charlier (ME)                     Auenweg, 4700 Eupen               Helper                          No    No    No       31/12/2015
 Dorothée Demeulenaere (ME)               Auf'm Rain, 4700 Eupen            Non-member                      No    No    No       31/12/2016
 ...
 Hedi Radermacher (ME)                    4730 Raeren                       Non-member                      No    No    No       31/12/2015
 Jean Radermacher (ME)                    4730 Raeren                       Member                          No    No    No       31/12/2015
 Marie-Louise Vandenmeulenbos (MEC)       Amsterdam, Netherlands            Helper                          No    Yes   No       31/12/2015
 Didier di Rupo (MS)                      4730 Raeren                       Non-member         Herresbach   No    No    No
 Erna Ärgerlich (ME)                      4730 Raeren                       Member                          No    No    No       31/12/2015
 Otto Östges (MCS)                        4730 Raeren                       Helper             Eynatten     No    Yes   No
======================================== ================================= ================== ============ ===== ===== ======== ==============
>>> print(dd.plugins.ledger.suppress_movements_until)
None
>>> rt.show(checkdata.MessagesByChecker, 'courses.MemberChecker')
============= ====================================== ==========================================
 Responsible   Database object                        Message text
------------- -------------------------------------- ------------------------------------------
 Robin Rood    *Karl Kaivers (ME)*                    Member until 2015-12-31, but no payment.
 Robin Rood    *Laura Laschet (ME)*                   Member until 2015-12-31, but no payment.
 Robin Rood    *Josefine Leffin (MEL)*                Member until 2015-12-31, but no payment.
 Robin Rood    *Marie-Louise Meier (ME)*              Member until 2015-12-31, but no payment.
 Robin Rood    *Alfons Radermacher (ME)*              Member until 2015-12-31, but no payment.
 Robin Rood    *Christian Radermacher (MEL)*          Member until 2015-12-31, but no payment.
 Robin Rood    *Edgard Radermacher (ME)*              Member until 2015-12-31, but no payment.
 Robin Rood    *Guido Radermacher (ME)*               Member until 2015-12-31, but no payment.
 Robin Rood    *Hedi Radermacher (ME)*                Member until 2015-12-31, but no payment.
 Robin Rood    *Jean Radermacher (ME)*                Member until 2015-12-31, but no payment.
 Robin Rood    *Erna Ärgerlich (ME)*                  Member until 2015-12-31, but no payment.
 Robin Rood    *Jean Dupont (ME)*                     Member until 2015-12-31, but no payment.
 Robin Rood    *Marie-Louise Vandenmeulenbos (MEC)*   Member until 2015-12-31, but no payment.
 Robin Rood    *Bernd Brecht (ME)*                    Member until 2015-12-31, but no payment.
 Robin Rood    *Jérôme Jeanémart (ME)*                Member until 2015-12-31, but no payment.
============= ====================================== ==========================================
>>> acc = rt.models.ledger.CommonAccounts.membership_fees.get_object()
>>> print(acc)
(7310) Membership fees
>>> rt.show(ledger.MovementsByAccount, acc)
============ ============== ===================================== ======= ============ =============
 Value date   Voucher        Description                           Debit   Credit       Match
------------ -------------- ------------------------------------- ------- ------------ -------------
 22/12/2015   *CSH 5/2015*   *Faymonville Luc*                             15,00        **CSH 5:1**
 22/12/2015   *CSH 5/2015*   *Groteclaes Gregory*                          15,00        **CSH 5:2**
 22/12/2015   *CSH 5/2015*   *Hilgers Hildegard*                           15,00        **CSH 5:3**
 22/12/2015   *CSH 5/2015*   *Jacobs Jacqueline*                           15,00        **CSH 5:4**
 22/12/2015   *CSH 5/2015*   *Jonas Josef*                                 15,00        **CSH 5:5**
 22/11/2015   *CSH 4/2015*   *Dobbelstein-Demeulenaere Dorothée*           15,00        **CSH 4:1**
 22/11/2015   *CSH 4/2015*   *Emonts Daniel*                               15,00        **CSH 4:3**
 22/11/2015   *CSH 4/2015*   *Engels Edgar*                                15,00        **CSH 4:4**
 22/11/2015   *CSH 4/2015*   *Evers Eberhart*                              15,00        **CSH 4:2**
 22/10/2015   *CSH 3/2015*   *Demeulenaere Dorothée*                       15,00        **CSH 3:2**
 22/10/2015   *CSH 3/2015*   *Dericum Daniel*                              15,00        **CSH 3:1**
 22/02/2015   *CSH 2/2015*   *Charlier Ulrike*                             15,00        **CSH 2:1**
 22/01/2015   *CSH 1/2015*   *Altenberg Hans*                              15,00        **CSH 1:2**
 22/01/2015   *CSH 1/2015*   *Arens Annette*                               15,00        **CSH 1:1**
 22/01/2015   *CSH 1/2015*   *Bastiaensen Laurent*                         15,00        **CSH 1:3**
                             **Balance -225.00 (15 movements)**            **225,00**
============ ============== ===================================== ======= ============ =============

Django and the VatVoucher model mixin

This section is no longer needed since 20230829. It helped us to discover that when you call contenttypes.ContentType.objects.get_for_model() with an abstract Model class, Django creates a contenttype database record. We then optimized lino_react.react.Renderer.actor2json() to not fall into that pit anymore.

The contenttypes framework seems to sometimes create an entry for the VatVoucher model mixin although this is an abstract model.

>>> from django.apps import apps
>>> cts = set([ct.model_class() for ct in contenttypes.ContentType.objects.all()])
>>> for m in apps.get_models():
...     if m in cts:
...         cts.remove(m)
>>> cts  
{None}
>>> for ct in contenttypes.ContentType.objects.all():  
...   if ct.model_class() is None:
...     print(ct)
vatvoucher

This is caused by the following method:

>>> get_for_model = contenttypes.ContentType.objects.get_for_model

When passing a non-existed Model class (that is abstract) it creates a contenttype database record. For example see how the contenttype count increasing when looking up abstract models:

>>> contenttypes.ContentType.objects.count()  
92
>>> users.UserAuthored._meta.abstract, contenttypes.ContentType.objects.filter(model="userauthored").count()  
(True, 0)

Now see when we look it up using get_for_model method:

>>> get_for_model(users.UserAuthored)  
<ContentType: userauthored>
>>> contenttypes.ContentType.objects.count()  
93

And there's one more contenttype object.

Database structure

>>> from lino.utils.diag import analyzer
>>> print(analyzer.show_db_overview())
... 
47 apps: lino, staticfiles, about, printing, system, jinja, react, help, users, office, xl, countries, contacts, phones, lists, beid, contenttypes, gfks, checkdata, cal, courses, products, rooms, memo, excerpts, weasyprint, uploads, ledger, bevats, vat, sales, summaries, storage, invoicing, finan, sepa, notes, outbox, voga, export_excel, calview, wkhtmltopdf, appypod, changes, bootstrap3, publisher, sessions.
92 models:
========================== ============================== ========= =======
 Name                       Default table                  #fields   #rows
-------------------------- ------------------------------ --------- -------
 bevats.Declaration         bevats.Declarations            28        15
 cal.Calendar               cal.Calendars                  6         8
 cal.EntryRepeater          cal.EntryRepeaterTable         17        0
 cal.Event                  cal.Events                     25        1171
 cal.EventPolicy            cal.EventPolicies              20        6
 cal.EventType              cal.EventTypes                 24        10
 cal.Guest                  cal.Guests                     6         0
 cal.GuestRole              cal.GuestRoles                 5         3
 cal.RecurrentEvent         cal.RecurrentEvents            22        16
 cal.RemoteCalendar         cal.RemoteCalendars            7         0
 cal.Room                   cal.Rooms                      11        7
 cal.Subscription           cal.Subscriptions              4         35
 cal.Task                   cal.Tasks                      16        0
 calview.DailyPlannerRow    calview.DailyPlannerRows       7         2
 changes.Change             changes.Changes                10        0
 checkdata.Message          checkdata.Messages             6         ...
 contacts.Company           contacts.Companies             26        31
 contacts.CompanyType       contacts.CompanyTypes          7         16
 contacts.Partner           contacts.Partners              24        103
 contacts.Person            contacts.Persons               42        72
 contacts.Role              contacts.Roles                 4         3
 contacts.RoleType          contacts.RoleTypes             5         5
 contenttypes.ContentType   gfks.ContentTypes              3         92
 countries.Country          countries.Countries            6         9
 countries.Place            countries.Places               9         80
 courses.Course             courses.Activities             34        26
 courses.CourseType         courses.CourseTypes            5         0
 courses.Enrolment          courses.Enrolments             18        95
 courses.Line               courses.Lines                  25        10
 courses.Pupil              courses.Pupils                 51        35
 courses.PupilType          courses.PupilTypes             5         3
 courses.Slot               courses.Slots                  5         0
 courses.Teacher            courses.Teachers               44        9
 courses.TeacherType        courses.TeacherTypes           5         4
 courses.Topic              courses.Topics                 4         5
 excerpts.Excerpt           excerpts.Excerpts              11        ...
 excerpts.ExcerptType       excerpts.ExcerptTypes          17        15
 finan.BankStatement        finan.BankStatements           16        21
 finan.BankStatementItem    finan.BankStatementItemTable   9         368
 finan.JournalEntry         finan.FinancialVouchers        14        0
 finan.JournalEntryItem     finan.JournalEntryItemTable    9         0
 finan.PaymentOrder         finan.PaymentOrders            15        16
 finan.PaymentOrderItem     finan.PaymentOrderItemTable    9         127
 invoicing.FollowUpRule     invoicing.FollowUpRules        4         0
 invoicing.Item             invoicing.Items                9         0
 invoicing.Plan             invoicing.Plans                8         1
 invoicing.SalesRule        invoicing.SalesRules           3         4
 invoicing.Tariff           invoicing.Tariffs              8         0
 ledger.Account             ledger.Accounts                18        21
 ledger.AccountingPeriod    ledger.AccountingPeriods       7         17
 ledger.FiscalYear          ledger.FiscalYears             5         7
 ledger.Journal             ledger.Journals                28        10
 ledger.LedgerInfo          ledger.LedgerInfoTable         2         0
 ledger.MatchRule           ledger.MatchRules              3         33
 ledger.Movement            ledger.Movements               11        1453
 ledger.PaymentTerm         ledger.PaymentTerms            11        8
 ledger.Voucher             ledger.AllVouchers             8         423
 lists.List                 lists.Lists                    7         8
 lists.ListType             lists.ListTypes                4         3
 lists.Member               lists.Members                  5         103
 memo.Mention               memo.Mentions                  5         0
 notes.EventType            notes.EventTypes               8         1
 notes.Note                 notes.Notes                    16        100
 notes.NoteType             notes.NoteTypes                11        3
 outbox.Attachment          outbox.Attachments             4         0
 outbox.Mail                outbox.Mails                   8         0
 outbox.Recipient           outbox.Recipients              6         0
 phones.ContactDetail       phones.ContactDetails          8         18
 products.Category          products.Categories            15        5
 products.PriceRule         products.PriceRules            4         0
 products.Product           products.Products              20        12
 rooms.Booking              rooms.Bookings                 25        3
 sales.InvoiceItem          sales.InvoiceItems             16        1109
 sales.PaperType            sales.PaperTypes               5         2
 sales.VatProductInvoice    sales.Invoices                 27        252
 sepa.Account               sepa.Accounts                  6         25
 sessions.Session           users.Sessions                 3         ...
 storage.Component          storage.Components             4         3
 storage.DeliveryItem       storage.DeliveryItems          8         0
 storage.DeliveryNote       storage.DeliveryNotes          14        0
 storage.Filler             storage.Fillers                7         43
 storage.Movement           storage.Movements              10        0
 storage.Provision          storage.Provisions             5         0
 storage.TransferRule       storage.TransferRules          5         0
 system.SiteConfig          system.SiteConfigs             12        1
 uploads.Upload             uploads.Uploads                13        7
 uploads.UploadType         uploads.UploadTypes            8         1
 uploads.Volume             uploads.Volumes                5         0
 users.Authority            users.Authorities              3         0
 users.User                 users.AllUsers                 19        6
 vat.InvoiceItem            vat.InvoiceItemTable           9         187
 vat.VatAccountInvoice      vat.Invoices                   20        119
========================== ============================== ========= =======

Foreign Keys and their on_delete setting

Here is a list of foreign keys in Lino Voga and their on_delete behaviour. See also Customize delete behaviour.

>>> from lino.utils.diag import analyzer
>>> print(analyzer.show_foreign_keys())
... 
- cal.Calendar :
  - CASCADE : cal.Subscription.calendar
  - PROTECT : cal.Room.calendar, system.SiteConfig.site_calendar
- cal.Event :
  - CASCADE : cal.Guest.event
  - PROTECT : cal.EntryRepeater.cal_entry
- cal.EventType :
  - PROTECT : cal.Event.event_type, cal.EventPolicy.event_type, cal.RecurrentEvent.event_type, courses.Line.event_type, products.PriceRule.selector, rooms.Booking.event_type, system.SiteConfig.default_event_type, users.User.event_type
- cal.GuestRole :
  - PROTECT : cal.Guest.role, courses.Line.guest_role, system.SiteConfig.pupil_guestrole
- cal.Room :
  - PROTECT : cal.Event.room, courses.Course.room, rooms.Booking.room
- contacts.Company :
  - PROTECT : cal.Room.company, contacts.Role.company, courses.Line.company, excerpts.Excerpt.company, ledger.Journal.partner, notes.Note.company, rooms.Booking.company, system.SiteConfig.site_company
- contacts.CompanyType :
  - PROTECT : contacts.Company.type
- contacts.Partner :
  - CASCADE : contacts.Company.partner_ptr, contacts.Person.partner_ptr, invoicing.Item.partner, invoicing.SalesRule.partner, lists.Member.partner, phones.ContactDetail.partner, sepa.Account.partner
  - PROTECT : bevats.Declaration.partner, finan.BankStatementItem.partner, finan.JournalEntryItem.partner, finan.PaymentOrderItem.partner, invoicing.Plan.partner, invoicing.SalesRule.invoice_recipient, ledger.Movement.partner, outbox.Recipient.partner, sales.VatProductInvoice.partner, storage.DeliveryNote.partner, storage.Filler.partner, storage.Movement.partner, storage.Provision.partner, users.User.partner, vat.VatAccountInvoice.partner
- contacts.Person :
  - CASCADE : courses.Pupil.person_ptr, courses.Teacher.person_ptr
  - PROTECT : cal.Guest.partner, cal.Room.contact_person, contacts.Role.person, courses.Line.contact_person, excerpts.Excerpt.contact_person, notes.Note.contact_person, rooms.Booking.contact_person
- contacts.RoleType :
  - PROTECT : cal.Room.contact_role, contacts.Role.type, courses.Line.contact_role, excerpts.Excerpt.contact_role, notes.Note.contact_role, rooms.Booking.contact_role
- contenttypes.ContentType :
  - PROTECT : cal.Event.owner_type, cal.Task.owner_type, changes.Change.master_type, changes.Change.object_type, checkdata.Message.owner_type, excerpts.Excerpt.owner_type, excerpts.ExcerptType.content_type, invoicing.Item.generator_type, memo.Mention.owner_type, memo.Mention.source_type, notes.Note.owner_type, outbox.Attachment.owner_type, outbox.Mail.owner_type, sales.InvoiceItem.invoiceable_type, storage.DeliveryItem.invoiceable_type, uploads.Upload.owner_type
- countries.Country :
  - PROTECT : contacts.Partner.country, contacts.Person.birth_country, contacts.Person.nationality, countries.Place.country
- countries.Place :
  - PROTECT : contacts.Partner.city, contacts.Partner.region, countries.Place.parent
- courses.Course :
  - PROTECT : courses.Enrolment.course, invoicing.Plan.order
- courses.CourseType :
  - PROTECT : courses.Line.course_type
- courses.Line :
  - PROTECT : courses.Course.line
- courses.Pupil :
  - PROTECT : courses.Enrolment.pupil
- courses.PupilType :
  - PROTECT : courses.Pupil.pupil_type
- courses.Slot :
  - PROTECT : courses.Course.slot
- courses.Teacher :
  - PROTECT : courses.Course.teacher
- courses.TeacherType :
  - PROTECT : courses.Teacher.teacher_type
- courses.Topic :
  - PROTECT : courses.Line.topic
- excerpts.Excerpt :
  - SET_NULL : bevats.Declaration.printed_by, courses.Enrolment.printed_by, finan.BankStatement.printed_by, finan.JournalEntry.printed_by, finan.PaymentOrder.printed_by, sales.VatProductInvoice.printed_by, storage.DeliveryNote.printed_by
- excerpts.ExcerptType :
  - PROTECT : excerpts.Excerpt.excerpt_type
- finan.BankStatement :
  - CASCADE : finan.BankStatementItem.voucher
- finan.JournalEntry :
  - CASCADE : finan.JournalEntryItem.voucher
- finan.PaymentOrder :
  - CASCADE : finan.PaymentOrderItem.voucher
- invoicing.Plan :
  - CASCADE : invoicing.Item.plan
- ledger.Account :
  - PROTECT : finan.BankStatement.item_account, finan.BankStatementItem.account, finan.JournalEntry.item_account, finan.JournalEntryItem.account, finan.PaymentOrder.item_account, finan.PaymentOrderItem.account, ledger.Journal.account, ledger.MatchRule.account, ledger.Movement.account, vat.InvoiceItem.account
- ledger.AccountingPeriod :
  - PROTECT : bevats.Declaration.end_period, bevats.Declaration.start_period, ledger.Voucher.accounting_period
- ledger.FiscalYear :
  - PROTECT : ledger.AccountingPeriod.year
- ledger.Journal :
  - CASCADE : invoicing.FollowUpRule.source_journal, ledger.MatchRule.journal
  - PROTECT : ledger.Voucher.journal, storage.TransferRule.journal
- ledger.PaymentTerm :
  - PROTECT : bevats.Declaration.payment_term, contacts.Partner.payment_term, courses.Course.payment_term, sales.VatProductInvoice.payment_term, vat.VatAccountInvoice.payment_term
- ledger.Voucher :
  - CASCADE : ledger.Movement.voucher, storage.Movement.voucher
  - PROTECT : bevats.Declaration.voucher_ptr, finan.BankStatement.voucher_ptr, finan.JournalEntry.voucher_ptr, finan.PaymentOrder.voucher_ptr, sales.VatProductInvoice.voucher_ptr, storage.DeliveryNote.voucher_ptr, vat.VatAccountInvoice.voucher_ptr
  - SET_NULL : invoicing.Item.invoice
- lists.List :
  - CASCADE : lists.Member.list
- lists.ListType :
  - PROTECT : lists.List.list_type
- notes.EventType :
  - PROTECT : notes.Note.event_type, system.SiteConfig.system_note_type
- notes.NoteType :
  - PROTECT : notes.Note.type
- outbox.Mail :
  - CASCADE : outbox.Attachment.mail, outbox.Recipient.mail
- products.Category :
  - PROTECT : courses.Line.fees_cat, courses.Line.options_cat, products.Category.parent, products.Product.category
- products.Product :
  - PROTECT : cal.Room.fee, courses.Course.fee, courses.Enrolment.fee, courses.Enrolment.option, courses.Line.fee, invoicing.Tariff.product, products.PriceRule.product, sales.InvoiceItem.product, storage.Component.child, storage.Component.parent, storage.DeliveryItem.product, storage.Filler.provision_product, storage.Movement.product, storage.Provision.product
- sales.PaperType :
  - PROTECT : courses.Course.paper_type, invoicing.SalesRule.paper_type, sales.VatProductInvoice.paper_type
- sales.VatProductInvoice :
  - CASCADE : sales.InvoiceItem.voucher
- sepa.Account :
  - PROTECT : finan.PaymentOrderItem.bank_account, ledger.Journal.sepa_account
- storage.DeliveryNote :
  - CASCADE : storage.DeliveryItem.voucher
- uploads.UploadType :
  - PROTECT : uploads.Upload.type
- uploads.Volume :
  - PROTECT : ledger.Journal.uploads_volume, uploads.Upload.volume
- users.User :
  - CASCADE : cal.Subscription.user, ledger.LedgerInfo.user
  - PROTECT : cal.Event.assigned_to, cal.Event.user, cal.RecurrentEvent.user, cal.Task.user, changes.Change.user, checkdata.Message.user, courses.Course.user, courses.Enrolment.user, excerpts.Excerpt.user, invoicing.Plan.user, ledger.Voucher.user, notes.Note.user, outbox.Mail.user, rooms.Booking.user, uploads.Upload.user, users.Authority.authorized, users.Authority.user
- vat.VatAccountInvoice :
  - CASCADE : vat.InvoiceItem.voucher