feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -0,0 +1,100 @@
/// Model de données Budget avec sérialisation JSON
library budget_model;
import 'package:json_annotation/json_annotation.dart';
import '../../domain/entities/budget.dart';
part 'budget_model.g.dart';
@JsonSerializable(explicitToJson: true)
class BudgetModel extends Budget {
@JsonKey(
fromJson: _linesFromJson,
toJson: _linesToJson,
)
@override
final List<BudgetLine> lines;
const BudgetModel({
required super.id,
required super.name,
super.description,
required super.organizationId,
required super.period,
required super.year,
super.month,
required super.status,
this.lines = const [],
required super.totalPlanned,
super.totalRealized,
super.currency,
required super.createdBy,
required super.createdAt,
super.approvedAt,
super.approvedBy,
required super.startDate,
required super.endDate,
super.metadata,
}) : super(lines: lines);
static List<BudgetLine> _linesFromJson(List<dynamic>? json) =>
json?.map((e) => BudgetLineModel.fromJson(e as Map<String, dynamic>)).toList() ?? [];
static List<Map<String, dynamic>> _linesToJson(List<BudgetLine>? lines) =>
lines?.map((l) => BudgetLineModel(
id: l.id,
category: l.category,
name: l.name,
description: l.description,
amountPlanned: l.amountPlanned,
amountRealized: l.amountRealized,
notes: l.notes,
).toJson()).toList() ?? [];
factory BudgetModel.fromJson(Map<String, dynamic> json) =>
_$BudgetModelFromJson(json);
Map<String, dynamic> toJson() => _$BudgetModelToJson(this);
factory BudgetModel.fromEntity(Budget entity) {
return BudgetModel(
id: entity.id,
name: entity.name,
description: entity.description,
organizationId: entity.organizationId,
period: entity.period,
year: entity.year,
month: entity.month,
status: entity.status,
lines: entity.lines,
totalPlanned: entity.totalPlanned,
totalRealized: entity.totalRealized,
currency: entity.currency,
createdBy: entity.createdBy,
createdAt: entity.createdAt,
approvedAt: entity.approvedAt,
approvedBy: entity.approvedBy,
startDate: entity.startDate,
endDate: entity.endDate,
metadata: entity.metadata,
);
}
}
@JsonSerializable()
class BudgetLineModel extends BudgetLine {
const BudgetLineModel({
required super.id,
required super.category,
required super.name,
super.description,
required super.amountPlanned,
super.amountRealized,
super.notes,
});
factory BudgetLineModel.fromJson(Map<String, dynamic> json) =>
_$BudgetLineModelFromJson(json);
Map<String, dynamic> toJson() => _$BudgetLineModelToJson(this);
}

View File

@@ -0,0 +1,92 @@
/// Model de données TransactionApproval avec sérialisation JSON
library transaction_approval_model;
import 'package:json_annotation/json_annotation.dart';
import '../../domain/entities/transaction_approval.dart';
part 'transaction_approval_model.g.dart';
@JsonSerializable(explicitToJson: true)
class TransactionApprovalModel extends TransactionApproval {
@JsonKey(
fromJson: _approversFromJson,
toJson: _approversToJson,
)
@override
final List<ApproverAction> approvers;
const TransactionApprovalModel({
required super.id,
required super.transactionId,
required super.transactionType,
required super.amount,
super.currency,
required super.requesterId,
required super.requesterName,
super.organizationId,
required super.requiredLevel,
required super.status,
this.approvers = const [],
super.rejectionReason,
required super.createdAt,
super.expiresAt,
super.completedAt,
super.metadata,
}) : super(approvers: approvers);
static List<ApproverAction> _approversFromJson(List<dynamic>? json) =>
json?.map((e) => ApproverActionModel.fromJson(e as Map<String, dynamic>)).toList() ?? [];
static List<Map<String, dynamic>> _approversToJson(List<ApproverAction>? approvers) =>
approvers?.map((a) => ApproverActionModel(
approverId: a.approverId,
approverName: a.approverName,
approverRole: a.approverRole,
decision: a.decision,
comment: a.comment,
decidedAt: a.decidedAt,
).toJson()).toList() ?? [];
factory TransactionApprovalModel.fromJson(Map<String, dynamic> json) =>
_$TransactionApprovalModelFromJson(json);
Map<String, dynamic> toJson() => _$TransactionApprovalModelToJson(this);
factory TransactionApprovalModel.fromEntity(TransactionApproval entity) {
return TransactionApprovalModel(
id: entity.id,
transactionId: entity.transactionId,
transactionType: entity.transactionType,
amount: entity.amount,
currency: entity.currency,
requesterId: entity.requesterId,
requesterName: entity.requesterName,
organizationId: entity.organizationId,
requiredLevel: entity.requiredLevel,
status: entity.status,
approvers: entity.approvers,
rejectionReason: entity.rejectionReason,
createdAt: entity.createdAt,
expiresAt: entity.expiresAt,
completedAt: entity.completedAt,
metadata: entity.metadata,
);
}
}
@JsonSerializable()
class ApproverActionModel extends ApproverAction {
const ApproverActionModel({
required super.approverId,
required super.approverName,
required super.approverRole,
required super.decision,
super.comment,
super.decidedAt,
});
factory ApproverActionModel.fromJson(Map<String, dynamic> json) =>
_$ApproverActionModelFromJson(json);
Map<String, dynamic> toJson() => _$ApproverActionModelToJson(this);
}