Here is a query that will identify the bibs in your database: select bmp.record,acpm.target_copy,acn.record from asset.copy ac, asset.call_number acn, asset.copy_part_map acpm, biblio.record_entry bre, biblio.monograph_part bmp where bre.id!=bmp.record and ac.call_number=acn.id and bmp.id=acpm.part and bre.id=acn.record and acpm.target_copy=ac.id and not bre.deleted and not acn.deleted and not ac.deleted and acn.record>0 limit 100 These are copies with parts where one of the parts point to a bib that is not equal to the call_number.record for the copy. In these cases, they are repeated in the OPAC. Running some experiments: 1. Pick a bib X 2. Pick a bib Y 3. Create a part for bib X (vol1) 4. Create a copy on bib X with part assigned vol1 and call number 1234 5. Repeat 3-4 with bib Y 6. Transfer volume from bib X to bib Y 7. The result will not move the part. Copy from bib X still points to the part for bib X 8. Refresh bib Y, right click the newly moved item and "Replace Barcode" 9. Select vol1 from the part dropdown and click "Re-Barcode / Update Items" 10. You will now have two rows in asset.copy_part_map one pointing to the old bib and one pointing to the current bib This will result in duplicate rows in the OPAC The issue with moving items is: var robj = network.simple_request( 'FM_ACP_FLESHED_BATCH_UPDATE', [ ses(), copies, true ], null, { 'title' : $("catStrings").getString('staff.cat.util.transfer_copies.override_transfer_failure'), 'overridable_events' : [ 1208 /* TITLE_LAST_COPY */, 1227 /* COPY_DELETE_WARNING */, ] } ); There needs to be logic to handle the part. And here is the code for the volume transfer: var robj = obj.network.simple_request( 'FM_ACN_TRANSFER', [ ses(), { 'docid' : obj.data.marked_library.docid, 'lib' : obj.data.marked_library.lib, 'volumes' : list } ], null, { 'title' : document.getElementById('catStrings').getString('staff.cat.copy_browser.transfer.override.failure'), 'overridable_events' : [ 1208 /* TITLE_LAST_COPY */, 1219 /* COPY_REMOTE_CIRC_LIB */, ], } ); and I think that the perl code could handle it instead of the JS: Cat.pm batch_volume_transfer OR* we just don't care that the part_map remains in the database and we handle the display better in the OPAC. Watch out for holds on these parts! select * from action.hold_request where target in( select acpm.part from asset.copy ac, asset.call_number acn, asset.copy_part_map acpm, biblio.record_entry bre, biblio.monograph_part bmp where bre.id!=bmp.record and ac.call_number=acn.id and bmp.id=acpm.part and bre.id=acn.record and acpm.target_copy=ac.id and not bre.deleted and not acn.deleted and not ac.deleted and acn.record>0 ) and hold_type='P' and capture_time is null and cancel_time is null In case you are considering deleting those stale part maps