@sankhyalabs/core 7.1.1-rc.1 → 7.2.1-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/classes/ColumnFilterManager.md +6 -6
- package/dist/utils/ColumnFilterManager.js +17 -1
- package/dist/utils/ColumnFilterManager.js.map +1 -1
- package/package.json +1 -1
- package/reports/test-report.xml +341 -338
- package/src/utils/ColumnFilterManager.ts +16 -2
- package/test/util/ColumnFilterManager.spec.ts +59 -1
|
@@ -3,6 +3,7 @@ import DataUnit from '../dataunit/DataUnit.js';
|
|
|
3
3
|
import { Record } from '../dataunit/interfaces/dataUnit.js';
|
|
4
4
|
import { FieldComparator } from '../dataunit/sorting/FieldComparator.js';
|
|
5
5
|
import { LoadDataRequest } from '../dataunit/loading/LoadDataRequest.js';
|
|
6
|
+
import { DataType } from '../dataunit/metadata/DataType.js';
|
|
6
7
|
|
|
7
8
|
const COLUMN_FILTER_PATTERN = /FILTRO_COLUNA_(.+)/;
|
|
8
9
|
|
|
@@ -46,13 +47,26 @@ export class ColumnFilterManager {
|
|
|
46
47
|
if (!columnFilter.params || !fieldDescriptor) return false;
|
|
47
48
|
|
|
48
49
|
const fieldValue = record[columnFilter.columnName];
|
|
50
|
+
/**
|
|
51
|
+
* Casa datas pelo valor FORMATADO — a mesma granularidade que o funil exibe/agrupa
|
|
52
|
+
* (dia p/ data pura, minuto p/ DATETIME). Nem por dia nem por timestamp exato. KB-171175.
|
|
53
|
+
*/
|
|
54
|
+
const isDate = fieldDescriptor.dataType === DataType.DATE;
|
|
49
55
|
|
|
50
56
|
for (let param of columnFilter.params) {
|
|
51
57
|
const paramValue = dataUnit.valueFromString(columnFilter.columnName, param.value);
|
|
52
|
-
|
|
58
|
+
let matches: boolean;
|
|
59
|
+
if (isDate) {
|
|
60
|
+
matches = (fieldValue == undefined || paramValue == undefined)
|
|
61
|
+
? fieldValue == paramValue
|
|
62
|
+
: dataUnit.getFormattedValue(columnFilter.columnName, fieldValue)
|
|
63
|
+
=== dataUnit.getFormattedValue(columnFilter.columnName, paramValue);
|
|
64
|
+
} else {
|
|
65
|
+
matches = FieldComparator.compareValues(fieldDescriptor, fieldValue, paramValue) === 0;
|
|
66
|
+
}
|
|
67
|
+
if (matches) {
|
|
53
68
|
return true;
|
|
54
69
|
}
|
|
55
|
-
|
|
56
70
|
}
|
|
57
71
|
return false;
|
|
58
72
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ColumnFilterManager, IColumnFilter } from '../../src/utils/ColumnFilterManager.js';
|
|
2
|
-
import { FieldDescriptor, Filter, FilterParam } from '../../src/dataunit/metadata/UnitMetadata.js';
|
|
2
|
+
import { FieldDescriptor, Filter, FilterParam, UserInterface } from '../../src/dataunit/metadata/UnitMetadata.js';
|
|
3
|
+
import { DataType } from '../../src/dataunit/metadata/DataType.js';
|
|
3
4
|
import DataUnit from '../../src/dataunit/DataUnit';
|
|
4
5
|
import { Record } from '../../src/dataunit/interfaces/dataUnit';
|
|
5
6
|
|
|
@@ -82,6 +83,63 @@ describe('ColumnFilterManager', () => {
|
|
|
82
83
|
const record: Record = { field1: 'test' } as unknown as Record;
|
|
83
84
|
expect(filterFunction!(record)).toBe(false);
|
|
84
85
|
});
|
|
86
|
+
|
|
87
|
+
describe('granularidade de data (KB-171175)', () => {
|
|
88
|
+
// Formata na granularidade EXIBIDA do campo, como o funil faz via getFormattedValue:
|
|
89
|
+
// DATE -> dia; DATETIME -> minuto. Usa toISOString (sempre UTC) p/ ser estável ao fuso do runner.
|
|
90
|
+
const formatterFor = (field: FieldDescriptor) => (_col: string, value: any) => {
|
|
91
|
+
if (value == undefined) return null;
|
|
92
|
+
const iso = new Date(value).toISOString();
|
|
93
|
+
return field.userInterface === UserInterface.DATE ? iso.slice(0, 10) : iso.slice(0, 16);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const buildDataUnit = (field: FieldDescriptor): DataUnit => ({
|
|
97
|
+
getField: jest.fn().mockReturnValue(field),
|
|
98
|
+
// O funil envia a data selecionada; devolvemos como Date, como faz o DataUnit real.
|
|
99
|
+
valueFromString: jest.fn().mockImplementation((_col: string, value: any) => new Date(value)),
|
|
100
|
+
getFormattedValue: jest.fn().mockImplementation(formatterFor(field)),
|
|
101
|
+
} as unknown as DataUnit);
|
|
102
|
+
|
|
103
|
+
const buildFilter = (...values: string[]): IColumnFilter[] => ([{
|
|
104
|
+
columnName: 'dt',
|
|
105
|
+
name: 'FILTRO_COLUNA_dt',
|
|
106
|
+
expression: '',
|
|
107
|
+
params: values.map(value => ({ value } as FilterParam)),
|
|
108
|
+
}]);
|
|
109
|
+
|
|
110
|
+
it('data PURA (DATE): casa por dia (granularidade exibida) — alinhado ao onlydate da exportação', () => {
|
|
111
|
+
const dataUnit = buildDataUnit({ name: 'dt', dataType: DataType.DATE, userInterface: UserInterface.DATE } as FieldDescriptor);
|
|
112
|
+
// Seleção do funil no nível de dia (03/08).
|
|
113
|
+
const filterFunction = ColumnFilterManager.getFilterFunction(dataUnit, buildFilter('2025-08-03T00:00:00Z'));
|
|
114
|
+
|
|
115
|
+
// Registros do mesmo dia, com horas diferentes, devem casar.
|
|
116
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:18:00Z') } as unknown as Record)).toBe(true);
|
|
117
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:22:00Z') } as unknown as Record)).toBe(true);
|
|
118
|
+
// Dia diferente não casa.
|
|
119
|
+
expect(filterFunction!({ dt: new Date('2025-08-04T09:18:00Z') } as unknown as Record)).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('data+hora (DATETIME): casa pelo minuto exibido — distingue 09:18 de 09:22', () => {
|
|
123
|
+
const dataUnit = buildDataUnit({ name: 'dt', dataType: DataType.DATE, userInterface: UserInterface.DATETIME } as FieldDescriptor);
|
|
124
|
+
// Funil com apenas 09:22 marcado; o param carrega o timestamp representativo (09:22:39).
|
|
125
|
+
const filterFunction = ColumnFilterManager.getFilterFunction(dataUnit, buildFilter('2025-08-03T09:22:39Z'));
|
|
126
|
+
|
|
127
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:22:00Z') } as unknown as Record)).toBe(true);
|
|
128
|
+
// O título das 09:18 deve sair da grade.
|
|
129
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:18:49Z') } as unknown as Record)).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('data+hora (DATETIME): mantém registros IRMÃOS do mesmo minuto com segundos diferentes (regressão KB-171175)', () => {
|
|
133
|
+
const dataUnit = buildDataUnit({ name: 'dt', dataType: DataType.DATE, userInterface: UserInterface.DATETIME } as FieldDescriptor);
|
|
134
|
+
// A opção "09:22" no funil = 1 param representativo (09:22:39), mas há irmãos :51/:55.
|
|
135
|
+
const filterFunction = ColumnFilterManager.getFilterFunction(dataUnit, buildFilter('2025-08-03T09:22:39Z'));
|
|
136
|
+
|
|
137
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:22:39Z') } as unknown as Record)).toBe(true);
|
|
138
|
+
// Casamento exato descartaria estes; por minuto exibido eles PERMANECEM.
|
|
139
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:22:51Z') } as unknown as Record)).toBe(true);
|
|
140
|
+
expect(filterFunction!({ dt: new Date('2025-08-03T09:22:55Z') } as unknown as Record)).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
85
143
|
});
|
|
86
144
|
|
|
87
145
|
|