import {
uniqueConstructeurIds,
resolveConstructeurs,
formatConstructeurContact,
} from '~/shared/constructeurUtils'
const currencyFormatter = new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR',
currencyDisplay: 'narrowSymbol',
})
const formatCurrency = (value) => {
if (value === undefined || value === null || value === '') {
return null
}
const number = Number(value)
if (Number.isNaN(number)) {
return null
}
return currencyFormatter.format(number)
}
const formatSize = (size) => {
if (size === undefined || size === null) { return '—' }
if (size === 0) { return '0 B' }
const units = ['B', 'KB', 'MB', 'GB']
const index = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024)))
const formatted = size / Math.pow(1024, index)
return `${formatted.toFixed(1)} ${units[index]}`
}
const renderPrintField = (label, value, fallback = '—') => {
const display = value !== undefined && value !== null && value !== '' ? value : fallback
return `
${display}
`
}
const renderPrintCustomFields = (fields = [], title, sectionClass = 'print-section') => {
if (!fields.length) { return '' }
const items = fields
.map(field => `${field.value || '—'}
`)
.join('')
return `
`
}
const renderPrintDocuments = (documents = [], title, sectionClass = 'print-section') => {
if (!documents.length) { return '' }
const rows = documents
.map(doc => `| ${doc.name} | ${doc.type} | ${doc.size} |
`)
.join('')
return `
`
}
const renderPrintProductSummary = (product, title = 'Produit catalogue', sectionClass = 'print-piece-section') => {
if (!product) { return '' }
const infoEntries = [
{ label: 'Nom', value: product.name || '—' },
{ label: 'Référence', value: product.reference || '—' },
{ label: 'Catégorie', value: product.typeName || '—' },
{
label: 'Prix indicatif',
value: product.supplierPrice || '—',
},
{
label: 'Fournisseur(s)',
value: product.constructeurs?.length
? product.constructeurs.map((constructeur) => constructeur.name).filter(Boolean).join(', ') || '—'
: '—',
},
]
const infoMarkup = infoEntries
.map((field) => `${field.value || '—'}
`)
.join('')
const customFieldsBlock = product.customFields?.length
? renderPrintCustomFields(product.customFields, 'Champs personnalisés du produit', 'print-subsection')
: ''
const documentsBlock = product.documents?.length
? renderPrintDocuments(product.documents, 'Documents du produit', 'print-subsection')
: ''
return `
${title}
${infoMarkup}
${customFieldsBlock}
${documentsBlock}
`
}
const renderPrintPieces = (
pieces = [],
title = 'Pièces indépendantes',
sectionClass = 'print-section print-section--pieces'
) => {
if (!pieces.length) { return '' }
const cards = pieces
.map((piece, idx) => {
const indexLabel = piece.indexPath ? piece.indexPath.join('.') : `${idx + 1}`
const constructeurBadges = (piece.constructeurs || [])
.map((constructeur, badgeIdx) => {
const suffix = piece.constructeurs.length > 1 ? ` ${badgeIdx + 1}` : ''
return `Fournisseur${suffix}: ${constructeur.name}`
})
.join('')
const customFields = (piece.customFields || [])
.filter(field => field.value && field.value !== '—' && field.value !== '')
.map(
field => `
${field.label}
${field.value}
`
)
.join('')
const customFieldsBlock = customFields
? ``
: ''
const documentsBlock = (piece.documents || []).length
? `Documents
${piece.documents
.map(doc => `- ${doc.name} (${doc.type} • ${doc.size})
`)
.join('')}
`
: ''
const productBlock = renderPrintProductSummary(piece.product, 'Produit catalogue')
return `
${piece.description ? `
${piece.description}
` : ''}
${productBlock}
${customFieldsBlock}
${documentsBlock}
`
})
.join('')
return `
`
}
const renderPrintComponents = (components = [], depth = 0, indexPath = []) => {
if (!components.length) { return '' }
return components
.map((component, idx) => {
const badges = []
if (component.constructeurs?.length) {
const label = component.constructeurs.map((constructeur, badgeIdx) => {
const suffix = component.constructeurs.length > 1 ? ` ${badgeIdx + 1}` : ''
return `Constructeur${suffix}: ${constructeur.name}`
})
badges.push(...label)
}
const sectionClass = `print-section print-section--component print-section-depth-${Math.min(depth, 3)}`
const currentIndex = [...indexPath, idx + 1]
const indexLabel = currentIndex.join('.')
const productBlock = renderPrintProductSummary(component.product, 'Produit catalogue', 'print-section print-subsection print-section--product')
return `
${indexLabel}
Composant : ${component.name}
${component.description ? `
${component.description}
` : ''}
${badges.length ? `
${badges.map(badge => `${badge}`).join('')}
` : ''}
${productBlock}
${renderPrintCustomFields(
component.customFields,
'Champs personnalisés',
'print-section print-subsection print-section--custom-fields'
)}
${renderPrintPieces(
(component.pieces || []).map((piece, pieceIdx) => ({ ...piece, indexPath: [...currentIndex, pieceIdx + 1] })),
'Pièces du composant',
'print-section print-subsection print-section--pieces'
)}
${renderPrintDocuments(
component.documents,
'Documents du composant',
'print-section print-subsection print-section--documents'
)}
${renderPrintComponents(component.subComponents || [], depth + 1, currentIndex)}
`
})
.join('')
}
const normalizeDocuments = (docs = []) => {
return docs.map(doc => ({
id: doc.id,
name: doc.name || doc.filename || 'Document',
type: doc.mimeType || doc.type || '—',
size: formatSize(doc.size)
}))
}
const normalizeCustomFields = (values = []) => {
return values.map(value => ({
id: value.id,
label: value.customField?.name || 'Champ',
value: value.value || '—'
}))
}
const normalizeConstructeur = (constructeur) => {
if (!constructeur) { return null }
const contact = formatConstructeurContact(constructeur)
return {
id: constructeur.id || null,
name: constructeur.name || '—',
contact: contact || '—'
}
}
const normalizeConstructeurList = (...sources) => {
const ids = uniqueConstructeurIds(...sources)
const pools = sources
.flatMap((source) => {
if (Array.isArray(source)) {
if (source.length && typeof source[0] === 'object') {
return [source]
}
return []
}
if (source && typeof source === 'object' && 'id' in source) {
return [[source]]
}
return []
})
.filter(Boolean)
const resolved = resolveConstructeurs(ids, ...pools)
return resolved
.map(normalizeConstructeur)
.filter(Boolean)
}
const normalizeProduct = (product) => {
if (!product) { return null }
const constructeurs = normalizeConstructeurList(
product.constructeurs,
product.constructeur,
product.constructeurIds,
product.constructeurId,
)
return {
id: product.id || null,
name: product.name || 'Produit sans nom',
reference: product.reference || '',
supplierPrice: formatCurrency(product.supplierPrice),
typeName: product.typeProduct?.name || null,
constructeurs,
customFields: normalizeCustomFields(product.customFieldValues || []),
documents: normalizeDocuments(product.documents || []),
}
}
const normalizePiece = piece => {
const rawProduct = piece.product || null
const constructeurs = normalizeConstructeurList(
piece.constructeurs,
piece.constructeur,
piece.originalPiece?.constructeurs,
piece.originalPiece?.constructeur,
piece.constructeurIds,
piece.constructeurId,
rawProduct?.constructeurs,
rawProduct?.constructeur,
rawProduct?.constructeurIds,
rawProduct?.constructeurId,
)
const product = normalizeProduct(rawProduct)
return {
id: piece.id,
name: piece.name || 'Pièce sans nom',
description: piece.description || '',
reference: piece.reference || '',
customFields: normalizeCustomFields(piece.customFieldValues || []),
documents: normalizeDocuments(piece.documents || []),
constructeurs,
constructeur: constructeurs[0] || null,
product,
indexPath: piece.indexPath || null
}
}
const normalizeComponent = component => {
const rawProduct = component.product || null
const constructeurs = normalizeConstructeurList(
component.constructeurs,
component.constructeur,
component.originalComposant?.constructeurs,
component.originalComposant?.constructeur,
component.constructeurIds,
component.constructeurId,
rawProduct?.constructeurs,
rawProduct?.constructeur,
rawProduct?.constructeurIds,
rawProduct?.constructeurId,
)
const product = normalizeProduct(rawProduct)
return {
id: component.id,
name: component.name || 'Composant sans nom',
description: component.description || '',
customFields: normalizeCustomFields(component.customFieldValues || []),
documents: normalizeDocuments(component.documents || []),
pieces: (component.pieces || []).map(normalizePiece),
subComponents: (component.sousComposants || component.subComponents || []).map(normalizeComponent),
constructeurs,
constructeur: constructeurs[0] || null,
product,
}
}
export const buildMachinePrintContext = ({
machine,
machineName,
machineReference,
machinePieces = [],
components = [],
selection
}) => {
const selectionState = selection || {}
const machineSelection = selectionState.machine || {}
const componentSelection = selectionState.components || {}
const pieceSelection = selectionState.pieces || {}
const includeMachineInfo = machineSelection.info !== false
const includeMachineCustomFields = machineSelection.customFields !== false
const includeMachineDocuments = machineSelection.documents !== false
const isComponentSelected = (id) => {
if (!id) { return true }
if (Object.prototype.hasOwnProperty.call(componentSelection, id)) {
return componentSelection[id]
}
return true
}
const isPieceSelected = (id) => {
if (!id) { return true }
if (Object.prototype.hasOwnProperty.call(pieceSelection, id)) {
return pieceSelection[id]
}
return true
}
const machineBadges = []
if (machine?.typeMachine?.category) {
machineBadges.push(machine.typeMachine.category)
}
if (machine?.site?.name) {
machineBadges.push(`Site: ${machine.site.name}`)
}
if (machineReference) {
machineBadges.push(`Ref: ${machineReference}`)
}
const machineConstructeurs = normalizeConstructeurList(
machine?.constructeurs,
machine?.constructeur,
machine?.constructeurIds,
machine?.constructeurId,
)
const machineConstructeurNames = machineConstructeurs.length
? machineConstructeurs.map((constructeur) => constructeur.name).join(', ')
: ''
const machineConstructeurContacts = machineConstructeurs.length
? machineConstructeurs
.map((constructeur) => constructeur.contact)
.filter(Boolean)
.join(' • ')
: ''
const normalizedPieces = machinePieces
.map(normalizePiece)
.filter(piece => isPieceSelected(piece.id))
.map((piece, idx) => ({
...piece,
indexPath: [idx + 1]
}))
const normalizedComponents = components.map(normalizeComponent)
const filterComponentTree = (component) => {
const filteredPieces = (component.pieces || []).filter(piece => isPieceSelected(piece.id))
const filteredSubComponents = (component.subComponents || [])
.map(filterComponentTree)
.filter(Boolean)
const includeSelf = isComponentSelected(component.id)
const shouldInclude = includeSelf || filteredPieces.length > 0 || filteredSubComponents.length > 0
if (!shouldInclude) {
return null
}
return {
...component,
pieces: filteredPieces,
subComponents: filteredSubComponents
}
}
const filteredComponents = normalizedComponents
.map(filterComponentTree)
.filter(Boolean)
return {
generatedAt: new Date().toLocaleString('fr-FR'),
machine: {
id: machine?.id || null,
name: machineName,
description: machine?.description || '',
typeDescription: machine?.typeMachine?.description || '',
reference: machineReference,
site: machine?.site?.name || '',
category: machine?.typeMachine?.category || '',
badges: machineBadges,
constructeurs: machineConstructeurs,
constructeur: machineConstructeurs[0] || null,
constructeurNames: machineConstructeurNames,
constructeurContacts: machineConstructeurContacts,
includeInfo: includeMachineInfo,
customFields: includeMachineCustomFields
? normalizeCustomFields(machine?.customFieldValues || [])
: [],
documents: includeMachineDocuments
? normalizeDocuments(machine?.documents || [])
: []
},
components: filteredComponents,
pieces: normalizedPieces
}
}
export const buildMachinePrintHtml = (context, styles) => {
const title = context.machine.name ? `Impression - ${context.machine.name}` : 'Impression machine'
const badgesHtml = context.machine.badges
.map(badge => `${badge}`)
.join('')
const sections = []
sections.push(`
Généré le ${context.generatedAt}
Machine ID: ${context.machine.id || '—'}
`)
if (context.machine.includeInfo) {
sections.push(`
Informations générales
${renderPrintField('Nom', context.machine.name)}
${renderPrintField('Référence', context.machine.reference, 'Non définie')}
${renderPrintField('Site', context.machine.site, 'Non défini')}
${renderPrintField('Constructeur(s)', context.machine.constructeurNames, 'Non défini')}
${renderPrintField('Contact(s) Constructeur(s)', context.machine.constructeurContacts, 'Non défini')}
`)
}
const customFieldsSection = renderPrintCustomFields(
context.machine.customFields,
'Champs personnalisés de la machine',
'print-section print-section--custom-fields'
)
if (customFieldsSection) {
sections.push(customFieldsSection)
}
const documentsSection = renderPrintDocuments(
context.machine.documents,
'Documents liés à la machine',
'print-section print-section--documents'
)
if (documentsSection) {
sections.push(documentsSection)
}
const componentsSection = renderPrintComponents(context.components)
if (componentsSection) {
sections.push(componentsSection)
}
const piecesSection = renderPrintPieces(
context.pieces,
'Pièces indépendantes',
'print-section print-section--pieces'
)
if (piecesSection) {
sections.push(piecesSection)
}
sections.push(`
Rapport généré automatiquement par Inventaire Pro.
`)
const content = sections.join('\n')
return `
${title}
${styles}
${content}
`
}