Initial commit: SecourIT Gestion

This commit is contained in:
2026-08-23 09:06:18 +00:00
commit cab93c7f7c
85 changed files with 15801 additions and 0 deletions
+253
View File
@@ -0,0 +1,253 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
passwordHash String?
role String @default("admin")
infomaniakId String? @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
quotes Quote[]
contracts Contract[]
clients Client[]
sales Sale[]
todos TodoItem[]
}
model Session {
id String @id @default(cuid())
userId String
token String @unique
expiresAt DateTime
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([token])
}
model Client {
id String @id @default(cuid())
name String
email String?
phone String?
address String?
siret String?
type ClientType @default(PROSPECT)
isProfessional Boolean? @default(true)
notes String?
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
quotes Quote[]
contracts Contract[]
sales Sale[]
@@index([userId])
@@index([type])
}
enum ClientType {
PROSPECT
CLIENT
}
model Quote {
id String @id @default(cuid())
clientId String
userId String
name String
quoteNumber String?
status QuoteStatus @default(DRAFT)
setupTotal Float @default(0)
monthlyTotal Float @default(0)
costTotal Float @default(0)
marginTotal Float @default(0)
discount Float @default(0)
travelKm Int @default(0)
travelCost Float @default(0)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
client Client @relation(fields: [clientId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
items QuoteItem[]
contract Contract?
@@index([clientId])
@@index([userId])
@@index([status])
}
enum QuoteStatus {
DRAFT
PROPOSAL
SENT
ACCEPTED
REJECTED
EXPIRED
}
model QuoteItem {
id String @id @default(cuid())
quoteId String
type QuoteItemType
label String
description String?
unitPrice Float
costPrice Float
quantity Int @default(1)
isRecurring Boolean @default(false)
setupPrice Float @default(0)
setupCost Float @default(0)
monthlyPrice Float @default(0)
monthlyCost Float @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade)
@@index([quoteId])
}
enum QuoteItemType {
MAINTENANCE_POSTE
MAINTENANCE_SERVER
KS_SUITE_STANDARD
KS_SUITE_BUSINESS
KS_SUITE_ENTREPRISE
HOURLY_INTERVENTION
TRAVEL
CUSTOM
}
model Contract {
id String @id @default(cuid())
clientId String
userId String
quoteId String? @unique
name String
startDate DateTime
endDate DateTime?
setupPrice Float @default(0)
setupCost Float @default(0)
monthlyPrice Float @default(0)
monthlyCost Float @default(0)
status ContractStatus @default(ACTIVE)
billingDay Int @default(1)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
client Client @relation(fields: [clientId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
quote Quote? @relation(fields: [quoteId], references: [id], onDelete: SetNull)
invoices Invoice[]
@@index([clientId])
@@index([userId])
@@index([status])
}
enum ContractStatus {
PROPOSAL
ACTIVE
PAUSED
CANCELLED
EXPIRED
}
model Invoice {
id String @id @default(cuid())
contractId String
month DateTime
amount Float
invoiceNumber String?
emitted Boolean @default(false)
emittedAt DateTime?
paidAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
@@index([contractId])
@@unique([contractId, month])
}
model TodoItem {
id String @id @default(cuid())
userId String
text String
position Int
done Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model Sale {
id String @id @default(cuid())
clientId String
userId String
date DateTime @default(now())
totalAmount Float @default(0)
totalCost Float @default(0)
paid Boolean @default(false)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
client Client @relation(fields: [clientId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
items SaleItem[]
@@index([clientId])
@@index([userId])
}
model SaleItem {
id String @id @default(cuid())
saleId String
label String
description String?
quantity Int @default(1)
unitPrice Float @default(0)
costPrice Float @default(0)
createdAt DateTime @default(now())
sale Sale @relation(fields: [saleId], references: [id], onDelete: Cascade)
@@index([saleId])
}
model ProductCost {
id String @id @default(cuid())
key String @unique
label String
costPrice Float
sellPrice Float
unit String
category String
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+93
View File
@@ -0,0 +1,93 @@
const { PrismaClient } = require('@prisma/client')
const bcrypt = require('bcryptjs')
const prisma = new PrismaClient()
async function main() {
const passwordHash = await bcrypt.hash('demo123', 12)
const user = await prisma.user.upsert({
where: { email: 'demo@secourit.fr' },
update: {},
create: {
email: 'demo@secourit.fr',
name: 'Démo',
passwordHash,
role: 'demo',
},
})
console.log('Demo user created:', user.email)
// Nettoyer les anciennes données démo
await prisma.contract.deleteMany({ where: { userId: user.id } })
await prisma.quote.deleteMany({ where: { userId: user.id } })
await prisma.client.deleteMany({ where: { userId: user.id } })
// Créer des clients
const clients = await Promise.all([
prisma.client.create({
data: { name: 'SARL Dupont Informatique', email: 'contact@dupont-info.fr', phone: '01 23 45 67 89', address: '12 Rue de Paris, 75001 Paris', siret: '12345678900012', type: 'CLIENT', notes: 'Client principal', userId: user.id },
}),
prisma.client.create({
data: { name: 'Cabinet Médical Martin', email: 'secretariat@cabinet-martin.fr', phone: '01 98 76 54 32', address: '5 Avenue des Lilas, 69001 Lyon', siret: '98765432100045', type: 'CLIENT', notes: '3 postes + 1 serveur', userId: user.id },
}),
prisma.client.create({
data: { name: 'Boulangerie Le Pain Doré', email: 'contact@lepaindore.fr', phone: '03 20 15 30 45', address: '8 Rue du Four, 59000 Lille', siret: '45678912300078', type: 'CLIENT', notes: '1 poste, maintenance simple', userId: user.id },
}),
prisma.client.create({
data: { name: 'SCI Bellevue', email: 'gestion@scibellevue.com', phone: '04 91 23 45 67', address: '15 Boulevard de la Mer, 13008 Marseille', siret: '32165498700023', type: 'PROSPECT', notes: 'Projet 5 postes + KSuite', userId: user.id },
}),
prisma.client.create({
data: { name: 'Restaurant Le Gourmet', email: 'contact@legourmet.fr', phone: '02 40 12 34 56', address: '3 Place du Marché, 44000 Nantes', siret: '65412378900056', type: 'PROSPECT', notes: 'A besoin de 2 caisses', userId: user.id },
}),
prisma.client.create({
data: { name: 'École Privée Saint-Joseph', email: 'direction@saint-joseph.fr', phone: '05 56 78 90 12', address: '20 Rue de lÉducation, 33000 Bordeaux', siret: '14725836900089', type: 'CLIENT', notes: '10 postes, 2 serveurs, KSuite Business 15 utilisateurs', userId: user.id },
}),
])
console.log('Clients created:', clients.length)
// Créer des contrats
const contracts = [
{ clientId: clients[0].id, name: 'Contrat maintenance SARL Dupont', startDate: new Date('2025-01-01'), monthlyPrice: 76.80, monthlyCost: 11.20, setupPrice: 500, setupCost: 150, status: 'ACTIVE' },
{ clientId: clients[0].id, name: 'KSuite Business - SARL Dupont (3 users)', startDate: new Date('2025-01-01'), monthlyPrice: 47.40, monthlyCost: 19.74, setupPrice: 0, setupCost: 0, status: 'ACTIVE' },
{ clientId: clients[1].id, name: 'Contrat maintenance Cabinet Médical', startDate: new Date('2025-03-15'), monthlyPrice: 54.00, monthlyCost: 8.60, setupPrice: 800, setupCost: 200, status: 'ACTIVE' },
{ clientId: clients[2].id, name: 'Maintenance Boulangerie', startDate: new Date('2025-06-01'), monthlyPrice: 9.60, monthlyCost: 1.40, setupPrice: 150, setupCost: 50, status: 'ACTIVE' },
{ clientId: clients[5].id, name: 'Contrat global École St-Joseph', startDate: new Date('2025-09-01'), monthlyPrice: 333.00, monthlyCost: 115.70, setupPrice: 2500, setupCost: 800, status: 'ACTIVE' },
{ clientId: clients[1].id, name: 'KSuite Business - Cabinet Médical (2 users)', startDate: new Date('2025-04-01'), monthlyPrice: 31.60, monthlyCost: 13.16, setupPrice: 0, setupCost: 0, status: 'PAUSED' },
]
for (const contract of contracts) {
await prisma.contract.create({
data: { ...contract, userId: user.id, billingDay: 1, notes: null },
})
}
console.log('Contracts created:', contracts.length)
// Créer des devis
const quotes = [
{ clientId: clients[3].id, name: 'Devis SCI Bellevue - 5 postes', status: 'DRAFT', setupTotal: 250, monthlyTotal: 48, costTotal: 7, marginTotal: 41, discount: 0, travelKm: 30, travelCost: 15 },
{ clientId: clients[4].id, name: 'Devis Restaurant Le Gourmet', status: 'SENT', setupTotal: 300, monthlyTotal: 19.20, costTotal: 2.80, marginTotal: 16.40, discount: 10, travelKm: 15, travelCost: 7.50 },
]
for (const quote of quotes) {
await prisma.quote.create({
data: { ...quote, userId: user.id, notes: null },
})
}
console.log('Quotes created:', quotes.length)
console.log('')
console.log('Demo account: demo@secourit.fr / demo123')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
+50
View File
@@ -0,0 +1,50 @@
const { PrismaClient } = require('@prisma/client')
const bcrypt = require('bcryptjs')
const prisma = new PrismaClient()
async function main() {
const passwordHash = await bcrypt.hash('admin123', 12)
const user = await prisma.user.upsert({
where: { email: 'pierre@secourit.fr' },
update: {},
create: {
email: 'pierre@secourit.fr',
name: 'Pierre',
passwordHash,
role: 'admin',
},
})
console.log('User created:', user.email)
const defaultProducts = [
{ key: 'maintenance-poste', label: 'Maintenance Poste (Windows/Mac)', costPrice: 1.40, sellPrice: 9.60, unit: 'mois', category: 'maintenance' },
{ key: 'maintenance-serveur', label: 'Maintenance Serveur', costPrice: 4.20, sellPrice: 25.20, unit: 'mois', category: 'maintenance' },
{ key: 'ksuite-business', label: 'KSuite Business / utilisateur', costPrice: 6.58, sellPrice: 15.80, unit: 'mois', category: 'ksuite' },
{ key: 'ksuite-standard', label: 'KSuite Standard / utilisateur', costPrice: 1.58, sellPrice: 0, unit: 'mois', category: 'ksuite' },
{ key: 'ksuite-entreprise', label: 'KSuite Entreprise / utilisateur', costPrice: 12.41, sellPrice: 0, unit: 'mois', category: 'ksuite' },
{ key: 'intervention-horaire', label: 'Intervention (Taux horaire)', costPrice: 0, sellPrice: 50, unit: 'heure', category: 'hourly' },
{ key: 'deplacement-km', label: 'Déplacement (par km)', costPrice: 0.30, sellPrice: 0.50, unit: 'km', category: 'travel' },
]
for (const product of defaultProducts) {
await prisma.productCost.upsert({
where: { key: product.key },
update: product,
create: product,
})
}
console.log('Default products created')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})