MAJ
This commit is contained in:
1 parent
27c4c57a58
commit
6bba34396c
3 files changed
+313
-41
No files matched your search
@@ -1,9 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import db from '../db/index.js';
|
||||
import { HttpError } from '../middleware/errorHandler.js';
|
||||
import { requireInvestisseur } from '../middleware/investisseurScope.js';
|
||||
import { resolveActiveWorkspaceId } from '../middleware/workspaceScope.js';
|
||||
import { createZip, sanitizeZipPart } from '../utils/zip.js';
|
||||
import { docsDir } from './documents.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -102,6 +106,7 @@ router.get('/', (req, res) => {
|
||||
const rows = db.prepare(`
|
||||
SELECT ipe.*,
|
||||
p.nom AS plateforme_nom,
|
||||
p.logo_filename AS plateforme_logo,
|
||||
inv.nom AS investisseur_nom,
|
||||
(SELECT v.valorisation_nette FROM investissements_pe_valorisations v
|
||||
WHERE v.investissement_pe_id = ipe.id
|
||||
@@ -211,6 +216,7 @@ router.get('/:id', (req, res, next) => {
|
||||
const row = db.prepare(`
|
||||
SELECT ipe.*,
|
||||
p.nom AS plateforme_nom,
|
||||
p.logo_filename AS plateforme_logo,
|
||||
inv.nom AS investisseur_nom,
|
||||
(SELECT v.valorisation_nette FROM investissements_pe_valorisations v
|
||||
WHERE v.investissement_pe_id = ipe.id
|
||||
@@ -240,6 +246,102 @@ router.get('/:id', (req, res, next) => {
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/investissements-pe/:id/export — dossier ZIP d'un deal PE (18/09/26, demande
|
||||
* Olivier — menu "⋮" de la fiche détail, inspiré du même menu côté crowdlending). Même
|
||||
* principe que GET /investissements/:id/export (manifest.json + documents/), mais le
|
||||
* contenu du manifeste reflète le modèle de données PE, plus réduit : pas d'échéancier
|
||||
* prévisionnel, pas d'historique de modifications, pas de révisions de conditions, pas de
|
||||
* pertes ni réinvestissements pour cette table (concepts qui n'existent pas côté PE) — en
|
||||
* repli, valorisations + remboursements (distributions) + frais_operations + documents.
|
||||
*/
|
||||
router.get('/:id/export', (req, res, next) => {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
ownedDeal(req, id);
|
||||
const meta = db.prepare(`
|
||||
SELECT ipe.*, p.nom AS plateforme_nom, inv.nom AS investisseur_nom
|
||||
FROM investissements_pe ipe
|
||||
JOIN plateformes p ON p.id = ipe.plateforme_id
|
||||
JOIN investisseurs inv ON inv.id = ipe.investisseur_id
|
||||
WHERE ipe.id = ?
|
||||
`).get(id);
|
||||
|
||||
const valorisations = db.prepare(
|
||||
'SELECT date_valorisation, valorisation_nette, capital_deploye, plus_value_latente, notes FROM investissements_pe_valorisations WHERE investissement_pe_id = ? ORDER BY date_valorisation ASC, id ASC'
|
||||
).all(id);
|
||||
const remboursements = db.prepare(
|
||||
'SELECT date_remb, capital, cashback, interets_bruts, prelev_sociaux, prelev_forfaitaire, interets_nets, net_recu, statut, distribution_nature, notes FROM remboursements WHERE investissement_pe_id = ? ORDER BY date_remb ASC'
|
||||
).all(id);
|
||||
const frais = db.prepare(
|
||||
'SELECT date_operation, montant, mode_reglement, origine, categorie, notes FROM frais_operations WHERE investissement_pe_id = ? ORDER BY date_operation ASC'
|
||||
).all(id);
|
||||
const documents = db.prepare(
|
||||
"SELECT * FROM documents WHERE user_id = ? AND entity_type = 'investissement_pe' AND entity_id = ? ORDER BY created_at ASC"
|
||||
).all(req.user.id, id);
|
||||
|
||||
// Fichiers du zip + métadonnées correspondantes dans le manifeste — même logique de
|
||||
// dédoublonnage des noms que l'export crowdlending (cf. investissements.js).
|
||||
const usedNames = new Set();
|
||||
const documentsMeta = [];
|
||||
const entries = [];
|
||||
for (const doc of documents) {
|
||||
const baseLabel = sanitizeZipPart(doc.nom_affichage);
|
||||
let finalName = `${baseLabel}.${doc.extension}`;
|
||||
let n = 2;
|
||||
while (usedNames.has(finalName)) { finalName = `${baseLabel} (${n}).${doc.extension}`; n++; }
|
||||
usedNames.add(finalName);
|
||||
const zipPath = `documents/${finalName}`;
|
||||
|
||||
documentsMeta.push({
|
||||
nom_affichage: doc.nom_affichage,
|
||||
nom_original: doc.nom_original,
|
||||
extension: doc.extension,
|
||||
mime_type: doc.mime_type,
|
||||
taille_octets: doc.taille_octets,
|
||||
categorie: doc.categorie,
|
||||
zip_path: zipPath,
|
||||
});
|
||||
|
||||
const filePath = path.join(docsDir, doc.filename);
|
||||
if (fs.existsSync(filePath)) entries.push({ name: zipPath, data: fs.readFileSync(filePath) });
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
version: '1.0',
|
||||
type: 'dossier_investissement_pe',
|
||||
exported_at: new Date().toISOString(),
|
||||
investissement_pe: {
|
||||
nom_deal: meta.nom_deal,
|
||||
strategie: meta.strategie,
|
||||
date_souscription: meta.date_souscription,
|
||||
duree_mois: meta.duree_mois,
|
||||
montant_investi: meta.montant_investi,
|
||||
capital_investissable: meta.capital_investissable,
|
||||
objectif: meta.objectif,
|
||||
statut: meta.statut,
|
||||
reference: meta.reference,
|
||||
notes: meta.notes,
|
||||
},
|
||||
plateforme: { nom: meta.plateforme_nom },
|
||||
investisseur: { nom: meta.investisseur_nom },
|
||||
valorisations,
|
||||
remboursements,
|
||||
frais_operations: frais,
|
||||
documents: documentsMeta,
|
||||
};
|
||||
|
||||
entries.unshift({ name: 'manifest.json', data: JSON.stringify(manifest, null, 2) });
|
||||
|
||||
const zipBuf = createZip(entries);
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||
const filename = `Dossier_PE_${id}_${ts}.zip`;
|
||||
res.setHeader('Content-Type', 'application/zip');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
||||
res.send(zipBuf);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
router.put('/:id', requireInvestisseur, (req, res, next) => {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
|
||||
Reference in new issue
Block a user