Lesson 8 — The SIF file, and why no bank layout ships
Say the honest thing first: no bank layout ships with this module. The engine is complete and tested, but the record layout is loaded as configuration, and until your bank's current specification is loaded the SIF screens have no layout to select.
Why that is a decision rather than an omission. The layout is held as data rather than as code, so a bank specification loads as a configuration record. On verification, one of the two expected bank sources no longer exists — its host has no DNS record and no archived copy — and the other is dated 2018, eight years older than the 2026 resolution it would have to satisfy. Shipping either would be worse than shipping nothing: a stale or unattributable layout produces a file your bank rejects, or, worse, one it accepts while carrying wrong values.
What to do instead. Ask your bank for its current SIF specification and load it as a layout record, filling in the provenance fields — source, URL, SHA-256 and date — so that a later reader knows exactly which document the layout came from. That request is a short email to your relationship manager, and it is the highest-value thing you can do in the week after finishing this course.
Once a layout exists, the flow is three steps.
- Go to Employees → SIF Files and create a record for the wage period, optionally pointing at a payslip batch.
- Validate. Every problem is reported at once, naming the employee and the field.
- Generate File. The file is written, stored, and the record sealed along with its SHA-256.
Why validation reports everything at once. A bank refuses a SIF as a unit. One bad IBAN on the last employee means nobody is paid, so a message that stopped at the first failure would turn a 400-employee batch into 400 round trips. Fix the whole list, revalidate, then generate.
Why the seal matters. A correction is a new export, never a regeneration. That is what makes "what exactly did we send on the 3rd?" a question with an answer, and it is Article 1(3) evidence in the most literal form available: a file, a hash and a date.
Who is in the file. Only establishment-regime workers. Domestic-regime workers and Article 4 excluded workers are accounted for in the compliance monitor instead, which is why the regime field in Lesson 6 decides two things at once.
Generating a SIF file tells the bank to pay, and tells your books nothing. Register WPS Payment on a generated SIF record closes that gap. It posts one journal entry that clears the net-wages payable the payslips raised and credits the bank, from the WPS Payment Journal set on the company.
The entry carries one debit line per employee, each with that employee's partner, against the account the NET salary rule maps to. That shape is the entire point. A single lump-sum line would balance perfectly and reconcile against nothing, leaving every employee's payable open forever — and nobody would find out until someone tried to age the account.
Three things it will refuse to do:
- Guess an account. With no payment journal, no default account on it, or no ledger mapping for the NET rule, it names the exact gap and posts nothing.
- Pay twice. Once an entry is registered the button is gone and a second attempt is refused. The first entry stands, and a correction is a reversal made deliberately in Accounting.
- Reopen the file. The SIF record stays sealed, with the same SHA-256 it had before. What you sent the bank does not change because you booked it.
The total posted always equals the file's own control total rather than a freshly recomputed net. If the two ever disagree, you learn it here instead of at the bank.
The four rights. The button is bound to accounting rights, but the action also reads the payroll run behind the file, and those reads are enforced independently. The person registering the payment holds all four of these.
| Requirement | Why it is needed |
|---|---|
Accounting → Accountant (account.group_account_user) |
It posts a journal entry to the bank, and the check is repeated inside the action, because a groups= attribute binds the button and never the RPC behind it |
| Payroll → Manager (Odoone Payroll Manager) | It reads every payslip in the run, and a payroll user is scoped by record rule to their own payslips, so a manager is genuinely required |
HR → Officer / HR Manager (hr.group_hr_manager) |
The income split reads the contract wage, which is gated on the HR manager group specifically |
| Member of the paying company | Multi-company record rules hide payslips outside the user's allowed companies, so without membership the action finds nobody to pay |
The failure mode. An access error here reads like a bug and is not one. Nothing in this flow runs with elevated privileges — what you are allowed to see is what gets paid — so the fix is to grant the missing group, never to work around it. If the action refuses and you cannot tell which of the four is missing, check company membership first: it is the one whose error message looks least like its real cause.