diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..1b85cd9
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,3 @@
+syntax: glob
+__pycache__/*
+locale/convert_de2en.py
diff --git a/__init__.py b/__init__.py
index fe669db..d7417b1 100644
--- a/__init__.py
+++ b/__init__.py
@@ -4,7 +4,13 @@
# full copyright notices and license terms.
from trytond.pool import Pool
+from .types import Type
+from .book import Book
+from .line import Line
def register():
Pool.register(
+ Type,
+ Book,
+ Line,
module='cashbook_investment', type_='model')
diff --git a/book.py b/book.py
new file mode 100644
index 0000000..98371c7
--- /dev/null
+++ b/book.py
@@ -0,0 +1,77 @@
+# -*- 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.model import fields
+from trytond.exceptions import UserError
+from trytond.pool import PoolMeta
+from trytond.pyson import Eval, Or, Len
+from trytond.modules.cashbook.book import STATES2, DEPENDS2
+
+
+class Book(metaclass=PoolMeta):
+ __name__ = 'cashbook.book'
+
+ asset = fields.Many2One(string='Asset',
+ model_name='investment.asset', ondelete='RESTRICT',
+ states={
+ 'required': Eval('feature', '') == 'asset',
+ 'invisible': Eval('feature', '') != 'asset',
+ 'readonly': Or(
+ STATES2['readonly'],
+ Len(Eval('lines')) > 0,
+ ),
+ }, depends=DEPENDS2+['feature', 'lines'])
+ quantity_digits = fields.Integer(string='Digits',
+ help='Quantity Digits',
+ domain=[
+ ('quantity_digits', '>=', 0),
+ ('quantity_digits', '<=', 6),
+ ],
+ states={
+ 'required': Eval('feature', '') == 'asset',
+ 'invisible': Eval('feature', '') != 'asset',
+ 'readonly': Or(
+ STATES2['readonly'],
+ Len(Eval('lines')) > 0,
+ ),
+ }, depends=DEPENDS2+['feature', 'lines'])
+ asset_uomcat = fields.Function(fields.Many2One(string='UOM Category',
+ readonly=True, model_name='product.uom.category',
+ states={'invisible': True}), 'on_change_with_asset_uomcat')
+ quantity_uom = fields.Many2One(string='UOM',
+ model_name='product.uom', ondelete='RESTRICT',
+ domain=[
+ ('category.id', '=', Eval('asset_uomcat', -1)),
+ ],
+ states={
+ 'required': Eval('feature', '') == 'asset',
+ 'invisible': Eval('feature', '') != 'asset',
+ 'readonly': Or(
+ STATES2['readonly'],
+ Len(Eval('lines')) > 0,
+ ),
+ }, depends=DEPENDS2+['feature', 'lines', 'asset_uomcat'])
+
+ @fields.depends('asset', 'quantity_uom')
+ def on_change_asset(self):
+ """ get uom from asset
+ """
+ if self.asset:
+ self.quantity_uom = self.asset.uom.id
+
+ @classmethod
+ def default_quantity_digits(cls):
+ """ default: 4
+ """
+ return 4
+
+ @fields.depends('asset', '_parent_asset.uom')
+ def on_change_with_asset_uomcat(self, name=None):
+ """ get uom-category of asset
+ """
+ if self.asset:
+ return self.asset.uom.category.id
+
+# end Book
diff --git a/book.xml b/book.xml
new file mode 100644
index 0000000..12806b1
--- /dev/null
+++ b/book.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ cashbook.book
+
+ book_form
+
+
+
+
diff --git a/line.py b/line.py
new file mode 100644
index 0000000..9fbdab6
--- /dev/null
+++ b/line.py
@@ -0,0 +1,69 @@
+# -*- 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 decimal import Decimal
+from trytond.model import fields
+from trytond.pool import PoolMeta
+from trytond.pyson import Eval, Or, If
+from trytond.modules.cashbook.line import STATES, DEPENDS
+
+
+class Line(metaclass=PoolMeta):
+ __name__ = 'cashbook.line'
+
+ quantity = fields.Numeric(string='Quantity',
+ digits=(16, Eval('quantity_digits', 4)),
+ states={
+ 'required': Eval('feature', '') == 'asset',
+ 'invisible': Eval('feature', '') != 'asset',
+ 'readonly': Or(
+ STATES['readonly'],
+ Eval('bookingtype', '').in_(['spin', 'spout']),
+ ),
+ }, depends=DEPENDS+['feature', 'quantity_digits', 'bookingtype'])
+ quantity_digits = fields.Function(fields.Integer(string='Digits',
+ readonly=True, states={'invisible': True}),
+ 'on_change_with_quantity_digits')
+ quantity_uom = fields.Function(fields.Many2One(string='Symbol',
+ readonly=True, model_name='product.uom'),
+ 'on_change_with_quantity_symbol')
+ asset_rate = fields.Function(fields.Numeric(string='Rate',
+ readonly=True,
+ digits=(16, If(
+ Eval('currency_digits', 2) > Eval('quantity_digits', 2),
+ Eval('currency_digits', 2), Eval('quantity_digits', 2)))),
+ 'on_change_with_asset_rate')
+ #asset_ratesymbol = fields.
+
+ @fields.depends('quantity', 'amount', 'currency_digits', 'quantity_digits')
+ def on_change_with_asset_rate(self, name=None):
+ """ get rate
+ """
+ if (self.quantity is None) or (self.amount is None):
+ return
+ digit = max(
+ self.currency_digits if self.currency_digits is not None else 2,
+ self.quantity_digits if self.quantity_digits is not None else 4)
+ if self.quantity != Decimal('0.0'):
+ return (
+ self.amount / self.quantity
+ ).quantize(Decimal(str(1/10**digit)))
+
+ @fields.depends('cashbook', '_parent_cashbook.quantity_uom')
+ def on_change_with_quantity_symbol(self, name=None):
+ """ get quantity-symbol of asset
+ """
+ if self.cashbook:
+ return self.cashbook.quantity_uom.id
+
+ @fields.depends('cashbook', '_parent_cashbook.quantity_digits')
+ def on_change_with_quantity_digits(self, name=None):
+ """ get digits from cashbook
+ """
+ if self.cashbook:
+ return self.cashbook.quantity_digits
+ return 4
+
+# end LineContext
diff --git a/line.xml b/line.xml
new file mode 100644
index 0000000..83df0ea
--- /dev/null
+++ b/line.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ cashbook.line
+
+ line_form
+
+
+
+
diff --git a/locale/de.po b/locale/de.po
new file mode 100644
index 0000000..2175463
--- /dev/null
+++ b/locale/de.po
@@ -0,0 +1,59 @@
+#
+msgid ""
+msgstr "Content-Type: text/plain; charset=utf-8\n"
+
+
+##############
+# ir.message #
+##############
+msgctxt "model:ir.message,text:msg_btype_asset"
+msgid "Asset"
+msgstr "Vermögenswert"
+
+
+#################
+# cashbook.book #
+#################
+msgctxt "view:cashbook.book:"
+msgid "Asset"
+msgstr "Vermögenswert"
+
+msgctxt "field:cashbook.book,asset:"
+msgid "Asset"
+msgstr "Vermögenswert"
+
+msgctxt "field:cashbook.book,quantity_digits:"
+msgid "Digits"
+msgstr "Dezimalstellen"
+
+msgctxt "help:cashbook.book,quantity_digits:"
+msgid "Quantity Digits"
+msgstr "Dezimalstellen für Anzahl"
+
+msgctxt "help:cashbook.book,asset_uomcat:"
+msgid "UOM Category"
+msgstr "Einheitenkategorie"
+
+msgctxt "help:cashbook.book,quantity_uom:"
+msgid "UOM"
+msgstr "Einheit"
+
+
+#################
+# cashbook.line #
+#################
+msgctxt "field:cashbook.line,quantity_digits:"
+msgid "Digits"
+msgstr "Dezimalstellen"
+
+msgctxt "field:cashbook.line,quantity:"
+msgid "Quantity"
+msgstr "Anzahl"
+
+msgctxt "field:cashbook.line,quantity_symbol:"
+msgid "Symbol"
+msgstr "Symbol"
+
+msgctxt "field:cashbook.line,asset_rate:"
+msgid "Rate"
+msgstr "Kurs"
diff --git a/locale/en.po b/locale/en.po
new file mode 100644
index 0000000..ff8880b
--- /dev/null
+++ b/locale/en.po
@@ -0,0 +1,44 @@
+#
+msgid ""
+msgstr "Content-Type: text/plain; charset=utf-8\n"
+
+msgctxt "model:ir.message,text:msg_btype_asset"
+msgid "Asset"
+msgstr "Asset"
+
+msgctxt "view:cashbook.book:"
+msgid "Asset"
+msgstr "Asset"
+
+msgctxt "field:cashbook.book,asset:"
+msgid "Asset"
+msgstr "Asset"
+
+msgctxt "field:cashbook.book,quantity_digits:"
+msgid "Digits"
+msgstr "Digits"
+
+msgctxt "help:cashbook.book,quantity_digits:"
+msgid "Quantity Digits"
+msgstr "Quantity Digits"
+
+msgctxt "help:cashbook.book,asset_uomcat:"
+msgid "UOM Category"
+msgstr "UOM Category"
+
+msgctxt "help:cashbook.book,quantity_uom:"
+msgid "UOM"
+msgstr "UOM"
+
+msgctxt "field:cashbook.line,quantity_digits:"
+msgid "Digits"
+msgstr "Digits"
+
+msgctxt "field:cashbook.line,quantity:"
+msgid "Quantity"
+msgstr "Quantity"
+
+msgctxt "field:cashbook.line,quantity_symbol:"
+msgid "Symbol"
+msgstr "Symbol"
+
diff --git a/message.xml b/message.xml
new file mode 100644
index 0000000..96ddc9f
--- /dev/null
+++ b/message.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Asset
+
+
+
+
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..51d7cde
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,24 @@
+# This file is part of Tryton. The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+import trytond.tests.test_tryton
+import unittest
+
+from trytond.modules.cashbook_investment.tests.test_book import CbInvTestCase
+
+
+__all__ = ['suite']
+
+
+class CashbookInvestmentTestCase(\
+ CbInvTestCase,\
+ ):
+ 'Test cashbook-investment module'
+ module = 'cashbook_investment'
+
+# end CashbookInvestmentTestCase
+
+def suite():
+ suite = trytond.tests.test_tryton.suite()
+ suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CashbookInvestmentTestCase))
+ return suite
diff --git a/tests/test_book.py b/tests/test_book.py
new file mode 100644
index 0000000..660de27
--- /dev/null
+++ b/tests/test_book.py
@@ -0,0 +1,191 @@
+# -*- 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 trytond.modules.cashbook.tests import CashbookTestCase
+from trytond.modules.investment.tests import InvestmentTestCase
+from datetime import date
+from decimal import Decimal
+
+
+class CbInvTestCase(CashbookTestCase, InvestmentTestCase):
+ 'Test cashbook-investment module'
+ module = 'cashbook_investment'
+
+ @with_transaction()
+ def test_assetbook_create(self):
+ """ create cashbook, set 'btype' to asset
+ """
+ pool = Pool()
+ Book = pool.get('cashbook.book')
+ BType = pool.get('cashbook.type')
+
+ 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,
+ }])
+
+ BType.write(*[
+ [types],
+ {
+ 'feature': 'asset',
+ }])
+
+ self.assertEqual(book.name, 'Book 1')
+ self.assertEqual(book.rec_name, 'Book 1 | 0.00 usd | Open')
+ self.assertEqual(book.btype.rec_name, 'CAS - Cash')
+ self.assertEqual(book.state, 'open')
+ self.assertEqual(book.state_string, 'Open')
+ self.assertEqual(book.feature, 'asset')
+ self.assertEqual(book.quantity_digits, 4)
+
+ @with_transaction()
+ def test_assetbook_create_line(self):
+ """ create cashbook, add line
+ """
+ pool = Pool()
+ Book = pool.get('cashbook.book')
+ BType = pool.get('cashbook.type')
+
+ types = self.prep_type()
+ BType.write(*[
+ [types],
+ {
+ 'feature': 'asset',
+ }])
+ category = self.prep_category(cattype='in')
+
+ company = self.prep_company()
+ party = self.prep_party()
+ asset = self.prep_asset_item(
+ company=company,
+ product = self.prep_asset_product(name='Product 1'))
+ self.assertEqual(asset.symbol, 'usd/u')
+
+ book, = Book.create([{
+ 'start_date': date(2022, 4, 1),
+ 'name': 'Book 1',
+ 'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
+ 'number_sequ': self.prep_sequence().id,
+ 'asset': asset.id,
+ 'quantity_uom': asset.uom.id,
+ 'quantity_digits': 3,
+ 'lines': [('create', [{
+ 'date': date(2022, 5, 1),
+ 'description': 'Text 1',
+ 'category': category.id,
+ 'bookingtype': 'in',
+ 'amount': Decimal('2.5'),
+ 'party': party.id,
+ 'quantity': Decimal('1.453'),
+ }],
+ )],
+ }])
+
+ self.assertEqual(book.name, 'Book 1')
+ self.assertEqual(book.rec_name, 'Book 1 | 2.50 usd | Open')
+ self.assertEqual(book.state, 'open')
+ self.assertEqual(book.feature, 'asset')
+ self.assertEqual(book.quantity_digits, 3)
+ self.assertEqual(book.balance_all, Decimal('2.5'))
+ self.assertEqual(len(book.lines), 1)
+ self.assertEqual(book.lines[0].amount, Decimal('2.5'))
+ self.assertEqual(book.lines[0].quantity, Decimal('1.453'))
+ self.assertEqual(book.lines[0].quantity_digits, 3)
+ self.assertEqual(book.lines[0].quantity_symbol, 'u')
+
+ @with_transaction()
+ def test_assetbook_book_uom(self):
+ """ check default auf uom
+ """
+ pool = Pool()
+ Book = pool.get('cashbook.book')
+ BType = pool.get('cashbook.type')
+
+ company = self.prep_company()
+ types = self.prep_type()
+ BType.write(*[
+ [types],
+ {
+ 'feature': 'asset',
+ 'name': 'Asset',
+ 'short': 'A',
+ }])
+
+ asset = self.prep_asset_item(
+ company=company,
+ product = self.prep_asset_product(name='Product 1'))
+ self.assertEqual(asset.symbol, 'usd/u')
+
+ book = Book(
+ asset = asset,
+ quantity_uom = None,
+ )
+ self.assertEqual(book.quantity_uom, None)
+ book.on_change_asset()
+ self.assertEqual(book.quantity_uom.rec_name, 'Unit')
+
+ @with_transaction()
+ def test_assetbook_book_with_asset(self):
+ """ create cashbook, set 'btype' to asset
+ """
+ pool = Pool()
+ Book = pool.get('cashbook.book')
+ BType = pool.get('cashbook.type')
+
+ company = self.prep_company()
+ types = self.prep_type()
+ BType.write(*[
+ [types],
+ {
+ 'feature': 'asset',
+ 'name': 'Asset',
+ 'short': 'A',
+ }])
+
+ asset = self.prep_asset_item(
+ company=company,
+ product = self.prep_asset_product(name='Product 1'))
+ self.assertEqual(asset.symbol, 'usd/u')
+
+ book, = Book.create([{
+ 'name': 'Book 1',
+ 'btype': types.id,
+ 'company': company.id,
+ 'currency': company.currency.id,
+ 'number_sequ': self.prep_sequence().id,
+ 'asset': asset.id,
+ 'quantity_uom': asset.uom.id,
+ }])
+
+ self.assertEqual(book.name, 'Book 1')
+ self.assertEqual(book.rec_name, 'Book 1 | 0.00 usd | Open')
+ self.assertEqual(book.btype.rec_name, 'A - Asset')
+ self.assertEqual(book.state, 'open')
+ self.assertEqual(book.feature, 'asset')
+ self.assertEqual(book.asset.rec_name, 'Product 1 | - usd/u | -')
+ self.assertEqual(book.quantity_uom.rec_name, 'Unit')
+
+ self.assertRaisesRegex(UserError,
+ 'A value is required for field "Asset" in "Cashbook".',
+ Book.write,
+ *[
+ [book],
+ {
+ 'asset': None,
+ }
+ ])
+
+# end CbInvTestCase
diff --git a/tryton.cfg b/tryton.cfg
index fceac5a..4ba0023 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -2,4 +2,8 @@
version=6.0.0
depends:
cashbook
+ investment
xml:
+ message.xml
+ book.xml
+ line.xml
diff --git a/types.py b/types.py
new file mode 100644
index 0000000..27f83f7
--- /dev/null
+++ b/types.py
@@ -0,0 +1,21 @@
+# -*- 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.i18n import gettext
+from trytond.pool import PoolMeta
+
+
+class Type(metaclass=PoolMeta):
+ __name__ = 'cashbook.type'
+
+ @classmethod
+ def get_sel_feature(cls):
+ """ get feature-modes
+ """
+ l1 = super(Type, cls).get_sel_feature()
+ l1.append(('asset', gettext('cashbook_investment.msg_btype_asset')))
+ return l1
+
+# end Type
diff --git a/view/book_form.xml b/view/book_form.xml
new file mode 100644
index 0000000..7a679d5
--- /dev/null
+++ b/view/book_form.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/view/line_form.xml b/view/line_form.xml
new file mode 100644
index 0000000..97e9d0b
--- /dev/null
+++ b/view/line_form.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+