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.
- {boarddata-6.7.2 → boarddata-6.7.4}/PKG-INFO +1 -1
- {boarddata-6.7.2 → boarddata-6.7.4}/__init__.py +1 -1
- {boarddata-6.7.2 → boarddata-6.7.4}/_comex.py +98 -16
- {boarddata-6.7.2 → boarddata-6.7.4}/_committees.py +1 -3
- {boarddata-6.7.2 → boarddata-6.7.4}/_directors.py +4 -4
- {boarddata-6.7.2 → boarddata-6.7.4}/_utilities.py +3 -2
- {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/PKG-INFO +1 -1
- {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/SOURCES.txt +2 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/pyproject.toml +1 -1
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_comex.py +156 -4
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_directors.py +2 -2
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_exports.py +4 -4
- boarddata-6.7.4/tests/test_politic_fixture_conformance.py +116 -0
- boarddata-6.7.4/tests/test_types_politic.py +398 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/__init__.py +26 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/auditors.py +3 -3
- {boarddata-6.7.2 → boarddata-6.7.4}/types/comex.py +251 -10
- boarddata-6.7.4/types/core.py +172 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/directors.py +2 -2
- boarddata-6.7.2/types/core.py +0 -97
- {boarddata-6.7.2 → boarddata-6.7.4}/CLAUDE.md +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/README.md +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_assemblies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_auditors.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_base.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_companies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_criteria.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_documents.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_esg.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_indexes.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_persons.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_risks.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/_sentinel.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/dependency_links.txt +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/requires.txt +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/boarddata.egg-info/top_level.txt +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/cache.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/client.py +1 -1
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/__init__.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/adjudicate.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/apply.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/context.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/db.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/domains.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/models.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/oracle.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/registry.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/report.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/__init__.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/cross_company.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/rules/lexical.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/s3.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/dq/verify_s3.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/errors.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/py.typed +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/__init__.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_apply.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_batch.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_plan.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_revert.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_scan.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/scripts/dq_verify_s3.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/setup.cfg +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/__init__.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/conftest.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_assemblies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_auditors.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_base.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_build_payload.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_cache.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_committees.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_companies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_config.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_criteria.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_documents.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_esg.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_indexes.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_persons.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_risks.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_sentinel.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/tests/test_utilities.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/assemblies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/committees.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/companies.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/criteria.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/documents.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/esg.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/indexes.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/persons.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/risks.py +0 -0
- {boarddata-6.7.2 → boarddata-6.7.4}/types/sentinel.py +0 -0
|
@@ -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
|
|
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 (``"
|
|
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 (``"
|
|
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:
|
|
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[
|
|
355
|
-
component_achievement_rates: list[
|
|
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 (``"
|
|
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
|
|
403
|
-
component_achievement_rates: List of achievement
|
|
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:
|
|
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[
|
|
504
|
-
component_achievement_rates: list[
|
|
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 (``"
|
|
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
|
|
552
|
-
component_achievement_rates: List of achievement
|
|
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
|
|
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 (``"
|
|
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 (``"
|
|
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 (``"
|
|
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 (``"
|
|
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
|
-
) ->
|
|
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
|
|
84
|
+
Dict mapping ``object_id -> field_name -> list`` of FieldSourceItem dicts.
|
|
84
85
|
|
|
85
86
|
Raises:
|
|
86
87
|
BoardDataError: On non-2xx API response.
|
|
@@ -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
|
|
@@ -27,7 +27,7 @@ class TestCreateComexCompensation:
|
|
|
27
27
|
c.create_comex_compensation(
|
|
28
28
|
"comex-uuid",
|
|
29
29
|
2024,
|
|
30
|
-
"
|
|
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
|
-
"
|
|
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"] == "
|
|
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, "
|
|
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, "
|
|
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"] == "
|
|
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="
|
|
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"] == "
|
|
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="
|
|
198
|
+
c.export_comex(nature="politic")
|
|
199
199
|
_, kwargs = mock_req.call_args
|
|
200
|
-
assert kwargs["params"]["nature"] == "
|
|
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()
|