Skip to content
bitzorcas
中EN

Reference

Industry Extensions HR leave policies

A deep guide to LeavePolicySchedule, four regional presets, annual-leave tiers and month approximation, fixed maternity increments, the medical-period matrix, EffectiveFrom gaps, local changes, and reproducible policy-store design.

Last updated

HR moves some leave values into LeavePolicySchedule, which is better than hiding every number inside methods. The schedule still expresses too few dimensions for local eligibility, source version, employee category, and complete effective history.

1. LeavePolicySchedule fields

FieldCurrent meaning
AnnualLeaveTiers(MinWorkYears, Days) list
MarriageDaysMarriage leave days
MaternityDaysNational base plus local extension total
PaternityDaysPaternity/care leave days
BereavementDaysBereavement leave days
RegionFree-form string
EffectiveFromDefaults to DateTime.Today

There is no EffectiveTo, PolicyId/Version, source URI/hash, PublishedAt, ApprovedBy, employee category, birth order, difficult-labor increment, miscarriage rules, or supersession history.

2. Creation validation

Create rejects empty tiers, non-strictly ascending MinWorkYears, and negative top-level leave-day values. It copies the tier array, and ValueObject equality includes every field and tier.

Create a regional snapshot with an explicit date
// Supply the date so the snapshot does not inherit a process-date default.
var schedule = LeavePolicySchedule.Create(
annualLeaveTiers: [(1, 5), (10, 10), (20, 15)],
marriageDays: 3,
maternityDays: 158,
paternityDays: 15,
bereavementDays: 3,
region: "ExampleProvince",
effectiveFrom: new DateOnly(2026, 1, 1));
// Structural success does not prove that an approved authority supports the values.
if (schedule.IsFailure)
return schedule.Error;
return schedule.Value;

Tier MinWorkYears and Days can themselves be negative. Region is not trimmed unless entirely blank. There is no maximum tier count or leave-day bound.

3. Four presets

LeavePolicies exposes National, Guangdong, Shanghai, and Beijing, with Default=National. Static initialization supplies no EffectiveFrom, so every process creates snapshots dated on its startup day.

The same binary therefore yields unequal policy value objects on different days, and the date is not a legal effective date. Comments describe the presets as 2024 reference values; they must not silently be treated as current in a 2026 product.

4. Statutory annual-leave tiers

National tiers are 5 days at one year, 10 at ten, and 15 at twenty, matching the official regulation. The algorithm walks ascending tiers and returns zero below one year.

Method XML instead says below one year gets five days, one-to-ten gets ten, and over ten gets fifteen. That contradicts both executable data and the authority. Code and authority are the documentation truth; the XML needs correction.

5. Month approximation is not the official calendar formula

CalcAnnualLeaveProrated accepts 0–12 worked months and floors fullDays × months / 12. Official measures use remaining calendar days for a new hire and elapsed calendar days minus leave already arranged for termination.

Treat the current method as a monthly approximation
var approximate = LeaveCalculator.CalcAnnualLeaveProrated(
totalWorkYears: 12m,
workedMonths: 7,
schedule: LeavePolicies.National);
// Current value is floor(10×7/12)=5; hire date and leave already taken are absent.
if (approximate.IsFailure)
return approximate.Error;
// Formal settlement needs a calendar-day engine and source dates.
return new AdvisoryLeaveEstimate(approximate.Value);

The source comment itself calls this a simplification, so it is not suitable for exact wage compensation or dispute resolution.

6. Marriage, paternity, and bereavement

The getters return schedule values directly. They do not model eligibility, registration date, relationship, travel leave, local conditions, employer-better policy, or policy selection by event date.

The wrapper must resolve employment jurisdiction and date, then combine statutory floor and employer policy. Passing no schedule silently uses National and can understate a local entitlement.

7. Maternity calculation

The engine starts with policy.MaternityDays, always adds 15 for difficult labor, and adds 15 per additional baby. The national special rules support these baseline increments.

Local implementations can define different increments or conditions. The schedule stores only total MaternityDays, so it cannot express a regional difficult-labor rule. A recent Guangdong implementation rule is an example of why this needs structured local policy.

8. Multiple-birth bounds

Counts below one fail. There is no upper bound, and (count-1)*15 can overflow int. Public wrappers need a reasonable maximum and checked arithmetic.

The policy also omits miscarriage leave by gestational age, nursing time, whether rewards stack, and spouse-care eligibility.

9. Medical-period matrix

CalcMedicalPeriodMonths returns 3/6/9/12/18/24 based on total and current-company service. Negative values fail. It does not require current-company years to be no greater than total years.

yesyesnonounder 55 to under 1010 to under 1515 to under 2020 or more

Total service < 10?

Company service < 5?

3 months

6 months

Company service

6 months

9 months

12 months

18 months

24 months

It returns only the maximum-month category, not accumulated sick leave, computation period, local policy, pay treatment, or occupational-injury classification.

10. Missing policy resolution

Callers manually pass a schedule; omission uses National. There is no Region + event-date resolution or rejection of expired/future policy. A Shanghai event can silently use National if the caller forgets one argument.

A fail-closed resolver requires a jurisdiction and selects exactly one Published policy covering the event date. Missing or overlapping policy is an Error rather than a reassuring but wrong default.

11. Target policy model

Target: publishable and replayable leave policy
var policy = await policies.ResolveAsync(new LeavePolicyQuery(
JurisdictionCode: employment.JurisdictionCode,
LeaveType: LeaveType.Maternity,
OccurredOn: birthDate,
EmployeeCategory: employment.Category), cancellationToken);
// Published policy carries version, source hash, effective interval, and structured rules.
if (policy.IsFailure)
return policy.Error;
// The result keeps each addition and policy-selection reason, not just a day count.
return leaveEngine.Calculate(facts, policy.Value)
.WithEvidence(policy.Value.Version, policy.Value.SourceHash);

This is a GA target, not the current API.

12. Regulatory-change workflow

HR/legal discovers a change → retain official text and hash → structure a draft → two-person review → golden cases → assign EffectiveFrom/To → publish → supersede but retain old policy → report affected calculations.

Never scrape legislation at startup and activate it automatically. External text needs trusted-source validation, legal interpretation, and approval.

13. Test matrix

ScenarioMust prove
Tier1/10/20 boundaries, negative tier, disorder, duplicates
ProrationLeap year, hire/termination date, leave taken, approximation delta
MaternityNational/local, difficult labor, multiple birth, overflow, stacking
RegionMissing, unknown, expired, overlapping, future policy
MedicalEvery matrix boundary and company service > total service
Value objectSame published version equals across process and date
SourceHash change creates review, not automatic activation
HistoryOld events always resolve the historical policy

No automated test currently exists.

14. Inspection commands

Terminal window
# Schedule fields, dynamic default date, presets, and fixed increments.
rg -n "DateTime.Today|LeavePolicies|MaternityDays|days \+= 15|workedMonths / 12m" \
src/Platform/IndustryExtensions/BitzOrcas.Platform.IndustryExtensions.Hr -g '*.cs'
# Version, end date, source evidence, and tests should currently have no matches.
rg -n "PolicyVersion|EffectiveTo|SourceHash|ApprovedBy|\[Fact\]" \
src/Platform/IndustryExtensions/BitzOrcas.Platform.IndustryExtensions.Hr tests -g '*.cs'

Industry Extensions overview · Finance calculators · Auction and GA

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%