diff --git a/book.py b/book.py
index 37e8320..3bdbfd6 100644
--- a/book.py
+++ b/book.py
@@ -4,10 +4,11 @@
# full copyright notices and license terms.
from trytond.model import Workflow, ModelView, ModelSQL, fields, Check
-from trytond.pyson import Eval
+from trytond.pyson import Eval, Or, Bool
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.transaction import Transaction
+from trytond.pool import Pool
STATES = {
@@ -26,6 +27,8 @@ class Book(Workflow, ModelSQL, ModelView):
'Cashbook'
__name__ = 'cashbook.book'
+ company = fields.Many2One(string='Company', model_name='company.company',
+ required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True,
states=STATES, depends=DEPENDS)
btype = fields.Many2One(string='Type', required=True,
@@ -45,6 +48,14 @@ class Book(Workflow, ModelSQL, ModelView):
account = fields.Many2One(string='Account', select=True,
model_name='account.account', ondelete='RESTRICT',
states=STATES, depends=DEPENDS)
+ currency = fields.Many2One(string='Currency', required=True,
+ model_name='currency.currency',
+ states={
+ 'readonly': Or(
+ STATES['readonly'],
+ Bool(Eval('lines', [])),
+ ),
+ }, depends=DEPENDS+['lines'])
state = fields.Selection(string='State', required=True,
readonly=True, selection=sel_state_book)
state_string = state.translated('state')
@@ -79,6 +90,22 @@ class Book(Workflow, ModelSQL, ModelView):
},
})
+ @classmethod
+ def default_currency(cls):
+ """ currency of company
+ """
+ Company = Pool().get('company.company')
+
+ company = cls.default_company()
+ if company:
+ company = Company(company)
+ if company.currency:
+ return company.currency.id
+
+ @staticmethod
+ def default_company():
+ return Transaction().context.get('company') or None
+
@classmethod
def default_state(cls):
return 'open'
diff --git a/book.xml b/book.xml
index ea9d71f..1aecde0 100644
--- a/book.xml
+++ b/book.xml
@@ -107,6 +107,19 @@ full copyright notices and license terms. -->
+
+ User in companies
+
+
+
+
+
+
+
+
wfopen
diff --git a/category.xml b/category.xml
index 7258106..6df3c33 100644
--- a/category.xml
+++ b/category.xml
@@ -87,5 +87,18 @@ full copyright notices and license terms. -->
+
+ User in companies
+
+
+
+
+
+
+
+
diff --git a/line.py b/line.py
index 56b8549..9e6ab0e 100644
--- a/line.py
+++ b/line.py
@@ -10,8 +10,10 @@ from trytond.transaction import Transaction
from trytond.report import Report
from trytond.exceptions import UserError
from trytond.i18n import gettext
+from decimal import Decimal
from sql import Cast, Literal
from sql.functions import DatePart
+from sql.conditionals import Case
from .book import sel_state_book
@@ -21,6 +23,13 @@ sel_linetype = [
('done', 'Done'),
]
+sel_bookingtype = [
+ ('in', 'Revenue'),
+ ('out', 'Expense'),
+ ('mvin', 'Transfer from'),
+ ('mvout', 'Transfer to'),
+ ]
+
STATES = {
'readonly': Or(
Eval('state', '') != 'edit',
@@ -47,6 +56,21 @@ class Line(Workflow, ModelSQL, ModelView):
states=STATES, depends=DEPENDS)
category_view = fields.Function(fields.Char(string='Category', readonly=True),
'on_change_with_category_view', searcher='search_category_view')
+
+ bookingtype = fields.Selection(string='Type', required=True,
+ help='Type of Booking', selection=sel_bookingtype,
+ states=STATES, depends=DEPENDS)
+ amount = fields.Numeric(string='Amount', digits=(16, Eval('currency_digits', 2)),
+ required=True, states=STATES, depends=DEPENDS+['currency_digits'])
+ debit = fields.Numeric(string='Debit', digits=(16, Eval('currency_digits', 2)),
+ required=True, readonly=True, depends=['currency_digits'])
+ credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
+ required=True, readonly=True, depends=['currency_digits'])
+ currency = fields.Function(fields.Many2One(model_name='currency.currency',
+ string="Currency"), 'on_change_with_currency')
+ currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
+ 'on_change_with_currency_digits')
+
state = fields.Selection(string='State', required=True, readonly=True,
select=True, selection=sel_linetype)
state_string = state.translated('state')
@@ -59,6 +83,7 @@ class Line(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(Line, cls).__setup__()
+ cls._order.insert(0, ('state', 'ASC'))
cls._order.insert(0, ('date', 'ASC'))
t = cls.__table__()
cls._sql_constraints.extend([
@@ -138,6 +163,23 @@ class Line(Workflow, ModelSQL, ModelView):
'desc': (self.description or '-')[:40],
}
+ @staticmethod
+ def order_state(tables):
+ """ edit = 0, check/done = 1
+ """
+ Line = Pool().get('cashbook.line')
+ tab_line = Line.__table__()
+ table, _ = tables[None]
+
+ query = tab_line.select(
+ Case(
+ (tab_line.state == 'edit', 1),
+ (tab_line.state.in_(['check', 'done']), 0),
+ else_ = 2),
+ where=tab_line.id==table.id
+ )
+ return [query]
+
@staticmethod
def order_category_view(tables):
""" order: name
@@ -219,11 +261,65 @@ class Line(Workflow, ModelSQL, ModelView):
"""
return [('cashbook.state',) + tuple(clause[1:])]
+ @fields.depends('cashbook', '_parent_cashbook.currency')
+ def on_change_with_currency(self, name=None):
+ """ currency of cashbook
+ """
+ if self.cashbook:
+ return self.cashbook.currency.id
+
+ @fields.depends('cashbook', '_parent_cashbook.currency')
+ def on_change_with_currency_digits(self, name=None):
+ """ currency of cashbook
+ """
+ if self.cashbook:
+ return self.cashbook.currency.digits
+ else:
+ return 2
+
+ @classmethod
+ def get_debit_credit(cls, values):
+ """ compute debit/credit from amount
+ """
+ if isinstance(values, dict):
+ type_ = values.get('bookingtype', None)
+ amount = values.get('amount', None)
+ else :
+ type_ = getattr(values, 'bookingtype', None)
+ amount = getattr(values, 'amount', None)
+
+ if type_:
+ if amount is not None:
+ if type_ in ['in', 'mvin']:
+ return {
+ 'debit': Decimal('0.0'),
+ 'credit': amount,
+ }
+ elif type_ in ['out', 'mvout']:
+ return {
+ 'debit': amount,
+ 'credit': Decimal('0.0'),
+ }
+ else :
+ raise ValueError('invalid "bookingtype"')
+ return {}
+
+ @classmethod
+ def create(cls, vlist):
+ """ add debit/credit
+ """
+ vlist = [x.copy() for x in vlist]
+ for vals in vlist:
+ vals.update(cls.get_debit_credit(vals))
+ return super(Line, cls).create(vlist)
+
@classmethod
def write(cls, *args):
- """ deny update if cashbook.line!='open'
+ """ deny update if cashbook.line!='open',
+ add or update debit/credit
"""
actions = iter(args)
+ to_write = []
for lines, values in zip(actions, actions):
for line in lines:
if line.cashbook.state != 'open':
@@ -232,7 +328,19 @@ class Line(Workflow, ModelSQL, ModelView):
bookname = line.cashbook.rec_name,
state_txt = line.cashbook.state_string,
))
- super(Line, cls).write(*args)
+
+ # debit / credit
+ if len(set(values.keys()).intersection(set({'amount', 'bookingtype'}))) > 0:
+ for line in lines:
+ values2 = {}
+ values2.update(values)
+ values2.update(cls.get_debit_credit({
+ x:values.get(x, getattr(line, x)) for x in ['amount', 'bookingtype']
+ }))
+ to_write.extend([lines, values2])
+ else :
+ to_write.extend([lines, values])
+ super(Line, cls).write(*to_write)
@classmethod
def delete(cls, lines):
diff --git a/line.xml b/line.xml
index d60291a..036f7b0 100644
--- a/line.xml
+++ b/line.xml
@@ -169,6 +169,19 @@ full copyright notices and license terms. -->
+
+ User in companies
+
+
+
+
+
+
+
+
diff --git a/locale/de.po b/locale/de.po
index 5e632e7..5f65023 100644
--- a/locale/de.po
+++ b/locale/de.po
@@ -27,7 +27,7 @@ msgid "The cashbook '%(bookname)s' cannot be deleted because it contains %(bookl
msgstr "Das Kassenbuch '%(bookname)s' kann nicht gelöscht werden, da es %(booklines)s Zeilen enthält und nicht im Status 'Archiv' ist."
msgctxt "model:ir.message,text:msg_book_deny_write"
-msgid "The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed."
+msgid "The cashbook '%(bookname)s' is '%(state_txt)s' and cannot be changed."
msgstr "Das Kassenbuch '%(bookname)s' ist '%(state_txt)s' und kann nicht geändert werden."
msgctxt "model:ir.message,text:msg_line_deny_delete1"
@@ -90,6 +90,18 @@ msgctxt "model:ir.rule.group,name:rg_line_read"
msgid "Observer: Cashbook line read"
msgstr "Beobachter: Kassenbuchzeile lesen"
+msgctxt "model:ir.rule.group,name:rg_line_read"
+msgid "User in companies"
+msgstr "Benutzer im Unternehmen"
+
+msgctxt "model:ir.rule.group,name:rg_type_companies"
+msgid "User in companies"
+msgstr "Benutzer im Unternehmen"
+
+msgctxt "model:ir.rule.group,name:rg_book_companies"
+msgid "User in companies"
+msgstr "Benutzer im Unternehmen"
+
##############
# ir.ui.menu #
@@ -127,8 +139,8 @@ msgstr "Kategorie"
# ir.action #
#############
msgctxt "model:ir.action,name:act_book_view"
-msgid "Account"
-msgstr "Konto"
+msgid "Cashbook"
+msgstr "Kassenbuch"
msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type"
@@ -147,6 +159,22 @@ msgid "Category"
msgstr "Kategorie"
+###############################
+# ir.action.act_window.domain #
+###############################
+msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
+msgid "Current Month"
+msgstr "aktueller Monat"
+
+msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
+msgid "Last Month"
+msgstr "letzter Monat"
+
+msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
+msgid "All"
+msgstr "Alle"
+
+
###################
# ir.model.button #
###################
@@ -183,8 +211,8 @@ msgid "Cashbook"
msgstr "Kassenbuch"
msgctxt "view:cashbook.book:"
-msgid "Owner & Authorizeds"
-msgstr "Eigentümer & Autorisierte"
+msgid "Owner and Authorizeds"
+msgstr "Eigentümer und Autorisierte"
msgctxt "field:cashbook.book,name:"
msgid "Name"
@@ -234,6 +262,14 @@ msgctxt "field:cashbook.book,account:"
msgid "Account"
msgstr "Konto"
+msgctxt "field:cashbook.book,company:"
+msgid "Company"
+msgstr "Unternehmen"
+
+msgctxt "field:cashbook.book,currency:"
+msgid "Currency"
+msgstr "Währung"
+
#################
# cashbook.line #
@@ -242,6 +278,14 @@ msgctxt "model:cashbook.line,name:"
msgid "Cashbook Line"
msgstr "Kassenbuchzeile"
+msgctxt "view:cashbook.line:"
+msgid "Credit"
+msgstr "Einnahme"
+
+msgctxt "view:cashbook.line:"
+msgid "Debit"
+msgstr "Ausgabe"
+
msgctxt "view:cashbook.line:"
msgid "Cashbook Line"
msgstr "Kassenbuchzeile"
@@ -294,17 +338,53 @@ msgctxt "field:cashbook.line,category_view:"
msgid "Category"
msgstr "Kategorie"
-msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
-msgid "Current Month"
-msgstr "aktueller Monat"
+msgctxt "field:cashbook.line,bookingtype:"
+msgid "Type"
+msgstr "Typ"
-msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
-msgid "Last Month"
-msgstr "letzter Monat"
+msgctxt "help:cashbook.line,bookingtype:"
+msgid "Type of Booking"
+msgstr "Typ der Buchung"
-msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
-msgid "All"
-msgstr "Alle"
+msgctxt "selection:cashbook.line,bookingtype:"
+msgid "Revenue"
+msgstr "Einnahme"
+
+msgctxt "selection:cashbook.line,bookingtype:"
+msgid "Expense"
+msgstr "Ausgabe"
+
+msgctxt "selection:cashbook.line,bookingtype:"
+msgid "Transfer from"
+msgstr "Umbuchung von"
+
+msgctxt "selection:cashbook.line,bookingtype:"
+msgid "Transfer to"
+msgstr "Umbuchung nach"
+
+msgctxt "field:cashbook.line,company:"
+msgid "Company"
+msgstr "Unternehmen"
+
+msgctxt "field:cashbook.line,amount:"
+msgid "Amount"
+msgstr "Betrag"
+
+msgctxt "field:cashbook.line,debit:"
+msgid "Debit"
+msgstr "Ausgabe"
+
+msgctxt "field:cashbook.line,credit:"
+msgid "Credit"
+msgstr "Einnahme"
+
+msgctxt "field:cashbook.line,currency:"
+msgid "Currency"
+msgstr "Währung"
+
+msgctxt "field:cashbook.line,currency_digits:"
+msgid "Currency Digits"
+msgstr "Nachkommastellen Währung"
#################
@@ -322,17 +402,9 @@ msgctxt "field:cashbook.type,short:"
msgid "Abbreviation"
msgstr "Kürzel"
-msgctxt "model:cashbook.type,name:atype_cash"
-msgid "Cash"
-msgstr "Bar"
-
-msgctxt "model:cashbook.type,name:atype_giro"
-msgid "Giro"
-msgstr "Giro"
-
-msgctxt "model:cashbook.type,name:atype_fixtermdep"
-msgid "Fixed-term deposit"
-msgstr "Festgeld"
+msgctxt "field:cashbook.type,company:"
+msgid "Company"
+msgstr "Unternehmen"
#####################
@@ -434,11 +506,11 @@ msgctxt "model:cashbook.open_lines,name:"
msgid "Open Cashbook"
msgstr "Kassenbuch öffnen"
-msgctxt "wizard_button:cashbook.open_lines,start,end:"
+msgctxt "wizard_button:cashbook.open_lines,askuser,end:"
msgid "Cancel"
msgstr "Abbruch"
-msgctxt "wizard_button:cashbook.open_lines,start,open_:"
+msgctxt "wizard_button:cashbook.open_lines,askuser,open_:"
msgid "Open"
msgstr "Öffnen"
diff --git a/message.xml b/message.xml
index 9ced7e9..ffc9d3f 100644
--- a/message.xml
+++ b/message.xml
@@ -18,10 +18,10 @@ full copyright notices and license terms. -->
Invalid value of the field 'Status', allowed: open, closed, archive.
- The cash book '%(bookname)s' cannot be deleted because it contains %(booklines)s lines and is not in the status 'Archive'.
+ The cashbook '%(bookname)s' cannot be deleted because it contains %(booklines)s lines and is not in the status 'Archive'.
- The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed.
+ The cashbook '%(bookname)s' is '%(state_txt)s' and cannot be changed.
The cashbook line '%(linetxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'.
diff --git a/tests/test_book.py b/tests/test_book.py
index 8c29cbb..0db06ec 100644
--- a/tests/test_book.py
+++ b/tests/test_book.py
@@ -8,6 +8,7 @@ from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
+from decimal import Decimal
class BookTestCase(ModuleTestCase):
@@ -20,13 +21,14 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
-
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.btype.rec_name, 'CAS - Cash')
@@ -39,18 +41,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -67,18 +72,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -97,18 +105,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -125,13 +136,14 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
-
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.state, 'open')
@@ -188,18 +200,22 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
usr_lst = ResUser.create([{
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -209,6 +225,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),
self.assertEqual(book.owner.rec_name, 'Frida'),
@@ -245,9 +263,9 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_reviewer, = ResGroup.create([{
'name': 'Cashbook Reviewer',
@@ -257,10 +275,14 @@ class BookTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_reviewer.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -272,6 +294,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'reviewer': grp_reviewer.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'btype': types.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@@ -301,9 +325,9 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_observer, = ResGroup.create([{
'name': 'Cashbook Observer',
@@ -313,10 +337,14 @@ class BookTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_observer.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -328,6 +356,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'observer': grp_observer.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'btype': types.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),
diff --git a/tests/test_category.py b/tests/test_category.py
index c3b5102..f4a7325 100644
--- a/tests/test_category.py
+++ b/tests/test_category.py
@@ -7,7 +7,6 @@ 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 trytond.modules.company.tests import create_company
class CategoryTestCase(ModuleTestCase):
@@ -21,12 +20,7 @@ class CategoryTestCase(ModuleTestCase):
Company = pool.get('company.company')
Category = pool.get('cashbook.category')
- company = Company.search([])
- if len(company) > 0:
- company = company[0]
- else :
- company = create_company(name='m-ds')
-
+ company = self.prep_company()
category, = Category.create([{
'company': company.id,
'name': name,
@@ -40,7 +34,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
- company = create_company(name='m-ds')
+ company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@@ -73,7 +67,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
- company = create_company(name='m-ds')
+ company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@@ -100,7 +94,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
- company = create_company(name='m-ds')
+ company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@@ -126,7 +120,7 @@ class CategoryTestCase(ModuleTestCase):
Account = pool.get('account.account')
Category = pool.get('cashbook.category')
- company = create_company(name='m-ds')
+ company = self.prep_company()
with Transaction().set_context({
'company': company.id,
diff --git a/tests/test_config.py b/tests/test_config.py
index 6f95827..f00d171 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -7,6 +7,7 @@ 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 trytond.modules.company.tests import create_company
from datetime import date
@@ -14,6 +15,18 @@ class ConfigTestCase(ModuleTestCase):
'Test config type module'
module = 'cashbook'
+ def prep_company(self):
+ """ get/create company
+ """
+ Company = Pool().get('company.company')
+
+ company = Company.search([])
+ if len(company) > 0:
+ company = company[0]
+ else :
+ company = create_company(name='m-ds')
+ return company
+
@with_transaction()
def test_config_create(self):
""" create config
diff --git a/tests/test_line.py b/tests/test_line.py
index b6a0fa5..5f7d905 100644
--- a/tests/test_line.py
+++ b/tests/test_line.py
@@ -9,6 +9,7 @@ from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
from unittest.mock import MagicMock
+from decimal import Decimal
class LineTestCase(ModuleTestCase):
@@ -21,23 +22,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -59,29 +65,62 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(Lines.search_count([('cashbook.state', '=', 'open')]), 2)
self.assertEqual(Lines.search_count([('cashbook.state', '=', 'closed')]), 0)
+ # sorting: date -> state -> id
+ self.assertEqual(len(book.lines), 2)
+ self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
+ self.assertEqual(book.lines[0].state, 'edit')
+ self.assertEqual(book.lines[1].rec_name, '05/02/2022 Text 2')
+ self.assertEqual(book.lines[1].state, 'edit')
+
+ # set to same date
+ Lines.write(*[
+ list(book.lines),
+ {
+ 'date': date(2022, 5, 1),
+ }])
+ # check again
+ book, = Book.search([])
+ self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
+ self.assertEqual(book.lines[0].state, 'edit')
+ self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 2')
+ self.assertEqual(book.lines[1].state, 'edit')
+
+ # set to 'check', will sort first
+ Lines.wfcheck([book.lines[1]])
+ book, = Book.search([])
+ self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 2')
+ self.assertEqual(book.lines[0].state, 'check')
+ self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 1')
+ self.assertEqual(book.lines[1].state, 'edit')
+
@with_transaction()
def test_line_create_check_deny_write(self):
""" create cashbook + line, 'close' book, write to line
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -107,26 +146,31 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
IrDate = pool.get('ir.date')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
IrDate.today = MagicMock(return_value=date(2022, 6, 1))
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -159,21 +203,125 @@ class LineTestCase(ModuleTestCase):
IrDate.today = MagicMock(return_value=date.today())
+ @with_transaction()
+ def test_line_create_check_debit_credit(self):
+ """ create cashbook + line, check calculation of debit/credit
+ """
+ pool = Pool()
+ Book = pool.get('cashbook.book')
+ Line = pool.get('cashbook.line')
+ Configuration = pool.get('cashbook.configuration')
+ Category = pool.get('cashbook.category')
+
+ types = self.prep_type()
+ category = self.prep_category()
+ company = self.prep_company()
+
+ category2, = Category.create([{
+ 'company': company.id,
+ 'name': 'Category',
+ }])
+
+ book, = Book.create([{
+ 'name': 'Book 1',
+ 'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
+ 'lines': [('create', [{
+ 'date': date(2022, 5, 1),
+ 'description': 'Revenue',
+ 'category': category2.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
+ }, {
+ 'date': date(2022, 6, 1),
+ 'description': 'Expense',
+ 'category': category2.id,
+ 'bookingtype': 'out',
+ 'amount': Decimal('1.0'),
+ }, {
+ 'date': date(2022, 6, 1),
+ 'description': 'Transfer from',
+ 'category': category2.id,
+ 'bookingtype': 'mvin',
+ 'amount': Decimal('1.0'),
+ }, {
+ 'date': date(2022, 6, 1),
+ 'description': 'Transfer to',
+ 'category': category2.id,
+ 'bookingtype': 'mvout',
+ 'amount': Decimal('1.0'),
+ }])],
+ }])
+ self.assertEqual(book.name, 'Book 1')
+ self.assertEqual(book.state, 'open')
+ self.assertEqual(len(book.lines), 4)
+
+ self.assertEqual(book.lines[0].amount, Decimal('1.0'))
+ self.assertEqual(book.lines[0].bookingtype, 'in')
+ self.assertEqual(book.lines[0].credit, Decimal('1.0'))
+ self.assertEqual(book.lines[0].debit, Decimal('0.0'))
+
+ self.assertEqual(book.lines[1].amount, Decimal('1.0'))
+ self.assertEqual(book.lines[1].bookingtype, 'out')
+ self.assertEqual(book.lines[1].credit, Decimal('0.0'))
+ self.assertEqual(book.lines[1].debit, Decimal('1.0'))
+
+ self.assertEqual(book.lines[2].amount, Decimal('1.0'))
+ self.assertEqual(book.lines[2].bookingtype, 'mvin')
+ self.assertEqual(book.lines[2].credit, Decimal('1.0'))
+ self.assertEqual(book.lines[2].debit, Decimal('0.0'))
+
+ self.assertEqual(book.lines[3].amount, Decimal('1.0'))
+ self.assertEqual(book.lines[3].bookingtype, 'mvout')
+ self.assertEqual(book.lines[3].credit, Decimal('0.0'))
+ self.assertEqual(book.lines[3].debit, Decimal('1.0'))
+
+ Line.write(*[
+ [book.lines[0]],
+ {
+ 'amount': Decimal('2.0'),
+ }])
+ self.assertEqual(book.lines[0].amount, Decimal('2.0'))
+ self.assertEqual(book.lines[0].bookingtype, 'in')
+ self.assertEqual(book.lines[0].credit, Decimal('2.0'))
+ self.assertEqual(book.lines[0].debit, Decimal('0.0'))
+
+ Line.write(*[
+ [book.lines[0]],
+ {
+ 'bookingtype': 'out',
+ }])
+ self.assertEqual(book.lines[0].amount, Decimal('2.0'))
+ self.assertEqual(book.lines[0].bookingtype, 'out')
+ self.assertEqual(book.lines[0].credit, Decimal('0.0'))
+ self.assertEqual(book.lines[0].debit, Decimal('2.0'))
+
+ Line.write(*[
+ [book.lines[0]],
+ {
+ 'bookingtype': 'mvin',
+ 'amount': Decimal('3.0'),
+ }])
+ self.assertEqual(book.lines[0].amount, Decimal('3.0'))
+ self.assertEqual(book.lines[0].bookingtype, 'mvin')
+ self.assertEqual(book.lines[0].credit, Decimal('3.0'))
+ self.assertEqual(book.lines[0].debit, Decimal('0.0'))
+
@with_transaction()
def test_line_create_check_category_view(self):
""" create cashbook + line, check 'category_view'
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
Configuration = pool.get('cashbook.configuration')
Category = pool.get('cashbook.category')
Account = pool.get('account.account')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
- company = category.company
+ company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@@ -208,14 +356,20 @@ class LineTestCase(ModuleTestCase):
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category2.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category2.childs[0].id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -257,23 +411,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -288,23 +447,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -324,23 +488,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
- Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
- types, = Types.search([('short', '=','CAS')])
+ types = self.prep_type()
category = self.prep_category()
-
+ company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@@ -365,19 +534,23 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
usr_lst = ResUser.create([{
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -387,10 +560,14 @@ class LineTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@@ -427,10 +604,10 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_reviewer, = ResGroup.create([{
'name': 'Cashbook Reviewer',
@@ -440,10 +617,14 @@ class LineTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_reviewer.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -456,10 +637,14 @@ class LineTestCase(ModuleTestCase):
'owner': usr_lst[0].id,
'reviewer': grp_reviewer.id,
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@@ -503,10 +688,10 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
- Types = pool.get('cashbook.type')
- types, = Types.search([('short', '=', 'CAS')])
+ types = self.prep_type()
category = self.prep_category()
+ company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_observer, = ResGroup.create([{
'name': 'Cashbook Observer',
@@ -516,10 +701,14 @@ class LineTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_observer.id])],
+ 'companies': [('add', [company.id])],
+ 'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@@ -532,10 +721,14 @@ class LineTestCase(ModuleTestCase):
'owner': usr_lst[0].id,
'observer': grp_observer.id,
'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),
diff --git a/tests/test_type.py b/tests/test_type.py
index 9e8544a..ab20b94 100644
--- a/tests/test_type.py
+++ b/tests/test_type.py
@@ -13,17 +13,20 @@ class TypeTestCase(ModuleTestCase):
'Test cashbook type module'
module = 'cashbook'
- @with_transaction()
- def test_type_read_existing(self):
- """ read predefined types
+ def prep_type(self, name='Cash', short='CAS'):
+ """ create book-type
"""
AccType = Pool().get('cashbook.type')
- t_lst = AccType.search([], order=[('name', 'ASC')])
- self.assertEqual(len(t_lst), 3)
- self.assertEqual(t_lst[0].rec_name, 'CAS - Cash')
- self.assertEqual(t_lst[1].rec_name, 'FTD - Fixed-term deposit')
- self.assertEqual(t_lst[2].rec_name, 'GIR - Giro')
+ company = self.prep_company()
+ at, = AccType.create([{
+ 'name': name,
+ 'short': short,
+ 'company': company.id,
+ }])
+ self.assertEqual(at.name, name)
+ self.assertEqual(at.short, short)
+ return at
@with_transaction()
def test_type_create(self):
@@ -31,9 +34,12 @@ class TypeTestCase(ModuleTestCase):
"""
AccType = Pool().get('cashbook.type')
+ company = self.prep_company()
+
at, = AccType.create([{
'name': 'Test 1',
'short': 'T1',
+ 'company': company.id,
}])
self.assertEqual(at.name, 'Test 1')
self.assertEqual(at.short, 'T1')
@@ -45,6 +51,7 @@ class TypeTestCase(ModuleTestCase):
[{
'name': 'Test 2',
'short': 'T1',
+ 'company': company.id,
}])
# end TypeTestCase
diff --git a/tryton.cfg b/tryton.cfg
index 62d0ac8..065201a 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -3,6 +3,7 @@ version=6.0.0
depends:
res
account
+ currency
xml:
icon.xml
group.xml
diff --git a/types.py b/types.py
index c8d76dd..7181cd2 100644
--- a/types.py
+++ b/types.py
@@ -4,6 +4,7 @@
# full copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, fields, Unique
+from trytond.transaction import Transaction
class Type(ModelSQL, ModelView):
@@ -12,6 +13,8 @@ class Type(ModelSQL, ModelView):
name = fields.Char(string='Name', required=True, translate=True)
short = fields.Char(string='Abbreviation', required=True, size=3)
+ company = fields.Many2One(string='Company', model_name='company.company',
+ required=True, ondelete="RESTRICT")
@classmethod
def __setup__(cls):
@@ -39,4 +42,8 @@ class Type(ModelSQL, ModelView):
('short',) + tuple(clause[1:]),
]
+ @staticmethod
+ def default_company():
+ return Transaction().context.get('company') or None
+
# end Type
diff --git a/types.xml b/types.xml
index 21d31ef..d3a628a 100644
--- a/types.xml
+++ b/types.xml
@@ -63,18 +63,17 @@ full copyright notices and license terms. -->
-
-
- CAS
- Cash
+
+ User in companies
+
+
-
- GIR
- Giro
-
-
- FTD
- Fixed-term deposit
+
+
+
diff --git a/view/book_form.xml b/view/book_form.xml
index 1a20cdf..ec543c1 100644
--- a/view/book_form.xml
+++ b/view/book_form.xml
@@ -8,9 +8,10 @@ full copyright notices and license terms. -->
+
+
-
diff --git a/view/book_list.xml b/view/book_list.xml
index a201a05..892ed2e 100644
--- a/view/book_list.xml
+++ b/view/book_list.xml
@@ -5,6 +5,7 @@ full copyright notices and license terms. -->
+
diff --git a/view/line_form.xml b/view/line_form.xml
index b37c587..9dcd58c 100644
--- a/view/line_form.xml
+++ b/view/line_form.xml
@@ -7,8 +7,7 @@ full copyright notices and license terms. -->
-
-
+
@@ -16,10 +15,17 @@ full copyright notices and license terms. -->
+
+
+
+
+
+
+
diff --git a/view/line_list.xml b/view/line_list.xml
index aeae0e5..4638907 100644
--- a/view/line_list.xml
+++ b/view/line_list.xml
@@ -3,10 +3,13 @@
The COPYRIGHT file at the top level of this repository contains the
full copyright notices and license terms. -->
-
+
-
+
+
+
+
diff --git a/wizard_openline.py b/wizard_openline.py
index 007614d..0d134f9 100644
--- a/wizard_openline.py
+++ b/wizard_openline.py
@@ -82,6 +82,10 @@ class OpenCashBook(Wizard):
Configuration = pool.get('cashbook.configuration')
cfg1 = Configuration.get_singleton()
+ if cfg1 is None:
+ cfg1 = Configuration()
+ cfg1.save()
+
book = getattr(self.askuser, 'cashbook', None)
if book is None:
with Transaction().set_context({