diff --git a/README.md b/README.md
index fd9b4e16b3..634c9474ed 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Available addons
----------------
addon | version | maintainers | summary
--- | --- | --- | ---
-[fs_attachment](fs_attachment/) | 17.0.1.6.2 |
| Store attachments on external object store
+[fs_attachment](fs_attachment/) | 17.0.1.6.3 |
| Store attachments on external object store
[fs_attachment_s3](fs_attachment_s3/) | 17.0.1.3.0 |
| Store attachments into S3 complient filesystem
[fs_base_multi_image](fs_base_multi_image/) | 17.0.1.0.1 |
| Mulitple Images from External File System
[fs_base_multi_media](fs_base_multi_media/) | 17.0.1.0.0 |
| Give the possibility to store media data in external filesystem from odoo
diff --git a/fs_attachment/README.rst b/fs_attachment/README.rst
index 4ffab93960..9726d1fb59 100644
--- a/fs_attachment/README.rst
+++ b/fs_attachment/README.rst
@@ -11,7 +11,7 @@ Base Attachment Object Store
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:806b032758242b6859e59cb57234cd85eb759feee930b452c25b66f03906b429
+ !! source digest: sha256:8c9ea21e76a948d543936d567f2b8f23f236f520cb2c01d9bb1150cd10d62e3a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
diff --git a/fs_attachment/__manifest__.py b/fs_attachment/__manifest__.py
index 3d01164033..2e87fd9a2b 100644
--- a/fs_attachment/__manifest__.py
+++ b/fs_attachment/__manifest__.py
@@ -5,7 +5,7 @@
{
"name": "Base Attachment Object Store",
"summary": "Store attachments on external object store",
- "version": "17.0.1.6.2",
+ "version": "17.0.1.6.3",
"author": "Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)",
"license": "AGPL-3",
"development_status": "Beta",
diff --git a/fs_attachment/models/ir_attachment.py b/fs_attachment/models/ir_attachment.py
index faaa976faf..caae6959c4 100644
--- a/fs_attachment/models/ir_attachment.py
+++ b/fs_attachment/models/ir_attachment.py
@@ -474,6 +474,7 @@ def _enforce_meaningful_storage_filename(self) -> None:
Keeping the same meaning and mimetype is important to also ease to provide
a meaningful and SEO friendly URL to the file in the filesystem storage.
"""
+ renamed_attachments = {}
for attachment in self:
if not self._is_file_from_a_storage(attachment.store_fname):
continue
@@ -487,7 +488,17 @@ def _enforce_meaningful_storage_filename(self) -> None:
new_filename_with_path = os.path.join(
os.path.dirname(filename), new_filename
)
- fs.rename(filename, new_filename_with_path)
+
+ if filename in renamed_attachments:
+ if renamed_attachments[filename] == new_filename_with_path:
+ # we already renamed this file, no need to rename it again
+ continue
+ else:
+ fs.copy(renamed_attachments[filename], new_filename_with_path)
+ else:
+ fs.rename(filename, new_filename_with_path)
+ renamed_attachments[filename] = new_filename_with_path
+
attachment.fs_filename = new_filename
# we need to update the store_fname with the new filename by
# calling the write method of the field since the write method
diff --git a/fs_attachment/static/description/index.html b/fs_attachment/static/description/index.html
index 500c27011d..fa1a2c85c3 100644
--- a/fs_attachment/static/description/index.html
+++ b/fs_attachment/static/description/index.html
@@ -372,7 +372,7 @@
In some cases, you need to store attachment in another system that the diff --git a/fs_attachment/tests/test_fs_attachment.py b/fs_attachment/tests/test_fs_attachment.py index 75c7793cca..053f5f597a 100644 --- a/fs_attachment/tests/test_fs_attachment.py +++ b/fs_attachment/tests/test_fs_attachment.py @@ -428,6 +428,56 @@ def test_create_attachments_basic_user(self): } ) + def test_create_two_attachments(self): + self.temp_backend.use_as_default_for_attachments = True + res = self.ir_attachment_model.create( + [ + {"name": "test2.txt", "raw": b"content"}, + {"name": "test.txt", "raw": b"content"}, + ] + ) + self.assertEqual(len(res), 2) + + def test_create_two_attachments_same_name(self): + self.temp_backend.use_as_default_for_attachments = True + res = self.ir_attachment_model.create( + [ + {"name": "test.txt", "raw": b"content"}, + {"name": "test.txt", "raw": b"content"}, + ] + ) + self.assertEqual(len(res), 2) + attachment1 = res[0] + attachment2 = res[1] + self.assertEqual(attachment1.name, "test.txt") + self.assertEqual(attachment2.name, "test.txt") + self.assertNotEqual(attachment1.store_fname, attachment2.store_fname) + self.assertEqual(attachment1.raw, attachment2.raw) + + attachment1.raw = b"new content" + self.assertNotEqual(attachment1.raw, attachment2.raw) + + attachment2 = self.ir_attachment_model.browse(attachment2.id) + self.assertEqual(attachment2.raw, b"content") + + def test_create_two_attachments_differnt_call(self): + self.temp_backend.use_as_default_for_attachments = True + res = self.ir_attachment_model.create( + [ + {"name": "test.txt", "raw": b"content"}, + ] + ) + res2 = self.ir_attachment_model.create( + [ + {"name": "test.txt", "raw": b"content"}, + ] + ) + self.assertEqual(len(res), 1) + self.assertEqual(len(res2), 1) + + self.assertNotEqual(res[0].store_fname, res2[0].store_fname) + self.assertEqual(res[0].raw, res2[0].raw) + def test_update_png_to_svg(self): b64_data_png = ( b"iVBORw0KGgoAAAANSUhEUgAAADMAAAAhCAIAAAD73QTtAAAAA3NCSVQICAjb4U/gAA"