add booking with category + test, todo: booking of asset

This commit is contained in:
Frederik Jaeckel 2024-03-03 21:54:16 +01:00
parent e51abf589b
commit b86e421298
6 changed files with 372 additions and 19 deletions

View file

@ -102,6 +102,10 @@ msgctxt "view:cashbook.planner:"
msgid "Booking"
msgstr "Buchung"
msgctxt "view:cashbook.planner:"
msgid "Booking text"
msgstr "Buchungstext"
msgctxt "field:cashbook.planner,company:"
msgid "Company"
msgstr "Unternehmen"
@ -238,18 +242,10 @@ msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Revenue"
msgstr "Einnahme"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Revenue Splitbooking"
msgstr "Einnahme Splitbuchung"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Expense"
msgstr "Ausgabe"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Expense Splitbooking"
msgstr "Ausgabe Splitbuchung"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Transfer from"
msgstr "Umbuchung von"
@ -258,6 +254,50 @@ msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Transfer to"
msgstr "Umbuchung nach"
msgctxt "field:cashbook.planner,currency_cashbook:"
msgid "Currency"
msgstr "Währung"
msgctxt "help:cashbook.planner,currency_cashbook:"
msgid "Currency of Cashbook"
msgstr "Währung des Kassenbuchs"
msgctxt "field:cashbook.planner,amount:"
msgid "Amount"
msgstr "Betrag"
msgctxt "field:cashbook.planner,category:"
msgid "Category"
msgstr "Kategorie"
msgctxt "help:cashbook.planner,category:"
msgid "Category for the planned booking"
msgstr "Kategorie für die geplante Buchung"
msgctxt "field:cashbook.planner,booktransf:"
msgid "Source/Dest"
msgstr "Quelle/Ziel"
msgctxt "field:cashbook.planner,owner_cashbook:"
msgid "Owner"
msgstr "Eigentümer"
msgctxt "field:cashbook.planner,state_cashbook:"
msgid "State of Cashbook"
msgstr "Status des Kassenbuchs"
msgctxt "field:cashbook.planner,subject:"
msgid "Booking text"
msgstr "Buchungstext"
msgctxt "field:cashbook.planner,wfcheck:"
msgid "Set to 'Checked'"
msgstr "auf 'Geprüft' setzen"
msgctxt "help:cashbook.planner,wfcheck:"
msgid "Switches the booking to the 'Verified' state."
msgstr "Schaltet die Buchung in den Zustand 'Geprüft'"
############################
# cashbook.planner.nextrun #

View file

@ -78,6 +78,10 @@ msgctxt "view:cashbook.planner:"
msgid "Booking"
msgstr "Booking"
msgctxt "view:cashbook.planner:"
msgid "Booking text"
msgstr "Booking text"
msgctxt "field:cashbook.planner,company:"
msgid "Company"
msgstr "Company"
@ -214,18 +218,10 @@ msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Revenue"
msgstr "Revenue"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Revenue Splitbooking"
msgstr "Revenue Splitbooking"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Expense"
msgstr "Expense"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Expense Splitbooking"
msgstr "Expense Splitbooking"
msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Transfer from"
msgstr "Transfer from"
@ -234,6 +230,50 @@ msgctxt "selection:cashbook.planner,bookingtype:"
msgid "Transfer to"
msgstr "Transfer to"
msgctxt "field:cashbook.planner,currency_cashbook:"
msgid "Currency"
msgstr "Currency"
msgctxt "help:cashbook.planner,currency_cashbook:"
msgid "Currency of Cashbook"
msgstr "Currency of Cashbook"
msgctxt "field:cashbook.planner,amount:"
msgid "Amount"
msgstr "Amount"
msgctxt "field:cashbook.planner,category:"
msgid "Category"
msgstr "Category"
msgctxt "help:cashbook.planner,category:"
msgid "Category for the planned booking"
msgstr "Category for the planned booking"
msgctxt "field:cashbook.planner,booktransf:"
msgid "Source/Dest"
msgstr "Source/Dest"
msgctxt "field:cashbook.planner,owner_cashbook:"
msgid "Owner"
msgstr "Owner"
msgctxt "field:cashbook.planner,state_cashbook:"
msgid "State of Cashbook"
msgstr "State of Cashbook"
msgctxt "field:cashbook.planner,subject:"
msgid "Booking text"
msgstr "Booking text"
msgctxt "field:cashbook.planner,wfcheck:"
msgid "Set to 'Checked'"
msgstr "Set to 'Checked'"
msgctxt "help:cashbook.planner,wfcheck:"
msgid "Switches the booking to the 'Verified' state."
msgstr "Switches the booking to the 'Verified' state."
msgctxt "model:cashbook.planner.nextrun,name:"
msgid "Next Execution Date"
msgstr "Next Execution Date"

View file

@ -3,6 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from decimal import Decimal
from datetime import date, timedelta
from dateutil.rrule import (
rrule, YEARLY, MONTHLY, WEEKLY, DAILY, MO, TU, WE, TH, FR, SA, SU)
@ -11,9 +12,15 @@ from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.report import Report
from trytond.pyson import Eval, Bool, If, And
from trytond.modules.cashbook.line import sel_bookingtype
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
sel_bookingtype = [
x for x in sel_bookingtype_cb if x[0]
in ['in', 'out', 'mvin', 'mvout']]
DEF_NONE = None
SEL_FREQU = [
('year', 'Yearly'),
@ -97,6 +104,48 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
bookingtype = fields.Selection(
string='Type', selection=sel_bookingtype, required=True,
help='Type of Booking')
currency_cashbook = fields.Function(fields.Many2One(
string='Currency', help='Currency of Cashbook',
model_name='currency.currency'), 'on_change_with_currency_cashbook')
amount = Monetary(
string='Amount', currency='currency_cashbook',
digits='currency_cashbook', required=True)
category = fields.Many2One(
string='Category', model_name='cashbook.category',
help='Category for the planned booking', depends=['bookingtype'],
states={
'required': Eval('bookingtype', '').in_(['in', 'out']),
'invisible': ~Eval('bookingtype', '').in_(['in', 'out'])})
party = fields.Many2One(
string='Party', model_name='party.party', depends=['bookingtype'],
states={
'required': Eval('bookingtype', '').in_(['in', 'out']),
'invisible': ~Eval('bookingtype', '').in_(['in', 'out'])})
booktransf = fields.Many2One(
string='Source/Dest',
ondelete='RESTRICT', model_name='cashbook.book',
domain=[
('owner.id', '=', Eval('owner_cashbook', -1)),
('id', '!=', Eval('cashbook', -1)),
('btype', '!=', None)],
states={
'readonly': Eval('state_cashbook', '') != 'open',
'invisible': ~Eval('bookingtype', '').in_(['mvin', 'mvout']),
'required': Eval('bookingtype', '').in_(['mvin', 'mvout'])},
depends=[
'state_cashbook', 'bookingtype', 'owner_cashbook', 'cashbook'])
owner_cashbook = fields.Function(fields.Many2One(
string='Owner', readonly=True,
states={'invisible': True}, model_name='res.user'),
'on_change_with_owner_cashbook')
state_cashbook = fields.Function(fields.Selection(
string='State of Cashbook',
readonly=True, states={'invisible': True}, selection=sel_state_book),
'on_change_with_state_cashbook')
subject = fields.Text(string='Booking text', required=True)
wfcheck = fields.Boolean(
string="Set to 'Checked'",
help="Switches the booking to the 'Verified' state.")
@classmethod
def __setup__(cls):
@ -177,6 +226,19 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
break
return result
@fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency_cashbook(self, name=None):
""" get currency of selected cashbook
Args:
name (str, optional): name of field. Defaults to None.
Returns:
int: id of cashbook currency
"""
if self.cashbook:
return self.cashbook.currency.id
@fields.depends('nextrun')
def on_change_with_nextrun_link(self, name=None):
""" get nextrun-record if exist
@ -229,6 +291,30 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
'setpos': self.setpos}
)])
@fields.depends('cashbook', '_parent_cashbook.owner')
def on_change_with_owner_cashbook(self, name=None):
""" get current owner
"""
if self.cashbook:
return self.cashbook.owner.id
@fields.depends('cashbook', '_parent_cashbook.state')
def on_change_with_state_cashbook(self, name=None):
""" get state of cashbook
"""
if self.cashbook:
return self.cashbook.state
@fields.depends('bookingtype', 'category', 'booktransf')
def on_change_bookingtype(self):
""" reset category/booktransf on change of bookingtype
"""
if self.bookingtype:
if self.bookingtype in ['in', 'out']:
self.booktransf = None
elif self.bookingtype in ['mvin', 'mvout']:
self.category = None
@fields.depends('frequ', 'setpos', 'weekday', 'monthday')
def on_change_frequ(self):
""" update fields
@ -254,6 +340,24 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
"""
self.on_change_frequ()
@classmethod
def default_wfcheck(cls):
""" False as default for wf-state 'checked'
Returns:
bool: False
"""
return False
@classmethod
def default_amount(cls):
""" default for amount
Returns:
Decimal: 0.00
"""
return Decimal('0.0')
@classmethod
def default_interval(cls):
""" get default for interval
@ -387,8 +491,40 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
def run_booking(cls, records):
""" do prepared booking
"""
pool = Pool()
IrDate = pool.get('ir.date')
Line = pool.get('cashbook.line')
to_create = []
to_create_check = []
for record in records:
print('-- booking:', record.rec_name)
line = {
'cashbook': record.cashbook.id,
'bookingtype': record.bookingtype,
'date': IrDate.today(),
'amount': record.amount,
'description': record.subject}
if record.bookingtype in ['in', 'out']:
if record.category:
line['category'] = record.category.id
if record.party:
line['party'] = record.party.id
elif record.bookingtype in ['mvin', 'mvout']:
if record.booktransf:
line['booktransf'] = record.booktransf.id
if record.wfcheck:
to_create_check.append(line)
else:
to_create.append(line)
if to_create_check:
lines = Line.create(to_create_check)
Line.wfcheck(lines)
if to_create:
Line.create(to_create)
@classmethod
def cronjob(cls):

View file

@ -3,6 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from decimal import Decimal
from unittest.mock import MagicMock
from trytond.tests.test_tryton import with_transaction
from trytond.pool import Pool
@ -19,6 +20,7 @@ class PlannerTestCase(object):
pool = Pool()
Book = pool.get('cashbook.book')
Planner = pool.get('cashbook.planner')
Party = pool.get('party.party')
types = self.prep_type()
company = self.prep_company()
@ -36,11 +38,18 @@ class PlannerTestCase(object):
}])
self.assertEqual(book.rec_name, 'Book 1 | 0.00 usd | Open')
category = self.prep_category()
party, = Party.create([{
'name': 'Party',
'addresses': [('create', [{}])]}])
job, = Planner.create([{
'cashbook': book.id,
'name': name,
'start_date': date(2022, 5, 1),
'bookingtype': 'out'}])
'bookingtype': 'out',
'category': category.id,
'party': party.id,
'subject': 'Booking text'}])
# check applied defaults
self.assertEqual(job.rec_name, 'Job 1')
self.assertEqual(job.start_date, date(2022, 5, 1))
@ -286,4 +295,111 @@ class PlannerTestCase(object):
IrDate.today = MagicMock(return_value=date.today())
@with_transaction()
def test_planner_cronjobs_booking_with_category(self):
""" create job, configure booking with category, run job
"""
pool = Pool()
Planner = pool.get('cashbook.planner')
IrDate = pool.get('ir.date')
Category = pool.get('cashbook.category')
Cashbook = pool.get('cashbook.book')
job = self.prep_create_job()
self.assertEqual(
job._compute_dates_by_rrule(
count=1, query_date=date(2022, 5, 1)), [
date(2022, 5, 1)])
IrDate.today = MagicMock(return_value=date(2022, 5, 24))
category, = Category.search([('name', '=', 'Cat1')])
Planner.write(*[
[job],
{
'name': 'Booking to category',
'amount': Decimal('10.0'),
'bookingtype': 'out',
'category': category.id,
'subject': 'booking ${month}/${year}, ${date}',
'wfcheck': True,
}])
self.assertEqual(job.rec_name, 'Booking to category')
self.assertEqual(job.cashbook.rec_name, 'Book 1 | 0.00 usd | Open')
self.assertEqual(len(job.cashbook.lines), 0)
job, = Planner.search([])
self.assertEqual(job.nextrun[0].date, date(2022, 6, 1))
IrDate.today = MagicMock(return_value=date(2022, 6, 1))
Planner.cronjob()
self.assertEqual(job.nextrun[0].date, date(2022, 7, 1))
# check cashbook
self.assertEqual(len(job.cashbook.lines), 1)
self.assertEqual(
job.cashbook.lines[0].rec_name,
"06/01/2022|Exp|-10.00 usd|booking " +
"${month}/${year}, ${date} [Cat1]")
self.assertEqual(job.cashbook.lines[0].state, 'check')
with Transaction().set_context({'date': date(2022, 6, 1)}):
cashbook, = Cashbook.browse([job.cashbook])
self.assertEqual(cashbook.rec_name, 'Book 1 | -10.00 usd | Open')
IrDate.today = MagicMock(return_value=date.today())
@with_transaction()
def test_planner_cronjobs_booking_transfer_nonasset_eur(self):
""" create job, configure transfer-booking to non-asset-cashbook,
run job
"""
pool = Pool()
Planner = pool.get('cashbook.planner')
IrDate = pool.get('ir.date')
Category = pool.get('cashbook.category')
Cashbook = pool.get('cashbook.book')
job = self.prep_create_job()
self.assertEqual(
job._compute_dates_by_rrule(
count=1, query_date=date(2022, 5, 1)), [
date(2022, 5, 1)])
IrDate.today = MagicMock(return_value=date(2022, 5, 24))
category, = Category.search([('name', '=', 'Cat1')])
Planner.write(*[
[job],
{
'name': 'Booking to category',
'amount': Decimal('10.0'),
'bookingtype': 'out',
'category': category.id,
'subject': 'booking ${month}/${year}, ${date}',
'wfcheck': True,
}])
self.assertEqual(job.rec_name, 'Booking to category')
self.assertEqual(job.cashbook.rec_name, 'Book 1 | 0.00 usd | Open')
self.assertEqual(len(job.cashbook.lines), 0)
job, = Planner.search([])
self.assertEqual(job.nextrun[0].date, date(2022, 6, 1))
IrDate.today = MagicMock(return_value=date(2022, 6, 1))
Planner.cronjob()
self.assertEqual(job.nextrun[0].date, date(2022, 7, 1))
# check cashbook
self.assertEqual(len(job.cashbook.lines), 1)
self.assertEqual(
job.cashbook.lines[0].rec_name,
"06/01/2022|Exp|-10.00 usd|booking " +
"${month}/${year}, ${date} [Cat1]")
self.assertEqual(job.cashbook.lines[0].state, 'check')
with Transaction().set_context({'date': date(2022, 6, 1)}):
cashbook, = Cashbook.browse([job.cashbook])
self.assertEqual(cashbook.rec_name, 'Book 1 | -10.00 usd | Open')
IrDate.today = MagicMock(return_value=date.today())
# end PlannerTestCase

View file

@ -41,6 +41,23 @@ full copyright notices and license terms. -->
<page id="booking" col="4" string="Booking">
<label name="bookingtype"/>
<field name="bookingtype"/>
<label name="amount"/>
<field name="amount"/>
<label name="category"/>
<field name="category"/>
<label name="party"/>
<field name="party"/>
<label name="booktransf"/>
<field name="booktransf" colspan="3"/>
<label name="wfcheck"/>
<field name="wfcheck"/>
<newline/>
<separator name="subject" colspan="4" string="Booking text"/>
<field name="subject" colspan="4"/>
</page>
<page name="description" col="1" string="Description">
<field name="description"/>

View file

@ -8,4 +8,8 @@ full copyright notices and license terms. -->
<field name="cashbook"/>
<field name="nextrun_link"/>
<field name="bookingtype"/>
<field name="amount"/>
<field name="category"/>
<field name="party"/>
<field name="booktransf"/>
</tree>