V2.0: Refonte système bancaire - DB SQLite, RIB FR, TVA auto, multi-banques

- Remplace JSON par SQLite (aiosqlite) pour atomicité et intégrité
- Nouveau format RIB français avec checksum modulo 97
- Chaque banque a son propre rôle Discord (isolation par serveur)
- TVA automatique Particulier -> Entreprise, redirection au Fisc
- Institutions gouvernementales avec droit d'audit optionnel
- Commandes: /gouv, /banque, /audit, /mes-ribs
- Supprime: 16 anciens fichiers (entreprises, FBM, URSAFF, etc.)
- Reset complet des données, repart de zéro
This commit is contained in:
utilisateurqu 2026-08-18 17:42:03 +02:00
parent 2e38d05152
commit c9ad155bf6
38 changed files with 3689 additions and 5106 deletions

View file

@ -33,7 +33,7 @@ permissions.update(
)
# Initialisation du bot
bot = commands.Bot(command_prefix="!", application_id="1310235313461002241", intents=intents)
bot = commands.Bot(command_prefix="!", application_id="1337733859814543410", intents=intents)
async def load_cogs():
"""Charge toutes les commandes."""
@ -42,38 +42,25 @@ async def load_cogs():
"commandes.identite",
"commandes.voir_carte_identite",
"commandes.virement",
"commandes.transactions",
"commandes.ajouter_argent",
"commandes.compte",
"commandes.mes_ribs",
"commandes.gouv",
"commandes.banque",
"commandes.audit",
"commandes.ticket",
"commandes.restranscription_staff",
"utils.welcome_message",
"commandes.permis",
"commandes.ouvrir_compte",
"commandes.compte",
"commandes.change_nickname",
"commandes.rename_all_on_join",
"commandes.message_tous",
"commandes.retirer_argent",
"commandes.ouvrir_comptes_entreprises",
"commandes.compte_gouvernement",
"commandes.comptes_entreprises",
"commandes.compte_urssaf",
"commandes.compte_banque_nationale",
"commandes.virement_entreprise",
"commandes.virement_gouvernement",
"commandes.valider_visite_médicale",
"commandes.abonnement",
"commandes.virement_banquier",
"commandes.virement_banquier_entreprise",
"commandes.compte_fbm",
"commandes.virement_fbm",
"commandes.virement_ursaff",
"commandes.stats_commandes",
"commandes.roll",
"commandes.annonces_sessions",
"commandes.retrait_gouv",
"commandes.counting",
"commandes.config"
"commandes.config",
"commandes.panel"
]
for ext in extensions:
@ -89,7 +76,7 @@ async def on_ready():
log_info(f"Connecté en tant que {bot.user}")
await load_cogs()
await bot.tree.sync()
await bot.change_presence(activity=discord.Game(name="la V1.8")) # Ajout du status
await bot.change_presence(activity=discord.Game(name="la V2.0"))
log_info("Commandes synchronisées.")
# Gère les erreurs de commandes classiques

View file

@ -1,218 +0,0 @@
[
{
"nom": "Test",
"rib_emetteur": "22144 59424 87742058459 72",
"rib_receveur": "66427 49104 57218570654 78",
"montant": 1.0,
"banque": "NGC Bank",
"jour": "samedi"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "36495 80083 39542816185 27",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "26384 93370 75890178377 91",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "22144 59424 87742058459 72",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "85047 60626 80894743346 94",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "28777 24580 29765721710 42",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "24483 98056 61791340797 80",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "10009 40554 56452790470 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "76644 32901 90493249064 16",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "15563 69899 51361446295 84",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "22128 86524 35982837421 58",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "55868 11389 89049343014 38",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "93266 82343 74845298309 22",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "10234 24218 10477731114 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "50970 51336 33017058086 50",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "87292 81104 84784268868 12",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "51229 80290 86979675797 93",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "28794 77878 41312953164 71",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "78740 15751 70195185799 30",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "92871 52823 19649316403 70",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frais_bancaire_et_de_gestion_1",
"rib_emetteur": "73834 54818 11833486819 17",
"rib_receveur": "15156 38436 35920 54357",
"montant": 5.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Frog",
"rib_emetteur": "48003 84424 36961155485 64",
"rib_receveur": "15156 38436 35920 54357",
"montant": 700.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Di regia",
"rib_emetteur": "10009 40554 56452790470 78",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4000.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Leonardo",
"rib_emetteur": "24756 54403 68357307735 95",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4000.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Correa",
"rib_emetteur": "28794 77878 41312953164 71",
"rib_receveur": "15156 38436 35920 54357",
"montant": 4300.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "jose",
"rib_emetteur": "87292 81104 84784268868 12",
"rib_receveur": "15156 38436 35920 54357",
"montant": 800.0,
"banque": "NGC Bank",
"jour": "dimanche"
},
{
"nom": "Paye_de_Salvatore_Di_Regia",
"rib_emetteur": "15156 38436 35920 54357",
"rib_receveur": "10009 40554 56452790470 78",
"montant": 5000.0,
"banque": "NGC Bank",
"jour": "dimanche"
}
]

View file

@ -1 +0,0 @@
{}

View file

@ -1,148 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
import json
import datetime
ABONNEMENTS_FILE = "abonnements.json"
BANQUES_DISPONIBLES = [
"Victoria's Credit Bank",
"NGC Bank"
]
JOURS_SEMAINE = [
"lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"
]
class Abonnement(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.abonnements = self.load_abonnements()
self.executer_abonnements.start()
def load_abonnements(self):
try:
with open(ABONNEMENTS_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_abonnements(self):
with open(ABONNEMENTS_FILE, 'w', encoding='utf-8') as f:
json.dump(self.abonnements, f, ensure_ascii=False, indent=4)
# 🎯 Auto-complétion pour les banques
async def autocomplete_banque(self, interaction: discord.Interaction, current: str):
return [
app_commands.Choice(name=b, value=b)
for b in BANQUES_DISPONIBLES if current.lower() in b.lower()
]
# 🎯 Auto-complétion pour les jours
async def autocomplete_jour(self, interaction: discord.Interaction, current: str):
return [
app_commands.Choice(name=day.capitalize(), value=day)
for day in JOURS_SEMAINE if current.lower() in day
]
@app_commands.command(name="abonnement", description="Crée un abonnement bancaire hebdomadaire.")
@app_commands.describe(
nom="Nom de l'abonnement",
rib_emetteur="RIB de l'expéditeur",
montant="Montant à envoyer",
rib_receveur="RIB du bénéficiaire",
banque="Nom de la banque",
jour="Jour de la semaine du virement"
)
@app_commands.autocomplete(banque=autocomplete_banque, jour=autocomplete_jour)
async def abonnement(
self,
interaction: discord.Interaction,
nom: str,
rib_emetteur: str,
montant: float,
rib_receveur: str,
banque: str,
jour: str
):
if jour.lower() not in JOURS_SEMAINE:
await interaction.response.send_message("❌ Jour invalide. Choisissez un jour valide de la semaine.", ephemeral=True)
return
abonnement = {
"nom": nom,
"rib_emetteur": rib_emetteur,
"rib_receveur": rib_receveur,
"montant": montant,
"banque": banque,
"jour": jour.lower()
}
self.abonnements.append(abonnement)
self.save_abonnements()
await interaction.response.send_message(f"✅ Abonnement **{nom}** enregistré. Virement automatique tous les **{jour}**.", ephemeral=True)
@tasks.loop(minutes=60)
async def executer_abonnements(self):
today = datetime.datetime.now().strftime("%A").lower()
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except:
comptes = {}
updated = False
for abonnement in self.abonnements:
if abonnement["jour"] != today:
continue
banque = abonnement["banque"]
rib_emetteur = abonnement["rib_emetteur"]
rib_receveur = abonnement["rib_receveur"]
montant = abonnement["montant"]
destinataire_id = None
emetteur_id = None
for user_id, data in comptes.get(banque, {}).items():
if data.get("rib") == rib_emetteur:
emetteur_id = user_id
if data.get("rib") == rib_receveur:
destinataire_id = user_id
if not emetteur_id or not destinataire_id:
continue
if comptes[banque][emetteur_id]["solde"] < montant:
continue
comptes[banque][emetteur_id]["solde"] -= montant
comptes[banque][emetteur_id]["transactions"].append({
"date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement automatique (abonnement)",
"source": f"Vers RIB {rib_receveur}",
"montant": -montant
})
comptes[banque][destinataire_id]["solde"] += montant
comptes[banque][destinataire_id]["transactions"].append({
"date": datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu (abonnement)",
"source": f"{rib_emetteur}",
"montant": montant
})
updated = True
if updated:
with open("comptes_bancaires.json", 'w', encoding='utf-8') as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
@executer_abonnements.before_loop
async def before_abonnement_loop(self):
await self.bot.wait_until_ready()
async def setup(bot):
await bot.add_cog(Abonnement(bot))

View file

@ -1,145 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
from utils.guild_config import has_role
class AjouterArgent(commands.Cog):
def __init__(self, bot):
self.bot = bot
# Choix de banques automatiques
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="ajouter_argent",
description="Ajoute de l'argent à un compte (réservé aux banquiers)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB du compte",
montant="Montant à ajouter",
banque="Banque cible (facultatif)"
)
async def ajouter_argent(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
# Vérification du rôle
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message(
"❌ Vous n'avez pas la permission d'utiliser cette commande.",
ephemeral=True
)
return
if montant <= 0:
await interaction.response.send_message(
"❌ Le montant doit être supérieur à zéro.",
ephemeral=True
)
return
banque_nom = banque.value if banque else None
# Chargement des fichiers
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
cible_data = None
user_id = None
is_entreprise = False
# Recherche dans la banque spécifiée (si précisée)
if banque_nom and banque_nom in comptes:
for uid, info in comptes[banque_nom].items():
if info.get("rib") == rib:
cible_data = comptes
user_id = uid
break
if not user_id and banque_nom and banque_nom in entreprises:
for nom_entreprise, info in entreprises[banque_nom].items():
if info.get("rib") == rib:
cible_data = entreprises
user_id = nom_entreprise
is_entreprise = True
break
# Si banque non précisée ou compte non trouvé, recherche dans toutes les banques
if not user_id and not banque_nom:
# Recherche dans toutes les banques utilisateurs
for banque_courante, comptes_banque in comptes.items():
for uid, info in comptes_banque.items():
if info.get("rib") == rib:
cible_data = comptes
user_id = uid
banque_nom = banque_courante
break
if user_id:
break
if not user_id and not is_entreprise:
# Recherche dans toutes les banques entreprises
for banque_courante, entreprises_banque in entreprises.items():
for nom_entreprise, info in entreprises_banque.items():
if info.get("rib") == rib:
cible_data = entreprises
user_id = nom_entreprise
banque_nom = banque_courante
is_entreprise = True
break
if user_id:
break
if not user_id:
await interaction.response.send_message(
"❌ Aucun compte trouvé avec ce RIB.",
ephemeral=True
)
return
# Ajout du montant
cible_data[banque_nom][user_id]["solde"] += montant
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Dépôt",
"source": "Banque",
"montant": montant,
"emetteur": interaction.user.id,
"destinataire": user_id
}
cible_data[banque_nom][user_id]["transactions"].append(transaction)
# Sauvegarde
if is_entreprise:
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
else:
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
# Envoyer un MP à l'utilisateur (sauf entreprise)
try:
user = await self.bot.fetch_user(int(user_id))
await user.send(
f"💰 Un dépôt de **{montant:.2f}€** a été effectué sur votre compte dans **{banque_nom}**."
)
except Exception:
pass # Si l'utilisateur a désactivé les MP
await interaction.response.send_message(
f"✅ **{montant:.2f}€** ajoutés avec succès au compte `{rib}` dans la banque **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(AjouterArgent(bot))

75
commandes/audit.py Normal file
View file

@ -0,0 +1,75 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.guild_config import has_role, get_guild_role
from utils.bank_manager import BankManager
bank_manager = BankManager()
class Audit(commands.Cog):
def __init__(self, bot):
self.bot = bot
audit_group = app_commands.Group(name="audit", description="Audit bancaire (institutions)")
@audit_group.command(name="consulter-banque", description="Consulter les comptes d'une banque (institutions avec audit)")
@app_commands.describe(nom_banque="Nom de la banque à auditer")
async def consulter_banque(self, interaction: discord.Interaction, nom_banque: str):
user_roles = [r.id for r in interaction.user.roles]
institutions = await bank_manager.lister_institutions(interaction.guild.id)
authorized = None
for inst in institutions:
if inst["role_id"] and inst["role_id"] in user_roles and inst["can_audit_all"]:
authorized = inst
break
if not authorized:
await interaction.response.send_message(
"❌ Vous n'avez pas le droit d'audit. Réservé aux institutions avec le droit d'audit activé.",
ephemeral=True,
)
return
banque = await bank_manager.get_banque_by_name(interaction.guild.id, nom_banque)
if not banque:
banques = await bank_manager.lister_banques(interaction.guild.id)
noms = ", ".join(b["name"] for b in banques) or "aucune"
await interaction.response.send_message(
f"❌ Banque **{nom_banque}** introuvable. Banques disponibles : {noms}",
ephemeral=True,
)
return
comptes = await bank_manager.get_comptes_banque(interaction.guild.id, banque["id"])
if not comptes:
await interaction.response.send_message("Aucun compte dans cette banque.", ephemeral=True)
return
embed = discord.Embed(
title=f"🔍 Audit : {banque['name']}",
description=f"Effectué par l'institution **{authorized['name']}**",
color=discord.Color.orange(),
)
for c in comptes:
member = interaction.guild.get_member(c["user_id"])
name = member.display_name if member else f"User#{c['user_id']}"
tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else ""
embed.add_field(
name=f"{name} ({c['account_type']})",
value=(
f"**RIB :** `{c['rib']}`\n"
f"**Solde :** `{c['solde']:.2f}€`{tva_text}"
),
inline=False,
)
embed.set_footer(text=f"Total : {len(comptes)} compte(s) | Réserve banque : {banque['vault_balance']:.2f}€")
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(Audit(bot))

245
commandes/banque.py Normal file
View file

@ -0,0 +1,245 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.guild_config import has_role, get_guild_role
from utils.bank_manager import BankManager
from utils.rib import decoder_rib
bank_manager = BankManager()
class Banque(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_group = app_commands.Group(name="banque", description="Opérations bancaires")
async def _get_user_bank(self, interaction: discord.Interaction) -> dict:
banques = await bank_manager.lister_banques(interaction.guild.id)
for b in banques:
role = interaction.guild.get_role(b["role_id"])
if role and role in interaction.user.roles:
return b
return None
@banque_group.command(name="ouvrir-compte", description="Ouvrir un compte pour un membre")
@app_commands.describe(
membre="Le membre qui recevra le compte",
type_compte="Type de compte",
taux_tva="Taux TVA (entreprises uniquement, défaut 0%)"
)
@app_commands.choices(
type_compte=[
app_commands.Choice(name="Particulier", value="particulier"),
app_commands.Choice(name="Entreprise", value="entreprise"),
]
)
async def ouvrir_compte(
self,
interaction: discord.Interaction,
membre: discord.Member,
type_compte: app_commands.Choice[str],
taux_tva: float = 0.0,
):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
if type_compte.value == "entreprise" and taux_tva < 0:
await interaction.response.send_message("❌ Le taux TVA ne peut pas être négatif.", ephemeral=True)
return
try:
result = await bank_manager.ouvrir_compte(
guild_id=interaction.guild.id,
bank_id=bank["id"],
user_id=membre.id,
account_type=type_compte.value,
tva_rate=taux_tva if type_compte.value == "entreprise" else 0.0,
)
embed = discord.Embed(
title="💳 Compte ouvert",
description=(
f"**Banque :** {bank['name']}\n"
f"**Membre :** {membre.mention}\n"
f"**Type :** {type_compte.value.title()}\n"
f"**RIB :** `{result['rib']}`\n"
),
color=discord.Color.green(),
)
if type_compte.value == "entreprise":
embed.add_field(name="Taux TVA", value=f"{taux_tva}%")
await interaction.response.send_message(embed=embed, ephemeral=True)
try:
await membre.send(
f"💳 Votre compte **{type_compte.value}** chez **{bank['name']}** a été ouvert.\n"
f"**RIB :** `{result['rib']}`"
)
except discord.Forbidden:
pass
except ValueError as e:
await interaction.response.send_message(f"❌ {e}", ephemeral=True)
@banque_group.command(name="modifier-tva", description="Modifier le taux TVA d'une entreprise")
@app_commands.describe(
rib="RIB de l'entreprise",
nouveau_taux="Nouveau taux TVA (ex: 5.5, 20)"
)
async def modifier_tva(self, interaction: discord.Interaction, rib: str, nouveau_taux: float):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
if nouveau_taux < 0 or nouveau_taux > 100:
await interaction.response.send_message("❌ Le taux doit être entre 0 et 100%.", ephemeral=True)
return
compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib)
if not compte:
await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True)
return
if compte["bank_id"] != bank["id"]:
await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True)
return
if compte["account_type"] != "entreprise":
await interaction.response.send_message("❌ La TVA ne s'applique qu'aux comptes entreprise.", ephemeral=True)
return
ok = await bank_manager.modifier_tva(interaction.guild.id, rib, nouveau_taux)
if ok:
await interaction.response.send_message(
f"✅ Taux TVA mis à jour : **{nouveau_taux}%** pour `{rib}`.", ephemeral=True
)
else:
await interaction.response.send_message("❌ Erreur lors de la mise à jour.", ephemeral=True)
@banque_group.command(name="consulter-clients", description="Voir tous les comptes de votre banque")
async def consulter_clients(self, interaction: discord.Interaction):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
comptes = await bank_manager.get_comptes_banque(interaction.guild.id, bank["id"])
if not comptes:
await interaction.response.send_message("Aucun compte dans cette banque.", ephemeral=True)
return
embed = discord.Embed(
title=f"🏦 Clients de {bank['name']}",
color=discord.Color.blue(),
)
for c in comptes:
member = interaction.guild.get_member(c["user_id"])
name = member.display_name if member else f"<@{c['user_id']}>"
tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else ""
embed.add_field(
name=f"{name} ({c['account_type']})",
value=(
f"**RIB :** `{c['rib']}`\n"
f"**Solde :** `{c['solde']:.2f}€`{tva_text}"
),
inline=False,
)
embed.set_footer(text=f"{len(comptes)} compte(s)")
await interaction.response.send_message(embed=embed, ephemeral=True)
@banque_group.command(name="info", description="Informations sur votre banque")
async def info_banque(self, interaction: discord.Interaction):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
role = interaction.guild.get_role(bank["role_id"])
comptes = await bank_manager.get_comptes_banque(interaction.guild.id, bank["id"])
nb_particuliers = sum(1 for c in comptes if c["account_type"] == "particulier")
nb_entreprises = sum(1 for c in comptes if c["account_type"] == "entreprise")
embed = discord.Embed(
title=f"🏦 {bank['name']}",
color=discord.Color.blue(),
)
embed.add_field(name="Rôle", value=role.mention if role else "Inconnu", inline=True)
embed.add_field(name="Code établissement", value=f"`{bank['establishment_code']}`", inline=True)
embed.add_field(name="Solde propre (réserve)", value=f"`{bank['vault_balance']:.2f}€`", inline=True)
embed.add_field(name="Encours clients", value=f"`{bank['clients_total_balance']:.2f}€`", inline=True)
embed.add_field(name="Comptes particuliers", value=str(nb_particuliers), inline=True)
embed.add_field(name="Comptes entreprises", value=str(nb_entreprises), inline=True)
await interaction.response.send_message(embed=embed, ephemeral=True)
@banque_group.command(name="depot", description="Déposer de l'argent sur un compte")
@app_commands.describe(rib="RIB du compte", montant="Montant à déposer")
async def depot(self, interaction: discord.Interaction, rib: str, montant: float):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib)
if not compte:
await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True)
return
if compte["bank_id"] != bank["id"]:
await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True)
return
try:
result = await bank_manager.depot_banquier(interaction.guild.id, rib, montant, interaction.user.id)
await interaction.response.send_message(
f"✅ Dépôt de **{montant:.2f}€** sur `{rib}`. Nouveau solde : **{result['nouveau_solde']:.2f}€**",
ephemeral=True,
)
except ValueError as e:
await interaction.response.send_message(f"❌ {e}", ephemeral=True)
@banque_group.command(name="retrait", description="Retirer de l'argent d'un compte")
@app_commands.describe(rib="RIB du compte", montant="Montant à retirer")
async def retrait(self, interaction: discord.Interaction, rib: str, montant: float):
bank = await self._get_user_bank(interaction)
if not bank:
await interaction.response.send_message(
"❌ Vous n'avez pas de rôle de banquier sur ce serveur.", ephemeral=True
)
return
compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib)
if not compte:
await interaction.response.send_message("❌ Compte introuvable.", ephemeral=True)
return
if compte["bank_id"] != bank["id"]:
await interaction.response.send_message("❌ Ce RIB n'appartient pas à votre banque.", ephemeral=True)
return
try:
result = await bank_manager.retrait_banquier(interaction.guild.id, rib, montant, interaction.user.id)
await interaction.response.send_message(
f"✅ Retrait de **{montant:.2f}€** sur `{rib}`. Nouveau solde : **{result['nouveau_solde']:.2f}€**",
ephemeral=True,
)
except ValueError as e:
await interaction.response.send_message(f"❌ {e}", ephemeral=True)
async def setup(bot):
await bot.add_cog(Banque(bot))

View file

@ -1,93 +1,62 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
banque_manager = BanqueManager()
from utils.bank_manager import BankManager
bank_manager = BankManager()
class Compte(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(
name="compte",
description="Afficher vos comptes bancaires et vos 3 dernières transactions"
)
@app_commands.describe(
membre="Afficher le compte d'un autre membre (réservé au gouvernement)"
)
async def compte(self, interaction: discord.Interaction, membre: discord.Member = None):
"""Affiche les soldes et dernières transactions de l'utilisateur ou d'un autre membre (si gouvernement)"""
@app_commands.command(name="compte", description="Afficher vos comptes bancaires et vos dernières transactions")
async def compte(self, interaction: discord.Interaction):
comptes = await bank_manager.get_comptes_user(interaction.guild.id, interaction.user.id)
banque_manager.load_data()
# Par défaut, on cible l'utilisateur qui exécute la commande
cible = interaction.user
user_id = str(cible.id)
# Si un autre membre est précisé, on vérifie le rôle
if membre and membre != interaction.user:
role_gouv = discord.utils.get(interaction.guild.roles, name="🌍│Gouvernement")
if role_gouv not in interaction.user.roles:
if not comptes:
await interaction.response.send_message(
"❌ Vous devez avoir le rôle **gouvernement** pour consulter le compte d'un autre membre.",
ephemeral=True
"❌ Vous n'avez aucun compte bancaire. Utilisez `/mes-ribs` pour voir vos RIBs.",
ephemeral=True,
)
return
cible = membre
user_id = str(membre.id)
comptes = banque_manager.comptes
embed = discord.Embed(
title=f"💳 Comptes bancaires de {cible.display_name}",
color=discord.Color.blue()
title=f"💳 Comptes de {interaction.user.display_name}",
color=discord.Color.blue(),
)
banques_trouvees = False
for c in comptes:
transactions = await bank_manager.get_transactions(interaction.guild.id, c["rib"], limit=5)
tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else ""
for banque, data in comptes.items():
if not isinstance(data, dict) or user_id not in data:
continue # Ce n'est pas une banque valide pour cet utilisateur
compte = data[user_id]
rib = compte.get("rib", "Non défini")
solde = compte.get("solde", 0)
transactions = compte.get("transactions", [])
# Récupérer les 3 dernières transactions avec date
last_transactions = transactions[-3:] if transactions else []
tx_str = ""
for tx in last_transactions:
date = tx.get("date", "Date inconnue")
tx_type = tx.get("type", "Inconnu")
montant = tx.get("montant", 0)
source = tx.get("emetteur", tx.get("source", "Inconnu"))
dest = tx.get("destinataire", "Inconnu")
if tx_type.lower() == "virement" or tx_type.lower().startswith("virement"):
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** à <@{dest}>\n"
elif tx_type.lower() == "virement reçu":
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€** de <@{source}>\n"
tx_lines = []
for tx in transactions:
is_outgoing = tx["emetteur_rib"] == c["rib"]
direction = "→" if is_outgoing else "←"
other_rib = tx["destinataire_rib"] if is_outgoing else tx["emetteur_rib"]
signe = "-" if is_outgoing else "+"
tx_type = tx["type"]
if tx_type == "tva_prelevee":
tx_lines.append(f"- {direction} **TVA** : `{signe}{tx['montant']:.2f}€`")
else:
tx_str += f"- 🕒 {date} : **{tx_type}** de **{montant}€**\n"
tx_lines.append(f"- {direction} **{tx_type}** : `{signe}{tx['montant']:.2f}€` vers `{other_rib or '?'}`")
if not tx_str:
tx_str = "*Aucune transaction.*"
tx_text = "\n".join(tx_lines) if tx_lines else "*Aucune transaction récente.*"
embed.add_field(
name=f"🏦 {banque}",
value=f"💳 **RIB** : `{rib}`\n💰 **Solde** : `{solde}€`\n📜 **Transactions récentes :**\n{tx_str}",
inline=False
name=f"🏦 {c['bank_name']} ({c['account_type']}){tva_text}",
value=(
f"**RIB :** `{c['rib']}`\n"
f"**Solde :** `{c['solde']:.2f}€`\n\n"
f"**Transactions récentes :**\n{tx_text}"
),
inline=False,
)
banques_trouvees = True
if not banques_trouvees:
embed.description = "❌ Aucun compte bancaire trouvé dans les banques enregistrées pour cet utilisateur."
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(Compte(bot))

View file

@ -1,101 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
from utils.guild_config import has_role
class CompteBanqueNationale(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_banque_nationale_data(self):
try:
with open("comptes_entreprises.json", "r+", encoding="utf-8") as f:
data = json.load(f)
# Crée la section si elle n'existe pas
if "banque_nationale" not in data:
data["banque_nationale"] = {
"BANQUE": {
"rib": "11111 22222 33333333333 44",
"solde": 0.0,
"transactions": [{
"date": "Création automatique",
"type": "Création de compte",
"source": "Système",
"montant": 0.0
}]
}
}
f.seek(0)
json.dump(data, f, indent=4, ensure_ascii=False)
f.truncate()
banque_data = data["banque_nationale"].get("BANQUE", {})
return banque_data.get("solde", 0.0), banque_data.get("transactions", [])
except Exception as e:
print(f"Erreur lecture ou création banque_nationale : {e}")
return 0.0, []
@app_commands.command(name="voir_banque_nationale", description="Voir solde et transactions du compte banque nationale")
async def voir_banque_nationale(self, interaction: discord.Interaction):
if not has_role(interaction.user, "banque_nationale"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_banque_nationale_data()
if not transactions:
await interaction.response.send_message("📄 Aucune transaction trouvée sur le compte banque nationale.", ephemeral=True)
return
# Pagination
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="🏦 Banque Nationale - Historique",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.blue()
)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
description = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=description, inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteBanqueNationale(bot))

View file

@ -1,91 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
from utils.guild_config import has_role
class CompteFBM(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_all_transactions(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
fbm_data = data.setdefault("fbm", {}).setdefault("FBM", {
"solde": 0.0,
"transactions": []
})
return fbm_data["solde"], fbm_data["transactions"]
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
@app_commands.command(name="compte_fbm", description="Affiche le solde et l'historique du compte FBM (réservé au personnel FBM).")
async def compte_fbm(self, interaction: discord.Interaction):
if not has_role(interaction.user, "fbm"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_all_transactions()
if not transactions:
embed = discord.Embed(
title="📄 Compte FBM",
description="Aucune transaction enregistrée.",
color=discord.Color.orange()
)
embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False)
await interaction.response.send_message(embed=embed, ephemeral=True)
return
# Pagination
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="📄 Historique FBM",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.orange()
)
embed.add_field(name="💰 Solde actuel", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="🔸 Transaction", value=desc, inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteFBM(bot))

View file

@ -1,84 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
from utils.guild_config import has_role
class CompteGouvernement(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_gouv_data(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
gouv_data = data.get("Gouvernement", {}).get("GOUV", {})
return gouv_data.get("solde", 0.0), gouv_data.get("transactions", [])
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
@app_commands.command(name="compte_gouvernement", description="Affiche le solde et les transactions du compte Gouvernement.")
async def compte_gouvernement(self, interaction: discord.Interaction):
if not has_role(interaction.user, "gouvernement"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_gouv_data()
if not transactions:
await interaction.response.send_message("📄 Aucune transaction trouvée pour le gouvernement.", ephemeral=True)
return
# Pagination setup
pages = []
per_page = 5
total_pages = math.ceil(len(transactions) / per_page)
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="🏛️ Compte Gouvernement",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.dark_gold()
)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
for tx in chunk:
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=desc, inline=False)
pages.append(embed)
# Vue de pagination
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteGouvernement(bot))

View file

@ -1,95 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
import math
from utils.guild_config import has_role
class CompteUrssaf(commands.Cog):
def __init__(self, bot):
self.bot = bot
def get_all_transactions(self):
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
data = json.load(f)
except Exception as e:
print(f"Erreur lecture comptes_entreprises.json : {e}")
return 0.0, []
# Si URSAFF n'existe pas, on le crée
if "ursaff" not in data:
data["ursaff"] = {
"URSSAFF": {
"rib": "00000 00000 00000000000 01",
"solde": 0.0,
"transactions": []
}
}
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
ursaff_data = data.get("ursaff", {}).get("URSAFF", {})
return ursaff_data.get("solde", 0.0), ursaff_data.get("transactions", [])
@app_commands.command(name="compte_ursaff", description="Affiche le solde et l'historique complet du compte URSAFF (réservé aux membres URSAFF).")
async def compte_ursaff(self, interaction: discord.Interaction):
# Vérifie que l'utilisateur a le rôle URSAFF
if not has_role(interaction.user, "ursaff"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'accéder à ce compte.", ephemeral=True)
return
solde, transactions = self.get_all_transactions()
# Pagination setup
pages = []
per_page = 5
total_pages = max(1, math.ceil(len(transactions) / per_page))
for i in range(total_pages):
start = i * per_page
end = start + per_page
chunk = transactions[start:end]
embed = discord.Embed(
title="📄 Historique URSSAF",
description=f"Page {i+1}/{total_pages}",
color=discord.Color.dark_purple()
)
embed.add_field(name="💼 Solde actuel", value=f"{solde:.2f}€", inline=False)
if chunk:
for tx in chunk:
description = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="🔹 Transaction", value=description, inline=False)
else:
embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False)
pages.append(embed)
class Pagination(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
self.current = 0
@discord.ui.button(label="⬅️", style=discord.ButtonStyle.grey)
async def previous(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current > 0:
self.current -= 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
@discord.ui.button(label="➡️", style=discord.ButtonStyle.grey)
async def next(self, interaction_btn: discord.Interaction, button: discord.ui.Button):
if self.current < len(pages) - 1:
self.current += 1
await interaction_btn.response.edit_message(embed=pages[self.current], view=self)
await interaction.response.send_message(embed=pages[0], view=Pagination(), ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteUrssaf(bot))

View file

@ -1,73 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
class CompteEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="compte_entreprise", description="Consulte le solde et les transactions d'une entreprise.")
@app_commands.choices(banque=banque_choices)
async def compte_entreprise(
self,
interaction: discord.Interaction,
banque: app_commands.Choice[str],
nom_entreprise: str,
mot_de_passe: str
):
banque_nom = banque.value
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True)
return
if banque_nom not in comptes or nom_entreprise not in comptes[banque_nom]:
await interaction.response.send_message("❌ Ce compte entreprise n'existe pas.", ephemeral=True)
return
compte = comptes[banque_nom][nom_entreprise]
# 🔐 Vérification du mot de passe avec la bonne clé
mdp_stocke = compte.get("mdp", "").strip()
mdp_fournit = mot_de_passe.strip()
if mdp_stocke != mdp_fournit:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
solde = compte.get("solde", 0)
transactions = compte.get("transactions", [])
embed = discord.Embed(
title=f"🏢 Compte Entreprise : {nom_entreprise}",
color=discord.Color.teal()
)
embed.add_field(name="🏦 Banque", value=banque_nom, inline=True)
embed.add_field(name="💰 Solde", value=f"{solde:.2f}€", inline=False)
if transactions:
last_transactions = transactions[-3:]
for tx in reversed(last_transactions):
desc = (
f"**Date**: {tx.get('date', 'N/A')}\n"
f"**Type**: {tx.get('type', 'N/A')}\n"
f"**Montant**: {tx.get('montant', 0)}€\n"
f"**Source**: {tx.get('source', 'N/A')}"
)
embed.add_field(name="📄 Transaction", value=desc, inline=False)
else:
embed.add_field(name="📄 Transactions", value="Aucune transaction récente.", inline=False)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(CompteEntreprise(bot))

194
commandes/gouv.py Normal file
View file

@ -0,0 +1,194 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.guild_config import has_role
from utils.bank_manager import BankManager
bank_manager = BankManager()
class Gouv(commands.Cog):
def __init__(self, bot):
self.bot = bot
gouv_group = app_commands.Group(name="gouv", description="Commandes gouvernementales")
def _check_gouv(self, interaction: discord.Interaction) -> bool:
return has_role(interaction.user, "gouvernement")
@gouv_group.command(name="creer-banque", description="Créer une nouvelle banque commerciale")
@app_commands.describe(
nom="Nom de la banque (ex: NGC Bank)",
role="Le rôle Discord des banquiers de cette banque"
)
async def creer_banque(self, interaction: discord.Interaction, nom: str, role: discord.Role):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
try:
result = await bank_manager.creer_banque(interaction.guild.id, nom, role.id)
embed = discord.Embed(
title="🏦 Banque créée",
description=(
f"**{nom}** a été créée avec succès.\n\n"
f"**Rôle banquiers :** {role.mention}\n"
f"**Code établissement :** `{result['establishment_code']}`\n"
f"**ID banque :** `{result['id']}`"
),
color=discord.Color.green(),
)
await interaction.response.send_message(embed=embed, ephemeral=True)
except Exception as e:
await interaction.response.send_message(f"❌ Erreur : {e}", ephemeral=True)
@gouv_group.command(name="creer-institution", description="Créer une institution (Fisc, URSAFF, etc.)")
@app_commands.describe(
nom="Nom de l'institution",
role="Le rôle Discord qui gère cette institution",
audit="Peut-elle consulter les comptes de toutes les banques ?"
)
async def creer_institution(
self,
interaction: discord.Interaction,
nom: str,
role: discord.Role,
audit: bool = False,
):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
try:
result = await bank_manager.creer_institution(
interaction.guild.id, nom, role.id, audit
)
embed = discord.Embed(
title="🏛️ Institution créée",
description=(
f"**{nom}** a été créée.\n\n"
f"**Rôle :** {role.mention}\n"
f"**Droit d'audit :** {'✅ Oui' if audit else '❌ Non'}\n"
f"**ID :** `{result['id']}`"
),
color=discord.Color.blue(),
)
await interaction.response.send_message(embed=embed, ephemeral=True)
except Exception as e:
await interaction.response.send_message(f"❌ Erreur : {e}", ephemeral=True)
@gouv_group.command(name="config-fisc", description="Définir quelle institution reçoit la TVA")
@app_commands.describe(
institution="L'institution qui servira de Fisc"
)
async def config_fisc(self, interaction: discord.Interaction, institution: str):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
institutions = await bank_manager.lister_institutions(interaction.guild.id)
target = None
for inst in institutions:
if inst["name"].lower() == institution.lower():
target = inst
break
if not target:
await interaction.response.send_message(
f"❌ Institution **{institution}** introuvable. Institutions disponibles : {', '.join(i['name'] for i in institutions) or 'aucune'}",
ephemeral=True,
)
return
ok = await bank_manager.configurer_fisc(interaction.guild.id, target["id"])
if ok:
embed = discord.Embed(
title="🏛️ Fisc configuré",
description=f"**{target['name']}** reçoit désormais la TVA de tous les virements Particulier → Entreprise.",
color=discord.Color.green(),
)
await interaction.response.send_message(embed=embed, ephemeral=True)
else:
await interaction.response.send_message("❌ Erreur lors de la configuration.", ephemeral=True)
@gouv_group.command(name="lister-banques", description="Lister toutes les banques du serveur")
async def lister_banques(self, interaction: discord.Interaction):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
banques = await bank_manager.lister_banques(interaction.guild.id)
if not banques:
await interaction.response.send_message("Aucune banque configurée.", ephemeral=True)
return
embed = discord.Embed(title="🏦 Banques du serveur", color=discord.Color.blue())
for b in banques:
role = interaction.guild.get_role(b["role_id"])
role_text = role.mention if role else f"`{b['role_id']}` introuvable"
embed.add_field(
name=b["name"],
value=(
f"**Rôle :** {role_text}\n"
f"**Code établissement :** `{b['establishment_code']}`\n"
f"**Réserve :** `{b['vault_balance']:.2f}€`\n"
f"**Encours clients :** `{b['clients_total_balance']:.2f}€`"
),
inline=False,
)
await interaction.response.send_message(embed=embed, ephemeral=True)
@gouv_group.command(name="lister-institutions", description="Lister toutes les institutions du serveur")
async def lister_institutions(self, interaction: discord.Interaction):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
institutions = await bank_manager.lister_institutions(interaction.guild.id)
if not institutions:
await interaction.response.send_message("Aucune institution configurée.", ephemeral=True)
return
fisc_config = await bank_manager.get_fisc_config(interaction.guild.id)
fisc_id = fisc_config["institution_id"] if fisc_config else None
embed = discord.Embed(title="🏛️ Institutions du serveur", color=discord.Color.blue())
for inst in institutions:
role = interaction.guild.get_role(inst["role_id"]) if inst["role_id"] else None
role_text = role.mention if role else "Non assigné"
is_fisc = " 💰 **(Fisc)**" if inst["id"] == fisc_id else ""
embed.add_field(
name=f"{inst['name']}{is_fisc}",
value=(
f"**Rôle :** {role_text}\n"
f"**Audit :** {'✅ Toutes les banques' if inst['can_audit_all'] else '❌ Non'}\n"
f"**ID :** `{inst['id']}`"
),
inline=False,
)
await interaction.response.send_message(embed=embed, ephemeral=True)
@gouv_group.command(name="supprimer-banque", description="Supprimer une banque")
@app_commands.describe(nom="Nom de la banque à supprimer")
async def supprimer_banque(self, interaction: discord.Interaction, nom: str):
if not self._check_gouv(interaction):
await interaction.response.send_message("❌ Réservé au rôle **Gouvernement**.", ephemeral=True)
return
banque = await bank_manager.get_banque_by_name(interaction.guild.id, nom)
if not banque:
await interaction.response.send_message(f"❌ Banque **{nom}** introuvable.", ephemeral=True)
return
ok = await bank_manager.supprimer_banque(interaction.guild.id, banque["id"])
if ok:
await interaction.response.send_message(
f"✅ Banque **{nom}** supprimée avec tous ses comptes.", ephemeral=True
)
else:
await interaction.response.send_message("❌ Erreur lors de la suppression.", ephemeral=True)
async def setup(bot):
await bot.add_cog(Gouv(bot))

44
commandes/mes_ribs.py Normal file
View file

@ -0,0 +1,44 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.bank_manager import BankManager
bank_manager = BankManager()
class MesRibs(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="mes-ribs", description="Afficher tous vos RIBs")
async def mes_ribs(self, interaction: discord.Interaction):
comptes = await bank_manager.get_comptes_user(interaction.guild.id, interaction.user.id)
if not comptes:
await interaction.response.send_message(
"❌ Vous n'avez aucun compte bancaire.", ephemeral=True
)
return
embed = discord.Embed(
title=f"💳 RIBs de {interaction.user.display_name}",
color=discord.Color.blue(),
)
for c in comptes:
tva_text = f" | TVA: {c['tva_rate']}%" if c["account_type"] == "entreprise" else ""
embed.add_field(
name=f"{c['bank_name']} ({c['account_type']})",
value=(
f"**RIB :** `{c['rib']}`\n"
f"**Solde :** `{c['solde']:.2f}€`{tva_text}"
),
inline=False,
)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(MesRibs(bot))

View file

@ -1,74 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
import random
from utils.guild_config import has_role
# Liste des banques disponibles (id interne : nom affiché)
BANQUES = {
"Victoria's Credit Bank": "Victoria's Credit Bank",
"NGC Bank": "Nexus Global Corporation Bank"
}
banque_manager = BanqueManager()
class OuvrirCompte(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="ouvrir_compte", description="Ouvre un compte bancaire pour un utilisateur (banquier uniquement).")
@app_commands.choices(
banque=[app_commands.Choice(name=nom, value=id_banque) for id_banque, nom in BANQUES.items()]
)
async def ouvrir_compte(self, interaction: discord.Interaction, utilisateur: discord.Member, banque: app_commands.Choice[str]):
banque_id = banque.value
banque_nom = BANQUES[banque_id]
# Vérification du rôle de banquier
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'ouvrir un compte.", ephemeral=True)
return
user_id = str(utilisateur.id)
banque_manager.load_data()
# Conversion automatique si l'ancien format est utilisé
if all(isinstance(v, dict) and 'rib' in v for v in banque_manager.comptes.values()):
banque_manager.comptes = {"vicynationale": banque_manager.comptes}
# Vérification si la banque existe dans les données
if banque_id not in banque_manager.comptes:
banque_manager.comptes[banque_id] = {}
# Vérification si l'utilisateur a déjà un compte dans cette banque
if user_id in banque_manager.comptes[banque_id]:
await interaction.response.send_message(
f"❌ {utilisateur.mention} possède déjà un compte à la {banque_nom} !",
ephemeral=True
)
return
# Génération du RIB aléatoire
rib = f"{random.randint(10000, 99999)} {random.randint(10000, 99999)} {random.randint(10000000000, 99999999999)} {random.randint(10, 99)}"
# Création du compte
banque_manager.comptes[banque_id][user_id] = {
"rib": rib,
"solde": 0.0,
"transactions": []
}
banque_manager.save_data()
# Envoi du RIB en MP à l'utilisateur cible
try:
await utilisateur.send(f"🏦 Un compte a été créé pour vous à la **{banque_nom}**.\nVoici votre RIB : `{rib}`")
except discord.Forbidden:
await interaction.response.send_message("❌ Impossible d'envoyer le RIB en message privé (l'utilisateur a les MP fermés).", ephemeral=True)
return
await interaction.response.send_message(f"✅ Compte créé avec succès pour {utilisateur.mention} à la **{banque_nom}**.", ephemeral=True)
async def setup(bot):
await bot.add_cog(OuvrirCompte(bot))

View file

@ -1,93 +0,0 @@
import json
import os
import random
import string
import discord
from discord import app_commands
from discord.ext import commands
from utils.guild_config import has_role
COMPTES_ENTREPRISES_PATH = "comptes_entreprises.json"
def generer_rib():
return " ".join(["".join(random.choices(string.digits, k=5)) for _ in range(4)])
def generer_mdp():
return "".join(random.choices(string.ascii_letters + string.digits, k=8))
class OuvrirCompteEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choix = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
tva_choix = [
app_commands.Choice(name="2%", value=2),
app_commands.Choice(name="5%", value=5),
app_commands.Choice(name="10%", value=10),
app_commands.Choice(name="20%", value=20),
]
@app_commands.command(name="ouvrir_compte_entreprise", description="Ouvre un compte bancaire pour une entreprise")
@app_commands.choices(banque=banque_choix, tva=tva_choix)
async def ouvrir_compte_entreprise(self, interaction: discord.Interaction, entreprise: str, banque: app_commands.Choice[str], tva: app_commands.Choice[int]):
# ✅ Vérifie que l'utilisateur est banquier
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'exécuter cette commande.", ephemeral=True)
return
banque_name = banque.value
entreprise = entreprise.strip()
# ✅ Chargement sécurisé du fichier
if os.path.exists(COMPTES_ENTREPRISES_PATH):
try:
with open(COMPTES_ENTREPRISES_PATH, "r", encoding="utf-8") as f:
comptes = json.load(f)
except json.JSONDecodeError:
comptes = {}
else:
comptes = {}
if banque_name not in comptes:
comptes[banque_name] = {}
if entreprise in comptes[banque_name]:
await interaction.response.send_message(f"❌ Un compte existe déjà pour l'entreprise **{entreprise}** dans la banque **{banque_name}**.", ephemeral=True)
return
rib = generer_rib()
mdp = generer_mdp()
comptes[banque_name][entreprise] = {
"rib": rib,
"solde": 0,
"mdp": mdp,
"tva": tva.value,
"transactions": []
}
with open(COMPTES_ENTREPRISES_PATH, "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
# ✅ MP à l'utilisateur avec le RIB et le mot de passe
try:
await interaction.user.send(
f"🏦 Compte de l'entreprise **{entreprise}** ouvert avec succès dans la banque **{banque_name}** !\n"
f"💳 RIB : `{rib}`\n"
f"🔐 Mot de passe : `{mdp}`\n"
f"💼 Taux de TVA : `{tva.value}%`\n\n"
f"Conservez bien ces informations."
)
except discord.Forbidden:
await interaction.response.send_message("⚠️ Impossible de vous envoyer un message privé avec les informations du compte.", ephemeral=True)
return
await interaction.response.send_message(f"✅ Le compte de l'entreprise **{entreprise}** a été créé avec succès.", ephemeral=True)
async def setup(bot):
await bot.add_cog(OuvrirCompteEntreprise(bot))

2389
commandes/panel.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,74 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
from utils.guild_config import has_role
banque_manager = BanqueManager()
class Pret(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="calculer_pret", description="Accorde un prêt bancaire à un utilisateur (Réservé aux banquiers).")
async def calculer_pret(
self,
interaction: discord.Interaction,
utilisateur: discord.Member,
banque: str,
montant: float,
duree_annees: int
):
"""Les banquiers peuvent accorder un prêt à un utilisateur si son solde le permet."""
# Vérification de si le joueur executant la commande est un banquier
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message(
"❌ Vous n'avez pas la permission d'accorder un prêt.",
ephemeral=True
)
return
user_id = str(utilisateur.id)
success, result = banque_manager.ajouter_pret(user_id, banque, montant, duree_annees)
if not success:
await interaction.response.send_message(result, ephemeral=True)
return
total_a_rembourser = result
taux_interet = banque_manager.comptes[user_id]['prets'][-1]['taux_interet'] * 100
# Vérifier si l'utilisateur a assez d'argent pour rembourser
solde_utilisateur = banque_manager.comptes[user_id].get("solde", 0)
if solde_utilisateur < total_a_rembourser:
await interaction.response.send_message(
f"❌ {utilisateur.mention} n'a pas assez d'argent pour rembourser ce prêt.\n"
f"💰 **Solde actuel** : {solde_utilisateur}€\n"
f"💳 **Montant à rembourser** : {total_a_rembourser}€",
ephemeral=True
)
return
# Débiter le solde de l'utilisateur
banque_manager.comptes[user_id]["solde"] -= total_a_rembourser
banque_manager.save_data()
# Création de l'embed
embed = discord.Embed(
title="🏦 Prêt bancaire accordé",
description=f"Le prêt a été accordé par {interaction.user.mention} pour {utilisateur.mention}.",
color=discord.Color.green()
)
embed.set_thumbnail(url="https://cdn-icons-png.flaticon.com/512/2910/2910827.png") # Icône banque
embed.add_field(name="💰 Montant emprunté", value=f"{montant}€", inline=True)
embed.add_field(name="📅 Durée", value=f"{duree_annees} ans", inline=True)
embed.add_field(name="📈 Taux d'intérêt", value=f"{taux_interet}%", inline=True)
embed.add_field(name="💳 Montant total à rembourser", value=f"{total_a_rembourser}€", inline=False)
embed.add_field(name="💰 Solde après prêt", value=f"{banque_manager.comptes[user_id]['solde']}€", inline=True)
embed.set_footer(text="Prêt enregistré dans le compte bancaire ✅")
await interaction.response.send_message(embed=embed)
async def setup(bot):
await bot.add_cog(Pret(bot))

View file

@ -1,146 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
from utils.guild_config import has_role
class RetirerArgent(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="retirer_argent",
description="Retire de l'argent d'un compte via son RIB (utilisateur ou entreprise)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB du compte",
montant="Montant à retirer",
banque="Banque cible (facultatif)"
)
async def retirer_argent(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cela.", ephemeral=True)
return
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
banque_nom = banque.value if banque else None
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True)
return
compte = None
source = None # "utilisateur" ou "entreprise"
user_id = None
# Recherche dans la banque spécifiée (si précisée)
if banque_nom and banque_nom in comptes_bancaires:
for uid, data in comptes_bancaires[banque_nom].items():
if data.get("rib") == rib:
compte = data
source = "utilisateur"
user_id = uid
break
if not compte and banque_nom and banque_nom in comptes_entreprises:
for entreprise, data in comptes_entreprises[banque_nom].items():
if data.get("rib") == rib:
compte = data
source = "entreprise"
user_id = entreprise
break
# Si banque non précisée ou compte non trouvé, recherche dans toutes les banques
if not compte and not banque_nom:
# Recherche dans toutes les banques utilisateurs
for banque_courante, comptes_banque in comptes_bancaires.items():
for uid, data in comptes_banque.items():
if data.get("rib") == rib:
compte = data
source = "utilisateur"
user_id = uid
banque_nom = banque_courante
break
if compte:
break
if not compte:
# Recherche dans toutes les banques entreprises
for banque_courante, entreprises_banque in comptes_entreprises.items():
for entreprise, data in entreprises_banque.items():
if data.get("rib") == rib:
compte = data
source = "entreprise"
user_id = entreprise
banque_nom = banque_courante
break
if compte:
break
if not compte:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour retirer cette somme.", ephemeral=True)
return
# Appliquer le retrait
compte["solde"] -= montant
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Retrait",
"source": "Banque",
"montant": -montant,
"emetteur": interaction.user.id,
"destinataire": user_id
}
compte.setdefault("transactions", []).append(transaction)
# Sauvegarde dans le bon fichier
if source == "utilisateur":
comptes_bancaires[banque_nom][user_id] = compte
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False)
else:
comptes_entreprises[banque_nom][user_id] = compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
# Message de confirmation
embed = discord.Embed(
title="💸 Retrait effectué",
description=f"💼 Banque : **{banque_nom}**\n📌 Type : **{source.capitalize()}**",
color=discord.Color.red()
)
embed.add_field(name="RIB", value=rib, inline=False)
embed.add_field(name="Montant retiré", value=f"{montant:.2f}€", inline=True)
embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True)
embed.set_footer(text="Transaction enregistrée ✅")
await interaction.response.send_message(embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(RetirerArgent(bot))

View file

@ -1,184 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
from utils.guild_config import has_role
GOUV_BANQUE = "Gouvernement"
GOUV_COMPTE = "GOUV"
GOUV_RIB = "00000 00000 00000000000 00"
class GouvVirement(commands.Cog):
def __init__(self, bot):
self.bot = bot
# Liste des banques à jour (ajoute ici toutes les banques possibles de ton système)
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(
name="gouv_virer",
description="Le Gouvernement vire de l'argent de son compte vers un autre RIB (entreprise ou particulier)."
)
@app_commands.choices(banque=banque_choices)
@app_commands.describe(
rib="Le RIB destinataire",
montant="Montant à virer",
banque="Banque cible (facultatif)"
)
async def gouv_virer(
self,
interaction: discord.Interaction,
rib: str,
montant: float,
banque: app_commands.Choice[str] = None
):
# Vérification du rôle Gouvernement
if not has_role(interaction.user, "gouvernement"):
await interaction.response.send_message("❌ Seul le Gouvernement peut utiliser cette commande.", ephemeral=True)
return
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
banque_nom = banque.value if banque else None
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
except Exception:
comptes_bancaires = {}
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des fichiers JSON.", ephemeral=True)
return
# Récupérer le compte du Gouvernement
try:
gouv_compte = comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE]
except KeyError:
await interaction.response.send_message("❌ Compte Gouvernement introuvable.", ephemeral=True)
return
if gouv_compte["solde"] < montant:
await interaction.response.send_message("❌ Le Gouvernement n'a pas assez de fonds.", ephemeral=True)
return
# Chercher le destinataire
destinataire_compte = None
destinataire_banque = None
destinataire_nom = None
destinataire_type = None
# Si banque précisée, chercher d'abord dans cette banque (entreprises puis particuliers)
if banque_nom:
# Entreprises
if banque_nom in comptes_entreprises:
for nom, data in comptes_entreprises[banque_nom].items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_nom
destinataire_nom = nom
destinataire_type = "entreprise"
break
# Particuliers
if not destinataire_compte and banque_nom in comptes_bancaires:
for uid, data in comptes_bancaires[banque_nom].items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_nom
destinataire_nom = uid
destinataire_type = "particulier"
break
# Si banque non précisée, chercher dans toutes les banques
else:
# Entreprises
for banque_courante, entreprises in comptes_entreprises.items():
for nom, data in entreprises.items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_courante
destinataire_nom = nom
destinataire_type = "entreprise"
break
if destinataire_compte:
break
# Particuliers
if not destinataire_compte:
for banque_courante, comptes in comptes_bancaires.items():
for uid, data in comptes.items():
if data.get("rib") == rib:
destinataire_compte = data
destinataire_banque = banque_courante
destinataire_nom = uid
destinataire_type = "particulier"
break
if destinataire_compte:
break
if not destinataire_compte:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB dans la banque spécifiée." if banque_nom else "❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
# Effectuer le virement
gouv_compte["solde"] -= montant
destinataire_compte["solde"] += montant
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Transaction côté Gouvernement (sortant)
gouv_compte.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": "Gouvernement",
"montant": -montant,
"emetteur": "Gouvernement",
"destinataire": destinataire_nom
})
# Transaction côté destinataire (entrant)
destinataire_compte.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": "Gouvernement",
"montant": montant,
"emetteur": "Gouvernement",
"destinataire": destinataire_nom
})
# Sauvegarde
comptes_entreprises[GOUV_BANQUE][GOUV_COMPTE] = gouv_compte
if destinataire_type == "entreprise":
comptes_entreprises[destinataire_banque][destinataire_nom] = destinataire_compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
else:
comptes_bancaires[destinataire_banque][destinataire_nom] = destinataire_compte
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, indent=4, ensure_ascii=False)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, indent=4, ensure_ascii=False)
# Message de confirmation
embed = discord.Embed(
title="🏛️ Virement Gouvernemental effectué",
description=f"💼 Banque destinataire : **{destinataire_banque}**\n📌 Type : **{destinataire_type.capitalize()}**",
color=discord.Color.gold()
)
embed.add_field(name="RIB destinataire", value=rib, inline=False)
embed.add_field(name="Montant viré", value=f"{montant:.2f}€", inline=True)
embed.add_field(name="Effectué par", value=interaction.user.mention, inline=True)
embed.set_footer(text="Transaction enregistrée ✅")
await interaction.response.send_message(embed=embed, ephemeral=False)
async def setup(bot):
await bot.add_cog(GouvVirement(bot))

View file

@ -1,66 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
from utils.banque_manager import BanqueManager
from utils.guild_config import has_role
banque_manager = BanqueManager()
class Transactions(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="transactions", description="Affiche les transactions d'un utilisateur (réservé aux banquiers).")
async def transactions(self, interaction: discord.Interaction, utilisateur: discord.Member):
"""Affiche l'historique des transactions d'un utilisateur (banquiers uniquement)."""
# ✅ Récupération du vrai membre pour avoir les rôles
member = interaction.guild.get_member(interaction.user.id)
if not member:
await interaction.response.send_message("❌ Impossible de vérifier vos permissions.", ephemeral=True)
return
# ✅ Vérification du rôle banquier
if not has_role(member, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission de voir les transactions.", ephemeral=True)
return
banque_manager.load_data()
user_id = str(utilisateur.id)
# ✅ Vérification si l'utilisateur a des transactions
if user_id not in banque_manager.comptes or "transactions" not in banque_manager.comptes[user_id]:
await interaction.response.send_message(f"❌ {utilisateur.mention} n'a pas d'historique de transactions.", ephemeral=True)
return
transactions = banque_manager.comptes[user_id].get("transactions", [])
if not transactions:
await interaction.response.send_message(f"📜 {utilisateur.mention} n'a encore effectué aucune transaction.", ephemeral=True)
return
# ✅ Création de l'embed
embed = discord.Embed(
title=f"📜 Historique des transactions de {utilisateur.name}",
color=discord.Color.gold()
)
for t in transactions[-10:]:
embed.add_field(
name=f"📅 {t['date']}",
value=(
f"**Type**: {t['type']}\n"
f"💰 **Montant**: {t['montant']}€\n"
f"📤 **Émetteur**: <@{t['emetteur']}>\n"
f"📥 **Destinataire**: <@{t['destinataire']}>"
),
inline=False
)
embed.set_footer(text="Seules les 10 dernières transactions sont affichées.")
await interaction.response.send_message(embed=embed)
async def setup(bot):
await bot.add_cog(Transactions(bot))

View file

@ -1,138 +1,71 @@
import json
import discord
from discord import app_commands
from discord.ext import commands
from datetime import datetime
from utils.bank_manager import BankManager
from utils.rib import decoder_rib
bank_manager = BankManager()
class Virement(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement", description="Fait un virement bancaire à un autre membre via son RIB")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(rib="Le RIB du destinataire", montant="Le montant à envoyer", banque="Banque d'où retirer l'argent")
async def virement(self, interaction: discord.Interaction, rib: str, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_source = banque.value
# Charger les comptes utilisateurs
with open('comptes_bancaires.json', 'r', encoding='utf-8') as f:
comptes = json.load(f)
# Charger les comptes entreprises
with open('comptes_entreprises.json', 'r', encoding='utf-8') as f:
entreprises = json.load(f)
# Vérifier que l'utilisateur a un compte dans cette banque
if banque_source not in comptes or auteur_id not in comptes[banque_source]:
await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True)
return
compte_emetteur = comptes[banque_source][auteur_id]
if compte_emetteur["solde"] < montant:
await interaction.response.send_message("❌ Ton solde est insuffisant pour effectuer ce virement.", ephemeral=True)
return
# Rechercher le destinataire (utilisateur ou entreprise)
destinataire_id = None
banque_destinataire = None
is_entreprise = False
# Recherche dans comptes utilisateurs
for bq_name, bq_data in comptes.items():
if isinstance(bq_data, dict):
for user_id, info in bq_data.items():
if isinstance(info, dict) and info.get("rib") == rib:
destinataire_id = user_id
banque_destinataire = bq_name
break
if destinataire_id:
break
# Sinon, recherche dans les entreprises
if not destinataire_id:
for bq_name, bq_data in entreprises.items():
if isinstance(bq_data, dict):
for ent_id, info in bq_data.items():
if isinstance(info, dict) and info.get("rib") == rib:
destinataire_id = ent_id
banque_destinataire = bq_name
is_entreprise = True
break
if destinataire_id:
break
if not destinataire_id:
await interaction.response.send_message("❌ Aucun compte trouvé avec ce RIB.", ephemeral=True)
return
# Appliquer la TVA si entreprise
montant_net = montant
tva_envoyee = 0
if is_entreprise:
taux = entreprises[banque_destinataire][destinataire_id].get("tva", 0)
tva_envoyee = round(montant * taux / 100, 2)
montant_net = montant - tva_envoyee
# Ajouter la TVA au Gouvernement
if "Gouvernement" not in entreprises:
entreprises["Gouvernement"] = {}
if "GOUV" not in entreprises["Gouvernement"]:
entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee
entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "TVA reçue",
"source": auteur_id,
"montant": tva_envoyee
})
# Déduire du compte émetteur
comptes[banque_source][auteur_id]["solde"] -= montant
comptes[banque_source][auteur_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement",
"source": f"Vers RIB {rib}",
"montant": -montant
})
# Ajouter au compte destinataire
cible_data = entreprises if is_entreprise else comptes
cible_data[banque_destinataire][destinataire_id]["solde"] += montant_net
cible_data[banque_destinataire][destinataire_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant_net
})
# Sauvegarde
with open('comptes_bancaires.json', 'w', encoding='utf-8') as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
with open('comptes_entreprises.json', 'w', encoding='utf-8') as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
# Envoi MP au destinataire si c'est un utilisateur
if not is_entreprise:
try:
user_dest = await self.bot.fetch_user(int(destinataire_id))
await user_dest.send(f"📥 Vous avez reçu un virement de **{montant:.2f}€** de la part de **{interaction.user.display_name}**.")
except:
pass # Si l'utilisateur a les MP désactivés
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** effectué.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if is_entreprise else ''}",
ephemeral=True
@app_commands.command(name="virement", description="Faire un virement à un autre compte via RIB")
@app_commands.describe(
rib_source="Votre RIB (compte source)",
rib_dest="RIB du destinataire",
montant="Montant à envoyer",
)
async def virement(
self,
interaction: discord.Interaction,
rib_source: str,
rib_dest: str,
montant: float,
):
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True)
return
compte_source = await bank_manager.get_compte_by_rib(interaction.guild.id, rib_source)
if not compte_source:
await interaction.response.send_message("❌ RIB source introuvable.", ephemeral=True)
return
if compte_source["user_id"] != interaction.user.id:
await interaction.response.send_message("❌ Ce RIB ne vous appartient pas.", ephemeral=True)
return
try:
result = await bank_manager.virement(
guild_id=interaction.guild.id,
rib_source=rib_source,
rib_dest=rib_dest,
montant=montant,
emetteur_id=interaction.user.id,
)
msg = f"✅ Virement de **{result['montant_envoye']:.2f}€** effectué."
if result["tva_prelevee"] > 0:
msg += f"\n💰 TVA prélevée : **{result['tva_prelevee']:.2f}€** → Fisc."
msg += f"\n**Reçu par le destinataire :** {result['montant_recu']:.2f}€"
await interaction.response.send_message(msg, ephemeral=True)
try:
dest_compte = await bank_manager.get_compte_by_rib(interaction.guild.id, rib_dest)
if dest_compte and dest_compte["account_type"] == "particulier":
dest_user = await self.bot.fetch_user(dest_compte["user_id"])
await dest_user.send(
f"📥 Vous avez reçu **{result['montant_recu']:.2f}€** de la part de **{interaction.user.display_name}**."
)
except (discord.Forbidden, Exception):
pass
except ValueError as e:
await interaction.response.send_message(f"❌ {e}", ephemeral=True)
async def setup(bot):
await bot.add_cog(Virement(bot))

View file

@ -1,85 +0,0 @@
import json
import discord
from discord import app_commands
from discord.ext import commands
from datetime import datetime
from utils.guild_config import has_role
class VirementBanquier(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_banquier", description="Effectue un virement au nom d'un client (réservé aux banquiers).")
@app_commands.choices(banque=banque_choices)
async def virement_banquier(
self,
interaction: discord.Interaction,
rib_emetteur: str,
montant: float,
rib_destinataire: str,
banque: app_commands.Choice[str]
):
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission d'utiliser cette commande.", ephemeral=True)
return
banque_nom = banque.value
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except:
await interaction.response.send_message("❌ Erreur de lecture des comptes bancaires.", ephemeral=True)
return
# Trouver les ID à partir des RIB
emetteur_id = None
destinataire_id = None
for user_id, data in comptes.get(banque_nom, {}).items():
if data.get("rib") == rib_emetteur:
emetteur_id = user_id
if data.get("rib") == rib_destinataire:
destinataire_id = user_id
if not emetteur_id:
await interaction.response.send_message("❌ Le RIB émetteur est introuvable dans cette banque.", ephemeral=True)
return
if not destinataire_id:
await interaction.response.send_message("❌ Le RIB destinataire est introuvable dans cette banque.", ephemeral=True)
return
# Vérifier le solde
if comptes[banque_nom][emetteur_id]["solde"] < montant:
await interaction.response.send_message("❌ Le solde du compte émetteur est insuffisant.", ephemeral=True)
return
# Exécuter le virement
comptes[banque_nom][emetteur_id]["solde"] -= montant
comptes[banque_nom][emetteur_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement manuel (par banquier)",
"source": f"Vers RIB {rib_destinataire}",
"montant": -montant
})
comptes[banque_nom][destinataire_id]["solde"] += montant
comptes[banque_nom][destinataire_id]["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu (par banquier)",
"source": f"RIB {rib_emetteur}",
"montant": montant
})
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué de **{rib_emetteur}** vers **{rib_destinataire}** dans **{banque_nom}**.", ephemeral=True)
async def setup(bot):
await bot.add_cog(VirementBanquier(bot))

View file

@ -1,121 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
from utils.guild_config import has_role
class VirementBanquierEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banques = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank")
]
@app_commands.command(name="virement_banquier_entreprise", description="Fait un virement depuis une entreprise avec mot de passe (réservé aux banquiers).")
@app_commands.choices(banque=banques)
async def virement_banquier_entreprise(
self,
interaction: discord.Interaction,
banque: app_commands.Choice[str],
nom_entreprise: str,
mot_de_passe: str,
rib_destinataire: str,
montant: float
):
if not has_role(interaction.user, "banquier"):
await interaction.response.send_message("❌ Vous n'avez pas la permission de faire cette commande.", ephemeral=True)
return
banque_nom = banque.value
# Chargement des fichiers
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
particuliers = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des fichiers JSON.", ephemeral=True)
return
# Vérification de l'entreprise
if banque_nom not in entreprises or nom_entreprise not in entreprises[banque_nom]:
await interaction.response.send_message("❌ Compte entreprise introuvable.", ephemeral=True)
return
compte_entreprise = entreprises[banque_nom][nom_entreprise]
if compte_entreprise.get("mdp") != mot_de_passe:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
if compte_entreprise["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour ce virement.", ephemeral=True)
return
# Recherche du compte destinataire (entreprise ou particulier)
destinataire_id = None
banque_destinataire = None
is_entreprise = False
# Recherche dans entreprises
for bq, entreprises_data in entreprises.items():
for nom, infos in entreprises_data.items():
if infos.get("rib") == rib_destinataire:
destinataire_id = nom
banque_destinataire = bq
is_entreprise = True
break
if destinataire_id:
break
# Sinon, recherche dans particuliers
if not destinataire_id:
for bq, clients in particuliers.items():
for user_id, infos in clients.items():
if infos.get("rib") == rib_destinataire:
destinataire_id = user_id
banque_destinataire = bq
is_entreprise = False
break
if destinataire_id:
break
if not destinataire_id:
await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True)
return
# Retrait du montant
compte_entreprise["solde"] -= montant
compte_entreprise["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement",
"source": f"Entreprise {nom_entreprise}",
"montant": -montant,
"destinataire": rib_destinataire
})
# Créditer le destinataire
compte_cible = entreprises if is_entreprise else particuliers
compte_data = compte_cible[banque_destinataire][destinataire_id]
compte_data["solde"] += montant
compte_data["transactions"].append({
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": "Virement reçu",
"source": nom_entreprise,
"montant": montant
})
# Sauvegarde
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, ensure_ascii=False, indent=4)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(particuliers, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(f"✅ Virement de **{montant:.2f}€** effectué depuis **{nom_entreprise}** vers **{rib_destinataire}**.")
async def setup(bot):
await bot.add_cog(VirementBanquierEntreprise(bot))

View file

@ -1,149 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementEntreprise(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_entreprise", description="Effectuer un virement depuis une entreprise vers un RIB.")
@app_commands.choices(banque=banque_choices)
async def virement_entreprise(
self,
interaction: discord.Interaction,
rib_entreprise: str,
montant: float,
mot_de_passe: str,
rib_destinataire: str,
banque: app_commands.Choice[str]
):
banque_nom = banque.value
# Chargement des données
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
comptes_entreprises = json.load(f)
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes_bancaires = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur de lecture des données bancaires.", ephemeral=True)
return
# 🔐 Vérification de l'émetteur
entreprise_id = None
for nom_entreprise, infos in comptes_entreprises.get(banque_nom, {}).items():
if infos.get("rib") == rib_entreprise:
if infos.get("mdp") != mot_de_passe:
await interaction.response.send_message("❌ Mot de passe incorrect.", ephemeral=True)
return
entreprise_id = nom_entreprise
break
if not entreprise_id:
await interaction.response.send_message("❌ Aucun compte entreprise trouvé avec ce RIB.", ephemeral=True)
return
compte_emetteur = comptes_entreprises[banque_nom][entreprise_id]
if compte_emetteur["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant.", ephemeral=True)
return
# 🔎 Recherche du destinataire
destinataire_data = None
destinataire_type = None
destinataire_nom = None
banque_dest = None
for banque_cible, comptes in comptes_entreprises.items():
for nom, infos in comptes.items():
if infos.get("rib") == rib_destinataire:
destinataire_data = infos
destinataire_type = "entreprise"
destinataire_nom = nom
banque_dest = banque_cible
break
if destinataire_data:
break
if not destinataire_data:
for banque_cible, comptes in comptes_bancaires.items():
for uid, infos in comptes.items():
if infos.get("rib") == rib_destinataire:
destinataire_data = infos
destinataire_type = "particulier"
destinataire_nom = uid
banque_dest = banque_cible
break
if destinataire_data:
break
if not destinataire_data:
await interaction.response.send_message("❌ RIB destinataire introuvable.", ephemeral=True)
return
# 📉 Débit du compte entreprise
compte_emetteur["solde"] -= montant
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
compte_emetteur.setdefault("transactions", []).append({
"date": now,
"type": "Virement",
"source": f"Vers RIB {rib_destinataire}",
"montant": -montant
})
# 📈 Crédit du destinataire (avec TVA si entreprise)
montant_net = montant
tva_envoyee = 0
if destinataire_type == "entreprise":
tva = destinataire_data.get("tva", 0)
tva_envoyee = round(montant * tva / 100, 2)
montant_net = montant - tva_envoyee
# Crédit TVA à l'État
if "Gouvernement" not in comptes_entreprises:
comptes_entreprises["Gouvernement"] = {}
if "GOUV" not in comptes_entreprises["Gouvernement"]:
comptes_entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
comptes_entreprises["Gouvernement"]["GOUV"]["solde"] += tva_envoyee
comptes_entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": now,
"type": "TVA reçue",
"source": entreprise_id,
"montant": tva_envoyee
})
destinataire_data["solde"] += montant_net
destinataire_data.setdefault("transactions", []).append({
"date": now,
"type": "Virement reçu",
"source": entreprise_id,
"montant": montant_net
})
# Sauvegardes
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(comptes_entreprises, f, ensure_ascii=False, indent=4)
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes_bancaires, f, ensure_ascii=False, indent=4)
await interaction.response.send_message(
f"✅ Virement effectué avec succès.{' TVA de ' + str(tva_envoyee) + '€ envoyée au Gouvernement.' if tva_envoyee else ''}",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementEntreprise(bot))

View file

@ -1,97 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementFBM(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_fbm", description="Fait un virement au compte indépendant FBM.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser")
async def virement_fbm(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_nom = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True)
return
# Charger les fichiers
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True)
return
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
except:
entreprises = {}
# Vérification du compte de l'utilisateur
if banque_nom not in comptes or auteur_id not in comptes[banque_nom]:
await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True)
return
compte = comptes[banque_nom][auteur_id]
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True)
return
# Initialiser FBM si nécessaire
if "fbm" not in entreprises:
entreprises["fbm"] = {}
if "FBM" not in entreprises["fbm"]:
entreprises["fbm"]["FBM"] = {
"rib": "00000 00000 00000000000 03",
"solde": 0.0,
"transactions": []
}
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Mise à jour des soldes
compte["solde"] -= montant
entreprises["fbm"]["FBM"]["solde"] += montant
# Historique utilisateur
compte["transactions"].append({
"date": now,
"type": "Virement",
"source": f"Vers FBM",
"montant": -montant
})
# Historique FBM
entreprises["fbm"]["FBM"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
# Confirmation
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au compte **FBM** depuis **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementFBM(bot))

View file

@ -1,86 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementGouvernement(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_gouvernement", description="Effectue un virement au Gouvernement.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Le montant à envoyer", banque="Banque à utiliser pour le virement")
async def virement_gouvernement(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_source = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à zéro.", ephemeral=True)
return
# Charger les fichiers
try:
with open('comptes_bancaires.json', 'r', encoding='utf-8') as f:
comptes = json.load(f)
with open('comptes_entreprises.json', 'r', encoding='utf-8') as f:
entreprises = json.load(f)
except Exception as e:
await interaction.response.send_message(f"❌ Erreur de lecture des fichiers : {e}", ephemeral=True)
return
if banque_source not in comptes or auteur_id not in comptes[banque_source]:
await interaction.response.send_message(f"❌ Tu n'as pas de compte dans la banque **{banque_source}**.", ephemeral=True)
return
compte_user = comptes[banque_source][auteur_id]
if compte_user["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer ce virement.", ephemeral=True)
return
# Vérifier ou créer le compte Gouvernement
if "Gouvernement" not in entreprises:
entreprises["Gouvernement"] = {}
if "GOUV" not in entreprises["Gouvernement"]:
entreprises["Gouvernement"]["GOUV"] = {
"rib": "00000 00000 00000000000 00",
"solde": 0.0,
"transactions": []
}
# Effectuer le virement
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
compte_user["solde"] -= montant
entreprises["Gouvernement"]["GOUV"]["solde"] += montant
compte_user["transactions"].append({
"date": now,
"type": "Virement",
"source": f"Vers Gouvernement",
"montant": -montant
})
entreprises["Gouvernement"]["GOUV"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open('comptes_bancaires.json', 'w', encoding='utf-8') as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open('comptes_entreprises.json', 'w', encoding='utf-8') as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au Gouvernement avec succès.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementGouvernement(bot))

View file

@ -1,93 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
import json
from datetime import datetime
class VirementUrssaf(commands.Cog):
def __init__(self, bot):
self.bot = bot
banque_choices = [
app_commands.Choice(name="Victoria's Credit Bank", value="Victoria's Credit Bank"),
app_commands.Choice(name="NGC Bank", value="NGC Bank"),
]
@app_commands.command(name="virement_ursaff", description="Fait un virement vers le compte indépendant URSAFF.")
@app_commands.choices(banque=banque_choices)
@app_commands.describe(montant="Montant à transférer", banque="Banque à utiliser")
async def virement_ursaff(self, interaction: discord.Interaction, montant: float, banque: app_commands.Choice[str]):
auteur_id = str(interaction.user.id)
banque_nom = banque.value
if montant <= 0:
await interaction.response.send_message("❌ Le montant doit être supérieur à 0.", ephemeral=True)
return
try:
with open("comptes_bancaires.json", "r", encoding="utf-8") as f:
comptes = json.load(f)
except Exception:
await interaction.response.send_message("❌ Erreur lors de la lecture des comptes bancaires.", ephemeral=True)
return
try:
with open("comptes_entreprises.json", "r", encoding="utf-8") as f:
entreprises = json.load(f)
except:
entreprises = {}
if banque_nom not in comptes or auteur_id not in comptes[banque_nom]:
await interaction.response.send_message("❌ Tu n'as pas de compte dans cette banque.", ephemeral=True)
return
compte = comptes[banque_nom][auteur_id]
if compte["solde"] < montant:
await interaction.response.send_message("❌ Solde insuffisant pour effectuer le virement.", ephemeral=True)
return
# Initialiser URSAFF si nécessaire
if "ursaff" not in entreprises:
entreprises["ursaff"] = {}
if "URSAFF" not in entreprises["ursaff"]:
entreprises["ursaff"]["URSAFF"] = {
"rib": "00000 00000 00000000000 02",
"solde": 0.0,
"transactions": []
}
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Mise à jour des soldes
compte["solde"] -= montant
entreprises["ursaff"]["URSAFF"]["solde"] += montant
# Historique utilisateur
compte["transactions"].append({
"date": now,
"type": "Virement",
"source": "Vers URSAFF",
"montant": -montant
})
# Historique URSAFF
entreprises["ursaff"]["URSAFF"]["transactions"].append({
"date": now,
"type": "Virement reçu",
"source": interaction.user.name,
"montant": montant
})
# Sauvegarde
with open("comptes_bancaires.json", "w", encoding="utf-8") as f:
json.dump(comptes, f, indent=4, ensure_ascii=False)
with open("comptes_entreprises.json", "w", encoding="utf-8") as f:
json.dump(entreprises, f, indent=4, ensure_ascii=False)
await interaction.response.send_message(
f"✅ Virement de **{montant:.2f}€** envoyé au compte **URSAFF** depuis **{banque_nom}**.",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(VirementUrssaf(bot))

File diff suppressed because it is too large Load diff

View file

@ -1,492 +0,0 @@
{
"ngc_bank": {},
"Gouvernement": {
"GOUV": {
"rib": "00000 00000 00000000000 00",
"solde": 10085849.01,
"transactions": [
{
"date": "26/04/2025 15:55:13",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 0.2
},
{
"date": "27/04/2025 14:01:32",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 0.2
},
{
"date": "28/04/2025 08:53:44",
"type": "TVA reçue",
"source": "1108087282692522105",
"montant": 8.5
},
{
"date": "28/04/2025 10:42:38",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "28/04/2025 15:37:27",
"type": "TVA reçue",
"source": "1244182195300601920",
"montant": 8.5
},
{
"date": "01/05/2025 08:56:08",
"type": "TVA reçue",
"source": "914118793918312480",
"montant": 5.2
},
{
"date": "01/05/2025 13:08:59",
"type": "TVA reçue",
"source": "1321863072242995332",
"montant": 300.0
},
{
"date": "04/05/2025 16:51:20",
"type": "TVA reçue",
"source": "971446412690722826",
"montant": 0.01
},
{
"date": "05/05/2025 16:09:39",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 400.0
},
{
"date": "05/05/2025 16:23:40",
"type": "TVA reçue",
"source": "828239898975666216",
"montant": 10.0
},
{
"date": "05/05/2025 16:24:07",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "06/05/2025 10:49:55",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 8.0
},
{
"date": "06/05/2025 18:19:03",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 1.0
},
{
"date": "07/05/2025 19:45:13",
"type": "Virement reçu",
"source": "gameurpro12",
"montant": 1.0
},
{
"date": "09/05/2025 17:23:13",
"type": "TVA reçue",
"source": "1200313720195264613",
"montant": 7.8
},
{
"date": "09/05/2025 18:31:46",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "09/05/2025 19:10:15",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "09/05/2025 19:15:25",
"type": "TVA reçue",
"source": "534003230435442690",
"montant": 100.0
},
{
"date": "11/05/2025 13:06:24",
"type": "TVA reçue",
"source": "1321863072242995332",
"montant": 330.0
},
{
"date": "11/05/2025 14:49:46",
"type": "TVA reçue",
"source": "1072112886509473792",
"montant": 14000.0
},
{
"date": "11/05/2025 14:50:02",
"type": "TVA reçue",
"source": "501080933282218005",
"montant": 2400.0
},
{
"date": "12/05/2025 18:35:06",
"type": "TVA reçue",
"source": "1263573205268828231",
"montant": 7.6
},
{
"date": "18/05/2025 15:17:43",
"type": "TVA reçue",
"source": "1126995714552111215",
"montant": 2400.0
},
{
"date": "18/05/2025 15:34:58",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 2400.0
},
{
"date": "18/05/2025 15:34:58",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 2400.0
},
{
"date": "19/05/2025 11:55:01",
"type": "TVA reçue",
"source": "1200313720195264613",
"montant": 7.8
},
{
"date": "29/05/2025 13:51:53",
"type": "TVA reçue",
"source": "376796341071773706",
"montant": 6000.0
},
{
"date": "29/05/2025 15:16:42",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 27.0
},
{
"date": "06/06/2025 20:09:01",
"type": "TVA reçue",
"source": "353942721997832195",
"montant": 42.0
},
{
"date": "20/06/2025 19:06:14",
"type": "TVA reçue",
"source": "1244372888702816458",
"montant": 2000.0
},
{
"date": "29/06/2025 12:31:17",
"type": "Dépôt",
"source": "Banque",
"montant": 15869.0,
"emetteur": 353942721997832195,
"destinataire": "GOUV"
}
]
}
},
"NGC Bank": {
"Journal en onde": {
"rib": "93884 11674 79519 40904",
"solde": 6031.0,
"mdp": "E96zM0Jj",
"tva": 2,
"transactions": [
{
"date": "05/05/2025 16:23:40",
"type": "Virement reçu",
"source": "docteur_w.d_axolotl",
"montant": 490.0
},
{
"date": "05/05/2025 16:24:07",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "Journal en onde"
},
{
"date": "06/05/2025 10:49:55",
"type": "Virement reçu",
"source": "e.93130",
"montant": 392.0
},
{
"date": "06/05/2025 18:19:03",
"type": "Virement reçu",
"source": "e.93130",
"montant": 49.0
}
]
},
"Milano Motors": {
"rib": "79307 68952 45977 60249",
"solde": 126400.0,
"mdp": "9YcjNvud",
"tva": 20,
"transactions": [
{
"date": "11/05/2025 14:49:46",
"type": "Virement reçu",
"source": "lagrenouillebeka",
"montant": 56000.0
},
{
"date": "11/05/2025 14:50:02",
"type": "Virement reçu",
"source": "justeme",
"montant": 9600.0
},
{
"date": "18/05/2025 15:17:43",
"type": "Virement reçu",
"source": "jl_wesley.f1",
"montant": 9600.0
},
{
"date": "18/05/2025 15:34:58",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 9600.0
},
{
"date": "18/05/2025 15:34:58",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 9600.0
},
{
"date": "29/05/2025 13:51:53",
"type": "Virement reçu",
"source": "bitehoven",
"montant": 24000.0
},
{
"date": "20/06/2025 19:06:14",
"type": "Virement reçu",
"source": "oghacker1337",
"montant": 8000.0
}
]
},
"Sicilia mia": {
"rib": "19440 99822 26785 73807",
"solde": 108.0,
"mdp": "8sqmuoz4",
"tva": 20,
"transactions": [
{
"date": "29/05/2025 15:16:42",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 108.0
}
]
},
"Domaine de vins": {
"rib": "96241 34733 44548 77079",
"solde": 1768.0,
"mdp": "hIJj81ci",
"tva": 20,
"transactions": [
{
"date": "05/05/2025 16:09:39",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 1600.0
},
{
"date": "06/06/2025 20:09:01",
"type": "Virement reçu",
"source": "mg_studios_",
"montant": 168.0
}
]
},
"Umbrella Assurances": {
"rib": "54201 75079 71658 35659",
"solde": 2969.4,
"mdp": "KDRQGd3R",
"tva": 2,
"transactions": [
{
"date": "28/04/2025 08:53:44",
"type": "Virement reçu",
"source": "tricks212_zz",
"montant": 416.5
},
{
"date": "28/04/2025 10:42:38",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "28/04/2025 15:37:27",
"type": "Virement reçu",
"source": "hvk_pompotfraicz",
"montant": 416.5
},
{
"date": "01/05/2025 08:56:08",
"type": "Virement reçu",
"source": "neyzizz",
"montant": 254.8
},
{
"date": "09/05/2025 17:23:13",
"type": "Virement reçu",
"source": "agent_chaos_officiel",
"montant": 382.2
},
{
"date": "09/05/2025 18:31:46",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "12/05/2025 18:35:06",
"type": "Virement reçu",
"source": "e.93130",
"montant": 372.4
},
{
"date": "19/05/2025 11:55:01",
"type": "Virement reçu",
"source": "agent_chaos_officiel",
"montant": 382.2
}
]
},
"Auto école": {
"rib": "11940 73588 95258 04768",
"solde": 15971.0,
"mdp": "zcEKe45Y",
"tva": 20,
"transactions": [
{
"date": "26/04/2025 15:55:13",
"type": "Virement reçu",
"source": "nixfix06",
"montant": 0.8
},
{
"date": "27/04/2025 14:01:32",
"type": "Virement reçu",
"source": "nixfix06",
"montant": 0.8
},
{
"date": "01/05/2025 13:08:59",
"type": "Virement reçu",
"source": "sz94d",
"montant": 1200.0
},
{
"date": "11/05/2025 13:06:24",
"type": "Virement reçu",
"source": "sz94d",
"montant": 1320.0
}
]
},
"Sicilia Mia": {
"rib": "68229 13765 90163 35585",
"solde": 1907.09,
"mdp": "6uTORm3r",
"tva": 10,
"transactions": [
{
"date": "04/05/2025 16:51:20",
"type": "Virement reçu",
"source": "gameurpro12",
"montant": 0.09000000000000001
}
]
},
"NGC Bank": {
"rib": "15156 38436 35920 54357",
"solde": 1040900.0,
"mdp": "nxDcskg0",
"tva": 2,
"transactions": [
{
"date": "09/05/2025 19:10:15",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "09/05/2025 19:15:25",
"type": "Dépôt",
"source": "Banque",
"montant": 4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "09/05/2025 19:15:51",
"type": "Retrait",
"source": "Banque",
"montant": -4900.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "24/05/2025 13:44:09",
"type": "Dépôt",
"source": "Banque",
"montant": 1000000.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
},
{
"date": "04/06/2025 16:30:19",
"type": "Dépôt",
"source": "Banque",
"montant": 36000.0,
"emetteur": 534003230435442690,
"destinataire": "NGC Bank"
}
]
},
"Agence immobilière.": {
"rib": "54044 32109 51269 69180",
"solde": 108000.0,
"mdp": "tCH7QK7G",
"tva": 20,
"transactions": [
{
"date": "03/07/2025 22:35:20",
"type": "Dépôt",
"source": "Banque",
"montant": 108000.0,
"emetteur": 353942721997832195,
"destinataire": "Agence immobilière."
}
]
},
"La Concordia": {
"rib": "09860 71327 12935 47361",
"solde": 0,
"mdp": "3vo66Wqu",
"tva": 10,
"transactions": []
}
}
}

View file

@ -1,12 +1,12 @@
{
"identities": {
"353942721997832195": {
"nom": "RICCI",
"prenom": "Aldo",
"date_naissance": "22/05/1967",
"lieu_naissance": "Victoria's Vice",
"nom": "JOHN",
"prenom": "Marston",
"date_naissance": "22/05/2008",
"lieu_naissance": "Victorias",
"sexe": "Homme",
"date_expiration": "01/12/2026"
"date_expiration": "17/08/2028"
},
"1096453463849828362": {
"nom": "DE MONTROSE",

500
utils/bank_manager.py Normal file
View file

@ -0,0 +1,500 @@
import json
from datetime import datetime
from typing import Optional
import aiosqlite
from utils.db import get_db
from utils.rib import generer_rib, valider_rib, decoder_rib
GUIICHET_PARTICULIER = 4
GUIICHET_ENTREPRISE = 3
GUIICHET_BANQUE = 2
GUIICHET_INSTITUTION = 1
class BankManager:
async def creer_banque(self, guild_id: int, name: str, role_id: int) -> dict:
db = await get_db()
try:
cursor = await db.execute(
"SELECT COALESCE(MAX(establishment_code), 9999) FROM banks WHERE guild_id = ?",
(guild_id,),
)
row = await cursor.fetchone()
last_code = row[0]
new_code = last_code + 1
if new_code > 89999:
raise ValueError("Plus de codes d'établissement disponibles.")
cursor = await db.execute(
"INSERT INTO banks (guild_id, name, role_id, establishment_code) VALUES (?, ?, ?, ?)",
(guild_id, name, role_id, new_code),
)
await db.commit()
bank_id = cursor.lastrowid
return {"id": bank_id, "name": name, "role_id": role_id, "establishment_code": new_code}
finally:
await db.close()
async def supprimer_banque(self, guild_id: int, bank_id: int) -> bool:
db = await get_db()
try:
cursor = await db.execute(
"DELETE FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id)
)
await db.commit()
return cursor.rowcount > 0
finally:
await db.close()
async def get_banque(self, guild_id: int, bank_id: int) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id)
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def get_banque_by_role(self, guild_id: int, role_id: int) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM banks WHERE guild_id = ? AND role_id = ?", (guild_id, role_id)
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def get_banque_by_name(self, guild_id: int, name: str) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM banks WHERE guild_id = ? AND name = ?", (guild_id, name)
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def lister_banques(self, guild_id: int) -> list:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM banks WHERE guild_id = ? ORDER BY name", (guild_id,)
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
finally:
await db.close()
async def ouvrir_compte(
self,
guild_id: int,
bank_id: int,
user_id: int,
account_type: str,
tva_rate: float = 0.0,
password: str = None,
) -> dict:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM banks WHERE id = ? AND guild_id = ?", (bank_id, guild_id)
)
bank = await cursor.fetchone()
if not bank:
raise ValueError("Banque introuvable.")
guichet = GUIICHET_ENTREPRISE if account_type == "entreprise" else GUIICHET_PARTICULIER
cursor = await db.execute(
"SELECT COUNT(*) FROM accounts WHERE guild_id = ? AND bank_id = ? AND user_id = ? AND account_type = ?",
(guild_id, bank_id, user_id, account_type),
)
count = (await cursor.fetchone())[0]
if count > 0:
raise ValueError(f"Ce membre a déjà un compte {account_type} dans cette banque.")
rib = generer_rib(bank["establishment_code"], guichet)
cursor = await db.execute(
"INSERT INTO accounts (guild_id, bank_id, user_id, rib, account_type, tva_rate, password) VALUES (?, ?, ?, ?, ?, ?, ?)",
(guild_id, bank_id, user_id, rib, account_type, tva_rate, password),
)
await db.commit()
return {"id": cursor.lastrowid, "rib": rib, "account_type": account_type, "tva_rate": tva_rate}
finally:
await db.close()
async def get_compte_by_rib(self, guild_id: int, rib: str) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"""SELECT a.*, b.name as bank_name, b.establishment_code, b.role_id as bank_role_id
FROM accounts a JOIN banks b ON a.bank_id = b.id
WHERE a.rib = ? AND a.guild_id = ?""",
(rib, guild_id),
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def get_comptes_user(self, guild_id: int, user_id: int) -> list:
db = await get_db()
try:
cursor = await db.execute(
"""SELECT a.*, b.name as bank_name, b.establishment_code
FROM accounts a JOIN banks b ON a.bank_id = b.id
WHERE a.guild_id = ? AND a.user_id = ?
ORDER BY b.name, a.account_type""",
(guild_id, user_id),
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
finally:
await db.close()
async def get_comptes_banque(self, guild_id: int, bank_id: int) -> list:
db = await get_db()
try:
cursor = await db.execute(
"""SELECT a.*, b.name as bank_name
FROM accounts a JOIN banks b ON a.bank_id = b.id
WHERE a.guild_id = ? AND a.bank_id = ?
ORDER BY a.user_id, a.account_type""",
(guild_id, bank_id),
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
finally:
await db.close()
async def modifier_tva(self, guild_id: int, rib: str, nouveau_taux: float) -> bool:
db = await get_db()
try:
cursor = await db.execute(
"UPDATE accounts SET tva_rate = ? WHERE rib = ? AND guild_id = ? AND account_type = 'entreprise'",
(nouveau_taux, rib, guild_id),
)
await db.commit()
return cursor.rowcount > 0
finally:
await db.close()
async def virement(
self,
guild_id: int,
rib_source: str,
rib_dest: str,
montant: float,
emetteur_id: int = None,
destinataire_id: int = None,
) -> dict:
if montant <= 0:
raise ValueError("Le montant doit être supérieur à 0.")
if rib_source == rib_dest:
raise ValueError("Impossible de se virer à soi-même.")
db = await get_db()
try:
source = await self._get_account(db, guild_id, rib_source)
if not source:
raise ValueError("Compte source introuvable.")
dest = await self._get_account(db, guild_id, rib_dest)
if not dest:
raise ValueError("Compte destination introuvable.")
if source["solde"] < montant:
raise ValueError(f"Solde insuffisant. Disponible : {source['solde']:.2f}€")
tva_montant = 0.0
montant_net = montant
if source["account_type"] == "particulier" and dest["account_type"] == "entreprise":
tva_rate = dest["tva_rate"] or 0.0
if tva_rate > 0:
tva_montant = round(montant * tva_rate / 100, 2)
montant_net = round(montant - tva_montant, 2)
cursor = await db.execute(
"UPDATE accounts SET solde = solde - ? WHERE rib = ? AND guild_id = ?",
(montant, rib_source, guild_id),
)
cursor = await db.execute(
"UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?",
(montant_net, rib_dest, guild_id),
)
if tva_montant > 0:
fisc_rib = await self._get_fisc_rib(db, guild_id)
if fisc_rib:
cursor = await db.execute(
"UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?",
(tva_montant, fisc_rib, guild_id),
)
cursor = await db.execute(
"UPDATE banks SET clients_total_balance = clients_total_balance - ? WHERE id = ? AND guild_id = ?",
(montant, source["bank_id"], guild_id),
)
cursor = await db.execute(
"UPDATE banks SET clients_total_balance = clients_total_balance + ? WHERE id = ? AND guild_id = ?",
(montant_net, dest["bank_id"], guild_id),
)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
await db.execute(
"""INSERT INTO transactions (guild_id, emetteur_rib, destinataire_rib, emetteur_id, destinataire_id, montant, type, date)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(guild_id, rib_source, rib_dest, emetteur_id or source["user_id"], destinataire_id or dest["user_id"], montant, "virement", now),
)
if tva_montant > 0 and fisc_rib:
await db.execute(
"""INSERT INTO transactions (guild_id, destinataire_rib, montant, type, date, metadata)
VALUES (?, ?, ?, ?, ?, ?)""",
(guild_id, fisc_rib, tva_montant, "tva_prelevee", now, json.dumps({"source_rib": rib_source, "dest_rib": rib_dest})),
)
await db.commit()
result = {
"montant_envoye": montant,
"montant_recu": montant_net,
"tva_prelevee": tva_montant,
}
if tva_montant > 0:
result["tva_dest"] = fisc_rib
return result
finally:
await db.close()
async def depot_banquier(
self, guild_id: int, rib: str, montant: float, banquier_id: int
) -> dict:
if montant <= 0:
raise ValueError("Le montant doit être supérieur à 0.")
db = await get_db()
try:
compte = await self._get_account(db, guild_id, rib)
if not compte:
raise ValueError("Compte introuvable.")
cursor = await db.execute(
"UPDATE accounts SET solde = solde + ? WHERE rib = ? AND guild_id = ?",
(montant, rib, guild_id),
)
cursor = await db.execute(
"UPDATE banks SET clients_total_balance = clients_total_balance + ? WHERE id = ? AND guild_id = ?",
(montant, compte["bank_id"], guild_id),
)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
await db.execute(
"""INSERT INTO transactions (guild_id, destinataire_rib, destinataire_id, montant, type, date, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(guild_id, rib, compte["user_id"], montant, "depot_banquier", now, json.dumps({"banquier_id": banquier_id})),
)
await db.commit()
return {"nouveau_solde": compte["solde"] + montant}
finally:
await db.close()
async def retrait_banquier(
self, guild_id: int, rib: str, montant: float, banquier_id: int
) -> dict:
if montant <= 0:
raise ValueError("Le montant doit être supérieur à 0.")
db = await get_db()
try:
compte = await self._get_account(db, guild_id, rib)
if not compte:
raise ValueError("Compte introuvable.")
if compte["solde"] < montant:
raise ValueError(f"Solde insuffisant. Disponible : {compte['solde']:.2f}€")
cursor = await db.execute(
"UPDATE accounts SET solde = solde - ? WHERE rib = ? AND guild_id = ?",
(montant, rib, guild_id),
)
cursor = await db.execute(
"UPDATE banks SET clients_total_balance = clients_total_balance - ? WHERE id = ? AND guild_id = ?",
(montant, compte["bank_id"], guild_id),
)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
await db.execute(
"""INSERT INTO transactions (guild_id, emetteur_rib, emetteur_id, montant, type, date, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(guild_id, rib, compte["user_id"], montant, "retrait_banquier", now, json.dumps({"banquier_id": banquier_id})),
)
await db.commit()
return {"nouveau_solde": compte["solde"] - montant}
finally:
await db.close()
async def get_transactions(self, guild_id: int, rib: str, limit: int = 10) -> list:
db = await get_db()
try:
cursor = await db.execute(
"""SELECT * FROM transactions
WHERE guild_id = ? AND (emetteur_rib = ? OR destinataire_rib = ?)
ORDER BY date DESC LIMIT ?""",
(guild_id, rib, rib, limit),
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
finally:
await db.close()
async def creer_institution(
self, guild_id: int, name: str, role_id: int = None, can_audit_all: bool = False
) -> dict:
db = await get_db()
try:
cursor = await db.execute(
"INSERT INTO institutions (guild_id, name, role_id, can_audit_all) VALUES (?, ?, ?, ?)",
(guild_id, name, role_id, int(can_audit_all)),
)
await db.commit()
return {"id": cursor.lastrowid, "name": name, "can_audit_all": can_audit_all}
finally:
await db.close()
async def get_institution(self, guild_id: int, institution_id: int) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM institutions WHERE id = ? AND guild_id = ?",
(institution_id, guild_id),
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def get_institution_by_role(self, guild_id: int, role_id: int) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM institutions WHERE guild_id = ? AND role_id = ?",
(guild_id, role_id),
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def lister_institutions(self, guild_id: int) -> list:
db = await get_db()
try:
cursor = await db.execute(
"SELECT * FROM institutions WHERE guild_id = ? ORDER BY name", (guild_id,)
)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
finally:
await db.close()
async def configurer_fisc(self, guild_id: int, institution_id: int) -> bool:
db = await get_db()
try:
inst = await self.get_institution(guild_id, institution_id)
if not inst:
return False
guichet = GUIICHET_INSTITUTION
bank = await self._get_gouvernement_bank(db, guild_id)
if not bank:
cursor = await db.execute(
"INSERT INTO banks (guild_id, name, role_id, establishment_code) VALUES (?, ?, ?, ?)",
(guild_id, "Gouvernement", 0, 1),
)
bank_id = cursor.lastrowid
bank = {"id": bank_id, "establishment_code": 1}
else:
bank_id = bank["id"]
rib = generer_rib(bank["establishment_code"], guichet)
existing = await db.execute(
"SELECT rib FROM accounts WHERE guild_id = ? AND bank_id = ? AND user_id = ? AND account_type = 'entreprise'",
(guild_id, bank_id, institution_id),
)
existing_row = await existing.fetchone()
if existing_row:
rib = existing_row[0]
else:
cursor = await db.execute(
"INSERT INTO accounts (guild_id, bank_id, user_id, rib, account_type, solde) VALUES (?, ?, ?, ?, 'entreprise', 0.0)",
(guild_id, bank_id, institution_id, rib),
)
await db.execute(
"INSERT OR REPLACE INTO fisc_config (guild_id, institution_id) VALUES (?, ?)",
(guild_id, institution_id),
)
await db.commit()
return True
finally:
await db.close()
async def get_fisc_config(self, guild_id: int) -> Optional[dict]:
db = await get_db()
try:
cursor = await db.execute(
"""SELECT fc.*, i.name as institution_name, i.role_id as institution_role_id
FROM fisc_config fc
JOIN institutions i ON fc.institution_id = i.id
WHERE fc.guild_id = ?""",
(guild_id,),
)
row = await cursor.fetchone()
return dict(row) if row else None
finally:
await db.close()
async def get_next_bank_codes(self, guild_id: int) -> list:
db = await get_db()
try:
cursor = await db.execute(
"SELECT establishment_code FROM banks WHERE guild_id = ? ORDER BY establishment_code",
(guild_id,),
)
rows = await cursor.fetchall()
return [r[0] for r in rows]
finally:
await db.close()
async def _get_account(self, db: aiosqlite.Connection, guild_id: int, rib: str) -> Optional[dict]:
cursor = await db.execute(
"SELECT * FROM accounts WHERE rib = ? AND guild_id = ?", (rib, guild_id)
)
row = await cursor.fetchone()
return dict(row) if row else None
async def _get_fisc_rib(self, db: aiosqlite.Connection, guild_id: int) -> Optional[str]:
cursor = await db.execute(
"""SELECT a.rib FROM accounts a
JOIN fisc_config fc ON a.user_id = fc.institution_id
WHERE fc.guild_id = ? AND a.account_type = 'entreprise'""",
(guild_id,),
)
row = await cursor.fetchone()
return row[0] if row else None
async def _get_gouvernement_bank(self, db: aiosqlite.Connection, guild_id: int) -> Optional[dict]:
cursor = await db.execute(
"SELECT * FROM banks WHERE guild_id = ? AND name = 'Gouvernement'",
(guild_id,),
)
row = await cursor.fetchone()
return dict(row) if row else None

View file

@ -1,58 +0,0 @@
import json
import os
from datetime import datetime
from .logger import log_error
class BanqueManager:
def __init__(self):
self.file_path = "comptes_bancaires.json"
self.comptes = {}
self.load_data()
def load_data(self):
"""Charge les données bancaires depuis un fichier JSON."""
if os.path.exists(self.file_path):
try:
with open(self.file_path, "r", encoding="utf-8") as f:
data = f.read().strip()
self.comptes = json.loads(data) if data else {}
except json.JSONDecodeError:
log_error("Erreur de lecture dans comptes_bancaires.json, réinitialisation...")
self.comptes = {}
self.save_data()
def save_data(self):
"""Sauvegarde les comptes et transactions dans comptes_bancaires.json."""
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(self.comptes, f, indent=4, ensure_ascii=False)
def enregistrer_transaction(self, emetteur_id, destinataire_id, montant, type_transaction):
"""
Enregistre une transaction dans l'historique et recharge les données en temps réel.
"""
self.load_data()
transaction = {
"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
"type": type_transaction,
"montant": montant,
"emetteur": emetteur_id,
"destinataire": destinataire_id
}
if emetteur_id not in self.comptes:
self.comptes[emetteur_id] = {"solde": 0, "prets": [], "transactions": []}
elif "transactions" not in self.comptes[emetteur_id]:
self.comptes[emetteur_id]["transactions"] = []
self.comptes[emetteur_id]["transactions"].append(transaction)
if destinataire_id not in self.comptes:
self.comptes[destinataire_id] = {"solde": 0, "prets": [], "transactions": []}
elif "transactions" not in self.comptes[destinataire_id]:
self.comptes[destinataire_id]["transactions"] = []
self.comptes[destinataire_id]["transactions"].append(transaction)
self.save_data()

View file

@ -16,7 +16,8 @@ class CardGenerator:
def create_template(self):
try:
CI_path = "/home/discord/Bot/Vicy/ressources/assets/CIITAL.png"
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CI_path = os.path.join(base_dir, "ressources", "assets", "CIITAL.png")
if not os.path.exists(CI_path):
raise FileNotFoundError(f"Image de fond introuvable : {CI_path}")

82
utils/db.py Normal file
View file

@ -0,0 +1,82 @@
import aiosqlite
import os
DB_PATH = os.path.join(os.path.dirname(__file__), "..", "data", "bank_v2.db")
SCHEMA = """
CREATE TABLE IF NOT EXISTS banks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id INTEGER NOT NULL,
name TEXT NOT NULL,
role_id INTEGER NOT NULL,
establishment_code INTEGER NOT NULL,
vault_balance REAL DEFAULT 0.0,
clients_total_balance REAL DEFAULT 0.0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(guild_id, name),
UNIQUE(guild_id, establishment_code)
);
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id INTEGER NOT NULL,
bank_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
rib TEXT NOT NULL UNIQUE,
account_type TEXT NOT NULL CHECK(account_type IN ('particulier', 'entreprise')),
solde REAL DEFAULT 0.0,
tva_rate REAL DEFAULT 0.0,
password TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (bank_id) REFERENCES banks(id) ON DELETE CASCADE,
UNIQUE(guild_id, bank_id, user_id, account_type)
);
CREATE TABLE IF NOT EXISTS institutions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id INTEGER NOT NULL,
name TEXT NOT NULL,
role_id INTEGER,
can_audit_all INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(guild_id, name)
);
CREATE TABLE IF NOT EXISTS fisc_config (
guild_id INTEGER PRIMARY KEY,
institution_id INTEGER,
FOREIGN KEY (institution_id) REFERENCES institutions(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id INTEGER NOT NULL,
emetteur_rib TEXT,
destinataire_rib TEXT,
emetteur_id INTEGER,
destinataire_id INTEGER,
montant REAL NOT NULL,
type TEXT NOT NULL,
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata TEXT
);
CREATE INDEX IF NOT EXISTS idx_accounts_guild_user ON accounts(guild_id, user_id);
CREATE INDEX IF NOT EXISTS idx_accounts_rib ON accounts(rib);
CREATE INDEX IF NOT EXISTS idx_accounts_bank ON accounts(guild_id, bank_id);
CREATE INDEX IF NOT EXISTS idx_transactions_guild ON transactions(guild_id);
CREATE INDEX IF NOT EXISTS idx_transactions_ribs ON transactions(emetteur_rib, destinataire_rib);
CREATE INDEX IF NOT EXISTS idx_banks_guild ON banks(guild_id);
CREATE INDEX IF NOT EXISTS idx_institutions_guild ON institutions(guild_id);
"""
async def get_db() -> aiosqlite.Connection:
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
db = await aiosqlite.connect(DB_PATH)
db.row_factory = aiosqlite.Row
await db.execute("PRAGMA journal_mode=WAL")
await db.execute("PRAGMA foreign_keys=ON")
await db.executescript(SCHEMA)
await db.commit()
return db

View file

@ -18,9 +18,6 @@ DEFAULT_CONFIG = {
"visite_medicale": None,
"permis": None,
"auto_ecole": None,
"fbm": None,
"banque_nationale": None,
"ursaff": None,
"session_reminders": None,
},
"channels": {
@ -47,9 +44,6 @@ ROLE_LABELS = {
"visite_medicale": "Visite Médicale",
"permis": "Permis",
"auto_ecole": "Auto-École",
"fbm": "FBM",
"banque_nationale": "Banque Nationale",
"ursaff": "URSAFF",
"session_reminders": "Session Reminders",
}

55
utils/rib.py Normal file
View file

@ -0,0 +1,55 @@
import random
import string
def _checksum(digits: str) -> int:
return (97 - (int(digits) % 97)) % 97
def generer_rib(establishment_code: int, guichet_code: int) -> str:
etab = str(establishment_code).zfill(5)
guichet = str(guichet_code).zfill(5)
numero = "".join(random.choices(string.digits, k=11))
checksum_digits = f"76{etab}{guichet}{numero}"
cle = str(_checksum(checksum_digits)).zfill(2)
return f"FR76 {etab} {guichet} {numero} {cle}"
def valider_rib(rib: str) -> bool:
parts = rib.replace("FR76 ", "").split()
if len(parts) != 3:
return False
etab, guichet, rest = parts
if len(etab) != 5 or len(guichet) != 5:
return False
if len(rest) != 13:
return False
numero = rest[:11]
cle = rest[11:]
if not etab.isdigit() or not guichet.isdigit() or not numero.isdigit() or not cle.isdigit():
return False
checksum_digits = f"76{etab}{guichet}{numero}"
return _checksum(checksum_digits) == int(cle)
def decoder_rib(rib: str) -> dict:
parts = rib.replace("FR76 ", "").split()
if len(parts) != 3:
return None
etab, guichet, rest = parts
numero = rest[:11]
cle = rest[11:]
etab_int = int(etab)
guichet_int = int(guichet)
TYPE_MAP = {4: "particulier", 3: "entreprise", 2: "banque", 1: "institution"}
return {
"etablissement": etab_int,
"guichet": guichet_int,
"numero": numero,
"cle": int(cle),
"type_compte": TYPE_MAP.get(guichet_int, "inconnu"),
"est_gouvernement": etab_int < 100,
"valide": valider_rib(rib),
}