Api reference
components.payroll_tool.public.api ¶
Public API for payroll_tool component.
Re-exports types that other components (countries) need to implement their dependencies.
ActivityType ¶
Bases: AlanBaseEnum
Activity filter applied to the raw payroll changes (framing §5.3).
AdminCalendarRules
dataclass
¶
AdminCalendarRules(
max_period_weeks=7,
min_period_weeks=2,
max_abs_month_shift=2,
cutoff_freeze_lead_days=0,
label_change_permission=False,
)
Bases: DataClassJsonMixin
Validation rules applied when a company admin edits the calendar.
BaseCostBreakdown
dataclass
¶
BaseCostBreakdown(
employee_cost_total,
employee_cost_taxed,
employee_cost_untaxed,
company_cost_total,
company_cost_taxed,
company_cost_untaxed,
)
Bases: DataClassJsonMixin
Country-agnostic cost breakdown stored as JSONB on PayrollChange.
Per side (employee, company), *_taxed + *_untaxed = *_total.
All amounts are integers in cents. Negative amounts are valid — they occur on cancellations and regularizations.
Country subclasses extend this with country-specific decompositions (e.g. BE's channel axis: flexben / payslip on the employee side).
BaseExemptionType ¶
Bases: AlanBaseEnum
Base enum for exemption types.
Countries extend this with their specific exemption types. Example (FR): class FrExemptionType(BaseExemptionType): # Copy what can be found in components.fr.internal.models.enums.exemption_type.ExemptionType
BasePayrollChangeCategory ¶
Bases: AlanBaseEnum
Base enum for payroll change categories.
Countries extend this with their specific categories.
BaseProfessionalCategory ¶
Bases: AlanBaseEnum
Base enum for professional categories.
Countries extend this with their specific categories. Example (FR): class FrProfessionalCategory(BaseProfessionalCategory): CADRE = "CADRE" NON_CADRE = "NON_CADRE"
BaseRegimeType ¶
Bases: AlanBaseEnum
Base enum for regime types.
Countries extend this with their specific regime types. Example (FR): class FrRegimeType(BaseRegimeType): GENERAL = "GENERAL" ALSACE_MOSELLE = "ALSACE_MOSELLE"
EmploymentPayload
dataclass
¶
Bases: BasePayload
Base payload for employment entries (base employment relationship).
Countries extend this with their specific fields: - FR: population (FrProfessionalCategory - CADRE/NON_CADRE) - BE: No additional fields needed
EmptyPayload
dataclass
¶
Bases: BasePayload
Payload for empty entries (timeline fully cancelled).
This payload has no fields and represents an empty timeline state. No country-specific extension needed.
EnrollmentPayload
dataclass
¶
Bases: BasePayload
Base payload for enrollment entries (active relationship for a service).
Countries extend this with their specific fields: - FR: regime (FrRegimeType) - BE: coverage_module (str), is_adult_pricing (bool) - ES: TBD
EventType ¶
ExemptionPayload
dataclass
¶
Bases: BasePayload
Base payload for exemption entries (legal or contractual exemptions).
Base field
exemption_type: Type of exemption (countries provide their enum subclass)
Usage: - FR: Use existing ExemptionType enum - BE: Not used (Belgium has no exemptions)
MarmotCalendarRules
dataclass
¶
MarmotCalendarRules(
max_period_weeks=7,
min_period_weeks=2,
max_abs_month_shift=2,
cutoff_freeze_lead_days=0,
label_change_permission=True,
)
Bases: DataClassJsonMixin
Validation rules applied when a Marmot user edits the calendar.
OptionPayload
dataclass
¶
Bases: BasePayload
Base payload for option entries (top-ups on top of an enrollment).
Usage: - FR: Supplementary option references
PayrollCalendar ¶
Bases: BaseModel
Rolling payroll cutoff calendar — one row per company, JSONB array of periods.
__table_args__
class-attribute
instance-attribute
¶
admin_rules_config
class-attribute
instance-attribute
¶
admin_rules_config = mapped_column(
DataclassJSONB(AdminCalendarRules, none_as_null=True),
nullable=True,
)
company_id
class-attribute
instance-attribute
¶
current_start_date
class-attribute
instance-attribute
¶
entries
class-attribute
instance-attribute
¶
marmot_rules_config
class-attribute
instance-attribute
¶
marmot_rules_config = mapped_column(
DataclassJSONB(MarmotCalendarRules, none_as_null=True),
nullable=True,
)
recurring_rule
class-attribute
instance-attribute
¶
recurring_rule = mapped_column(
DataclassJSONB(RecurringRule),
nullable=False,
default=RecurringRule,
)
upcoming_cutoff_date
class-attribute
instance-attribute
¶
PayrollDataRequest
dataclass
¶
PayrollDataRequest(
company_ids,
period_label,
product_types=None,
categories=None,
excluded_categories=None,
excluded_categories_company_ids=None,
activity_type=ActivityType.all,
operational_scopes=None,
search=None,
sort_type=SortType.ascending,
cursor=None,
limit=20,
)
Runtime parameters for a Layer A query.
The template (passed alongside the request to the public entry point) contributes the what — columns, granularity, sort, filtering. The request contributes the where — which companies, which period, which scopes, runtime overrides.
The full PayrollTemplate shape is sent over the wire (rather than a
template id) so admins can preview unsaved column / sort / filter edits
in the UI without persisting them first. Resolved per framing discussion
https://github.com/alan-eu/Topics/discussions/32979#discussioncomment-16768041 ⧉.
Framing §5.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
set[str]
|
Companies to include. Single-country is enforced
structurally — one |
required |
period_label
|
date
|
The payroll period anchor. Matches rows where
|
required |
product_types
|
list[str] | None
|
Restrict to these product types. |
None
|
categories
|
list[str] | None
|
Restrict to these categories. |
None
|
excluded_categories
|
list[str] | None
|
Category blacklist. Applied after |
None
|
excluded_categories_company_ids
|
set[str] | None
|
When set, |
None
|
activity_type
|
ActivityType
|
|
all
|
operational_scopes
|
dict[str, dict[str, list[str]]] | None
|
Country-local scope spec. Passed straight to
|
None
|
search
|
str | None
|
Free-text search. Hits a hardcoded field allowlist (fullname, email, matricule — see PAY-1848). |
None
|
sort_type
|
SortType
|
Row sort direction. Applied to raw Layer A columns; the
what to sort on lives in the template ( |
ascending
|
cursor
|
str | None
|
Opaque keyset-pagination cursor. |
None
|
limit
|
int
|
Page size. Conservative default — admins can opt up via request override. |
20
|
excluded_categories_company_ids
class-attribute
instance-attribute
¶
PayrollDataResponse
dataclass
¶
Response shape returned by get_payroll_data (PAY-1853).
rows is Layer-B-rendered (each row is a dict of column-key → display
string). next_cursor is None when the last page has been reached.
total_rows is optional — Layer A may skip it when the count query is
expensive.
The response deliberately doesn't echo a template id back: the caller
sent the full PayrollTemplate alongside the request, so they already
know which template produced these rows.
PayrollPeriodSummariesResponse
dataclass
¶
PayrollPeriodSummary
dataclass
¶
PayrollPeriodSummary(
period_label,
total_changes,
non_no_change_changes,
total_employee_cost_cents,
total_company_cost_cents,
)
Summary counters for one payroll period (framing §7.1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
period_label
|
date
|
The period anchor ( |
required |
total_changes
|
int
|
Total rows in the period. |
required |
non_no_change_changes
|
int
|
Rows whose category is not |
required |
total_employee_cost_cents
|
int
|
Sum of |
required |
total_company_cost_cents
|
int
|
Sum of |
required |
PayrollTemplateDataInterface ¶
Placeholder contract for Layer B rendering.
Per-method implementations are registered by the Layer B dispatcher (PAY-1852). This class is intentionally empty until that dispatcher lands.
ProductType ¶
Bases: AlanBaseEnum
Product domains for snapshot entries.
RecurringRule
dataclass
¶
SnapshotEntryData
dataclass
¶
SnapshotEntryData(
snapshot_date,
country,
primary_user_id,
beneficiary_user_id,
enrollment_type,
company_id,
product_type,
entry_type,
start_date,
end_date,
entry_data,
link_id,
id=None,
)
Bases: Generic[TPayload]
Dataclass representation of a snapshot entry.
This is returned by queries instead of the SnapshotEntry model. The entry_data field is generic and can be specialized by country.
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
TPayload
|
Country-specific payload type (e.g., BeEnrollmentPayload) |
required |
Example usage
Global usage with BasePayload¶
entries: list[SnapshotEntryData[BasePayload]] = get_company_snapshot_entries_as_of(...)
Country-specific usage (in BE component)¶
BeSnapshotEntryData = SnapshotEntryData[BeEnrollmentPayload | BeEmploymentPayload | BeEmptyPayload] entries: list[BeSnapshotEntryData] = get_company_snapshot_entries_as_of(...)
content_eq ¶
Compare all fields except metadata (snapshot_date, id).
Source code in components/payroll_tool/internal/business_logic/entities/snapshot_entry_data.py
SnapshotEntryType ¶
Bases: AlanBaseEnum
Generic entry type categories shared across all countries.
SortType ¶
Bases: AlanBaseEnum
Row sort direction for the final Layer A output (framing §6 Step 7).
Direction only — the what to sort on lives in
PTSorting.sort_by_data_field (PAY-1842). Works for text and numeric
columns alike.
SuspensionPayload
dataclass
¶
Bases: BasePayload
Base payload for suspension entries (unpaid leave, parental leave, etc.).
Countries extend this with their specific fields as needed.
Example (FR): @dataclass class FrSuspensionPayload(SuspensionPayload): mandatory_coverage: bool | None = None
Usage: - FR: Uses suspensions for unpaid leave tracking - BE: Not used yet
UserTimeline
dataclass
¶
Immutable collection of snapshot entries keyed by (entry_type, product_type, link_id).
All mutation methods return a new instance. Equality uses content_eq to ignore metadata fields (snapshot_date, id).
__contains__ ¶
__eq__ ¶
Content equality: ignores snapshot_date and id on entries.
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
__hash__ ¶
__iter__ ¶
__len__ ¶
empty
classmethod
¶
from_entries
classmethod
¶
Build a timeline from a list of entries, keyed by match_key.
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
get ¶
has_identity ¶
keys ¶
same_identity ¶
Check that both timelines belong to the same (beneficiary, company) pair.
Empty timelines (no identity set) are compatible with any other timeline.
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
to_entries ¶
Return entries sorted by match_key for deterministic output.
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
with_entry ¶
Return new timeline with entry added or replaced.
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
without_key ¶
Return new timeline with key removed (no-op if absent).
Source code in components/payroll_tool/internal/business_logic/entities/user_timeline.py
fill_payroll_calendar_to_target ¶
Extend the calendar to TARGET_ENTRY_COUNT entries using the recurring rule.
Returns the full calendar (existing + newly generated). If already at or above target, returns existing_entries unchanged.
Generates entries strictly after the last entry's cutoff, or strictly after today when the calendar is empty. If the first generated entry would be less than min_period_weeks from the seed, it is pushed to the following month.
Source code in components/payroll_tool/internal/business_logic/actions/payroll_calendar_generator.py
get_company_snapshot_entries_as_of ¶
Retrieve snapshot entries for a company as of a specific date.
Returns entries from the most recent snapshot date <= as_of. Optionally filters by primary_user_ids for performance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
Company identifier |
required |
as_of
|
date
|
Date to retrieve snapshot as of (inclusive) |
required |
primary_user_ids
|
list[str] | None
|
Optional filter to only return entries for these users |
None
|
Source code in components/payroll_tool/internal/business_logic/queries/get_company_snapshot_entries.py
get_payroll_data ¶
Public 3b entry point — 3c calls this.
Resolves the country dependency from the current Flask app and delegates
to :func:run_pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
PayrollDataRequest
|
Runtime parameters. |
required |
template
|
PayrollTemplate
|
Resolved template. Template storage is Part 3a's concern;
during 3b dev pass a |
required |
locale
|
Locale | str
|
Keyword-only. Defaults to |
'en_US'
|
Returns:
| Type | Description |
|---|---|
PayrollDataResponse
|
|
Source code in components/payroll_tool/internal/business_logic/queries/data/pipeline.py
get_payroll_period_summaries ¶
Return per-period counters for a 3c landing page.
Applies every filter get_payroll_data understands except
period_label — the summary spans every period available to the
request's companies. operational_scopes routes through the country
dep so country-local scope rules apply identically to the main pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
PayrollDataRequest
|
Runtime parameters. |
required |
template
|
PayrollTemplate | None
|
Optional resolved template. When provided, its filtering rules (product-type / category allowlists and exclusions) are merged with request-level filters (request adds, never removes). |
None
|
Returns:
| Type | Description |
|---|---|
PayrollPeriodSummariesResponse
|
|
Source code in components/payroll_tool/internal/business_logic/queries/data/period_summary.py
components.payroll_tool.public.categorize ¶
Public surface for the MatchGroup categorizer.
categorize_match_group ¶
Apply the rule ladder. See module docstring.
Source code in components/payroll_tool/internal/business_logic/queries/categorize_match_group.py
components.payroll_tool.public.commands ¶
components.payroll_tool.public.dependencies ¶
PayrollToolDependency ¶
Bases: ABC
PayrollToolDependency defines the interface that apps using the payroll_tool component need to implement. This interface is used to take snapshots of payroll data.
apply_operational_scope_filter ¶
Apply country-local operational scope filtering.
Default is a passthrough — countries that don't model operational scopes leave this as-is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
Select[Any]
|
The Layer A select to filter. |
required |
operational_scopes
|
dict[str, dict[str, list[str]]]
|
Country-local scope spec from
|
required |
Returns:
| Type | Description |
|---|---|
Select[Any]
|
The filtered |
Source code in components/payroll_tool/public/dependencies.py
can_company_edit_payroll_calendar ¶
Whether this company may edit its payroll cutoff calendar.
Default: always allowed. Countries with ALM delivery-group restrictions (FR) override this so only a group's designated admin company can edit.
Source code in components/payroll_tool/public/dependencies.py
category_enum_class
instance-attribute
¶
Country-specific BasePayrollChangeCategory subclass. Passed to
build_in_memory_payroll_changes so the categorizer resolves
category names against the country's enum (BE: BePayrollChangeCategory).
compute_cost_breakdown
abstractmethod
¶
Aggregate the given premium entries into one cost breakdown.
All entries share a (enrollment, month) group — one PayrollChange row represents that group. The returned breakdown is persisted as JSONB on PayrollChange.cost_breakdown.
Source code in components/payroll_tool/public/dependencies.py
compute_match_key ¶
Derive the matcher key from a PremiumEntry.
Used by generate_payroll_changes to pair PEs with events.
Country-specific because the (link_id, product_type, service)
mapping depends on each country's PE shape. Default raises so
non-overriding countries fail loudly.
Source code in components/payroll_tool/public/dependencies.py
cost_breakdown_class
instance-attribute
¶
Country-specific BaseCostBreakdown subclass. Used by
InMemoryPayrollChange.from_db_payroll_change to deserialize the
cost_breakdown JSONB column back into a typed dataclass
(BE: BeCostBreakdown).
country
instance-attribute
¶
2-letter country code persisted on each PayrollChange row.
Passed to build_in_memory_payroll_changes as the country field
(BE: "BE").
get_active_companies_for_snapshot
abstractmethod
¶
Returns the list of company IDs eligible for snapshot generation.
Companies are eligible if they have an active contract in the past 3 months or in the future.
Returns:
| Type | Description |
|---|---|
list[str]
|
List of company IDs (as strings) to take snapshots for |
Source code in components/payroll_tool/public/dependencies.py
get_company_subquery ¶
Expose per-company metadata (display name, sector, account) for a 3b query.
Countries with no per-company UI needs may return None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
set[str]
|
Companies to scope the subquery to. |
required |
Source code in components/payroll_tool/public/dependencies.py
get_created_at_lower_bound ¶
Lower bound on created_at for events / PEs in this cycle.
Used by generate_payroll_changes: rows with
created_at > bound appeared after the previous cycle was
finalized and belong to the current cycle.
Implementation returns the wall-clock instant the previous cycle's
pay.csv was written. None means no prior cycle exists for this
company — caller treats that as "no lower bound" (include all
history).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
company being processed. |
required |
target_month
|
Month
|
the cycle being generated. Only PRIOR months count toward the bound, so a re-run picks up the previous cycle, not itself. |
required |
Source code in components/payroll_tool/public/dependencies.py
get_created_at_upper_bound ¶
Upper bound on created_at for events / PEs in this cycle.
Pinned at the wall-clock instant the cycle for target_month was
finalized — proxied by target_month's pay.csv created_at.
Once the paycsv exists, re-runs of the cycle reuse this bound so
the run is idempotent (rows from later cycles don't leak in).
None means no paycsv exists yet for target_month (= the
cycle hasn't been finalized) — caller falls back to utcnow().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
company being processed. |
required |
target_month
|
Month
|
the cycle being generated. |
required |
Source code in components/payroll_tool/public/dependencies.py
get_current_employees
abstractmethod
¶
Returns the list of employee user IDs for current employees at the company.
This is used as the base list for snapshot generation (section 7.1.B step 1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
The ID of the company to get employees for |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of employee user IDs (strings) |
Source code in components/payroll_tool/public/dependencies.py
get_employment_detail_subquery ¶
Expose employment metadata (matricule, population, …) for a 3b query.
Countries with no country-local employment table return None and
any template that references an employment-sourced field will skip
the join (leaving those columns empty).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_ids
|
set[str]
|
Companies to scope the subquery to. |
required |
Source code in components/payroll_tool/public/dependencies.py
get_enrollment_ids_for_company ¶
All enrollment ids belonging to company_id.
Used by generate_payroll_changes as the universe of link_ids
the orchestrator iterates over (and as the enrollment_ids filter
passed to PremiumDependency.get_entries_for_payroll).
Returns stringified ids — PayrollChange.link_id is String(36)
and country-native types differ (BE: UUID; FR: int). Country
overrides stringify before returning.
Default returns [] so countries not wired to the orchestrator
stay bootable. BE overrides; FR/CA/ES override when they migrate.
Source code in components/payroll_tool/public/dependencies.py
get_extra_interface_fields ¶
Return country-local extensions to the 3b field registry.
Used for country-specific data_fields that don't belong in the global
registry (e.g. be_contract_type). Default is empty.
Return type is list[Any] here because the concrete
InterfaceFieldDefinition is defined by the field registry in the
next task (PAY-1844) and importing it here would couple this public
contract to an internal module. Callers that care about the concrete
type can cast the return value.
Source code in components/payroll_tool/public/dependencies.py
get_last_pay_csv_generation_date ¶
Return the date of the most recent pay.csv generation for a company.
Each country derives this from its own payroll model (FR: max MonthlyPayrollInformation.upper_limit_datetime). Default returns None until the country override is wired up.
Source code in components/payroll_tool/public/dependencies.py
get_legacy_shadow_pay_csvs ¶
Return the country's legacy pay-CSV(s) for a (company, period), for shadow-diff parity validation against the new global export.
Only the country knows how to produce its legacy export, so it lives
on the dependency. The payroll_tool shadow-diff command (framing
§12) diffs each returned CSV against the new export before BE cutover.
BE returns one entry per health_contract active in the period
(legacy is per-contract). Countries with no legacy export to diff
against return None — the command reports the country as
unsupported.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
Company to produce legacy CSV(s) for. |
required |
period_label
|
date
|
Payroll period (first day of the month for monthly periods). |
required |
Source code in components/payroll_tool/public/dependencies.py
get_payload_types
abstractmethod
¶
Returns the country-specific payload type mapping.
This mapping defines which payload class to use for each SnapshotEntryType. Each country provides their own extended payload classes.
Returns:
| Type | Description |
|---|---|
dict[SnapshotEntryType, type[BasePayload]]
|
Dictionary mapping SnapshotEntryType to country-specific payload classes |
Source code in components/payroll_tool/public/dependencies.py
get_user_ids_by_link ¶
Resolve link_id → (primary_user_id, beneficiary_user_id) per link.
Used by generate_payroll_changes for no_change groups —
groups with no events, where PremiumEntry does not carry
user_ids. Event-bearing groups source the pair from the event
itself and don't call this hook. Reads must go through the passed
session (the worker's transaction), never current_session.
Default returns {}. Countries override to query their own
enrollment table (BE: get_user_ids_by_enrollment).
Source code in components/payroll_tool/public/dependencies.py
parse_link_ids ¶
Convert stringified link_ids back to the country's native id type.
generate_payroll_changes keeps link_ids as str end-to-end
(PayrollChange.link_id is String(36); events surface them
as strings too). The global PE fetch — PremiumAggregationService.
get_premiums_for_payroll — takes list[int | UUID]. The country
dep is the only place that knows whether to call UUID(s) or
int(s), so the conversion lives here.
Default raises so non-overriding countries fail loudly when the orchestrator tries to dispatch work to them.
Source code in components/payroll_tool/public/dependencies.py
take_snapshot
abstractmethod
¶
Build snapshot entry data for given employees. Does NOT persist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
company_id
|
str
|
Company to snapshot. |
required |
primary_user_ids
|
list[str]
|
Primary employee user IDs. |
required |
snapshot_params
|
dict[str, Any] | None
|
Country-specific snapshot options interpreted by the concrete dependency implementation. |
None
|
Source code in components/payroll_tool/public/dependencies.py
get_app_dependency ¶
Retrieves at runtime the payroll_tool dependency set by set_app_dependency
Source code in components/payroll_tool/public/dependencies.py
set_app_dependency ¶
Sets the payroll_tool dependency to the app so it can be accessed within this component at runtime
Source code in components/payroll_tool/public/dependencies.py
components.payroll_tool.public.entities ¶
Public dataclasses exposed to apps using the payroll_tool component.
Holds two groups of contracts:
PayrollTemplateDataInterface— placeholder contract for Layer B rendering, filled by the Layer B dispatcher (PAY-1852).- Country-dependency subquery shapes —
EmploymentDetailSubqueryandCompanySubquery. A country-specificPayrollToolDependencyreturns these so 3b's Layer A can join against country-local tables without importing country ORM models. Concrete select / column expressions are built by the country; 3b consumes them as opaqueFromClause/ColumnElement[Any].
Framing: https://github.com/alan-eu/Topics/discussions/32979 ⧉ (§5.5 dependency contract).
CompanySubquery
dataclass
¶
CompanySubquery(
selectable,
company_id_col,
display_name_col,
sector_col=None,
account_id_col=None,
)
Country-provided subquery exposing per-company metadata.
Joined against PayrollChange.company_id when a 3b template references
any company_* data field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
selectable
|
FromClause
|
The |
required |
company_id_col
|
ColumnElement[Any]
|
Column exposing the company ID. |
required |
display_name_col
|
ColumnElement[Any]
|
Column exposing the company display name. |
required |
sector_col
|
ColumnElement[Any] | None
|
Optional column exposing the sector (template scoping).
|
None
|
account_id_col
|
ColumnElement[Any] | None
|
Optional column exposing the account ID (template
scoping). |
None
|
EmploymentDetailSubquery
dataclass
¶
EmploymentDetailSubquery(
selectable,
user_id_col,
company_id_col,
external_employee_id_col,
population_col=None,
)
Country-provided subquery exposing per-user employment metadata.
The country-specific PayrollToolDependency.get_employment_detail_subquery
returns this (or None when the country has no employment detail). 3b's
Layer A joins selectable against PayrollChange on
user_id_col == PayrollChange.primary_user_id and
company_id_col == PayrollChange.company_id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
selectable
|
FromClause
|
The |
required |
user_id_col
|
ColumnElement[Any]
|
Column exposing the primary user ID. |
required |
company_id_col
|
ColumnElement[Any]
|
Column exposing the company ID. |
required |
external_employee_id_col
|
ColumnElement[Any]
|
Column exposing the country-local matricule.
On BE this maps to |
required |
population_col
|
ColumnElement[Any] | None
|
Optional column for the employee's population /
sub-group — |
None
|
LegacyShadowPayCsv
dataclass
¶
One country-legacy pay-CSV, for shadow-diff parity validation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Which legacy artifact this is (BE: the |
required |
csv_text
|
str
|
The decoded legacy CSV. |
required |
delimiter
|
str
|
Its field delimiter (BE: |
';'
|
locale
|
str | None
|
Locale the legacy CSV used; the new side is rendered in it so
headers/values compare like-for-like. |
None
|
PayrollTemplateDataInterface ¶
Placeholder contract for Layer B rendering.
Per-method implementations are registered by the Layer B dispatcher (PAY-1852). This class is intentionally empty until that dispatcher lands.
components.payroll_tool.public.enums ¶
Shared enums for the payroll tool — single source of truth across models and 3b pipeline.
ColumnAggregation ¶
Bases: AlanBaseEnum
How a column's per-group values are combined.
Used by both the ORM layer (PayrollColumnDefinition.aggregation) and
the 3b data-aggregation pipeline (grouping, pagination, Layer B dispatch).
The count_rows member is named to avoid shadowing str.count on the
AlanBaseEnum (str, Enum) base. The wire value stays "count".
Members:
sum:Nonetreated as 0, then summed.count_rows: number of rows in the group (includingNonevalues).concat: sorted unique stringified non-null values joined with ", ".min/max: min/max of non-null values.null: pass-through — all non-null values in the group must be equal; returns that value orNone; raises on inconsistency.
TemplateGranularity ¶
Bases: AlanBaseEnum
Row granularity for a payroll template / Layer A output.
per_change— one row perPayrollChange.per_enrollment— one row per (beneficiary, link_id, company).per_member— one row per (beneficiary, company).per_employee— one row per (primary_user, company).
components.payroll_tool.public.matching ¶
Public surface for the event/premium-entry matcher.
MatchGroup
dataclass
¶
All events + entries sharing one MatchGroupKey.
Tuples (not lists) so the dataclass stays hashable.
Invariant: entries is non-empty. Events without a matching premium
line never reach a MatchGroup — they're deferred by the matcher.
__post_init__ ¶
MatchGroupKey
module-attribute
¶
(period_start_month, link_id, product_type, service).
PayrollEventData
dataclass
¶
PayrollEventData(
event_type,
primary_user_id,
beneficiary_user_id,
company_id,
country,
entry_type,
product_type,
event_date,
effective_date,
link_id,
after_values,
before_values,
id=None,
before_entry=None,
after_entry=None,
)
Event produced by timeline diff. Converted to PayrollEvent ORM at persistence time.
match_events_to_premiums ¶
Group events + entries by MatchGroupKey.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
list[PayrollEventData]
|
snapshot events. |
required |
keyed_entries
|
list[tuple[MatchGroupKey, PremiumEntry]]
|
Premium entries already keyed by the country adapter. |
required |
target_month
|
Month
|
The payroll cycle month being processed. |
required |
Returns:
| Type | Description |
|---|---|
list[MatchGroup]
|
Groups in deterministic order: keys first seen on the event side |
list[MatchGroup]
|
come before keys seen only on the entry side. |