konfig: kassenbuch-default für buchungswizard

buchungswizard: ok + test
This commit is contained in:
Frederik Jaeckel 2022-09-07 11:36:37 +02:00
parent 326d87f758
commit 953bf248a4
8 changed files with 377 additions and 12 deletions

View file

@ -15,6 +15,9 @@ field_done = fields.Boolean(string='Done',
help='Show cashbook lines in Done-state.') help='Show cashbook lines in Done-state.')
field_catnamelong = fields.Boolean(string='Category: Show long name', field_catnamelong = fields.Boolean(string='Category: Show long name',
help='Shows the long name of the category in the Category field of a cash book line.') help='Shows the long name of the category in the Category field of a cash book line.')
field_defbook = fields.Many2One(string='Default Cashbook',
help='The default cashbook is selected when you open the booking wizard.',
model_name='cashbook.book', ondelete='SET NULL')
class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
@ -36,6 +39,7 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
checked = fields.MultiValue(field_checked) checked = fields.MultiValue(field_checked)
done = fields.MultiValue(field_done) done = fields.MultiValue(field_done)
catnamelong = fields.MultiValue(field_catnamelong) catnamelong = fields.MultiValue(field_catnamelong)
defbook = fields.MultiValue(field_defbook)
@classmethod @classmethod
def multivalue_model(cls, field): def multivalue_model(cls, field):
@ -44,7 +48,7 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
pool = Pool() pool = Pool()
if field in ['date_from', 'date_to', 'checked', 'done', if field in ['date_from', 'date_to', 'checked', 'done',
'catnamelong']: 'catnamelong', 'defbook']:
return pool.get('cashbook.configuration_user') return pool.get('cashbook.configuration_user')
return super(Configuration, cls).multivalue_model(field) return super(Configuration, cls).multivalue_model(field)
@ -82,6 +86,7 @@ class UserConfiguration(ModelSQL, UserValueMixin):
checked = field_checked checked = field_checked
done = field_done done = field_done
catnamelong = field_catnamelong catnamelong = field_catnamelong
defbook = field_defbook
@classmethod @classmethod
def default_checked(cls): def default_checked(cls):

View file

@ -998,6 +998,10 @@ msgctxt "model:cashbook.configuration,name:"
msgid "Configuration" msgid "Configuration"
msgstr "Konfiguration" msgstr "Konfiguration"
msgctxt "view:cashbook.configuration:"
msgid "Enter Booking Wizard"
msgstr "Dialog: Buchung eingeben"
msgctxt "view:cashbook.configuration:" msgctxt "view:cashbook.configuration:"
msgid "Open Cashbook Wizard" msgid "Open Cashbook Wizard"
msgstr "Dialog: Kassenbuch öffnen" msgstr "Dialog: Kassenbuch öffnen"
@ -1006,6 +1010,14 @@ msgctxt "view:cashbook.configuration:"
msgid "Cashbook" msgid "Cashbook"
msgstr "Kassenbuch" msgstr "Kassenbuch"
msgctxt "field:cashbook.configuration,defbook:"
msgid "Default Cashbook"
msgstr "Standardkassenbuch"
msgctxt "help:cashbook.configuration,defbook:"
msgid "The default cashbook is selected when you open the booking wizard."
msgstr "Das Standardkassenbuch wird beim Öffnen des Buchungswizards ausgewählt."
msgctxt "field:cashbook.configuration,date_from:" msgctxt "field:cashbook.configuration,date_from:"
msgid "Start Date" msgid "Start Date"
msgstr "Beginndatum" msgstr "Beginndatum"
@ -1078,6 +1090,14 @@ msgctxt "help:cashbook.configuration_user,catnamelong:"
msgid "Shows the long name of the category in the Category field of a cash book line." msgid "Shows the long name of the category in the Category field of a cash book line."
msgstr "Zeigt im Feld 'Kategorie' einer Kassenbuchzeile den langen Namen der Kategorie." msgstr "Zeigt im Feld 'Kategorie' einer Kassenbuchzeile den langen Namen der Kategorie."
msgctxt "field:cashbook.configuration_user,defbook:"
msgid "Default Cashbook"
msgstr "Standardkassenbuch"
msgctxt "help:cashbook.configuration_user,defbook:"
msgid "The default cashbook is selected when you open the booking wizard."
msgstr "Das Standardkassenbuch wird beim Öffnen des Buchungswizards ausgewählt."
################## ##################
# cashbook.recon # # cashbook.recon #
@ -1270,6 +1290,10 @@ msgctxt "model:cashbook.enterbooking.start,name:"
msgid "Enter Booking" msgid "Enter Booking"
msgstr "Buchung eingeben" msgstr "Buchung eingeben"
msgctxt "view:cashbook.enterbooking.start:"
msgid "Description"
msgstr "Beschreibung"
msgctxt "field:cashbook.enterbooking.start,cashbook:" msgctxt "field:cashbook.enterbooking.start,cashbook:"
msgid "Cashbook" msgid "Cashbook"
msgstr "Kassenbuch" msgstr "Kassenbuch"
@ -1322,6 +1346,21 @@ msgctxt "field:cashbook.enterbooking.start,amount:"
msgid "Amount" msgid "Amount"
msgstr "Betrag" msgstr "Betrag"
msgctxt "field:cashbook.enterbooking.start,owner_cashbook:"
msgid "Owner"
msgstr "Eigentümer"
msgctxt "field:cashbook.enterbooking.start,category:"
msgid "Category"
msgstr "Kategorie"
msgctxt "field:cashbook.enterbooking.start,booktransf:"
msgid "Source/Dest"
msgstr "Quelle/Ziel"
msgctxt "field:cashbook.enterbooking.start,party:"
msgid "Party"
msgstr "Partei"
######################### #########################
@ -1338,3 +1377,7 @@ msgstr "Abbruch"
msgctxt "wizard_button:cashbook.enterbooking,start,save_:" msgctxt "wizard_button:cashbook.enterbooking,start,save_:"
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
msgctxt "wizard_button:cashbook.enterbooking,start,savenext_:"
msgid "Save & Next"
msgstr "Speichern & Weiter"

View file

@ -11,11 +11,14 @@ from trytond.modules.cashbook.tests.test_splitline import SplitLineTestCase
from trytond.modules.cashbook.tests.test_config import ConfigTestCase from trytond.modules.cashbook.tests.test_config import ConfigTestCase
from trytond.modules.cashbook.tests.test_category import CategoryTestCase from trytond.modules.cashbook.tests.test_category import CategoryTestCase
from trytond.modules.cashbook.tests.test_reconciliation import ReconTestCase from trytond.modules.cashbook.tests.test_reconciliation import ReconTestCase
from trytond.modules.cashbook.tests.test_bookingwiz import BookingWizardTestCase
__all__ = ['suite'] __all__ = ['suite']
class CashbookTestCase(\ class CashbookTestCase(\
BookingWizardTestCase,\
ReconTestCase,\ ReconTestCase,\
CategoryTestCase,\ CategoryTestCase,\
ConfigTestCase,\ ConfigTestCase,\

179
tests/test_bookingwiz.py Normal file
View file

@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
# This file is part of the cashbook-module from m-ds for Tryton.
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
from decimal import Decimal
from unittest.mock import MagicMock
class BookingWizardTestCase(ModuleTestCase):
'Test cashbook booking wizard module'
module = 'cashbook'
@with_transaction()
def test_bookwiz_expense(self):
""" run booking-wizard to store expense
"""
pool = Pool()
BookingWiz = pool.get('cashbook.enterbooking', type='wizard')
Book = pool.get('cashbook.book')
Category = pool.get('cashbook.category')
Party = pool.get('party.party')
IrDate = pool.get('ir.date')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
}):
types = self.prep_type()
book, = Book.create([{
'name': 'Cash Book',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}])
party, = Party.create([{
'name': 'Foodshop Zehlendorf',
'addresses':[('create', [{}])],
}])
categories = Category.create([{
'name':'Income',
'cattype': 'in',
}, {
'name': 'Food',
'cattype': 'out',
}])
(sess_id, start_state, end_state) = BookingWiz.create()
w_obj = BookingWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
result = BookingWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['bookingtype'], 'out')
self.assertEqual(result['view']['defaults']['cashbook'], None)
self.assertEqual(result['view']['defaults']['amount'], None)
self.assertEqual(result['view']['defaults']['party'], None)
self.assertEqual(result['view']['defaults']['booktransf'], None)
self.assertEqual(result['view']['defaults']['description'], None)
self.assertEqual(result['view']['defaults']['category'], None)
self.assertEqual(len(book.lines), 0)
r1 = {
'amount': Decimal('10.0'),
'cashbook': book.id,
'party': party.id,
'description': 'Test 1',
'category': categories[1].id,
'bookingtype': 'out',
}
for x in r1.keys():
setattr(w_obj.start, x, r1[x])
IrDate.today = MagicMock(return_value=date(2022, 5, 1))
result = BookingWiz.execute(sess_id, {'start': r1}, 'save_')
BookingWiz.delete(sess_id)
IrDate.today = MagicMock(return_value=date.today())
self.assertEqual(len(book.lines), 1)
self.assertEqual(book.lines[0].rec_name, '05/01/2022|Exp|-10.00 usd|Test 1 [Food]')
@with_transaction()
def test_bookwiz_transfer(self):
""" run booking-wizard to store expense
"""
pool = Pool()
BookingWiz = pool.get('cashbook.enterbooking', type='wizard')
Book = pool.get('cashbook.book')
Category = pool.get('cashbook.category')
Party = pool.get('party.party')
IrDate = pool.get('ir.date')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
}):
types = self.prep_type()
books = Book.create([{
'name': 'Cash Book',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}, {
'name': 'Bank',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}])
party, = Party.create([{
'name': 'Foodshop Zehlendorf',
'addresses':[('create', [{}])],
}])
categories = Category.create([{
'name':'Income',
'cattype': 'in',
}, {
'name': 'Food',
'cattype': 'out',
}])
(sess_id, start_state, end_state) = BookingWiz.create()
w_obj = BookingWiz(sess_id)
self.assertEqual(start_state, 'start')
self.assertEqual(end_state, 'end')
result = BookingWiz.execute(sess_id, {}, start_state)
self.assertEqual(list(result.keys()), ['view'])
self.assertEqual(result['view']['defaults']['bookingtype'], 'out')
self.assertEqual(result['view']['defaults']['cashbook'], None)
self.assertEqual(result['view']['defaults']['amount'], None)
self.assertEqual(result['view']['defaults']['party'], None)
self.assertEqual(result['view']['defaults']['booktransf'], None)
self.assertEqual(result['view']['defaults']['description'], None)
self.assertEqual(result['view']['defaults']['category'], None)
self.assertEqual(len(books[0].lines), 0)
self.assertEqual(len(books[1].lines), 0)
r1 = {
'amount': Decimal('10.0'),
'cashbook': books[0].id,
'description': 'Test 1',
'booktransf': books[1].id,
'bookingtype': 'mvout',
}
for x in r1.keys():
setattr(w_obj.start, x, r1[x])
IrDate.today = MagicMock(return_value=date(2022, 5, 1))
result = BookingWiz.execute(sess_id, {'start': r1}, 'save_')
BookingWiz.delete(sess_id)
IrDate.today = MagicMock(return_value=date.today())
self.assertEqual(len(books[0].lines), 1)
self.assertEqual(len(books[1].lines), 0)
self.assertEqual(books[0].lines[0].rec_name,
'05/01/2022|to|-10.00 usd|Test 1 [Bank | 0.00 usd | Open]')
# end BookingWizardTestCase

View file

@ -42,6 +42,7 @@ class ConfigTestCase(ModuleTestCase):
self.assertEqual(cfg2.checked, True) self.assertEqual(cfg2.checked, True)
self.assertEqual(cfg2.done, False) self.assertEqual(cfg2.done, False)
self.assertEqual(cfg2.catnamelong, True) self.assertEqual(cfg2.catnamelong, True)
self.assertEqual(cfg2.defbook, None)
return cfg2 return cfg2
def prep_party(self, name='Party'): def prep_party(self, name='Party'):
@ -105,6 +106,37 @@ class ConfigTestCase(ModuleTestCase):
""" """
self.prep_config() self.prep_config()
@with_transaction()
def test_config_defbook(self):
""" create config, add default-cashbook
"""
pool = Pool()
Configuration = pool.get('cashbook.configuration')
Book = pool.get('cashbook.book')
self.prep_config()
types = self.prep_type()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
}])
self.assertEqual(book.name, 'Book 1')
cfg1 = Configuration.get_singleton()
Configuration.write(*[
[cfg1],
{
'defbook': book.id,
}])
cfg2 = Configuration.get_singleton()
self.assertEqual(cfg2.defbook.rec_name, 'Book 1 | 0.00 usd | Open')
@with_transaction() @with_transaction()
def test_config_create_multi_user(self): def test_config_create_multi_user(self):
""" create config, multi-user """ create config, multi-user

View file

@ -12,6 +12,10 @@ this repository contains the full copyright notices and license terms. -->
<label name="done"/> <label name="done"/>
<field name="done"/> <field name="done"/>
<separator id="sepenterbook" colspan="4" string="Enter Booking Wizard"/>
<label name="defbook"/>
<field name="defbook"/>
<separator id="sepcb" colspan="4" string="Cashbook"/> <separator id="sepcb" colspan="4" string="Cashbook"/>
<label name="catnamelong"/> <label name="catnamelong"/>
<field name="catnamelong"/> <field name="catnamelong"/>

View file

@ -11,10 +11,21 @@ full copyright notices and license terms. -->
<label name="bookingtype"/> <label name="bookingtype"/>
<field name="bookingtype"/> <field name="bookingtype"/>
<label name="amount"/> <label name="amount"/>
<field name="amount"/> <field name="amount" symbol="currency"/>
<label name="category"/>
<field name="category"/>
<label name="booktransf"/>
<field name="booktransf"/>
<label name="party"/>
<field name="party"/>
<group name="description" colspan="2" col="1" string="Description">
<field name="description"/>
</group>
<field name="cashbooks"/> <field name="cashbooks"/>
<field name="currency_digits"/> <field name="currency_digits"/>
<field name="currency"/> <field name="currency"/>
<field name="owner_cashbook"/>
</form> </form>

View file

@ -8,10 +8,12 @@ from trytond.wizard import Wizard, StateView, StateTransition, Button
from trytond.i18n import gettext from trytond.i18n import gettext
from trytond.pool import Pool from trytond.pool import Pool
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.pyson import Eval from trytond.pyson import Eval, Bool, If, And
from decimal import Decimal from decimal import Decimal
from .line import sel_bookingtype from .line import sel_bookingtype
sel_booktypewiz = [x for x in sel_bookingtype if not x[0] in ['spin', 'spout']]
class EnterBookingStart(ModelView): class EnterBookingStart(ModelView):
'Enter Booking' 'Enter Booking'
@ -23,6 +25,9 @@ class EnterBookingStart(ModelView):
cashbooks = fields.One2Many(string='Cashbooks', field=None, cashbooks = fields.One2Many(string='Cashbooks', field=None,
model_name='cashbook.book', readonly=True, model_name='cashbook.book', readonly=True,
states={'invisible': True}) states={'invisible': True})
owner_cashbook = fields.Function(fields.Many2One(string='Owner', readonly=True,
states={'invisible': True}, model_name='res.user'),
'on_change_with_owner_cashbook')
balance = fields.Numeric(string='Balance', readonly=True, balance = fields.Numeric(string='Balance', readonly=True,
digits=(16, Eval('currency_digits', 2)), digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']) depends=['currency_digits'])
@ -33,15 +38,55 @@ class EnterBookingStart(ModelView):
readonly=True, states={'invisible': True}), readonly=True, states={'invisible': True}),
'on_change_with_currency_digits') 'on_change_with_currency_digits')
bookingtype = fields.Selection(string='Type', required=True, bookingtype = fields.Selection(string='Type', required=True,
selection=sel_bookingtype) selection=sel_booktypewiz)
amount = fields.Numeric(string='Amount', amount = fields.Numeric(string='Amount',
depends=['currency_digits', 'bookingtype'], depends=['currency_digits', 'bookingtype'],
digits=(16, Eval('currency_digits', 2)), digits=(16, Eval('currency_digits', 2)), required=True,
domain=[('amount', '>=', Decimal('0.0'))], domain=[('amount', '>=', Decimal('0.0'))])
description = fields.Text(string='Description')
category = fields.Many2One(string='Category',
model_name='cashbook.category', depends=['bookingtype'],
states={ states={
'readonly': Eval('bookingtype', '').in_(['spin', 'spout']), 'readonly': Bool(Eval('bookingtype')) == False,
'required': Eval('bookingtype', '').in_(['in', 'out', 'mvin', 'mvout']), 'required': Eval('bookingtype', '').in_(['in', 'out']),
}) 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']),
},
domain=[
If(
Eval('bookingtype', '').in_(['in', 'mvin']),
('cattype', '=', 'in'),
('cattype', '=', 'out'),
)])
# party or cashbook as counterpart
booktransf = fields.Many2One(string='Source/Dest',
model_name='cashbook.book',
domain=[
('owner.id', '=', Eval('owner_cashbook', -1)),
('id', '!=', Eval('cashbook', -1)),
],
states={
'invisible': ~Eval('bookingtype', '').in_(['mvin', 'mvout']),
'required': Eval('bookingtype', '').in_(['mvin', 'mvout']),
}, depends=['bookingtype', 'owner_cashbook', 'cashbook'])
party = fields.Many2One(string='Party', model_name='party.party',
states={
'invisible': ~Eval('bookingtype', '').in_(['in', 'out']),
}, depends=['bookingtype'])
@fields.depends('bookingtype', 'category')
def on_change_bookingtype(self):
""" clear category if not valid type
"""
types = {
'in': ['in', 'mvin'],
'out': ['out', 'mvout'],
}
if self.bookingtype:
if self.category:
if not self.bookingtype in types.get(self.category.cattype, ''):
self.category = None
@fields.depends('cashbook', 'balance') @fields.depends('cashbook', 'balance')
def on_change_cashbook(self): def on_change_cashbook(self):
@ -52,6 +97,13 @@ class EnterBookingStart(ModelView):
else : else :
self.balance = None self.balance = None
@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.currency') @fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency(self, name=None): def on_change_with_currency(self, name=None):
""" digits """ digits
@ -79,28 +131,64 @@ class EnterBookingWizard(Wizard):
start = StateView('cashbook.enterbooking.start', start = StateView('cashbook.enterbooking.start',
'cashbook.enterbooking_start_form', [ 'cashbook.enterbooking_start_form', [
Button('Cancel', 'end', 'tryton-cancel'), Button('Cancel', 'end', 'tryton-cancel'),
Button('Save', 'save_', 'tryton-add', default=True), Button('Save', 'save_', 'tryton-save', default=True),
Button('Save & Next', 'savenext_', 'tryton-forward'),
]) ])
save_ = StateTransition() save_ = StateTransition()
savenext_ = StateTransition()
def default_start(self, fields): def default_start(self, fields):
""" setup form """ setup form
""" """
pool = Pool() pool = Pool()
Cashbook = pool.get('cashbook.book') Cashbook = pool.get('cashbook.book')
Configuration = pool.get('cashbook.configuration')
cfg1 = Configuration.get_singleton()
result = { result = {
'cashbooks': [x.id for x in Cashbook.search([ 'cashbooks': [x.id for x in Cashbook.search([
('state', '=', 'open'), ('state', '=', 'open'),
('owner.id', '=', Transaction().user), ('owner.id', '=', Transaction().user),
])], ])],
'bookingtype': 'out', 'bookingtype': getattr(self.start, 'bookingtype', 'out'),
'cashbook': getattr(getattr(cfg1, 'defbook', None), 'id', None),
'amount': None,
'party': None,
'booktransf': None,
'description': None,
'category': None,
} }
return result return result
def transition_save(self): def transition_save_(self):
""" store booking """ store booking
""" """
pool = Pool()
Line = pool.get('cashbook.line')
IrDate = pool.get('ir.date')
query = {
'cashbook': self.start.cashbook.id,
'description': self.start.description,
'date': IrDate.today(),
'bookingtype': self.start.bookingtype,
'amount': self.start.amount,
}
if self.start.bookingtype in ['in', 'out']:
query['category'] = self.start.category.id
query['party'] = getattr(self.start.party, 'id', None)
elif self.start.bookingtype in ['mvin', 'mvout']:
query['booktransf'] = self.start.booktransf.id
Line.create([query])
return 'end' return 'end'
def transition_savenext_(self):
""" store booking & restart
"""
self.transition_save_()
return 'start'
# end EnterBookingWizard # end EnterBookingWizard