Files
tryton/invoice.py
T

2700 lines
104 KiB
Python
Raw Normal View History

2008-10-01 17:53:49 +02: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-19 22:02:49 +02:00
from decimal import Decimal
import base64
import operator
2012-03-25 00:17:27 +01:00
from trytond.model import Workflow, ModelView, ModelSQL, fields
2008-04-14 14:37:04 +02:00
from trytond.report import Report
2012-01-02 17:57:03 +01:00
from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \
Button
from trytond.backend import TableHandler, FIELDS
2012-09-10 15:27:42 +02:00
from trytond.pyson import If, Eval, Bool, Id
2010-01-20 15:25:32 +01:00
from trytond.tools import reduce_ids
2010-08-19 22:02:49 +02:00
from trytond.transaction import Transaction
2011-07-03 13:18:43 +02:00
from trytond.pool import Pool
2012-09-24 22:17:32 +02:00
from trytond.rpc import RPC
__all__ = ['Invoice', 'InvoicePaymentLine', 'InvoiceLine',
'InvoiceLineTax', 'InvoiceTax',
'PrintInvoiceWarning', 'PrintInvoice', 'InvoiceReport',
'PayInvoiceStart', 'PayInvoiceAsk', 'PayInvoice',
'CreditInvoiceStart', 'CreditInvoice']
2008-04-10 17:21:44 +02:00
_STATES = {
2011-08-21 11:20:30 +02:00
'readonly': Eval('state') != 'draft',
2008-04-10 17:21:44 +02:00
}
2011-08-04 16:57:16 +02:00
_DEPENDS = ['state']
2008-04-10 17:21:44 +02:00
2008-11-28 18:29:40 +01:00
_TYPE = [
('out_invoice', 'Invoice'),
('in_invoice', 'Supplier Invoice'),
('out_credit_note', 'Credit Note'),
('in_credit_note', 'Supplier Credit Note'),
]
2008-04-10 17:21:44 +02:00
_TYPE2JOURNAL = {
'out_invoice': 'revenue',
'in_invoice': 'expense',
2008-10-03 17:12:23 +02:00
'out_credit_note': 'revenue',
'in_credit_note': 'expense',
2008-04-10 17:21:44 +02:00
}
2010-01-20 15:25:32 +01:00
_ZERO = Decimal('0.0')
2008-04-10 17:21:44 +02:00
2012-03-25 00:17:27 +01:00
class Invoice(Workflow, ModelSQL, ModelView):
2008-04-10 17:21:44 +02:00
'Invoice'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice'
2008-12-18 18:15:58 +01:00
_order_name = 'number'
2008-04-10 17:21:44 +02:00
company = fields.Many2One('company.company', 'Company', required=True,
2012-03-05 10:52:57 +01:00
states=_STATES, select=True, domain=[
2011-08-21 11:20:30 +02:00
('id', If(Eval('context', {}).contains('company'), '=', '!='),
Eval('context', {}).get('company', 0)),
2011-08-04 16:57:16 +02:00
],
depends=_DEPENDS)
2012-03-05 10:52:57 +01:00
type = fields.Selection(_TYPE, 'Type', select=True, on_change=['type',
'party', 'company'],
2011-08-04 16:57:16 +02:00
required=True, states={
2011-08-21 11:20:30 +02:00
'readonly': ((Eval('state') != 'draft')
| Eval('context', {}).get('type')
| (Eval('lines') & Eval('type'))),
2011-08-04 16:57:16 +02:00
}, depends=['state', 'lines'])
2010-03-11 16:45:42 +01:00
type_name = fields.Function(fields.Char('Type'), 'get_type_name')
2012-03-05 10:52:57 +01:00
number = fields.Char('Number', size=None, readonly=True, select=True)
2011-08-04 16:57:16 +02:00
reference = fields.Char('Reference', size=None, states=_STATES,
depends=_DEPENDS)
description = fields.Char('Description', size=None, states=_STATES,
depends=_DEPENDS)
2008-04-10 17:21:44 +02:00
state = fields.Selection([
('draft', 'Draft'),
2012-12-23 22:28:41 +01:00
('validated', 'Validated'),
('posted', 'Posted'),
2008-04-10 17:21:44 +02:00
('paid', 'Paid'),
('cancel', 'Canceled'),
], 'State', readonly=True)
2009-07-18 14:27:14 +02:00
invoice_date = fields.Date('Invoice Date',
states={
2012-12-23 22:28:41 +01:00
'readonly': Eval('state').in_(['posted', 'paid', 'cancel']),
'required': Eval('state').in_(
If(Eval('type').in_(['in_invoice', 'in_credit_note']),
['validated', 'posted', 'paid'],
['posted', 'paid'])),
2011-08-04 16:57:16 +02:00
},
depends=['state'])
accounting_date = fields.Date('Accounting Date', states=_STATES,
depends=_DEPENDS)
2011-12-22 22:04:29 +01:00
party = fields.Many2One('party.party', 'Party',
2011-08-04 16:57:16 +02:00
required=True, states=_STATES, depends=_DEPENDS,
on_change=['party', 'payment_term', 'type', 'company'])
2010-03-11 16:45:42 +01:00
party_lang = fields.Function(fields.Char('Party Language',
2012-09-24 22:17:32 +02:00
on_change_with=['party']), 'on_change_with_party_lang')
2008-10-22 20:08:25 +02:00
invoice_address = fields.Many2One('party.address', 'Invoice Address',
2011-08-04 16:57:16 +02:00
required=True, states=_STATES, depends=['state', 'party'],
domain=[('party', '=', Eval('party'))])
2008-05-09 17:26:41 +02:00
currency = fields.Many2One('currency.currency', 'Currency', required=True,
states={
2011-08-21 11:20:30 +02:00
'readonly': ((Eval('state') != 'draft')
| (Eval('lines') & Eval('currency'))),
2011-08-04 16:57:16 +02:00
}, depends=['state', 'lines'])
2010-03-11 16:45:42 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits',
2012-09-24 22:17:32 +02:00
on_change_with=['currency']), 'on_change_with_currency_digits')
currency_date = fields.Function(fields.Date('Currency Date',
2012-09-24 22:17:32 +02:00
on_change_with=['invoice_date']), 'on_change_with_currency_date',)
2008-04-10 17:21:44 +02:00
journal = fields.Many2One('account.journal', 'Journal', required=True,
2013-05-21 19:12:48 +02:00
states=_STATES, depends=_DEPENDS)
2008-04-10 17:21:44 +02:00
move = fields.Many2One('account.move', 'Move', readonly=True)
2012-12-23 22:28:41 +01:00
cancel_move = fields.Many2One('account.move', 'Cancel Move', readonly=True,
states={
'invisible': Eval('type').in_(['out_invoice', 'out_credit_note']),
})
2008-04-10 17:21:44 +02:00
account = fields.Many2One('account.account', 'Account', required=True,
2012-03-30 14:19:39 +02:00
states=_STATES, depends=_DEPENDS + ['type'],
2010-01-20 10:57:08 +01:00
domain=[
2011-08-21 11:20:30 +02:00
('company', '=', Eval('context', {}).get('company', 0)),
2012-03-30 14:19:39 +02:00
If(Eval('type').in_(['out_invoice', 'out_credit_note']),
('kind', '=', 'receivable'),
('kind', '=', 'payable')),
2011-08-04 16:57:16 +02:00
])
2008-04-10 17:21:44 +02:00
payment_term = fields.Many2One('account.invoice.payment_term',
2011-08-04 16:57:16 +02:00
'Payment Term', required=True, states=_STATES, depends=_DEPENDS)
2008-04-10 17:21:44 +02:00
lines = fields.One2Many('account.invoice.line', 'invoice', 'Lines',
states=_STATES, on_change=[
'lines', 'taxes', 'currency', 'party', 'type'
2011-08-04 16:57:16 +02:00
], depends=['state', 'currency_date'])
2008-04-10 17:21:44 +02:00
taxes = fields.One2Many('account.invoice.tax', 'invoice', 'Tax Lines',
2011-08-04 16:57:16 +02:00
states=_STATES, depends=_DEPENDS,
on_change=['lines', 'taxes', 'currency', 'party', 'type'])
comment = fields.Text('Comment', states=_STATES, depends=_DEPENDS)
2010-03-11 16:45:42 +01:00
untaxed_amount = fields.Function(fields.Numeric('Untaxed',
2011-08-04 16:57:16 +02:00
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']),
'get_untaxed_amount', searcher='search_untaxed_amount')
tax_amount = fields.Function(fields.Numeric('Tax', digits=(16,
Eval('currency_digits', 2)), depends=['currency_digits']),
'get_tax_amount', searcher='search_tax_amount')
total_amount = fields.Function(fields.Numeric('Total', digits=(16,
Eval('currency_digits', 2)), depends=['currency_digits']),
'get_total_amount', searcher='search_total_amount')
2010-03-11 16:45:42 +01:00
reconciled = fields.Function(fields.Boolean('Reconciled'),
'get_reconciled')
lines_to_pay = fields.Function(fields.One2Many('account.move.line', None,
'Lines to Pay'), 'get_lines_to_pay')
2009-03-05 14:51:35 +01:00
payment_lines = fields.Many2Many('account.invoice-account.move.line',
'invoice', 'line', readonly=True, string='Payment Lines')
2010-03-11 16:45:42 +01:00
amount_to_pay_today = fields.Function(fields.Numeric('Amount to Pay Today',
2011-08-04 16:57:16 +02:00
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']), 'get_amount_to_pay')
2010-03-11 16:45:42 +01:00
amount_to_pay = fields.Function(fields.Numeric('Amount to Pay',
2011-08-04 16:57:16 +02:00
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']), 'get_amount_to_pay')
2011-09-17 14:43:48 +02:00
invoice_report_cache = fields.Binary('Invoice Report', readonly=True)
invoice_report_format = fields.Char('Invoice Report Format', readonly=True)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
2012-12-23 22:28:41 +01:00
cls._check_modify_exclude = ['state', 'payment_lines', 'cancel_move',
2011-09-17 14:43:48 +02:00
'invoice_report_cache', 'invoice_report_format']
2012-09-24 22:17:32 +02:00
cls._order.insert(0, ('number', 'DESC'))
cls._order.insert(1, ('id', 'DESC'))
cls._error_messages.update({
'missing_tax_line': ('Invoice "%s" has taxes defined but not '
'on invoice lines.\nRe-compute the invoice.'),
'diff_tax_line': ('Invoice "%s" tax bases are different from '
'invoice lines.\nRe-compute the invoice.'),
'missing_tax_line2': ('Invoice "%s" has taxes on invoice lines '
'that are not in the invoice.\nRe-compute the invoice.'),
'no_invoice_sequence': ('There is no invoice sequence for '
'invoice "%(invoice)s" on the period/fiscal year '
'"%(period)s".'),
'modify_invoice': ('You can not modify invoice "%s" because '
'it is posted, paid or cancelled.'),
'same_debit_account': ('The debit account on journal '
'"%(journal)s" is the same as invoice "%(invoice)s"\'s '
'account.'),
'missing_debit_account': ('The debit account on journal "%s" '
'is missing.'),
'same_credit_account': ('The credit account on journal '
'"%(journal)s" is the same as invoice "%(invoice)s"\'s '
'account.'),
'missing_credit_account': ('The credit account on journal %s" '
'is missing.'),
'account_different_company': ('You can not create invoice '
'"%(invoice)s" on company "%(invoice_company)s because '
'account "%(account)s has a different company '
'(%(account_company)s.)'),
'same_account_on_line': ('Invoice "%(invoice)s" uses the same '
'account "%(account)s" for the invoice and in line '
'"%(line)s".'),
'delete_cancel': ('Invoice "%s" must be cancelled before '
'deletion.'),
'delete_numbered': ('The numbered invoice "%s" can not be '
'deleted.'),
'customer_invoice_cancel_move': ('Customer invoice/credit note '
'"%s" can not be cancelled once posted.'),
'period_cancel_move': ('The period of Invoice "%s" is closed.\n'
2013-02-16 22:06:26 +01:00
'Use the today for cancel move?'),
})
2012-09-24 22:17:32 +02:00
cls._transitions |= set((
2012-12-23 22:28:41 +01:00
('draft', 'validated'),
('validated', 'posted'),
('draft', 'posted'),
('posted', 'paid'),
('validated', 'draft'),
('paid', 'posted'),
2012-03-25 00:17:27 +01:00
('draft', 'cancel'),
2012-12-23 22:28:41 +01:00
('validated', 'cancel'),
('posted', 'cancel'),
2012-04-03 12:20:28 +02:00
('cancel', 'draft'),
2012-03-25 00:17:27 +01:00
))
2012-09-24 22:17:32 +02:00
cls._buttons.update({
2012-03-25 00:17:27 +01:00
'cancel': {
2012-12-23 22:28:41 +01:00
'invisible': (~Eval('state').in_(['draft', 'validated'])
& ~((Eval('state') == 'posted')
& Eval('type').in_(
['in_invoice', 'in_credit_note']))),
2012-03-25 00:17:27 +01:00
},
'draft': {
2012-12-23 22:28:41 +01:00
'invisible': (~Eval('state').in_(['cancel', 'validated'])
| ((Eval('state') == 'cancel') & Eval('cancel_move'))),
2012-03-25 00:17:27 +01:00
'icon': If(Eval('state') == 'cancel', 'tryton-clear',
'tryton-go-previous'),
},
'validate_invoice': {
2012-03-25 00:17:27 +01:00
'invisible': Eval('state') != 'draft',
},
2012-12-23 22:28:41 +01:00
'post': {
'invisible': ~Eval('state').in_(['draft', 'validated']),
2012-03-25 00:17:27 +01:00
},
2012-09-10 15:27:42 +02:00
'pay': {
2012-12-23 22:28:41 +01:00
'invisible': Eval('state') != 'posted',
2012-09-10 15:27:42 +02:00
'readonly': ~Eval('groups', []).contains(
Id('account', 'group_account')),
},
2012-03-25 00:17:27 +01:00
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __register__(cls, module_name):
super(Invoice, cls).__register__(module_name)
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
table = TableHandler(cursor, cls, module_name)
2009-07-18 14:27:14 +02:00
# Migration from 1.2 invoice_date is no more required
table.not_null_action('invoice_date', action='remove')
2011-09-17 14:43:48 +02:00
# Migration from 2.0 invoice_report renamed into invoice_report_cache
# to remove base64 encoding
if (table.column_exist('invoice_report')
and table.column_exist('invoice_report_cache')):
limit = cursor.IN_MAX
cursor.execute('SELECT COUNT(id) '
2012-09-24 22:17:32 +02:00
'FROM "' + cls._table + '"')
invoice_count, = cursor.fetchone()
for offset in range(0, invoice_count, limit):
cursor.execute(cursor.limit_clause(
'SELECT id, invoice_report '
2012-09-24 22:17:32 +02:00
'FROM "' + cls._table + '"'
'ORDER BY id',
limit, offset))
for invoice_id, report in cursor.fetchall():
if report:
report = buffer(base64.decodestring(str(report)))
2012-09-24 22:17:32 +02:00
cursor.execute('UPDATE "' + cls._table + '" '
'SET invoice_report_cache = %s '
'WHERE id = %s', (report, invoice_id))
2011-12-26 16:28:02 +01:00
table.drop_column('invoice_report')
2011-09-17 14:43:48 +02:00
2012-12-23 22:28:41 +01:00
# Migration from 2.6:
# - proforma renamed into validated
# - open renamed into posted
cursor.execute('UPDATE "' + cls._table + '" '
'SET state = %s WHERE state = %s', ('validated', 'proforma'))
cursor.execute('UPDATE "' + cls._table + '" '
'SET state = %s WHERE state = %s', ('posted', 'open'))
2010-01-23 11:41:42 +01:00
# Add index on create_date
table.index_action('create_date', action='add')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_type():
2010-08-19 22:02:49 +02:00
return Transaction().context.get('type', 'out_invoice')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@staticmethod
def default_state():
2008-04-10 17:21:44 +02:00
return 'draft'
2012-09-24 22:17:32 +02:00
@staticmethod
def default_currency():
Company = Pool().get('company.company')
2010-08-19 22:02:49 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:32 +02:00
company = Company(Transaction().context['company'])
return company.currency.id
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@staticmethod
def default_currency_digits():
Company = Pool().get('company.company')
2010-08-19 22:02:49 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:32 +02:00
company = Company(Transaction().context['company'])
return company.currency.digits
return 2
2012-09-24 22:17:32 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:07 +02:00
return Transaction().context.get('company')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def default_payment_term(cls):
PaymentTerm = Pool().get('account.invoice.payment_term')
payment_terms = PaymentTerm.search(cls.payment_term.domain)
if len(payment_terms) == 1:
return payment_terms[0].id
2012-09-24 22:17:32 +02:00
def __get_account_payment_term(self):
'''
Return default account and payment term
'''
2012-09-24 22:17:32 +02:00
account = None
payment_term = None
result = {
2012-03-27 10:43:07 +02:00
'account': None,
}
2012-09-24 22:17:32 +02:00
if self.party:
if self.type in ('out_invoice', 'out_credit_note'):
account = self.party.account_receivable
if (self.type == 'out_invoice'
and self.party.customer_payment_term):
payment_term = self.party.customer_payment_term
elif self.type in ('in_invoice', 'in_credit_note'):
account = self.party.account_payable
if (self.type == 'in_invoice'
and self.party.supplier_payment_term):
payment_term = self.party.supplier_payment_term
if self.company and self.type in ('out_credit_note', 'in_credit_note'):
if self.type == 'out_credit_note':
2013-03-20 22:50:23 +01:00
payment_term = self.company.party.customer_payment_term
else:
2013-03-20 22:50:23 +01:00
payment_term = self.company.party.supplier_payment_term
2012-09-24 22:17:32 +02:00
if account:
result['account'] = account.id
result['account.rec_name'] = account.rec_name
if payment_term:
result['payment_term'] = payment_term.id
result['payment_term.rec_name'] = payment_term.rec_name
return result
2012-09-24 22:17:32 +02:00
def on_change_type(self):
Journal = Pool().get('account.journal')
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
journals = Journal.search([
('type', '=', _TYPE2JOURNAL.get(self.type or 'out_invoice',
'revenue')),
], limit=1)
if journals:
journal, = journals
res['journal'] = journal.id
res['journal.rec_name'] = journal.rec_name
2012-09-24 22:17:32 +02:00
res.update(self.__get_account_payment_term())
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def on_change_party(self):
2008-04-10 17:21:44 +02:00
res = {
2012-03-27 10:43:07 +02:00
'invoice_address': None,
2012-09-24 22:17:32 +02:00
}
res.update(self.__get_account_payment_term())
2012-09-24 22:17:32 +02:00
if self.party:
invoice_address = self.party.address_get(type='invoice')
res['invoice_address'] = invoice_address.id
res['invoice_address.rec_name'] = invoice_address.rec_name
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def on_change_with_currency_digits(self, name=None):
if self.currency:
return self.currency.digits
2008-07-28 20:16:08 +02:00
return 2
2012-09-24 22:17:32 +02:00
def on_change_with_currency_date(self, name=None):
Date = Pool().get('ir.date')
return self.invoice_date or Date.today()
2008-07-28 20:16:08 +02:00
2012-09-24 22:17:32 +02:00
def on_change_with_party_lang(self, name=None):
2012-11-23 18:00:20 +01:00
Config = Pool().get('ir.configuration')
2012-09-24 22:17:32 +02:00
if self.party:
if self.party.lang:
return self.party.lang.code
2012-11-23 18:00:20 +01:00
return Config.get_language()
2009-01-20 00:04:31 +01:00
2012-09-24 22:17:32 +02:00
@classmethod
def get_type_name(self, invoices, name):
type_names = {}
2008-04-14 14:37:04 +02:00
type2name = {}
2010-08-19 22:02:49 +02:00
for type, name in self.fields_get(fields_names=['type']
)['type']['selection']:
2008-04-14 14:37:04 +02:00
type2name[type] = name
2012-09-24 22:17:32 +02:00
for invoice in invoices:
type_names[invoice.id] = type2name[invoice.type]
return type_names
2008-04-14 14:37:04 +02:00
2012-09-24 22:17:32 +02:00
def on_change_lines(self):
return self._on_change_lines_taxes()
2008-08-04 19:17:48 +02:00
2012-09-24 22:17:32 +02:00
def on_change_taxes(self):
return self._on_change_lines_taxes()
2008-08-04 19:17:48 +02:00
2012-09-24 22:17:32 +02:00
def _on_change_lines_taxes(self):
pool = Pool()
Tax = pool.get('account.tax')
2013-02-23 17:40:40 +01:00
InvoiceTax = pool.get('account.invoice.tax')
Account = pool.get('account.account')
TaxCode = pool.get('account.tax.code')
2008-08-04 19:17:48 +02:00
res = {
'untaxed_amount': Decimal('0.0'),
'tax_amount': Decimal('0.0'),
'total_amount': Decimal('0.0'),
'taxes': {},
2012-09-24 22:17:32 +02:00
}
2008-08-04 19:17:48 +02:00
computed_taxes = {}
2012-09-24 22:17:32 +02:00
if self.lines:
context = self.get_tax_context()
for line in self.lines:
if (line.type or 'line') != 'line':
2008-08-04 19:17:48 +02:00
continue
2012-09-24 22:17:32 +02:00
res['untaxed_amount'] += line.amount or 0
2010-08-19 22:02:49 +02:00
with Transaction().set_context(**context):
2012-09-24 22:17:32 +02:00
taxes = Tax.compute(line.taxes,
line.unit_price or Decimal('0.0'),
line.quantity or 0.0)
2010-08-19 22:02:49 +02:00
for tax in taxes:
key, val = self._compute_tax(tax,
2012-09-24 22:17:32 +02:00
self.type or 'out_invoice')
2008-08-04 19:17:48 +02:00
if not key in computed_taxes:
computed_taxes[key] = val
else:
computed_taxes[key]['base'] += val['base']
computed_taxes[key]['amount'] += val['amount']
2012-09-24 22:17:32 +02:00
if self.currency:
2009-06-16 22:47:11 +02:00
for key in computed_taxes:
for field in ('base', 'amount'):
2012-09-24 22:17:32 +02:00
computed_taxes[key][field] = self.currency.round(
computed_taxes[key][field])
2008-08-04 19:17:48 +02:00
tax_keys = []
2012-09-24 22:17:32 +02:00
for tax in (self.taxes or []):
if tax.manual:
res['tax_amount'] += tax.amount or Decimal('0.0')
2008-08-04 19:17:48 +02:00
continue
key = (tax.base_code.id if tax.base_code else None, tax.base_sign,
tax.tax_code.id if tax.tax_code else None, tax.tax_sign,
tax.account.id if tax.account else None,
tax.tax.id if tax.tax else None)
if (key not in computed_taxes) or (key in tax_keys):
2008-08-04 19:17:48 +02:00
res['taxes'].setdefault('remove', [])
2012-09-24 22:17:32 +02:00
res['taxes']['remove'].append(tax.id)
2008-08-04 19:17:48 +02:00
continue
tax_keys.append(key)
2012-09-24 22:17:32 +02:00
if self.currency:
if not self.currency.is_zero(
2013-02-16 22:06:26 +01:00
computed_taxes[key]['base']
- (tax.base or Decimal('0.0'))):
2008-08-04 19:17:48 +02:00
res['tax_amount'] += computed_taxes[key]['amount']
res['taxes'].setdefault('update', [])
res['taxes']['update'].append({
2012-09-24 22:17:32 +02:00
'id': tax.id,
'amount': computed_taxes[key]['amount'],
'base': computed_taxes[key]['base'],
})
2008-08-04 19:17:48 +02:00
else:
2012-09-24 22:17:32 +02:00
res['tax_amount'] += tax.amount or Decimal('0.0')
2008-08-04 19:17:48 +02:00
else:
2012-09-24 22:17:32 +02:00
if (computed_taxes[key]['base'] - (tax.base or Decimal('0.0'))
!= Decimal('0.0')):
2008-08-04 19:17:48 +02:00
res['tax_amount'] += computed_taxes[key]['amount']
res['taxes'].setdefault('update', [])
res['taxes']['update'].append({
2012-09-24 22:17:32 +02:00
'id': tax.id,
2008-08-04 19:17:48 +02:00
'amount': computed_taxes[key]['amount'],
'base': computed_taxes[key]['base'],
})
else:
2012-09-24 22:17:32 +02:00
res['tax_amount'] += tax.amount or Decimal('0.0')
2008-08-04 19:17:48 +02:00
for key in computed_taxes:
if key not in tax_keys:
res['tax_amount'] += computed_taxes[key]['amount']
res['taxes'].setdefault('add', [])
2013-02-23 17:40:40 +01:00
value = InvoiceTax.default_get(InvoiceTax._fields.keys())
value.update(computed_taxes[key])
for field, Target in (
('account', Account),
('base_code', TaxCode),
('tax_code', TaxCode),
('tax', Tax),
):
if value.get(field):
value[field + '.rec_name'] = \
Target(value[field]).rec_name
res['taxes']['add'].append(value)
2012-09-24 22:17:32 +02:00
if self.currency:
res['untaxed_amount'] = self.currency.round(res['untaxed_amount'])
res['tax_amount'] = self.currency.round(res['tax_amount'])
2008-08-04 19:17:48 +02:00
res['total_amount'] = res['untaxed_amount'] + res['tax_amount']
2012-09-24 22:17:32 +02:00
if self.currency:
res['total_amount'] = self.currency.round(res['total_amount'])
2008-08-04 19:17:48 +02:00
return res
2012-09-24 22:17:32 +02:00
def get_untaxed_amount(self, name):
amount = _ZERO
for line in self.lines:
if line.type != 'line':
continue
amount += line.amount
return self.currency.round(amount)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def get_tax_amount(cls, invoices, name):
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
type_name = FIELDS[cls.tax_amount._type].sql_type(cls.tax_amount)[0]
red_sql, red_ids = reduce_ids('invoice', [i.id for i in invoices])
2013-02-16 22:06:26 +01:00
cursor.execute('SELECT invoice, '
'CAST(COALESCE(SUM(amount), 0) AS ' + type_name + ') '
'FROM account_invoice_tax '
'WHERE ' + red_sql + ' '
2010-01-20 15:25:32 +01:00
'GROUP BY invoice', red_ids)
2008-04-10 17:21:44 +02:00
for invoice_id, sum in cursor.fetchall():
2009-09-22 16:08:56 +02:00
# SQLite uses float for SUM
if not isinstance(sum, Decimal):
sum = Decimal(str(sum))
2008-04-10 17:21:44 +02:00
res[invoice_id] = sum
2012-09-24 22:17:32 +02:00
for invoice in invoices:
2008-04-10 17:21:44 +02:00
res.setdefault(invoice.id, Decimal('0.0'))
2012-09-24 22:17:32 +02:00
res[invoice.id] = invoice.currency.round(res[invoice.id])
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def get_total_amount(self, name):
return self.currency.round(self.untaxed_amount + self.tax_amount)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def get_reconciled(self, name):
if not self.lines_to_pay:
return False
for line in self.lines_to_pay:
if not line.reconciliation:
return False
return True
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def get_lines_to_pay(self, name):
lines = []
if self.move:
for line in self.move.lines:
if (line.account.id == self.account.id
and line.maturity_date):
lines.append(line)
lines.sort(key=operator.attrgetter('maturity_date'))
return [x.id for x in lines]
2012-09-24 22:17:32 +02:00
@classmethod
def get_amount_to_pay(cls, invoices, name):
pool = Pool()
Currency = pool.get('currency.currency')
Date = pool.get('ir.date')
2008-10-08 12:18:05 +02:00
2012-09-24 22:17:32 +02:00
computes = cls.search([
('id', 'in', [i.id for i in invoices]),
2012-12-23 22:28:41 +01:00
('state', '=', 'posted'),
2012-09-24 22:17:32 +02:00
])
2010-01-20 15:25:32 +01:00
2012-09-24 22:17:32 +02:00
today = Date.today()
res = dict((x.id, _ZERO) for x in invoices)
for invoice in computes:
2010-01-20 15:25:32 +01:00
amount = _ZERO
amount_currency = _ZERO
for line in invoice.lines_to_pay:
if line.reconciliation:
continue
2012-09-24 22:17:32 +02:00
if (name == 'amount_to_pay_today'
and line.maturity_date > today):
continue
2012-09-24 22:17:32 +02:00
if (line.second_currency
and line.second_currency == invoice.currency):
2010-01-20 15:25:32 +01:00
if line.debit - line.credit > _ZERO:
amount_currency += abs(line.amount_second_currency)
else:
amount_currency -= abs(line.amount_second_currency)
else:
amount += line.debit - line.credit
for line in invoice.payment_lines:
if line.reconciliation:
continue
2012-09-24 22:17:32 +02:00
if (line.second_currency
and line.second_currency == invoice.currency):
2010-01-20 15:25:32 +01:00
if line.debit - line.credit > _ZERO:
amount_currency += abs(line.amount_second_currency)
else:
amount_currency -= abs(line.amount_second_currency)
else:
amount += line.debit - line.credit
2008-10-03 17:12:23 +02:00
if invoice.type in ('in_invoice', 'out_credit_note'):
amount = - amount
amount_currency = - amount_currency
2010-01-20 15:25:32 +01:00
if amount != _ZERO:
with Transaction().set_context(date=invoice.currency_date):
2012-09-24 22:17:32 +02:00
amount_currency += Currency.compute(
invoice.company.currency, amount, invoice.currency)
2010-01-20 15:25:32 +01:00
if amount_currency < _ZERO:
amount_currency = _ZERO
res[invoice.id] = amount_currency
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
@classmethod
def search_total_amount(cls, name, clause):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Rule = pool.get('ir.rule')
Line = pool.get('account.invoice.line')
Tax = pool.get('account.invoice.tax')
type_name = FIELDS[cls.total_amount._type].sql_type(
cls.total_amount)[0]
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
invoice_query, invoice_val = Rule.domain_get('account.invoice')
2009-03-23 21:01:52 +01:00
cursor.execute('SELECT invoice FROM ('
'SELECT invoice, '
'COALESCE(SUM(quantity * unit_price), 0) '
'AS total_amount '
2012-09-24 22:17:32 +02:00
'FROM "' + Line._table + '" '
'JOIN "' + cls._table + '" ON '
'("' + cls._table + '".id = '
'"' + Line._table + '".invoice) '
'WHERE ' + invoice_query + ' '
'GROUP BY invoice '
'UNION '
'SELECT invoice, COALESCE(SUM(amount), 0) AS total_amount '
2012-09-24 22:17:32 +02:00
'FROM "' + Tax._table + '" '
'JOIN "' + cls._table + '" ON '
'("' + cls._table + '".id = '
'"' + Tax._table + '".invoice) '
'WHERE ' + invoice_query + ' '
'GROUP BY invoice '
') AS u '
'GROUP BY u.invoice '
'HAVING (CAST(SUM(u.total_amount) AS ' + type_name + ') '
+ clause[1] + ' %s)',
invoice_val + invoice_val + [str(clause[2])])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
2012-09-24 22:17:32 +02:00
@classmethod
def search_untaxed_amount(cls, name, clause):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Rule = pool.get('ir.rule')
Line = pool.get('account.invoice.line')
type_name = FIELDS[cls.untaxed_amount._type].sql_type(
cls.untaxed_amount)[0]
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
invoice_query, invoice_val = Rule.domain_get('account.invoice')
2012-09-24 22:17:32 +02:00
cursor.execute('SELECT invoice FROM "' + Line._table + '" '
'JOIN "' + cls._table + '" ON '
'("' + cls._table + '".id = '
'"' + Line._table + '".invoice) '
'WHERE ' + invoice_query + ' '
'GROUP BY invoice '
'HAVING (CAST(COALESCE(SUM(quantity * unit_price), 0) '
'AS ' + type_name + ') ' + clause[1] + ' %s)',
invoice_val + [str(clause[2])])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
2012-09-24 22:17:32 +02:00
@classmethod
def search_tax_amount(cls, name, clause):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Rule = pool.get('ir.rule')
Tax = pool.get('account.invoice.tax')
type_name = FIELDS[cls.tax_amount._type].sql_type(
cls.tax_amount)[0]
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
invoice_query, invoice_val = Rule.domain_get('account.invoice')
2012-09-24 22:17:32 +02:00
cursor.execute('SELECT invoice FROM "' + Tax._table + '" '
'JOIN "' + cls._table + '" ON '
'("' + cls._table + '".id = '
'"' + Tax._table + '".invoice) '
'WHERE ' + invoice_query + ' '
'GROUP BY invoice '
'HAVING (CAST(COALESCE(SUM(amount), 0) '
'AS ' + type_name + ') ' + clause[1] + ' %s)',
invoice_val + [str(clause[2])])
2009-03-23 21:01:52 +01:00
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
2012-09-24 22:17:32 +02:00
def get_tax_context(self):
context = {}
if self.party and self.party.lang:
context['language'] = self.party.lang.code
return context
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@staticmethod
def _compute_tax(invoice_tax, invoice_type):
2008-08-04 19:17:48 +02:00
val = {}
2012-09-24 22:17:32 +02:00
tax = invoice_tax['tax']
2008-08-04 19:17:48 +02:00
val['manual'] = False
2012-09-24 22:17:32 +02:00
val['description'] = tax.description
val['base'] = invoice_tax['base']
val['amount'] = invoice_tax['amount']
val['tax'] = tax.id if tax else None
2008-08-04 19:17:48 +02:00
if invoice_type in ('out_invoice', 'in_invoice'):
2012-09-24 22:17:32 +02:00
val['base_code'] = (tax.invoice_base_code.id
if tax.invoice_base_code else None)
val['base_sign'] = tax.invoice_base_sign
val['tax_code'] = (tax.invoice_tax_code.id
if tax.invoice_tax_code else None)
val['tax_sign'] = tax.invoice_tax_sign
val['account'] = (tax.invoice_account.id
if tax.invoice_account else None)
2008-08-04 19:17:48 +02:00
else:
2012-09-24 22:17:32 +02:00
val['base_code'] = (tax.credit_note_base_code.id
if tax.credit_note_base_code else None)
val['base_sign'] = tax.credit_note_base_sign
val['tax_code'] = (tax.credit_note_tax_code.id
if tax.credit_note_tax_code else None)
val['tax_sign'] = tax.credit_note_tax_sign
val['account'] = (tax.credit_note_account.id
if tax.credit_note_account else None)
2008-08-04 19:17:48 +02:00
key = (val['base_code'], val['base_sign'],
2013-02-16 22:06:26 +01:00
val['tax_code'], val['tax_sign'],
val['account'], val['tax'])
2008-08-04 19:17:48 +02:00
return key, val
2012-09-24 22:17:32 +02:00
def _compute_taxes(self):
Tax = Pool().get('account.tax')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
context = self.get_tax_context()
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
for line in self.lines:
# Don't round on each line to handle rounding error
2008-04-10 17:21:44 +02:00
if line.type != 'line':
continue
2010-08-19 22:02:49 +02:00
with Transaction().set_context(**context):
2012-09-24 22:17:32 +02:00
taxes = Tax.compute(line.taxes, line.unit_price,
2010-08-19 22:02:49 +02:00
line.quantity)
for tax in taxes:
2012-09-24 22:17:32 +02:00
key, val = self._compute_tax(tax, self.type)
val['invoice'] = self.id
2008-04-10 17:21:44 +02:00
if not key in res:
res[key] = val
else:
res[key]['base'] += val['base']
res[key]['amount'] += val['amount']
2009-06-16 22:47:11 +02:00
for key in res:
for field in ('base', 'amount'):
2012-09-24 22:17:32 +02:00
res[key][field] = self.currency.round(res[key][field])
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
@classmethod
def update_taxes(cls, invoices, exception=False):
Tax = Pool().get('account.invoice.tax')
for invoice in invoices:
2012-12-23 22:28:41 +01:00
if invoice.state in ('posted', 'paid', 'cancel'):
continue
2012-09-24 22:17:32 +02:00
computed_taxes = invoice._compute_taxes()
2008-04-10 17:21:44 +02:00
if not invoice.taxes:
2013-01-06 22:18:13 +01:00
Tax.create([tax for tax in computed_taxes.values()])
2008-04-10 17:21:44 +02:00
else:
tax_keys = []
for tax in invoice.taxes:
if tax.manual:
continue
2012-09-24 22:17:32 +02:00
base_code_id = tax.base_code.id if tax.base_code else None
tax_code_id = tax.tax_code.id if tax.tax_code else None
tax_id = tax.tax.id if tax.tax else None
key = (base_code_id, tax.base_sign,
2013-02-16 22:06:26 +01:00
tax_code_id, tax.tax_sign,
tax.account.id, tax_id)
if (not key in computed_taxes) or (key in tax_keys):
2008-04-10 17:21:44 +02:00
if exception:
cls.raise_user_error('missing_tax_line',
(invoice.rec_name,))
2012-09-24 22:17:32 +02:00
Tax.delete([tax])
2008-04-10 17:21:44 +02:00
continue
tax_keys.append(key)
2012-09-24 22:17:32 +02:00
if not invoice.currency.is_zero(
2008-04-10 17:21:44 +02:00
computed_taxes[key]['base'] - tax.base):
if exception:
cls.raise_user_error('diff_tax_line',
(invoice.rec_name,))
2012-09-24 22:17:32 +02:00
Tax.write([tax], computed_taxes[key])
2008-04-10 17:21:44 +02:00
for key in computed_taxes:
if not key in tax_keys:
if exception:
cls.raise_user_error('missing_tax_line2',
(invoice.rec_name,))
2013-01-06 22:18:13 +01:00
Tax.create([computed_taxes[key]])
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def _get_move_line_invoice_line(self):
2008-04-10 17:21:44 +02:00
'''
Return list of move line values for each invoice lines
'''
res = []
2012-09-24 22:17:32 +02:00
for line in self.lines:
val = line.get_move_line()
2008-04-10 17:21:44 +02:00
if val:
res.extend(val)
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def _get_move_line_invoice_tax(self):
2008-04-10 17:21:44 +02:00
'''
Return list of move line values for each invoice taxes
'''
res = []
2012-09-24 22:17:32 +02:00
for tax in self.taxes:
val = tax.get_move_line()
if val:
res.extend(val)
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def _get_move_line(self, date, amount):
2008-04-10 17:21:44 +02:00
'''
Return move line
'''
2012-09-24 22:17:32 +02:00
Currency = Pool().get('currency.currency')
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
if self.currency.id != self.company.currency.id:
with Transaction().set_context(date=self.currency_date):
res['amount_second_currency'] = Currency.compute(
2013-01-19 17:11:54 +01:00
self.company.currency, amount, self.currency)
res['amount_second_currency'] = abs(res['amount_second_currency'])
2012-09-24 22:17:32 +02:00
res['second_currency'] = self.currency.id
2008-04-10 17:21:44 +02:00
else:
res['amount_second_currency'] = Decimal('0.0')
2012-03-27 10:43:07 +02:00
res['second_currency'] = None
2008-04-10 17:21:44 +02:00
if amount >= Decimal('0.0'):
res['debit'] = Decimal('0.0')
res['credit'] = amount
else:
res['debit'] = - amount
res['credit'] = Decimal('0.0')
2012-09-24 22:17:32 +02:00
res['account'] = self.account.id
2008-04-10 17:21:44 +02:00
res['maturity_date'] = date
2012-09-29 15:34:34 +02:00
res['description'] = self.description
2012-09-24 22:17:32 +02:00
res['party'] = self.party.id
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def create_move(self):
2012-03-25 00:17:27 +01:00
'''
2012-09-24 22:17:32 +02:00
Create account move for the invoice and return the created move
2012-03-25 00:17:27 +01:00
'''
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Move = pool.get('account.move')
Period = pool.get('account.period')
Date = pool.get('ir.date')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
if self.move:
2012-12-23 22:28:41 +01:00
return self.move
2012-09-24 22:17:32 +02:00
self.update_taxes([self], exception=True)
move_lines = self._get_move_line_invoice_line()
move_lines += self._get_move_line_invoice_tax()
2008-04-10 17:21:44 +02:00
total = Decimal('0.0')
total_currency = Decimal('0.0')
for line in move_lines:
total += line['debit'] - line['credit']
total_currency += line['amount_second_currency']
2012-09-24 22:17:32 +02:00
term_lines = self.payment_term.compute(total, self.company.currency,
self.invoice_date)
2008-04-10 17:21:44 +02:00
remainder_total_currency = total_currency
if not term_lines:
2012-09-24 22:17:32 +02:00
term_lines = [(Date.today(), total)]
2008-04-10 17:21:44 +02:00
for date, amount in term_lines:
2012-09-24 22:17:32 +02:00
val = self._get_move_line(date, amount)
2008-04-10 17:21:44 +02:00
remainder_total_currency -= val['amount_second_currency']
move_lines.append(val)
2012-09-24 22:17:32 +02:00
if not self.currency.is_zero(remainder_total_currency):
2012-04-03 20:06:19 +02:00
move_lines[-1]['amount_second_currency'] += \
remainder_total_currency
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
accounting_date = self.accounting_date or self.invoice_date
period_id = Period.find(self.company.id, date=accounting_date)
2008-04-10 17:21:44 +02:00
2013-01-06 22:18:13 +01:00
move, = Move.create([{
'journal': self.journal.id,
'period': period_id,
'date': accounting_date,
'origin': str(self),
'lines': [('create', move_lines)],
}])
2012-09-24 22:17:32 +02:00
self.write([self], {
'move': move.id,
})
return move
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def set_number(self):
2012-03-25 00:17:27 +01:00
'''
Set number to the invoice
'''
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Period = pool.get('account.period')
Sequence = pool.get('ir.sequence.strict')
Date = pool.get('ir.date')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
if self.number:
2012-03-25 00:17:27 +01:00
return
2008-04-10 17:21:44 +02:00
2009-04-03 14:06:02 +02:00
test_state = True
2012-09-24 22:17:32 +02:00
if self.type in ('in_invoice', 'in_credit_note'):
2009-04-03 14:06:02 +02:00
test_state = False
accounting_date = self.accounting_date or self.invoice_date
2012-09-24 22:17:32 +02:00
period_id = Period.find(self.company.id,
date=accounting_date, test_state=test_state)
2012-09-24 22:17:32 +02:00
period = Period(period_id)
2013-05-23 23:01:09 +02:00
sequence = period.get_invoice_sequence(self.type)
if not sequence:
self.raise_user_error('no_invoice_sequence', {
2013-03-26 11:38:41 +00:00
'invoice': self.rec_name,
'period': period.rec_name,
})
2010-08-19 22:02:49 +02:00
with Transaction().set_context(
2012-09-24 22:17:32 +02:00
date=self.invoice_date or Date.today()):
2013-05-23 23:01:09 +02:00
number = Sequence.get_id(sequence.id)
2010-08-19 22:02:49 +02:00
vals = {'number': number}
2012-12-23 22:28:41 +01:00
if (not self.invoice_date
and self.type in ('out_invoice', 'out_credit_note')):
2010-08-19 22:02:49 +02:00
vals['invoice_date'] = Transaction().context['date']
2012-09-24 22:17:32 +02:00
self.write([self], vals)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def check_modify(cls, invoices):
2008-04-10 17:21:44 +02:00
'''
Check if the invoices can be modified
'''
2012-09-24 22:17:32 +02:00
for invoice in invoices:
2012-12-23 22:28:41 +01:00
if (invoice.state in ('posted', 'paid')
or (invoice.state == 'cancel'
and invoice.cancel_move)):
cls.raise_user_error('modify_invoice', (invoice.rec_name,))
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def get_rec_name(self, name):
return (self.number or unicode(self.id)
+ (self.reference and (' ' + self.reference) or '')
+ ' ' + self.party.rec_name)
2008-04-14 11:16:24 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def search_rec_name(cls, name, clause):
invoices = cls.search(['OR',
('number',) + clause[1:],
('reference',) + clause[1:],
], order=[])
if invoices:
return [('id', 'in', [i.id for i in invoices])]
return [('party',) + clause[1:]]
2008-04-14 11:16:24 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def delete(cls, invoices):
cls.check_modify(invoices)
# Cancel before delete
2012-09-24 22:17:32 +02:00
cls.cancel(invoices)
for invoice in invoices:
if invoice.state != 'cancel':
cls.raise_user_error('delete_cancel', (invoice.rec_name,))
2012-12-23 22:28:41 +01:00
if invoice.number:
cls.raise_user_error('delete_numbered', (invoice.rec_name,))
2012-09-24 22:17:32 +02:00
super(Invoice, cls).delete(invoices)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def write(cls, invoices, vals):
2008-04-10 17:21:44 +02:00
keys = vals.keys()
2012-09-24 22:17:32 +02:00
for key in cls._check_modify_exclude:
2008-04-10 17:21:44 +02:00
if key in keys:
keys.remove(key)
if len(keys):
2012-09-24 22:17:32 +02:00
cls.check_modify(invoices)
update_tax = [i for i in invoices if i.state == 'draft']
super(Invoice, cls).write(invoices, vals)
if update_tax:
cls.update_taxes(update_tax)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def copy(cls, invoices, default=None):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Line = pool.get('account.invoice.line')
Tax = pool.get('account.invoice.tax')
2008-12-05 13:25:54 +01:00
2008-04-16 19:21:05 +02:00
if default is None:
default = {}
default = default.copy()
default['state'] = 'draft'
2012-03-27 10:43:07 +02:00
default['number'] = None
default['move'] = None
2012-12-23 22:28:41 +01:00
default['cancel_move'] = None
2012-03-27 10:43:07 +02:00
default['invoice_report_cache'] = None
default['invoice_report_format'] = None
default['payment_lines'] = None
default['lines'] = None
default['taxes'] = None
default.setdefault('invoice_date', None)
default.setdefault('accounting_date', None)
default['lines_to_pay'] = None
2013-03-23 15:37:44 +01:00
default.setdefault('origin', None)
2008-04-16 19:21:05 +02:00
2012-09-24 22:17:32 +02:00
new_invoices = []
for invoice in invoices:
new_invoice, = super(Invoice, cls).copy([invoice], default=default)
Line.copy(invoice.lines, default={
'invoice': new_invoice.id,
})
Tax.copy(invoice.taxes, default={
'invoice': new_invoice.id,
})
new_invoices.append(new_invoice)
return new_invoices
2008-12-05 13:25:54 +01:00
@classmethod
def validate(cls, invoices):
super(Invoice, cls).validate(invoices)
for invoice in invoices:
invoice.check_account()
invoice.check_same_account()
invoice.check_cancel_move()
2008-04-16 19:21:05 +02:00
def check_account(self):
if self.account.company != self.company:
self.raise_user_error('check_account', {
2013-03-25 18:49:29 +00:00
'invoice': self.rec_name,
'invoice_company': self.company.rec_name,
'account': self.account.rec_name,
'account_company': self.account.company.rec_name,
})
def check_same_account(self):
2012-09-24 22:17:32 +02:00
for line in self.lines:
if (line.type == 'line'
and line.account == self.account):
self.raise_user_error('same_account_on_line', {
2013-03-25 18:49:29 +00:00
'invoice': self.rec_name,
'account': self.account.rec_name,
'line': line.rec_name,
})
2008-04-10 17:21:44 +02:00
2012-12-23 22:28:41 +01:00
def check_cancel_move(self):
if (self.type in ('out_invoice', 'out_credit_note')
and self.cancel_move):
self.raise_user_error('customer_invoice_cancel_move',
self.rec_name)
2012-12-23 22:28:41 +01:00
2012-09-24 22:17:32 +02:00
def get_reconcile_lines_for_amount(self, amount, exclude_lines=None):
'''
2012-09-24 22:17:32 +02:00
Return list of lines and the remainder to make reconciliation.
'''
2012-09-24 22:17:32 +02:00
if exclude_lines is None:
exclude_lines = []
payment_amount = Decimal('0.0')
2012-09-24 22:17:32 +02:00
remainder = self.total_amount
lines = []
payment_lines = []
2012-09-24 22:17:32 +02:00
for line in self.payment_lines:
if line.reconciliation:
continue
payment_amount += line.debit - line.credit
2012-09-24 22:17:32 +02:00
payment_lines.append(line)
2012-09-24 22:17:32 +02:00
if self.type in ('out_invoice', 'in_credit_note'):
amount = - abs(amount)
else:
amount = abs(amount)
2012-09-24 22:17:32 +02:00
for line in self.lines_to_pay:
if line.reconciliation:
continue
2012-09-24 22:17:32 +02:00
if line in exclude_lines:
continue
test_amount = amount + (line.debit - line.credit)
2012-09-24 22:17:32 +02:00
if self.currency.is_zero(test_amount):
return ([line], Decimal('0.0'))
if abs(test_amount) < abs(remainder):
2012-09-24 22:17:32 +02:00
lines = [line]
remainder = test_amount
2012-04-03 20:06:19 +02:00
test_amount = (amount + payment_amount)
test_amount += (line.debit - line.credit)
2012-09-24 22:17:32 +02:00
if self.currency.is_zero(test_amount):
return ([line] + payment_lines, Decimal('0.0'))
if abs(test_amount) < abs(remainder):
2012-09-24 22:17:32 +02:00
lines = [line] + payment_lines
remainder = test_amount
2012-09-24 22:17:32 +02:00
exclude_lines2 = exclude_lines[:]
exclude_lines2.append(line)
lines2, remainder2 = self.get_reconcile_lines_for_amount(
(amount + (line.debit - line.credit)),
exclude_lines=exclude_lines2)
if remainder2 == Decimal('0.0'):
lines2.append(line)
return lines2, remainder2
if abs(remainder2) < abs(remainder):
lines2.append(line)
lines, remainder = lines2, remainder2
return (lines, remainder)
2012-09-24 22:17:32 +02:00
def pay_invoice(self, amount, journal, date, description,
2012-03-27 10:43:07 +02:00
amount_second_currency=None, second_currency=None):
2009-03-18 17:55:15 +01:00
'''
2012-09-24 22:17:32 +02:00
Adds a payment of amount to an invoice using the journal, date and
description.
Returns the payment line.
2009-03-18 17:55:15 +01:00
'''
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Move = pool.get('account.move')
Period = pool.get('account.period')
lines = []
2012-09-24 22:17:32 +02:00
if self.type in ('out_invoice', 'in_credit_note'):
2012-10-10 17:54:13 +02:00
if self.account == journal.debit_account:
2013-03-25 09:47:43 +00:00
self.raise_user_error('same_debit_account', {
'journal': journal.rec_name,
2013-03-25 18:49:29 +00:00
'invoice': self.rec_name,
2013-03-25 09:47:43 +00:00
})
2012-10-10 17:54:13 +02:00
if not journal.debit_account:
2013-03-25 09:47:43 +00:00
self.raise_user_error('missing_debit_account',
(journal.rec_name,))
2012-10-10 17:54:13 +02:00
lines.append({
2012-09-29 15:34:34 +02:00
'description': description,
2012-09-24 22:17:32 +02:00
'account': self.account.id,
'party': self.party.id,
'debit': Decimal('0.0'),
'credit': amount,
'amount_second_currency': amount_second_currency,
'second_currency': second_currency,
})
lines.append({
2012-09-29 15:34:34 +02:00
'description': description,
2012-09-24 22:17:32 +02:00
'account': journal.debit_account.id,
'party': self.party.id,
'debit': amount,
'credit': Decimal('0.0'),
'amount_second_currency': amount_second_currency,
'second_currency': second_currency,
})
else:
2012-10-10 17:54:13 +02:00
if self.account == journal.credit_account:
self.raise_user_error('same_credit_account', {
'journal': journal.rec_name,
2013-03-25 18:49:29 +00:00
'invoice': self.rec_name,
})
2012-10-10 17:54:13 +02:00
if not journal.credit_account:
self.raise_user_error('missing_credit_account',
(journal.rec_name,))
2012-10-10 17:54:13 +02:00
lines.append({
2012-09-29 15:34:34 +02:00
'description': description,
2012-09-24 22:17:32 +02:00
'account': self.account.id,
'party': self.party.id,
'debit': amount,
'credit': Decimal('0.0'),
'amount_second_currency': amount_second_currency,
'second_currency': second_currency,
})
lines.append({
2012-09-29 15:34:34 +02:00
'description': description,
2012-09-24 22:17:32 +02:00
'account': journal.credit_account.id,
'party': self.party.id,
'debit': Decimal('0.0'),
'credit': amount,
'amount_second_currency': amount_second_currency,
'second_currency': second_currency,
})
2012-09-24 22:17:32 +02:00
period_id = Period.find(self.company.id, date=date)
2013-01-06 22:18:13 +01:00
move, = Move.create([{
'journal': journal.id,
'period': period_id,
'date': date,
'lines': [('create', lines)],
}])
2012-09-24 22:17:32 +02:00
Move.post([move])
for line in move.lines:
2012-09-24 22:17:32 +02:00
if line.account == self.account:
self.write([self], {
'payment_lines': [('add', [line.id])],
})
return line
raise Exception('Missing account')
2012-09-24 22:17:32 +02:00
def print_invoice(self):
'''
Generate invoice report and store it in invoice_report field.
'''
2012-09-24 22:17:32 +02:00
if self.invoice_report_cache:
2012-03-25 00:17:27 +01:00
return
2012-09-24 22:17:32 +02:00
InvoiceReport = Pool().get('account.invoice', type='report')
InvoiceReport.execute([self.id], {})
2012-09-24 22:17:32 +02:00
def _credit(self):
2008-04-16 23:56:33 +02:00
'''
2008-10-03 17:12:23 +02:00
Return values to credit invoice.
2008-04-16 23:56:33 +02:00
'''
res = {}
2012-09-24 22:17:32 +02:00
if self.type == 'out_invoice':
2008-10-03 17:12:23 +02:00
res['type'] = 'out_credit_note'
2012-09-24 22:17:32 +02:00
elif self.type == 'in_invoice':
2008-10-03 17:12:23 +02:00
res['type'] = 'in_credit_note'
2012-09-24 22:17:32 +02:00
elif self.type == 'out_credit_note':
2008-04-16 23:56:33 +02:00
res['type'] = 'out_invoice'
2012-09-24 22:17:32 +02:00
elif self.type == 'in_credit_note':
2008-04-16 23:56:33 +02:00
res['type'] = 'in_invoice'
for field in ('description', 'comment'):
2012-09-24 22:17:32 +02:00
res[field] = getattr(self, field)
2008-04-16 23:56:33 +02:00
2012-09-24 22:17:32 +02:00
res['reference'] = self.number or self.reference
2008-11-14 18:59:02 +01:00
for field in ('company', 'party', 'invoice_address', 'currency',
'journal', 'account', 'payment_term'):
2012-09-24 22:17:32 +02:00
res[field] = getattr(self, field).id
2008-04-16 23:56:33 +02:00
res['lines'] = []
2013-01-06 22:18:13 +01:00
if self.lines:
2013-02-16 22:06:26 +01:00
res['lines'].append(('create',
[line._credit() for line in self.lines]))
2008-04-16 23:56:33 +02:00
res['taxes'] = []
2013-01-06 22:18:13 +01:00
to_create = [tax._credit() for tax in self.taxes if tax.manual]
if to_create:
res['taxes'].append(('create', to_create))
2008-04-16 23:56:33 +02:00
return res
2012-09-24 22:17:32 +02:00
@classmethod
def credit(cls, invoices, refund=False):
2008-04-16 23:56:33 +02:00
'''
2008-10-03 17:12:23 +02:00
Credit invoices and return ids of new invoices.
2012-09-24 22:17:32 +02:00
Return the list of new invoice
2008-04-16 23:56:33 +02:00
'''
2012-09-24 22:17:32 +02:00
MoveLine = Pool().get('account.move.line')
2012-09-24 22:17:32 +02:00
new_invoices = []
for invoice in invoices:
2013-01-06 22:18:13 +01:00
new_invoice, = cls.create([invoice._credit()])
2012-09-24 22:17:32 +02:00
new_invoices.append(new_invoice)
if refund:
2012-12-23 22:28:41 +01:00
cls.post([new_invoice])
if new_invoice.state == 'posted':
2012-09-24 22:17:32 +02:00
MoveLine.reconcile([l for l in invoice.lines_to_pay
if not l.reconciliation] +
[l for l in new_invoice.lines_to_pay
if not l.reconciliation])
2013-02-23 17:42:07 +01:00
cls.update_taxes(new_invoices)
2012-09-24 22:17:32 +02:00
return new_invoices
2008-04-16 23:56:33 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
2012-03-25 00:17:27 +01:00
@ModelView.button
@Workflow.transition('draft')
2012-09-24 22:17:32 +02:00
def draft(cls, invoices):
2012-12-23 22:28:41 +01:00
Move = Pool().get('account.move')
moves = []
for invoice in invoices:
if invoice.move:
moves.append(invoice.move)
if moves:
with Transaction().set_user(0, set_context=True):
Move.delete(moves)
2012-09-24 22:17:32 +02:00
@classmethod
2012-03-25 00:17:27 +01:00
@ModelView.button
2012-12-23 22:28:41 +01:00
@Workflow.transition('validated')
def validate_invoice(cls, invoices):
2012-12-23 22:28:41 +01:00
for invoice in invoices:
if invoice.type in ('in_invoice', 'in_credit_note'):
invoice.set_number()
invoice.create_move()
2012-09-24 22:17:32 +02:00
@classmethod
2012-03-25 00:17:27 +01:00
@ModelView.button
2012-12-23 22:28:41 +01:00
@Workflow.transition('posted')
def post(cls, invoices):
Move = Pool().get('account.move')
moves = []
2012-09-24 22:17:32 +02:00
for invoice in invoices:
invoice.set_number()
2012-12-23 22:28:41 +01:00
moves.append(invoice.create_move())
Move.post(moves)
cls.write(invoices, {
'state': 'posted',
})
for invoice in invoices:
2012-12-23 22:28:41 +01:00
if invoice.type in ('out_invoice', 'out_credit_note'):
invoice.print_invoice()
2012-09-24 22:17:32 +02:00
@classmethod
2012-09-10 15:27:42 +02:00
@ModelView.button_action('account_invoice.wizard_pay')
2012-09-24 22:17:32 +02:00
def pay(cls, invoices):
2012-09-10 15:27:42 +02:00
pass
2012-09-24 22:17:32 +02:00
@classmethod
def process(cls, invoices):
2012-03-25 00:17:27 +01:00
paid = []
2012-12-23 22:28:41 +01:00
posted = []
2012-09-24 22:17:32 +02:00
for invoice in invoices:
2012-12-23 22:28:41 +01:00
if invoice.state not in ('posted', 'paid'):
2012-03-25 00:17:27 +01:00
continue
if invoice.reconciled:
2012-09-24 22:17:32 +02:00
paid.append(invoice)
2012-03-25 00:17:27 +01:00
else:
2012-12-23 22:28:41 +01:00
posted.append(invoice)
2012-09-24 22:17:32 +02:00
cls.paid(paid)
2012-12-23 22:28:41 +01:00
cls.post(posted)
2012-09-24 22:17:32 +02:00
@classmethod
2012-03-25 00:17:27 +01:00
@Workflow.transition('paid')
2012-09-24 22:17:32 +02:00
def paid(cls, invoices):
2012-03-25 00:17:27 +01:00
pass
2012-12-23 22:28:41 +01:00
def get_cancel_move(self):
pool = Pool()
Date = pool.get('ir.date')
Move = pool.get('account.move')
Period = pool.get('account.period')
move = self.move
default = {}
if move.period.state == 'close':
self.raise_user_warning('%s.get_cancel_move' % self,
'period_cancel_move', self.rec_name)
date = Date.today()
period_id = Period.find(self.company.id, date=date)
default.update({
'date': date,
'period': period_id,
})
cancel_move, = Move.copy([move], default=default)
for line in cancel_move.lines:
line.debit *= -1
line.credit *= -1
line.save()
for tax_line in line.tax_lines:
tax_line.amount *= -1
tax_line.save()
return cancel_move
2012-09-24 22:17:32 +02:00
@classmethod
2012-03-25 00:17:27 +01:00
@ModelView.button
@Workflow.transition('cancel')
2012-09-24 22:17:32 +02:00
def cancel(cls, invoices):
2012-12-23 22:28:41 +01:00
Move = Pool().get('account.move')
cancel_moves = []
delete_moves = []
for invoice in invoices:
if invoice.move:
if invoice.move.state == 'draft':
delete_moves.append(invoice.move)
elif not invoice.cancel_move:
invoice.cancel_move = invoice.get_cancel_move()
invoice.save()
cancel_moves.append(invoice.cancel_move)
if delete_moves:
with Transaction().set_user(0, set_context=True):
Move.delete(delete_moves)
if cancel_moves:
Move.post(cancel_moves)
2008-04-10 17:21:44 +02:00
2009-03-05 14:51:35 +01:00
class InvoicePaymentLine(ModelSQL):
'Invoice - Payment Line'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice-account.move.line'
2009-03-05 14:51:35 +01:00
invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
2012-03-05 10:52:57 +01:00
select=True, required=True)
2009-03-05 14:51:35 +01:00
line = fields.Many2One('account.move.line', 'Payment Line',
2012-03-05 10:52:57 +01:00
ondelete='CASCADE', select=True, required=True)
2009-03-05 14:51:35 +01:00
2009-02-28 16:57:26 +01:00
class InvoiceLine(ModelSQL, ModelView):
2008-04-10 17:21:44 +02:00
'Invoice Line'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.line'
2008-04-10 17:21:44 +02:00
_rec_name = 'description'
invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
2012-03-05 10:52:57 +01:00
select=True, states={
2011-08-21 11:20:30 +02:00
'required': (~Eval('invoice_type') & Eval('party')
& Eval('currency') & Eval('company')),
'invisible': Bool(Eval('context', {}).get('standalone')),
2011-08-04 16:57:16 +02:00
},
depends=['invoice_type', 'party', 'company', 'currency'])
2012-03-05 10:52:57 +01:00
invoice_type = fields.Selection(_TYPE, 'Invoice Type', select=True,
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'readonly': Eval('context', {}).get('type') | Eval('type'),
'required': ~Eval('invoice'),
2011-08-04 16:57:16 +02:00
},
depends=['invoice', 'type'])
2012-03-05 10:52:57 +01:00
party = fields.Many2One('party.party', 'Party', select=True,
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'required': ~Eval('invoice'),
2011-08-04 16:57:16 +02:00
},
depends=['invoice'])
2010-03-11 16:45:42 +01:00
party_lang = fields.Function(fields.Char('Party Language',
2012-09-24 22:17:32 +02:00
on_change_with=['party']), 'on_change_with_party_lang')
2010-01-20 10:57:08 +01:00
currency = fields.Many2One('currency.currency', 'Currency',
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'required': ~Eval('invoice'),
2011-08-04 16:57:16 +02:00
},
depends=['invoice'])
2010-03-11 16:45:42 +01:00
currency_digits = fields.Function(fields.Integer('Currency Digits',
2012-09-24 22:17:32 +02:00
on_change_with=['currency']), 'on_change_with_currency_digits')
2010-01-20 10:57:08 +01:00
company = fields.Many2One('company.company', 'Company',
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'required': ~Eval('invoice'),
2013-02-16 22:06:26 +01:00
},
domain=[
2011-08-21 11:20:30 +02:00
('id', If(Eval('context', {}).contains('company'), '=', '!='),
Eval('context', {}).get('company', 0)),
2011-08-04 16:57:16 +02:00
],
2012-03-14 09:24:43 +01:00
depends=['invoice'], select=True)
2008-11-28 18:29:40 +01:00
2012-05-14 15:36:16 +02:00
sequence = fields.Integer('Sequence',
order_field='(%(table)s.sequence IS NULL) %(order)s, '
'%(table)s.sequence %(order)s',
2011-08-21 11:20:30 +02:00
states={
'invisible': Bool(Eval('context', {}).get('standalone')),
2010-01-20 10:57:08 +01:00
})
2008-04-10 17:21:44 +02:00
type = fields.Selection([
('line', 'Line'),
('subtotal', 'Subtotal'),
('title', 'Title'),
2008-05-21 10:54:56 +02:00
('comment', 'Comment'),
2012-03-05 10:52:57 +01:00
], 'Type', select=True, required=True, states={
2011-08-21 11:20:30 +02:00
'invisible': Bool(Eval('context', {}).get('standalone')),
2008-11-28 18:29:40 +01:00
})
2008-04-10 17:21:44 +02:00
quantity = fields.Float('Quantity',
2011-08-04 16:57:16 +02:00
digits=(16, Eval('unit_digits', 2)),
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
2011-08-04 16:57:16 +02:00
},
depends=['type', 'unit_digits'])
2008-04-10 17:21:44 +02:00
unit = fields.Many2One('product.uom', 'Unit',
2011-08-04 16:57:16 +02:00
states={
'required': Bool(Eval('product')),
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
2011-08-04 16:57:16 +02:00
},
domain=[
2012-03-25 15:41:01 +02:00
If(Bool(Eval('product_uom_category')),
('category', '=', Eval('product_uom_category')),
('category', '!=', -1)),
2010-01-20 10:57:08 +01:00
],
2012-03-25 15:41:01 +02:00
depends=['product', 'type', 'product_uom_category'])
2010-03-11 16:45:42 +01:00
unit_digits = fields.Function(fields.Integer('Unit Digits',
2012-09-24 22:17:32 +02:00
on_change_with=['unit']), 'on_change_with_unit_digits')
2008-04-10 17:21:44 +02:00
product = fields.Many2One('product.product', 'Product',
2010-03-11 16:45:42 +01:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
2011-08-04 16:57:16 +02:00
},
on_change=['product', 'unit', 'quantity', 'description',
'_parent_invoice.type', '_parent_invoice.party',
'_parent_invoice.currency', '_parent_invoice.currency_date',
'party', 'currency', 'invoice'],
depends=['type'])
2012-03-25 15:41:01 +02:00
product_uom_category = fields.Function(
fields.Many2One('product.uom.category', 'Product Uom Category',
on_change_with=['product']),
2012-09-24 22:17:32 +02:00
'on_change_with_product_uom_category')
2011-08-04 16:57:16 +02:00
account = fields.Many2One('account.account', 'Account',
domain=[
2011-08-21 11:20:30 +02:00
('company', '=', Eval('_parent_invoice', {}).get('company',
Eval('context', {}).get('company', 0))),
If(Bool(Eval('_parent_invoice')),
If(Eval('_parent_invoice', {}).get('type').in_(['out_invoice',
'out_credit_note']),
('kind', '=', 'revenue'),
('kind', '=', 'expense')),
If(Eval('invoice_type').in_(['out_invoice',
'out_credit_note']),
('kind', '=', 'revenue'),
('kind', '=', 'expense')))
2011-08-04 16:57:16 +02:00
],
2012-09-06 15:12:40 +02:00
on_change=['account', 'product', '_parent_invoice.party',
'_parent_invoice.type'],
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
2011-08-04 16:57:16 +02:00
},
depends=['type', 'invoice_type'])
2011-08-04 16:57:16 +02:00
unit_price = fields.Numeric('Unit Price', digits=(16, 4),
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
2011-08-04 16:57:16 +02:00
},
depends=['type'])
amount = fields.Function(fields.Numeric('Amount',
2011-08-21 11:20:30 +02:00
digits=(16, Eval('_parent_invoice', {}).get('currency_digits',
2011-08-04 16:57:16 +02:00
Eval('currency_digits', 2))),
states={
2011-08-21 11:20:30 +02:00
'invisible': ~Eval('type').in_(['line', 'subtotal']),
2011-08-04 16:57:16 +02:00
},
on_change_with=['type', 'quantity', 'unit_price',
'_parent_invoice.currency', 'currency'],
depends=['type', 'currency_digits']), 'get_amount')
2009-01-10 16:39:19 +01:00
description = fields.Text('Description', size=None, required=True)
2009-01-20 00:04:31 +01:00
note = fields.Text('Note')
2009-03-05 14:51:35 +01:00
taxes = fields.Many2Many('account.invoice.line-account.tax',
'line', 'tax', 'Taxes',
domain=[('parent', '=', None), ['OR',
('group', '=', None),
('group.kind', 'in',
If(Bool(Eval('_parent_invoice')),
If(Eval('_parent_invoice', {}).get('type').in_(
['out_invoice', 'out_credit_note']),
['sale', 'both'],
['purchase', 'both']),
If(Eval('invoice_type').in_(
['out_invoice', 'out_credit_note']),
['sale', 'both'],
['purchase', 'both']))
)],
],
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'line',
2011-08-04 16:57:16 +02:00
},
2012-12-06 19:30:11 +01:00
depends=['type', 'invoice_type'])
2010-03-11 16:45:42 +01:00
invoice_taxes = fields.Function(fields.One2Many('account.invoice.tax',
None, 'Invoice Taxes'), 'get_invoice_taxes')
2013-03-23 15:37:44 +01:00
origin = fields.Reference('Origin', selection='get_origin', select=True,
states={
2013-03-27 22:42:57 +01:00
'readonly': Eval('_parent_invoice', {}
).get('state') != 'draft',
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(InvoiceLine, cls).__setup__()
cls._sql_constraints += [
2008-04-10 17:21:44 +02:00
('type_account',
2013-02-16 22:06:26 +01:00
'CHECK((type = \'line\' AND account IS NOT NULL) '
'OR (type != \'line\'))',
'Line with "line" type must have an account.'),
2008-11-28 18:29:40 +01:00
('type_invoice',
2013-02-16 22:06:26 +01:00
'CHECK((type != \'line\' AND invoice IS NOT NULL) '
'OR (type = \'line\'))',
'Line without "line" type must have an invoice.'),
2012-09-24 22:17:32 +02:00
]
cls._order.insert(0, ('sequence', 'ASC'))
cls._error_messages.update({
'modify': ('You can not modify line "%(line)s" from invoice '
'"%(invoice)s" that is posted or paid.'),
'create': ('You can not add a line to invoice "%(invoice)s" '
'that is posted, paid or cancelled.'),
'account_different_company': ('You can not create invoice line '
'"%(line)s" on invoice "%(invoice)s of company '
'"%(invoice_line_company)s because account "%(account)s '
'has company "%(account_company)s".'),
'same_account_on_invoice': ('You can not create invoice line '
'"%(line)s" on invoice "%(invoice)s" because the invoice '
'uses the same account (%(account)s).'),
2013-02-16 22:06:26 +01:00
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __register__(cls, module_name):
super(InvoiceLine, cls).__register__(module_name)
2010-08-19 22:02:49 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
table = TableHandler(cursor, cls, module_name)
2008-11-28 18:29:40 +01:00
# Migration from 1.0 invoice is no more required
2009-02-04 13:30:48 +01:00
table.not_null_action('invoice', action='remove')
2008-11-28 18:29:40 +01:00
2012-05-14 15:36:16 +02:00
# Migration from 2.4: drop required on sequence
table.not_null_action('sequence', action='remove')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_invoice_type():
2010-08-19 22:02:49 +02:00
return Transaction().context.get('invoice_type', 'out_invoice')
2008-11-28 18:29:40 +01:00
2012-09-24 22:17:32 +02:00
@staticmethod
def default_currency():
Company = Pool().get('company.company')
2010-08-19 22:02:49 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:32 +02:00
company = Company(Transaction().context['company'])
return company.currency.id
2008-11-28 18:29:40 +01:00
2012-09-24 22:17:32 +02:00
@staticmethod
def default_currency_digits():
Company = Pool().get('company.company')
2010-08-19 22:02:49 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:32 +02:00
company = Company(Transaction().context['company'])
2008-11-28 18:29:40 +01:00
return company.currency.digits
return 2
2012-09-24 22:17:32 +02:00
@staticmethod
def default_unit_digits():
return 2
2012-09-24 22:17:32 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:07 +02:00
return Transaction().context.get('company')
2008-11-28 18:29:40 +01:00
2012-09-24 22:17:32 +02:00
@staticmethod
def default_type():
2008-04-10 17:21:44 +02:00
return 'line'
2012-09-24 22:17:32 +02:00
def on_change_with_party_lang(self, name=None):
2012-11-23 18:00:20 +01:00
Config = Pool().get('ir.configuration')
2012-09-24 22:17:32 +02:00
if self.party and self.party.lang:
return self.party.lang.code
2012-11-23 18:00:20 +01:00
return Config.get_language()
2012-09-24 22:17:32 +02:00
def on_change_with_unit_digits(self, name=None):
if self.unit:
return self.unit.digits
return 2
2012-09-24 22:17:32 +02:00
def on_change_with_currency_digits(self, name=None):
if self.currency:
return self.currency.digits
return 2
2012-09-24 22:17:32 +02:00
def on_change_with_amount(self):
if self.type == 'line':
currency = (self.invoice.currency if self.invoice
else self.currency)
amount = (Decimal(str(self.quantity or '0.0'))
* (self.unit_price or Decimal('0.0')))
if currency:
2012-09-24 22:17:32 +02:00
return currency.round(amount)
return amount
2008-07-28 20:16:08 +02:00
return Decimal('0.0')
2012-09-24 22:17:32 +02:00
def get_amount(self, name):
if self.type == 'line':
return self.on_change_with_amount()
elif self.type == 'subtotal':
subtotal = _ZERO
for line2 in self.invoice.lines:
if line2.type == 'line':
subtotal += line2.invoice.currency.round(
Decimal(str(line2.quantity)) * line2.unit_price)
elif line2.type == 'subtotal':
if self == line2:
break
subtotal = _ZERO
return subtotal
else:
return _ZERO
2008-08-26 18:34:14 +02:00
2012-09-24 22:17:32 +02:00
def get_invoice_taxes(self, name):
pool = Pool()
Tax = pool.get('account.tax')
Invoice = pool.get('account.invoice')
2008-08-26 18:34:14 +02:00
2012-09-24 22:17:32 +02:00
if not self.invoice:
return
context = self.invoice.get_tax_context()
taxes_keys = []
with Transaction().set_context(**context):
taxes = Tax.compute(self.taxes, self.unit_price, self.quantity)
for tax in taxes:
key, _ = Invoice._compute_tax(tax, self.invoice.type)
taxes_keys.append(key)
taxes = []
for tax in self.invoice.taxes:
if tax.manual:
continue
2012-09-24 22:17:32 +02:00
base_code_id = tax.base_code.id if tax.base_code else None
tax_code_id = tax.tax_code.id if tax.tax_code else None
tax_id = tax.tax.id if tax.tax else None
key = (base_code_id, tax.base_sign,
2013-02-16 22:06:26 +01:00
tax_code_id, tax.tax_sign,
tax.account.id, tax_id)
2012-09-24 22:17:32 +02:00
if key in taxes_keys:
taxes.append(tax.id)
return taxes
2012-09-24 22:17:32 +02:00
def _get_tax_rule_pattern(self):
2009-03-28 19:32:35 +01:00
'''
Get tax rule pattern
'''
2012-09-24 22:17:32 +02:00
return {}
2009-03-28 19:32:35 +01:00
2012-09-24 22:17:32 +02:00
def on_change_product(self):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Product = pool.get('product.product')
Company = pool.get('company.company')
Currency = pool.get('currency.currency')
Date = pool.get('ir.date')
2009-03-28 19:32:35 +01:00
2012-09-24 22:17:32 +02:00
if not self.product:
2008-04-10 17:21:44 +02:00
return {}
res = {}
2010-08-19 22:02:49 +02:00
context = {}
2008-05-21 18:14:05 +02:00
party = None
2012-09-24 22:17:32 +02:00
if self.invoice and self.invoice.party:
party = self.invoice.party
elif self.party:
party = self.party
if party and party.lang:
context['language'] = party.lang.code
2008-04-10 17:21:44 +02:00
company = None
2010-08-19 22:02:49 +02:00
if Transaction().context.get('company'):
2012-09-24 22:17:32 +02:00
company = Company(Transaction().context['company'])
2008-04-10 17:21:44 +02:00
currency = None
2012-09-24 22:17:32 +02:00
currency_date = Date.today()
if self.invoice and self.invoice.currency_date:
currency_date = self.invoice.currency_date
#TODO check if today date is correct
if self.invoice and self.invoice.currency:
currency = self.invoice.currency
elif self.currency:
currency = self.currency
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
if self.invoice and self.invoice.type:
type_ = self.invoice.type
elif self.invoice_type:
type_ = self.invoice_type
if type_ in ('in_invoice', 'in_credit_note'):
2008-04-10 17:21:44 +02:00
if company and currency:
with Transaction().set_context(date=currency_date):
2012-09-24 22:17:32 +02:00
res['unit_price'] = Currency.compute(
company.currency, self.product.cost_price,
currency, round=False)
2008-04-10 17:21:44 +02:00
else:
2012-09-24 22:17:32 +02:00
res['unit_price'] = self.product.cost_price
try:
2012-09-24 22:17:32 +02:00
account_expense = self.product.account_expense_used
res['account'] = account_expense.id
res['account.rec_name'] = account_expense.rec_name
2010-07-08 19:55:01 +02:00
except Exception:
pass
2008-04-10 17:21:44 +02:00
res['taxes'] = []
2012-09-24 22:17:32 +02:00
pattern = self._get_tax_rule_pattern()
for tax in self.product.supplier_taxes_used:
2009-03-28 19:32:35 +01:00
if party and party.supplier_tax_rule:
2012-09-24 22:17:32 +02:00
tax_ids = party.supplier_tax_rule.apply(tax, pattern)
2009-04-06 10:32:08 +02:00
if tax_ids:
res['taxes'].extend(tax_ids)
2009-03-28 19:32:35 +01:00
continue
2008-04-10 17:21:44 +02:00
res['taxes'].append(tax.id)
2009-04-06 10:32:08 +02:00
if party and party.supplier_tax_rule:
2012-09-24 22:17:32 +02:00
tax_ids = party.supplier_tax_rule.apply(None, pattern)
2009-04-06 10:32:08 +02:00
if tax_ids:
res['taxes'].extend(tax_ids)
2008-04-10 17:21:44 +02:00
else:
if company and currency:
with Transaction().set_context(date=currency_date):
2012-09-24 22:17:32 +02:00
res['unit_price'] = Currency.compute(
company.currency, self.product.list_price,
currency, round=False)
2008-04-10 17:21:44 +02:00
else:
2012-09-24 22:17:32 +02:00
res['unit_price'] = self.product.list_price
try:
2012-09-24 22:17:32 +02:00
account_revenue = self.product.account_revenue_used
res['account'] = account_revenue.id
res['account.rec_name'] = account_revenue.rec_name
2010-07-08 19:55:01 +02:00
except Exception:
pass
2008-04-10 17:21:44 +02:00
res['taxes'] = []
2012-09-24 22:17:32 +02:00
pattern = self._get_tax_rule_pattern()
for tax in self.product.customer_taxes_used:
2009-03-28 19:32:35 +01:00
if party and party.customer_tax_rule:
2012-09-24 22:17:32 +02:00
tax_ids = party.customer_tax_rule.apply(tax, pattern)
2009-04-06 10:32:08 +02:00
if tax_ids:
res['taxes'].extend(tax_ids)
2009-03-28 19:32:35 +01:00
continue
2008-04-10 17:21:44 +02:00
res['taxes'].append(tax.id)
2009-04-06 10:32:08 +02:00
if party and party.customer_tax_rule:
2012-09-24 22:17:32 +02:00
tax_ids = party.customer_tax_rule.apply(None, pattern)
2009-04-06 10:32:08 +02:00
if tax_ids:
res['taxes'].extend(tax_ids)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
if not self.description:
2010-08-19 22:02:49 +02:00
with Transaction().set_context(**context):
2012-09-24 22:17:32 +02:00
res['description'] = Product(self.product.id).rec_name
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
category = self.product.default_uom.category
if not self.unit or self.unit not in category.uoms:
res['unit'] = self.product.default_uom.id
res['unit.rec_name'] = self.product.default_uom.rec_name
res['unit_digits'] = self.product.default_uom.digits
2012-09-24 22:17:32 +02:00
self.unit_price = res['unit_price']
self.type = 'line'
res['amount'] = self.on_change_with_amount()
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def on_change_with_product_uom_category(self, name=None):
if self.product:
return self.product.default_uom_category.id
2012-03-25 15:41:01 +02:00
2012-09-24 22:17:32 +02:00
def on_change_account(self):
if self.product:
return {}
taxes = []
result = {
'taxes': taxes,
2012-09-24 22:17:32 +02:00
}
if (self.invoice and self.invoice.party
and self.invoice.type):
party = self.invoice.party
if self.invoice.type in ('in_invoice', 'in_credit_note'):
2012-09-06 15:12:40 +02:00
tax_rule = party.supplier_tax_rule
else:
tax_rule = party.customer_tax_rule
else:
tax_rule = None
2012-09-24 22:17:32 +02:00
if self.account:
pattern = self._get_tax_rule_pattern()
for tax in self.account.taxes:
2012-09-06 15:12:40 +02:00
if tax_rule:
2012-09-24 22:17:32 +02:00
tax_ids = tax_rule.apply(tax, pattern)
2012-09-06 15:12:40 +02:00
if tax_ids:
taxes.extend(tax_ids)
continue
taxes.append(tax.id)
return result
2013-03-23 15:37:44 +01:00
@staticmethod
def _get_origin():
'Return list of Model names for origin Reference'
return []
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
models = cls._get_origin()
models = Model.search([
('model', 'in', models),
])
return [(None, '')] + [(m.model, m.name) for m in models]
2012-09-24 22:17:32 +02:00
@classmethod
def check_modify(cls, lines):
2008-04-10 17:21:44 +02:00
'''
Check if the lines can be modified
'''
2012-09-24 22:17:32 +02:00
for line in lines:
if (line.invoice
2012-12-23 22:28:41 +01:00
and line.invoice.state in ('posted', 'paid')):
cls.raise_user_error('modify', {
'line': line.rec_name,
'invoice': line.invoice.rec_name
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def delete(cls, lines):
cls.check_modify(lines)
super(InvoiceLine, cls).delete(lines)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def write(cls, lines, vals):
cls.check_modify(lines)
super(InvoiceLine, cls).write(lines, vals)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
2013-01-06 22:18:13 +01:00
def create(cls, vlist):
2012-09-24 22:17:32 +02:00
Invoice = Pool().get('account.invoice')
2013-01-06 22:18:13 +01:00
invoice_ids = []
for vals in vlist:
if vals.get('invoice'):
invoice_ids.append(vals.get('invoice'))
for invoice in Invoice.browse(invoice_ids):
2012-12-23 22:28:41 +01:00
if invoice.state in ('posted', 'paid', 'cancel'):
cls.raise_user_error('create', (invoice.rec_name,))
2013-01-06 22:18:13 +01:00
return super(InvoiceLine, cls).create(vlist)
2008-04-10 17:21:44 +02:00
@classmethod
def validate(cls, lines):
super(InvoiceLine, cls).validate(lines)
for line in lines:
line.check_account_company()
line.check_same_account()
def check_account_company(self):
2012-09-24 22:17:32 +02:00
if self.type == 'line':
if self.invoice:
if self.account.company != self.invoice.company:
self.raise_user_error('account_different_company', {
'line': self.rec_name,
'invoice': self.invoice.rec_name,
'invoice_line_company':
self.invoice.company.rec_name,
'account': self.account.rec_name,
'account_company': self.account.company.rec_name,
})
2012-09-24 22:17:32 +02:00
elif self.company:
if self.account.company != self.company:
self.raise_user_error('account_different_company', {
'line': self.rec_name,
'invoice': '/',
'invoice_line_company': self.company.rec_name,
'account': self.account.rec_name,
'account_company': self.account.company.rec_name,
})
2008-04-10 17:21:44 +02:00
def check_same_account(self):
2012-09-24 22:17:32 +02:00
if self.type == 'line':
if (self.invoice
and self.account == self.invoice.account):
self.raise_user_error('same_account_on_invoice', {
'line': self.rec_name,
'invoice': self.invoice.rec_name,
'account': self.account.rec_name,
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def _compute_taxes(self):
pool = Pool()
Tax = pool.get('account.tax')
Currency = pool.get('currency.currency')
context = self.invoice.get_tax_context()
2008-04-10 17:21:44 +02:00
res = []
2012-09-24 22:17:32 +02:00
if self.type != 'line':
2008-04-10 17:21:44 +02:00
return res
2010-08-19 22:02:49 +02:00
with Transaction().set_context(**context):
2012-09-24 22:17:32 +02:00
taxes = Tax.compute(self.taxes, self.unit_price, self.quantity)
2010-08-19 22:02:49 +02:00
for tax in taxes:
2012-09-24 22:17:32 +02:00
if self.invoice.type in ('out_invoice', 'in_invoice'):
base_code_id = (tax['tax'].invoice_base_code.id
if tax['tax'].invoice_base_code else None)
2008-04-10 17:21:44 +02:00
amount = tax['base'] * tax['tax'].invoice_base_sign
else:
2012-09-24 22:17:32 +02:00
base_code_id = (tax['tax'].credit_note_base_code.id
if tax['tax'].credit_note_base_code else None)
2008-10-03 17:12:23 +02:00
amount = tax['base'] * tax['tax'].credit_note_base_sign
2008-04-10 17:21:44 +02:00
if base_code_id:
with Transaction().set_context(
2012-09-24 22:17:32 +02:00
date=self.invoice.currency_date):
amount = Currency.compute(self.invoice.currency,
amount, self.invoice.company.currency)
2008-04-10 17:21:44 +02:00
res.append({
2012-09-24 22:17:32 +02:00
'code': base_code_id,
'amount': amount,
'tax': tax['tax'].id if tax['tax'] else None,
})
2008-04-10 17:21:44 +02:00
return res
2012-09-24 22:17:32 +02:00
def get_move_line(self):
2008-04-10 17:21:44 +02:00
'''
Return a list of move lines values for invoice line
2008-04-10 17:21:44 +02:00
'''
2012-09-24 22:17:32 +02:00
Currency = Pool().get('currency.currency')
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
if self.type != 'line':
return []
2012-09-29 15:34:34 +02:00
res['description'] = self.description
2012-09-24 22:17:32 +02:00
if self.invoice.currency != self.invoice.company.currency:
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.invoice.currency,
self.amount, self.invoice.company.currency)
res['amount_second_currency'] = self.amount
res['second_currency'] = self.invoice.currency.id
2008-04-10 17:21:44 +02:00
else:
2012-09-24 22:17:32 +02:00
amount = self.amount
2008-04-10 17:21:44 +02:00
res['amount_second_currency'] = Decimal('0.0')
2012-03-27 10:43:07 +02:00
res['second_currency'] = None
2012-09-24 22:17:32 +02:00
if self.invoice.type in ('in_invoice', 'out_credit_note'):
if amount >= Decimal('0.0'):
res['debit'] = amount
res['credit'] = Decimal('0.0')
else:
res['debit'] = Decimal('0.0')
res['credit'] = - amount
res['amount_second_currency'] = - res['amount_second_currency']
2008-04-10 17:21:44 +02:00
else:
if amount >= Decimal('0.0'):
res['debit'] = Decimal('0.0')
res['credit'] = amount
res['amount_second_currency'] = - res['amount_second_currency']
else:
res['debit'] = - amount
res['credit'] = Decimal('0.0')
2012-09-24 22:17:32 +02:00
res['account'] = self.account.id
res['party'] = self.invoice.party.id
computed_taxes = self._compute_taxes()
2013-01-06 22:18:13 +01:00
if computed_taxes:
res['tax_lines'] = [('create', [tax for tax in computed_taxes])]
return [res]
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def _credit(self):
2008-04-16 23:56:33 +02:00
'''
2008-10-03 17:12:23 +02:00
Return values to credit line.
2008-04-16 23:56:33 +02:00
'''
res = {}
for field in ('sequence', 'type', 'quantity', 'unit_price',
'description'):
2012-09-24 22:17:32 +02:00
res[field] = getattr(self, field)
2008-04-16 23:56:33 +02:00
for field in ('unit', 'product', 'account'):
2013-02-04 20:05:15 +01:00
res[field] = getattr(getattr(self, field), 'id', None)
2008-04-16 23:56:33 +02:00
res['taxes'] = []
if self.taxes:
res['taxes'].append(('add', [tax.id for tax in self.taxes]))
2008-04-16 23:56:33 +02:00
return res
2008-04-10 17:21:44 +02:00
2009-03-05 14:51:35 +01:00
class InvoiceLineTax(ModelSQL):
'Invoice Line - Tax'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.line-account.tax'
2009-03-05 14:51:35 +01:00
_table = 'account_invoice_line_account_tax'
line = fields.Many2One('account.invoice.line', 'Invoice Line',
2012-03-05 10:52:57 +01:00
ondelete='CASCADE', select=True, required=True)
2009-03-05 14:51:35 +01:00
tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
required=True)
2009-02-28 16:57:26 +01:00
class InvoiceTax(ModelSQL, ModelView):
2008-04-10 17:21:44 +02:00
'Invoice Tax'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.tax'
2008-04-10 17:21:44 +02:00
_rec_name = 'description'
invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
2012-03-05 10:52:57 +01:00
select=True)
2008-04-10 17:21:44 +02:00
description = fields.Char('Description', size=None, required=True)
2012-05-14 15:36:16 +02:00
sequence = fields.Integer('Sequence',
order_field='(%(table)s.sequence IS NULL) %(order)s, '
'%(table)s.sequence %(order)s')
2010-03-11 16:45:42 +01:00
sequence_number = fields.Function(fields.Integer('Sequence Number'),
'get_sequence_number')
2008-04-10 17:21:44 +02:00
account = fields.Many2One('account.account', 'Account', required=True,
2011-08-21 11:20:30 +02:00
domain=[
('kind', '!=', 'view'),
('company', '=', Eval('_parent_invoice', {}).get('company', 0)),
2010-01-20 10:57:08 +01:00
])
base = fields.Numeric('Base', required=True,
2011-08-21 11:20:30 +02:00
digits=(16, Eval('_parent_invoice', {}).get('currency_digits', 2)))
amount = fields.Numeric('Amount', required=True,
2012-06-28 15:04:57 +02:00
digits=(16, Eval('_parent_invoice', {}).get('currency_digits', 2)),
on_change_with=['tax', 'base', 'amount', 'manual'],
depends=['tax', 'base', 'manual'])
2008-04-10 17:21:44 +02:00
manual = fields.Boolean('Manual')
base_code = fields.Many2One('account.tax.code', 'Base Code',
2011-08-21 11:20:30 +02:00
domain=[
('company', '=', Eval('_parent_invoice', {}).get('company', 0)),
2010-01-20 10:57:08 +01:00
])
base_sign = fields.Numeric('Base Sign', digits=(2, 0), required=True)
2008-04-10 17:21:44 +02:00
tax_code = fields.Many2One('account.tax.code', 'Tax Code',
2011-08-21 11:20:30 +02:00
domain=[
('company', '=', Eval('_parent_invoice', {}).get('company', 0)),
2010-01-20 10:57:08 +01:00
])
tax_sign = fields.Numeric('Tax Sign', digits=(2, 0), required=True)
2012-06-28 15:04:57 +02:00
tax = fields.Many2One('account.tax', 'Tax',
states={
'readonly': ~Eval('manual', False),
},
on_change=['tax', '_parent_invoice.party',
'_parent_invoice.type'],
depends=['manual'])
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(InvoiceTax, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
cls._error_messages.update({
'modify': ('You can not modify tax "%(tax)s" from invoice '
'"%(invoice)s" because it is posted or paid.'),
'create': ('You can not add line "%(line)s" to invoice '
'"%(invoice)s" because it is posted, paid or canceled.'),
'invalid_account_company': ('You can not create invoice '
'"%(invoice)s" on company "%(invoice_company)s" using '
'account "%(account)s" from company '
'"%(account_company)s".'),
'invalid_base_code_company': ('You can not create invoice '
'"%(invoice)s" on company "%(invoice_company)s" using base '
'tax code "%(base_code)s" from company '
'"%(base_code_company)s".'),
'invalid_tax_code_company': ('You can not create invoice '
'"%(invoice)s" on company "%(invoice_company)s" using tax '
'code "%(tax_code)s" from company "%(tax_code_company)s".'),
2013-02-16 22:06:26 +01:00
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __register__(cls, module_name):
2012-05-14 15:36:16 +02:00
cursor = Transaction().cursor
2012-09-24 22:17:32 +02:00
table = TableHandler(cursor, cls, module_name)
2012-05-14 15:36:16 +02:00
2012-09-24 22:17:32 +02:00
super(InvoiceTax, cls).__register__(module_name)
2012-05-14 15:36:16 +02:00
# Migration from 2.4: drop required on sequence
table.not_null_action('sequence', action='remove')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_base():
2008-04-10 17:21:44 +02:00
return Decimal('0.0')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_amount():
2008-04-10 17:21:44 +02:00
return Decimal('0.0')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_manual():
2008-04-10 17:21:44 +02:00
return True
2012-09-24 22:17:32 +02:00
@staticmethod
def default_base_sign():
2008-04-10 17:21:44 +02:00
return Decimal('1')
2012-09-24 22:17:32 +02:00
@staticmethod
def default_tax_sign():
2008-04-10 17:21:44 +02:00
return Decimal('1')
2012-09-24 22:17:32 +02:00
def on_change_tax(self):
Tax = Pool().get('account.tax')
2012-06-28 15:04:57 +02:00
changes = {}
2012-09-24 22:17:32 +02:00
if self.tax:
if self.invoice:
context = self.invoice.get_tax_context()
else:
context = {}
2012-06-28 15:04:57 +02:00
with Transaction().set_context(**context):
2012-09-24 22:17:32 +02:00
tax = Tax(self.tax.id)
2012-06-28 15:04:57 +02:00
changes['description'] = tax.description
2012-09-24 22:17:32 +02:00
if self.invoice and self.invoice.type:
invoice_type = self.invoice.type
else:
invoice_type = 'out_invoice'
2012-06-28 15:04:57 +02:00
if invoice_type in ('out_invoice', 'in_invoice'):
2012-12-06 19:31:04 +01:00
changes['base_code'] = (tax.invoice_base_code.id
if tax.invoice_base_code else None)
2012-06-28 15:04:57 +02:00
changes['base_sign'] = tax.invoice_base_sign
2012-12-06 19:31:04 +01:00
changes['tax_code'] = (tax.invoice_tax_code.id
if tax.invoice_tax_code else None)
2012-06-28 15:04:57 +02:00
changes['tax_sign'] = tax.invoice_tax_sign
changes['account'] = tax.invoice_account.id
else:
2012-12-06 19:31:04 +01:00
changes['base_code'] = (tax.credit_note_base_code.id
if tax.credit_note_base_code else None)
2012-06-28 15:04:57 +02:00
changes['base_sign'] = tax.credit_note_base_sign
2012-12-06 19:31:04 +01:00
changes['tax_code'] = (tax.credit_note_tax_code.id
if tax.credit_note_tax_code else None)
2012-06-28 15:04:57 +02:00
changes['tax_sign'] = tax.credit_note_tax_sign
changes['account'] = tax.credit_note_account.id
return changes
2012-09-24 22:17:32 +02:00
def on_change_with_amount(self):
Tax = Pool().get('account.tax')
if self.tax and self.manual:
tax = self.tax
base = self.base or Decimal(0)
for values in Tax.compute([tax], base, 1):
if (values['tax'] == tax
and values['base'] == base):
return values['amount']
2012-09-24 22:17:32 +02:00
return self.amount
2012-06-28 15:04:57 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def check_modify(cls, taxes):
2008-04-10 17:21:44 +02:00
'''
Check if the taxes can be modified
'''
2012-09-24 22:17:32 +02:00
for tax in taxes:
2012-12-23 22:28:41 +01:00
if tax.invoice.state in ('posted', 'paid'):
2012-09-24 22:17:32 +02:00
cls.raise_user_error('modify')
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def get_sequence_number(self, name):
i = 1
for tax in self.invoice.taxes:
if tax == self:
return i
i += 1
return 0
2012-09-24 22:17:32 +02:00
@classmethod
def delete(cls, taxes):
cls.check_modify(taxes)
super(InvoiceTax, cls).delete(taxes)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def write(cls, taxes, vals):
cls.check_modify(taxes)
super(InvoiceTax, cls).write(taxes, vals)
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
2013-01-06 22:18:13 +01:00
def create(cls, vlist):
2012-09-24 22:17:32 +02:00
Invoice = Pool().get('account.invoice')
2013-01-06 22:18:13 +01:00
invoice_ids = []
for vals in vlist:
if vals.get('invoice'):
invoice_ids.append(vals['invoice'])
for invoice in Invoice.browse(invoice_ids):
2012-12-23 22:28:41 +01:00
if invoice.state in ('posted', 'paid', 'cancel'):
2012-09-24 22:17:32 +02:00
cls.raise_user_error('create')
2013-01-06 22:18:13 +01:00
return super(InvoiceTax, cls).create(vlist)
2008-04-10 17:21:44 +02:00
@classmethod
def validate(cls, taxes):
super(InvoiceTax, cls).validate(taxes)
for tax in taxes:
tax.check_company()
2012-09-24 22:17:32 +02:00
def check_company(self):
company = self.invoice.company
if self.account.company != company:
self.raise_user_error('invalid_account_company', {
'invoice': self.invoice.rec_name,
'invoice_company': self.invoice.company.rec_name,
'account': self.account.rec_name,
'account_company': self.account.company.rec_name,
})
2012-09-24 22:17:32 +02:00
if self.base_code:
if self.base_code.company != company:
self.raise_user_error('invalid_base_code_company', {
2013-03-25 18:49:29 +00:00
'invoice': self.invoice.rec_name,
'invoice_company': self.invoice.company.rec_name,
'base_code': self.base_code.rec_name,
'base_code_company': self.base_code.company.rec_name,
})
2012-09-24 22:17:32 +02:00
if self.tax_code:
if self.tax_code.company != company:
self.raise_user_error('invalid_tax_code_company', {
2013-03-25 18:49:29 +00:00
'invoice': self.invoice.rec_name,
'invoice_company': self.invoice.company.rec_name,
'tax_code': self.tax_code.rec_name,
'tax_code_company': self.tax_code.company.rec_name,
})
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def get_move_line(self):
2008-04-10 17:21:44 +02:00
'''
Return a list of move lines values for invoice tax
2008-04-10 17:21:44 +02:00
'''
2012-09-24 22:17:32 +02:00
Currency = Pool().get('currency.currency')
2008-04-10 17:21:44 +02:00
res = {}
2012-09-24 22:17:32 +02:00
if not self.amount:
return []
2012-09-29 15:34:34 +02:00
res['description'] = self.description
2012-09-24 22:17:32 +02:00
if self.invoice.currency != self.invoice.company.currency:
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.invoice.currency, self.amount,
self.invoice.company.currency)
res['amount_second_currency'] = self.amount
res['second_currency'] = self.invoice.currency.id
2008-04-10 17:21:44 +02:00
else:
2012-09-24 22:17:32 +02:00
amount = self.amount
2008-04-10 17:21:44 +02:00
res['amount_second_currency'] = Decimal('0.0')
2012-03-27 10:43:07 +02:00
res['second_currency'] = None
2012-09-24 22:17:32 +02:00
if self.invoice.type in ('in_invoice', 'out_credit_note'):
2008-04-10 17:21:44 +02:00
if amount >= Decimal('0.0'):
res['debit'] = amount
res['credit'] = Decimal('0.0')
else:
res['debit'] = Decimal('0.0')
res['credit'] = - amount
res['amount_second_currency'] = - res['amount_second_currency']
else:
if amount >= Decimal('0.0'):
res['debit'] = Decimal('0.0')
res['credit'] = amount
res['amount_second_currency'] = - res['amount_second_currency']
else:
res['debit'] = - amount
res['credit'] = Decimal('0.0')
2012-09-24 22:17:32 +02:00
res['account'] = self.account.id
res['party'] = self.invoice.party.id
if self.tax_code:
2013-01-06 22:18:13 +01:00
res['tax_lines'] = [('create', [{
'code': self.tax_code.id,
'amount': amount * self.tax_sign,
'tax': self.tax and self.tax.id or None
}])]
return [res]
2008-04-10 17:21:44 +02:00
2012-09-24 22:17:32 +02:00
def _credit(self):
2008-04-16 23:56:33 +02:00
'''
2008-10-03 17:12:23 +02:00
Return values to credit tax.
2008-04-16 23:56:33 +02:00
'''
res = {}
for field in ('description', 'sequence', 'base', 'amount',
'manual', 'base_sign', 'tax_sign'):
2012-09-24 22:17:32 +02:00
res[field] = getattr(self, field)
2008-04-16 23:56:33 +02:00
for field in ('account', 'base_code', 'tax_code', 'tax'):
2012-09-24 22:17:32 +02:00
res[field] = getattr(self, field).id
2008-04-16 23:56:33 +02:00
return res
2008-04-25 20:34:17 +02:00
2012-01-02 17:57:03 +01:00
class PrintInvoiceWarning(ModelView):
'Print Invoice Report Warning'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.print.warning'
2012-01-02 17:57:03 +01:00
class PrintInvoice(Wizard):
'Print Invoice Report'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.print'
2012-01-02 17:57:03 +01:00
start = StateTransition()
warning = StateView('account.invoice.print.warning',
'account_invoice.print_warning_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-print', default=True),
])
print_ = StateAction('account_invoice.report_invoice')
2012-09-24 22:17:32 +02:00
def transition_start(self):
2012-01-02 17:57:03 +01:00
if len(Transaction().context['active_ids']) > 1:
return 'warning'
2012-01-02 17:57:03 +01:00
return 'print_'
2012-09-24 22:17:32 +02:00
def do_print_(self, action):
2012-01-02 17:57:03 +01:00
data = {}
data['id'] = Transaction().context['active_ids'].pop()
data['ids'] = [data['id']]
return action, data
2012-09-24 22:17:32 +02:00
def transition_print_(self):
2012-01-02 17:57:03 +01:00
if Transaction().context['active_ids']:
return 'print_'
return 'end'
2008-04-10 17:21:44 +02:00
2008-04-14 19:05:52 +02:00
class InvoiceReport(Report):
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice'
2008-04-14 19:05:52 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(InvoiceReport, cls).__setup__()
cls.__rpc__['execute'] = RPC(False)
2012-09-24 22:17:32 +02:00
@classmethod
def execute(cls, ids, data):
Invoice = Pool().get('account.invoice')
2009-06-22 19:08:54 +02:00
2012-09-24 22:17:32 +02:00
res = super(InvoiceReport, cls).execute(ids, data)
2012-01-02 17:57:03 +01:00
if len(ids) > 1:
res = (res[0], res[1], True, res[3])
2009-06-22 19:08:54 +02:00
else:
2012-09-24 22:17:32 +02:00
invoice = Invoice(ids[0])
2009-06-22 19:08:54 +02:00
if invoice.number:
res = (res[0], res[1], res[2], res[3] + ' - ' + invoice.number)
return res
2012-09-24 22:17:32 +02:00
@classmethod
def _get_records(cls, ids, model, data):
2010-08-19 22:02:49 +02:00
with Transaction().set_context(language=False):
2012-09-24 22:17:32 +02:00
return super(InvoiceReport, cls)._get_records(ids[:1], model, data)
2012-09-24 22:17:32 +02:00
@classmethod
def parse(cls, report, records, data, localcontext):
pool = Pool()
User = pool.get('res.user')
Invoice = pool.get('account.invoice')
2012-09-24 22:17:32 +02:00
invoice = records[0]
2011-09-17 14:43:48 +02:00
if invoice.invoice_report_cache:
return (invoice.invoice_report_format,
2011-09-17 14:43:48 +02:00
invoice.invoice_report_cache)
2012-09-24 22:17:32 +02:00
user = User(Transaction().user)
2010-08-19 22:02:49 +02:00
localcontext['company'] = user.company
2012-09-24 22:17:32 +02:00
res = super(InvoiceReport, cls).parse(report, records, data,
2010-08-19 22:02:49 +02:00
localcontext)
2012-12-23 22:28:41 +01:00
# If the invoice is posted or paid and the report not saved in
2011-09-17 14:43:48 +02:00
# invoice_report_cache there was an error somewhere. So we save it now
# in invoice_report_cache
2012-12-23 22:28:41 +01:00
if (invoice.state in ('posted', 'paid')
and invoice.type in ('out_invoice', 'out_credit_note')):
Invoice.write([Invoice(invoice.id)], {
'invoice_report_format': res[0],
2011-09-17 14:43:48 +02:00
'invoice_report_cache': res[1],
})
return res
2008-04-14 19:05:52 +02:00
2012-01-02 17:57:03 +01:00
class PayInvoiceStart(ModelView):
'Pay Invoice'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.pay.start'
2011-08-04 16:57:16 +02:00
amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'], required=True)
2008-05-09 17:26:41 +02:00
currency = fields.Many2One('currency.currency', 'Currency', required=True)
2008-11-04 18:10:39 +01:00
currency_digits = fields.Integer('Currency Digits', readonly=True,
on_change_with=['currency'])
2012-09-29 15:34:34 +02:00
description = fields.Char('Description', size=None)
journal = fields.Many2One('account.journal', 'Journal', required=True,
domain=[('type', '=', 'cash')])
date = fields.Date('Date', required=True)
2012-09-24 22:17:32 +02:00
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
2012-09-24 22:17:32 +02:00
@staticmethod
def default_currency_digits():
return 2
2012-09-24 22:17:32 +02:00
def on_change_with_currency_digits(self):
if self.currency:
return self.currency.digits
2008-11-04 18:10:39 +01:00
return 2
2009-02-28 16:57:26 +01:00
class PayInvoiceAsk(ModelView):
2012-01-02 17:57:03 +01:00
'Pay Invoice'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.pay.ask'
type = fields.Selection([
('writeoff', 'Write-Off'),
('partial', 'Partial Payment'),
], 'Type', required=True)
journal_writeoff = fields.Many2One('account.journal', 'Write-Off Journal',
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'writeoff',
'required': Eval('type') == 'writeoff',
2011-08-04 16:57:16 +02:00
}, depends=['type'])
account_writeoff = fields.Many2One('account.account', 'Write-Off Account',
2011-08-04 16:57:16 +02:00
domain=[
('kind', '!=', 'view'),
2011-08-21 11:20:30 +02:00
('company', '=', Eval('context', {}).get('company', 0)),
2010-01-20 10:57:08 +01:00
],
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'writeoff',
'required': Eval('type') == 'writeoff',
2011-08-04 16:57:16 +02:00
}, depends=['type'])
2010-01-20 10:57:08 +01:00
amount = fields.Numeric('Payment Amount',
digits=(16, Eval('currency_digits', 2)),
2009-03-18 17:56:58 +01:00
readonly=True, depends=['currency_digits'])
currency = fields.Many2One('currency.currency', 'Payment Currency',
readonly=True)
currency_digits = fields.Integer('Payment Currency Digits', readonly=True)
amount_writeoff = fields.Numeric('Write-Off Amount',
2011-08-21 11:20:30 +02:00
digits=(16, Eval('currency_digits_writeoff', 2)), readonly=True,
depends=['currency_digits_writeoff', 'type'], states={
'invisible': Eval('type') != 'writeoff',
2009-03-18 17:56:58 +01:00
})
currency_writeoff = fields.Many2One('currency.currency',
2011-08-04 16:57:16 +02:00
'Write-Off Currency', readonly=True, states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'writeoff',
2011-08-04 16:57:16 +02:00
}, depends=['type'])
2009-03-18 17:56:58 +01:00
currency_digits_writeoff = fields.Integer('Write-Off Currency Digits',
required=True, readonly=True)
lines_to_pay = fields.Many2Many('account.move.line', None, None,
'Lines to Pay', readonly=True)
2009-03-05 14:51:35 +01:00
lines = fields.Many2Many('account.move.line', None, None, 'Lines',
2011-08-04 16:57:16 +02:00
domain=[
('id', 'in', Eval('lines_to_pay')),
('reconciliation', '=', False),
2010-01-20 10:57:08 +01:00
],
2011-08-04 16:57:16 +02:00
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'writeoff',
2009-03-18 17:56:58 +01:00
}, on_change=['lines', 'amount', 'currency', 'currency_writeoff',
2011-08-04 16:57:16 +02:00
'invoice'],
depends=['lines_to_pay', 'type'])
payment_lines = fields.Many2Many('account.move.line', None, None,
2011-08-04 16:57:16 +02:00
'Payment Lines', readonly=True,
states={
2011-08-21 11:20:30 +02:00
'invisible': Eval('type') != 'writeoff',
2011-08-04 16:57:16 +02:00
}, depends=['type'])
company = fields.Many2One('company.company', 'Company', readonly=True)
2009-03-18 17:56:58 +01:00
invoice = fields.Many2One('account.invoice', 'Invoice', readonly=True)
2012-09-24 22:17:32 +02:00
@staticmethod
def default_type():
2009-03-18 17:56:58 +01:00
return 'partial'
2012-09-24 22:17:32 +02:00
def on_change_lines(self):
Currency = Pool().get('currency.currency')
2009-03-18 17:56:58 +01:00
res = {}
2012-09-24 22:17:32 +02:00
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.currency, self.amount,
self.currency_writeoff)
2009-03-18 17:56:58 +01:00
res['amount_writeoff'] = Decimal('0.0')
2012-09-24 22:17:32 +02:00
for line in self.lines:
2009-03-18 17:56:58 +01:00
res['amount_writeoff'] += line.debit - line.credit
2012-09-24 22:17:32 +02:00
if self.invoice.type in ('in_invoice', 'out_credit_note'):
2009-03-18 17:56:58 +01:00
res['amount_writeoff'] = - res['amount_writeoff'] - amount
else:
res['amount_writeoff'] = res['amount_writeoff'] - amount
return res
2008-11-04 18:10:39 +01:00
class PayInvoice(Wizard):
'Pay Invoice'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.pay'
2012-01-02 17:57:03 +01:00
start = StateView('account.invoice.pay.start',
'account_invoice.pay_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'choice', 'tryton-ok', default=True),
])
choice = StateTransition()
ask = StateView('account.invoice.pay.ask',
'account_invoice.pay_ask_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'pay', 'tryton-ok', default=True),
])
pay = StateTransition()
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(PayInvoice, cls).__setup__()
cls._error_messages.update({
'amount_greater_amount_to_pay': ('On invoice "%s" you can not '
'create a partial payment with an amount greater than the '
'amount to pay.'),
2013-02-16 22:06:26 +01:00
})
2012-09-24 22:17:32 +02:00
def default_start(self, fields):
Invoice = Pool().get('account.invoice')
2012-01-02 17:57:03 +01:00
default = {}
2012-09-24 22:17:32 +02:00
invoice = Invoice(Transaction().context['active_id'])
2012-01-02 17:57:03 +01:00
default['currency'] = invoice.currency.id
default['currency_digits'] = invoice.currency.digits
2012-04-03 20:06:19 +02:00
default['amount'] = (invoice.amount_to_pay_today
or invoice.amount_to_pay)
2012-01-02 17:57:03 +01:00
default['description'] = invoice.number
return default
2012-09-24 22:17:32 +02:00
def transition_choice(self):
pool = Pool()
Invoice = pool.get('account.invoice')
Currency = pool.get('currency.currency')
2012-09-24 22:17:32 +02:00
invoice = Invoice(Transaction().context['active_id'])
2012-09-24 22:17:32 +02:00
with Transaction().set_context(date=self.start.date):
amount = Currency.compute(self.start.currency,
self.start.amount, invoice.company.currency)
_, remainder = invoice.get_reconcile_lines_for_amount(amount)
2012-01-02 17:57:03 +01:00
if remainder == Decimal('0.0') and amount <= invoice.amount_to_pay:
return 'pay'
return 'ask'
2012-09-24 22:17:32 +02:00
def default_ask(self, fields):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Invoice = pool.get('account.invoice')
Currency = pool.get('currency.currency')
Line = pool.get('account.move.line')
2012-01-02 17:57:03 +01:00
default = {}
2012-09-24 22:17:32 +02:00
invoice = Invoice(Transaction().context['active_id'])
2012-01-02 17:57:03 +01:00
default['lines_to_pay'] = [x.id for x in invoice.lines_to_pay
if not x.reconciliation]
2012-09-24 22:17:32 +02:00
default['amount'] = self.start.amount
default['currency'] = self.start.currency.id
default['currency_digits'] = self.start.currency_digits
2012-01-02 17:57:03 +01:00
default['company'] = invoice.company.id
2012-09-24 22:17:32 +02:00
with Transaction().set_context(date=self.start.date):
2013-01-19 17:11:54 +01:00
amount = Currency.compute(self.start.currency,
self.start.amount, invoice.company.currency)
2012-09-24 22:17:32 +02:00
if invoice.company.currency.is_zero(amount):
2012-01-02 17:57:03 +01:00
default['lines'] = [x.id for x in invoice.lines_to_pay]
else:
2012-01-02 17:57:03 +01:00
default['lines'], _ = \
2012-09-24 22:17:32 +02:00
invoice.get_reconcile_lines_for_amount(amount)
2012-01-02 17:57:03 +01:00
for line_id in default['lines'][:]:
if line_id not in default['lines_to_pay']:
default['lines'].remove(line_id)
2012-01-02 17:57:03 +01:00
default['amount_writeoff'] = Decimal('0.0')
2012-09-24 22:17:32 +02:00
for line in Line.browse(default['lines']):
2012-01-02 17:57:03 +01:00
default['amount_writeoff'] += line.debit - line.credit
for line in invoice.payment_lines:
2012-01-02 17:57:03 +01:00
default['amount_writeoff'] += line.debit - line.credit
2009-03-18 17:56:58 +01:00
if invoice.type in ('in_invoice', 'out_credit_note'):
2012-01-02 17:57:03 +01:00
default['amount_writeoff'] = - default['amount_writeoff'] - amount
2009-03-18 17:56:58 +01:00
else:
2012-01-02 17:57:03 +01:00
default['amount_writeoff'] = default['amount_writeoff'] - amount
2012-01-02 17:57:03 +01:00
default['currency_writeoff'] = invoice.company.currency.id
default['currency_digits_writeoff'] = invoice.company.currency.digits
default['invoice'] = invoice.id
default['payment_lines'] = [x.id for x in invoice.payment_lines
if not x.reconciliation]
2012-09-24 22:17:32 +02:00
if (amount > invoice.amount_to_pay
or invoice.company.currency.is_zero(amount)):
2012-01-02 17:57:03 +01:00
default['type'] = 'writeoff'
return default
2012-09-24 22:17:32 +02:00
def transition_pay(self):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Invoice = pool.get('account.invoice')
Currency = pool.get('currency.currency')
MoveLine = pool.get('account.move.line')
2012-09-24 22:17:32 +02:00
invoice = Invoice(Transaction().context['active_id'])
2012-09-24 22:17:32 +02:00
with Transaction().set_context(date=self.start.date):
amount = Currency.compute(self.start.currency,
self.start.amount, invoice.company.currency)
2012-01-02 17:57:03 +01:00
reconcile_lines, remainder = \
2012-09-24 22:17:32 +02:00
invoice.get_reconcile_lines_for_amount(amount)
2012-03-27 10:43:07 +02:00
amount_second_currency = None
second_currency = None
2012-09-24 22:17:32 +02:00
if self.start.currency != invoice.company.currency:
amount_second_currency = self.start.amount
second_currency = self.start.currency
if amount > invoice.amount_to_pay and \
2012-09-24 22:17:32 +02:00
self.ask.type != 'writeoff':
self.raise_user_error('amount_greater_amount_to_pay',
(invoice.rec_name,))
2012-09-24 22:17:32 +02:00
line = None
if not invoice.company.currency.is_zero(amount):
line = invoice.pay_invoice(amount,
self.start.journal, self.start.date,
self.start.description, amount_second_currency,
2012-01-02 17:57:03 +01:00
second_currency)
2012-01-02 17:57:03 +01:00
if remainder != Decimal('0.0'):
2012-09-24 22:17:32 +02:00
if self.ask.type == 'writeoff':
lines = [l for l in self.ask.lines] + \
[l for l in invoice.payment_lines
if not l.reconciliation]
if line and line not in lines:
# Add new payment line if payment_lines was cached before
# its creation
2012-09-24 22:17:32 +02:00
lines += [line]
if lines:
MoveLine.reconcile(lines,
journal=self.ask.journal_writeoff,
date=self.start.date,
account=self.ask.account_writeoff)
else:
2012-09-24 22:17:32 +02:00
if line:
reconcile_lines += [line]
2012-01-02 17:57:03 +01:00
if reconcile_lines:
2012-09-24 22:17:32 +02:00
MoveLine.reconcile(reconcile_lines)
2012-01-02 17:57:03 +01:00
return 'end'
2008-04-16 23:56:33 +02:00
2012-01-02 17:57:03 +01:00
class CreditInvoiceStart(ModelView):
'Credit Invoice Init'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.credit.start'
2013-02-16 22:06:26 +01:00
with_refund = fields.Boolean('With Refund',
help='If true, the current invoice(s) will be paid.')
2008-04-16 23:56:33 +02:00
2008-10-03 17:12:23 +02:00
class CreditInvoice(Wizard):
'Credit Invoice'
2012-09-24 22:17:32 +02:00
__name__ = 'account.invoice.credit'
2012-01-02 17:57:03 +01:00
start = StateView('account.invoice.credit.start',
'account_invoice.credit_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Credit', 'credit', 'tryton-ok', default=True),
])
credit = StateAction('account_invoice.act_invoice_form')
2008-04-16 23:56:33 +02:00
2012-09-24 22:17:32 +02:00
@classmethod
def __setup__(cls):
super(CreditInvoice, cls).__setup__()
cls._error_messages.update({
2013-02-16 22:06:26 +01:00
'refund_non_posted': ('You can not credit with refund '
'invoice "%s" because it is not posted.'),
2013-02-16 22:06:26 +01:00
'refund_with_payement': ('You can not credit with refund '
'invoice "%s" because it has payments.'),
'refund_supplier': ('You can not credit with refund '
'a supplier invoice/credit note.'),
2013-02-16 22:06:26 +01:00
})
2012-09-24 22:17:32 +02:00
def default_start(self, fields):
Invoice = Pool().get('account.invoice')
2012-01-02 17:57:03 +01:00
default = {
'with_refund': True,
2012-09-24 22:17:32 +02:00
}
for invoice in Invoice.browse(Transaction().context['active_ids']):
if (invoice.state != 'posted'
or invoice.payment_lines
or invoice.type in ('in_invoice', 'in_credit_note')):
2012-01-02 17:57:03 +01:00
default['with_refund'] = False
break
2012-01-02 17:57:03 +01:00
return default
2012-09-24 22:17:32 +02:00
def do_credit(self, action):
2011-07-03 13:18:43 +02:00
pool = Pool()
2012-09-24 22:17:32 +02:00
Invoice = pool.get('account.invoice')
2008-04-16 23:56:33 +02:00
2012-09-24 22:17:32 +02:00
refund = self.start.with_refund
invoices = Invoice.browse(Transaction().context['active_ids'])
if refund:
2012-09-24 22:17:32 +02:00
for invoice in invoices:
2012-12-23 22:28:41 +01:00
if invoice.state != 'posted':
self.raise_user_error('refund_non_posted',
(invoice.rec_name,))
if invoice.payment_lines:
self.raise_user_error('refund_with_payement',
(invoice.rec_name,))
self.raise_user_error('refund_supplier')
credit_invoices = Invoice.credit(invoices, refund=refund)
2008-04-16 23:56:33 +02:00
data = {'res_id': [i.id for i in credit_invoices]}
if len(credit_invoices) == 1:
2012-01-02 17:57:03 +01:00
action['views'].reverse()
return action, data