Skip to content

fluence-eu/penelop

Repository files navigation

Fluence::Penelop

Ruby gem for importing and exporting Penelop XML files, a French financial data exchange standard used for life insurance, capitalization contracts, PEA (stock savings plans), and securities accounts.

Installation

Add this line to your Gemfile:

gem 'fluence-penelop'

Then run:

bundle install

Or install directly:

gem install fluence-penelop

Usage

Importing a Penelop file

require 'fluence/penelop'

# Import from a file
document = Fluence::Penelop.import('/path/to/file.xml')

# Import from an XML string
document = Fluence::Penelop.import(xml_string)

# Import from an IO object
document = Fluence::Penelop.import(io_object)

Exporting to XML

# Export to an XML string
xml_string = Fluence::Penelop.export(document)

# Export to a file
Fluence::Penelop.export_to_file(document, '/path/to/output.xml')

# Export options
xml = Fluence::Penelop.export(document, pretty: true, indent: 2)

Navigating the document

# Access main data
document.version_penelop        # => 1
document.expediteur             # => "ALLIANZ"
document.date_creation          # => Time

# Persons
document.personnes              # => Array<Personne>
personne = document.find_personne('ID123')
personne.nom_patronymique       # => "DUPONT"
personne.prenoms                # => "Jean"
personne.date_naissance         # => Date
personne.age                    # => 45
personne.legal_entity?          # => false

# Contracts
document.contrats               # => Array<Contrat>
contrat = document.find_contrat('CTR001')
contrat.num_contrat             # => "123456789"
contrat.valeur_acquise          # => BigDecimal
contrat.active?                 # => true
contrat.state                   # => :active

# Active contracts only
document.active_contrats        # => Array<Contrat>

# Products
document.produits               # => Array<Produit>
produit = document.find_produit('PRD001')
produit.designation             # => "Multi-support Life Insurance"
produit.multisupport?           # => true

Direct relations

Models have direct access to their relations via belongs_to:

contrat = document.contrats.first

# Direct relation access
contrat.produit                    # => <Produit>
contrat.produit.fournisseur        # => <FournisseurConsomme>
contrat.conseiller                 # => <IntermediaireConsomme>

# Roles and persons
role = contrat.roles.first
role.personne                      # => <Personne>
role.personne.nom_patronymique     # => "DUPONT"

# Supports (fund holdings)
support = contrat.support_composants.first
support.support_info               # => <SupportInfoGenConsomme>
support.support_info.fournisseur   # => <FournisseurConsomme>
support.support_info.categorisation # => <CodeCategorisationSupport>

# Events
evt = contrat.evt_globaux.first
evt.evt_details.first.support_info # => <SupportInfoGenConsomme>

# Basket links
contrat.basket_links.first.contrat # => <Contrat> (linked contract)

Finders

The document exposes find_* methods for ID lookups:

document.find_personne('ID123')
document.find_contrat('CTR001')
document.find_produit('PRD001')
document.find_fournisseur('FRN001')
document.find_intermediaire('INT001')
document.find_support_info('SUP001')
document.find_categorisation_support('CAT001')

Contract roles

Roles can be combined (e.g., 'A S' = Insured + Subscriber):

role = contrat.roles.first

# Role codes
role.code_role          # => "A S"
role.role_codes         # => ["A", "S"]
role.role_types         # => [:insured, :subscriber]
role.combined?          # => true

# Role checks
role.subscriber?        # => true (S)
role.insured?           # => true (A)
role.beneficiary?       # => false (B)
role.full_owner?        # => false (PP - Full ownership)
role.usufructuary?      # => false (U)
role.bare_owner?        # => false (N - Bare ownership)
role.has_role?('S')     # => true

# Available codes
# S  = Subscriber (Souscripteur)
# A  = Insured (Assuré)
# B  = Beneficiary (Bénéficiaire)
# CO = Co-subscriber (Co-souscripteur)
# CA = Co-insured (Co-assuré)
# U  = Usufructuary (Usufruitier)
# N  = Bare owner (Nu-propriétaire)
# PP = Full owner (Pleine Propriété)
# G  = Manager (Gestionnaire)

Events

contrat.evt_globaux.each do |evt|
  evt.code_type_evt_global    # => "VRS" (payment), "RCH" (surrender), etc.
  evt.date_effet_evt          # => Date
  evt.montant_total_brut      # => BigDecimal
  evt.validated?              # => true

  # Details by support
  evt.evt_details.each do |detail|
    detail.nombre_de_parts    # => BigDecimal
    detail.montant_en_euro    # => BigDecimal
    detail.support_info       # => <SupportInfoGenConsomme>
  end
end

Supplementary info

The info_complementaire fields can contain key-value pairs:

# Format: "key1=value1;key2=value2;"
contrat.info_complementaire   # => "type=UC;origin=transfer;"
contrat.parsed_info           # => { type: "UC", origin: "transfer" }

# Automatic type conversion
# "count=42"       => { count: 42 }
# "rate=3.5"       => { rate: BigDecimal("3.5") }
# "active=OUI"     => { active: true }
# "active=NON"     => { active: false }

Statistics

stats = document.statistics
# => {
#   version: 1,
#   date_creation: Time,
#   expediteur: "ALLIANZ",
#   nombre_personnes: 2,
#   nombre_contrats: 1,
#   nombre_produits: 1,
#   valeur_totale: BigDecimal,
#   contrats_actifs: 1
# }

Model structure

Document
├── donnee_reguliere
│   └── destinataire
│       ├── personnes[]
│       └── contrats[]
│           ├── roles[]
│           ├── support_composants[]
│           ├── evt_globaux[]
│           │   └── evt_details[]
│           └── basket_links[]
└── donnee_specifique
    ├── produits[]
    ├── fournisseurs_consommes[]
    ├── intermediaires_consommes[]
    ├── supports_info_gen_consommes[]
    ├── codes_categorisation_support[]
    └── codes_evenement_expediteur[]

Centralized schema

XML ↔ Ruby mappings are defined in Fluence::Penelop::Schema:

# Access mappings
Schema::DOCUMENT      # => { version_penelop: 'version_penelop', ... }
Schema::CONTRAT       # => { id_contrat: 'ID_contrat', ... }

# Invert for parsing
Schema.invert(Schema::DOCUMENT)  # => { 'version_penelop' => :version_penelop, ... }

Supported Penelop versions

This gem supports all 1.x versions of the Penelop format (1.17, 1.21, 1.24, 1.25, etc.).

Development

# Install dependencies
bin/setup

# Interactive console
bin/console

# Run tests
bundle exec rspec

# Local installation
bundle exec rake install

License

MIT License - see LICENSE

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published