cpro-client 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/Gemfile.lock +1 -1
- data/README.md +68 -13
- data/lib/cpro/client.rb +6 -0
- data/lib/cpro/entities/facture.rb +35 -0
- data/lib/cpro/resources/annuaire.rb +25 -13
- data/lib/cpro/suivi.rb +92 -0
- data/lib/cpro/version.rb +1 -1
- data/lib/cpro.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f1452657739318ca5d2a71a6673d046dea03e4f5e3b00befc41ae210fcf3ee4
|
|
4
|
+
data.tar.gz: b36804bf664635e9d5f510fe8a8ed2e52a6aeb1753b9de3bc3b919475d3e51cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 918adeba480ffaaec7508e21e68c31799708e568fb4dc5f2678072ab9f10971f31a03c8af893e57548cce0d5c93b9ad2b9d5387a50a7d629f40f945dfb16b93a
|
|
7
|
+
data.tar.gz: 299ffdbbc8b8c9f25b24c09d1577fdd4e0d1f3b9bc9f3b0eed7c6d607f428e86e87d1a1978bb55c10ab6e450bdb24c996b5909673113031cfb375bb65ab30255
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.2.6] - 2026-09-05
|
|
5
|
+
|
|
6
|
+
### Modifié
|
|
7
|
+
- `Cpro::Resources::Annuaire#find_identifiant_actif` devient **`#find_identifiant_adressage`**, et
|
|
8
|
+
ne filtre plus sur le statut de plateforme : il le fait départager. À proximité égale une adresse
|
|
9
|
+
active l'emporte, mais faute d'adresse active la plus proche est rendue quand même.
|
|
10
|
+
|
|
11
|
+
L'ancien comportement interdisait de facturer un destinataire non raccordé, alors que le dossier
|
|
12
|
+
de spécifications externes pose l'inverse : une telle facture est *déposée*, avec le motif
|
|
13
|
+
`NON_TRANSMISE`, et ce statut « permet à l'entité publique de justifier qu'elle a bien émis une
|
|
14
|
+
facture électronique et qu'elle a donc respecté ses obligations réglementaires ». L'émetteur doit
|
|
15
|
+
remettre un duplicata à son client, mais l'obligation réglementaire est remplie — la gem n'a pas
|
|
16
|
+
à s'y opposer.
|
|
17
|
+
|
|
18
|
+
L'appelant lit `plateforme_active?` sur la ligne rendue pour distinguer les deux cas, et `nil` ne
|
|
19
|
+
signifie plus qu'une chose : aucune adresse à aucun niveau.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [0.2.5] - 2026-09-05
|
|
23
|
+
|
|
24
|
+
### Ajouté
|
|
25
|
+
- `Cpro::Client#suivi` enchaîne les appels de suivi d'un dépôt — statut du flux, recherche de la
|
|
26
|
+
facture, consultation avec son historique — et rend un `Cpro::Suivi` qui nomme l'étape atteinte :
|
|
27
|
+
`:flux_en_attente` (404 du PPF, cas nominal juste après un dépôt), `:flux_irrecevable`,
|
|
28
|
+
`:facture_introuvable` (204, ambigu par nature) ou `:facture_connue`. Il court-circuite dès
|
|
29
|
+
qu'un palier interdit d'aller plus loin : un appel tant que le flux n'est pas traité, trois au
|
|
30
|
+
plus. Il n'attend jamais — la cadence d'interrogation appartient à l'appelant — et laisse
|
|
31
|
+
`statut_flux` et `facture` accessibles pour ce qu'il n'interprète pas.
|
|
32
|
+
- `Cpro::Entities::Facture#dernier_incident`, relayé par `Cpro::Suivi#dernier_incident`, isole le
|
|
33
|
+
dernier changement de statut porteur de motifs — l'incident en cours — là où `motifs_rejet`
|
|
34
|
+
cumule tout l'historique. Il rend le changement et non le motif : un même changement en porte
|
|
35
|
+
plusieurs, une facture « Déposée » pouvant cumuler `NON_TRANSMISE` et `REJ_UNI`.
|
|
36
|
+
- `Cpro::Suivi#motifs` rassemble les motifs de rejet, qu'ils viennent du flux ou de l'historique de
|
|
37
|
+
la facture : l'appelant n'a plus à savoir où le dépôt s'est arrêté pour aller les chercher.
|
|
38
|
+
- `Cpro::Entities::Facture#en_echec?` — vrai dès qu'une facture demande une intervention, que son
|
|
39
|
+
statut l'annonce ou non. Chorus Pro accroche ses motifs de rejet au statut porteur, qui peut
|
|
40
|
+
être anodin : une facture « Déposée » assortie de `NON_TRANSMISE` ne parviendra pas à son
|
|
41
|
+
destinataire alors que `rejetee?` répond faux. Le prédicat couvre les statuts `REJETEE` et
|
|
42
|
+
`ERREUR_ROUTAGE` ainsi que tout motif présent dans l'historique — lequel n'est renseigné que
|
|
43
|
+
par `Factures#find`.
|
|
44
|
+
|
|
45
|
+
|
|
4
46
|
## [0.2.3] - 2026-08-31
|
|
5
47
|
|
|
6
48
|
### Ajouté
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -142,27 +142,40 @@ BT-49, l'adresse électronique de l'acheteur, ne se transmet pas à l'API : elle
|
|
|
142
142
|
Factur-X, et c'est d'elle que le PPF déduit l'acheminement. Une facture qui la porte mal est
|
|
143
143
|
rejetée `REJ_ADR` — « le destinataire n'est pas trouvable » — plusieurs minutes après le dépôt.
|
|
144
144
|
|
|
145
|
-
`
|
|
146
|
-
d'adressage — SIREN, SIRET, SIRET_SERVICE, SIREN_SIRET_SERVICE — et remonte la hiérarchie
|
|
147
|
-
SIREN → SIRET → service
|
|
145
|
+
`find_identifiant_adressage` fait la résolution en un appel. Il accepte les quatre formes d'une
|
|
146
|
+
valeur d'adressage — SIREN, SIRET, SIRET_SERVICE, SIREN_SIRET_SERVICE — et remonte la hiérarchie
|
|
147
|
+
SIREN → SIRET → service pour rendre l'adresse la plus proche que l'annuaire connaisse :
|
|
148
148
|
|
|
149
149
|
```ruby
|
|
150
|
-
ligne = client.annuaire.
|
|
150
|
+
ligne = client.annuaire.find_identifiant_adressage("71915767420316")
|
|
151
151
|
ligne.to_s # => "719157674_71915767420316", à porter en BT-49
|
|
152
|
-
ligne.
|
|
152
|
+
ligne.plateforme_active? # => la facture sera-t-elle transmise ?
|
|
153
153
|
|
|
154
|
-
client.annuaire.
|
|
154
|
+
client.annuaire.find_identifiant_adressage("71915767420316_SERVICE_INEXISTANT").to_s
|
|
155
155
|
# => "719157674_71915767420316" — service inconnu, on retombe sur son parent
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
`
|
|
158
|
+
**Le statut de plateforme départage, il n'exclut pas.** À égalité de proximité, une adresse dont la
|
|
159
|
+
plateforme est active l'emporte ; mais si aucune ne l'est, l'adresse est rendue quand même. Elle
|
|
160
|
+
reste la bonne : une facture destinée à un non-raccordé est bien *déposée*, avec le motif
|
|
161
|
+
`NON_TRANSMISE`, et ce statut « permet à l'entité publique de justifier qu'elle a bien émis une
|
|
162
|
+
facture électronique et qu'elle a donc respecté ses obligations réglementaires ». L'émetteur devra
|
|
163
|
+
remettre un duplicata à son client, mais l'obligation est remplie.
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
L'appelant lit donc `plateforme_active?` sur la ligne rendue pour savoir dans quel cas il est :
|
|
166
|
+
|
|
167
|
+
| Résultat | Ce qui se passera |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| ligne, `plateforme_active?` vrai | la facture est transmise au destinataire |
|
|
170
|
+
| ligne, `plateforme_active?` faux | déposée et conforme, mais non transmise — duplicata à prévoir |
|
|
171
|
+
| `nil` | aucune adresse à aucun niveau : on ne peut pas facturer |
|
|
172
|
+
|
|
173
|
+
Pour distinguer « unité légale inconnue » de « connue mais sans adresse exploitable »,
|
|
174
|
+
`find_lignes_adressage` rend les mêmes lignes sans les départager et laisse remonter la
|
|
175
|
+
`Cpro::NotFoundError`.
|
|
176
|
+
|
|
177
|
+
Une limite à connaître : la résolution ne redescend jamais vers les enfants. Un SIREN nu ne remonte
|
|
178
|
+
rien si la structure n'a d'adresses qu'au niveau SIRET.
|
|
166
179
|
|
|
167
180
|
## Transmission d'une facture Factur-X
|
|
168
181
|
|
|
@@ -254,6 +267,48 @@ aucune facture ne correspond que lorsque le compte technique n'est rattaché ni
|
|
|
254
267
|
callback, et les statuts des factures G2B ne sont pas consultables sur le portail Chorus Pro : le
|
|
255
268
|
suivi passe nécessairement par une interrogation périodique de cette API.
|
|
256
269
|
|
|
270
|
+
## Suivre un dépôt en un appel
|
|
271
|
+
|
|
272
|
+
Les quatre étapes du suivi — statut du flux, recherche par `nomFlux`, consultation, lecture des
|
|
273
|
+
motifs — se réduisent à une méthode, qui nomme l'étape atteinte plutôt que de lever ou de rendre
|
|
274
|
+
`nil` :
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
suivi = client.suivi(depot)
|
|
278
|
+
|
|
279
|
+
suivi.etape # :flux_en_attente, :flux_irrecevable, :facture_introuvable, :facture_connue
|
|
280
|
+
suivi.en_echec? # au niveau de l'enveloppe comme à celui de la facture
|
|
281
|
+
suivi.motifs # les motifs, d'où qu'ils viennent
|
|
282
|
+
suivi.facture # la facture et son historique, à l'étape :facture_connue
|
|
283
|
+
suivi.statut_flux # le statut brut du flux, dès qu'il est connu
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`motifs` cumule tout l'historique — un incident réglé y côtoie l'incident courant. Pour n'avoir que
|
|
287
|
+
le plus récent, avec le statut qui le portait et sa date :
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
incident = suivi.dernier_incident # un ChangementStatut, ou nil
|
|
291
|
+
|
|
292
|
+
incident.statut # "DEPOSEE"
|
|
293
|
+
incident.date # 2026-09-04 19:18:31 UTC
|
|
294
|
+
incident.motifs_rejet.map(&:code) # ["NON_TRANSMISE", "REJ_UNI"]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Il rend le changement et non le motif : un même changement en porte plusieurs, et n'en exposer
|
|
298
|
+
qu'un les masquerait. Il vaut `nil` à l'étape `:flux_irrecevable`, où il n'y a pas encore de
|
|
299
|
+
facture — `motifs` reste alors l'accès uniforme.
|
|
300
|
+
|
|
301
|
+
| Étape | Ce qui s'est passé | Appels | Suite |
|
|
302
|
+
| --- | --- | :-: | --- |
|
|
303
|
+
| `:flux_en_attente` | 404 sur le flux — cas nominal | 1 | réinterroger |
|
|
304
|
+
| `:flux_irrecevable` | enveloppe refusée, motifs sur le flux | 1 | terminal |
|
|
305
|
+
| `:facture_introuvable` | flux recevable, recherche vide | 2 | réinterroger, puis vérifier le rattachement du compte |
|
|
306
|
+
| `:facture_connue` | facture et historique disponibles | 3 | lire `en_echec?` et les motifs |
|
|
307
|
+
|
|
308
|
+
La méthode **n'attend pas** : c'est une photographie, la cadence d'interrogation reste à l'appelant.
|
|
309
|
+
Seuls le 404 du flux et la recherche vide deviennent des étapes ; un 401, un 403 ou un 5xx
|
|
310
|
+
continuent de lever.
|
|
311
|
+
|
|
257
312
|
## Servir plusieurs entités
|
|
258
313
|
|
|
259
314
|
L'AIFE distingue trois types de raccordement API. Celui qui s'applique détermine le modèle à
|
data/lib/cpro/client.rb
CHANGED
|
@@ -19,6 +19,12 @@ module Cpro
|
|
|
19
19
|
self.class.new(configuration, account: account, token_provider: token_provider, connections: connections)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Enchaîne les appels de suivi d'un dépôt et rend une synthèse. Traverse deux ressources,
|
|
23
|
+
# d'où sa place ici plutôt que sur l'une d'elles.
|
|
24
|
+
def suivi(depot)
|
|
25
|
+
Suivi.pour(self, depot)
|
|
26
|
+
end
|
|
27
|
+
|
|
22
28
|
def annuaire
|
|
23
29
|
@annuaire ||= Resources::Annuaire.new(self, connection_for(ANNUAIRE_PATH))
|
|
24
30
|
end
|
|
@@ -10,6 +10,11 @@ module Cpro
|
|
|
10
10
|
CHANGEMENT_DE_COMPTE_A_PAYER NON_AFFACTUREE AFFACTUREE AFFACTUREE_CONFIDENTIEL
|
|
11
11
|
].freeze
|
|
12
12
|
|
|
13
|
+
EPOQUE = Time.at(0).utc
|
|
14
|
+
|
|
15
|
+
# Statuts qui constatent par eux-mêmes qu'une facture n'arrivera pas.
|
|
16
|
+
STATUTS_EN_ECHEC = %w[REJETEE ERREUR_ROUTAGE].freeze
|
|
17
|
+
|
|
13
18
|
CHAMPS = {
|
|
14
19
|
uid: "uidFacture",
|
|
15
20
|
numero: "numeroFacture",
|
|
@@ -77,6 +82,36 @@ module Cpro
|
|
|
77
82
|
historique.flat_map(&:motifs_rejet)
|
|
78
83
|
end
|
|
79
84
|
|
|
85
|
+
# Le dernier changement de statut porteur de motifs — celui qui décrit l'incident en cours,
|
|
86
|
+
# là où `motifs_rejet` cumule tout l'historique, motifs devenus caducs compris.
|
|
87
|
+
#
|
|
88
|
+
# Rend le changement et non le motif : un même changement en porte plusieurs — une facture
|
|
89
|
+
# « Déposée » peut cumuler NON_TRANSMISE et REJ_UNI — et n'en exposer qu'un les masquerait.
|
|
90
|
+
# Le rang départage deux changements de même date, le plus récent l'emportant.
|
|
91
|
+
def dernier_incident
|
|
92
|
+
incidents = historique.select(&:rejet?)
|
|
93
|
+
incidents.each_with_index.max_by { |changement, rang| [changement.date || EPOQUE, rang] }&.first
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Vrai dès qu'une facture demande une intervention, que son statut l'annonce ou non : un
|
|
97
|
+
# motif de rejet peut être porté par un statut anodin. Une facture « Déposée » accompagnée
|
|
98
|
+
# de NON_TRANSMISE en est le cas type — `rejetee?` répond faux, et pourtant le destinataire
|
|
99
|
+
# ne la recevra pas par la plateforme.
|
|
100
|
+
#
|
|
101
|
+
# Attention au mot « échec » : NON_TRANSMISE n'invalide pas la facture. Le dossier de
|
|
102
|
+
# spécifications externes est explicite — ce statut « permet à l'entité publique de
|
|
103
|
+
# justifier qu'elle a bien émis une facture électronique et qu'elle a donc respecté ses
|
|
104
|
+
# obligations réglementaires ». Il n'y a donc rien à redéposer ; il y a à livrer autrement.
|
|
105
|
+
#
|
|
106
|
+
# Le refus de l'acheteur (REFUSEE) n'en fait pas partie : c'est une décision métier, une
|
|
107
|
+
# étape normale du cycle de vie, pas un incident. Elle se lit par `refusee?`.
|
|
108
|
+
#
|
|
109
|
+
# L'historique n'étant renseigné que par `Factures#find`, une facture issue d'une recherche
|
|
110
|
+
# ne peut être jugée que sur son statut.
|
|
111
|
+
def en_echec?
|
|
112
|
+
STATUTS_EN_ECHEC.include?(statut) || historique.any?(&:rejet?)
|
|
113
|
+
end
|
|
114
|
+
|
|
80
115
|
def to_s
|
|
81
116
|
[numero, statut].compact.join(" ")
|
|
82
117
|
end
|
|
@@ -77,23 +77,25 @@ module Cpro
|
|
|
77
77
|
Entities::ResultatRecherche.new(payload, item_class: Entities::CodeRoutage, total_key: TOTAL_KEY)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
# Adresse
|
|
81
|
-
#
|
|
82
|
-
#
|
|
80
|
+
# Adresse à porter en BT-49 pour une valeur d'adressage : la plus proche que l'annuaire
|
|
81
|
+
# connaisse, en privilégiant celles dont la plateforme est active. Rend la ligne d'annuaire,
|
|
82
|
+
# dont `to_s` donne l'identifiant.
|
|
83
83
|
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
84
|
+
# Le statut de plateforme départage, il n'exclut pas. Une adresse sans plateforme reste
|
|
85
|
+
# utilisable : le dossier de spécifications externes pose qu'une facture destinée à un
|
|
86
|
+
# destinataire non raccordé prend le statut « Déposée » avec le motif NON_TRANSMISE, et que
|
|
87
|
+
# ce statut « permet à l'entité publique de justifier qu'elle a bien émis une facture
|
|
88
|
+
# électronique et qu'elle a donc respecté ses obligations réglementaires ». L'émetteur devra
|
|
89
|
+
# remettre un duplicata à son client, mais la facture est valide et l'obligation remplie.
|
|
90
|
+
#
|
|
91
|
+
# L'appelant lit `plateforme_active?` sur la ligne rendue pour savoir dans lequel des deux
|
|
92
|
+
# cas il se trouve. `nil` ne signifie plus qu'une chose : aucune adresse, à aucun niveau.
|
|
87
93
|
#
|
|
88
94
|
# Un seul appel réseau : `GET /siren/code-insee:` retourne aussi les lignes SIRET et service.
|
|
89
|
-
def
|
|
95
|
+
def find_identifiant_adressage(valeur)
|
|
90
96
|
adressage = Adressage.parse(valeur)
|
|
91
|
-
|
|
92
|
-
adressage.
|
|
93
|
-
ligne = actives.find { |active| palier.correspond?(active) }
|
|
94
|
-
return ligne if ligne
|
|
95
|
-
end
|
|
96
|
-
nil
|
|
97
|
+
lignes = find_lignes_adressage(adressage)
|
|
98
|
+
plus_proche(adressage, lignes.select(&:plateforme_active?)) || plus_proche(adressage, lignes)
|
|
97
99
|
rescue NotFoundError
|
|
98
100
|
nil
|
|
99
101
|
end
|
|
@@ -110,6 +112,16 @@ module Cpro
|
|
|
110
112
|
|
|
111
113
|
private
|
|
112
114
|
|
|
115
|
+
# Descend les paliers du plus précis au plus général et rend la première ligne qui
|
|
116
|
+
# corresponde. Appelée deux fois : sur les adresses actives d'abord, sur toutes ensuite.
|
|
117
|
+
def plus_proche(adressage, lignes)
|
|
118
|
+
adressage.paliers.each do |palier|
|
|
119
|
+
ligne = lignes.find { |candidate| palier.correspond?(candidate) }
|
|
120
|
+
return ligne if ligne
|
|
121
|
+
end
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
|
|
113
125
|
def normalize(value, length, label)
|
|
114
126
|
digits = value.to_s.gsub(/\s+/, "")
|
|
115
127
|
return digits if digits.match?(/\A\d{#{length}}\z/)
|
data/lib/cpro/suivi.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cpro
|
|
4
|
+
# Photographie du parcours d'un dépôt, à un instant donné. Elle enchaîne les appels que l'hôte
|
|
5
|
+
# devrait sinon écrire lui-même — statut du flux, recherche de la facture, consultation avec son
|
|
6
|
+
# historique — et s'arrête dès qu'un palier ne permet pas d'aller plus loin.
|
|
7
|
+
#
|
|
8
|
+
# Elle n'attend pas : la cadence d'interrogation appartient à l'appelant. Et elle n'avale rien,
|
|
9
|
+
# `statut_flux` et `facture` restant accessibles pour ce qu'elle n'interprète pas.
|
|
10
|
+
class Suivi
|
|
11
|
+
ETAPES = %i[flux_en_attente flux_irrecevable facture_introuvable facture_connue].freeze
|
|
12
|
+
|
|
13
|
+
attr_reader :etape, :statut_flux, :facture
|
|
14
|
+
|
|
15
|
+
# Un 404 sur le flux et une recherche vide sont des états du parcours, pas des erreurs : ils
|
|
16
|
+
# deviennent des étapes. Tout le reste — 401, 403, 5xx — continue de lever.
|
|
17
|
+
def self.pour(client, depot)
|
|
18
|
+
statut = client.flux.status(depot)
|
|
19
|
+
return new(etape: :flux_en_attente) if statut.nil?
|
|
20
|
+
return new(etape: :flux_irrecevable, statut_flux: statut) unless statut.recevable?
|
|
21
|
+
|
|
22
|
+
facture = premiere_facture(client, statut)
|
|
23
|
+
return new(etape: :facture_introuvable, statut_flux: statut) if facture.nil?
|
|
24
|
+
|
|
25
|
+
new(etape: :facture_connue, statut_flux: statut, facture: facture)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# `find` seul renseigne l'historique, et c'est lui qui porte les motifs.
|
|
29
|
+
def self.premiere_facture(client, statut)
|
|
30
|
+
return nil if statut.nom_flux.nil? || statut.nom_flux.empty?
|
|
31
|
+
|
|
32
|
+
resultat = client.factures.search_by_nom_flux(statut).first
|
|
33
|
+
resultat && client.factures.find(resultat)
|
|
34
|
+
end
|
|
35
|
+
private_class_method :premiere_facture
|
|
36
|
+
|
|
37
|
+
def initialize(etape:, statut_flux: nil, facture: nil)
|
|
38
|
+
raise ArgumentError, "étape inconnue : #{etape.inspect}" unless ETAPES.include?(etape)
|
|
39
|
+
|
|
40
|
+
@etape = etape
|
|
41
|
+
@statut_flux = statut_flux
|
|
42
|
+
@facture = facture
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def en_attente?
|
|
46
|
+
etape == :flux_en_attente
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def irrecevable?
|
|
50
|
+
etape == :flux_irrecevable
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Ambigu par nature : l'API répond 204 aussi bien parce que la facture n'est pas encore
|
|
54
|
+
# indexée que parce que le compte technique n'est rattaché à aucune des deux structures.
|
|
55
|
+
def introuvable?
|
|
56
|
+
etape == :facture_introuvable
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def connue?
|
|
60
|
+
etape == :facture_connue
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Vrai que l'incident soit survenu au niveau de l'enveloppe ou à celui de la facture.
|
|
64
|
+
def en_echec?
|
|
65
|
+
irrecevable? || facture&.en_echec? || false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Les motifs vivent à deux endroits selon l'endroit où le dépôt s'est arrêté : l'appelant n'a
|
|
69
|
+
# pas à savoir lequel.
|
|
70
|
+
def motifs
|
|
71
|
+
return statut_flux.motifs_rejet if irrecevable?
|
|
72
|
+
|
|
73
|
+
facture ? facture.motifs_rejet : []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Le dernier changement de statut porteur de motifs. Nil à l'étape `:flux_irrecevable` : il
|
|
77
|
+
# n'y a alors pas de facture, et les motifs sont portés par le flux — `motifs` reste l'accès
|
|
78
|
+
# uniforme, quelle que soit l'étape atteinte.
|
|
79
|
+
def dernier_incident
|
|
80
|
+
facture&.dernier_incident
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def nom_flux
|
|
84
|
+
statut_flux&.nom_flux
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_s
|
|
88
|
+
[etape, facture&.statut, motifs.map(&:code).join(", ")].reject { |v| v.nil? || v.to_s.empty? }
|
|
89
|
+
.join(" — ")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/cpro/version.rb
CHANGED
data/lib/cpro.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cpro-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hôtentic
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -85,6 +85,7 @@ files:
|
|
|
85
85
|
- lib/cpro/resources/base.rb
|
|
86
86
|
- lib/cpro/resources/factures.rb
|
|
87
87
|
- lib/cpro/resources/flux.rb
|
|
88
|
+
- lib/cpro/suivi.rb
|
|
88
89
|
- lib/cpro/version.rb
|
|
89
90
|
homepage: https://hotentic.com
|
|
90
91
|
licenses:
|