boarddata 6.7.2__tar.gz → 6.7.4__tar.gz

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 (91) hide show
  1. {boarddata-6.7.2 → boarddata-6.7.4}/PKG-INFO +1 -1
  2. {boarddata-6.7.2 → boarddata-6.7.4}/__init__.py +1 -1
  3. {boarddata-6.7.2 → boarddata-6.7.4}/_comex.py +98 -16
  4. {boarddata-6.7.2 → boarddata-6.7.4}/_committees.py +1 -3
  5. {boarddata-6.7.2 → boarddata-6.7.4}/_directors.py +4 -4
  6. {boarddata-6.7.2 → boarddata-6.7.4}/_utilities.py +3 -2
  7. {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/PKG-INFO +1 -1
  8. {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/SOURCES.txt +2 -0
  9. {boarddata-6.7.2 → boarddata-6.7.4}/pyproject.toml +1 -1
  10. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_comex.py +156 -4
  11. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_directors.py +2 -2
  12. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_exports.py +4 -4
  13. boarddata-6.7.4/tests/test_politic_fixture_conformance.py +116 -0
  14. boarddata-6.7.4/tests/test_types_politic.py +398 -0
  15. {boarddata-6.7.2 → boarddata-6.7.4}/types/__init__.py +26 -0
  16. {boarddata-6.7.2 → boarddata-6.7.4}/types/auditors.py +3 -3
  17. {boarddata-6.7.2 → boarddata-6.7.4}/types/comex.py +251 -10
  18. boarddata-6.7.4/types/core.py +172 -0
  19. {boarddata-6.7.2 → boarddata-6.7.4}/types/directors.py +2 -2
  20. boarddata-6.7.2/types/core.py +0 -97
  21. {boarddata-6.7.2 → boarddata-6.7.4}/CLAUDE.md +0 -0
  22. {boarddata-6.7.2 → boarddata-6.7.4}/README.md +0 -0
  23. {boarddata-6.7.2 → boarddata-6.7.4}/_assemblies.py +0 -0
  24. {boarddata-6.7.2 → boarddata-6.7.4}/_auditors.py +0 -0
  25. {boarddata-6.7.2 → boarddata-6.7.4}/_base.py +0 -0
  26. {boarddata-6.7.2 → boarddata-6.7.4}/_companies.py +0 -0
  27. {boarddata-6.7.2 → boarddata-6.7.4}/_criteria.py +0 -0
  28. {boarddata-6.7.2 → boarddata-6.7.4}/_documents.py +0 -0
  29. {boarddata-6.7.2 → boarddata-6.7.4}/_esg.py +0 -0
  30. {boarddata-6.7.2 → boarddata-6.7.4}/_indexes.py +0 -0
  31. {boarddata-6.7.2 → boarddata-6.7.4}/_persons.py +0 -0
  32. {boarddata-6.7.2 → boarddata-6.7.4}/_risks.py +0 -0
  33. {boarddata-6.7.2 → boarddata-6.7.4}/_sentinel.py +0 -0
  34. {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/dependency_links.txt +0 -0
  35. {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/requires.txt +0 -0
  36. {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/top_level.txt +0 -0
  37. {boarddata-6.7.2 → boarddata-6.7.4}/cache.py +0 -0
  38. {boarddata-6.7.2 → boarddata-6.7.4}/client.py +1 -1
  39. {boarddata-6.7.2 → boarddata-6.7.4}/dq/__init__.py +0 -0
  40. {boarddata-6.7.2 → boarddata-6.7.4}/dq/adjudicate.py +0 -0
  41. {boarddata-6.7.2 → boarddata-6.7.4}/dq/apply.py +0 -0
  42. {boarddata-6.7.2 → boarddata-6.7.4}/dq/context.py +0 -0
  43. {boarddata-6.7.2 → boarddata-6.7.4}/dq/db.py +0 -0
  44. {boarddata-6.7.2 → boarddata-6.7.4}/dq/domains.py +0 -0
  45. {boarddata-6.7.2 → boarddata-6.7.4}/dq/models.py +0 -0
  46. {boarddata-6.7.2 → boarddata-6.7.4}/dq/oracle.py +0 -0
  47. {boarddata-6.7.2 → boarddata-6.7.4}/dq/registry.py +0 -0
  48. {boarddata-6.7.2 → boarddata-6.7.4}/dq/report.py +0 -0
  49. {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/__init__.py +0 -0
  50. {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/cross_company.py +0 -0
  51. {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/lexical.py +0 -0
  52. {boarddata-6.7.2 → boarddata-6.7.4}/dq/s3.py +0 -0
  53. {boarddata-6.7.2 → boarddata-6.7.4}/dq/verify_s3.py +0 -0
  54. {boarddata-6.7.2 → boarddata-6.7.4}/errors.py +0 -0
  55. {boarddata-6.7.2 → boarddata-6.7.4}/py.typed +0 -0
  56. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/__init__.py +0 -0
  57. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_apply.py +0 -0
  58. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_batch.py +0 -0
  59. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_plan.py +0 -0
  60. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_revert.py +0 -0
  61. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_scan.py +0 -0
  62. {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_verify_s3.py +0 -0
  63. {boarddata-6.7.2 → boarddata-6.7.4}/setup.cfg +0 -0
  64. {boarddata-6.7.2 → boarddata-6.7.4}/tests/__init__.py +0 -0
  65. {boarddata-6.7.2 → boarddata-6.7.4}/tests/conftest.py +0 -0
  66. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_assemblies.py +0 -0
  67. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_auditors.py +0 -0
  68. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_base.py +0 -0
  69. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_build_payload.py +0 -0
  70. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_cache.py +0 -0
  71. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_committees.py +0 -0
  72. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_companies.py +0 -0
  73. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_config.py +0 -0
  74. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_criteria.py +0 -0
  75. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_documents.py +0 -0
  76. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_esg.py +0 -0
  77. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_indexes.py +0 -0
  78. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_persons.py +0 -0
  79. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_risks.py +0 -0
  80. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_sentinel.py +0 -0
  81. {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_utilities.py +0 -0
  82. {boarddata-6.7.2 → boarddata-6.7.4}/types/assemblies.py +0 -0
  83. {boarddata-6.7.2 → boarddata-6.7.4}/types/committees.py +0 -0
  84. {boarddata-6.7.2 → boarddata-6.7.4}/types/companies.py +0 -0
  85. {boarddata-6.7.2 → boarddata-6.7.4}/types/criteria.py +0 -0
  86. {boarddata-6.7.2 → boarddata-6.7.4}/types/documents.py +0 -0
  87. {boarddata-6.7.2 → boarddata-6.7.4}/types/esg.py +0 -0
  88. {boarddata-6.7.2 → boarddata-6.7.4}/types/indexes.py +0 -0
  89. {boarddata-6.7.2 → boarddata-6.7.4}/types/persons.py +0 -0
  90. {boarddata-6.7.2 → boarddata-6.7.4}/types/risks.py +0 -0
  91. {boarddata-6.7.2 → boarddata-6.7.4}/types/sentinel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.7.2
3
+ Version: 6.7.4
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -6,7 +6,7 @@ from .cache import FileTokenCache, ScalewaySecretTokenCache
6
6
  from .client import BoardDataClient
7
7
  from .errors import BoardDataError, ExportTooLargeError
8
8
 
9
- __version__ = "6.7.2"
9
+ __version__ = "6.7.4"
10
10
  __all__ = [
11
11
  "Base",
12
12
  "BoardDataClient",
@@ -5,7 +5,14 @@ from __future__ import annotations
5
5
  import logging
6
6
  from typing import Any
7
7
 
8
- from .types.comex import ComexCompensationDetail, ComexDetail, ComexExportItem
8
+ from .types.comex import (
9
+ ComexCompensationDetail,
10
+ ComexDetail,
11
+ ComexExportItem,
12
+ ComponentAchievementRateWritePayload,
13
+ CriterionWritePayload,
14
+ Nature,
15
+ )
9
16
  from .types.core import FieldSourcePayload, PaginatedResponse, UpsertResult
10
17
 
11
18
  logger = logging.getLogger("boarddata")
@@ -50,7 +57,7 @@ class ComexMixin:
50
57
  date_from: Filter mandates valid from this date.
51
58
  date_to: Filter mandates valid to this date.
52
59
  as_of: Show mandates active on this date.
53
- nature: Filter by compensation nature (``"REAL"`` or ``"POLITIC"``).
60
+ nature: Filter by compensation nature (``"real"`` or ``"politic"``).
54
61
  roles: Filter by role identifiers (e.g. ``["CEO", "CFO"]``).
55
62
  role_at: Filter for mandates that held a role at this date (``"YYYY-MM-DD"``).
56
63
  page: Page number (1-indexed).
@@ -231,7 +238,7 @@ class ComexMixin:
231
238
  date_from: Filter mandates valid from this date (``"YYYY-MM-DD"``).
232
239
  date_to: Filter mandates valid to this date (``"YYYY-MM-DD"``).
233
240
  as_of: Show mandates active on this date (``"YYYY-MM-DD"``).
234
- nature: Filter by compensation nature (``"REAL"`` or ``"POLITIC"``).
241
+ nature: Filter by compensation nature (``"real"`` or ``"politic"``).
235
242
  **kwargs: Advanced filters forwarded as query params.
236
243
 
237
244
  Returns:
@@ -310,7 +317,7 @@ class ComexMixin:
310
317
  self,
311
318
  comex_uid: str,
312
319
  fiscal_year: int,
313
- nature: str,
320
+ nature: Nature,
314
321
  *,
315
322
  assembly: str | None = None,
316
323
  fixed: str | None = None,
@@ -349,17 +356,27 @@ class ComexMixin:
349
356
  discretionary_grant: bool | None = None,
350
357
  subsidiary_remuneration: str | None = None,
351
358
  remuneration_via_holding: bool | None = None,
359
+ sti_basis: str | None = None,
360
+ variable_max: str | None = None,
361
+ variable_target_pct: str | None = None,
362
+ variable_max_pct: str | None = None,
363
+ variable_target_value: str | None = None,
364
+ variable_max_value: str | None = None,
365
+ reference_amount: str | None = None,
366
+ lti_grant_cap_basis: str | None = None,
367
+ lti_grant_cap_value: str | None = None,
368
+ policy_changed_midyear: bool | None = None,
352
369
  currency: str | None = None,
353
370
  field_sources: list[FieldSourcePayload] | None = None,
354
- criteria: list[dict[str, Any]] | None = None,
355
- component_achievement_rates: list[dict[str, Any]] | None = None,
371
+ criteria: list[CriterionWritePayload] | None = None,
372
+ component_achievement_rates: list[ComponentAchievementRateWritePayload] | None = None,
356
373
  ) -> ComexCompensationDetail:
357
374
  """Create a compensation entry for a comex mandate.
358
375
 
359
376
  Args:
360
377
  comex_uid: Comex mandate UUID.
361
378
  fiscal_year: Fiscal year.
362
- nature: Compensation nature (``"REAL"`` or ``"POLITIC"``).
379
+ nature: Compensation nature (``"real"`` or ``"politic"``).
363
380
  assembly: Assembly UUID.
364
381
  fixed: Fixed compensation (decimal).
365
382
  variable: Variable compensation (decimal).
@@ -397,10 +414,27 @@ class ComexMixin:
397
414
  discretionary_grant: Whether discretionary grant applies.
398
415
  subsidiary_remuneration: Subsidiary remuneration (decimal).
399
416
  remuneration_via_holding: Whether remuneration is provided via holding.
417
+ sti_basis: Disclosed STI base form (frozen enum, e.g. ``"pct_of_fixed"``).
418
+ variable_max: STI cap in EUR (decimal string; always €, never
419
+ disclosed-basis — the disclosed-basis cap goes in
420
+ ``variable_max_value``).
421
+ variable_target_pct: STI target normalized to % of fixed (decimal
422
+ string; stated or derivation-backed, never back-solved).
423
+ variable_max_pct: STI cap normalized to % of fixed (decimal string).
424
+ variable_target_value: STI target in the DISCLOSED basis (decimal string;
425
+ interpretation governed by ``sti_basis`` — never converted).
426
+ variable_max_value: STI cap in the DISCLOSED basis (decimal string).
427
+ reference_amount: STI reference base amount in EUR (decimal string;
428
+ ``sti_basis="pct_of_reference_amount"`` archetype).
429
+ lti_grant_cap_basis: Disclosed LTI grant-cap basis (frozen enum).
430
+ lti_grant_cap_value: LTI grant cap in the DISCLOSED basis (decimal string).
431
+ policy_changed_midyear: Policy regime changed mid-year; the stored
432
+ row is the fiscal-year-end regime (D5).
400
433
  currency: Currency code (e.g. ``"EUR"``).
401
434
  field_sources: Document provenance for fields.
402
- criteria: List of compensation criteria dicts.
403
- component_achievement_rates: List of achievement rate dicts.
435
+ criteria: List of nested criteria entries (see ``CriterionWritePayload``).
436
+ component_achievement_rates: List of achievement-rate entries (see
437
+ ``ComponentAchievementRateWritePayload``).
404
438
 
405
439
  Returns:
406
440
  Created ComexCompensationDetail.
@@ -448,6 +482,16 @@ class ComexMixin:
448
482
  discretionary_grant=discretionary_grant,
449
483
  subsidiary_remuneration=subsidiary_remuneration,
450
484
  remuneration_via_holding=remuneration_via_holding,
485
+ sti_basis=sti_basis,
486
+ variable_max=variable_max,
487
+ variable_target_pct=variable_target_pct,
488
+ variable_max_pct=variable_max_pct,
489
+ variable_target_value=variable_target_value,
490
+ variable_max_value=variable_max_value,
491
+ reference_amount=reference_amount,
492
+ lti_grant_cap_basis=lti_grant_cap_basis,
493
+ lti_grant_cap_value=lti_grant_cap_value,
494
+ policy_changed_midyear=policy_changed_midyear,
451
495
  currency=currency,
452
496
  field_sources=field_sources,
453
497
  criteria=criteria,
@@ -459,7 +503,7 @@ class ComexMixin:
459
503
  self,
460
504
  comex_uid: str,
461
505
  fiscal_year: int,
462
- nature: str,
506
+ nature: Nature,
463
507
  *,
464
508
  assembly: str | None = None,
465
509
  fixed: str | None = None,
@@ -498,17 +542,27 @@ class ComexMixin:
498
542
  discretionary_grant: bool | None = None,
499
543
  subsidiary_remuneration: str | None = None,
500
544
  remuneration_via_holding: bool | None = None,
545
+ sti_basis: str | None = None,
546
+ variable_max: str | None = None,
547
+ variable_target_pct: str | None = None,
548
+ variable_max_pct: str | None = None,
549
+ variable_target_value: str | None = None,
550
+ variable_max_value: str | None = None,
551
+ reference_amount: str | None = None,
552
+ lti_grant_cap_basis: str | None = None,
553
+ lti_grant_cap_value: str | None = None,
554
+ policy_changed_midyear: bool | None = None,
501
555
  currency: str | None = None,
502
556
  field_sources: list[FieldSourcePayload] | None = None,
503
- criteria: list[dict[str, Any]] | None = None,
504
- component_achievement_rates: list[dict[str, Any]] | None = None,
557
+ criteria: list[CriterionWritePayload] | None = None,
558
+ component_achievement_rates: list[ComponentAchievementRateWritePayload] | None = None,
505
559
  ) -> dict[str, Any]:
506
560
  """Replace (clean-swap) a compensation entry for a comex mandate.
507
561
 
508
562
  Args:
509
563
  comex_uid: Comex mandate UUID.
510
564
  fiscal_year: Fiscal year.
511
- nature: Compensation nature (``"REAL"`` or ``"POLITIC"``).
565
+ nature: Compensation nature (``"real"`` or ``"politic"``).
512
566
  assembly: Assembly UUID.
513
567
  fixed: Fixed compensation (decimal).
514
568
  variable: Variable compensation (decimal).
@@ -546,13 +600,31 @@ class ComexMixin:
546
600
  discretionary_grant: Whether discretionary grant applies.
547
601
  subsidiary_remuneration: Subsidiary remuneration (decimal).
548
602
  remuneration_via_holding: Whether remuneration is provided via holding.
603
+ sti_basis: Disclosed STI base form (frozen enum, e.g. ``"pct_of_fixed"``).
604
+ variable_max: STI cap in EUR (decimal string; always €, never
605
+ disclosed-basis — the disclosed-basis cap goes in
606
+ ``variable_max_value``).
607
+ variable_target_pct: STI target normalized to % of fixed (decimal
608
+ string; stated or derivation-backed, never back-solved).
609
+ variable_max_pct: STI cap normalized to % of fixed (decimal string).
610
+ variable_target_value: STI target in the DISCLOSED basis (decimal string;
611
+ interpretation governed by ``sti_basis`` — never converted).
612
+ variable_max_value: STI cap in the DISCLOSED basis (decimal string).
613
+ reference_amount: STI reference base amount in EUR (decimal string;
614
+ ``sti_basis="pct_of_reference_amount"`` archetype).
615
+ lti_grant_cap_basis: Disclosed LTI grant-cap basis (frozen enum).
616
+ lti_grant_cap_value: LTI grant cap in the DISCLOSED basis (decimal string).
617
+ policy_changed_midyear: Policy regime changed mid-year; the stored
618
+ row is the fiscal-year-end regime (D5).
549
619
  currency: Currency code (e.g. ``"EUR"``).
550
620
  field_sources: Document provenance for fields.
551
- criteria: List of compensation criteria dicts.
552
- component_achievement_rates: List of achievement rate dicts.
621
+ criteria: List of nested criteria entries (see ``CriterionWritePayload``).
622
+ component_achievement_rates: List of achievement-rate entries (see
623
+ ``ComponentAchievementRateWritePayload``).
553
624
 
554
625
  Returns:
555
- dict with {"id", "action"} where action is "replaced" (a prior live row existed) or "created" (none existed).
626
+ dict with {"id", "action"} where action is "replaced" (a prior live row
627
+ existed) or "created" (none existed).
556
628
 
557
629
  Raises:
558
630
  BoardDataError: On non-2xx API response.
@@ -597,6 +669,16 @@ class ComexMixin:
597
669
  discretionary_grant=discretionary_grant,
598
670
  subsidiary_remuneration=subsidiary_remuneration,
599
671
  remuneration_via_holding=remuneration_via_holding,
672
+ sti_basis=sti_basis,
673
+ variable_max=variable_max,
674
+ variable_target_pct=variable_target_pct,
675
+ variable_max_pct=variable_max_pct,
676
+ variable_target_value=variable_target_value,
677
+ variable_max_value=variable_max_value,
678
+ reference_amount=reference_amount,
679
+ lti_grant_cap_basis=lti_grant_cap_basis,
680
+ lti_grant_cap_value=lti_grant_cap_value,
681
+ policy_changed_midyear=policy_changed_midyear,
600
682
  currency=currency,
601
683
  field_sources=field_sources,
602
684
  criteria=criteria,
@@ -6,9 +6,7 @@ from .types.committees import CommitteeBatchItemPayload, CommitteeBatchResultIte
6
6
 
7
7
 
8
8
  class CommitteesMixin:
9
- def batch_create_committees(
10
- self, items: list[CommitteeBatchItemPayload]
11
- ) -> list[CommitteeBatchResultItem]:
9
+ def batch_create_committees(self, items: list[CommitteeBatchItemPayload]) -> list[CommitteeBatchResultItem]:
12
10
  """Batch upsert board committees. Each item is keyed by issuer ISIN;
13
11
  the backend resolves members to existing persons and idempotently
14
12
  upserts Committee + CommitteeMembership. Returns one result per item
@@ -55,7 +55,7 @@ class DirectorMixin:
55
55
  expiring_year: Keep only mandates whose ``valid_to`` falls inside
56
56
  the given calendar year. Strictly excludes mandates with
57
57
  ``valid_to IS NULL`` (still active).
58
- nature: Filter by compensation nature (``"REAL"`` or ``"POLITIC"``).
58
+ nature: Filter by compensation nature (``"real"`` or ``"politic"``).
59
59
  roles: Filter by role identifiers (e.g. ``["Independent", "Chairman"]``).
60
60
  role_at: Filter for mandates that held a role at this date (``"YYYY-MM-DD"``).
61
61
  page: Page number (1-indexed).
@@ -244,7 +244,7 @@ class DirectorMixin:
244
244
  date_to: Filter mandates valid to this date (``"YYYY-MM-DD"``).
245
245
  as_of: Show mandates active on this date (``"YYYY-MM-DD"``).
246
246
  expiring_year: Keep only mandates expiring in the given year.
247
- nature: Filter by compensation nature (``"REAL"`` or ``"POLITIC"``).
247
+ nature: Filter by compensation nature (``"real"`` or ``"politic"``).
248
248
  **kwargs: Advanced filters forwarded as query params.
249
249
 
250
250
  Returns:
@@ -293,7 +293,7 @@ class DirectorMixin:
293
293
  Args:
294
294
  director_uid: Director mandate UUID.
295
295
  fiscal_year: Filter by fiscal year.
296
- nature: Filter by compensation nature (``"REAL"`` or ``"POLITIC"``).
296
+ nature: Filter by compensation nature (``"real"`` or ``"politic"``).
297
297
  page: Page number.
298
298
  page_size: Results per page.
299
299
 
@@ -356,7 +356,7 @@ class DirectorMixin:
356
356
  Args:
357
357
  director_uid: Director mandate UUID.
358
358
  fiscal_year: Fiscal year.
359
- nature: Compensation nature (``"REAL"`` or ``"POLITIC"``).
359
+ nature: Compensation nature (``"real"`` or ``"politic"``).
360
360
  assembly: Assembly UUID.
361
361
  token_personal: Personal attendance tokens (decimal).
362
362
  token_personal_paid: Personal attendance tokens paid (decimal).
@@ -7,6 +7,7 @@ from typing import Any
7
7
  from .types.core import (
8
8
  CreateFieldSourceDisputePayload,
9
9
  FieldSourceDisputeDetail,
10
+ FieldSourceItem,
10
11
  SearchResult,
11
12
  )
12
13
 
@@ -70,7 +71,7 @@ class UtilitiesMixin:
70
71
  object_ids: list[str],
71
72
  *,
72
73
  fields: list[str] | None = None,
73
- ) -> Any:
74
+ ) -> dict[str, dict[str, list[FieldSourceItem]]]:
74
75
  """Batch-fetch field sources for objects of a given content type.
75
76
 
76
77
  Args:
@@ -80,7 +81,7 @@ class UtilitiesMixin:
80
81
  fields: Optional list of field names to filter by.
81
82
 
82
83
  Returns:
83
- Dict mapping ``object_id -> field_name -> list`` of source dicts.
84
+ Dict mapping ``object_id -> field_name -> list`` of FieldSourceItem dicts.
84
85
 
85
86
  Raises:
86
87
  BoardDataError: On non-2xx API response.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.7.2
3
+ Version: 6.7.4
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -109,8 +109,10 @@ tests/test_esg.py
109
109
  tests/test_exports.py
110
110
  tests/test_indexes.py
111
111
  tests/test_persons.py
112
+ tests/test_politic_fixture_conformance.py
112
113
  tests/test_risks.py
113
114
  tests/test_sentinel.py
115
+ tests/test_types_politic.py
114
116
  tests/test_utilities.py
115
117
  types/__init__.py
116
118
  types/assemblies.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "boarddata"
7
- version = "6.7.2"
7
+ version = "6.7.4"
8
8
  description = "Python SDK for the BoardData V2 REST API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -27,7 +27,7 @@ class TestCreateComexCompensation:
27
27
  c.create_comex_compensation(
28
28
  "comex-uuid",
29
29
  2024,
30
- "REAL",
30
+ "real",
31
31
  fixed="500000.00",
32
32
  variable="200000.00",
33
33
  currency="EUR",
@@ -137,7 +137,7 @@ class TestReplaceComexCompensation:
137
137
  result = c.replace_comex_compensation(
138
138
  "comex-uuid",
139
139
  2024,
140
- "REAL",
140
+ "real",
141
141
  fixed="500000.00",
142
142
  variable="200000.00",
143
143
  currency="EUR",
@@ -151,11 +151,163 @@ class TestReplaceComexCompensation:
151
151
  assert payload["variable"] == "200000.00"
152
152
  assert payload["currency"] == "EUR"
153
153
  assert payload["fiscal_year"] == 2024
154
- assert payload["nature"] == "REAL"
154
+ assert payload["nature"] == "real"
155
155
  assert result == {"id": "comp9", "action": "replaced"}
156
156
 
157
157
  def test_returns_created_when_no_prior_row(self):
158
158
  c = make_client()
159
159
  c._request.return_value = {"id": "comp10", "action": "created"}
160
- result = c.replace_comex_compensation("comex-uuid", 2024, "POLITIC", fixed="1.00")
160
+ result = c.replace_comex_compensation("comex-uuid", 2024, "politic", fixed="1.00")
161
161
  assert result["action"] == "created"
162
+
163
+
164
+ def test_envelope_scalar_kwargs_in_signatures():
165
+ import inspect
166
+
167
+ from boarddata import BoardDataClient
168
+
169
+ for method in (
170
+ BoardDataClient.create_comex_compensation,
171
+ BoardDataClient.replace_comex_compensation,
172
+ ):
173
+ params = inspect.signature(method).parameters
174
+ for k in (
175
+ "sti_basis",
176
+ "variable_max",
177
+ "variable_target_pct",
178
+ "variable_max_pct",
179
+ "variable_target_value",
180
+ "variable_max_value",
181
+ "reference_amount",
182
+ "lti_grant_cap_basis",
183
+ "lti_grant_cap_value",
184
+ "policy_changed_midyear",
185
+ ):
186
+ assert k in params, f"{method.__name__} missing {k}"
187
+
188
+
189
+ def test_envelope_scalars_reach_the_wire():
190
+ c = make_client()
191
+ c._request.return_value = {"id": "1", "action": "created"}
192
+ c.replace_comex_compensation(
193
+ "uid",
194
+ 2025,
195
+ "real",
196
+ sti_basis="pct_of_fixed",
197
+ variable_target_value="150",
198
+ variable_max_value="180",
199
+ lti_grant_cap_basis="pct_of_fixed",
200
+ lti_grant_cap_value="150",
201
+ )
202
+ _, kwargs = c._request.call_args
203
+ p = kwargs["json"]
204
+ assert p["sti_basis"] == "pct_of_fixed"
205
+ assert p["variable_target_value"] == "150"
206
+ assert p["variable_max_value"] == "180"
207
+ assert p["lti_grant_cap_basis"] == "pct_of_fixed"
208
+ assert p["lti_grant_cap_value"] == "150"
209
+
210
+
211
+ def test_politic_envelope_scalars_reach_the_wire():
212
+ c = make_client()
213
+ c._request.return_value = {"id": "1", "action": "created"}
214
+ c.replace_comex_compensation(
215
+ "uid",
216
+ 2026,
217
+ "politic",
218
+ variable_max="2790000.00",
219
+ variable_target_pct="100.00",
220
+ variable_max_pct="180.00",
221
+ reference_amount="1400000.00",
222
+ policy_changed_midyear=False,
223
+ )
224
+ _, kwargs = c._request.call_args
225
+ p = kwargs["json"]
226
+ assert p["variable_max"] == "2790000.00"
227
+ assert p["variable_target_pct"] == "100.00"
228
+ assert p["variable_max_pct"] == "180.00"
229
+ assert p["reference_amount"] == "1400000.00"
230
+ # False is falsy but NOT None — it must reach the wire (only None is dropped).
231
+ assert p["policy_changed_midyear"] is False
232
+
233
+
234
+ def test_nested_criteria_pass_through_verbatim():
235
+ """Nested entries are passthrough: the SDK must never filter, coerce, or
236
+ rename keys inside criteria / rates / field_sources dicts."""
237
+ c = make_client()
238
+ c._request.return_value = {"id": "1", "action": "created"}
239
+ criterion = {
240
+ "criteria": "cat-uuid",
241
+ "component_type": "variable",
242
+ "weight": "30.00",
243
+ "weight_basis": "share_of_fixed",
244
+ "weight_is_max": True,
245
+ "rate_basis": "points_of_fixed",
246
+ "min_value": "ROE <= 6%",
247
+ "max_value": "ROE >= 15%",
248
+ "payout_curve": {
249
+ "v": 1,
250
+ "curve_type": "linear_ramp",
251
+ "interpolation": "linear",
252
+ "rate_basis": "points_of_fixed",
253
+ "points": [
254
+ {"label": "floor", "metric_numeric": "6", "payout_pct": "0"},
255
+ {"label": "cap", "metric_numeric": "15", "payout_pct": "30"},
256
+ ],
257
+ },
258
+ "disclosure": {"weight": "numeric", "rate_max": "numeric"},
259
+ "field_sources": [
260
+ {
261
+ "document": "doc-uuid",
262
+ "field_name": "amount_at_max",
263
+ "page_number": 424,
264
+ "derivation": {
265
+ "v": 1,
266
+ "status": "derived",
267
+ "formula_display": "€465,000 = 30% × €1,550,000",
268
+ "inputs": [{"name": "fixed", "value": "1550000", "page": 424}],
269
+ },
270
+ }
271
+ ],
272
+ }
273
+ rate = {
274
+ "component_type": "variable",
275
+ "achievement_rate_max": "100.00",
276
+ "payout_curve": {
277
+ "v": 1,
278
+ "curve_type": "rank_scale",
279
+ "interpolation": "none",
280
+ "rate_basis": "points_of_fixed",
281
+ "points": [{"label": "rank_1", "payout_pct": "180"}],
282
+ "overlays": [{"kind": "cap", "factor": "100", "applies_to": "summary"}],
283
+ },
284
+ }
285
+ c.create_comex_compensation(
286
+ "uid",
287
+ 2026,
288
+ "politic",
289
+ criteria=[criterion],
290
+ component_achievement_rates=[rate],
291
+ )
292
+ _, kwargs = c._request.call_args
293
+ p = kwargs["json"]
294
+ assert p["criteria"] == [criterion]
295
+ assert p["component_achievement_rates"] == [rate]
296
+
297
+
298
+ def test_no_uppercase_nature_in_docstrings():
299
+ """The wire is lowercase; docstrings advertising "REAL"/"POLITIC" are the
300
+ latent casing bug fixed EVERYWHERE by the 6.7.0 politic parts (design
301
+ §4.7 / §2 item 9) — comex and directors both."""
302
+ import inspect
303
+
304
+ from boarddata._comex import ComexMixin
305
+ from boarddata._directors import DirectorMixin
306
+
307
+ offenders = []
308
+ for mixin in (ComexMixin, DirectorMixin):
309
+ for name, member in inspect.getmembers(mixin, inspect.isfunction):
310
+ doc = inspect.getdoc(member) or ""
311
+ if '"REAL"' in doc or '"POLITIC"' in doc:
312
+ offenders.append(f"{mixin.__name__}.{name}")
313
+ assert not offenders, offenders
@@ -49,11 +49,11 @@ class TestCreateDirectorCompensation:
49
49
  def test_sends_token_fields(self):
50
50
  c = make_client()
51
51
  c._request.return_value = {"id": "comp1", "fiscal_year": 2024}
52
- c.create_director_compensation("dir-uuid", 2024, "REAL", token_personal="15000.00", currency="EUR")
52
+ c.create_director_compensation("dir-uuid", 2024, "real", token_personal="15000.00", currency="EUR")
53
53
  _, kwargs = c._request.call_args
54
54
  payload = kwargs["json"]
55
55
  assert payload["token_personal"] == "15000.00"
56
- assert payload["nature"] == "REAL"
56
+ assert payload["nature"] == "real"
57
57
  assert "fixed" not in payload # comex field, not board
58
58
 
59
59
 
@@ -152,10 +152,10 @@ class TestExportDirectors:
152
152
  def test_passes_filters(self):
153
153
  c = self._client()
154
154
  with patch("requests.request", return_value=_make_response(200, [])) as mock_req:
155
- c.export_directors(company="comp-uuid", nature="REAL")
155
+ c.export_directors(company="comp-uuid", nature="real")
156
156
  _, kwargs = mock_req.call_args
157
157
  assert kwargs["params"]["company"] == "comp-uuid"
158
- assert kwargs["params"]["nature"] == "REAL"
158
+ assert kwargs["params"]["nature"] == "real"
159
159
 
160
160
  def test_too_large_raises_export_too_large_error(self):
161
161
  c = self._client()
@@ -195,9 +195,9 @@ class TestExportComex:
195
195
  def test_passes_nature_filter(self):
196
196
  c = self._client()
197
197
  with patch("requests.request", return_value=_make_response(200, [])) as mock_req:
198
- c.export_comex(nature="POLITIC")
198
+ c.export_comex(nature="politic")
199
199
  _, kwargs = mock_req.call_args
200
- assert kwargs["params"]["nature"] == "POLITIC"
200
+ assert kwargs["params"]["nature"] == "politic"
201
201
 
202
202
  def test_too_large_raises_export_too_large_error(self):
203
203
  c = self._client()