Api reference
components.employment.public.api ¶
cancel ¶
Sets the employment to cancelled.
Source code in components/employment/public/api.py
employment_component_context ¶
A decorator to set a context variable indicating that an employment component function is running. This is useful to highlight updates of the legacy Employment table outside of the usage of the Employment Component which will help us finish the migration to the Employment Component.
Source code in components/employment/public/api.py
ingest_employment_declaration ¶
ingest_employment_declaration(
employment_declaration,
source_information,
commit,
event_bus_orchestrator=None,
retried_blocked_movement=None,
upstream_retried_blocked_movement=None,
source_rules_override=None,
)
Ingests the given employment declarations, notifies the consumers, and executes side effects.
Source code in components/employment/public/api.py
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | |
ingest_employment_declaration_without_blocked_movement ¶
ingest_employment_declaration_without_blocked_movement(
employment_declaration,
source_information,
commit,
event_bus_orchestrator=None,
source_rules_override=None,
)
Ingests the given employment declarations, notifies the consumers, and executes side effects.
Source code in components/employment/public/api.py
is_running_employment_component
module-attribute
¶
resume ¶
resume(
employment_id,
source_type,
source_information,
commit,
extended_information=None,
event_bus_orchestrator=None,
)
Resumes an ended employment.
Source code in components/employment/public/api.py
set_extended_employment_values ¶
set_extended_employment_values(
employment_id,
values,
validity_period,
source_type,
source_information,
commit,
event_bus_orchestrator=None,
)
Create a new ExtendedEmploymentUpdate with the given values for the employment with the given id.
The update will contain only the values passed as argument, the other values will remain unchanged. Doc: https://www.notion.so/alaninsurance/Definitions-employments-sources-etc-1301426e8be780cc9796ee31f49559e5?pvs=4#1301426e8be78052977ff4fbb932c8f7 ⧉
Source code in components/employment/public/api.py
set_external_employee_id ¶
set_external_employee_id(
employment_id,
new_external_employee_id,
source_type,
source_information,
commit,
event_bus_orchestrator=None,
)
Updates the external employee id of the employment with the given id. WARNING: This function does not check if the external employee id is valid according to local country rules. Please do that check upstream.
Source code in components/employment/public/api.py
terminate_or_update_termination ¶
terminate_or_update_termination(
employment_id,
end_date,
extended_information,
source_type,
source_information,
commit,
event_bus_orchestrator=None,
)
Source code in components/employment/public/api.py
transfer ¶
transfer(
current_employment_id,
new_employment_declaration,
source_information,
commit,
event_bus_orchestrator=None,
)
Source code in components/employment/public/api.py
uncancel ¶
Undo the cancellation of the employment.
Source code in components/employment/public/api.py
update_start_date ¶
update_start_date(
employment_id,
new_start_date,
source_type,
source_information,
commit,
event_bus_orchestrator=None,
)
Source code in components/employment/public/api.py
components.employment.public.api_v2 ¶
EmploymentApiSession ¶
Bases: EventBusOrchestrator
This is the primary API facade to use the Employment Component's write API.
You can obtain an object of this type by using the employment_session utility.
Note that most of the functions available here are idempotent.
Implementation note¶
- This is an abstract base class that implements the bridge between the employment_session and the "raw" API.
- Implementations only need to implement the plumbing functions (such as EventBus management).
applied_events
abstractmethod
property
¶
You can use this property in order to retrieve all of the events that were applied (via the EventBus mechanism) as part of the Employment Session.
This value is only set AFTER the employment_session block is exited, and will error out if you try to call it beforehand.
cancel ¶
Cancels the provided employment.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
defer_on_commit ¶
Defers a function call until after the transaction handled by this employment session is committed.
Note that the function is NOT executed if the employment session is in dry-run mode.
This is syntactic sugar over the EventBus mechanism.
Source code in components/employment/public/api_v2.py
derive_for_legacy_backfill
abstractmethod
¶
Copy this EmploymentApiSession, but set up the source_type for legacy backfill.
The main change is that, when using the returned EmploymentApiSession, (most) local consumers will not be notified when a change happens.
For example, if your EmploymentApiSession has the source_type fr_admin_dashboard, this would result in a
GlobalSourceType.legacy_backfill.with_country(CountryCode.fr) source_type being used.
Source code in components/employment/public/api_v2.py
find_similar_source_data_and_mark_as_duplicate ¶
find_similar_source_data_and_mark_as_duplicate(
user_identifier_field_name,
company_identifier_field_name,
start_date_field_name,
field_names_to_compare,
)
Entrypoint for the Source Deduplication feature (docs ⧉) Looks at the latest EmploymentSourceData with the same source type and identifier fields.
If the values are the same as raw_data_to_compare for all fields in field_names_to_compare,
it updates the source's metadata with last_received_at=utcnow() and returns its ID.
Otherwise, it returns None.
Note: if an identifier field is not present in raw_data_to_compare,
it will only consider EmploymentSourceData that do not have this field in their raw_data.
param field_names_to_compare: list of fields from raw_data that will need to be equal to be considered as similar
param raw_data_to_compare: new raw data dict for which we want to look for existing similar data
Source code in components/employment/public/api_v2.py
ingest_or_block ¶
Ingest the provided data using the provided upstream. Errors will result in a blocked movement that will appear in local tooling.
See the documentation for Upstream for more details.
This function returns an UpstreamResult which you can use to inspect what happened - see the docstrings
for the various "URXxx" classes for details.
You can use the source_rules_override parameter to override the SourceRules that would apply to the provided
source_type.
Source code in components/employment/public/api_v2.py
ingest_or_raise ¶
Ingest the provided EmploymentDeclaration, without any blocked movements management. Errors will be raised as regular Python exceptions.
Notes: - this function is fairly advanced - in most cases, you should primarily use the Upstream API and resort to this function if required. - the source_type of the EmploymentDeclaration must match that of this EmploymentApiSession.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
employment_declaration
|
EmploymentDeclaration[_TValues]
|
the EmploymentDeclaration to ingest. |
required |
source_rules_override
|
SourceRules | None
|
you can use this parameter to override the |
None
|
Source code in components/employment/public/api_v2.py
resume ¶
Resume (aka "unterminate") the provided employment.
You may optionally provided extended information that will be set on the employment.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
set_extended_employment_values ¶
Set extended values on the provided employment.
To learn more about extended values, see here ⧉
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
set_external_employee_id ¶
Set the external employee ID (aka matricule, payroll ID, etc.) of the provided employment to the value.
External employee IDs must be unique within a company for the provided user (employments for different users with the same external employee ID may not overlap).
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
terminate_or_update_termination ¶
Terminate or update the end date of the provided employment.
- If no end_date is currently set, terminates the employment
- If an end_date is already set, update the end date
If you want to remove the existing end_date, you'll need to use the 'resume' function instead.
You can optionally provide extended values that will be stored in the employment.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
transfer ¶
Transfer an existing employment to a new company.
- The existing employment will be terminated (or its end date will be updated) if the new start date is after the existing employment's start date.
- The existing employment will be cancelled if the new start date is on or before the existing employment's start date.
The new employment will be initialized using the data from the new_employment_declaration. The source_type of the new_employment_declaration must match that of this EmploymentApiSession.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
uncancel ¶
"Uncancel" the provided employment.
Use with care: "uncancellation" can be generally quite error-prone in local code, e.g. if multiple cancelled
policies match a cancelled employment, you may not know which one to uncancel. You can use the metadata field
of the employment_session to provide data that can be used to resolve situations like this.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
update_start_date ¶
Update the start date of the provided employment.
Note that the employment_id must be that of the Employment Component's model, NOT the local model (Employment, EsEmployment, etc.)
Source code in components/employment/public/api_v2.py
with_nested_orchestrator
abstractmethod
¶
Copy this EmploymentApiSession, in such a way that events published within this block are:
- Kept if the block completes successfully
- "Rolled back" if the block fails with an exception
For example:
with employment_session(...) as employment_api:
a = employment_api.wrap(BasicEventBus())
a.publish(lambda commit: print("A")
with employment_api.with_nested_orchestrator() as employment_api_nested:
b = employment_api_nested.wrap(BasicEventBus())
b.publish(lambda commit: print("B"))
# Block completes normally, events are pushed to the employment_api normally
try:
with employment_api.with_nested_orchestrator() as employment_api_nested:
c = employment_api_nested.wrap(BasicEventBus())
c.publish(lambda commit: print("C"))
raise ValueError("Oops")
# Block completes with an exception, events are NOT pushed to employment_api
except:
pass
# Printed => A B, but not C
Source code in components/employment/public/api_v2.py
SessionDerivedCallArgs ¶
employment_session ¶
This is the primary entrypoint to the Employment Component's Write API.
- This context manager represents an "Employment Component transaction", and will handle things like commit, event buses, etc.
- It yields an EmploymentApiSession object you can use to:
- Use the write API
- Add additional side-effects to be run after the commit (via the
EventBusAPI)
Here's an example:
with employment_session(
source_type=SourceType.fr_admin_dashboard,
dry_run=False,
raw_data=params, # or whichever "raw data" arguments are being used
metadata={"endpoint": "admin_dashboard.cancel_employment"}
) as employment_api:
employment_api.cancel(employment_id=...)
Transaction management and side-effects¶
employment_session takes care of the entire SQLAlchemy transaction and side-effects (i.e. "post-commit-stuff")
execution.
- If dry_run is set to
False, the transaction is committed at the end of thewith employment_sessionblock and side-effects are executed - If dry_run is set to
True, the transaction will be flushed and side-effects will be run in dry run mode (= most of them will do nothing)- Note that you are expected to rollback the transaction at some point - DO NOT commit it, since side-effects, such as emails and triggering RQ jobs, will not be executed.
Side-effects can be registered by leveraging the EventBus API, since the employment_api object implements EventBusOrchestrator. For example, to trigger a callable:
with employment_session(dry_run=False, ...) as employment_api:
employment_api.defer_on_commit(lambda: current_logger.info("I'm a side effect!"))
current_logger.info("Foobar")
# => Foobar
# (db commit is issued since dry_run=False)
# => I'm a side effect
Note that side-effects deferred in this manner will only be executed if dry_run=False. For more advanced use cases, and if you need to support dry-runnable side-effects, you can instead use the EventBusOrchestrator API:
with employment_session(dry_run=True, ...) as employment_api:
simple_event_bus = employment_api.wrap(BasicEventBus())
simple_event_bus.publish(lambda commit: current_logger.info("It's for real" if commit else "It's dry-run"))
current_logger.info("Foobar")
# => Foobar
# (db transaction is flushed but not committed since dry_run=True)
# => It's dry-run
The EventBus API also has a compatibility layer for shared.side_effects side-effects, see SharedSideEffectsEventBus for details.
Caveats¶
- ⚠️ You can only either fully dry run the process OR fully commit and execute side effects. It is not possible to use this in a "do not commit yet, I'll commit later" scenario.
- "raw_data"/"metadata"/"source_type" are persisted into an EmploymentSourceData row if and only if you call an Employment Component API.
Arguments¶
- source_type: the source from which your action comes from.
- dry_run: if False, transaction will be committed and side-effects will be executed at the end of the block. if True, the block is run in "dry run" mode (you MUST NOT commit what was created as part of the dry run mode). In dry-run mode:
- Model changes are not persisted to the DB (but are still kept in the transaction if you need to inspect them).
- raw_data: this is the "raw data" behind your action. This can correspond to an Excel row, a
paramsdictionary - it depends on what your use case is. This data will end up logged in anEmploymentSourceDatarow for later use. This value is immutable once persisted. - metadata: this is the metadata attached to your action. This will generally contain identifiers,
actor_ids, etc. This value is optional and will result in an empty dictionary if not specified. For example, if processing a file that is recorded in the DB, you would:- Use one
employment_sessionper row - Write the file's database ID in
metadata - And the row's content in
raw_data
- Use one
Source code in components/employment/public/api_v2.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
components.employment.public.boomerang_companies ¶
Boomerang company logic - framing: https://github.com/alan-eu/Topics/discussions/32109?sort=old#discussioncomment-15825299 ⧉.
A "boomerang company" is one that churned (ended all contracts) and later re-signed after a gap: - step A: when contracts are terminated -> do nothing - step B: when a new contract is signed -> terminate "stale" employments - step C: when a boomerang companies re-invite employments for which we terminated employments -> we resume them, and consumers must properly implement this type of resumption
get_boomerang_end_date_for_company ¶
Return the end date of the most recent contract if the company is a boomerang, else None.
A company is a boomerang if it has no active contracts (other than the new one) but has previous ended contracts. Returns None if the company has active contracts (no gap), has never had a contract, or the new contract is not yet visible in the subscriptions API.
Occupational Health (OH) subscriptions must be checked separately from health
insurance subscriptions. OH contracts are fully unknown from that part of contracting
so they are invisible to get_subscriptions_for_companies_by_app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
ID of the company to check. |
required |
app_name
|
AppName
|
country app (FR, BE, ES, CA). |
required |
new_contract_id
|
str
|
ID of the newly signed contract; used to find its start date (the reference date) and exclude it from the boomerang check. |
required |
Source code in components/employment/public/boomerang_companies.py
terminate_all_open_employments_for_company ¶
terminate_all_open_employments_for_company(
company_id,
end_date,
country_code,
dry_run,
context=None,
)
Terminate all employments active on end_date for a company.
Handles two cases:
- open employments (end_date IS NULL): terminated at end_date
- employments terminated too late (end_date > end_date): updated to end_date
The boomerang_company source type allows Step C to later identify these
terminations and bypass the usual resume=ignore rules on re-invitation.
Source code in components/employment/public/boomerang_companies.py
components.employment.public.business_logic ¶
actions ¶
ai_analysis ¶
launch_blocked_movement_ai_agent ¶
launch_blocked_movement_ai_agent(
blocked_movement_id,
agent_branch=None,
wait_for_completion=False,
tool_mocks=None,
bypass_tool_review=False,
)
Launch AI agent conversation for a blocked movement.
Source code in components/employment/public/business_logic/actions/ai_analysis.py
blocked_movement ¶
amend_core_blocked_movement_note ¶
Appends the provided note to the end of the core blocked movement's current note.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
amend_upstream_blocked_movement_note ¶
Appends the provided note to the end of the upstream blocked movement's current note.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
automatically_cancel_pending_core_blocked_movement ¶
Automatically cancel a core blocked movement.
This should only be used to automatically cancel a blocked movement immediately after its creation.
Otherwise: - If the cancellation is a result of a manual, human action, use manually_cancel_core_blocked_movement instead. - If the cancellation is part of an async/automated process, use mark_...as_self_healing as soon as possible, then cancel_self_healing..._blocked_movement
Source code in components/employment/public/business_logic/actions/blocked_movement.py
automatically_cancel_pending_upstream_blocked_movement ¶
Automatically cancel an upstream blocked movement.
This should only be used to automatically cancel a blocked movement immediately after its creation.
Otherwise: - If the cancellation is a result of a manual, human action, use manually_cancel_upstream_blocked_movement instead. - If the cancellation is part of an async/automated process, use mark_...as_self_healing as soon as possible, then cancel_self_healing..._blocked_movement
Source code in components/employment/public/business_logic/actions/blocked_movement.py
cancel_pending_blocked_movement ¶
Cancels a pending blocked movement by its ID. It'll manage indifferently if it's an upstream or core blocked movement.
If blocked movement is not found or is not in the 'pending' status, an error will be raised
Source code in components/employment/public/business_logic/actions/blocked_movement.py
cancel_self_healing_core_blocked_movement ¶
Cancels a core blocked movement which you previously took ownership of via
mark_pending_core_blocked_movement_as_self_healing.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
cancel_self_healing_upstream_blocked_movement ¶
Cancels an upstream blocked movement which you previously took ownership of via
mark_pending_upstream_blocked_movement_as_self_healing.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
manually_cancel_core_blocked_movement ¶
Cancel a core blocked movement as a result of a manual, human action. The blocked movement may be pending, awaiting_user_response, or awaiting_user_response_self_healing.
To automatically cancel a blocked movement as a result of automated action, mark the blocked movement as self
healing as early as possible (via mark_pending_core_blocked_movement_as_self_healing), then cancel it
using cancel_self_healing_core_blocked_movement when needed.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
manually_cancel_upstream_blocked_movement ¶
Cancel an upstream blocked movement as a result of a manual, human action. The blocked movement may be pending, awaiting_user_response, or awaiting_user_response_self_healing.
To automatically cancel a blocked movement as a result of automated action, mark the blocked movement as self
healing as early as possible (via mark_pending_upstream_blocked_movement_as_self_healing), then cancel it
using cancel_self_healing_upstream_blocked_movement when needed.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
mark_pending_core_blocked_movement_as_self_healing ¶
Mark a core blocked movement as self healing. By using this, you signal to the Employment Component that a custom local process will take care of the blocked movement from now on.
To "end" a blocked movement you take ownership of via this function, use:
cancel_self_healing_core_blocked_movementto cancel it (automatically_cancelled)resolve_self_healing_core_blocked_movementto resolve it (automatically_resolved)
Source code in components/employment/public/business_logic/actions/blocked_movement.py
mark_pending_or_awaiting_user_response_core_blocked_movement_as_self_healing ¶
mark_pending_or_awaiting_user_response_core_blocked_movement_as_self_healing(
core_blocked_movement_id, commit
)
Mark a core blocked movement as self healing. This is a variant of mark_pending_core_blocked_movement_as_self_healing you can use in case the blocked movement may have been marked as "awaiting_user_response" by Ops. Typical use cases include:
- Automatically moving the to a self-healing resolution process
- An Ops taking a blocked movement (marking it as awaiting_user_response) then manually putting it back to a self-healing resolution process.
By using this, you signal to the Employment Component that a custom local process will take care of the blocked movement from now on.
To "end" a blocked movement you take ownership of via this function, use:
cancel_self_healing_core_blocked_movementto cancel it (automatically_cancelled)resolve_self_healing_core_blocked_movementto resolve it (automatically_resolved)
Source code in components/employment/public/business_logic/actions/blocked_movement.py
mark_pending_or_awaiting_user_response_upstream_blocked_movement_as_self_healing ¶
mark_pending_or_awaiting_user_response_upstream_blocked_movement_as_self_healing(
upstream_blocked_movement_id, commit
)
Mark a upstream blocked movement as self healing. This is a variant of mark_pending_upstream_blocked_movement_as_self_healing you can use in case the blocked movement may have been marked as "awaiting_user_response" by Ops. Typical use cases include:
- Automatically moving the to a self-healing resolution process
- An Ops contacting admin or member about a blocked movement (marking it as awaiting_user_response) then manually putting it back to a self-healing resolution process.
By using this, you signal to the Employment Component that a custom local process will take care of the blocked movement from now on.
To "end" a blocked movement you take ownership of via this function, use:
cancel_self_healing_upstream_blocked_movementto cancel it (automatically_cancelled)resolve_self_healing_upstream_blocked_movementto resolve it (automatically_resolved)
Source code in components/employment/public/business_logic/actions/blocked_movement.py
mark_pending_self_healing_core_blocked_movement_as_awaiting_user_response_self_healing ¶
mark_pending_self_healing_core_blocked_movement_as_awaiting_user_response_self_healing(
core_blocked_movement_id, commit
)
Mark a core blocked movement you previously took ownership of via
mark_pending_core_blocked_movement_as_self_healing as awaiting user response,
but still under the "self healing" process.
This is useful if you need to contact the user/admin about the blocked movement, but you still want to keep ownership of the blocked movement.
awaiting_user_response_self_healing is considered as a terminal status in SLA computation.
By using this, you signal to the Employment Component that a custom local process still takes care of the blocked movement. Typically, this blocked movement will not be automatically retried, and won't be displayed by default in the Ops tool.
To "end" a blocked movement you took ownership of, use:
- cancel_self_healing_core_blocked_movement to cancel it (automatically_cancelled)
- resolve_self_healing_core_blocked_movement to resolve it (automatically_resolved)
Source code in components/employment/public/business_logic/actions/blocked_movement.py
mark_pending_upstream_blocked_movement_as_self_healing ¶
Mark an upstream blocked movement as self healing. By using this, you signal to the Employment Component that a custom local process will take care of the blocked movement from now on.
To "end" a blocked movement you take ownership of via this function, use:
cancel_self_healing_upstream_blocked_movementto cancel it (automatically_cancelled)resolve_self_healing_upstream_blocked_movementto resolve it (automatically_resolved)
Source code in components/employment/public/business_logic/actions/blocked_movement.py
resolve_self_healing_core_blocked_movement ¶
Resolves a core blocked movement which you previously took ownership of via
mark_pending_core_blocked_movement_as_self_healing.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
resolve_self_healing_upstream_blocked_movement ¶
Resolves an upstream blocked movement which you previously took ownership of via
mark_pending_upstream_blocked_movement_as_self_healing.
Source code in components/employment/public/business_logic/actions/blocked_movement.py
retry_core_blocked_movement ¶
retry_core_blocked_movement(
blocked_movement_id,
*,
employment_declaration_override=None,
source_rules_override=None,
commit=False,
event_bus_orchestrator=None
)
Retry a core blocked movement
Retries a core blocked movement, using the provided overrides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement_id
|
UUID
|
The ID of the core blocked movement to retry |
required |
employment_declaration_override
|
EmploymentDeclaration | None
|
If set, will be used instead of the blocked movement's declaration. Defaults to None. |
None
|
source_rules_override
|
SourceRules | None
|
If set, will be used instead of the blocked movement's original source type's source rules and persisted for future retries. Defaults to None. |
None
|
commit
|
bool
|
True will commit (incl. executing side effects), False will dry-run. Defaults to False. |
False
|
event_bus_orchestrator
|
EventBusOrchestrator
|
If set, events will be published to this orchestrator, |
None
|
Source code in components/employment/public/business_logic/actions/blocked_movement.py
retry_upstream_blocked_movement ¶
retry_upstream_blocked_movement(
upstream_blocked_movement_id,
*,
upstream_data_override=None,
source_rules_override=None,
commit=False
)
Retries an upstream blocked movement
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upstream_blocked_movement_id
|
UUID
|
The ID of the blocked movement to retry |
required |
upstream_data_override
|
Optional[dict[str, Any]]
|
If set, values present in this dict will override those present in the upstream blocked movement's |
None
|
source_rules_override
|
SourceRules | None
|
If set, will be used instead of the blocked movement's original source type's source rules and persisted for future retries. Defaults to None. |
None
|
commit
|
bool
|
True will commit (incl. executing side effects), False will dry-run. Defaults to False. |
False
|
Source code in components/employment/public/business_logic/actions/blocked_movement.py
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 | |
delete_user ¶
delete_user_in_employment_component ¶
Support function for the "delete user" procedure. Delete all employments (core and extended) that target user and related data (blocked movements and source data).
Note that this will NOT be notified to consumers.
Source code in components/employment/public/business_logic/actions/delete_user.py
employment_source_data ¶
find_similar_source_data_and_mark_as_duplicate ¶
find_similar_source_data_and_mark_as_duplicate(
source_type,
user_identifier_field_name,
company_identifier_field_name,
start_date_field_name,
field_names_to_compare,
raw_data_to_compare,
commit,
)
Looks at the latest EmploymentSourceData with the same source type and identifier fields.
If the values are the same as raw_data_to_compare for all fields in field_names_to_compare,
it updates the source's metadata with last_received_at=utcnow() and returns its ID.
Otherwise, it returns None.
Note: if an identifier field is not present in raw_data_to_compare,
it will only consider EmploymentSourceData that do not have this field in their raw_data.
param field_names_to_compare: list of fields from raw_data that will need to be equal to be considered as similar
param raw_data_to_compare: new raw data dict for which we want to look for existing similar data
Source code in components/employment/public/business_logic/actions/employment_source_data.py
run_rollout_initialisation ¶
For some sources (most likely automated one), when activating it for a company, we don't want to process ALL past data (because of the high risk of data inconsistencies that can be too costly to fix).
Instead, we want to start processing data from the moment the source is activated.
To do so, we need to create a new EmploymentSourceDataModel with the source_information provided and rely on the "source deduplication" mechanism (see find_similar_source_data_and_mark_as_duplicate above and doc: https://www.notion.so/alaninsurance/Source-Deduplication-37060a0d1c884c1b970aa6b2a4600ea4?pvs=26&qid=1%3Ac0d72661-8c33-41aa-9b99-25d9fd8203ee%3A5 ⧉)
We use the rollout_initialisation metadata key to mark the source as "initialised" (to help excluded those when needed)
These sources are excluded from SLA computations.
Source code in components/employment/public/business_logic/actions/employment_source_data.py
update_employment_source_data_metadata_with ¶
Update the metadata stored in the DB using the provided update_dict. Each value in the update_dict will update the existing value for the given key. Keys not present in update_dict will not be changed.
Source code in components/employment/public/business_logic/actions/employment_source_data.py
merge_users ¶
merge_users_in_employment_component ¶
Support function for the "merge user" procedure: change all employments and blocked movements that target user
merge_from so that they target user merge_into instead.
This will change ALL employments for the user, including cancelled ones. This will change ALL blocked movements for the user (active and terminal).
Note that this will NOT be notified to consumers.
If logs is not None, strings describing the actions will be appended to it.
Source code in components/employment/public/business_logic/actions/merge_users.py
source_information ¶
update_source_information_metadata_with ¶
Update the provided source_information's metadata in place using the provided update_dict.
Never commits, even if the underlying source_information is a persisted DB model.
Source code in components/employment/public/business_logic/actions/source_information.py
upstream_blocked_movement_creator ¶
UpstreamBlockedMovementCreator ¶
UpstreamBlockedMovementCreator(
commit,
source,
upstream_data,
source_information,
upstream_retried_blocked_movement=None,
source_rules_override=None,
)
Context manager that catch exceptions and create an upstream blocked movement if needed
upstream_data: data used to decide if we should create a new blocked movement or not Basically, if a new movement is failing with the same error and the same upstream_data, we won't create a new blocked movement
Source code in components/employment/public/business_logic/actions/upstream_blocked_movement_creator.py
__enter__ ¶
Source code in components/employment/public/business_logic/actions/upstream_blocked_movement_creator.py
__exit__ ¶
Source code in components/employment/public/business_logic/actions/upstream_blocked_movement_creator.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
employment_source_data
instance-attribute
¶
If created_blocked_movement is True (i.e. an upstream blocked movement was created because the block inside this creator was unsuccessful), employment_source_data will be set to the corresponding EmploymentSourceData.
If created_blocked_movement is False (i.e. the block inside this creator executed without raising), employment_source_data will be None.
retried_blocked_movement
instance-attribute
¶
admin_resolvers ¶
Admin error resolvers for blocked movements.
This package provides the infrastructure for admin-resolvable blocked movements.
admin_error_resolver ¶
AdminErrorResolver ¶
Bases: ABC
Base class for admin error resolvers.
Each resolver handles a specific error code and provides
- Ability to check if a blocked movement can be resolved
- Data needed for the admin to make a resolution decision
- Validation of resolution parameters
- Application of the resolution chosen by the admin
Global resolver classes are defined in GLOBAL_RESOLVER_CLASSES dict. Country-specific resolver classes are provided via CountryGateway.get_admin_error_resolver_classes().
apply_resolution
abstractmethod
¶
Apply the admin's resolution choice to the blocked movement. Implementations should call validate_params() before processing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement
|
CoreBlockedMovement | UpstreamBlockedMovement
|
The blocked movement to resolve |
required |
action
|
AdminResolutionAction
|
The action chosen by the admin (cancel or apply) |
required |
params
|
dict[str, Any] | None
|
Optional parameters for the action. For 'apply' action: - if provided, used as override data for retry. - if None, retries as-is without modifications. |
None
|
Raises:
| Type | Description |
|---|---|
ResolutionActionError
|
If the action is invalid or the resolution cannot be applied |
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
can_resolve ¶
Check if this resolver can handle the given blocked movement.
Default implementation checks: - The error_code matches - The status is awaiting_user_response_self_healing
Override this method if you need additional validation.
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
filter_resolvable_movements
classmethod
¶
Pre-filter movements before building resolution contexts.
Override to apply per-resolver deduplication or filtering logic (e.g., keeping only one BM per employee). Default returns all movements unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movements
|
list[CoreBlockedMovement | UpstreamBlockedMovement]
|
Candidate blocked movements for this resolver. |
required |
Returns:
| Type | Description |
|---|---|
list[CoreBlockedMovement | UpstreamBlockedMovement]
|
Filtered list of blocked movements. |
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
get_resolution_context
abstractmethod
¶
Fetch and return the data needed for the admin to resolve this blocked movement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement
|
CoreBlockedMovement | UpstreamBlockedMovement
|
The blocked movement to resolve |
required |
Returns:
| Type | Description |
|---|---|
AdminResolutionContext
|
AdminResolutionContext containing resolution options and context. |
AdminResolutionContext
|
The context dict MUST include all fields from RequiredContextFields: - employee_full_name: str - employee_email: str (can be empty) - company_display_name: str - created_at: str (ISO timestamp) |
Raises:
| Type | Description |
|---|---|
ResolutionActionError
|
If the blocked movement is invalid or missing required data |
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
validate_params ¶
Validate the resolution parameters before applying.
Override this method to add resolver-specific validation. Default implementation does nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action
|
The action chosen by the admin |
required | |
params
|
Optional parameters for the action |
required |
Raises:
| Type | Description |
|---|---|
ResolutionActionError
|
If params are invalid |
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
RequiredContextFields ¶
Bases: TypedDict
Required context fields for admin resolution dashboard.
These fields are required by the frontend to display blocked movements in the admin dashboard. Resolvers MUST include these fields in their context.
employee_email
instance-attribute
¶
Email of the affected employee (can be empty string).
create_admin_resolution_context ¶
create_admin_resolution_context(
*,
blocked_movement_id,
error_code,
source_type,
employee_full_name,
employee_email,
company_display_name,
created_at,
**extra_context
)
Build an AdminResolutionContext with required fields enforced at call site.
This helper ensures all required fields are provided by making them explicit keyword arguments. Additional error-specific context can be passed as extra context
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement_id
|
UUID
|
ID of the blocked movement |
required |
error_code
|
str
|
Error code for routing to resolver |
required |
source_type
|
BaseSourceType
|
Source type of the blocked movement (e.g. "fr_dsn", "fr_admin_dashboard") |
required |
employee_full_name
|
str
|
Display name of affected employee |
required |
employee_email
|
str
|
Email of affected employee (can be empty) |
required |
company_display_name
|
str
|
Display name of company |
required |
created_at
|
str
|
ISO timestamp string |
required |
**extra_context
|
Any
|
Additional error-specific context fields |
{}
|
Returns:
| Type | Description |
|---|---|
AdminResolutionContext
|
AdminResolutionContext with all required fields |
Source code in components/employment/public/business_logic/admin_resolvers/admin_error_resolver.py
registry ¶
Registry for admin error resolvers.
Global resolvers are defined in the GLOBAL_RESOLVER_CLASSES dict. Country-specific resolvers are provided via CountryGateway.get_admin_error_resolver_classes().
GLOBAL_RESOLVER_CLASSES
module-attribute
¶
GLOBAL_RESOLVER_CLASSES = {
error_code: ExternalEmployeeIdConflictResolver,
value: MissingRequiredExternalEmployeeIdResolver,
error_code: StaleInvitationResolver,
}
get_admin_error_resolver_classes ¶
Get all admin error resolver classes for a given country.
Combines global resolvers with country-specific resolvers. Country-specific resolvers take precedence if there's a conflict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
country_code
|
CountryCode
|
The country code to get resolvers for |
required |
Returns:
| Type | Description |
|---|---|
dict[str, type[AdminErrorResolver]]
|
Dictionary mapping error codes to resolver classes |
Source code in components/employment/public/business_logic/admin_resolvers/registry.py
get_all_resolvable_error_codes ¶
Get all error codes that have registered resolvers for a given country.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
country_code
|
CountryCode
|
The country code to get resolvers for |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of error codes that can be resolved by admins |
Source code in components/employment/public/business_logic/admin_resolvers/registry.py
get_resolver ¶
Get a resolver instance for a given error code in a specific country.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error_code
|
str
|
The error code to get a resolver for |
required |
country_code
|
CountryCode
|
The country code to get resolvers for |
required |
Returns:
| Type | Description |
|---|---|
AdminErrorResolver | None
|
A new resolver instance, or None if no resolver is registered for this error code |
Source code in components/employment/public/business_logic/admin_resolvers/registry.py
resolvers ¶
external_employee_id_conflict_resolver ¶
ExternalEmployeeIdConflictResolver ¶
Bases: AdminErrorResolver
Resolver for external_employee_id_conflict blocked movements.
This error occurs when an employment declaration has an external employee ID that already belongs to another active employment in the same company.
Apply the resolution action to the blocked movement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement
|
CoreBlockedMovement | UpstreamBlockedMovement
|
The blocked movement to resolve |
required |
action
|
AdminResolutionAction
|
The action chosen by the admin (cancel or apply) |
required |
params
|
dict[str, Any] | None
|
Parameters for the action (required for apply): - new_external_employee_id (str): The new external employee ID |
None
|
Raises:
| Type | Description |
|---|---|
ResolutionAttemptError
|
If the action is invalid or the resolution cannot be applied |
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/external_employee_id_conflict_resolver.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
Check if this resolver can handle the given blocked movement. Only core blocked movements can be resolved.
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/external_employee_id_conflict_resolver.py
Get resolution data for the blocked movement.
Retrieves the blocked movement and its conflicting employment information to provide context for resolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement
|
CoreBlockedMovement | UpstreamBlockedMovement
|
The blocked movement to build context for |
required |
Returns:
| Type | Description |
|---|---|
AdminResolutionContext
|
AdminResolutionContext with inbound + conflicting employee info |
Raises:
| Type | Description |
|---|---|
ResolutionAttemptError
|
If the blocked movement is missing required data |
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/external_employee_id_conflict_resolver.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
missing_required_external_employee_id_resolver ¶
MissingRequiredExternalEmployeeIdResolver ¶
Bases: AdminErrorResolver
Resolver for missing_required_external_employee_id blocked movements.
Apply the resolution action to the blocked movement.
Supports 'cancel' and 'apply' actions. For 'apply', requires 'new_external_employee_id' in the params.
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/missing_required_external_employee_id_resolver.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
Check if this resolver can handle the given blocked movement. In this case, we only handle upstream instances.
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/missing_required_external_employee_id_resolver.py
Get resolution data for the blocked movement.
Retrieves the blocked movement information to provide context for resolution.
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/missing_required_external_employee_id_resolver.py
stale_invitation_resolver ¶
StaleInvitationResolutionParams ¶
Bases: TypedDict
Parameters for resolving a stale invitation blocked movement.
All fields are optional. When params is None, the resolution will retry without any overrides.
Expected keys
- start_date_override: str (ISO date string for new start date) or None
StaleInvitationResolver ¶
Bases: AdminErrorResolver
Resolver for stale invitation blocked movements.
Stale invitations occur when an employee invitation has an effective start date that is more than 100 days in the past. Admins can resolve these by: - Canceling the invitation - Retrying with a corrected start date - Retrying with permission to skip the stale check
Apply the admin's resolution choice to the stale invitation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocked_movement
|
CoreBlockedMovement | UpstreamBlockedMovement
|
The blocked movement to resolve |
required |
action
|
AdminResolutionAction
|
The action chosen by the admin (cancel or apply) |
required |
params
|
dict[str, Any] | None
|
Optional parameters for the action: - start_date_override (str, optional): ISO date string for new start date |
None
|
Raises:
| Type | Description |
|---|---|
ResolutionActionError
|
If the action is invalid or the resolution cannot be applied |
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/stale_invitation_resolver.py
Check if this resolver can handle the given blocked movement.
Only core blocked movements can be resolved (stale invitations are always core).
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/stale_invitation_resolver.py
Return resolution data for a stale invitation blocked movement.
Extracts employee information and defines display fields for the admin.
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/stale_invitation_resolver.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
Validate resolution parameters for stale invitation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action
|
AdminResolutionAction
|
The action chosen by the admin |
required |
params
|
dict[str, Any] | None
|
Optional parameters for the action |
required |
Raises:
| Type | Description |
|---|---|
ResolutionActionError
|
If params contain invalid keys or values |
Source code in components/employment/public/business_logic/admin_resolvers/resolvers/stale_invitation_resolver.py
queries ¶
admin_blocked_movements ¶
get_admin_resolvable_blocked_movements ¶
Get blocked movements grouped by action type for admin resolution dashboard.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
list[str]
|
Company IDs to filter blocked movements for. |
required |
country_code
|
CountryCode
|
The country from which the resolvable BM list is requested. |
required |
Returns:
| Type | Description |
|---|---|
AdminResolutionsResponse
|
AdminResolutionsResponse with action_required (awaiting_user_response_self_healing) |
AdminResolutionsResponse
|
and under_investigation (pending) lists. |
Source code in components/employment/public/business_logic/queries/admin_blocked_movements.py
get_admin_resolvable_blocked_movements_stats ¶
Get statistics about pending admin resolutions grouped by category.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
list[str]
|
Company IDs to filter blocked movements for |
required |
country_code
|
CountryCode
|
The country from which the resolvable BM list is requested |
required |
error_codes
|
list[str] | None
|
Optional list of error codes to filter by. If None, uses all resolvable error codes. |
None
|
Returns:
| Type | Description |
|---|---|
AdminResolutionsStats
|
AdminResolutionsStats, containing statistics grouped by category. |
Source code in components/employment/public/business_logic/queries/admin_blocked_movements.py
get_company_ids_with_resolvable_errors ¶
Return company_ids that have at least one admin-resolvable blocked movement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
country_code
|
CountryCode
|
Country to get resolvable error codes for. |
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
Set of company_id strings with resolvable errors. |
Source code in components/employment/public/business_logic/queries/admin_blocked_movements.py
get_resolvable_errors_count_by_category ¶
Return count of resolvable blocked movements grouped by reporting category.
Lightweight SQL COUNT alternative to get_admin_resolvable_blocked_movements_stats for cases where only category totals are needed (e.g., digest emails).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
list[str]
|
Company IDs to scope the query to. |
required |
country_code
|
CountryCode
|
Country to get resolvable error codes for. |
required |
Returns:
| Type | Description |
|---|---|
dict[AdminReportingCategory, int]
|
Mapping of reporting category to blocked movement count. |
dict[AdminReportingCategory, int]
|
Categories with zero matches are omitted. |
Source code in components/employment/public/business_logic/queries/admin_blocked_movements.py
blocked_movement ¶
get_blocked_movement_ids_by_upstream_data_path_value ¶
Get the upstream blocked movement ids for which upstream_data matches a given value at the specified path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Dot-separated path to the nested key (e.g., "invite_email" or "record.email") |
required |
value
|
str
|
The value to match |
required |
Source code in components/employment/public/business_logic/queries/blocked_movement.py
get_core_blocked_movement_ids_by_context_path_value ¶
Get the core blocked movement ids for which context matches a given value at the specified path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Dot-separated path to the nested key (e.g., "employment_declaration.extended_informations.0.values.invite_email") |
required |
value
|
str
|
The value to match |
required |
Source code in components/employment/public/business_logic/queries/blocked_movement.py
get_core_blocked_movement_or_none ¶
Get a core blocked movement by its ID.
Source code in components/employment/public/business_logic/queries/blocked_movement.py
get_pending_blocked_movements_for_company_id ¶
Gets all pending blocked movements for the provided company ID. The intended usage for this function is to retrieve blocked movements for company admins (both displaying them and acting upon them).
This function specifically returns only 'pending' blocked movements (i.e. with the 'pending' status), but not all active blocked movements. This is intentional: all other statuses indicate that the blocked movement is currently being handled, either manually (e.g. 'awaiting_user_response' status) or by some automated process (e.g. 'pending_self_healing'), as it is assumed that we do not want to provide actionability to admins on these blocked movements.
Note: upstream blocked movements must have a proper company_id value set in order to be returned by this function.
The blocked movements are returned in an arbitrary order.
Source code in components/employment/public/business_logic/queries/blocked_movement.py
get_upstream_blocked_movement_or_none ¶
Get an upstream blocked movement by its ID.
Source code in components/employment/public/business_logic/queries/blocked_movement.py
get_upstream_or_core_blocked_movement_or_none ¶
Get an upstream or core blocked movement by its ID.
Source code in components/employment/public/business_logic/queries/blocked_movement.py
core_employment_version ¶
get_all_latest_core_employment_versions_on ¶
Like get_latest_core_employment_version_on, but supports multiple users.
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_all_latest_core_employment_versions_overlapping ¶
Like get_latest_core_employment_versions_overlapping, but for multiple users.
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_core_employments_for_company ¶
Return the core employments for all employees of a company, using their latest version.
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_core_employments_for_external_employee_id_in_company_overlapping ¶
get_core_employments_for_external_employee_id_in_company_overlapping(
external_employee_id, company_id, start_date, end_date
)
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_core_employments_for_user ¶
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_core_employments_for_user_company ¶
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_latest_cancelled_core_employment_versions_overlapping ¶
get_latest_cancelled_core_employment_versions_overlapping(
user_id, company_id, start_date, end_date
)
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_latest_core_employment_version_on ¶
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_latest_core_employment_versions_overlapping ¶
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_latest_version_for_employment_or_raise ¶
Source code in components/employment/public/business_logic/queries/core_employment_version.py
get_or_raise_latest_core_employment_version_on ¶
Get the latest core employment version for a user and company on a given date.
Raises BaseErrorCode.missing_resource if no employment is found.
Source code in components/employment/public/business_logic/queries/core_employment_version.py
country_helpers ¶
fr ¶
get_all_employment_source_data_for_bulk_action_report ¶
FR-specific DB helper: retrieves all Employment Source Data objects that we need to send bulk action reports.
Source code in components/employment/public/business_logic/queries/country_helpers/fr.py
get_blocked_movements_for_dsn_removal_suggestions ¶
get_blocked_movements_for_dsn_removal_suggestions(
company_ids=None,
status=BlockedMovementStatus.pending_self_healing,
created_before=None,
keep_duplicates_for_same_company_user=False,
)
Returns blocked movements for DSN removal suggestions.
By default, returns one movement per (user_id, company_id) pair, the one with the
latest end date. Set keep_duplicates_for_same_company_user=True to return all
matching movements, which is useful for bulk cancellation where every stale movement
must be processed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
Optional[set[str]]
|
Filter by company IDs. If None, returns movements for all companies. |
None
|
status
|
BlockedMovementStatus
|
The status to filter by. Use |
pending_self_healing
|
created_before
|
datetime | None
|
If set, return only movements created before this datetime. |
None
|
keep_duplicates_for_same_company_user
|
bool
|
If False (default), return one movement per (user_id, company_id) pair, ordered by end date descending. If True, return all matching movements. |
False
|
Source code in components/employment/public/business_logic/queries/country_helpers/fr.py
get_dsn_removal_suggestion_blocked_movements_to_cancel ¶
When getting the blocked movements for DSN removal suggestions, we only want to keep the latest one. (This is done in the get_blocked_movements_for_dsn_removal_suggestions function above.) However, we need to cancel ALL the suggestion blocked movements, because we don't want to leave them in a pending_self_healing state.
Source code in components/employment/public/business_logic/queries/country_helpers/fr.py
get_employment_source_data_extra_fields_for_company ¶
get_employment_source_data_extra_fields_for_company(
company_id,
keys,
on_date,
raw_data_additional_filters=None,
)
Get values for the specified keys from EmploymentSourceData.raw_data["extra"] for all active employees in company.
This has been built for Finance integration "Flux Retour"
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
Company ID to filter by |
required |
keys
|
list[str]
|
List of keys to extract from raw_data["extra"] |
required |
on_date
|
date
|
Date to check for active employments |
required |
raw_data_additional_filters
|
dict[str, Any] | None
|
Additional filters to apply on raw_data |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, dict[str, Any]]
|
Dictionary mapping user_id to a dictionary of key-value pairs from raw_data["extra"]. |
dict[str, dict[str, Any]]
|
Only keys that exist in the data are included. |
dict[str, dict[str, Any]]
|
If the keys are present in several EmploymentSourceData entries for the same user, |
dict[str, dict[str, Any]]
|
the returned values are the initial ones (first received). |
Source code in components/employment/public/business_logic/queries/country_helpers/fr.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
employment_source_data ¶
get_employment_metadata_value ¶
Get the value of the provided metadata key for the employment source data with the provided ID.
Source code in components/employment/public/business_logic/queries/employment_source_data.py
get_employment_source_data_from_id ¶
Get an EmploymentSourceData object from its ID.
Source code in components/employment/public/business_logic/queries/employment_source_data.py
get_employment_source_data_from_workflow_id ¶
Get all EmploymentSourceData objects from a workflow.
Source code in components/employment/public/business_logic/queries/employment_source_data.py
extended_employment_update ¶
batch_get_employment_value_timeline ¶
Return the timeline of values for a given key, for multiple employments in a single query.
Source code in components/employment/public/business_logic/queries/extended_employment_update.py
get_employment_value_timeline ¶
Return the full timeline of values for a given key on an employment.
Source code in components/employment/public/business_logic/queries/extended_employment_update.py
get_employment_values_batch_on ¶
Return the employment values for a batch of employment IDs, on a specific date per employment ID.
Return a dictionary mapping employment IDs to their corresponding extended values dictionary.
Source code in components/employment/public/business_logic/queries/extended_employment_update.py
get_employment_values_on ¶
Source code in components/employment/public/business_logic/queries/extended_employment_update.py
source_information ¶
get_metadata_from_source_information_like ¶
Retrieve the metadata dictionary from a SourceInformationLike object.
Must not be used for updating the metadata, should only be used for reading the metadata! If you want to update
the metadata, use update_employment_source_data_metadata_with or update_source_information_metadata_with
instead.
Source code in components/employment/public/business_logic/queries/source_information.py
get_raw_data_from_source_information_like ¶
Retrieve the raw_data dictionary from a SourceInformationLike object.
Can only be used for reading the raw_data - this value is immutable by design.
Source code in components/employment/public/business_logic/queries/source_information.py
stale_invitations ¶
get_stale_invitations ¶
Retrieves stale invitations for the given company IDs, optionally filtering by creation date.
Source code in components/employment/public/business_logic/queries/stale_invitations.py
weekly_employment_stats ¶
Query functions for weekly employment statistics.
WeeklyEmploymentStats
dataclass
¶
WeeklyEmploymentStats(
new_employees_added,
employees_transferred,
employees_removed,
source_types=frozenset(),
)
Weekly employment change counts per change type.
get_company_ids_with_weekly_changes ¶
Get company IDs that have at least one employment change in the period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_datetime
|
datetime
|
Start of reporting period (inclusive). |
required |
end_datetime
|
datetime
|
End of reporting period (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
Set of company_id strings with changes. |
Source code in components/employment/public/business_logic/queries/weekly_employment_stats.py
get_weekly_employment_stats_for_companies ¶
Get employment statistics for companies within a date range.
Queries CoreEmploymentVersionModel for changes created within the period, grouped by EmploymentChangeType.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
list[str]
|
Company IDs to filter by. |
required |
start_datetime
|
datetime
|
Start of reporting period (inclusive). |
required |
end_datetime
|
datetime
|
End of reporting period (exclusive). |
required |
Returns:
| Type | Description |
|---|---|
WeeklyEmploymentStats
|
WeeklyEmploymentStats with counts per change type and distinct source types. |
Source code in components/employment/public/business_logic/queries/weekly_employment_stats.py
rules ¶
blocked_movements ¶
make_error_message_from_integrity_error ¶
Format an integrity error in a meaningful message to be used in blocked movements.
It doesn't contain the IDs of objects because the error message is used for deduplication of blocked movements.
Source code in components/employment/public/business_logic/rules/blocked_movements.py
employment_input_error_info ¶
EmploymentInputErrorCode ¶
Bases: AlanBaseEnum
Generic and global error codes for Employment Component upstreams, especially for extractors
company_is_not_authorized
class-attribute
instance-attribute
¶
dsn_contract_without_company
class-attribute
instance-attribute
¶
dsn_invitation_without_email
class-attribute
instance-attribute
¶
has_reimbursed_care_acts
class-attribute
instance-attribute
¶
invalid_external_employee_id
class-attribute
instance-attribute
¶
invalid_hiring_date
class-attribute
instance-attribute
¶
invalid_identity_document_type
class-attribute
instance-attribute
¶
invalid_is_alsace_moselle
class-attribute
instance-attribute
¶
invalid_is_unpaid_leave
class-attribute
instance-attribute
¶
invalid_passport_number
class-attribute
instance-attribute
¶
invalid_professional_category
class-attribute
instance-attribute
¶
invalid_termination_reason
class-attribute
instance-attribute
¶
invalid_unpaid_leave_end_date
class-attribute
instance-attribute
¶
mandatory_termination_type
class-attribute
instance-attribute
¶
missing_hiring_date
class-attribute
instance-attribute
¶
missing_required_birth_date
class-attribute
instance-attribute
¶
missing_required_email
class-attribute
instance-attribute
¶
missing_required_external_employee_id
class-attribute
instance-attribute
¶
missing_required_gender
class-attribute
instance-attribute
¶
missing_required_identity_document_type
class-attribute
instance-attribute
¶
missing_required_nif
class-attribute
instance-attribute
¶
missing_required_ssn_ntt
class-attribute
instance-attribute
¶
multiple_employments_found
class-attribute
instance-attribute
¶
no_active_employment_found
class-attribute
instance-attribute
¶
not_matching_external_employee_id
class-attribute
instance-attribute
¶
retry_failed_given_admin_overrides
class-attribute
instance-attribute
¶
start_date_too_far_back
class-attribute
instance-attribute
¶
EmploymentInputErrorInfo
dataclass
¶
extractors ¶
This file contains "extractors", which are utility functions that aim to parse and validate items from a dict.
This file only contains global extractors for use in any country - more specialized extractors (e.g. French SSN extractors) should be put in an appropriate spot in each country's component.
Extractors ensure that we perform validation consistently across a range of input methods.
Typical usage of extractors will follow this pattern:
- Provide a function that raises an exception for a specific error
- Use
partialonextract_or_raiseto get your extraction function - Use the extraction function with the extractors you need
Here's a working example:
def raiser(error: EmploymentInputErrorInfo) -> NoReturn:
raise ExtractorInputException(
company_id=..., # or None if you don't have it yet
user_id=..., # or None if you don't have it yet
error=error
)
extract = partial(extract_or_raise, data=YOUR_DICT, raiser=raiser)
first_name = extract(extractor=extract_mandatory_first_name, field="first_name")
ExtractorInputException ¶
Bases: UpstreamError
A simple exception when you don't need to use a specific exception with except_or_raise.
Usage:
def raiser(error: EmploymentInputErrorInfo) -> NoReturn:
raise ExtractorInputException(
company_id=..., # or None if you don't have it yet
user_id=..., # or None if you don't have it yet
error=error
)
Optionally, you can also pass a context value, which will end up in the UpstreamBlockedMovement's context field.
Source code in components/employment/public/business_logic/rules/extractors.py
to_blocked_movement_info ¶
Source code in components/employment/public/business_logic/rules/extractors.py
ExtractorResult
module-attribute
¶
Result of an extractor function. Always a tuple:
- If a success, the first value is the parsed value (may be
Nonedepending on the extractor), the second value is None - If a failure, the first value is None, the second value is an
EmploymentInputErrorInfowith details on the error.
cast_to_date_with_validation ¶
Cast the provided value to a datetime object.
By default, strings are parsed using the regular ISO format (YYYY-MM-DD).
Returns a tuple with both the parsed value (or none if the input value was falsy) and a boolean indicating whether the parsing was successful or not.
Note: this in itself is not an extractor, but can be used to build extractors with more useful error diagnostics.
Source code in components/employment/public/business_logic/rules/extractors.py
extract_birth_date_from_data ¶
Birth date extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_boolean_from_data ¶
Utility for parsing booleans written in plain language.
Note: this is not a real extract (as it doesn't return a proper ExtractorResult), this is intended to be used
as a utility to build extractors.
Source code in components/employment/public/business_logic/rules/extractors.py
extract_email_from_data ¶
Email extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_end_date_from_data ¶
End date extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_external_employee_id_from_data_without_validation ¶
External employee ID (matricule, payroll ID, etc.) extractor.
Note: this extractor does NOT perform validation. For France, use the "extract_external_employee_id" function, which does.
Source code in components/employment/public/business_logic/rules/extractors.py
extract_gender_from_data ¶
Gender extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_mandatory_birth_date_from_data
module-attribute
¶
extract_mandatory_birth_date_from_data = mandatory_extractor(
extractor=extract_birth_date_from_data,
missing_value_error_code=missing_required_birth_date,
value_name="birth date",
)
extract_mandatory_email_from_data
module-attribute
¶
extract_mandatory_email_from_data = mandatory_extractor(
extractor=extract_email_from_data,
missing_value_error_code=missing_required_email,
value_name="e-mail",
)
extract_mandatory_first_name_from_data ¶
First name extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_mandatory_gender_from_data
module-attribute
¶
extract_mandatory_gender_from_data = mandatory_extractor(
extractor=extract_gender_from_data,
missing_value_error_code=missing_required_gender,
value_name="gender",
)
extract_mandatory_last_name_from_data ¶
Last name extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_mandatory_start_date_from_data ¶
Start date extractor
Source code in components/employment/public/business_logic/rules/extractors.py
extract_or_raise ¶
Used to wrap an extractor to make its use easier in upstream scenarios.
Typical usage:
def raiser(error: EmploymentInputErrorInfo) -> NoReturn:
raise SomeException(...)
extract = partial(extract_or_raise, data=..., raiser=raiser)
first_name = extract(extractor=extract_mandatory_first_name_from_data, field_name="first_name")
# ...
Note: if you do not have or need a custom exception for your use case, you can use ExtractorInputException, like
so:
def raiser(error: EmploymentInputErrorInfo) -> NoReturn:
raise ExtractorInputException(
company_id=..., # or None if you don't have it yet
user_id=..., # or None if you don't have it yet
error=error
)
Source code in components/employment/public/business_logic/rules/extractors.py
mandatory_extractor ¶
Utility function to turn an extractor that returns a value or None if absent into a "mandatory" extractor.
Usage:
extract_mandatory_email_from_data = mandatory_extractor(
extract_email_from_data,
EmploymentInputErrorCode.missing_invite_email,
"Invitation Email"
)
This function is typed properly: the signature of the initial extractor is retained (and you can safely use it with 'partial'), and the resulting function is guaranteed to return a non-None value.
Source code in components/employment/public/business_logic/rules/extractors.py
parse_integer_string ¶
Used for SSNs, NTTs, SIRENs and external employee IDs ("matricules"). Sometimes these integer strings are floats in the excel spreadsheet, so it adds a decimal part when we stringify them (str(float(1)) -> '1.0')
Source code in components/employment/public/business_logic/rules/extractors.py
stale_invitations ¶
SKIP_STALE_INVITATION_CHECK_METADATA_KEY
module-attribute
¶
check_invitation_is_not_stale ¶
Verifies that an invitation with the provided date does not fall under a "stale invitation" scenario.
Stale invitations are a special flow used to handle invitations that would incur more than 100 days of coverage upon invitation. These can be admin mistakes and might need to be checked - otherwise, admins would incur a lot of regularization.
Note that 'start_date' is not the start date of the employee in the company, but the effective start date of their coverage. For example, employees added with a start date in the 2000s but whose company just signed up for Alan do not count as "stale" invitations.
Source code in components/employment/public/business_logic/rules/stale_invitations.py
components.employment.public.constants ¶
components.employment.public.country_gateway ¶
AffiliationItem
dataclass
¶
AffiliationItem(
title,
id,
is_cancelled,
start_date=optional_isodate_field(),
end_date=optional_isodate_field(),
)
Bases: DataClassJsonMixin
An affiliation item related to an employment (see CountryGateway.get_affiliation_items_related_to_employment for details).
This dataclass is only used as a DTO sent to the front-end for display purposes. All values within it should not be used in business logic.
CompanyInformation
dataclass
¶
CountryGateway ¶
Bases: ABC, Generic[_TValues]
Class used by the Employment Component to retrieve local-specific data.
!!! ALL imports to local components MUST be done as local imports. !!!
There should be one implementation of CountryGateway per country that uses the Employment Component. You need to add your country's implementation in components.employment.external.country_gateways
app_name
abstractmethod
property
¶
App name identifying the country this gateway operates for.
are_companies_in_same_account
abstractmethod
¶
Returns True if the two companies are in the same account - false otherwise.
This is used to determine whether a transfer is necessary or not - see here ⧉.
Source code in components/employment/public/country_gateway.py
extract_external_employee_id_from_data ¶
Extracts and validates the external employee ID from the provided data.
Default implementation uses extract_external_employee_id_from_data_without_validation. Override this method if you need country-specific validation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, Any]
|
dict containing the external employee ID |
required |
field_name
|
str
|
key to read from the data dict |
required |
company_id
|
int
|
used by some country implementations for validation |
required |
Source code in components/employment/public/country_gateway.py
get_account_name
abstractmethod
¶
get_admin_error_resolver_classes ¶
Gets all admin error resolver classes contributed by this country.
Returns:
| Type | Description |
|---|---|
dict[str, type[Any]]
|
Dictionary mapping error codes to AdminErrorResolver classes. |
dict[str, type[Any]]
|
All imports to local components MUST be done as local imports (e.g, loading non-CA models in Canada is forbidden). |
Note
If your country does not have any country-specific resolvers, you can leave this unimplemented. (it will return an empty dict by default).
Source code in components/employment/public/country_gateway.py
get_affiliation_items_related_to_employment ¶
Returns a list of affiliation items "related to" the provided employment, or None if this feature is not supported by this CountryGateway. The employment_id is the Employment Component ID, NOT the local ID.
There is no specific definition of what "related to" means, although this should roughly corresponds to affiliations that would be modified, impacted, clamped or otherwise checked in case of modifications for this employment. The exact semantics are up to the local logic.
Note: this function is used for display purposes only in the Scrappy Employment Component Tool.
Source code in components/employment/public/country_gateway.py
get_all_under_investigation_error_codes ¶
All error codes for the under_investigation tab (global + country-specific).
Source code in components/employment/public/country_gateway.py
get_blocked_invitations_validation_broadcast_id ¶
(Advanced) Get the ID of the daily broadcast to send to admins with stale invitations that need to be validated.
This is used by the send_stale_invitations_validation_broadcasts command
to send a daily broadcast to all admins with stale invitations that need to be validated.
If your country does not require Stale Invitations support, you can leave this unimplemented.
Returns:
| Type | Description |
|---|---|
str | None
|
str | None: The ID of the daily broadcast, or None if no broadcast is required. |
Source code in components/employment/public/country_gateway.py
get_company_information
abstractmethod
¶
Retrieves comprehensive company information including display name, account ID, and settings. Can return None if the company does not exist.
Source code in components/employment/public/country_gateway.py
get_consumers_to_notify_for_legacy_backfill ¶
(Advanced) Gets all employment consumers to notify for legacy backfill.
Typically, consumers are not notified for legacy backfill, as it's data ingested from the country legacy tables so countries already have the data. Some consumers may not look at the "legacy tables" and hence need to be notified for legacy backfill though (e.g. Occupational Health in FR).
If you want the consumer to also be notified for other source types, you should add it to the get_employment_consumers method.
Source code in components/employment/public/country_gateway.py
get_employee_email_from_extended_values
abstractmethod
¶
Retrieves the employee email from the provided extended values.
Note: implementation is only required if your country requires Stale Invitations support.
Source code in components/employment/public/country_gateway.py
get_employee_identifier_for_country
abstractmethod
¶
Retrieves the Employee Identifier from the provided extended values.
Note: implementation is only required if your country requires Stale Invitations support.
Source code in components/employment/public/country_gateway.py
get_employment_consumers
abstractmethod
¶
Gets all employment consumers contributed by this country.
Notes: 1. ALL Employment Consumers will be called regardless of the country of origin. 2. The function that will be called must have all local code as LOCAL (in-function) imports - otherwise, this breaks Canada (where loading non-CA models is forbidden)
Source code in components/employment/public/country_gateway.py
get_local_under_investigation_error_codes ¶
Country-specific error codes to display in the admin under_investigation tab.
Returns:
| Type | Description |
|---|---|
frozenset[str]
|
Error codes for which pending blocked movements should be shown to admins. |
frozenset[str]
|
All imports to local components must be done as local imports. |
Source code in components/employment/public/country_gateway.py
get_retry_function ¶
(Advanced) Get the function used for retrying Core Blocked Movements.
You should generally not need to implement this.
Source code in components/employment/public/country_gateway.py
get_source_detail_for_blocked_movement ¶
(Advanced) Get detailed information on the blocked movement source.
For example, in France, this is used to extract the source's external provider when the source type is an external API. If not null, it will then be displayed alongside the source type on the blocked movements ops tool.
Source code in components/employment/public/country_gateway.py
get_subscriptions_for_company ¶
Get all contract subscriptions for a company. It is used to compute whether a new contract is a boomerang or not. If get_subscriptions_for_companies_by_app doesn't return ALL contract types, it can be override to include them (e.g. Occupation Health in France that uses a different stack)
Source code in components/employment/public/country_gateway.py
get_upstream_retry_handler
abstractmethod
¶
Retrieves the upstream blocked movement retry function that corresponds to the given source_type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_type
|
SourceType
|
The source type for which to retrieve the retry function - guaranteed to be of the CountryCode that corresponds to this CountryGateway. |
required |
Returns:
| Type | Description |
|---|---|
UpstreamBlockedMovementRetryFunction[_TValues] | None
|
UpstreamBlockedMovementRetryFunction[_TValues] | None: The retry function, or None if no retry function is available for the given source type. |
Source code in components/employment/public/country_gateway.py
get_user_admined_company_ids
abstractmethod
¶
Retrieves the list of company IDs admined by this user.
Note: implementation is only required if your country requires Stale Invitations support.
Source code in components/employment/public/country_gateway.py
get_user_email ¶
Retrieves a user's email address, or None if the user does not exist.
Override in country gateways that need email display for under_investigation BMs.
Source code in components/employment/public/country_gateway.py
get_user_full_name
abstractmethod
¶
Retrieves a user's full name (BaseUser.full_name), or None if the user does not exist
last_stale_invite_notification_email_sent_to_admin_on ¶
(Advanced) Get the timestamp of the last email sent to an admin of the given company regarding stale invitations.
This is used to determine whether an admin has already been notified of the stale invitations
If they have, we will set the stale invitation's status to awaiting_user_response_pending_self_healing
which will
- count as a terminal status for the purpose of our affiliation speed computation
- not notify admins again for this stale invitation.
If your country does not require Stale Invitations support, you can leave this unimplemented.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
The company ID for which to retrieve the last notification timestamp. |
required |
Returns: datetime | None: The timestamp of the last email sent, or None if no email has been sent.
Source code in components/employment/public/country_gateway.py
UpstreamBlockedMovementRetryFunction ¶
Bases: Protocol, Generic[_TValues]
A function that retries an Upstream Blocked Movement:
__call__ ¶
Retry an Upstream Blocked Movement from the provided upstream_data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upstream_data
|
dict
|
The |
required |
root_employment_source_data_id
|
UUID
|
The Employment Source Data ID that corresponds to this upstream blocked movement - in order to correctly correlate the upstream blocked movement with the retry's results, you must set the EmploymentDeclaration's source_information field to this UUID. |
required |
event_bus_orchestrator
|
EventBusOrchestrator
|
Should you need side-effects to be run, you can use this EventBusOrchestrator to do so. |
required |
Returns:
| Type | Description |
|---|---|
EmploymentDeclaration[_TValues] | None
|
EmploymentDeclaration[_TValues] - if an EmploymentDeclaration needs to be processed as part of the retry |
EmploymentDeclaration[_TValues] | None
|
None - if no EmploymentDeclaration needs to be processed as part of the retry. |
Source code in components/employment/public/country_gateway.py
components.employment.public.entities ¶
ExtendedEmploymentValue
dataclass
¶
Bases: WithValidityPeriod
WARNING: In this class, infinitely valid values are represented as validity_period = ValidityPeriod.infinity instead of None.
__repr__ ¶
do_overlap ¶
Source code in components/employment/internal/entities/extended_employment_value.py
validity_period
instance-attribute
¶
WARNING: Infinitely valid values are represented as validity_period = ValidityPeriod.infinity instead of None.
ValueTimeline
dataclass
¶
Bases: Timeline[ExtendedEmploymentValue]
components.employment.public.enums ¶
ACTIVE_BLOCKED_MOVEMENT_STATUSES
module-attribute
¶
ACTIVE_BLOCKED_MOVEMENT_STATUSES = frozenset(
{
pending,
awaiting_user_response,
pending_self_healing,
awaiting_user_response_self_healing,
}
)
An immutable set containing all of the BlockedMovementStatuses considered "active", i.e. actions (of whichever kind) are required or are being done.
AdminReportingCategory ¶
Bases: AlanBaseEnum
Categories for grouping blocked movements in admin reporting alerts.
creation
class-attribute
instance-attribute
¶
Enrollment/creation failures requiring admin action.
non_actionable
class-attribute
instance-attribute
¶
Issues under investigation by Alan (informational only).
termination
class-attribute
instance-attribute
¶
Termination failures requiring admin action.
AdminResolutionAction ¶
Bases: AlanBaseEnum
Possible resolution actions admins can take when resolving blocked movements.
These are basic actions similar to what Ops can do in the internal tool: - cancel: Cancel the blocked movement - apply: Retry the blocked movement. If params are provided, they are used as overrides. If no params are provided, retries as-is without modifications.
apply
class-attribute
instance-attribute
¶
Retry the blocked movement.
Behavior depends on whether params are provided: - If params are provided: retries with override data (params are used as overrides) - If params are None/not provided: retries as-is without modifications
The params dict structure depends on the specific blocked movement type and resolver.
cancel
class-attribute
instance-attribute
¶
Cancel the blocked movement without retrying.
AnyRuleCaseAction
module-attribute
¶
BLOCKED_MOVEMENT_SLA_TERMINAL_STATUSES
module-attribute
¶
BLOCKED_MOVEMENT_SLA_TERMINAL_STATUSES = frozenset(
{
cancelled,
resolved,
awaiting_user_response,
awaiting_user_response_self_healing,
automatically_resolved,
automatically_ignored,
automatically_cancelled,
}
)
BlockedMovementStatus ¶
Bases: AlanBaseEnum
The status of a blocked movement.
For more information, see https://www.notion.so/alaninsurance/Blocked-Movements-246a87a20f7a4cd388aee8a6327c4148?pvs=4 ⧉
automatically_cancelled
class-attribute
instance-attribute
¶
A self-healing blocked movement (whose status was pending_self_healing) that was cancelled as part of its automatic
self-healing process.
This is equivalent to cancelled for regular pending blocked movements.
automatically_ignored
class-attribute
instance-attribute
¶
A blocked movement that was created, but immediately ignored.
When a source rule dictates that a movement shall be ignored, instead of silently ignoring, a blocked movement with this status is created instead.
Automatically ignored blocked movements are automatically retried daily for technical reasons (context ⧉).
automatically_resolved
class-attribute
instance-attribute
¶
A self-healing blocked movement (whose status was pending_self_healing) that was resolved as part of its automatic
self-healing process.
This is equivalent to resolved for regular pending blocked movements.
awaiting_user_response
class-attribute
instance-attribute
¶
Ops has contacted the admin or member about this Blocked Movement. It is considered a terminal status in terms of SLA computation (our Ops' job is done), but is not the final status for a blocked movement (see https://www.notion.so/alaninsurance/Blocked-Movements-246a87a20f7a4cd388aee8a6327c4148?pvs=4#11c0a501e8b24cbb9603fa7dd273c321 ⧉).
awaiting_user_response blocked movements are automatically retried daily.
awaiting_user_response_self_healing
class-attribute
instance-attribute
¶
A blocked movement that is 'awaiting_user_response', but whose resolution is expected to happen automatically - this is a "self-healing" blocked movement.
Typical use cases include blocked movements that are created and automatically sent to the admin for action, and if the admin doesn't act after a certain time, the blocked movement is automatically resolved or cancelled by a daily command.
They are NOT automatically retried. This gives full control over their lifecycle to local code.
cancelled
class-attribute
instance-attribute
¶
A blocked movement that was cancelled - it was not resolved, usually because no action was required.
pending
class-attribute
instance-attribute
¶
A blocked movement that requires Ops attention. This is the default for newly created blocked movements.
Pending blocked movements are automatically retried daily.
pending_self_healing
class-attribute
instance-attribute
¶
A blocked movement that is 'pending', but whose resolution is expected to happen automatically - this is a "self-healing" blocked movement.
Typical use cases include blocked movements that are created, but then resolved or cancelled by a daily command. By
setting a blocked movement to have pending_self_healing status, it will not appear in the Blocked Movements tool
by default - meaning it will not create noise for Ops.
Note that blocked movements are never created with this state directly - you must manually mark pending blocked
movements as self-healing via the mark_pending_(core|upstream)_blocked_movement_as_self_healing function.
Self-healing blocked movements, i.e. those with status pending_self_healing, are NOT automatically retried. This
gives full control over their lifecycle to local code.
resolved
class-attribute
instance-attribute
¶
A blocked movement that was resolved, either automatically (by a daily retry, or a retry triggered by an Ops) or manually (status manually set to 'resolved').
Note that just because a blocked movement is resolved doesn't mean that the situation at hand is solved. A blocked movement may be resolved because another error happened - so this blocked movement is 'resolved' in the sense that we no longer get this specific error.
CANCELLED_BLOCKED_MOVEMENT_STATUSES
module-attribute
¶
An immutable set containing all of the BlockedMovementStatuses considered "cancelled", i.e. the blocked movement is not valid and/or there's nothing to process.
CountryCode ¶
Bases: AlanBaseEnum
The country associated with something in the Employment Component.
Usually deduced from the source_type of what you're looking at.
from_app_name
staticmethod
¶
Turns an AppName into a CountryCode - not compatible with all AppNames.
Source code in components/employment/public/enums.py
EmploymentChangeType ¶
Bases: AlanBaseEnum
The type of change that occurred.
cancellation
class-attribute
instance-attribute
¶
An employment is cancelled, and code should act as if this employment had never existed.
end_date_change
class-attribute
instance-attribute
¶
A change in the end date of an employment.
external_employee_id_change
class-attribute
instance-attribute
¶
The external employee ID of an employee is changed.
invitation
class-attribute
instance-attribute
¶
An invitation: an employee joins a company.
Mutually exclusive with all other change types.
NB: An invitation can also have an end_date in case of short-term work contracts. ⚠️ In this case, you will not receive a termination change type
resumption
class-attribute
instance-attribute
¶
A termination is cancelled, i.e. the end_date of an employment is cleared.
start_date_change
class-attribute
instance-attribute
¶
A change in the start date of an employment.
termination
class-attribute
instance-attribute
¶
A termination: an employee is terminated (an end_date is set).
NB: In case an employee is invited or transferred with an end_date, only invitation/transfer is sent.
transfer
class-attribute
instance-attribute
¶
An employee is transferred from one company to another. This is both a termination (in the previous company) and an
invitation (in the new company).
This movement is computed when we receive a new employment for an employee that already has an employment in another
company of the same account.
Notes:
- An employee moving from one company to another company not part of the same account is not considered as a transfer,
an invitation will be computed in the new company and a termination in the old company when we receive the information.
This can potentially happen with a delay between the two actions
- It is still possible to force a transfer between two companies that are not part of the same account
by using the imperative function transfer
Mutually exclusive with all other change types.
NB: A transfer can also have an end_date in the new company in case it's already known. ⚠️ In this case, you will not receive a termination change type
uncancellation
class-attribute
instance-attribute
¶
An employment was cancelled by mistake and we want to undo the cancellation.
RuleCase ¶
Bases: AlanBaseEnum
The different cases that can Source Rules rule over.
change_end_date_more_than_100_days_in_the_past
class-attribute
instance-attribute
¶
change_end_date_more_than_100_days_in_the_past = (
"change_end_date_more_than_100_days_in_the_past"
)
change_external_employee_id
class-attribute
instance-attribute
¶
change_external_employee_id_if_none
class-attribute
instance-attribute
¶
terminate_with_end_date_more_than_100_days_in_the_past
class-attribute
instance-attribute
¶
terminate_with_end_date_more_than_100_days_in_the_past = (
"terminate_with_end_date_more_than_100_days_in_the_past"
)
transfer_more_than_100_days_in_the_past
class-attribute
instance-attribute
¶
RuleCaseAction ¶
Bases: AlanBaseEnum
An action to take when a rule is met.
block
class-attribute
instance-attribute
¶
Block: the whole declaration is blocked (a core blocked movement is created).
ignore
class-attribute
instance-attribute
¶
Ignore: skip this rule.
- If all movements are ignored, creates a core blocked movement with status
automatically_ignored - If some movements are ignored, the ignored one are not processed while the other are processed as usual
TransferRuleCaseAction ¶
Bases: UseAlternativeRuleCaseAction, AlanBaseEnum
Special RuleCaseAction that can only be used with transfers: instead of transferring, invite in the new company instead.
Note that, when a transfer is changed to an invitation like this, the RuleCase.invitation rule is NOT checked.
process_as_invitation
class-attribute
instance-attribute
¶
UseAlternativeRuleCaseAction ¶
Simple marker parent class, used to identify RuleCaseActions which should result in using an "alternative payload". The exact meaning depends on the rule case.
components.employment.public.exceptions ¶
ActionNotAllowed ¶
Bases: DeclarativeInputError
Raised when the source rules don't allow an action
Source code in components/employment/public/exceptions.py
create_blocked_movement_context ¶
Source code in components/employment/public/exceptions.py
AdminResolverInternalError ¶
Bases: Exception
Error raised when an admin resolver encounters an internal error indicating a bug or data inconsistency that should not happen under normal circumstances.
Use this for cases like
- Missing required data that should always be present
- Invalid state that indicates a programming error
- Data corruption or inconsistency
Do NOT use this for expected business errors that admin can fix (use ResolutionAttemptError instead).
Source code in components/employment/public/exceptions.py
AutomaticallyIgnoreConsumerError ¶
Bases: ConsumerError
A consumer error that is automatically ignored when created as a blocked movement.
Source code in components/employment/public/exceptions.py
to_blocked_movement_info ¶
Source code in components/employment/public/exceptions.py
AutomaticallyIgnoreUpstreamError ¶
Bases: UpstreamError
An upstream error that is automatically ignored when created as a blocked movement.
Source code in components/employment/public/exceptions.py
ConsumerError ¶
Bases: DeclarativeInputError
Root class for errors raised within employment consumers that should lead to core blocked movements.
Source code in components/employment/public/exceptions.py
create_blocked_movement_context ¶
Source code in components/employment/public/exceptions.py
ExternalEmployeeIdConflictError ¶
Bases: DeclarativeInputError
Error raised by external employee ID changes that would lead to a conflict with an existing employment. External employee IDs must be unique within a company for overlapping employments.
Source code in components/employment/public/exceptions.py
conflicting_employment_id
instance-attribute
¶
create_blocked_movement_context ¶
Source code in components/employment/public/exceptions.py
ResolutionActionError ¶
Bases: Exception
Error raised when an admin's resolution action is invalid or cannot be applied.
ResolutionAttemptError ¶
Bases: Exception
Error raised when
- preflight validation on admin resolution parameters fails
- an attempt to resolve a blocked movement fails following a retry with admin overrides
Source code in components/employment/public/exceptions.py
TooManyResolutionsError ¶
Bases: Exception
Error raised when there are too many pending resolutions for an admin to handle.
UnexpectedConsumerError ¶
Bases: ConsumerError
Error class used for unexpected errors raised from employment consumers.
Source code in components/employment/public/exceptions.py
create_blocked_movement_context ¶
Source code in components/employment/public/exceptions.py
UnexpectedLegacyBackfillError ¶
Bases: DeclarativeInputError
Error type for unexpected errors that occur during the legacy_backfill process.
Source code in components/employment/public/exceptions.py
create_blocked_movement_context ¶
Source code in components/employment/public/exceptions.py
to_blocked_movement_info ¶
Source code in components/employment/public/exceptions.py
UnexpectedUpstreamError ¶
Bases: UpstreamError
Error type used when an unexpected error occurs within an upstream (i.e. while preparing an Employment Declaration)
Source code in components/employment/public/exceptions.py
to_blocked_movement_info ¶
Source code in components/employment/public/exceptions.py
UnexpectedUpstreamErrorContext
dataclass
¶
Bases: UpstreamBlockedMovementContext
Context type for UnexpectedUpstreamError
UpstreamError ¶
Bases: ABC, Exception
An error that occurs within the upstream phase, i.e. when preparing an Employment Declaration.
Source code in components/employment/public/exceptions.py
to_blocked_movement_info
abstractmethod
¶
Generate an UpstreamBlockedMovementInfo object that will be used for creating the blocked movement.
UserNotFoundError ¶
Bases: ConsumerError
Error raised when a user ID cannot be found, typically after a user merge event when the employment change still refers to the old user ID.
Source code in components/employment/public/exceptions.py
components.employment.public.source_type ¶
components.employment.public.upstream_api ¶
URBlockedMovement
dataclass
¶
Bases: ABC
An UpstreamResult where a blocked movement happened.
Check the URCoreBlockedMovement and URUpstreamBlockedMovement classes for details.
blocked_movement
abstractmethod
property
¶
Returns the blocked movement represented by this UpstreamResult.
with_data ¶
Returns an UpstreamResultWithData, which includes both the data from this UpstreamResult along with the
corresponding EmploymentSourceData object.
Source code in components/employment/public/upstream_api.py
URCoreBlockedMovement
dataclass
¶
Bases: URBlockedMovement
The Upstream successfully built an EmploymentDeclaration, but the ingestion or a local consumer raised an exception, which created a blocked movement.
URSuccess
dataclass
¶
The Upstream successfully processed the input, resulting in changes in the Employment Component, which you can
inspect via the with_data function.
with_data ¶
Returns an UpstreamResultWithData, which includes both the data from this UpstreamResult along with the
corresponding EmploymentSourceData object.
Source code in components/employment/public/upstream_api.py
URSuccessNoOp
dataclass
¶
The Upstream successfully processed the input, but no declaration was ingested. This can happen for two reasons:
- A regular upstream returned
Nonein itsbuild_declarationfunction - An action upstream succeeded.
URUpstreamBlockedMovement
dataclass
¶
Bases: URBlockedMovement
The Upstream's build_declaration (or process_input_data for action upstreams) raised an exception, which
created a blocked movement.
Upstream ¶
Bases: ABC, Generic[_TInput, _TValues]
Upstreams are a mechanism which allow you to:
- Ingest data into the Employment Component.
- Perform actions within the Employment Component via its API.
- Affect other stacks.
- In a way that is retriable: failures lead to a "blocked movement", which can be retried and acted upon via tooling ⧉
The type parameters of this class are:
_TInput: The type of the object passed to this upstream as input_TValues: The type of local extended values (checkExtendedValuesDictfor details)
Kinds of upstreams¶
Broadly speaking, there are two kinds of upstreams:
Regular upstreams¶
Upstreams can declaratively send data to the Employment Component. This is similar to the ingest_or_raise
function, but with blocked movements support.
To implement this kind of upstream, subclass Upstream, DictUpstream or DataclassUpstream (available in upstream_utils).
For example:
class MySourceParams(TypedDict):
foo: str
bar: str
# ...
# Xx = the country code, e.g. Fr, Es, Ca...
class _XxMySourceUpstream(DictUpstream[MySourceParams, XxExtendedValues]):
source_type: ClassVar = SourceType.xx_my_source
@override
def build_declaration(self, input_data: BulkImportRowParams, source_information: SourceInformationLike) -> EmploymentDeclaration[XxExtendedValues]:
# ...
XxMySourceUpstream: Final = _XxMySourceUpstream()
# ...
with employment_session(...) as employment_api:
employment_api.ingest_or_block(MySourceUpstream, {"foo": ..., "bar": ...})
Action upstreams¶
Action upstreams do NOT send data to the Employment Component, and instead perform some other action, which may leverage the Employment API or not.
To implement this kind of upstream, subclass Upstream + ActionUpstreamMixin, or ActionDictUpstream, or
ActionDataclassUpstream (available in upstream_utils).
class MySourceParams(TypedDict):
foo: str
bar: str
# ...
# Xx = the country code, e.g. Fr, Es, Ca...
class _XxMySourceUpstream(DictActionUpstream[MySourceParams]):
source_type: ClassVar = SourceType.xx_my_source
@override
def process_input_data(self, input_data: MySourceParams, employment_api: EmploymentApiSession) -> None:
# ...
XxMySourceUpstream: Final = _XxMySourceUpstream()
# ...
with employment_session(...) as employment_api:
employment_api.ingest_or_block(MySourceUpstream, {"foo": ..., "bar": ...})
Registering upstreams¶
For the retry mechanism to work, upstreams must be registered in the local country's CountryGateway.get_retry_handlers
implementation like so:
@override
def get_upstream_retry_handler(
self, source_type: SourceType
) -> UpstreamBlockedMovementRetryFunction[XxExtendedValues] | None:
from components.xx...my_source import XxMySourceUpstream
return {
SourceType.xx_my_source: XxMySourceUpstream.retry_handler
}
build_declaration
abstractmethod
¶
This function builds an EmploymentDeclaration from the provided input_data. This EmploymentDecalration will
then be ingested in the Employment Component.
Note: this function must not commit nor persist anything definitively.
You can optionally return None if you do not want/need an EmploymentDeclaration to be ingested.
Source code in components/employment/public/upstream_api.py
ingest_data ¶
Main function used to run the Upstream on some data.
Note: you should use employment_api.ingest_or_block instead directly calling this function.
Source code in components/employment/public/upstream_api.py
retry_handler ¶
Function that you must register to your CountryGateway to allow blocked movements to be retried (see Upstream docstring for details).
Source code in components/employment/public/upstream_api.py
source_type
abstractmethod
property
¶
UpstreamResult
module-attribute
¶
UpstreamResultWithData
dataclass
¶
components.employment.public.upstream_utils ¶
Convenience utility classes built on top of the Upstream API
ActionUpstreamMixin ¶
Bases: Upstream[_TInput, Never]
A mixin that allows you to define action upstreams. Refer to the Upstream documentation for more information.
Instead of overriding build_declaration, override process_input_data.
build_declaration ¶
Source code in components/employment/public/upstream_utils.py
process_input_data
abstractmethod
¶
retry_handler ¶
Source code in components/employment/public/upstream_utils.py
DataclassActionUpstream ¶
Bases: DataclassUpstream[TInputDataclass, Never], ActionUpstreamMixin[TInputDict]
Convenience superclass that combines DataclassUpstream and ActionUpstreamMixin
Source code in components/employment/public/upstream_utils.py
DataclassUpstream ¶
Bases: Upstream[TInputDataclass, _TValues]
Upstream superclass that provides default serialization and deserialization functions for dataclasses which
implement DataClassJsonMixin
Source code in components/employment/public/upstream_utils.py
DictActionUpstream ¶
Bases: DictUpstream[TInputDict, Never], ActionUpstreamMixin[TInputDict]
Convenience superclass that combines DictUpstream and ActionUpstreamMixin
DictUpstream ¶
Bases: Upstream[TInputDict, _TValues]
Upstream superclass that provides default serialization and deserialization functions for dictionaries/TypedDicts.
TInputDataclass
module-attribute
¶
components.employment.public.utils ¶
dump_results ¶
ExtraDumpData
dataclass
¶
upload_ingestion_results_as_csv ¶
Utility function that uploads the ingestion provided ingestion results as CSVs to S3 and prints links to download the files in the logs.
The main use-case for this function is for outputting the results of dry-run processes.
Source code in components/employment/public/utils/dump_results.py
override_employment_declaration ¶
override_employment_declaration_start_date ¶
Updates both the start date of the employment declaration and the validity period of any extended information linked to it.
When the start date is overridden, any extended information that had a validity period starting on the original start date will have its validity period updated to start on the new start date as well.
Source code in components/employment/public/utils/override_employment_declaration.py
user_matching ¶
UserMatchingProcessorWithEmploymentException ¶
Bases: UserMatchingProcessor[TSearchParams, TUser], Generic[TSearchParams, TUser]
A UserMatchingProcessor that raises exceptions that are compatible with the Employment Component's exception typing.
By inheriting from this class instead of the regular UserMatchingProcessor, your errors will show up as clean blocked movement instead of 'unexpected_upstream_error' blocked movements