diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index 4ae77f46b0..2f783f29c4 100644 --- a/chrome/content/zotero/xpcom/data/items.js +++ b/chrome/content/zotero/xpcom/data/items.js @@ -820,7 +820,21 @@ Zotero.Items = function() { // Add tags to master var tags = otherItem.getTags(); for (let j = 0; j < tags.length; j++) { - item.addTag(tags[j].tag); + let tagName = tags[j].tag; + if (item.hasTag(tagName)) { + let type = item.getTagType(tagName); + // If existing manual tag, leave that + if (type == 0) { + continue; + } + // Otherwise, add the non-master item's tag, which may be manual, in which + // case it will remain at the end + item.addTag(tagName, tags[j].type); + } + // If no existing tag, add with the type from the non-master item + else { + item.addTag(tagName, tags[j].type); + } } // Add relation to track merge diff --git a/test/tests/itemsTest.js b/test/tests/itemsTest.js index eedf08d89d..ec16ea440c 100644 --- a/test/tests/itemsTest.js +++ b/test/tests/itemsTest.js @@ -200,6 +200,32 @@ describe("Zotero.Items", function () { assert.equal(item1.dateAdded, '2019-01-01 00:00:00'); }); + it("should keep automatic tag on non-master item as automatic", async function () { + var item1 = await createDataObject('item', { tags: [{ tag: 'A' }] }); + var item2 = await createDataObject('item', { tags: [{ tag: 'B', type: 1 }] }); + await Zotero.Items.merge(item1, [item2]); + var tags = item1.getTags(); + var tag = tags.find(x => x.tag == 'B'); + assert.propertyVal(tag, 'type', 1); + }); + + it("should skip automatic tag on non-master item that exists as manual tag on master", async function () { + var item1 = await createDataObject('item', { tags: [{ tag: 'A' }, { tag: 'B' }] }); + var item2 = await createDataObject('item', { tags: [{ tag: 'B', type: 1 }] }); + await Zotero.Items.merge(item1, [item2]); + var tags = item1.getTags(); + var tag = tags.find(x => x.tag == 'B'); + assert.notProperty(tag, 'type'); + }); + + it("should keep automatic tag on master if it also exists on non-master item", async function () { + var item1 = await createDataObject('item', { tags: [{ tag: 'B', type: 1 }] }); + var item2 = await createDataObject('item', { tags: [{ tag: 'B', type: 1 }] }); + await Zotero.Items.merge(item1, [item2]); + var tags = item1.getTags(); + assert.propertyVal(tags[0], 'type', 1); + }); + it("should merge two items when servant is linked to an item absent from cache", function* () { // two group libraries var groupOneInfo = yield createGroup({