@reekon-tools/boldr-utils 1.4.9 → 1.4.11

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.
@@ -1,2 +1,2 @@
1
- import { Formula, Measurement, Units } from '../types/firestore.js';
2
- export declare const calculateFormula: (formula: Formula, formulas: Formula[], columns: Record<string, any>, measurements: Measurement[], unit: Units) => string | number | null;
1
+ import { ColumnConfig, DecimalTolerance, FractionalTolerance, Formula, Measurement, Units } from '../types/firestore.js';
2
+ export declare const calculateFormula: (formula: Formula, formulas: Formula[], columns: Record<string, string>, tableConfig: ColumnConfig[], measurements: Measurement[], unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => string | number | null;
@@ -1,5 +1,7 @@
1
- import { evaluateFormula } from './evaluateFormula.js';
2
- export const calculateFormula = (formula, formulas, columns, measurements, unit) => {
1
+ import { ColumnType, } from '../types/firestore.js';
2
+ import { evaluateFormula, parseMixedNumber } from './evaluateFormula.js';
3
+ import { convertMicrometers } from '../utils/micrometersToUnit.js';
4
+ export const calculateFormula = (formula, formulas, columns, tableConfig, measurements, unit, fractionalTolerance, decimalTolerance) => {
3
5
  // Convert Firestore Map to plain object if necessary
4
6
  let variableToColumnMap = {};
5
7
  if (formula.variableToColumnMap) {
@@ -17,17 +19,29 @@ export const calculateFormula = (formula, formulas, columns, measurements, unit)
17
19
  }
18
20
  // Build valueMap from group.columns and measurementMap
19
21
  const valueMap = {};
20
- // Map column IDs to their actual measurement values
21
22
  for (const [columnId, measurementId] of Object.entries(columns)) {
22
- const measurement = measurements.find((m) => m.id === measurementId);
23
- if (measurement?.value !== undefined) {
23
+ const column = tableConfig.find((c) => c.id === columnId);
24
+ if (!column)
25
+ continue;
26
+ if (column.type === ColumnType.Measurement) {
27
+ const measurement = measurements.find((m) => m.id === measurementId);
28
+ if (!measurement)
29
+ continue;
30
+ const { value, unit: displayUnit } = convertMicrometers(measurement.value, unit, fractionalTolerance, decimalTolerance);
31
+ valueMap[columnId] = parseMixedNumber(value);
32
+ }
33
+ else if (column.type === ColumnType.Angle) {
34
+ const measurement = measurements.find((m) => m.id === measurementId);
35
+ if (!measurement)
36
+ continue;
24
37
  valueMap[columnId] = measurement.value;
25
38
  }
26
- else {
27
- const numericValue = typeof measurementId === 'number'
28
- ? measurementId
29
- : parseFloat(measurementId || '0');
30
- valueMap[columnId] = isNaN(numericValue) ? 0 : numericValue;
39
+ else if (column.type === ColumnType.ConversionTable) {
40
+ const value = measurementId.split(':')[1];
41
+ valueMap[columnId] = parseFloat(value);
42
+ }
43
+ else if (column.type === ColumnType.Number) {
44
+ valueMap[columnId] = parseFloat(measurementId);
31
45
  }
32
46
  }
33
47
  // Convert formulas to the expected FormulaDefinition format
@@ -73,7 +87,7 @@ export const calculateFormula = (formula, formulas, columns, measurements, unit)
73
87
  const result = evaluateFormula({
74
88
  expression: formula.expression,
75
89
  mappings: currentMappings,
76
- valueMap,
90
+ valueMap: valueMap,
77
91
  unit: unit,
78
92
  formulas: formulaDefinitions,
79
93
  });
@@ -17,6 +17,7 @@ export interface FormulaEvaluationOptions {
17
17
  formulas?: FormulaDefinition[];
18
18
  scope?: Record<string, number>;
19
19
  }
20
+ export declare function parseMixedNumber(str: string): number;
20
21
  export declare function evaluateFormula({ expression, mappings, valueMap, unit, formulas, scope, }: FormulaEvaluationOptions): number | string | null;
21
22
  export declare function createFormulaScope({ mappings, valueMap, unit, }: {
22
23
  mappings: Record<string, string>;
@@ -2,6 +2,24 @@ import { Units } from '../types/firestore.js';
2
2
  import { compile, unit as mathUnit } from 'mathjs';
3
3
  // Cache for evaluated formulas to improve performance
4
4
  const formulaCache = new Map();
5
+ export function parseMixedNumber(str) {
6
+ const parts = str.trim().split(' ');
7
+ let whole = 0;
8
+ let fraction = 0;
9
+ if (parts.length === 2) {
10
+ whole = parseFloat(parts[0]);
11
+ const [num, den] = parts[1].split('/').map(Number);
12
+ fraction = num / den;
13
+ }
14
+ else if (parts[0].includes('/')) {
15
+ const [num, den] = parts[0].split('/').map(Number);
16
+ fraction = num / den;
17
+ }
18
+ else {
19
+ whole = parseFloat(parts[0]);
20
+ }
21
+ return whole + fraction;
22
+ }
5
23
  function normalizeUnitForMathJS(unit) {
6
24
  switch (unit) {
7
25
  case Units.Centimeters:
@@ -84,7 +102,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
84
102
  if (micrometers === 0) {
85
103
  console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
86
104
  }
87
- const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
105
+ const valueInUnit = micrometers;
88
106
  scope[variable] = valueInUnit;
89
107
  }
90
108
  else if (mapping.type === 'formula') {
@@ -128,25 +146,20 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
128
146
  // Legacy createFormulaScope function for backward compatibility
129
147
  export function createFormulaScope({ mappings, valueMap, unit, }) {
130
148
  const scope = {};
131
- const normalizedUnit = normalizeUnitForMathJS(unit);
132
149
  for (const [variable, columnId] of Object.entries(mappings)) {
133
150
  const micrometers = valueMap[columnId] ?? 0;
134
- // Convert using math.js directly
135
- const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
136
- scope[variable] = valueInUnit;
151
+ scope[variable] = micrometers;
137
152
  }
138
153
  return scope;
139
154
  }
140
155
  // Enhanced createFormulaScope for new mapping structure
141
156
  export function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas = [], }) {
142
157
  const scope = {};
143
- const normalizedUnit = normalizeUnitForMathJS(unit);
144
158
  for (const [variable, rawMapping] of Object.entries(mappings)) {
145
159
  const mapping = normalizeMapping(rawMapping);
146
160
  if (mapping.type === 'measurement') {
147
161
  const micrometers = valueMap[mapping.id] ?? 0;
148
- const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
149
- scope[variable] = valueInUnit;
162
+ scope[variable] = micrometers;
150
163
  }
151
164
  else if (mapping.type === 'formula') {
152
165
  // Find and evaluate the referenced formula
@@ -160,8 +173,7 @@ export function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas
160
173
  formulas,
161
174
  });
162
175
  if (typeof result === 'number') {
163
- const valueInUnit = mathUnit(result, 'um').toNumber(normalizedUnit);
164
- scope[variable] = valueInUnit;
176
+ scope[variable] = result;
165
177
  }
166
178
  else {
167
179
  scope[variable] = 0; // Error case
@@ -152,6 +152,8 @@ export interface Job extends FirestoreDoc, Timestamps {
152
152
  };
153
153
  areaMapFileId?: string;
154
154
  type?: JobType;
155
+ address?: string;
156
+ description?: string;
155
157
  }
156
158
  export interface Section extends Timestamps {
157
159
  id: string;
@@ -187,6 +189,7 @@ export interface Group extends FirestoreDoc, Timestamps {
187
189
  finish: string;
188
190
  finishDate: string;
189
191
  };
192
+ path?: string;
190
193
  }
191
194
  export declare enum ColumnType {
192
195
  Text = "text",
@@ -1,5 +1,5 @@
1
1
  import { DecimalTolerance, FractionalTolerance, Units } from '../types/firestore.js';
2
- export declare const convertMicrometers: (micrometers: number, unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => {
2
+ export declare const convertMicrometers: (micrometers: number | null | undefined, unit: Units, fractionalTolerance: FractionalTolerance, decimalTolerance: DecimalTolerance) => {
3
3
  value: string;
4
- unit: string | null;
4
+ unit: string;
5
5
  };
@@ -2,75 +2,84 @@ import { Units, convertUnitsToReadable, } from '../types/firestore.js';
2
2
  import { create, all } from 'mathjs';
3
3
  const math = create(all);
4
4
  export const convertMicrometers = (micrometers, unit, fractionalTolerance, decimalTolerance) => {
5
- const converted = math.unit(micrometers, 'um');
6
- let value = 0;
7
- let displayUnit = '';
8
- switch (unit) {
9
- case Units.Inches:
10
- case Units.FractionalInches: {
11
- const inches = converted.toNumber('in');
12
- displayUnit = 'in';
13
- if (unit === Units.FractionalInches) {
14
- const denominator = parseInt(fractionalTolerance, 10);
15
- const whole = Math.floor(inches);
16
- const fractional = inches - whole;
17
- const numerator = Math.round(fractional * denominator);
18
- if (numerator === denominator) {
19
- value = `${whole + 1}`;
5
+ if (micrometers == null || isNaN(micrometers)) {
6
+ return { value: 'NaN', unit: '' };
7
+ }
8
+ try {
9
+ const converted = math.unit(micrometers, 'um');
10
+ let value = 0;
11
+ let displayUnit = '';
12
+ switch (unit) {
13
+ case Units.Inches:
14
+ case Units.FractionalInches: {
15
+ const inches = converted.toNumber('in');
16
+ displayUnit = 'in';
17
+ if (unit === Units.FractionalInches) {
18
+ const denominator = parseInt(fractionalTolerance, 10);
19
+ const whole = Math.floor(inches);
20
+ const fractional = inches - whole;
21
+ const numerator = Math.round(fractional * denominator);
22
+ if (numerator === denominator) {
23
+ value = `${whole + 1}`;
24
+ }
25
+ else {
26
+ value =
27
+ numerator === 0
28
+ ? `${whole}`
29
+ : `${whole} ${numerator}/${denominator}`;
30
+ }
20
31
  }
21
32
  else {
22
- value =
23
- numerator === 0
24
- ? `${whole}`
25
- : `${whole} ${numerator}/${denominator}`;
33
+ value = inches.toFixed(decimalTolerance.length - 2);
26
34
  }
35
+ break;
27
36
  }
28
- else {
29
- value = inches.toFixed(decimalTolerance.length - 2); // Match decimal places to tolerance
30
- }
31
- break;
32
- }
33
- case Units.Feet:
34
- case Units.FeetInchesFractional:
35
- case Units.FeetInchesDecimal: {
36
- const feet = converted.toNumber('ft');
37
- const wholeFeet = Math.floor(feet);
38
- const fractionalFeet = feet - wholeFeet;
39
- displayUnit = 'ft';
40
- if (unit === Units.FeetInchesFractional) {
41
- const inches = fractionalFeet * 12;
42
- const wholeInches = Math.floor(inches);
43
- const fractional = inches - wholeInches;
44
- const denominator = parseInt(fractionalTolerance, 10);
45
- const numerator = Math.round(fractional * denominator);
46
- if (numerator === denominator) {
47
- value = `${wholeFeet}' ${wholeInches + 1}"`;
37
+ case Units.Feet:
38
+ case Units.FeetInchesFractional:
39
+ case Units.FeetInchesDecimal: {
40
+ const feet = converted.toNumber('ft');
41
+ const wholeFeet = Math.floor(feet);
42
+ const fractionalFeet = feet - wholeFeet;
43
+ displayUnit = 'ft';
44
+ if (unit === Units.FeetInchesFractional) {
45
+ const inches = fractionalFeet * 12;
46
+ const wholeInches = Math.floor(inches);
47
+ const fractional = inches - wholeInches;
48
+ const denominator = parseInt(fractionalTolerance, 10);
49
+ const numerator = Math.round(fractional * denominator);
50
+ if (numerator === denominator) {
51
+ value = `${wholeFeet}' ${wholeInches + 1}"`;
52
+ }
53
+ else {
54
+ value =
55
+ numerator === 0
56
+ ? `${wholeFeet}' ${wholeInches}"`
57
+ : `${wholeFeet}' ${wholeInches} ${numerator}/${denominator}"`;
58
+ }
59
+ }
60
+ else if (unit === Units.FeetInchesDecimal) {
61
+ const inches = (fractionalFeet * 12).toFixed(decimalTolerance.length - 2);
62
+ value = `${wholeFeet}' ${inches}"`;
48
63
  }
49
64
  else {
50
- value =
51
- numerator === 0
52
- ? `${wholeFeet}' ${wholeInches}"`
53
- : `${wholeFeet}' ${wholeInches} ${numerator}/${denominator}"`;
65
+ value = feet.toFixed(decimalTolerance.length - 2);
54
66
  }
67
+ break;
55
68
  }
56
- else if (unit === Units.FeetInchesDecimal) {
57
- const inches = (fractionalFeet * 12).toFixed(decimalTolerance.length - 2);
58
- value = `${wholeFeet}' ${inches}"`;
69
+ case Units.Meters:
70
+ case Units.Centimeters:
71
+ case Units.Millimeters: {
72
+ displayUnit = convertUnitsToReadable(unit) ?? '';
73
+ value = converted.toNumber(unit).toFixed(decimalTolerance.length - 2);
74
+ break;
59
75
  }
60
- else {
61
- value = feet.toFixed(decimalTolerance.length - 2);
62
- }
63
- break;
64
- }
65
- case Units.Meters:
66
- case Units.Centimeters:
67
- case Units.Millimeters: {
68
- displayUnit = convertUnitsToReadable(unit);
69
- value = converted.toNumber(unit).toFixed(decimalTolerance.length - 2);
70
- break;
76
+ default:
77
+ throw new Error('Unsupported unit');
71
78
  }
72
- default:
73
- throw new Error('Unsupported unit');
79
+ return { value, unit: displayUnit };
80
+ }
81
+ catch (err) {
82
+ console.warn('Error converting micrometers:', err);
83
+ return { value: 'NaN', unit: '' };
74
84
  }
75
- return { value, unit: displayUnit };
76
85
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reekon-tools/boldr-utils",
3
- "version": "1.4.9",
3
+ "version": "1.4.11",
4
4
  "description": "Shared utilities for formulas and measurement conversion used in Reekon apps",
5
5
  "author": "REEKON Tools",
6
6
  "license": "MIT",