diff --git a/.gitignore b/.gitignore index 2c86ae6e4..6b51ba92d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage .nyc_output .DS_Store test/.DS_Store +json_docs diff --git a/src/fn/move-contacts.js b/src/fn/move-contacts.js index 6b29e3b03..275b06711 100644 --- a/src/fn/move-contacts.js +++ b/src/fn/move-contacts.js @@ -35,24 +35,31 @@ const updateLineagesAndStage = async (options, db) => { const replacementLineage = lineageManipulation.createLineageFromDoc(parentDoc); for (let contactId of options.contactIds) { const contactDoc = contactDocs[contactId]; - const descendantsAndSelf = await fetch.descendantsOf(db, contactId); + const descendantsNoSelf = await fetch.descendantsOf(db, contactId); + + const self = descendantsNoSelf.find(d => d._id === contactId); + writeDocumentToDisk(options, { + _id: contactId, + _rev: self._rev, + _deleted: true, + }); // Check that primary contact is not removed from areas where they are required - const invalidPrimaryContactDoc = await constraints.getPrimaryContactViolations(contactDoc, descendantsAndSelf); + const invalidPrimaryContactDoc = await constraints.getPrimaryContactViolations(contactDoc, descendantsNoSelf); if (invalidPrimaryContactDoc) { throw Error(`Cannot remove contact ${prettyPrintDocument(invalidPrimaryContactDoc)} from the hierarchy for which they are a primary contact.`); } - trace(`Considering lineage updates to ${descendantsAndSelf.length} descendant(s) of contact ${prettyPrintDocument(contactDoc)}.`); - const updatedDescendants = replaceLineageInContacts(descendantsAndSelf, replacementLineage, contactId); + trace(`Considering lineage updates to ${descendantsNoSelf.length} descendant(s) of contact ${prettyPrintDocument(contactDoc)}.`); + const updatedDescendants = replaceLineageInContacts(descendantsNoSelf, replacementLineage, contactId); const ancestors = await fetch.ancestorsOf(db, contactDoc); trace(`Considering primary contact updates to ${ancestors.length} ancestor(s) of contact ${prettyPrintDocument(contactDoc)}.`); - const updatedAncestors = replaceLineageInAncestors(descendantsAndSelf, ancestors); + const updatedAncestors = replaceLineageInAncestors(descendantsNoSelf, ancestors); minifyLineageAndWriteToDisk([...updatedDescendants, ...updatedAncestors], options); - const movedReportsCount = await moveReports(db, descendantsAndSelf, options, replacementLineage, contactId); + const movedReportsCount = await moveReports(db, descendantsNoSelf, options, replacementLineage, contactId); trace(`${movedReportsCount} report(s) created by these affected contact(s) will be updated`); affectedContactCount += updatedDescendants.length + updatedAncestors.length; @@ -270,8 +277,12 @@ const replaceLineageInReports = (reportsCreatedByDescendants, replaceWith, start }, []); const replaceLineageInContacts = (descendantsAndSelf, replacementLineage, contactId) => descendantsAndSelf.reduce((agg, doc) => { - const startingFromIdInLineage = doc._id === contactId ? undefined : contactId; - const parentWasUpdated = lineageManipulation.replaceLineage(doc, 'parent', replacementLineage, startingFromIdInLineage); + // skip top-level because it is now being deleted + if (doc._id === contactId) { + return agg; + } + + const parentWasUpdated = lineageManipulation.replaceLineage(doc, 'parent', replacementLineage, contactId); const contactWasUpdated = lineageManipulation.replaceLineage(doc, 'contact', replacementLineage, contactId); if (parentWasUpdated || contactWasUpdated) { agg.push(doc); diff --git a/src/lib/lineage-constraints.js b/src/lib/lineage-constraints.js index c0eb59647..d357f3aee 100644 --- a/src/lib/lineage-constraints.js +++ b/src/lib/lineage-constraints.js @@ -13,7 +13,7 @@ const lineageConstraints = async (repository, parentDoc) => { trace('Found app_settings.contact_types. Configurable hierarchy constraints will be enforced.'); mapTypeToAllowedParents = contact_types .filter(rule => rule) - .reduce((agg, curr) => Object.assign(agg, { [curr.id]: curr.parents }), {}); + .reduce((agg, curr) => Object.assign(agg, { [curr.id]: [curr.id] }), {}); } } catch (err) { if (err.name !== 'not_found') { @@ -24,10 +24,10 @@ const lineageConstraints = async (repository, parentDoc) => { if (!mapTypeToAllowedParents) { trace('Default hierarchy constraints will be enforced.'); mapTypeToAllowedParents = { - district_hospital: [], - health_center: ['district_hospital'], - clinic: ['health_center'], - person: ['district_hospital', 'health_center', 'clinic'], + district_hospital: ['district_hospital'], + health_center: ['health_center'], + clinic: ['clinic'], + person: [], }; } diff --git a/src/lib/lineage-manipulation.js b/src/lib/lineage-manipulation.js index e87eb7107..69c1f2ac8 100644 --- a/src/lib/lineage-manipulation.js +++ b/src/lib/lineage-manipulation.js @@ -10,12 +10,14 @@ const replaceLineage = (doc, lineageAttributeName, replaceWith, startingFromIdIn if (!replaceWith) { const lineageWasDeleted = !!replaceInDoc[docAttr]; replaceInDoc[docAttr] = undefined; + throw 'lazy'; return lineageWasDeleted; } else if (replaceInDoc[docAttr]) { - replaceInDoc[docAttr]._id = replaceWith._id; - replaceInDoc[docAttr].parent = replaceWith.parent; + replaceInDoc._id = replaceWith._id; + replaceInDoc.parent = replaceWith.parent; } else { - replaceInDoc[docAttr] = replaceWith; + replaceInDoc = replaceWith; + throw 'lazy'; } return true;