splitbuchungen + test

This commit is contained in:
Frederik Jaeckel 2022-08-25 15:55:24 +02:00
parent bdbc9dc27f
commit aefb5cde51
9 changed files with 618 additions and 124 deletions

318
line.py
View file

@ -81,8 +81,15 @@ class Line(Workflow, ModelSQL, ModelView):
bookingtype = fields.Selection(string='Type', required=True,
help='Type of Booking', selection=sel_bookingtype,
states=STATES, depends=DEPENDS)
bookingtype_string = bookingtype.translated('bookingtype')
amount = fields.Numeric(string='Amount', digits=(16, Eval('currency_digits', 2)),
required=True, states=STATES, depends=DEPENDS+['currency_digits'])
required=True,
states={
'readonly': Or(
STATES['readonly'],
Eval('bookingtype', '').in_(['spin', 'spout']),
),
}, depends=DEPENDS+['currency_digits', 'bookingtype'])
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)),
@ -123,10 +130,15 @@ class Line(Workflow, ModelSQL, ModelView):
}, field='reference', readonly=True)
splitlines = fields.One2Many(string='Split booking lines',
model_name='cashbook.split',
help='The rows are created and managed by the current record.',
help='Rows with different categories form the total sum of the booking',
states={
'invisible': ~Bool(Eval('splitlines')),
}, field='line', readonly=True)
'invisible': ~Eval('bookingtype' '').in_(['spin', 'spout']),
'readonly': Or(
~Eval('bookingtype' '').in_(['spin', 'spout']),
STATES['readonly'],
),
'required': Eval('bookingtype' '').in_(['spin', 'spout']),
}, field='line', depends=DEPENDS+['bookingtype'])
reconciliation = fields.Many2One(string='Reconciliation', readonly=True,
model_name='cashbook.recon', ondelete='SET NULL',
@ -143,9 +155,9 @@ class Line(Workflow, ModelSQL, ModelView):
'on_change_with_balance')
currency = fields.Function(fields.Many2One(model_name='currency.currency',
string="Currency"), 'on_change_with_currency')
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
'on_change_with_currency_digits')
string="Currency", readonly=True), 'on_change_with_currency')
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,
select=True, selection=sel_linetype)
@ -351,14 +363,14 @@ class Line(Workflow, ModelSQL, ModelView):
'type': gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype),
}
def get_amount_by_second_currency(self, to_currency):
def get_amount_by_second_currency(self, to_currency, amount=None):
""" get amount, calculate credit/debit from currency of current
cashbook to 'to_currency'
"""
Currency = Pool().get('currency.currency')
values = {
'amount': self.amount,
'amount': amount if amount is not None else self.amount,
}
if to_currency.id != self.cashbook.currency.id:
@ -402,18 +414,6 @@ class Line(Workflow, ModelSQL, ModelView):
return [tab2]
@fields.depends('party', 'booktransf', 'bookingtype')
def on_change_with_payee(self, name=None):
""" get party or cashbook
"""
if self.bookingtype:
if self.bookingtype in ['in', 'out', 'spin', 'spout']:
if self.party:
return 'party.party,%d' % self.party.id
elif self.bookingtype in ['mvin', 'mvout']:
if self.booktransf:
return 'cashbook.book,%d' % self.booktransf.id
@classmethod
def search_payee(cls, names, clause):
""" search in payee for party or cashbook
@ -423,36 +423,12 @@ class Line(Workflow, ModelSQL, ModelView):
('booktransf.rec_name',) + tuple(clause[1:]),
]
@fields.depends('category')
def on_change_with_category_view(self, name=None):
""" show optimizef form of category for list-view
"""
Configuration = Pool().get('cashbook.configuration')
if self.category:
cfg1 = Configuration.get_singleton()
if getattr(cfg1, 'catnamelong', True) == True:
return self.category.rec_name
else :
return self.category.name
@classmethod
def search_category_view(cls, name, clause):
""" search in category
"""
return [('category.rec_name',) + tuple(clause[1:])]
@fields.depends('date')
def on_change_with_month(self, name=None):
""" get difference of month to current date
"""
IrDate = Pool().get('ir.date')
if self.date is not None:
dt1 = IrDate.today()
return (12 * dt1.year + dt1.month) - \
(12 * self.date.year + self.date.month)
@classmethod
def search_month(cls, names, clause):
""" search in month
@ -472,6 +448,75 @@ class Line(Workflow, ModelSQL, ModelView):
)
return [('id', 'in', query)]
@classmethod
def search_state_cashbook(cls, names, clause):
""" search in state of cashbook
"""
return [('cashbook.state',) + tuple(clause[1:])]
@fields.depends('amount', 'splitlines')
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])
@fields.depends('bookingtype', 'category', 'splitlines')
def on_change_bookingtype(self):
""" clear category if not valid type
"""
types = {
'in': ['in', 'mvin', 'spin'],
'out': ['out', 'mvout', 'spout'],
}
if self.bookingtype:
if self.category:
if not self.bookingtype in types.get(self.category.cattype, ''):
self.category = None
if self.bookingtype in ['spin', 'spout']:
for spline in self.splitlines:
if not self.bookingtype in types.get(getattr(spline.category, 'cattype', '-'), ''):
spline.category = None
else :
self.splitlines = []
@fields.depends('party', 'booktransf', 'bookingtype')
def on_change_with_payee(self, name=None):
""" get party or cashbook
"""
if self.bookingtype:
if self.bookingtype in ['in', 'out', 'spin', 'spout']:
if self.party:
return 'party.party,%d' % self.party.id
elif self.bookingtype in ['mvin', 'mvout']:
if self.booktransf:
return 'cashbook.book,%d' % self.booktransf.id
@fields.depends('category')
def on_change_with_category_view(self, name=None):
""" show optimizef form of category for list-view
"""
Configuration = Pool().get('cashbook.configuration')
if self.category:
cfg1 = Configuration.get_singleton()
if getattr(cfg1, 'catnamelong', True) == True:
return self.category.rec_name
else :
return self.category.name
@fields.depends('date')
def on_change_with_month(self, name=None):
""" get difference of month to current date
"""
IrDate = Pool().get('ir.date')
if self.date is not None:
dt1 = IrDate.today()
return (12 * dt1.year + dt1.month) - \
(12 * self.date.year + self.date.month)
@fields.depends('cashbook', '_parent_cashbook.owner')
def on_change_with_owner_cashbook(self, name=None):
""" get current owner
@ -486,26 +531,6 @@ class Line(Workflow, ModelSQL, ModelView):
if self.cashbook:
return self.cashbook.state
@classmethod
def search_state_cashbook(cls, names, clause):
""" search in state of cashbook
"""
return [('cashbook.state',) + tuple(clause[1:])]
@fields.depends('bookingtype', 'category')
def on_change_bookingtype(self):
""" clear category if not valid type
"""
types = {
'in': ['in', 'mvin', 'spin'],
'out': ['out', 'mvout', 'spout'],
}
if self.bookingtype:
if self.category:
if not self.bookingtype in types.get(self.category.cattype, ''):
self.category = None
@fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency(self, name=None):
""" currency of cashbook
@ -566,6 +591,31 @@ class Line(Workflow, ModelSQL, ModelView):
break
return balance
@classmethod
def clear_by_bookingtype(cls, values, line=None):
""" clear some fields by value of bookingtype
"""
values2 = {}
values2.update(values)
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])]
if bookingtype in ['in', 'out']:
values2['booktransf'] = None
if bookingtype in ['spin', 'spout']:
values2['category'] = None
values2['booktransf'] = None
if bookingtype in ['mvin', 'mvout']:
values2['category'] = None
return values2
@classmethod
def get_debit_credit(cls, values):
""" compute debit/credit from amount
@ -593,11 +643,33 @@ class Line(Workflow, ModelSQL, ModelView):
raise ValueError('invalid "bookingtype"')
return {}
@classmethod
def update_amount_by_splitlines(cls, lines):
""" update amounts from split-lines
"""
Line2 = Pool().get('cashbook.line')
to_write = []
for line in lines:
to_write.extend([
[line],
{
'amount': sum([x.amount for x in line.splitlines]),
}])
if len(to_write) > 0:
Line2.write(*to_write)
@classmethod
def validate(cls, lines):
""" deny date before 'start_date' of cashbook
"""
super(Line, cls).validate(lines)
types = {
'in': ['in', 'mvin', 'spin'],
'out': ['out', 'mvout', 'spout'],
}
for line in lines:
if line.date < line.cashbook.start_date:
raise UserError(gettext(
@ -606,6 +678,77 @@ class Line(Workflow, ModelSQL, ModelView):
recname = line.rec_name,
))
# line: category <--> bookingtype?
if line.category:
if not line.bookingtype in types[line.category.cattype]:
raise UserError(gettext(
'cashbook.msg_line_invalid_category',
recname = line.rec_name,
booktype = line.bookingtype_string,
))
# splitline: category <--> bookingtype?
for spline in line.splitlines:
if not line.bookingtype 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,
))
@classmethod
def check_permission_write(cls, lines, values={}):
""" deny update if cashbook.line!='open',
"""
for line in lines:
# deny write if cashbook is not open
if line.cashbook.state != 'open':
raise UserError(gettext(
'cashbook.msg_book_deny_write',
bookname = line.cashbook.rec_name,
state_txt = line.cashbook.state_string,
))
# deny write if reconciliation is 'check' or 'done'
if line.reconciliation:
if line.reconciliation.state == 'done':
raise UserError(gettext(
'cashbook.msg_line_deny_write_by_reconciliation',
recname = line.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'}).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,
))
@classmethod
def check_permission_delete(cls, lines):
""" deny delete if book is not 'open' or wf is not 'edit'
"""
for line in lines:
if line.cashbook.state == 'closed':
raise UserError(gettext(
'cashbook.msg_line_deny_delete1',
linetxt = line.rec_name,
bookname = line.cashbook.rec_name,
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,
))
@classmethod
def copy(cls, lines, default=None):
""" reset values
@ -625,6 +768,7 @@ class Line(Workflow, ModelSQL, ModelView):
vlist = [x.copy() for x in vlist]
for values in vlist:
values.update(cls.get_debit_credit(values))
values.update(cls.clear_by_bookingtype(values))
# deny add to reconciliation if state is not 'check' or 'done'
if values.get('reconciliation', None):
@ -650,14 +794,9 @@ class Line(Workflow, ModelSQL, ModelView):
actions = iter(args)
to_write = []
for lines, values in zip(actions, actions):
cls.check_permission_write(lines, values)
for line in lines:
# deny write if cashbook is not open
if line.cashbook.state != 'open':
raise UserError(gettext(
'cashbook.msg_book_deny_write',
bookname = line.cashbook.rec_name,
state_txt = line.cashbook.state_string,
))
if line.reconciliation:
# deny state-change to 'edit' if line is linked to reconciliation
if values.get('state', '-') == 'edit':
@ -666,24 +805,6 @@ class Line(Workflow, ModelSQL, ModelView):
recname = line.rec_name,
))
# deny write if reconciliation is 'check' or 'done'
if line.reconciliation.state == 'done':
raise UserError(gettext(
'cashbook.msg_line_deny_write_by_reconciliation',
recname = line.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'}).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,
))
# deny add to reconciliation if state is not 'check' or 'done'
if values.get('reconciliation', None):
for line in lines:
@ -698,6 +819,7 @@ class Line(Workflow, ModelSQL, ModelView):
for line in lines:
values2 = {}
values2.update(values)
values2.update(cls.clear_by_bookingtype(values, line))
values2.update(cls.get_debit_credit({
x:values.get(x, getattr(line, x)) for x in ['amount', 'bookingtype']
}))
@ -710,21 +832,7 @@ class Line(Workflow, ModelSQL, ModelView):
def delete(cls, lines):
""" deny delete if book is not 'open' or wf is not 'edit'
"""
for line in lines:
if line.cashbook.state == 'closed':
raise UserError(gettext(
'cashbook.msg_line_deny_delete1',
linetxt = line.rec_name,
bookname = line.cashbook.rec_name,
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,
))
cls.check_permission_delete(lines)
return super(Line, cls).delete(lines)
# end Line