trytond-account_invoice_int.../invoice.py

310 lines
13 KiB
Python
Raw Normal View History

2015-11-18 08:14:31 +01:00
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from collections import defaultdict
from trytond.model import fields, ModelView
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Equal, Eval, If, Bool
2015-11-18 08:14:31 +01:00
from trytond.transaction import Transaction
2019-04-02 10:31:04 +02:00
class Invoice(metaclass=PoolMeta):
2015-11-18 08:14:31 +01:00
__name__ = 'account.invoice'
_intercompany_excluded_fields = ['id', 'company', 'party', 'lines',
'account', 'type', 'state', 'create_date', 'create_uid', 'write_date',
'write_uid', 'target_company', 'taxes', 'invoice_report_cache',
'invoice_report_format', 'move', 'number', 'taxes']
target_company = fields.Many2One('company.company', 'Target company',
domain=[
('party', '=', Eval('party')),
],
states={
2016-08-23 14:38:07 +02:00
'invisible': Eval('type', '') != 'out',
2015-11-18 08:14:31 +01:00
'readonly': Eval('state') != 'draft',
},
depends=['type', 'state', 'party'])
intercompany_invoices = fields.Function(fields.One2Many('account.invoice',
None, 'Intercompany Invoice'),
'get_intercompany_invoices')
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
cls._buttons.update({
'create_intercompany_invoices': {
'invisible': (~Eval('state').in_(['posted', 'paid'])
| Equal(Eval('type'), 'in')),
2015-11-18 08:14:31 +01:00
},
})
def get_intercompany_invoices(self, name):
2019-05-21 17:58:50 +02:00
if not self.target_company or not self.target_company.intercompany_user:
2018-10-18 09:31:22 +02:00
return
2019-05-21 17:58:50 +02:00
with Transaction().set_user(self.target_company.intercompany_user.id), \
Transaction().set_context(company=self.target_company.id,
_check_access=False):
2015-11-18 08:14:31 +01:00
return [i.id for i in self.search([
('lines.origin.invoice.id', '=', self.id,
'account.invoice.line'),
('company', '=', self.target_company.id),
('type', '=', self.intercompany_type),
])]
@classmethod
def post(cls, invoices):
super(Invoice, cls).post(invoices)
cls.create_intercompany_invoices(invoices)
@classmethod
@ModelView.button
def create_intercompany_invoices(cls, invoices):
intercompany_invoices = defaultdict(list)
2018-10-18 09:31:22 +02:00
for invoice in invoices:
intercompany_invoice = invoice.get_intercompany_invoice()
if intercompany_invoice:
company = intercompany_invoice.company
intercompany_invoices[company].append(
intercompany_invoice)
2019-05-21 17:58:50 +02:00
2019-04-27 06:31:00 +02:00
for company, new_invoices in intercompany_invoices.items():
2018-10-18 09:31:22 +02:00
# Company must be set on context to avoid domain errors
with Transaction().set_user(company.intercompany_user.id), \
Transaction().set_context(company=company.id,
_check_access=False):
to_write, to_create, to_post = [], [], []
# XXX: Use save multi on version 3.6
for new_invoice in new_invoices:
if new_invoice.id is None or invoice.id < 0:
to_create.append(new_invoice._save_values)
elif new_invoice._save_values:
to_write.append(new_invoice)
else:
to_post.append(new_invoice)
to_post += cls.create(to_create)
if to_write:
cls.write(*sum(
(([i], i._save_values) for i in to_write),
()))
# We must reload invoices
to_post += cls.browse(to_write)
super(Invoice, cls).post(to_post)
2015-11-18 08:14:31 +01:00
@classmethod
def draft(cls, invoices):
to_delete = defaultdict(list)
for invoice in invoices:
with Transaction().set_user(0):
intercompany = cls.browse(invoice.intercompany_invoices)
for iinvoice in intercompany:
to_delete[iinvoice.company.id].append(iinvoice)
2019-05-21 17:58:50 +02:00
2015-11-18 08:14:31 +01:00
if to_delete:
2019-04-27 06:31:00 +02:00
for company, delete in to_delete.items():
2019-05-21 17:58:50 +02:00
if not company.intercompany_user:
continue
with Transaction().set_user(company.intercompany_user.id), \
Transaction().set_context(company=company.id,
_check_access=False):
2015-11-18 08:14:31 +01:00
cls.draft(delete)
cls.delete(delete)
super(Invoice, cls).draft(invoices)
@classmethod
def credit(cls, invoices, refund=False, **values):
2015-11-18 08:14:31 +01:00
pool = Pool()
MoveLine = pool.get('account.move.line')
new_invoices = super(Invoice, cls).credit(invoices, refund, **values)
2015-11-18 08:14:31 +01:00
if refund:
for invoice, new_invoice in zip(invoices, new_invoices):
if new_invoice.state == 'paid':
for source, target in zip(invoice.intercompany_invoices,
new_invoice.intercompany_invoices):
2019-05-21 17:58:50 +02:00
if not source.company.intercompany_user:
continue
2015-11-18 08:14:31 +01:00
with Transaction().set_user(0):
source, = cls.browse([source])
target, = cls.browse([target])
2019-05-21 17:58:50 +02:00
company_id = source.company.id
user_id = source.company.intercompany_user.id
with Transaction().set_user(user_id), \
Transaction().set_context(company=company_id,
_check_access=False):
2015-11-18 08:14:31 +01:00
lines = ([l for l in source.lines_to_pay
if not l.reconciliation] +
[l for l in target.lines_to_pay
if not l.reconciliation])
MoveLine.reconcile(lines)
return new_invoices
def get_intercompany_account(self):
2019-05-21 17:58:50 +02:00
Party = Pool().get('party.party')
with Transaction().set_user(self.target_company.intercompany_user.id), \
Transaction().set_context(company=self.target_company.id,
_check_access=False):
2015-11-18 08:14:31 +01:00
party = Party(self.company.party)
2018-10-18 09:31:22 +02:00
if self.type == 'out':
return party.account_payable_used
2015-11-18 08:14:31 +01:00
else:
return party.account_receivable_used
2015-11-18 08:14:31 +01:00
@property
def intercompany_type(self):
2018-10-18 09:31:22 +02:00
return 'in'
2015-11-18 08:14:31 +01:00
def get_intercompany_invoice(self):
2019-05-21 17:58:50 +02:00
Party = Pool().get('party.party')
2016-08-23 14:38:07 +02:00
if (self.type != 'out' or not self.target_company
2015-11-18 08:14:31 +01:00
or self.intercompany_invoices):
return
values = {}
2019-04-27 06:31:00 +02:00
for name, field in self.__class__._fields.items():
2015-11-18 08:14:31 +01:00
if (name in set(self._intercompany_excluded_fields) or
isinstance(field, fields.Function)):
continue
values[name] = getattr(self, name)
2018-10-18 09:31:22 +02:00
if not self.target_company.intercompany_user:
return
2019-05-21 17:58:50 +02:00
with Transaction().set_user(self.target_company.intercompany_user.id), \
Transaction().set_context(company=self.target_company.id,
_check_access=False):
2015-11-18 08:14:31 +01:00
invoice = self.__class__(**values)
invoice.type = self.intercompany_type
invoice.company = self.target_company
# Rebrowse party in order to pick the correct company context
invoice.party = Party(self.company.party)
invoice.state = 'draft'
invoice.reference = self.number
2016-09-26 09:43:46 +02:00
invoice.on_change_party()
2015-11-18 08:14:31 +01:00
invoice.account = self.get_intercompany_account()
invoice.set_journal()
2015-11-18 08:14:31 +01:00
lines = []
for line in self.lines:
2015-11-18 08:14:31 +01:00
lines.append(line.get_intercompany_line())
invoice.lines = lines
return invoice
def _credit(self, **values):
credit = super(Invoice, self)._credit(**values)
2015-11-18 08:14:31 +01:00
if self.target_company:
2019-02-02 07:38:17 +01:00
credit.target_company = self.target_company.id
return credit
2015-11-18 08:14:31 +01:00
2019-04-02 10:31:04 +02:00
class InvoiceLine(metaclass=PoolMeta):
2015-11-18 08:14:31 +01:00
__name__ = 'account.invoice.line'
_intercompany_excluded_fields = ['id', 'account', 'taxes', 'origin',
'party', 'invoice_type', 'company', 'create_date', 'create_uid',
'write_date', 'write_uid', 'intercompany_account']
intercompany_invoice = fields.Function(fields.Boolean(
'Intercompany Invoice'),
'on_change_with_intercompany_invoice')
intercompany_account = fields.Many2One('account.account.template',
'Intercompany Account',
domain=[
If(Bool(Eval('_parent_invoice')),
2016-08-23 14:38:07 +02:00
If(Eval('_parent_invoice', {}).get('type') == 'out',
2019-04-27 06:31:00 +02:00
('type.expense', '=', True),
2023-06-15 13:33:35 +02:00
('type.revenue', '=', True)),
2016-08-23 14:38:07 +02:00
If(Eval('invoice_type') == 'out',
2019-04-27 06:31:00 +02:00
('type.expense', '=', True),
('type.revenue', '=', True)))
2015-11-18 08:14:31 +01:00
],
states={
'invisible': If(Bool(Eval('_parent_invoice')),
~Bool(Eval('_parent_invoice', {}).get('target_company')),
~Eval('intercompany_invoice', False)),
'required': If(Bool(Eval('_parent_invoice')),
Bool(Eval('_parent_invoice', {}).get('target_company')),
Eval('intercompany_invoice', False)),
2015-11-18 08:14:31 +01:00
},
2023-06-15 13:33:35 +02:00
depends=['invoice'])
2015-11-18 08:14:31 +01:00
@classmethod
def __setup__(cls):
super(InvoiceLine, cls).__setup__()
if 'intercompany_invoice' not in cls.product.depends:
required = Bool(Eval('intercompany_invoice'))
old_required = cls.product.states.get('required')
if old_required:
required |= old_required
cls.product.states['required'] = required
2022-04-29 12:34:50 +02:00
cls.product.depends.add('intercompany_invoice')
2015-11-18 08:14:31 +01:00
@fields.depends('_parent_invoice.target_company', '_parent_invoice.type',
2018-10-18 09:31:22 +02:00
'invoice_type', 'invoice')
2015-11-18 08:14:31 +01:00
def on_change_product(self):
super(InvoiceLine, self).on_change_product()
2015-11-18 08:14:31 +01:00
type_ = self.invoice.type if self.invoice else self.invoice_type
if self.product and self.invoice and self.invoice.target_company:
account_name = 'account_%s_used' % ('revenue' if type_[:2] == 'in'
else 'expense')
account = getattr(self.product, account_name)
if account and account.template:
self.intercompany_account = account.template
2015-11-18 08:14:31 +01:00
@fields.depends('invoice', '_parent_invoice.target_company')
def on_change_with_intercompany_invoice(self, name=None):
return self.invoice and bool(self.invoice.target_company)
def get_intercompany_account(self):
target_company = self.invoice.target_company
# TODO: Check for account without template
return self.intercompany_account.get_syncronized_company_value(
target_company)
def get_intercompany_taxes(self):
pool = Pool()
Product = pool.get('product.product')
taxes = []
if not self.product:
return taxes
target_company = self.invoice.target_company
type = self.invoice.type if self.invoice else self.invoice_type
tax_name = '%s_taxes_used' % ('customer' if type[:2] == 'in'
else 'supplier')
2019-05-21 17:58:50 +02:00
with Transaction().set_user(target_company.intercompany_user.id), \
Transaction().set_context(company=target_company.id,
_check_access=False):
2017-07-12 12:38:29 +02:00
product = Product(self.product.id)
2015-11-18 08:14:31 +01:00
taxes = getattr(product, tax_name, [])
return taxes
def get_intercompany_line(self):
with Transaction().set_user(0):
line = self.__class__()
2019-05-21 17:58:50 +02:00
2019-04-27 06:31:00 +02:00
for name, field in self.__class__._fields.items():
2015-11-18 08:14:31 +01:00
if (name in set(self._intercompany_excluded_fields) or
isinstance(field, fields.Function)):
continue
setattr(line, name, getattr(self, name))
target_company = self.invoice.target_company
2019-05-21 17:58:50 +02:00
2018-10-18 09:31:22 +02:00
with Transaction().set_user(target_company.intercompany_user.id), \
2019-05-21 17:58:50 +02:00
Transaction().set_context(company=target_company.id,
_check_access=False):
2015-11-18 08:14:31 +01:00
line.invoice_type = self.invoice.intercompany_type
line.company = target_company
if self.party:
line.party = target_company.party
line.account = self.get_intercompany_account()
line.taxes = self.get_intercompany_taxes()
line.origin = self
return line
def _credit(self):
2019-02-02 07:38:17 +01:00
credit = super(InvoiceLine, self)._credit()
credit.intercompany_account = self.intercompany_account
return credit