def sync_all_children(self):
        # First, unclaim any child lines we've claimed in a previous call to this function.  They'll get reclaimed in this function if necessary.
        self.unclaimed_lines = list(set(self.unclaimed_lines + [line for line in self.final_lines if not line.traded and line.parent]))

        # This is a copy of self.final_lines, filtered down to not include child lines (except trades)
        # It's used to detect differences at the end from self.final_lines, and will get assigned there at the end.
        final_lines = [line for line in self.final_lines if line.traded or not line.parent]

        # This function can be called more than once in a row, which is necessary if more lines get added as a result
        # of child lines being added.  (You add a smoke package, it has a smoke detector, which requires the smoke service,
        # hypothetically has a smoke permit kit or something.)

        # A queue of lines that need their children synced.  Starts with final_lines which is the top and traded lines.
        lines_to_sync = list(final_lines)


        pos = 0
        while pos < len(lines_to_sync):
            # walk through each line and sync its children.  As children get added, they'll be queued onto
            # the end of this loop, so they can get their nested children.
            line = lines_to_sync[pos]
            pos += 1

            prod = self.products.get(line.code)
            if not prod:
                self.errors.append("Could not find product %r to sync child lines on line %r" % (line.code, line.pk))
                continue
            # Index the contents of this line's product.
            contents = self.by_code(self.product_contents.get(prod.code, []))

            # for every product in that product:
            for code, pc in contents.iteritems():
                child = self.reclaim_line(parent_id=line.pk, code=code, line_type='CHILD')
                if not child:
                    # If I wasn't able to reclaim an existing child, we need to make a new one for this agreement/line:
                    child = InvoiceLine(agreement=self.agreement, parent=line)

                # sync the child line with the product (gets updated quantity, product type, category, etc.)
                child.update_child(product=self.products[code], pc=pc, parent_line=line)

                # Save this child. It's either been updated or is new.
                child.save()

                # Queue up this line to be synced for ITS children
                lines_to_sync.append(child)
                # and add it to final_lines
                final_lines.append(child)

        # Did the final set of lines change from what we had before?
        changed = set(final_lines) ^ set(self.final_lines)

        # And then, assign the final set of children.
        self.final_lines = final_lines

        return bool(changed)
    def sync_all_children(self):
        # First, unclaim any child lines we've claimed in a previous call to this function.  They'll get reclaimed in this function if necessary.
        self.unclaimed_lines = list(
            set(self.unclaimed_lines + [
                line
                for line in self.final_lines if not line.traded and line.parent
            ]))

        # This is a copy of self.final_lines, filtered down to not include child lines (except trades)
        # It's used to detect differences at the end from self.final_lines, and will get assigned there at the end.
        final_lines = [
            line for line in self.final_lines if line.traded or not line.parent
        ]

        # This function can be called more than once in a row, which is necessary if more lines get added as a result
        # of child lines being added.  (You add a smoke package, it has a smoke detector, which requires the smoke service,
        # hypothetically has a smoke permit kit or something.)

        # A queue of lines that need their children synced.  Starts with final_lines which is the top and traded lines.
        lines_to_sync = list(final_lines)

        pos = 0
        while pos < len(lines_to_sync):
            # walk through each line and sync its children.  As children get added, they'll be queued onto
            # the end of this loop, so they can get their nested children.
            line = lines_to_sync[pos]
            pos += 1

            prod = self.products.get(line.code)
            if not prod:
                self.errors.append(
                    "Could not find product %r to sync child lines on line %r"
                    % (line.code, line.pk))
                continue
            # Index the contents of this line's product.
            contents = self.by_code(self.product_contents.get(prod.code, []))

            # for every product in that product:
            for code, pc in contents.iteritems():
                child = self.reclaim_line(parent_id=line.pk,
                                          code=code,
                                          line_type='CHILD')
                if not child:
                    # If I wasn't able to reclaim an existing child, we need to make a new one for this agreement/line:
                    child = InvoiceLine(agreement=self.agreement, parent=line)

                # sync the child line with the product (gets updated quantity, product type, category, etc.)
                child.update_child(product=self.products[code],
                                   pc=pc,
                                   parent_line=line)

                # Save this child. It's either been updated or is new.
                child.save()

                # Queue up this line to be synced for ITS children
                lines_to_sync.append(child)
                # and add it to final_lines
                final_lines.append(child)

        # Did the final set of lines change from what we had before?
        changed = set(final_lines) ^ set(self.final_lines)

        # And then, assign the final set of children.
        self.final_lines = final_lines

        return bool(changed)