fix(clients): retourner les vraies factures du client (TODO restant)

Le détail client servait `invoices: []` en hardcoded — la liste sur
/clients/:id était toujours vide. On préload la relation client + plan
sur les factures de l'org filtrées par client_id, on trie par priorité
de statut puis échéance, et on serialize via InvoiceTransformer (même
shape que GET /invoices, donc le SPA peut réutiliser InvoiceListItem).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ordinarthur 2026-05-07 09:43:16 +02:00
parent ec2232e4b3
commit 2c2724c634

View File

@ -1,10 +1,22 @@
import Client from '#models/client'
import Invoice from '#models/invoice'
import ClientTransformer from '#transformers/client_transformer'
import InvoiceTransformer from '#transformers/invoice_transformer'
import { createClientValidator, updateClientValidator } from '#validators/client'
import { bulkComputeClientStats } from '#services/client_stats'
import type { HttpContext } from '@adonisjs/core/http'
import { Exception } from '@adonisjs/core/exceptions'
// Priorité d'affichage : ce qui est actionnable en haut.
const INVOICE_STATUS_PRIORITY: Record<string, number> = {
awaiting_user_confirmation: 0,
in_relance: 1,
pending: 2,
litigation: 3,
paid: 4,
cancelled: 5,
}
/**
* Petite cohérence d'identification orgnisation : si l'utilisateur
* n'en a pas, on est dans un état illégal V1 on bloque ferme.
@ -96,11 +108,27 @@ export default class ClientsController {
const statsMap = await bulkComputeClientStats(organizationId, [client.id])
const stats = statsMap.get(client.id)!
// Factures du client — actionnables en premier, puis échéance asc.
const invoices = await Invoice.query()
.where('organization_id', organizationId)
.where('client_id', client.id)
.preload('client')
.preload('plan')
.exec()
invoices.sort((a, b) => {
const dp =
(INVOICE_STATUS_PRIORITY[a.status] ?? 99) -
(INVOICE_STATUS_PRIORITY[b.status] ?? 99)
if (dp !== 0) return dp
return a.dueDate.toMillis() - b.dueDate.toMillis()
})
return response.json({
data: {
...serializeClient(client),
...stats,
invoices: [], // TODO: brancher quand le domaine Invoice arrive
invoices: invoices.map((inv) => new InvoiceTransformer(inv).toObject()),
},
})
}