Employees
Monthly inputs
Overtime, bonus, absences and one-off items for the period go here.
Run · 2026-10
Run = signed in the enclave, receipt on Sui, payslip links e-mailed. Buttons are disabled in this static preview; the amounts are the results of the test run. Files for the period are under the account menu → Documents.
Logs
Storage: local folder in this test run (live accounts use Supabase Storage); registry: local mock of the Sui object.
Documents
October 2026
Switzerland · CHF
Year end 2026
Switzerland · CHF
- 2026_E001_lohnausweis.json
- 2026_E001_lohnausweis.pdf
- 2026_E002_lohnausweis.json
- 2026_E002_lohnausweis.pdf
- ahv_annual_2026.csv
- ahv_annual_2026_summary.json
- bvg_annual_2026.csv
- bvg_annual_2026_summary.json
- fak_annual_2026.csv
- fak_annual_2026_summary.json
- quellensteuer_annual_2026.csv
- uvg_annual_2026.csv
- uvg_annual_2026_summary.json
Company
Editing is disabled in this static preview.
Reporting requirements
Generated from bs_payroll/reporting.py (python3 -m bs_payroll.cli reporting). Each country's legal entity files its own returns; nothing is combined across countries.
CH: Swiss entity, canton Basel-Stadt
Retention: 10 years for payroll records and accounting vouchers (OR art. 958f).
| Filing | Recipient | Frequency | Deadline | Output |
|---|---|---|---|---|
| Payslip (Lohnabrechnung) | Employee | monthly | with each salary payment (OR art. 323b) | {period}_{id}_payslip.pdf |
| Salary payment file (pain.001, CHF) | Company bank | monthly | before pay day | pain001_salaries_{period}.xml |
| Quellensteuer statement | Steuerverwaltung Basel-Stadt | monthly | within 30 days after the end of the month (ELM-QST or e-portal) | quellensteuer_BS_{period}.csv + _summary.json |
| Payroll journal | Accounting | monthly | month end close | journal_{period}.csv |
| AHV/IV/EO, ALV, FAK annual wage declaration | Ausgleichskasse / FAK Basel-Stadt | annual (advance payments monthly or quarterly) | 30 January | ahv_annual_{year}.csv, fak_annual_{year}.csv |
| UVG / KTG wage declaration | Accident and sickness insurer | annual | per insurer (usually end of January or February) | uvg_annual_{year}.csv |
| BVG salary and contribution statement | Pension fund | annual | per pension fund regulations | bvg_annual_{year}.csv |
| Quellensteuer annual statement | Steuerverwaltung Basel-Stadt | annual | with the December statement | quellensteuer_annual_{year}.csv |
| Lohnausweis (Form 11) | Employee (copy to tax office where the canton requires it) | annual | end of January | {year}_{id}_lohnausweis.pdf |
ES: Spanish entity, Valencia
Retention: 4 years for tax, social security and labour records (LGT art. 66, LISOS art. 21); 6 years for accounting books and vouchers (Código de Comercio art. 30).
| Filing | Recipient | Frequency | Deadline | Output |
|---|---|---|---|---|
| Recibo de salarios (nómina) | Employee | monthly | with each salary payment (ET art. 29, Orden ESS/2098/2014) | {period}_{id}_payslip.pdf |
| Salary payment file (SEPA pain.001, EUR) | Entity bank | monthly | before pay day | ES_pain001_salaries_{period}.xml |
| Seguridad Social liquidation (RNT per worker, RLC totals) | TGSS via SILTRA / Sistema RED | monthly | last calendar day of the following month | ES_seguridad_social_{period}.csv + _summary.json |
| Modelo 111 (IRPF withholding) | AEAT | quarterly (monthly for large companies) | 1-20 of the month after the quarter (4T: by 20 January) | ES_modelo111_{period}.json; ES_modelo111_{year}_quarters.json |
| Payroll journal | Accounting | monthly | month end close | ES_journal_{period}.csv |
| Modelo 190 (annual withholding summary) | AEAT | annual | 1-31 January | ES_modelo190_{year}.csv + _summary.json |
| Certificado de retenciones | Employee | annual | before the IRPF return period opens (RIRPF art. 108.3) | {year}_{id}_certificado.pdf |
| Afiliación, alta and baja of workers | TGSS (Sistema RED) | per event | before the start of work; baja within 3 days | not produced by the platform |
| Contract communication (Contrat@) | SEPE | per contract | within 10 days of signing | not produced by the platform |
| Registro de jornada and registro retributivo | Kept by the entity (Inspección de Trabajo) | daily / annual | available on request (ET arts. 34.9, 28.2) | not produced by the platform |
One TEE or one per country
Recommendation: one TEE for both countries. Run a single enclave image (one attested measurement) in one warm pool in Zurich, and keep the countries apart with keys and outputs rather than with separate enclaves: one AWS KMS key per legal entity, released only to the approved enclave measurement, and payslips, returns and retention handled per entity.
Why
- The law does not require a Spanish or EU location. Switzerland has an EU adequacy decision, so the Spanish entity's employee data can be processed in Zurich without standard contractual clauses. Spanish law requires payroll, tax and social security records to be kept and produced on request (AEAT, TGSS, Inspección de Trabajo), not to be processed in Spain.
- The roles are the same in both countries. Each entity is the controller for its own employees (Swiss entity under the DSG, Spanish entity under the GDPR and LOPDGDD) and the platform is their processor. One enclave changes nothing here; what changes is the agreement, which needs GDPR Art. 28 terms for the Spanish entity.
- One measurement to attest and audit. The calculation code is the same image for both countries. Two TEEs would mean two sets of PCRs to register on Sui, two deployments to patch and two attestations to review, with no legal gain.
- Isolation comes from keys, not from enclave count. With a KMS key per entity, the Spanish data is encrypted under the Spanish entity's key and the Swiss data under the Swiss one. Losing one key does not open the other, and each entity can be deleted on its own schedule by destroying its key (Spain: 4 years for tax, social security and labour records, 6 years for accounting; Switzerland: 10 years).
- Cheaper and simpler to run. One warm pool serves both entities, and a run covers both in one call, as the code does today (
run_payrollpays CH and ES together and writes separate reports per entity).
When to split
Run a second enclave in an EU region (for example Frankfurt or Madrid) only if a Spanish customer or collective agreement requires EU hosting by contract, or if a lawyer concludes that the onward transfers below need it.
What a lawyer should confirm
- The GDPR Art. 28 addendum to the processing agreement for the Spanish entity, and that the platform, as a Swiss processor serving an EU controller, does not need an Art. 27 EU representative.
- Group access: when one company account sees both entities, the Swiss entity's users see Spanish employee data. That needs an intra-group basis, or access limited per entity in the portal.
- Onward transfers of Spanish employees' data to US or third-country sub-processors (Resend payslip e-mail, the LLM host): the EU-US Data Privacy Framework certification of each recipient, not only the Swiss-US one.
- Retention by key destruction meeting the Spanish periods (LGT art. 66, LISOS art. 21, Código de Comercio art. 30) alongside the Swiss 10 years (OR art. 958f).
- Whether employee representatives or the Inspección de Trabajo must have access to the registro retributivo and registro de jornada from Spain, which the platform does not produce yet.
Test log
PASS Swiss-only account: no Spanish questions
PASS Vicente added with country ES; wrong DNI letter caught [E003]
PASS Spanish employee without Spanish entity: entity questions asked [10 questions]
PASS payroll blocked until the Spanish entity is set
PASS Spanish entity saved; nothing missing [[]]
PASS employee list shows countries
PASS preview: estimate, not attested, nothing stored
PASS preview: totals per entity in CHF and EUR [{"CH": {"currency": "CHF", "employees": 2, "gross": "16700.00", "payout": "14896.50", "employer_total": "2037.30", "qst": "651.70"}, "ES": {"currency": "EUR", "employees": 1, "gross": "3000.00", "payout": "2205.20", "employer_total": "1125.25", "irpf": "567.30"}}]
preview Vicente: {'name': 'Vicente Pastor', 'country': 'ES', 'currency': 'EUR', 'gross': '3000.00', 'net_salary': '2205.20', 'payout': '2205.20', 'flags': ['9 earlier month(s) of 2026 not paid on the platform: IRPF rate uses the contractual pay for them; enter ytd_opening for exact regularisation']}
PASS run pays all three [E001,E002,E003]
PASS run attested: enclave signature verified on apply
PASS Swiss reports present
PASS Spanish reports present
PASS Swiss journal has no Spanish employee
PASS Spanish payment file in EUR from the Spanish IBAN
Vicente 2026-10: {'gross': '3000.00', 'base_cc': '3500.00', 'ss_ee': '227.50', 'irpf': '567.30', 'payout': '2205.20', 'employer_total': '1125.25'} IRPF 18.91 %
PASS Vicente payslip in EUR, IRPF 18.91 %
PASS Seguridad Social liquidation for the CCC [{"ccc": "46123456752", "period": "2026-10", "workers": 1, "base_cc": "3500.00", "base_atep": "3500.00", "employee": "227.50", "employer": "1125.25", "total_due": "1352.75", "deadline": "last calendar day of 2026-11 (SILTRA, liquidaci\u00f3n directa)"}]
PASS Vicente notified
PASS Vicente reads his own payslip
PASS Vicente cannot open Anna's payslip
PASS stored blobs are ciphertext only
PASS year end: Spanish and Swiss returns separate
PASS certificado de retenciones for Vicente, Lohnausweis for the Swiss staff
certificado: {'rendimientos_trabajo_dinerarios': '12000.00', 'retenciones_practicadas': '2269.20', 'gastos_deducibles_seguridad_social': '682.50'}
PASS Vicente's Oct-Dec earnings incl. December extra pay
PASS AHV return excludes Vicente
PASS reporting requirements recorded per country
SUMMARY 25 / 25 passed