@odoo/o-spreadsheet 17.0.71 → 17.0.73

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.0.71
6
- * @date 2025-08-21T06:37:34.192Z
7
- * @hash 1592964
5
+ * @version 17.0.73
6
+ * @date 2025-09-11T08:40:46.367Z
7
+ * @hash a11c622
8
8
  */
9
9
 
10
10
  'use strict';
@@ -11422,8 +11422,11 @@ function invertMatrix(M) {
11422
11422
  // (a) Swap 2 rows. This multiply the determinant by -1.
11423
11423
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
11424
11424
  // (c) Add to a row a multiple of another row. This does not change the determinant.
11425
+ if (M.length < 1 || M[0].length < 1) {
11426
+ throw new Error("invertMatrix: an empty matrix cannot be inverted.");
11427
+ }
11425
11428
  if (M.length !== M[0].length) {
11426
- throw new Error(`Function [[FUNCTION_NAME]] invert matrix error, only square matrices are invertible`);
11429
+ throw new Error("invertMatrix: only square matrices are invertible");
11427
11430
  }
11428
11431
  let determinant = 1;
11429
11432
  const dim = M.length;
@@ -11492,8 +11495,11 @@ function swapMatrixRows(matrix, row1, row2) {
11492
11495
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
11493
11496
  */
11494
11497
  function multiplyMatrices(matrix1, matrix2) {
11498
+ if (matrix1.length < 1 || matrix2.length < 1) {
11499
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
11500
+ }
11495
11501
  if (matrix1.length !== matrix2[0].length) {
11496
- throw new Error(_t("Cannot multiply matrices : incompatible matrices size."));
11502
+ throw new Error("multiplyMatrices: incompatible matrices size.");
11497
11503
  }
11498
11504
  const rowsM1 = matrix1[0].length;
11499
11505
  const colsM2 = matrix2.length;
@@ -11761,6 +11767,7 @@ const MMULT = {
11761
11767
  compute: function (matrix1, matrix2) {
11762
11768
  const _matrix1 = toMatrix(matrix1);
11763
11769
  const _matrix2 = toMatrix(matrix2);
11770
+ assert(() => _matrix1.length > 0 && _matrix2.length > 0, _t("The first and second arguments of [[FUNCTION_NAME]] must be non-empty matrices."));
11764
11771
  assert(() => _matrix1.length === _matrix2[0].length, _t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
11765
11772
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11766
11773
  if (!isNumberMatrix(_matrix1) || !isNumberMatrix(_matrix2)) {
@@ -13250,6 +13257,16 @@ function assertSameNumberOfElements(...args) {
13250
13257
  const dims = args[0].length;
13251
13258
  args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
13252
13259
  }
13260
+ function assertNonEmptyMatrix(matrix, argName) {
13261
+ assert(() => matrix.length > 0 && matrix[0].length > 0, _t("[[FUNCTION_NAME]] expects the provided values of %(argName)s to be a non-empty matrix.", {
13262
+ argName,
13263
+ }));
13264
+ }
13265
+ function assertNonEmpty(...data) {
13266
+ if (data.length === 0 || data.some((arg) => arg.length === 0)) {
13267
+ throw new NotAvailableError(_t("[[FUNCTION_NAME]] has no valid input data."));
13268
+ }
13269
+ }
13253
13270
 
13254
13271
  function filterAndFlatData(dataY, dataX) {
13255
13272
  const _flatDataY = [];
@@ -13780,6 +13797,7 @@ const FORECAST = {
13780
13797
  returns: ["NUMBER"],
13781
13798
  compute: function (x, dataY, dataX) {
13782
13799
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13800
+ assertNonEmpty(flatDataX, flatDataY);
13783
13801
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13784
13802
  },
13785
13803
  isExported: true,
@@ -13797,6 +13815,7 @@ const GROWTH = {
13797
13815
  ],
13798
13816
  returns: ["NUMBER"],
13799
13817
  compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
13818
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13800
13819
  return expM(predictLinearValues(logM(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)")), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13801
13820
  },
13802
13821
  };
@@ -13812,6 +13831,7 @@ const INTERCEPT = {
13812
13831
  returns: ["NUMBER"],
13813
13832
  compute: function (dataY, dataX) {
13814
13833
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13834
+ assertNonEmpty(flatDataX, flatDataY);
13815
13835
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13816
13836
  return intercept;
13817
13837
  },
@@ -13863,6 +13883,7 @@ const LINEST = {
13863
13883
  ],
13864
13884
  returns: ["NUMBER"],
13865
13885
  compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
13886
+ assertNonEmptyMatrix(dataY, "data_y");
13866
13887
  return fullLinearRegression(tryCastAsNumberMatrix(dataX, "the first argument (data_y)"), tryCastAsNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13867
13888
  },
13868
13889
  isExported: true,
@@ -13880,6 +13901,7 @@ const LOGEST = {
13880
13901
  ],
13881
13902
  returns: ["NUMBER"],
13882
13903
  compute: function (dataY, dataX = [], calculateB = true, verbose = false) {
13904
+ assertNonEmptyMatrix(dataY, "data_y");
13883
13905
  const coeffs = fullLinearRegression(tryCastAsNumberMatrix(dataX, "the second argument (data_x)"), logM(tryCastAsNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13884
13906
  for (let i = 0; i < coeffs.length; i++) {
13885
13907
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13902,9 +13924,7 @@ const MATTHEWS = {
13902
13924
  const flatX = dataX.flat();
13903
13925
  const flatY = dataY.flat();
13904
13926
  assertSameNumberOfElements(flatX, flatY);
13905
- if (flatX.length === 0) {
13906
- throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13907
- }
13927
+ assertNonEmpty(flatX, flatY);
13908
13928
  const n = flatX.length;
13909
13929
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13910
13930
  for (let i = 0; i < n; ++i) {
@@ -14098,12 +14118,7 @@ const PEARSON = {
14098
14118
  returns: ["NUMBER"],
14099
14119
  compute: function (dataY, dataX) {
14100
14120
  const { flatDataX, flatDataY } = filterAndFlatData(dataX, dataY);
14101
- if (flatDataX.length === 0) {
14102
- throw new Error(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
14103
- }
14104
- if (flatDataX.length < 2) {
14105
- throw new Error(_t("[[FUNCTION_NAME]] needs at least two values for both parameters"));
14106
- }
14121
+ assertNonEmpty(flatDataX, flatDataY);
14107
14122
  const n = flatDataX.length;
14108
14123
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
14109
14124
  for (let i = 0; i < n; i++) {
@@ -14189,6 +14204,7 @@ const POLYFIT_COEFFS = {
14189
14204
  returns: ["RANGE<NUMBER>"],
14190
14205
  compute: function (dataY, dataX, order, intercept = true) {
14191
14206
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14207
+ assertNonEmpty(flatDataX, flatDataY);
14192
14208
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
14193
14209
  },
14194
14210
  isExported: false,
@@ -14209,6 +14225,7 @@ const POLYFIT_FORECAST = {
14209
14225
  compute: function (x, dataY, dataX, order, intercept = true) {
14210
14226
  const _order = toNumber(order, this.locale);
14211
14227
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14228
+ assertNonEmpty(flatDataX, flatDataY);
14212
14229
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
14213
14230
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
14214
14231
  },
@@ -14334,6 +14351,7 @@ const SLOPE = {
14334
14351
  returns: ["NUMBER"],
14335
14352
  compute: function (dataY, dataX) {
14336
14353
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14354
+ assertNonEmpty(flatDataX, flatDataY);
14337
14355
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14338
14356
  return slope;
14339
14357
  },
@@ -14384,6 +14402,7 @@ const SPEARMAN = {
14384
14402
  returns: ["NUMBER"],
14385
14403
  compute: function (dataX, dataY) {
14386
14404
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14405
+ assertNonEmpty(flatDataX, flatDataY);
14387
14406
  const n = flatDataX.length;
14388
14407
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14389
14408
  order.sort((a, b) => a[0] - b[0]);
@@ -14501,6 +14520,7 @@ const STEYX = {
14501
14520
  returns: ["NUMBER"],
14502
14521
  compute: function (dataY, dataX) {
14503
14522
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14523
+ assertNonEmpty(flatDataX, flatDataY);
14504
14524
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14505
14525
  return data[1][2];
14506
14526
  },
@@ -14519,6 +14539,7 @@ const TREND = {
14519
14539
  ],
14520
14540
  returns: ["NUMBER"],
14521
14541
  compute: function (knownDataY, knownDataX = [], newDataX = [], b = true) {
14542
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14522
14543
  return predictLinearValues(tryCastAsNumberMatrix(knownDataY, "the first argument (known_data_y)"), tryCastAsNumberMatrix(knownDataX, "the second argument (known_data_x)"), tryCastAsNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
14523
14544
  },
14524
14545
  };
@@ -31645,7 +31666,7 @@ const SUPPORTED_HORIZONTAL_ALIGNMENTS = [
31645
31666
  ];
31646
31667
  const SUPPORTED_VERTICAL_ALIGNMENTS = ["top", "center", "bottom"];
31647
31668
  const SUPPORTED_FONTS = ["Arial"];
31648
- const SUPPORTED_FILL_PATTERNS = ["solid"];
31669
+ const SUPPORTED_FILL_PATTERNS = ["solid", "none"];
31649
31670
  const SUPPORTED_CF_TYPES = [
31650
31671
  "expression",
31651
31672
  "cellIs",
@@ -31829,7 +31850,7 @@ const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
31829
31850
  };
31830
31851
  /** Mapping between Excel format indexes (see XLSX_FORMAT_MAP) and some supported formats */
31831
31852
  const XLSX_FORMATS_CONVERSION_MAP = {
31832
- 0: "",
31853
+ 0: "General",
31833
31854
  1: "0",
31834
31855
  2: "0.00",
31835
31856
  3: "#,#00",
@@ -32128,11 +32149,11 @@ const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/
32128
32149
  * Excel format are defined in openXML §18.8.31
32129
32150
  */
32130
32151
  function convertXlsxFormat(numFmtId, formats, warningManager) {
32131
- if (numFmtId === 0) {
32132
- return undefined;
32133
- }
32134
32152
  // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
32135
32153
  let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || formats.find((f) => f.id === numFmtId)?.format;
32154
+ if (format === "General") {
32155
+ return undefined;
32156
+ }
32136
32157
  if (format) {
32137
32158
  try {
32138
32159
  let convertedFormat = format.replace(/(.*?);.*/, "$1"); // only take first part of multi-part format
@@ -34434,10 +34455,11 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
34434
34455
  });
34435
34456
  }
34436
34457
  extractRows(worksheet) {
34458
+ const spilledCells = new Set();
34437
34459
  return this.mapOnElements({ parent: worksheet, query: "sheetData row" }, (rowElement) => {
34438
34460
  return {
34439
34461
  index: this.extractAttr(rowElement, "r", { required: true })?.asNum(),
34440
- cells: this.extractCells(rowElement),
34462
+ cells: this.extractCells(rowElement, spilledCells),
34441
34463
  height: this.extractAttr(rowElement, "ht")?.asNum(),
34442
34464
  customHeight: this.extractAttr(rowElement, "customHeight")?.asBool(),
34443
34465
  hidden: this.extractAttr(rowElement, "hidden")?.asBool(),
@@ -34445,14 +34467,26 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
34445
34467
  };
34446
34468
  });
34447
34469
  }
34448
- extractCells(row) {
34470
+ extractCells(row, spilledCells) {
34449
34471
  return this.mapOnElements({ parent: row, query: "c" }, (cellElement) => {
34472
+ const xc = this.extractAttr(cellElement, "r", { required: true })?.asString();
34473
+ const formula = this.extractCellFormula(cellElement);
34474
+ if (formula?.ref && formula.sharedIndex === undefined) {
34475
+ const zone = toZone(formula.ref);
34476
+ for (const { col, row } of positions(zone)) {
34477
+ const followerXc = toXC(col, row);
34478
+ if (followerXc !== xc) {
34479
+ spilledCells.add(followerXc);
34480
+ }
34481
+ }
34482
+ }
34483
+ const isSpilled = spilledCells.has(xc);
34450
34484
  return {
34451
- xc: this.extractAttr(cellElement, "r", { required: true })?.asString(),
34485
+ xc,
34452
34486
  styleIndex: this.extractAttr(cellElement, "s")?.asNum(),
34453
34487
  type: CELL_TYPE_CONVERSION_MAP[this.extractAttr(cellElement, "t", { default: "n" })?.asString()],
34454
- value: this.extractChildTextContent(cellElement, "v"),
34455
- formula: this.extractCellFormula(cellElement),
34488
+ value: isSpilled ? undefined : this.extractChildTextContent(cellElement, "v") ?? undefined,
34489
+ formula: isSpilled ? undefined : formula,
34456
34490
  };
34457
34491
  });
34458
34492
  }
@@ -34460,11 +34494,14 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
34460
34494
  const formulaElement = this.querySelector(cellElement, "f");
34461
34495
  if (!formulaElement)
34462
34496
  return undefined;
34463
- return {
34464
- content: this.extractTextContent(formulaElement),
34465
- sharedIndex: this.extractAttr(formulaElement, "si")?.asNum(),
34466
- ref: this.extractAttr(formulaElement, "ref")?.asString(),
34467
- };
34497
+ const content = this.extractTextContent(formulaElement);
34498
+ const sharedIndex = this.extractAttr(formulaElement, "si")?.asNum();
34499
+ const ref = this.extractAttr(formulaElement, "ref")?.asString();
34500
+ // This is the case of spilled cells of array formulas where <f> is empty
34501
+ if ((content === undefined || content.trim() === "") && sharedIndex === undefined) {
34502
+ return undefined;
34503
+ }
34504
+ return { content, sharedIndex, ref };
34468
34505
  }
34469
34506
  extractHyperLinks(worksheet) {
34470
34507
  return this.mapOnElements({ parent: worksheet, query: "hyperlink" }, (linkElement) => {
@@ -45498,7 +45535,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45498
45535
  queuedBordersToAdd = {};
45499
45536
  queuedCFChanges = {};
45500
45537
  queuedDVChanges = {};
45501
- constructor(zones, operation, getters, dispatch, selection) {
45538
+ constructor(zones, operation, getters, dispatch, selection, mode = "copyPaste") {
45502
45539
  super(operation, getters, dispatch, selection);
45503
45540
  if (!zones.length) {
45504
45541
  this.cells = [[]];
@@ -45528,7 +45565,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45528
45565
  const spreader = getters.getArrayFormulaSpreadingOn(position);
45529
45566
  let cell = getters.getCell(position);
45530
45567
  const evaluatedCell = getters.getEvaluatedCell(position);
45531
- if (spreader) {
45568
+ if (mode !== "shiftCells" && spreader) {
45532
45569
  const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
45533
45570
  const content = isSpreaderCopied
45534
45571
  ? ""
@@ -49089,12 +49126,12 @@ class ClipboardPlugin extends UIPlugin {
49089
49126
  }
49090
49127
  case "INSERT_CELL": {
49091
49128
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
49092
- const state = this.getClipboardStateForCopyCells(cut, "CUT");
49129
+ const state = this.getClipboardStateForCopyCells(cut, "CUT", "shiftCells");
49093
49130
  return state.isPasteAllowed(paste);
49094
49131
  }
49095
49132
  case "DELETE_CELL": {
49096
49133
  const { cut, paste } = this.getDeleteCellsTargets(cmd.zone, cmd.shiftDimension);
49097
- const state = this.getClipboardStateForCopyCells(cut, "CUT");
49134
+ const state = this.getClipboardStateForCopyCells(cut, "CUT", "shiftCells");
49098
49135
  return state.isPasteAllowed(paste);
49099
49136
  }
49100
49137
  case "ACTIVATE_PAINT_FORMAT": {
@@ -49175,13 +49212,13 @@ class ClipboardPlugin extends UIPlugin {
49175
49212
  });
49176
49213
  break;
49177
49214
  }
49178
- const state = this.getClipboardStateForCopyCells(cut, "CUT");
49215
+ const state = this.getClipboardStateForCopyCells(cut, "CUT", "shiftCells");
49179
49216
  state.paste(paste);
49180
49217
  break;
49181
49218
  }
49182
49219
  case "INSERT_CELL": {
49183
49220
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
49184
- const state = this.getClipboardStateForCopyCells(cut, "CUT");
49221
+ const state = this.getClipboardStateForCopyCells(cut, "CUT", "shiftCells");
49185
49222
  state.paste(paste);
49186
49223
  break;
49187
49224
  }
@@ -49331,8 +49368,8 @@ class ClipboardPlugin extends UIPlugin {
49331
49368
  }
49332
49369
  return { cut: [cut], paste: [paste] };
49333
49370
  }
49334
- getClipboardStateForCopyCells(zones, operation) {
49335
- return new ClipboardCellsState(zones, operation, this.getters, this.dispatch, this.selection);
49371
+ getClipboardStateForCopyCells(zones, operation, mode = "copyPaste") {
49372
+ return new ClipboardCellsState(zones, operation, this.getters, this.dispatch, this.selection, mode);
49336
49373
  }
49337
49374
  /**
49338
49375
  * Get the clipboard state from the given zones.
@@ -50902,7 +50939,7 @@ class GridSelectionPlugin extends UIPlugin {
50902
50939
  bottom: !isCol ? end + deltaRow : this.getters.getNumberRows(cmd.sheetId) - 1,
50903
50940
  },
50904
50941
  ];
50905
- const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
50942
+ const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection, "shiftCells");
50906
50943
  const base = isBasedBefore ? cmd.base : cmd.base + 1;
50907
50944
  const pasteTarget = [
50908
50945
  {
@@ -58454,6 +58491,6 @@ exports.setTranslationMethod = setTranslationMethod;
58454
58491
  exports.tokenize = tokenize;
58455
58492
 
58456
58493
 
58457
- __info__.version = "17.0.71";
58458
- __info__.date = "2025-08-21T06:37:34.192Z";
58459
- __info__.hash = "1592964";
58494
+ __info__.version = "17.0.73";
58495
+ __info__.date = "2025-09-11T08:40:46.367Z";
58496
+ __info__.hash = "a11c622";