docs: def assign_funds_to_invoice(invoice_id: str) -> bool:
      """
      Uses the available funds on the account (credits and payments) to pay the given invoice.
      :param invoice_id: The id of the invoice.
      :return: True if the invoice status is paid.
  
      A lot of side effects may occur in the database:
      - Funds (either payments or credits) may get assigned to the invoice.
      - The invoice status may change.
      - Credits entities may be created.
      """
  
      logger.info('assign-funds-to-invoice', invoice_id=invoice_id)
      invoice = Invoice.objects.get(pk=invoice_id)
      account_id = invoice.account_id
  
      #
      # Precondition. Don't touch invoices that are not PENDING
      #
      if invoice.status != Invoice.PENDING:
          logger.info('assign-funds-to-invoice.status-is-not-pending', invoice_id=invoice_id)
          return False
  
      #
      # Precondition: Only handle invoices in a single currency
      #
      invoice_due_monies = invoice.due().monies()
      if len(invoice_due_monies) != 1:
          logger.info('assign-funds-to-invoice.more-than-one-currency', invoice_id=invoice_id)
          return False
      invoice_due_amount = invoice_due_monies[0].amount
      invoice_due_currency = invoice_due_monies[0].currency
  
      #
      # 1. Collect funds as long as long as we need them
      #
      if invoice_due_amount > 0:
  
          payments = Transaction.successful \
              .payments() \
              .uninvoiced(account_id=account_id) \
              .in_currency(invoice_due_currency) \
              .order_by('created')
  
          credits = Charge.objects \
              .credits() \
              .uninvoiced(account_id=account_id) \
              .in_currency(invoice_due_currency) \
              .order_by('created')
  
          funds = list(credits) + list(payments)
          for fund in funds:
              contributed_amount = abs(fund.amount.amount)  # 'abs' because credits have a negative value
              logger.info('assign-funds-to-invoice.assigning-fund',
                          invoice_id=invoice_id,
                          fund_type=type(fund).__name__,
                          fund_id=str(fund.pk),
                          contributed_amount=contributed_amount)
              fund.invoice_id = invoice_id
              fund.save()
              invoice_due_amount -= contributed_amount
              if invoice_due_amount <= 0:
                  break
  
      #
      # 2. Mark invoice paid if nothing is due.
      #
      if invoice_due_amount <= 0:
          logger.info('assign-funds-to-invoice.mark-paid', invoice_id=invoice_id, invoice_due_amount=invoice_due_amount)
          invoice.status = Invoice.PAID
          invoice.save()
  
      #
      # 3. Carry forward any overpaid money.
      #
      if invoice_due_amount < 0:
          overpayment = Money(abs(invoice_due_amount), invoice_due_currency)
          logger.info('assign-funds-to-invoice.handling-overpayment',
                      invoice_id=invoice_id,
                      overpayment=overpayment)
          with transaction.atomic():
              Charge.objects.create(account_id=account_id, amount=overpayment, product_code=CARRIED_FORWARD,
                                    invoice_id=invoice_id)
              Charge.objects.create(account_id=account_id, amount=-overpayment, product_code=CREDIT_REMAINING)
  
      return invoice.status == Invoice.PAID