Files
enterprise-addons/documents_project/tests/test_documents.py
T

246 lines
11 KiB
Python

# -*- coding: utf-8 -*-
import base64
from odoo.exceptions import UserError
from odoo.tests.common import users
from odoo.addons.documents.tests.test_documents_common import TransactionCaseDocuments
from odoo.addons.project.tests.test_project_base import TestProjectCommon
GIF = b"R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs="
TEXT = base64.b64encode(bytes("workflow bridge project", 'utf-8'))
class TestDocumentsBridgeProject(TestProjectCommon, TransactionCaseDocuments):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.document_txt_2 = cls.env['documents.document'].create({
'datas': TEXT,
'name': 'file2.txt',
'mimetype': 'text/plain',
'folder_id': cls.folder_a_a.id,
})
cls.pro_admin = cls.env['res.users'].create({
'name': 'Project Admin',
'login': 'proj_admin',
'email': '[email protected]',
'group_ids': [(4, cls.env.ref('project.group_project_manager').id)],
})
def test_archive_folder_on_projects_unlinked(self):
"""
Projects folders should be archived if every related projects have been unlinked.
"""
folder_1, folder_2, folder_3, folder_4 = self.env['documents.document'].create(
[{'name': f'F{i}', 'type': 'folder'} for i in range(4)]
)
(
project_0, project_1, project_2, project_3,
project_4, project_5, project_6, _,
) = self.env['project.project'].create([
{'name': f'p{i}', 'documents_folder_id': folder.id}
for i, folder in enumerate([folder_1] * 4 + [folder_2] * 3 + [folder_3])
])
cases = [
(
project_1 | project_4,
folder_1 | folder_2 | folder_3 | folder_4,
self.env['documents.document']
), (
project_2,
folder_1 | folder_2 | folder_3 | folder_4,
self.env['documents.document']
), (
project_0 | project_3 | project_5 | project_6,
folder_3 | folder_4,
folder_1 | folder_2
),
]
count_start = self.env['documents.document'].search_count([('active', '=', True)])
for projects_to_unlink, active_folders, inactive_folders in cases:
with self.subTest(projects_to_unlink=projects_to_unlink, active_folders=active_folders, inactive_folders=inactive_folders):
projects_to_unlink.unlink()
self.assertTrue(all(active_folders.mapped('active')))
self.assertFalse(any(inactive_folders.mapped('active')))
count_end = self.env['documents.document'].search_count([('active', '=', True)])
self.assertEqual(count_end, count_start - 2)
def test_bridge_parent_folder(self):
"""
Tests the "Parent Folder" setting
"""
parent_folder = self.env.company.documents_project_folder_id
self.assertEqual(self.project_pigs.documents_folder_id.folder_id, parent_folder, "The folder of the project should be a child of the 'Projects' folder.")
def test_project_folder_creation(self):
project = self.env['project.project'].create({
'name': 'Project',
})
self.assertTrue(project.documents_folder_id, "A folder should be created for the project")
def test_project_task_access_document(self):
"""
Tests that 'MissingRecord' error should not be raised when trying to switch
folder for a non-existing document.
- The 'active_id' here is the 'id' of a non-existing document.
- We then try to access 'All' folder by calling the 'search_panel_select_range'
method. We should be able to access the folder.
"""
missing_id = self.env['documents.document'].search([], order='id DESC', limit=1).id + 1
result = self.env['documents.document'].with_context(
active_id=missing_id, active_model='project.task').search_panel_select_range('user_folder_id')
self.assertTrue(result)
def test_copy_project(self):
"""
When duplicating a project, there should be exactly one copy of the folder linked to the project.
If there is the `no_create_folder` context key, then the folder should not be copied (note that in normal flows,
when this context key is used, it is expected that a folder will be copied/created manually, so that we don't
end up with a project having the documents feature enabled but no folder).
"""
last_folder_id = self.env['documents.document'].search([('type', '=', 'folder')], order='id desc', limit=1).id
self.project_pigs.copy()
new_folder = self.env['documents.document'].search([('type', '=', 'folder'), ('id', '>', last_folder_id)])
self.assertEqual(len(new_folder), 1, "There should only be one new folder created.")
self.project_goats.with_context(no_create_folder=True).copy()
self.assertEqual(self.env['documents.document'].search_count(
[('type', '=', 'folder'), ('id', '>', new_folder.id)], limit=1),
0,
"There should be no new folder created."
)
@users('proj_admin')
def test_rename_project(self):
"""
When renaming a project, the corresponding folder should be renamed as well.
Even when the user does not have write access on the folder, the project should be able to rename it.
"""
new_name = 'New Name'
self.project_pigs.with_user(self.env.user).name = new_name
self.assertEqual(self.project_pigs.documents_folder_id.name, new_name, "The folder should have been renamed along with the project.")
def test_delete_project_folder(self):
"""
It should not be possible to delete the "Projects" folder.
"""
project_folder = self.env.ref('documents_project.document_project_folder')
with self.assertRaises(UserError, msg="It should not be possible to delete the 'Projects' folder"):
project_folder.unlink()
current = project_folder
for i in range(3):
current.folder_id = self.env['documents.document'].create({
"name": f"Ancestor Test {i}",
"type": "folder",
})
current = current.folder_id
with self.assertRaises(UserError, msg="It should not be possible to delete an ancestor of the 'Projects' folder"):
current.unlink()
# But it shouldn't interfere with legit deletion/archiving
project_folder.action_update_access_rights(
access_internal='none', access_via_link='none',
partners={self.doc_user.partner_id.id: (False, False)})
project_folder.invalidate_recordset(fnames=['parent_path'])
self.document_txt.with_user(self.doc_user).action_archive()
self.assertFalse(self.document_txt.active)
self.document_txt.with_user(self.doc_user).unlink()
self.assertFalse(self.document_txt.exists())
def test_changing_project_folder_moves_documents(self):
"""
When a project folder changes, move documents.
"""
project = self.env['project.project'].create({'name': 'Project'})
project_document, other_folder = self.env['documents.document'].create([{
'name': 'Test project request',
'folder_id': project.documents_folder_id.id,
}, {
'name': 'Other Folder',
'type': 'folder',
'folder_id': self.env.ref('documents_project.document_project_folder').id,
}])
project.invalidate_recordset()
self.assertEqual(project_document, project.document_ids)
project.documents_folder_id = other_folder
self.assertEqual(project_document.folder_id, other_folder)
self.assertEqual(project_document, project.document_ids)
def test_delete_folder_used_by_project(self):
"""
It shouldn't be possible to delete a folder that is used by one or multiple projects.
"""
project = self.env['project.project'].create({
'name': "Test",
})
with self.assertRaises(UserError):
project.documents_folder_id.unlink()
project_1, project_2, project_3 = self.env['project.project'].create([{
'name': f"Test Project {i}",
} for i in range(3)])
project_2.documents_folder_id = project_3.documents_folder_id
(project_1 | project_2).documents_folder_id.folder_id = parent_folder = self.env['documents.document'].create({
'name': "Test Folder",
'type': 'folder',
})
with self.assertRaises(UserError, msg="It shouldn't be possible to delete a folder that is used by one or multiple projects."):
parent_folder.unlink()
def test_add_attachment_to_project_folder_id(self):
project = self.env['project.project'].create({'name': "Test Project"})
self.assertTrue(project.documents_folder_id)
task = self.env['project.task'].create({'name': "Test Task", 'project_id': project.id})
project_attachment, task_attachment = self.env['ir.attachment'].create([
{"name": "project_image.png", "datas": GIF, "res_model": "project.project", "res_id": project.id},
{"name": "task_image.png", "datas": GIF, "res_model": "project.task", "res_id": task.id},
])
for attachment in (project_attachment, task_attachment):
self.assertEqual(attachment.get_documents_operation_add_destination(), {
'destination': str(project.documents_folder_id.id),
'display_name': project.documents_folder_id.display_name,
})
def test_project_copy_includes_linked_documents(self):
"""
Test that copying a project also copies its linked document.
Steps:
1. Link a document to the project (set folder and res_id).
2. Copy the project.
3. Check that the copied project has one linked document.
"""
self.document_txt_2.write({
'folder_id': self.project_pigs.documents_folder_id.id,
'res_id': self.project_pigs.id,
})
copied_project_documents = self.project_pigs.copy().document_ids
self.assertEqual(
len(copied_project_documents), 1,
"The copied project should have one document copied."
)
def test_project_document_unarchive_on_revert(self):
""" Ensure project documents are unarchived when a project is reverted
Steps:
1. Create a project
2. Convert the project into a project template
3. Undo the project
4. Check project documents
"""
client_action = self.project_pigs.action_create_template_from_project()
self.env['project.project'].browse(client_action['params']['project_id']).unlink()
self.project_pigs.create_template_from_project_undo_callback(
client_action['params']['callback_data']['args'][1]
)
self.project_pigs.action_create_template_from_project()