Files
tryton/account.py
T
2014-12-02 12:54:39 +01:00

341 lines
13 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Null
from trytond.model import ModelSQL, ModelView, MatchMixin, fields
from trytond.pyson import Eval
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
__all__ = ['Configuration', 'ConfigurationTaxRounding',
'FiscalYear', 'Period', 'Move', 'Reconciliation']
__metaclass__ = PoolMeta
tax_roundings = [
('document', 'Per Document'),
('line', 'Per Line'),
]
class Configuration:
__name__ = 'account.configuration'
tax_rounding = fields.Function(fields.Selection(tax_roundings,
'Tax Rounding'), 'on_change_with_tax_rounding')
tax_roundings = fields.One2Many('account.configuration.tax_rounding',
'configuration', 'Tax Roundings')
@staticmethod
def default_tax_rounding():
return 'document'
@fields.depends('tax_roundings')
def on_change_with_tax_rounding(self, name=None, pattern=None):
context = Transaction().context
if pattern is None:
pattern = {}
pattern = pattern.copy()
pattern['company'] = context.get('company')
for line in self.tax_roundings:
if line.match(pattern):
return line.method
return self.default_tax_rounding()
class ConfigurationTaxRounding(ModelSQL, ModelView, MatchMixin):
'Account Configuration Tax Rounding'
__name__ = 'account.configuration.tax_rounding'
configuration = fields.Many2One('account.configuration', 'Configuration',
required=True, ondelete='CASCADE')
sequence = fields.Integer('Sequence')
company = fields.Many2One('company.company', 'Company')
method = fields.Selection(tax_roundings, 'Method', required=True)
@classmethod
def __setup__(cls):
super(ConfigurationTaxRounding, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == Null, table.sequence]
@staticmethod
def default_method():
return 'document'
class FiscalYear:
__name__ = 'account.fiscalyear'
out_invoice_sequence = fields.Many2One('ir.sequence.strict',
'Customer Invoice Sequence', required=True,
domain=[
('code', '=', 'account.invoice'),
['OR',
('company', '=', Eval('company')),
('company', '=', None),
],
],
context={
'code': 'account.invoice',
'company': Eval('company'),
},
depends=['company'])
in_invoice_sequence = fields.Many2One('ir.sequence.strict',
'Supplier Invoice Sequence', required=True,
domain=[
('code', '=', 'account.invoice'),
['OR',
('company', '=', Eval('company')),
('company', '=', None),
],
],
context={
'code': 'account.invoice',
'company': Eval('company'),
},
depends=['company'])
out_credit_note_sequence = fields.Many2One('ir.sequence.strict',
'Customer Credit Note Sequence', required=True,
domain=[
('code', '=', 'account.invoice'),
['OR',
('company', '=', Eval('company')),
('company', '=', None),
],
],
context={
'code': 'account.invoice',
'company': Eval('company'),
}, depends=['company'])
in_credit_note_sequence = fields.Many2One('ir.sequence.strict',
'Supplier Credit Note Sequence', required=True,
domain=[
('code', '=', 'account.invoice'),
['OR',
('company', '=', Eval('company')),
('company', '=', None),
],
],
context={
'code': 'account.invoice',
'company': Eval('company'),
}, depends=['company'])
@classmethod
def __setup__(cls):
super(FiscalYear, cls).__setup__()
cls._error_messages.update({
'change_invoice_sequence': ('You can not change '
'invoice sequence in fiscal year "%s" because there are '
'already posted invoices in this fiscal year.'),
'different_invoice_sequence': ('Fiscal year "%(first)s" and '
'"%(second)s" have the same invoice sequence.'),
})
@classmethod
def validate(cls, years):
super(FiscalYear, cls).validate(years)
for year in years:
year.check_invoice_sequences()
def check_invoice_sequences(self):
for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
'out_credit_note_sequence', 'in_credit_note_sequence'):
fiscalyears = self.search([
(sequence, '=', getattr(self, sequence).id),
('id', '!=', self.id),
])
if fiscalyears:
self.raise_user_error('different_invoice_sequence', {
'first': self.rec_name,
'second': fiscalyears[0].rec_name,
})
@classmethod
def write(cls, *args):
Invoice = Pool().get('account.invoice')
actions = iter(args)
for fiscalyears, values in zip(actions, actions):
for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
'out_credit_note_sequence', 'in_credit_note_sequence'):
if not values.get(sequence):
continue
for fiscalyear in fiscalyears:
if (getattr(fiscalyear, sequence)
and (getattr(fiscalyear, sequence).id !=
values[sequence])):
if Invoice.search([
('invoice_date', '>=',
fiscalyear.start_date),
('invoice_date', '<=',
fiscalyear.end_date),
('number', '!=', None),
('type', '=', sequence[:-9]),
]):
cls.raise_user_error('change_invoice_sequence',
(fiscalyear.rec_name,))
super(FiscalYear, cls).write(*args)
class Period:
__name__ = 'account.period'
out_invoice_sequence = fields.Many2One('ir.sequence.strict',
'Customer Invoice Sequence',
domain=[('code', '=', 'account.invoice')],
context={'code': 'account.invoice'},
states={
'invisible': Eval('type') != 'standard',
},
depends=['type'])
in_invoice_sequence = fields.Many2One('ir.sequence.strict',
'Supplier Invoice Sequence',
domain=[('code', '=', 'account.invoice')],
context={'code': 'account.invoice'},
states={
'invisible': Eval('type') != 'standard',
},
depends=['type'])
out_credit_note_sequence = fields.Many2One('ir.sequence.strict',
'Customer Credit Note Sequence',
domain=[('code', '=', 'account.invoice')],
context={'code': 'account.invoice'},
states={
'invisible': Eval('type') != 'standard',
},
depends=['type'])
in_credit_note_sequence = fields.Many2One('ir.sequence.strict',
'Supplier Credit Note Sequence',
domain=[('code', '=', 'account.invoice')],
context={'code': 'account.invoice'},
states={
'invisible': Eval('type') != 'standard',
},
depends=['type'])
@classmethod
def __setup__(cls):
super(Period, cls).__setup__()
cls._error_messages.update({
'change_invoice_sequence': ('You can not change the invoice '
'sequence in period "%s" because there is already an '
'invoice posted in this period'),
'different_invoice_sequence': ('Period "%(first)s" and '
'"%(second)s" have the same invoice sequence.'),
'different_period_fiscalyear_company': ('Period "%(period)s" '
'must have the same company as its fiscal year '
'(%(fiscalyear)s).'),
})
@classmethod
def validate(cls, periods):
super(Period, cls).validate(periods)
for period in periods:
period.check_invoice_sequences()
def check_invoice_sequences(self):
for sequence_name in ('out_invoice_sequence', 'in_invoice_sequence',
'out_credit_note_sequence', 'in_credit_note_sequence'):
sequence = getattr(self, sequence_name)
if not sequence:
continue
periods = self.search([
(sequence_name, '=', sequence.id),
('fiscalyear', '!=', self.fiscalyear.id),
])
if periods:
self.raise_user_error('different_invoice_sequence', {
'first': self.rec_name,
'second': periods[0].rec_name,
})
if (sequence.company
and sequence.company != self.fiscalyear.company):
self.raise_user_error('different_period_fiscalyear_company', {
'period': self.rec_name,
'fiscalyear': self.fiscalyear.rec_name,
})
@classmethod
def create(cls, vlist):
FiscalYear = Pool().get('account.fiscalyear')
vlist = [v.copy() for v in vlist]
for vals in vlist:
if vals.get('fiscalyear'):
fiscalyear = FiscalYear(vals['fiscalyear'])
for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
'out_credit_note_sequence', 'in_credit_note_sequence'):
if not vals.get(sequence):
vals[sequence] = getattr(fiscalyear, sequence).id
return super(Period, cls).create(vlist)
@classmethod
def write(cls, *args):
Invoice = Pool().get('account.invoice')
actions = iter(args)
for periods, values in zip(actions, actions):
for sequence_name in ('out_invoice_sequence',
'in_invoice_sequence', 'out_credit_note_sequence',
'in_credit_note_sequence'):
if not values.get(sequence_name):
continue
for period in periods:
sequence = getattr(period, sequence_name)
if (sequence and sequence.id != values[sequence_name]):
if Invoice.search([
('invoice_date', '>=', period.start_date),
('invoice_date', '<=', period.end_date),
('number', '!=', None),
('type', '=', sequence_name[:-9]),
]):
cls.raise_user_error('change_invoice_sequence',
(period.rec_name,))
super(Period, cls).write(*args)
def get_invoice_sequence(self, invoice_type):
sequence = getattr(self, invoice_type + '_sequence')
if sequence:
return sequence
return getattr(self.fiscalyear, invoice_type + '_sequence')
class Move:
__name__ = 'account.move'
@classmethod
def _get_origin(cls):
return super(Move, cls)._get_origin() + ['account.invoice']
class Reconciliation:
__name__ = 'account.move.reconciliation'
@classmethod
def create(cls, vlist):
Invoice = Pool().get('account.invoice')
reconciliations = super(Reconciliation, cls).create(vlist)
move_ids = set()
account_ids = set()
for reconciliation in reconciliations:
move_ids |= {l.move.id for l in reconciliation.lines}
account_ids |= {l.account.id for l in reconciliation.lines}
invoices = Invoice.search([
('move', 'in', list(move_ids)),
('account', 'in', list(account_ids)),
])
Invoice.process(invoices)
return reconciliations
@classmethod
def delete(cls, reconciliations):
Invoice = Pool().get('account.invoice')
move_ids = set(l.move.id for r in reconciliations for l in r.lines)
invoices = Invoice.search([
('move', 'in', list(move_ids)),
])
super(Reconciliation, cls).delete(reconciliations)
Invoice.process(invoices)