140 lines
6.5 KiB
Python
140 lines
6.5 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class AccountMove(models.Model):
|
|
_name = 'account.move'
|
|
_inherit = ['account.move', 'documents.unlink.mixin']
|
|
|
|
suspense_statement_line_id = fields.Many2one(
|
|
comodel_name='account.bank.statement.line',
|
|
string="Request document from a bank statement line",
|
|
index='btree_not_null',
|
|
)
|
|
|
|
has_documents = fields.Boolean(string='Has Documents', compute='_compute_has_documents')
|
|
|
|
@api.depends('attachment_ids')
|
|
def _compute_has_documents(self):
|
|
result = self.env['documents.document']._read_group(
|
|
domain=[('res_model', '=', self._name), ('res_id', 'in', self.ids)],
|
|
groupby=['res_id'],
|
|
)
|
|
has_documents_ids = {r[0] for r in result}
|
|
for move in self:
|
|
move.has_documents = move.id in has_documents_ids
|
|
|
|
def write(self, vals):
|
|
main_attachment_id = vals.get('message_main_attachment_id')
|
|
new_documents = [False] * len(self)
|
|
journals_changed = [('journal_id' in vals and move.journal_id.id != vals['journal_id']) for move in self]
|
|
moves_with_updated_partner = self.filtered(
|
|
lambda move: vals.get('partner_id', move.partner_id.id) != move.partner_id.id
|
|
)
|
|
for i, move in enumerate(self):
|
|
if main_attachment_id and not move.env.context.get('no_document') and move.move_type != 'entry':
|
|
previous_attachment_id = move.message_main_attachment_id.id
|
|
if previous_attachment_id == main_attachment_id:
|
|
continue
|
|
document = False
|
|
if previous_attachment_id:
|
|
# Search also in previous_attachment_ids to handle the case where
|
|
# the attachment is not the main one due to versioning
|
|
document = (
|
|
move.env["documents.document"]
|
|
.sudo()
|
|
.search([
|
|
"|",
|
|
("attachment_id", "=", previous_attachment_id),
|
|
("previous_attachment_ids", "in", previous_attachment_id),
|
|
], limit=1)
|
|
)
|
|
if document:
|
|
document.attachment_id = main_attachment_id
|
|
else:
|
|
new_documents[i] = True
|
|
res = super().write(vals)
|
|
for new_document, journal_changed, move in zip(new_documents, journals_changed, self):
|
|
if (new_document or journal_changed) and move.message_main_attachment_id:
|
|
move._update_or_create_document(move.message_main_attachment_id.id)
|
|
for move in moves_with_updated_partner:
|
|
move._sync_partner_on_document()
|
|
return res
|
|
|
|
def button_reconcile_with_st_line(self):
|
|
""" When using the "Reconciliation Request" next activity on the statement line's chatter, the invoice is linked
|
|
to this statement line through the 'suspense_statement_line_id' field.
|
|
When checking this link on your invoice, you are able to click on a button triggering this method opening the
|
|
bank reconciliation widget for this specific statement line to easily match it with the current invoice.
|
|
|
|
:return: An action opening the bank reconciliation widget.
|
|
"""
|
|
self.ensure_one()
|
|
st_line = self.suspense_statement_line_id
|
|
rec_pay_lines = self.line_ids.filtered(lambda x: x.account_id.account_type in ('asset_receivable', 'liability_payable'))
|
|
default_todo_command = ','.join(['add_new_amls'] + [str(x) for x in rec_pay_lines.ids])
|
|
return self.env['account.bank.statement.line']._action_open_bank_reconciliation_widget(
|
|
default_context={
|
|
'search_default_journal_id': st_line.journal_id.id,
|
|
'search_default_statement_id': st_line.statement_id.id,
|
|
'default_journal_id': st_line.journal_id.id,
|
|
'default_st_line_id': st_line.id,
|
|
'default_todo_command': default_todo_command,
|
|
},
|
|
)
|
|
|
|
def _update_or_create_document(self, attachment_id):
|
|
super()._update_or_create_document(attachment_id)
|
|
if not self.env['ir.attachment'].browse(attachment_id).res_model:
|
|
return
|
|
setting = self.env['documents.account.folder.setting'].sudo().search(
|
|
[('journal_id', '=', self.journal_id.id),
|
|
('company_id', '=', self.company_id.id)], limit=1)
|
|
if setting:
|
|
Documents_sudo = self.env['documents.document'].sudo()
|
|
doc_sudo = Documents_sudo.with_context(active_test=False).search(
|
|
[('attachment_id', '=', attachment_id)], limit=1)
|
|
values = {
|
|
'active': True, # if archived, unarchive it
|
|
'folder_id': setting.folder_id.id,
|
|
'partner_id': self.partner_id.id or doc_sudo.partner_id.id,
|
|
'owner_id': self.create_uid.active and self.create_uid.id,
|
|
'tag_ids': [(4, tag.id) for tag in setting.tag_ids],
|
|
}
|
|
if doc_sudo:
|
|
doc_sudo.write(values)
|
|
else:
|
|
# backward compatibility with documents that may be not
|
|
# registered as attachments yet
|
|
values.update({
|
|
"attachment_id": attachment_id,
|
|
"company_id": self.company_id.id,
|
|
})
|
|
doc_sudo.create(values)
|
|
|
|
def _sync_partner_on_document(self):
|
|
for move in self:
|
|
if not move.message_main_attachment_id:
|
|
continue
|
|
doc = self.env['documents.document'].sudo().search([('attachment_id', '=', move.message_main_attachment_id.id)], limit=1)
|
|
if not doc or doc.partner_id == move.partner_id:
|
|
continue
|
|
doc.partner_id = move.partner_id
|
|
|
|
def action_view_documents_account_move(self):
|
|
self.ensure_one()
|
|
domain = [('res_model', '=', self._name), ('res_id', '=', self.id)]
|
|
documents = self.env['documents.document'].search(domain)
|
|
default_user_folder_id = (
|
|
documents[0].user_folder_id
|
|
if len(set(documents.mapped('user_folder_id'))) == 1
|
|
else False
|
|
)
|
|
|
|
return {
|
|
**self.env['ir.actions.actions']._for_xml_id('documents.document_action_preference'),
|
|
'domain': domain,
|
|
'context': {'searchpanel_default_user_folder_id': default_user_folder_id},
|
|
}
|