Compare commits
3 commits
18f194caa5
...
7504c676e7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7504c676e7 | ||
![]() |
321aeb827d | ||
![]() |
181f7405d6 |
8 changed files with 233 additions and 13 deletions
40
locale/de.po
40
locale/de.po
|
@ -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 #
|
||||
|
|
31
locale/en.po
31
locale/en.po
|
@ -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
16
message.xml
Normal 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>
|
104
planner.py
104
planner.py
|
@ -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,7 +191,9 @@ 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):
|
||||
|
@ -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,13 +461,17 @@ 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.last_day_of_month:
|
||||
self.monthday = None
|
||||
else:
|
||||
if self.monthday is None:
|
||||
self.monthday = 1
|
||||
self.setpos = None
|
||||
|
@ -442,12 +479,15 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
|
|||
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):
|
||||
|
|
12
planner.xml
12
planner.xml
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,7 @@ depends:
|
|||
extras_depend:
|
||||
cashbook_investment
|
||||
xml:
|
||||
message.xml
|
||||
group.xml
|
||||
planner.xml
|
||||
nextrun.xml
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Loading…
Reference in a new issue