add form for nextrun-records, update of nextrun-records, tests

This commit is contained in:
Frederik Jaeckel 2024-02-29 23:20:19 +01:00
parent 9f3e33d225
commit 0fc0c4c8b9
8 changed files with 472 additions and 194 deletions

View file

@ -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 #

View file

@ -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
View 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>

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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>