add form for nextrun-records, update of nextrun-records, tests
This commit is contained in:
parent
9f3e33d225
commit
0fc0c4c8b9
8 changed files with 472 additions and 194 deletions
22
locale/de.po
22
locale/de.po
|
@ -38,7 +38,7 @@ msgstr "geplante Buchungen"
|
|||
#################
|
||||
# ir.rule.group #
|
||||
#################
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_write_admin"
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_admin"
|
||||
msgid "Administrators: scheduled bookings read/write"
|
||||
msgstr "Administratoren: geplante Buchungen schreiben"
|
||||
|
||||
|
@ -58,6 +58,26 @@ msgctxt "model:ir.rule.group,name:rg_planner_companies"
|
|||
msgid "User in companies"
|
||||
msgstr "Benutzer im Unternehmen"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_nextrun_admin"
|
||||
msgid "Administrators: scheduled bookings read/write"
|
||||
msgstr "Administratoren: geplante Buchungen schreiben"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_owner"
|
||||
msgid "Owners: scheduled bookings"
|
||||
msgstr "Eigentümer: geplante Buchungen"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_read_nonowner"
|
||||
msgid "Observers: scheduled bookings read"
|
||||
msgstr "Beobachter: geplante Buchungen lesen"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_write_nonowner"
|
||||
msgid "Reviewer: scheduled bookings write"
|
||||
msgstr "Bearbeiter: geplante Buchungen schreiben"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_nextrun_companies"
|
||||
msgid "User in companies"
|
||||
msgstr "Benutzer im Unternehmen"
|
||||
|
||||
|
||||
####################
|
||||
# cashbook.planner #
|
||||
|
|
38
locale/en.po
38
locale/en.po
|
@ -18,7 +18,7 @@ msgctxt "model:ir.action,name:act_planner_view"
|
|||
msgid "Scheduled Bookings"
|
||||
msgstr "Scheduled Bookings"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_write_admin"
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_admin"
|
||||
msgid "Administrators: scheduled bookings read/write"
|
||||
msgstr "Administrators: scheduled bookings read/write"
|
||||
|
||||
|
@ -38,6 +38,26 @@ msgctxt "model:ir.rule.group,name:rg_planner_companies"
|
|||
msgid "User in companies"
|
||||
msgstr "User in companies"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_nextrun_admin"
|
||||
msgid "Administrators: scheduled bookings read/write"
|
||||
msgstr "Administrators: scheduled bookings read/write"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_owner"
|
||||
msgid "Owners: scheduled bookings"
|
||||
msgstr "Owners: scheduled bookings"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_read_nonowner"
|
||||
msgid "Observers: scheduled bookings read"
|
||||
msgstr "Observers: scheduled bookings read"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_planner_write_nonowner"
|
||||
msgid "Reviewer: scheduled bookings write"
|
||||
msgstr "Reviewer: scheduled bookings write"
|
||||
|
||||
msgctxt "model:ir.rule.group,name:rg_nextrun_companies"
|
||||
msgid "User in companies"
|
||||
msgstr "User in companies"
|
||||
|
||||
msgctxt "model:cashbook.planner,name:"
|
||||
msgid "Scheduled Booking"
|
||||
msgstr "Scheduled Booking"
|
||||
|
@ -166,6 +186,22 @@ msgctxt "help:cashbook.planner,setpos:"
|
|||
msgid "For example, if you want to run the rule on the second Wednesday of the month, enter 2 here."
|
||||
msgstr "For example, if you want to run the rule on the second Wednesday of the month, enter 2 here."
|
||||
|
||||
msgctxt "field:cashbook.planner,nextrun:"
|
||||
msgid "Next Execution Date"
|
||||
msgstr "Next Execution Date"
|
||||
|
||||
msgctxt "model:cashbook.planner.nextrun,name:"
|
||||
msgid "Next Execution Date"
|
||||
msgstr "Next Execution Date"
|
||||
|
||||
msgctxt "field:cashbook.planner.nextrun,planner:"
|
||||
msgid "Planner"
|
||||
msgstr "Planner"
|
||||
|
||||
msgctxt "field:cashbook.planner.nextrun,date:"
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
msgctxt "view:cashbook.book:"
|
||||
msgid "Scheduled Bookings"
|
||||
msgstr "Scheduled Bookings"
|
||||
|
|
145
nextrun.xml
Normal file
145
nextrun.xml
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the cashbook-planner from m-ds for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the
|
||||
full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
|
||||
<!-- views -->
|
||||
<record model="ir.ui.view" id="nextrun_view_list">
|
||||
<field name="model">cashbook.planner.nextrun</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">nextrun_list</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="nextrun_view_form">
|
||||
<field name="model">cashbook.planner.nextrun</field>
|
||||
<field name="type">form</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">nextrun_form</field>
|
||||
</record>
|
||||
|
||||
<!-- permission -->
|
||||
<!-- anon: deny all -->
|
||||
<record model="ir.model.access" id="access_nextrun-anon">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<!-- group_planner_write: read/write -->
|
||||
<record model="ir.model.access" id="access_nextrun-group_planner">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="group" ref="group_planner"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- permission by rule - admin-write -->
|
||||
<record model="ir.rule.group" id="rg_nextrun_admin">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="name">Administrators: scheduled bookings read/write</field>
|
||||
<field name="global_p" eval="False"/>
|
||||
<field name="default_p" eval="False"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rg_nextrun_admin-1">
|
||||
<field name="domain" eval="[]" pyson="1"/>
|
||||
<field name="rule_group" ref="rg_nextrun_admin"/>
|
||||
</record>
|
||||
<record model="ir.rule.group-res.group"
|
||||
id="rg_nextrun_admin-group_cashbook_admin">
|
||||
<field name="rule_group" ref="rg_nextrun_admin"/>
|
||||
<field name="group" ref="cashbook.group_cashbook_admin"/>
|
||||
</record>
|
||||
|
||||
<!-- permission by rule - read/write: owner -->
|
||||
<record model="ir.rule.group" id="rg_nextrun_owner">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="name">Owners: scheduled bookings</field>
|
||||
<field name="global_p" eval="False"/>
|
||||
<field name="default_p" eval="False"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rg_nextrun_owner-1">
|
||||
<field name="domain" eval="[
|
||||
('planner.cashbook.owner.id', '=', Eval('user_id', -1)),
|
||||
]" pyson="1"/>
|
||||
<field name="rule_group" ref="rg_nextrun_owner"/>
|
||||
</record>
|
||||
<record model="ir.rule.group-res.group"
|
||||
id="rg_nextrun_owner-group_planner">
|
||||
<field name="rule_group" ref="rg_nextrun_owner"/>
|
||||
<field name="group" ref="group_planner"/>
|
||||
</record>
|
||||
|
||||
<!-- permission by rule - read: observer -->
|
||||
<record model="ir.rule.group" id="rg_nextrun_read_nonowner">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="name">Observers: scheduled bookings read</field>
|
||||
<field name="global_p" eval="False"/>
|
||||
<field name="default_p" eval="False"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rg_nextrun_read_nonowner-1">
|
||||
<field name="domain" eval="[
|
||||
('planner.cashbook.observer.id', 'in', Eval('groups', [])),
|
||||
]" pyson="1"/>
|
||||
<field name="rule_group" ref="rg_nextrun_read_nonowner"/>
|
||||
</record>
|
||||
<record model="ir.rule.group-res.group"
|
||||
id="rg_nextrun_read_nonowner-group_planner">
|
||||
<field name="rule_group" ref="rg_nextrun_read_nonowner"/>
|
||||
<field name="group" ref="group_planner"/>
|
||||
</record>
|
||||
|
||||
<!-- permission by rule - write: reviewer -->
|
||||
<record model="ir.rule.group" id="rg_nextrun_write_nonowner">
|
||||
<field name="model" search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="name">Reviewer: scheduled bookings write</field>
|
||||
<field name="global_p" eval="False"/>
|
||||
<field name="default_p" eval="False"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rg_nextrun_write_nonowner-1">
|
||||
<field name="domain" eval="[
|
||||
('planner.cashbook.reviewer.id', 'in', Eval('groups', [])),
|
||||
]" pyson="1"/>
|
||||
<field name="rule_group" ref="rg_nextrun_write_nonowner"/>
|
||||
</record>
|
||||
<record model="ir.rule.group-res.group"
|
||||
id="rg_nextrun_write_nonowner-group_planner">
|
||||
<field name="rule_group" ref="rg_nextrun_write_nonowner"/>
|
||||
<field name="group" ref="group_planner"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rg_nextrun_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model"
|
||||
search="[('model', '=', 'cashbook.planner.nextrun')]"/>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="r_nextrun_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rg_nextrun_companies"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
50
planner.py
50
planner.py
|
@ -105,11 +105,11 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
where=t.end_date != DEF_NONE),
|
||||
})
|
||||
|
||||
def _compute_dates_by_rrule(self, start_date=None, count=5, params={}):
|
||||
def _compute_dates_by_rrule(self, query_date=None, count=5, params={}):
|
||||
""" run rrule with values from record or from 'params'
|
||||
|
||||
Args:
|
||||
start_date (date, optional): Start date as a filter for
|
||||
query_date (date, optional): Start date as a filter for
|
||||
recurrences. Defaults to None.
|
||||
count (int, optional): number of recurrences in result.
|
||||
Defaults to 5. max value = 100
|
||||
|
@ -160,8 +160,8 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
|
||||
result = []
|
||||
for x in dtrule:
|
||||
if (start_date and (x.date() >= start_date)) or \
|
||||
(start_date is None):
|
||||
if (query_date and (x.date() >= query_date)) or \
|
||||
(query_date is None):
|
||||
result.append(x.date())
|
||||
if len(result) >= count:
|
||||
break
|
||||
|
@ -178,7 +178,7 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
name (string, optional): name of field. Defaults to None.
|
||||
|
||||
context:
|
||||
start_date (date, optional): start date for dates in result,
|
||||
nextrun_querydate (date, optional): start date for dates in result,
|
||||
defaults to today if not set or None
|
||||
|
||||
Returns:
|
||||
|
@ -187,14 +187,14 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
IrDate = Pool().get('ir.date')
|
||||
context = Transaction().context
|
||||
|
||||
start_date = context.get('start_date', None)
|
||||
if not isinstance(start_date, date):
|
||||
start_date = IrDate.today()
|
||||
query_date = context.get('nextrun_querydate', None)
|
||||
if not isinstance(query_date, date):
|
||||
query_date = IrDate.today()
|
||||
|
||||
return ' | '.join([
|
||||
Report.format_date(x)
|
||||
for x in self._compute_dates_by_rrule(
|
||||
start_date=start_date,
|
||||
query_date=query_date,
|
||||
params={
|
||||
'start_date': self.start_date,
|
||||
'end_date': self.end_date,
|
||||
|
@ -291,6 +291,7 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
pool = Pool()
|
||||
IrDate = pool.get('ir.date')
|
||||
NextRun = pool.get('cashbook.planner.nextrun')
|
||||
context = Transaction().context
|
||||
|
||||
to_create = []
|
||||
to_write = []
|
||||
|
@ -303,10 +304,14 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
elif record.active:
|
||||
# get next-run date
|
||||
next_date = record._compute_dates_by_rrule(
|
||||
start_date=IrDate.today(), count=1)
|
||||
query_date=context.get(
|
||||
'nextrun_querydate', IrDate.today()),
|
||||
count=1)
|
||||
if next_date:
|
||||
next_date = next_date[0]
|
||||
else:
|
||||
if record.nextrun:
|
||||
to_delete.extend(record.nextrun)
|
||||
continue
|
||||
|
||||
if not record.nextrun:
|
||||
|
@ -324,6 +329,31 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
if to_write:
|
||||
NextRun.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
""" update nextrun-records on create of planner-records
|
||||
|
||||
Args:
|
||||
vlist (list of dict): values to create records
|
||||
|
||||
Returns:
|
||||
list: created records
|
||||
"""
|
||||
records = super(ScheduledBooking, cls).create(vlist)
|
||||
cls.update_next_occurence(records)
|
||||
return records
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
""" update nextrun-records on create of planner-records
|
||||
"""
|
||||
to_update = []
|
||||
actions = iter(args)
|
||||
for records, values in zip(actions, actions):
|
||||
to_update.extend(records)
|
||||
super(ScheduledBooking, cls).write(*args)
|
||||
cls.update_next_occurence(records)
|
||||
|
||||
@classmethod
|
||||
def cronjob(cls):
|
||||
pass
|
||||
|
|
|
@ -24,7 +24,7 @@ class PlannerTestCase(object):
|
|||
job = None
|
||||
with Transaction().set_context({
|
||||
'company': company.id,
|
||||
'start_date': date(2022, 5, 1)}):
|
||||
'nextrun_querydate': date(2022, 5, 1)}):
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
'btype': types.id,
|
||||
|
@ -63,7 +63,7 @@ class PlannerTestCase(object):
|
|||
job = self.prep_create_job()
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
start_date=date(2022, 5, 1), count=5), [
|
||||
query_date=date(2022, 5, 1), count=5), [
|
||||
date(2022, 5, 1), date(2022, 6, 1),
|
||||
date(2022, 7, 1), date(2022, 8, 1),
|
||||
date(2022, 9, 1)])
|
||||
|
@ -78,7 +78,7 @@ class PlannerTestCase(object):
|
|||
Planner.write(*[[job], {
|
||||
'end_date': date(2022, 9, 15), 'monthday': 3}])
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(start_date=date(2022, 5, 1)), [
|
||||
job._compute_dates_by_rrule(query_date=date(2022, 5, 1)), [
|
||||
date(2022, 5, 3), date(2022, 6, 3),
|
||||
date(2022, 7, 3), date(2022, 8, 3),
|
||||
date(2022, 9, 3)])
|
||||
|
@ -86,7 +86,7 @@ class PlannerTestCase(object):
|
|||
Planner.write(*[[job], {
|
||||
'end_date': date(2022, 9, 15), 'monthday': 3, 'interval': 2}])
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(start_date=date(2022, 5, 1)), [
|
||||
job._compute_dates_by_rrule(query_date=date(2022, 5, 1)), [
|
||||
date(2022, 5, 3), date(2022, 7, 3),
|
||||
date(2022, 9, 3)])
|
||||
|
||||
|
@ -95,7 +95,7 @@ class PlannerTestCase(object):
|
|||
'end_date': None, 'monthday': 1, 'interval': 1}])
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
start_date=date(2022, 5, 1),
|
||||
query_date=date(2022, 5, 1),
|
||||
params={
|
||||
'end_date': date(2022, 9, 15), 'monthday': 3,
|
||||
'interval': 2}),
|
||||
|
@ -104,7 +104,7 @@ class PlannerTestCase(object):
|
|||
# 1st wednesday of each 2nd month
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
start_date=date(2022, 5, 1),
|
||||
query_date=date(2022, 5, 1),
|
||||
params={
|
||||
'end_date': date(2022, 9, 15), 'weekday': '2',
|
||||
'interval': 2, 'setpos': 1, 'monthday': None}),
|
||||
|
@ -113,7 +113,7 @@ class PlannerTestCase(object):
|
|||
# 2nd wednesday of each 2nd month
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
start_date=date(2022, 5, 1),
|
||||
query_date=date(2022, 5, 1),
|
||||
params={
|
||||
'end_date': date(2022, 9, 15), 'weekday': '2',
|
||||
'interval': 2, 'setpos': 2, 'monthday': None}),
|
||||
|
@ -122,7 +122,7 @@ class PlannerTestCase(object):
|
|||
# 2nd wednesday of each month, 6x occurences
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
start_date=date(2022, 5, 1), count=6,
|
||||
query_date=date(2022, 5, 1), count=6,
|
||||
params={
|
||||
'weekday': '2', 'end_date': None,
|
||||
'interval': 1, 'setpos': 2, 'monthday': None}),
|
||||
|
@ -216,10 +216,38 @@ class PlannerTestCase(object):
|
|||
job = self.prep_create_job()
|
||||
self.assertEqual(
|
||||
job._compute_dates_by_rrule(
|
||||
count=1, start_date=date(2022, 5, 1)), [
|
||||
count=1, query_date=date(2022, 5, 1)), [
|
||||
date(2022, 5, 1)])
|
||||
|
||||
job.update_next_occurence([job])
|
||||
with Transaction().set_context({
|
||||
'nextrun_querydate': date(2022, 5, 25)}):
|
||||
Planner.update_next_occurence([job])
|
||||
self.assertEqual(len(job.nextrun), 1)
|
||||
self.assertEqual(job.nextrun[0].date, date(2022, 6, 1))
|
||||
|
||||
with Transaction().set_context({
|
||||
'nextrun_querydate': date(2022, 5, 30)}):
|
||||
Planner.update_next_occurence([job])
|
||||
self.assertEqual(len(job.nextrun), 1)
|
||||
self.assertEqual(job.nextrun[0].date, date(2022, 6, 1))
|
||||
|
||||
with Transaction().set_context({
|
||||
'nextrun_querydate': date(2022, 6, 1)}):
|
||||
Planner.update_next_occurence([job])
|
||||
self.assertEqual(len(job.nextrun), 1)
|
||||
self.assertEqual(job.nextrun[0].date, date(2022, 6, 1))
|
||||
|
||||
with Transaction().set_context({
|
||||
'nextrun_querydate': date(2022, 6, 2)}):
|
||||
Planner.update_next_occurence([job])
|
||||
self.assertEqual(len(job.nextrun), 1)
|
||||
self.assertEqual(job.nextrun[0].date, date(2022, 7, 1))
|
||||
|
||||
with Transaction().set_context({
|
||||
'nextrun_querydate': date(2022, 6, 2)}):
|
||||
# set end-date to check delete of futore runs
|
||||
Planner.write(*[[job], {'end_date': date(2022, 6, 20)}])
|
||||
# write to planner-record updates nextrun-records too
|
||||
self.assertEqual(len(job.nextrun), 0)
|
||||
|
||||
# end PlannerTestCase
|
||||
|
|
|
@ -7,6 +7,7 @@ extras_depend:
|
|||
xml:
|
||||
group.xml
|
||||
planner.xml
|
||||
nextrun.xml
|
||||
cashbook.xml
|
||||
cron.xml
|
||||
menu.xml
|
||||
|
|
10
view/nextrun_form.xml
Normal file
10
view/nextrun_form.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the cashbook-planner from m-ds for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the
|
||||
full copyright notices and license terms. -->
|
||||
<form col="4">
|
||||
<label name="planner"/>
|
||||
<field name="planner"/>
|
||||
<label name="date"/>
|
||||
<field name="date"/>
|
||||
</form>
|
8
view/nextrun_list.xml
Normal file
8
view/nextrun_list.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of the cashbook-planner from m-ds for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the
|
||||
full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="planner"/>
|
||||
<field name="date"/>
|
||||
</tree>
|
Loading…
Reference in a new issue