Repository: saleor/saleor

Issue:
Bug: InvoiceRequested doesn't have Order reference
### What are you trying to achieve?

I want to to listen on `INVOICE_CREATED` event to generate Invoice.

[invoiceRequest](https://docs.saleor.io/docs/3.x/api-reference/mutations/invoice-request) correctly requires OrderID, but the [resulting event ](https://docs.saleor.io/docs/3.x/api-reference/objects/invoice-requested) doesn't have order reference, so invoice can't be generated



### Steps to reproduce the problem

Check [docs](https://docs.saleor.io/docs/3.x/api-reference/objects/invoice-requested) and/or check schema. Field is missing

### What did you expect to happen?

1. [Invoice](https://docs.saleor.io/docs/3.x/api-reference/objects/invoice) should have new field `orderID: ID!` (required) 
2. [INVOICE_REQUESTED](https://docs.saleor.io/docs/3.x/api-reference/objects/invoice-requested) Subscription should allow me to query `order: Order!` in payload

### Logs

_No response_

### Environment

Saleor version: 3.10
OS and version: Saleor Cloud



Discussion:

Resolving patch:
diff --git a/saleor/graphql/invoice/types.py b/saleor/graphql/invoice/types.py
--- a/saleor/graphql/invoice/types.py
+++ b/saleor/graphql/invoice/types.py
@@ -1,8 +1,10 @@
 import graphene
 
 from ...invoice import models
+from ..core.descriptions import ADDED_IN_310
 from ..core.types import Job, ModelObjectType
 from ..meta.types import ObjectWithMetadata
+from ..order.dataloaders import OrderByIdLoader
 
 
 class Invoice(ModelObjectType):
@@ -12,8 +14,16 @@ class Invoice(ModelObjectType):
     updated_at = graphene.DateTime(required=True)
     message = graphene.String()
     url = graphene.String(description="URL to download an invoice.")
+    order = graphene.Field(
+        "saleor.graphql.order.types.Order",
+        description="Order related to the invoice." + ADDED_IN_310,
+    )
 
     class Meta:
         description = "Represents an Invoice."
         interfaces = [ObjectWithMetadata, Job, graphene.relay.Node]
         model = models.Invoice
+
+    @staticmethod
+    def resolve_order(root: models.Invoice, info):
+        return OrderByIdLoader(info.context).load(root.order_id)
diff --git a/saleor/graphql/query_cost_map.py b/saleor/graphql/query_cost_map.py
--- a/saleor/graphql/query_cost_map.py
+++ b/saleor/graphql/query_cost_map.py
@@ -190,6 +190,9 @@
         "permissions": {"complexity": 1},
         "users": {"complexity": 1},
     },
+    "Invoice": {
+        "order": {"complexity": 1},
+    },
     "Menu": {
         "items": {"complexity": 1},
     },
@@ -211,7 +214,7 @@
         "events": {"complexity": 1},
         "fulfillments": {"complexity": 1},
         "giftCards": {"complexity": 1},
-        "invoices": {"complexity": 1},
+        "invoices": {"complexity": 10},
         "lines": {"complexity": 1},
         "payments": {"complexity": 1},
         "shippingAddress": {"complexity": 1},
diff --git a/saleor/graphql/webhook/subscription_types.py b/saleor/graphql/webhook/subscription_types.py
--- a/saleor/graphql/webhook/subscription_types.py
+++ b/saleor/graphql/webhook/subscription_types.py
@@ -31,10 +31,12 @@
     ADDED_IN_36,
     ADDED_IN_37,
     ADDED_IN_38,
+    ADDED_IN_310,
     PREVIEW_FEATURE,
 )
 from ..core.scalars import PositiveDecimal
 from ..core.types import NonNullList
+from ..order.dataloaders import OrderByIdLoader
 from ..payment.enums import TransactionActionEnum
 from ..payment.types import TransactionItem
 from ..plugins.dataloaders import plugin_manager_promise_callback
@@ -790,14 +792,29 @@ class InvoiceBase(AbstractType):
         "saleor.graphql.invoice.types.Invoice",
         description="The invoice the event relates to.",
     )
+    order = graphene.Field(
+        "saleor.graphql.order.types.Order",
+        description="Order related to the invoice." + ADDED_IN_310,
+    )
 
     @staticmethod
     def resolve_invoice(root, _info):
         _, invoice = root
         return invoice
 
+    @staticmethod
+    def resolve_order(root, _info):
+        _, invoice = root
+        return OrderByIdLoader(_info.context).load(invoice.order_id)
+
 
 class InvoiceRequested(ObjectType, InvoiceBase):
+    order = graphene.Field(
+        "saleor.graphql.order.types.Order",
+        required=True,
+        description="Order related to the invoice." + ADDED_IN_310,
+    )
+
     class Meta:
         interfaces = (Event,)
         description = (


Test patch:
diff --git a/saleor/graphql/invoice/tests/test_invoice_create.py b/saleor/graphql/invoice/tests/test_invoice_create.py
--- a/saleor/graphql/invoice/tests/test_invoice_create.py
+++ b/saleor/graphql/invoice/tests/test_invoice_create.py
@@ -22,6 +22,9 @@
                 status
                 number
                 url
+                order {
+                    id
+                }
             }
             errors {
                 field
@@ -35,8 +38,9 @@
 def test_create_invoice(staff_api_client, permission_manage_orders, order):
     number = "01/12/2020/TEST"
     url = "http://www.example.com"
+    order_id = graphene.Node.to_global_id("Order", order.pk)
     variables = {
-        "orderId": graphene.Node.to_global_id("Order", order.pk),
+        "orderId": order_id,
         "number": number,
         "url": url,
     }
@@ -45,6 +49,7 @@ def test_create_invoice(staff_api_client, permission_manage_orders, order):
     )
     content = get_graphql_content(response)
     invoice = Invoice.objects.get(order=order, status=JobStatus.SUCCESS)
+    assert order_id == content["data"]["invoiceCreate"]["invoice"]["order"]["id"]
     assert invoice.url == content["data"]["invoiceCreate"]["invoice"]["url"]
     assert invoice.number == content["data"]["invoiceCreate"]["invoice"]["number"]
     assert (
diff --git a/saleor/graphql/tests/queries/fragments.py b/saleor/graphql/tests/queries/fragments.py
--- a/saleor/graphql/tests/queries/fragments.py
+++ b/saleor/graphql/tests/queries/fragments.py
@@ -107,6 +107,18 @@
   id
   status
   number
+  order {
+    id
+  }
+}
+"""
+
+INVOICE_ORDER_DETAILS = """
+fragment InvoiceOrderDetails on Order {
+  id
+  number
+  userEmail
+  isPaid
 }
 """
 
diff --git a/saleor/plugins/webhook/tests/subscription_webhooks/payloads.py b/saleor/plugins/webhook/tests/subscription_webhooks/payloads.py
--- a/saleor/plugins/webhook/tests/subscription_webhooks/payloads.py
+++ b/saleor/plugins/webhook/tests/subscription_webhooks/payloads.py
@@ -233,13 +233,24 @@ def generate_permission_group_payload(group):
 
 
 def generate_invoice_payload(invoice):
-    return {
+    payload = {
         "invoice": {
             "id": graphene.Node.to_global_id("Invoice", invoice.pk),
             "status": invoice.status.upper(),
             "number": invoice.number,
+            "order": None,
         }
     }
+    if invoice.order_id:
+        order_id = graphene.Node.to_global_id("Order", invoice.order_id)
+        payload["invoice"]["order"] = {"id": order_id}
+        payload["order"] = {
+            "id": order_id,
+            "number": str(invoice.order.number),
+            "userEmail": invoice.order.user_email,
+            "isPaid": invoice.order.is_fully_paid(),
+        }
+    return payload
 
 
 def generate_category_payload(category):
diff --git a/saleor/plugins/webhook/tests/subscription_webhooks/subscription_queries.py b/saleor/plugins/webhook/tests/subscription_webhooks/subscription_queries.py
--- a/saleor/plugins/webhook/tests/subscription_webhooks/subscription_queries.py
+++ b/saleor/plugins/webhook/tests/subscription_webhooks/subscription_queries.py
@@ -944,6 +944,7 @@
 
 INVOICE_REQUESTED = (
     fragments.INVOICE_DETAILS
+    + fragments.INVOICE_ORDER_DETAILS
     + """
     subscription{
       event{
@@ -951,6 +952,9 @@
           invoice{
             ...InvoiceDetails
           }
+          order {
+            ...InvoiceOrderDetails
+          }
         }
       }
     }
@@ -959,6 +963,7 @@
 
 INVOICE_DELETED = (
     fragments.INVOICE_DETAILS
+    + fragments.INVOICE_ORDER_DETAILS
     + """
     subscription{
       event{
@@ -966,6 +971,9 @@
           invoice{
             ...InvoiceDetails
           }
+          order {
+            ...InvoiceOrderDetails
+          }
         }
       }
     }
@@ -974,6 +982,7 @@
 
 INVOICE_SENT = (
     fragments.INVOICE_DETAILS
+    + fragments.INVOICE_ORDER_DETAILS
     + """
     subscription{
       event{
@@ -981,6 +990,9 @@
           invoice{
             ...InvoiceDetails
           }
+          order {
+            ...InvoiceOrderDetails
+          }
         }
       }
     }