diff --git a/book.py b/book.py index a4fd5a7..872f54a 100644 --- a/book.py +++ b/book.py @@ -74,8 +74,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): states={ 'readonly': Or( STATES['readonly'], - Len(Eval('lines')) > 0, - ), + Len(Eval('lines')) > 0), }, depends=DEPENDS+['lines']) feature = fields.Function(fields.Char( string='Feature', readonly=True, @@ -124,8 +123,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): states={ 'readonly': Or( STATES2['readonly'], - Len(Eval('lines')) > 0, - ), + Len(Eval('lines')) > 0), 'invisible': STATES2['invisible'], 'required': ~STATES2['invisible'], }, depends=DEPENDS2+['lines']) @@ -165,8 +163,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): states={ 'readonly': Or( STATES2['readonly'], - Len(Eval('lines', [])) > 0, - ), + Len(Eval('lines', [])) > 0), }, depends=DEPENDS2+['lines']) currency_digits = fields.Function(fields.Integer( string='Currency Digits', @@ -202,9 +199,6 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): Index( t, (t.btype, Index.Equality())), - Index( - t, - (t.parent, Index.Equality())), Index( t, (t.company, Index.Equality())), @@ -396,8 +390,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): query = tab_line.select( tab_line.cashbook, where=Operator( - getattr(tab_line, name), clause[2]), - ) + getattr(tab_line, name), clause[2])) return [('id', 'in', query)] @classmethod diff --git a/category.py b/category.py index 888697a..a10b3d1 100644 --- a/category.py +++ b/category.py @@ -11,6 +11,7 @@ from trytond.exceptions import UserError from trytond.i18n import gettext from sql.operators import Equal from .model import order_name_hierarchical +from .const import DEF_NONE sel_categorytype = [ @@ -70,7 +71,7 @@ class Category(tree(separator='/'), ModelSQL, ModelView): t, (t.name, Equal), (t.cattype, Equal), - where=(t.parent == None)), + where=(t.parent == DEF_NONE)), 'cashbook.msg_category_name_unique'), ]) diff --git a/configuration.py b/configuration.py index 2da84ee..2f2f5db 100644 --- a/configuration.py +++ b/configuration.py @@ -17,7 +17,8 @@ field_done = fields.Boolean( help='Show cashbook lines in Done-state.') 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.') class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): @@ -29,60 +30,47 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): domain=[ If(Eval('date_to') & Eval('date_from'), ('date_from', '<=', Eval('date_to')), - ()), - ])) + ())])) date_to = fields.MultiValue(fields.Date( string='End Date', depends=['date_from'], domain=[ If(Eval('date_to') & Eval('date_from'), ('date_from', '<=', Eval('date_to')), - ()), - ])) + ())])) checked = fields.MultiValue(field_checked) done = fields.MultiValue(field_done) catnamelong = fields.MultiValue(field_catnamelong) defbook = fields.MultiValue(fields.Many2One( string='Default Cashbook', - help='The default cashbook is selected when you open the booking wizard.', + help='The default cashbook is selected when you open ' + + 'the booking wizard.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) book1 = fields.MultiValue(fields.Many2One( string='Cashbook 1', help='Cash book available in selection dialog.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) book2 = fields.MultiValue(fields.Many2One( string='Cashbook 2', help='Cash book available in selection dialog.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) book3 = fields.MultiValue(fields.Many2One( string='Cashbook 3', help='Cash book available in selection dialog.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) book4 = fields.MultiValue(fields.Many2One( string='Cashbook 4', help='Cash book available in selection dialog.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) book5 = fields.MultiValue(fields.Many2One( string='Cashbook 5', help='Cash book available in selection dialog.', model_name='cashbook.book', ondelete='SET NULL', - domain=[ - ('btype', '!=', None), ('state', '=', 'open'), - ])) + domain=[('btype', '!=', None), ('state', '=', 'open')])) @classmethod def multivalue_model(cls, field): @@ -121,21 +109,20 @@ class UserConfiguration(ModelSQL, UserValueMixin): domain=[ If(Eval('date_to') & Eval('date_from'), ('date_from', '<=', Eval('date_to')), - ()), - ]) + ())]) date_to = fields.Date( string='End Date', depends=['date_from'], domain=[ If(Eval('date_to') & Eval('date_from'), ('date_from', '<=', Eval('date_to')), - ()), - ]) + ())]) checked = field_checked done = field_done catnamelong = field_catnamelong defbook = fields.Many2One( string='Default Cashbook', - help='The default cashbook is selected when you open the booking wizard.', + help='The default cashbook is selected when you open ' + + 'the booking wizard.', model_name='cashbook.book', ondelete='SET NULL', domain=[ ('btype', '!=', None), diff --git a/const.py b/const.py new file mode 100644 index 0000000..6eaa075 --- /dev/null +++ b/const.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# This file is part of the cashbook-module from m-ds.de for Tryton. +# The COPYRIGHT file at the top level of this repository contains the +# full copyright notices and license terms. + + +DEF_NONE = None + diff --git a/currency.py b/currency.py index 9d406c6..817812f 100644 --- a/currency.py +++ b/currency.py @@ -42,7 +42,8 @@ class CurrencyRate(metaclass=PoolMeta): MemCache = Pool().get('cashbook.memcache') for record in records: - MemCache.record_update(CACHEKEY_CURRENCY % record.currency.id, None) + MemCache.record_update( + CACHEKEY_CURRENCY % record.currency.id, None) super(CurrencyRate, cls).delete(records) # end diff --git a/line.py b/line.py index 41c6c9a..45dae05 100644 --- a/line.py +++ b/line.py @@ -16,6 +16,7 @@ from sql.functions import DatePart from sql.conditionals import Case from .book import sel_state_book from .mixin import SecondCurrencyMixin, MemCacheIndexMx +from .const import DEF_NONE sel_payee = [ @@ -48,7 +49,9 @@ STATES = { DEPENDS = ['state', 'state_cashbook'] -class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): +class Line( + SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, + ModelView): 'Cashbook Line' __name__ = 'cashbook.line' @@ -75,8 +78,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): states={ 'readonly': Or( STATES['readonly'], - Bool(Eval('bookingtype')) == False, - ), + ~Bool(Eval('bookingtype'))), 'required': Eval('bookingtype', '').in_(['in', 'out']), 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']), }, depends=DEPENDS+['bookingtype'], @@ -161,7 +163,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): splitlines = fields.One2Many( string='Split booking lines', model_name='cashbook.split', - help='Rows with different categories form the total sum of the booking', + help='Rows with different categories form the total ' + + 'sum of the booking', states={ 'invisible': ~Eval('bookingtype' '').in_(['spin', 'spout']), 'readonly': Or( @@ -225,9 +228,6 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): t = cls.__table__() cls._sql_indexes.update({ - Index( - t, - (t.cashbook, Index.Equality())), Index( t, (t.date, Index.Range(order='ASC'))), @@ -308,7 +308,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): ).select( tab_line.id, where=tab_line.bookingtype.in_(['mvin', 'mvout']) & - (tab_line.amount_2nd_currency == None) & + (tab_line.amount_2nd_currency == DEF_NONE) & (tab_book.currency != tab_book2.currency) ) lines = Line2.search([('id', 'in', query)]) @@ -344,7 +344,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): for line in lines: if line.reference: if Transaction().context.get( - 'line.allow.wfedit', False) == False: + 'line.allow.wfedit', False) is False: raise UserError(gettext( 'cashbook.msg_line_denywf_by_reference', recname=line.reference.rec_name, @@ -508,19 +508,20 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): """ credit = self.credit if self.credit is not None else Decimal('0.0') debit = self.debit if self.debit is not None else Decimal('0.0') - return '%(date)s|%(type)s|%(amount)s %(symbol)s|%(desc)s [%(category)s]' % { - 'date': Report.format_date(self.date), - 'desc': (self.description or '-')[:40], - 'amount': Report.format_number( - credit - debit, None, - digits=getattr(self.currency, 'digits', 2)), - 'symbol': getattr(self.currency, 'symbol', '-'), - 'category': self.category_view - if self.bookingtype in ['in', 'out'] - else getattr(self.booktransf, 'rec_name', '-'), - 'type': gettext( - 'cashbook.msg_line_bookingtype_%s' % - self.bookingtype)} + return '|'.join([ + Report.format_date(self.date), + gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype), + '%(amount)s %(symbol)s' % { + 'amount': Report.format_number( + credit - debit, None, + digits=getattr(self.currency, 'digits', 2)), + 'symbol': getattr(self.currency, 'symbol', '-')}, + '%(desc)s [%(category)s]' % { + 'desc': (self.description or '-')[:40], + 'category': self.category_view + if self.bookingtype in ['in', 'out'] + else getattr(self.booktransf, 'rec_name', '-')}, + ]) @staticmethod def order_state(tables): @@ -535,8 +536,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): (tab_line.state == 'edit', 1), (tab_line.state.in_(['check', 'recon', 'done']), 0), else_=2), - where=tab_line.id == table.id - ) + where=tab_line.id == table.id) return [query] @staticmethod @@ -626,7 +626,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView): if self.bookingtype: if self.category: - if self.bookingtype not in types.get(self.category.cattype, ''): + if self.bookingtype not in types.get( + self.category.cattype, ''): self.category = None if self.bookingtype.startswith('sp'): # split booking diff --git a/mixin.py b/mixin.py index 5b4f054..363bed5 100644 --- a/mixin.py +++ b/mixin.py @@ -31,20 +31,19 @@ class SecondCurrencyMixin: states={ 'readonly': Or( STATES['readonly'], - ~Bool(Eval('currency2nd')) - ), + ~Bool(Eval('currency2nd'))), 'required': Bool(Eval('currency2nd')), 'invisible': ~Bool(Eval('currency2nd')), }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']) rate_2nd_currency = fields.Function(fields.Numeric( string='Rate', - help='Exchange rate between the currencies of the participating cashbooks.', + help='Exchange rate between the currencies of the ' + + 'participating cashbooks.', digits=(rate_decimal * 2, rate_decimal), states={ 'readonly': Or( STATES['readonly'], - ~Bool(Eval('currency2nd')) - ), + ~Bool(Eval('currency2nd'))), 'required': Bool(Eval('currency2nd')), 'invisible': ~Bool(Eval('currency2nd')), }, depends=DEPENDS+['currency2nd_digits', 'currency2nd']), @@ -124,15 +123,13 @@ class SecondCurrencyMixin: self.amount_2nd_currency = Currency.compute( self.currency, self.amount, - self.booktransf.currency - ) + self.booktransf.currency) if self.amount != Decimal('0.0'): self.rate_2nd_currency = \ self.amount_2nd_currency / self.amount else: self.amount_2nd_currency = self.booktransf.currency.round( - self.amount * self.rate_2nd_currency - ) + self.amount * self.rate_2nd_currency) @classmethod def set_rate_2nd_currency(cls, lines, name, value): diff --git a/model.py b/model.py index 0d5598c..e7b4ebc 100644 --- a/model.py +++ b/model.py @@ -15,6 +15,8 @@ from sql import With from sql.functions import Function from sql.conditionals import Coalesce import copy +from .const import DEF_NONE + if config.get('cashbook', 'memcache', default='yes').lower() \ in ['yes', '1', 'true']: @@ -269,7 +271,7 @@ def order_name_hierarchical(model_name, tables): lines = With('id', 'name', 'name_path', recursive=True) lines.query = tab_mod.select( tab_mod.id, tab_mod.name, Array(tab_mod.name), - where=tab_mod.parent == None, + where=tab_mod.parent == DEF_NONE, ) lines.query |= tab_mod2.join( lines, diff --git a/reconciliation.py b/reconciliation.py index e27e0c9..d0d5196 100644 --- a/reconciliation.py +++ b/reconciliation.py @@ -204,7 +204,8 @@ class Reconciliation(Workflow, ModelSQL, ModelView): # unlink lines from reconciliation if len(reconciliation.lines) > 0: - values['lines'] = [('remove', [x.id for x in reconciliation.lines])] + values['lines'] = [ + ('remove', [x.id for x in reconciliation.lines])] return values @classmethod @@ -339,20 +340,24 @@ class Reconciliation(Workflow, ModelSQL, ModelView): def get_rec_name(self, name): """ short + name """ - return '%(from)s - %(to)s | %(start_amount)s %(symbol)s - %(end_amount)s %(symbol)s [%(num)s]' % { - 'from': Report.format_date(self.date_from, None) + return ' '.join([ + Report.format_date(self.date_from, None) if self.date_from is not None else '-', - 'to': Report.format_date(self.date_to, None) - if self.date_to is not None else '-', - 'start_amount': Report.format_number( + '-', + Report.format_date(self.date_to, None) + if self.date_to is not None else '-', + '|', + Report.format_number( self.start_amount or 0.0, None, digits=getattr(self.currency, 'digits', 2)), - 'end_amount': Report.format_number( + getattr(self.currency, 'symbol', '-'), + '-', + Report.format_number( self.end_amount or 0.0, None, digits=getattr(self.currency, 'digits', 2)), - 'symbol': getattr(self.currency, 'symbol', '-'), - 'num': len(self.lines), - } + getattr(self.currency, 'symbol', '-'), + '[%(num)s]' % {'num': len(self.lines)}, + ]) @classmethod def default_date_from(cls): diff --git a/setup.py b/setup.py index ff59617..0a3a2ba 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """ # Always prefer setuptools over distutils -from setuptools import setup, find_packages +from setuptools import setup # To use a consistent encoding from codecs import open from os import path @@ -36,7 +36,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f: l2 = i.strip().split(';') if len(l2) < 4: continue - modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]} + modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]} # tryton-version major_version = 6 @@ -51,19 +51,21 @@ for dep in info.get('depends', []): prefix = modversion[dep]['prefix'] if len(modversion[dep]['max']) > 0: - requires.append('%s_%s >= %s, <= %s' % - (prefix, dep, modversion[dep]['min'], modversion[dep]['max'])) - else : - requires.append('%s_%s >= %s' % - (prefix, dep, modversion[dep]['min'])) - else : - requires.append('%s_%s >= %s.%s, < %s.%s' % - ('trytond', dep, major_version, minor_version, + requires.append('%s_%s >= %s, <= %s' % ( + prefix, dep, modversion[dep]['min'], + modversion[dep]['max'])) + else: + requires.append('%s_%s >= %s' % ( + prefix, dep, modversion[dep]['min'])) + else: + requires.append('%s_%s >= %s.%s, < %s.%s' % ( + 'trytond', dep, major_version, minor_version, major_version, minor_version + 1)) -requires.append('trytond >= %s.%s, < %s.%s' % - (major_version, minor_version, major_version, minor_version + 1)) +requires.append('trytond >= %s.%s, < %s.%s' % ( + major_version, minor_version, major_version, minor_version + 1)) -setup(name='%s_%s' % (PREFIX, MODULE), +setup( + name='%s_%s' % (PREFIX, MODULE), version=info.get('version', '0.0.1'), description='Tryton module to add a cashbook.', long_description=long_description, @@ -74,21 +76,21 @@ setup(name='%s_%s' % (PREFIX, MODULE), author_email='service@m-ds.de', license='GPL-3', classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Plugins', - 'Framework :: Tryton', - 'Intended Audience :: Developers', - 'Intended Audience :: Customer Service', - 'Intended Audience :: Information Technology', - 'Intended Audience :: Financial and Insurance Industry', - 'Topic :: Office/Business', - 'Topic :: Office/Business :: Financial :: Accounting', - 'Natural Language :: German', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Plugins', + 'Framework :: Tryton', + 'Intended Audience :: Developers', + 'Intended Audience :: Customer Service', + 'Intended Audience :: Information Technology', + 'Intended Audience :: Financial and Insurance Industry', + 'Topic :: Office/Business', + 'Topic :: Office/Business :: Financial :: Accounting', + 'Natural Language :: German', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], keywords='tryton cashbook', @@ -97,10 +99,10 @@ setup(name='%s_%s' % (PREFIX, MODULE), 'trytond.modules.%s' % MODULE, ], package_data={ - 'trytond.modules.%s' % MODULE: (info.get('xml', []) - + ['tryton.cfg', 'locale/*.po', 'tests/*.py', - 'view/*.xml', 'icon/*.svg', 'docs/*.txt', - 'report/*.fods', 'versiondep.txt', 'README.rst']), + 'trytond.modules.%s' % MODULE: (info.get('xml', []) + [ + 'tryton.cfg', 'locale/*.po', 'tests/*.py', + 'view/*.xml', 'icon/*.svg', 'docs/*.txt', + 'report/*.fods', 'versiondep.txt', 'README.rst']), }, install_requires=requires, diff --git a/types.py b/types.py index 42bb54d..11bcaf5 100644 --- a/types.py +++ b/types.py @@ -28,7 +28,9 @@ class Type(ModelSQL, ModelView): cls._order.insert(0, ('name', 'ASC')) t = cls.__table__() cls._sql_constraints.extend([ - ('code_uniq', Unique(t, t.short), 'cashbook.msg_type_short_unique'), + ('code_uniq', + Unique(t, t.short), + 'cashbook.msg_type_short_unique'), ]) cls._sql_indexes.update({ Index( @@ -53,8 +55,7 @@ class Type(ModelSQL, ModelView): """ return '%(short)s - %(name)s' % { 'short': self.short or '-', - 'name': self.name or '-', - } + 'name': self.name or '-'} @classmethod def search_rec_name(cls, name, clause): @@ -63,8 +64,7 @@ class Type(ModelSQL, ModelView): return [ 'OR', ('name',) + tuple(clause[1:]), - ('short',) + tuple(clause[1:]), - ] + ('short',) + tuple(clause[1:])] @staticmethod def default_company(): diff --git a/wizard_booking.py b/wizard_booking.py index 429deae..9530a35 100644 --- a/wizard_booking.py +++ b/wizard_booking.py @@ -50,7 +50,7 @@ class EnterBookingStart(ModelView): string='Category', model_name='cashbook.category', depends=['bookingtype'], states={ - 'readonly': Bool(Eval('bookingtype')) == False, + 'readonly': ~Bool(Eval('bookingtype')), 'required': Eval('bookingtype', '').in_(['in', 'out']), 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']), }, @@ -90,7 +90,8 @@ class EnterBookingStart(ModelView): if self.bookingtype: if self.category: - if self.bookingtype not in types.get(self.category.cattype, ''): + if self.bookingtype not in types.get( + self.category.cattype, ''): self.category = None @fields.depends('cashbook', '_parent_cashbook.owner')