# Cegid (izibizi.pt) integration

Sibyla keeps PostgreSQL as the canonical financial state. Cegid and the financial workbook are independently staged projections; a purchase document reaches `Integrated` only after every enabled target is confirmed.

The optional Hermes `financial-agent` is not an integration target. It receives a
normalized snapshot and stores proposal-only assessments in a separate job lane.
Assessment success, failure, or timing cannot approve a payment, change Cegid or
workbook state, or determine whether the deterministic integration job completes.

## Configuration

Company connections are stored in PostgreSQL as one connection per internal
company. In **Empresas internas**, first select **Software de integração: Cegid**;
the Cegid section is hidden for `Nenhum` and replaced by a non-functional placeholder
for `Moloni`. Then save a disabled draft with the
endpoint/client fields and secret, use **Discover and test fiscal years**, keep the
intended years, save any selection change, test again, then enable it. A draft may
have no fiscal-year mappings; activation may not. The secret is write-only; leaving
the field empty during a later edit preserves the current value.

The generic selection is stored separately in `CompanyIntegrationSettings`. Existing
rows in `CegidCompanyConnections` are backfilled to `Provider = Cegid` by the schema
migration. Changing away from Cegid is rejected while that company's Cegid connection
is enabled; disable it first. A disabled connection and all its credentials/mappings
are preserved when another provider is selected.

Sibyla encrypts each database secret with ASP.NET Data Protection. API, Web, and
Worker share a key-ring directory and a certificate from `LocalMachine\\My`. Grant
only their runtime identities access to the directory and certificate private key,
and back up both the key ring and an exportable certificate/private key.

Only global integration switches remain in host configuration:

```json
{
  "Cegid": {
    "Enabled": true,
    "SalesPollInterval": "00:15:00",
    "ImportSalesPdfs": true,
    "ProjectPurchasesToExcel": true,
    "ProjectSalesToExcel": true,
    "SecretProtection": {
      "KeyRingPath": "D:\\SibylaData\\Keys\\Cegid",
      "CertificateThumbprint": "CERTIFICATE-THUMBPRINT"
    }
  }
}
```

The equivalent environment variables are
`Cegid__SecretProtection__KeyRingPath` and
`Cegid__SecretProtection__CertificateThumbprint`. Never put a company client secret
in environment variables, `appsettings.json`, logs, or a deployment ZIP. Testing
requests a base OAuth access token, sends that token in the `Authorization` header to
`GET <API_URL>/fiscal_years_list`, and stores the discovered fiscal-year `id` as
the mapping's `to_subentity_id` (for example `pt999999990_1#y2024_1_`). It then
uses the same base token to validate each selected mapping with
`PUT <API_URL>/entity_sub_switch`.

The discovery parser accepts the common JSON:API collection shape
`{"data":[{"id":"...","attributes":{"year":2024}}]}` and a root array. It
looks for an unambiguous year in `year`, `name`, or `code` on the resource or its
`attributes`, falling back to the resource `id`. Missing IDs/years and different
IDs resolving to the same year are rejected rather than guessed.

An internal company without a tested and enabled Cegid connection is intentionally
ignored by the recurring poller and by the Cegid portion of document integration.
It is not a configuration error and produces no recurring Cegid jobs.
The poller, integration handlers, reconciliation and projection services remain
Cegid-specific and continue to decide eligibility solely from a tested, enabled
`CegidCompanyConnection`; `CompanyIntegrationSettings` does not alter their runtime
selection logic.

## Bank and cash accounts

Each internal company owns its rows in the `BankAccounts` and `CashAccounts`
tables (name, IBAN for bank accounts, currency, optional Cegid account ID, active
flag), managed from **Empresas internas** or via
`GET|PUT /api/admin/companies/{id}/bank-accounts` and
`GET|PUT /api/admin/companies/{id}/cash-accounts`. Payment instructions and the
per-connection payment defaults reference these rows by ID; the raw Cegid account
IDs live only on the account rows. During integration the worker resolves the
selected account to its Cegid ID and fails closed when the account is missing,
belongs to another company, or has no Cegid ID configured.

## Workbook preparation

Test the schema changes on an isolated workbook copy before enabling the worker:

- add `MST_ID` to `PaymentsControl` / `Table2`;
- add `MST_ID` to `ReceivablesControl` / `Table4`;
- add `CegidProductCode` to `SUPDTL` / `Table13`;
- calculate and configure the post-change schema fingerprints for all three tables;
- initialize the DPAPI-protected delegated Graph token cache under the same Windows account that runs the worker.

The relevant configuration is under `ExcelCommit:MicrosoftGraph`: `ExpectedSchemaFingerprint`, `ReceivablesExpectedSchemaFingerprint`, and `SupplierDetailExpectedSchemaFingerprint`. Purchase and sales rows are upserted by `MST_ID`; receipt imports revise existing invoice-line rows rather than adding receipt rows.

## Purchase flow

The purchase integration handler requires `Direction.Purchase`. Sale documents are
not passed through this purchase-only path and remain unsupported there pending a
dedicated outbound-sales document integration.

The integration handler uses the approved extraction revision and performs durable stages:

1. Generate a deterministic product code (`SIB-` plus 12 SHA-256 hex characters) from normalized `SUPDTL.Service`; reconcile or create `/products`.
2. Create and finalize the purchase document with a unique `external_reference`.
3. Upload the immutable original and associate it with the purchase document.
4. Create a purchase payment only after an operator approves the payment instruction.
5. Upsert one `PaymentsControl` row per purchase line and publish the generated product code to matching `SUPDTL.Service` rows.

Mappings are `Invoice → FC`, `InvoiceReceipt → FC + approved payment`, `CreditNote → NCF`, and `DebitNote → NDF`. Credit and debit notes require `parent_document_reference` in the revision's additional data. A receipt creates no purchase document; its approved allocations pay selected existing Cegid purchase documents.

Attachment association and purchase payment creation have no documented remote idempotency key. A timeout or ambiguous 5xx response therefore becomes `WriteUnknown` and must be confirmed by an operator before processing resumes.

## Sales flow

The worker enqueues a current-fiscal-year sync only for tested, enabled database
connections owned by active internal companies, every 15 minutes by default. It
imports finalized invoices, invoice-receipts, debit/credit notes, receipts, void
state, raw payload hashes, line detail, customer identity, receipt allocations, and
immutable PDF versions.

Customer NIF `999999990` remains raw document data and does not create a canonical customer. Other customers are matched by normalized country/NIF and receive the `Customer` role. Document-level receipt values are allocated across invoice lines using deterministic largest-remainder rounding; direct line allocations remain direct.

Every Cegid sales product code needs an active database mapping to a workbook `PLKEY`. Missing mappings block only the affected Excel projection; PostgreSQL and PDF import still complete. Adding a mapping automatically queues the blocked document for reprojection.

## Operations API

All routes require the existing Sibyla authorization policies:

- `GET /api/integrations/cegid/status`
- `GET|PUT /api/admin/companies/{id}/integration-provider`
- `GET|PUT /api/admin/companies/{id}/cegid`
- `POST /api/admin/companies/{id}/cegid/test` (discovers fiscal years, persists
  them for an empty draft, and validates selected mappings)
- `POST /api/admin/companies/{id}/cegid/enable`
- `POST /api/admin/companies/{id}/cegid/disable`
- `POST /api/integrations/cegid/sales/sync`
- `GET /api/integrations/cegid/sync-runs`
- `GET /api/integrations/cegid/sales/documents`
- `GET|PUT /api/integrations/cegid/product-plkey-mappings`
- `GET /api/documents/{id}/cegid`
- `PUT /api/documents/{id}/cegid/payment-instruction`
- `POST /api/documents/{id}/cegid/payment-instruction/approve`
- `POST /api/integrations/cegid/commits/{id}/reconcile`
- `POST /api/integrations/cegid/operations/{id}/confirm`
- `GET|POST /api/documents/{id}/financial-assessments` (proposal-only; no apply route)

Enable Cegid first with workbook projection flags off, validate purchase and sales imports, enable PDF ingestion, then enable `Table2`, `Table13`, and `Table4` projections after their schema fingerprints have been confirmed.

For the first application deployment, keep `Cegid:Enabled` and
`ExcelCommit:MicrosoftGraph:Enabled` false. This still installs the additive
database schema and operations surfaces without contacting Cegid or changing the
financial workbook. Activation requires provisioning the shared Data Protection
certificate/key ring, saving and testing each company connection in Sibyla, and
verifying the workbook copy, fingerprints, and DPAPI token cache on the target host.
Existing historical data must reuse its original immutable connection key when the
database connection record is created.
