Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

periods: Stored periods

The lino.modlib.periods plugin adds the notions of “stored” years and periods, often called fiscal year and accounting period. But these designations can be customized e.g. into “academic years”, “semesters”, “trimesters”, or “quarters”.

Table of contents:

Examples in this document use the lino_book.projects.cosi1 demo project.

>>> from lino import startup
>>> startup('lino_book.projects.cosi1.settings')
>>> from lino.api.doctest import *
>>> ses = rt.login("robin")
>>> translation.activate('en')

Overview

accounting period

The smallest time slice to be observed in accounting reports. It usually corresponds to one month, but you can choose to report per quarter, per trimester or per semester. See Accounting periods below.

fiscal year

Usually a calendar year, but there can be situations where a company reports a shorter fiscal year. See Fiscal years below.

The plugin defines two models: StoredYear and StoredPeriod.

Fiscal years

Lino has a table of fiscal years. A fiscal year often corresponds to the calendar year, but not necessarily.

If lino_xl.lib.sheets is installed, the detail window of a fiscal year object shows the financial reports (balance sheet and income statement) for this year.

The std fixture of the lino.modlib.periods plugins fills StoredYear with 5 years starting from periods.start_year.

>>> settings.SITE.the_demo_date
datetime.date(2015, 3, 12)
>>> dd.plugins.periods.start_year
2014
>>> dd.today()
datetime.date(2015, 3, 12)
>>> dd.today().year + 5
2020
>>> rt.show(periods.StoredYears)
... 
=========== ============ ============ =======
 Reference   Start date   End date     State
----------- ------------ ------------ -------
 2014        01/01/2014   31/12/2014   Open
 2015        01/01/2015   31/12/2015   Open
 2016        01/01/2016   31/12/2016   Open
 2017        01/01/2017   31/12/2017   Open
 2018        01/01/2018   31/12/2018   Open
 2019        01/01/2019   31/12/2019   Open
 2020        01/01/2020   31/12/2020   Open
=========== ============ ============ =======

Accounting periods

For each period it is possible to specify the dates during which it is allowed to register vouchers into this period, and also its “state”: whether it is “closed” or not.

Each ledger movement happens in a given accounting period. An accounting period usually corresponds to a month of the calendar.

Accounting periods are automatically created the first time they are needed by some operation.

>>> rt.show(periods.StoredPeriods)
... 
=========== ============ ============ ============= ======= ========
 Reference   Start date   End date     Fiscal year   State   Remark
----------- ------------ ------------ ------------- ------- --------
 2014-01     01/01/2014   31/01/2014   2014          Open
 2014-02     01/02/2014   28/02/2014   2014          Open
 2014-03     01/03/2014   31/03/2014   2014          Open
 2014-04     01/04/2014   30/04/2014   2014          Open
 2014-05     01/05/2014   31/05/2014   2014          Open
 2014-06     01/06/2014   30/06/2014   2014          Open
 2014-07     01/07/2014   31/07/2014   2014          Open
 2014-08     01/08/2014   31/08/2014   2014          Open
 2014-09     01/09/2014   30/09/2014   2014          Open
 2014-10     01/10/2014   31/10/2014   2014          Open
 2014-11     01/11/2014   30/11/2014   2014          Open
 2014-12     01/12/2014   31/12/2014   2014          Open
 2015-01     01/01/2015   31/01/2015   2015          Open
 2015-02     01/02/2015   28/02/2015   2015          Open
 2015-03     01/03/2015   31/03/2015   2015          Open
 2015-12     01/12/2015   31/12/2015   2015          Open
=========== ============ ============ ============= ======= ========

The reference of a new accounting period is computed by applying the voucher’s entry date to the template defined in the date_to_period_tpl attribute of the accounting plugin. The default implementation leads to the following references:

>>> from lino.modlib.periods.models import date2ref
>>> date2ref(i2d(19940202))
'1994-02'
>>> date2ref(i2d(20150228))
'2015-02'
>>> date2ref(i2d(20150401))
'2015-04'

Special accounting periods

You may manually create additional accounting periods. For example

  • 2015-00 might stand for a fictive “opening” period before January 2015 and after December 2014.

  • 2015-13 might stand for January 2016 in a company that is changing their fiscal year from “January-December” to “July-June”.

  • in certain public administrations “January 2024” can be considered as the “Thirteenth month of 2023” for operations that are bound to the fiscal year 2023. We call this overlapping periods.

Filtering the list of stored periods

The parameter panel of the StoredPeriods table has two fields, start_date and end_date.

>>> ses = rt.login("robin")
>>> pv = dict(start_date=i2d(20140212))
>>> ses.show(periods.StoredPeriods, param_values=pv)
... 
=========== ============ ============ ============= ======= ========
 Reference   Start date   End date     Fiscal year   State   Remark
----------- ------------ ------------ ------------- ------- --------
 2014-02     01/02/2014   28/02/2014   2014          Open
=========== ============ ============ ============= ======= ========
>>> pv = dict(start_date=i2d(20140212), end_date=i2d(20140312))
>>> ses.show(periods.StoredPeriods, param_values=pv)
... 
=========== ============ ============ ============= ======= ========
 Reference   Start date   End date     Fiscal year   State   Remark
----------- ------------ ------------ ------------- ------- --------
 2014-02     01/02/2014   28/02/2014   2014          Open
 2014-03     01/03/2014   31/03/2014   2014          Open
=========== ============ ============ ============= ======= ========
>>> dd.today()
datetime.date(2015, 3, 12)
>>> pv = dict(start_date=i2d(20141118), end_date=i2d(20160312))
>>> ses.show(periods.StoredPeriods, param_values=pv, display_mode="summary")
... 
`2014-11 <…>`__, `2014-12 <…>`__, `2015-01 <…>`__, `2015-02 <…>`__, `2015-03 <…>`__, `2015-12 <…>`__

Test examples

>>> TEST_DATES = list(map(i2d, [
...    19850203, 19990901, 20000101, 20150427, 20240324,
...    20590601, 29970123]))
>>> Y, P = periods.StoredYear, periods.StoredPeriod
>>> from lino.modlib.periods.mixins import get_range_for_date
>>> def run_test():
...     headers = ["Date", "year", "period", "ref", "start date", "end date"]
...     rows = []
...     for d in TEST_DATES:
...         sd, ed = get_range_for_date(d)
...         rows.append([str(d), Y.get_ref_for_date(d), P.get_ref_for_date(d),
...             date2ref(d), str(sd), str(ed)])
...     print(table(headers, rows))
>>> run_test()
============ ====== ======== ========= ============ ============
 Date         year   period   ref       start date   end date
------------ ------ -------- --------- ------------ ------------
 1985-02-03   1985   02       1985-02   1985-02-01   1985-02-28
 1999-09-01   1999   09       1999-09   1999-09-01   1999-09-30
 2000-01-01   2000   01       2000-01   2000-01-01   2000-01-31
 2015-04-27   2015   04       2015-04   2015-04-01   2015-04-30
 2024-03-24   2024   03       2024-03   2024-03-01   2024-03-31
 2059-06-01   2059   06       2059-06   2059-06-01   2059-06-30
 2997-01-23   2997   01       2997-01   2997-01-01   2997-01-31
============ ====== ======== ========= ============ ============

Period types

Some organizations subdivide their years in periods other than months. You can customize this by setting periods.period_type, which contains one of the choices defined in PeriodTypes.

>>> rt.show(periods.PeriodTypes)
=========== =========== ========== ========================
 value       text        Duration   Template for reference
----------- ----------- ---------- ------------------------
 month       Month       1          {month:0>2}
 quarter     Quarter     3          Q{period}
 trimester   Trimester   4          T{period}
 semester    Semester    6          S{period}
=========== =========== ========== ========================
>>> dd.plugins.periods.period_type = periods.PeriodTypes.quarter
>>> run_test()
============ ====== ======== ========= ============ ============
 Date         year   period   ref       start date   end date
------------ ------ -------- --------- ------------ ------------
 1985-02-03   1985   Q1       1985-Q1   1985-01-01   1985-03-31
 1999-09-01   1999   Q3       1999-Q3   1999-07-01   1999-09-30
 2000-01-01   2000   Q1       2000-Q1   2000-01-01   2000-03-31
 2015-04-27   2015   Q2       2015-Q2   2015-04-01   2015-06-30
 2024-03-24   2024   Q1       2024-Q1   2024-01-01   2024-03-31
 2059-06-01   2059   Q2       2059-Q2   2059-04-01   2059-06-30
 2997-01-23   2997   Q1       2997-Q1   2997-01-01   2997-03-31
============ ====== ======== ========= ============ ============
>>> dd.plugins.periods.period_type = periods.PeriodTypes.trimester
>>> run_test()
============ ====== ======== ========= ============ ============
 Date         year   period   ref       start date   end date
------------ ------ -------- --------- ------------ ------------
 1985-02-03   1985   T1       1985-T1   1985-01-01   1985-04-30
 1999-09-01   1999   T3       1999-T3   1999-09-01   1999-12-31
 2000-01-01   2000   T1       2000-T1   2000-01-01   2000-04-30
 2015-04-27   2015   T1       2015-T1   2015-01-01   2015-04-30
 2024-03-24   2024   T1       2024-T1   2024-01-01   2024-04-30
 2059-06-01   2059   T2       2059-T2   2059-05-01   2059-08-31
 2997-01-23   2997   T1       2997-T1   2997-01-01   2997-04-30
============ ====== ======== ========= ============ ============
>>> dd.plugins.periods.period_type = periods.PeriodTypes.month  # restore default

Short references

Lino usually represents a fiscal year using 4 digits. You can set periods.short_ref to use a two-letter code.

>>> dd.plugins.periods.short_ref = True
>>> run_test()
============ ====== ======== ======= ============ ============
 Date         year   period   ref     start date   end date
------------ ------ -------- ------- ------------ ------------
 1985-02-03   85     02       85-02   1985-02-01   1985-02-28
 1999-09-01   99     09       99-09   1999-09-01   1999-09-30
 2000-01-01   00     01       00-01   2000-01-01   2000-01-31
 2015-04-27   15     04       15-04   2015-04-01   2015-04-30
 2024-03-24   24     03       24-03   2024-03-01   2024-03-31
 2059-06-01   59     06       59-06   2059-06-01   2059-06-30
 2997-01-23   97     01       97-01   2997-01-01   2997-01-31
============ ====== ======== ======= ============ ============
>>> dd.plugins.periods.short_ref = False  # restore default

The Y2K problem

There are legacy systems where the year was internally represented using a two-letter code. That’s why we have a setting periods.fix_y2k, which is either True or False. Default is False. If you want to represent years using only two digits and remain y2k-proof, set periods.fix_y2k to True and Lino will give different reference names:

>>> dd.plugins.periods.fix_y2k = True
>>> run_test()
============ ====== ======== ======= ============ ============
 Date         year   period   ref     start date   end date
------------ ------ -------- ------- ------------ ------------
 1985-02-03   85     02       85-02   1985-02-01   1985-02-28
 1999-09-01   99     09       99-09   1999-09-01   1999-09-30
 2000-01-01   A0     01       A0-01   2000-01-01   2000-01-31
 2015-04-27   B5     04       B5-04   2015-04-01   2015-04-30
 2024-03-24   C4     03       C4-03   2024-03-01   2024-03-31
 2059-06-01   F9     06       F9-06   2059-06-01   2059-06-30
 2997-01-23   ¤7     01       ¤7-01   2997-01-01   2997-01-31
============ ====== ======== ======= ============ ============

This system works only for the next two hundred years, more precisely until 2259. After this the short references will look silly:

>>> print(date2ref(i2d(22591231)))
Z9-12
>>> print(date2ref(i2d(22600101)))
[0-01
>>> dd.plugins.periods.fix_y2k = False  # Restore default value

Shifted years

When your fiscal or academic year starts in another month than January, you set periods.start_month to the number of the first month of your fiscal year.

On a site with shifted year, Lino represents the stored year as “YEAR/YEAR+1”

>>> dd.plugins.periods.start_month = 9
>>> run_test()
============ ========= ======== ============ ============ ============
 Date         year      period   ref          start date   end date
------------ --------- -------- ------------ ------------ ------------
 1985-02-03   1984/85   02       1984/85-02   1985-02-01   1985-02-28
 1999-09-01   1999/00   09       1999/00-09   1999-09-01   1999-09-30
 2000-01-01   1999/00   01       1999/00-01   2000-01-01   2000-01-31
 2015-04-27   2014/15   04       2014/15-04   2015-04-01   2015-04-30
 2024-03-24   2023/24   03       2023/24-03   2024-03-01   2024-03-31
 2059-06-01   2058/59   06       2058/59-06   2059-06-01   2059-06-30
 2997-01-23   2996/97   01       2996/97-01   2997-01-01   2997-01-31
============ ========= ======== ============ ============ ============
>>> dd.plugins.periods.start_month = 1  # Restore default value

School years

A school year in Belgium starts in September and has two periods called “semesters”:

>>> dd.plugins.periods.year_name = "Year"
>>> dd.plugins.periods.period_name = "Period"
>>> dd.plugins.periods.short_ref = True
>>> dd.plugins.periods.start_month = 9
>>> dd.plugins.periods.period_type = periods.PeriodTypes.semester
>>> run_test()  
============ ======= ======== ========== ============ ============
 Date         year    period   ref        start date   end date
------------ ------- -------- ---------- ------------ ------------
 1985-02-03   84/85   S1       84/85-S1   1984-09-01   1985-02-28
 1999-09-01   99/00   S1       99/00-S1   1999-09-01   2000-02-29
 2000-01-01   99/00   S1       99/00-S1   1999-09-01   2000-02-29
 2015-04-27   14/15   S2       14/15-S2   2015-03-01   2015-08-31
 2024-03-24   23/24   S2       23/24-S2   2024-03-01   2024-08-31
 2059-06-01   58/59   S2       58/59-S2   2059-03-01   2059-08-31
 2997-01-23   96/97   S1       96/97-S1   2996-09-01   2997-02-28
============ ======= ======== ========== ============ ============

You can customize the format used to represent a period

>>> dd.plugins.periods.period_type.ref_template
'S{period}'
>>> dd.plugins.periods.period_type.ref_template = 'P{period}'
>>> run_test()  
============ ======= ======== ========== ============ ============
 Date         year    period   ref        start date   end date
------------ ------- -------- ---------- ------------ ------------
 1985-02-03   84/85   P1       84/85-P1   1984-09-01   1985-02-28
 1999-09-01   99/00   P1       99/00-P1   1999-09-01   2000-02-29
 2000-01-01   99/00   P1       99/00-P1   1999-09-01   2000-02-29
 2015-04-27   14/15   P2       14/15-P2   2015-03-01   2015-08-31
 2024-03-24   23/24   P2       23/24-P2   2024-03-01   2024-08-31
 2059-06-01   58/59   P2       58/59-P2   2059-03-01   2059-08-31
 2997-01-23   96/97   P1       96/97-P1   2996-09-01   2997-02-28
============ ======= ======== ========== ============ ============
>>> dd.plugins.periods.start_month = 1  # Restore default value
>>> dd.plugins.periods.short_ref = False
>>> dd.plugins.periods.period_type.ref_template = 'S{period}'

Plugin configuration settings

Here is a list of the plugin settings for this plugin.

periods.period_name
periods.period_name_plural
periods.year_name
periods.year_name_plural

The end-user designation of a “stored period” and a “stored year”, respectively.

periods.start_year

An integer with the calendar year in which this site starts working.

This is used by the std fixture to fill the default list of StoredYears, and by certain demo fixtures for generating demo invoices.

periods.fix_y2k

Whether to use a Y2K compatible representation for fiscal years. See The Y2K problem

periods.start_month

The number of the first month of your fiscal year. Allowed values are 1 to 12. Default value is 1 (January). See Shifted years.

periods.period_type

A string that specifies into what kinds of periods to subdivide a years. Default value is ‘month’. See Period types.

Class reference

class lino.modlib.periods.StoredYear

The Django model used to store a fiscal year.

start_date
end_date
state
class lino.modlib.periods.StoredPeriod

The Django model used to store an accounting period.

start_date
end_date
state
year
ref
class lino.modlib.periods.StoredYears

The fiscal years defined in this database.

class lino.modlib.periods.StoredPeriods

The accounting periods defined in this database.

class lino.modlib.periods.PeriodTypes

A list of choices for the values allowed as periods.period_type.

class lino.modlib.periods.PeriodRange

Model mixin for objects that cover a range of accounting periods.

start_period

The first period of the range to cover.

end_period

The last period of the range to cover.

Leave empty if you want only one period (specified in start_period). If this is non-empty, all periods between and including these two are covered.

get_period_filter(self, voucher_prefix, **kwargs)
class lino.modlib.periods.PeriodRangeObservable

Model mixin for objects that can be filtered by a range of accounting periods. This adds two parameter fields start_period and end_period to every table on this model.

Class attribute:

observable_period_field = 'accounting_period'

The name of the database field on the observed model to use for filtering.

class lino.modlib.periods.StoredPeriodRange

A parameter panel with two fields:

start_period

Start of observed period range.

end_period

Optional end of observed period range. Leave empty to consider only the Start period.