@odoo/o-spreadsheet 19.3.15 → 19.3.17

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 19.3.15
6
- * @date 2026-08-10T12:32:40.247Z
7
- * @hash 25f760e
5
+ * @version 19.3.17
6
+ * @date 2026-08-21T15:31:50.940Z
7
+ * @hash cf8c614
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -4071,6 +4071,18 @@ stores.inject(MyMetaStore, storeInstance);
4071
4071
  default: return "";
4072
4072
  }
4073
4073
  }
4074
+ /**
4075
+ * Only converts the decimal separator, ignore number format such as % or dates
4076
+ */
4077
+ function toLocaleString(data, locale) {
4078
+ const value = toValue(data);
4079
+ switch (typeof value) {
4080
+ case "string": return value;
4081
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
4082
+ case "boolean": return value ? "TRUE" : "FALSE";
4083
+ default: return "";
4084
+ }
4085
+ }
4074
4086
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
4075
4087
  const normalizeString = memoize(function normalizeString(str) {
4076
4088
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -4745,6 +4757,9 @@ stores.inject(MyMetaStore, storeInstance);
4745
4757
  }
4746
4758
  };
4747
4759
  const functionRegistry = new FunctionRegistry();
4760
+ /** Formulas using one of these functions are never squished: they are always exported
4761
+ * as a full formula string, and they don't become the base formula of the next cells. */
4762
+ const NonSquishableFunctionRegistry = new Registry();
4748
4763
 
4749
4764
  //#endregion
4750
4765
  //#region src/types/locale.ts
@@ -29086,12 +29101,13 @@ stores.inject(MyMetaStore, storeInstance);
29086
29101
  }
29087
29102
  sortFilterZone(sortDirection) {
29088
29103
  const filterPosition = this.props.filterPosition;
29089
- const tableZone = this.table?.range.zone;
29104
+ const table = this.table;
29105
+ const tableZone = table?.range.zone;
29090
29106
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
29091
29107
  const sheetId = this.env.model.getters.getActiveSheetId();
29092
29108
  const contentZone = {
29093
29109
  ...tableZone,
29094
- top: tableZone.top + 1
29110
+ top: tableZone.top + table.config.numberOfHeaders
29095
29111
  };
29096
29112
  const sortAnchor = {
29097
29113
  col: filterPosition.col,
@@ -49865,7 +49881,8 @@ stores.inject(MyMetaStore, storeInstance);
49865
49881
  * The result of this method is:
49866
49882
  * - if the cell is not a formula, returns the content as is and resets the base formula
49867
49883
  * - if the cell is a formula:
49868
- * - if there is no previous formula, or the normalized formula is different from the previous one, resets the base formula to this one and returns the full formula string
49884
+ * - if there is no previous formula, or the normalized formula is different from the previous one or it contains blacklisted functions (see NonSquishableFunctionRegistry),
49885
+ * resets the base formula to this one and returns the full formula string
49869
49886
  * - else, compares the literal values and range dependencies to the previous formula, and for each parameter:
49870
49887
  * - for numbers: returns a relative change (+N or -N) if possible, else the full number or "=" if unchanged
49871
49888
  * - for strings: returns the full string if changed, else "="
@@ -49873,6 +49890,10 @@ stores.inject(MyMetaStore, storeInstance);
49873
49890
  * */
49874
49891
  squish(cell, forSheetId) {
49875
49892
  if (cell.isFormula) {
49893
+ if (NonSquishableFunctionRegistry.getAll().some((functionName) => cell.compiledFormula.usesSymbol(functionName))) {
49894
+ this.resetBaseFormula();
49895
+ return cell.compiledFormula.toFormulaString(this.getters, this.rangeToStringOptions);
49896
+ }
49876
49897
  let numbers = [];
49877
49898
  let strings = [];
49878
49899
  let references = [];
@@ -61054,7 +61075,7 @@ stores.inject(MyMetaStore, storeInstance);
61054
61075
  }
61055
61076
  message = {
61056
61077
  ...message,
61057
- commands: this.commandSquisher.squish(revision.commands)
61078
+ commands: revision.commands
61058
61079
  };
61059
61080
  }
61060
61081
  if (this.isReplayingInitialRevisions) throw new Error(`Trying to send a new revision while replaying initial revision. This can lead to endless dispatches every time the spreadsheet is open.
@@ -77208,7 +77229,7 @@ stores.inject(MyMetaStore, storeInstance);
77208
77229
  else if (_format === 0) {
77209
77230
  const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
77210
77231
  return transposeMatrix(_array).flatMap((row) => row.map((value) => {
77211
- return isEvaluationError(value.value) ? value.value : toString(value);
77232
+ return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
77212
77233
  })).join(rowSeparator);
77213
77234
  } else return new EvaluationError(_t("Format must be 0 or 1"));
77214
77235
  },
@@ -82969,7 +82990,7 @@ stores.inject(MyMetaStore, storeInstance);
82969
82990
  description: _t("Concatenation of two values."),
82970
82991
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
82971
82992
  compute: function(value1, value2) {
82972
- return toString(value1) + toString(value2);
82993
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
82973
82994
  },
82974
82995
  isExported: true
82975
82996
  };
@@ -83834,7 +83855,7 @@ stores.inject(MyMetaStore, storeInstance);
83834
83855
  description: _t("Remove non-printable characters from a piece of text."),
83835
83856
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
83836
83857
  compute: function(text) {
83837
- const _text = toString(text);
83858
+ const _text = toLocaleString(text, this.locale);
83838
83859
  let cleanedStr = "";
83839
83860
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
83840
83861
  return cleanedStr;
@@ -83845,7 +83866,7 @@ stores.inject(MyMetaStore, storeInstance);
83845
83866
  description: _t("Appends strings to one another."),
83846
83867
  args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
83847
83868
  compute: function(...datas) {
83848
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
83869
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
83849
83870
  },
83850
83871
  isExported: true
83851
83872
  };
@@ -83853,7 +83874,7 @@ stores.inject(MyMetaStore, storeInstance);
83853
83874
  description: _t("Tests whether two strings are identical."),
83854
83875
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
83855
83876
  compute: function(string1, string2) {
83856
- return toString(string1) === toString(string2);
83877
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
83857
83878
  },
83858
83879
  isExported: true
83859
83880
  };
@@ -83865,8 +83886,8 @@ stores.inject(MyMetaStore, storeInstance);
83865
83886
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
83866
83887
  ],
83867
83888
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
83868
- const _searchFor = toString(searchFor);
83869
- const _textToSearch = toString(textToSearch);
83889
+ const _searchFor = toLocaleString(searchFor, this.locale);
83890
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
83870
83891
  const _startingAt = toNumber(startingAt, this.locale);
83871
83892
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
83872
83893
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -83903,8 +83924,8 @@ stores.inject(MyMetaStore, storeInstance);
83903
83924
  description: _t("Concatenates elements of arrays with delimiter."),
83904
83925
  args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
83905
83926
  compute: function(delimiter, ...valuesOrArrays) {
83906
- const _delimiter = toString(delimiter);
83907
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
83927
+ const _delimiter = toLocaleString(delimiter, this.locale);
83928
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
83908
83929
  }
83909
83930
  };
83910
83931
  const LEFT = {
@@ -83913,7 +83934,7 @@ stores.inject(MyMetaStore, storeInstance);
83913
83934
  compute: function(text, ...args) {
83914
83935
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
83915
83936
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
83916
- return toString(text).substring(0, _numberOfCharacters);
83937
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
83917
83938
  },
83918
83939
  isExported: true
83919
83940
  };
@@ -83921,7 +83942,7 @@ stores.inject(MyMetaStore, storeInstance);
83921
83942
  description: _t("Length of a string."),
83922
83943
  args: [arg("text (string)", _t("The string whose length will be returned."))],
83923
83944
  compute: function(text) {
83924
- return toString(text).length;
83945
+ return toLocaleString(text, this.locale).length;
83925
83946
  },
83926
83947
  isExported: true
83927
83948
  };
@@ -83929,7 +83950,7 @@ stores.inject(MyMetaStore, storeInstance);
83929
83950
  description: _t("Converts a specified string to lowercase."),
83930
83951
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
83931
83952
  compute: function(text) {
83932
- return toString(text).toLowerCase();
83953
+ return toLocaleString(text, this.locale).toLowerCase();
83933
83954
  },
83934
83955
  isExported: true
83935
83956
  };
@@ -83941,7 +83962,7 @@ stores.inject(MyMetaStore, storeInstance);
83941
83962
  arg("extract_length (number)", _t("The length of the segment to extract."))
83942
83963
  ],
83943
83964
  compute: function(text, starting_at, extract_length) {
83944
- const _text = toString(text);
83965
+ const _text = toLocaleString(text, this.locale);
83945
83966
  const _starting_at = toNumber(starting_at, this.locale);
83946
83967
  const _extract_length = toNumber(extract_length, this.locale);
83947
83968
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -83954,7 +83975,7 @@ stores.inject(MyMetaStore, storeInstance);
83954
83975
  description: _t("Capitalizes each word in a specified string."),
83955
83976
  args: [arg("text_to_capitalize (string)", _t("The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."))],
83956
83977
  compute: function(text) {
83957
- return toString(text).replace(wordRegex, (word) => {
83978
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
83958
83979
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
83959
83980
  });
83960
83981
  },
@@ -84102,9 +84123,9 @@ stores.inject(MyMetaStore, storeInstance);
84102
84123
  compute: function(text, position, length, newText) {
84103
84124
  const _position = toNumber(position, this.locale);
84104
84125
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
84105
- const _text = toString(text);
84126
+ const _text = toLocaleString(text, this.locale);
84106
84127
  const _length = toNumber(length, this.locale);
84107
- const _newText = toString(newText);
84128
+ const _newText = toLocaleString(newText, this.locale);
84108
84129
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
84109
84130
  },
84110
84131
  isExported: true
@@ -84115,7 +84136,7 @@ stores.inject(MyMetaStore, storeInstance);
84115
84136
  compute: function(text, ...args) {
84116
84137
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
84117
84138
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
84118
- const _text = toString(text);
84139
+ const _text = toLocaleString(text, this.locale);
84119
84140
  const stringLength = _text.length;
84120
84141
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
84121
84142
  },
@@ -84129,8 +84150,8 @@ stores.inject(MyMetaStore, storeInstance);
84129
84150
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
84130
84151
  ],
84131
84152
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
84132
- const _searchFor = toString(searchFor).toLowerCase();
84133
- const _textToSearch = toString(textToSearch).toLowerCase();
84153
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
84154
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
84134
84155
  const _startingAt = toNumber(startingAt, this.locale);
84135
84156
  if (_textToSearch === "") return {
84136
84157
  value: CellErrorType.GenericError,
@@ -84160,8 +84181,8 @@ stores.inject(MyMetaStore, storeInstance);
84160
84181
  arg(`remove_empty_text (boolean, default=${SPLIT_DEFAULT_REMOVE_EMPTY_TEXT})`, _t("Whether or not to remove empty text messages from the split results. The default behavior is to treat consecutive delimiters as one (if TRUE). If FALSE, empty cells values are added between consecutive delimiters."))
84161
84182
  ],
84162
84183
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
84163
- const _text = toString(text);
84164
- const _delimiter = escapeRegExp(toString(delimiter));
84184
+ const _text = toLocaleString(text, this.locale);
84185
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
84165
84186
  const _splitByEach = toBoolean(splitByEach);
84166
84187
  const _removeEmptyText = toBoolean(removeEmptyText);
84167
84188
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -84183,10 +84204,10 @@ stores.inject(MyMetaStore, storeInstance);
84183
84204
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
84184
84205
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
84185
84206
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
84186
- const _textToSearch = toString(textToSearch);
84187
- const _searchFor = toString(searchFor);
84207
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
84208
+ const _searchFor = toLocaleString(searchFor, this.locale);
84188
84209
  if (_searchFor === "") return _textToSearch;
84189
- const _replaceWith = toString(replaceWith);
84210
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
84190
84211
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
84191
84212
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
84192
84213
  let n = 0;
@@ -84209,10 +84230,10 @@ stores.inject(MyMetaStore, storeInstance);
84209
84230
  arg("texts (string, range<string>, repeating)", _t("Text item to join."))
84210
84231
  ],
84211
84232
  compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
84212
- const _delimiter = toString(delimiter);
84233
+ const _delimiter = toLocaleString(delimiter, this.locale);
84213
84234
  const _ignoreEmpty = toBoolean(ignoreEmpty);
84214
84235
  let n = 0;
84215
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
84236
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
84216
84237
  },
84217
84238
  isExported: true
84218
84239
  };
@@ -84265,7 +84286,7 @@ stores.inject(MyMetaStore, storeInstance);
84265
84286
  description: _t("Removes space characters."),
84266
84287
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
84267
84288
  compute: function(text) {
84268
- return trimContent(toString(text));
84289
+ return trimContent(toLocaleString(text, this.locale));
84269
84290
  },
84270
84291
  isExported: true
84271
84292
  };
@@ -84273,7 +84294,7 @@ stores.inject(MyMetaStore, storeInstance);
84273
84294
  description: _t("Converts a specified string to uppercase."),
84274
84295
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
84275
84296
  compute: function(text) {
84276
- return toString(text).toUpperCase();
84297
+ return toLocaleString(text, this.locale).toUpperCase();
84277
84298
  },
84278
84299
  isExported: true
84279
84300
  };
@@ -84370,7 +84391,7 @@ stores.inject(MyMetaStore, storeInstance);
84370
84391
  args: [arg("url (string)", _t("The full URL of the link enclosed in quotation marks.")), arg("link_label (string, optional)", _t("The text to display in the cell, enclosed in quotation marks."))],
84371
84392
  compute: function(url, linkLabel) {
84372
84393
  const processedUrl = toString(url).trim();
84373
- const processedLabel = toString(linkLabel) || processedUrl;
84394
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
84374
84395
  if (processedUrl === "") return processedLabel;
84375
84396
  return markdownLink(processedLabel, processedUrl);
84376
84397
  },
@@ -84513,7 +84534,8 @@ stores.inject(MyMetaStore, storeInstance);
84513
84534
  supportedPivotPositionalFormulaRegistry,
84514
84535
  pivotToFunctionValueRegistry,
84515
84536
  migrationStepRegistry,
84516
- chartJsExtensionRegistry
84537
+ chartJsExtensionRegistry,
84538
+ NonSquishableFunctionRegistry
84517
84539
  };
84518
84540
  const helpers = {
84519
84541
  arg,
@@ -84766,8 +84788,8 @@ exports.stores = stores;
84766
84788
  exports.tokenColors = tokenColors;
84767
84789
  exports.tokenize = tokenize;
84768
84790
 
84769
- __info__.version = "19.3.15";
84770
- __info__.date = "2026-08-10T12:32:40.247Z";
84771
- __info__.hash = "25f760e";
84791
+ __info__.version = "19.3.17";
84792
+ __info__.date = "2026-08-21T15:31:50.940Z";
84793
+ __info__.hash = "cf8c614";
84772
84794
 
84773
84795
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);