docs: public void mergeProposedItem(final InvoiceItem invoiceItem) {
          Preconditions.checkState(!isBuilt, "Tree already built, unable to add new invoiceItem=%s", invoiceItem);
  
          // Check if it was an existing item ignored for tree purposes (e.g. FIXED or $0 RECURRING, both of which aren't repaired)
          final InvoiceItem existingItem = Iterables.tryFind(existingIgnoredItems, new Predicate<InvoiceItem>() {
              @Override
              public boolean apply(final InvoiceItem input) {
                  return input.matches(invoiceItem);
              }
          }).orNull();
          if (existingItem != null) {
              return;
          }
  
          switch (invoiceItem.getInvoiceItemType()) {
              case RECURRING:
                  // merged means we've either matched the proposed to an existing, or triggered a repair
                  final boolean merged = root.addProposedItem(new ItemsNodeInterval(root, new Item(invoiceItem, targetInvoiceId, ItemAction.ADD)));
                  if (!merged) {
                      items.add(new Item(invoiceItem, targetInvoiceId, ItemAction.ADD));
                  }
                  break;
  
              case FIXED:
                  remainingIgnoredItems.add(invoiceItem);
                  break;
  
              default:
                  Preconditions.checkState(false, "Unexpected proposed item " + invoiceItem);
          }
      }