@harbortouch/skytab-analytics-report-utils 0.9.0 → 0.10.0

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/dist/index.cjs CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS: () => TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS,
52
52
  TICKET_SUMMARY_AVAILABLE_COLUMNS: () => TICKET_SUMMARY_AVAILABLE_COLUMNS,
53
53
  TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS: () => TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS,
54
+ calculateFieldTotal: () => calculateFieldTotal,
54
55
  calculateReportTotals: () => calculateReportTotals,
55
56
  dailySalesConfig: () => dailySalesConfig,
56
57
  dailySalesDiscountsConfig: () => dailySalesDiscountsConfig,
@@ -98,18 +99,18 @@ var FIELDS = {
98
99
  type: "percent",
99
100
  columnKey: "salesVarLwPct",
100
101
  footerCalculation: {
101
- type: "percentChange",
102
- numeratorField: "salesAmountNet",
103
- denominatorField: "salesAmountNetLastWeek"
102
+ type: "variancePercent",
103
+ numeratorField: "salesVarLW",
104
+ denominatorField: "salesAmountNet"
104
105
  }
105
106
  },
106
107
  salesVarLYPct: {
107
108
  type: "percent",
108
109
  columnKey: "salesVarLyPct",
109
110
  footerCalculation: {
110
- type: "percentChange",
111
- numeratorField: "salesAmountNet",
112
- denominatorField: "salesAmountNetLastYear"
111
+ type: "variancePercent",
112
+ numeratorField: "salesVarLY",
113
+ denominatorField: "salesAmountNet"
113
114
  }
114
115
  },
115
116
  salesAmountNetLastWeek: { type: "money" },
@@ -127,18 +128,18 @@ var FIELDS = {
127
128
  type: "percent",
128
129
  columnKey: "guestsVarLwPct",
129
130
  footerCalculation: {
130
- type: "percentChange",
131
- numeratorField: "guestsCount",
132
- denominatorField: "guestsCountLastWeek"
131
+ type: "variancePercent",
132
+ numeratorField: "guestsVarLW",
133
+ denominatorField: "guestsCount"
133
134
  }
134
135
  },
135
136
  guestsVarLYPct: {
136
137
  type: "percent",
137
138
  columnKey: "guestsVarLyPct",
138
139
  footerCalculation: {
139
- type: "percentChange",
140
- numeratorField: "guestsCount",
141
- denominatorField: "guestsCountLastYear"
140
+ type: "variancePercent",
141
+ numeratorField: "guestsVarLY",
142
+ denominatorField: "guestsCount"
142
143
  }
143
144
  },
144
145
  guestsCountLastWeek: { type: "number" },
@@ -156,18 +157,22 @@ var FIELDS = {
156
157
  type: "money",
157
158
  columnKey: "salesPerGuestLw",
158
159
  footerCalculation: {
159
- type: "average",
160
- numeratorField: "salesAmountNetLastWeek",
161
- denominatorField: "guestsCountLastWeek"
160
+ type: "quotientOfDifferences",
161
+ numeratorField: "salesAmountNet",
162
+ numeratorAdjustField: "salesVarLW",
163
+ denominatorField: "guestsCount",
164
+ denominatorAdjustField: "guestsVarLW"
162
165
  }
163
166
  },
164
167
  salesPerGuestLY: {
165
168
  type: "money",
166
169
  columnKey: "salesPerGuestLy",
167
170
  footerCalculation: {
168
- type: "average",
169
- numeratorField: "salesAmountNetLastYear",
170
- denominatorField: "guestsCountLastYear"
171
+ type: "quotientOfDifferences",
172
+ numeratorField: "salesAmountNet",
173
+ numeratorAdjustField: "salesVarLY",
174
+ denominatorField: "guestsCount",
175
+ denominatorAdjustField: "guestsVarLY"
171
176
  }
172
177
  }
173
178
  };
@@ -1524,65 +1529,54 @@ function isNumericType(type) {
1524
1529
  }
1525
1530
 
1526
1531
  // src/totals.ts
1527
- function calculateReportTotals(data, fieldConfig, opts) {
1528
- const rows = data;
1529
- const { labelField, label } = opts;
1530
- const fieldsToSum = /* @__PURE__ */ new Set();
1531
- const derivedFields = [];
1532
- for (const [field, config] of Object.entries(fieldConfig)) {
1533
- const { type, columnKey, footerCalculation: calc } = config;
1534
- if (!type || type === "string" || type === "date" || type === "time" || type === "boolean") {
1535
- continue;
1536
- }
1537
- if (calc?.type === "none") {
1538
- } else if (!calc || calc.type === "sum") {
1539
- if (columnKey || calc?.type === "sum") {
1540
- fieldsToSum.add(field);
1541
- }
1542
- } else {
1543
- derivedFields.push({ field, calc });
1544
- if (calc.numeratorField) {
1545
- fieldsToSum.add(calc.numeratorField);
1546
- }
1547
- if (calc.denominatorField) {
1548
- fieldsToSum.add(calc.denominatorField);
1549
- }
1550
- }
1532
+ var sumField = (rows, field) => rows.reduce((acc, row) => acc + (row[field] ?? 0), 0);
1533
+ var calculateFieldTotal = (rows, field, config) => {
1534
+ const { type } = config;
1535
+ if (!type || type === "string" || type === "date" || type === "time" || type === "boolean" || type === "conditional") {
1536
+ return null;
1551
1537
  }
1552
- const sums = {};
1553
- for (const field of fieldsToSum) {
1554
- sums[field] = rows.reduce((acc, row) => acc + (row[field] ?? 0), 0);
1538
+ const calc = config.footerCalculation;
1539
+ if (calc?.type === "none") {
1540
+ return 0;
1555
1541
  }
1556
- const derived = {};
1557
- for (const { field, calc } of derivedFields) {
1558
- const num = sums[calc.numeratorField ?? ""] ?? 0;
1559
- const den = sums[calc.denominatorField ?? ""] ?? 0;
1560
- if (calc.type === "percentChange") {
1561
- derived[field] = den ? (num - den) / den : 0;
1562
- } else if (calc.type === "average") {
1563
- derived[field] = den > 0 ? num / den : 0;
1564
- }
1542
+ if (!calc || calc.type === "sum") {
1543
+ return config.columnKey || calc?.type === "sum" ? sumField(rows, field) : 0;
1544
+ }
1545
+ const { numeratorField, denominatorField } = calc;
1546
+ if (!numeratorField || !denominatorField) {
1547
+ return 0;
1548
+ }
1549
+ const num = sumField(rows, numeratorField);
1550
+ const den = sumField(rows, denominatorField);
1551
+ if (calc.type === "percentChange") {
1552
+ return den ? (num - den) / den : 0;
1553
+ }
1554
+ if (calc.type === "variancePercent") {
1555
+ const prior = den - num;
1556
+ return prior ? num / prior : 0;
1557
+ }
1558
+ if (calc.type === "average") {
1559
+ return den > 0 ? num / den : 0;
1560
+ }
1561
+ if (calc.type === "quotientOfDifferences") {
1562
+ const { numeratorAdjustField, denominatorAdjustField } = calc;
1563
+ const numAdj = numeratorAdjustField ? sumField(rows, numeratorAdjustField) : 0;
1564
+ const denAdj = denominatorAdjustField ? sumField(rows, denominatorAdjustField) : 0;
1565
+ const effectiveDen = den - denAdj;
1566
+ return effectiveDen !== 0 ? (num - numAdj) / effectiveDen : 0;
1565
1567
  }
1568
+ return 0;
1569
+ };
1570
+ var calculateReportTotals = (data, fieldConfig, opts) => {
1571
+ const { labelField, label } = opts;
1566
1572
  const result = {};
1567
1573
  for (const [field, config] of Object.entries(fieldConfig)) {
1568
- const { type } = config;
1569
- if (!type || type === "string" || type === "date" || type === "time") {
1570
- result[field] = "";
1571
- } else if (type === "boolean") {
1572
- result[field] = false;
1573
- } else {
1574
- result[field] = 0;
1575
- }
1574
+ const total = calculateFieldTotal(data, field, config);
1575
+ result[field] = total ?? "";
1576
1576
  }
1577
1577
  result[labelField] = label;
1578
- for (const [field, value] of Object.entries(sums)) {
1579
- result[field] = value;
1580
- }
1581
- for (const [field, value] of Object.entries(derived)) {
1582
- result[field] = value;
1583
- }
1584
1578
  return result;
1585
- }
1579
+ };
1586
1580
 
1587
1581
  // src/reports/dailySalesRefundsVoids.ts
1588
1582
  var FIRST_COLUMN_WIDTH8 = 240;
@@ -1795,6 +1789,7 @@ var formatFieldValue = (value, type, localeOptions, fullRecord, fieldConfig = DE
1795
1789
  TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS,
1796
1790
  TICKET_SUMMARY_AVAILABLE_COLUMNS,
1797
1791
  TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS,
1792
+ calculateFieldTotal,
1798
1793
  calculateReportTotals,
1799
1794
  dailySalesConfig,
1800
1795
  dailySalesDiscountsConfig,
package/dist/index.d.cts CHANGED
@@ -1181,11 +1181,13 @@ declare function getColumnExportHeaderLabel(colTitle: string, groupTitle: string
1181
1181
 
1182
1182
  type FieldType = 'string' | 'money' | 'percent' | 'number' | 'fixedNumber' | 'date' | 'time' | 'boolean' | 'conditional';
1183
1183
  type ColumnAlignment = 'left' | 'right';
1184
- type FooterCalculationType = 'sum' | 'percentChange' | 'average' | 'none';
1184
+ type FooterCalculationType = 'sum' | 'percentChange' | 'variancePercent' | 'average' | 'quotientOfDifferences' | 'none';
1185
1185
  interface FooterCalculation<TField = string> {
1186
1186
  type: FooterCalculationType;
1187
1187
  numeratorField?: TField;
1188
+ numeratorAdjustField?: string;
1188
1189
  denominatorField?: TField;
1190
+ denominatorAdjustField?: string;
1189
1191
  }
1190
1192
  interface ConditionalConfiguration<TField = string> {
1191
1193
  conditionalField: TField;
@@ -1242,7 +1244,8 @@ interface CalculateReportTotalsOptions {
1242
1244
  labelField: string;
1243
1245
  label: string;
1244
1246
  }
1245
- declare function calculateReportTotals<T>(data: T[], fieldConfig: Record<string, ColumnPresentationConfig>, opts: CalculateReportTotalsOptions): T;
1247
+ declare const calculateFieldTotal: <T>(rows: T[], field: keyof T, config: ColumnPresentationConfig) => number | null;
1248
+ declare const calculateReportTotals: <T>(data: T[], fieldConfig: Record<string, ColumnPresentationConfig>, opts: CalculateReportTotalsOptions) => T;
1246
1249
 
1247
1250
  type SalesSummaryField = 'groupById' | 'groupByName' | 'salesAmountNet' | 'salesVarLW' | 'salesVarLY' | 'salesVarLWPct' | 'salesVarLYPct' | 'salesAmountNetLastWeek' | 'salesAmountNetLastYear' | 'salesAmountGross' | 'salesAmountGrossLastWeek' | 'salesAmountGrossLastYear' | 'ticketsCount' | 'ticketsCountLastWeek' | 'ticketsCountLastYear' | 'guestsCount' | 'guestsVarLW' | 'guestsVarLY' | 'guestsVarLWPct' | 'guestsVarLYPct' | 'guestsCountLastWeek' | 'guestsCountLastYear' | 'salesPerGuestTY' | 'salesPerGuestLW' | 'salesPerGuestLY';
1248
1251
  declare const SALES_SUMMARY_AVAILABLE_COLUMNS: SalesSummaryField[];
@@ -1352,6 +1355,6 @@ declare const getReportFormattingLocaleOptions: (locations: {
1352
1355
  countryCode: string;
1353
1356
  language: string;
1354
1357
  }[]) => LocaleOption[];
1355
- declare const formatFieldValue: (value: unknown, type: FieldType, localeOptions: LocaleOption, fullRecord: Record<string, unknown> | null, fieldConfig?: ColumnPresentationConfig) => string;
1358
+ declare const formatFieldValue: (value: unknown, type: FieldType, localeOptions: LocaleOption, fullRecord: Record<string, unknown> | null | undefined, fieldConfig?: ColumnPresentationConfig) => string;
1356
1359
 
1357
- export { COLUMN_REGISTRY, type CalculateReportTotalsOptions, type ColumnAlignment, type ColumnGroupConfig, type ColumnMetadata, type ColumnPresentationConfig, DAILY_SALES_AVAILABLE_COLUMNS, DAILY_SALES_BY_GROUP_AVAILABLE_COLUMNS, DAILY_SALES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_DISCOUNTS_AVAILABLE_COLUMNS, DAILY_SALES_DISCOUNTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_PAYMENTS_AVAILABLE_COLUMNS, DAILY_SALES_PAYMENTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_AVAILABLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TAXES_AVAILABLE_COLUMNS, DAILY_SALES_TAXES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TRENDS_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_DEFAULT_VISIBLE_COLUMNS, type FieldType, type FooterCalculation, type FooterCalculationType, type FormatDateOptions, type FormatMoneyOptions, type FormatNumberOptions, type FormatTimeOptions, ITEM_TAX_AVAILABLE_COLUMNS, ITEM_TAX_DEFAULT_VISIBLE_COLUMNS, type LocaleOption, MODIFIER_MIX_AVAILABLE_COLUMNS, MODIFIER_MIX_DEFAULT_VISIBLE_COLUMNS, PRODUCT_MIX_AVAILABLE_COLUMNS, PRODUCT_MIX_CHART_FIELDS, PRODUCT_MIX_DEFAULT_VISIBLE_COLUMNS, Report, type ReportColumnKey, type ReportConfig, type ReportType, SALES_BY_ITEM_DETAIL_AVAILABLE_COLUMNS, SALES_BY_ITEM_DETAIL_DEFAULT_VISIBLE_COLUMNS, SALES_SUMMARY_AVAILABLE_COLUMNS, SALES_SUMMARY_DEFAULT_VISIBLE_COLUMNS, TICKET_LIVE_AVAILABLE_COLUMNS, TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS, TICKET_SUMMARY_AVAILABLE_COLUMNS, TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS, type TimeFormat, calculateReportTotals, dailySalesConfig, dailySalesDiscountsConfig, dailySalesPaymentsConfig, dailySalesRefundsVoidsConfig, dailySalesTaxesConfig, dailySalesTrendsConfig, employeeTimecardConfig, formatDate, formatFieldValue, formatFixedNumber, formatInteger, formatMoney, formatMoneyWithoutSymbol, formatPercent, formatString, formatTime, getColumnAlignment, getColumnExportHeaderLabel, getColumnMetadata, getDateToFormat, getNumberToFormat, getReportConfig, getReportFormattingLocaleOptions, isNumericType, itemTaxConfig, modifierMixConfig, productMixConfig, salesByItemDetailConfig, salesSummaryConfig, ticketLiveConfig, ticketSummaryConfig };
1360
+ export { COLUMN_REGISTRY, type CalculateReportTotalsOptions, type ColumnAlignment, type ColumnGroupConfig, type ColumnMetadata, type ColumnPresentationConfig, DAILY_SALES_AVAILABLE_COLUMNS, DAILY_SALES_BY_GROUP_AVAILABLE_COLUMNS, DAILY_SALES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_DISCOUNTS_AVAILABLE_COLUMNS, DAILY_SALES_DISCOUNTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_PAYMENTS_AVAILABLE_COLUMNS, DAILY_SALES_PAYMENTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_AVAILABLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TAXES_AVAILABLE_COLUMNS, DAILY_SALES_TAXES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TRENDS_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_DEFAULT_VISIBLE_COLUMNS, type FieldType, type FooterCalculation, type FooterCalculationType, type FormatDateOptions, type FormatMoneyOptions, type FormatNumberOptions, type FormatTimeOptions, ITEM_TAX_AVAILABLE_COLUMNS, ITEM_TAX_DEFAULT_VISIBLE_COLUMNS, type LocaleOption, MODIFIER_MIX_AVAILABLE_COLUMNS, MODIFIER_MIX_DEFAULT_VISIBLE_COLUMNS, PRODUCT_MIX_AVAILABLE_COLUMNS, PRODUCT_MIX_CHART_FIELDS, PRODUCT_MIX_DEFAULT_VISIBLE_COLUMNS, Report, type ReportColumnKey, type ReportConfig, type ReportType, SALES_BY_ITEM_DETAIL_AVAILABLE_COLUMNS, SALES_BY_ITEM_DETAIL_DEFAULT_VISIBLE_COLUMNS, SALES_SUMMARY_AVAILABLE_COLUMNS, SALES_SUMMARY_DEFAULT_VISIBLE_COLUMNS, TICKET_LIVE_AVAILABLE_COLUMNS, TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS, TICKET_SUMMARY_AVAILABLE_COLUMNS, TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS, type TimeFormat, calculateFieldTotal, calculateReportTotals, dailySalesConfig, dailySalesDiscountsConfig, dailySalesPaymentsConfig, dailySalesRefundsVoidsConfig, dailySalesTaxesConfig, dailySalesTrendsConfig, employeeTimecardConfig, formatDate, formatFieldValue, formatFixedNumber, formatInteger, formatMoney, formatMoneyWithoutSymbol, formatPercent, formatString, formatTime, getColumnAlignment, getColumnExportHeaderLabel, getColumnMetadata, getDateToFormat, getNumberToFormat, getReportConfig, getReportFormattingLocaleOptions, isNumericType, itemTaxConfig, modifierMixConfig, productMixConfig, salesByItemDetailConfig, salesSummaryConfig, ticketLiveConfig, ticketSummaryConfig };
package/dist/index.d.ts CHANGED
@@ -1181,11 +1181,13 @@ declare function getColumnExportHeaderLabel(colTitle: string, groupTitle: string
1181
1181
 
1182
1182
  type FieldType = 'string' | 'money' | 'percent' | 'number' | 'fixedNumber' | 'date' | 'time' | 'boolean' | 'conditional';
1183
1183
  type ColumnAlignment = 'left' | 'right';
1184
- type FooterCalculationType = 'sum' | 'percentChange' | 'average' | 'none';
1184
+ type FooterCalculationType = 'sum' | 'percentChange' | 'variancePercent' | 'average' | 'quotientOfDifferences' | 'none';
1185
1185
  interface FooterCalculation<TField = string> {
1186
1186
  type: FooterCalculationType;
1187
1187
  numeratorField?: TField;
1188
+ numeratorAdjustField?: string;
1188
1189
  denominatorField?: TField;
1190
+ denominatorAdjustField?: string;
1189
1191
  }
1190
1192
  interface ConditionalConfiguration<TField = string> {
1191
1193
  conditionalField: TField;
@@ -1242,7 +1244,8 @@ interface CalculateReportTotalsOptions {
1242
1244
  labelField: string;
1243
1245
  label: string;
1244
1246
  }
1245
- declare function calculateReportTotals<T>(data: T[], fieldConfig: Record<string, ColumnPresentationConfig>, opts: CalculateReportTotalsOptions): T;
1247
+ declare const calculateFieldTotal: <T>(rows: T[], field: keyof T, config: ColumnPresentationConfig) => number | null;
1248
+ declare const calculateReportTotals: <T>(data: T[], fieldConfig: Record<string, ColumnPresentationConfig>, opts: CalculateReportTotalsOptions) => T;
1246
1249
 
1247
1250
  type SalesSummaryField = 'groupById' | 'groupByName' | 'salesAmountNet' | 'salesVarLW' | 'salesVarLY' | 'salesVarLWPct' | 'salesVarLYPct' | 'salesAmountNetLastWeek' | 'salesAmountNetLastYear' | 'salesAmountGross' | 'salesAmountGrossLastWeek' | 'salesAmountGrossLastYear' | 'ticketsCount' | 'ticketsCountLastWeek' | 'ticketsCountLastYear' | 'guestsCount' | 'guestsVarLW' | 'guestsVarLY' | 'guestsVarLWPct' | 'guestsVarLYPct' | 'guestsCountLastWeek' | 'guestsCountLastYear' | 'salesPerGuestTY' | 'salesPerGuestLW' | 'salesPerGuestLY';
1248
1251
  declare const SALES_SUMMARY_AVAILABLE_COLUMNS: SalesSummaryField[];
@@ -1352,6 +1355,6 @@ declare const getReportFormattingLocaleOptions: (locations: {
1352
1355
  countryCode: string;
1353
1356
  language: string;
1354
1357
  }[]) => LocaleOption[];
1355
- declare const formatFieldValue: (value: unknown, type: FieldType, localeOptions: LocaleOption, fullRecord: Record<string, unknown> | null, fieldConfig?: ColumnPresentationConfig) => string;
1358
+ declare const formatFieldValue: (value: unknown, type: FieldType, localeOptions: LocaleOption, fullRecord: Record<string, unknown> | null | undefined, fieldConfig?: ColumnPresentationConfig) => string;
1356
1359
 
1357
- export { COLUMN_REGISTRY, type CalculateReportTotalsOptions, type ColumnAlignment, type ColumnGroupConfig, type ColumnMetadata, type ColumnPresentationConfig, DAILY_SALES_AVAILABLE_COLUMNS, DAILY_SALES_BY_GROUP_AVAILABLE_COLUMNS, DAILY_SALES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_DISCOUNTS_AVAILABLE_COLUMNS, DAILY_SALES_DISCOUNTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_PAYMENTS_AVAILABLE_COLUMNS, DAILY_SALES_PAYMENTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_AVAILABLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TAXES_AVAILABLE_COLUMNS, DAILY_SALES_TAXES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TRENDS_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_DEFAULT_VISIBLE_COLUMNS, type FieldType, type FooterCalculation, type FooterCalculationType, type FormatDateOptions, type FormatMoneyOptions, type FormatNumberOptions, type FormatTimeOptions, ITEM_TAX_AVAILABLE_COLUMNS, ITEM_TAX_DEFAULT_VISIBLE_COLUMNS, type LocaleOption, MODIFIER_MIX_AVAILABLE_COLUMNS, MODIFIER_MIX_DEFAULT_VISIBLE_COLUMNS, PRODUCT_MIX_AVAILABLE_COLUMNS, PRODUCT_MIX_CHART_FIELDS, PRODUCT_MIX_DEFAULT_VISIBLE_COLUMNS, Report, type ReportColumnKey, type ReportConfig, type ReportType, SALES_BY_ITEM_DETAIL_AVAILABLE_COLUMNS, SALES_BY_ITEM_DETAIL_DEFAULT_VISIBLE_COLUMNS, SALES_SUMMARY_AVAILABLE_COLUMNS, SALES_SUMMARY_DEFAULT_VISIBLE_COLUMNS, TICKET_LIVE_AVAILABLE_COLUMNS, TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS, TICKET_SUMMARY_AVAILABLE_COLUMNS, TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS, type TimeFormat, calculateReportTotals, dailySalesConfig, dailySalesDiscountsConfig, dailySalesPaymentsConfig, dailySalesRefundsVoidsConfig, dailySalesTaxesConfig, dailySalesTrendsConfig, employeeTimecardConfig, formatDate, formatFieldValue, formatFixedNumber, formatInteger, formatMoney, formatMoneyWithoutSymbol, formatPercent, formatString, formatTime, getColumnAlignment, getColumnExportHeaderLabel, getColumnMetadata, getDateToFormat, getNumberToFormat, getReportConfig, getReportFormattingLocaleOptions, isNumericType, itemTaxConfig, modifierMixConfig, productMixConfig, salesByItemDetailConfig, salesSummaryConfig, ticketLiveConfig, ticketSummaryConfig };
1360
+ export { COLUMN_REGISTRY, type CalculateReportTotalsOptions, type ColumnAlignment, type ColumnGroupConfig, type ColumnMetadata, type ColumnPresentationConfig, DAILY_SALES_AVAILABLE_COLUMNS, DAILY_SALES_BY_GROUP_AVAILABLE_COLUMNS, DAILY_SALES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_DISCOUNTS_AVAILABLE_COLUMNS, DAILY_SALES_DISCOUNTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_PAYMENTS_AVAILABLE_COLUMNS, DAILY_SALES_PAYMENTS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_AVAILABLE_COLUMNS, DAILY_SALES_REFUNDS_VOIDS_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TAXES_AVAILABLE_COLUMNS, DAILY_SALES_TAXES_DEFAULT_VISIBLE_COLUMNS, DAILY_SALES_TRENDS_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_AVAILABLE_COLUMNS, EMPLOYEE_TIMECARD_DEFAULT_VISIBLE_COLUMNS, type FieldType, type FooterCalculation, type FooterCalculationType, type FormatDateOptions, type FormatMoneyOptions, type FormatNumberOptions, type FormatTimeOptions, ITEM_TAX_AVAILABLE_COLUMNS, ITEM_TAX_DEFAULT_VISIBLE_COLUMNS, type LocaleOption, MODIFIER_MIX_AVAILABLE_COLUMNS, MODIFIER_MIX_DEFAULT_VISIBLE_COLUMNS, PRODUCT_MIX_AVAILABLE_COLUMNS, PRODUCT_MIX_CHART_FIELDS, PRODUCT_MIX_DEFAULT_VISIBLE_COLUMNS, Report, type ReportColumnKey, type ReportConfig, type ReportType, SALES_BY_ITEM_DETAIL_AVAILABLE_COLUMNS, SALES_BY_ITEM_DETAIL_DEFAULT_VISIBLE_COLUMNS, SALES_SUMMARY_AVAILABLE_COLUMNS, SALES_SUMMARY_DEFAULT_VISIBLE_COLUMNS, TICKET_LIVE_AVAILABLE_COLUMNS, TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS, TICKET_SUMMARY_AVAILABLE_COLUMNS, TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS, type TimeFormat, calculateFieldTotal, calculateReportTotals, dailySalesConfig, dailySalesDiscountsConfig, dailySalesPaymentsConfig, dailySalesRefundsVoidsConfig, dailySalesTaxesConfig, dailySalesTrendsConfig, employeeTimecardConfig, formatDate, formatFieldValue, formatFixedNumber, formatInteger, formatMoney, formatMoneyWithoutSymbol, formatPercent, formatString, formatTime, getColumnAlignment, getColumnExportHeaderLabel, getColumnMetadata, getDateToFormat, getNumberToFormat, getReportConfig, getReportFormattingLocaleOptions, isNumericType, itemTaxConfig, modifierMixConfig, productMixConfig, salesByItemDetailConfig, salesSummaryConfig, ticketLiveConfig, ticketSummaryConfig };
package/dist/index.js CHANGED
@@ -10,18 +10,18 @@ var FIELDS = {
10
10
  type: "percent",
11
11
  columnKey: "salesVarLwPct",
12
12
  footerCalculation: {
13
- type: "percentChange",
14
- numeratorField: "salesAmountNet",
15
- denominatorField: "salesAmountNetLastWeek"
13
+ type: "variancePercent",
14
+ numeratorField: "salesVarLW",
15
+ denominatorField: "salesAmountNet"
16
16
  }
17
17
  },
18
18
  salesVarLYPct: {
19
19
  type: "percent",
20
20
  columnKey: "salesVarLyPct",
21
21
  footerCalculation: {
22
- type: "percentChange",
23
- numeratorField: "salesAmountNet",
24
- denominatorField: "salesAmountNetLastYear"
22
+ type: "variancePercent",
23
+ numeratorField: "salesVarLY",
24
+ denominatorField: "salesAmountNet"
25
25
  }
26
26
  },
27
27
  salesAmountNetLastWeek: { type: "money" },
@@ -39,18 +39,18 @@ var FIELDS = {
39
39
  type: "percent",
40
40
  columnKey: "guestsVarLwPct",
41
41
  footerCalculation: {
42
- type: "percentChange",
43
- numeratorField: "guestsCount",
44
- denominatorField: "guestsCountLastWeek"
42
+ type: "variancePercent",
43
+ numeratorField: "guestsVarLW",
44
+ denominatorField: "guestsCount"
45
45
  }
46
46
  },
47
47
  guestsVarLYPct: {
48
48
  type: "percent",
49
49
  columnKey: "guestsVarLyPct",
50
50
  footerCalculation: {
51
- type: "percentChange",
52
- numeratorField: "guestsCount",
53
- denominatorField: "guestsCountLastYear"
51
+ type: "variancePercent",
52
+ numeratorField: "guestsVarLY",
53
+ denominatorField: "guestsCount"
54
54
  }
55
55
  },
56
56
  guestsCountLastWeek: { type: "number" },
@@ -68,18 +68,22 @@ var FIELDS = {
68
68
  type: "money",
69
69
  columnKey: "salesPerGuestLw",
70
70
  footerCalculation: {
71
- type: "average",
72
- numeratorField: "salesAmountNetLastWeek",
73
- denominatorField: "guestsCountLastWeek"
71
+ type: "quotientOfDifferences",
72
+ numeratorField: "salesAmountNet",
73
+ numeratorAdjustField: "salesVarLW",
74
+ denominatorField: "guestsCount",
75
+ denominatorAdjustField: "guestsVarLW"
74
76
  }
75
77
  },
76
78
  salesPerGuestLY: {
77
79
  type: "money",
78
80
  columnKey: "salesPerGuestLy",
79
81
  footerCalculation: {
80
- type: "average",
81
- numeratorField: "salesAmountNetLastYear",
82
- denominatorField: "guestsCountLastYear"
82
+ type: "quotientOfDifferences",
83
+ numeratorField: "salesAmountNet",
84
+ numeratorAdjustField: "salesVarLY",
85
+ denominatorField: "guestsCount",
86
+ denominatorAdjustField: "guestsVarLY"
83
87
  }
84
88
  }
85
89
  };
@@ -1436,65 +1440,54 @@ function isNumericType(type) {
1436
1440
  }
1437
1441
 
1438
1442
  // src/totals.ts
1439
- function calculateReportTotals(data, fieldConfig, opts) {
1440
- const rows = data;
1441
- const { labelField, label } = opts;
1442
- const fieldsToSum = /* @__PURE__ */ new Set();
1443
- const derivedFields = [];
1444
- for (const [field, config] of Object.entries(fieldConfig)) {
1445
- const { type, columnKey, footerCalculation: calc } = config;
1446
- if (!type || type === "string" || type === "date" || type === "time" || type === "boolean") {
1447
- continue;
1448
- }
1449
- if (calc?.type === "none") {
1450
- } else if (!calc || calc.type === "sum") {
1451
- if (columnKey || calc?.type === "sum") {
1452
- fieldsToSum.add(field);
1453
- }
1454
- } else {
1455
- derivedFields.push({ field, calc });
1456
- if (calc.numeratorField) {
1457
- fieldsToSum.add(calc.numeratorField);
1458
- }
1459
- if (calc.denominatorField) {
1460
- fieldsToSum.add(calc.denominatorField);
1461
- }
1462
- }
1443
+ var sumField = (rows, field) => rows.reduce((acc, row) => acc + (row[field] ?? 0), 0);
1444
+ var calculateFieldTotal = (rows, field, config) => {
1445
+ const { type } = config;
1446
+ if (!type || type === "string" || type === "date" || type === "time" || type === "boolean" || type === "conditional") {
1447
+ return null;
1463
1448
  }
1464
- const sums = {};
1465
- for (const field of fieldsToSum) {
1466
- sums[field] = rows.reduce((acc, row) => acc + (row[field] ?? 0), 0);
1449
+ const calc = config.footerCalculation;
1450
+ if (calc?.type === "none") {
1451
+ return 0;
1467
1452
  }
1468
- const derived = {};
1469
- for (const { field, calc } of derivedFields) {
1470
- const num = sums[calc.numeratorField ?? ""] ?? 0;
1471
- const den = sums[calc.denominatorField ?? ""] ?? 0;
1472
- if (calc.type === "percentChange") {
1473
- derived[field] = den ? (num - den) / den : 0;
1474
- } else if (calc.type === "average") {
1475
- derived[field] = den > 0 ? num / den : 0;
1476
- }
1453
+ if (!calc || calc.type === "sum") {
1454
+ return config.columnKey || calc?.type === "sum" ? sumField(rows, field) : 0;
1455
+ }
1456
+ const { numeratorField, denominatorField } = calc;
1457
+ if (!numeratorField || !denominatorField) {
1458
+ return 0;
1459
+ }
1460
+ const num = sumField(rows, numeratorField);
1461
+ const den = sumField(rows, denominatorField);
1462
+ if (calc.type === "percentChange") {
1463
+ return den ? (num - den) / den : 0;
1464
+ }
1465
+ if (calc.type === "variancePercent") {
1466
+ const prior = den - num;
1467
+ return prior ? num / prior : 0;
1468
+ }
1469
+ if (calc.type === "average") {
1470
+ return den > 0 ? num / den : 0;
1471
+ }
1472
+ if (calc.type === "quotientOfDifferences") {
1473
+ const { numeratorAdjustField, denominatorAdjustField } = calc;
1474
+ const numAdj = numeratorAdjustField ? sumField(rows, numeratorAdjustField) : 0;
1475
+ const denAdj = denominatorAdjustField ? sumField(rows, denominatorAdjustField) : 0;
1476
+ const effectiveDen = den - denAdj;
1477
+ return effectiveDen !== 0 ? (num - numAdj) / effectiveDen : 0;
1477
1478
  }
1479
+ return 0;
1480
+ };
1481
+ var calculateReportTotals = (data, fieldConfig, opts) => {
1482
+ const { labelField, label } = opts;
1478
1483
  const result = {};
1479
1484
  for (const [field, config] of Object.entries(fieldConfig)) {
1480
- const { type } = config;
1481
- if (!type || type === "string" || type === "date" || type === "time") {
1482
- result[field] = "";
1483
- } else if (type === "boolean") {
1484
- result[field] = false;
1485
- } else {
1486
- result[field] = 0;
1487
- }
1485
+ const total = calculateFieldTotal(data, field, config);
1486
+ result[field] = total ?? "";
1488
1487
  }
1489
1488
  result[labelField] = label;
1490
- for (const [field, value] of Object.entries(sums)) {
1491
- result[field] = value;
1492
- }
1493
- for (const [field, value] of Object.entries(derived)) {
1494
- result[field] = value;
1495
- }
1496
1489
  return result;
1497
- }
1490
+ };
1498
1491
 
1499
1492
  // src/reports/dailySalesRefundsVoids.ts
1500
1493
  var FIRST_COLUMN_WIDTH8 = 240;
@@ -1706,6 +1699,7 @@ export {
1706
1699
  TICKET_LIVE_DEFAULT_VISIBLE_COLUMNS,
1707
1700
  TICKET_SUMMARY_AVAILABLE_COLUMNS,
1708
1701
  TICKET_SUMMARY_DEFAULT_VISIBLE_COLUMNS,
1702
+ calculateFieldTotal,
1709
1703
  calculateReportTotals,
1710
1704
  dailySalesConfig,
1711
1705
  dailySalesDiscountsConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harbortouch/skytab-analytics-report-utils",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Centralized report column presentation configuration for SkyTab Analytics",
5
5
  "engines": {
6
6
  "node": ">=22.6.0",
@@ -22,7 +22,7 @@
22
22
  "type": "module",
23
23
  "scripts": {
24
24
  "build": "tsup",
25
- "dev": "tsup --watch",
25
+ "dev": "tsup --watch --no-clean",
26
26
  "lint:js": "eslint src",
27
27
  "lint:js:fix": "eslint src --fix",
28
28
  "lint:types": "tsc --noEmit",