Initial commit: unionflow-mobile-apps

Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 16:30:08 +00:00
commit d094d6db9c
1790 changed files with 507435 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
/// Entité métier Budget
///
/// Représente un budget prévisionnel (mensuel/annuel) avec suivi réalisé
library budget;
import 'package:equatable/equatable.dart';
/// Période du budget
enum BudgetPeriod {
/// Budget mensuel
monthly,
/// Budget trimestriel
quarterly,
/// Budget semestriel
semiannual,
/// Budget annuel
annual,
}
/// Statut du budget
enum BudgetStatus {
/// Brouillon (en cours de création)
draft,
/// Actif
active,
/// Clos (période terminée)
closed,
/// Annulé
cancelled,
}
/// Catégorie budgétaire
enum BudgetCategory {
/// Cotisations/contributions
contributions,
/// Épargne
savings,
/// Solidarité
solidarity,
/// Événements
events,
/// Fonctionnement (frais généraux)
operational,
/// Investissements
investments,
/// Autres
other,
}
/// Entité Budget
class Budget extends Equatable {
final String id;
final String name;
final String? description;
final String organizationId;
final BudgetPeriod period;
final int year;
final int? month;
final BudgetStatus status;
final List<BudgetLine> lines;
final double totalPlanned;
final double totalRealized;
final String currency;
final String createdBy;
final DateTime createdAt;
final DateTime? approvedAt;
final String? approvedBy;
final DateTime startDate;
final DateTime endDate;
final Map<String, dynamic>? metadata;
const Budget({
required this.id,
required this.name,
this.description,
required this.organizationId,
required this.period,
required this.year,
this.month,
required this.status,
this.lines = const [],
required this.totalPlanned,
this.totalRealized = 0,
this.currency = 'XOF',
required this.createdBy,
required this.createdAt,
this.approvedAt,
this.approvedBy,
required this.startDate,
required this.endDate,
this.metadata,
});
/// Taux de réalisation (%)
double get realizationRate {
if (totalPlanned == 0) return 0;
return (totalRealized / totalPlanned) * 100;
}
/// Écart (réalisé - prévu)
double get variance => totalRealized - totalPlanned;
/// Taux d'écart (%)
double get varianceRate {
if (totalPlanned == 0) return 0;
return (variance / totalPlanned) * 100;
}
/// Vérifie si le budget est dépassé
bool get isOverBudget => totalRealized > totalPlanned;
/// Vérifie si le budget est actif
bool get isActive => status == BudgetStatus.active;
/// Vérifie si la période est en cours
bool get isCurrentPeriod {
final now = DateTime.now();
return now.isAfter(startDate) && now.isBefore(endDate);
}
/// Copie avec modifications
Budget copyWith({
String? id,
String? name,
String? description,
String? organizationId,
BudgetPeriod? period,
int? year,
int? month,
BudgetStatus? status,
List<BudgetLine>? lines,
double? totalPlanned,
double? totalRealized,
String? currency,
String? createdBy,
DateTime? createdAt,
DateTime? approvedAt,
String? approvedBy,
DateTime? startDate,
DateTime? endDate,
Map<String, dynamic>? metadata,
}) {
return Budget(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
organizationId: organizationId ?? this.organizationId,
period: period ?? this.period,
year: year ?? this.year,
month: month ?? this.month,
status: status ?? this.status,
lines: lines ?? this.lines,
totalPlanned: totalPlanned ?? this.totalPlanned,
totalRealized: totalRealized ?? this.totalRealized,
currency: currency ?? this.currency,
createdBy: createdBy ?? this.createdBy,
createdAt: createdAt ?? this.createdAt,
approvedAt: approvedAt ?? this.approvedAt,
approvedBy: approvedBy ?? this.approvedBy,
startDate: startDate ?? this.startDate,
endDate: endDate ?? this.endDate,
metadata: metadata ?? this.metadata,
);
}
@override
List<Object?> get props => [
id,
name,
description,
organizationId,
period,
year,
month,
status,
lines,
totalPlanned,
totalRealized,
currency,
createdBy,
createdAt,
approvedAt,
approvedBy,
startDate,
endDate,
metadata,
];
}
/// Ligne budgétaire
class BudgetLine extends Equatable {
final String id;
final BudgetCategory category;
final String name;
final String? description;
final double amountPlanned;
final double amountRealized;
final String? notes;
const BudgetLine({
required this.id,
required this.category,
required this.name,
this.description,
required this.amountPlanned,
this.amountRealized = 0,
this.notes,
});
/// Taux de réalisation (%)
double get realizationRate {
if (amountPlanned == 0) return 0;
return (amountRealized / amountPlanned) * 100;
}
/// Écart
double get variance => amountRealized - amountPlanned;
/// Vérifie si la ligne est dépassée
bool get isOverBudget => amountRealized > amountPlanned;
@override
List<Object?> get props => [
id,
category,
name,
description,
amountPlanned,
amountRealized,
notes,
];
}

View File

@@ -0,0 +1,162 @@
/// Entité métier Audit Log Financier
///
/// Représente une entrée d'audit trail pour traçabilité financière complète
library financial_audit_log;
import 'package:equatable/equatable.dart';
/// Type d'opération auditée
enum AuditOperation {
/// Création
create,
/// Lecture/consultation
read,
/// Mise à jour
update,
/// Suppression
delete,
/// Approbation
approve,
/// Rejet
reject,
/// Validation
validate,
/// Annulation
cancel,
/// Export
export,
}
/// Type d'entité auditée
enum AuditEntityType {
/// Contribution/cotisation
contribution,
/// Transaction épargne
savingsTransaction,
/// Approbation
approval,
/// Budget
budget,
/// Dépense solidarité
solidarity,
/// Paiement
payment,
/// Autre
other,
}
/// Niveau de sévérité
enum AuditSeverity {
/// Information
info,
/// Avertissement
warning,
/// Erreur
error,
/// Critique (anomalie détectée)
critical,
}
/// Entité Audit Log Financier
class FinancialAuditLog extends Equatable {
final String id;
final AuditOperation operation;
final AuditEntityType entityType;
final String entityId;
final String userId;
final String userName;
final String userRole;
final String? organizationId;
final AuditSeverity severity;
final String description;
final Map<String, dynamic>? dataBefore;
final Map<String, dynamic>? dataAfter;
final double? amountBefore;
final double? amountAfter;
final String? ipAddress;
final String? userAgent;
final DateTime timestamp;
final Map<String, dynamic>? metadata;
const FinancialAuditLog({
required this.id,
required this.operation,
required this.entityType,
required this.entityId,
required this.userId,
required this.userName,
required this.userRole,
this.organizationId,
this.severity = AuditSeverity.info,
required this.description,
this.dataBefore,
this.dataAfter,
this.amountBefore,
this.amountAfter,
this.ipAddress,
this.userAgent,
required this.timestamp,
this.metadata,
});
/// Vérifie si c'est une opération de modification de montant
bool get isAmountChanged =>
amountBefore != null &&
amountAfter != null &&
amountBefore != amountAfter;
/// Écart de montant
double? get amountDifference {
if (amountBefore == null || amountAfter == null) return null;
return amountAfter! - amountBefore!;
}
/// Vérifie si c'est une anomalie critique
bool get isCritical => severity == AuditSeverity.critical;
/// Vérifie si c'est une opération sensible
bool get isSensitiveOperation =>
operation == AuditOperation.delete ||
operation == AuditOperation.approve ||
operation == AuditOperation.reject ||
operation == AuditOperation.cancel;
@override
List<Object?> get props => [
id,
operation,
entityType,
entityId,
userId,
userName,
userRole,
organizationId,
severity,
description,
dataBefore,
dataAfter,
amountBefore,
amountAfter,
ipAddress,
userAgent,
timestamp,
metadata,
];
}

View File

@@ -0,0 +1,241 @@
/// Entité métier Approbation Transaction
///
/// Représente une approbation dans le workflow financier multi-niveaux
library transaction_approval;
import 'package:equatable/equatable.dart';
/// Statut de l'approbation
enum ApprovalStatus {
/// En attente d'approbation
pending,
/// Approuvée (niveau 1)
approved,
/// Validée (niveau 2 - validation finale)
validated,
/// Rejetée
rejected,
/// Expirée (timeout)
expired,
/// Annulée
cancelled,
}
/// Niveau d'approbation requis selon montant
enum ApprovalLevel {
/// Aucune approbation requise (< seuil)
none,
/// Niveau 1 : Un approbateur requis
level1,
/// Niveau 2 : Deux approbateurs requis
level2,
/// Niveau 3 : Trois approbateurs requis (montants très élevés)
level3,
}
/// Type de transaction financière
enum TransactionType {
/// Cotisation/contribution
contribution,
/// Dépôt épargne
deposit,
/// Retrait épargne
withdrawal,
/// Transfert
transfer,
/// Dépense solidarité
solidarity,
/// Dépense événement
event,
/// Autre dépense
other,
}
/// Entité Approbation de Transaction
class TransactionApproval extends Equatable {
final String id;
final String transactionId;
final TransactionType transactionType;
final double amount;
final String currency;
final String requesterId;
final String requesterName;
final String? organizationId;
final ApprovalLevel requiredLevel;
final ApprovalStatus status;
final List<ApproverAction> approvers;
final String? rejectionReason;
final DateTime createdAt;
final DateTime? expiresAt;
final DateTime? completedAt;
final Map<String, dynamic>? metadata;
const TransactionApproval({
required this.id,
required this.transactionId,
required this.transactionType,
required this.amount,
this.currency = 'XOF',
required this.requesterId,
required this.requesterName,
this.organizationId,
required this.requiredLevel,
required this.status,
this.approvers = const [],
this.rejectionReason,
required this.createdAt,
this.expiresAt,
this.completedAt,
this.metadata,
});
/// Vérifie si l'approbation est en attente
bool get isPending => status == ApprovalStatus.pending;
/// Vérifie si l'approbation est complétée
bool get isCompleted =>
status == ApprovalStatus.validated ||
status == ApprovalStatus.rejected ||
status == ApprovalStatus.cancelled;
/// Vérifie si l'approbation est expirée
bool get isExpired {
if (expiresAt == null) return false;
return DateTime.now().isAfter(expiresAt!);
}
/// Nombre d'approbations reçues
int get approvalCount =>
approvers.where((a) => a.decision == ApprovalDecision.approved).length;
/// Nombre d'approbations requises
int get requiredApprovals {
switch (requiredLevel) {
case ApprovalLevel.none:
return 0;
case ApprovalLevel.level1:
return 1;
case ApprovalLevel.level2:
return 2;
case ApprovalLevel.level3:
return 3;
}
}
/// Vérifie si toutes les approbations sont reçues
bool get hasAllApprovals => approvalCount >= requiredApprovals;
/// Copie avec modifications
TransactionApproval copyWith({
String? id,
String? transactionId,
TransactionType? transactionType,
double? amount,
String? currency,
String? requesterId,
String? requesterName,
String? organizationId,
ApprovalLevel? requiredLevel,
ApprovalStatus? status,
List<ApproverAction>? approvers,
String? rejectionReason,
DateTime? createdAt,
DateTime? expiresAt,
DateTime? completedAt,
Map<String, dynamic>? metadata,
}) {
return TransactionApproval(
id: id ?? this.id,
transactionId: transactionId ?? this.transactionId,
transactionType: transactionType ?? this.transactionType,
amount: amount ?? this.amount,
currency: currency ?? this.currency,
requesterId: requesterId ?? this.requesterId,
requesterName: requesterName ?? this.requesterName,
organizationId: organizationId ?? this.organizationId,
requiredLevel: requiredLevel ?? this.requiredLevel,
status: status ?? this.status,
approvers: approvers ?? this.approvers,
rejectionReason: rejectionReason ?? this.rejectionReason,
createdAt: createdAt ?? this.createdAt,
expiresAt: expiresAt ?? this.expiresAt,
completedAt: completedAt ?? this.completedAt,
metadata: metadata ?? this.metadata,
);
}
@override
List<Object?> get props => [
id,
transactionId,
transactionType,
amount,
currency,
requesterId,
requesterName,
organizationId,
requiredLevel,
status,
approvers,
rejectionReason,
createdAt,
expiresAt,
completedAt,
metadata,
];
}
/// Décision d'un approbateur
enum ApprovalDecision {
/// En attente
pending,
/// Approuvé
approved,
/// Rejeté
rejected,
}
/// Action d'un approbateur
class ApproverAction extends Equatable {
final String approverId;
final String approverName;
final String approverRole;
final ApprovalDecision decision;
final String? comment;
final DateTime? decidedAt;
const ApproverAction({
required this.approverId,
required this.approverName,
required this.approverRole,
required this.decision,
this.comment,
this.decidedAt,
});
@override
List<Object?> get props => [
approverId,
approverName,
approverRole,
decision,
comment,
decidedAt,
];
}