1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
use crate::com::{FromDispatchNew, SafeDispatch, SafeVariant};
use crate::errors::{SageError, SageResult};
use crate::wrappers::cpta::objects::CompteG;
use windows::Win32::System::Com::IDispatch;
/// Types de journal disponibles dans Sage 100c
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum JournalType {
/// Journal des achats
Achat = 0,
/// Journal des ventes
Vente = 1,
/// Journal de trésorerie
Tresorerie = 2,
/// Journal des opérations diverses
OperationsDiverses = 3,
/// Journal des à-nouveaux
ANouveaux = 4,
}
impl JournalType {
/// Convertit une valeur i32 en JournalType
pub fn from_i32(value: i32) -> Option<Self> {
match value {
0 => Some(JournalType::Achat),
1 => Some(JournalType::Vente),
2 => Some(JournalType::Tresorerie),
3 => Some(JournalType::OperationsDiverses),
4 => Some(JournalType::ANouveaux),
_ => None,
}
}
}
/// Wrapper pour l'objet Journal de Sage 100c (IBOJournal3)
#[derive(Debug)]
pub struct Journal {
pub dispatch: IDispatch,
}
impl Journal {
/// Crée un SafeDispatch temporaire pour les appels
fn dispatch(&self) -> SafeDispatch<'_> {
SafeDispatch::new(&self.dispatch)
}
/// Récupère le numéro/code du journal
pub fn jo_num(&self) -> SageResult<String> {
self.dispatch()
.call_method_by_name("JO_Num", &[])?
.to_string()
}
pub fn jo_intitule(&self) -> SageResult<String> {
self.dispatch()
.call_method_by_name("JO_Intitule", &[])?
.to_string()
}
pub fn jo_contrepartie(&self) -> SageResult<bool> {
self.dispatch()
.call_method_by_name("JO_Contrepartie", &[])?
.to_bool()
}
pub fn jo_ifrs(&self) -> SageResult<bool> {
self.dispatch()
.call_method_by_name("JO_IFRS", &[])?
.to_bool()
}
pub fn jo_reglement(&self) -> SageResult<bool> {
self.dispatch()
.call_method_by_name("JO_Reglement", &[])?
.to_bool()
}
pub fn jo_saisi_anal(&self) -> SageResult<bool> {
self.dispatch()
.call_method_by_name("JO_SaisAnal", &[])?
.to_bool()
}
pub fn jo_sommeil(&self) -> SageResult<bool> {
self.dispatch()
.call_method_by_name("JO_Sommeil", &[])?
.to_bool()
}
pub fn jo_type(&self) -> SageResult<i32> {
self.dispatch()
.call_method_by_name("JO_Type", &[])?
.to_i32()
}
/// Accède à l'objet CompteG
/// Retourne directement un CompteG prêt à utiliser
/// Retourne None si le journal n'a pas de compte général associé
pub fn compte_general(&self) -> SageResult<Option<CompteG>> {
let variant = self.dispatch().call_method_by_name("CompteG", &[])?;
// Vérifier si c'est null/empty avant de tenter la conversion
if variant.is_empty_or_null() {
return Ok(None);
}
if !variant.is_object() {
return Err(SageError::ConversionError {
from_type: variant.type_name().to_string(),
to_type: "CompteG".to_string(),
value: format!(
"Propriété CompteG n'est pas un objet COM valide: {}",
variant.type_name()
),
});
}
match variant.to_dispatch() {
Ok(compte_general_dispatch) => Ok(Some(CompteG {
dispatch: compte_general_dispatch,
})),
Err(_) => {
// Si la conversion échoue, c'est probablement null
Ok(None)
}
}
}
pub fn compte_general_raw(&self) -> SageResult<SafeVariant> {
self.dispatch().call_method_by_name("CompteG", &[])
}
/// Retourne le prochain numéro de pièce pour le journal à une date donnée
///
/// # Arguments
/// * `date_periode` - Date pour laquelle obtenir le prochain numéro de pièce (format YYYY-MM-DD)
///
/// # Retour
/// Retourne le prochain numéro de pièce disponible selon les règles de numérotation du journal
///
/// # Exemple
/// ```no_run
/// # use objets_metier_rs::{CptaApplication, SageResult};
/// # fn exemple() -> SageResult<()> {
/// # let app = CptaApplication::new("309DE0FB-9FB8-4F4E-8295-CC60C60DAA33")?;
/// # app.set_name(r"D:\TMP\BIJOU.MAE")?;
/// # let loggable = app.loggable()?;
/// # loggable.set_user_name("<Administrateur>")?;
/// # loggable.set_user_pwd("")?;
/// # app.open()?;
/// let factory_journal = app.factory_journal()?;
/// let journal = factory_journal.read_numero("BQ")?;
///
/// // Obtenir le prochain numéro de pièce pour le 31 décembre 2024
/// let num_piece = journal.next_ec_piece("2024-12-31")?;
/// println!("Prochain n° de pièce : {}", num_piece);
/// # Ok(())
/// # }
/// ```
pub fn next_ec_piece(&self, date_periode: &str) -> SageResult<String> {
let date_variant = SafeVariant::from_string(date_periode);
self.dispatch()
.call_method_by_name("NextEC_Piece", &[date_variant])?
.to_string()
}
// ==================== SETTERS POUR PROPRIÉTÉS ====================
/// Définit le code/numéro du journal
pub fn set_jo_num(&self, num: &str) -> SageResult<()> {
let num_variant = SafeVariant::from_string(num);
self.dispatch()
.call_property_put("JO_Num", &[num_variant])?;
Ok(())
}
/// Définit l'intitulé du journal
pub fn set_jo_intitule(&self, intitule: &str) -> SageResult<()> {
let intitule_variant = SafeVariant::from_string(intitule);
self.dispatch()
.call_property_put("JO_Intitule", &[intitule_variant])?;
Ok(())
}
/// Définit si le journal utilise la contrepartie automatique
pub fn set_jo_contrepartie(&self, contrepartie: bool) -> SageResult<()> {
let contrepartie_variant = SafeVariant::from_bool(contrepartie);
self.dispatch()
.call_property_put("JO_Contrepartie", &[contrepartie_variant])?;
Ok(())
}
/// Définit si le journal est IFRS
pub fn set_jo_ifrs(&self, ifrs: bool) -> SageResult<()> {
let ifrs_variant = SafeVariant::from_bool(ifrs);
self.dispatch()
.call_property_put("JO_IFRS", &[ifrs_variant])?;
Ok(())
}
/// Définit si le journal gère les règlements
pub fn set_jo_reglement(&self, reglement: bool) -> SageResult<()> {
let reglement_variant = SafeVariant::from_bool(reglement);
self.dispatch()
.call_property_put("JO_Reglement", &[reglement_variant])?;
Ok(())
}
/// Définit si le journal permet la saisie analytique
pub fn set_jo_saisi_anal(&self, saisi_anal: bool) -> SageResult<()> {
let saisi_anal_variant = SafeVariant::from_bool(saisi_anal);
self.dispatch()
.call_property_put("JO_SaisAnal", &[saisi_anal_variant])?;
Ok(())
}
/// Définit si le journal est en sommeil
pub fn set_jo_sommeil(&self, sommeil: bool) -> SageResult<()> {
let sommeil_variant = SafeVariant::from_bool(sommeil);
self.dispatch()
.call_property_put("JO_Sommeil", &[sommeil_variant])?;
Ok(())
}
/// Définit le type du journal
/// 0 = Achat, 1 = Vente, 2 = Trésorerie, 3 = OD, 4 = A-nouveaux
pub fn set_jo_type(&self, jo_type: i32) -> SageResult<()> {
let type_variant = SafeVariant::from_i32(jo_type);
self.dispatch()
.call_property_put("JO_Type", &[type_variant])?;
Ok(())
}
/// Définit le type du journal avec l'enum JournalType
pub fn set_jo_type_enum(&self, jo_type: JournalType) -> SageResult<()> {
self.set_jo_type(jo_type as i32)
}
/// Définit le compte général associé au journal
pub fn set_compte_general(&self, compte: &CompteG) -> SageResult<()> {
let compte_variant = SafeVariant::from_dispatch(compte.dispatch.clone());
self.dispatch()
.call_property_put("CompteG", &[compte_variant])?;
Ok(())
}
}
// Implémentation du trait FromDispatchNew pour permettre l'utilisation avec TypedComCollection
impl FromDispatchNew for Journal {
fn from_dispatch_new(dispatch: IDispatch) -> SageResult<Self> {
Ok(Self { dispatch })
}
}