Compare commits

..

92 Commits

Author SHA1 Message Date
Ahmed Ejaz
b455755bfc Update all locales with the latest Transifex translations 2026-02-21 02:34:10 +05:00
Gaetan Craig-Riou
619ecf9432 Merge pull request #13820 from openfoodfoundation/dependabot/npm_and_yarn/hotkeys-js-4.0.0
Bump hotkeys-js from 3.13.15 to 4.0.0
2026-02-20 09:29:22 +11:00
Gaetan Craig-Riou
4f152a9151 Merge pull request #13942 from openfoodfoundation/dependabot/bundler/rubocop-rails-2.34.3
Bump rubocop-rails from 2.34.2 to 2.34.3
2026-02-20 09:21:21 +11:00
dependabot[bot]
8d7252f078 Bump hotkeys-js from 3.13.15 to 4.0.0
Bumps [hotkeys-js](https://github.com/jaywcjlove/hotkeys-js) from 3.13.15 to 4.0.0.
- [Release notes](https://github.com/jaywcjlove/hotkeys-js/releases)
- [Commits](https://github.com/jaywcjlove/hotkeys-js/compare/v3.13.15...v4.0.0)

---
updated-dependencies:
- dependency-name: hotkeys-js
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-19 22:15:49 +00:00
Gaetan Craig-Riou
08399e753d Merge pull request #13941 from openfoodfoundation/dependabot/npm_and_yarn/tom-select-2.5.1
Bump tom-select from 2.4.6 to 2.5.1
2026-02-20 09:13:15 +11:00
Gaetan Craig-Riou
8d721ccac8 Merge pull request #13940 from mkllnk/faraday
Bump faraday from 2.9.0 to 2.14.1
2026-02-20 09:12:08 +11:00
Gaetan Craig-Riou
b7a1b39c1a Merge pull request #13937 from mkllnk/rack
Bump rack from 2.2.21 to 2.2.22
2026-02-20 09:06:16 +11:00
Gaetan Craig-Riou
e276fb0386 Merge pull request #13930 from openfoodfoundation/dependabot/bundler/rubocop-1.84.1
Bump rubocop from 1.81.7 to 1.84.2
2026-02-20 09:05:41 +11:00
Rachel Arnould
f86eb3fb82 Merge pull request #13920 from mkllnk/clean-name
Stop pre-selecting Paypal when creating a payment method
2026-02-19 11:38:56 +01:00
Rachel Arnould
0d46a3bc2e Merge pull request #13902 from mkllnk/refund-simple
Simplify refund logic
2026-02-19 11:06:00 +01:00
dependabot[bot]
0418163ad7 Bump rubocop-rails from 2.34.2 to 2.34.3
Bumps [rubocop-rails](https://github.com/rubocop/rubocop-rails) from 2.34.2 to 2.34.3.
- [Release notes](https://github.com/rubocop/rubocop-rails/releases)
- [Changelog](https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop-rails/compare/v2.34.2...v2.34.3)

---
updated-dependencies:
- dependency-name: rubocop-rails
  dependency-version: 2.34.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-19 09:34:06 +00:00
dependabot[bot]
2e67899bcc Bump tom-select from 2.4.6 to 2.5.1
Bumps [tom-select](https://github.com/orchidjs/tom-select) from 2.4.6 to 2.5.1.
- [Release notes](https://github.com/orchidjs/tom-select/releases)
- [Commits](https://github.com/orchidjs/tom-select/compare/v2.4.6...v2.5.1)

---
updated-dependencies:
- dependency-name: tom-select
  dependency-version: 2.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-19 09:32:42 +00:00
Maikel Linke
e2536ffe71 Bump faraday from 2.9.0 to 2.14.1 2026-02-19 14:07:47 +11:00
Maikel
2c6b758f66 Merge pull request #13938 from openfoodfoundation/dependabot/bundler/spring-4.4.2
Bump spring from 4.4.0 to 4.4.2
2026-02-19 09:54:03 +11:00
Rachel Arnould
fb0f379c43 Merge pull request #13823 from chahmedejaz/bugfix/13625-enterprise-fee-reports-throws-504
Some Enterprise Fee reports are unusable when managing big shops
2026-02-18 15:22:21 +01:00
dependabot[bot]
7a4aa9dcb1 Bump spring from 4.4.0 to 4.4.2
Bumps [spring](https://github.com/rails/spring) from 4.4.0 to 4.4.2.
- [Release notes](https://github.com/rails/spring/releases)
- [Changelog](https://github.com/rails/spring/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rails/spring/compare/v4.4.0...v4.4.2)

---
updated-dependencies:
- dependency-name: spring
  dependency-version: 4.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 09:33:00 +00:00
Maikel
4e8d37b0b5 Merge pull request #13935 from openfoodfoundation/dependabot/npm_and_yarn/webpack-5.105.1
Bump webpack from 5.105.0 to 5.105.1
2026-02-18 15:51:35 +11:00
Maikel
616de3a9c0 Merge pull request #13936 from openfoodfoundation/dependabot/bundler/knapsack_pro-9.2.2
Bump knapsack_pro from 8.4.0 to 9.2.2
2026-02-18 15:50:59 +11:00
Maikel Linke
16b3da66c2 Style/HashAsLastArrayItem 2026-02-18 12:45:37 +11:00
Maikel Linke
691995eeaa Bump rubocop from 1.84.1 to 1.84.2 2026-02-18 12:44:41 +11:00
Maikel Linke
045482e07d Revert "Deactivate cooldown for testing"
This reverts commit 13cec27f6b.
2026-02-18 12:11:37 +11:00
dependabot[bot]
62f25d4d31 Bump knapsack_pro from 8.4.0 to 9.2.2
Bumps [knapsack_pro](https://github.com/KnapsackPro/knapsack_pro-ruby) from 8.4.0 to 9.2.2.
- [Changelog](https://github.com/KnapsackPro/knapsack_pro-ruby/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v8.4.0...v9.2.2)

---
updated-dependencies:
- dependency-name: knapsack_pro
  dependency-version: 9.2.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 00:57:55 +00:00
dependabot[bot]
618c7028e2 Bump webpack from 5.105.0 to 5.105.1
Bumps [webpack](https://github.com/webpack/webpack) from 5.105.0 to 5.105.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.105.0...v5.105.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 00:55:57 +00:00
Maikel Linke
13cec27f6b Deactivate cooldown for testing 2026-02-18 11:54:32 +11:00
Maikel Linke
5b925517f0 Bump rack from 2.2.21 to 2.2.22 2026-02-18 11:52:33 +11:00
Maikel Linke
167099badf Lint/NoReturnInBeginEndBlocks 2026-02-18 09:44:19 +11:00
Maikel Linke
40d8839bc4 Style Heredoc indentation 2026-02-18 09:42:33 +11:00
Maikel Linke
da1e1a9859 Style indentation 2026-02-18 09:42:07 +11:00
Maikel
43ad7654b6 Merge pull request #13931 from openfoodfoundation/dependabot/bundler/listen-3.10.0
Bump listen from 3.9.0 to 3.10.0
2026-02-18 09:16:12 +11:00
dependabot[bot]
15d83724ea Bump listen from 3.9.0 to 3.10.0
Bumps [listen](https://github.com/guard/listen) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/guard/listen/releases)
- [Commits](https://github.com/guard/listen/compare/v3.9.0...v3.10.0)

---
updated-dependencies:
- dependency-name: listen
  dependency-version: 3.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:34:08 +00:00
dependabot[bot]
47652e9d25 Bump rubocop from 1.81.7 to 1.84.1
Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.81.7 to 1.84.1.
- [Release notes](https://github.com/rubocop/rubocop/releases)
- [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop/compare/v1.81.7...v1.84.1)

---
updated-dependencies:
- dependency-name: rubocop
  dependency-version: 1.84.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:31:48 +00:00
Gaetan Craig-Riou
b5315c2123 Merge pull request #13925 from openfoodfoundation/dependabot/npm_and_yarn/tom-select-2.4.6
Bump tom-select from 2.4.5 to 2.4.6
2026-02-17 09:56:09 +11:00
Gaetan Craig-Riou
97ce4eaccd Merge pull request #13928 from openfoodfoundation/dependabot/bundler/pry-0.16.0
Bump pry from 0.15.2 to 0.16.0
2026-02-17 09:48:40 +11:00
Gaetan Craig-Riou
8814427677 Merge pull request #13927 from openfoodfoundation/dependabot/bundler/bootsnap-1.22.0
Bump bootsnap from 1.19.0 to 1.22.0
2026-02-17 09:47:13 +11:00
Gaetan Craig-Riou
4b69e192ec Merge pull request #13926 from openfoodfoundation/dependabot/bundler/rubocop-rspec-3.9.0
Bump rubocop-rspec from 3.8.0 to 3.9.0
2026-02-17 09:45:46 +11:00
dependabot[bot]
9c99f4868c Bump pry from 0.15.2 to 0.16.0
Bumps [pry](https://github.com/pry/pry) from 0.15.2 to 0.16.0.
- [Release notes](https://github.com/pry/pry/releases)
- [Changelog](https://github.com/pry/pry/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pry/pry/compare/v0.15.2...v0.16.0)

---
updated-dependencies:
- dependency-name: pry
  dependency-version: 0.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:22:39 +00:00
dependabot[bot]
39d59c3a5f Bump bootsnap from 1.19.0 to 1.22.0
Bumps [bootsnap](https://github.com/rails/bootsnap) from 1.19.0 to 1.22.0.
- [Release notes](https://github.com/rails/bootsnap/releases)
- [Changelog](https://github.com/rails/bootsnap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rails/bootsnap/compare/v1.19.0...v1.22.0)

---
updated-dependencies:
- dependency-name: bootsnap
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:21:52 +00:00
dependabot[bot]
ad9a12da0e Bump rubocop-rspec from 3.8.0 to 3.9.0
Bumps [rubocop-rspec](https://github.com/rubocop/rubocop-rspec) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/rubocop/rubocop-rspec/releases)
- [Changelog](https://github.com/rubocop/rubocop-rspec/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop-rspec/compare/v3.8.0...v3.9.0)

---
updated-dependencies:
- dependency-name: rubocop-rspec
  dependency-version: 3.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:20:43 +00:00
dependabot[bot]
9968776726 Bump tom-select from 2.4.5 to 2.4.6
Bumps [tom-select](https://github.com/orchidjs/tom-select) from 2.4.5 to 2.4.6.
- [Release notes](https://github.com/orchidjs/tom-select/releases)
- [Commits](https://github.com/orchidjs/tom-select/compare/v2.4.5...v2.4.6)

---
updated-dependencies:
- dependency-name: tom-select
  dependency-version: 2.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:20:27 +00:00
Gaetan Craig-Riou
d99a88817d Merge pull request #13825 from openfoodfoundation/dependabot/bundler/omniauth-rails_csrf_protection-2.0.1
Bump omniauth-rails_csrf_protection from 1.0.2 to 2.0.1
2026-02-16 11:52:16 +11:00
dependabot[bot]
1b36cce816 Bump omniauth-rails_csrf_protection from 1.0.2 to 2.0.1
Bumps [omniauth-rails_csrf_protection](https://github.com/cookpad/omniauth-rails_csrf_protection) from 1.0.2 to 2.0.1.
- [Release notes](https://github.com/cookpad/omniauth-rails_csrf_protection/releases)
- [Commits](https://github.com/cookpad/omniauth-rails_csrf_protection/compare/v1.0.2...v2.0.1)

---
updated-dependencies:
- dependency-name: omniauth-rails_csrf_protection
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 00:30:33 +00:00
Gaetan Craig-Riou
4b157abd4d Merge pull request #13809 from openfoodfoundation/dependabot/bundler/active_storage_validations-3.0.3
Bump active_storage_validations from 3.0.2 to 3.0.3
2026-02-16 11:28:41 +11:00
Gaetan Craig-Riou
dc494bb0f9 Merge pull request #13801 from openfoodfoundation/dependabot/bundler/bullet-8.1.0
Bump bullet from 8.0.8 to 8.1.0
2026-02-16 11:12:39 +11:00
dependabot[bot]
b3e7b12b86 Bump active_storage_validations from 1.1.4 to 3.0.3
Bumps [active_storage_validations](https://github.com/igorkasyanchuk/active_storage_validations) from 1.1.4 to 3.0.3.
- [Release notes](https://github.com/igorkasyanchuk/active_storage_validations/releases)
- [Changelog](https://github.com/igorkasyanchuk/active_storage_validations/blob/master/CHANGES.md)
- [Commits](https://github.com/igorkasyanchuk/active_storage_validations/commits/3.0.03)

---
updated-dependencies:
- dependency-name: active_storage_validations
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 23:47:01 +00:00
dependabot[bot]
b69e6827cc Bump bullet from 8.0.8 to 8.1.0
Bumps [bullet](https://github.com/flyerhzm/bullet) from 8.0.8 to 8.1.0.
- [Changelog](https://github.com/flyerhzm/bullet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flyerhzm/bullet/compare/8.0.8...8.1.0)

---
updated-dependencies:
- dependency-name: bullet
  dependency-version: 8.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 23:46:31 +00:00
Gaetan Craig-Riou
340f459912 Merge pull request #13791 from openfoodfoundation/dependabot/bundler/responders-3.2.0
Bump responders from 3.1.1 to 3.2.0
2026-02-16 10:44:39 +11:00
dependabot[bot]
33a96d5a0f Bump responders from 3.1.1 to 3.2.0
Bumps [responders](https://github.com/heartcombo/responders) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/heartcombo/responders/releases)
- [Changelog](https://github.com/heartcombo/responders/blob/main/CHANGELOG.md)
- [Commits](https://github.com/heartcombo/responders/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: responders
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 22:49:10 +00:00
Gaetan Craig-Riou
93d6db9d44 Merge pull request #13924 from openfoodfoundation/dependabot/npm_and_yarn/sass-loader-16.0.7
Bump sass-loader from 16.0.6 to 16.0.7
2026-02-16 09:42:37 +11:00
dependabot[bot]
c1068af0ba Bump sass-loader from 16.0.6 to 16.0.7
Bumps [sass-loader](https://github.com/webpack/sass-loader) from 16.0.6 to 16.0.7.
- [Release notes](https://github.com/webpack/sass-loader/releases)
- [Changelog](https://github.com/webpack/sass-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/sass-loader/compare/v16.0.6...v16.0.7)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-version: 16.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 09:32:32 +00:00
Maikel Linke
482c2a4a6e Delete now unused method clean_name 2026-02-13 12:36:26 +11:00
Gaetan Craig-Riou
2557a4de39 Merge pull request #13921 from openfoodfoundation/dependabot/npm_and_yarn/qs-6.14.2
Bump qs from 6.14.1 to 6.14.2
2026-02-13 12:27:04 +11:00
dependabot[bot]
ab1f43f1ac Bump qs from 6.14.1 to 6.14.2
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 01:17:44 +00:00
Maikel
f921524588 Merge pull request #13923 from rioug/upgrade-redit-github-action
Upgrade redis-github-action to 1.8.1
2026-02-13 11:45:13 +11:00
Gaetan Craig-Riou
5cbd507c1e Upgrade redis-github-action to 1.8.1
The version we were using, uses an obsolete version of Docker
2026-02-13 10:20:51 +11:00
Gaetan Craig-Riou
532bfaaa2d Merge pull request #13919 from pacodelaluna/allow-s3-compatible-provider-for-db2fog-config
Allow S3-compatible provider for db2fog configuration
2026-02-13 10:07:20 +11:00
Gaetan Craig-Riou
70b31a4212 Merge pull request #13918 from openfoodfoundation/dependabot/npm_and_yarn/webpack-5.105.0
Bump webpack from 5.104.1 to 5.105.0
2026-02-13 09:49:06 +11:00
Maikel Linke
9da3b0ea01 User must choose payment method type on creation 2026-02-12 15:15:27 +11:00
Maikel Linke
51df612c51 Spec current default selection
And fix the matcher!

This matcher was written over ten years ago in 2015. But it never failed
wrong tests.
2026-02-12 15:15:27 +11:00
Maikel Linke
3be0cca230 Move options code into helper 2026-02-12 11:47:01 +11:00
Maikel Linke
283d13eb35 Move payment method display logic to helper
So we don't need to convert strings into classes to then only convert it
into the same string again.
2026-02-12 11:25:13 +11:00
François Turbelin
b21224d5ff Allow s3-compatible provider for db2fog configuration 2026-02-11 13:35:22 +01:00
dependabot[bot]
4744d7b741 Bump webpack from 5.104.1 to 5.105.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.104.1 to 5.105.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.104.1...v5.105.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 09:33:05 +00:00
Gaetan Craig-Riou
ac1dd74e23 Merge pull request #13914 from openfoodfoundation/dependabot/npm_and_yarn/babel/plugin-transform-runtime-7.29.0
Bump @babel/plugin-transform-runtime from 7.28.5 to 7.29.0
2026-02-11 10:13:13 +11:00
Gaetan Craig-Riou
970f486ec0 Merge pull request #13913 from openfoodfoundation/dependabot/npm_and_yarn/tom-select-2.4.5
Bump tom-select from 2.4.3 to 2.4.5
2026-02-11 10:12:11 +11:00
dependabot[bot]
8c244e8b56 Bump @babel/plugin-transform-runtime from 7.28.5 to 7.29.0
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.28.5 to 7.29.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.0/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-version: 7.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 09:33:14 +00:00
dependabot[bot]
c3cc01c677 Bump tom-select from 2.4.3 to 2.4.5
Bumps [tom-select](https://github.com/orchidjs/tom-select) from 2.4.3 to 2.4.5.
- [Release notes](https://github.com/orchidjs/tom-select/releases)
- [Commits](https://github.com/orchidjs/tom-select/compare/v2.4.3...v2.4.5)

---
updated-dependencies:
- dependency-name: tom-select
  dependency-version: 2.4.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 09:32:51 +00:00
Gaetan Craig-Riou
c63e60d782 Merge pull request #13910 from openfoodfoundation/dependabot/npm_and_yarn/babel/core-7.29.0
Bump @babel/core from 7.28.6 to 7.29.0
2026-02-10 09:24:06 +11:00
dependabot[bot]
11e8c9456d Bump @babel/core from 7.28.6 to 7.29.0
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.28.6 to 7.29.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-09 22:10:18 +00:00
Gaetan Craig-Riou
860a21f86f Merge pull request #13909 from openfoodfoundation/dependabot/npm_and_yarn/babel/preset-env-7.29.0
Bump @babel/preset-env from 7.28.6 to 7.29.0
2026-02-10 09:09:09 +11:00
dependabot[bot]
d24af18ff4 Bump @babel/preset-env from 7.28.6 to 7.29.0
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.28.6 to 7.29.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-version: 7.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-09 10:39:41 +00:00
David Cook
33b191f439 Merge pull request #13908 from chahmedejaz/bugfix/13776-datetime-format-for-finnish
Update Finnish date and datetime formats in locale file
2026-02-09 10:41:22 +11:00
Ahmed Ejaz
a690d39864 Update Finnish date and datetime formats in locale file 2026-02-08 18:41:49 +05:00
Ahmed Ejaz
7c0586db7b Enhance comment for shouldLoad function 2026-02-08 18:29:55 +05:00
Ahmed Ejaz
461fd00ccd Add specs for non-linked fees by introducing specs for non-admin users. Plus include specs refactoring 2026-02-08 18:06:54 +05:00
Maikel Linke
f872201fef Delete defunct spec
From a comment on Github:

> In this particular case, the spec is broken, actually. I just looked into it and you are right that StripeSCA supports payment profiles but the spec finds that the profile is not stored. And that is because `source.try(:save_requested_by_customer?)` returns false. We only store the profile when the customer is storing their credit card info.
2026-02-06 11:35:24 +11:00
Maikel Linke
162c58ac39 Check which methods we need to delegate
This implicit delegation makes it impossible to know which code is used
and which code is dead. The refund method is very rarely used though. So
we'll need to wait for a while.
2026-02-04 12:24:50 +11:00
Maikel Linke
02d47d0a0d Remove unused method #payment_profiles_supported?
And useless specs.
2026-02-04 11:37:53 +11:00
Maikel Linke
e86bf441ab Remove last use of #payment_profiles_supported? 2026-02-04 11:30:36 +11:00
Maikel Linke
f9617b8156 Remove broken dead code branch
The StripeSCA method is forwarding all missing methods to the provider
gateway. The ActiveMerchant gateway used to be decorated by our code and
that's how this code may have worked one day. But we removed the
decorator years ago:

- 549610bc35

A bit later we found that refunds are broken with Stripe:

- https://github.com/openfoodfoundation/openfoodnetwork/issues/12843

This commit could potentiall fix that, I guess? I haven't tested that.
It's a rare use case and not the aim of this work.
2026-02-04 11:30:36 +11:00
Maikel Linke
4650e30c09 Remove useless branch on credit method 2026-02-04 11:30:36 +11:00
Maikel Linke
2749965e73 Remove useless branch calling #void
StripeSCA is the only method with a different method signature for
`#void` but the additional parameter wasn't used. So this special case
can just be removed.
2026-02-04 11:30:36 +11:00
Maikel Linke
e87965bda0 Simplify storing of payment profiles
StripeSCA is the only payment method storing profiles following this
logic. This is the first step to remove indirection and let the payment
method handle this instead of the payment decided for the payment
method.
2026-02-04 11:30:36 +11:00
Ahmed Ejaz
9dcb3ec748 Refactor report routes to use scoped routes for better organization and readability 2026-02-04 03:55:07 +05:00
Ahmed Ejaz
90c23d0245 fix lint issue 2026-02-04 02:26:44 +05:00
Ahmed Ejaz
99e238d92d Improve reports AJAX search safety, permissions, and TomSelect UX
- Sanitize AJAX search queries to safely support wildcard searches (ILIKE)
- Centralize reports search actions in Spree::Ability and reuse across roles
- Fix TomSelect remote loading to fetch on dropdown open and typing
- Surface HTTP errors in TomSelect via showHttpError and improve error handling
- Update dropdown behavior to show proper “no results” feedback
- Move reports AJAX specs to request specs and expand pagination coverage
- Simplify searchable dropdown component attribute passing
2026-02-04 02:25:41 +05:00
Ahmed Ejaz
8eb9709a04 Fix specs:
Fix tom_select_options_value to conditionally include 'virtual_scroll' plugin based on remote URL presence
2026-02-01 19:45:39 +05:00
Ahmed Ejaz
5f31baa022 Refactor TomSelectController to enhance remote data loading and update SearchableDropdownComponent options 2026-02-01 19:32:18 +05:00
Ahmed Ejaz
b58834b11f Fix failing specs 2026-01-25 19:13:43 +05:00
Ahmed Ejaz
d4811648f1 Merge branch 'master' into bugfix/13625-enterprise-fee-reports-throws-504 2026-01-25 11:22:57 +05:00
Ahmed Ejaz
77fe1fa6f9 Refactor SearchableDropdownComponent and integrate remote data loading with TomSelect 2026-01-25 11:14:49 +05:00
Ahmed Ejaz
03b7c07495 Add tests for Select2AjaxController with AJAX functionality 2025-12-30 01:25:35 +05:00
Ahmed Ejaz
a3c08ceb7c Add AJAX search functionality for enterprise fees and related entities in reports 2025-12-30 01:25:25 +05:00
64 changed files with 1589 additions and 485 deletions

View File

@@ -46,7 +46,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
uses: supercharge/redis-github-action@1.8.1
with:
redis-version: 6
@@ -124,7 +124,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
uses: supercharge/redis-github-action@1.8.1
with:
redis-version: 6
@@ -211,7 +211,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
uses: supercharge/redis-github-action@1.8.1
with:
redis-version: 6
@@ -290,7 +290,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup redis
uses: supercharge/redis-github-action@1.4.0
uses: supercharge/redis-github-action@1.8.1
with:
redis-version: 6

View File

@@ -112,7 +112,7 @@ GEM
rails-html-sanitizer (~> 1.6)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (3.0.2)
active_storage_validations (3.0.3)
activejob (>= 6.1.4)
activemodel (>= 6.1.4)
activestorage (>= 6.1.4)
@@ -210,12 +210,12 @@ GEM
bigdecimal (3.3.1)
bindata (2.5.1)
bindex (0.8.1)
bootsnap (1.19.0)
bootsnap (1.22.0)
msgpack (~> 1.2)
bugsnag (6.28.0)
concurrent-ruby (~> 1.0)
builder (3.3.0)
bullet (8.0.8)
bullet (8.1.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
cable_ready (5.0.6)
@@ -242,7 +242,7 @@ GEM
marcel (~> 1.0)
nokogiri (~> 1.10, >= 1.10.4)
rubyzip (>= 1.3.0, < 3)
cgi (0.5.0)
cgi (0.5.1)
childprocess (5.0.0)
choice (0.2.0)
chronic (0.10.2)
@@ -257,7 +257,7 @@ GEM
combine_pdf (1.0.31)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.3.5)
concurrent-ruby (1.3.6)
connection_pool (2.5.5)
cookiejar (0.3.4)
crack (1.0.1)
@@ -278,7 +278,7 @@ GEM
database_cleaner-core (2.0.1)
datafoodconsortium-connector (1.2.0)
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
date (3.5.0)
date (3.5.1)
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
@@ -326,19 +326,21 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-follow_redirects (0.4.0)
faraday (>= 1, < 3)
faraday-net_http (3.1.1)
net-http
faraday-net_http (3.4.2)
net-http (~> 0.5)
ferrum (0.14)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
websocket-driver (>= 0.6, < 0.8)
ffaker (2.25.0)
ffi (1.17.2)
ffi (1.17.3)
flipper (1.3.6)
concurrent-ruby (< 2)
flipper-active_record (1.3.6)
@@ -396,7 +398,8 @@ GEM
sysexits (~> 1.1)
hashdiff (1.2.1)
hashery (2.1.2)
hashie (5.0.0)
hashie (5.1.0)
logger
highline (3.1.2)
reline
htmlentities (4.4.2)
@@ -425,10 +428,11 @@ GEM
activerecord (>= 3.0)
invisible_captcha (2.3.0)
rails (>= 5.2)
io-console (0.8.1)
io-console (0.8.2)
ipaddress (0.8.3)
irb (1.15.3)
irb (1.17.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jmespath (1.6.2)
@@ -438,7 +442,7 @@ GEM
thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (2.15.2)
json (2.18.1)
json-canonicalization (1.0.0)
json-jwt (1.17.0)
activesupport (>= 4.2)
@@ -464,7 +468,7 @@ GEM
activesupport (>= 4.2)
jwt (2.10.2)
base64
knapsack_pro (8.4.0)
knapsack_pro (9.2.2)
rake
language_server-protocol (3.17.0.5)
launchy (3.0.0)
@@ -474,11 +478,12 @@ GEM
launchy (>= 2.2, < 4)
link_header (0.0.8)
lint_roller (1.1.0)
listen (3.9.0)
listen (3.10.0)
logger
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0)
loofah (2.24.1)
loofah (2.25.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.0)
@@ -501,7 +506,8 @@ GEM
logger
mini_mime (1.1.5)
mini_portile2 (2.8.6)
minitest (5.26.2)
minitest (6.0.1)
prism (~> 1.5)
monetize (1.13.0)
money (~> 6.12)
money (6.16.0)
@@ -510,8 +516,8 @@ GEM
multi_json (1.17.0)
multi_xml (0.6.0)
mutex_m (0.3.0)
net-http (0.7.0)
uri
net-http (0.9.1)
uri (>= 0.11.1)
net-imap (0.5.12)
date
net-protocol
@@ -523,7 +529,8 @@ GEM
net-protocol
newrelic_rpm (9.24.0)
nio4r (2.7.5)
nokogiri (1.18.10)
nokogiri (1.19.0)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri-html5-inference (0.3.0)
nokogiri (~> 1.14)
@@ -539,7 +546,7 @@ GEM
logger
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.2)
omniauth-rails_csrf_protection (2.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.8.0)
@@ -568,7 +575,7 @@ GEM
parallel (1.27.0)
paranoia (2.6.4)
activerecord (>= 5.1, < 7.2)
parser (3.3.10.0)
parser (3.3.10.2)
ast (~> 2.4.1)
racc
paypal-sdk-core (0.3.4)
@@ -586,11 +593,12 @@ GEM
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.7.0)
prism (1.9.0)
private_address_check (0.5.0)
pry (0.15.2)
pry (0.16.0)
coderay (~> 1.1)
method_source (~> 1.0)
reline (>= 0.6.0)
psych (5.3.1)
date
stringio
@@ -610,7 +618,7 @@ GEM
railties (>= 4.2)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.21)
rack (2.2.22)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-oauth2 (2.3.0)
@@ -684,7 +692,7 @@ GEM
activesupport (>= 6.1.5)
i18n
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
rb-inotify (0.11.1)
ffi (~> 1.0)
rdf (3.3.4)
bcp47_spec (~> 0.2)
@@ -693,7 +701,7 @@ GEM
logger (~> 1.5)
ostruct (~> 0.6)
readline (~> 0.0)
rdoc (7.0.1)
rdoc (7.2.0)
erb
psych (>= 4.0.0)
tsort
@@ -709,9 +717,9 @@ GEM
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
responders (3.2.0)
actionpack (>= 7.0)
railties (>= 7.0)
rexml (3.4.4)
roadie (5.2.1)
css_parser (~> 1.4)
@@ -766,7 +774,7 @@ GEM
rswag-ui (2.17.0)
actionpack (>= 5.2, < 8.2)
railties (>= 5.2, < 8.2)
rubocop (1.81.7)
rubocop (1.84.2)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -774,25 +782,25 @@ GEM
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.47.1, < 2.0)
rubocop-ast (>= 1.49.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.48.0)
rubocop-ast (1.49.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
prism (~> 1.7)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-factory_bot (2.28.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.34.2)
rubocop-rails (2.34.3)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rspec (3.8.0)
rubocop-rspec (3.9.0)
lint_roller (~> 1.1)
rubocop (~> 1.81)
rubocop-rspec_rails (2.32.0)
@@ -844,7 +852,7 @@ GEM
caxlsx (<= 4.0)
csv
rodf
spring (4.4.0)
spring (4.4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-rubocop (0.4.0)
@@ -889,10 +897,10 @@ GEM
temple (0.10.4)
terminal-table (4.0.0)
unicode-display_width (>= 1.1.1, < 4)
thor (1.4.0)
thor (1.5.0)
thread-local (1.1.0)
tilt (2.6.1)
timeout (0.4.4)
timeout (0.6.0)
tsort (0.2.0)
ttfunk (1.8.0)
bigdecimal (~> 3.1)
@@ -914,7 +922,7 @@ GEM
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
uniform_notifier (1.17.0)
uniform_notifier (1.18.0)
uri (1.1.1)
valid_email2 (5.2.3)
activemodel (>= 3.2)

View File

@@ -1,17 +1,18 @@
# frozen_string_literal: true
class SearchableDropdownComponent < ViewComponent::Base
REMOVED_SEARCH_PLUGIN = { 'tom-select-options-value': '{ "plugins": [] }' }.freeze
MINIMUM_OPTIONS_FOR_SEARCH_FIELD = 11 # at least 11 options are required for the search field
def initialize(
form:,
name:,
options:,
selected_option:,
placeholder_value:,
form: nil,
placeholder_value: '',
include_blank: false,
aria_label: '',
multiple: false,
remote_url: nil,
other_attrs: {}
)
@f = form
@@ -21,13 +22,15 @@ class SearchableDropdownComponent < ViewComponent::Base
@placeholder_value = placeholder_value
@include_blank = include_blank
@aria_label = aria_label
@multiple = multiple
@remote_url = remote_url
@other_attrs = other_attrs
end
private
attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank,
:aria_label, :other_attrs
:aria_label, :multiple, :remote_url, :other_attrs
def classes
"fullwidth #{'no-input' if remove_search_plugin?}"
@@ -36,11 +39,33 @@ class SearchableDropdownComponent < ViewComponent::Base
def data
{
controller: "tom-select",
'tom-select-placeholder-value': placeholder_value
}.merge(remove_search_plugin? ? REMOVED_SEARCH_PLUGIN : {})
'tom-select-placeholder-value': placeholder_value,
'tom-select-options-value': tom_select_options_value,
'tom-select-remote-url-value': remote_url,
}
end
def tom_select_options_value
plugins = []
plugins << 'virtual_scroll' if @remote_url.present?
plugins << 'dropdown_input' unless remove_search_plugin?
plugins << 'remove_button' if multiple
{
plugins:,
maxItems: multiple ? nil : 1,
}
end
def uses_form_builder?
f.present?
end
def remove_search_plugin?
@remove_search_plugin ||= options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD
# Remove the search plugin when:
# - the select is multiple (it already includes a search field), or
# - there is no remote URL and the options are below the search threshold
@remove_search_plugin ||= multiple ||
(@remote_url.nil? && options.count < MINIMUM_OPTIONS_FOR_SEARCH_FIELD)
end
end

View File

@@ -1 +1,4 @@
= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label, **other_attrs
- if uses_form_builder?
= f.select name, options, { selected: selected_option, include_blank:, multiple: }, class: classes, data:, 'aria-label': aria_label, **other_attrs
- else
= select_tag name, options_for_select(options, selected_option), include_blank:, multiple:, class: classes, data:, 'aria-label': aria_label, **other_attrs

View File

@@ -4,6 +4,7 @@ module Admin
class ReportsController < Spree::Admin::BaseController
include ActiveStorage::SetCurrent
include ReportsActions
include Reports::AjaxSearch
helper ReportsHelper

View File

@@ -0,0 +1,108 @@
# frozen_string_literal: true
module Reports
module AjaxSearch
extend ActiveSupport::Concern
def search_enterprise_fees
report = report_class.new(spree_current_user, params, render: false)
fee_ids = enterprise_fee_ids(report.search.result)
query = EnterpriseFee.where(id: fee_ids)
render json: build_search_response(query)
end
def search_enterprise_fee_owners
report = report_class.new(spree_current_user, params, render: false)
owner_ids = enterprise_fee_owner_ids(report.search.result)
query = Enterprise.where(id: owner_ids)
render json: build_search_response(query)
end
def search_distributors
query = frontend_data.distributors
render json: build_search_response(query)
end
def search_order_cycles
query = frontend_data.order_cycles
render json: build_search_response(query)
end
def search_order_customers
query = frontend_data.order_customers
render json: build_search_response(query)
end
def search_suppliers
query = frontend_data.orders_suppliers
render json: build_search_response(query)
end
private
def build_search_response(query)
page = (params[:page] || 1).to_i
per_page = 30
filtered_query = apply_search_filter(query)
total_count = filtered_query.size
items = paginated_items(filtered_query, page, per_page)
results = format_results(items)
{ results: results, pagination: { more: (page * per_page) < total_count } }
end
def apply_search_filter(query)
search_term = params[:q]
return query if search_term.blank?
escaped_search_term = ActiveRecord::Base.sanitize_sql_like(search_term)
pattern = "%#{escaped_search_term}%"
# Handle different model types
if query.model == OrderCycle
query.where("order_cycles.name ILIKE ?", pattern)
elsif query.model == Customer
query.where("customers.email ILIKE ?", pattern)
else
query.where("name ILIKE ?", pattern)
end
end
def paginated_items(query, page, per_page)
if query.model == Customer
query.order(:email).offset((page - 1) * per_page).limit(per_page).pluck(:email, :id)
elsif query.model == OrderCycle
query.order('order_cycles.orders_close_at DESC')
.offset((page - 1) * per_page)
.limit(per_page).pluck(
:name, :id
)
else
query.order(:name).offset((page - 1) * per_page).limit(per_page).pluck(:name, :id)
end
end
def format_results(items)
items.map { |label, value| { value:, label: } }
end
def frontend_data
@frontend_data ||= Reporting::FrontendData.new(spree_current_user)
end
def enterprise_fee_owner_ids(orders)
EnterpriseFee.where(id: enterprise_fee_ids(orders)).select(:enterprise_id)
end
def enterprise_fee_ids(orders)
Spree::Adjustment.enterprise_fee.where(order_id: orders.select(:id)).select(:originator_id)
end
end
end

View File

@@ -138,7 +138,7 @@ module Spree
providers.delete("Spree::Gateway::StripeSCA") unless show_stripe?
providers.map(&:constantize)
providers
end
# Show Stripe as an option if enabled, or if the

View File

@@ -95,8 +95,7 @@ module Spree
private
def load_payment_source
if @payment.payment_method.is_a?(Spree::Gateway) &&
@payment.payment_method.payment_profiles_supported? &&
if @payment.payment_method.is_a?(Gateway::StripeSCA) &&
params[:card].present? &&
(params[:card] != 'new')
@payment.source = CreditCard.find_by(id: params[:card])

View File

@@ -34,29 +34,8 @@ module ReportsHelper
end
end
def fee_name_options(orders)
EnterpriseFee.where(id: enterprise_fee_ids(orders))
.pluck(:name, :id)
end
def fee_owner_options(orders)
Enterprise.where(id: enterprise_fee_owner_ids(orders))
.pluck(:name, :id)
end
delegate :currency_symbol, to: :'Spree::Money'
def enterprise_fee_owner_ids(orders)
EnterpriseFee.where(id: enterprise_fee_ids(orders))
.pluck(:enterprise_id)
end
def enterprise_fee_ids(orders)
Spree::Adjustment.enterprise_fee
.where(order_id: orders.map(&:id))
.pluck(:originator_id)
end
def datepicker_time(datetime)
datetime = Time.zone.parse(datetime) if datetime.is_a? String
datetime.strftime('%Y-%m-%d %H:%M')

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Spree
module Admin
module PaymentMethodsHelper
def payment_method_type_name(class_name)
scope = "spree.admin.payment_methods.providers"
key = class_name.demodulize.downcase
I18n.t(key, scope:)
end
def payment_method_type_options(providers)
providers.map { |p| [payment_method_type_name(p), p] }
end
end
end
end

View File

@@ -6,6 +6,11 @@ module Spree
class Ability
include CanCan::Ability
REPORTS_SEARCH_ACTIONS = [
:search_enterprise_fees, :search_enterprise_fee_owners, :search_distributors,
:search_suppliers, :search_order_cycles, :search_order_customers
].freeze
def initialize(user)
clear_aliased_actions
@@ -260,7 +265,8 @@ module Spree
can [:admin, :index, :import], ::Admin::DfcProductImportsController
# Reports page
can [:admin, :index, :show, :create], ::Admin::ReportsController
can [:admin, :index, :show, :create, *REPORTS_SEARCH_ACTIONS],
::Admin::ReportsController
can [:admin, :show, :create, :customers, :orders_and_distributors, :group_buys, :payments,
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management,
:packing, :enterprise_fee_summary, :bulk_coop, :suppliers], :report
@@ -392,7 +398,7 @@ module Spree
end
# Reports page
can [:admin, :index, :show, :create], ::Admin::ReportsController
can [:admin, :index, :show, :create, *REPORTS_SEARCH_ACTIONS], ::Admin::ReportsController
can [:admin, :customers, :group_buys, :sales_tax, :payments,
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
:order_cycle_management, :xero_invoices, :enterprise_fee_summary, :bulk_coop], :report

View File

@@ -5,7 +5,7 @@ module Spree
acts_as_taggable
include PaymentMethodDistributors
delegate :authorize, :purchase, :capture, :void, :credit, to: :provider
delegate :authorize, :purchase, :capture, :void, :credit, :refund, to: :provider
validates :name, :type, presence: true
@@ -35,6 +35,10 @@ module Spree
end
def method_missing(method, *)
message = "Deprecated delegation of Gateway##{method}"
Alert.raise(message)
raise message if Rails.env.local?
if @provider.nil? || !@provider.respond_to?(method)
super
else
@@ -42,10 +46,6 @@ module Spree
end
end
def payment_profiles_supported?
false
end
def method_type
'gateway'
end

View File

@@ -35,10 +35,6 @@ module Spree
ActiveMerchant::Billing::StripePaymentIntentsGateway
end
def payment_profiles_supported?
true
end
def stripe_account_id
StripeAccount.find_by(enterprise_id: preferred_enterprise_id)&.stripe_user_id
end
@@ -84,7 +80,7 @@ module Spree
end
# NOTE: this method is required by Spree::Payment::Processing
def void(payment_intent_id, _creditcard, gateway_options)
def void(payment_intent_id, gateway_options)
payment_intent_response = Stripe::PaymentIntent.retrieve(
payment_intent_id, stripe_account: stripe_account_id
)
@@ -101,7 +97,13 @@ module Spree
end
# NOTE: this method is required by Spree::Payment::Processing
def credit(money, _creditcard, payment_intent_id, gateway_options)
def credit(money, payment_intent_id, gateway_options)
gateway_options[:stripe_account] = stripe_account_id
provider.refund(money, payment_intent_id, gateway_options)
end
# NOTE: this method is required by Spree::Payment::Processing
def refund(money, payment_intent_id, gateway_options)
gateway_options[:stripe_account] = stripe_account_id
provider.refund(money, payment_intent_id, gateway_options)
end

View File

@@ -34,7 +34,7 @@ module Spree
# invalidate previously entered payments
after_create :invalidate_old_payments
after_save :create_payment_profile, if: :profiles_supported?
after_save :create_payment_profile
# update the order totals, etc.
after_save :ensure_correct_adjustment, :update_order
@@ -217,18 +217,13 @@ module Spree
errors.blank?
end
def profiles_supported?
payment_method.respond_to?(:payment_profiles_supported?) &&
payment_method.payment_profiles_supported?
end
def create_payment_profile
return unless source.is_a?(CreditCard)
return unless source.try(:save_requested_by_customer?)
return unless source.number || source.gateway_payment_profile_id
return unless source.gateway_customer_profile_id.nil?
payment_method.create_profile(self)
payment_method.try(:create_profile, self)
rescue ActiveMerchant::ConnectionError => e
gateway_error e
end

View File

@@ -58,16 +58,7 @@ module Spree
protect_from_connection_error do
check_environment
response = if payment_method.payment_profiles_supported?
# Gateways supporting payment profiles will need access to credit
# card object because this stores the payment profile information
# so supply the authorization itself as well as the credit card,
# rather than just the authorization code
payment_method.void(response_code, source, gateway_options)
else
# Standard ActiveMerchant void usage
payment_method.void(response_code, gateway_options)
end
response = payment_method.void(response_code, gateway_options)
record_response(response)
@@ -86,20 +77,11 @@ module Spree
credit_amount = calculate_refund_amount(credit_amount)
response = if payment_method.payment_profiles_supported?
payment_method.credit(
(credit_amount * 100).round,
source,
response_code,
gateway_options
)
else
payment_method.credit(
(credit_amount * 100).round,
response_code,
gateway_options
)
end
response = payment_method.credit(
(credit_amount * 100).round,
response_code,
gateway_options
)
record_response(response)
@@ -125,20 +107,11 @@ module Spree
refund_amount = calculate_refund_amount(refund_amount)
response = if payment_method.payment_profiles_supported?
payment_method.refund(
(refund_amount * 100).round,
source,
response_code,
gateway_options
)
else
payment_method.refund(
(refund_amount * 100).round,
response_code,
gateway_options
)
end
response = payment_method.refund(
(refund_amount * 100).round,
response_code,
gateway_options
)
record_response(response)

View File

@@ -93,10 +93,6 @@ module Spree
unscoped { find(*) }
end
def payment_profiles_supported?
false
end
def source_required?
true
end
@@ -113,11 +109,6 @@ module Spree
distributors.include?(distributor)
end
def self.clean_name
scope = "spree.admin.payment_methods.providers"
I18n.t(name.demodulize.downcase, scope:)
end
private
def distributor_validation

View File

@@ -251,7 +251,7 @@ module Spree
transaction do
ExchangeVariant.
where(exchange_variants: { variant_id: variants.with_deleted.
select(:id) }).destroy_all
select(:id) }).destroy_all
yield
end

View File

@@ -35,8 +35,8 @@ module Spree
taxons
.pluck('spree_taxons.id, enterprises.id AS enterprise_id')
.each_with_object({}) do |(taxon_id, enterprise_id), collection|
collection[enterprise_id.to_i] ||= Set.new
collection[enterprise_id.to_i] << taxon_id
collection[enterprise_id.to_i] ||= Set.new
collection[enterprise_id.to_i] << taxon_id
end
end

View File

@@ -167,8 +167,8 @@ module Spree
# In Rails 3, merging two scopes on the same column will consider only the last scope.
def self.in_distributor(distributor)
where(id: ExchangeVariant.select(:variant_id).
joins(:exchange).
where('exchanges.incoming = ? AND exchanges.receiver_id = ?', false, distributor))
joins(:exchange).
where('exchanges.incoming = ? AND exchanges.receiver_id = ?', false, distributor))
end
def self.indexed
@@ -179,11 +179,11 @@ module Spree
# "where(id:" is necessary so that the returned relation has no includes
# The relation without includes will not be readonly and allow updates on it
where(spree_variants: { id: joins(:prices).
where(deleted_at: nil).
where('spree_prices.currency' =>
where(deleted_at: nil).
where('spree_prices.currency' =>
currency || CurrentConfig.get(:currency)).
where.not(spree_prices: { amount: nil }).
select("spree_variants.id") })
where.not(spree_prices: { amount: nil }).
select("spree_variants.id") })
end
def self.linked_to(semantic_id)

View File

@@ -60,8 +60,8 @@ class ProductScopeQuery
def product_query_includes
[
image: { attachment_attachment: :blob },
variants: [:default_price, :stock_items, :variant_overrides]
{ image: { attachment_attachment: :blob },
variants: [:default_price, :stock_items, :variant_overrides] }
]
end

View File

@@ -42,10 +42,10 @@ class DfcCatalogImporter
.includes(:semantic_links).references(:semantic_links)
.where.not(semantic_links: { semantic_id: present_ids })
.select do |variant|
# Variants that were in the same catalog before:
variant.semantic_links.map(&:semantic_id).any? do |semantic_id|
FdcUrlBuilder.new(semantic_id).catalog_url == catalog_url
end
# Variants that were in the same catalog before:
variant.semantic_links.map(&:semantic_id).any? do |semantic_id|
FdcUrlBuilder.new(semantic_id).catalog_url == catalog_url
end
end
end
end

View File

@@ -51,8 +51,8 @@ class LineItemSyncer
def destroy_obsolete_items(order)
order.line_items.
where(variant_id: subscription_line_items.
select(&:marked_for_destruction?).
map(&:variant_id)).
select(&:marked_for_destruction?).
map(&:variant_id)).
destroy_all
end

View File

@@ -130,12 +130,11 @@ module Orders
def order_cycle_fees
return @order_cycle_fees if defined? @order_cycle_fees
return [] unless order_cycle && distributor
@order_cycle_fees = begin
fees = []
return fees unless order_cycle && distributor
order_cycle.exchanges.supplying_to(distributor).each do |exchange|
exchange.enterprise_fees.per_item.each do |enterprise_fee|
fee_value = FeeValue.new(fee: enterprise_fee, role: exchange.role)

View File

@@ -14,12 +14,12 @@ module PermittedAttributes
def self.attributes
basic_permitted_attributes + [
group_ids: [], user_ids: [],
shipping_method_ids: [], payment_method_ids: [],
address_attributes: PermittedAttributes::Address.attributes,
business_address_attributes: PermittedAttributes::BusinessAddress.attributes,
producer_properties_attributes: [:id, :property_name, :value, :_destroy],
custom_tab_attributes: PermittedAttributes::CustomTab.attributes,
{ group_ids: [], user_ids: [],
shipping_method_ids: [], payment_method_ids: [],
address_attributes: PermittedAttributes::Address.attributes,
business_address_attributes: PermittedAttributes::BusinessAddress.attributes,
producer_properties_attributes: [:id, :property_name, :value, :_destroy],
custom_tab_attributes: PermittedAttributes::CustomTab.attributes },
]
end

View File

@@ -25,8 +25,8 @@ module PermittedAttributes
private
def attributes
self.class.basic_attributes + [incoming_exchanges: permitted_exchange_attributes,
outgoing_exchanges: permitted_exchange_attributes]
self.class.basic_attributes + [{ incoming_exchanges: permitted_exchange_attributes,
outgoing_exchanges: permitted_exchange_attributes }]
end
def permitted_exchange_attributes

View File

@@ -26,11 +26,11 @@ module PermittedAttributes
def other_permitted_attributes
[
subscription_line_items_attributes: [
:id, :quantity, :variant_id, :price_estimate, :_destroy
],
bill_address_attributes: PermittedAttributes::Address.attributes,
ship_address_attributes: PermittedAttributes::Address.attributes
{ subscription_line_items_attributes: [
:id, :quantity, :variant_id, :price_estimate, :_destroy
],
bill_address_attributes: PermittedAttributes::Address.attributes,
ship_address_attributes: PermittedAttributes::Address.attributes }
]
end
end

View File

@@ -1,23 +1,50 @@
- search_url_query = {report_type: :enterprise_fee_summary, report_subtype: :enterprise_fees_with_tax_report_by_order}
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.omega.fourteen.columns
= render(SearchableDropdownComponent.new(form: f,
name: :distributor_id_in,
options: [],
selected_option: params.dig(:q, :distributor_id_in),
multiple: true,
remote_url: admin_reports_search_distributors_url))
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :order_cycle_id_in,
options: [],
selected_option: params.dig(:q, :order_cycle_id_in),
multiple: true,
remote_url: admin_reports_search_order_cycles_url))
.row
.alpha.two.columns= label_tag nil, t(:fee_name)
.omega.fourteen.columns
= f.select(:enterprise_fee_id_in, fee_name_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :enterprise_fee_id_in,
options: [],
selected_option: params.dig(:q, :enterprise_fee_id_in),
multiple: true,
remote_url: admin_reports_search_enterprise_fees_url(search_url_query)))
.row
.alpha.two.columns= label_tag nil, t(:fee_owner)
.omega.fourteen.columns
= f.select(:enterprise_fee_owner_id_in, fee_owner_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_owner_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :enterprise_fee_owner_id_in,
options: [],
selected_option: params.dig(:q, :enterprise_fee_owner_id_in),
multiple: true,
remote_url: admin_reports_search_enterprise_fee_owners_url(search_url_query)))
.row
.alpha.two.columns= label_tag nil, t(:report_customers)
.omega.fourteen.columns
= f.select(:customer_id_in, customer_email_options(@data.order_customers), {selected: params.dig(:q, :customer_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :customer_id_in,
options: [],
selected_option: params.dig(:q, :customer_id_in),
multiple: true,
remote_url: admin_reports_search_order_customers_url))

View File

@@ -1,27 +1,57 @@
- search_url_query = {report_type: :enterprise_fee_summary, report_subtype: :enterprise_fees_with_tax_report_by_producer}
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.omega.fourteen.columns
= render(SearchableDropdownComponent.new(form: f,
name: :distributor_id_in,
options: [],
selected_option: params.dig(:q, :distributor_id_in),
multiple: true,
remote_url: admin_reports_search_distributors_url))
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@data.orders_suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true})
.omega.fourteen.columns
= render(SearchableDropdownComponent.new(name: :supplier_id_in,
options: [],
selected_option: params.dig(:supplier_id_in),
multiple: true,
remote_url: admin_reports_search_suppliers_url))
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :order_cycle_id_in,
options: [],
selected_option: params.dig(:q, :order_cycle_id_in),
multiple: true,
remote_url: admin_reports_search_order_cycles_url))
.row
.alpha.two.columns= label_tag nil, t(:fee_name)
.omega.fourteen.columns
= f.select(:enterprise_fee_id_in, fee_name_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :enterprise_fee_id_in,
options: [],
selected_option: params.dig(:q, :enterprise_fee_id_in),
multiple: true,
remote_url: admin_reports_search_enterprise_fees_url(search_url_query)))
.row
.alpha.two.columns= label_tag nil, t(:fee_owner)
.omega.fourteen.columns
= f.select(:enterprise_fee_owner_id_in, fee_owner_options(@report.search.result), {selected: params.dig(:q, :enterprise_fee_owner_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :enterprise_fee_owner_id_in,
options: [],
selected_option: params.dig(:q, :enterprise_fee_owner_id_in),
multiple: true,
remote_url: admin_reports_search_enterprise_fee_owners_url(search_url_query)))
.row
.alpha.two.columns= label_tag nil, t(:report_customers)
.omega.fourteen.columns
= f.select(:customer_id_in, customer_email_options(@data.order_customers), {selected: params.dig(:q, :customer_id_in)}, {class: "select2 fullwidth", multiple: true})
= render(SearchableDropdownComponent.new(form: f,
name: :customer_id_in,
options: [],
selected_option: params.dig(:q, :customer_id_in),
multiple: true,
remote_url: admin_reports_search_order_customers_url))

View File

@@ -3,6 +3,6 @@
.alpha.four.columns
= label :payment_method, :type, t('.provider')
.omega.twelve.columns
= collection_select(:payment_method, :type, @providers, :to_s, :clean_name, {}, { class: 'select2 fullwidth', 'provider-prefs-for' => "#{@object.id}"})
= select(:payment_method, :type, payment_method_type_options(@providers), {}, { class: 'select2 fullwidth', required: true, placeholder: t("admin.choose"), 'provider-prefs-for' => "#{@object.id}"})
%div{"ng-include" => "include_html" }

View File

@@ -37,7 +37,7 @@
- method.distributors.each do |distributor|
= distributor.name
%br/
%td= method.class.clean_name
%td= payment_method_type_name(method.class.name)
- if spree_current_user.admin?
%td.align-center= method.environment.to_s.titleize
%td.align-center= method.display_on.blank? ? t('.both') : t('.' + method.display_on.to_s)

View File

@@ -1,11 +1,16 @@
import { Controller } from "stimulus";
import TomSelect from "tom-select/dist/esm/tom-select.complete";
import showHttpError from "../../webpacker/js/services/show_http_error";
export default class extends Controller {
static values = { options: Object, placeholder: String };
static values = {
options: Object,
placeholder: String,
remoteUrl: String,
};
connect(options = {}) {
this.control = new TomSelect(this.element, {
let tomSelectOptions = {
maxItems: 1,
maxOptions: null,
plugins: ["dropdown_input"],
@@ -16,7 +21,13 @@ export default class extends Controller {
},
...this.optionsValue,
...options,
});
};
if (this.remoteUrlValue) {
this.#addRemoteOptions(tomSelectOptions);
}
this.control = new TomSelect(this.element, tomSelectOptions);
}
disconnect() {
@@ -29,4 +40,78 @@ export default class extends Controller {
const optionsArray = [...this.element.options];
return optionsArray.find((option) => [null, ""].includes(option.value))?.text;
}
#buildUrl(query, page = 1) {
const url = new URL(this.remoteUrlValue, window.location.origin);
url.searchParams.set("q", query);
url.searchParams.set("page", page);
return url.toString();
}
#fetchOptions(query, callback) {
const url = this.control.getUrl(query);
fetch(url)
.then((response) => {
if (!response.ok) {
showHttpError(response.status);
throw response;
}
return response.json();
})
.then((json) => {
/**
* Expected API shape:
* {
* results: [{ value, label }],
* pagination: { more: boolean }
* }
*/
if (json.pagination?.more) {
const currentUrl = new URL(url);
const currentPage = parseInt(currentUrl.searchParams.get("page") || "1");
const nextUrl = this.#buildUrl(query, currentPage + 1);
this.control.setNextUrl(query, nextUrl);
}
callback(json.results || []);
})
.catch((error) => {
callback();
console.error(error);
});
}
#addRemoteOptions(options) {
this.openedByClick = false;
options.firstUrl = (query) => {
return this.#buildUrl(query, 1);
};
options.load = this.#fetchOptions.bind(this);
options.onFocus = function () {
this.control.load("", () => {});
}.bind(this);
options.onDropdownOpen = function () {
this.openedByClick = true;
}.bind(this);
options.onType = function () {
this.openedByClick = false;
}.bind(this);
// As per TomSelect source code, Loading state is shown on the UI when this function returns true.
// By default it shows loading state only when there is some input in the search box.
// We want to show loading state on focus as well (when there is no input) to indicate that options are being loaded.
options.shouldLoad = function (query) {
return this.openedByClick || query.length > 0;
}.bind(this);
options.valueField = "value";
options.labelField = "label";
options.searchField = "label";
}
}

View File

@@ -2,16 +2,27 @@ require_relative 'spree'
Rails.application.reloader.to_prepare do
# See: https://github.com/openfoodfoundation/db2fog
DB2Fog.config = {
if ENV['S3_BACKUPS_HOST'].present?
DB2Fog.config = {
aws_access_key_id: ENV['S3_BACKUPS_ACCESS_KEY'],
aws_secret_access_key: ENV['S3_BACKUPS_SECRET'],
directory: ENV['S3_BACKUPS_BUCKET'],
provider: 'AWS',
scheme: ENV['S3_BACKUPS_SCHEME'],
host: ENV['S3_BACKUPS_HOST']
}
else
DB2Fog.config = {
:aws_access_key_id => Spree::Config[:s3_access_key],
:aws_secret_access_key => Spree::Config[:s3_secret],
:directory => ENV['S3_BACKUPS_BUCKET'],
:provider => 'AWS'
}
}
region = ENV['S3_BACKUPS_REGION'] || ENV['S3_REGION']
region = ENV['S3_BACKUPS_REGION'] || ENV['S3_REGION']
# If no region is defined we leave this config key undefined (instead of nil),
# so that db2fog correctly applies it's default
DB2Fog.config[:region] = region if region
# If no region is defined we leave this config key undefined (instead of nil),
# so that db2fog correctly applies it's default
DB2Fog.config[:region] = region if region
end
end

View File

@@ -246,6 +246,9 @@ en_CA:
disconnect_failure: "Failed to disconnecct Stripe."
success_code:
disconnected: "Stripe account disconnected."
taler:
order_status:
claimed: "The payment request expired. Please try again."
activemodel:
errors:
messages:
@@ -3358,6 +3361,8 @@ en_CA:
payment_processing_failed: "Payment could not be processed, please check the details you entered."
payment_method_not_supported: "That payment method is unsupported. Please choose another one."
payment_updated: "Payment Updated"
payment_method_taler:
order_summary: "Open Food Network order"
cannot_perform_operation: "Could not update the payment"
action_required: "Action required"
tag_rules: "Tag Rules"
@@ -4171,6 +4176,7 @@ en_CA:
alt_text: "Alternative Text"
thumbnail: "Thumbnail"
back_to_images_list: "Back to Images List"
backend_url: "Backend URL"
api_key: "API key"
email: Email
account_updated: "Account updated!"
@@ -4490,6 +4496,7 @@ en_CA:
check: "Cash/EFT/Bank Transfer etc. (payments for which automatic validation is not required)"
paypalexpress: "PayPal Express"
stripesca: "Stripe SCA"
taler: "Taler"
payments:
source_forms:
stripe:

View File

@@ -115,6 +115,67 @@ en_GB:
blank: "can't be blank"
too_short: "is too short (minimum is %{count} characters)"
errors:
messages:
content_type_invalid:
one: "has an invalid content type (authorised content type is %{authorized_human_content_types})"
other: "has an invalid content type (authorised content types are\n%{authorized_human_content_types})"
content_type_spoofed:
one: "has a content type that is not equivalent to the one that is detected through its content (authorised content type is %{authorized_human_content_types})"
other: "has a content type that is not equivalent to the one that is detected through its content (authorised content types are %{authorized_human_content_types})"
file_size_not_less_than: "file size must be less than %{max} (current size is %{file_size})"
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max} (current size is %{file_size})"
file_size_not_greater_than: "file size must be greater than %{min} (current size is %{file_size})"
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min} (current size is %{file_size})"
file_size_not_between: "file size must be between %{min} and %{max} (current size is %{file_size})"
file_size_not_equal_to: "file size must be equal to %{exact} (current size is %{file_size})"
total_file_size_not_less_than: "total file size must be less than %{max} (current size is %{total_file_size})"
total_file_size_not_less_than_or_equal_to: "total file size must be less than or equal to %{max} (current size is %{total_file_size})"
total_file_size_not_greater_than: "total file size must be greater than %{min} (current size is %{total_file_size})"
total_file_size_not_greater_than_or_equal_to: "total file size must be greater than or equal to %{min} (current size is %{total_file_size})"
total_file_size_not_between: "total file size must be between %{min} and %{max} (current size is %{total_file_size})"
total_file_size_not_equal_to: "total file size must be equal to %{exact} (current size is %{total_file_size})"
duration_not_less_than: "duration must be less than %{max} (current duration is %{duration})"
duration_not_less_than_or_equal_to: "duration must be less than or equal to %{max} (current duration is %{duration})"
duration_not_greater_than: "duration must be greater than %{min} (current duration is %{duration})"
duration_not_greater_than_or_equal_to: "duration must be greater than or equal to %{min} (current duration is %{duration})"
duration_not_between: "duration must be between %{min} and %{max} (current duration is %{duration})"
duration_not_equal_to: "duration must be equal to %{exact} (current duration is %{duration})"
limit_out_of_range:
zero: "no files attached (must have between %{min} and %{max}files)"
one: "only 1 file attached (must have between %{min} and %{max}files)"
other: "total number of files must be between %{min} and %{max} files (there are %{count}files attached)"
limit_min_not_reached:
zero: "no files attached (must have at least %{min} files)"
one: "only 1 file attached (must have at least %{min} files)"
other: "%{count} files attached (must have at least %{min} files)"
limit_max_exceeded:
zero: "no files attached (maximum is %{max} files)"
one: "too many files attached (maximum is %{max} files, got %{count})"
other: "too many files attached (maximum is %{max} files, got %{count})"
attachment_missing: "is missing its attachment"
media_metadata_missing: "is not a valid media file"
dimension_min_not_included_in: "must be greater than or equal to %{width} x %{height} pixels"
dimension_max_not_included_in: "must be less than or equal to %{width} x %{height} pixels"
dimension_width_not_included_in: "width is not included between %{min} and %{max} pixels"
dimension_height_not_included_in: "height is not included between %{min} and %{max} pixels"
dimension_width_not_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixels"
dimension_height_not_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixels"
dimension_width_not_less_than_or_equal_to: "width must be less than or equal to %{length} pixels"
dimension_height_not_less_than_or_equal_to: "height must be less than or equal to %{length} pixels"
dimension_width_not_equal_to: "width must be equal to %{length} pixels"
dimension_height_not_equal_to: "height must be equal to %{length} pixels"
aspect_ratio_not_square: "must be square (current file is %{width}x%{height}px)"
aspect_ratio_not_portrait: "must be portrait (current file is %{width}x%{height}px)"
aspect_ratio_not_landscape: "must be landscape (current file is %{width}x%{height}px)"
aspect_ratio_not_x_y: "must be %{authorized_aspect_ratios} (current file is %{width}x%{height}px)"
aspect_ratio_invalid: "has an invalid aspect ratio (valid aspect ratios are %{authorized_aspect_ratios})"
file_not_processable: "is not identified as a valid media file"
pages_not_less_than: "page count must be less than %{max} (current page count is %{pages})"
pages_not_less_than_or_equal_to: "page count must be less than or equal to %{max} (current page count is %{pages})"
pages_not_greater_than: "page count must be greater than %{min} (current page count is %{pages})"
pages_not_greater_than_or_equal_to: "page count must be greater than or equal to %{min} (current page count is %{pages})"
pages_not_between: "page count must be between %{min} and %{max} (current page count is %{pages})"
pages_not_equal_to: "page count must be equal to %{exact} (current page count is %{pages})"
not_found:
title: "The page you were looking for doesn't exist (404)"
message_html: "<b>Please try again</b> <p>This might be a temporary problem. Please click the back button to return to the previous screen or go back to <a href='/'>Home</a> and try again.</p> <b>Contact support</b> <p>If the problem persists or is urgent, please tell us about it. Find our contact details from the global <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>Open Food Network Local page</a>.</p> <p>It really helps us if you can give as much detail as possible about what the missing page is about.</p>"
@@ -185,6 +246,9 @@ en_GB:
disconnect_failure: "Failed to disconnect Stripe."
success_code:
disconnected: "Stripe account disconnected."
taler:
order_status:
claimed: "The payment request expired. Please try again."
activemodel:
errors:
messages:
@@ -520,6 +584,7 @@ en_GB:
errors:
vine_api: "There was an error communicating with the API, please try again later."
invalid_voucher: "The voucher is not valid"
expired: "The voucher has expired"
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
vine_voucher_redeemer_service:
errors:
@@ -934,6 +999,10 @@ en_GB:
clone:
success: Successfully cloned the product
error: Unable to clone the product
tag_rules:
rules_per_tag:
one: "%{tag} has 1 rule"
other: "%{tag} has %{count} rules"
product_import:
title: Product Import
file_not_found: File not found or could not be opened
@@ -2374,9 +2443,9 @@ en_GB:
order_includes_tax: (includes tax)
order_payment_paypal_successful: Your payment via PayPal has been processed successfully.
order_hub_info: Hub info
order_back_to_store: Back To Store
order_back_to_cart: Back To Cart
order_back_to_website: Back To Website
order_back_to_store: Back to shop
order_back_to_cart: Back to cart
order_back_to_website: Back to website
checkout_details_title: Checkout Details
checkout_payment_title: Checkout Payment
checkout_summary_title: Checkout Summary
@@ -3292,11 +3361,13 @@ en_GB:
payment_processing_failed: "Payment could not be processed, please check the details you entered"
payment_method_not_supported: "That payment method is unsupported. Please choose another one."
payment_updated: "Payment Updated"
payment_method_taler:
order_summary: "Open Food Network order"
cannot_perform_operation: "Could not update the payment"
action_required: "Action required"
tag_rules: "Tag Rules"
enterprise_fee_whole_order: Whole order
enterprise_fee_by_name: "%{name} fee by %{role}%{enterprise_name}"
enterprise_fee_by_name: "%{name} fee by %{role} %{enterprise_name}"
validation_msg_relationship_already_established: "^That relationship is already established."
validation_msg_at_least_one_hub: "^At least one hub must be selected"
validation_msg_tax_category_cant_be_blank: "^Tax Category can't be blank"
@@ -3337,6 +3408,7 @@ en_GB:
order_cycles_no_permission_to_coordinate_error: "None of your enterprises have permission to coordinate an order cycle"
order_cycles_no_permission_to_create_error: "You don't have permission to create an order cycle coordinated by that enterprise"
order_cycle_closed: "The order cycle you've selected has just closed."
order_cycle_closed_next_steps: "The order cycle you've selected has just closed. Please contact us to complete your order #\n%{order_number}!"
back_to_orders_list: "Back To Orders List"
no_orders_found: "No Orders Found"
order_information: "Order Information"
@@ -3911,6 +3983,8 @@ en_GB:
destroy:
success: Webhook endpoint successfully deleted
error: Webhook endpoint failed to delete
test:
success: Some test data will be sent to the webhook url
spree:
order_updated: "Order Updated"
cannot_perform_operation: "Can not perform this operation"
@@ -4017,6 +4091,7 @@ en_GB:
logourl: "Logo url"
are_you_sure_delete: "Are you sure you want to delete this record?"
confirm_delete: "Confirm Deletion"
tag_rule: "Tag Rule"
voucher: "Voucher"
configurations: "Configurations"
general_settings: "General Settings"
@@ -4108,6 +4183,7 @@ en_GB:
alt_text: "Alternative Text"
thumbnail: "Thumbnail"
back_to_images_list: "Back To Images List"
backend_url: "Backend URL"
api_key: "API key"
email: Email
account_updated: "Account updated!"
@@ -4427,6 +4503,7 @@ en_GB:
check: "Cash/EFT/etc. (payments for which automatic validation is not required)"
paypalexpress: "PayPal Express"
stripesca: "Stripe SCA "
taler: "Taler"
payments:
source_forms:
stripe:
@@ -4883,6 +4960,7 @@ en_GB:
order_cycle_tagged_bottom: "are:"
inventory_tagged_top: "Inventory variants tagged"
inventory_tagged_bottom: "are:"
variant_tagged_top: "Variants tagged"
variant_tagged_bottom: "are:"
visible: VISIBLE
not_visible: NOT VISIBLE
@@ -4898,6 +4976,8 @@ en_GB:
create_placeholder: Enter the URL of the remote webhook endpoint
event_types:
order_cycle_opened: Order Cycle Opened
payment_status_changed: Post webhook on Payment status change
test_endpoint: Test webhook endpoint
invisible_captcha:
sentence_for_humans: "Please leave empty"
timestamp_error_message: "Please try again after 5 seconds."

View File

@@ -18,7 +18,7 @@ fi:
phone: "Puhelinnumero"
firstname: "Etunimi"
lastname: "Sukunimi"
zipcode: "Toimituspostinumero"
zipcode: "Toimitusosoitteen postinumero"
spree/order/bill_address:
address1: "Laskutusosoite (Katu ja talonumero)"
zipcode: "Laskutuspostinumero"
@@ -115,6 +115,67 @@ fi:
blank: "ei voi olla tyhjä"
too_short: "on liian lyhyt (vähintään %{count} merkkiä)"
errors:
messages:
content_type_invalid:
one: "sisältää virheellisen sisältötyypin (valtuutettu sisältötyyppi on %{authorized_human_content_types} )"
other: "sisältää virheellisen sisältötyypin (valtuutetut sisältötyypit ovat %{authorized_human_content_types} )"
content_type_spoofed:
one: "sisältötyyppi ei vastaa sisällöstä havaittua tyyppiä (valtuutettu sisältötyyppi on %{authorized_human_content_types} )"
other: "sisältötyyppi ei vastaa sisällöstä havaittua tyyppiä (valtuutetut sisältötyypit ovat %{authorized_human_content_types} )"
file_size_not_less_than: "tiedoston koko on oltava pienempi kuin %{max} (nykyinen koko on %{file_size} )"
file_size_not_less_than_or_equal_to: "tiedoston koko on oltava pienempi tai yhtä suuri kuin %{max} (nykyinen koko on %{file_size} )"
file_size_not_greater_than: "tiedoston koko on oltava suurempi kuin %{min} (nykyinen koko on %{file_size} )"
file_size_not_greater_than_or_equal_to: "tiedoston koko on oltava suurempi tai yhtä suuri kuin %{min} (nykyinen koko on %{file_size} )"
file_size_not_between: "koko on oltava %{min} ja %{max} välillä (nykyinen koko on %{file_size} )"
file_size_not_equal_to: "tiedoston koko on oltava yhtä suuri kuin %{exact} (nykyinen koko on %{file_size} )"
total_file_size_not_less_than: "yhteensä tiedoston koko on oltava pienempi kuin %{max} (nykyinen koko on %{total_file_size} )"
total_file_size_not_less_than_or_equal_to: "yhteensä tiedoston koko on oltava pienempi tai yhtä suuri kuin %{max} (nykyinen koko on %{total_file_size} )"
total_file_size_not_greater_than: "yhteensä tiedoston koko on oltava suurempi kuin %{min} (nykyinen koko on %{total_file_size} )"
total_file_size_not_greater_than_or_equal_to: "yhteensä tiedoston koko on oltava suurempi tai yhtä suuri kuin %{min} (nykyinen koko on %{total_file_size} )"
total_file_size_not_between: "yhteensä tiedoston koko on oltava %{min} ja %{max} välillä (nykyinen koko on %{total_file_size} )"
total_file_size_not_equal_to: "yhteensä tiedoston koko on oltava yhtä suuri kuin %{exact} (nykyinen koko on %{total_file_size} )"
duration_not_less_than: "keston on oltava pienempi kuin %{max} (nykyinen kesto on %{duration} )"
duration_not_less_than_or_equal_to: "keston on oltava pienempi tai yhtä suuri kuin %{max} (nykyinen kesto on %{duration} )"
duration_not_greater_than: "keston on oltava suurempi kuin %{min} (nykyinen kesto on %{duration} )"
duration_not_greater_than_or_equal_to: "keston on oltava suurempi tai yhtä suuri kuin %{min} (nykyinen kesto on %{duration} )"
duration_not_between: "keston on oltava välillä %{min} ja %{max} (nykyinen kesto on %{duration} )"
duration_not_equal_to: "keston on oltava yhtä suuri kuin %{exact} (nykyinen kesto on %{duration} )"
limit_out_of_range:
zero: "ei liitettyjä tiedostoja (tiedostojen on oltava %{min} ja %{max} välillä)"
one: "vain yksi liitetty tiedosto (tiedostojen on oltava %{min} ja %{max} välillä)"
other: "Tiedostojen yhteensä on oltava %{min} ja %{max} välillä (liitteenä on %{count} tiedostot)"
limit_min_not_reached:
zero: "ei liitettyjä tiedostoja (tiedostoissa on oltava vähintään %{min} )"
one: "vain yksi tiedosto liitettynä (tiedostojen on oltava vähintään %{min} )"
other: "%{count} tiedostot liitteenä (vähintään %{min} tiedostoja on oltava)"
limit_max_exceeded:
zero: "ei liitettyjä tiedostoja (enintään %{max} tiedostoja)"
one: "liian monta tiedostoa liitettynä (suurin sallittu määrä on %{max} , sain tulokseksi %{count} )"
other: "liian monta tiedostoa liitettynä (suurin sallittu määrä on %{max} , sain tulokseksi %{count} )"
attachment_missing: "puuttuu sen liite"
media_metadata_missing: "ei ole kelvollinen mediatiedosto"
dimension_min_not_included_in: "on oltava suurempi tai yhtä suuri kuin %{width} x %{height} pikseliä"
dimension_max_not_included_in: "on oltava pienempi tai yhtä suuri kuin %{width} x %{height} pikseliä"
dimension_width_not_included_in: "Leveys ei sisälly %{min} ja %{max} pikselien väliin"
dimension_height_not_included_in: "Korkeutta ei ole %{min} ja %{max} pikselien välillä."
dimension_width_not_greater_than_or_equal_to: "leveyden on oltava suurempi tai yhtä suuri kuin %{length} pikseliä"
dimension_height_not_greater_than_or_equal_to: "korkeuden on oltava suurempi tai yhtä suuri kuin %{length} pikseliä"
dimension_width_not_less_than_or_equal_to: "leveyden on oltava pienempi tai yhtä suuri kuin %{length} pikseliä"
dimension_height_not_less_than_or_equal_to: "korkeuden on oltava pienempi tai yhtä suuri kuin %{length} pikseliä"
dimension_width_not_equal_to: "leveyden on oltava yhtä suuri kuin %{length} pikseliä"
dimension_height_not_equal_to: "korkeuden on oltava yhtä suuri kuin %{length} pikselit"
aspect_ratio_not_square: "on oltava neliön muotoinen (nykyinen tiedosto on %{width} x %{height} px)"
aspect_ratio_not_portrait: "tiedoston on oltava pystysuuntainen (nykyinen tiedosto on %{width} x %{height} px)"
aspect_ratio_not_landscape: "tiedoston on oltava vaakasuuntainen (nykyinen tiedosto on %{width} x %{height} px)"
aspect_ratio_not_x_y: "tiedoston on oltava %{authorized_aspect_ratios} (nykyinen tiedosto on %{width} x %{height} px)"
aspect_ratio_invalid: "kuvasuhde on virheellinen (kelvolliset kuvasuhteet ovat %{authorized_aspect_ratios} )"
file_not_processable: "ei ole tunnistettu kelvolliseksi mediatiedostoksi"
pages_not_less_than: "sivumäärän on oltava pienempi kuin %{max} (nykyinen sivumäärä on %{pages} )"
pages_not_less_than_or_equal_to: "sivumäärän on oltava pienempi tai yhtä suuri kuin %{max} (nykyinen sivumäärä on %{pages} )"
pages_not_greater_than: "sivumäärän on oltava suurempi kuin %{min} (nykyinen sivumäärä on %{pages} )"
pages_not_greater_than_or_equal_to: "sivumäärän on oltava suurempi tai yhtä suuri kuin %{min} (nykyinen sivumäärä on %{pages} )"
pages_not_between: "sivumäärän on oltava välillä %{min} ja %{max} (nykyinen sivumäärä on %{pages} )"
pages_not_equal_to: "sivumäärän on oltava yhtä suuri kuin %{exact} (nykyinen sivumäärä on %{pages} )"
not_found:
title: "Etsimääsi sivua ei löytynyt (404)"
message_html: "<b>Yritä uudelleen</b> <p>Tämä voi olla tilapäinen ongelma. Palaa edelliselle sivulle tai <a href='/'>etusivulle</a> ja yritä uudelleen.</p> <b>Ota yhteyttä tukeen</b> <p>Jos ongelma jatkuu tai on kiireellinen, kerro siitä meille. Löydä yhteystiedot globaalilta <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>Open Food Network Local -sivulta</a>.</p> <p>Autat meitä paljon, jos annat mahdollisimman paljon yksityiskohtia puuttuvasta sivusta.</p>"
@@ -185,6 +246,9 @@ fi:
disconnect_failure: "Stripe-tilin irrottaminen epäonnistui."
success_code:
disconnected: "Stripe-tili irrotettu."
taler:
order_status:
claimed: "Maksupyyntö vanheni. Yritä uudelleen."
activemodel:
errors:
messages:
@@ -518,6 +582,7 @@ fi:
errors:
vine_api: "API-yhteydessä tapahtui virhe, yritä myöhemmin uudelleen."
invalid_voucher: "Alennuskuponki ei ole kelvollinen"
expired: "Kuponki on vanhentunut"
not_found_voucher: "Anteeksi, emme löytäneet tätä alennuskuponkia, tarkista koodi."
vine_voucher_redeemer_service:
errors:
@@ -932,6 +997,10 @@ fi:
clone:
success: Tuote kloonattiin onnistuneesti
error: Tuotteen kloonaaminen epäonnistui
tag_rules:
rules_per_tag:
one: "%{tag} llä on 1 sääntö"
other: "%{tag} llä on %{count} säännöt."
product_import:
title: Tuontituotteet
file_not_found: Tiedostoa ei löytynyt tai sitä ei voitu avata
@@ -1326,6 +1395,9 @@ fi:
add_new_button: '+ Lisää uusi oletussääntö'
no_tags_yet: Tähän yritykseen ei vielä liity tägejä
add_new_tag: '+ Lisää uusi tägi'
show_hide_variants: 'Näytä tai Piilota variantit myymälästäni'
show_hide_shipping: 'Näytä tai piilota toimitustavat kassalla'
show_hide_payment: 'Näytä tai Piilota maksutavat kassalla'
show_hide_order_cycles: 'Näytä tai piilota tilausjaksot verkkokaupassani'
users:
legend: "Käyttäjät"
@@ -1479,6 +1551,10 @@ fi:
invite_manager:
user_already_exists: "Käyttäjä on jo olemassa"
error: "Jotain meni pieleen"
tag_rules:
not_supported_type: tägi -sääntötyyppiä ei tueta
confirm_delete: Haluatko varmasti poistaa tämän säännön?
destroy_error: tägi -säännön poistamisessa oli ongelma.
order_cycles:
loading_flash:
loading_order_cycles: LADATAAN TILAUSJAKSOJA
@@ -1702,6 +1778,8 @@ fi:
not_visible: "%{enterprise} ei ole näkyvissä, joten sitä ei löydy kartalta tai hauista"
reports:
none: ei yhtään
metadata:
report_title: Ilmianna otsikko
deprecated: "Tämä raportti on vanhentunut ja se poistetaan tulevasta julkaisusta."
hidden_field: "< Piilotettu >"
unitsize: YKSIKKÖKOKO
@@ -1804,6 +1882,7 @@ fi:
display: Näyttö
summary_row: Yhteenvetorivi
header_row: Otsikkorivi
metadata_rows: Metadata-rivit
raw_data: Raakadata
formatted_data: Muotoiltu data
packing:
@@ -2379,8 +2458,15 @@ fi:
email_confirmed: "Kiitos sähköpostiosoitteesi vahvistamisesta."
email_confirmation_activate_account: "Ennen kuin voimme aktivoida uuden tilisi, meidän on vahvistettava sähköpostiosoitteesi."
email_confirmation_greeting: "Hei, %{contact} !"
email_confirmation_profile_created: >
Profiili käyttäjälle %{name} on luotu onnistuneesti! Profiilisi aktivoimiseksi
meidän on vahvistettava tämä sähköpostiosoite.
email_confirmation_click_link: "Vahvista sähköpostiosoitteesi ja jatka profiilisi luomista napsauttamalla alla olevaa linkkiä."
email_confirmation_link_label: "Vahvista tämä sähköpostiosoite »"
email_confirmation_help_html: >
Kun olet vahvistanut sähköpostiosoitteesi, voit käyttää ylläpitäjän tiliäsi
tässä yrityksessä. Katso %{link} saadaksesi about %{sitename} :n ominaisuuksista
ja aloittaaksesi profiilisi tai verkkokauppasi käytön.
email_confirmation_notice_unexpected: "Sait tämän viestin, koska rekisteröidyit palvelussa %{sitename} tai joku luultavasti tuntemasi henkilö kutsui sinut rekisteröitymään. Jos et ymmärrä, miksi saat tämän sähköpostin, kirjoita osoitteeseen %{contact} ."
email_social: "Ota yhteyttä:"
email_contact: "Lähetä meille sähköpostia:"
@@ -3276,6 +3362,8 @@ fi:
payment_processing_failed: "Payment could not be processed, please check the details you entered"
payment_method_not_supported: "Maksutapaa ei tueta. Valitse toinen."
payment_updated: "Maksu päivitetty"
payment_method_taler:
order_summary: "Open Food Network tilaus"
cannot_perform_operation: "Maksua ei voitu päivittää"
action_required: "Toimenpide vaaditaan"
tag_rules: "Tägisäännöt"
@@ -3321,6 +3409,7 @@ fi:
order_cycles_no_permission_to_coordinate_error: "Yhdelläkään yritykselläsi ei ole lupaa koordinoida tilauskiertoa."
order_cycles_no_permission_to_create_error: "Sinulla ei ole oikeutta luoda kyseisen yrityksen koordinoimaa tilausjaksoa."
order_cycle_closed: "Valitsemasi tilausjakso on juuri päättynyt. Yritä uudelleen!"
order_cycle_closed_next_steps: "Valitsemasi tilausjakso on juuri sulkeutunut. Ota meihin yhteyttä täydentääksesi tilaustasi# %{order_number} !"
back_to_orders_list: "Takaisin tilauslistaan"
no_orders_found: "Ei tilaukset löytynyt"
order_information: "Tilaustiedot"
@@ -3888,6 +3977,8 @@ fi:
destroy:
success: Webhook-päätepiste poistettu onnistuneesti
error: Webhook-päätepisteen poistaminen epäonnistui
test:
success: Joitakin testitietoja lähetetään webhookin URL-osoitteeseen
spree:
order_updated: "Tilaus päivitetty"
cannot_perform_operation: "Tätä toimintoa ei voida suorittaa"
@@ -3994,6 +4085,7 @@ fi:
logourl: "Logourl"
are_you_sure_delete: "Haluatko varmasti poistaa tämän tietueen?"
confirm_delete: "Vahvista poisto"
tag_rule: "tägi Rule"
voucher: "Alennuskuponki"
configurations: "Asetukset"
general_settings: "Yleiset asetukset"
@@ -4085,6 +4177,8 @@ fi:
alt_text: "Vaihtoehtoinen teksti"
thumbnail: "Pienoiskuva"
back_to_images_list: "Takaisin kuvaluetteloon"
backend_url: "Taustapalvelun URL-osoite"
api_key: "API-avain"
email: Sähköposti
account_updated: "Tili päivitetty!"
email_updated: "Tili päivitetään, kun uusi sähköpostiosoite on vahvistettu."
@@ -4403,6 +4497,7 @@ fi:
check: "Käteinen/sähköinen maksu/jne. (maksut, joille ei vaadita automaattista vahvistusta)"
paypalexpress: "PayPal Express"
stripesca: "Stripe SCA"
taler: "Taleri"
payments:
source_forms:
stripe:
@@ -4852,13 +4947,31 @@ fi:
tag_rule_form:
tag_rules:
shipping_method_tagged_top: "Toimitustavat merkitty tägillä"
shipping_method_tagged_bottom: "ovat:"
payment_method_tagged_top: "Maksutavat merkitty tägillä"
payment_method_tagged_bottom: "ovat:"
order_cycle_tagged_top: "Tilausjaksot merkitty tägillä"
order_cycle_tagged_bottom: "ovat:"
inventory_tagged_top: "Tägätyt varastomuunnelmat"
inventory_tagged_bottom: "ovat:"
variant_tagged_top: "Variantit -tunnisteella"
variant_tagged_bottom: "ovat:"
visible: NÄKYVÄ
not_visible: EI NÄKYVÄ
tag_rule_group_form:
for_customers_tagged: 'Asiakkaille, jotka on tägätty:'
add_new_rule: '+ Lisää uusi sääntö'
no_rules_yet: Tähän tägiin ei vielä sovelleta sääntöjä
add_tag_rule_modal:
select_rule_type: "Valitse säännön tyyppi:"
add_rule: "lisää sääntö"
webhook_endpoint_form:
url:
create_placeholder: Anna etäwebhook-päätepisteen URL-osoite
event_types:
order_cycle_opened: Tilausjakso avattu
payment_status_changed: Lähetä webhook maksun tilan muutoksesta
test_endpoint: Testaa webhookin päätepiste
invisible_captcha:
sentence_for_humans: "Jätä tyhjäksi"
timestamp_error_message: "Yritä uudelleen 5 sekunnin kuluttua."

View File

@@ -2945,7 +2945,7 @@ fr:
shipping_method_destroy_error: "Cette méthode de livraison ne peut pas être supprimée car elle est référencée dans une commande : %{number}."
fees: "Frais"
fee_name: "Nom de la marge/commission"
fee_owner: "Propriétaire des droits"
fee_owner: "Propriétaire de la marge/commission"
item_cost: "Coût du produit"
bulk: "Vrac"
shop_variant_quantity_min: "min"

View File

@@ -115,6 +115,69 @@ fr_CA:
blank: "Champ obligatoire"
too_short: "est trop court (minimum %{count} caractère)"
errors:
messages:
content_type_invalid:
one: "a un contenu invalide (le type de contenu autorisé est %{authorized_human_content_types})"
many: "a un contenu invalide (les types de contenus autorisés sont %{authorized_human_content_types})"
other: "a un contenu invalide (les types de contenus autorisés sont %{authorized_human_content_types})"
content_type_spoofed:
one: "a un type de contenu qui n'est pas équivalent avec celui détecté (le type de contenu autorisé est %{authorized_human_content_types})"
many: "a un type de contenu qui n'est pas équivalent avec celui détecté (les types de contenus autorisés sont %{authorized_human_content_types})"
other: "a un type de contenu qui n'est pas équivalent avec celui détecté (les types de contenus autorisés sont %{authorized_human_content_types})"
file_size_not_less_than: "la taille du fichier doit être inférieure à %{max} (la taille actuelle est %{file_size})"
file_size_not_less_than_or_equal_to: "la taille du fichier doit être inférieure ou égale à %{max} (la taille actuelle est %{file_size})"
file_size_not_greater_than: "la taille du fichier doit être supérieure à %{min} (la taille actuelle est%{file_size})"
file_size_not_greater_than_or_equal_to: "la taille du fichier doit être supérieure ou égale à %{min} (la taille actuelle est %{file_size})"
file_size_not_between: "la taille du fichier doit être comprise entre%{min} et %{max} (la taille actuelle est %{file_size})"
file_size_not_equal_to: "la taille du fichier doit être égale à %{exact} (la taille actuelle est %{file_size})"
total_file_size_not_less_than: "La taille totale du fichier doit être inférieure à %{max} (la taille actuelle est%{total_file_size})"
total_file_size_not_less_than_or_equal_to: "La taille totale du fichier doit être inférieure ou égale à %{max} (la taille actuelle est %{total_file_size})"
total_file_size_not_greater_than: "La taille totale du fichier doit être supérieure à %{min} (la taille actuelle est%{total_file_size})"
total_file_size_not_greater_than_or_equal_to: "La taille totale du fichier doit être supérieure ou égale à %{min} (la taille actuelle est %{total_file_size})"
total_file_size_not_between: "La taille totale du fichier doit être comprise entre %{min} et %{max} (la taille actuelle est %{total_file_size})"
total_file_size_not_equal_to: "La taille totale du fichier doit être égale à %{exact} (la taille actuelle est%{total_file_size})"
duration_not_less_than: "La durée doit être inférieure à %{max} (la durée actuelle est %{duration})"
duration_not_less_than_or_equal_to: "La durée doit être inférieure ou égale à %{max} (la durée actuelle est%{duration})"
duration_not_greater_than: "La durée doit être supérieure à %{min} (la durée actuelle est %{duration})"
duration_not_greater_than_or_equal_to: "La durée doit être supérieure ou égale à %{min} (la durée actuelle est%{duration})"
duration_not_between: "La durée doit être comprise entre %{min} et %{max} (la durée actuelle est %{duration})"
duration_not_equal_to: "La durée doit être égale à %{exact} (la durée actuelle est %{duration})"
limit_out_of_range:
zero: "Aucun fichier joint (doit contenir entre %{min} et %{max} fichiers)"
one: "Seulement 1 fichier joint (doit contenir entre%{min} et %{max} fichiers)"
other: "Le nombre total de fichiers doit être compris entre %{min} et %{max} fichiers (il y a %{count} fichiers joints)"
limit_min_not_reached:
zero: "Aucun fichier joint (doit contenir au moins %{min} fichiers)"
one: "Seulement 1 fichier joint (doit contenir au moins %{min} fichiers)"
other: "%{count} Aucun fichier joint (doit contenir au moins %{min} fichiers)"
limit_max_exceeded:
zero: "Aucun fichier joint (au maximum %{max} fichiers)"
one: "Trop de fichiers joints (le maximum est %{max} fichiers, il y en a %{count})"
other: "Trop de fichiers joints (le maximum est %{max} fichiers, il y en a%{count})"
attachment_missing: "une pièce jointe est manquante"
media_metadata_missing: "n'est pas un fichier multimédia valide"
dimension_min_not_included_in: "doit être plus grand ou égal à %{width} x %{height} pixels"
dimension_max_not_included_in: "doit être plus petit ou égal à %{width} x %{height} pixels"
dimension_width_not_included_in: "la largeur n'est pas comprise entre %{min} et %{max} pixels"
dimension_height_not_included_in: "la hauteur n'est pas comprise entre %{min} et %{max} pixels"
dimension_width_not_greater_than_or_equal_to: "la largeur doit être supérieure ou égale à %{length} pixels"
dimension_height_not_greater_than_or_equal_to: "la hauteur doit être supérieure ou égale à %{length} pixels"
dimension_width_not_less_than_or_equal_to: "la largeur doit être inférieure ou égale à %{length} pixels"
dimension_height_not_less_than_or_equal_to: "la hauteur doit être inférieure ou égale à %{length} pixels"
dimension_width_not_equal_to: "la largeur doit être égale à %{length} pixels"
dimension_height_not_equal_to: "La hauteur doit être égale à %{length} pixels"
aspect_ratio_not_square: "doit être carré (le fichier actuel est %{width}x%{height}px)"
aspect_ratio_not_portrait: "doit être au format portrait (le fichier actuel mesure %{width}x%{height}px)"
aspect_ratio_not_landscape: "doit être au format paysage (le fichier actuel mesure %{width}x%{height}px)"
aspect_ratio_not_x_y: "doit être %{authorized_aspect_ratios} (le fichier actuel est %{width}x%{height}px)"
aspect_ratio_invalid: "possède un ratio d'image invalide (les ratios d'image valides sont %{authorized_aspect_ratios})"
file_not_processable: "n'est pas identifié comme un fichier multimédia valide"
pages_not_less_than: "La durée doit être inférieure à %{max} (la durée actuelle est %{pages})"
pages_not_less_than_or_equal_to: "La durée doit être inférieure ou égale à %{max} (la durée actuelle est %{pages})"
pages_not_greater_than: "La durée doit être supérieure à %{min}(la durée actuelle est %{pages})"
pages_not_greater_than_or_equal_to: "La durée doit être supérieure ou égale à %{min} (la durée actuelle %{pages})"
pages_not_between: "La durée doit être comprise entre %{min} et %{max} (la durée actuelle est %{pages})"
pages_not_equal_to: "La durée doit être égale à %{exact} (la durée actuelle est %{pages})"
not_found:
title: "La page que vous recherchez n'existe pas (erreur 404)"
message_html: "<b>Veuillez essayer à nouveau </b> <p> Il s'agit peut-être d'un problème temporaire. Veuillez cliquer sur le bouton retour de votre navigateur ou retourner à l'<a href='/'> Accueil </a> et recommencez. <b> Contacter notre support  </b> <p> Si le problème persiste ou si c'est urgent, veuillez nous contacter. </p>"
@@ -185,6 +248,9 @@ fr_CA:
disconnect_failure: "Déconnecter Stripe a échoué."
success_code:
disconnected: "Le compte Stripe est déconnecté."
taler:
order_status:
claimed: "La demande de paiement a expiré. Merci d'essayer à nouveau."
activemodel:
errors:
messages:
@@ -520,6 +586,7 @@ fr_CA:
errors:
vine_api: "There was an error communicating with the API, please try again later."
invalid_voucher: "The voucher is not valid"
expired: "Le bon de réduction a expiré."
not_found_voucher: "Désolé, nous n'avons pas trouvé ce bon de réduction. Merci de vérifier le code qui vous a été transmis."
vine_voucher_redeemer_service:
errors:
@@ -3305,6 +3372,8 @@ fr_CA:
payment_processing_failed: "Le paiement n'a pas pu être traité, veuillez vérifier les informations saisies"
payment_method_not_supported: "Cette méthode de paiement n'est pas maintenue. Veuillez en sélectionner une autre."
payment_updated: "Paiement mis à jour"
payment_method_taler:
order_summary: "Commande Open Food Network"
cannot_perform_operation: "Le paiement n'a pas pu être mis à jour."
action_required: "Une action est requise"
tag_rules: "Règles de tag"
@@ -3965,6 +4034,8 @@ fr_CA:
destroy:
success: Le webhook a bien été supprimé.
error: Le webhook n'a pas pu être supprimé.
test:
success: Des données test vont être envoyées à l'URL du webhook
spree:
order_updated: "La commande a été mise à jour"
cannot_perform_operation: "Cette opération ne peut pas être réalisée"
@@ -4071,6 +4142,7 @@ fr_CA:
logourl: "URL du logo"
are_you_sure_delete: "Etes-vous certain de vouloir supprimer cet élément ?"
confirm_delete: "Confirmer la suppression"
tag_rule: "Règle de tag"
voucher: "Bon de réduction"
configurations: "Configurations"
general_settings: "Configurations générales"
@@ -4162,6 +4234,7 @@ fr_CA:
alt_text: "Texte alternatif"
thumbnail: "Miniature"
back_to_images_list: "Retour à la liste des images"
backend_url: "URL du backend"
api_key: "Clé API"
email: Email
account_updated: "Compte mis à jour!"
@@ -4481,6 +4554,7 @@ fr_CA:
check: "Espèces / chèques / virements / autres "
paypalexpress: "PayPal Express"
stripesca: "Stripe SCA"
taler: "Taler"
payments:
source_forms:
stripe:
@@ -4967,6 +5041,8 @@ fr_CA:
create_placeholder: Entrez l'URL du point de terminaison du webhook
event_types:
order_cycle_opened: Cycle de vente ouvert
payment_status_changed: Poster webhook sur le changement du statut de paiement
test_endpoint: Tester le point de terminaison du webhook
invisible_captcha:
sentence_for_humans: "Merci de laisser ce champ libre"
timestamp_error_message: "S'il vous plaît réessayez après 5 secondes."

View File

@@ -136,7 +136,15 @@ Openfoodnetwork::Application.routes.draw do
put :resume, on: :member, format: :json
end
get '/reports', to: 'reports#index', as: :reports
scope :reports, as: :reports do
get '/', to: 'reports#index'
get '/search_enterprise_fees', to: 'reports#search_enterprise_fees', as: :search_enterprise_fees
get '/search_enterprise_fee_owners', to: 'reports#search_enterprise_fee_owners', as: :search_enterprise_fee_owners
get '/search_distributors', to: 'reports#search_distributors', as: :search_distributors
get '/search_suppliers', to: 'reports#search_suppliers', as: :search_suppliers
get '/search_order_cycles', to: 'reports#search_order_cycles', as: :search_order_cycles
get '/search_order_customers', to: 'reports#search_order_customers', as: :search_order_customers
end
match '/reports/:report_type(/:report_subtype)', to: 'reports#show', via: :get, as: :report
match '/reports/:report_type(/:report_subtype)', to: 'reports#create', via: :post
end

View File

@@ -36,7 +36,7 @@ RSpec.describe "CatalogItems", swagger_doc: "dfc.yaml" do
get "List CatalogItems" do
produces "application/json"
security [oidc_token: []]
security [{ oidc_token: [] }]
response "404", "not found" do
context "as platform user" do

View File

@@ -35,7 +35,7 @@ RSpec.describe "ProductGroups", swagger_doc: "dfc.yaml" do
get "Show ProductGroup" do
produces "application/json"
security [oidc_token: []]
security [{ oidc_token: [] }]
response "200", "success" do
let(:id) { product.id }

View File

@@ -179,7 +179,7 @@ module.exports = {
// transform: { "\\.[jt]sx?$": "babel-jest" },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: ["/node_modules/(?!stimulus)/"],
transformIgnorePatterns: ["/node_modules/(?!(stimulus.+|tom-select)/)"],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,

View File

@@ -46,7 +46,7 @@ module Reporting
end
def order_customers
Customer.where(id: visible_order_customer_ids).select("customers.id, customers.email")
Customer.where(id: visible_order_customer_ids_query).select("customers.id, customers.email")
end
private
@@ -57,8 +57,8 @@ module Reporting
@permissions ||= OpenFoodNetwork::Permissions.new(current_user)
end
def visible_order_customer_ids
Permissions::Order.new(current_user).visible_orders.pluck(:customer_id)
def visible_order_customer_ids_query
Permissions::Order.new(current_user).visible_orders.select(:customer_id)
end
end
end

View File

@@ -15,7 +15,7 @@ module Reporting
@orders ||= search_orders
end
def list(line_item_includes = [variant: [:supplier, :product]])
def list(line_item_includes = [{ variant: [:supplier, :product] }])
line_items = order_permissions.visible_line_items.in_orders(orders.result)
.order(
"supplier.name",

View File

@@ -10,10 +10,10 @@ module Reporting
.complete.not_state(:canceled)
.order(:id))
.group_by do |order|
{
customer_id: order.customer_id || order.email,
hub_id: order.distributor_id,
}
{
customer_id: order.customer_id || order.email,
hub_id: order.distributor_id,
}
end.values
end

View File

@@ -44,11 +44,11 @@ module Reporting
.filter(&method(:filter_enterprise_fee_by_id))
.filter(&method(:filter_enterprise_fee_by_owner))
.map do |enterprise_fee_id, enterprise_fee_adjustment_ids|
{
enterprise_fee_id:,
enterprise_fee_adjustment_ids:,
order:
}
{
enterprise_fee_id:,
enterprise_fee_adjustment_ids:,
order:
}
end
end
end

View File

@@ -0,0 +1,296 @@
/**
* @jest-environment jsdom
*/
import { Application } from "stimulus";
import { fireEvent, waitFor } from "@testing-library/dom";
import tom_select_controller from "controllers/tom_select_controller";
import showHttpError from "js/services/show_http_error";
jest.mock("js/services/show_http_error", () => ({
__esModule: true,
default: jest.fn(),
}));
/* ------------------------------------------------------------------
* Helpers
* ------------------------------------------------------------------ */
const buildResults = (count, start = 1) =>
Array.from({ length: count }, (_, i) => ({
value: String(start + i),
label: `Option ${start + i}`,
}));
const setupDOM = (html) => {
document.body.innerHTML = html;
};
const getSelect = () => document.getElementById("select");
const getTomSelect = () => getSelect().tomselect;
const openDropdown = () => fireEvent.click(document.getElementById("select-ts-control"));
const mockRemoteFetch = (...responses) => {
responses.forEach((response) => {
fetch.mockResolvedValueOnce({
ok: true,
json: async () => response,
});
});
};
const mockDropdownScroll = (
dropdown,
{ scrollHeight = 1000, clientHeight = 300, scrollTop = 700 } = {},
) => {
Object.defineProperty(dropdown, "scrollHeight", {
configurable: true,
value: scrollHeight,
});
Object.defineProperty(dropdown, "clientHeight", {
configurable: true,
value: clientHeight,
});
Object.defineProperty(dropdown, "scrollTop", {
configurable: true,
writable: true,
value: scrollTop,
});
fireEvent.scroll(dropdown);
};
/* ------------------------------------------------------------------
* Expectation helpers
* ------------------------------------------------------------------ */
const expectOptionsCount = (count) => {
expect(document.querySelectorAll('.ts-dropdown-content [role="option"]').length).toBe(count);
};
const expectDropdownToContain = (text) => {
expect(document.querySelector(".ts-dropdown-content")?.textContent).toContain(text);
};
const expectDropdownWithNoResults = () => {
expect(document.querySelector(".ts-dropdown-content")?.textContent).toBe("No results found");
};
/* ------------------------------------------------------------------
* Specs
* ------------------------------------------------------------------ */
describe("TomSelectController", () => {
let application;
beforeAll(() => {
application = Application.start();
application.register("tom-select", tom_select_controller);
});
beforeEach(() => {
global.fetch = jest.fn();
global.I18n = { t: (key) => key };
});
afterEach(() => {
document.body.innerHTML = "";
jest.clearAllMocks();
});
describe("connect()", () => {
beforeEach(() => {
setupDOM(`
<select id="select" data-controller="tom-select">
<option value="">Default Option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
`);
});
it("initializes TomSelect with default options", () => {
const settings = getTomSelect().settings;
expect(settings.placeholder).toBe("Default Option");
expect(settings.maxItems).toBe(1);
expect(settings.plugins).toEqual(["dropdown_input"]);
expect(settings.allowEmptyOption).toBe(true);
});
});
describe("connect() with custom values", () => {
beforeEach(() => {
setupDOM(`
<select
id="select"
data-controller="tom-select"
data-tom-select-placeholder-value="Choose an option"
data-tom-select-options-value='{"maxItems": 3, "plugins": ["remove_button"]}'
>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
`);
});
it("applies custom placeholder and options", () => {
const settings = getTomSelect().settings;
expect(settings.placeholder).toBe("Choose an option");
expect(settings.maxItems).toBe(3);
expect(settings.plugins).toEqual(["remove_button"]);
});
});
describe("connect() with remoteUrl", () => {
beforeEach(() => {
setupDOM(`
<select
id="select"
data-controller="tom-select"
data-tom-select-options-value='{"plugins":["virtual_scroll"]}'
data-tom-select-remote-url-value="https://ofn-tests.com/api/search"
></select>
`);
});
it("configures remote loading callbacks", () => {
const settings = getTomSelect().settings;
expect(settings.valueField).toBe("value");
expect(settings.labelField).toBe("label");
expect(settings.searchField).toBe("label");
expect(settings.load).toEqual(expect.any(Function));
expect(settings.firstUrl).toEqual(expect.any(Function));
expect(settings.onFocus).toEqual(expect.any(Function));
});
it("fetches page 1 on focus", async () => {
mockRemoteFetch({
results: buildResults(1),
pagination: { more: false },
});
openDropdown();
await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1));
expect(fetch).toHaveBeenCalledWith(expect.stringContaining("q=&page=1"));
await waitFor(() => {
expectOptionsCount(1);
expectDropdownToContain("Option 1");
});
});
it("fetches remote options using search query", async () => {
const appleOption = { value: "apple", label: "Apple" };
mockRemoteFetch({
results: [...buildResults(1), appleOption],
pagination: { more: false },
});
openDropdown();
await waitFor(() => {
expectOptionsCount(2);
});
mockRemoteFetch({
results: [appleOption],
pagination: { more: false },
});
fireEvent.input(document.getElementById("select-ts-control"), {
target: { value: "apple" },
});
await waitFor(() =>
expect(fetch).toHaveBeenCalledWith(expect.stringContaining("q=apple&page=1")),
);
await waitFor(() => {
expectOptionsCount(1);
expectDropdownToContain("Apple");
});
});
it("loads next page on scroll (infinite scroll)", async () => {
mockRemoteFetch(
{
results: buildResults(30),
pagination: { more: true },
},
{
results: buildResults(1, 31),
pagination: { more: false },
},
);
openDropdown();
await waitFor(() => {
expectOptionsCount(30);
});
const dropdown = document.querySelector(".ts-dropdown-content");
mockDropdownScroll(dropdown);
await waitFor(() => {
expectOptionsCount(31);
});
expect(fetch).toHaveBeenCalledTimes(2);
});
it("handles fetch errors gracefully", async () => {
fetch.mockRejectedValueOnce(new Error("Fetch error"));
openDropdown();
await waitFor(() => {
expectDropdownWithNoResults();
});
expect(showHttpError).not.toHaveBeenCalled();
});
it("displays HTTP error on failure", async () => {
fetch.mockResolvedValueOnce({
ok: false,
status: 500,
json: async () => ({}),
});
openDropdown();
await waitFor(() => {
expect(showHttpError).toHaveBeenCalledWith(500);
});
expectDropdownWithNoResults();
});
it("controls loading behavior based on user interaction", () => {
const settings = getTomSelect().settings;
// Initial state: openedByClick is false, query is empty
expect(settings.shouldLoad("")).toBe(false);
// Simulating opening the dropdown
settings.onDropdownOpen();
expect(settings.shouldLoad("")).toBe(true);
// Simulating typing
settings.onType();
expect(settings.shouldLoad("")).toBe(false);
// Query present
expect(settings.shouldLoad("a")).toBe(true);
});
});
});

View File

@@ -109,7 +109,7 @@ RSpec.describe Spree::Gateway::StripeSCA, :vcr, :stripe_version do
end
it "refunds the payment" do
response = subject.void(payment_intent.id, nil, {})
response = subject.void(payment_intent.id, {})
expect(response.success?).to eq true
end
@@ -131,7 +131,7 @@ RSpec.describe Spree::Gateway::StripeSCA, :vcr, :stripe_version do
end
it "void the payment" do
response = subject.void(payment_intent.id, nil, {})
response = subject.void(payment_intent.id, {})
expect(response.success?).to eq true
end
@@ -162,7 +162,7 @@ RSpec.describe Spree::Gateway::StripeSCA, :vcr, :stripe_version do
stripe_account: stripe_test_account
)
response = subject.credit(1000, nil, payment_intent.id, {})
response = subject.credit(1000, payment_intent.id, {})
expect(response.success?).to eq true
end

View File

@@ -14,8 +14,14 @@ RSpec.describe Spree::Gateway do
end
it "passes through all arguments on a method_missing call" do
expect(Rails.env).to receive(:local?).and_return(false)
gateway = test_gateway.new
expect(gateway.provider).to receive(:imaginary_method).with('foo')
gateway.imaginary_method('foo')
end
it "raises an error in test env" do
gateway = test_gateway.new
expect { gateway.imaginary_method('foo') }.to raise_error StandardError
end
end

View File

@@ -86,11 +86,11 @@ RSpec.describe Spree::Order do
(Spree::Shipment.state_machine.states.keys - [:pending, :backorder, :ready])
.each do |shipment_state|
it "should be false if shipment_state is #{shipment_state}" do
allow(order).to receive_messages completed?: true
order.shipment_state = shipment_state
expect(order.can_cancel?).to be_falsy
end
it "should be false if shipment_state is #{shipment_state}" do
allow(order).to receive_messages completed?: true
order.shipment_state = shipment_state
expect(order.can_cancel?).to be_falsy
end
end
end

View File

@@ -131,13 +131,6 @@ RSpec.describe Spree::PaymentMethod do
expect(pm.errors.to_a).to eq(["Name can't be blank", "At least one hub must be selected"])
end
it "generates a clean name for known Payment Method types" do
expect(Spree::PaymentMethod::Check.clean_name)
.to eq('Cash/EFT/etc. (payments for which automatic validation is not required)')
expect(Spree::Gateway::PayPalExpress.clean_name).to eq('PayPal Express')
expect(Spree::Gateway::StripeSCA.clean_name).to eq('Stripe SCA')
end
it "computes the amount of fees" do
order = create(:order)

View File

@@ -345,24 +345,6 @@ RSpec.describe Spree::Payment do
allow(payment_method).to receive(:void).and_return(success_response)
end
context "when profiles are supported" do
it "should call payment_enterprise.void with the payment's response_code" do
allow(payment_method).to receive(:payment_profiles_supported) { true }
expect(payment_method).to receive(:void).with('123', card,
anything).and_return(success_response)
payment.void_transaction!
end
end
context "when profiles are not supported" do
it "should call payment_gateway.void with the payment's response_code" do
allow(payment_method).to receive(:payment_profiles_supported) { false }
expect(payment_method).to receive(:void).with('123', card,
anything).and_return(success_response)
payment.void_transaction!
end
end
it "should log the response" do
payment.void_transaction!
expect(payment).to have_received(:record_response)
@@ -437,7 +419,7 @@ RSpec.describe Spree::Payment do
end
it "should call credit on the gateway with the credit amount and response_code" do
expect(payment_method).to receive(:credit).with(1000, card, '123',
expect(payment_method).to receive(:credit).with(1000, '123',
anything).and_return(success_response)
payment.credit!
end
@@ -463,7 +445,7 @@ RSpec.describe Spree::Payment do
it "should call credit on the gateway with the credit amount and response_code" do
expect(payment_method).to receive(:credit).with(
amount_in_cents, card, '123', anything
amount_in_cents, '123', anything
).and_return(success_response)
payment.credit!
end
@@ -476,7 +458,7 @@ RSpec.describe Spree::Payment do
it "should call credit on the gateway with original payment amount and response_code" do
expect(payment_method).to receive(:credit).with(
amount_in_cents.to_f, card, '123', anything
amount_in_cents.to_f, '123', anything
).and_return(success_response)
payment.credit!
end
@@ -658,7 +640,6 @@ RSpec.describe Spree::Payment do
context "when profiles are supported" do
before do
allow(payment_method).to receive(:payment_profiles_supported?) { true }
allow(payment.source).to receive(:has_payment_profile?) { false }
end
@@ -700,26 +681,6 @@ RSpec.describe Spree::Payment do
end
end
context "when profiles are not supported" do
before do
allow(payment_method).to receive(:payment_profiles_supported?) { false }
end
it "should not create a payment profile" do
payment_method.name = 'Gateway'
payment_method.distributors << create(:distributor_enterprise)
payment_method.save!
expect(payment_method).not_to receive :create_profile
payment = Spree::Payment.create(
amount: 100,
order: create(:order),
source: card,
payment_method:
)
end
end
context 'when the payment was completed but now void' do
let(:payment) { create(:payment, :completed, amount: 100, order:) }
@@ -877,23 +838,6 @@ RSpec.describe Spree::Payment do
end
end
describe "performing refunds" do
before do
allow(payment).to receive(:calculate_refund_amount) { 123 }
expect(payment.payment_method).to receive(:refund).and_return(success)
end
it "performs the refund without payment profiles" do
allow(payment.payment_method).to receive(:payment_profiles_supported?) { false }
payment.refund!
end
it "performs the refund with payment profiles" do
allow(payment.payment_method).to receive(:payment_profiles_supported?) { true }
payment.refund!
end
end
it "records the response" do
allow(payment).to receive(:calculate_refund_amount) { 123 }
allow(payment.payment_method).to receive(:refund).and_return(success)

View File

@@ -17,7 +17,7 @@ RSpec.describe Spree::User do
bill_address_attributes: new_bill_address.dup.attributes.merge(
'id' => old_bill_address.id
)
.except!('created_at', 'updated_at')
.except!('created_at', 'updated_at')
)
expect(user.bill_address.id).to eq old_bill_address.id

View File

@@ -10,7 +10,7 @@ RSpec.describe OutstandingBalanceQuery do
let(:normalized_sql_statement) { normalize(query.statement) }
it 'returns the CASE statement necessary to compute the order balance' do
expect(normalized_sql_statement).to eq(normalize(<<-SQL.squish))
expect(normalized_sql_statement).to eq(normalize(<<~SQL.squish))
CASE WHEN "spree_orders"."state" IN ('canceled', 'returned') THEN "spree_orders"."payment_total"
WHEN "spree_orders"."state" IS NOT NULL THEN "spree_orders"."payment_total" - "spree_orders"."total"
ELSE 0 END

View File

@@ -0,0 +1,328 @@
# frozen_string_literal: true
RSpec.describe "Admin Reports AJAX Search API" do
let(:bill_address) { create(:address) }
let(:ship_address) { create(:address) }
let(:instructions) { "pick up on thursday please" }
let(:coordinator1) { create(:distributor_enterprise) }
let(:supplier1) { create(:supplier_enterprise) }
let(:supplier2) { create(:supplier_enterprise) }
let(:supplier3) { create(:supplier_enterprise) }
let(:distributor1) { create(:distributor_enterprise) }
let(:distributor2) { create(:distributor_enterprise) }
let(:product1) { create(:product, price: 12.34, supplier_id: supplier1.id) }
let(:product2) { create(:product, price: 23.45, supplier_id: supplier2.id) }
let(:product3) { create(:product, price: 34.56, supplier_id: supplier3.id) }
let(:enterprise_fee1) { create(:enterprise_fee, name: "Delivery Fee", enterprise: distributor1) }
let(:enterprise_fee2) { create(:enterprise_fee, name: "Admin Fee", enterprise: distributor2) }
let(:ocA) {
create(:simple_order_cycle, coordinator: coordinator1,
distributors: [distributor1, distributor2],
suppliers: [supplier1, supplier2, supplier3],
variants: [product1.variants.first, product3.variants.first])
}
let(:ocB) {
create(:simple_order_cycle, coordinator: coordinator1,
distributors: [distributor1, distributor2],
suppliers: [supplier1, supplier2, supplier3],
variants: [product2.variants.first])
}
let(:orderA1) do
order = create(:order, distributor: distributor1, bill_address:,
ship_address:, special_instructions: instructions,
order_cycle: ocA)
order.line_items << create(:line_item, variant: product1.variants.first)
order.line_items << create(:line_item, variant: product3.variants.first)
order.finalize!
order.save
order
end
let(:orderA2) do
order = create(:order, distributor: distributor2, bill_address:,
ship_address:, special_instructions: instructions,
order_cycle: ocA)
order.line_items << create(:line_item, variant: product2.variants.first)
order.finalize!
order.save
order
end
let(:orderB1) do
order = create(:order, distributor: distributor1, bill_address:,
ship_address:, special_instructions: instructions,
order_cycle: ocB)
order.line_items << create(:line_item, variant: product1.variants.first)
order.line_items << create(:line_item, variant: product3.variants.first)
order.finalize!
order.save
order
end
let(:base_params) do
{
report_type: :enterprise_fee_summary,
report_subtype: :enterprise_fees_with_tax_report_by_order
}
end
def create_adjustment(order, fee, amount)
order.adjustments.create!(
originator: fee,
label: fee.name,
amount:,
state: "finalized",
order:
)
end
context "when user is an admin" do
before do
login_as create(:admin_user)
create_adjustment(orderA1, enterprise_fee1, 5.0)
create_adjustment(orderB1, enterprise_fee2, 3.0)
end
describe "GET /admin/reports/search_enterprise_fees" do
it "returns enterprise fees sorted alphabetically by name" do
get "/admin/reports/search_enterprise_fees", params: base_params
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Admin Fee', 'Delivery Fee'])
expect(json_response["pagination"]["more"]).to be false
end
context "with more than 30 records" do
before do
create_list(:enterprise_fee, 35, enterprise: distributor1) do |fee, i|
index = (i + 1).to_s.rjust(2, "0")
fee.update!(name: "Fee #{index}")
create_adjustment(orderA1, fee, 1.0)
end
end
it "returns first page with 30 results and more flag as true" do
get "/admin/reports/search_enterprise_fees", params: base_params.merge(page: 1)
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get "/admin/reports/search_enterprise_fees", params: base_params.merge(page: 2)
json_response = response.parsed_body
expect(json_response["results"].length).to eq(7)
expect(json_response["pagination"]["more"]).to be false
end
end
end
describe "GET /admin/reports/search_enterprise_fee_owners" do
it "returns unique enterprise fee owners sorted alphabetically by name" do
distributor1.update!(name: "Zebra Farm")
distributor2.update!(name: "Alpha Market")
get "/admin/reports/search_enterprise_fee_owners", params: base_params
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Alpha Market', 'Zebra Farm'])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_order_customers" do
let!(:customer1) { create(:customer, email: "alice@example.com", enterprise: distributor1) }
let!(:customer2) { create(:customer, email: "bob@example.com", enterprise: distributor1) }
before do
orderA1.update!(customer: customer1)
orderA2.update!(customer: customer2)
end
it "returns all customers sorted by email" do
get "/admin/reports/search_order_customers", params: base_params
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["alice@example.com",
"bob@example.com"])
expect(json_response["pagination"]["more"]).to be false
end
it "filters customers by email query" do
get "/admin/reports/search_order_customers", params: base_params.merge(q: "alice")
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["alice@example.com"])
expect(json_response["pagination"]["more"]).to be false
end
context "with more than 30 customers" do
before do
create_list(:customer, 35, enterprise: distributor1) do |customer, i|
customer.update!(email: "customer#{(i + 1).to_s.rjust(2, '0')}@example.com")
order = create(:order, distributor: distributor1, order_cycle: ocA, customer:)
order.line_items << create(:line_item, variant: product1.variants.first)
order.finalize!
end
end
it "returns first page with 30 results and more flag as true" do
get "/admin/reports/search_order_customers", params: base_params.merge(page: 1)
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get "/admin/reports/search_order_customers", params: base_params.merge(page: 2)
json_response = response.parsed_body
expect(json_response["results"].length).to eq(7)
expect(json_response["pagination"]["more"]).to be false
end
end
end
describe "GET /admin/reports/search_order_cycles" do
before do
ocA.update!(name: "Winter Market")
ocB.update!(name: "Summer Market")
end
it "returns order cycles sorted by close date" do
get "/admin/reports/search_order_cycles", params: base_params
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["Summer Market", "Winter Market"])
expect(json_response["pagination"]["more"]).to be false
end
it "filters order cycles by name query" do
get "/admin/reports/search_order_cycles", params: base_params.merge(q: "Winter")
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["Winter Market"])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_distributors" do
before do
distributor1.update!(name: "Alpha Farm")
distributor2.update!(name: "Beta Market")
end
it "filters distributors by name query" do
get "/admin/reports/search_distributors", params: base_params.merge(q: "Alpha")
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["Alpha Farm"])
expect(json_response["pagination"]["more"]).to be false
end
context "with more than 30 distributors" do
before { create_list(:distributor_enterprise, 35) }
it "returns first page with 30 results and more flag as true" do
get "/admin/reports/search_distributors", params: base_params.merge(page: 1)
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on subsequent pages with more flag as false" do
get "/admin/reports/search_distributors", params: base_params.merge(page: 2)
json_response = response.parsed_body
expect(json_response["results"].length).to be > 0
expect(json_response["pagination"]["more"]).to be false
end
end
end
end
context "when user is not an admin" do
before do
login_as distributor1.users.first
create_adjustment(orderA1, enterprise_fee1, 5.0)
create_adjustment(orderA2, enterprise_fee2, 3.0)
end
describe "GET /admin/reports/search_enterprise_fees" do
it "returns only enterprise fees for user's managed enterprises" do
get "/admin/reports/search_enterprise_fees", params: base_params
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Delivery Fee'])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_enterprise_fee_owners" do
it "returns only enterprise fee owners for user's managed enterprises" do
get "/admin/reports/search_enterprise_fee_owners", params: base_params
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq([distributor1.name])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_order_customers" do
it "returns only customers from user's managed enterprises" do
customer1 = create(:customer, email: "alice@example.com", enterprise: distributor1)
customer2 = create(:customer, email: "bob@example.com", enterprise: distributor1)
orderA1.update!(customer: customer1)
orderA2.update!(customer: customer2)
get "/admin/reports/search_order_customers", params: base_params
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["alice@example.com"])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_order_cycles" do
it "returns only order cycles accessible to user's managed enterprises" do
ocA.update!(name: "Winter Market")
ocB.update!(name: "Summer Market")
create(:simple_order_cycle, name: 'Autumn Market', coordinator: coordinator1,
distributors: [distributor2],
suppliers: [supplier1, supplier2, supplier3],
variants: [product2.variants.first])
get "/admin/reports/search_order_cycles", params: base_params
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(["Summer Market", "Winter Market"])
expect(json_response["pagination"]["more"]).to be false
end
end
describe "GET /admin/reports/search_distributors" do
it "returns only user's managed distributors" do
get "/admin/reports/search_distributors", params: base_params
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq([distributor1.name])
expect(json_response["pagination"]["more"]).to be false
end
end
end
end

View File

@@ -38,8 +38,8 @@ RSpec.shared_examples "attribute changes - tax total changes" do |boolean, type,
create(:order_with_taxes, product_price: 110, tax_rate_amount: 0.1,
included_in_price: included_boolean)
.tap do |order|
order.create_tax_charge!
order.update_shipping_fees!
order.create_tax_charge!
order.update_shipping_fees!
end
end

View File

@@ -92,9 +92,7 @@ RSpec::Matchers.define :have_select2 do |id, options = {}|
end
def selected_option_is(from, text)
within find(from) do
find("a.select2-choice").text == text
end
find("#{from} a.select2-choice", text:)
end
def with_select2_open(from)

View File

@@ -144,7 +144,7 @@ module StripeStubs
customer_id = options[:customer_id] || "cus_A123"
{ status: 200,
body: JSON.generate(id: customer_id,
sources: { data: [id: customer_id] }) }
sources: { data: [{ id: customer_id }] }) }
end
def payment_successful_refund_mock

View File

@@ -69,8 +69,6 @@ module WebHelper
.find(:css, '.select2-drop-active .select2-result-label',
text: options[:select_text] || value)
.click
expect(page).to have_select2 options[:from], selected: options[:select_text] || value
end
def open_select2(selector)

View File

@@ -152,7 +152,7 @@ RSpec.describe '
click_link 'Adjustments'
page.find('tr', text: 'Extra Adjustment').find('a.icon-edit').click
expect(page).to have_select2 :adjustment_tax_category_id, selected: []
expect(page).to have_select2 :adjustment_tax_category_id, selected: "None"
# When I edit the adjustment, setting a tax rate
select2_select 'GST', from: :adjustment_tax_category_id

View File

@@ -1044,10 +1044,10 @@ RSpec.describe '
page.driver
.dismiss_modal :confirm,
text: "Unsaved changes exist and will be lost if you continue." do
within "tr#li_#{li1.id}" do
fill_in "quantity", with: (li1.quantity + 1)
find("a.edit-order").click
end
within "tr#li_#{li1.id}" do
fill_in "quantity", with: (li1.quantity + 1)
find("a.edit-order").click
end
end
# So we save the changes

View File

@@ -20,7 +20,11 @@ RSpec.describe '
click_link 'Payment Methods'
click_link 'New Payment Method'
expect(page).to have_select2 "payment_method_type", selected: "Choose..."
fill_in 'payment_method_name', with: 'Cheque payment method'
cash_name = "Cash/EFT/etc. (payments for which automatic validation is not required)"
select2_select cash_name, from: "payment_method_type"
check "payment_method_distributor_ids_#{@distributors[0].id}"
click_button 'Create'
@@ -243,6 +247,9 @@ RSpec.describe '
it "creates payment methods" do
visit spree.new_admin_payment_method_path
fill_in 'payment_method_name', with: 'Cheque payment method'
cash_name = "Cash/EFT/etc. (payments for which automatic validation is not required)"
select2_select cash_name, from: "payment_method_type"
expect(page).to have_field 'payment_method_description'
expect(page).to have_select 'payment_method_display_on'

View File

@@ -296,11 +296,8 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
end
it "should filter by distributor and order cycle" do
page.find("#s2id_autogen1").click
find('li', text: distributor.name).click # selects Distributor
page.find("#s2id_q_order_cycle_id_in").click
find('li', text: order_cycle.name).click
tomselect_multiselect distributor.name, from: 'q[distributor_id_in][]'
tomselect_multiselect order_cycle.name, from: 'q[order_cycle_id_in][]'
run_report
expect(page.find("table.report__table thead tr")).to have_content(table_header)
@@ -455,9 +452,6 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
}
context "filtering" do
let(:fee_name_selector){ "#s2id_q_enterprise_fee_id_in" }
let(:fee_owner_selector){ "#s2id_q_enterprise_fee_owner_id_in" }
let(:summary_row_after_filtering_by_fee_name){
[cost_of_produce1, "TOTAL", "120.0", "4.8", "124.8"].join(" ")
}
@@ -471,11 +465,8 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
end
it "should filter by distributor and order cycle" do
page.find("#s2id_autogen1").click
find('li', text: distributor.name).click # selects Distributor
page.find("#s2id_q_order_cycle_id_in").click
find('li', text: order_cycle3.name).click
tomselect_multiselect distributor.name, from: 'q[distributor_id_in][]'
tomselect_multiselect order_cycle3.name, from: 'q[order_cycle_id_in][]'
run_report
expect(page.find("table.report__table thead tr")).to have_content(table_header)
@@ -504,8 +495,7 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
end
it "should filter by producer" do
page.find("#s2id_supplier_id_in").click
find('li', text: supplier2.name).click
tomselect_multiselect supplier2.name, from: 'supplier_id_in[]'
run_report
expect(page.find("table.report__table thead tr")).to have_content(table_header)
@@ -528,8 +518,7 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
end
it "should filter by fee name" do
page.find(fee_name_selector).click
find('li', text: supplier_fees.name).click
tomselect_multiselect supplier_fees.name, from: 'q[enterprise_fee_id_in][]'
run_report
@@ -557,8 +546,7 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do
end
it "should filter by fee owner" do
page.find(fee_owner_selector).click
find('li', text: supplier.name).click
tomselect_multiselect supplier.name, from: 'q[enterprise_fee_owner_id_in][]'
run_report
expect(page.find("table.report__table thead tr")).to have_content(table_header)

View File

@@ -360,10 +360,10 @@ RSpec.describe "As a consumer, I want to checkout my order" do
# And fake the payment status to avoid user interaction.
allow_any_instance_of(Taler::Client)
.to receive(:fetch_order) do
payment = Spree::Payment.last
url = payment_gateways_confirm_taler_path(payment_id: payment.id)
payment = Spree::Payment.last
url = payment_gateways_confirm_taler_path(payment_id: payment.id)
{ "order_status_url" => url, "order_status" => "paid" }
{ "order_status_url" => url, "order_status" => "paid" }
end
end

302
yarn.lock
View File

@@ -13,34 +13,34 @@
"@csstools/css-tokenizer" "^3.0.3"
lru-cache "^10.4.3"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7"
integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==
dependencies:
"@babel/helper-validator-identifier" "^7.28.5"
js-tokens "^4.0.0"
picocolors "^1.1.1"
"@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.6.tgz#103f466803fa0f059e82ccac271475470570d74c"
integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==
"@babel/compat-data@^7.28.6", "@babel/compat-data@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d"
integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==
"@babel/core@^7.23.9", "@babel/core@^7.27.4", "@babel/core@^7.28.5":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.6.tgz#531bf883a1126e53501ba46eb3bb414047af507f"
integrity sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322"
integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/generator" "^7.28.6"
"@babel/code-frame" "^7.29.0"
"@babel/generator" "^7.29.0"
"@babel/helper-compilation-targets" "^7.28.6"
"@babel/helper-module-transforms" "^7.28.6"
"@babel/helpers" "^7.28.6"
"@babel/parser" "^7.28.6"
"@babel/parser" "^7.29.0"
"@babel/template" "^7.28.6"
"@babel/traverse" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse" "^7.29.0"
"@babel/types" "^7.29.0"
"@jridgewell/remapping" "^2.3.5"
convert-source-map "^2.0.0"
debug "^4.1.0"
@@ -48,13 +48,13 @@
json5 "^2.2.3"
semver "^6.3.1"
"@babel/generator@^7.27.5", "@babel/generator@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1"
integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==
"@babel/generator@^7.27.5", "@babel/generator@^7.29.0":
version "7.29.1"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50"
integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==
dependencies:
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/parser" "^7.29.0"
"@babel/types" "^7.29.0"
"@jridgewell/gen-mapping" "^0.3.12"
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
@@ -110,6 +110,17 @@
lodash.debounce "^4.0.8"
resolve "^1.22.10"
"@babel/helper-define-polyfill-provider@^0.6.6":
version "0.6.6"
resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz#714dfe33d8bd710f556df59953720f6eeb6c1a14"
integrity sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==
dependencies:
"@babel/helper-compilation-targets" "^7.28.6"
"@babel/helper-plugin-utils" "^7.28.6"
debug "^4.4.3"
lodash.debounce "^4.0.8"
resolve "^1.22.11"
"@babel/helper-globals@^7.28.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
@@ -123,7 +134,7 @@
"@babel/traverse" "^7.28.5"
"@babel/types" "^7.28.5"
"@babel/helper-module-imports@^7.27.1", "@babel/helper-module-imports@^7.28.6":
"@babel/helper-module-imports@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c"
integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==
@@ -131,7 +142,7 @@
"@babel/traverse" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3", "@babel/helper-module-transforms@^7.28.6":
"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e"
integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==
@@ -147,21 +158,11 @@
dependencies:
"@babel/types" "^7.27.1"
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.28.6":
"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.28.6", "@babel/helper-plugin-utils@^7.8.0":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8"
integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==
"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c"
integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
"@babel/helper-plugin-utils@^7.14.5":
version "7.21.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56"
integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==
"@babel/helper-remap-async-to-generator@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6"
@@ -220,12 +221,12 @@
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd"
integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==
dependencies:
"@babel/types" "^7.28.6"
"@babel/types" "^7.29.0"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5":
version "7.28.5"
@@ -306,14 +307,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/plugin-syntax-import-attributes@^7.24.7":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07"
integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-syntax-import-attributes@^7.28.6":
"@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503"
integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==
@@ -419,14 +413,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-async-generator-functions@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz#80cb86d3eaa2102e18ae90dd05ab87bdcad3877d"
integrity sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==
"@babel/plugin-transform-async-generator-functions@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz#63ed829820298f0bf143d5a4a68fb8c06ffd742f"
integrity sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==
dependencies:
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/helper-remap-async-to-generator" "^7.27.1"
"@babel/traverse" "^7.28.6"
"@babel/traverse" "^7.29.0"
"@babel/plugin-transform-async-to-generator@^7.28.6":
version "7.28.6"
@@ -510,10 +504,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz#e0c59ba54f1655dd682f2edf5f101b5910a8f6f3"
integrity sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==
"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz#8014b8a6cfd0e7b92762724443bf0d2400f26df1"
integrity sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==
dependencies:
"@babel/helper-create-regexp-features-plugin" "^7.28.5"
"@babel/helper-plugin-utils" "^7.28.6"
@@ -608,15 +602,15 @@
"@babel/helper-module-transforms" "^7.28.6"
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/plugin-transform-modules-systemjs@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz#7439e592a92d7670dfcb95d0cbc04bd3e64801d2"
integrity sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==
"@babel/plugin-transform-modules-systemjs@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz#e458a95a17807c415924106a3ff188a3b8dee964"
integrity sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==
dependencies:
"@babel/helper-module-transforms" "^7.28.3"
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/helper-module-transforms" "^7.28.6"
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/helper-validator-identifier" "^7.28.5"
"@babel/traverse" "^7.28.5"
"@babel/traverse" "^7.29.0"
"@babel/plugin-transform-modules-umd@^7.27.1":
version "7.27.1"
@@ -626,13 +620,13 @@
"@babel/helper-module-transforms" "^7.27.1"
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-named-capturing-groups-regex@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1"
integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==
"@babel/plugin-transform-named-capturing-groups-regex@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz#a26cd51e09c4718588fc4cce1c5d1c0152102d6a"
integrity sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==
dependencies:
"@babel/helper-create-regexp-features-plugin" "^7.27.1"
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/helper-create-regexp-features-plugin" "^7.28.5"
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/plugin-transform-new-target@^7.27.1":
version "7.27.1"
@@ -720,10 +714,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-regenerator@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz#6ca2ed5b76cff87980f96eaacfc2ce833e8e7a1b"
integrity sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==
"@babel/plugin-transform-regenerator@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz#dec237cec1b93330876d6da9992c4abd42c9d18b"
integrity sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==
dependencies:
"@babel/helper-plugin-utils" "^7.28.6"
@@ -743,12 +737,12 @@
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-runtime@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz#ae3e21fbefe2831ebac04dfa6b463691696afe17"
integrity sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz#a5fded13cc656700804bfd6e5ebd7fffd5266803"
integrity sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==
dependencies:
"@babel/helper-module-imports" "^7.27.1"
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/helper-module-imports" "^7.28.6"
"@babel/helper-plugin-utils" "^7.28.6"
babel-plugin-polyfill-corejs2 "^0.4.14"
babel-plugin-polyfill-corejs3 "^0.13.0"
babel-plugin-polyfill-regenerator "^0.6.5"
@@ -822,11 +816,11 @@
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/preset-env@^7.28.5":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.6.tgz#b4586bb59d8c61be6c58997f4912e7ea6bd17178"
integrity sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw==
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.0.tgz#c55db400c515a303662faaefd2d87e796efa08d0"
integrity sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==
dependencies:
"@babel/compat-data" "^7.28.6"
"@babel/compat-data" "^7.29.0"
"@babel/helper-compilation-targets" "^7.28.6"
"@babel/helper-plugin-utils" "^7.28.6"
"@babel/helper-validator-option" "^7.27.1"
@@ -840,7 +834,7 @@
"@babel/plugin-syntax-import-attributes" "^7.28.6"
"@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
"@babel/plugin-transform-arrow-functions" "^7.27.1"
"@babel/plugin-transform-async-generator-functions" "^7.28.6"
"@babel/plugin-transform-async-generator-functions" "^7.29.0"
"@babel/plugin-transform-async-to-generator" "^7.28.6"
"@babel/plugin-transform-block-scoped-functions" "^7.27.1"
"@babel/plugin-transform-block-scoping" "^7.28.6"
@@ -851,7 +845,7 @@
"@babel/plugin-transform-destructuring" "^7.28.5"
"@babel/plugin-transform-dotall-regex" "^7.28.6"
"@babel/plugin-transform-duplicate-keys" "^7.27.1"
"@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.28.6"
"@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.29.0"
"@babel/plugin-transform-dynamic-import" "^7.27.1"
"@babel/plugin-transform-explicit-resource-management" "^7.28.6"
"@babel/plugin-transform-exponentiation-operator" "^7.28.6"
@@ -864,9 +858,9 @@
"@babel/plugin-transform-member-expression-literals" "^7.27.1"
"@babel/plugin-transform-modules-amd" "^7.27.1"
"@babel/plugin-transform-modules-commonjs" "^7.28.6"
"@babel/plugin-transform-modules-systemjs" "^7.28.5"
"@babel/plugin-transform-modules-systemjs" "^7.29.0"
"@babel/plugin-transform-modules-umd" "^7.27.1"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.27.1"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.29.0"
"@babel/plugin-transform-new-target" "^7.27.1"
"@babel/plugin-transform-nullish-coalescing-operator" "^7.28.6"
"@babel/plugin-transform-numeric-separator" "^7.28.6"
@@ -878,7 +872,7 @@
"@babel/plugin-transform-private-methods" "^7.28.6"
"@babel/plugin-transform-private-property-in-object" "^7.28.6"
"@babel/plugin-transform-property-literals" "^7.27.1"
"@babel/plugin-transform-regenerator" "^7.28.6"
"@babel/plugin-transform-regenerator" "^7.29.0"
"@babel/plugin-transform-regexp-modifiers" "^7.28.6"
"@babel/plugin-transform-reserved-words" "^7.27.1"
"@babel/plugin-transform-shorthand-properties" "^7.27.1"
@@ -891,10 +885,10 @@
"@babel/plugin-transform-unicode-regex" "^7.27.1"
"@babel/plugin-transform-unicode-sets-regex" "^7.28.6"
"@babel/preset-modules" "0.1.6-no-external-plugins"
babel-plugin-polyfill-corejs2 "^0.4.14"
babel-plugin-polyfill-corejs3 "^0.13.0"
babel-plugin-polyfill-regenerator "^0.6.5"
core-js-compat "^3.43.0"
babel-plugin-polyfill-corejs2 "^0.4.15"
babel-plugin-polyfill-corejs3 "^0.14.0"
babel-plugin-polyfill-regenerator "^0.6.6"
core-js-compat "^3.48.0"
semver "^6.3.1"
"@babel/preset-modules@0.1.6-no-external-plugins":
@@ -925,23 +919,23 @@
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e"
integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==
"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a"
integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/generator" "^7.28.6"
"@babel/code-frame" "^7.29.0"
"@babel/generator" "^7.29.0"
"@babel/helper-globals" "^7.28.0"
"@babel/parser" "^7.28.6"
"@babel/parser" "^7.29.0"
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/types" "^7.29.0"
debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.4.4":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df"
integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.4.4":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
@@ -2639,13 +2633,13 @@ babel-plugin-jest-hoist@30.2.0:
dependencies:
"@types/babel__core" "^7.20.5"
babel-plugin-polyfill-corejs2@^0.4.14:
version "0.4.14"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f"
integrity sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==
babel-plugin-polyfill-corejs2@^0.4.14, babel-plugin-polyfill-corejs2@^0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz#808fa349686eea4741807cfaaa2aa3aa57ce120a"
integrity sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==
dependencies:
"@babel/compat-data" "^7.27.7"
"@babel/helper-define-polyfill-provider" "^0.6.5"
"@babel/compat-data" "^7.28.6"
"@babel/helper-define-polyfill-provider" "^0.6.6"
semver "^6.3.1"
babel-plugin-polyfill-corejs3@^0.13.0:
@@ -2656,12 +2650,20 @@ babel-plugin-polyfill-corejs3@^0.13.0:
"@babel/helper-define-polyfill-provider" "^0.6.5"
core-js-compat "^3.43.0"
babel-plugin-polyfill-regenerator@^0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz#32752e38ab6f6767b92650347bf26a31b16ae8c5"
integrity sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==
babel-plugin-polyfill-corejs3@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz#65b06cda48d6e447e1e926681f5a247c6ae2b9cf"
integrity sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==
dependencies:
"@babel/helper-define-polyfill-provider" "^0.6.5"
"@babel/helper-define-polyfill-provider" "^0.6.6"
core-js-compat "^3.48.0"
babel-plugin-polyfill-regenerator@^0.6.5, babel-plugin-polyfill-regenerator@^0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz#69f5dd263cab933c42fe5ea05e83443b374bd4bf"
integrity sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==
dependencies:
"@babel/helper-define-polyfill-provider" "^0.6.6"
babel-preset-current-node-syntax@^1.2.0:
version "1.2.0"
@@ -2775,7 +2777,7 @@ braces@^3.0.2, braces@^3.0.3, braces@~3.0.2:
dependencies:
fill-range "^7.1.1"
browserslist@^4.0.0, browserslist@^4.24.0, browserslist@^4.27.0, browserslist@^4.28.0, browserslist@^4.28.1:
browserslist@^4.0.0, browserslist@^4.24.0, browserslist@^4.27.0, browserslist@^4.28.1:
version "4.28.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95"
integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==
@@ -3075,12 +3077,12 @@ cookie@~0.7.1, cookie@~0.7.2:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
core-js-compat@^3.43.0:
version "3.47.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.47.0.tgz#698224bbdbb6f2e3f39decdda4147b161e3772a3"
integrity sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==
core-js-compat@^3.43.0, core-js-compat@^3.48.0:
version "3.48.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.48.0.tgz#7efbe1fc1cbad44008190462217cc5558adaeaa6"
integrity sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==
dependencies:
browserslist "^4.28.0"
browserslist "^4.28.1"
core-util-is@~1.0.0:
version "1.0.3"
@@ -3297,7 +3299,7 @@ debug@2.6.9:
dependencies:
ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.4.1:
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.4.1, debug@^4.4.3:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
@@ -3511,13 +3513,13 @@ engine.io@~6.6.0:
engine.io-parser "~5.2.1"
ws "~8.17.1"
enhanced-resolve@^5.17.4:
version "5.18.4"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828"
integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==
enhanced-resolve@^5.19.0:
version "5.19.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz#6687446a15e969eaa63c2fa2694510e17ae6d97c"
integrity sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
tapable "^2.3.0"
ent@~2.2.0:
version "2.2.2"
@@ -4003,9 +4005,9 @@ hasown@^2.0.2:
function-bind "^1.1.2"
hotkeys-js@*:
version "3.13.15"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.13.15.tgz#2d394bd6bd78857d4b24dc86bdba2fa1cf7012fc"
integrity sha512-gHh8a/cPTCpanraePpjRxyIlxDFrIhYqjuh01UHWEwDpglJKCnvLW8kqSx5gQtOuSsJogNZXLhOdbSExpgUiqg==
version "4.0.0"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-4.0.0.tgz#75336c0ac610ad384d286c61c519909dcd4bdf6b"
integrity sha512-gIoeqMWYqPIItc4HaseVbtTRpEpBbeufZMUcoWtN62JZdDq3KadS1ijN6wpaDjTzRK7PjT3QOPUcx+yNT0rrZQ==
hpack.js@^2.1.6:
version "2.1.6"
@@ -6078,9 +6080,9 @@ qjobs@^1.2.0:
integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==
qs@~6.14.0:
version "6.14.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159"
integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==
version "6.14.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c"
integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==
dependencies:
side-channel "^1.1.0"
@@ -6239,7 +6241,7 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.1.7, resolve@^1.22.10, resolve@^1.9.0:
resolve@^1.1.7, resolve@^1.22.10, resolve@^1.22.11, resolve@^1.9.0:
version "1.22.11"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262"
integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==
@@ -6428,9 +6430,9 @@ sass-embedded@^1.96.0:
sass-embedded-win32-x64 "1.97.3"
sass-loader@^16.0.6:
version "16.0.6"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-16.0.6.tgz#913b05607d06c386bc37870494e1e3a3e091fd3b"
integrity sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==
version "16.0.7"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-16.0.7.tgz#d1f8723b795805831d41b5825e3d9cd72cb939e7"
integrity sha512-w6q+fRHourZ+e+xA1kcsF27iGM6jdB8teexYCfdUw0sYgcDNeZESnDNT9sUmmPm3ooziwUJXGwZJSTF3kOdBfA==
dependencies:
neo-async "^2.6.2"
@@ -6987,7 +6989,7 @@ synckit@^0.11.8:
dependencies:
"@pkgr/core" "^0.2.9"
tapable@^2.2.0, tapable@^2.2.1, tapable@^2.3.0:
tapable@^2.2.1, tapable@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6"
integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==
@@ -7062,9 +7064,9 @@ toidentifier@1.0.1, toidentifier@~1.0.1:
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tom-select@*:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.4.3.tgz#1daa4131cd317de691f39eb5bf41148265986c1f"
integrity sha512-MFFrMxP1bpnAMPbdvPCZk0KwYxLqhYZso39torcdoefeV/NThNyDu8dV96/INJ5XQVTL3O55+GqQ78Pkj5oCfw==
version "2.5.1"
resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.5.1.tgz#8c8d3f11e5c1780b5f26c9e90f4e650842ff9596"
integrity sha512-63D5/Qf6bb6kLSgksEuas/60oawDcuUHrD90jZofeOpF6bkQFYriKrvtpJBQQ4xIA5dUGcjhBbk/yrlfOQsy3g==
dependencies:
"@orchidjs/sifter" "^1.1.0"
"@orchidjs/unicode-variants" "^1.1.2"
@@ -7261,10 +7263,10 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
watchpack@^2.4.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.0.tgz#fa115d5ccaa4bf3aa594f586257c0bc4768939fd"
integrity sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==
watchpack@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102"
integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
@@ -7374,9 +7376,9 @@ webpack-sources@^3.3.3:
integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==
webpack@^5.104.0:
version "5.104.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.104.1.tgz#94bd41eb5dbf06e93be165ba8be41b8260d4fb1a"
integrity sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==
version "5.105.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.105.1.tgz#c05cb3621196c76fa3b3a9bea446d14616b83778"
integrity sha512-Gdj3X74CLJJ8zy4URmK42W7wTZUJrqL+z8nyGEr4dTN0kb3nVs+ZvjbTOqRYPD7qX4tUmwyHL9Q9K6T1seW6Yw==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.8"
@@ -7388,7 +7390,7 @@ webpack@^5.104.0:
acorn-import-phases "^1.0.3"
browserslist "^4.28.1"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.17.4"
enhanced-resolve "^5.19.0"
es-module-lexer "^2.0.0"
eslint-scope "5.1.1"
events "^3.2.0"
@@ -7401,7 +7403,7 @@ webpack@^5.104.0:
schema-utils "^4.3.3"
tapable "^2.3.0"
terser-webpack-plugin "^5.3.16"
watchpack "^2.4.4"
watchpack "^2.5.1"
webpack-sources "^3.3.3"
websocket-driver@>=0.5.1, websocket-driver@^0.7.4: