backbone-payroll 0.3.49

Payroll: salary structures, payroll runs and computed salary slips over effective-dated statutory tables, plus compensation changes
Documentation
# backbone-payroll — Extension Guide

## Public surface (stable)
- **GL port** (`application::service::payroll_gl`): `GlPostSink` + the contract envelope
  (`AccountingPostEnvelope`, `GlPostLine`, `GlPostAck`, `GlPostRejected`) — the salary-journal posting
  seam a composing service implements over accounting's `PostingService`. Zero normal Cargo edge; the
  envelope is the wire contract (duplicated per producer by design).
- **Events** (`application::service::payroll_events`): `PayrollPosted` (with `PayrollPayable`), the
  `PayrollEvent` union, and `PayrollEventSink` (a consuming service supplies its own — bus, outbox, …).
  `backbone-payments` subscribes and settles from the event alone: net pay clears
  `salary_payable_account_id` (amount = `total_net`), and each `payables[]` entry
  (`{gl_account_id, amount, statutory}`) is remitted to its account — statutory ones to their authority.
- **Write path** (`application::service::payroll_write_service::PayrollWriteService`): the guarded verbs
  (`create_structure`, `create_payroll_entry`, `add_salary_slip`, `process_payroll_entry`,
  `post_payroll_entry`) + DTOs (`NewStructure`, `NewComponent`, `NewPayrollEntry`, `NewSalarySlip`,
  `StatutoryLine`, `PostOutcome`, `PayrollError`).

## How a consuming service uses payroll
Supply the statutory deduction amounts (BPJS, PPh 21) on each `add_salary_slip` call as `StatutoryLine`s
— computed by the deferred `backbone-tax-id` overlay from the Employee's `tax_status` (PTKP) — exactly as
billing supplies tax lines. Read unpaid days from `hr.period_summary` and pass them as `unpaid_days`.
Implement `GlPostSink` over accounting to post the run; subscribe to `PayrollPosted` to settle — the event
carries the full payable breakdown (net-pay account + each remittance by account), so settlement never
re-queries payroll's private slip tables. The consumer's sink + adapter survive a regen of both modules (§5).

## Not a contract
- The 12 generated CRUD endpoints per entity are convenience scaffolding. Do **not** move a run's status
  or write a slip's amounts through the generic PATCH surface — it bypasses the net-pay math, the
  balanced-posting build, and the post-once gate. Use `PayrollWriteService`.
- `// <<< CUSTOM` blocks preserve local edits only; not a cross-module extension point.

## Invariants a consumer must not break
- `net_pay = gross_pay − total_deductions` and is never negative; the salary journal always balances
  (`gross = net + Σ deductions`).
- A run posts to the ledger **at most once** (idempotent on `source_id = payroll_entry_id`).
- The statutory **amounts** are supplied to payroll; the PTKP/BPJS/PPh-21 **math** lives in the tax
  overlay — don't recompute it here.