129 lines
8.8 KiB
Python
129 lines
8.8 KiB
Python
from contextlib import contextmanager
|
|
from datetime import datetime
|
|
from freezegun import freeze_time
|
|
from unittest.mock import patch
|
|
|
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
|
from odoo.addons.documents.tests.test_documents_common import GIF, TEXT
|
|
from odoo.tests import RecordCapturer
|
|
|
|
PDF = 'JVBERi0xLjYNJeLjz9MNCjI0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9GaXJzdCA0L0xlbmd0aCAyMTYvTiAxL1R5cGUvT2JqU3RtPj5zdHJlYW0NCmjePI9RS8MwFIX/yn1bi9jepCQ6GYNpFBTEMsW97CVLbjWYNpImmz/fVsXXcw/f/c4SEFarepPTe4iFok8dU09DgtDBQx6TMwT74vaLTE7uSPDUdXM0Xe/73r1FnVwYYEtHR6d9WdY3kX4ipRMV6oojSmxQMoGyac5RLBAXf63p38aGA7XPorLewyvFcYaJile8rB+D/YcwiRdMMGScszO8/IW0MdhsaKKYGA46gXKTr/cUQVY4We/cYMNpnLVeXPJUXHs9fECr7kAFk+eZ5Xr9LcAAfKpQrA0KZW5kc3RyZWFtDWVuZG9iag0yNSAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvRmlyc3QgNC9MZW5ndGggNDkvTiAxL1R5cGUvT2JqU3RtPj5zdHJlYW0NCmjeslAwULCx0XfOL80rUTDU985MKY42NAIKBsXqh1QWpOoHJKanFtvZAQQYAN/6C60NCmVuZHN0cmVhbQ1lbmRvYmoNMjYgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0ZpcnN0IDkvTGVuZ3RoIDQyL04gMi9UeXBlL09ialN0bT4+c3RyZWFtDQpo3jJTMFAwVzC0ULCx0fcrzS2OBnENFIJi7eyAIsH6LnZ2AAEGAI2FCDcNCmVuZHN0cmVhbQ1lbmRvYmoNMjcgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0ZpcnN0IDUvTGVuZ3RoIDEyMC9OIDEvVHlwZS9PYmpTdG0+PnN0cmVhbQ0KaN4yNFIwULCx0XfOzytJzSspVjAyBgoE6TsX5Rc45VdEGwB5ZoZGCuaWRrH6vqkpmYkYogGJRUCdChZgfUGpxfmlRcmpxUAzA4ryk4NTS6L1A1zc9ENSK0pi7ez0g/JLEktSFQz0QyoLUoF601Pt7AACDADYoCeWDQplbmRzdHJlYW0NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDM1MjUvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjQtYzAwNSA3OC4xNDczMjYsIDIwMTIvMDgvMjMtMTM6MDM6MDMgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnBkZj0iaHR0cDovL25zLmFkb2JlLmNvbS9wZGYvMS4zLyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPHBkZjpQcm9kdWNlcj5BY3JvYmF0IERpc3RpbGxlciA2LjAgKFdpbmRvd3MpPC9wZGY6UHJvZHVjZXI+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDA2LTAzLTA2VDE1OjA2OjMzLTA1OjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZVBTNS5kbGwgVmVyc2lvbiA1LjIuMjwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNi0wNy0xNVQxMDoxMjoyMSswODowMDwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTYtMDctMTVUMTA6MTI6MjErMDg6MDA8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnV1aWQ6ZmYzZGNmZDEtMjNmYS00NzZmLTgzOWEtM2U1Y2FlMmRhMmViPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD51dWlkOjM1OTM1MGIzLWFmNDAtNGQ4YS05ZDZjLTAzMTg2YjRmZmIzNjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPGRjOmZvcm1hdD5hcHBsaWNhdGlvbi9wZGY8L2RjOmZvcm1hdD4KICAgICAgICAgPGRjOnRpdGxlPgogICAgICAgICAgICA8cmRmOkFsdD4KICAgICAgICAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij5CbGFuayBQREYgRG9jdW1lbnQ8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGk+RGVwYXJ0bWVudCBvZiBKdXN0aWNlIChFeGVjdXRpdmUgT2ZmaWNlIG9mIEltbWlncmF0aW9uIFJldmlldyk8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgog' + 682 * 'ICAg' + 'Cjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMTEgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZUxhYmVscyA2IDAgUi9QYWdlcyA4IDAgUi9UeXBlL0NhdGFsb2c+Pg1lbmRvYmoNMjMgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAxMD4+c3RyZWFtDQpIiQIIMAAAAAABDQplbmRzdHJlYW0NZW5kb2JqDTI4IDAgb2JqDTw8L0RlY29kZVBhcm1zPDwvQ29sdW1ucyA0L1ByZWRpY3RvciAxMj4+L0ZpbHRlci9GbGF0ZURlY29kZS9JRFs8REI3Nzc1Q0NFMjI3RjZCMzBDNDQwREY0MjIxREMzOTA+PEJGQ0NDRjNGNTdGNjEzNEFCRDNDMDRBOUU0Q0ExMDZFPl0vSW5mbyA5IDAgUi9MZW5ndGggODAvUm9vdCAxMSAwIFIvU2l6ZSAyOS9UeXBlL1hSZWYvV1sxIDIgMV0+PnN0cmVhbQ0KaN5iYgACJjDByGzIwPT/73koF0wwMUiBWYxA4v9/EMHA9I/hBVCxoDOQeH8DxH2KrIMIglFwIpD1vh5IMJqBxPpArHYgwd/KABBgAP8bEC0NCmVuZHN0cmVhbQ1lbmRvYmoNc3RhcnR4cmVmDQo0NTc2DQolJUVPRg0K'
|
|
|
|
|
|
class DocumentsAccountHelpersCommon:
|
|
@classmethod
|
|
@contextmanager
|
|
def _freeze_time(cls, time):
|
|
time_format = "%Y-%m-%d %H:%M" if ':' in time else "%Y-%m-%d"
|
|
with patch.object(cls.env.cr, 'now', lambda: datetime.strptime(time, time_format)), freeze_time(time):
|
|
yield
|
|
|
|
@classmethod
|
|
def setup_sync_journal_folder(cls, journal, folder, company=False):
|
|
"""Update or create configuration for journal document synchronization."""
|
|
if setting := cls.env['documents.account.folder.setting'].search(
|
|
[('journal_id', '=', journal.id), ('company_id', '=', company.id if company else cls.env.company.id)]):
|
|
setting.folder_id = folder
|
|
return setting
|
|
setting = cls.env['documents.account.folder.setting'].create({
|
|
'folder_id': folder.id,
|
|
'journal_id': journal.id,
|
|
})
|
|
return setting
|
|
|
|
|
|
class DocumentsAccountTestCommon(AccountTestInvoicingCommon, DocumentsAccountHelpersCommon):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.env.user.group_ids += cls.quick_ref('documents.group_documents_manager')
|
|
|
|
cls.folder_a = cls.env['documents.document'].create({
|
|
'name': 'folder A',
|
|
'type': 'folder',
|
|
})
|
|
cls.folder_a_a = cls.env['documents.document'].create({
|
|
'name': 'folder A - A',
|
|
'folder_id': cls.folder_a.id,
|
|
'type': 'folder',
|
|
})
|
|
cls.document_txt = cls.env['documents.document'].create({
|
|
'datas': TEXT,
|
|
'name': 'file.txt',
|
|
'mimetype': 'text/plain',
|
|
'folder_id': cls.folder_a_a.id,
|
|
})
|
|
cls.document_gif = cls.env['documents.document'].create({
|
|
'datas': GIF,
|
|
'name': 'file.gif',
|
|
'mimetype': 'image/gif',
|
|
'folder_id': cls.folder_a.id,
|
|
})
|
|
cls.document_pdf = cls.env['documents.document'].create({
|
|
'datas': PDF,
|
|
'name': 'file.pdf',
|
|
'mimetype': 'application/pdf',
|
|
'folder_id': cls.folder_a.id,
|
|
})
|
|
|
|
|
|
class DocumentsAccountActionsServerCommon(DocumentsAccountTestCommon):
|
|
"""Specialized class for testing Server Actions"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.finance_folder = cls.env.ref('documents.document_finance_folder')
|
|
cls.pdf_in_finance_folder = cls.document_pdf.copy()
|
|
cls.pdf_in_finance_folder.folder_id = cls.finance_folder
|
|
|
|
cls.journal_type_labels = dict(
|
|
cls.env['account.journal'].fields_get(['type'], ['selection'])['type']['selection']
|
|
)
|
|
|
|
target_journals = {
|
|
'sale': 'Sales',
|
|
'purchase': 'Purchases',
|
|
'bank': 'Bank',
|
|
'general': 'Miscellaneous Operations',
|
|
'cash': 'Cash',
|
|
'credit': 'Credit Journal',
|
|
}
|
|
|
|
all_journals = cls.env['account.journal'].search(
|
|
cls.env['account.journal']._check_company_domain(cls.env.company)
|
|
)
|
|
|
|
cls.journals_by_type = {}
|
|
for j_type, name in target_journals.items():
|
|
journal = all_journals.filtered(lambda j: j.type == j_type and j.name == name)
|
|
if len(journal) != 1:
|
|
raise AssertionError(
|
|
f"Test setup failed: Expected exactly 1 journal with type='{j_type}' and name='{name}'. "
|
|
f"Found {len(journal)}."
|
|
)
|
|
cls.journals_by_type[j_type] = journal
|
|
|
|
cls.actions_by_type = cls.env['account.journal']._documents_get_embed_on_sync_actions()
|
|
assert cls.actions_by_type
|
|
|
|
def _run_action_and_assert_sync(self, action, document, expected_journal):
|
|
"""Execute action and assert that the document/record are correctly linked and synced."""
|
|
target_model = 'account.bank.statement' if expected_journal.type == 'bank' else 'account.move'
|
|
with RecordCapturer(self.env[target_model], []) as capture:
|
|
action.with_context(active_model='documents.document', active_id=document.id).run()
|
|
|
|
created_record = capture.records
|
|
self.assertEqual(len(created_record), 1)
|
|
self.assertEqual(document.res_model, created_record._name)
|
|
self.assertEqual(document.res_id, created_record.id)
|
|
self.assertEqual(created_record.message_main_attachment_id, document.attachment_id)
|
|
self.assertEqual(created_record.journal_id, expected_journal)
|
|
|
|
# Bank statements will only be synced correctly from 19.1.
|
|
if created_record._name != 'account.bank.statement':
|
|
expected_folder = self.journal_type_labels[expected_journal.type]
|
|
self.assertEqual(document.folder_id.name, expected_folder)
|
|
self.assertIn(document.tag_ids.name, expected_journal.name)
|