line: änderungesperre bei diversen wf-zuständen + test,
abstimmung: datum/betrag anfang/ende korrekt + test für beträge muß noch
This commit is contained in:
parent
01825cc09c
commit
149baef174
10 changed files with 1099 additions and 64 deletions
|
@ -3,12 +3,16 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains the
|
||||
# full copyright notices and license terms.
|
||||
|
||||
from trytond.model import Workflow, ModelView, ModelSQL, fields, Unique
|
||||
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
||||
from trytond.transaction import Transaction
|
||||
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 .book import sel_state_book
|
||||
|
||||
|
||||
|
@ -52,11 +56,19 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
()),
|
||||
],
|
||||
states=STATES, depends=DEPENDS+['date_from'])
|
||||
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,
|
||||
readonly=True, digits=(16, Eval('currency_digits', 2)),
|
||||
depends=['currency_digits'])
|
||||
|
||||
lines = fields.One2Many(string='Lines', field='reconciliation',
|
||||
model_name='cashbook.line', states=STATES,
|
||||
depends=DEPENDS+['date_from', 'date_to', 'cashbook'],
|
||||
add_remove=[
|
||||
('cashbook', '=', Eval('cashbook')),
|
||||
('state', 'in', ['check', 'done']),
|
||||
('date', '>=', Eval('date_from')),
|
||||
('date', '<=', Eval('date_to')),
|
||||
],
|
||||
|
@ -65,6 +77,11 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
('date', '<=', Eval('date_to')),
|
||||
])
|
||||
|
||||
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
string="Currency"), 'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
|
||||
'on_change_with_currency_digits')
|
||||
|
||||
state = fields.Selection(string='State', required=True, readonly=True,
|
||||
select=True, selection=sel_reconstate)
|
||||
state_string = state.translated('state')
|
||||
|
@ -96,40 +113,169 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
},
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def check_overlap_dates(cls, date_from, date_to, id_cashbook, record=None):
|
||||
""" deny overlap of date_from/date_to between records of same cashbook
|
||||
allow: date_to=date_from
|
||||
"""
|
||||
Recon = Pool().get('cashbook.recon')
|
||||
|
||||
query = [
|
||||
('cashbook.id', '=', id_cashbook),
|
||||
['OR',
|
||||
[ # 'start' is inside of other record
|
||||
('date_from', '<=', date_from),
|
||||
('date_to', '>', date_from),
|
||||
],
|
||||
[ # 'end' is inside of other record
|
||||
('date_from', '<', date_to),
|
||||
('date_to', '>=', date_to),
|
||||
],
|
||||
[ # enclose other record
|
||||
('date_from', '>=', date_from),
|
||||
('date_to', '<=', date_to),
|
||||
],
|
||||
],
|
||||
]
|
||||
|
||||
# avoid finding ourselves
|
||||
if record:
|
||||
query.append(('id', '!=', record.id))
|
||||
|
||||
if Recon.search_count(query) > 0:
|
||||
raise UserError(gettext('cashbook.msg_recon_err_overlap'))
|
||||
|
||||
@classmethod
|
||||
def check_lines_not_checked(cls, reconciliations):
|
||||
""" deny lines in date-range not 'checked', w/o records at date-limit
|
||||
"""
|
||||
Line = Pool().get('cashbook.line')
|
||||
|
||||
for reconciliation in reconciliations:
|
||||
if Line.search_count([
|
||||
('date', '>', reconciliation.date_from),
|
||||
('date', '<', reconciliation.date_to),
|
||||
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||
('state', '!=', 'check'),
|
||||
]) > 0:
|
||||
raise UserError(gettext(
|
||||
'cashbook.mds_recon_deny_line_not_check',
|
||||
bookname = reconciliation.cashbook.rec_name,
|
||||
reconame = reconciliation.rec_name,
|
||||
datefrom = Report.format_date(reconciliation.date_from),
|
||||
dateto = Report.format_date(reconciliation.date_to),
|
||||
))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('edit')
|
||||
def wfedit(cls, lines):
|
||||
def wfedit(cls, reconciliations):
|
||||
""" edit
|
||||
"""
|
||||
pass
|
||||
Recon = Pool().get('cashbook.recon')
|
||||
|
||||
to_write = []
|
||||
for reconciliation in reconciliations:
|
||||
values = {
|
||||
'start_amount': Decimal('0.0'),
|
||||
'end_amount': Decimal('0.0'),
|
||||
}
|
||||
|
||||
# unlink lines from reconciliation
|
||||
if len(reconciliation.lines) > 0:
|
||||
values['lines'] = [('remove', [x.id for x in reconciliation.lines])]
|
||||
|
||||
to_write.extend([[reconciliation], values])
|
||||
|
||||
if len(to_write) > 0:
|
||||
Recon.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('check')
|
||||
def wfcheck(cls, lines):
|
||||
""" is checked
|
||||
def wfcheck(cls, reconciliations):
|
||||
""" checked: add lines of book in date-range to reconciliation,
|
||||
state of lines must be 'checked'
|
||||
"""
|
||||
pass
|
||||
pool = Pool()
|
||||
Line = pool.get('cashbook.line')
|
||||
Recon = pool.get('cashbook.recon')
|
||||
|
||||
cls.check_lines_not_checked(reconciliations)
|
||||
|
||||
to_write = []
|
||||
for reconciliation in reconciliations:
|
||||
values = {}
|
||||
|
||||
# get start_amount: end_amount of predecessor
|
||||
pre_recon = Recon.search([
|
||||
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||
('date_to', '<=', reconciliation.date_from),
|
||||
('state', 'in', ['check', 'done']),
|
||||
], order=[('date_to', 'DESC')], limit=1)
|
||||
if len(pre_recon) > 0:
|
||||
values['start_amount'] = pre_recon[0].end_amount
|
||||
else :
|
||||
# not found, use 'start_balance' of cashbook
|
||||
values['start_amount'] = reconciliation.cashbook.start_balance
|
||||
values['end_amount'] = values['start_amount']
|
||||
|
||||
# add 'checked'-lines to reconciliation
|
||||
lines = Line.search([
|
||||
('date', '>=', reconciliation.date_from),
|
||||
('date', '<=', reconciliation.date_to),
|
||||
('cashbook.id', '=', reconciliation.cashbook.id),
|
||||
('reconciliation', '=', None),
|
||||
('state', '=', 'check'),
|
||||
])
|
||||
if len(lines) > 0:
|
||||
values['lines'] = [('add', [x.id for x in lines])]
|
||||
|
||||
# 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])
|
||||
|
||||
to_write.extend([[reconciliation], values])
|
||||
|
||||
if len(to_write) > 0:
|
||||
Recon.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
def wfdone(cls, lines):
|
||||
def wfdone(cls, reconciliations):
|
||||
""" is done
|
||||
"""
|
||||
pass
|
||||
Line = Pool().get('cashbook.line')
|
||||
|
||||
to_wfdone_line = []
|
||||
for reconciliation in reconciliations:
|
||||
to_wfdone_line.extend(list(reconciliation.lines))
|
||||
|
||||
if len(to_wfdone_line) > 0:
|
||||
Line.wfdone(to_wfdone_line)
|
||||
|
||||
def get_rec_name(self, name):
|
||||
""" short + name
|
||||
"""
|
||||
return '%(from)s - %(to)s: %(amount)s %(symbol)s' % {
|
||||
return '%(from)s - %(to)s | %(start_amount)s %(symbol)s - %(start_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 '-',
|
||||
'amount': Decimal('0.0'),
|
||||
'symbol': getattr(getattr(self.cashbook, 'currency', None), 'symbol', '-'),
|
||||
'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),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def default_start_amount(cls):
|
||||
return Decimal('0.0')
|
||||
|
||||
@classmethod
|
||||
def default_end_amount(cls):
|
||||
return Decimal('0.0')
|
||||
|
||||
@classmethod
|
||||
def default_state(cls):
|
||||
return 'edit'
|
||||
|
@ -141,6 +287,22 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
IrDate = Pool().get('ir.date')
|
||||
return IrDate.today()
|
||||
|
||||
@fields.depends('cashbook', '_parent_cashbook.currency')
|
||||
def on_change_with_currency(self, name=None):
|
||||
""" currency of cashbook
|
||||
"""
|
||||
if self.cashbook:
|
||||
return self.cashbook.currency.id
|
||||
|
||||
@fields.depends('cashbook', '_parent_cashbook.currency')
|
||||
def on_change_with_currency_digits(self, name=None):
|
||||
""" currency of cashbook
|
||||
"""
|
||||
if self.cashbook:
|
||||
return self.cashbook.currency.digits
|
||||
else:
|
||||
return 2
|
||||
|
||||
@fields.depends('cashbook', '_parent_cashbook.state')
|
||||
def on_change_with_state_cashbook(self, name=None):
|
||||
""" get state of cashbook
|
||||
|
@ -148,4 +310,63 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
|
|||
if self.cashbook:
|
||||
return self.cashbook.state
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
""" add debit/credit
|
||||
"""
|
||||
for values in vlist:
|
||||
cls.check_overlap_dates(
|
||||
values.get('date_from', None),
|
||||
values.get('date_to', None),
|
||||
values.get('cashbook', None))
|
||||
return super(Reconciliation, cls).create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
""" deny update if cashbook.line!='open',
|
||||
add or update debit/credit
|
||||
"""
|
||||
actions = iter(args)
|
||||
for reconciliations, values in zip(actions, actions):
|
||||
# deny write if chashbook is not open
|
||||
for reconciliation in reconciliations:
|
||||
|
||||
# deny overlap
|
||||
if len(set({'date_from', 'date_to'}).intersection(set(values.keys()))) > 0:
|
||||
cls.check_overlap_dates(
|
||||
values.get('date_from', reconciliation.date_from),
|
||||
values.get('date_to', reconciliation.date_to),
|
||||
reconciliation.cashbook.id,
|
||||
reconciliation)
|
||||
|
||||
if reconciliation.cashbook.state != 'open':
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_book_deny_write',
|
||||
bookname = reconciliation.cashbook.rec_name,
|
||||
state_txt = reconciliation.cashbook.state_string,
|
||||
))
|
||||
|
||||
super(Reconciliation, cls).write(*args)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, reconciliations):
|
||||
""" deny delete if book is not 'open' or wf is not 'edit'
|
||||
"""
|
||||
for reconciliation in reconciliations:
|
||||
if reconciliation.cashbook.state == 'closed':
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_line_deny_delete1',
|
||||
linetxt = reconciliation.rec_name,
|
||||
bookname = reconciliation.cashbook.rec_name,
|
||||
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,
|
||||
))
|
||||
|
||||
return super(Reconciliation, cls).delete(reconciliations)
|
||||
|
||||
# end Type
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue