def update_invoice_lines(self):

        # The incoming invoice lines should look like:
        '''
        [{
            'code': 'COPPER',
            'quantity': 1
            'traded': true // or not present.
        }, ...]
        '''

        # coerce the incoming invoice lines:
        incoming_lines = [
            IL(line, updater=self) for line in self.blob['invoice_lines']
        ]
        # If the IL constructor put any errors in, stop now.
        if self.errors:
            return

        # Next get every existing invoice line.
        self.existing_lines = list(self.agreement.invoice_lines.all())
        self.unclaimed_lines = list(
            self.existing_lines
        )  # Unclaim all existing lines.  We may reclaim them soon.

        # this is the list of lines that will be on the agreement at the end:
        self.final_lines = []

        for il in list(incoming_lines):
            if not il.price:
                incoming_lines.remove(il)
                self.messages.append(
                    '%s removed from the agreement because it is not available.'
                    % il.code)
                continue
            available = getattr(il.price, 'available_mask', None)
            if available is None:
                available = il.price.available
            if not available:
                incoming_lines.remove(il)
                self.messages.append(
                    '%s removed from the agreement because it is no longer available.'
                    % il.code)
                continue

        # First, do every line that came in from the system NOT traded.
        # Invoice Lines for these should all be TOP.
        # (This includes package, monitoring, alacarte, but not children, mandatory services...)
        for il in incoming_lines:
            if not il.line_type == 'TOP':
                continue
            # il is a fakey line.
            line = self.reclaim_line(code=il.code, line_type='TOP')
            if not line:
                line = InvoiceLine(agreement=self.agreement)
            line.update_top(product=il.product,
                            quantity=il.quantity,
                            price=il.price,
                            pricedate=self.agreement.pricedate)

            self.final_lines.append(line)

        # Now do it again for trade lines.
        for il in incoming_lines:
            if not il.line_type == 'TRADE':
                continue

            line = self.reclaim_line(code=il.code, line_type='TRADE')
            if not line:
                line = InvoiceLine(agreement=self.agreement)

            line.update_trade(product=il.product,
                              quantity=il.quantity,
                              price=il.price,
                              pricedate=self.agreement.pricedate)

            self.final_lines.append(line)

        # Save any lines in final_lines because they'll need pks for their children.
        for line in self.final_lines:
            line.save()

        print "Final lines BEFORE CHILD SYNC:"
        for line in self.final_lines:
            print "{}, {}, {}".format(line.code, line.quantity, line.traded)

        # loop through, adding mandatory items and syncing children until no changes are made.
        loops = 0
        self.sync_all_children()
        while True:
            # Next, we need to process mandatory items.
            changed = self.add_mandatory_items()

            # Then, sync children again.
            changed = self.sync_all_children() or changed

            if not changed:
                break

            loops += 1
            if loops >= 10:
                self.errors.append(
                    "Probably an infinite loop in mandatories/children.  Needs fixin."
                )
                break

        # Next reclaim/create a permit line if needed.
        self.add_permit_lines()

        self.sanity_check()

        print "Final lines:"
        for line in self.final_lines:
            print "{}, {}, {}".format(line.code, line.quantity, line.traded)

        # Finally, any lines that are in existing_lines but not in final_lines should be deleted.
        for orphan in self.existing_lines:
            if orphan in self.final_lines:
                continue
            orphan.delete()
    def update_invoice_lines(self):

        # The incoming invoice lines should look like:
        '''
        [{
            'code': 'COPPER',
            'quantity': 1
            'traded': true // or not present.
        }, ...]
        '''

        # coerce the incoming invoice lines:
        incoming_lines = [IL(line, updater=self) for line in self.blob['invoice_lines']]
        # If the IL constructor put any errors in, stop now.
        if self.errors:
            return

        # Next get every existing invoice line.
        self.existing_lines = list(self.agreement.invoice_lines.all())
        self.unclaimed_lines = list(self.existing_lines)  # Unclaim all existing lines.  We may reclaim them soon.

        # this is the list of lines that will be on the agreement at the end:
        self.final_lines = []

        for il in list(incoming_lines):
            if not il.price:
                incoming_lines.remove(il)
                self.messages.append('%s removed from the agreement because it is not available.' % il.code)
                continue
            available = getattr(il.price, 'available_mask', None)
            if available is None:
                available = il.price.available
            if not available:
                incoming_lines.remove(il)
                self.messages.append('%s removed from the agreement because it is no longer available.' % il.code)
                continue



        # First, do every line that came in from the system NOT traded.
        # Invoice Lines for these should all be TOP.
        # (This includes package, monitoring, alacarte, but not children, mandatory services...)
        for il in incoming_lines:
            if not il.line_type == 'TOP':
                continue
            # il is a fakey line.
            line = self.reclaim_line(code=il.code, line_type='TOP')
            if not line:
                line = InvoiceLine(agreement=self.agreement)
            line.update_top(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate)

            self.final_lines.append(line)

        # Now do it again for trade lines.
        for il in incoming_lines:
            if not il.line_type == 'TRADE':
                continue

            line = self.reclaim_line(code=il.code, line_type='TRADE')
            if not line:
                line = InvoiceLine(agreement=self.agreement)

            line.update_trade(product=il.product, quantity=il.quantity, price=il.price, pricedate=self.agreement.pricedate)

            self.final_lines.append(line)

        # Save any lines in final_lines because they'll need pks for their children.
        for line in self.final_lines:
            line.save()

        print "Final lines BEFORE CHILD SYNC:"
        for line in self.final_lines:
            print "{}, {}, {}".format(line.code, line.quantity, line.traded)


        # loop through, adding mandatory items and syncing children until no changes are made.
        loops = 0
        self.sync_all_children()
        while True:
            # Next, we need to process mandatory items.
            changed = self.add_mandatory_items()

            # Then, sync children again.
            changed = self.sync_all_children() or changed

            if not changed:
                break

            loops += 1
            if loops >= 10:
                self.errors.append("Probably an infinite loop in mandatories/children.  Needs fixin.")
                break

        # Next reclaim/create a permit line if needed.
        self.add_permit_lines()

        self.sanity_check()

        print "Final lines:"
        for line in self.final_lines:
            print "{}, {}, {}".format(line.code, line.quantity, line.traded)

        # Finally, any lines that are in existing_lines but not in final_lines should be deleted.
        for orphan in self.existing_lines:
            if orphan in self.final_lines:
                continue
            orphan.delete()