Repository: zulip/zulip

Issue:
Collect additional information on sponsorship request form
To avoid unnecessary back-and-forth, we should collect some more info on the sponsorship request form. We should add the following fields below "organization type":

* Expected number of users (approximate range)
* How many paid staff does your organization have?
  *  If more than zero, please describe

- The first two fields should be required, but don't have to be numeric. Should be large enough to allow a brief comment, e.g., "probably no more than 100", etc.
- For simplicity, we can always show "If more than zero, please describe", rather than making it conditional on the value of the paid staff field.



Discussion:

Resolving patch:
diff --git a/corporate/migrations/0019_zulipsponsorshiprequest_expected_total_users_and_more.py b/corporate/migrations/0019_zulipsponsorshiprequest_expected_total_users_and_more.py
new file mode 100644
--- /dev/null
+++ b/corporate/migrations/0019_zulipsponsorshiprequest_expected_total_users_and_more.py
@@ -0,0 +1,27 @@
+# Generated by Django 4.2.6 on 2023-11-11 14:16
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("corporate", "0018_customer_cloud_xor_self_hosted"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="zulipsponsorshiprequest",
+            name="expected_total_users",
+            field=models.TextField(default=""),
+        ),
+        migrations.AddField(
+            model_name="zulipsponsorshiprequest",
+            name="paid_users_count",
+            field=models.TextField(default=""),
+        ),
+        migrations.AddField(
+            model_name="zulipsponsorshiprequest",
+            name="paid_users_description",
+            field=models.TextField(default=""),
+        ),
+    ]
diff --git a/corporate/models.py b/corporate/models.py
--- a/corporate/models.py
+++ b/corporate/models.py
@@ -360,3 +360,6 @@ class ZulipSponsorshipRequest(models.Model):
     org_website = models.URLField(max_length=MAX_ORG_URL_LENGTH, blank=True, null=True)
 
     org_description = models.TextField(default="")
+    expected_total_users = models.TextField(default="")
+    paid_users_count = models.TextField(default="")
+    paid_users_description = models.TextField(default="")
diff --git a/corporate/views/upgrade.py b/corporate/views/upgrade.py
--- a/corporate/views/upgrade.py
+++ b/corporate/views/upgrade.py
@@ -233,6 +233,9 @@ class SponsorshipRequestForm(forms.Form):
     website = forms.URLField(max_length=ZulipSponsorshipRequest.MAX_ORG_URL_LENGTH, required=False)
     organization_type = forms.IntegerField()
     description = forms.CharField(widget=forms.Textarea)
+    expected_total_users = forms.CharField(widget=forms.Textarea)
+    paid_users_count = forms.CharField(widget=forms.Textarea)
+    paid_users_description = forms.CharField(widget=forms.Textarea, required=False)
 
 
 @require_organization_member
@@ -243,6 +246,9 @@ def sponsorship(
     organization_type: str = REQ("organization-type"),
     website: str = REQ(),
     description: str = REQ(),
+    expected_total_users: str = REQ(),
+    paid_users_count: str = REQ(),
+    paid_users_description: str = REQ(),
 ) -> HttpResponse:
     realm = user.realm
     billing_session = RealmBillingSession(user)
@@ -266,6 +272,9 @@ def sponsorship(
                 org_website=form.cleaned_data["website"],
                 org_description=form.cleaned_data["description"],
                 org_type=form.cleaned_data["organization_type"],
+                expected_total_users=form.cleaned_data["expected_total_users"],
+                paid_users_count=form.cleaned_data["paid_users_count"],
+                paid_users_description=form.cleaned_data["paid_users_description"],
             )
             sponsorship_request.save()
 
@@ -287,6 +296,9 @@ def sponsorship(
             "organization_type": org_type_display_name,
             "website": website,
             "description": description,
+            "expected_total_users": expected_total_users,
+            "paid_users_count": paid_users_count,
+            "paid_users_description": paid_users_description,
         }
         send_email(
             "zerver/emails/sponsorship_request",


Test patch:
diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py
--- a/corporate/tests/test_stripe.py
+++ b/corporate/tests/test_stripe.py
@@ -2159,6 +2159,9 @@ def test_request_sponsorship_form_with_invalid_url(self) -> None:
             "organization-type": Realm.ORG_TYPES["opensource"]["id"],
             "website": "invalid-url",
             "description": "Infinispan is a distributed in-memory key/value data store with optional schema.",
+            "expected_total_users": "10 users",
+            "paid_users_count": "1 user",
+            "paid_users_description": "We have 1 paid user.",
         }
 
         response = self.client_post("/json/billing/sponsorship", data)
@@ -2172,6 +2175,9 @@ def test_request_sponsorship_form_with_blank_url(self) -> None:
             "organization-type": Realm.ORG_TYPES["opensource"]["id"],
             "website": "",
             "description": "Infinispan is a distributed in-memory key/value data store with optional schema.",
+            "expected_total_users": "10 users",
+            "paid_users_count": "1 user",
+            "paid_users_description": "We have 1 paid user.",
         }
 
         response = self.client_post("/json/billing/sponsorship", data)
@@ -2223,6 +2229,9 @@ def test_request_sponsorship(self) -> None:
             "organization-type": Realm.ORG_TYPES["opensource"]["id"],
             "website": "https://infinispan.org/",
             "description": "Infinispan is a distributed in-memory key/value data store with optional schema.",
+            "expected_total_users": "10 users",
+            "paid_users_count": "1 user",
+            "paid_users_description": "We have 1 paid user.",
         }
         response = self.client_post("/json/billing/sponsorship", data)
         self.assert_json_success(response)