refactor: remove category rules and related logic from defaults, types, and services for cleaner codebase
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import type { CategoryRule } from "./types"
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// STRUCTURE HIÉRARCHIQUE DES CATÉGORIES
|
// STRUCTURE HIÉRARCHIQUE DES CATÉGORIES
|
||||||
@@ -986,58 +985,6 @@ export const defaultCategories: CategoryDefinition[] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
// RÈGLES DE CATÉGORISATION AVANCÉES
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
export interface CategoryRuleDefinition extends Omit<CategoryRule, "id" | "categoryId"> {
|
|
||||||
categorySlug: string // Référence au slug de la catégorie
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultCategoryRules: CategoryRuleDefinition[] = [
|
|
||||||
// Salaire - patterns typiques de virements salaire
|
|
||||||
{ categorySlug: "revenus-salaire", pattern: "^VIR(EMENT)? (RECU )?.*SALAIRE", isRegex: true },
|
|
||||||
{ categorySlug: "revenus-salaire", pattern: "^VIR(EMENT)? (RECU )?.*PAIE", isRegex: true },
|
|
||||||
|
|
||||||
// Loyer - patterns de prélèvement loyer
|
|
||||||
{ categorySlug: "logement-loyer", pattern: "^PRLV.*LOYER", isRegex: true },
|
|
||||||
{ categorySlug: "logement-loyer", pattern: "^PRLV.*FONCIA", isRegex: true },
|
|
||||||
{ categorySlug: "logement-loyer", pattern: "^PRLV.*NEXITY", isRegex: true },
|
|
||||||
|
|
||||||
// EDF/Engie
|
|
||||||
{ categorySlug: "logement-electricite", pattern: "^PRLV.*EDF", isRegex: true },
|
|
||||||
{ categorySlug: "logement-electricite", pattern: "^PRLV.*ENGIE", isRegex: true },
|
|
||||||
{ categorySlug: "logement-electricite", pattern: "^PRLV.*TOTAL.?ENERGIE", isRegex: true },
|
|
||||||
|
|
||||||
// Télécom
|
|
||||||
{ categorySlug: "abonnements-telecom", pattern: "^PRLV.*FREE( MOBILE)?", isRegex: true },
|
|
||||||
{ categorySlug: "abonnements-telecom", pattern: "^PRLV.*ORANGE", isRegex: true },
|
|
||||||
{ categorySlug: "abonnements-telecom", pattern: "^PRLV.*SFR", isRegex: true },
|
|
||||||
{ categorySlug: "abonnements-telecom", pattern: "^PRLV.*BOUYGUES", isRegex: true },
|
|
||||||
|
|
||||||
// Assurances
|
|
||||||
{ categorySlug: "finance-assurance", pattern: "^PRLV.*AXA", isRegex: true },
|
|
||||||
{ categorySlug: "finance-assurance", pattern: "^PRLV.*MAIF", isRegex: true },
|
|
||||||
{ categorySlug: "finance-assurance", pattern: "^PRLV.*MACIF", isRegex: true },
|
|
||||||
{ categorySlug: "finance-assurance", pattern: "^PRLV.*MATMUT", isRegex: true },
|
|
||||||
|
|
||||||
// Impôts
|
|
||||||
{ categorySlug: "finance-impots", pattern: "^PRLV.*DGFIP", isRegex: true },
|
|
||||||
{ categorySlug: "finance-impots", pattern: "^PRLV.*TRESOR PUBLIC", isRegex: true },
|
|
||||||
{ categorySlug: "finance-impots", pattern: "IMPOT", isRegex: false },
|
|
||||||
|
|
||||||
// Remboursements
|
|
||||||
{ categorySlug: "revenus-remboursements", pattern: "^VIR(EMENT)? (RECU )?.*CPAM", isRegex: true },
|
|
||||||
{ categorySlug: "revenus-remboursements", pattern: "^VIR(EMENT)? (RECU )?.*AMELI", isRegex: true },
|
|
||||||
{ categorySlug: "revenus-remboursements", pattern: "REMBOURSEMENT", isRegex: false },
|
|
||||||
|
|
||||||
// CAF
|
|
||||||
{ categorySlug: "revenus-allocations", pattern: "^VIR(EMENT)? (RECU )?.*CAF", isRegex: true },
|
|
||||||
{ categorySlug: "revenus-allocations", pattern: "ALLOCATION", isRegex: false },
|
|
||||||
|
|
||||||
// Retraits
|
|
||||||
{ categorySlug: "divers-retraits", pattern: "^RETRAIT DAB", isRegex: true },
|
|
||||||
{ categorySlug: "divers-retraits", pattern: "^RET DAB", isRegex: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// DOSSIER RACINE
|
// DOSSIER RACINE
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ const defaultData: BankingData = {
|
|||||||
transactions: [],
|
transactions: [],
|
||||||
folders: [defaultRootFolder],
|
folders: [defaultRootFolder],
|
||||||
categories: buildCategoriesFromDefaults(),
|
categories: buildCategoriesFromDefaults(),
|
||||||
categoryRules: [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadData(): BankingData {
|
export function loadData(): BankingData {
|
||||||
|
|||||||
@@ -43,19 +43,11 @@ export interface Category {
|
|||||||
parentId: string | null
|
parentId: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CategoryRule {
|
|
||||||
id: string
|
|
||||||
categoryId: string
|
|
||||||
pattern: string
|
|
||||||
isRegex: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BankingData {
|
export interface BankingData {
|
||||||
accounts: Account[]
|
accounts: Account[]
|
||||||
transactions: Transaction[]
|
transactions: Transaction[]
|
||||||
folders: Folder[]
|
folders: Folder[]
|
||||||
categories: Category[]
|
categories: Category[]
|
||||||
categoryRules: CategoryRule[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OFX Parsed types
|
// OFX Parsed types
|
||||||
|
|||||||
@@ -79,23 +79,9 @@ model Category {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
parent Category? @relation("CategoryHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
|
parent Category? @relation("CategoryHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
|
||||||
children Category[] @relation("CategoryHierarchy")
|
children Category[] @relation("CategoryHierarchy")
|
||||||
transactions Transaction[]
|
transactions Transaction[]
|
||||||
rules CategoryRule[]
|
|
||||||
|
|
||||||
@@index([parentId])
|
@@index([parentId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model CategoryRule {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
categoryId String
|
|
||||||
pattern String
|
|
||||||
isRegex Boolean @default(false)
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@index([categoryId])
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PrismaClient } from "@prisma/client"
|
import { PrismaClient } from "@prisma/client"
|
||||||
import { defaultCategories, defaultCategoryRules, type CategoryDefinition } from "../lib/defaults"
|
import { defaultCategories, type CategoryDefinition } from "../lib/defaults"
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
@@ -113,51 +113,6 @@ async function main() {
|
|||||||
else unchanged++
|
else unchanged++
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
// PHASE 3: Sync des règles (optionnel)
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
if (defaultCategoryRules.length > 0) {
|
|
||||||
console.log("\n" + "═".repeat(50))
|
|
||||||
console.log("PHASE 3: Règles de catégorisation")
|
|
||||||
console.log("═".repeat(50))
|
|
||||||
|
|
||||||
let rulesCreated = 0
|
|
||||||
let rulesSkipped = 0
|
|
||||||
|
|
||||||
for (const rule of defaultCategoryRules) {
|
|
||||||
const categoryId = slugToId.get(rule.categorySlug)
|
|
||||||
if (!categoryId) {
|
|
||||||
console.log(`⚠️ Catégorie introuvable pour règle: ${rule.categorySlug}`)
|
|
||||||
rulesSkipped++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier si la règle existe déjà
|
|
||||||
const existing = await prisma.categoryRule.findFirst({
|
|
||||||
where: {
|
|
||||||
categoryId,
|
|
||||||
pattern: rule.pattern,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!existing) {
|
|
||||||
await prisma.categoryRule.create({
|
|
||||||
data: {
|
|
||||||
categoryId,
|
|
||||||
pattern: rule.pattern,
|
|
||||||
isRegex: rule.isRegex,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
console.log(`✅ Règle créée: ${rule.pattern.substring(0, 40)}...`)
|
|
||||||
rulesCreated++
|
|
||||||
} else {
|
|
||||||
rulesSkipped++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`\n📊 Règles: ${rulesCreated} créées, ${rulesSkipped} existantes`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// RÉSUMÉ
|
// RÉSUMÉ
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -172,12 +127,10 @@ async function main() {
|
|||||||
const totalCategories = await prisma.category.count()
|
const totalCategories = await prisma.category.count()
|
||||||
const parentCount = await prisma.category.count({ where: { parentId: null } })
|
const parentCount = await prisma.category.count({ where: { parentId: null } })
|
||||||
const childCount = await prisma.category.count({ where: { NOT: { parentId: null } } })
|
const childCount = await prisma.category.count({ where: { NOT: { parentId: null } } })
|
||||||
const totalRules = await prisma.categoryRule.count()
|
|
||||||
const totalKeywords = defaultCategories.reduce((sum, c) => sum + c.keywords.length, 0)
|
const totalKeywords = defaultCategories.reduce((sum, c) => sum + c.keywords.length, 0)
|
||||||
|
|
||||||
console.log("\n📈 Base de données:")
|
console.log("\n📈 Base de données:")
|
||||||
console.log(` Total catégories: ${totalCategories} (${parentCount} parents, ${childCount} enfants)`)
|
console.log(` Total catégories: ${totalCategories} (${parentCount} parents, ${childCount} enfants)`)
|
||||||
console.log(` Total règles: ${totalRules}`)
|
|
||||||
console.log(` Total keywords: ${totalKeywords}`)
|
console.log(` Total keywords: ${totalKeywords}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { prisma } from "@/lib/prisma"
|
import { prisma } from "@/lib/prisma"
|
||||||
import type { BankingData, Account, Transaction, Folder, Category, CategoryRule } from "@/lib/types"
|
import type { BankingData, Account, Transaction, Folder, Category } from "@/lib/types"
|
||||||
|
|
||||||
export const bankingService = {
|
export const bankingService = {
|
||||||
async getAllData(): Promise<BankingData> {
|
async getAllData(): Promise<BankingData> {
|
||||||
const [accounts, transactions, folders, categories, categoryRules] = await Promise.all([
|
const [accounts, transactions, folders, categories] = await Promise.all([
|
||||||
prisma.account.findMany({
|
prisma.account.findMany({
|
||||||
include: {
|
include: {
|
||||||
folder: true,
|
folder: true,
|
||||||
@@ -17,7 +17,6 @@ export const bankingService = {
|
|||||||
}),
|
}),
|
||||||
prisma.folder.findMany(),
|
prisma.folder.findMany(),
|
||||||
prisma.category.findMany(),
|
prisma.category.findMany(),
|
||||||
prisma.categoryRule.findMany(),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
// Transform Prisma models to match our types
|
// Transform Prisma models to match our types
|
||||||
@@ -61,12 +60,6 @@ export const bankingService = {
|
|||||||
keywords: JSON.parse(c.keywords) as string[],
|
keywords: JSON.parse(c.keywords) as string[],
|
||||||
parentId: c.parentId,
|
parentId: c.parentId,
|
||||||
})),
|
})),
|
||||||
categoryRules: categoryRules.map((r): CategoryRule => ({
|
|
||||||
id: r.id,
|
|
||||||
categoryId: r.categoryId,
|
|
||||||
pattern: r.pattern,
|
|
||||||
isRegex: r.isRegex,
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user