def send(self):
        if not self.instance.transaction:
            transaction_mdl = self.instance._meta.model.transaction.field.related_field.model
            item_cls = transaction_mdl.items.related.model
            items = self.instance.invoice_products.select_related('product').all()
            transaction = transaction_mdl(
                project=self.instance.project,
                text=self.instance.invoice_number,
                created_by=self.user,
                modified_by=self.user,
            )
            transaction.save()

            # stores informations about the accounts:
            # 0: account_id
            # 1: value
            # 2: Credit (True), Debit(False)
            # 3: Execute transaction
            accounts = []

            for item in items:
                # calculate taxes for the products
                taxes = item.product.calc_tax(item.amount, item.price)
                # add total net to income account (product)
                accounts.append((item.product.income_account_id, taxes[1], False, True))
                for tax, value in taxes[3]:
                    # add taxes
                    accounts.append((tax.account_id, value, False, True))
                # add the gross value to the credit and debit site of the customers liability account
                # and only mark the debit site as executed
                accounts.append((self.instance.project.customer.asset_account_id, taxes[2], False, False))
                accounts.append((self.instance.project.customer.asset_account_id, taxes[2], True, True))

            # sort and summerize the accounts
            credit_execute = {}
            debit_execute = {}
            credit_virtual = {}
            debit_virtual = {}

            for data in accounts:
                if data[2]:
                    if data[3]:
                        arr = credit_execute
                    else:
                        arr = credit_virtual
                else:
                    if data[3]:
                        arr = debit_execute
                    else:  # pragma: no cover
                        # i can't imagine a case where this list is filled
                        arr = debit_virtual
                if data[0] in arr:
                    arr[data[0]] += data[1]
                else:
                    arr[data[0]] = data[1]

            for account, value in credit_execute.items():
                item = item_cls(
                    account_id=account,
                    transaction=transaction,
                    amount=value,
                    credit=True,
                    draft=False,
                )
                item.save()
                calc_account_balance(account)

            for account, value in debit_execute.items():
                item = item_cls(
                    account_id=account,
                    transaction=transaction,
                    amount=value,
                    credit=False,
                    draft=False,
                )
                item.save()
                calc_account_balance(account)

            for account, value in credit_virtual.items():
                item = item_cls(
                    account_id=account,
                    transaction=transaction,
                    amount=value,
                    credit=True,
                    draft=True,
                )
                item.save()

            for account, value in debit_virtual.items():  # pragma: no cover / see above
                item = item_cls(
                    account_id=account,
                    transaction=transaction,
                    amount=value,
                    credit=False,
                    draft=True,
                )
                item.save()

            self.instance.transaction = transaction
iterates over each product and generates the transaction for invoice and payment
dataset/ETHPy150Open django-bmf/django-bmf/djangobmf/contrib/invoice/workflows.py/InvoiceWorkflow.send