Files
tryton/statement.py
T

829 lines
29 KiB
Python
Raw Normal View History

2014-12-02 12:54:50 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2010-08-16 19:23:32 +02:00
from decimal import Decimal
2014-07-20 13:13:00 +02:00
from collections import namedtuple
from itertools import groupby
from sql import Null
2015-03-02 23:36:17 +01:00
from sql.aggregate import Max, Sum
2014-07-20 13:13:00 +02:00
2016-09-13 14:04:17 +02:00
from trytond.model import Workflow, ModelView, ModelSQL, fields, Check, \
sequence_ordered
from trytond.pyson import Eval, If, Bool
2010-08-16 19:23:32 +02:00
from trytond.transaction import Transaction
2013-09-06 22:33:56 +02:00
from trytond import backend
2011-07-03 13:18:45 +02:00
from trytond.pool import Pool
2014-07-20 13:13:00 +02:00
from trytond.modules.company import CompanyReport
2008-06-17 12:09:01 +02:00
2015-03-02 23:36:17 +01:00
__all__ = ['Statement', 'Line', 'LineGroup', 'StatementReport']
2012-09-24 22:17:36 +02:00
2011-08-21 11:20:33 +02:00
_STATES = {'readonly': Eval('state') != 'draft'}
2011-08-04 16:57:19 +02:00
_DEPENDS = ['state']
2008-06-10 22:01:27 +02:00
2014-07-20 13:45:45 +02:00
_BALANCE_STATES = _STATES.copy()
_BALANCE_STATES.update({
'invisible': ~Eval('validation', '').in_(['balance']),
'required': Eval('validation', '').in_(['balance']),
})
_BALANCE_DEPENDS = _DEPENDS + ['validation']
_AMOUNT_STATES = _STATES.copy()
_AMOUNT_STATES.update({
'invisible': ~Eval('validation', '').in_(['amount']),
'required': Eval('validation', '').in_(['amount']),
})
_AMOUNT_DEPENDS = _DEPENDS + ['validation']
_NUMBER_STATES = _STATES.copy()
_NUMBER_STATES.update({
'invisible': ~Eval('validation', '').in_(['number_of_lines']),
'required': Eval('validation', '').in_(['number_of_lines']),
})
_NUMBER_DEPENDS = _DEPENDS + ['validation']
2016-06-30 18:55:57 +02:00
STATES = [
('draft', 'Draft'),
('validated', 'Validated'),
('cancel', 'Canceled'),
('posted', 'Posted'),
]
2008-06-17 12:09:01 +02:00
class Unequal(object):
2014-07-20 13:13:00 +02:00
"Always different"
def __eq__(self, other):
return False
def __ne__(self, other):
return True
def __str__(self):
return ''
2012-03-25 00:17:31 +01:00
class Statement(Workflow, ModelSQL, ModelView):
'Account Statement'
2012-09-24 22:17:36 +02:00
__name__ = 'account.statement'
2014-07-20 13:45:45 +02:00
name = fields.Char('Name', required=True)
2011-04-10 20:59:25 +02:00
company = fields.Many2One('company.company', 'Company', required=True,
2012-03-05 10:52:58 +01:00
select=True, states=_STATES, domain=[
2011-08-21 11:20:33 +02:00
('id', If(Eval('context', {}).contains('company'), '=', '!='),
2013-10-09 18:25:13 +02:00
Eval('context', {}).get('company', -1)),
2011-08-04 16:57:19 +02:00
],
depends=_DEPENDS)
2012-04-03 20:06:27 +02:00
journal = fields.Many2One('account.statement.journal', 'Journal',
2014-01-26 14:28:55 +01:00
required=True, select=True,
2011-04-10 20:59:25 +02:00
domain=[
2014-12-03 16:13:25 +01:00
('company', '=', Eval('company', -1)),
2011-08-04 16:57:19 +02:00
],
2011-04-10 20:59:25 +02:00
states={
2013-12-18 23:15:29 +01:00
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
2011-08-04 16:57:19 +02:00
},
2014-12-03 16:13:25 +01:00
depends=['state', 'company'])
2014-01-26 14:28:55 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'on_change_with_currency_digits')
2015-10-04 00:39:51 +02:00
date = fields.Date('Date', required=True, select=True)
2014-07-20 13:45:45 +02:00
start_balance = fields.Numeric('Start Balance',
2011-08-04 16:57:19 +02:00
digits=(16, Eval('currency_digits', 2)),
2014-07-20 13:45:45 +02:00
states=_BALANCE_STATES, depends=_BALANCE_DEPENDS + ['currency_digits'])
end_balance = fields.Numeric('End Balance',
2011-08-04 16:57:19 +02:00
digits=(16, Eval('currency_digits', 2)),
2014-07-20 13:45:45 +02:00
states=_BALANCE_STATES, depends=_BALANCE_DEPENDS + ['currency_digits'])
balance = fields.Function(
fields.Numeric('Balance',
digits=(16, Eval('currency_digits', 2)),
2014-07-20 13:45:45 +02:00
states=_BALANCE_STATES,
depends=_BALANCE_DEPENDS + ['currency_digits']),
2012-09-24 22:17:36 +02:00
'on_change_with_balance')
2014-07-20 13:45:45 +02:00
total_amount = fields.Numeric('Total Amount',
digits=(16, Eval('currency_digits', 2)),
states=_AMOUNT_STATES, depends=_AMOUNT_DEPENDS + ['currency_digits'])
number_of_lines = fields.Integer('Number of Lines',
states=_NUMBER_STATES, depends=_NUMBER_DEPENDS)
lines = fields.One2Many('account.statement.line', 'statement',
2014-07-10 00:08:44 +02:00
'Lines', states={
2011-08-21 11:20:33 +02:00
'readonly': (Eval('state') != 'draft') | ~Eval('journal'),
2014-01-26 14:28:55 +01:00
},
2011-08-04 16:57:19 +02:00
depends=['state', 'journal'])
2016-06-30 18:55:57 +02:00
state = fields.Selection(STATES, 'State', readonly=True, select=True)
2014-07-20 13:45:45 +02:00
validation = fields.Function(fields.Char('Validation'),
'on_change_with_validation')
2008-06-10 22:01:27 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
def __setup__(cls):
super(Statement, cls).__setup__()
cls._order[0] = ('id', 'DESC')
cls._error_messages.update({
'wrong_end_balance': 'End Balance must be "%s".',
2014-07-20 13:45:45 +02:00
'wrong_total_amount': 'Total Amount must be "%s".',
'wrong_number_of_lines': 'Number of Lines must be "%s".',
2013-02-17 00:37:49 +01:00
'delete_cancel': ('Statement "%s" must be cancelled before '
'deletion.'),
'paid_invoice_draft_statement': ('There are paid invoices on '
'draft statements.'),
2015-01-02 15:43:29 +01:00
'debit_credit_account_statement_journal': ('Please provide '
'debit and credit account on statement journal "%s".'),
})
2012-09-24 22:17:36 +02:00
cls._transitions |= set((
2012-03-25 00:17:31 +01:00
('draft', 'validated'),
('draft', 'cancel'),
2012-03-25 00:17:31 +01:00
('validated', 'posted'),
('validated', 'cancel'),
('cancel', 'draft'),
))
2012-09-24 22:17:36 +02:00
cls._buttons.update({
2012-03-25 00:17:31 +01:00
'draft': {
'invisible': Eval('state') != 'cancel',
},
'validate_statement': {
2012-03-25 00:17:31 +01:00
'invisible': Eval('state') != 'draft',
},
'post': {
'invisible': Eval('state') != 'validated',
},
'cancel': {
'invisible': ~Eval('state').in_(['draft', 'validated']),
2012-03-25 00:17:31 +01:00
},
})
2008-06-10 22:01:27 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
def __register__(cls, module_name):
2013-09-06 22:33:56 +02:00
TableHandler = backend.get('TableHandler')
transaction = Transaction()
cursor = transaction.connection.cursor()
2014-07-20 13:45:45 +02:00
sql_table = cls.__table__()
2011-04-10 20:59:25 +02:00
# Migration from 1.8: new field company
table = TableHandler(cls, module_name)
2011-04-10 20:59:25 +02:00
company_exist = table.column_exist('company')
2012-09-24 22:17:36 +02:00
super(Statement, cls).__register__(module_name)
2011-04-10 20:59:25 +02:00
# Migration from 1.8: fill new field company
if not company_exist:
offset = 0
limit = transaction.database.IN_MAX
2012-09-24 22:17:36 +02:00
statements = True
while statements:
statements = cls.search([], offset=offset, limit=limit)
2011-04-10 20:59:25 +02:00
offset += limit
2012-09-24 22:17:36 +02:00
for statement in statements:
cls.write([statement], {
'company': statement.journal.company.id,
})
table = TableHandler(cls, module_name)
2011-04-10 20:59:25 +02:00
table.not_null_action('company', action='add')
2014-07-20 13:45:45 +02:00
# Migration from 3.2: remove required on start/end balance
table.not_null_action('start_balance', action='remove')
table.not_null_action('end_balance', action='remove')
# Migration from 3.2: add required name
cursor.execute(*sql_table.update([sql_table.name],
[sql_table.id.cast(cls.name.sql_type().base)],
where=sql_table.name == Null))
2014-07-20 13:45:45 +02:00
2012-09-24 22:17:36 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:12 +02:00
return Transaction().context.get('company')
2011-04-10 20:59:25 +02:00
2012-09-24 22:17:36 +02:00
@staticmethod
def default_state():
2008-06-10 22:01:27 +02:00
return 'draft'
2012-09-24 22:17:36 +02:00
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
2008-07-30 16:49:16 +02:00
2012-09-24 22:17:36 +02:00
@staticmethod
def default_currency_digits():
Company = Pool().get('company.company')
2010-08-16 19:23:32 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:36 +02:00
company = Company(Transaction().context['company'])
return company.currency.digits
return 2
2014-01-26 14:28:55 +01:00
@fields.depends('journal', 'state', 'lines')
2012-09-24 22:17:36 +02:00
def on_change_journal(self):
if not self.journal:
2014-10-29 20:39:23 +01:00
return
2012-09-24 22:17:36 +02:00
statements = self.search([
('journal', '=', self.journal.id),
], order=[
('date', 'DESC'),
('id', 'DESC'),
2012-09-24 22:17:36 +02:00
], limit=1)
if not statements:
2014-10-29 20:39:23 +01:00
return
2012-09-24 22:17:36 +02:00
statement, = statements
2014-10-29 20:39:23 +01:00
self.start_balance = statement.end_balance
2008-06-10 22:01:27 +02:00
2014-01-26 14:28:55 +01:00
@fields.depends('journal')
2012-09-24 22:17:36 +02:00
def on_change_with_currency_digits(self, name=None):
if self.journal:
return self.journal.currency.digits
return 2
2012-09-24 22:17:36 +02:00
def get_end_balance(self, name):
end_balance = self.start_balance
for line in self.lines:
end_balance += line.amount
return end_balance
2014-01-26 14:28:55 +01:00
@fields.depends('start_balance', 'end_balance')
2012-09-24 22:17:36 +02:00
def on_change_with_balance(self, name=None):
return ((getattr(self, 'end_balance', 0) or 0)
- (getattr(self, 'start_balance', 0) or 0))
2014-01-26 14:28:55 +01:00
@fields.depends('lines', 'journal')
2012-09-24 22:17:36 +02:00
def on_change_lines(self):
2011-07-03 13:18:45 +02:00
pool = Pool()
2012-09-24 22:17:36 +02:00
Currency = pool.get('currency.currency')
Line = pool.get('account.statement.line')
if self.journal and self.lines:
invoices = set()
for line in self.lines:
if getattr(line, 'invoice', None):
2012-09-24 22:17:36 +02:00
invoices.add(line.invoice)
invoice_id2amount_to_pay = {}
2012-09-24 22:17:36 +02:00
for invoice in invoices:
with Transaction().set_context(date=invoice.currency_date):
2016-03-20 22:58:29 +01:00
if invoice.type == 'out':
sign = -1
else:
sign = 1
invoice_id2amount_to_pay[invoice.id] = sign * (
2012-09-24 22:17:36 +02:00
Currency.compute(invoice.currency,
invoice.amount_to_pay, self.journal.currency))
2014-10-29 20:39:23 +01:00
lines = list(self.lines)
2014-03-16 20:54:54 +01:00
line_offset = 0
for index, line in enumerate(self.lines or []):
if getattr(line, 'invoice', None) and line.id:
2012-09-24 22:17:36 +02:00
amount_to_pay = invoice_id2amount_to_pay[line.invoice.id]
if (not self.journal.currency.is_zero(amount_to_pay)
and getattr(line, 'amount', None)
and (line.amount >= 0) == (amount_to_pay <= 0)):
if abs(line.amount) > abs(amount_to_pay):
2014-10-29 20:39:23 +01:00
new_line = Line()
2012-09-24 22:17:36 +02:00
for field_name, field in Line._fields.iteritems():
2014-10-29 20:39:23 +01:00
if field_name == 'id':
2012-09-24 22:17:36 +02:00
continue
2014-10-29 20:39:23 +01:00
try:
setattr(new_line, field_name,
getattr(line, field_name))
except AttributeError:
pass
new_line.amount = line.amount + amount_to_pay
new_line.invoice = None
2014-03-16 20:54:54 +01:00
line_offset += 1
2014-10-29 20:39:23 +01:00
lines.insert(index + line_offset, new_line)
invoice_id2amount_to_pay[line.invoice.id] = 0
2014-10-29 20:39:23 +01:00
line.amount = amount_to_pay.copy_sign(line.amount)
else:
invoice_id2amount_to_pay[line.invoice.id] = (
line.amount + amount_to_pay)
else:
2014-10-29 20:39:23 +01:00
line.invoice = None
self.lines = lines
2014-07-20 13:45:45 +02:00
@fields.depends('journal')
def on_change_with_validation(self, name=None):
if self.journal:
return self.journal.validation
2014-07-20 13:13:00 +02:00
def _group_key(self, line):
key = (
('number', line.number or Unequal()),
2014-07-20 13:13:00 +02:00
('date', line.date),
('party', line.party),
)
return key
def _get_grouped_line(self):
"Return Line class for grouped lines"
assert self.lines
keys = [k[0] for k in self._group_key(self.lines[0])]
class Line(namedtuple('Line', keys + ['lines'])):
@property
def amount(self):
return sum((l.amount for l in self.lines))
@property
def descriptions(self):
done = set()
for line in self.lines:
if line.description and line.description not in done:
done.add(line.description)
yield line.description
return Line
@property
def grouped_lines(self):
if self.lines:
Line = self._get_grouped_line()
for key, lines in groupby(self.lines, key=self._group_key):
yield Line(**dict(key + (('lines', list(lines)),)))
2012-09-24 22:17:36 +02:00
@classmethod
def delete(cls, statements):
# Cancel before delete
2012-09-24 22:17:36 +02:00
cls.cancel(statements)
for statement in statements:
if statement.state != 'cancel':
cls.raise_user_error('delete_cancel', (statement.rec_name,))
2012-09-24 22:17:36 +02:00
super(Statement, cls).delete(statements)
2012-09-24 22:17:36 +02:00
@classmethod
2012-03-25 00:17:31 +01:00
@ModelView.button
@Workflow.transition('draft')
2012-09-24 22:17:36 +02:00
def draft(cls, statements):
2012-03-25 00:17:31 +01:00
pass
2014-07-20 13:45:45 +02:00
def validate_balance(self):
pool = Pool()
Lang = pool.get('ir.lang')
end_balance = (self.start_balance
+ sum(l.amount for l in self.lines))
if end_balance != self.end_balance:
lang, = Lang.search([
('code', '=', Transaction().language),
])
amount = Lang.format(lang,
'%.' + str(self.journal.currency.digits) + 'f',
end_balance, True)
self.raise_user_error('wrong_end_balance', amount)
def validate_amount(self):
pool = Pool()
Lang = pool.get('ir.lang')
amount = sum(l.amount for l in self.lines)
if amount != self.total_amount:
lang, = Lang.search([
('code', '=', Transaction().language),
])
amount = Lang.format(lang,
'%.' + str(self.journal.currency.digits) + 'f',
amount, True)
self.raise_user_error('wrong_total_amount', amount)
def validate_number_of_lines(self):
number = len(list(self.grouped_lines))
if number != self.number_of_lines:
self.raise_user_error('wrong_number_of_lines', number)
2012-09-24 22:17:36 +02:00
@classmethod
2012-03-25 00:17:31 +01:00
@ModelView.button
@Workflow.transition('validated')
def validate_statement(cls, statements):
pool = Pool()
Line = pool.get('account.statement.line')
2012-09-24 22:17:36 +02:00
for statement in statements:
2014-07-20 13:45:45 +02:00
getattr(statement, 'validate_%s' % statement.validation)()
2015-01-02 15:43:29 +01:00
cls.create_move(statements)
cls.write(statements, {
'state': 'validated',
})
common_lines = Line.search([
('statement.state', '=', 'draft'),
('invoice.state', '=', 'paid'),
])
if common_lines:
warning_key = '_'.join(str(l.id) for l in common_lines)
cls.raise_user_warning(warning_key, 'paid_invoice_draft_statement')
Line.write(common_lines, {
'invoice': None,
})
2008-06-10 22:01:27 +02:00
2015-01-02 15:43:29 +01:00
@classmethod
def create_move(cls, statements):
2015-01-07 14:51:34 +01:00
'''Create move for the statements and try to reconcile the lines.
Returns the list of move, statement and lines
'''
2015-01-02 15:43:29 +01:00
pool = Pool()
Line = pool.get('account.statement.line')
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
moves = []
for statement in statements:
for key, lines in groupby(
statement.lines, key=statement._group_key):
lines = list(lines)
key = dict(key)
move = statement._get_move(key)
moves.append((move, statement, lines))
Move.save([m for m, _, _ in moves])
to_write = []
for move, _, lines in moves:
to_write.append(lines)
to_write.append({
'move': move.id,
})
if to_write:
Line.write(*to_write)
2015-01-02 15:43:29 +01:00
move_lines = []
for move, statement, lines in moves:
amount = 0
amount_second_currency = 0
for line in lines:
move_line = line.get_move_line()
move_line.move = move
amount += move_line.debit - move_line.credit
if move_line.amount_second_currency:
amount_second_currency += move_line.amount_second_currency
move_lines.append((move_line, line))
move_line = statement._get_move_line(
2017-02-01 21:43:20 +01:00
amount, amount_second_currency, lines)
2015-01-02 15:43:29 +01:00
move_line.move = move
move_lines.append((move_line, None))
MoveLine.save([l for l, _ in move_lines])
for move_line, line in move_lines:
if line:
line.reconcile(move_line)
2015-01-07 14:51:34 +01:00
return moves
2015-01-02 15:43:29 +01:00
def _get_move(self, key):
'Return Move for the grouping key'
pool = Pool()
Move = pool.get('account.move')
Period = pool.get('account.period')
period_id = Period.find(self.company.id, date=key['date'])
return Move(
period=period_id,
journal=self.journal.journal,
date=key['date'],
origin=self,
company=self.company,
2017-02-01 21:43:20 +01:00
description=unicode(key['number']),
2015-01-02 15:43:29 +01:00
)
2017-02-01 21:43:20 +01:00
def _get_move_line(self, amount, amount_second_currency, lines):
2015-01-02 15:43:29 +01:00
'Return counterpart Move Line for the amount'
pool = Pool()
MoveLine = pool.get('account.move.line')
if amount < 0:
account = self.journal.journal.debit_account
else:
account = self.journal.journal.credit_account
if not account:
self.raise_user_error('debit_credit_account_statement_journal',
(self.journal.rec_name,))
if self.journal.currency != self.company.currency:
second_currency = self.journal.currency
amount_second_currency *= -1
2015-01-02 15:43:29 +01:00
else:
second_currency = None
amount_second_currency = None
2017-02-01 21:43:20 +01:00
descriptions = {l.description for l in lines}
if len(descriptions) == 1:
description, = descriptions
else:
description = ''
2015-01-02 15:43:29 +01:00
return MoveLine(
debit=abs(amount) if amount < 0 else 0,
credit=abs(amount) if amount > 0 else 0,
account=account,
second_currency=second_currency,
amount_second_currency=amount_second_currency,
2017-02-01 21:43:20 +01:00
description=description,
2015-01-02 15:43:29 +01:00
)
2012-09-24 22:17:36 +02:00
@classmethod
2012-03-25 00:17:31 +01:00
@ModelView.button
@Workflow.transition('posted')
2012-09-24 22:17:36 +02:00
def post(cls, statements):
StatementLine = Pool().get('account.statement.line')
2012-03-25 00:17:31 +01:00
lines = [l for s in statements for l in s.lines]
2012-09-24 22:17:36 +02:00
StatementLine.post_move(lines)
2008-06-10 22:01:27 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
2012-03-25 00:17:31 +01:00
@ModelView.button
@Workflow.transition('cancel')
2012-09-24 22:17:36 +02:00
def cancel(cls, statements):
StatementLine = Pool().get('account.statement.line')
2012-03-25 00:17:31 +01:00
lines = [l for s in statements for l in s.lines]
2012-09-24 22:17:36 +02:00
StatementLine.delete_move(lines)
2008-06-10 22:01:27 +02:00
2008-06-17 12:09:01 +02:00
2016-09-13 14:04:17 +02:00
class Line(sequence_ordered(), ModelSQL, ModelView):
'Account Statement Line'
2012-09-24 22:17:36 +02:00
__name__ = 'account.statement.line'
2016-06-30 18:55:57 +02:00
_states = {
'readonly': Eval('statement_state') != 'draft',
}
_depends = ['statement_state']
statement = fields.Many2One('account.statement', 'Statement',
2016-06-30 18:55:57 +02:00
required=True, ondelete='CASCADE', states=_states, depends=_depends)
statement_state = fields.Function(
fields.Selection(STATES, 'Statement State'),
'on_change_with_statement_state')
2014-07-04 14:43:49 +02:00
number = fields.Char('Number')
2016-06-30 18:55:57 +02:00
date = fields.Date('Date', required=True, states=_states, depends=_depends)
amount = fields.Numeric('Amount', required=True,
2016-06-30 18:55:57 +02:00
digits=(16, Eval('_parent_statement', {}).get('currency_digits', 2)),
states=_states, depends=_depends)
party = fields.Many2One('party.party', 'Party',
states=_states, depends=_depends)
account = fields.Many2One('account.account', 'Account', required=True,
2014-01-26 14:28:55 +01:00
domain=[
2011-08-21 11:20:33 +02:00
('company', '=', Eval('_parent_statement', {}).get('company', 0)),
('kind', '!=', 'view'),
2016-06-30 18:55:57 +02:00
],
states=_states, depends=_depends)
description = fields.Char('Description', states=_states, depends=_depends)
2014-12-03 16:13:25 +01:00
move = fields.Many2One('account.move', 'Account Move', readonly=True,
domain=[
('company', '=', Eval('_parent_statement', {}).get('company', -1)),
])
invoice = fields.Many2One('account.invoice', 'Invoice',
2011-08-04 16:57:19 +02:00
domain=[
If(Bool(Eval('party')), [('party', '=', Eval('party'))], []),
If(Bool(Eval('account')), [('account', '=', Eval('account'))], []),
2012-06-01 23:44:37 +02:00
If(Eval('_parent_statement', {}).get('state') == 'draft',
2012-12-23 22:29:15 +01:00
('state', '=', 'posted'),
2012-06-01 23:44:37 +02:00
('state', '!=', '')),
2010-01-20 10:57:50 +01:00
],
2016-06-30 18:55:57 +02:00
states=_states,
depends=['party', 'account'] + _depends)
del _states, _depends
2008-06-10 22:01:27 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
def __setup__(cls):
super(Line, cls).__setup__()
cls._error_messages.update({
'amount_greater_invoice_amount_to_pay': ('Amount "%s" is '
'greater than the amount to pay of invoice.'),
})
t = cls.__table__()
2012-09-24 22:17:36 +02:00
cls._sql_constraints += [
('check_statement_line_amount', Check(t, t.amount != 0),
'Amount should be a positive or negative value.'),
]
2008-07-17 19:36:26 +02:00
2016-06-30 18:55:57 +02:00
@fields.depends('statement', '_parent_statement.state')
def on_change_with_statement_state(self, name=None):
if self.statement:
return self.statement.state
2012-09-24 22:17:36 +02:00
@staticmethod
def default_amount():
2012-04-02 10:52:36 +02:00
return Decimal(0)
2014-01-26 14:28:55 +01:00
@fields.depends('amount', 'party', 'invoice')
2012-09-24 22:17:36 +02:00
def on_change_party(self):
if self.party:
if self.amount:
if self.amount > Decimal("0.0"):
2014-10-29 20:39:23 +01:00
self.account = self.party.account_receivable
else:
2014-10-29 20:39:23 +01:00
self.account = self.party.account_payable
2012-09-24 22:17:36 +02:00
if self.invoice:
if self.party:
if self.invoice.party != self.party:
2014-10-29 20:39:23 +01:00
self.invoice = None
else:
2014-10-29 20:39:23 +01:00
self.invoice = None
2008-06-10 22:01:27 +02:00
2014-01-26 14:28:55 +01:00
@fields.depends('amount', 'party', 'account', 'invoice',
'_parent_statement.journal')
2012-09-24 22:17:36 +02:00
def on_change_amount(self):
Currency = Pool().get('currency.currency')
if self.party:
if self.account and self.account not in (
self.party.account_receivable, self.party.account_payable):
# The user has entered a non-default value, we keep it.
pass
2012-09-24 22:17:36 +02:00
elif self.amount:
if self.amount > Decimal("0.0"):
2014-10-29 20:39:23 +01:00
self.account = self.party.account_receivable
else:
2014-10-29 20:39:23 +01:00
self.account = self.party.account_payable
2012-09-24 22:17:36 +02:00
if self.invoice:
if self.amount and self.statement and self.statement.journal:
invoice = self.invoice
journal = self.statement.journal
with Transaction().set_context(date=invoice.currency_date):
2012-09-24 22:17:36 +02:00
amount_to_pay = Currency.compute(invoice.currency,
invoice.amount_to_pay, journal.currency)
if abs(self.amount) > amount_to_pay:
2014-10-29 20:39:23 +01:00
self.invoice = None
else:
2014-10-29 20:39:23 +01:00
self.invoice = None
2014-01-26 14:28:55 +01:00
@fields.depends('account', 'invoice')
2012-09-24 22:17:36 +02:00
def on_change_account(self):
if self.invoice:
if self.account:
if self.invoice.account != self.account:
2014-10-29 20:39:23 +01:00
self.invoice = None
else:
2014-10-29 20:39:23 +01:00
self.invoice = None
2008-06-17 18:33:07 +02:00
2014-01-26 14:28:55 +01:00
@fields.depends('party', 'account', 'invoice')
def on_change_invoice(self):
if self.invoice:
if not self.party:
2014-10-29 20:39:23 +01:00
self.party = self.invoice.party
if not self.account:
2014-10-29 20:39:23 +01:00
self.account = self.invoice.account
2012-09-29 15:34:37 +02:00
def get_rec_name(self, name):
return self.statement.rec_name
2013-11-25 12:31:02 +01:00
@classmethod
def search_rec_name(cls, name, clause):
return [('statement.rec_name',) + tuple(clause[1:])]
2012-09-24 22:17:36 +02:00
@classmethod
def copy(cls, lines, default=None):
if default is None:
default = {}
default = default.copy()
default.setdefault('move', None)
default.setdefault('invoice', None)
2012-09-24 22:17:36 +02:00
return super(Line, cls).copy(lines, default=default)
2015-01-02 15:43:29 +01:00
def reconcile(self, move_line):
2011-07-03 13:18:45 +02:00
pool = Pool()
2012-09-24 22:17:36 +02:00
Currency = pool.get('currency.currency')
Lang = pool.get('ir.lang')
2015-01-02 15:43:29 +01:00
Invoice = pool.get('account.invoice')
MoveLine = pool.get('account.move.line')
2012-09-24 22:17:36 +02:00
if self.invoice:
with Transaction().set_context(date=self.invoice.currency_date):
amount_to_pay = Currency.compute(self.invoice.currency,
self.invoice.amount_to_pay,
self.statement.journal.currency)
if abs(amount_to_pay) < abs(self.amount):
2012-09-24 22:17:36 +02:00
lang, = Lang.search([
2012-01-27 21:15:07 +01:00
('code', '=', Transaction().language),
2010-08-16 19:23:32 +02:00
])
2012-09-24 22:17:36 +02:00
amount = Lang.format(lang,
'%.' + str(self.statement.journal.currency.digits) + 'f',
self.amount, True)
2010-08-16 19:23:32 +02:00
self.raise_user_error('amount_greater_invoice_amount_to_pay',
error_args=(amount,))
2012-09-24 22:17:36 +02:00
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.statement.journal.currency,
self.amount, self.statement.company.currency)
2012-09-24 22:17:36 +02:00
reconcile_lines = self.invoice.get_reconcile_lines_for_amount(
amount)
2015-01-02 15:43:29 +01:00
assert move_line.account == self.invoice.account
Invoice.write([self.invoice], {
'payment_lines': [('add', [move_line.id])],
})
if reconcile_lines[1] == Decimal('0.0'):
2012-09-24 22:17:36 +02:00
lines = reconcile_lines[0] + [move_line]
MoveLine.reconcile(lines)
2008-06-17 19:21:23 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
def post_move(cls, lines):
Move = Pool().get('account.move')
2015-01-02 15:43:29 +01:00
Move.post(list({l.move for l in lines if l.move}))
2008-06-17 19:21:23 +02:00
2012-09-24 22:17:36 +02:00
@classmethod
def delete_move(cls, lines):
Move = Pool().get('account.move')
2015-01-02 15:43:29 +01:00
Move.delete(list({l.move for l in lines if l.move}))
2008-07-25 12:51:24 +02:00
2015-01-02 15:43:29 +01:00
def get_move_line(self):
2008-09-24 17:43:00 +02:00
'''
2015-01-02 15:43:29 +01:00
Return the move line for the statement line
2008-09-24 17:43:00 +02:00
'''
2012-09-24 22:17:36 +02:00
pool = Pool()
MoveLine = pool.get('account.move.line')
Currency = Pool().get('currency.currency')
zero = Decimal("0.0")
with Transaction().set_context(date=self.date):
amount = Currency.compute(self.statement.journal.currency,
self.amount, self.statement.company.currency)
2012-09-24 22:17:36 +02:00
if self.statement.journal.currency != self.statement.company.currency:
second_currency = self.statement.journal.currency.id
amount_second_currency = -self.amount
else:
amount_second_currency = None
second_currency = None
2015-01-02 15:43:29 +01:00
return MoveLine(
description=self.description,
debit=amount < zero and -amount or zero,
credit=amount >= zero and amount or zero,
account=self.account,
party=self.party if self.account.party_required else None,
second_currency=second_currency,
amount_second_currency=amount_second_currency,
)
2014-07-20 13:13:00 +02:00
2015-03-02 23:36:17 +01:00
class LineGroup(ModelSQL, ModelView):
'Account Statement Line Group'
__name__ = 'account.statement.line.group'
_rec_name = 'number'
statement = fields.Many2One('account.statement', 'Statement')
journal = fields.Function(fields.Many2One('account.statement.journal',
'Journal'), 'get_journal', searcher='search_journal')
number = fields.Char('Number')
date = fields.Date('Date')
amount = fields.Numeric('Amount',
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'])
currency = fields.Function(fields.Many2One('currency.currency',
'Currency'), 'get_currency')
currency_digits = fields.Function(fields.Integer('Currency Digits'),
'get_currency_digits')
party = fields.Many2One('party.party', 'Party')
move = fields.Many2One('account.move', 'Move')
@classmethod
def __setup__(cls):
super(LineGroup, cls).__setup__()
cls._order.insert(0, ('date', 'DESC'))
@classmethod
def _grouped_columns(cls, line):
return [
Max(line.statement).as_('statement'),
Max(line.number).as_('number'),
Max(line.date).as_('date'),
Sum(line.amount).as_('amount'),
Max(line.party).as_('party'),
]
@classmethod
def table_query(cls):
pool = Pool()
Move = pool.get('account.move')
Line = pool.get('account.statement.line')
move = Move.__table__()
line = Line.__table__()
std_columns = [
move.id,
move.create_uid,
move.create_date,
move.write_uid,
move.write_date,
]
columns = (std_columns + [move.id.as_('move')]
+ cls._grouped_columns(line))
return move.join(line,
condition=move.id == line.move
).select(*columns,
where=move.origin.ilike(Statement.__name__ + ',%'),
group_by=std_columns + [move.id]
)
def get_journal(self, name):
return self.statement.journal.id
@classmethod
def search_journal(cls, name, clause):
return [('statement.journal',) + tuple(clause[1:])]
def get_currency(self, name):
return self.statement.journal.currency.id
def get_currency_digits(self, name):
return self.statement.journal.currency.digits
2014-07-20 13:13:00 +02:00
class StatementReport(CompanyReport):
__name__ = 'account.statement'