Skip to content

FIX enforce VeriFactu isolation across MultiCompany entities - #34

Open
braito4 wants to merge 10 commits into
garacil:mainfrom
braito4:fix/multicompany-entity-isolation
Open

FIX enforce VeriFactu isolation across MultiCompany entities#34
braito4 wants to merge 10 commits into
garacil:mainfrom
braito4:fix/multicompany-entity-isolation

Conversation

@braito4

@braito4 braito4 commented Jul 27, 2026

Copy link
Copy Markdown

Summary

This PR makes VeriFactu data, fiscal chains, configuration, and AEAT operations strictly entity-specific when Dolibarr MultiCompany is enabled.

It also prevents VeriFactu from operating with MultiCompany sharing settings that would break the legal separation between taxpayers.

Changes

Strict entity isolation

  • scope the previous-invoice hash lookup to the active entity
  • scope retries and pending/error invoice processing to the active entity
  • scope dashboard statistics, integrity checks, invoice lists, and invoice searches to the active entity
  • scope VeriFactu trigger checks and extrafield updates to the invoice entity
  • initialize VeriFactu extrafields only for invoices belonging to the active entity
  • create mandatory default extrafield values for the active entity instead of hard-coding entity 1
  • keep configuration constants and document directories managed independently per entity

Fiscal queries use strict equality:

entity = ((int) $conf->entity)

They do not rely on getEntity('invoice'), because that function may include entities shared by MultiCompany.

MultiCompany sharing safeguards

VeriFactu requires the following resources to remain independent:

  • customer invoices
  • customer invoice numbering
  • bank accounts

The new isEntitySharingAllowed() check detects both directions:

  • resources shared by the VeriFactu entity with another entity
  • resources shared by another entity with the VeriFactu entity

Stored sharing options are ignored when the corresponding MultiCompany sharing feature is disabled.

The aggregate isVerifactuEntityIsolated() check is applied:

  • when VeriFactu is enabled
  • again during every BILL_VALIDATE event

Rechecking during validation covers sharing changes made after activation, entities created from templates, API operations, imports, TakePOS flows, and mass validation.

Validation is also rejected when the invoice belongs to an entity different from the active entity.

Taxpayer identity and certificate isolation

Each VeriFactu-enabled entity must represent a different taxpayer.

The taxpayer NIF is treated as the primary and mandatory cross-entity identity boundary. It is checked before configuration is saved, during module activation, during invoice validation, and before any AEAT submission/query.

The module now:

  • normalizes and compares VERIFACTU_HOLDER_NIF against all other VeriFactu-enabled entities
  • rejects a taxpayer NIF already used by another active VeriFactu entity before it can be saved or activated
  • calculates the SHA-256 fingerprint of the public X.509 certificate
  • rejects a certificate fingerprint already registered by another active VeriFactu entity
  • stores the fingerprint as an entity-specific VERIFACTU_CERTIFICATE_FINGERPRINT_SHA256 constant

Certificate files, passphrases, schemas, configuration constants, and output directories remain stored under the current entity context.

These checks are neutral when MultiCompany is not enabled, so standalone VeriFactu behavior is unchanged.

AEAT Multi-OT indicators

Enabling MultiCompany alone no longer implies TipoUsoPosibleMultiOT=S.

Both indicators are enabled only when more than one active MultiCompany entity has VeriFactu enabled:

Active entities with VeriFactu TipoUsoPosibleMultiOT IndicadorMultiplesOT
1 N N
More than 1 S S

The installation number remains unique per entity.

Defense in depth

The implementation uses three complementary safeguards:

  1. activation-time configuration validation
  2. validation-time isolation and taxpayer-identity enforcement
  3. certificate fingerprint validation before AEAT operations
  4. strict entity filters in every fiscal query

This means an incompatible sharing configuration, duplicated taxpayer identity, or reused certificate cannot enter the AEAT workflow even if introduced after module activation.

Tests and validation

  • added tests for one and multiple active VeriFactu entities
  • added tests for incoming and outgoing invoice, invoice-number, and bank-account sharing
  • added normalized taxpayer-NIF uniqueness tests
  • verified duplicate NIF protection at configuration, activation, validation, submission, and query boundaries
  • added normalized X.509 SHA-256 fingerprint uniqueness tests
  • verified that disabled MultiCompany sharing features do not produce false positives
  • verified aggregate isolation checks report the incompatible resource
  • kept all 52 mass-validation assertions passing
  • activated VeriFactu and its dependencies in entities 1 and 2 on Dolibarr 24.0
  • verified 27 VeriFactu extrafields and 15 VeriFactu constants per entity
  • verified separate output directories for both entities
  • executed previous-invoice hash queries independently in both entity contexts
  • ran PHP syntax checks on all module PHP files
  • ran git diff --check

Scope

This PR does not modify the external MultiCompany module. VeriFactu enforces isolation at activation and invoice validation time while retaining strict entity filtering at runtime.

A future generic Dolibarr core sharing API could allow MultiCompany to disable incompatible options directly in its administration interface, but it is not required for the fiscal isolation provided by this PR.

General taxpayer-identity immutability, certificate validity/permissions, and OpenSSL command hardening have been separated into PR #41.

@braito4

braito4 commented Jul 27, 2026

Copy link
Copy Markdown
Author

Compatibility validation completed:

  • PHP 8.3.30: 127 files linted; 71/71 test assertions passed; Dolibarr runtime loaded for entities 1 and 2
  • PHP 8.4.17: 127 files linted; 71/71 test assertions passed; Dolibarr runtime loaded for entities 1 and 2
  • PHP 8.5.2: 127 files linted; 71/71 test assertions passed; Dolibarr runtime loaded for entities 1 and 2
  • MySQL 8.0.44 with ONLY_FULL_GROUP_BY and STRICT_TRANS_TABLES: all modified query families validated with EXPLAIN

The MySQL validation found an existing invalid invoice column (date_creation). Commit 6bbf668 fixes it by selecting Dolibarr's datec AS date_creation.

@braito4
braito4 force-pushed the fix/multicompany-entity-isolation branch from 6bbf668 to 072eba1 Compare July 27, 2026 13:45
@braito4

braito4 commented Jul 27, 2026

Copy link
Copy Markdown
Author

The unrelated date_creation compatibility fix has been removed from this branch and moved to the independent PR #35. This PR now contains only the MultiCompany entity-isolation changes.

@braito4

braito4 commented Jul 27, 2026

Copy link
Copy Markdown
Author

Added dynamic AEAT Multi-OT indicators:

  • TipoUsoPosibleMultiOT=S when MultiCompany is enabled
  • IndicadorMultiplesOT=S when more than one active entity exists
  • the installation number remains unique per entity

Validated in the two active local entities; both report Multi-OT support and active multi-taxpayer use.

@braito4 braito4 changed the title FIX isolate VeriFactu data by MultiCompany entity FIX enforce VeriFactu isolation across MultiCompany entities Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant