formatting, line: test for delete of party
This commit is contained in:
parent
78f160bf0b
commit
619a17bcd6
16 changed files with 701 additions and 516 deletions
|
@ -18,6 +18,7 @@ from .cbreport import ReconciliationReport
|
|||
from .currency import CurrencyRate
|
||||
from .model import MemCache
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
MemCache,
|
||||
|
|
155
book.py
155
book.py
|
@ -20,7 +20,9 @@ from .model import order_name_hierarchical, sub_ids_hierarchical, \
|
|||
|
||||
|
||||
# enable/disable caching of cachekey for 'currency.rate'
|
||||
if config.get('cashbook', 'cache_currency', default='yes').lower() in ['yes', '1', 'true']:
|
||||
if config.get(
|
||||
'cashbook', 'cache_currency', default='yes'
|
||||
).lower() in ['yes', '1', 'true']:
|
||||
ENA_CURRKEY = True
|
||||
else:
|
||||
ENA_CURRKEY = False
|
||||
|
@ -55,14 +57,17 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
'Cashbook'
|
||||
__name__ = 'cashbook.book'
|
||||
|
||||
company = fields.Many2One(string='Company', model_name='company.company',
|
||||
company = fields.Many2One(
|
||||
string='Company', model_name='company.company',
|
||||
required=True, select=True, ondelete="RESTRICT")
|
||||
name = fields.Char(string='Name', required=True,
|
||||
states=STATES, depends=DEPENDS)
|
||||
description = fields.Text(string='Description',
|
||||
states=STATES, depends=DEPENDS)
|
||||
btype = fields.Many2One(string='Type', select=True,
|
||||
help='A cash book with type can contain postings. Without type is a view.',
|
||||
name = fields.Char(
|
||||
string='Name', required=True, states=STATES, depends=DEPENDS)
|
||||
description = fields.Text(
|
||||
string='Description', states=STATES, depends=DEPENDS)
|
||||
btype = fields.Many2One(
|
||||
string='Type', select=True,
|
||||
help='A cash book with type can contain postings. ' +
|
||||
'Without type is a view.',
|
||||
model_name='cashbook.type', ondelete='RESTRICT',
|
||||
states={
|
||||
'readonly': Or(
|
||||
|
@ -70,40 +75,50 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
Len(Eval('lines')) > 0,
|
||||
),
|
||||
}, depends=DEPENDS+['lines'])
|
||||
feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_feature')
|
||||
owner = fields.Many2One(string='Owner', required=True, select=True,
|
||||
owner = fields.Many2One(
|
||||
string='Owner', required=True, select=True,
|
||||
model_name='res.user', ondelete='SET NULL',
|
||||
states=STATES, depends=DEPENDS)
|
||||
reviewer = fields.Many2One(string='Reviewer', select=True,
|
||||
reviewer = fields.Many2One(
|
||||
string='Reviewer', select=True,
|
||||
help='Group of users who have write access to the cashbook.',
|
||||
model_name='res.group', ondelete='SET NULL',
|
||||
states=STATES, depends=DEPENDS)
|
||||
observer = fields.Many2One(string='Observer', select=True,
|
||||
observer = fields.Many2One(
|
||||
string='Observer', select=True,
|
||||
help='Group of users who have read-only access to the cashbook.',
|
||||
model_name='res.group', ondelete='SET NULL',
|
||||
states=STATES, depends=DEPENDS)
|
||||
lines = fields.One2Many(string='Lines', field='cashbook',
|
||||
lines = fields.One2Many(
|
||||
string='Lines', field='cashbook',
|
||||
model_name='cashbook.line',
|
||||
states=STATES, depends=DEPENDS)
|
||||
reconciliations = fields.One2Many(string='Reconciliations',
|
||||
reconciliations = fields.One2Many(
|
||||
string='Reconciliations',
|
||||
field='cashbook', model_name='cashbook.recon',
|
||||
states=STATES2, depends=DEPENDS2)
|
||||
number_sequ = fields.Many2One(string='Line numbering',
|
||||
number_sequ = fields.Many2One(
|
||||
string='Line numbering',
|
||||
help='Number sequence for numbering of the cash book lines.',
|
||||
model_name='ir.sequence',
|
||||
domain=[
|
||||
('sequence_type', '=', Id('cashbook', 'sequence_type_cashbook_line')),
|
||||
('sequence_type', '=',
|
||||
Id('cashbook', 'sequence_type_cashbook_line')),
|
||||
['OR',
|
||||
('company', '=', None),
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
('company', '=', Eval('company', -1))],
|
||||
],
|
||||
states=STATES3, depends=DEPENDS2+['company'])
|
||||
number_atcheck = fields.Boolean(string="number when 'Checking'",
|
||||
help="The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done.",
|
||||
number_atcheck = fields.Boolean(
|
||||
string="number when 'Checking'",
|
||||
help="The numbering of the lines is done in the step Check. " +
|
||||
"If the check mark is inactive, this happens with Done.",
|
||||
states=STATES2, depends=DEPENDS2)
|
||||
start_date = fields.Date(string='Initial Date',
|
||||
start_date = fields.Date(
|
||||
string='Initial Date',
|
||||
states={
|
||||
'readonly': Or(
|
||||
STATES2['readonly'],
|
||||
|
@ -112,25 +127,29 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
'invisible': STATES2['invisible'],
|
||||
'required': ~STATES2['invisible'],
|
||||
}, depends=DEPENDS2+['lines'])
|
||||
balance = fields.Function(fields.Numeric(string='Balance',
|
||||
balance = fields.Function(fields.Numeric(
|
||||
string='Balance',
|
||||
readonly=True, depends=['currency_digits'],
|
||||
help='Balance of bookings to date',
|
||||
digits=(16, Eval('currency_digits', 2))),
|
||||
'get_balance_cashbook', searcher='search_balance')
|
||||
balance_all = fields.Function(fields.Numeric(string='Total balance',
|
||||
balance_all = fields.Function(fields.Numeric(
|
||||
string='Total balance',
|
||||
readonly=True, depends=['currency_digits'],
|
||||
help='Balance of all bookings',
|
||||
digits=(16, Eval('currency_digits', 2))),
|
||||
'get_balance_cashbook', searcher='search_balance')
|
||||
|
||||
balance_ref = fields.Function(fields.Numeric(string='Balance (Ref.)',
|
||||
balance_ref = fields.Function(fields.Numeric(
|
||||
string='Balance (Ref.)',
|
||||
help='Balance in company currency',
|
||||
readonly=True, digits=(16, Eval('company_currency_digits', 2)),
|
||||
states={
|
||||
'invisible': ~Bool(Eval('company_currency')),
|
||||
}, depends=['company_currency_digits', 'company_currency']),
|
||||
'get_balance_cashbook')
|
||||
company_currency = fields.Function(fields.Many2One(readonly=True,
|
||||
company_currency = fields.Function(fields.Many2One(
|
||||
readonly=True,
|
||||
string='Company Currency', states={'invisible': True},
|
||||
model_name='currency.currency'),
|
||||
'on_change_with_company_currency')
|
||||
|
@ -138,7 +157,8 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
string='Currency Digits (Ref.)', readonly=True),
|
||||
'on_change_with_currency_digits')
|
||||
|
||||
currency = fields.Many2One(string='Currency', select=True,
|
||||
currency = fields.Many2One(
|
||||
string='Currency', select=True,
|
||||
model_name='currency.currency',
|
||||
states={
|
||||
'readonly': Or(
|
||||
|
@ -146,15 +166,19 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
Len(Eval('lines', [])) > 0,
|
||||
),
|
||||
}, depends=DEPENDS2+['lines'])
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
|
||||
currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits',
|
||||
readonly=True), 'on_change_with_currency_digits')
|
||||
state = fields.Selection(string='State', required=True,
|
||||
state = fields.Selection(
|
||||
string='State', required=True,
|
||||
readonly=True, selection=sel_state_book)
|
||||
state_string = state.translated('state')
|
||||
|
||||
parent = fields.Many2One(string="Parent",
|
||||
parent = fields.Many2One(
|
||||
string="Parent",
|
||||
model_name='cashbook.book', ondelete='RESTRICT')
|
||||
childs = fields.One2Many(string='Children', field='parent',
|
||||
childs = fields.One2Many(
|
||||
string='Children', field='parent',
|
||||
model_name='cashbook.book')
|
||||
|
||||
@classmethod
|
||||
|
@ -246,8 +270,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
Case(
|
||||
(tab_book.state == 'open', 0),
|
||||
else_=1),
|
||||
where=tab_book.id==table.id
|
||||
)
|
||||
where=tab_book.id == table.id)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
|
@ -287,17 +310,19 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
# deny invalid date in context
|
||||
if isinstance(query_date, str):
|
||||
try:
|
||||
dt1 = date.fromisoformat(query_date)
|
||||
except :
|
||||
date.fromisoformat(query_date)
|
||||
except Exception:
|
||||
query_date = IrDate.today()
|
||||
|
||||
query = tab_book.join(tab_line,
|
||||
query = tab_book.join(
|
||||
tab_line,
|
||||
condition=tab_book.id == tab_line.cashbook,
|
||||
).select(
|
||||
tab_line.cashbook,
|
||||
tab_book.currency,
|
||||
Sum(Case(
|
||||
(tab_line.date <= query_date, tab_line.credit - tab_line.debit),
|
||||
(tab_line.date <= query_date,
|
||||
tab_line.credit - tab_line.debit),
|
||||
else_=Decimal('0.0'),
|
||||
)).as_('balance'),
|
||||
Sum(tab_line.credit - tab_line.debit).as_('balance_all'),
|
||||
|
@ -313,9 +338,9 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
(tab_book, tab2) = Book2.get_balance_of_cashbook_sql()
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_book.select(tab_book.balance,
|
||||
where=tab_book.cashbook==table.id,
|
||||
)
|
||||
query = tab_book.select(
|
||||
tab_book.balance,
|
||||
where=tab_book.cashbook == table.id)
|
||||
return [query]
|
||||
|
||||
@staticmethod
|
||||
|
@ -326,9 +351,9 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
(tab_book, tab2) = Book2.get_balance_of_cashbook_sql()
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_book.select(tab_book.balance_all,
|
||||
where=tab_book.cashbook==table.id,
|
||||
)
|
||||
query = tab_book.select(
|
||||
tab_book.balance_all,
|
||||
where=tab_book.cashbook == table.id)
|
||||
return [query]
|
||||
|
||||
@classmethod
|
||||
|
@ -368,8 +393,8 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
query_date = context.get('date', IrDate.today())
|
||||
if isinstance(query_date, str):
|
||||
try:
|
||||
dt1 = date.fromisoformat(query_date)
|
||||
except :
|
||||
date.fromisoformat(query_date)
|
||||
except Exception:
|
||||
query_date = IrDate.today()
|
||||
|
||||
cache_keys = {
|
||||
|
@ -382,11 +407,11 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
}, {
|
||||
'model': 'currency.currency.rate',
|
||||
'query': [('currency.id', '=', x.currency.id)],
|
||||
'cachekey' if ENA_CURRKEY else 'disabled': CACHEKEY_CURRENCY % x.currency.id,
|
||||
'cachekey' if ENA_CURRKEY
|
||||
else 'disabled': CACHEKEY_CURRENCY % x.currency.id,
|
||||
}, ],
|
||||
addkeys=[query_date.isoformat()])
|
||||
for x in cashbooks
|
||||
}
|
||||
for x in cashbooks}
|
||||
|
||||
# read from cache
|
||||
(todo_cashbook, result) = MemCache.read_from_cache(
|
||||
|
@ -396,16 +421,19 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
|
||||
# query balances of cashbooks and sub-cashbooks
|
||||
with Transaction().set_context({
|
||||
'date': query_date,
|
||||
}):
|
||||
'date': query_date}):
|
||||
(tab_line, tab2) = cls.get_balance_of_cashbook_sql()
|
||||
tab_subids = sub_ids_hierarchical('cashbook.book')
|
||||
query = tab_book.join(tab_subids,
|
||||
query = tab_book.join(
|
||||
tab_subids,
|
||||
condition=tab_book.id == tab_subids.parent,
|
||||
).join(tab_comp,
|
||||
).join(
|
||||
tab_comp,
|
||||
condition=tab_book.company == tab_comp.id,
|
||||
).join(tab_line,
|
||||
condition=tab_line.cashbook==AnyInArray(tab_subids.subids),
|
||||
).join(
|
||||
tab_line,
|
||||
condition=tab_line.cashbook == AnyInArray(
|
||||
tab_subids.subids),
|
||||
).select(
|
||||
tab_book.id,
|
||||
tab_book.currency.as_('to_currency'),
|
||||
|
@ -413,7 +441,8 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
tab_comp.currency.as_('company_currency'),
|
||||
Sum(tab_line.balance).as_('balance'),
|
||||
Sum(tab_line.balance_all).as_('balance_all'),
|
||||
group_by=[tab_book.id, tab_line.currency, tab_comp.currency],
|
||||
group_by=[
|
||||
tab_book.id, tab_line.currency, tab_comp.currency],
|
||||
where=tab_book.id.in_([x.id for x in todo_cashbook]),
|
||||
)
|
||||
cursor.execute(*query)
|
||||
|
@ -497,27 +526,26 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_book_btype_with_lines',
|
||||
cbname=book.rec_name,
|
||||
numlines = len(book.lines),
|
||||
))
|
||||
numlines=len(book.lines)))
|
||||
|
||||
if book.state != 'open':
|
||||
# allow state-update, if its the only action
|
||||
if not (('state' in values.keys()) and (len(values.keys()) == 1)):
|
||||
if not (('state' in values.keys()) and
|
||||
(len(values.keys()) == 1)):
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_book_deny_write',
|
||||
bookname=book.rec_name,
|
||||
state_txt = book.state_string,
|
||||
))
|
||||
state_txt=book.state_string))
|
||||
|
||||
# if owner changes, remove book from user-config
|
||||
if 'owner' in values.keys():
|
||||
if book.owner.id != values['owner']:
|
||||
for x in ['defbook', 'book1', 'book2', 'book3',
|
||||
for x in [
|
||||
'defbook', 'book1', 'book2', 'book3',
|
||||
'book4', 'book5']:
|
||||
cfg1 = ConfigUser.search([
|
||||
('iduser.id', '=', book.owner.id),
|
||||
('%s.id' % x, '=', book.id),
|
||||
])
|
||||
('%s.id' % x, '=', book.id)])
|
||||
if len(cfg1) > 0:
|
||||
to_write_config.extend([cfg1, {x: None}])
|
||||
super(Book, cls).write(*args)
|
||||
|
@ -534,8 +562,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_book_deny_delete',
|
||||
bookname=book.rec_name,
|
||||
booklines = len(book.lines),
|
||||
))
|
||||
booklines=len(book.lines)))
|
||||
super(Book, cls).delete(books)
|
||||
|
||||
# end Book
|
||||
|
|
21
category.py
21
category.py
|
@ -25,22 +25,27 @@ class Category(tree(separator='/'), ModelSQL, ModelView):
|
|||
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
description = fields.Char(string='Description', translate=True)
|
||||
cattype = fields.Selection(string='Type', required=True,
|
||||
cattype = fields.Selection(
|
||||
string='Type', required=True,
|
||||
help='Type of Category', selection=sel_categorytype,
|
||||
states={'readonly': Bool(Eval('parent_cattype'))},
|
||||
domain=[If(Bool(Eval('parent_cattype')),
|
||||
('cattype', '=', Eval('parent_cattype', '')),
|
||||
())],
|
||||
depends=['parent_cattype'])
|
||||
parent_cattype = fields.Function(fields.Char(string='Parent Category Type',
|
||||
parent_cattype = fields.Function(fields.Char(
|
||||
string='Parent Category Type',
|
||||
readonly=True, states={'invisible': True}),
|
||||
'on_change_with_parent_cattype')
|
||||
|
||||
company = fields.Many2One(string='Company', model_name='company.company',
|
||||
company = fields.Many2One(
|
||||
string='Company', model_name='company.company',
|
||||
required=True, ondelete="RESTRICT")
|
||||
parent = fields.Many2One(string="Parent",
|
||||
parent = fields.Many2One(
|
||||
string="Parent",
|
||||
model_name='cashbook.category', ondelete='RESTRICT')
|
||||
childs = fields.One2Many(string='Children', field='parent',
|
||||
childs = fields.One2Many(
|
||||
string='Children', field='parent',
|
||||
model_name='cashbook.category')
|
||||
|
||||
@classmethod
|
||||
|
@ -61,7 +66,8 @@ class Category(tree(separator='/'), ModelSQL, ModelView):
|
|||
Unique(t, t.name, t.company, t.parent),
|
||||
'cashbook.msg_category_name_unique'),
|
||||
('name2_uniq',
|
||||
Exclude(t,
|
||||
Exclude(
|
||||
t,
|
||||
(t.name, Equal),
|
||||
(t.cattype, Equal),
|
||||
where=(t.parent == None)),
|
||||
|
@ -107,8 +113,7 @@ class Category(tree(separator='/'), ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_category_type_not_like_parent',
|
||||
parentname=category.parent.rec_name,
|
||||
catname = category.rec_name,
|
||||
))
|
||||
catname=category.rec_name,))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
|
|
11
cbreport.py
11
cbreport.py
|
@ -20,7 +20,8 @@ class ReconciliationReport(Report):
|
|||
Company = Pool().get('company.company')
|
||||
context2 = Transaction().context
|
||||
|
||||
context = super(ReconciliationReport, cls).get_context(records, header, data)
|
||||
context = super(
|
||||
ReconciliationReport, cls).get_context(records, header, data)
|
||||
context['company'] = Company(context2['company'])
|
||||
return context
|
||||
|
||||
|
@ -43,12 +44,12 @@ class ReconciliationReport(Report):
|
|||
'cashbook.msg_rep_reconciliation_fname',
|
||||
recname=recon_obj.cashbook.rec_name[:50],
|
||||
date_from=recon_obj.date_from.isoformat(),
|
||||
date_to = recon_obj.date_to.isoformat(),
|
||||
)
|
||||
date_to=recon_obj.date_to.isoformat())
|
||||
else:
|
||||
raise ValueError('invalid model')
|
||||
|
||||
(ext1, cont1, dirprint, title) = super(ReconciliationReport, cls).execute(ids, data)
|
||||
(ext1, cont1, dirprint, title) = super(
|
||||
ReconciliationReport, cls).execute(ids, data)
|
||||
|
||||
return (
|
||||
ext1,
|
||||
|
@ -63,5 +64,3 @@ class ReconciliationReport(Report):
|
|||
)
|
||||
|
||||
# end ReconciliationReport
|
||||
|
||||
|
||||
|
|
|
@ -9,11 +9,14 @@ from trytond.pyson import Eval, If
|
|||
from trytond.pool import Pool
|
||||
|
||||
|
||||
field_checked = fields.Boolean(string='Checked',
|
||||
field_checked = fields.Boolean(
|
||||
string='Checked',
|
||||
help='Show cashbook lines in Checked-state.')
|
||||
field_done = fields.Boolean(string='Done',
|
||||
field_done = fields.Boolean(
|
||||
string='Done',
|
||||
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.')
|
||||
|
||||
|
||||
|
@ -21,13 +24,15 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
|
|||
'Configuration'
|
||||
__name__ = 'cashbook.configuration'
|
||||
|
||||
date_from = fields.MultiValue(fields.Date(string='Start Date', depends=['date_to'],
|
||||
date_from = fields.MultiValue(fields.Date(
|
||||
string='Start Date', depends=['date_to'],
|
||||
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'],
|
||||
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')),
|
||||
|
@ -36,37 +41,43 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
|
|||
checked = fields.MultiValue(field_checked)
|
||||
done = fields.MultiValue(field_done)
|
||||
catnamelong = fields.MultiValue(field_catnamelong)
|
||||
defbook = fields.MultiValue(fields.Many2One(string='Default Cashbook',
|
||||
defbook = fields.MultiValue(fields.Many2One(
|
||||
string='Default Cashbook',
|
||||
help='The default cashbook is selected when you open the booking wizard.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
('btype', '!=', None), ('state', '=', 'open'),
|
||||
]))
|
||||
book1 = fields.MultiValue(fields.Many2One(string='Cashbook 1',
|
||||
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'),
|
||||
]))
|
||||
book2 = fields.MultiValue(fields.Many2One(string='Cashbook 2',
|
||||
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'),
|
||||
]))
|
||||
book3 = fields.MultiValue(fields.Many2One(string='Cashbook 3',
|
||||
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'),
|
||||
]))
|
||||
book4 = fields.MultiValue(fields.Many2One(string='Cashbook 4',
|
||||
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'),
|
||||
]))
|
||||
book5 = fields.MultiValue(fields.Many2One(string='Cashbook 5',
|
||||
book5 = fields.MultiValue(fields.Many2One(
|
||||
string='Cashbook 5',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
@ -79,7 +90,8 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
|
|||
"""
|
||||
pool = Pool()
|
||||
|
||||
if field in ['date_from', 'date_to', 'checked', 'done',
|
||||
if field in [
|
||||
'date_from', 'date_to', 'checked', 'done',
|
||||
'catnamelong', 'defbook', 'book1', 'book2',
|
||||
'book3', 'book4', 'book5']:
|
||||
return pool.get('cashbook.configuration_user')
|
||||
|
@ -104,13 +116,15 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
'User Configuration'
|
||||
__name__ = 'cashbook.configuration_user'
|
||||
|
||||
date_from = fields.Date(string='Start Date', depends=['date_to'],
|
||||
date_from = fields.Date(
|
||||
string='Start Date', depends=['date_to'],
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
('date_from', '<=', Eval('date_to')),
|
||||
()),
|
||||
])
|
||||
date_to = fields.Date(string='End Date', depends=['date_from'],
|
||||
date_to = fields.Date(
|
||||
string='End Date', depends=['date_from'],
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
('date_from', '<=', Eval('date_to')),
|
||||
|
@ -119,7 +133,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
checked = field_checked
|
||||
done = field_done
|
||||
catnamelong = field_catnamelong
|
||||
defbook = fields.Many2One(string='Default Cashbook',
|
||||
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',
|
||||
domain=[
|
||||
|
@ -127,7 +142,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
('state', '=', 'open'),
|
||||
('owner.id', '=', Eval('iduser', -1))
|
||||
], depends=['iduser'])
|
||||
book1 = fields.Many2One(string='Cashbook 1',
|
||||
book1 = fields.Many2One(
|
||||
string='Cashbook 1',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
@ -135,7 +151,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
('state', '=', 'open'),
|
||||
('owner.id', '=', Eval('iduser', -1))
|
||||
], depends=['iduser'])
|
||||
book2 = fields.Many2One(string='Cashbook 2',
|
||||
book2 = fields.Many2One(
|
||||
string='Cashbook 2',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
@ -143,7 +160,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
('state', '=', 'open'),
|
||||
('owner.id', '=', Eval('iduser', -1))
|
||||
], depends=['iduser'])
|
||||
book3 = fields.Many2One(string='Cashbook 3',
|
||||
book3 = fields.Many2One(
|
||||
string='Cashbook 3',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
@ -151,7 +169,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
('state', '=', 'open'),
|
||||
('owner.id', '=', Eval('iduser', -1))
|
||||
], depends=['iduser'])
|
||||
book4 = fields.Many2One(string='Cashbook 4',
|
||||
book4 = fields.Many2One(
|
||||
string='Cashbook 4',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
@ -159,7 +178,8 @@ class UserConfiguration(ModelSQL, UserValueMixin):
|
|||
('state', '=', 'open'),
|
||||
('owner.id', '=', Eval('iduser', -1))
|
||||
], depends=['iduser'])
|
||||
book5 = fields.Many2One(string='Cashbook 5',
|
||||
book5 = fields.Many2One(
|
||||
string='Cashbook 5',
|
||||
help='Cash book available in selection dialog.',
|
||||
model_name='cashbook.book', ondelete='SET NULL',
|
||||
domain=[
|
||||
|
|
|
@ -32,7 +32,8 @@ class CurrencyRate(metaclass=PoolMeta):
|
|||
actions = iter(args)
|
||||
for rates, values in zip(actions, actions):
|
||||
for rate in rates:
|
||||
MemCache.record_update(CACHEKEY_CURRENCY % rate.currency.id, rate)
|
||||
MemCache.record_update(
|
||||
CACHEKEY_CURRENCY % rate.currency.id, rate)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, records):
|
||||
|
|
318
line.py
318
line.py
|
@ -52,19 +52,25 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'Cashbook Line'
|
||||
__name__ = 'cashbook.line'
|
||||
|
||||
cashbook = fields.Many2One(string='Cashbook', required=True, select=True,
|
||||
cashbook = fields.Many2One(
|
||||
string='Cashbook', required=True, select=True,
|
||||
model_name='cashbook.book', ondelete='CASCADE', readonly=True,
|
||||
domain=[('btype', '!=', None)])
|
||||
date = fields.Date(string='Date', required=True, select=True,
|
||||
date = fields.Date(
|
||||
string='Date', required=True, select=True,
|
||||
states=STATES, depends=DEPENDS)
|
||||
month = fields.Function(fields.Integer(string='Month', readonly=True),
|
||||
month = fields.Function(fields.Integer(
|
||||
string='Month', readonly=True),
|
||||
'on_change_with_month', searcher='search_month')
|
||||
number = fields.Char(string='Number', readonly=True)
|
||||
description = fields.Text(string='Description', select=True,
|
||||
description = fields.Text(
|
||||
string='Description', select=True,
|
||||
states=STATES, depends=DEPENDS)
|
||||
descr_short = fields.Function(fields.Char(string='Description', readonly=True),
|
||||
descr_short = fields.Function(fields.Char(
|
||||
string='Description', readonly=True),
|
||||
'on_change_with_descr_short', searcher='search_descr_short')
|
||||
category = fields.Many2One(string='Category', select=True,
|
||||
category = fields.Many2One(
|
||||
string='Category', select=True,
|
||||
model_name='cashbook.category', ondelete='RESTRICT',
|
||||
states={
|
||||
'readonly': Or(
|
||||
|
@ -80,18 +86,23 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
('cattype', '=', 'in'),
|
||||
('cattype', '=', 'out'),
|
||||
)])
|
||||
category_view = fields.Function(fields.Char(string='Category', readonly=True),
|
||||
category_view = fields.Function(fields.Char(
|
||||
string='Category', readonly=True),
|
||||
'on_change_with_category_view', searcher='search_category_view')
|
||||
feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_feature')
|
||||
booktransf_feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
booktransf_feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_booktransf_feature')
|
||||
|
||||
bookingtype = fields.Selection(string='Type', required=True,
|
||||
bookingtype = fields.Selection(
|
||||
string='Type', required=True,
|
||||
help='Type of Booking', selection=sel_bookingtype, select=True,
|
||||
states=STATES, depends=DEPENDS)
|
||||
bookingtype_string = bookingtype.translated('bookingtype')
|
||||
amount = fields.Numeric(string='Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
amount = fields.Numeric(
|
||||
string='Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
required=True,
|
||||
states={
|
||||
'readonly': Or(
|
||||
|
@ -99,13 +110,16 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
Eval('bookingtype', '').in_(['spin', 'spout']),
|
||||
),
|
||||
}, depends=DEPENDS+['currency_digits', 'bookingtype'])
|
||||
debit = fields.Numeric(string='Debit', digits=(16, Eval('currency_digits', 2)),
|
||||
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)),
|
||||
credit = fields.Numeric(
|
||||
string='Credit', digits=(16, Eval('currency_digits', 2)),
|
||||
required=True, readonly=True, depends=['currency_digits'])
|
||||
|
||||
# party or cashbook as counterpart
|
||||
booktransf = fields.Many2One(string='Source/Dest',
|
||||
booktransf = fields.Many2One(
|
||||
string='Source/Dest',
|
||||
ondelete='RESTRICT', model_name='cashbook.book',
|
||||
domain=[
|
||||
('owner.id', '=', Eval('owner_cashbook', -1)),
|
||||
|
@ -117,28 +131,35 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'invisible': ~Eval('bookingtype', '').in_(['mvin', 'mvout']),
|
||||
'required': Eval('bookingtype', '').in_(['mvin', 'mvout']),
|
||||
}, depends=DEPENDS+['bookingtype', 'owner_cashbook', 'cashbook'])
|
||||
party = fields.Many2One(string='Party', model_name='party.party',
|
||||
party = fields.Many2One(
|
||||
string='Party', model_name='party.party',
|
||||
ondelete='RESTRICT',
|
||||
states={
|
||||
'readonly': STATES['readonly'],
|
||||
'invisible': ~Eval('bookingtype', '').in_(['in', 'out', 'spin', 'spout']),
|
||||
'invisible': ~Eval('bookingtype', '').in_(
|
||||
['in', 'out', 'spin', 'spout']),
|
||||
}, depends=DEPENDS+['bookingtype'])
|
||||
payee = fields.Function(fields.Reference(string='Payee', readonly=True,
|
||||
payee = fields.Function(fields.Reference(
|
||||
string='Payee', readonly=True,
|
||||
selection=sel_payee), 'on_change_with_payee', searcher='search_payee')
|
||||
|
||||
# link to lines created by this record
|
||||
reference = fields.Many2One(string='Reference', readonly=True, select=True,
|
||||
reference = fields.Many2One(
|
||||
string='Reference', readonly=True, select=True,
|
||||
states={
|
||||
'invisible': ~Bool(Eval('reference')),
|
||||
}, model_name='cashbook.line', ondelete='CASCADE',
|
||||
help='The current row was created by and is controlled by the reference row.')
|
||||
references = fields.One2Many(string='References',
|
||||
help='The current row was created by and is controlled ' +
|
||||
'by the reference row.')
|
||||
references = fields.One2Many(
|
||||
string='References',
|
||||
model_name='cashbook.line',
|
||||
help='The rows are created and managed by the current record.',
|
||||
states={
|
||||
'invisible': ~Bool(Eval('references')),
|
||||
}, field='reference', readonly=True)
|
||||
splitlines = fields.One2Many(string='Split booking lines',
|
||||
splitlines = fields.One2Many(
|
||||
string='Split booking lines',
|
||||
model_name='cashbook.split',
|
||||
help='Rows with different categories form the total sum of the booking',
|
||||
states={
|
||||
|
@ -150,7 +171,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'required': Eval('bookingtype' '').in_(['spin', 'spout']),
|
||||
}, field='line', depends=DEPENDS+['bookingtype'])
|
||||
|
||||
reconciliation = fields.Many2One(string='Reconciliation', readonly=True,
|
||||
reconciliation = fields.Many2One(
|
||||
string='Reconciliation', readonly=True,
|
||||
model_name='cashbook.recon', ondelete='SET NULL',
|
||||
domain=[('cashbook.id', '=', Eval('cashbook'))],
|
||||
depends=['cashbook'],
|
||||
|
@ -158,24 +180,31 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'invisible': ~Bool(Eval('reconciliation')),
|
||||
})
|
||||
|
||||
balance = fields.Function(fields.Numeric(string='Balance',
|
||||
balance = fields.Function(fields.Numeric(
|
||||
string='Balance',
|
||||
digits=(16, Eval('currency_digits', 2)),
|
||||
help='Balance of the cash book up to the current line, if the default sorting applies.',
|
||||
help='Balance of the cash book up to the current line, ' +
|
||||
'if the default sorting applies.',
|
||||
readonly=True, depends=['currency_digits']),
|
||||
'on_change_with_balance')
|
||||
|
||||
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
currency = fields.Function(fields.Many2One(
|
||||
model_name='currency.currency',
|
||||
string="Currency", readonly=True), 'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
|
||||
currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits',
|
||||
readonly=True), 'on_change_with_currency_digits')
|
||||
|
||||
state = fields.Selection(string='State', required=True, readonly=True,
|
||||
state = fields.Selection(
|
||||
string='State', required=True, readonly=True,
|
||||
select=True, selection=sel_linetype)
|
||||
state_string = state.translated('state')
|
||||
state_cashbook = fields.Function(fields.Selection(string='State of Cashbook',
|
||||
state_cashbook = fields.Function(fields.Selection(
|
||||
string='State of Cashbook',
|
||||
readonly=True, states={'invisible': True}, selection=sel_state_book),
|
||||
'on_change_with_state_cashbook', searcher='search_state_cashbook')
|
||||
owner_cashbook = fields.Function(fields.Many2One(string='Owner', readonly=True,
|
||||
owner_cashbook = fields.Function(fields.Many2One(
|
||||
string='Owner', readonly=True,
|
||||
states={'invisible': True}, model_name='res.user'),
|
||||
'on_change_with_owner_cashbook')
|
||||
|
||||
|
@ -230,8 +259,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
return super().view_attributes() + [
|
||||
('/tree', 'visual',
|
||||
If(Eval('balance', 0) < 0, 'warning',
|
||||
If(Eval('date', Date()) > Date(), 'muted', '')
|
||||
)),
|
||||
If(Eval('date', Date()) > Date(), 'muted', ''))),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -247,13 +275,16 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
tab_book2 = Book2.__table__() # transfer-target
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
query = tab_line.join(tab_book,
|
||||
query = tab_line.join(
|
||||
tab_book,
|
||||
condition=tab_line.cashbook == tab_book.id,
|
||||
).join(tab_book2,
|
||||
).join(
|
||||
tab_book2,
|
||||
condition=tab_line.booktransf == tab_book2.id,
|
||||
).select(tab_line.id,
|
||||
where=tab_line.bookingtype.in_(['mvin', 'mvout']) & \
|
||||
(tab_line.amount_2nd_currency == None) & \
|
||||
).select(
|
||||
tab_line.id,
|
||||
where=tab_line.bookingtype.in_(['mvin', 'mvout']) &
|
||||
(tab_line.amount_2nd_currency == None) &
|
||||
(tab_book.currency != tab_book2.currency)
|
||||
)
|
||||
lines = Line2.search([('id', 'in', query)])
|
||||
|
@ -288,19 +319,18 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
to_delete_line = []
|
||||
for line in lines:
|
||||
if line.reference:
|
||||
if Transaction().context.get('line.allow.wfedit', False) == False:
|
||||
if Transaction().context.get(
|
||||
'line.allow.wfedit', False) == False:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_denywf_by_reference',
|
||||
recname=line.reference.rec_name,
|
||||
cbook = line.reference.cashbook.rec_name,
|
||||
))
|
||||
cbook=line.reference.cashbook.rec_name))
|
||||
# delete references
|
||||
to_delete_line.extend(list(line.references))
|
||||
|
||||
if len(to_delete_line) > 0:
|
||||
with Transaction().set_context({
|
||||
'line.allow.wfedit': True,
|
||||
}):
|
||||
'line.allow.wfedit': True}):
|
||||
Line2.wfedit(to_delete_line)
|
||||
Line2.delete(to_delete_line)
|
||||
|
||||
|
@ -323,8 +353,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
('state', 'in', ['check', 'done']),
|
||||
('cashbook.id', '=', line.cashbook.id),
|
||||
('date_from', '<', line.date),
|
||||
('date_to', '>', line.date),
|
||||
]) > 0:
|
||||
('date_to', '>', line.date)]) > 0:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_err_write_to_reconciled',
|
||||
datetxt=Report.format_date(line.date),
|
||||
|
@ -336,9 +365,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
('cashbook.id', '=', line.cashbook.id),
|
||||
['OR',
|
||||
('date_from', '=', line.date),
|
||||
('date_to', '=', line.date),
|
||||
]
|
||||
]) > 1:
|
||||
('date_to', '=', line.date)]]) > 1:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_err_write_to_reconciled',
|
||||
datetxt=Report.format_date(line.date),
|
||||
|
@ -356,25 +383,28 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
if sp_line.splittype != 'tr':
|
||||
continue
|
||||
|
||||
values = cls.get_counterpart_values(line,
|
||||
splitline = sp_line,
|
||||
values = cls.get_counterpart_values(
|
||||
line, splitline=sp_line,
|
||||
values={
|
||||
'cashbook': sp_line.booktransf.id,
|
||||
'description': sp_line.description,
|
||||
'amount': sp_line.amount \
|
||||
if sp_line.currency.id == sp_line.booktransf.currency.id \
|
||||
'amount': sp_line.amount
|
||||
if sp_line.currency.id == sp_line.
|
||||
booktransf.currency.id
|
||||
else sp_line.amount_2nd_currency,
|
||||
'amount_2nd_currency': sp_line.amount \
|
||||
if sp_line.currency.id != sp_line.booktransf.currency.id \
|
||||
'amount_2nd_currency': sp_line.amount
|
||||
if sp_line.currency.id != sp_line.
|
||||
booktransf.currency.id
|
||||
else None,
|
||||
'bookingtype': 'mvin' \
|
||||
if line.bookingtype.endswith('out') else 'mvout',
|
||||
'bookingtype': 'mvin'
|
||||
if line.bookingtype.endswith('out')
|
||||
else 'mvout',
|
||||
})
|
||||
values.update(cls.get_debit_credit(values))
|
||||
to_create_line.append(values)
|
||||
|
||||
# add number to line
|
||||
if line.cashbook.number_atcheck == True:
|
||||
if line.cashbook.number_atcheck is True:
|
||||
if len(line.number or '') == 0:
|
||||
to_write_line.extend([
|
||||
[line],
|
||||
|
@ -459,11 +489,12 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'desc': (self.description or '-')[:40],
|
||||
'amount': Report.format_number(credit - debit, None),
|
||||
'symbol': getattr(self.currency, 'symbol', '-'),
|
||||
'category': self.category_view \
|
||||
if self.bookingtype in ['in', 'out'] \
|
||||
'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),
|
||||
}
|
||||
'type': gettext(
|
||||
'cashbook.msg_line_bookingtype_%s' %
|
||||
self.bookingtype)}
|
||||
|
||||
@staticmethod
|
||||
def order_state(tables):
|
||||
|
@ -490,10 +521,9 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
Category = Pool().get('cashbook.category')
|
||||
tab_cat = Category.__table__()
|
||||
|
||||
tab2 = tab_cat.select(tab_cat.name,
|
||||
where=tab_cat.id==table.category
|
||||
)
|
||||
|
||||
tab2 = tab_cat.select(
|
||||
tab_cat.name,
|
||||
where=tab_cat.id == table.category)
|
||||
return [tab2]
|
||||
|
||||
@staticmethod
|
||||
|
@ -509,8 +539,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
"""
|
||||
return ['OR',
|
||||
('party.rec_name',) + tuple(clause[1:]),
|
||||
('booktransf.rec_name',) + tuple(clause[1:]),
|
||||
]
|
||||
('booktransf.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def search_category_view(cls, name, clause):
|
||||
|
@ -529,10 +558,12 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
Operator = fields.SQL_OPERATORS[clause[1]]
|
||||
|
||||
dt1 = IrDate.today()
|
||||
query = tab_line.select(tab_line.id,
|
||||
query = tab_line.select(
|
||||
tab_line.id,
|
||||
where=Operator(
|
||||
Literal(12 * dt1.year + dt1.month) - \
|
||||
(Literal(12) * DatePart('year', tab_line.date) + DatePart('month', tab_line.date)),
|
||||
Literal(12 * dt1.year + dt1.month) -
|
||||
(Literal(12) * DatePart('year', tab_line.date) +
|
||||
DatePart('month', tab_line.date)),
|
||||
clause[2]),
|
||||
)
|
||||
return [('id', 'in', query)]
|
||||
|
@ -553,9 +584,11 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
def on_change_splitlines(self):
|
||||
""" update amount if splitlines change
|
||||
"""
|
||||
self.amount = sum([x.amount for x in self.splitlines if x.amount is not None])
|
||||
self.amount = sum([
|
||||
x.amount for x in self.splitlines if x.amount is not None])
|
||||
|
||||
@fields.depends('bookingtype', 'category', 'splitlines', 'booktransf',\
|
||||
@fields.depends(
|
||||
'bookingtype', 'category', 'splitlines', 'booktransf',
|
||||
'currency2nd')
|
||||
def on_change_bookingtype(self):
|
||||
""" clear category if not valid type
|
||||
|
@ -567,14 +600,15 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
|
||||
if self.bookingtype:
|
||||
if self.category:
|
||||
if not self.bookingtype 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
|
||||
self.category = None
|
||||
self.booktransf = None
|
||||
for spline in self.splitlines:
|
||||
if not self.bookingtype in types.get(getattr(spline.category, 'cattype', '-'), ''):
|
||||
if self.bookingtype not in types.get(
|
||||
getattr(spline.category, 'cattype', '-'), ''):
|
||||
spline.category = None
|
||||
elif self.bookingtype.startswith('mv'): # transfer
|
||||
self.splitlines = []
|
||||
|
@ -627,7 +661,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
if self.category:
|
||||
cfg1 = Configuration.get_singleton()
|
||||
|
||||
if getattr(cfg1, 'catnamelong', True) == True:
|
||||
if getattr(cfg1, 'catnamelong', True) is True:
|
||||
return self.category.rec_name
|
||||
else:
|
||||
return self.category.name
|
||||
|
@ -673,7 +707,9 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
return 2
|
||||
|
||||
@classmethod
|
||||
def get_balance_of_line(cls, line, field_name='amount', credit_name='credit', debit_name='debit'):
|
||||
def get_balance_of_line(
|
||||
cls, line, field_name='amount', credit_name='credit',
|
||||
debit_name='debit'):
|
||||
""" get balance of current line,
|
||||
try to speed up by usage of last reconcilitaion
|
||||
"""
|
||||
|
@ -699,8 +735,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
('date', '<=', line2.date),
|
||||
['OR',
|
||||
('reconciliation', '=', None),
|
||||
('reconciliation.id', '!=', recons[0]),
|
||||
],
|
||||
('reconciliation.id', '!=', recons[0])],
|
||||
])
|
||||
end_value = getattr(recons[0], 'end_%s' % field_name)
|
||||
return (query2, end_value)
|
||||
|
@ -719,7 +754,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
query.append(
|
||||
('reconciliation.id', '=', line.reconciliation.id),
|
||||
)
|
||||
balance = getattr(line.reconciliation, 'start_%s' % field_name)
|
||||
balance = getattr(
|
||||
line.reconciliation, 'start_%s' % field_name)
|
||||
else:
|
||||
(query2, balance2) = get_from_last_recon(line)
|
||||
query.extend(query2)
|
||||
|
@ -743,18 +779,16 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
break
|
||||
return balance
|
||||
|
||||
@fields.depends('id', 'date', 'cashbook', \
|
||||
'_parent_cashbook.id', 'reconciliation', \
|
||||
'_parent_reconciliation.start_amount',\
|
||||
'_parent_reconciliation.state')
|
||||
@fields.depends(
|
||||
'id', 'date', 'cashbook', '_parent_cashbook.id', 'reconciliation',
|
||||
'_parent_reconciliation.start_amount', '_parent_reconciliation.state')
|
||||
def on_change_with_balance(self, name=None):
|
||||
""" compute balance until current line, with current sort order,
|
||||
try to use a reconciliation as start to speed up calculation
|
||||
"""
|
||||
Line2 = Pool().get('cashbook.line')
|
||||
return Line2.get_balance_of_line(self,
|
||||
field_name='amount',
|
||||
credit_name='credit',
|
||||
return Line2.get_balance_of_line(
|
||||
self, field_name='amount', credit_name='credit',
|
||||
debit_name='debit')
|
||||
|
||||
@classmethod
|
||||
|
@ -764,12 +798,14 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
values2 = {}
|
||||
values2.update(values)
|
||||
|
||||
bookingtype = values2.get('bookingtype', getattr(line, 'bookingtype', None))
|
||||
bookingtype = values2.get(
|
||||
'bookingtype', getattr(line, 'bookingtype', None))
|
||||
if (bookingtype in ['in', 'out', 'mvin', 'mvout']) and \
|
||||
('splitlines' not in values2.keys()):
|
||||
if line:
|
||||
if len(line.splitlines) > 0:
|
||||
values2['splitlines'] = [('delete', [x.id for x in line.splitlines])]
|
||||
values2['splitlines'] = [
|
||||
('delete', [x.id for x in line.splitlines])]
|
||||
|
||||
if bookingtype in ['in', 'out']:
|
||||
values2['booktransf'] = None
|
||||
|
@ -788,7 +824,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
transfer booking
|
||||
"""
|
||||
line_currency = getattr(line.currency, 'id', None)
|
||||
booktransf_currency = getattr(getattr(line.booktransf, 'currency', {}), 'id', None)
|
||||
booktransf_currency = getattr(getattr(
|
||||
line.booktransf, 'currency', {}), 'id', None)
|
||||
|
||||
result = {
|
||||
'cashbook': getattr(line.booktransf, 'id', None),
|
||||
|
@ -797,13 +834,11 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'description': line.description,
|
||||
'booktransf': line.cashbook.id,
|
||||
'reference': line.id,
|
||||
'amount': line.amount \
|
||||
if line_currency == booktransf_currency \
|
||||
'amount': line.amount
|
||||
if line_currency == booktransf_currency
|
||||
else line.amount_2nd_currency,
|
||||
'amount_2nd_currency': line.amount \
|
||||
if line_currency != booktransf_currency \
|
||||
else None,
|
||||
}
|
||||
'amount_2nd_currency': line.amount
|
||||
if line_currency != booktransf_currency else None}
|
||||
# update values from 'values'
|
||||
result.update(values)
|
||||
return result
|
||||
|
@ -813,10 +848,12 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
""" compute debit/credit from amount
|
||||
"""
|
||||
if isinstance(values, dict):
|
||||
type_ = values.get('bookingtype', getattr(line, 'bookingtype', None))
|
||||
type_ = values.get(
|
||||
'bookingtype', getattr(line, 'bookingtype', None))
|
||||
amount = values.get('amount', None)
|
||||
else:
|
||||
type_ = getattr(values, 'bookingtype', getattr(line, 'bookingtype', None))
|
||||
type_ = getattr(
|
||||
values, 'bookingtype', getattr(line, 'bookingtype', None))
|
||||
amount = getattr(values, 'amount', None)
|
||||
|
||||
result = {}
|
||||
|
@ -824,14 +861,10 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
if amount is not None:
|
||||
if type_ in ['in', 'mvin', 'spin']:
|
||||
result.update({
|
||||
'debit': Decimal('0.0'),
|
||||
'credit': amount,
|
||||
})
|
||||
'debit': Decimal('0.0'), 'credit': amount})
|
||||
elif type_ in ['out', 'mvout', 'spout']:
|
||||
result.update({
|
||||
'debit': amount,
|
||||
'credit': Decimal('0.0'),
|
||||
})
|
||||
'debit': amount, 'credit': Decimal('0.0')})
|
||||
else:
|
||||
raise ValueError('invalid "bookingtype"')
|
||||
return result
|
||||
|
@ -852,29 +885,26 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_line_date_before_book',
|
||||
datebook=Report.format_date(line.cashbook.start_date),
|
||||
recname = line.rec_name,
|
||||
))
|
||||
recname=line.rec_name))
|
||||
|
||||
# line: category <--> bookingtype?
|
||||
if line.category:
|
||||
if not line.bookingtype in types[line.category.cattype]:
|
||||
if line.bookingtype not in types[line.category.cattype]:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_invalid_category',
|
||||
recname=line.rec_name,
|
||||
booktype = line.bookingtype_string,
|
||||
))
|
||||
booktype=line.bookingtype_string))
|
||||
|
||||
# splitline: category <--> bookingtype?
|
||||
for spline in line.splitlines:
|
||||
if spline.splittype != 'cat':
|
||||
continue
|
||||
if not line.bookingtype in types[spline.category.cattype]:
|
||||
if line.bookingtype not in types[spline.category.cattype]:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_split_invalid_category',
|
||||
recname=line.rec_name,
|
||||
splitrecname=spline.rec_name,
|
||||
booktype = line.bookingtype_string,
|
||||
))
|
||||
booktype=line.bookingtype_string))
|
||||
|
||||
@classmethod
|
||||
def check_permission_write(cls, lines, values={}):
|
||||
|
@ -886,8 +916,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_book_deny_write',
|
||||
bookname=line.cashbook.rec_name,
|
||||
state_txt = line.cashbook.state_string,
|
||||
))
|
||||
state_txt=line.cashbook.state_string))
|
||||
|
||||
# deny write if reconciliation is 'check' or 'done'
|
||||
if line.reconciliation:
|
||||
|
@ -895,19 +924,19 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_write_by_reconciliation',
|
||||
recname=line.rec_name,
|
||||
reconame = line.reconciliation.rec_name,
|
||||
))
|
||||
reconame=line.reconciliation.rec_name))
|
||||
|
||||
# deny write if line is not 'Edit'
|
||||
if line.state != 'edit':
|
||||
# allow state-update, if its the only action
|
||||
if not ((len(set({'state', 'reconciliation', 'number'}).intersection(values.keys())) > 0) \
|
||||
if not ((len(set({
|
||||
'state', 'reconciliation', 'number'
|
||||
}).intersection(values.keys())) > 0)
|
||||
and (len(values.keys()) == 1)):
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_write',
|
||||
recname=line.rec_name,
|
||||
state_txt = line.state_string,
|
||||
))
|
||||
state_txt=line.state_string))
|
||||
|
||||
@classmethod
|
||||
def check_permission_delete(cls, lines):
|
||||
|
@ -919,14 +948,12 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'cashbook.msg_line_deny_delete1',
|
||||
linetxt=line.rec_name,
|
||||
bookname=line.cashbook.rec_name,
|
||||
bookstate = line.cashbook.state_string,
|
||||
))
|
||||
bookstate=line.cashbook.state_string))
|
||||
if line.state != 'edit':
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_delete2',
|
||||
linetxt=line.rec_name,
|
||||
linestate = line.state_string,
|
||||
))
|
||||
linestate=line.state_string))
|
||||
|
||||
@classmethod
|
||||
def update_values_by_splitlines(cls, lines):
|
||||
|
@ -936,7 +963,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
for line in lines:
|
||||
amount = sum([x.amount for x in line.splitlines])
|
||||
if amount != line.amount:
|
||||
to_write.extend([ [line], {'amount': amount,} ])
|
||||
to_write.extend([[line], {'amount': amount}])
|
||||
return to_write
|
||||
|
||||
@classmethod
|
||||
|
@ -946,7 +973,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
if ('splitlines' in values.keys()) and ('amount' not in values.keys()):
|
||||
for action in values['splitlines']:
|
||||
if action[0] == 'create':
|
||||
values['amount'] = sum([x.get('amount', None) for x in action[1]])
|
||||
values['amount'] = sum([
|
||||
x.get('amount', None) for x in action[1]])
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
|
@ -956,7 +984,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
Cashbook = Pool().get('cashbook.book')
|
||||
cashbook = values.get('cashbook', None)
|
||||
if cashbook:
|
||||
values.update(cls.add_2nd_currency(values, Cashbook(cashbook).currency))
|
||||
values.update(cls.add_2nd_currency(
|
||||
values, Cashbook(cashbook).currency))
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
|
@ -975,7 +1004,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
default = default.copy()
|
||||
default.setdefault('number', None)
|
||||
default.setdefault('state', cls.default_state())
|
||||
return super(Line, cls).copy(moves, default=default)
|
||||
return super(Line, cls).copy(lines, default=default)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
|
@ -988,7 +1017,8 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
values.update(cls.clear_by_bookingtype(values))
|
||||
values.update(cls.add_2nd_unit_values(values))
|
||||
|
||||
# deny add to reconciliation if state is not 'check', 'recon' or 'done'
|
||||
# deny add to reconciliation if state is
|
||||
# not 'check', 'recon' or 'done'
|
||||
if values.get('reconciliation', None):
|
||||
if not values.get('state', '-') in ['check', 'done', 'recon']:
|
||||
date_txt = '-'
|
||||
|
@ -998,9 +1028,7 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
'cashbook.msg_line_deny_recon_by_state',
|
||||
recname='%(date)s|%(descr)s' % {
|
||||
'date': date_txt,
|
||||
'descr': values.get('description', '-'),
|
||||
},
|
||||
))
|
||||
'descr': values.get('description', '-')}))
|
||||
return super(Line, cls).create(vlist)
|
||||
|
||||
@classmethod
|
||||
|
@ -1015,21 +1043,21 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
|
||||
for line in lines:
|
||||
if line.reconciliation:
|
||||
# deny state-change to 'edit' if line is linked to reconciliation
|
||||
# deny state-change to 'edit' if line is
|
||||
# linked to reconciliation
|
||||
if values.get('state', '-') == 'edit':
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_stateedit_with_recon',
|
||||
recname = line.rec_name,
|
||||
))
|
||||
recname=line.rec_name))
|
||||
|
||||
# deny add to reconciliation if state is not 'check', 'recon' or 'done'
|
||||
# deny add to reconciliation if state is
|
||||
# not 'check', 'recon' or 'done'
|
||||
if values.get('reconciliation', None):
|
||||
for line in lines:
|
||||
if not line.state in ['check', 'done', 'recon']:
|
||||
if line.state not in ['check', 'done', 'recon']:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_recon_by_state',
|
||||
recname = line.rec_name
|
||||
))
|
||||
recname=line.rec_name))
|
||||
|
||||
# update debit / credit
|
||||
fields_update = cls.get_fields_write_update()
|
||||
|
@ -1043,11 +1071,13 @@ class Line(SecondCurrencyMixin, MemCacheIndexMx, Workflow, ModelSQL, ModelView):
|
|||
updt_fields = []
|
||||
updt_fields.extend(values.keys())
|
||||
if 'bookingtype' in values.keys():
|
||||
updt_fields.extend([x for x in fields_update if x not in values.keys()])
|
||||
updt_fields.extend([
|
||||
x for x in fields_update
|
||||
if x not in values.keys()])
|
||||
|
||||
values2.update(cls.get_debit_credit({
|
||||
x:values.get(x, getattr(line, x)) for x in updt_fields
|
||||
}, line=line))
|
||||
x: values.get(x, getattr(line, x))
|
||||
for x in updt_fields}, line=line))
|
||||
to_write.extend([lines, values2])
|
||||
else:
|
||||
to_write.extend([lines, values])
|
||||
|
@ -1068,23 +1098,27 @@ class LineContext(ModelView):
|
|||
'Line Context'
|
||||
__name__ = 'cashbook.line.context'
|
||||
|
||||
date_from = fields.Date(string='Start Date', depends=['date_to'],
|
||||
date_from = fields.Date(
|
||||
string='Start Date', depends=['date_to'],
|
||||
help='Limits the date range for the displayed entries.',
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
('date_from', '<=', Eval('date_to')),
|
||||
()),
|
||||
])
|
||||
date_to = fields.Date(string='End Date', depends=['date_from'],
|
||||
date_to = fields.Date(
|
||||
string='End Date', depends=['date_from'],
|
||||
help='Limits the date range for the displayed entries.',
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
('date_from', '<=', Eval('date_to')),
|
||||
()),
|
||||
])
|
||||
checked = fields.Boolean(string='Checked',
|
||||
checked = fields.Boolean(
|
||||
string='Checked',
|
||||
help='Show account lines in Checked-state.')
|
||||
done = fields.Boolean(string='Done',
|
||||
done = fields.Boolean(
|
||||
string='Done',
|
||||
help='Show account lines in Done-state.')
|
||||
|
||||
@classmethod
|
||||
|
|
41
mixin.py
41
mixin.py
|
@ -23,7 +23,8 @@ DEPENDS=['state', 'state_cashbook']
|
|||
class SecondCurrencyMixin:
|
||||
""" two fields for 2nd currency: amount + rate
|
||||
"""
|
||||
amount_2nd_currency = fields.Numeric(string='Amount Second Currency',
|
||||
amount_2nd_currency = fields.Numeric(
|
||||
string='Amount Second Currency',
|
||||
digits=(16, Eval('currency2nd_digits', 2)),
|
||||
states={
|
||||
'readonly': Or(
|
||||
|
@ -33,7 +34,8 @@ class SecondCurrencyMixin:
|
|||
'required': Bool(Eval('currency2nd')),
|
||||
'invisible': ~Bool(Eval('currency2nd')),
|
||||
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd'])
|
||||
rate_2nd_currency = fields.Function(fields.Numeric(string='Rate',
|
||||
rate_2nd_currency = fields.Function(fields.Numeric(
|
||||
string='Rate',
|
||||
help='Exchange rate between the currencies of the participating cashbooks.',
|
||||
digits=(rate_decimal * 2, rate_decimal),
|
||||
states={
|
||||
|
@ -46,9 +48,11 @@ class SecondCurrencyMixin:
|
|||
}, depends=DEPENDS+['currency2nd_digits', 'currency2nd']),
|
||||
'on_change_with_rate_2nd_currency', setter='set_rate_2nd_currency')
|
||||
|
||||
currency2nd = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
currency2nd = fields.Function(fields.Many2One(
|
||||
model_name='currency.currency',
|
||||
string="2nd Currency", readonly=True), 'on_change_with_currency2nd')
|
||||
currency2nd_digits = fields.Function(fields.Integer(string='2nd Currency Digits',
|
||||
currency2nd_digits = fields.Function(fields.Integer(
|
||||
string='2nd Currency Digits',
|
||||
readonly=True), 'on_change_with_currency2nd_digits')
|
||||
|
||||
@classmethod
|
||||
|
@ -69,8 +73,7 @@ class SecondCurrencyMixin:
|
|||
booktransf = Cashbook(booktransf)
|
||||
if from_currency.id != booktransf.currency.id:
|
||||
with Transaction().set_context({
|
||||
'date': values.get('date', IrDate.today()),
|
||||
}):
|
||||
'date': values.get('date', IrDate.today())}):
|
||||
values['amount_2nd_currency'] = Currency.compute(
|
||||
from_currency,
|
||||
amount,
|
||||
|
@ -78,22 +81,28 @@ class SecondCurrencyMixin:
|
|||
)
|
||||
return values
|
||||
|
||||
@fields.depends('booktransf', '_parent_booktransf.currency', \
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
|
||||
@fields.depends(
|
||||
'booktransf', '_parent_booktransf.currency',
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency',
|
||||
'rate_2nd_currency')
|
||||
def on_change_booktransf(self):
|
||||
""" update amount_2nd_currency
|
||||
"""
|
||||
self.on_change_rate_2nd_currency()
|
||||
|
||||
@fields.depends('booktransf', '_parent_booktransf.currency', \
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
|
||||
@fields.depends(
|
||||
'booktransf', '_parent_booktransf.currency',
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency',
|
||||
'rate_2nd_currency')
|
||||
def on_change_amount(self):
|
||||
""" update amount_2nd_currency
|
||||
"""
|
||||
self.on_change_rate_2nd_currency()
|
||||
|
||||
@fields.depends('booktransf', '_parent_booktransf.currency', \
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency', 'rate_2nd_currency')
|
||||
@fields.depends(
|
||||
'booktransf', '_parent_booktransf.currency',
|
||||
'currency', 'amount', 'date', 'amount_2nd_currency',
|
||||
'rate_2nd_currency')
|
||||
def on_change_rate_2nd_currency(self):
|
||||
""" update amount_2nd_currency + rate_2nd_currency
|
||||
"""
|
||||
|
@ -109,15 +118,15 @@ class SecondCurrencyMixin:
|
|||
if self.rate_2nd_currency is None:
|
||||
# no rate set, use current rate of target-currency
|
||||
with Transaction().set_context({
|
||||
'date': self.date or IrDate.today(),
|
||||
}):
|
||||
'date': self.date or IrDate.today()}):
|
||||
self.amount_2nd_currency = Currency.compute(
|
||||
self.currency,
|
||||
self.amount,
|
||||
self.booktransf.currency
|
||||
)
|
||||
if self.amount != Decimal('0.0'):
|
||||
self.rate_2nd_currency = self.amount_2nd_currency / self.amount
|
||||
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
|
||||
|
@ -201,5 +210,3 @@ class MemCacheIndexMx:
|
|||
cls.create_date.select = True
|
||||
|
||||
# end MemCacheIndexMx
|
||||
|
||||
|
||||
|
|
63
model.py
63
model.py
|
@ -8,19 +8,21 @@ from trytond.transaction import Transaction
|
|||
from trytond.pool import Pool
|
||||
from trytond.cache import MemoryCache
|
||||
from trytond.config import config
|
||||
from datetime import timedelta, datetime
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from sql import With, Literal
|
||||
from sql import With
|
||||
from sql.functions import Function
|
||||
from sql.conditionals import Coalesce
|
||||
import copy
|
||||
|
||||
if config.get('cashbook', 'memcache', default='yes').lower() in ['yes', '1', 'true']:
|
||||
if config.get('cashbook', 'memcache', default='yes').lower() \
|
||||
in ['yes', '1', 'true']:
|
||||
ENABLE_CACHE = True
|
||||
else:
|
||||
ENABLE_CACHE = False
|
||||
|
||||
if config.get('cashbook', 'sync', default='yes').lower() in ['yes', '1', 'true']:
|
||||
if config.get('cashbook', 'sync', default='yes').lower() \
|
||||
in ['yes', '1', 'true']:
|
||||
ENABLE_CACHESYNC = True
|
||||
else:
|
||||
ENABLE_CACHESYNC = False
|
||||
|
@ -36,6 +38,7 @@ class ArrayAgg(Function):
|
|||
|
||||
# end ArrayAgg
|
||||
|
||||
|
||||
class ArrayAppend(Function):
|
||||
""" sql: array_append
|
||||
"""
|
||||
|
@ -94,7 +97,7 @@ class MemCache(Model):
|
|||
def read_value(cls, cache_key):
|
||||
""" read values from cache
|
||||
"""
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return None
|
||||
return copy.deepcopy(cls._cashbook_value_cache.get(cache_key))
|
||||
|
||||
|
@ -102,23 +105,24 @@ class MemCache(Model):
|
|||
def store_result(cls, records, cache_keys, values, skip_records=[]):
|
||||
""" store result to cache
|
||||
"""
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return
|
||||
for record in records:
|
||||
if record not in skip_records:
|
||||
continue
|
||||
data = {x:values[x][record.id]
|
||||
for x in values.keys()
|
||||
if record.id in values[x].keys()}
|
||||
cls._cashbook_value_cache.set(cache_keys[record.id], copy.deepcopy(data))
|
||||
if ENABLE_CACHESYNC == True:
|
||||
data = {
|
||||
x: values[x][record.id]
|
||||
for x in values.keys() if record.id in values[x].keys()}
|
||||
cls._cashbook_value_cache.set(
|
||||
cache_keys[record.id], copy.deepcopy(data))
|
||||
if ENABLE_CACHESYNC is True:
|
||||
cls._cashbook_value_cache.sync(Transaction())
|
||||
|
||||
@classmethod
|
||||
def store_value(cls, cache_key, values):
|
||||
""" store values to cache
|
||||
"""
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return
|
||||
cls._cashbook_value_cache.set(cache_key, copy.deepcopy(values))
|
||||
|
||||
|
@ -126,7 +130,7 @@ class MemCache(Model):
|
|||
def read_from_cache(cls, records, cache_keys, names, result):
|
||||
""" get stored values from memcache
|
||||
"""
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return (records, result)
|
||||
|
||||
todo_records = []
|
||||
|
@ -152,7 +156,7 @@ class MemCache(Model):
|
|||
pool = Pool()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return '-'
|
||||
|
||||
fname = [name, str(record.id)]
|
||||
|
@ -173,7 +177,8 @@ class MemCache(Model):
|
|||
tab_model = Model.__table__()
|
||||
|
||||
tab_query = Model.search(line['query'], query=True)
|
||||
qu1 = tab_model.join(tab_query,
|
||||
qu1 = tab_model.join(
|
||||
tab_query,
|
||||
condition=tab_query.id == tab_model.id,
|
||||
).select(
|
||||
tab_model.id,
|
||||
|
@ -181,7 +186,8 @@ class MemCache(Model):
|
|||
tab_model.create_date,
|
||||
limit=1,
|
||||
order_by=[
|
||||
Coalesce(tab_model.write_date, tab_model.create_date).desc,
|
||||
Coalesce(
|
||||
tab_model.write_date, tab_model.create_date).desc,
|
||||
tab_model.id.desc,
|
||||
],
|
||||
)
|
||||
|
@ -216,9 +222,10 @@ class MemCache(Model):
|
|||
def record_update(cls, cache_key, record):
|
||||
""" update cache-value
|
||||
"""
|
||||
if ENABLE_CACHE == False:
|
||||
if ENABLE_CACHE is False:
|
||||
return
|
||||
cls.store_value(cache_key,
|
||||
cls.store_value(
|
||||
cache_key,
|
||||
cls.genkey(record.id, record.write_date, record.create_date)
|
||||
if record is not None else None)
|
||||
|
||||
|
@ -235,11 +242,10 @@ def sub_ids_hierarchical(model_name):
|
|||
lines = With('parent', 'id', recursive=True)
|
||||
lines.query = tab_mod.select(
|
||||
tab_mod.id, tab_mod.id,
|
||||
) | tab_mod2.join(lines,
|
||||
) | tab_mod2.join(
|
||||
lines,
|
||||
condition=lines.id == tab_mod2.parent,
|
||||
).select(
|
||||
lines.parent, tab_mod2.id,
|
||||
)
|
||||
).select(lines.parent, tab_mod2.id)
|
||||
lines.query.all_ = True
|
||||
|
||||
query = lines.select(
|
||||
|
@ -264,10 +270,13 @@ def order_name_hierarchical(model_name, tables):
|
|||
tab_mod.id, tab_mod.name, Array(tab_mod.name),
|
||||
where=tab_mod.parent == None,
|
||||
)
|
||||
lines.query |= tab_mod2.join(lines,
|
||||
lines.query |= tab_mod2.join(
|
||||
lines,
|
||||
condition=lines.id == tab_mod2.parent,
|
||||
).select(
|
||||
tab_mod2.id, tab_mod2.name, ArrayAppend(lines.name_path, tab_mod2.name),
|
||||
tab_mod2.id,
|
||||
tab_mod2.name,
|
||||
ArrayAppend(lines.name_path, tab_mod2.name),
|
||||
)
|
||||
lines.query.all_ = True
|
||||
|
||||
|
@ -279,7 +288,8 @@ def order_name_hierarchical(model_name, tables):
|
|||
|
||||
|
||||
class UserValueMixin(ValueMixin):
|
||||
iduser = fields.Many2One(model_name='res.user', string="User",
|
||||
iduser = fields.Many2One(
|
||||
model_name='res.user', string="User",
|
||||
select=True, ondelete='CASCADE', required=True)
|
||||
|
||||
@classmethod
|
||||
|
@ -313,6 +323,7 @@ class UserMultiValueMixin(MultiValueMixin):
|
|||
Value = self.multivalue_model(name)
|
||||
if issubclass(Value, UserValueMixin):
|
||||
pattern = self.updt_multivalue_pattern(pattern)
|
||||
return super(UserMultiValueMixin, self).set_multivalue(name, value, **pattern)
|
||||
return super(
|
||||
UserMultiValueMixin, self).set_multivalue(name, value, **pattern)
|
||||
|
||||
# end UserMultiValueMixin
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
# full copyright notices and license terms.
|
||||
|
||||
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pyson import Eval, If, Or, Bool
|
||||
from trytond.pyson import Eval, If, Or
|
||||
from trytond.pool import Pool
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
from decimal import Decimal
|
||||
from sql.operators import Equal, Between
|
||||
from sql import Literal, Null
|
||||
from datetime import timedelta
|
||||
from .book import sel_state_book
|
||||
|
||||
|
@ -36,14 +33,18 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
'Cashbook Reconciliation'
|
||||
__name__ = 'cashbook.recon'
|
||||
|
||||
cashbook = fields.Many2One(string='Cashbook', required=True, select=True,
|
||||
cashbook = fields.Many2One(
|
||||
string='Cashbook', required=True, select=True,
|
||||
model_name='cashbook.book', ondelete='CASCADE', readonly=True)
|
||||
date = fields.Date(string='Date', required=True, select=True,
|
||||
date = fields.Date(
|
||||
string='Date', required=True, select=True,
|
||||
states=STATES, depends=DEPENDS)
|
||||
feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_feature')
|
||||
|
||||
date_from = fields.Date(string='Start Date',
|
||||
date_from = fields.Date(
|
||||
string='Start Date',
|
||||
required=True,
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
|
@ -51,7 +52,8 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
()),
|
||||
],
|
||||
states=STATES, depends=DEPENDS+['date_to'])
|
||||
date_to = fields.Date(string='End Date',
|
||||
date_to = fields.Date(
|
||||
string='End Date',
|
||||
required=True, select=True,
|
||||
domain=[
|
||||
If(Eval('date_to') & Eval('date_from'),
|
||||
|
@ -59,14 +61,17 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
()),
|
||||
],
|
||||
states=STATES, depends=DEPENDS+['date_from'])
|
||||
start_amount = fields.Numeric(string='Start Amount', required=True,
|
||||
start_amount = fields.Numeric(
|
||||
string='Start Amount', required=True,
|
||||
readonly=True, digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
end_amount = fields.Numeric(string='End Amount', required=True,
|
||||
end_amount = fields.Numeric(
|
||||
string='End Amount', required=True,
|
||||
readonly=True, digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
|
||||
lines = fields.One2Many(string='Lines', field='reconciliation',
|
||||
lines = fields.One2Many(
|
||||
string='Lines', field='reconciliation',
|
||||
model_name='cashbook.line', states=STATES,
|
||||
depends=DEPENDS+['date_from', 'date_to', 'cashbook'],
|
||||
add_remove=[
|
||||
|
@ -80,18 +85,23 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
('date', '<=', Eval('date_to')),
|
||||
])
|
||||
|
||||
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
currency = fields.Function(fields.Many2One(
|
||||
model_name='currency.currency',
|
||||
string="Currency"), 'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
|
||||
currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits'),
|
||||
'on_change_with_currency_digits')
|
||||
predecessor = fields.Function(fields.Many2One(string='Predecessor', readonly=True,
|
||||
predecessor = fields.Function(fields.Many2One(
|
||||
string='Predecessor', readonly=True,
|
||||
model_name='cashbook.recon'),
|
||||
'on_change_with_predecessor')
|
||||
|
||||
state = fields.Selection(string='State', required=True, readonly=True,
|
||||
state = fields.Selection(
|
||||
string='State', required=True, readonly=True,
|
||||
select=True, selection=sel_reconstate)
|
||||
state_string = state.translated('state')
|
||||
state_cashbook = fields.Function(fields.Selection(string='State of Cashbook',
|
||||
state_cashbook = fields.Function(fields.Selection(
|
||||
string='State of Cashbook',
|
||||
readonly=True, states={'invisible': True}, selection=sel_state_book),
|
||||
'on_change_with_state_cashbook')
|
||||
|
||||
|
@ -140,9 +150,7 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
[ # enclose other record
|
||||
('date_from', '>=', self.date_from),
|
||||
('date_to', '<=', self.date_to),
|
||||
],
|
||||
],
|
||||
]
|
||||
]]]
|
||||
|
||||
if Recon.search_count(query) > 0:
|
||||
raise UserError(gettext('cashbook.msg_recon_err_overlap'))
|
||||
|
@ -165,8 +173,7 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
bookname=reconciliation.cashbook.rec_name,
|
||||
reconame=reconciliation.rec_name,
|
||||
datefrom=Report.format_date(reconciliation.date_from),
|
||||
dateto = Report.format_date(reconciliation.date_to),
|
||||
))
|
||||
dateto=Report.format_date(reconciliation.date_to)))
|
||||
|
||||
@classmethod
|
||||
def get_values_wfedit(cls, reconciliation):
|
||||
|
@ -209,7 +216,8 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
# add amounts of new lines
|
||||
values['end_amount'] += sum([x.credit - x.debit for x in lines])
|
||||
# add amounts of already linked lines
|
||||
values['end_amount'] += sum([x.credit - x.debit for x in reconciliation.lines])
|
||||
values['end_amount'] += sum([
|
||||
x.credit - x.debit for x in reconciliation.lines])
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
|
@ -249,17 +257,17 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_recon_predecessor_not_done',
|
||||
recname_p=reconciliation.predecessor.rec_name,
|
||||
recname_c = reconciliation.rec_name,
|
||||
))
|
||||
recname_c=reconciliation.rec_name))
|
||||
|
||||
# check if current.date_from == predecessor.date_to
|
||||
if reconciliation.predecessor.date_to != reconciliation.date_from:
|
||||
if reconciliation.predecessor.date_to != \
|
||||
reconciliation.date_from:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_recon_date_from_to_mismatch',
|
||||
datefrom=Report.format_date(reconciliation.date_from),
|
||||
dateto = Report.format_date(reconciliation.predecessor.date_to),
|
||||
recname = reconciliation.rec_name,
|
||||
))
|
||||
dateto=Report.format_date(
|
||||
reconciliation.predecessor.date_to),
|
||||
recname=reconciliation.rec_name))
|
||||
|
||||
to_write.extend([
|
||||
[reconciliation],
|
||||
|
@ -281,13 +289,11 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
to_wfrecon_line = []
|
||||
for reconciliation in reconciliations:
|
||||
to_wfrecon_line.extend([
|
||||
x for x in reconciliation.lines \
|
||||
if x.state == 'check'
|
||||
])
|
||||
x for x in reconciliation.lines
|
||||
if x.state == 'check'])
|
||||
to_wfdone_line.extend([
|
||||
x for x in reconciliation.lines \
|
||||
if x.state == 'recon'
|
||||
])
|
||||
x for x in reconciliation.lines
|
||||
if x.state == 'recon'])
|
||||
|
||||
# deny if there are lines not linked to reconciliation
|
||||
if Line.search_count([
|
||||
|
@ -299,14 +305,12 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
('date', '<', reconciliation.date_to),
|
||||
],
|
||||
# lines at from-date must relate to a reconciliation
|
||||
('date', '=', reconciliation.date_from),
|
||||
],
|
||||
('date', '=', reconciliation.date_from)],
|
||||
]) > 0:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_recon_lines_no_linked',
|
||||
date_from=Report.format_date(reconciliation.date_from),
|
||||
date_to = Report.format_date(reconciliation.date_to),
|
||||
))
|
||||
date_to=Report.format_date(reconciliation.date_to),))
|
||||
|
||||
if len(to_wfrecon_line) > 0:
|
||||
Line.wfrecon(to_wfrecon_line)
|
||||
|
@ -318,9 +322,12 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
""" 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) 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(self.start_amount or 0.0, None),
|
||||
'from': 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(
|
||||
self.start_amount or 0.0, None),
|
||||
'end_amount': Report.format_number(self.end_amount or 0.0, None),
|
||||
'symbol': getattr(self.currency, 'symbol', '-'),
|
||||
'num': len(self.lines),
|
||||
|
@ -460,8 +467,7 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
raise UserError(gettext(
|
||||
'cashbook.msg_book_deny_write',
|
||||
bookname=reconciliation.cashbook.rec_name,
|
||||
state_txt = reconciliation.cashbook.state_string,
|
||||
))
|
||||
state_txt=reconciliation.cashbook.state_string))
|
||||
super(Reconciliation, cls).write(*args)
|
||||
|
||||
@classmethod
|
||||
|
@ -474,14 +480,12 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
'cashbook.msg_line_deny_delete1',
|
||||
linetxt=reconciliation.rec_name,
|
||||
bookname=reconciliation.cashbook.rec_name,
|
||||
bookstate = reconciliation.cashbook.state_string,
|
||||
))
|
||||
bookstate=reconciliation.cashbook.state_string))
|
||||
if reconciliation.state != 'edit':
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_recon_deny_delete2',
|
||||
recontxt=reconciliation.rec_name,
|
||||
reconstate = reconciliation.state_string,
|
||||
))
|
||||
reconstate=reconciliation.state_string))
|
||||
|
||||
super(Reconciliation, cls).delete(reconciliations)
|
||||
|
||||
|
|
73
splitline.py
73
splitline.py
|
@ -4,13 +4,12 @@
|
|||
# full copyright notices and license terms.
|
||||
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, Workflow, fields, Check
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.report import Report
|
||||
from trytond.i18n import gettext
|
||||
from trytond.transaction import Transaction
|
||||
from .line import sel_linetype, sel_bookingtype, STATES, DEPENDS
|
||||
from .line import sel_bookingtype, STATES, DEPENDS
|
||||
from .book import sel_state_book
|
||||
from .mixin import SecondCurrencyMixin, MemCacheIndexMx
|
||||
|
||||
|
@ -30,15 +29,18 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
'Split booking line'
|
||||
__name__ = 'cashbook.split'
|
||||
|
||||
line = fields.Many2One(string='Line', required=True,
|
||||
line = fields.Many2One(
|
||||
string='Line', required=True,
|
||||
select=True, ondelete='CASCADE', model_name='cashbook.line',
|
||||
readonly=True)
|
||||
description = fields.Text(string='Description',
|
||||
states=STATES, depends=DEPENDS)
|
||||
splittype = fields.Selection(string='Type', required=True,
|
||||
description = fields.Text(
|
||||
string='Description', states=STATES, depends=DEPENDS)
|
||||
splittype = fields.Selection(
|
||||
string='Type', required=True,
|
||||
help='Type of split booking line', selection=sel_linetype,
|
||||
states=STATES, depends=DEPENDS, select=True)
|
||||
category = fields.Many2One(string='Category', select=True,
|
||||
category = fields.Many2One(
|
||||
string='Category', select=True,
|
||||
model_name='cashbook.category', ondelete='RESTRICT',
|
||||
states={
|
||||
'readonly': STATES['readonly'],
|
||||
|
@ -51,9 +53,11 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
('cattype', '=', 'in'),
|
||||
('cattype', '=', 'out'),
|
||||
)])
|
||||
category_view = fields.Function(fields.Char(string='Category', readonly=True),
|
||||
category_view = fields.Function(fields.Char(
|
||||
string='Category', readonly=True),
|
||||
'on_change_with_category_view')
|
||||
booktransf = fields.Many2One(string='Source/Dest',
|
||||
booktransf = fields.Many2One(
|
||||
string='Source/Dest',
|
||||
ondelete='RESTRICT', model_name='cashbook.book',
|
||||
domain=[
|
||||
('owner.id', '=', Eval('owner_cashbook', -1)),
|
||||
|
@ -66,32 +70,43 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
'required': Eval('splittype', '') == 'tr',
|
||||
}, depends=DEPENDS+['bookingtype', 'owner_cashbook', 'cashbook'])
|
||||
|
||||
amount = fields.Numeric(string='Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
amount = fields.Numeric(
|
||||
string='Amount', digits=(16, Eval('currency_digits', 2)),
|
||||
required=True, states=STATES, depends=DEPENDS+['currency_digits'])
|
||||
|
||||
date = fields.Function(fields.Date(string='Date', readonly=True),
|
||||
'on_change_with_date')
|
||||
target = fields.Function(fields.Reference(string='Target', readonly=True,
|
||||
date = fields.Function(fields.Date(
|
||||
string='Date', readonly=True), 'on_change_with_date')
|
||||
target = fields.Function(fields.Reference(
|
||||
string='Target', readonly=True,
|
||||
selection=sel_target), 'on_change_with_target')
|
||||
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
currency = fields.Function(fields.Many2One(
|
||||
model_name='currency.currency',
|
||||
string="Currency", readonly=True), 'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
|
||||
currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits',
|
||||
readonly=True), 'on_change_with_currency_digits')
|
||||
bookingtype = fields.Function(fields.Selection(string='Type', readonly=True,
|
||||
bookingtype = fields.Function(fields.Selection(
|
||||
string='Type', readonly=True,
|
||||
selection=sel_bookingtype), 'on_change_with_bookingtype')
|
||||
state = fields.Function(fields.Selection(string='State', readonly=True,
|
||||
state = fields.Function(fields.Selection(
|
||||
string='State', readonly=True,
|
||||
selection=sel_linetype), 'on_change_with_state')
|
||||
cashbook = fields.Function(fields.Many2One(string='Cashbook',
|
||||
cashbook = fields.Function(fields.Many2One(
|
||||
string='Cashbook',
|
||||
readonly=True, states={'invisible': True}, model_name='cashbook.book'),
|
||||
'on_change_with_cashbook')
|
||||
feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_feature')
|
||||
booktransf_feature = fields.Function(fields.Char(string='Feature', readonly=True,
|
||||
booktransf_feature = fields.Function(fields.Char(
|
||||
string='Feature', readonly=True,
|
||||
states={'invisible': True}), 'on_change_with_booktransf_feature')
|
||||
state_cashbook = fields.Function(fields.Selection(string='State of Cashbook',
|
||||
state_cashbook = fields.Function(fields.Selection(
|
||||
string='State of Cashbook',
|
||||
readonly=True, states={'invisible': True}, selection=sel_state_book),
|
||||
'on_change_with_state_cashbook')
|
||||
owner_cashbook = fields.Function(fields.Many2One(string='Owner', readonly=True,
|
||||
owner_cashbook = fields.Function(fields.Many2One(
|
||||
string='Owner', readonly=True,
|
||||
states={'invisible': True}, model_name='res.user'),
|
||||
'on_change_with_owner_cashbook')
|
||||
|
||||
|
@ -108,8 +123,10 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
'desc': (self.description or '-')[:40],
|
||||
'amount': Report.format_number(self.amount, None),
|
||||
'symbol': getattr(self.currency, 'symbol', '-'),
|
||||
'target': self.category_view if self.splittype == 'cat' else self.booktransf.rec_name,
|
||||
'type': gettext('cashbook.msg_line_bookingtype_%s' % self.line.bookingtype),
|
||||
'target': self.category_view
|
||||
if self.splittype == 'cat' else self.booktransf.rec_name,
|
||||
'type': gettext(
|
||||
'cashbook.msg_line_bookingtype_%s' % self.line.bookingtype),
|
||||
}
|
||||
|
||||
@fields.depends('splittype', 'category', 'booktransf')
|
||||
|
@ -151,7 +168,7 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
if self.category:
|
||||
cfg1 = Configuration.get_singleton()
|
||||
|
||||
if getattr(cfg1, 'catnamelong', True) == True:
|
||||
if getattr(cfg1, 'catnamelong', True) is True:
|
||||
return self.category.rec_name
|
||||
else:
|
||||
return self.category.name
|
||||
|
@ -246,7 +263,7 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
|
||||
to_update_line = []
|
||||
for record in records:
|
||||
if not record.line in to_update_line:
|
||||
if record.line not in to_update_line:
|
||||
to_update_line.append(record.line)
|
||||
|
||||
to_write = Line2.update_values_by_splitlines(to_update_line)
|
||||
|
@ -268,7 +285,7 @@ class SplitLine(SecondCurrencyMixin, MemCacheIndexMx, ModelSQL, ModelView):
|
|||
|
||||
if 'amount' in values.keys():
|
||||
for record in records:
|
||||
if not record.line in to_update_line:
|
||||
if record.line not in to_update_line:
|
||||
to_update_line.append(record.line)
|
||||
super(SplitLine, cls).write(*args)
|
||||
|
||||
|
|
|
@ -811,6 +811,49 @@ class LineTestCase(ModuleTestCase):
|
|||
'amount': Decimal('0.0'),
|
||||
}])
|
||||
|
||||
@with_transaction()
|
||||
def test_line_check_deny_delete_of_party(self):
|
||||
""" create cashbook + line, delete party should fail
|
||||
"""
|
||||
pool = Pool()
|
||||
Book = pool.get('cashbook.book')
|
||||
Line = pool.get('cashbook.line')
|
||||
Party = pool.get('party.party')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
party = self.prep_party()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
'number_sequ': self.prep_sequence().id,
|
||||
'start_date': date(2022, 5, 1),
|
||||
'lines': [('create', [{
|
||||
'date': date(2022, 5, 1),
|
||||
'description': 'Text 1',
|
||||
'category': category.id,
|
||||
'bookingtype': 'in',
|
||||
'amount': Decimal('1.0'),
|
||||
'party': party.id,
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(book.name, 'Book 1')
|
||||
self.assertEqual(book.state, 'open')
|
||||
self.assertEqual(len(book.lines), 1)
|
||||
self.assertEqual(book.lines[0].party.rec_name, 'Party')
|
||||
self.assertEqual(book.lines[0].party.id, party.id)
|
||||
|
||||
self.assertEqual(Party.search_count([('name', '=', 'Party')]), 1)
|
||||
|
||||
self.assertRaisesRegex(
|
||||
UserError,
|
||||
'The records could not be deleted because they are used by field "Party" of "Cashbook Line".',
|
||||
Party.delete,
|
||||
[party])
|
||||
|
||||
@with_transaction()
|
||||
def test_line_create_check_deny_write(self):
|
||||
""" create cashbook + line, 'close' book, write to line
|
||||
|
|
9
types.py
9
types.py
|
@ -14,9 +14,11 @@ 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',
|
||||
company = fields.Many2One(
|
||||
string='Company', model_name='company.company',
|
||||
required=True, ondelete="RESTRICT")
|
||||
feature = fields.Selection(string='Feature', required=True,
|
||||
feature = fields.Selection(
|
||||
string='Feature', required=True,
|
||||
selection='get_sel_feature', select=True,
|
||||
help='Select feature set of the Cashbook.')
|
||||
|
||||
|
@ -53,7 +55,8 @@ class Type(ModelSQL, ModelView):
|
|||
def search_rec_name(cls, name, clause):
|
||||
""" search in name + short
|
||||
"""
|
||||
return ['OR',
|
||||
return [
|
||||
'OR',
|
||||
('name',) + tuple(clause[1:]),
|
||||
('short',) + tuple(clause[1:]),
|
||||
]
|
||||
|
|
|
@ -7,7 +7,7 @@ from trytond.model import ModelView, fields
|
|||
from trytond.wizard import Wizard, StateView, StateTransition, Button
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pyson import Eval, Bool, If, And
|
||||
from trytond.pyson import Eval, Bool, If
|
||||
from decimal import Decimal
|
||||
from .line import sel_bookingtype
|
||||
|
||||
|
@ -18,29 +18,36 @@ class EnterBookingStart(ModelView):
|
|||
'Enter Booking'
|
||||
__name__ = 'cashbook.enterbooking.start'
|
||||
|
||||
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
||||
cashbook = fields.Many2One(
|
||||
string='Cashbook', model_name='cashbook.book',
|
||||
domain=[('id', 'in', Eval('cashbooks', [])), ('btype', '!=', None)],
|
||||
depends=['cashbooks'], required=True)
|
||||
cashbooks = fields.One2Many(string='Cashbooks', field=None,
|
||||
cashbooks = fields.One2Many(
|
||||
string='Cashbooks', field=None,
|
||||
model_name='cashbook.book', readonly=True,
|
||||
states={'invisible': True})
|
||||
owner_cashbook = fields.Function(fields.Many2One(string='Owner', readonly=True,
|
||||
owner_cashbook = fields.Function(fields.Many2One(
|
||||
string='Owner', readonly=True,
|
||||
states={'invisible': True}, model_name='res.user'),
|
||||
'on_change_with_owner_cashbook')
|
||||
currency = fields.Function(fields.Many2One(string='Currency',
|
||||
currency = fields.Function(fields.Many2One(
|
||||
string='Currency',
|
||||
model_name='currency.currency', states={'invisible': True}),
|
||||
'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
|
||||
currency_digits = fields.Function(fields.Integer(
|
||||
string='Currency Digits',
|
||||
readonly=True, states={'invisible': True}),
|
||||
'on_change_with_currency_digits')
|
||||
bookingtype = fields.Selection(string='Type', required=True,
|
||||
selection=sel_booktypewiz)
|
||||
amount = fields.Numeric(string='Amount',
|
||||
bookingtype = fields.Selection(
|
||||
string='Type', required=True, selection=sel_booktypewiz)
|
||||
amount = fields.Numeric(
|
||||
string='Amount',
|
||||
depends=['currency_digits', 'bookingtype'],
|
||||
digits=(16, Eval('currency_digits', 2)), required=True,
|
||||
domain=[('amount', '>=', Decimal('0.0'))])
|
||||
description = fields.Text(string='Description')
|
||||
category = fields.Many2One(string='Category',
|
||||
category = fields.Many2One(
|
||||
string='Category',
|
||||
model_name='cashbook.category', depends=['bookingtype'],
|
||||
states={
|
||||
'readonly': Bool(Eval('bookingtype')) == False,
|
||||
|
@ -55,7 +62,8 @@ class EnterBookingStart(ModelView):
|
|||
)])
|
||||
|
||||
# party or cashbook as counterpart
|
||||
booktransf = fields.Many2One(string='Source/Dest',
|
||||
booktransf = fields.Many2One(
|
||||
string='Source/Dest',
|
||||
model_name='cashbook.book',
|
||||
domain=[
|
||||
('owner.id', '=', Eval('owner_cashbook', -1)),
|
||||
|
@ -65,7 +73,8 @@ class EnterBookingStart(ModelView):
|
|||
'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',
|
||||
party = fields.Many2One(
|
||||
string='Party', model_name='party.party',
|
||||
states={
|
||||
'invisible': ~Eval('bookingtype', '').in_(['in', 'out']),
|
||||
}, depends=['bookingtype'])
|
||||
|
@ -81,7 +90,7 @@ class EnterBookingStart(ModelView):
|
|||
|
||||
if self.bookingtype:
|
||||
if self.category:
|
||||
if not self.bookingtype 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')
|
||||
|
@ -115,7 +124,8 @@ class EnterBookingWizard(Wizard):
|
|||
__name__ = 'cashbook.enterbooking'
|
||||
|
||||
start_state = 'start'
|
||||
start = StateView('cashbook.enterbooking.start',
|
||||
start = StateView(
|
||||
'cashbook.enterbooking.start',
|
||||
'cashbook.enterbooking_start_form', [
|
||||
Button('Cancel', 'end', 'tryton-cancel'),
|
||||
Button('Save', 'save_', 'tryton-save', default=True),
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
from trytond.model import ModelView, fields
|
||||
from trytond.pyson import PYSONEncoder
|
||||
from trytond.wizard import Wizard, StateView, StateTransition, StateAction, Button
|
||||
from trytond.wizard import Wizard, StateView, StateTransition, \
|
||||
StateAction, Button
|
||||
from trytond.i18n import gettext
|
||||
from trytond.pool import Pool
|
||||
from trytond.exceptions import UserError
|
||||
|
@ -42,10 +43,13 @@ class OpenCashBookStart(ModelView):
|
|||
'Open Cashbook'
|
||||
__name__ = 'cashbook.open_lines.start'
|
||||
|
||||
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
||||
cashbook = fields.Many2One(
|
||||
string='Cashbook', model_name='cashbook.book',
|
||||
required=True, domain=[('btype', '!=', None)])
|
||||
checked = fields.Boolean(string='Checked', help="Show cashbook lines in Checked-state.")
|
||||
done = fields.Boolean(string='Done', help="Show cashbook lines in Done-state")
|
||||
checked = fields.Boolean(
|
||||
string='Checked', help="Show cashbook lines in Checked-state.")
|
||||
done = fields.Boolean(
|
||||
string='Done', help="Show cashbook lines in Done-state")
|
||||
date_from = fields.Date(string='Start Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
|
||||
|
@ -66,7 +70,8 @@ class OpenCashBook(OLineMixin, Wizard):
|
|||
|
||||
start_state = 'check'
|
||||
check = StateTransition()
|
||||
askuser = StateView('cashbook.open_lines.start',
|
||||
askuser = StateView(
|
||||
'cashbook.open_lines.start',
|
||||
'cashbook.open_lines_view_form', [
|
||||
Button('Cancel', 'end', 'tryton-cancel'),
|
||||
Button('Open', 'open_', 'tryton-ok', default=True),
|
||||
|
@ -79,8 +84,7 @@ class OpenCashBook(OLineMixin, Wizard):
|
|||
Book = Pool().get('cashbook.book')
|
||||
|
||||
with Transaction().set_context({
|
||||
'_check_access': True,
|
||||
}):
|
||||
'_check_access': True}):
|
||||
books = Book.search([('btype', '!=', None)])
|
||||
if len(books) == 1:
|
||||
return 'open_'
|
||||
|
@ -115,8 +119,7 @@ class OpenCashBook(OLineMixin, Wizard):
|
|||
book = getattr(self.askuser, 'cashbook', None)
|
||||
if book is None:
|
||||
with Transaction().set_context({
|
||||
'_check_access': True,
|
||||
}):
|
||||
'_check_access': True}):
|
||||
books = Book.search([('btype', '!=', None)])
|
||||
if len(books) > 0:
|
||||
book = books[0]
|
||||
|
@ -158,8 +161,7 @@ class OpenCashBookTree(OLineMixin, Wizard):
|
|||
book = self.record
|
||||
if book is None:
|
||||
with Transaction().set_context({
|
||||
'_check_access': True,
|
||||
}):
|
||||
'_check_access': True}):
|
||||
books = Book.search([('btype', '!=', None)])
|
||||
if len(books) > 0:
|
||||
book = books[0]
|
||||
|
@ -167,8 +169,7 @@ class OpenCashBookTree(OLineMixin, Wizard):
|
|||
if book.btype is None:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_book_no_type_noopen',
|
||||
bookname = book.rec_name,
|
||||
))
|
||||
bookname=book.rec_name))
|
||||
|
||||
action.update(self.add_action_data(book))
|
||||
return action, {}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
# full copyright notices and license terms.
|
||||
|
||||
from trytond.model import ModelView, fields
|
||||
from trytond.pyson import PYSONEncoder
|
||||
from trytond.wizard import Wizard, StateView, StateReport, Button
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, Bool
|
||||
|
@ -15,17 +14,21 @@ class RunCbReportStart(ModelView):
|
|||
'Cashbook Report'
|
||||
__name__ = 'cashbook.runrepbook.start'
|
||||
|
||||
cashbook = fields.Many2One(string='Cashbook', required=True,
|
||||
cashbook = fields.Many2One(
|
||||
string='Cashbook', required=True,
|
||||
model_name='cashbook.book', depends=['cashbooks'],
|
||||
domain=[('id', 'in', Eval('cashbooks', []))])
|
||||
cashbooks = fields.One2Many(string='Cashbooks', model_name='cashbook.book',
|
||||
cashbooks = fields.One2Many(
|
||||
string='Cashbooks', model_name='cashbook.book',
|
||||
field=None, readonly=True, states={'invisible': True})
|
||||
reconciliation = fields.Many2One(string='Reconciliation', required=True,
|
||||
reconciliation = fields.Many2One(
|
||||
string='Reconciliation', required=True,
|
||||
model_name='cashbook.recon', depends=['reconciliations'],
|
||||
states={
|
||||
'readonly': ~Bool(Eval('reconciliations')),
|
||||
}, domain=[('id', 'in', Eval('reconciliations', []))])
|
||||
reconciliations = fields.Function(fields.One2Many(string='Reconciliations',
|
||||
reconciliations = fields.Function(fields.One2Many(
|
||||
string='Reconciliations',
|
||||
model_name='cashbook.recon', field=None, readonly=True,
|
||||
states={'invisible': True}),
|
||||
'on_change_with_reconciliations')
|
||||
|
@ -64,14 +67,14 @@ class RunCbReport(Wizard):
|
|||
__name__ = 'cashbook.runrepbook'
|
||||
|
||||
start_state = 'selrecon'
|
||||
selrecon = StateView('cashbook.runrepbook.start',
|
||||
selrecon = StateView(
|
||||
'cashbook.runrepbook.start',
|
||||
'cashbook.runrepbook_view_form', [
|
||||
Button(string='Cancel', state='end', icon='tryton-cancel'),
|
||||
Button(string='Report', state='report_', icon='tryton-ok', default=True,
|
||||
states={
|
||||
'readonly': ~Bool(Eval('reconciliation')),
|
||||
}),
|
||||
])
|
||||
Button(
|
||||
string='Report', state='report_', icon='tryton-ok',
|
||||
default=True,
|
||||
states={'readonly': ~Bool(Eval('reconciliation'))})])
|
||||
report_ = StateReport('cashbook.reprecon')
|
||||
|
||||
def default_selrecon(self, fields):
|
||||
|
@ -91,8 +94,7 @@ class RunCbReport(Wizard):
|
|||
raise ValueError('invalid model')
|
||||
|
||||
with Transaction().set_context({
|
||||
'_check_access': True,
|
||||
}):
|
||||
'_check_access': True}):
|
||||
books = Book.search([])
|
||||
result['cashbooks'] = [x.id for x in books]
|
||||
|
||||
|
|
Loading…
Reference in a new issue