#49 feat: graficos de provenance en columnas por nivel y aristas deduplicadas

This commit is contained in:
2026-08-16 02:21:41 -05:00
parent 2967ea4f4d
commit 5c91e66429
6 changed files with 166 additions and 25 deletions

View File

@@ -139,4 +139,44 @@ describe('ProductSupplierOrganizationChart', () => {
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(false)
})
it('alinea los productos en una columna vertical fija y centrada', () => {
const provenance = [
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'A' } }] },
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 6, name: 'B' } }] },
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 7, name: 'C' } }] },
]
const wrapper = mountChart({ provenance })
const products = visChart(wrapper).props('nodes').filter(node => node.kind === 'product')
expect(products).toHaveLength(3)
expect(new Set(products.map(node => node.x)).size).toBe(1)
expect(products.map(node => node.y)).toEqual([-110, 0, 110])
products.forEach(node => {
expect(node.fixed).toEqual({ x: true, y: true })
})
})
it('alinea proveedores y organizaciones en columnas por nivel', () => {
const wrapper = mountChart()
const supplier = visChart(wrapper).props('nodes').find(node => node.id === 'supplier:5')
expect(supplier.x).toBe(0)
expect(supplier.fixed).toEqual({ x: true, y: false })
expect(supplier.y).toBeUndefined()
const organization = visChart(wrapper).props('nodes').find(node => node.id === 'organization:3')
expect(organization.x).toBe(240)
expect(organization.fixed).toEqual({ x: true, y: false })
})
it('el punto de disyunción queda a la misma altura que su producto', () => {
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
const junction = visChart(wrapper).props('nodes').find(node => node.id === 'junction:1')
const product = visChart(wrapper).props('nodes').find(node => node.id === 'product:1')
expect(junction.x).toBe(-120)
expect(junction.y).toBe(product.y)
expect(junction.fixed).toEqual({ x: true, y: true })
})
})

View File

@@ -63,7 +63,7 @@ describe('ProductTerritoryChart', () => {
vi.clearAllMocks()
})
it('renderiza el gráfico con municipio, departamento y país', () => {
it('renderiza el gráfico con municipio y departamento, sin país', () => {
const wrapper = mountChart()
const chart = visChart(wrapper)
@@ -73,7 +73,7 @@ describe('ProductTerritoryChart', () => {
expect(nodeIds).toContain('supplier:5')
expect(nodeIds).toContain('municipality:7')
expect(nodeIds).toContain('department:2')
expect(nodeIds).toContain('country:1')
expect(nodeIds).not.toContain('country:1')
})
it('incluye la etiqueta del nodo y el color según el tipo', () => {
@@ -96,7 +96,20 @@ describe('ProductTerritoryChart', () => {
expect(edges.find(edge => edge.from === 'junction:1' && edge.to === 'supplier:5').dashes).toBe(true)
expect(edges.find(edge => edge.from === 'supplier:5' && edge.to === 'municipality:7').dashes).toBe(true)
expect(edges.find(edge => edge.from === 'municipality:7' && edge.to === 'department:2').dashes).toBe(true)
expect(edges.find(edge => edge.from === 'department:2' && edge.to === 'country:1').dashes).toBe(false)
})
it('con varios productos del mismo proveedor solo traza una línea al municipio', () => {
const provenanceWithRepeatedSupplier = [
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
]
const wrapper = mountChart({ provenance: provenanceWithRepeatedSupplier })
const chart = visChart(wrapper)
const edges = chart.props('edges')
expect(edges.filter(edge => edge.from === 'supplier:5' && edge.to === 'municipality:7')).toHaveLength(1)
expect(chart.props('nodes').filter(node => node.id === 'municipality:7')).toHaveLength(1)
})
it('muestra mensaje de próximamente cuando ningún proveedor tiene territorio', () => {
@@ -121,4 +134,13 @@ describe('ProductTerritoryChart', () => {
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(true)
expect(document.body.textContent).toContain('La Mesa')
})
it('alinea municipios y departamentos en columnas por nivel', () => {
const wrapper = mountChart()
const nodes = visChart(wrapper).props('nodes')
expect(nodes.find(node => node.id === 'supplier:5').x).toBe(0)
expect(nodes.find(node => node.id === 'municipality:7').x).toBe(240)
expect(nodes.find(node => node.id === 'department:2').x).toBe(480)
})
})

View File

@@ -87,6 +87,26 @@ describe('hasAnyTerritory', () => {
const noSuppliers = [{ product: { id: 1, name: 'Panela' }, suppliers: [] }]
expect(hasAnyTerritory(noSuppliers)).toBe(false)
})
it('es true cuando hay municipio aunque el país venga null', () => {
const data = [
{
product: { id: 1, name: 'Panela' },
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: null, country: null }],
},
]
expect(hasAnyTerritory(data)).toBe(true)
})
it('es false cuando solo hay país', () => {
const data = [
{
product: { id: 1, name: 'Panela' },
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: null, department: null, country: { id: 1, name: 'Colombia' } }],
},
]
expect(hasAnyTerritory(data)).toBe(false)
})
})
describe('buildSupplierOrganizationGraph', () => {
@@ -146,7 +166,7 @@ describe('buildSupplierOrganizationGraph', () => {
expect(graph.edges).toEqual([{ from: 'product:1', to: 'supplier:5', certain: true }])
})
it('no duplica nodos que se repiten entre productos', () => {
it('no duplica nodos ni aristas cuando se repiten entre productos', () => {
const data = [
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: { id: 3, name: 'Red' } }] },
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: { id: 3, name: 'Red' } }] },
@@ -155,7 +175,19 @@ describe('buildSupplierOrganizationGraph', () => {
expect(graph.nodes.filter(n => n.id === 'supplier:5')).toHaveLength(1)
expect(graph.nodes.filter(n => n.id === 'organization:3')).toHaveLength(1)
expect(graph.edges).toHaveLength(4)
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'organization:3')).toHaveLength(1)
expect(graph.edges).toHaveLength(3)
})
it('si el mismo par de nodos repite con distinta certeza, gana la duda', () => {
const data = [
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'A' }, organization: { id: 3, name: 'Red' } }] },
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'A' }, organization: { id: 3, name: 'Red' } }, { supplier: { id: 6, name: 'B' }, organization: { id: 4, name: 'Coop' } }] },
]
const graph = buildSupplierOrganizationGraph(data)
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'organization:3')).toHaveLength(1)
expect(edgeOf(graph, 'supplier:5', 'organization:3').certain).toBe(false)
})
it('no incluye posiciones de layout', () => {
@@ -181,7 +213,25 @@ describe('buildTerritoryGraph', () => {
expect(edgeOf(graph, 'product:1', 'supplier:5')).toEqual({ from: 'product:1', to: 'supplier:5', certain: true })
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toEqual({ from: 'supplier:5', to: 'municipality:7', certain: true })
expect(edgeOf(graph, 'municipality:7', 'department:2')).toEqual({ from: 'municipality:7', to: 'department:2', certain: true })
expect(edgeOf(graph, 'department:2', 'country:1')).toEqual({ from: 'department:2', to: 'country:1', certain: true })
})
it('no incluye nodos ni aristas de país', () => {
const graph = buildTerritoryGraph(singleSupplier)
expect(graph.nodes.map(n => n.id)).not.toContain('country:1')
expect(graph.edges.some(edge => edge.from.startsWith('country:') || edge.to.startsWith('country:'))).toBe(false)
})
it('con varios productos del mismo proveedor solo traza una línea hacia el municipio', () => {
const data = [
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
]
const graph = buildTerritoryGraph(data)
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'municipality:7')).toHaveLength(1)
expect(graph.nodes.filter(n => n.id === 'municipality:7')).toHaveLength(1)
})
it('cuando los proveedores comparten municipio, el territorio es cierto desde allí', () => {
@@ -194,7 +244,6 @@ describe('buildTerritoryGraph', () => {
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toEqual({ from: 'supplier:5', to: 'municipality:7', certain: true })
expect(edgeOf(graph, 'supplier:6', 'municipality:7')).toEqual({ from: 'supplier:6', to: 'municipality:7', certain: true })
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(true)
expect(edgeOf(graph, 'department:2', 'country:1').certain).toBe(true)
})
it('municipios distintos pero mismo departamento: dudoso hasta el municipio y cierto desde el departamento', () => {
@@ -206,18 +255,15 @@ describe('buildTerritoryGraph', () => {
expect(edgeOf(graph, 'supplier:6', 'municipality:8').certain).toBe(false)
expect(edgeOf(graph, 'municipality:7', 'department:2')).toEqual({ from: 'municipality:7', to: 'department:2', certain: true })
expect(edgeOf(graph, 'municipality:8', 'department:2')).toEqual({ from: 'municipality:8', to: 'department:2', certain: true })
expect(edgeOf(graph, 'department:2', 'country:1').certain).toBe(true)
})
it('municipios y departamentos distintos mantienen la duda, y el país común es cierto', () => {
it('municipios y departamentos distintos mantienen la duda', () => {
const graph = buildTerritoryGraph(twoSuppliers())
expect(edgeOf(graph, 'supplier:5', 'municipality:7').certain).toBe(false)
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(false)
expect(edgeOf(graph, 'supplier:6', 'municipality:8').certain).toBe(false)
expect(edgeOf(graph, 'municipality:8', 'department:3').certain).toBe(false)
expect(edgeOf(graph, 'department:2', 'country:1')).toEqual({ from: 'department:2', to: 'country:1', certain: true })
expect(edgeOf(graph, 'department:3', 'country:1')).toEqual({ from: 'department:3', to: 'country:1', certain: true })
})
it('no crea nodos de territorio cuando el proveedor no tiene municipio', () => {