dub 0.34.1__py3-none-any.whl → 0.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. dub/_version.py +3 -3
  2. dub/basesdk.py +20 -6
  3. dub/models/components/__init__.py +108 -26
  4. dub/models/components/analyticsbrowsers.py +18 -1
  5. dub/models/components/analyticscities.py +18 -1
  6. dub/models/components/analyticscontinents.py +18 -1
  7. dub/models/components/analyticscount.py +18 -1
  8. dub/models/components/analyticscountries.py +20 -1
  9. dub/models/components/analyticsdevices.py +18 -1
  10. dub/models/components/analyticsos.py +18 -1
  11. dub/models/components/analyticsreferers.py +18 -1
  12. dub/models/components/analyticsrefererurls.py +18 -1
  13. dub/models/components/analyticsregions.py +18 -1
  14. dub/models/components/analyticstimeseries.py +18 -1
  15. dub/models/components/analyticstoplinks.py +16 -26
  16. dub/models/components/analyticstopurls.py +18 -1
  17. dub/models/components/analyticstriggers.py +18 -1
  18. dub/models/components/commissioncreatedevent.py +96 -64
  19. dub/models/components/domainschema.py +31 -50
  20. dub/models/components/folderschema.py +18 -19
  21. dub/models/components/leadcreatedevent.py +151 -134
  22. dub/models/components/linkclickedevent.py +57 -70
  23. dub/models/components/linkschema.py +63 -64
  24. dub/models/components/linkwebhookevent.py +43 -51
  25. dub/models/components/partneranalyticscount.py +18 -1
  26. dub/models/components/partneranalyticstimeseries.py +18 -1
  27. dub/models/components/partneranalyticstoplinks.py +16 -27
  28. dub/models/components/partnerapplicationsubmittedevent.py +42 -75
  29. dub/models/components/partnerenrolledevent.py +477 -83
  30. dub/models/components/salecreatedevent.py +152 -151
  31. dub/models/errors/badrequest.py +18 -1
  32. dub/models/errors/conflict.py +18 -1
  33. dub/models/errors/forbidden.py +18 -1
  34. dub/models/errors/internalservererror.py +18 -1
  35. dub/models/errors/inviteexpired.py +18 -1
  36. dub/models/errors/notfound.py +18 -1
  37. dub/models/errors/ratelimitexceeded.py +18 -1
  38. dub/models/errors/unauthorized.py +18 -1
  39. dub/models/errors/unprocessableentity.py +18 -1
  40. dub/models/operations/__init__.py +230 -19
  41. dub/models/operations/approvebountysubmission.py +71 -45
  42. dub/models/operations/banpartner.py +14 -19
  43. dub/models/operations/bulkcreatelinks.py +86 -87
  44. dub/models/operations/bulkupdatelinks.py +97 -82
  45. dub/models/operations/checkdomainstatus.py +1 -17
  46. dub/models/operations/createdomain.py +33 -34
  47. dub/models/operations/createfolder.py +18 -19
  48. dub/models/operations/createlink.py +86 -87
  49. dub/models/operations/createpartner.py +560 -168
  50. dub/models/operations/createpartnerlink.py +74 -85
  51. dub/models/operations/createreferralsembedtoken.py +99 -87
  52. dub/models/operations/createtag.py +18 -1
  53. dub/models/operations/deactivatepartner.py +65 -0
  54. dub/models/operations/getcustomer.py +106 -105
  55. dub/models/operations/getcustomers.py +123 -105
  56. dub/models/operations/getlinkinfo.py +18 -1
  57. dub/models/operations/getlinks.py +36 -1
  58. dub/models/operations/getlinkscount.py +32 -1
  59. dub/models/operations/getqrcode.py +29 -1
  60. dub/models/operations/gettags.py +20 -1
  61. dub/models/operations/listbountysubmissions.py +63 -26
  62. dub/models/operations/listcommissions.py +129 -64
  63. dub/models/operations/listdomains.py +18 -1
  64. dub/models/operations/listevents.py +414 -389
  65. dub/models/operations/listfolders.py +18 -1
  66. dub/models/operations/listpartners.py +510 -84
  67. dub/models/operations/registerdomain.py +1 -17
  68. dub/models/operations/rejectbountysubmission.py +71 -26
  69. dub/models/operations/retrieveanalytics.py +65 -66
  70. dub/models/operations/retrievelinks.py +30 -19
  71. dub/models/operations/retrievepartneranalytics.py +25 -28
  72. dub/models/operations/tracklead.py +38 -83
  73. dub/models/operations/tracksale.py +52 -95
  74. dub/models/operations/updatecommission.py +126 -64
  75. dub/models/operations/updatecustomer.py +122 -131
  76. dub/models/operations/updatedomain.py +50 -35
  77. dub/models/operations/updatefolder.py +34 -19
  78. dub/models/operations/updatelink.py +101 -86
  79. dub/models/operations/updatetag.py +34 -1
  80. dub/models/operations/upsertlink.py +86 -87
  81. dub/models/operations/upsertpartnerlink.py +72 -78
  82. dub/partners.py +288 -0
  83. dub/sdk.py +0 -3
  84. dub/utils/__init__.py +10 -1
  85. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/METADATA +4 -8
  86. dub-0.35.0.dist-info/RECORD +143 -0
  87. dub/models/components/workspaceschema.py +0 -328
  88. dub/models/operations/getworkspace.py +0 -21
  89. dub/models/operations/updateworkspace.py +0 -78
  90. dub/workspaces.py +0 -561
  91. dub-0.34.1.dist-info/RECORD +0 -146
  92. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/WHEEL +0 -0
  93. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/licenses/LICENSE +0 -0
@@ -76,44 +76,47 @@ class TrackLeadRequestBody(BaseModel):
76
76
 
77
77
  @model_serializer(mode="wrap")
78
78
  def serialize_model(self, handler):
79
- optional_fields = [
80
- "customerName",
81
- "customerEmail",
82
- "customerAvatar",
83
- "mode",
84
- "eventQuantity",
85
- "metadata",
86
- ]
87
- nullable_fields = [
88
- "customerName",
89
- "customerEmail",
90
- "customerAvatar",
91
- "eventQuantity",
92
- "metadata",
93
- ]
94
- null_default_fields = ["customerName", "customerEmail", "customerAvatar"]
95
-
79
+ optional_fields = set(
80
+ [
81
+ "customerName",
82
+ "customerEmail",
83
+ "customerAvatar",
84
+ "mode",
85
+ "eventQuantity",
86
+ "metadata",
87
+ ]
88
+ )
89
+ nullable_fields = set(
90
+ [
91
+ "customerName",
92
+ "customerEmail",
93
+ "customerAvatar",
94
+ "eventQuantity",
95
+ "metadata",
96
+ ]
97
+ )
98
+ null_default_fields = set(["customerName", "customerEmail", "customerAvatar"])
96
99
  serialized = handler(self)
97
-
98
100
  m = {}
99
101
 
100
102
  for n, f in type(self).model_fields.items():
101
103
  k = f.alias or n
102
104
  val = serialized.get(k)
103
- serialized.pop(k, None)
104
-
105
- optional_nullable = k in optional_fields and k in nullable_fields
106
- is_set = (
107
- self.__pydantic_fields_set__.intersection({n})
108
- or k in null_default_fields
109
- ) # pylint: disable=no-member
110
-
111
- if val is not None and val != UNSET_SENTINEL:
112
- m[k] = val
113
- elif val != UNSET_SENTINEL and (
114
- not k in optional_fields or (optional_nullable and is_set)
115
- ):
116
- m[k] = val
105
+ is_nullable_and_explicitly_set = (
106
+ k in nullable_fields
107
+ and (
108
+ self.__pydantic_fields_set__.intersection({n})
109
+ or k in null_default_fields
110
+ ) # pylint: disable=no-member
111
+ )
112
+
113
+ if val != UNSET_SENTINEL:
114
+ if (
115
+ val is not None
116
+ or k not in optional_fields
117
+ or is_nullable_and_explicitly_set
118
+ ):
119
+ m[k] = val
117
120
 
118
121
  return m
119
122
 
@@ -177,30 +180,14 @@ class Link(BaseModel):
177
180
 
178
181
  @model_serializer(mode="wrap")
179
182
  def serialize_model(self, handler):
180
- optional_fields = []
181
- nullable_fields = ["partnerId", "programId", "tenantId", "externalId"]
182
- null_default_fields = []
183
-
184
183
  serialized = handler(self)
185
-
186
184
  m = {}
187
185
 
188
186
  for n, f in type(self).model_fields.items():
189
187
  k = f.alias or n
190
188
  val = serialized.get(k)
191
- serialized.pop(k, None)
192
-
193
- optional_nullable = k in optional_fields and k in nullable_fields
194
- is_set = (
195
- self.__pydantic_fields_set__.intersection({n})
196
- or k in null_default_fields
197
- ) # pylint: disable=no-member
198
189
 
199
- if val is not None and val != UNSET_SENTINEL:
200
- m[k] = val
201
- elif val != UNSET_SENTINEL and (
202
- not k in optional_fields or (optional_nullable and is_set)
203
- ):
190
+ if val != UNSET_SENTINEL:
204
191
  m[k] = val
205
192
 
206
193
  return m
@@ -224,30 +211,14 @@ class Customer(BaseModel):
224
211
 
225
212
  @model_serializer(mode="wrap")
226
213
  def serialize_model(self, handler):
227
- optional_fields = []
228
- nullable_fields = ["name", "email", "avatar", "externalId"]
229
- null_default_fields = []
230
-
231
214
  serialized = handler(self)
232
-
233
215
  m = {}
234
216
 
235
217
  for n, f in type(self).model_fields.items():
236
218
  k = f.alias or n
237
219
  val = serialized.get(k)
238
- serialized.pop(k, None)
239
220
 
240
- optional_nullable = k in optional_fields and k in nullable_fields
241
- is_set = (
242
- self.__pydantic_fields_set__.intersection({n})
243
- or k in null_default_fields
244
- ) # pylint: disable=no-member
245
-
246
- if val is not None and val != UNSET_SENTINEL:
247
- m[k] = val
248
- elif val != UNSET_SENTINEL and (
249
- not k in optional_fields or (optional_nullable and is_set)
250
- ):
221
+ if val != UNSET_SENTINEL:
251
222
  m[k] = val
252
223
 
253
224
  return m
@@ -272,30 +243,14 @@ class TrackLeadResponseBody(BaseModel):
272
243
 
273
244
  @model_serializer(mode="wrap")
274
245
  def serialize_model(self, handler):
275
- optional_fields = []
276
- nullable_fields = ["link"]
277
- null_default_fields = []
278
-
279
246
  serialized = handler(self)
280
-
281
247
  m = {}
282
248
 
283
249
  for n, f in type(self).model_fields.items():
284
250
  k = f.alias or n
285
251
  val = serialized.get(k)
286
- serialized.pop(k, None)
287
252
 
288
- optional_nullable = k in optional_fields and k in nullable_fields
289
- is_set = (
290
- self.__pydantic_fields_set__.intersection({n})
291
- or k in null_default_fields
292
- ) # pylint: disable=no-member
293
-
294
- if val is not None and val != UNSET_SENTINEL:
295
- m[k] = val
296
- elif val != UNSET_SENTINEL and (
297
- not k in optional_fields or (optional_nullable and is_set)
298
- ):
253
+ if val != UNSET_SENTINEL:
299
254
  m[k] = val
300
255
 
301
256
  return m
@@ -98,56 +98,61 @@ class TrackSaleRequestBody(BaseModel):
98
98
 
99
99
  @model_serializer(mode="wrap")
100
100
  def serialize_model(self, handler):
101
- optional_fields = [
102
- "currency",
103
- "eventName",
104
- "paymentProcessor",
105
- "invoiceId",
106
- "metadata",
107
- "leadEventName",
108
- "clickId",
109
- "customerName",
110
- "customerEmail",
111
- "customerAvatar",
112
- ]
113
- nullable_fields = [
114
- "invoiceId",
115
- "metadata",
116
- "leadEventName",
117
- "clickId",
118
- "customerName",
119
- "customerEmail",
120
- "customerAvatar",
121
- ]
122
- null_default_fields = [
123
- "invoiceId",
124
- "leadEventName",
125
- "customerName",
126
- "customerEmail",
127
- "customerAvatar",
128
- ]
129
-
101
+ optional_fields = set(
102
+ [
103
+ "currency",
104
+ "eventName",
105
+ "paymentProcessor",
106
+ "invoiceId",
107
+ "metadata",
108
+ "leadEventName",
109
+ "clickId",
110
+ "customerName",
111
+ "customerEmail",
112
+ "customerAvatar",
113
+ ]
114
+ )
115
+ nullable_fields = set(
116
+ [
117
+ "invoiceId",
118
+ "metadata",
119
+ "leadEventName",
120
+ "clickId",
121
+ "customerName",
122
+ "customerEmail",
123
+ "customerAvatar",
124
+ ]
125
+ )
126
+ null_default_fields = set(
127
+ [
128
+ "invoiceId",
129
+ "leadEventName",
130
+ "customerName",
131
+ "customerEmail",
132
+ "customerAvatar",
133
+ ]
134
+ )
130
135
  serialized = handler(self)
131
-
132
136
  m = {}
133
137
 
134
138
  for n, f in type(self).model_fields.items():
135
139
  k = f.alias or n
136
140
  val = serialized.get(k)
137
- serialized.pop(k, None)
138
-
139
- optional_nullable = k in optional_fields and k in nullable_fields
140
- is_set = (
141
- self.__pydantic_fields_set__.intersection({n})
142
- or k in null_default_fields
143
- ) # pylint: disable=no-member
144
-
145
- if val is not None and val != UNSET_SENTINEL:
146
- m[k] = val
147
- elif val != UNSET_SENTINEL and (
148
- not k in optional_fields or (optional_nullable and is_set)
149
- ):
150
- m[k] = val
141
+ is_nullable_and_explicitly_set = (
142
+ k in nullable_fields
143
+ and (
144
+ self.__pydantic_fields_set__.intersection({n})
145
+ or k in null_default_fields
146
+ ) # pylint: disable=no-member
147
+ )
148
+
149
+ if val != UNSET_SENTINEL:
150
+ if (
151
+ val is not None
152
+ or k not in optional_fields
153
+ or is_nullable_and_explicitly_set
154
+ ):
155
+ m[k] = val
151
156
 
152
157
  return m
153
158
 
@@ -173,30 +178,14 @@ class TrackSaleCustomer(BaseModel):
173
178
 
174
179
  @model_serializer(mode="wrap")
175
180
  def serialize_model(self, handler):
176
- optional_fields = []
177
- nullable_fields = ["name", "email", "avatar", "externalId"]
178
- null_default_fields = []
179
-
180
181
  serialized = handler(self)
181
-
182
182
  m = {}
183
183
 
184
184
  for n, f in type(self).model_fields.items():
185
185
  k = f.alias or n
186
186
  val = serialized.get(k)
187
- serialized.pop(k, None)
188
-
189
- optional_nullable = k in optional_fields and k in nullable_fields
190
- is_set = (
191
- self.__pydantic_fields_set__.intersection({n})
192
- or k in null_default_fields
193
- ) # pylint: disable=no-member
194
187
 
195
- if val is not None and val != UNSET_SENTINEL:
196
- m[k] = val
197
- elif val != UNSET_SENTINEL and (
198
- not k in optional_fields or (optional_nullable and is_set)
199
- ):
188
+ if val != UNSET_SENTINEL:
200
189
  m[k] = val
201
190
 
202
191
  return m
@@ -223,30 +212,14 @@ class Sale(BaseModel):
223
212
 
224
213
  @model_serializer(mode="wrap")
225
214
  def serialize_model(self, handler):
226
- optional_fields = []
227
- nullable_fields = ["invoiceId", "metadata"]
228
- null_default_fields = []
229
-
230
215
  serialized = handler(self)
231
-
232
216
  m = {}
233
217
 
234
218
  for n, f in type(self).model_fields.items():
235
219
  k = f.alias or n
236
220
  val = serialized.get(k)
237
- serialized.pop(k, None)
238
221
 
239
- optional_nullable = k in optional_fields and k in nullable_fields
240
- is_set = (
241
- self.__pydantic_fields_set__.intersection({n})
242
- or k in null_default_fields
243
- ) # pylint: disable=no-member
244
-
245
- if val is not None and val != UNSET_SENTINEL:
246
- m[k] = val
247
- elif val != UNSET_SENTINEL and (
248
- not k in optional_fields or (optional_nullable and is_set)
249
- ):
222
+ if val != UNSET_SENTINEL:
250
223
  m[k] = val
251
224
 
252
225
  return m
@@ -271,30 +244,14 @@ class TrackSaleResponseBody(BaseModel):
271
244
 
272
245
  @model_serializer(mode="wrap")
273
246
  def serialize_model(self, handler):
274
- optional_fields = []
275
- nullable_fields = ["customer", "sale"]
276
- null_default_fields = []
277
-
278
247
  serialized = handler(self)
279
-
280
248
  m = {}
281
249
 
282
250
  for n, f in type(self).model_fields.items():
283
251
  k = f.alias or n
284
252
  val = serialized.get(k)
285
- serialized.pop(k, None)
286
253
 
287
- optional_nullable = k in optional_fields and k in nullable_fields
288
- is_set = (
289
- self.__pydantic_fields_set__.intersection({n})
290
- or k in null_default_fields
291
- ) # pylint: disable=no-member
292
-
293
- if val is not None and val != UNSET_SENTINEL:
294
- m[k] = val
295
- elif val != UNSET_SENTINEL and (
296
- not k in optional_fields or (optional_nullable and is_set)
297
- ):
254
+ if val != UNSET_SENTINEL:
298
255
  m[k] = val
299
256
 
300
257
  return m
@@ -45,6 +45,22 @@ class UpdateCommissionRequestBody(BaseModel):
45
45
  status: Optional[Status] = None
46
46
  r"""Useful for marking a commission as refunded, duplicate, canceled, or fraudulent. Takes precedence over `amount` and `modifyAmount`. When a commission is marked as refunded, duplicate, canceled, or fraudulent, it will be omitted from the payout, and the payout amount will be recalculated accordingly. Paid commissions cannot be updated."""
47
47
 
48
+ @model_serializer(mode="wrap")
49
+ def serialize_model(self, handler):
50
+ optional_fields = set(["amount", "modifyAmount", "currency", "status"])
51
+ serialized = handler(self)
52
+ m = {}
53
+
54
+ for n, f in type(self).model_fields.items():
55
+ k = f.alias or n
56
+ val = serialized.get(k)
57
+
58
+ if val != UNSET_SENTINEL:
59
+ if val is not None or k not in optional_fields:
60
+ m[k] = val
61
+
62
+ return m
63
+
48
64
 
49
65
  class UpdateCommissionRequestTypedDict(TypedDict):
50
66
  id: str
@@ -63,6 +79,22 @@ class UpdateCommissionRequest(BaseModel):
63
79
  FieldMetadata(request=RequestMetadata(media_type="application/json")),
64
80
  ] = None
65
81
 
82
+ @model_serializer(mode="wrap")
83
+ def serialize_model(self, handler):
84
+ optional_fields = set(["RequestBody"])
85
+ serialized = handler(self)
86
+ m = {}
87
+
88
+ for n, f in type(self).model_fields.items():
89
+ k = f.alias or n
90
+ val = serialized.get(k)
91
+
92
+ if val != UNSET_SENTINEL:
93
+ if val is not None or k not in optional_fields:
94
+ m[k] = val
95
+
96
+ return m
97
+
66
98
 
67
99
  class UpdateCommissionType(str, Enum):
68
100
  CLICK = "click"
@@ -124,31 +156,28 @@ class UpdateCommissionPartner(BaseModel):
124
156
 
125
157
  @model_serializer(mode="wrap")
126
158
  def serialize_model(self, handler):
127
- optional_fields = ["groupId"]
128
- nullable_fields = ["email", "image", "payoutsEnabledAt", "country", "groupId"]
129
- null_default_fields = []
130
-
159
+ optional_fields = set(["groupId"])
160
+ nullable_fields = set(
161
+ ["email", "image", "payoutsEnabledAt", "country", "groupId"]
162
+ )
131
163
  serialized = handler(self)
132
-
133
164
  m = {}
134
165
 
135
166
  for n, f in type(self).model_fields.items():
136
167
  k = f.alias or n
137
168
  val = serialized.get(k)
138
- serialized.pop(k, None)
139
-
140
- optional_nullable = k in optional_fields and k in nullable_fields
141
- is_set = (
142
- self.__pydantic_fields_set__.intersection({n})
143
- or k in null_default_fields
144
- ) # pylint: disable=no-member
145
-
146
- if val is not None and val != UNSET_SENTINEL:
147
- m[k] = val
148
- elif val != UNSET_SENTINEL and (
149
- not k in optional_fields or (optional_nullable and is_set)
150
- ):
151
- m[k] = val
169
+ is_nullable_and_explicitly_set = (
170
+ k in nullable_fields
171
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
172
+ )
173
+
174
+ if val != UNSET_SENTINEL:
175
+ if (
176
+ val is not None
177
+ or k not in optional_fields
178
+ or is_nullable_and_explicitly_set
179
+ ):
180
+ m[k] = val
152
181
 
153
182
  return m
154
183
 
@@ -156,36 +185,42 @@ class UpdateCommissionPartner(BaseModel):
156
185
  class UpdateCommissionCustomerTypedDict(TypedDict):
157
186
  id: str
158
187
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
159
- external_id: str
160
- r"""Unique identifier for the customer in the client's app."""
161
188
  name: str
162
189
  r"""Name of the customer."""
190
+ external_id: str
191
+ r"""Unique identifier for the customer in the client's app."""
163
192
  created_at: str
164
- r"""The date the customer was created."""
193
+ r"""The date the customer was created (usually the signup date or trial start date)."""
165
194
  email: NotRequired[Nullable[str]]
166
195
  r"""Email of the customer."""
167
196
  avatar: NotRequired[Nullable[str]]
168
197
  r"""Avatar URL of the customer."""
198
+ stripe_customer_id: NotRequired[Nullable[str]]
199
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
169
200
  country: NotRequired[Nullable[str]]
170
201
  r"""Country of the customer."""
171
202
  sales: NotRequired[Nullable[float]]
172
203
  r"""Total number of sales for the customer."""
173
204
  sale_amount: NotRequired[Nullable[float]]
174
205
  r"""Total amount of sales for the customer."""
206
+ first_sale_at: NotRequired[Nullable[str]]
207
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
208
+ subscription_canceled_at: NotRequired[Nullable[str]]
209
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
175
210
 
176
211
 
177
212
  class UpdateCommissionCustomer(BaseModel):
178
213
  id: str
179
214
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
180
215
 
181
- external_id: Annotated[str, pydantic.Field(alias="externalId")]
182
- r"""Unique identifier for the customer in the client's app."""
183
-
184
216
  name: str
185
217
  r"""Name of the customer."""
186
218
 
219
+ external_id: Annotated[str, pydantic.Field(alias="externalId")]
220
+ r"""Unique identifier for the customer in the client's app."""
221
+
187
222
  created_at: Annotated[str, pydantic.Field(alias="createdAt")]
188
- r"""The date the customer was created."""
223
+ r"""The date the customer was created (usually the signup date or trial start date)."""
189
224
 
190
225
  email: OptionalNullable[str] = UNSET
191
226
  r"""Email of the customer."""
@@ -193,6 +228,11 @@ class UpdateCommissionCustomer(BaseModel):
193
228
  avatar: OptionalNullable[str] = UNSET
194
229
  r"""Avatar URL of the customer."""
195
230
 
231
+ stripe_customer_id: Annotated[
232
+ OptionalNullable[str], pydantic.Field(alias="stripeCustomerId")
233
+ ] = UNSET
234
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
235
+
196
236
  country: OptionalNullable[str] = UNSET
197
237
  r"""Country of the customer."""
198
238
 
@@ -204,33 +244,60 @@ class UpdateCommissionCustomer(BaseModel):
204
244
  ] = UNSET
205
245
  r"""Total amount of sales for the customer."""
206
246
 
247
+ first_sale_at: Annotated[
248
+ OptionalNullable[str], pydantic.Field(alias="firstSaleAt")
249
+ ] = UNSET
250
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
251
+
252
+ subscription_canceled_at: Annotated[
253
+ OptionalNullable[str], pydantic.Field(alias="subscriptionCanceledAt")
254
+ ] = UNSET
255
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
256
+
207
257
  @model_serializer(mode="wrap")
208
258
  def serialize_model(self, handler):
209
- optional_fields = ["email", "avatar", "country", "sales", "saleAmount"]
210
- nullable_fields = ["email", "avatar", "country", "sales", "saleAmount"]
211
- null_default_fields = []
212
-
259
+ optional_fields = set(
260
+ [
261
+ "email",
262
+ "avatar",
263
+ "stripeCustomerId",
264
+ "country",
265
+ "sales",
266
+ "saleAmount",
267
+ "firstSaleAt",
268
+ "subscriptionCanceledAt",
269
+ ]
270
+ )
271
+ nullable_fields = set(
272
+ [
273
+ "email",
274
+ "avatar",
275
+ "stripeCustomerId",
276
+ "country",
277
+ "sales",
278
+ "saleAmount",
279
+ "firstSaleAt",
280
+ "subscriptionCanceledAt",
281
+ ]
282
+ )
213
283
  serialized = handler(self)
214
-
215
284
  m = {}
216
285
 
217
286
  for n, f in type(self).model_fields.items():
218
287
  k = f.alias or n
219
288
  val = serialized.get(k)
220
- serialized.pop(k, None)
221
-
222
- optional_nullable = k in optional_fields and k in nullable_fields
223
- is_set = (
224
- self.__pydantic_fields_set__.intersection({n})
225
- or k in null_default_fields
226
- ) # pylint: disable=no-member
227
-
228
- if val is not None and val != UNSET_SENTINEL:
229
- m[k] = val
230
- elif val != UNSET_SENTINEL and (
231
- not k in optional_fields or (optional_nullable and is_set)
232
- ):
233
- m[k] = val
289
+ is_nullable_and_explicitly_set = (
290
+ k in nullable_fields
291
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
292
+ )
293
+
294
+ if val != UNSET_SENTINEL:
295
+ if (
296
+ val is not None
297
+ or k not in optional_fields
298
+ or is_nullable_and_explicitly_set
299
+ ):
300
+ m[k] = val
234
301
 
235
302
  return m
236
303
 
@@ -291,30 +358,25 @@ class UpdateCommissionResponseBody(BaseModel):
291
358
 
292
359
  @model_serializer(mode="wrap")
293
360
  def serialize_model(self, handler):
294
- optional_fields = ["type", "userId", "customer"]
295
- nullable_fields = ["invoiceId", "description", "userId", "customer"]
296
- null_default_fields = []
297
-
361
+ optional_fields = set(["type", "userId", "customer"])
362
+ nullable_fields = set(["invoiceId", "description", "userId", "customer"])
298
363
  serialized = handler(self)
299
-
300
364
  m = {}
301
365
 
302
366
  for n, f in type(self).model_fields.items():
303
367
  k = f.alias or n
304
368
  val = serialized.get(k)
305
- serialized.pop(k, None)
306
-
307
- optional_nullable = k in optional_fields and k in nullable_fields
308
- is_set = (
309
- self.__pydantic_fields_set__.intersection({n})
310
- or k in null_default_fields
311
- ) # pylint: disable=no-member
312
-
313
- if val is not None and val != UNSET_SENTINEL:
314
- m[k] = val
315
- elif val != UNSET_SENTINEL and (
316
- not k in optional_fields or (optional_nullable and is_set)
317
- ):
318
- m[k] = val
369
+ is_nullable_and_explicitly_set = (
370
+ k in nullable_fields
371
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
372
+ )
373
+
374
+ if val != UNSET_SENTINEL:
375
+ if (
376
+ val is not None
377
+ or k not in optional_fields
378
+ or is_nullable_and_explicitly_set
379
+ ):
380
+ m[k] = val
319
381
 
320
382
  return m