Initial commit: SecourIT Gestion
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
Reference in New Issue
Block a user