Compare commits

..

3 commits

Author SHA1 Message Date
=Frederik Jaeckel
7504c676e7 add notify 2024-09-28 20:42:32 +02:00
=Frederik Jaeckel
321aeb827d add button 'execute booking now' 2024-09-28 18:30:43 +02:00
=Frederik Jaeckel
181f7405d6 recurrence: add last-day-of-month 2024-09-27 23:24:48 +02:00
8 changed files with 233 additions and 13 deletions

View file

@ -3,6 +3,18 @@ msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
##############
# ir.message #
##############
msgctxt "model:ir.message,text:msg_title_notify"
msgid "Cashbook Scheduled Booking"
msgstr "Kassenbuch geplante Buchung"
msgctxt "model:ir.message,text:msg_text_notify"
msgid "The following transaction was generated: %(bname)s"
msgstr "Die folgende Buchung wurde erzeugt: %(bname)s"
###########
# ir.cron #
###########
@ -35,6 +47,18 @@ msgid "Scheduled Bookings"
msgstr "geplante Buchungen"
###################
# ir.model.button #
###################
msgctxt "model:ir.model.button,string:book_now_button"
msgid "Execute Booking Now"
msgstr "Buchung jetzt ausführen"
msgctxt "model:ir.model.button,help:book_now_button"
msgid "The planned booking is brought forward and executed now. The next posting is then scheduled regularly for the following execution."
msgstr "Die geplante Buchung wird vorgezogen und jetzt ausgeführt. Die nächste Buchung wird dann regulär für die nachfolgende Ausführung geplant."
#################
# ir.rule.group #
#################
@ -366,6 +390,22 @@ msgctxt "help:cashbook.planner,move_event:"
msgid "If the date of execution falls on a weekend or holiday, it can be moved to a business day."
msgstr "Wenn das Datum der Ausführung auf ein Wochenende oder Feiertag fällt, kann es auf einen Geschäftstag verschoben werden."
msgctxt "field:cashbook.planner,last_day_of_month:"
msgid "Last day of the month"
msgstr "letzer Tag des Monats"
msgctxt "help:cashbook.planner,last_day_of_month:"
msgid "The booking is made on the last day of the month."
msgstr "Die Buchung wird am letzten Tag des Monats ausgeführt."
msgctxt "field:cashbook.planner,notify_bycron:"
msgid "Notify"
msgstr "Benachrichtigen"
msgctxt "help:cashbook.planner,notify_bycron:"
msgid "A notification will appear in the web browser when the booking has been created."
msgstr "Es wird eine Benachrichtigung im Webbrowser angezeigt wenn die Buchung erstellt wurde."
############################
# cashbook.planner.nextrun #

View file

@ -2,6 +2,14 @@
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "model:ir.message,text:msg_title_notify"
msgid "Cashbook Scheduled Booking"
msgstr "Cashbook Scheduled Booking"
msgctxt "model:ir.message,text:msg_text_notify"
msgid "The following transaction was generated: %(bname)s"
msgstr "The following transaction was generated: %(bname)s"
msgctxt "selection:ir.cron,method:"
msgid "Execute scheduled bookings"
msgstr "Execute scheduled bookings"
@ -18,6 +26,14 @@ msgctxt "model:ir.action,name:act_planner_view"
msgid "Scheduled Bookings"
msgstr "Scheduled Bookings"
msgctxt "model:ir.model.button,string:book_now_button"
msgid "Execute Booking Now"
msgstr "Execute Booking Now"
msgctxt "model:ir.model.button,help:book_now_button"
msgid "The planned booking is brought forward and executed now. The next posting is then scheduled regularly for the following execution."
msgstr "The planned booking is brought forward and executed now. The next posting is then scheduled regularly for the following execution."
msgctxt "model:ir.rule.group,name:rg_planner_admin"
msgid "Administrators: scheduled bookings read/write"
msgstr "Administrators: scheduled bookings read/write"
@ -374,3 +390,18 @@ msgctxt "view:cashbook.line:"
msgid "Scheduled Bookings"
msgstr "Scheduled Bookings"
msgctxt "field:cashbook.planner,last_day_of_month:"
msgid "Last day of the month"
msgstr "Last day of the month"
msgctxt "help:cashbook.planner,last_day_of_month:"
msgid "The booking is made on the last day of the month."
msgstr "The booking is made on the last day of the month."
msgctxt "field:cashbook.planner,notify_bycron:"
msgid "Notify"
msgstr "Notify"
msgctxt "help:cashbook.planner,notify_bycron:"
msgid "A notification will appear in the web browser when the booking has been created."
msgstr "A notification will appear in the web browser when the booking has been created."

16
message.xml Normal file
View file

@ -0,0 +1,16 @@
<?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>
<record model="ir.message" id="msg_title_notify">
<field name="text">Cashbook Scheduled Booking</field>
</record>
<record model="ir.message" id="msg_text_notify">
<field name="text">The following transaction was generated: %(bname)s</field>
</record>
</data>
</tryton>

View file

@ -14,6 +14,7 @@ from trytond.pool import Pool
from trytond.report import Report
from trytond.i18n import gettext
from trytond.pyson import Eval, Bool, If, And
from trytond.bus import notify
from trytond.modules.currency.fields import Monetary
from trytond.modules.cashbook.book import sel_state_book
from trytond.modules.cashbook.line import sel_bookingtype as sel_bookingtype_cb
@ -91,7 +92,14 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
[('monthday', '>=', 1), ('monthday', '<=', 31)],
('monthday', '=', None))],
depends=['weekday', 'frequ'],
states={'required': COND_MONTHDAY, 'invisible': ~COND_MONTHDAY})
states={
'required': And(COND_MONTHDAY, ~Eval('last_day_of_month', False)),
'invisible': ~And(
COND_MONTHDAY, ~Eval('last_day_of_month', False))})
last_day_of_month = fields.Boolean(
string='Last day of the month', depends=['weekday', 'frequ'],
help='The booking is made on the last day of the month.',
states={'invisible': ~COND_MONTHDAY})
interval = fields.Integer(
string='Interval', required=True,
help='Select an interval to run the rule on every n-th date.',
@ -110,6 +118,9 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
string='If no business day', required=True, selection=SEL_MOVE_EVENT,
help='If the date of execution falls on a weekend or holiday, ' +
'it can be moved to a business day.')
notify_bycron = fields.Boolean(
string='Notify', help='A notification will appear in the web ' +
'browser when the booking has been created.')
bookingtype = fields.Selection(
string='Type', selection=sel_bookingtype, required=True,
@ -180,8 +191,10 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
Index(
t,
(t.end_date, Index.Range(order='ASC')),
where=t.end_date != DEF_NONE),
})
where=t.end_date != DEF_NONE)})
cls._buttons.update({
'booknow': {'readonly': ~Eval('active', False)},
})
def get_rec_name(self, name=None):
""" get formatted name of record
@ -263,6 +276,8 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
count = 5
count = 1 if count < 1 else 100 if count > 100 else count
last_day_of_month = params.get(
'last_day_of_month', self.last_day_of_month)
end_date = params.get('end_date', self.end_date)
frequ = pfrequ[params.get('frequ', self.frequ)]
@ -283,6 +298,18 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
interval = 1
interval = 1 if interval < 1 else 10 if interval > 10 else interval
# last-day-of-month: set date short before end of month,
# then compute move result to end of month
updt_lastday = False
if last_day_of_month and (frequ == MONTHLY) and not pweekday:
monthday = 28
updt_lastday = True
lastday_valid = last_day_of_month and (
frequ == MONTHLY) and (pweekday is None)
assert (lastday_valid or not last_day_of_month), \
('last-day-of-month can only be used with frequ=month ' +
'and weekday=99.')
assert (monthday is None) or (pweekday is None), \
"weekday and monthday cannot be used together"
@ -297,7 +324,12 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
for x in dtrule:
if (query_date and (x.date() >= query_date)) or \
(query_date is None):
x_date = get_moved_date(x.date(), move_event)
x_date = x.date()
if updt_lastday:
x_date = (
(x_date + timedelta(days=5)).replace(day=1) -
timedelta(days=1))
x_date = get_moved_date(x_date, move_event)
# if date was re-arranged backwards and we are before
# query_date - skip it
@ -368,7 +400,7 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
@fields.depends(
'start_date', 'end_date', 'frequ', 'weekday', 'monthday',
'interval', 'setpos', 'move_event')
'interval', 'setpos', 'move_event', 'last_day_of_month')
def on_change_with_nextdates(self, name=None):
""" Calculates the next 5 appointments based on the configured rule,
returns a formatted date list
@ -401,7 +433,8 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
'weekday': self.weekday,
'monthday': self.monthday,
'interval': self.interval,
'setpos': self.setpos}
'setpos': self.setpos,
'last_day_of_month': self.last_day_of_month}
)])
@fields.depends('cashbook', '_parent_cashbook.owner')
@ -428,26 +461,33 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
elif self.bookingtype in ['mvin', 'mvout']:
self.category = None
@fields.depends('frequ', 'setpos', 'weekday', 'monthday')
@fields.depends(
'frequ', 'setpos', 'weekday', 'monthday', 'last_day_of_month')
def on_change_frequ(self):
""" update fields
"""
if self.frequ and self.frequ == 'month':
if self.weekday:
if self.weekday == '99':
if self.monthday is None:
self.monthday = 1
if self.last_day_of_month:
self.monthday = None
else:
if self.monthday is None:
self.monthday = 1
self.setpos = None
else:
if self.setpos is None:
self.setpos = 1
self.monthday = None
self.last_day_of_month = False
else:
self.setpos = None
self.monthday = None
self.weekday = '99'
self.last_day_of_month = False
@fields.depends('frequ', 'setpos', 'weekday', 'monthday')
@fields.depends(
'frequ', 'setpos', 'weekday', 'monthday', 'last_day_of_month')
def on_change_weekday(self):
""" clear day-of-month if weekday is used
"""
@ -546,6 +586,15 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
"""
return 1
@classmethod
def default_last_day_of_month(cls):
""" get default for last-day-of-month
Returns:
boolean: False
"""
return False
@classmethod
def default_frequ(cls):
""" get default for frequency
@ -569,6 +618,15 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
IrDate = Pool().get('ir.date')
return IrDate.today()
@classmethod
def default_notify_bycron(cls):
""" get False as default
Returns:
boolean: False
"""
return False
@classmethod
def fill_placeholder(cls, linedata):
""" replace placeholder in description
@ -687,6 +745,20 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
if to_write:
NextRun.write(*to_write)
@classmethod
@ModelView.button
def booknow(cls, records):
""" run planned booking now
"""
to_work = [x for x in records if x.active and x.nextrun_date]
cls.run_booking(to_work)
for record in to_work:
if record.active:
cls.update_next_occurence(
[record],
query_date=record.nextrun_date + timedelta(days=1))
@classmethod
def create(cls, vlist):
""" update nextrun-records on create of planner-records
@ -784,11 +856,27 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
else:
to_create.append(line)
to_notify = []
if to_create_check:
lines = Line.create(to_create_check)
Line.wfcheck(lines)
to_notify.extend([
x for x in lines
if x.planners[0].notify_bycron])
if to_create:
Line.create(to_create)
lines = Line.create(to_create)
to_notify.extend([
x for x in lines
if x.planners[0].notify_bycron])
for line in to_notify:
notify(
title=gettext('cashbook_planner.msg_title_notify'),
body=gettext(
'cashbook_planner.msg_text_notify',
bname=line.rec_name),
user=line.cashbook.owner.id)
@classmethod
def cronjob(cls):

View file

@ -157,5 +157,17 @@ full copyright notices and license terms. -->
<field name="rule_group" ref="rg_planner_companies"/>
</record>
<!-- button -->
<record model="ir.model.button" id="book_now_button">
<field name="name">booknow</field>
<field name="string">Execute Booking Now</field>
<field name="help">The planned booking is brought forward and executed now. The next posting is then scheduled regularly for the following execution.</field>
<field name="model" search="[('model', '=', 'cashbook.planner')]"/>
</record>
<record model="ir.model.button-res.group" id="book_now_button-group_planner">
<field name="button" ref="book_now_button"/>
<field name="group" ref="group_planner"/>
</record>
</data>
</tryton>

View file

@ -245,6 +245,17 @@ class PlannerTestCase(object):
[date(2022, 5, 11), date(2022, 6, 8), date(2022, 7, 13),
date(2022, 8, 10), date(2022, 9, 14), date(2022, 10, 12)])
# last day of month
self.assertEqual(
job._compute_dates_by_rrule(
query_date=date(2022, 5, 1), count=6,
params={
'weekday': '99', 'end_date': None, 'frequ': 'month',
'interval': 1, 'setpos': None, 'monthday': None,
'last_day_of_month': True}),
[date(2022, 5, 31), date(2022, 6, 30), date(2022, 7, 31),
date(2022, 8, 31), date(2022, 9, 30), date(2022, 10, 31)])
# set up holidays
cfg1 = Config(
holidays='01-01;05-01;easter:+1;easter:-2;ascension;whitsun:+1')
@ -377,6 +388,23 @@ class PlannerTestCase(object):
'setpos': 5, 'monthday': None, 'weekday': '2',
'end_date': None}])
@with_transaction()
def test_planner_run_booking_now(self):
""" create job, press button 'booknow'
"""
Planner = Pool().get('cashbook.planner')
job = self.prep_create_job()
self.assertEqual(
job.rec_name, "Job 1|Book 1|Exp|Cat1|05/01/2022|usd0.00")
self.assertEqual(
job._compute_dates_by_rrule(
count=1, query_date=date(2022, 5, 1)), [
date(2022, 5, 1)])
Planner.booknow([job])
self.assertEqual(
job.rec_name, "Job 1|Book 1|Exp|Cat1|06/01/2022|usd0.00")
@with_transaction()
def test_planner_create_update_nextrun(self):
""" create job, check nextrun-record

View file

@ -5,6 +5,7 @@ depends:
extras_depend:
cashbook_investment
xml:
message.xml
group.xml
planner.xml
nextrun.xml

View file

@ -12,7 +12,8 @@ full copyright notices and license terms. -->
<label name="active"/>
<field name="active"/>
<label name="nextrun_date" colspan="5"/>
<button colspan="2" name="booknow"/>
<label name="nextrun_date" colspan="3"/>
<field name="nextrun_date"/>
<notebook colspan="6">
@ -34,6 +35,8 @@ full copyright notices and license terms. -->
<field name="weekday"/>
<label name="monthday"/>
<field name="monthday"/>
<label name="last_day_of_month"/>
<field name="last_day_of_month"/>
<label name="move_event"/>
<field name="move_event"/>
@ -58,7 +61,8 @@ full copyright notices and license terms. -->
<label name="wfcheck"/>
<field name="wfcheck"/>
<newline/>
<label name="notify_bycron"/>
<field name="notify_bycron"/>
<separator name="subject" colspan="4" string="Booking text"/>
<field name="subject" colspan="4"/>