Coverage for functions \ flipdare \ wrapper \ user_wrapper.py: 78%

442 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# confidential and copyrighted material. Unauthorised copying, 

6# modification, distribution, or use of this file is strictly 

7# prohibited without prior written permission from Flipdare Pty Ltd. 

8# 

9# This software includes third-party components licensed under MIT, 

10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

11# 

12 

13from __future__ import annotations 

14 

15from typing import TypedDict, Unpack 

16from flipdare.app_log import LOG 

17from flipdare.constants import IS_TRACE 

18from flipdare.generated.model.internal.dare_stats_model import DareStatsModel 

19from flipdare.generated.model.internal.image_model import ImageModel 

20from flipdare.generated.model.internal.location_model import LocationModel 

21from flipdare.generated.model.payment.stripe_account_model import StripeAccountModel 

22from flipdare.generated.model.payment.stripe_customer_model import StripeCustomerModel 

23from flipdare.generated.model.internal.view_stats_model import ViewStatsModel 

24from flipdare.generated.model.issue.flag_model import FlagModel 

25from flipdare.generated.model.user_model import StripeSettingsType, UserKeys, UserModel 

26from flipdare.generated.shared.model.app_visibility import AppVisibility 

27from flipdare.generated.shared.model.user.auth_type import AuthType 

28from flipdare.generated.shared.model.user.app_fee_type import AppFeeType 

29from flipdare.generated.shared.model.user.user_archive_type import UserArchiveType 

30from flipdare.generated.shared.model.user.user_cache_type import UserCacheType 

31from flipdare.generated.shared.model.user.user_level_type import UserLevelType 

32from flipdare.generated.shared.model.user.video_continue_type import VideoContinueType 

33from flipdare.generated.shared.stripe.stripe_account_type import StripeAccountType 

34from flipdare.generated.shared.stripe.stripe_country_code import StripeCountryCode 

35from flipdare.generated.shared.stripe.stripe_currency_code import StripeCurrencyCode 

36from flipdare.generated.shared.stripe.stripe_onboard_result import StripeOnboardResult 

37from flipdare.generated.shared.stripe.stripe_onboard_state import StripeOnboardState 

38from flipdare.payments.core.stripe_guard import StripeGuard 

39from flipdare.payments.core.stripe_invoice_prefix import StripeInvoicePrefix 

40from flipdare.wrapper._persisted_wrapper import PersistedWrapper 

41 

42_K = UserKeys 

43 

44 

45class StripeCustomerUpdateDict(TypedDict, total=False): 

46 """Fields that can be updated on a StripeCustomerModel (or the customer portion of an Account).""" 

47 

48 email: str 

49 name: str 

50 customer_id: str 

51 country_code: StripeCountryCode 

52 alt_currency_code: StripeCurrencyCode 

53 invoice_prefix: str 

54 

55 

56class StripeAccountUpdateDict(TypedDict, total=False): 

57 """Optional fields for update_stripe_account beyond the required positional params.""" 

58 

59 # shared customer fields 

60 email: str 

61 name: str 

62 customer_id: str 

63 country_code: StripeCountryCode 

64 invoice_prefix: str 

65 # account-only fields 

66 onboard_state: StripeOnboardState 

67 onboard_result: StripeOnboardResult | None 

68 account_link: str | None 

69 account_link_expires: float | None 

70 stripe_disabled: bool 

71 stripe_disabled_reason: str | None 

72 stripe_closed: bool 

73 highest_transaction_amount: int 

74 transaction_volume: int 

75 dispute_for_ct: int 

76 refund_for_ct: int 

77 payout_for_ct: int 

78 dispute_against_ct: int 

79 refund_against_ct: int 

80 payout_against_ct: int 

81 

82 

83class UserWrapper(PersistedWrapper[UserModel]): 

84 MODEL_CLASS = UserModel 

85 

86 def validate_can_create_dare(self) -> tuple[bool, str | None]: 

87 """ 

88 Check if user can create a dare. 

89 

90 Returns: 

91 Tuple of (can_create, error_message) 

92 

93 """ 

94 if self.flagged is not None: 

95 return False, "User is flagged and cannot create dares" 

96 

97 if self.visibility != AppVisibility.PUBLIC: 

98 return False, "User must have public visibility to create dares" 

99 

100 if not self.email_verified: 

101 return False, "Email must be verified to create dares" 

102 

103 return True, None 

104 

105 @property 

106 def views(self) -> int: 

107 return self.view_stats.views 

108 

109 @property 

110 def flags(self) -> int: 

111 return self.view_stats.flags 

112 

113 @property 

114 def bookmarks(self) -> int: 

115 return self.view_stats.bookmarks 

116 

117 @property 

118 def processing_complete(self) -> bool: 

119 return self.processed and self.context_created and self.search_indexed 

120 

121 def reindex(self) -> None: 

122 """Force reindexing of user in search.""" 

123 self.search_indexed = False 

124 

125 def create_stripe_customer( 

126 self, 

127 customer_id: str, 

128 email: str, 

129 country_code: StripeCountryCode, 

130 currency_code: StripeCurrencyCode | None = None, 

131 name: str | None = None, 

132 first_name: str | None = None, 

133 last_name: str | None = None, 

134 ) -> None: 

135 uid = self.doc_id 

136 settings = self.stripe_settings 

137 if StripeGuard.is_account(settings): 

138 msg = f"User {uid} already has stripe account settings, updating customer fields ONLY." 

139 LOG().info(msg) 

140 

141 settings.customer_id = customer_id 

142 settings.email = email 

143 if currency_code is not None: 

144 settings.currency_code = currency_code 

145 else: 

146 name = name or self.model.contact_name 

147 name_tokens: tuple[str, str] | None = None 

148 if first_name is not None and last_name is not None: 

149 name_tokens = (first_name, last_name) 

150 

151 inv_prefix = StripeInvoicePrefix.create( 

152 uid=uid, 

153 name=name, 

154 email=email, 

155 name_tokens=name_tokens, 

156 ) 

157 

158 settings = StripeCustomerModel( 

159 email=email, 

160 name=name, 

161 customer_id=customer_id, 

162 country_code=country_code, 

163 invoice_prefix=inv_prefix.prefix, 

164 ) 

165 if currency_code is not None: 

166 settings.currency_code = currency_code 

167 

168 self.update_field(_K.STRIPE_SETTINGS, settings) 

169 

170 def create_stripe_account( 

171 self, 

172 country_code: StripeCountryCode, 

173 currency_code: StripeCurrencyCode, 

174 account_type: StripeAccountType, 

175 account_id: str, 

176 customer_id: str, 

177 account_link: str | None = None, 

178 account_link_expires: float | None = None, 

179 stripe_disabled: bool = False, 

180 stripe_disabled_reason: str | None = None, 

181 stripe_closed: bool = False, 

182 onboard_state: StripeOnboardState = StripeOnboardState.NOT_STARTED, 

183 # the user may want to user a different name/email for stripe 

184 # than in flipdare.. 

185 name_tokens: tuple[str, str] | None = None, 

186 email: str | None = None, 

187 name: str | None = None, 

188 ) -> None: 

189 uid = self.doc_id 

190 email = email if email is not None else self.email 

191 name = name if name is not None else self.model.contact_name 

192 

193 inv_prefix = StripeInvoicePrefix.create( 

194 uid=uid, 

195 name=name, 

196 email=email, 

197 name_tokens=name_tokens, 

198 ).prefix 

199 

200 if IS_TRACE: 

201 msg = ( 

202 f"Making StripeSettingsModel for user {uid} with country {country_code}, " 

203 f"currency {currency_code}, account_id {account_id}, customer_id {customer_id}, " 

204 f"account_link {account_link}, account_link_expires {account_link_expires}, " 

205 f"account_type {account_type}, invoice_prefix {inv_prefix}, name {name}, email {email}" 

206 f" onboard_state {onboard_state}, stripe_disabled {stripe_disabled}, " 

207 f"stripe_disabled_reason {stripe_disabled_reason}, stripe_closed {stripe_closed}" 

208 ) 

209 LOG().trace(msg) 

210 

211 settings = StripeAccountModel( 

212 email=email, 

213 name=name, 

214 invoice_prefix=inv_prefix, 

215 account_id=account_id, 

216 account_type=account_type, 

217 country_code=country_code, 

218 currency_code=currency_code, 

219 customer_id=customer_id, 

220 account_link=account_link, 

221 account_link_expires=account_link_expires, 

222 onboard_state=StripeOnboardState.NOT_STARTED, 

223 stripe_disabled=stripe_disabled, 

224 stripe_disabled_reason=stripe_disabled_reason, 

225 stripe_closed=stripe_closed, 

226 ) 

227 

228 self.update_field(_K.STRIPE_SETTINGS, settings) 

229 

230 def update_stripe_customer(self, **kwargs: Unpack[StripeCustomerUpdateDict]) -> None: 

231 """ 

232 Update customer-compatible fields on the existing stripe settings. 

233 

234 Works on both StripeCustomerModel and StripeAccountModel in-place. 

235 To promote a customer to an account use update_stripe_account instead. 

236 """ 

237 old_settings = self.stripe_settings 

238 if old_settings is None: 

239 msg = f"User {self.doc_id} has no existing stripe settings, cannot update." 

240 LOG().warning(msg) 

241 return 

242 

243 update_data = {k: v for k, v in kwargs.items() if v is not None} 

244 if not update_data: 

245 return 

246 

247 # Both Customer and Account share the same customer field names 

248 customer_fields = StripeCustomerModel.model_fields.keys() 

249 filtered_data = {k: v for k, v in update_data.items() if k in customer_fields} 

250 new_settings = old_settings.model_copy(update=filtered_data) 

251 

252 if old_settings.model_dump() == new_settings.model_dump(): 

253 if IS_TRACE: 

254 LOG().trace(f"No changes to stripe customer fields for user {self.doc_id}.") 

255 return 

256 

257 self.update_field(_K.STRIPE_SETTINGS, new_settings) 

258 

259 def update_stripe_account( 

260 self, 

261 account_id: str, 

262 account_type: StripeAccountType, 

263 currency_code: StripeCurrencyCode, 

264 **kwargs: Unpack[StripeAccountUpdateDict], 

265 ) -> None: 

266 """ 

267 Update account fields. Promotes StripeCustomerModel → StripeAccountModel if needed. 

268 

269 The three required positional params are the minimum fields to construct a 

270 StripeAccountModel. The caller is responsible for supplying valid values — 

271 no partial promotion is possible. 

272 """ 

273 old_settings = self.stripe_settings 

274 if old_settings is None: 

275 msg = f"User {self.doc_id} has no existing stripe settings, cannot update." 

276 LOG().warning(msg) 

277 return 

278 

279 # Required fields always win; optional kwargs are filtered for None 

280 update_data: dict[str, object] = {k: v for k, v in kwargs.items() if v is not None} 

281 update_data["account_id"] = account_id 

282 update_data["account_type"] = account_type 

283 update_data["currency_code"] = currency_code 

284 

285 new_settings: StripeSettingsType 

286 match old_settings: 

287 case StripeAccountModel(): 

288 new_settings = old_settings.model_copy(update=update_data) 

289 case StripeCustomerModel(): 

290 # Promote: carry over existing customer data, overlay with caller-supplied values 

291 if IS_TRACE: 

292 LOG().trace( 

293 f"Promoting user {self.doc_id} from Customer -> Account " 

294 f"with account_id={account_id}." 

295 ) 

296 customer_data = old_settings.model_dump( 

297 exclude={"type", "updated_at", "created_at"} 

298 ) 

299 merged = {**customer_data, **update_data} 

300 onboard_state = merged.pop("onboard_state", StripeOnboardState.IN_PROGRESS) 

301 new_settings = StripeAccountModel(onboard_state=onboard_state, **merged) 

302 

303 if old_settings.model_dump() == new_settings.model_dump(): 

304 if IS_TRACE: 

305 LOG().trace(f"No changes to stripe account fields for user {self.doc_id}.") 

306 return 

307 

308 self.update_field(_K.STRIPE_SETTINGS, new_settings) 

309 

310 def demote_stripe_account(self) -> None: 

311 """Demote StripeAccountModel → StripeCustomerModel, discarding all account-only fields.""" 

312 old_settings = self.stripe_settings 

313 if not StripeGuard.is_account(old_settings): 

314 msg = ( 

315 f"User {self.doc_id} does not have stripe account settings, demotion not required." 

316 ) 

317 LOG().info(msg) 

318 return 

319 

320 if IS_TRACE: 

321 LOG().trace(f"Demoting user {self.doc_id} from Account -> Customer.") 

322 

323 customer_fields = StripeCustomerModel.model_fields.keys() 

324 customer_data = { 

325 k: v 

326 for k, v in old_settings.model_dump(exclude={"type"}).items() 

327 if k in customer_fields 

328 } 

329 new_settings = StripeCustomerModel(**customer_data) 

330 self.update_field(_K.STRIPE_SETTINGS, new_settings) 

331 

332 # <AUTO_GENERATED_CONTENT> - do not edit 

333 

334 @property 

335 def contact_name(self) -> str: 

336 return self._model.contact_name 

337 

338 @property 

339 def tz_str(self) -> str | None: 

340 return self._model.tz_str 

341 

342 @tz_str.setter 

343 def tz_str(self, value: str | None) -> None: 

344 self.update_field(_K.TZ_STR, value) 

345 

346 @property 

347 def auth_type(self) -> AuthType: 

348 return self._model.auth_type 

349 

350 @auth_type.setter 

351 def auth_type(self, value: AuthType) -> None: 

352 self.update_field(_K.AUTH_TYPE, value) 

353 

354 @property 

355 def email(self) -> str: 

356 return self._model.email 

357 

358 @email.setter 

359 def email(self, value: str) -> None: 

360 self.update_field(_K.EMAIL, value) 

361 

362 @property 

363 def reputation(self) -> int: 

364 return self._model.reputation 

365 

366 @reputation.setter 

367 def reputation(self, value: int) -> None: 

368 self.update_field(_K.REPUTATION, value) 

369 

370 @property 

371 def fee_type(self) -> AppFeeType: 

372 return self._model.fee_type 

373 

374 @fee_type.setter 

375 def fee_type(self, value: AppFeeType) -> None: 

376 self.update_field(_K.FEE_TYPE, value) 

377 

378 @property 

379 def level(self) -> UserLevelType: 

380 return self._model.level 

381 

382 @level.setter 

383 def level(self, value: UserLevelType) -> None: 

384 self.update_field(_K.LEVEL, value) 

385 

386 @property 

387 def visibility(self) -> AppVisibility: 

388 return self._model.visibility 

389 

390 @visibility.setter 

391 def visibility(self, value: AppVisibility) -> None: 

392 self.update_field(_K.VISIBILITY, value) 

393 

394 @property 

395 def restriction_id(self) -> str | None: 

396 return self._model.restriction_id 

397 

398 @restriction_id.setter 

399 def restriction_id(self, value: str | None) -> None: 

400 self.update_field(_K.RESTRICTION_ID, value) 

401 

402 @property 

403 def invite_id(self) -> str | None: 

404 return self._model.invite_id 

405 

406 @invite_id.setter 

407 def invite_id(self, value: str | None) -> None: 

408 self.update_field(_K.INVITE_ID, value) 

409 

410 @property 

411 def facebook_token(self) -> str | None: 

412 return self._model.facebook_token 

413 

414 @facebook_token.setter 

415 def facebook_token(self, value: str | None) -> None: 

416 self.update_field(_K.FACEBOOK_TOKEN, value) 

417 

418 @property 

419 def facebook_id(self) -> str | None: 

420 return self._model.facebook_id 

421 

422 @facebook_id.setter 

423 def facebook_id(self, value: str | None) -> None: 

424 self.update_field(_K.FACEBOOK_ID, value) 

425 

426 @property 

427 def password(self) -> str | None: 

428 return self._model.password 

429 

430 @password.setter 

431 def password(self, value: str | None) -> None: 

432 self.update_field(_K.PASSWORD, value) 

433 

434 @property 

435 def pin_code(self) -> str | None: 

436 return self._model.pin_code 

437 

438 @pin_code.setter 

439 def pin_code(self, value: str | None) -> None: 

440 self.update_field(_K.PIN_CODE, value) 

441 

442 @property 

443 def delete_code(self) -> str | None: 

444 return self._model.delete_code 

445 

446 @delete_code.setter 

447 def delete_code(self, value: str | None) -> None: 

448 self.update_field(_K.DELETE_CODE, value) 

449 

450 @property 

451 def name(self) -> str | None: 

452 return self._model.name 

453 

454 @name.setter 

455 def name(self, value: str | None) -> None: 

456 self.update_field(_K.NAME, value) 

457 

458 @property 

459 def display_name(self) -> str | None: 

460 return self._model.display_name 

461 

462 @display_name.setter 

463 def display_name(self, value: str | None) -> None: 

464 self.update_field(_K.DISPLAY_NAME, value) 

465 

466 @property 

467 def description(self) -> str | None: 

468 return self._model.description 

469 

470 @description.setter 

471 def description(self, value: str | None) -> None: 

472 self.update_field(_K.DESCRIPTION, value) 

473 

474 @property 

475 def avatar(self) -> ImageModel | None: 

476 return self._model.avatar 

477 

478 @avatar.setter 

479 def avatar(self, value: ImageModel | None) -> None: 

480 self.update_field(_K.AVATAR, value) 

481 

482 @property 

483 def website_uri(self) -> str | None: 

484 return self._model.website_uri 

485 

486 @website_uri.setter 

487 def website_uri(self, value: str | None) -> None: 

488 self.update_field(_K.WEBSITE_URI, value) 

489 

490 @property 

491 def email_verified(self) -> bool: 

492 return self._model.email_verified 

493 

494 @email_verified.setter 

495 def email_verified(self, value: bool) -> None: 

496 self.update_field(_K.EMAIL_VERIFIED, value) 

497 

498 @property 

499 def must_reset_password(self) -> bool: 

500 return self._model.must_reset_password 

501 

502 @must_reset_password.setter 

503 def must_reset_password(self, value: bool) -> None: 

504 self.update_field(_K.MUST_RESET_PASSWORD, value) 

505 

506 @property 

507 def email_notifs_enabled(self) -> bool: 

508 return self._model.email_notifs_enabled 

509 

510 @email_notifs_enabled.setter 

511 def email_notifs_enabled(self, value: bool) -> None: 

512 self.update_field(_K.EMAIL_NOTIFS_ENABLED, value) 

513 

514 @property 

515 def enable_haptic(self) -> bool: 

516 return self._model.enable_haptic 

517 

518 @enable_haptic.setter 

519 def enable_haptic(self, value: bool) -> None: 

520 self.update_field(_K.ENABLE_HAPTIC, value) 

521 

522 @property 

523 def unread_activity_count(self) -> int: 

524 return self._model.unread_activity_count 

525 

526 @unread_activity_count.setter 

527 def unread_activity_count(self, value: int) -> None: 

528 self.update_field(_K.UNREAD_ACTIVITY_COUNT, value) 

529 

530 @property 

531 def video_history_count(self) -> int: 

532 return self._model.video_history_count 

533 

534 @video_history_count.setter 

535 def video_history_count(self, value: int) -> None: 

536 self.update_field(_K.VIDEO_HISTORY_COUNT, value) 

537 

538 @property 

539 def notification_count(self) -> int: 

540 return self._model.notification_count 

541 

542 @notification_count.setter 

543 def notification_count(self, value: int) -> None: 

544 self.update_field(_K.NOTIFICATION_COUNT, value) 

545 

546 @property 

547 def archive_count(self) -> int: 

548 return self._model.archive_count 

549 

550 @archive_count.setter 

551 def archive_count(self, value: int) -> None: 

552 self.update_field(_K.ARCHIVE_COUNT, value) 

553 

554 @property 

555 def auto_play_on_scroll(self) -> bool: 

556 return self._model.auto_play_on_scroll 

557 

558 @auto_play_on_scroll.setter 

559 def auto_play_on_scroll(self, value: bool) -> None: 

560 self.update_field(_K.AUTO_PLAY_ON_SCROLL, value) 

561 

562 @property 

563 def continue_type(self) -> VideoContinueType: 

564 return self._model.continue_type 

565 

566 @continue_type.setter 

567 def continue_type(self, value: VideoContinueType) -> None: 

568 self.update_field(_K.CONTINUE_TYPE, value) 

569 

570 @property 

571 def auto_mute(self) -> bool: 

572 return self._model.auto_mute 

573 

574 @auto_mute.setter 

575 def auto_mute(self, value: bool) -> None: 

576 self.update_field(_K.AUTO_MUTE, value) 

577 

578 @property 

579 def swipe_left_to_archive(self) -> bool: 

580 return self._model.swipe_left_to_archive 

581 

582 @swipe_left_to_archive.setter 

583 def swipe_left_to_archive(self, value: bool) -> None: 

584 self.update_field(_K.SWIPE_LEFT_TO_ARCHIVE, value) 

585 

586 @property 

587 def prompt_for_confirmation(self) -> bool: 

588 return self._model.prompt_for_confirmation 

589 

590 @prompt_for_confirmation.setter 

591 def prompt_for_confirmation(self, value: bool) -> None: 

592 self.update_field(_K.PROMPT_FOR_CONFIRMATION, value) 

593 

594 @property 

595 def show_system_notifications(self) -> bool: 

596 return self._model.show_system_notifications 

597 

598 @show_system_notifications.setter 

599 def show_system_notifications(self, value: bool) -> None: 

600 self.update_field(_K.SHOW_SYSTEM_NOTIFICATIONS, value) 

601 

602 @property 

603 def cache_size(self) -> UserCacheType: 

604 return self._model.cache_size 

605 

606 @cache_size.setter 

607 def cache_size(self, value: UserCacheType) -> None: 

608 self.update_field(_K.CACHE_SIZE, value) 

609 

610 @property 

611 def archive_time(self) -> UserArchiveType: 

612 return self._model.archive_time 

613 

614 @archive_time.setter 

615 def archive_time(self, value: UserArchiveType) -> None: 

616 self.update_field(_K.ARCHIVE_TIME, value) 

617 

618 @property 

619 def view_stats(self) -> ViewStatsModel: 

620 return self._model.view_stats 

621 

622 @view_stats.setter 

623 def view_stats(self, value: ViewStatsModel) -> None: 

624 self.update_field(_K.VIEW_STATS, value) 

625 

626 @property 

627 def dare_stats(self) -> DareStatsModel: 

628 return self._model.dare_stats 

629 

630 @dare_stats.setter 

631 def dare_stats(self, value: DareStatsModel) -> None: 

632 self.update_field(_K.DARE_STATS, value) 

633 

634 @property 

635 def stripe_settings(self) -> StripeSettingsType | None: 

636 return self._model.stripe_settings 

637 

638 @stripe_settings.setter 

639 def stripe_settings(self, value: StripeSettingsType | None) -> None: 

640 self.update_field(_K.STRIPE_SETTINGS, value) 

641 

642 @property 

643 def location(self) -> LocationModel | None: 

644 return self._model.location 

645 

646 @location.setter 

647 def location(self, value: LocationModel | None) -> None: 

648 self.update_field(_K.LOCATION, value) 

649 

650 @property 

651 def flagged(self) -> FlagModel | None: 

652 return self._model.flagged 

653 

654 @flagged.setter 

655 def flagged(self, value: FlagModel | None) -> None: 

656 self.update_field(_K.FLAGGED, value) 

657 

658 # base internal fields 

659 @property 

660 def version(self) -> int: 

661 return self._model.version 

662 

663 @version.setter 

664 def version(self, value: int) -> None: 

665 self.update_field(_K.VERSION, value) 

666 

667 @property 

668 def processed(self) -> bool: 

669 return self._model.processed 

670 

671 @processed.setter 

672 def processed(self, value: bool) -> None: 

673 self.update_field(_K.PROCESSED, value) 

674 

675 @property 

676 def error_count(self) -> int: 

677 return self._model.error_count 

678 

679 @error_count.setter 

680 def error_count(self, value: int) -> None: 

681 self.update_field(_K.ERROR_COUNT, value) 

682 

683 # user specific internal fields 

684 @property 

685 def invite_processed(self) -> bool: 

686 return self._model.invite_processed 

687 

688 @invite_processed.setter 

689 def invite_processed(self, value: bool) -> None: 

690 self.update_field(_K.INVITE_PROCESSED, value) 

691 

692 @property 

693 def context_created(self) -> bool: 

694 return self._model.context_created 

695 

696 @context_created.setter 

697 def context_created(self, value: bool) -> None: 

698 self.update_field(_K.CONTEXT_CREATED, value) 

699 

700 @property 

701 def search_indexed(self) -> bool: 

702 return self._model.search_indexed 

703 

704 @search_indexed.setter 

705 def search_indexed(self, value: bool) -> None: 

706 self.update_field(_K.SEARCH_INDEXED, value) 

707 

708 # </AUTO_GENERATED_CONTENT> - do not edit