diff --git a/.env.test b/.env.test index e1d7d47cc2..09881648aa 100644 --- a/.env.test +++ b/.env.test @@ -1,11 +1,17 @@ # ENV vars for the test environment # Override locally with `.env.test.local` +OFN_REDIS_JOBS_URL="redis://localhost:6379/2" + SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -STRIPE_SECRET_TEST_API_KEY="bogus_key" +STRIPE_INSTANCE_SECRET_KEY="bogus_key" STRIPE_CUSTOMER="bogus_customer" +STRIPE_ACCOUNT="bogus_account" +STRIPE_CLIENT_ID="bogus_client_id" +STRIPE_PUBLIC_TEST_API_KEY="bogus_stripe_publishable_key" SITE_URL="test.host" OPENID_APP_ID="test-provider" OPENID_APP_SECRET="12345" +OPENID_REFRESH_TOKEN="dummy-refresh-token" diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index 482b5bbc69..caf9fb39b8 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -39,11 +39,11 @@ assignees: '' - [ ] Notify [#instance-managers]: > @instance_managers The new release has been deployed. -- [ ] Nudge next release manager +- [ ] [Create issue] for next release and confirm with next release manager in [#core-devs]. The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing. -[Ready To Go]: #zenhub +[Ready To Go]: https://github.com/orgs/openfoodfoundation/projects/8?filterQuery=status%3A%22Ready+to+go+%F0%9F%9A%80%22 [Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex [Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A [releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases @@ -51,3 +51,5 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn [#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00 [Deploy to Staging]: https://github.com/openfoodfoundation/openfoodnetwork/actions/workflows/stage.yml [#global-community]: https://app.slack.com/client/T02G54U79/C59ADD8F2 +[Create issue]: https://github.com/openfoodfoundation/openfoodnetwork/issues/new?assignees=&labels=&projects=&template=release.md&title=Release +[#core-devs]: https://openfoodnetwork.slack.com/archives/GK2T38QPJ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2a2f3fb88b..0d46875442 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,7 +22,7 @@ Changelog Category (reviewers may add a label for the release notes): -- [x] User facing changes +- [ ] User facing changes - [ ] API changes (V0, V1, DFC or Webhook) - [ ] Technical changes only - [ ] Feature toggled diff --git a/.github/release.yml b/.github/release.yml index ce24912dc0..5f9b85166d 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,8 +1,9 @@ changelog: # Categorise according to what an instance manager needs to know categories: - # Posted in advance for #instance-managers - - title: "User-facing changes 👀" + # Add the right label if anything appears in here. + # Then re-generate the release notes. + - title: "❓❓❓ Uncategorised ❓❓❓" labels: - '*' exclude: @@ -11,6 +12,12 @@ changelog: - dependencies - feature toggled - technical changes only + - user facing changes + + # Posted in advance for #instance-managers + - title: "User-facing changes 👀" + labels: + - user facing changes - title: "API changes ⚠️" labels: diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index fa30eb7d99..31f95933d4 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -18,7 +18,7 @@ jobs: uses: reviewdog/action-rubocop@v2 with: rubocop_version: gemfile - rubocop_extensions: rubocop-rails:gemfile + rubocop_extensions: rubocop-rails:gemfile rubocop-rspec:gemfile reporter: github-pr-check level: error fail_on_error: true diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index ee76864644..0000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - - yarn pretty-quick --check --staged diff --git a/.rubocop.yml b/.rubocop.yml index 779460f826..937a2acf17 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,9 @@ # # The configuration is split into three files. Look into those files for more details. # -require: rubocop-rails +require: + - rubocop-rails + - rubocop-rspec inherit_from: # The automatically generated todo list to ignore all current violations. @@ -13,9 +15,10 @@ inherit_from: # The relaxed style rules as a common starting point which we can refine. - .rubocop_relaxed_styleguide.yml - # Our Open Food Network style guide. If you want to see all violations, + # Our Open Food Network style guides. If you want to see all violations, # then use only that configuration: # # bundle exec rubocop -c .rubocop_styleguide.yml # - .rubocop_styleguide.yml + - .rubocop_rspec_styleguide.yml diff --git a/.rubocop_rspec_styleguide.yml b/.rubocop_rspec_styleguide.yml new file mode 100644 index 0000000000..aef9fa3ba8 --- /dev/null +++ b/.rubocop_rspec_styleguide.yml @@ -0,0 +1,24 @@ +# OFN styleguide for rubocop-rspec +# Because there are so many, we will disable by default, and enable rules as needed. + +Capybara: + Enabled: false + +RSpec: + Enabled: false + +FactoryBot: + Enabled: false + +# Enabled rules + +Capybara/NegationMatcher: + Enabled: true + EnforcedStyle: not_to + +RSpec/ExpectChange: + Enabled: true + EnforcedStyle: block + +RSpec/NotToNot: + Enabled: true diff --git a/.rubocop_styleguide.yml b/.rubocop_styleguide.yml index 5606130daa..0264a99261 100644 --- a/.rubocop_styleguide.yml +++ b/.rubocop_styleguide.yml @@ -5,22 +5,55 @@ AllCops: NewCops: enable SuggestExtensions: false Exclude: - - 'bin/**/*' - - 'db/**/*' - - 'config/**/*' - - 'script/**/*' - - 'vendor/**/*' - - 'node_modules/**/*' + - bin/**/* + - db/**/* + - config/**/* + - script/**/* + - vendor/**/* + - node_modules/**/* # Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase - - 'engines/web/Gemfile' + - engines/web/Gemfile -## OFN SETTINGS -# -# Cop settings that have been agreed upon by the OFN community +Bundler/DuplicatedGem: + Enabled: false + +Layout/LineLength: + Enabled: true + Max: 100 + +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented + +# Don't think this is a big issue, mostly picking up RPSEC scope definitions +# with lamdas and RSpec '.to change{}' blocks +Lint/AmbiguousBlockAssociation: + Enabled: false + +Lint/MissingSuper: + Exclude: + - app/components/**/* + +Lint/RaiseException: + Enabled: true + +Lint/StructNewOverride: + Enabled: true + +# Heaps of offences (> 100) in specs, mostly in situations where two or more +# instances of a model are required, but only one is referenced. Difficult to +# fix without making the spec look messy or rewriting it. +# Should definitely fix at some point. +Lint/UselessAssignment: + Exclude: + - spec/**/* Metrics: Enabled: true +Metrics/AbcSize: + Max: 30 # default 17 + Metrics/BlockLength: AllowedMethods: [ "class_eval", @@ -46,13 +79,31 @@ Metrics/BlockLength: "xdescribe", ] +Metrics/MethodLength: + Enabled: true + Max: 25 # default 10 + Metrics/ParameterLists: CountKeywordArgs: false +Metrics/PerceivedComplexity: + Enabled: true + Max: 14 # default 8 + +Naming/PredicateName: + Enabled: false + +Naming/VariableNumber: + AllowedIdentifiers: + - street_address_1 + - street_address_2 + AllowedPatterns: + - _v[\d]+ + Rails/ApplicationRecord: Exclude: # Migrations should not contain application code: - - "db/migrate/*.rb" + - db/migrate/*.rb # Allow many-to-many associations without explicit model. # - It avoids the additional code of a model class. @@ -61,23 +112,23 @@ Rails/ApplicationRecord: Rails/HasAndBelongsToMany: Enabled: false -Rails/SkipsModelValidations: - AllowedMethods: - - "touch" - - "touch_all" - - "update_all" - - "update_attribute" - - "update_column" - - "update_columns" - Rails/OutputSafety: Exclude: - - 'spec/**/*' + - spec/**/* + +Rails/SkipsModelValidations: + AllowedMethods: + - touch + - touch_all + - update_all + - update_attribute + - update_column + - update_columns Style/Documentation: Enabled: false -Style/StringLiterals: +Style/FormatStringToken: Enabled: false Style/HashSyntax: @@ -87,62 +138,5 @@ Style/HashSyntax: Style/Send: Enabled: true -Layout/MultilineMethodCallIndentation: - Enabled: true - EnforcedStyle: indented - -Layout/LineLength: - Enabled: true - Max: 100 - -Lint/RaiseException: - Enabled: true - -Lint/StructNewOverride: - Enabled: true - -Naming/VariableNumber: - AllowedIdentifiers: - - street_address_1 - - street_address_2 - AllowedPatterns: - - _v[\d]+ - -Bundler/DuplicatedGem: - Enabled: false - -## TEMPORARY/CONTESTED SETTINGS -# -# These are still to be decided upon, but recommended for inclusion by -# oeoeaio after scrutinising offenses the codebase - -# Don't think this is a big issue, mostly picking up RPSEC scope definitions -# with lamdas and RSpec '.to change{}' blocks -Lint/AmbiguousBlockAssociation: - Enabled: false - -# Heaps of offences (> 100) in specs, mostly in situations where two or more -# instances of a model are required, but only one is referenced. Difficult to -# fix without making the spec look messy or rewriting it. -# Should definitely fix at some point. -Lint/UselessAssignment: - Exclude: - - spec/**/* - -Lint/MissingSuper: - Exclude: - - 'app/components/**/*' - -Metrics/AbcSize: - Max: 30 # default 17 - -Metrics/MethodLength: - Enabled: true - Max: 25 # default 10 - -Metrics/PerceivedComplexity: - Enabled: true - Max: 14 # default 8 - -Naming/PredicateName: +Style/StringLiterals: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index aa12b8a9a5..44ed59b1a2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,26 +1,16 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp` -# using RuboCop version 1.57.2. +# using RuboCop version 1.62.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. -# URISchemes: http, https -Layout/LineLength: - Exclude: - - 'app/models/spree/payment.rb' - - 'spec/system/admin/tag_rules_spec.rb' - -# Offense count: 17 +# Offense count: 16 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - - 'lib/active_merchant/billing/gateways/stripe_payment_intents_decorator.rb' - 'lib/tasks/import_product_images.rake' - 'lib/tasks/users.rake' - 'spec/controllers/spree/admin/base_controller_spec.rb' @@ -116,13 +106,19 @@ Lint/RedundantSafeNavigation: Exclude: - 'app/models/spree/payment.rb' +# Offense count: 1 +Lint/SelfAssignment: + Exclude: + - 'app/models/spree/order/checkout.rb' + # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AutoCorrect. Lint/UselessMethodDefinition: Exclude: - 'app/models/spree/gateway.rb' -# Offense count: 27 +# Offense count: 26 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: @@ -146,10 +142,10 @@ Metrics/AbcSize: - 'lib/open_food_network/order_cycle_permissions.rb' - 'lib/spree/core/controller_helpers/order.rb' - 'lib/tasks/enterprises.rake' - - 'spec/services/order_checkout_restart_spec.rb' + - 'spec/services/orders/checkout_restart_service_spec.rb' -# Offense count: 44 -# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. +# Offense count: 9 +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: Exclude: @@ -159,26 +155,6 @@ Metrics/BlockLength: - 'app/models/spree/shipment.rb' - 'lib/spree/core/controller_helpers/common.rb' - 'lib/tasks/data.rake' - - 'spec/factories.rb' - - 'spec/factories/address_factory.rb' - - 'spec/factories/enterprise_factory.rb' - - 'spec/factories/line_item_factory.rb' - - 'spec/factories/order_cycle_factory.rb' - - 'spec/factories/order_factory.rb' - - 'spec/factories/product_factory.rb' - - 'spec/factories/shipment_factory.rb' - - 'spec/factories/shipping_method_factory.rb' - - 'spec/factories/subscription_factory.rb' - - 'spec/factories/user_factory.rb' - - 'spec/factories/variant_factory.rb' - - 'spec/requests/checkout/failed_checkout_spec.rb' - - 'spec/requests/checkout/stripe_sca_spec.rb' - - 'spec/support/cancan_helper.rb' - - 'spec/support/matchers/select2_matchers.rb' - - 'spec/support/matchers/table_matchers.rb' - - 'spec/system/consumer/shopping/checkout_spec.rb' - - 'spec/system/consumer/shopping/checkout_stripe_spec.rb' - - 'spec/system/consumer/shopping/variant_overrides_spec.rb' # Offense count: 1 # Configuration parameters: CountBlocks, Max. @@ -225,8 +201,8 @@ Metrics/ClassLength: - 'app/serializers/api/cached_enterprise_serializer.rb' - 'app/serializers/api/enterprise_shopfront_serializer.rb' - 'app/services/cart_service.rb' - - 'app/services/order_cycle_form.rb' - - 'app/services/order_syncer.rb' + - 'app/services/orders/sync_service.rb' + - 'app/services/order_cycles/form_service.rb' - 'engines/order_management/app/services/order_management/order/updater.rb' - 'lib/open_food_network/enterprise_fee_calculator.rb' - 'lib/open_food_network/order_cycle_form_applicator.rb' @@ -414,21 +390,6 @@ Naming/VariableNumber: - 'spec/models/spree/tax_rate_spec.rb' - 'spec/requests/api/orders_spec.rb' -# Offense count: 11 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowedMethods, AllowedPatterns. -# AllowedMethods: order, limit, select, lock -Rails/FindEach: - Exclude: - - 'app/controllers/admin/order_cycles_controller.rb' - - 'app/jobs/subscription_confirm_job.rb' - - 'app/services/orders_bulk_cancel_service.rb' - - 'app/services/products_renderer.rb' - - 'lib/tasks/data.rake' - - 'lib/tasks/subscriptions/debug.rake' - - 'spec/system/admin/bulk_order_management_spec.rb' - - 'spec/system/admin/enterprise_relationships_spec.rb' - # Offense count: 11 # Configuration parameters: Include. # Include: app/models/**/*.rb @@ -440,7 +401,7 @@ Rails/HasManyOrHasOneDependent: - 'app/models/spree/tax_rate.rb' - 'app/models/spree/variant.rb' -# Offense count: 25 +# Offense count: 26 # Configuration parameters: Include. # Include: app/helpers/**/*.rb Rails/HelperInstanceVariable: @@ -520,21 +481,7 @@ Rails/NegateInclude: - 'lib/spree/localized_number.rb' - 'spec/support/matchers/table_matchers.rb' -# Offense count: 16 -Rails/OutputSafety: - Exclude: - - 'app/helpers/angular_form_helper.rb' - - 'app/helpers/application_helper.rb' - - 'app/helpers/reports_helper.rb' - - 'app/helpers/spree/admin/base_helper.rb' - - 'app/helpers/spree/admin/navigation_helper.rb' - - 'app/helpers/spree/admin/orders_helper.rb' - - 'app/helpers/spree/admin/zones_helper.rb' - - 'lib/reporting/queries/query_builder.rb' - - 'lib/reporting/queries/query_interface.rb' - - 'lib/spree/money.rb' - -# Offense count: 31 +# Offense count: 32 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/Pluck: Exclude: @@ -553,6 +500,7 @@ Rails/Pluck: - 'spec/lib/reports/lettuce_share_report_spec.rb' - 'spec/lib/reports/users_and_enterprises_report_spec.rb' - 'spec/serializers/api/admin/for_order_cycle/supplied_product_serializer_spec.rb' + - 'spec/support/api_helper.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -620,13 +568,11 @@ Rails/ResponseParsedBody: - 'spec/controllers/spree/credit_cards_controller_spec.rb' - 'spec/controllers/user_registrations_controller_spec.rb' -# Offense count: 3 +# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/RootPathnameMethods: Exclude: - 'spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb' - - 'spec/models/terms_of_service_file_spec.rb' - - 'spec/system/admin/configuration/terms_of_service_files_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). @@ -642,14 +588,6 @@ Rails/SkipsModelValidations: - 'app/models/variant_override.rb' - 'spec/models/spree/line_item_spec.rb' -# Offense count: 3 -# This cop supports unsafe autocorrection (--autocorrect-all). -Rails/SquishedSQLHeredocs: - Exclude: - - 'app/queries/customers_with_balance.rb' - - 'app/queries/outstanding_balance.rb' - - 'spec/queries/outstanding_balance_spec.rb' - # Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. @@ -658,8 +596,8 @@ Rails/TimeZone: Exclude: - 'app/models/spree/gateway/pay_pal_express.rb' - 'spec/controllers/spree/credit_cards_controller_spec.rb' - - 'spec/services/customer_order_cancellation_spec.rb' - - 'spec/services/order_cycle_webhook_service_spec.rb' + - 'spec/services/orders/customer_cancellation_service_spec.rb' + - 'spec/services/order_cycles/webhook_service_spec.rb' # Offense count: 1 # Configuration parameters: TransactionMethods. @@ -696,7 +634,7 @@ Rails/UnusedRenderContent: - 'app/controllers/api/v0/taxons_controller.rb' - 'app/controllers/api/v0/variants_controller.rb' -# Offense count: 55 +# Offense count: 54 # This cop supports unsafe autocorrection (--autocorrect-all). Rails/WhereEquals: Exclude: @@ -718,7 +656,6 @@ Rails/WhereEquals: - 'app/models/spree/shipping_method.rb' - 'app/models/spree/variant.rb' - 'app/models/subscription.rb' - - 'app/queries/payments_requiring_action.rb' - 'app/serializers/api/enterprise_shopfront_serializer.rb' - 'app/serializers/api/order_serializer.rb' - 'lib/open_food_network/enterprise_fee_calculator.rb' @@ -799,14 +736,6 @@ Style/ClassAndModuleChildren: - 'spec/controllers/spree/admin/base_controller_spec.rb' - 'spec/models/spree/payment_method_spec.rb' -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. -# SupportedStyles: annotated, template, unannotated -# AllowedMethods: redirect -Style/FormatStringToken: - EnforcedStyle: unannotated - # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. @@ -841,6 +770,22 @@ Style/HashConversion: - 'spec/controllers/admin/inventory_items_controller_spec.rb' - 'spec/controllers/admin/variant_overrides_controller_spec.rb' +# Offense count: 13 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedReceivers. +# AllowedReceivers: Thread.current +Style/HashEachMethods: + Exclude: + - 'app/controllers/admin/enterprises_controller.rb' + - 'app/controllers/spree/admin/shipping_methods_controller.rb' + - 'app/forms/enterprise_fees_bulk_update.rb' + - 'app/models/product_import/entry_processor.rb' + - 'app/models/spree/preferences/configuration.rb' + - 'app/services/sets/model_set.rb' + - 'lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb' + - 'spec/models/product_importer_spec.rb' + - 'spec/support/cancan_helper.rb' + # Offense count: 1 # Configuration parameters: MinBranchesCount. Style/HashLikeCase: @@ -915,17 +860,22 @@ Style/PreferredHashMethods: Exclude: - 'app/controllers/api/v0/shipments_controller.rb' -# Offense count: 3 +# Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Methods. Style/RedundantArgument: Exclude: - 'engines/dfc_provider/app/services/authorization_control.rb' - - 'spec/support/query_counter.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantAssignment: + Exclude: + - 'spec/models/database_spec.rb' # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: AllowComments. +# Configuration parameters: AutoCorrect, AllowComments. Style/RedundantInitialize: Exclude: - 'spec/models/spree/gateway_spec.rb' @@ -937,6 +887,12 @@ Style/RedundantInterpolation: - 'lib/tasks/karma.rake' - 'spec/base_spec_helper.rb' +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +Style/RedundantLineContinuation: + Exclude: + - 'lib/reporting/reports/enterprise_fee_summary/scope.rb' + # Offense count: 19 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, AllowedPatterns. @@ -988,7 +944,7 @@ Style/SlicingWithRange: - 'engines/order_management/app/services/order_management/subscriptions/validator.rb' - 'lib/discourse/single_sign_on.rb' -# Offense count: 28 +# Offense count: 25 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: @@ -1010,6 +966,5 @@ Style/StringConcatenation: - 'spec/models/spree/line_item_spec.rb' - 'spec/models/spree/product_spec.rb' - 'spec/services/embedded_page_service_spec.rb' - - 'spec/support/api_helper.rb' - 'spec/support/features/datepicker_helper.rb' - 'spec/system/admin/products_spec.rb' diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 65b8cca69d..461aa86f53 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -6,28 +6,9 @@ This is a general guide to setting up an Open Food Network **development environ Head to our wiki on [Learning Rails](https://github.com/openfoodfoundation/openfoodnetwork/wiki/Learning-Rails) to find some good starting points. -### Requirements - -The fastest way to make it work locally is to use Docker, you only need to setup git, see the [Docker setup guide](docker/README.md). -Otherwise, for a local setup you will need: -* Ruby and bundler (check current Ruby version in [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file) - - To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/) -* Node and yarn (check current Node version in [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) file) - - [nodevn](https://github.com/nodenv/nodenv) is recommended. -* PostgreSQL database -* Redis (for background jobs) -* Chrome (for testing) - -The following guides will provide OS-specific step-by-step instructions to get these requirements installed: -- [Ubuntu Setup Guide][ubuntu] -- [Debian Setup Guide][debian] -- [OSX Setup Guide][osx] - -For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html). - ### Get it -So you have set up your local environment according to the requirements listed above. If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine: +If you're planning on contributing code to the project (which we [LOVE](CONTRIBUTING.md)), it is a good idea to begin by forking this repo using the `Fork` button in the top-right corner of this screen. You should then be able to use `git clone` to copy your fork onto your local machine: git clone git@github.com:YOUR_GITHUB_USERNAME_HERE/openfoodnetwork.git @@ -43,6 +24,27 @@ Fetch the latest version of `master` from `upstream` (ie. the main repo): git fetch upstream master +### Installation + +This project needs specific ruby/bundler versions as well as node/yarn specific versions. For a local setup you will need: + +* Install or change your Ruby version according to the one specified at [.ruby-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.ruby-version) file. + - To manage versions, it's recommended to use [rbenv](https://github.com/rbenv/rbenv). +* Install [nodenv](https://github.com/nodenv/nodenv) to ensure the correct [.node-version](https://github.com/openfoodfoundation/openfoodnetwork/blob/master/.node-version) is used. + - [nodevn](https://github.com/nodenv/nodenv) is recommended as a node version manager. +* PostgreSQL database +* Redis (for background jobs) +* Chrome (for testing) + +The following guides will provide OS-specific step-by-step instructions to get these requirements installed: +- [Ubuntu Setup Guide][ubuntu] +- [Debian Setup Guide][debian] +- [OSX Setup Guide][osx] + +For those new to Rails, the following tutorial will help get you up to speed with configuring a [Rails environment](http://guides.rubyonrails.org/getting_started.html). + +Another way to make it work locally would be using Docker. See the [Docker setup guide](docker/README.md). + ### Get it running First, you need to create the database user the app will use by manually typing the following in your terminal: @@ -53,7 +55,8 @@ sudo --login --user=postgres psql -c "CREATE USER ofn WITH SUPERUSER CREATEDB PA This will create the "ofn" user as superuser and allowing it to create databases. If this command fails, check the [troubleshooting section](#creating-the-database) for an alternative. -Next, it is _strongly recommended_ to run the setup script. +Next, it is _strongly recommended_ to run the setup script: + ```sh ./script/setup ``` diff --git a/Gemfile b/Gemfile index 72e893263e..d4eea771b0 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" } ruby File.read('.ruby-version').chomp -gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems +gem 'dotenv', require: 'dotenv/load' # Load ENV vars before other gems gem 'rails' @@ -17,7 +17,7 @@ gem "image_processing" gem 'activemerchant', '>= 1.78.0' gem 'angular-rails-templates', '>= 0.3.0' gem 'awesome_nested_set' -gem 'ransack', '~> 2.6.0' +gem 'ransack', '~> 4.1.0' gem 'responders' gem 'rexml' gem 'webpacker', '~> 5' @@ -74,7 +74,7 @@ gem 'rswag-ui' gem 'omniauth_openid_connect' gem 'omniauth-rails_csrf_protection' -gem 'openid_connect', '~> 1.3' +gem 'openid_connect' gem 'angularjs-rails', '1.8.0' gem 'bugsnag' @@ -93,14 +93,13 @@ gem 'bootsnap', require: false gem 'geocoder' gem 'gmaps4rails' gem 'mimemagic', '> 0.3.5' -gem 'paper_trail', '~> 12.1' +gem 'paper_trail' gem 'rack-rewrite' gem 'rack-timeout' gem 'roadie-rails' -gem 'hiredis' gem 'puma' -gem 'redis', '>= 4.0', require: ['redis', 'redis/connection/hiredis'] +gem 'redis' gem 'sidekiq' gem 'sidekiq-scheduler' @@ -143,6 +142,8 @@ gem "private_address_check" gem 'newrelic_rpm' +gem 'invisible_captcha' + group :production, :staging do gem 'sd_notify' # For better Systemd process management. Used by Puma. end @@ -160,6 +161,7 @@ group :test, :development do gem 'letter_opener', '>= 1.4.1' gem 'rspec-rails', ">= 3.5.2" gem 'rspec-retry', require: false + gem 'rspec-sql' gem 'rswag' gem 'shoulda-matchers' gem 'stimulus_reflex_testing' @@ -185,8 +187,10 @@ group :development do gem 'rails-erd' gem 'rubocop' gem 'rubocop-rails' + gem 'rubocop-rspec' gem 'spring' gem 'spring-commands-rspec' + gem 'spring-commands-rubocop' gem 'web-console' gem 'rack-mini-profiler', '< 3.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3f2e9073f9..d2ecf1af63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -90,7 +90,7 @@ GEM rails-html-sanitizer (~> 1.1, >= 1.2.0) active_model_serializers (0.8.4) activemodel (>= 3.0) - active_storage_validations (1.1.3) + active_storage_validations (1.1.4) activejob (>= 5.2.0) activemodel (>= 5.2.0) activestorage (>= 5.2.0) @@ -98,17 +98,18 @@ GEM activejob (7.0.8) activesupport (= 7.0.8) globalid (>= 0.3.6) - activemerchant (1.123.0) + activemerchant (1.133.0) activesupport (>= 4.2) builder (>= 2.1.2, < 4.0.0) i18n (>= 0.6.9) nokogiri (~> 1.4) + rexml (~> 3.2.5) activemodel (7.0.8) activesupport (= 7.0.8) activerecord (7.0.8) activemodel (= 7.0.8) activesupport (= 7.0.8) - activerecord-import (1.5.1) + activerecord-import (1.6.0) activerecord (>= 4.2) activerecord-postgresql-adapter (0.0.1) pg @@ -135,7 +136,7 @@ GEM activerecord (>= 6.1, < 7.2) acts_as_list (1.0.4) activerecord (>= 4.2) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) afm (0.2.2) @@ -151,21 +152,21 @@ GEM arel-helpers (2.14.0) activerecord (>= 3.1.0, < 8) ast (2.4.2) - attr_required (1.0.1) + attr_required (1.0.2) awesome_nested_set (3.6.0) activerecord (>= 4.0.0, < 7.2) aws-eventstream (1.3.0) - aws-partitions (1.860.0) - aws-sdk-core (3.189.0) + aws-partitions (1.899.0) + aws-sdk-core (3.191.4) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.74.0) - aws-sdk-core (~> 3, >= 3.188.0) + aws-sdk-kms (1.78.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.141.0) - aws-sdk-core (~> 3, >= 3.189.0) + aws-sdk-s3 (1.146.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) @@ -174,14 +175,14 @@ GEM bcp47_spec (0.2.1) bcrypt (3.1.19) bigdecimal (3.0.2) - bindata (2.4.15) + bindata (2.5.0) bindex (0.8.1) - bootsnap (1.17.0) + bootsnap (1.18.3) msgpack (~> 1.2) - bugsnag (6.26.0) + bugsnag (6.26.3) concurrent-ruby (~> 1.0) builder (3.2.4) - bullet (7.1.4) + bullet (7.1.6) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) cable_ready (5.0.1) @@ -191,11 +192,11 @@ GEM railties (>= 5.2) thread-local (>= 1.1.0) cancancan (1.15.0) - capybara (3.39.2) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) @@ -216,12 +217,13 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - combine_pdf (1.0.24) + combine_pdf (1.0.26) matrix ruby-rc4 (>= 0.1.5) - concurrent-ruby (1.2.2) + concurrent-ruby (1.2.3) connection_pool (2.4.1) - crack (0.4.5) + crack (1.0.0) + bigdecimal rexml crass (1.0.6) css_parser (1.16.0) @@ -235,12 +237,12 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - datafoodconsortium-connector (1.0.0.pre.alpha.9) + datafoodconsortium-connector (1.0.0.pre.alpha.10) virtual_assembly-semantizer (~> 1.0, >= 1.0.5) - date (3.3.3) - debug (1.8.0) - irb (>= 1.5.0) - reline (>= 0.3.1) + date (3.3.4) + debug (1.9.1) + irb (~> 1.10) + reline (>= 0.3.8) debugger-linecache (1.2.0) devise (4.9.3) bcrypt (~> 3.0) @@ -254,13 +256,12 @@ GEM devise (>= 4.9.0) devise-token_authenticatable (1.1.0) devise (>= 4.0.0, < 5.0.0) - diff-lcs (1.5.0) + diff-lcs (1.5.1) digest (3.1.1) docile (1.4.0) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) + dotenv (3.1.0) + email_validator (2.2.4) + activemodel erubi (1.12.0) et-orbi (1.2.7) tzinfo @@ -271,20 +272,19 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faraday (2.7.12) - base64 - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) faraday-follow_redirects (0.3.0) faraday (>= 1, < 3) - faraday-net_http (3.0.2) + faraday-net_http (3.1.0) + net-http ferrum (0.14) addressable (~> 2.5) concurrent-ruby (~> 1.1) webrick (~> 1.7) websocket-driver (>= 0.6, < 0.8) ffaker (2.23.0) - ffi (1.15.5) + ffi (1.16.3) flipper (0.26.2) concurrent-ruby (< 2) flipper-active_record (0.26.2) @@ -326,17 +326,16 @@ GEM good_migrations (0.2.1) activerecord (>= 3.1) railties (>= 3.1) - haml (5.2.2) - temple (>= 0.8.0) + haml (6.3.0) + temple (>= 0.8.2) + thor tilt - hashdiff (1.0.1) + hashdiff (1.1.0) hashery (2.1.2) hashie (5.0.0) highline (2.0.3) - hiredis (0.6.3) htmlentities (4.3.4) - httpclient (2.8.3) - i18n (1.14.1) + i18n (1.14.4) concurrent-ruby (~> 1.0) i18n-js (3.9.2) i18n (>= 0.6.6) @@ -345,10 +344,13 @@ GEM ruby-vips (>= 2.0.17, < 3) immigrant (0.3.6) activerecord (>= 3.0) - io-console (0.6.0) + invisible_captcha (2.2.0) + rails (>= 5.2) + io-console (0.7.1) ipaddress (0.8.3) - irb (1.6.4) - reline (>= 0.3.0) + irb (1.11.0) + rdoc + reline (>= 0.3.8) jmespath (1.6.2) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) @@ -357,16 +359,17 @@ GEM jquery-ui-rails (4.2.1) railties (>= 3.2.16) json (2.7.1) - json-canonicalization (0.3.2) - json-jwt (1.16.3) + json-canonicalization (1.0.0) + json-jwt (1.16.6) activesupport (>= 4.2) aes_key_wrap + base64 bindata faraday (~> 2.0) faraday-follow_redirects - json-ld (3.3.0) + json-ld (3.3.1) htmlentities (~> 4.3) - json-canonicalization (~> 0.3, >= 0.3.2) + json-canonicalization (~> 1.0) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) rack (>= 2.2, < 4) @@ -378,16 +381,17 @@ GEM rspec (>= 2.0, < 4.0) jsonapi-serializer (2.2.0) activesupport (>= 4.2) - jwt (2.7.1) - knapsack_pro (6.0.1) + jwt (2.8.1) + base64 + knapsack_pro (6.0.4) rake language_server-protocol (3.17.0.3) - launchy (2.5.0) - addressable (~> 2.7) - letter_opener (1.8.1) + launchy (2.5.2) + addressable (~> 2.8) + letter_opener (1.9.0) launchy (>= 2.2, < 3) link_header (0.0.8) - listen (3.8.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) loofah (2.22.0) @@ -401,36 +405,37 @@ GEM marcel (1.0.2) matrix (0.4.2) method_source (1.0.0) - mime-types (3.5.1) + mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0808) + mime-types-data (3.2023.1205) mimemagic (0.4.3) nokogiri (~> 1) rake mini_magick (4.11.0) mini_mime (1.1.5) mini_portile2 (2.8.5) - minitest (5.20.0) - monetize (1.12.0) + minitest (5.22.3) + monetize (1.13.0) money (~> 6.12) money (6.16.0) i18n (>= 0.6.4, <= 2) msgpack (1.7.2) multi_json (1.15.0) multi_xml (0.6.0) - net-imap (0.4.2) + net-http (0.4.1) + uri + net-imap (0.4.10) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.4.0) + net-smtp (0.4.0.1) net-protocol - newrelic_rpm (9.6.0) - base64 - nio4r (2.5.9) - nokogiri (1.15.5) + newrelic_rpm (9.7.1) + nio4r (2.7.0) + nokogiri (1.16.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) oauth2 (1.4.11) @@ -439,37 +444,39 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 4) - omniauth (2.1.1) + omniauth (2.1.2) hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection omniauth-rails_csrf_protection (1.0.1) actionpack (>= 4.2) omniauth (~> 2.0) - omniauth_openid_connect (0.6.1) + omniauth_openid_connect (0.7.1) omniauth (>= 1.9, < 3) - openid_connect (~> 1.1) - openid_connect (1.4.2) + openid_connect (~> 2.2) + openid_connect (2.3.0) activemodel attr_required (>= 1.0.0) - json-jwt (>= 1.15.0) - net-smtp - rack-oauth2 (~> 1.21) - swd (~> 1.3) + email_validator + faraday (~> 2.0) + faraday-follow_redirects + json-jwt (>= 1.16) + mail + rack-oauth2 (~> 2.2) + swd (~> 2.0) tzinfo - validate_email validate_url - webfinger (~> 1.2) + webfinger (~> 2.0) orm_adapter (0.5.0) pagy (5.10.1) activesupport - paper_trail (12.3.0) - activerecord (>= 5.2) - request_store (~> 1.1) - parallel (1.23.0) + paper_trail (15.1.0) + activerecord (>= 6.1) + request_store (~> 1.4) + parallel (1.24.0) paranoia (2.6.3) activerecord (>= 5.1, < 7.2) - parser (3.2.2.4) + parser (3.3.0.5) ast (~> 2.4.1) racc paypal-sdk-core (0.3.4) @@ -477,7 +484,7 @@ GEM xml-simple paypal-sdk-merchant (1.117.2) paypal-sdk-core (~> 0.3.0) - pdf-reader (2.11.0) + pdf-reader (2.12.0) Ascii85 (~> 1.0) afm (~> 0.2.1) hashery (~> 2.0) @@ -488,25 +495,29 @@ GEM pry (0.13.1) coderay (~> 1.1) method_source (~> 1.0) + psych (5.1.2) + stringio public_suffix (5.0.4) - puma (6.4.0) + puma (6.4.2) nio4r (~> 2.0) query_count (1.1.1) activerecord (>= 4.2) railties (>= 4.2) raabro (1.4.0) racc (1.7.3) - rack (2.2.8) + rack (2.2.8.1) rack-mini-profiler (2.3.4) rack (>= 1.2.0) - rack-oauth2 (1.21.3) + rack-oauth2 (2.2.1) activesupport attr_required - httpclient + faraday (~> 2.0) + faraday-follow_redirects json-jwt (>= 1.11.0) rack (>= 2.1.0) - rack-protection (3.0.5) - rack + rack-protection (3.2.0) + base64 (>= 0.1.0) + rack (~> 2.2, >= 2.2.4) rack-proxy (0.7.6) rack rack-rewrite (1.5.1) @@ -543,7 +554,7 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - rails-i18n (7.0.8) + rails-i18n (7.0.9) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) rails_safe_tasks (1.0.0) @@ -556,9 +567,9 @@ GEM zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.1.0) - ransack (2.6.0) - activerecord (>= 6.0.4) - activesupport (>= 6.0.4) + ransack (4.1.1) + activerecord (>= 6.1.5) + activesupport (>= 6.1.5) i18n rb-fsevent (0.11.2) rb-inotify (0.10.1) @@ -566,12 +577,15 @@ GEM rdf (3.3.1) bcp47_spec (~> 0.2) link_header (~> 0.0, >= 0.0.8) + rdoc (6.6.2) + psych (>= 4.0.0) redcarpet (3.6.0) - redis (4.8.1) - redis-client (0.18.0) + redis (5.1.0) + redis-client (>= 0.17.0) + redis-client (0.20.0) connection_pool - regexp_parser (2.8.2) - reline (0.3.3) + regexp_parser (2.9.0) + reline (0.4.1) io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) @@ -588,32 +602,35 @@ GEM rodf (1.2.0) builder (>= 3.0) rubyzip (>= 1.0) - roo (2.10.0) + roo (2.10.1) nokogiri (~> 1) rubyzip (>= 1.3.0, < 3.0.0) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.1.0) + rspec-support (~> 3.13.0) + rspec-rails (6.1.2) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) rspec-retry (0.6.2) rspec-core (> 3.3) - rspec-support (3.12.1) + rspec-sql (0.0.1) + activesupport + rspec + rspec-support (3.13.1) rswag (2.13.0) rswag-api (= 2.13.0) rswag-specs (= 2.13.0) @@ -629,31 +646,38 @@ GEM rswag-ui (2.13.0) actionpack (>= 3.1, < 7.2) railties (>= 3.1, < 7.2) - rubocop (1.58.0) + rubocop (1.62.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.4) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) - rubocop-rails (2.22.2) + rubocop-ast (1.31.2) + parser (>= 3.3.0.4) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-rails (2.24.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rspec (2.27.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) ruby-graphviz (1.2.5) rexml ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) ruby-vips (2.1.4) ffi (~> 1.12) - ruby2_keywords (0.0.5) rubyzip (2.3.2) rufus-scheduler (3.8.2) fugit (~> 1.1, >= 1.1.6) @@ -669,13 +693,13 @@ GEM tilt (>= 1.1, < 3) sd_notify (0.1.1) semantic_range (3.0.0) - shoulda-matchers (5.3.0) + shoulda-matchers (6.2.0) activesupport (>= 5.2.0) - sidekiq (7.2.0) + sidekiq (7.2.2) concurrent-ruby (< 2) connection_pool (>= 2.3.0) rack (>= 2.2.4) - redis-client (>= 0.14.0) + redis-client (>= 0.19.0) sidekiq-scheduler (5.0.3) rufus-scheduler (~> 3.2) sidekiq (>= 6, < 8) @@ -692,6 +716,8 @@ GEM spring (4.1.3) spring-commands-rspec (1.0.4) spring (>= 0.9.1) + spring-commands-rubocop (0.4.0) + spring (>= 1.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -719,35 +745,35 @@ GEM stimulus_reflex_testing (0.3.0) stimulus_reflex (>= 3.3.0) stringex (2.8.6) - stripe (10.2.0) - swd (1.3.0) + stringio (3.1.0) + stripe (10.12.0) + swd (2.0.3) activesupport (>= 3) attr_required (>= 0.0.5) - httpclient (>= 2.4) + faraday (~> 2.0) + faraday-follow_redirects temple (0.8.2) - thor (1.3.0) + thor (1.3.1) thread-local (1.1.0) tilt (2.3.0) timecop (0.9.8) - timeout (0.4.0) + timeout (0.4.1) ttfunk (1.7.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) uniform_notifier (1.16.0) - valid_email2 (5.1.1) + uri (0.13.0) + valid_email2 (5.2.1) activemodel (>= 3.2) mail (~> 2.5) - validate_email (0.1.6) - activemodel (>= 3.0) - mail (>= 2.2.5) validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix validates_lengths_from_database (0.8.0) activerecord (>= 4) vcr (6.2.0) - view_component (3.8.0) + view_component (3.11.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) @@ -764,10 +790,11 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webfinger (1.2.0) + webfinger (2.1.3) activesupport - httpclient (>= 2.4) - webmock (3.18.1) + faraday (~> 2.0) + faraday-follow_redirects + webmock (3.23.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -788,7 +815,7 @@ GEM xml-simple (1.1.8) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.12) + zeitwerk (2.6.13) PLATFORMS ruby @@ -832,7 +859,7 @@ DEPENDENCIES devise-token_authenticatable dfc_provider! digest - dotenv-rails + dotenv factory_bot_rails (= 6.2.0) faraday ffaker @@ -847,11 +874,11 @@ DEPENDENCIES good_migrations haml highline (= 2.0.3) - hiredis i18n i18n-js (~> 3.9.0) image_processing immigrant + invisible_captcha jquery-rails (= 4.4.0) jquery-ui-rails (~> 4.2) json @@ -869,10 +896,10 @@ DEPENDENCIES oauth2 (~> 1.4.7) omniauth-rails_csrf_protection omniauth_openid_connect - openid_connect (~> 1.3) + openid_connect order_management! pagy (~> 5.1) - paper_trail (~> 12.1) + paper_trail paranoia (~> 2.4) paypal-sdk-merchant (= 1.117.2) pdf-reader @@ -889,20 +916,22 @@ DEPENDENCIES rails-erd rails-i18n rails_safe_tasks (~> 1.0) - ransack (~> 2.6.0) + ransack (~> 4.1.0) redcarpet - redis (>= 4.0) + redis responders rexml roadie-rails roo rspec-rails (>= 3.5.2) rspec-retry + rspec-sql rswag rswag-api rswag-ui rubocop rubocop-rails + rubocop-rspec sd_notify select2-rails! shoulda-matchers @@ -912,6 +941,7 @@ DEPENDENCIES spreadsheet_architect spring spring-commands-rspec + spring-commands-rubocop state_machines-activerecord stimulus_reflex (= 3.5.0.rc3) stimulus_reflex_testing diff --git a/README.md b/README.md index 41d0633c31..bd855e097d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ We also have a [Super Admin Guide][super-admin-guide] to help with configuration ## Testing -If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite] and download the [ZenHub browser extension][zenhub] to view the development pipeline. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara). +If you'd like to help out with testing, please introduce yourself on the #testing channel on [Slack][slack-invite]. Also, do have a look in our [Welcome New QAs board][welcome-qa] for some good first issues, both on manual and automated testing (RSpec/Capybara). We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. BrowserStack provides open source projects with unlimited and free of charge accounts. A big thanks to them! @@ -44,7 +44,7 @@ We use [KnapsackPro](https://knapsackpro.com/) for optimal parallelisation of ou ## Licence -Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence. +Copyright (c) 2012 - 2024 Open Food Foundation, released under the AGPL licence. [survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit# [slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A @@ -53,4 +53,3 @@ Copyright (c) 2012 - 2022 Open Food Foundation, released under the AGPL licence. [super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide [welcome-dev]: https://github.com/orgs/openfoodfoundation/projects/5 [welcome-qa]: https://github.com/orgs/openfoodfoundation/projects/6 -[zenhub]: https://www.zenhub.com/extension diff --git a/app/assets/javascripts/admin/payments/services/payment.js.coffee b/app/assets/javascripts/admin/payments/services/payment.js.coffee index 487dafcd03..ea40e12f96 100644 --- a/app/assets/javascripts/admin/payments/services/payment.js.coffee +++ b/app/assets/javascripts/admin/payments/services/payment.js.coffee @@ -43,7 +43,8 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren submit: => munged = @preprocess() PaymentResource.create({order_id: munged.order_id}, munged, (response, headers, status) -> - document.body.innerHTML = Object.values(response).join('') + rawHtml = Object.values(response).join('').replace('[object Object]true', '') + document.body.innerHTML = rawHtml $window.history.pushState({}, '', "/admin/orders/" + munged.order_id + "/payments") , (response) -> StatusMessage.display 'error', t("spree.admin.payments.source_forms.stripe.error_saving_payment") diff --git a/app/assets/javascripts/admin/spree/base.js.erb b/app/assets/javascripts/admin/spree/base.js.erb index 8a2015d786..99aada8cb5 100644 --- a/app/assets/javascripts/admin/spree/base.js.erb +++ b/app/assets/javascripts/admin/spree/base.js.erb @@ -32,9 +32,6 @@ jQuery(function($) { }); } - // Make flash messages dissapear - setTimeout('$(".flash").fadeOut()', 5000); - // Highlight hovered table column $('table tbody tr td.actions a').hover(function(){ var tr = $(this).closest('tr'); diff --git a/app/assets/javascripts/admin/spree/orders/shipments.js.erb b/app/assets/javascripts/admin/spree/orders/shipments.js.erb index 322a648683..f28cbeeee8 100644 --- a/app/assets/javascripts/admin/spree/orders/shipments.js.erb +++ b/app/assets/javascripts/admin/spree/orders/shipments.js.erb @@ -1,22 +1,6 @@ // Shipments AJAX API $(document).ready(function() { - - handle_ship_click = function(){ - var link = $(this); - var shipment_number = link.data('shipment-number'); - var url = Spree.url( Spree.routes.orders_api + "/" + order_number + "/shipments/" + shipment_number + "/ship.json"); - $.ajax({ - type: "PUT", - url: url - }).done(function( msg ) { - window.location.reload(); - }).error(function( msg ) { - console.log(msg); - }); - } - $('.admin-order-edit-form a.ship').click(handle_ship_click); - //handle shipping method edit click $('a.edit-method').click(toggleMethodEdit); $('a.cancel-method').click(toggleMethodEdit); diff --git a/app/assets/javascripts/templates/active_selector.html.haml b/app/assets/javascripts/templates/active_selector.html.haml index 87fb066636..c7ddfc3577 100644 --- a/app/assets/javascripts/templates/active_selector.html.haml +++ b/app/assets/javascripts/templates/active_selector.html.haml @@ -1,3 +1,2 @@ -%li{ ng: { class: "{active: selector.active}" } } - %a{ "tooltip" => "{{selector.object.value}}", "tooltip-placement" => "bottom", - ng: { transclude: true, class: "{active: selector.active, 'has-tip': selector.object.value}" } } +%li{ "ng-class": "{active: selector.active}" } + %a{ tooltip: "{{selector.object.value}}", "tooltip-placement": "bottom", "ng-transclude": true, "ng-class": "{active: selector.active, 'has-tip': selector.object.value}" } diff --git a/app/assets/javascripts/templates/admin/alert_row.html.haml b/app/assets/javascripts/templates/admin/alert_row.html.haml index c1dfe45c6f..52df4db4e2 100644 --- a/app/assets/javascripts/templates/admin/alert_row.html.haml +++ b/app/assets/javascripts/templates/admin/alert_row.html.haml @@ -1,8 +1,8 @@ -.sixteen.columns.alpha.omega.alert-row{ ng: { show: '!dismissed' } } +.sixteen.columns.alpha.omega.alert-row{ "ng-show": '!dismissed' } .fifteen.columns.pad.alpha - %span.message.text-big{ ng: { bind: 'message'} } + %span.message.text-big{ "ng-bind": 'message' }     - %input{ type: 'button', ng: { value: "buttonText", show: 'buttonText && buttonAction', click: "buttonAction()" } } + %input{ type: 'button', "ng-value": "buttonText", "ng-show": 'buttonText && buttonAction', "ng-click": "buttonAction()" } .one.column.omega.pad.text-center - %a.close{ href: "#", ng: { click: "dismiss()" } } + %a.close{ href: "#", "ng-click": "dismiss()" } × diff --git a/app/assets/javascripts/templates/admin/columns_dropdown.html.haml b/app/assets/javascripts/templates/admin/columns_dropdown.html.haml index 7d6059de4d..61f42974db 100644 --- a/app/assets/javascripts/templates/admin/columns_dropdown.html.haml +++ b/app/assets/javascripts/templates/admin/columns_dropdown.html.haml @@ -1,13 +1,13 @@ -.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ ng: { controller: 'ColumnsDropdownCtrl' } } +.ofn-drop-down.ofn-drop-down-v2.right#columns-dropdown{ "ng-controller": 'ColumnsDropdownCtrl' } .ofn-drop-down-label = "  #{t('admin.columns')}".html_safe %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } .menu_items - .menu_item{ ng: { repeat: "column in columns", click: "toggle(column);" } } - %input.redesigned-input{ type: "checkbox", ng: { checked: "column.visible" } } + .menu_item{ "ng-repeat": "column in columns", "ng-click": "toggle(column);" } + %input.redesigned-input{ type: "checkbox", "ng-checked": "column.visible" } {{ column.name }} %hr %div.menu_item.text-center - %input.fullwidth.orange{ type: "button", ng: { value: "saved() ? 'Saved': 'Saving'", show: "saved() || saving", disabled: "saved()" } } - %input.fullwidth.red{ type: "button", :value => t('admin.column_save_as_default').html_safe, ng: { show: "!saved() && !saving", click: "saveColumnPreferences(action)"} } + %input.fullwidth.orange{ type: "button", "ng-value": "saved() ? 'Saved': 'Saving'", "ng-show": "saved() || saving", "ng-disabled": "saved()" } + %input.fullwidth.red{ type: "button", value: t('admin.column_save_as_default').html_safe, "ng-show": "!saved() && !saving", "ng-click": "saveColumnPreferences(action)" } diff --git a/app/assets/javascripts/templates/admin/confirm_dialog.html.haml b/app/assets/javascripts/templates/admin/confirm_dialog.html.haml index cfd6a011f8..36d29a522b 100644 --- a/app/assets/javascripts/templates/admin/confirm_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/confirm_dialog.html.haml @@ -1,8 +1,8 @@ -#confirm-dialog{ ng: { class: "dialog_class" } } +#confirm-dialog{ "ng-class": "dialog_class" } .message.clearfix.margin-bottom-30 .icon.text-center %i.icon-question-sign - .text{ ng: { bind: "::message" } } + .text{ "ng-bind": "::message" } .action-buttons.text-center - %button.cancel{ ng: { click: "close()", bind: "::cancelText" } } - %button.confirm.red{ ng: { click: "confirm()", bind: "::confirmText" } } + %button.cancel{ "ng-click": "close()", "ng-bind": "::cancelText" } + %button.confirm.red{ "ng-click": "confirm()", "ng-bind": "::confirmText" } diff --git a/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml b/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml index 4dd6f53aec..7a8ddd4216 100644 --- a/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/edit_address_dialog.html.haml @@ -1,12 +1,12 @@ #edit-address-dialog %h2 {{ addressType === 'bill_address' ? "#{t('admin.customers.index.edit_bill_address')}" : "#{t('admin.customers.index.edit_ship_address')}" }} - %form{ name: 'edit_address_form', novalidate: true, ng: { submit: 'updateAddress()'}} + %form{ name: 'edit_address_form', novalidate: true, "ng-submit": 'updateAddress()' } .row {{ 'admin.customers.index.required_fileds' | t }} ( %span.required * ) - .error{ ng: { repeat: "error in errors", bind: "error" } } + .error{ "ng-repeat": "error in errors", "ng-bind": "error" } %table.no-borders %tr @@ -14,61 +14,55 @@ {{ 'first_name' | t }} %span.required * %td - %input{ type: 'text', name: 'firstname', required: true, ng: { model: 'address.firstname'} } + %input{ type: 'text', name: 'firstname', required: true, "ng-model": 'address.firstname' } %tr %td {{ 'last_name' | t }} %span.required * %td - %input{ type: 'text', name: 'lastname', required: true, ng: { model: 'address.lastname'} } + %input{ type: 'text', name: 'lastname', required: true, "ng-model": 'address.lastname' } %tr %td {{ 'address' | t }} %span.required * %td - %input{ type: 'text', name: 'address1', required: true, ng: { model: 'address.address1'} } + %input{ type: 'text', name: 'address1', required: true, "ng-model": 'address.address1' } %tr %td {{ 'address2' | t }} %td - %input{ type: 'text', name: 'address2', ng: { model: 'address.address2'} } + %input{ type: 'text', name: 'address2', "ng-model": 'address.address2' } %tr %td {{ 'city' | t }} %span.required * %td - %input{ type: 'text', name: 'city', required: true, ng: { model: 'address.city'} } + %input{ type: 'text', name: 'city', required: true, "ng-model": 'address.city' } %tr %td {{ 'postcode' | t }} %span.required * %td - %input{ type: 'text', name: 'zipcode', required: true, ng: { model: 'address.zipcode'} } + %input{ type: 'text', name: 'zipcode', required: true, "ng-model": 'address.zipcode' } %tr %td {{ 'country' | t }} %span.required * %td - %input.ofn-select2.fullwidth#country_id{ type: 'number', - name: 'country_id', required: true, - placeholder: "{{ 'admin.customers.index.select_country' | t }}", - data: 'availableCountries', ng: { model: 'address.country_id' } } + %input.ofn-select2.fullwidth#country_id{ type: 'number', name: 'country_id', required: true, placeholder: "{{ 'admin.customers.index.select_country' | t }}", data: 'availableCountries', "ng-model": 'address.country_id' } %tr %td {{ 'state' | t }} %span.required * %td - %input.ofn-select2.fullwidth#state_id{ type: 'number', - name: 'state_id', required: true, - placeholder: "{{ 'admin.customers.index.select_state' | t }}", - data: 'states', ng: { model: 'address.state_id' } } + %input.ofn-select2.fullwidth#state_id{ type: 'number', name: 'state_id', required: true, placeholder: "{{ 'admin.customers.index.select_state' | t }}", data: 'states', "ng-model": 'address.state_id' } %tr %td {{ 'phone' | t }} %span.required * %td - %input{ type: 'text', name: 'phone', required: true, ng: { model: 'address.phone'} } + %input{ type: 'text', name: 'phone', required: true, "ng-model": 'address.phone' } .text-center %input.button.red.icon-plus{ type: 'submit', value: t('admin.customers.index.update_address')} diff --git a/app/assets/javascripts/templates/admin/info_dialog.html.haml b/app/assets/javascripts/templates/admin/info_dialog.html.haml index b69e3b0a4f..3e474c5501 100644 --- a/app/assets/javascripts/templates/admin/info_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/info_dialog.html.haml @@ -1,9 +1,9 @@ -#info-dialog{ ng: { class: "dialog_class" } } +#info-dialog{ "ng-class": "dialog_class" } .message.clearfix.margin-bottom-30 .icon.text-center - %i{ ng: { class: "icon_class" } } + %i{ "ng-class": "icon_class" } .text {{ message }} .action-buttons.text-center - %button{ ng: { click: "close()" } } + %button{ "ng-click": "close()" } = t(:ok) diff --git a/app/assets/javascripts/templates/admin/modals/bulk_invoice.html.haml b/app/assets/javascripts/templates/admin/modals/bulk_invoice.html.haml index 4c3fd55488..c2a026d19e 100644 --- a/app/assets/javascripts/templates/admin/modals/bulk_invoice.html.haml +++ b/app/assets/javascripts/templates/admin/modals/bulk_invoice.html.haml @@ -1,15 +1,15 @@ %h4.modal-title = t('js.admin.orders.index.compiling_invoices') -%p.message{ ng: { show: 'message' } } +%p.message{ "ng-show": 'message' } {{message}} -%p.error{ ng: { show: 'error' } } +%p.error{ "ng-show": 'error' } {{error}} -%img.spinner{ src: image_path("/spinning-circles.svg"), ng: { show: "loading" } } -%p{ ng: { show: "loading" } } +%img.spinner{ src: image_path("/spinning-circles.svg"), "ng-show": "loading" } +%p{ "ng-show": "loading" } = t('js.admin.orders.index.please_wait') -%a.button{ target: '_blank', ng: { click: 'showBulkInvoice()', href: '/admin/orders/invoices/{{invoice_id}}', show: "!loading && !error" } } +%a.button{ target: '_blank', "ng-click": 'showBulkInvoice()', "ng-href": '/admin/orders/invoices/{{invoice_id}}', "ng-show": "!loading && !error" } = t('js.admin.orders.index.view_file') diff --git a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml index 28d01d8ada..ebc198b178 100644 --- a/app/assets/javascripts/templates/admin/modals/image_upload.html.haml +++ b/app/assets/javascripts/templates/admin/modals/image_upload.html.haml @@ -1,10 +1,10 @@ %a.close-reveal-modal{"ng-click" => "$close()"} %i.fa.fa-times-circle{'aria-hidden' => "true"} -%form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, ng: { controller: "ProductImageCtrl" } } +%form#image_upload{ name: 'form', novalidate: true, enctype: 'multipart/form-data', multipart: true, "ng-controller": "ProductImageCtrl" } %div.image-preview - %img.spinner{ src: image_path("/spinning-circles.svg"), ng: { hide: "!imageUploader.isUploading" }} - %img.preview{ng: {src: "{{imagePreview}}", class: "{'faded': imageUploader.isUploading}"}} + %img.spinner{ src: image_path("/spinning-circles.svg"), "ng-hide": "!imageUploader.isUploading" } + %img.preview{ "ng-src": "{{imagePreview}}", "ng-class": "{'faded': imageUploader.isUploading}" } %label{for: 'image-upload', class: 'button'} {{ 'admin.products.index.upload_an_image' | t }} %input#image-upload{hidden: true, type: 'file', 'nv-file-select' => true, uploader: "imageUploader"} diff --git a/app/assets/javascripts/templates/admin/new_customer_dialog.html.haml b/app/assets/javascripts/templates/admin/new_customer_dialog.html.haml index 9cdcb97713..8914e21f08 100644 --- a/app/assets/javascripts/templates/admin/new_customer_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/new_customer_dialog.html.haml @@ -2,14 +2,14 @@ .text-normal.margin-bottom-30.text-center {{ 'js.admin.customers.index.add_a_new_customer_for' | t:{ shop_name: CurrentShop.shop.name } }} - %form{ name: 'new_customer_form', novalidate: true, ng: { submit: "addCustomer()" }} + %form{ name: 'new_customer_form', novalidate: true, "ng-submit": "addCustomer()" } .text-center.margin-bottom-30 - %input.fullwidth{ type: 'email', name: 'email', required: true, placeholder: "{{ 'js.admin.customers.index.customer_placeholder' | t }}", ng: { model: "email" } } - %div{ ng: { show: "submitted && new_customer_form.$pristine" } } - .error{ ng: { show: "(new_customer_form.email.$error.email || new_customer_form.email.$error.required)" } } + %input.fullwidth{ type: 'email', name: 'email', required: true, placeholder: "{{ 'js.admin.customers.index.customer_placeholder' | t }}", "ng-model": "email" } + %div{ "ng-show": "submitted && new_customer_form.$pristine" } + .error{ "ng-show": "(new_customer_form.email.$error.email || new_customer_form.email.$error.required)" } {{ 'js.admin.customers.index.valid_email_error' | t }} - .error{ ng: { repeat: "error in errors", bind: "error" } } + .error{ "ng-repeat": "error in errors", "ng-bind": "error" } .text-center %input.button.red.icon-plus{ type: 'submit', value: "{{ 'js.admin.customers.index.add_customer' | t }}" } diff --git a/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml index 50d61d3563..cc43116af1 100644 --- a/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml @@ -4,7 +4,7 @@ .text-center.margin-bottom-30 -# %select.fullwidth{ 'select2-min-search' => 5, 'ng-model' => 'newRuleType', 'ng-options' => 'ruleType.id as ruleType.name for ruleType in availableRuleTypes' } - %input.ofn-select2.fullwidth{ :id => 'rule_type_selector', ng: { model: "ruleType" }, data: "ruleTypes", 'min-search' => "5" } + %input.ofn-select2.fullwidth{ id: 'rule_type_selector', data: "ruleTypes", "min-search": "5", "ng-model": "ruleType" } .text-center - %input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", ng: { click: 'addRule(tagGroup, ruleType)' } } + %input.button.red.icon-plus{ type: 'button', value: "{{ 'js.admin.new_tag_rule_dialog.add_rule' | t }}", "ng-click": 'addRule(tagGroup, ruleType)' } diff --git a/app/assets/javascripts/templates/admin/order_cycles_selector.html.haml b/app/assets/javascripts/templates/admin/order_cycles_selector.html.haml index a0b9e7776c..e39cc7ca8b 100644 --- a/app/assets/javascripts/templates/admin/order_cycles_selector.html.haml +++ b/app/assets/javascripts/templates/admin/order_cycles_selector.html.haml @@ -3,16 +3,16 @@ %td#available-order-cycles {{ 'js.admin.order_cycles.schedules.available' | t }} .order-cycles - .order-cycle{ ng: { repeat: 'orderCycle in orderCycles | available:selectedOrderCycles as availableOrderCycles', click: 'selections.available = orderCycle', dblclick: 'add(orderCycle)', class: '{selected: selections.available == orderCycle}' } } + .order-cycle{ "ng-repeat": 'orderCycle in orderCycles | available:selectedOrderCycles as availableOrderCycles', "ng-click": 'selections.available = orderCycle', "ng-dblclick": 'add(orderCycle)', "ng-class": '{selected: selections.available == orderCycle}' } {{ orderCycle.name }} %td#add-remove-buttons - %a.add.button{ href: 'javascript:void(0)', ng: { click: 'add()' } } + %a.add.button{ href: 'javascript:void(0)', "ng-click": 'add()' } %i.icon-chevron-right - %a.remove.button{ href: 'javascript:void(0)', ng: { click: 'remove()' } } + %a.remove.button{ href: 'javascript:void(0)', "ng-click": 'remove()' } %i.icon-chevron-left %td#selected-order-cycles {{ 'js.admin.order_cycles.schedules.selected' | t }} .order-cycles - .order-cycle{ ng: { repeat: 'orderCycle in selectedOrderCycles', click: 'selections.selected = orderCycle', dblclick: 'remove(orderCycle)', class: '{selected: selections.selected == orderCycle}' } } + .order-cycle{ "ng-repeat": 'orderCycle in selectedOrderCycles', "ng-click": 'selections.selected = orderCycle', "ng-dblclick": 'remove(orderCycle)', "ng-class": '{selected: selections.selected == orderCycle}' } {{ orderCycle.name }} -.error{ ng: { repeat: "error in errors", bind: "error" } } +.error{ "ng-repeat": "error in errors", "ng-bind": "error" } diff --git a/app/assets/javascripts/templates/admin/panel.html.haml b/app/assets/javascripts/templates/admin/panel.html.haml index 4d3780aed0..54b9fa5c39 100644 --- a/app/assets/javascripts/templates/admin/panel.html.haml +++ b/app/assets/javascripts/templates/admin/panel.html.haml @@ -1,2 +1,2 @@ -%td{ colspan: "{{columnCount}}", ng: { if: "template" } } - .panel{ ng: { include: "template" } } +%td{ colspan: "{{columnCount}}", "ng-if": "template" } + .panel{ "ng-include": "template" } diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml index 4ef49a3bc0..0c3a737a90 100644 --- a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml +++ b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml @@ -1,7 +1,7 @@ -.row.enterprise_package_panel{ ng: { controller: 'indexPackagePanelCtrl' } } +.row.enterprise_package_panel{ "ng-controller": 'indexPackagePanelCtrl' } .alpha.eight.columns - %div{ ng: { if: "!enterprise.is_primary_producer", switch: "enterprise.sells" } } - .info{ ng: { switch: { when: "none" } } } + %div{ "ng-if": "!enterprise.is_primary_producer", "ng-switch": "enterprise.sells" } + .info{ "ng-switch-when": "none" } %h3 {{ 'js.admin.panels.enterprise_package.hub_profile' | t }} @@ -15,7 +15,7 @@ %p {{ 'js.admin.panels.enterprise_package.hub_profile_text2' | t }} - .info{ ng: { switch: { when: "any" } } } + .info{ "ng-switch-when": "any" } %h3 {{ 'js.admin.panels.enterprise_package.hub_shop' | t }} @@ -28,7 +28,7 @@ %p {{ 'js.admin.panels.enterprise_package.hub_shop_text3' | t }} - .info{ ng: { switch: { default: true } } } + .info{ "ng-switch-default": true } %h3 {{ 'js.admin.panels.enterprise_package.choose_package' | t }} %i.icon-arrow-right @@ -40,8 +40,8 @@ %p {{ 'js.admin.panels.enterprise_package.choose_package_text2' | t }} - %div{ ng: { if: "enterprise.is_primary_producer", switch: "enterprise.sells" } } - .info{ ng: { switch: { when: "none" } } } + %div{ "ng-if": "enterprise.is_primary_producer", "ng-switch": "enterprise.sells" } + .info{ "ng-switch-when": "none" } %h3 {{ 'js.admin.panels.enterprise_package.profile_only' | t }} @@ -58,7 +58,7 @@ %p {{ 'js.admin.panels.enterprise_package.profile_only_text3' | t }} - .info{ ng: { switch: { when: "own" } } } + .info{ "ng-switch-when": "own" } %h3 {{ 'js.admin.panels.enterprise_package.producer_shop' | t }} @@ -68,7 +68,7 @@ %p {{ 'js.admin.panels.enterprise_package.producer_shop_text2' | t }} - .info{ ng: { switch: { when: "any" } } } + .info{ "ng-switch-when": "any" } %h3 {{ 'js.admin.panels.enterprise_package.producer_hub' | t }} @@ -81,7 +81,7 @@ %p {{ 'js.admin.panels.enterprise_package.producer_hub_text3' | t }} - .info{ ng: { switch: { default: true } } } + .info{ "ng-switch-default": true } %h3 {{ 'js.admin.panels.enterprise_package.choose_package' | t }} %i.icon-arrow-right @@ -93,9 +93,9 @@ %p {{ 'js.admin.panels.enterprise_package.choose_package_text2' | t }} - .omega.eight.columns{ ng: { switch: "enterprise.is_primary_producer" } } - %div{ ng: { switch: { when: "false" } } } - %a.button.selector.hub-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + .omega.eight.columns{ "ng-switch": "enterprise.is_primary_producer" } + %div{ "ng-switch-when": "false" } + %a.button.selector.hub-profile{ "ng-click": "enterprise.owned && (enterprise.sells='none')", "ng-class": "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_package.profile_only' | t }} @@ -103,15 +103,15 @@ {{ 'js.admin.panels.enterprise_package.get_listing' | t }} .bottom {{ 'js.admin.panels.enterprise_package.always_free' | t }} - %a.button.selector.hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + %a.button.selector.hub{ "ng-click": "enterprise.owned && (enterprise.sells='any')", "ng-class": "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_package.hub_shop' | t }} %p {{ 'js.admin.panels.enterprise_package.sell_produce_others' | t }} - %div{ ng: { switch: { when: "true" } } } - %a.button.selector.producer-profile{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + %div{ "ng-switch-when": "true" } + %a.button.selector.producer-profile{ "ng-click": "enterprise.owned && (enterprise.sells='none')", "ng-class": "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_package.profile_only' | t }} @@ -119,27 +119,27 @@ {{ 'js.admin.panels.enterprise_package.get_listing' | t }} .bottom {{ 'js.admin.panels.enterprise_package.always_free' | t }} - %a.button.selector.producer-shop{ ng: { click: "enterprise.owned && (enterprise.sells='own')", class: "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } } + %a.button.selector.producer-shop{ "ng-click": "enterprise.owned && (enterprise.sells='own')", "ng-class": "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_package.producer_shop' | t }} %p {{ 'js.admin.panels.enterprise_package.sell_own_produce' | t }} - %a.button.selector.producer-hub{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + %a.button.selector.producer-hub{ "ng-click": "enterprise.owned && (enterprise.sells='any')", "ng-class": "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_package.producer_hub' | t }} %p {{ 'js.admin.panels.enterprise_package.sell_both' | t }} - %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } - %span{ ng: {hide: "saved() || saving" } } + %a.button.update.fullwidth{ "ng-show": "enterprise.owned", "ng-class": "{disabled: saved() && !saving, saving: saving}", "ng-click": "save()" } + %span{ "ng-hide": "saved() || saving" } {{ 'js.admin.panels.save' | t }} %i.icon-save - %span{ ng: {show: "saved() && !saving" } } + %span{ "ng-show": "saved() && !saving" } {{ 'js.admin.panels.saved' | t }} %i.icon-ok-sign - %span{ ng: {show: "saving" } } + %span{ "ng-show": "saving" } {{ 'js.admin.panels.saving' | t }} %i.icon-refresh diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml index b1f022420d..f85ecc2a09 100644 --- a/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml +++ b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml @@ -1,6 +1,6 @@ -.row.enterprise_producer_panel{ ng: { controller: 'indexProducerPanelCtrl' } } +.row.enterprise_producer_panel{ "ng-controller": 'indexProducerPanelCtrl' } .alpha.eight.columns - .info{ ng: { show: "enterprise.is_primary_producer==true" } } + .info{ "ng-show": "enterprise.is_primary_producer==true" } %h3 {{ 'js.admin.panels.enterprise_producer.producer' | t }} %p @@ -8,7 +8,7 @@ %p {{ 'js.admin.panels.enterprise_producer.producer_text2' | t }} - .info{ ng: { show: "enterprise.is_primary_producer==false" } } + .info{ "ng-show": "enterprise.is_primary_producer==false" } %h3 {{ 'js.admin.panels.enterprise_producer.non_producer' | t }} %p @@ -17,7 +17,7 @@ {{ 'js.admin.panels.enterprise_producer.non_producer_text2' | t }} .omega.eight.columns - %a.button.selector.producer{ ng: { click: 'enterprise.owned && changeToProducer()', class: "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } } + %a.button.selector.producer{ "ng-click": 'enterprise.owned && changeToProducer()', "ng-class": "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_producer.producer' | t }} @@ -26,7 +26,7 @@ .bottom {{ 'js.admin.panels.enterprise_producer.producer_example' | t }} - %a.button.selector.non-producer{ ng: { click: 'enterprise.owned && changeToNonProducer()', class: "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } } + %a.button.selector.non-producer{ "ng-click": 'enterprise.owned && changeToNonProducer()', "ng-class": "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } .top %h3 {{ 'js.admin.panels.enterprise_producer.non_producer' | t }} @@ -35,13 +35,13 @@ .bottom {{ 'js.admin.panels.enterprise_producer.non_producer_example' | t }} - %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } - %span{ ng: {hide: "saved() || saving" } } + %a.button.update.fullwidth{ "ng-show": "enterprise.owned", "ng-class": "{disabled: saved() && !saving, saving: saving}", "ng-click": "save()" } + %span{ "ng-hide": "saved() || saving" } {{ 'js.admin.panels.save' | t }} %i.icon-save - %span{ ng: {show: "saved() && !saving" } } + %span{ "ng-show": "saved() && !saving" } {{ 'js.admin.panels.saved' | t }} %i.icon-ok-sign - %span{ ng: {show: "saving" } } + %span{ "ng-show": "saving" } {{ 'js.admin.panels.saving' | t }} %i.icon-refresh diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml index d5022fd26e..3e0a2d396e 100644 --- a/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml +++ b/app/assets/javascripts/templates/admin/panels/enterprise_status.html.haml @@ -1,10 +1,10 @@ -.row.enterprise_status_panel{ ng: { controller: 'indexStatusPanelCtrl' } } +.row.enterprise_status_panel{ "ng-controller": 'indexStatusPanelCtrl' } .alpha.omega.sixteen.columns - %h4.status-ok.text-center{ ng: { show: "issues.length == 0 && warnings.length == 0" } } + %h4.status-ok.text-center{ "ng-show": "issues.length == 0 && warnings.length == 0" } %i.icon-ok-sign {{ 'js.admin.panels.enterprise_status.status_title' | t:{ name: object.name } }} - %table{ ng: { show: "issues.length > 0 || warnings.length > 0" } } + %table{ "ng-show": "issues.length > 0 || warnings.length > 0" } %thead %th.severity {{ 'js.admin.panels.enterprise_status.severity' | t }} @@ -12,17 +12,17 @@ {{ 'js.admin.panels.enterprise_status.description' | t }} %th.resolve {{ 'js.admin.panels.enterprise_status.resolve' | t }} - %tr{ ng: { repeat: "issue in issues"} } + %tr{ "ng-repeat": "issue in issues" } %td.severity %i.icon-warning-sign.issue %td.description - %span{ ng: { bind: "::issue.description" } } + %span{ "ng-bind": "::issue.description" } %td.resolve - %div{ ng: { bind: { html: "issue.link" } } } - %tr{ ng: { repeat: "warning in warnings"} } + %div{ "ng-bind-html": "issue.link" } + %tr{ "ng-repeat": "warning in warnings" } %td.severity %i.icon-warning-sign.warning %td.description - %span{ ng: { bind: "::warning.description" } } + %span{ "ng-bind": "::warning.description" } %td.resolve - %div{ ng: { bind: { html: "warning.link" } } } + %div{ "ng-bind-html": "warning.link" } diff --git a/app/assets/javascripts/templates/admin/save_bar.html.haml b/app/assets/javascripts/templates/admin/save_bar.html.haml index 08b6da5361..2a33901459 100644 --- a/app/assets/javascripts/templates/admin/save_bar.html.haml +++ b/app/assets/javascripts/templates/admin/save_bar.html.haml @@ -1,9 +1,9 @@ -#save-bar.animate-show{ ng: { show: 'dirty || persist || StatusMessage.active()' } } +#save-bar.animate-show{ "ng-show": 'dirty || persist || StatusMessage.active()' } .container .seven.columns.alpha - %h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } } + %h5#status-message{ "ng-show": "StatusMessage.invalidMessage == ''", "ng-style": 'StatusMessage.statusMessage.style' } {{ StatusMessage.statusMessage.text || " " }} - %h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #C85136' } + %h5#status-message{ style: 'color: #C85136', "ng-show": "StatusMessage.invalidMessage !== ''" } {{ StatusMessage.invalidMessage || " " }} - .nine.columns.omega.text-right{ ng: { transclude: true } } + .nine.columns.omega.text-right{ "ng-transclude": true } diff --git a/app/assets/javascripts/templates/admin/schedule_dialog.html.haml b/app/assets/javascripts/templates/admin/schedule_dialog.html.haml index 421cf25d42..928cf3da90 100644 --- a/app/assets/javascripts/templates/admin/schedule_dialog.html.haml +++ b/app/assets/javascripts/templates/admin/schedule_dialog.html.haml @@ -1,24 +1,24 @@ #schedule-dialog .text-normal.margin-bottom-30.text-center - %span{ ng: { hide: 'schedule.id' } } + %span{ "ng-hide": 'schedule.id' } {{ 'js.admin.order_cycles.schedules.adding_a_new_schedule' | t }} - %span{ ng: { show: 'schedule.id' } } + %span{ "ng-show": 'schedule.id' } {{ 'js.admin.order_cycles.schedules.updating_a_schedule' | t }} - %form{ name: 'schedule_form', novalidate: true, ng: { submit: "submit()" }} + %form{ name: 'schedule_form', novalidate: true, "ng-submit": "submit()" } .text-center.margin-bottom-20 - %input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: "{{ 'js.admin.order_cycles.schedules.schedule_name_placeholder' | t }}", ng: { model: "schedule.name" } } - %div{ ng: { show: "submitted && schedule_form.$pristine" } } - .error{ ng: { show: "(schedule_form.name.$error.required)" } } + %input.fullwidth{ type: 'text', name: 'name', required: true, placeholder: "{{ 'js.admin.order_cycles.schedules.schedule_name_placeholder' | t }}", "ng-model": "schedule.name" } + %div{ "ng-show": "submitted && schedule_form.$pristine" } + .error{ "ng-show": "(schedule_form.name.$error.required)" } {{ 'js.admin.order_cycles.schedules.name_required_error' | t }} .order-cycles-selector.text-center.margin-bottom-30 .text-center - %input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.create_schedule' | t }}", ng: { hide: 'schedule.id' } } - %input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.update_schedule' | t }}", ng: { show: 'schedule.id' } } - %span{ ng: { show: 'schedule.id' } } or - %input.button.red{ type: 'button', value: "{{ 'js.admin.order_cycles.schedules.delete_schedule' | t }}", ng: { show: 'schedule.id', click: 'delete()'} } - %input.button{ type: 'button', value: "{{ 'actions.cancel' | t }}", ng: { click: 'close()' } } + %input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.create_schedule' | t }}", "ng-hide": 'schedule.id' } + %input.button{ type: 'submit', value: "{{ 'js.admin.order_cycles.schedules.update_schedule' | t }}", "ng-show": 'schedule.id' } + %span{ "ng-show": 'schedule.id' } or + %input.button.red{ type: 'button', value: "{{ 'js.admin.order_cycles.schedules.delete_schedule' | t }}", "ng-show": 'schedule.id', "ng-click": 'delete()' } + %input.button{ type: 'button', value: "{{ 'actions.cancel' | t }}", "ng-click": 'close()' } diff --git a/app/assets/javascripts/templates/admin/tag.html.haml b/app/assets/javascripts/templates/admin/tag.html.haml index c4afcfa5ad..a9db76671a 100644 --- a/app/assets/javascripts/templates/admin/tag.html.haml +++ b/app/assets/javascripts/templates/admin/tag.html.haml @@ -1,8 +1,8 @@ .tag-template %div - %span.tag-with-rules{ ng: { if: "data.rules" }, "ofn-with-tip" => "{{ 'admin.tag_has_rules' | t:{num: data.rules} }}" } + %span.tag-with-rules{ "ofn-with-tip": "{{ 'admin.tag_has_rules' | t:{num: data.rules} }}", "ng-if": "data.rules" } {{$getDisplayText()}} - %span{ ng: { if: "!data.rules" } } + %span{ "ng-if": "!data.rules" } {{$getDisplayText()}} - %a.remove-button{ ng: {click: "$removeTag()"} } + %a.remove-button{ "ng-click": "$removeTag()" } ✖ diff --git a/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml b/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml index 2b9b3f3f2f..39b321128c 100644 --- a/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml +++ b/app/assets/javascripts/templates/admin/tag_autocomplete.html.haml @@ -1,11 +1,11 @@ .autocomplete-template - %span.tag-with-rules{ ng: { if: "data.rules" } } + %span.tag-with-rules{ "ng-if": "data.rules" } {{$getDisplayText()}} - %span.tag-with-rules{ ng: { if: "data.rules == 1" } } + %span.tag-with-rules{ "ng-if": "data.rules == 1" } — {{ 'admin.has_one_rule' | t }} - %span.tag-with-rules{ ng: { if: "data.rules > 1" } } + %span.tag-with-rules{ "ng-if": "data.rules > 1" } — {{ 'admin.has_n_rules' | t:{ num: data.rules } }} - %span{ ng: { if: "!data.rules" } } + %span{ "ng-if": "!data.rules" } {{$getDisplayText()}} diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order_input.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order_input.html.haml index abad41ea62..ac087096ad 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/discount_order_input.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order_input.html.haml @@ -1,7 +1,3 @@ %div - %input{ type: "number", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", - min: -100, - max: 100, - ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } + %input{ type: "number", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", min: -100, max: 100, "invert-number": true, "ng-model": "rule.calculator.preferred_flat_percent" } %span.text-normal % diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_order_cycles_input.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_order_cycles_input.html.haml index 87eddb331e..43458b40c6 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/filter_order_cycles_input.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_order_cycles_input.html.haml @@ -1,11 +1,5 @@ %div - %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", - ng: { model: "rule.preferred_matched_order_cycles_visibility", if: "!rule.is_default" }, - data: 'visibilityOptions', "min-search" => 5 } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", - ng: { value: "'hidden'", if: "rule.is_default" } } - %span.text-normal{ ng: { if: "rule.is_default" } } + %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_order_cycles_visibility", "ng-if": "!rule.is_default" } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_order_cycles_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_order_cycles_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" } + %span.text-normal{ "ng-if": "rule.is_default" } =t(:not_visible) diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_payment_methods_input.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_payment_methods_input.html.haml index 8151feb677..013e606987 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/filter_payment_methods_input.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_payment_methods_input.html.haml @@ -1,11 +1,5 @@ %div - %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", - ng: { model: "rule.preferred_matched_payment_methods_visibility", if: "!rule.is_default" }, - data: 'visibilityOptions', "min-search" => 5 } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", - ng: { value: "'hidden'", if: "rule.is_default" } } - %span.text-normal{ ng: { if: "rule.is_default" } } + %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_payment_methods_visibility", "ng-if": "!rule.is_default" } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_payment_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_payment_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" } + %span.text-normal{ "ng-if": "rule.is_default" } = t(:not_visible) diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_products_input.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_products_input.html.haml index b30901791a..302b5fb85b 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/filter_products_input.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_products_input.html.haml @@ -1,11 +1,5 @@ %div - %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", - ng: { model: "rule.preferred_matched_variants_visibility", if: "!rule.is_default" }, - data: 'visibilityOptions', "min-search" => 5 } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", - ng: { value: "'hidden'", if: "rule.is_default" } } - %span.text-normal{ ng: { if: "rule.is_default" } } + %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_variants_visibility", "ng-if": "!rule.is_default" } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_variants_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_variants_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" } + %span.text-normal{ "ng-if": "rule.is_default" } = t(:not_visible) diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods_input.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods_input.html.haml index 48cf29a732..ab6a5aeb80 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods_input.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods_input.html.haml @@ -1,12 +1,6 @@ %div - %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", - ng: { model: "rule.preferred_matched_shipping_methods_visibility", if: "!rule.is_default" }, - data: 'visibilityOptions', "min-search" => 5 } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", - ng: { value: "'hidden'", if: "rule.is_default" } } - %span.text-normal{ ng: { if: "rule.is_default" } } + %input.fullwidth.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", data: 'visibilityOptions', "min-search": 5, "ng-model": "rule.preferred_matched_shipping_methods_visibility", "ng-if": "!rule.is_default" } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", "ng-value": "'hidden'", "ng-if": "rule.is_default" } + %span.text-normal{ "ng-if": "rule.is_default" } = t(:not_visible) diff --git a/app/assets/javascripts/templates/admin/tag_rules/tag_rule.html.haml b/app/assets/javascripts/templates/admin/tag_rules/tag_rule.html.haml index 7907b9dfda..598eafe972 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/tag_rule.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/tag_rule.html.haml @@ -6,45 +6,27 @@ %col.actions{ width: "10%" } %tr %td - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", - ng: { value: "rule.id" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", "ng-value": "rule.id" } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", - ng: { value: "rule.type" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", "ng-value": "rule.type" } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_priority", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][priority]", - ng: { value: "tagGroup.startIndex + $index" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_priority", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][priority]", "ng-value": "tagGroup.startIndex + $index" } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_is_default", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][is_default]", - ng: { value: "rule.is_default" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_is_default", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][is_default]", "ng-value": "rule.is_default" } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", - ng: { value: "rule.preferred_customer_tags" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", "ng-value": "rule.preferred_customer_tags" } - %input{ type: "hidden", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_{{opt[rule.type].taggable}}_tags", - name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_{{opt[rule.type].taggable}}_tags]", - ng: { value: "opt[rule.type].tagListFor(rule)" } } + %input{ type: "hidden", id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_{{opt[rule.type].taggable}}_tags", name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_{{opt[rule.type].taggable}}_tags]", "ng-value": "opt[rule.type].tagListFor(rule)" } %span.text-normal {{ opt[rule.type].textTop }} %td %tags-with-translation{ object: "rule", max: 1, "tags-attr" => "{{opt[rule.type].tagsAttr}}", "tag-list-attr" => "{{opt[rule.type].tagListAttr}}" } %td.actions{ rowspan: 2 } - %a{ ng: { click: "deleteTagRule(tagGroup || defaultTagGroup, rule)" }, :class => "delete-tag-rule icon-trash no-text" } + %a{ class: "delete-tag-rule icon-trash no-text", "ng-click": "deleteTagRule(tagGroup || defaultTagGroup, rule)" } %tr %td %span.text-normal {{ opt[rule.type].textBottom }} %td - %div{ ng: { include: "opt[rule.type].inputTemplate"} } + %div{ "ng-include": "opt[rule.type].inputTemplate" } %hr diff --git a/app/assets/javascripts/templates/admin/tags_input.html.haml b/app/assets/javascripts/templates/admin/tags_input.html.haml index c652aea610..d74c4d7de0 100644 --- a/app/assets/javascripts/templates/admin/tags_input.html.haml +++ b/app/assets/javascripts/templates/admin/tags_input.html.haml @@ -1,10 +1,2 @@ -%tags-input{ template: 'admin/tag.html', - "placeholder" => t('admin.order_cycles.form.add_a_tag'), - ng: { model: 'object[tagsAttr]', class: "{'limit-reached': limitReached}"}, - on: { tag: { added: 'tagAdded($tag)', removed:'tagRemoved()' } } } - %auto-complete{ ng: { if: "findTags" }, source: "findTags({query: $query})", - template: "admin/tag_autocomplete.html", - "min-length" => "0", - "load-on-focus" => "true", - "load-on-empty" => "true", - "max-results-to-show" => "32"} +%tags-input{ template: 'admin/tag.html', placeholder: t('admin.order_cycles.form.add_a_tag'), "ng-model": 'object[tagsAttr]', "ng-class": "{'limit-reached': limitReached}", "on-tag-added": 'tagAdded($tag)', "on-tag-removed": 'tagRemoved()' } + %auto-complete{ source: "findTags({query: $query})", template: "admin/tag_autocomplete.html", "min-length": "0", "load-on-focus": "true", "load-on-empty": "true", "max-results-to-show": "32", "ng-if": "findTags" } diff --git a/app/assets/javascripts/templates/bulk_buy_modal.html.haml b/app/assets/javascripts/templates/bulk_buy_modal.html.haml index 0c4706e43c..7788285342 100644 --- a/app/assets/javascripts/templates/bulk_buy_modal.html.haml +++ b/app/assets/javascripts/templates/bulk_buy_modal.html.haml @@ -22,24 +22,22 @@ .variant-bulk-buy-quantity-label {{ "js.shopfront.bulk_buy_modal.min_quantity" | t }} %div - %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}> + %button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "add(-1)", "ng-disabled": "!canAdd(-1)" }> -# U+FF0D Fullwidth Hyphen-Minus - - %input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}", - ng: {model: "variant.line_item.quantity", max: "Infinity"}}> - %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}} + %input.bulk-buy.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", "ng-model": "variant.line_item.quantity", "ng-max": "Infinity" }> + %button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "add(1)", "ng-disabled": "!canAdd(1)" } -# U+FF0B Fullwidth Plus Sign + .columns.small-12.medium-6 .variant-bulk-buy-quantity-label {{ "js.shopfront.bulk_buy_modal.max_quantity" | t }} %div - %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(-1)", disabled: "!canAddMax(-1)"}}> + %button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "addMax(-1)", "ng-disabled": "!canAddMax(-1)" }> -# U+FF0D Fullwidth Hyphen-Minus - - %input.bulk-buy.variant-quantity{type: "number", min: "0", max: "{{ available() }}", - ng: {model: "variant.line_item.max_quantity", max: "Infinity"}}> - %button.bulk-buy-add.variant-quantity{type: "button", ng: {click: "addMax(1)", disabled: "!canAddMax(1)"}} + %input.bulk-buy.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", "ng-model": "variant.line_item.max_quantity", "ng-max": "Infinity" }> + %button.bulk-buy-add.variant-quantity{ type: "button", "ng-click": "addMax(1)", "ng-disabled": "!canAddMax(1)" } -# U+FF0B Fullwidth Plus Sign + diff --git a/app/assets/javascripts/templates/filter_selector.html.haml b/app/assets/javascripts/templates/filter_selector.html.haml index 742ace2a0e..81559e4004 100644 --- a/app/assets/javascripts/templates/filter_selector.html.haml +++ b/app/assets/javascripts/templates/filter_selector.html.haml @@ -1,3 +1,3 @@ %ul - %active-selector{ ng: { repeat: "selector in allSelectors", show: "ifDefined(selector.fits, true)" } } + %active-selector{ "ng-repeat": "selector in allSelectors", "ng-show": "ifDefined(selector.fits, true)" } %span{"ng-bind" => "::selector.object.name"} diff --git a/app/assets/javascripts/templates/help-modal.html.haml b/app/assets/javascripts/templates/help-modal.html.haml index f87abd8fd4..0d7c3ad1e6 100644 --- a/app/assets/javascripts/templates/help-modal.html.haml +++ b/app/assets/javascripts/templates/help-modal.html.haml @@ -5,5 +5,5 @@ .small-12.columns.text-center {{ helpText }} .row.text-center - %button.primary.small{ ng: { click: '$close()' } } + %button.primary.small{ "ng-click": '$close()' } = t(:ok) diff --git a/app/assets/javascripts/templates/partials/hub_details.html.haml b/app/assets/javascripts/templates/partials/hub_details.html.haml index 9ecaef9493..0f58547413 100644 --- a/app/assets/javascripts/templates/partials/hub_details.html.haml +++ b/app/assets/javascripts/templates/partials/hub_details.html.haml @@ -1,4 +1,4 @@ -.row.pad-top{ng: { if: 'enterprise.is_distributor' } } +.row.pad-top{ "ng-if": 'enterprise.is_distributor' } .cta-container.small-12.columns .row .small-4.columns @@ -14,7 +14,7 @@ {{'hubs_delivery' | t}} .row .columns.small-12 - %a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", + %a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop_panel", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", "ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}", "ng-click" => "$close()", "ofn-change-hub" => "enterprise"} diff --git a/app/assets/javascripts/templates/partials/producer_details.html.haml b/app/assets/javascripts/templates/partials/producer_details.html.haml index 0e4bf23513..814e55b241 100644 --- a/app/assets/javascripts/templates/partials/producer_details.html.haml +++ b/app/assets/javascripts/templates/partials/producer_details.html.haml @@ -12,7 +12,7 @@ .row .columns.small-12 %a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'", - "ng-href" => "{{::hub.path}}#/shop", "ofn-empties-cart" => "hub", + "ng-href" => "{{::hub.path}}#/shop_panel", "ofn-empties-cart" => "hub", "ng-class" => "::{primary: hub.active, secondary: !hub.active}", "ng-click" => "$close()", "ofn-change-hub" => "hub"} diff --git a/app/assets/javascripts/templates/price_breakdown.html.haml b/app/assets/javascripts/templates/price_breakdown.html.haml index 78d4ed7f33..6c3cf06029 100644 --- a/app/assets/javascripts/templates/price_breakdown.html.haml +++ b/app/assets/javascripts/templates/price_breakdown.html.haml @@ -1,6 +1,6 @@ -.joyride-tip-guide.price_breakdown{ng: {class: "{ in: tt_isOpen, fade: tt_animation }", show: "tt_isOpen"}} +.joyride-tip-guide.price_breakdown{ "ng-class": "{ in: tt_isOpen, fade: tt_animation }", "ng-show": "tt_isOpen" } %span.joyride-nub.top - .background{ng: {click: "tt_isOpen = false"}} + .background{ "ng-click": "tt_isOpen = false" } .joyride-content-wrapper %h6 {{ "js.shopfront.price_breakdown" | t }} %ul diff --git a/app/assets/javascripts/templates/shared/question_mark_with_tooltip.html.haml b/app/assets/javascripts/templates/shared/question_mark_with_tooltip.html.haml index 9c68ca47c4..0ec169915b 100644 --- a/app/assets/javascripts/templates/shared/question_mark_with_tooltip.html.haml +++ b/app/assets/javascripts/templates/shared/question_mark_with_tooltip.html.haml @@ -1,5 +1,5 @@ -.joyride-tip-guide.question-mark-tooltip{class: "{{ context }}", ng: {class: "{ in: tt_isOpen, fade: tt_animation }", show: "tt_isOpen"}} - .background{ng: {click: "tt_isOpen = false"}} +.joyride-tip-guide.question-mark-tooltip{ class: "{{ context }}", "ng-class": "{ in: tt_isOpen, fade: tt_animation }", "ng-show": "tt_isOpen" } + .background{ "ng-click": "tt_isOpen = false" } .joyride-content-wrapper {{ key | t }} %span.joyride-nub.bottom diff --git a/app/components/confirm_modal_component.rb b/app/components/confirm_modal_component.rb index ccd0c67797..8149a729e3 100644 --- a/app/components/confirm_modal_component.rb +++ b/app/components/confirm_modal_component.rb @@ -1,14 +1,29 @@ # frozen_string_literal: true class ConfirmModalComponent < ModalComponent - def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil, - confirm_reflexes: nil) + # @param actions_alignment_class [String] possible classes: 'justify-space-around', 'justify-end' + def initialize( + id:, + reflex: nil, + controller: nil, + message: nil, + confirm_actions: nil, + confirm_reflexes: nil, + confirm_button_class: :primary, + confirm_button_text: I18n.t('js.admin.modals.confirm'), + cancel_button_text: I18n.t('js.admin.modals.cancel'), + actions_alignment_class: 'justify-space-around' + ) super(id:, close_button: true) @confirm_actions = confirm_actions @reflex = reflex @confirm_reflexes = confirm_reflexes @controller = controller @message = message + @confirm_button_class = confirm_button_class + @confirm_button_text = confirm_button_text + @cancel_button_text = cancel_button_text + @actions_alignment_class = actions_alignment_class end private diff --git a/app/components/confirm_modal_component/confirm_modal_component.html.haml b/app/components/confirm_modal_component/confirm_modal_component.html.haml index 1b3e1ff217..1184602a67 100644 --- a/app/components/confirm_modal_component/confirm_modal_component.html.haml +++ b/app/components/confirm_modal_component/confirm_modal_component.html.haml @@ -1,10 +1,10 @@ %div{ id: @id, "data-controller": "modal #{@controller}", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-#{@controller}-reflex-value": @reflex } .reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" } - .reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" } + .reveal-modal.fade.tiny.modal-component{ "data-modal-target": "modal" } = content = render @message if @message - .modal-actions - %input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.cancel'), "data-action": "click->modal#close" } - %input{ class: "button icon-plus primary", type: 'button', value: t('js.admin.modals.confirm'), "data-action": @confirm_actions, "data-reflex": @confirm_reflexes } + %div{ class: "modal-actions #{@actions_alignment_class}" } + %input{ class: "button icon-plus #{close_button_class}", type: 'button', value: @cancel_button_text, "data-action": "click->modal#close" } + %input{ id: 'modal-confirm-button', class: "button icon-plus #{@confirm_button_class}", type: 'button', value: @confirm_button_text, "data-action": @confirm_actions, "data-reflex": @confirm_reflexes } diff --git a/app/components/confirm_modal_component/confirm_modal_component.scss b/app/components/confirm_modal_component/confirm_modal_component.scss deleted file mode 100644 index ccbf18169c..0000000000 --- a/app/components/confirm_modal_component/confirm_modal_component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.modal-actions { - display: flex; - justify-content: space-around; -} diff --git a/app/components/help_modal_component/help_modal_component.html.haml b/app/components/help_modal_component/help_modal_component.html.haml index af7d602724..adc6c54822 100644 --- a/app/components/help_modal_component/help_modal_component.html.haml +++ b/app/components/help_modal_component/help_modal_component.html.haml @@ -1,6 +1,6 @@ %div{ id: @id, "data-controller": "help-modal", "data-action": "keyup@document->help-modal#closeIfEscapeKey" } .reveal-modal-bg.fade{ "data-help-modal-target": "background", "data-action": "click->help-modal#close" } - .reveal-modal.fade.small.help-modal{ "data-help-modal-target": "modal" } + .reveal-modal.fade.small.modal-component{ "data-help-modal-target": "modal" } = content - if close_button? diff --git a/app/components/help_modal_component/help_modal_component.scss b/app/components/help_modal_component/help_modal_component.scss deleted file mode 100644 index 48f6255a2e..0000000000 --- a/app/components/help_modal_component/help_modal_component.scss +++ /dev/null @@ -1,10 +0,0 @@ -.help-modal { - visibility: visible; - position: fixed; - top: 3em; -} - -/* prevent arrow on selected admin menu item appearing above modal */ -body.modal-open #admin-menu li.selected a::after { - z-index: 0; -} diff --git a/app/components/modal_component.rb b/app/components/modal_component.rb index 70226295c7..1de8852a8c 100644 --- a/app/components/modal_component.rb +++ b/app/components/modal_component.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true class ModalComponent < ViewComponent::Base - def initialize(id:, close_button: true) + def initialize(id:, close_button: true, instant: false, modal_class: :small) @id = id @close_button = close_button + @instant = instant + @modal_class = modal_class end private diff --git a/app/components/modal_component/modal_component.html.haml b/app/components/modal_component/modal_component.html.haml new file mode 100644 index 0000000000..7be73deca4 --- /dev/null +++ b/app/components/modal_component/modal_component.html.haml @@ -0,0 +1,8 @@ +%div{ id: @id, "data-controller": "modal", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-modal-instant-value": @instant } + .reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" } + .reveal-modal.fade.modal-component{ "data-modal-target": "modal", class: @modal_class } + = content + + - if close_button? + .text-center + %input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.close'), "data-action": "click->modal#close" } diff --git a/app/components/modal_component/modal_component.scss b/app/components/modal_component/modal_component.scss new file mode 100644 index 0000000000..72c0e74088 --- /dev/null +++ b/app/components/modal_component/modal_component.scss @@ -0,0 +1,56 @@ +// class name 'modal' is already taken by 'custom-alert' and 'custom-confirm'. +.modal-component { + visibility: visible; + position: fixed; + top: 3em; + min-height: auto; // reset from reveal-modal + + &.in { + padding: 1.2rem; + } + + h1, + h2, + h3, + h4, + h5, + h6, + p { + margin-bottom: 0.5em; + } + + img { + // Ensure image fits in container + max-width: 100%; + height: auto; + } +} + +/* prevent arrow on selected admin menu item appearing above modal */ +body.modal-open #admin-menu li.selected a::after { + z-index: 0; +} + +.modal-actions { + display: flex; + text-align: center; // Ensure text inside fullwidth buttons are centred on small screens + + &.justify-space-around { + justify-content: space-around; + } + + &.justify-end { + justify-content: flex-end; + input[type="button"] { + margin: 0 5px; + } + + @media only screen and (max-width: 1024px) { + flex-direction: column; + justify-content: space-around; + input[type="button"] { + margin: 5px 0; + } + } + } +} diff --git a/app/components/ship_order_component.html.haml b/app/components/ship_order_component.html.haml new file mode 100644 index 0000000000..be05f354e1 --- /dev/null +++ b/app/components/ship_order_component.html.haml @@ -0,0 +1,8 @@ += render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do + %div{class: "margin-bottom-30"} + %p= t('spree.admin.orders.shipment.mark_as_shipped_message_html') + %div{class: "margin-bottom-30"} + = hidden_field_tag :id, @order.id + = label_tag do + = check_box_tag :send_shipment_email, "1", true + = t('spree.admin.orders.shipment.mark_as_shipped_label_message') diff --git a/app/components/ship_order_component.rb b/app/components/ship_order_component.rb new file mode 100644 index 0000000000..89f500afcb --- /dev/null +++ b/app/components/ship_order_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ShipOrderComponent < ViewComponent::Base + def initialize(order:) + @order = order + end +end diff --git a/app/components/vertical_ellipsis_menu/component.scss b/app/components/vertical_ellipsis_menu/component.scss index 9434a94bc4..dd698ee331 100644 --- a/app/components/vertical_ellipsis_menu/component.scss +++ b/app/components/vertical_ellipsis_menu/component.scss @@ -1,16 +1,13 @@ .vertical-ellipsis-menu { position: relative; - width: $btn-relaxed-height; i.fa-ellipsis-v { cursor: pointer; display: block; - height: $btn-relaxed-height; - width: $btn-relaxed-height; - line-height: $btn-relaxed-height; text-align: center; border-radius: 3px; background-color: white; + padding: 9px 14px; } .vertical-ellipsis-menu-content { @@ -20,7 +17,7 @@ padding-top: 5px; padding-bottom: 5px; background-color: white; - @include defaultBoxShadow; + box-shadow: $box-shadow; border-radius: 3px; min-width: 80px; display: none; diff --git a/app/controllers/admin/customers_controller.rb b/app/controllers/admin/customers_controller.rb index 9751e9c6bb..ff5c43ab63 100644 --- a/app/controllers/admin/customers_controller.rb +++ b/app/controllers/admin/customers_controller.rb @@ -70,7 +70,7 @@ module Admin def collection if json_request? && params[:enterprise_id].present? - CustomersWithBalance.new(customers).query. + CustomersWithBalanceQuery.new(customers).call. includes( :enterprise, { bill_address: [:state, :country] }, diff --git a/app/controllers/admin/dfc_product_imports_controller.rb b/app/controllers/admin/dfc_product_imports_controller.rb new file mode 100644 index 0000000000..1b3f8dfb88 --- /dev/null +++ b/app/controllers/admin/dfc_product_imports_controller.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "private_address_check" +require "private_address_check/tcpsocket_ext" + +module Admin + class DfcProductImportsController < Spree::Admin::BaseController + # Define model class for `can?` permissions: + def model_class + self.class + end + + def index + # The plan: + # + # * Fetch DFC catalog as JSON from URL. + enterprise = OpenFoodNetwork::Permissions.new(spree_current_user) + .managed_product_enterprises.is_primary_producer + .find(params.require(:enterprise_id)) + + catalog_url = params.require(:catalog_url) + + json_catalog = DfcRequest.new(spree_current_user).get(catalog_url) + graph = DfcIo.import(json_catalog) + + # * First step: import all products for given enterprise. + # * Second step: render table and let user decide which ones to import. + imported = graph.map do |subject| + import_product(subject, enterprise) + end + + @count = imported.compact.count + end + + private + + # Most of this code is the same as in the DfcProvider::SuppliedProductsController. + def import_product(subject, enterprise) + return unless subject.is_a? DataFoodConsortium::Connector::SuppliedProduct + + variant = SuppliedProductBuilder.import_variant(subject, enterprise) + product = variant.product + + product.save! if product.new_record? + variant.save! if variant.new_record? + + variant + end + end +end diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 011795c8da..ebe62dc10b 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -38,7 +38,8 @@ module Admin @enterprise_fee_set = EnterpriseFeesBulkUpdate.new(params) if @enterprise_fee_set.save - redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice) + flash[:success] = I18n.t(:enterprise_fees_update_notice) + redirect_to redirect_path else redirect_to redirect_path, flash: { error: @enterprise_fee_set.errors.full_messages.to_sentence } diff --git a/app/controllers/admin/enterprise_roles_controller.rb b/app/controllers/admin/enterprise_roles_controller.rb index 79a52d8b41..4c0aeffc86 100644 --- a/app/controllers/admin/enterprise_roles_controller.rb +++ b/app/controllers/admin/enterprise_roles_controller.rb @@ -3,9 +3,8 @@ module Admin class EnterpriseRolesController < Admin::ResourceController def index - @enterprise_roles = EnterpriseRole.by_user_email - @users = Spree::User.order('spree_users.email') - @my_enterprises = @all_enterprises = Enterprise.by_name + @enterprise_roles, @users, @all_enterprises = Admin::EnterpriseRolesQuery.query + @my_enterprises = @all_enterprises end def create diff --git a/app/controllers/admin/oidc_settings_controller.rb b/app/controllers/admin/oidc_settings_controller.rb index f668442bc0..69ce9dbfc9 100644 --- a/app/controllers/admin/oidc_settings_controller.rb +++ b/app/controllers/admin/oidc_settings_controller.rb @@ -2,6 +2,13 @@ module Admin class OidcSettingsController < Spree::Admin::BaseController - def index; end + def index + @account = spree_current_user.oidc_account + end + + def destroy + spree_current_user.oidc_account&.destroy + redirect_to admin_oidc_settings_path + end end end diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 2e1258eaa0..35333e7af7 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -45,7 +45,8 @@ module Admin end def create - @order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user) + @order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params, + spree_current_user) if @order_cycle_form.save flash[:success] = t('.success') @@ -61,7 +62,8 @@ module Admin end def update - @order_cycle_form = OrderCycleForm.new(@order_cycle, order_cycle_params, spree_current_user) + @order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params, + spree_current_user) if @order_cycle_form.save update_nil_subscription_line_items_price_estimate(@order_cycle) @@ -98,7 +100,7 @@ module Admin def update_nil_subscription_line_items_price_estimate(order_cycle) order_cycle.schedules.each do |schedule| - Subscription.where(schedule_id: schedule.id).each do |subscription| + Subscription.where(schedule_id: schedule.id).find_each do |subscription| shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id) fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle) subscription.subscription_line_items.nil_price_estimate.each do |line_item| diff --git a/app/controllers/admin/product_import_controller.rb b/app/controllers/admin/product_import_controller.rb index 181870f3b2..c71e3ad015 100644 --- a/app/controllers/admin/product_import_controller.rb +++ b/app/controllers/admin/product_import_controller.rb @@ -10,6 +10,8 @@ module Admin @product_categories = Spree::Taxon.order('name ASC').pluck(:name).uniq @tax_categories = Spree::TaxCategory.order('name ASC').pluck(:name) @shipping_categories = Spree::ShippingCategory.order('name ASC').pluck(:name) + @producers = OpenFoodNetwork::Permissions.new(spree_current_user). + managed_product_enterprises.is_primary_producer.by_name.to_a end def import diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 8a972a859a..14dbad7dd8 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -21,18 +21,10 @@ module Admin def show @report = report_class.new(spree_current_user, params, render: render_data?) + @rendering_options = rendering_options # also stores user preferences - @background_reports = OpenFoodNetwork::FeatureToggle - .enabled?(:background_reports, spree_current_user) - - if @background_reports && request.post? - rendering_options # stores user preferences - - return background(report_format) - end - - if params[:report_format].present? - export_report + if render_data? + render_in_background else show_report end @@ -40,13 +32,8 @@ module Admin private - def export_report - send_data @report.render_as(report_format), filename: report_filename - end - def show_report assign_view_data - @table = @report.render_as(:html) if render_data? render "show" end @@ -55,7 +42,6 @@ module Admin @report_subtypes = report_subtypes @report_subtype = report_subtype @report_title = report_title - @rendering_options = rendering_options @data = Reporting::FrontendData.new(spree_current_user) variant_id_in = params[:variant_id_in]&.compact_blank @@ -72,7 +58,7 @@ module Admin request.post? end - def background(format) + def render_in_background cable_ready[ScopedChannel.for_id(params[:uuid])] .inner_html( selector: "#report-table", @@ -84,7 +70,8 @@ module Admin ReportJob.perform_later( report_class:, user: spree_current_user, params:, - format:, filename: report_filename, + format: report_format, + filename: report_filename, channel: ScopedChannel.for_id(params[:uuid]), ) diff --git a/app/controllers/admin/schedules_controller.rb b/app/controllers/admin/schedules_controller.rb index 3165667312..46854d254d 100644 --- a/app/controllers/admin/schedules_controller.rb +++ b/app/controllers/admin/schedules_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'open_food_network/permissions' -require 'order_management/subscriptions/proxy_order_syncer' module Admin class SchedulesController < Admin::ResourceController diff --git a/app/controllers/api/v0/enterprise_attachment_controller.rb b/app/controllers/api/v0/enterprise_attachment_controller.rb index b46f29b355..cf467aad98 100644 --- a/app/controllers/api/v0/enterprise_attachment_controller.rb +++ b/app/controllers/api/v0/enterprise_attachment_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'api/admin/enterprise_serializer' - module Api module V0 class EnterpriseAttachmentController < Api::V0::BaseController diff --git a/app/controllers/api/v0/order_cycles_controller.rb b/app/controllers/api/v0/order_cycles_controller.rb index 289b0260e4..2c3c0b2318 100644 --- a/app/controllers/api/v0/order_cycles_controller.rb +++ b/app/controllers/api/v0/order_cycles_controller.rb @@ -101,7 +101,8 @@ module Api end def distributed_products - OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation + OrderCycles::DistributedProductsService.new(distributor, order_cycle, + customer).products_relation end end end diff --git a/app/controllers/api/v0/orders_controller.rb b/app/controllers/api/v0/orders_controller.rb index 8570ac3db5..2ffb4304b3 100644 --- a/app/controllers/api/v0/orders_controller.rb +++ b/app/controllers/api/v0/orders_controller.rb @@ -47,7 +47,7 @@ module Api def capture authorize! :admin, order - payment_capture = OrderCaptureService.new(order) + payment_capture = Orders::CaptureService.new(order) if payment_capture.call render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok diff --git a/app/controllers/api/v0/shipments_controller.rb b/app/controllers/api/v0/shipments_controller.rb index 05376a2666..4d3d469597 100644 --- a/app/controllers/api/v0/shipments_controller.rb +++ b/app/controllers/api/v0/shipments_controller.rb @@ -21,7 +21,9 @@ module Api @shipment.refresh_rates @shipment.save! - OrderWorkflow.new(@order).advance_to_payment if @order.line_items.any? + Orders::WorkflowService.new(@order).advance_to_payment if @order.line_items.any? + + @order.recreate_all_fees! render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok end @@ -83,6 +85,8 @@ module Api @order.contents.remove(variant, quantity, @shipment, restock_item) @shipment.reload if @shipment.persisted? + @order.recreate_all_fees! + render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok end diff --git a/app/controllers/api/v1/customers_controller.rb b/app/controllers/api/v1/customers_controller.rb index 3f488251e7..0fe756efc5 100644 --- a/app/controllers/api/v1/customers_controller.rb +++ b/app/controllers/api/v1/customers_controller.rb @@ -59,7 +59,7 @@ module Api def customer @customer ||= if action_name == "show" - CustomersWithBalance.new(Customer.where(id: params[:id])).query.first! + CustomersWithBalanceQuery.new(Customer.where(id: params[:id])).call.first! else Customer.find(params[:id]) end @@ -74,7 +74,7 @@ module Api customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id] if @extra_customer_fields.include?(:balance) - customers = CustomersWithBalance.new(customers).query + customers = CustomersWithBalanceQuery.new(customers).call end customers.ransack(params[:q]).result.order(:id) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 3c0254e0e4..efd8e80eeb 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -128,7 +128,7 @@ class CheckoutController < BaseController def advance_order_state return if @order.complete? - OrderWorkflow.new(@order).advance_checkout(raw_params.slice(:shipping_method_id)) + Orders::WorkflowService.new(@order).advance_checkout(raw_params.slice(:shipping_method_id)) end def order_params diff --git a/app/controllers/concerns/checkout_callbacks.rb b/app/controllers/concerns/checkout_callbacks.rb index 7da81a592b..c7eb90ebde 100644 --- a/app/controllers/concerns/checkout_callbacks.rb +++ b/app/controllers/concerns/checkout_callbacks.rb @@ -65,7 +65,7 @@ module CheckoutCallbacks def valid_order_line_items? @order.insufficient_stock_lines.empty? && - OrderCycleDistributedVariants.new(@order.order_cycle, @order.distributor). + OrderCycles::DistributedVariantsService.new(@order.order_cycle, @order.distributor). distributes_order_variants?(@order) end diff --git a/app/controllers/concerns/order_completion.rb b/app/controllers/concerns/order_completion.rb index 5e3d2c198c..58bf7a03b9 100644 --- a/app/controllers/concerns/order_completion.rb +++ b/app/controllers/concerns/order_completion.rb @@ -64,7 +64,7 @@ module OrderCompletion return redirect_to order_failed_route(step: 'payment') end - if OrderWorkflow.new(@order).next && @order.complete? + if Orders::WorkflowService.new(@order).next && @order.complete? processing_succeeded redirect_to order_completion_route else diff --git a/app/controllers/concerns/order_stock_check.rb b/app/controllers/concerns/order_stock_check.rb index c979948686..13ff145ae8 100644 --- a/app/controllers/concerns/order_stock_check.rb +++ b/app/controllers/concerns/order_stock_check.rb @@ -6,7 +6,7 @@ module OrderStockCheck def valid_order_line_items? @order.insufficient_stock_lines.empty? && - OrderCycleDistributedVariants.new(@order.order_cycle, @order.distributor). + OrderCycles::DistributedVariantsService.new(@order.order_cycle, @order.distributor). distributes_order_variants?(@order) end diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 1043fdb4c6..bc16d12f18 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -70,7 +70,7 @@ class EnterprisesController < BaseController order = current_order(true) # reset_distributor must be called before any call to current_customer or current_distributor - order_cart_reset = OrderCartReset.new(order, params[:id]) + order_cart_reset = Orders::CartResetService.new(order, params[:id]) order_cart_reset.reset_distributor order_cart_reset.reset_other!(spree_current_user, current_customer) rescue ActiveRecord::RecordNotFound diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b3db1607ef..c483d6f4b7 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -2,7 +2,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController def openid_connect - spree_current_user.link_from_omniauth(request.env["omniauth.auth"]) + OidcAccount.link(spree_current_user, request.env["omniauth.auth"]) redirect_to admin_oidc_settings_path end diff --git a/app/controllers/payment_gateways/stripe_controller.rb b/app/controllers/payment_gateways/stripe_controller.rb index cabf14b4bf..8364e8e376 100644 --- a/app/controllers/payment_gateways/stripe_controller.rb +++ b/app/controllers/payment_gateways/stripe_controller.rb @@ -79,7 +79,7 @@ module PaymentGateways end def last_payment - @last_payment ||= OrderPaymentFinder.new(@order).last_payment + @last_payment ||= Orders::FindPaymentService.new(@order).last_payment end def cancel_incomplete_payments diff --git a/app/controllers/spree/admin/base_controller.rb b/app/controllers/spree/admin/base_controller.rb index e542b797be..f926612ef0 100644 --- a/app/controllers/spree/admin/base_controller.rb +++ b/app/controllers/spree/admin/base_controller.rb @@ -9,6 +9,7 @@ module Spree helper 'admin/injection' helper 'admin/orders' helper 'admin/enterprises' + helper 'admin/terms_of_service' helper 'enterprise_fees' helper 'angular_form' @@ -25,7 +26,7 @@ module Spree def warn_invalid_order_cycles return if flash[:notice].present? - warning = OrderCycleWarning.new(spree_current_user).call + warning = OrderCycles::WarningService.new(spree_current_user).call flash[:notice] = warning if warning.present? end diff --git a/app/controllers/spree/admin/images_controller.rb b/app/controllers/spree/admin/images_controller.rb index 4fed1aba31..0e0890a229 100644 --- a/app/controllers/spree/admin/images_controller.rb +++ b/app/controllers/spree/admin/images_controller.rb @@ -32,10 +32,13 @@ module Spree if @object.save flash[:success] = flash_message_for(@object, :successfully_created) - redirect_to spree.admin_product_images_url(params[:product_id], @url_filters) + redirect_to location_after_save else respond_with(@object) end + rescue ActiveStorage::IntegrityError + @object.errors.add :attachment, :integrity_error + respond_with(@object) end def update @@ -44,10 +47,13 @@ module Spree if @object.update(permitted_resource_params) flash[:success] = flash_message_for(@object, :successfully_updated) - redirect_to spree.admin_product_images_url(params[:product_id], @url_filters) + redirect_to location_after_save else respond_with(@object) end + rescue ActiveStorage::IntegrityError + @object.errors.add :attachment, :integrity_error + respond_with(@object) end def destroy @@ -58,7 +64,7 @@ module Spree flash[:success] = flash_message_for(@object, :successfully_removed) end - redirect_to spree.admin_product_images_url(params[:product_id], @url_filters) + redirect_to location_after_save end private @@ -76,7 +82,7 @@ module Spree end def location_after_save - spree.admin_product_images_url(@product) + params[:return_url] || spree.admin_product_images_url(params[:product_id], @url_filters) end def load_data diff --git a/app/controllers/spree/admin/invoices_controller.rb b/app/controllers/spree/admin/invoices_controller.rb index 81f79a299e..8fa92475a3 100644 --- a/app/controllers/spree/admin/invoices_controller.rb +++ b/app/controllers/spree/admin/invoices_controller.rb @@ -20,7 +20,7 @@ module Spree def generate @order = Order.find_by(number: params[:order_id]) authorize! :invoice, @order - OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice + ::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice redirect_back(fallback_location: spree.admin_dashboard_path) end diff --git a/app/controllers/spree/admin/orders/customer_details_controller.rb b/app/controllers/spree/admin/orders/customer_details_controller.rb index 7f93651e48..1f67f790df 100644 --- a/app/controllers/spree/admin/orders/customer_details_controller.rb +++ b/app/controllers/spree/admin/orders/customer_details_controller.rb @@ -24,7 +24,7 @@ module Spree end refresh_shipment_rates - OrderWorkflow.new(@order).advance_to_payment + ::Orders::WorkflowService.new(@order).advance_to_payment flash[:success] = Spree.t('customer_details_updated') redirect_to spree.admin_order_customer_path(@order) diff --git a/app/controllers/spree/admin/orders_controller.rb b/app/controllers/spree/admin/orders_controller.rb index 4e2b729dd6..a73adf58c4 100644 --- a/app/controllers/spree/admin/orders_controller.rb +++ b/app/controllers/spree/admin/orders_controller.rb @@ -50,7 +50,7 @@ module Spree return redirect_to spree.edit_admin_order_path(@order) end - OrderWorkflow.new(@order).advance_to_payment + ::Orders::WorkflowService.new(@order).advance_to_payment if @order.complete? redirect_to spree.edit_admin_order_path(@order) @@ -104,7 +104,7 @@ module Spree @order = if params[:invoice_id].present? @order.invoices.find(params[:invoice_id]).presenter else - OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice + ::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice @order.invoices.first.presenter end end diff --git a/app/controllers/spree/admin/payment_methods_controller.rb b/app/controllers/spree/admin/payment_methods_controller.rb index 37c8529f3d..204de6936f 100644 --- a/app/controllers/spree/admin/payment_methods_controller.rb +++ b/app/controllers/spree/admin/payment_methods_controller.rb @@ -193,7 +193,9 @@ module Spree def clear_preference_cache @payment_method.calculator.preferences.each_key do |key| - Rails.cache.delete(@payment_method.calculator.preference_cache_key(key)) + # Only persisted models have cache keys. + cache_key = @payment_method.calculator.preference_cache_key(key) + Rails.cache.delete(cache_key) if cache_key end end diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index 40dcd40adc..0afaa5227c 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -33,7 +33,7 @@ module Spree return end - OrderWorkflow.new(@order).complete! unless @order.completed? + ::Orders::WorkflowService.new(@order).complete! unless @order.completed? authorize_stripe_sca_payment @payment.process_offline! @@ -59,6 +59,8 @@ module Spree flash[:error] = t(:cannot_perform_operation) end rescue StandardError => e + logger.error e.message + Bugsnag.notify(e) flash[:error] = e.message ensure redirect_to request.referer diff --git a/app/controllers/spree/admin/products_controller.rb b/app/controllers/spree/admin/products_controller.rb index 3ecc96dab6..e153faff38 100644 --- a/app/controllers/spree/admin/products_controller.rb +++ b/app/controllers/spree/admin/products_controller.rb @@ -82,8 +82,9 @@ module Spree def clone @new = @product.duplicate - @new.save + raise "Clone failed" unless @new.save + flash[:success] = t('.success') redirect_to spree.admin_products_url end diff --git a/app/controllers/spree/admin/taxons_controller.rb b/app/controllers/spree/admin/taxons_controller.rb index b586866110..9efc97ed79 100644 --- a/app/controllers/spree/admin/taxons_controller.rb +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -117,8 +117,8 @@ module Spree def taxon_params params.require(:taxon).permit( - :name, :parent_id, :position, :icon, :description, :permalink, - :taxonomy_id, :meta_description, :meta_keywords, :meta_title + :name, :parent_id, :position, :icon, :description, :permalink, :taxonomy_id, + :meta_description, :meta_keywords, :meta_title, :dfc_id ) end end diff --git a/app/controllers/spree/orders_controller.rb b/app/controllers/spree/orders_controller.rb index 113f827628..f1055785c6 100644 --- a/app/controllers/spree/orders_controller.rb +++ b/app/controllers/spree/orders_controller.rb @@ -42,7 +42,7 @@ module Spree # Patching to redirect to shop if order is empty def edit @insufficient_stock_lines = @order.insufficient_stock_lines - @unavailable_order_variants = OrderCycleDistributedVariants. + @unavailable_order_variants = OrderCycles::DistributedVariantsService. new(current_order_cycle, current_distributor).unavailable_order_variants(@order) if @order.line_items.empty? @@ -102,7 +102,7 @@ module Spree @order = Spree::Order.find_by!(number: params[:id]) authorize! :cancel, @order - if CustomerOrderCancellation.new(@order).call + if Orders::CustomerCancellationService.new(@order).call flash[:success] = I18n.t(:orders_your_order_has_been_cancelled) else flash[:error] = I18n.t(:orders_could_not_cancel) diff --git a/app/controllers/spree/users_controller.rb b/app/controllers/spree/users_controller.rb index 6fdcebe89b..ad4e9082ae 100644 --- a/app/controllers/spree/users_controller.rb +++ b/app/controllers/spree/users_controller.rb @@ -8,6 +8,7 @@ module Spree layout 'darkswarm' + invisible_captcha only: [:create], on_timestamp_spam: :render_alert_timestamp_error_message skip_before_action :set_current_order, only: :show prepend_before_action :load_object, only: [:show, :edit, :update] prepend_before_action :authorize_actions, only: :new @@ -15,7 +16,7 @@ module Spree before_action :set_locale def show - @payments_requiring_action = PaymentsRequiringAction.new(spree_current_user).query + @payments_requiring_action = PaymentsRequiringActionQuery.new(spree_current_user).call @orders = orders_collection.includes(:line_items) customers = spree_current_user.customers @@ -78,7 +79,7 @@ module Spree private def orders_collection - CompleteOrdersWithBalance.new(@user).query + CompleteOrdersWithBalanceQuery.new(@user).call end def load_object @@ -101,5 +102,16 @@ module Spree def user_params ::PermittedAttributes::User.new(params).call end + + def render_alert_timestamp_error_message + render cable_ready: cable_car.inner_html( + "#signup-feedback", + partial("layouts/alert", + locals: { + type: "alert", + message: InvisibleCaptcha.timestamp_error_message + }) + ) + end end end diff --git a/app/helpers/admin/enterprises_helper.rb b/app/helpers/admin/enterprises_helper.rb index 7201d9f87f..e78029c762 100644 --- a/app/helpers/admin/enterprises_helper.rb +++ b/app/helpers/admin/enterprises_helper.rb @@ -21,15 +21,29 @@ module Admin show_payment_methods = can?(:manage_payment_methods, enterprise) && is_shop show_enterprise_fees = can?(:manage_enterprise_fees, enterprise) && (is_shop || enterprise.is_primary_producer) + show_connected_apps = can?(:manage_connected_apps, enterprise) && + feature?(:connected_apps, spree_current_user, enterprise) - build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods, - show_payment_methods, show_enterprise_fees) + build_enterprise_side_menu_items( + is_shop:, + show_properties:, + show_shipping_methods:, + show_payment_methods:, + show_enterprise_fees:, + show_connected_apps:, + ) end private - def build_enterprise_side_menu_items(is_shop, show_properties, show_shipping_methods, - show_payment_methods, show_enterprise_fees) + def build_enterprise_side_menu_items( + is_shop:, + show_properties:, + show_shipping_methods:, + show_payment_methods:, + show_enterprise_fees:, + show_connected_apps: + ) [ { name: 'primary_details', icon_class: "icon-home", show: true, selected: 'selected' }, { name: 'address', icon_class: "icon-map-marker", show: true }, @@ -50,6 +64,7 @@ module Admin { name: 'shop_preferences', icon_class: "icon-shopping-cart", show: is_shop }, { name: 'users', icon_class: "icon-user", show: true }, { name: 'white_label', icon_class: "icon-leaf", show: true }, + { name: 'connected_apps', icon_class: "icon-puzzle-piece", show: show_connected_apps }, ] end end diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index f85d9a6f54..3585fb0817 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -27,13 +27,6 @@ module Admin Api::Admin::EnterpriseRelationshipSerializer end - def admin_inject_enterprise_roles(enterprise_roles) - admin_inject_json_ams_array "ofn.admin", - "enterpriseRoles", - enterprise_roles, - Api::Admin::EnterpriseRoleSerializer - end - def admin_inject_payment_methods(payment_methods) admin_inject_json_ams_array "admin.paymentMethods", "paymentMethods", @@ -137,13 +130,6 @@ module Admin Api::Admin::TaxonSerializer end - def admin_inject_users(users) - admin_inject_json_ams_array "ofn.admin", - "users", - users, - Api::Admin::UserSerializer - end - def admin_inject_variant_overrides(variant_overrides) admin_inject_json_ams_array "admin.variantOverrides", "variantOverrides", diff --git a/app/helpers/admin/terms_of_service_helper.rb b/app/helpers/admin/terms_of_service_helper.rb new file mode 100644 index 0000000000..87b0db25bc --- /dev/null +++ b/app/helpers/admin/terms_of_service_helper.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Admin + module TermsOfServiceHelper + def tos_need_accepting? + return false unless spree_user_signed_in? + + return false if Spree::Config.enterprises_require_tos == false + + return false if TermsOfServiceFile.current.nil? + + !accepted_tos? + end + + private + + def accepted_tos? + file_uploaded_at = TermsOfServiceFile.updated_at + + current_spree_user.terms_of_service_accepted_at.present? && + current_spree_user.terms_of_service_accepted_at > file_uploaded_at && + current_spree_user.terms_of_service_accepted_at < DateTime.now + end + end +end diff --git a/app/helpers/angular_form_helper.rb b/app/helpers/angular_form_helper.rb index 3b1233302e..a9b03e9608 100644 --- a/app/helpers/angular_form_helper.rb +++ b/app/helpers/angular_form_helper.rb @@ -9,7 +9,7 @@ module AngularFormHelper text, value = option_text_and_value(element).map(&:to_s) %() - end.join("\n").html_safe + end.join("\n").html_safe # rubocop:disable Rails/OutputSafety end def ng_options_from_collection_for_select(collection, value_method, text_method, angular_field) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 223a4c8f41..2f470529b2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,7 +10,7 @@ module ApplicationHelper return "" unless obj && obj.errors[method].present? - errors = obj.errors[method].map { |err| h(err) }.join('
').html_safe + errors = obj.errors[method].map { |err| h(err) }.join('
').html_safe # rubocop:disable Rails/OutputSafety if options[:standalone] content_tag( @@ -36,7 +36,7 @@ module ApplicationHelper hreflang: locale.to_s.gsub("_", "-").downcase, href: "#{request.protocol}#{request.host_with_port}/locales/#{locale}" ) - end.join("\n").html_safe + end.join("\n").html_safe # rubocop:disable Rails/OutputSafety end def ng_form_for(name, *args, &) diff --git a/app/helpers/checkout_helper.rb b/app/helpers/checkout_helper.rb index dabbf3d4a6..393c606ed7 100644 --- a/app/helpers/checkout_helper.rb +++ b/app/helpers/checkout_helper.rb @@ -59,7 +59,7 @@ module CheckoutHelper end def display_checkout_taxes_hash(order) - totals = OrderTaxAdjustmentsFetcher.new(order).totals + totals = Orders::FetchTaxAdjustmentsService.new(order).totals totals.map do |tax_rate, tax_amount| { @@ -148,4 +148,10 @@ module CheckoutHelper ] end end + + # Set the Page title of checkout process as step based like + # Checkout Details, Checkout Payment and Checkout Summary + def checkout_page_title + t("checkout_#{checkout_step}_title") + end end diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index efb09655dd..4a7c6ae817 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -12,11 +12,11 @@ module EnterprisesHelper end def available_shipping_methods - OrderAvailableShippingMethods.new(current_order, current_customer).to_a + Orders::AvailableShippingMethodsService.new(current_order, current_customer).to_a end def available_payment_methods - OrderAvailablePaymentMethods.new(current_order, current_customer).to_a + Orders::AvailablePaymentMethodsService.new(current_order, current_customer).to_a end def managed_enterprises diff --git a/app/helpers/order_helper.rb b/app/helpers/order_helper.rb index cdafb9f55b..e0d6c65313 100644 --- a/app/helpers/order_helper.rb +++ b/app/helpers/order_helper.rb @@ -2,7 +2,7 @@ module OrderHelper def last_payment_method(order) - OrderPaymentFinder.new(order).last_payment&.payment_method + Orders::FindPaymentService.new(order).last_payment&.payment_method end def outstanding_balance_label(order) @@ -16,6 +16,6 @@ module OrderHelper end def order_comparator(order) - OrderInvoiceComparator.new(order) + Orders::CompareInvoiceService.new(order) end end diff --git a/app/helpers/reports_helper.rb b/app/helpers/reports_helper.rb index 2809ac459a..1f9bf91ed7 100644 --- a/app/helpers/reports_helper.rb +++ b/app/helpers/reports_helper.rb @@ -5,7 +5,9 @@ module ReportsHelper order_cycles.map do |oc| orders_open_at = oc.orders_open_at&.to_fs(:short) || 'NA' orders_close_at = oc.orders_close_at&.to_fs(:short) || 'NA' + # rubocop:disable Rails/OutputSafety ["#{oc.name}   (#{orders_open_at} - #{orders_close_at})".html_safe, oc.id] + # rubocop:enable Rails/OutputSafety end end diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb index b1f51ab004..17bd12adbe 100644 --- a/app/helpers/shared_helper.rb +++ b/app/helpers/shared_helper.rb @@ -20,6 +20,6 @@ module SharedHelper end def current_shop_products_path - "#{main_app.enterprise_shop_path(current_distributor)}#/shop" + "#{main_app.enterprise_shop_path(current_distributor)}#/shop_panel" end end diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index 3b649b8af2..fa248c8047 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -22,7 +22,7 @@ module ShopHelper { name: 'home', title: t(:shopping_tabs_home), show: show_home_tab?, default: show_home_tab? }, { name: 'shop', title: t(:shopping_tabs_shop), show: !require_customer?, - default: !show_home_tab? }, + default: !show_home_tab?, shop: true }, { name: 'about', title: t(:shopping_tabs_about), show: true }, { name: 'producers', title: t(:shopping_tabs_producers), show: true }, { name: 'contact', title: t(:shopping_tabs_contact), show: true }, @@ -45,7 +45,7 @@ module ShopHelper end def shopfront_closed_message?(order_cycles) - no_open_order_cycles?(order_cycles) && \ + no_open_order_cycles?(order_cycles) && current_distributor.preferred_shopfront_closed_message.present? end diff --git a/app/helpers/spree/admin/base_helper.rb b/app/helpers/spree/admin/base_helper.rb index 5615d4d62a..652e2a4dd4 100644 --- a/app/helpers/spree/admin/base_helper.rb +++ b/app/helpers/spree/admin/base_helper.rb @@ -12,13 +12,15 @@ module Spree id: "#{model}_#{method}_field") end - def error_message_on(object, method, _options = {}) + def error_message_on(object, method, options = {}) object = convert_to_model(object) obj = object.respond_to?(:errors) ? object : instance_variable_get("@#{object}") if obj && obj.errors[method].present? + # rubocop:disable Rails/OutputSafety errors = obj.errors[method].map { |err| h(err) }.join('
').html_safe - content_tag(:span, errors, class: 'formError') + # rubocop:enable Rails/OutputSafety + content_tag(:span, errors, class: 'formError', **options) else '' end @@ -110,12 +112,12 @@ module Spree object.preferences.keys.map { |key| preference_label = form.label("preferred_#{key}", - Spree.t(key.to_s.gsub("_from_list", "")) + ": ").html_safe + Spree.t(key.to_s.gsub("_from_list", "")) + ": ") preference_field = preference_field_for( form, "preferred_#{key}", { type: object.preference_type(key) }, object - ).html_safe + ) { label: preference_label, field: preference_field } } end diff --git a/app/helpers/spree/admin/navigation_helper.rb b/app/helpers/spree/admin/navigation_helper.rb index 5b71c2780a..d39ac5b3cc 100644 --- a/app/helpers/spree/admin/navigation_helper.rb +++ b/app/helpers/spree/admin/navigation_helper.rb @@ -98,7 +98,9 @@ module Spree options[:class] = (options[:class].to_s + " icon_link with-tip #{icon_name}").strip options[:class] += ' no-text' if options[:no_text] options[:title] = text if options[:no_text] + # rubocop:disable Rails/OutputSafety text = options[:no_text] ? '' : raw("#{text}") + # rubocop:enable Rails/OutputSafety options.delete(:no_text) link_to(text, url, options) end @@ -131,14 +133,14 @@ module Spree if html_options[:icon] html_options[:class] += " #{html_options[:icon]}" end - link_to(text_for_button_link(text, html_options), url, html_options) + link_to(text, url, html_options) end end def text_for_button_link(text, _html_options) s = '' s << text - raw(s) + raw(s) # rubocop:disable Rails/OutputSafety end def configurations_sidebar_menu_item(link_text, url, options = {}) diff --git a/app/helpers/spree/admin/orders_helper.rb b/app/helpers/spree/admin/orders_helper.rb index 81f9066f90..893a417297 100644 --- a/app/helpers/spree/admin/orders_helper.rb +++ b/app/helpers/spree/admin/orders_helper.rb @@ -7,7 +7,18 @@ module Spree links = [] links << cancel_event_link if @order.can_cancel? links << resume_event_link if @order.can_resume? - links.join(' ').html_safe + links.join(' ').html_safe # rubocop:disable Rails/OutputSafety + end + + def generate_invoice_button(order) + if order.distributor.can_invoice? + button_link_to t(:create_or_update_invoice), generate_admin_order_invoices_path(@order), + data: { method: 'post' }, icon: 'icon-plus' + else + button_link_to t(:create_or_update_invoice), "#", data: { + confirm: t(:must_have_valid_business_number, enterprise_name: @order.distributor.name) + }, icon: 'icon-plus' + end end def line_item_shipment_price(line_item, quantity) @@ -95,10 +106,8 @@ module Spree def ship_order_link { name: t(:ship_order), - url: spree.fire_admin_order_path(@order, e: 'ship'), - method: 'put', - icon: 'icon-truck', - confirm: t(:are_you_sure) } + url: '#', + icon: 'icon-truck' } end def cancel_order_link diff --git a/app/helpers/spree/admin/zones_helper.rb b/app/helpers/spree/admin/zones_helper.rb index 57a1a639c1..1ad0927b9f 100644 --- a/app/helpers/spree/admin/zones_helper.rb +++ b/app/helpers/spree/admin/zones_helper.rb @@ -31,7 +31,7 @@ module Spree out = '' out << fields.hidden_field(:_destroy) unless fields.object.new_record? out << (link_to icon('icon-remove'), "#", class: 'remove') - out.html_safe + out.html_safe # rubocop:disable Rails/OutputSafety end end end diff --git a/app/helpers/tax_helper.rb b/app/helpers/tax_helper.rb index 4b3c61ace8..a60481b389 100644 --- a/app/helpers/tax_helper.rb +++ b/app/helpers/tax_helper.rb @@ -14,9 +14,11 @@ module TaxHelper def display_line_items_taxes(line_item, display_zero: true) if line_item.included_tax.positive? - Spree::Money.new(line_item.included_tax, currency: line_item.currency) + tax = line_item.included_tax + enterprise_fee_adjustments(line_item).total_included_tax + Spree::Money.new(tax, currency: line_item.currency) elsif line_item.added_tax.positive? - Spree::Money.new(line_item.added_tax, currency: line_item.currency) + tax = line_item.added_tax + enterprise_fee_adjustments(line_item).total_additional_tax + Spree::Money.new(tax, currency: line_item.currency) elsif display_zero Spree::Money.new(0.00, currency: line_item.currency) end @@ -26,4 +28,10 @@ module TaxHelper total = taxable.amount + taxable.additional_tax_total Spree::Money.new(total, currency: taxable.currency) end + + private + + def enterprise_fee_adjustments(line_item) + EnterpriseFeeAdjustments.new(line_item.enterprise_fee_adjustments) + end end diff --git a/app/jobs/bulk_invoice_job.rb b/app/jobs/bulk_invoice_job.rb index 3c360005fa..517cba1c22 100644 --- a/app/jobs/bulk_invoice_job.rb +++ b/app/jobs/bulk_invoice_job.rb @@ -33,7 +33,7 @@ class BulkInvoiceJob < ApplicationJob def generate_invoice(order) renderer_data = if OpenFoodNetwork::FeatureToggle.enabled?(:invoices, current_user) - OrderInvoiceGenerator.new(order).generate_or_update_latest_invoice + Orders::GenerateInvoiceService.new(order).generate_or_update_latest_invoice order.invoices.first.presenter else order diff --git a/app/jobs/connect_app_job.rb b/app/jobs/connect_app_job.rb new file mode 100644 index 0000000000..9ecfad4cd7 --- /dev/null +++ b/app/jobs/connect_app_job.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class ConnectAppJob < ApplicationJob + include CableReady::Broadcaster + + def perform(app, token, channel: nil) + url = "https://n8n.openfoodnetwork.org.uk/webhook/regen/connect-enterprise" + event = "connect-app" + enterprise = app.enterprise + payload = { + '@id': DfcBuilder.urls.enterprise_url(enterprise.id), + access_token: token, + } + + response = WebhookDeliveryJob.perform_now(url, event, payload) + app.update!(data: JSON.parse(response)) + + return unless channel + + selector = "#edit_enterprise_#{enterprise.id} #connected-app-discover-regen" + html = ApplicationController.render( + partial: "admin/enterprises/form/connected_apps", + locals: { enterprise: }, + ) + + cable_ready[channel].morph(selector:, html:).broadcast + end +end diff --git a/app/jobs/order_cycle_opened_job.rb b/app/jobs/order_cycle_opened_job.rb index 9d84027fe3..3b4cee16db 100644 --- a/app/jobs/order_cycle_opened_job.rb +++ b/app/jobs/order_cycle_opened_job.rb @@ -5,7 +5,7 @@ class OrderCycleOpenedJob < ApplicationJob def perform ActiveRecord::Base.transaction do recently_opened_order_cycles.find_each do |order_cycle| - OrderCycleWebhookService.create_webhook_job(order_cycle, 'order_cycle.opened') + OrderCycles::WebhookService.create_webhook_job(order_cycle, 'order_cycle.opened') end mark_as_opened(recently_opened_order_cycles) end diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index f26fbcaf61..4a7e69a384 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'order_management/subscriptions/summarizer' - # Confirms orders of unconfirmed proxy orders in recently closed Order Cycles class SubscriptionConfirmJob < ApplicationJob def perform @@ -25,7 +23,7 @@ class SubscriptionConfirmJob < ApplicationJob unconfirmed_proxy_orders.update_all(confirmed_at: Time.zone.now) # Confirm these proxy orders - ProxyOrder.where(id: unconfirmed_proxy_orders_ids).each do |proxy_order| + ProxyOrder.where(id: unconfirmed_proxy_orders_ids).find_each do |proxy_order| JobLogger.logger.info "Confirming Order for Proxy Order #{proxy_order.id}" confirm_order!(proxy_order.order) end diff --git a/app/jobs/subscription_placement_job.rb b/app/jobs/subscription_placement_job.rb index fe15e99113..cb3609a5b2 100644 --- a/app/jobs/subscription_placement_job.rb +++ b/app/jobs/subscription_placement_job.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'order_management/subscriptions/summarizer' - class SubscriptionPlacementJob < ApplicationJob def perform proxy_orders.each do |proxy_order| diff --git a/app/jobs/webhook_delivery_job.rb b/app/jobs/webhook_delivery_job.rb index 6de75361dd..1196e6a1d6 100644 --- a/app/jobs/webhook_delivery_job.rb +++ b/app/jobs/webhook_delivery_job.rb @@ -46,5 +46,7 @@ class WebhookDeliveryJob < ApplicationJob # Raise a failed request error and let job runner handle retrying. # In theory, only 5xx errors should be retried, but who knows. raise FailedWebhookRequestError, response.status.to_s unless response.success? + + response.body end end diff --git a/app/mailers/spree/order_mailer.rb b/app/mailers/spree/order_mailer.rb index e3430c7787..97ffd3f6b1 100644 --- a/app/mailers/spree/order_mailer.rb +++ b/app/mailers/spree/order_mailer.rb @@ -52,7 +52,8 @@ module Spree find_user(options[:current_user_id]) end renderer_data = if OpenFoodNetwork::FeatureToggle.enabled?(:invoices, current_user) - OrderInvoiceGenerator.new(@order).generate_or_update_latest_invoice + ::Orders::GenerateInvoiceService + .new(@order).generate_or_update_latest_invoice @order.invoices.first.presenter else @order diff --git a/app/models/concerns/order_validations.rb b/app/models/concerns/order_validations.rb index 36733f5c35..52aa257b5f 100644 --- a/app/models/concerns/order_validations.rb +++ b/app/models/concerns/order_validations.rb @@ -15,7 +15,7 @@ module OrderValidations # Check that line_items in the current order are available from a newly selected distribution def products_available_from_new_distribution - return if OrderCycleDistributedVariants.new(order_cycle, distributor) + return if OrderCycles::DistributedVariantsService.new(order_cycle, distributor) .distributes_order_variants?(self) errors.add(:base, I18n.t(:spree_order_availability_error)) diff --git a/app/models/connected_app.rb b/app/models/connected_app.rb new file mode 100644 index 0000000000..3edc7c4147 --- /dev/null +++ b/app/models/connected_app.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# An enterprise can be connected to other apps. +# +# Here we store keys and links to access the app. +class ConnectedApp < ApplicationRecord + belongs_to :enterprise + + scope :connecting, -> { where(data: nil) } + scope :ready, -> { where.not(data: nil) } +end diff --git a/app/models/content_configuration.rb b/app/models/content_configuration.rb index 9276e97a4c..3ad99b5c0e 100644 --- a/app/models/content_configuration.rb +++ b/app/models/content_configuration.rb @@ -27,21 +27,24 @@ class ContentConfiguration < Spree::Preferences::Configuration default: I18n.t(:content_configuration_pricing_table) preference :producer_signup_case_studies_html, :text, default: I18n.t(:content_configuration_case_studies) - preference :producer_signup_detail_html, :text, default: I18n.t(:content_configuration_detail) + preference :producer_signup_detail_html, :text, + default: I18n.t(:content_configuration_detail) # Hubs sign-up page preference :hub_signup_pricing_table_html, :text, default: I18n.t(:content_configuration_pricing_table) preference :hub_signup_case_studies_html, :text, default: I18n.t(:content_configuration_case_studies) - preference :hub_signup_detail_html, :text, default: I18n.t(:content_configuration_detail) + preference :hub_signup_detail_html, :text, + default: I18n.t(:content_configuration_detail) # Groups sign-up page preference :group_signup_pricing_table_html, :text, default: I18n.t(:content_configuration_pricing_table) preference :group_signup_case_studies_html, :text, default: I18n.t(:content_configuration_case_studies) - preference :group_signup_detail_html, :text, default: I18n.t(:content_configuration_detail) + preference :group_signup_detail_html, :text, + default: I18n.t(:content_configuration_detail) # Main URLs preference :menu_1, :boolean, default: true diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index bd9aedbc93..7f9ab5a965 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -62,6 +62,7 @@ class Enterprise < ApplicationRecord has_many :tag_rules, dependent: :destroy has_one :stripe_account, dependent: :destroy has_many :vouchers + has_many :connected_apps, dependent: :destroy has_one :custom_tab, dependent: :destroy delegate :latitude, :longitude, :city, :state_name, to: :address diff --git a/app/models/enterprise_fee_adjustments.rb b/app/models/enterprise_fee_adjustments.rb new file mode 100644 index 0000000000..9679809191 --- /dev/null +++ b/app/models/enterprise_fee_adjustments.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# EnterpriseFeeAdjustments represent a collection of enterprise fee adjustments +# +class EnterpriseFeeAdjustments + def initialize(adjustments) + @adjustments = adjustments + end + + def total_additional_tax + @adjustments.sum { |enterprise_fee| enterprise_fee.additional_tax_total.to_f } + end + + def total_included_tax + @adjustments.sum { |enterprise_fee| enterprise_fee.included_tax_total.to_f } + end +end diff --git a/app/models/invoice.rb b/app/models/invoice.rb index cf12c401dc..7cfc71c9a6 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -4,7 +4,7 @@ class Invoice < ApplicationRecord self.belongs_to_required_by_default = false belongs_to :order, class_name: 'Spree::Order' - serialize :data, Hash + serialize :data, Hash, coder: YAML before_validation :serialize_order after_create :cancel_previous_invoices default_scope { order(created_at: :desc) } @@ -26,4 +26,12 @@ class Invoice < ApplicationRecord def cancel_previous_invoices order.invoices.where.not(id:).update_all(cancelled: true) end + + def display_number + "#{order.distributor.id}-#{number}" + end + + def previous_invoice + order.invoices.where("id < ?", id).first + end end diff --git a/app/models/invoice/data_presenter.rb b/app/models/invoice/data_presenter.rb index 692c99486b..f45e35612b 100644 --- a/app/models/invoice/data_presenter.rb +++ b/app/models/invoice/data_presenter.rb @@ -5,8 +5,8 @@ class Invoice include ::ActionView::Helpers::NumberHelper attr_reader :invoice - delegate :data, to: :invoice - delegate :number, :date, to: :invoice, prefix: true + delegate :display_number, :data, :previous_invoice, to: :invoice + delegate :date, to: :invoice, prefix: true FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze @@ -91,6 +91,14 @@ class Invoice Spree::Money.new(shipment.amount + shipment.additional_tax_total, currency:) end + def display_line_item_tax_rate(item) + all_tax_adjustments.select { |a| + a.adjustable.type == 'Spree::LineItem' && a.adjustable.id == item.id + }.map(&:originator).map { |tr| + number_to_percentage(tr.amount * 100, precision: 1) + }.join(", ") + end + def display_shipment_tax_rates all_eligible_adjustments.select { |a| a.originator.type == 'Spree::TaxRate' && a.adjustable_type == 'Spree::Shipment' diff --git a/app/models/invoice/data_presenter/line_item.rb b/app/models/invoice/data_presenter/line_item.rb index 2e46936c7e..2b55cca925 100644 --- a/app/models/invoice/data_presenter/line_item.rb +++ b/app/models/invoice/data_presenter/line_item.rb @@ -3,21 +3,23 @@ class Invoice class DataPresenter class LineItem < Invoice::DataPresenter::Base - attributes :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity, - :variant_id, :unit_price_price_and_unit, :unit_presentation + attributes :id, :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity, + :variant_id, :unit_price_price_and_unit, :unit_presentation, + :enterprise_fee_additional_tax, :enterprise_fee_included_tax attributes_with_presenter :variant - array_attribute :tax_rates, class_name: 'TaxRate' invoice_generation_attributes :added_tax, :included_tax, :price_with_adjustments, :quantity, :variant_id delegate :name_to_display, :options_text, to: :variant def amount_with_adjustments_without_taxes - (price_with_adjustments * quantity) - included_tax + fee_tax = enterprise_fee_included_tax || 0.0 + (price_with_adjustments * quantity) - included_tax - fee_tax end def amount_with_adjustments_and_with_taxes - ( price_with_adjustments * quantity) + added_tax + fee_tax = enterprise_fee_additional_tax || 0.0 + ( price_with_adjustments * quantity) + added_tax + fee_tax end def display_amount_with_adjustments_without_taxes @@ -29,21 +31,8 @@ class Invoice end def single_display_amount_with_adjustments - Spree::Money.new(price_with_adjustments - (included_tax / quantity), currency:) - end - - def display_line_items_taxes(display_zero: true) - if included_tax.positive? - Spree::Money.new( included_tax, currency:) - elsif added_tax.positive? - Spree::Money.new( added_tax, currency:) - elsif display_zero - Spree::Money.new(0.00, currency:) - end - end - - def display_line_item_tax_rates - tax_rates.map { |tr| number_to_percentage(tr.amount * 100, precision: 1) }.join(", ") + fee_tax = enterprise_fee_included_tax || 0.0 + Spree::Money.new(price_with_adjustments - ((included_tax + fee_tax) / quantity), currency:) end end end diff --git a/app/models/invoice/data_presenter/shipping_method.rb b/app/models/invoice/data_presenter/shipping_method.rb index 2d9ba86015..ed80c9ffa0 100644 --- a/app/models/invoice/data_presenter/shipping_method.rb +++ b/app/models/invoice/data_presenter/shipping_method.rb @@ -5,6 +5,10 @@ class Invoice class ShippingMethod < Invoice::DataPresenter::Base attributes :id, :name, :require_ship_address invoice_generation_attributes :id + + def category + I18n.t "invoice_shipping_category_#{require_ship_address ? 'delivery' : 'pickup'}" + end end end end diff --git a/app/models/oidc_account.rb b/app/models/oidc_account.rb new file mode 100644 index 0000000000..b755849753 --- /dev/null +++ b/app/models/oidc_account.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class OidcAccount < ApplicationRecord + belongs_to :user, class_name: "Spree::User" + + # When a user authenticates via token, the `uid` should be mapped to only one + # OFN user and therefore it needs to be unique. + validates :uid, presence: true, uniqueness: true + + def self.link(user, auth) + attributes = { + user_id: user.id, + provider: auth.provider, + uid: auth.uid, + token: auth.dig(:credentials, :token), + refresh_token: auth.dig(:credentials, :refresh_token), + } + # This skips validations but we have database constraints in place. + # We may replace this at some point. + upsert_all([attributes], unique_by: [:user_id]) # rubocop:disable Rails/SkipsModelValidations + end +end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index b6e7d80913..7fa53009eb 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -175,7 +175,7 @@ class OrderCycle < ApplicationRecord end def clone! - OrderCycleClone.new(self).create + OrderCycles::CloneService.new(self).create end def variants diff --git a/app/models/proxy_order.rb b/app/models/proxy_order.rb index 449478b44e..2f64fe0a2b 100644 --- a/app/models/proxy_order.rb +++ b/app/models/proxy_order.rb @@ -58,7 +58,7 @@ class ProxyOrder < ApplicationRecord def initialise_order! return order if order.present? - factory = OrderFactory.new(order_attrs, skip_stock_check: true) + factory = Orders::FactoryService.new(order_attrs, skip_stock_check: true) self.order = factory.create save! order diff --git a/app/models/report_rendering_options.rb b/app/models/report_rendering_options.rb index feadbaf7ff..dd69ecf887 100644 --- a/app/models/report_rendering_options.rb +++ b/app/models/report_rendering_options.rb @@ -4,5 +4,5 @@ class ReportRenderingOptions < ApplicationRecord self.belongs_to_required_by_default = false belongs_to :user, class_name: "Spree::User" - serialize :options, Hash + serialize :options, Hash, coder: YAML end diff --git a/app/models/semantic_link.rb b/app/models/semantic_link.rb new file mode 100644 index 0000000000..ea8fa0bdba --- /dev/null +++ b/app/models/semantic_link.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Link a Spree::Variant to an external DFC SuppliedProduct. +class SemanticLink < ApplicationRecord + belongs_to :variant, class_name: "Spree::Variant" + + validates :semantic_id, presence: true +end diff --git a/app/models/spree/ability.rb b/app/models/spree/ability.rb index 37587a0111..037bfecdb6 100644 --- a/app/models/spree/ability.rb +++ b/app/models/spree/ability.rb @@ -147,7 +147,8 @@ module Spree end can [:manage_payment_methods, :manage_shipping_methods, - :manage_enterprise_fees], Enterprise do |enterprise| + :manage_enterprise_fees, + :manage_connected_apps], Enterprise do |enterprise| user.enterprises.include? enterprise end @@ -176,7 +177,7 @@ module Spree can [:admin, :create], :manager_invitation - can [:admin, :index], :oidc_setting + can [:admin, :index, :destroy], :oidc_setting can [:admin, :create], Voucher end @@ -238,6 +239,8 @@ module Spree can [:admin, :index, :guide, :import, :save, :save_data, :validate_data, :reset_absent_products], ProductImport::ProductImporter + can [:admin, :index], ::Admin::DfcProductImportsController + # Reports page can [:admin, :index, :show], ::Admin::ReportsController can [:admin, :show, :customers, :orders_and_distributors, :group_buys, :payments, diff --git a/app/models/spree/adjustment.rb b/app/models/spree/adjustment.rb index 7625b34962..90d2cca514 100644 --- a/app/models/spree/adjustment.rb +++ b/app/models/spree/adjustment.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'spree/localized_number' -require 'concerns/adjustment_scopes' # Adjustments represent a change to the +item_total+ of an Order. Each adjustment # has an +amount+ that can be either positive or negative. diff --git a/app/models/spree/app_configuration.rb b/app/models/spree/app_configuration.rb index 97f704eae3..0490c6432f 100644 --- a/app/models/spree/app_configuration.rb +++ b/app/models/spree/app_configuration.rb @@ -135,7 +135,7 @@ module Spree # Enable cache preference :enable_products_cache?, :boolean, - default: (Rails.env.production? || Rails.env.staging?) + default: Rails.env.production? || Rails.env.staging? # Available units preference :available_units, :string, default: "g,kg,T,mL,L,kL" diff --git a/app/models/spree/country.rb b/app/models/spree/country.rb index cc9e3dbed9..ca83337caf 100644 --- a/app/models/spree/country.rb +++ b/app/models/spree/country.rb @@ -6,12 +6,6 @@ module Spree validates :name, :iso_name, presence: true - def self.cached_find_by(attrs) - Rails.cache.fetch("countries/#{attrs.hash}", expires_in: 1.hour) do - find_by(attrs) - end - end - def <=>(other) name <=> other.name end diff --git a/app/models/spree/gateway.rb b/app/models/spree/gateway.rb index 05d3e8abaf..42fc9b6eec 100644 --- a/app/models/spree/gateway.rb +++ b/app/models/spree/gateway.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'concerns/payment_method_distributors' - module Spree class Gateway < PaymentMethod acts_as_taggable diff --git a/app/models/spree/gateway/stripe_sca.rb b/app/models/spree/gateway/stripe_sca.rb index b4a3ea95b4..78e658ab9c 100644 --- a/app/models/spree/gateway/stripe_sca.rb +++ b/app/models/spree/gateway/stripe_sca.rb @@ -4,7 +4,6 @@ require 'stripe/profile_storer' require 'stripe/credit_card_cloner' require 'stripe/authorize_response_patcher' require 'stripe/payment_intent_validator' -require 'active_merchant/billing/gateways/stripe_payment_intents_decorator' require 'active_merchant/billing/gateways/stripe' module Spree @@ -44,7 +43,7 @@ module Spree StripeAccount.find_by(enterprise_id: preferred_enterprise_id)&.stripe_user_id end - # NOTE: the name of this method is determined by Spree::Payment::Processing + # NOTE: this method is required by Spree::Payment::Processing def purchase(money, creditcard, gateway_options) begin payment_intent_id = fetch_payment_intent(creditcard, gateway_options) @@ -64,7 +63,7 @@ module Spree provider.capture(money, payment_intent_id, options) end - # NOTE: the name of this method is determined by Spree::Payment::Processing + # NOTE: this method is required by Spree::Payment::Processing def charge_offline(money, creditcard, gateway_options) customer, payment_method = Stripe::CreditCardCloner.new(creditcard, stripe_account_id).find_or_clone @@ -75,7 +74,7 @@ module Spree failed_activemerchant_billing_response(e.message) end - # NOTE: the name of this method is determined by Spree::Payment::Processing + # NOTE: this method is required by Spree::Payment::Processing def authorize(money, creditcard, gateway_options) authorize_response = provider.authorize(*options_for_authorize(money, creditcard, gateway_options)) @@ -84,26 +83,27 @@ module Spree failed_activemerchant_billing_response(e.message) end - # NOTE: the name of this method is determined by Spree::Payment::Processing - def void(response_code, _creditcard, gateway_options) - payment_intent_id = response_code - payment_intent_response = Stripe::PaymentIntent.retrieve(payment_intent_id, - stripe_account: stripe_account_id) + # NOTE: this method is required by Spree::Payment::Processing + def void(payment_intent_id, _creditcard, gateway_options) + payment_intent_response = Stripe::PaymentIntent.retrieve( + payment_intent_id, stripe_account: stripe_account_id + ) gateway_options[:stripe_account] = stripe_account_id # If a payment has been confirmed it can't be voided by Stripe, and must be refunded instead if voidable?(payment_intent_response) - provider.void(response_code, gateway_options) + provider.void(payment_intent_id, gateway_options) else - provider.refund(refundable_amount(payment_intent_response), response_code, - gateway_options) + provider.refund( + payment_intent_response.amount_received, payment_intent_id, gateway_options + ) end end - # NOTE: the name of this method is determined by Spree::Payment::Processing - def credit(money, _creditcard, response_code, gateway_options) + # NOTE: this method is required by Spree::Payment::Processing + def credit(money, _creditcard, payment_intent_id, gateway_options) gateway_options[:stripe_account] = stripe_account_id - provider.refund(money, response_code, gateway_options) + provider.refund(money, payment_intent_id, gateway_options) end def create_profile(payment) @@ -119,11 +119,6 @@ module Spree VOIDABLE_STATES.include? payment_intent_response.status end - def refundable_amount(payment_intent_response) - payment_intent_response.amount_received - - payment_intent_response.charges.data.map(&:amount_refunded).sum - end - # In this gateway, what we call 'secret_key' is the 'login' def options options = super diff --git a/app/models/spree/line_item.rb b/app/models/spree/line_item.rb index 6de3fe9601..571a9adfd4 100644 --- a/app/models/spree/line_item.rb +++ b/app/models/spree/line_item.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'open_food_network/scope_variant_to_hub' -require 'variant_units/variant_and_line_item_naming' module Spree class LineItem < ApplicationRecord @@ -194,6 +193,7 @@ module Spree adjustments.tax.additional.sum(:amount) end + # Some of the tax rates may not be applicable depending to the order's tax zone def tax_rates variant&.tax_category&.tax_rates || [] end @@ -203,7 +203,7 @@ module Spree # so line_item.adjustments returns an empty array return 0 if quantity.zero? - fees = adjustments.enterprise_fee.sum(:amount) + fees = enterprise_fee_adjustments.sum(:amount) (price + (fees / quantity)).round(2) end @@ -242,6 +242,10 @@ module Spree @scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(order.distributor) end + def enterprise_fee_adjustments + adjustments.enterprise_fee + end + private def computed_weight_from_variant diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index 3dced4e802..5e56610c07 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -1,10 +1,5 @@ # frozen_string_literal: true -require 'spree/order/checkout' -require 'open_food_network/enterprise_fee_calculator' -require 'open_food_network/feature_toggle' -require 'open_food_network/tag_rule_applicator' - module Spree class Order < ApplicationRecord include OrderShipment @@ -32,7 +27,9 @@ module Spree go_to_state :complete end - attr_accessor :use_billing, :checkout_processing, :save_bill_address, :save_ship_address + attr_accessor :use_billing, :checkout_processing, :save_bill_address, + :save_ship_address + attr_writer :send_shipment_email token_resource @@ -621,6 +618,22 @@ module Spree state.in?(["payment", "confirmation"]) end + def send_shipment_email + return true if @send_shipment_email.nil? + + @send_shipment_email + end + + # @return [BigDecimal] The rate of the voucher if applied to the order + def applied_voucher_rate + # As an order can have only one voucher, + # hence using +take+ as each voucher adjustment will have the same voucher + return BigDecimal(0) unless (voucher_adjustment = voucher_adjustments.take) + + voucher = voucher_adjustment.originator + voucher.rate(self) + end + private def reapply_tax_on_changed_address @@ -639,7 +652,7 @@ module Spree end def fee_handler - @fee_handler ||= OrderFeesHandler.new(self) + @fee_handler ||= Orders::HandleFeesService.new(self) end def clear_legacy_taxes! @@ -688,7 +701,7 @@ module Spree end def adjustments_fetcher - @adjustments_fetcher ||= OrderAdjustmentsFetcher.new(self) + @adjustments_fetcher ||= Orders::FetchAdjustmentsService.new(self) end def skip_payment_for_subscription? diff --git a/app/models/spree/payment.rb b/app/models/spree/payment.rb index 6f32de6f9b..4c209d10d7 100644 --- a/app/models/spree/payment.rb +++ b/app/models/spree/payment.rb @@ -21,7 +21,8 @@ module Spree belongs_to :payment_method, class_name: 'Spree::PaymentMethod' has_many :offsets, -> { where("source_type = 'Spree::Payment' AND amount < 0").completed }, - class_name: "Spree::Payment", foreign_key: :source_id, dependent: :restrict_with_exception + class_name: "Spree::Payment", foreign_key: :source_id, + dependent: :restrict_with_exception has_many :log_entries, as: :source, dependent: :destroy has_one :adjustment, as: :adjustable, dependent: :destroy diff --git a/app/models/spree/payment/processing.rb b/app/models/spree/payment/processing.rb index 499a669987..8f9d0e4573 100644 --- a/app/models/spree/payment/processing.rb +++ b/app/models/spree/payment/processing.rb @@ -48,7 +48,7 @@ module Spree end def capture_and_complete_order! - OrderWorkflow.new(order).complete! + Orders::WorkflowService.new(order).complete! capture! end diff --git a/app/models/spree/payment_method.rb b/app/models/spree/payment_method.rb index f3011b222c..5b60aded62 100644 --- a/app/models/spree/payment_method.rb +++ b/app/models/spree/payment_method.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'concerns/payment_method_distributors' - module Spree class PaymentMethod < ApplicationRecord include CalculatedAdjustments diff --git a/app/models/spree/preference.rb b/app/models/spree/preference.rb index ea2c63688c..6f64edf9cf 100644 --- a/app/models/spree/preference.rb +++ b/app/models/spree/preference.rb @@ -2,8 +2,7 @@ module Spree class Preference < ApplicationRecord - serialize :value - + serialize :value, coder: YAML validates :key, presence: true validates :value_type, presence: true diff --git a/app/models/spree/preferences/preferable_class_methods.rb b/app/models/spree/preferences/preferable_class_methods.rb index 4bff9406f6..71f6b746f7 100644 --- a/app/models/spree/preferences/preferable_class_methods.rb +++ b/app/models/spree/preferences/preferable_class_methods.rb @@ -70,31 +70,31 @@ module Spree end def preference_getter_method(name) - "preferred_#{name}".to_sym + :"preferred_#{name}" end def preference_setter_method(name) - "preferred_#{name}=".to_sym + :"preferred_#{name}=" end def prefers_getter_method(name) - "prefers_#{name}?".to_sym + :"prefers_#{name}?" end def prefers_setter_method(name) - "prefers_#{name}=".to_sym + :"prefers_#{name}=" end def preference_default_getter_method(name) - "preferred_#{name}_default".to_sym + :"preferred_#{name}_default" end def preference_type_getter_method(name) - "preferred_#{name}_type".to_sym + :"preferred_#{name}_type" end def preference_description_getter_method(name) - "preferred_#{name}_description".to_sym + :"preferred_#{name}_description" end end end diff --git a/app/models/spree/product.rb b/app/models/spree/product.rb index 41b8c8f610..fcc133aa8d 100755 --- a/app/models/spree/product.rb +++ b/app/models/spree/product.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'open_food_network/property_merge' -require 'concerns/product_stock' # PRODUCTS # Products represent an entity for sale in a store. @@ -189,7 +188,7 @@ module Spree .with_permission(:add_to_order_cycle) .where(enterprises: { is_primary_producer: true }) .pluck(:parent_id) - return where('spree_products.supplier_id IN (?)', [enterprise.id] | permitted_producer_ids) + where('spree_products.supplier_id IN (?)', [enterprise.id] | permitted_producer_ids) } scope :active, lambda { where("spree_products.deleted_at IS NULL") } @@ -288,6 +287,22 @@ module Spree variants << variant end + # Format as per WeightsAndMeasures (todo: re-orgnaise maybe after product/variant refactor) + def variant_unit_with_scale + scale_clean = ActiveSupport::NumberHelper.number_to_rounded(variant_unit_scale, + precision: nil, + strip_insignificant_zeros: true) + [variant_unit, scale_clean].compact_blank.join("_") + end + + def variant_unit_with_scale=(variant_unit_with_scale) + values = variant_unit_with_scale.split("_") + assign_attributes( + variant_unit: values[0], + variant_unit_scale: values[1] || nil + ) + end + private def update_units diff --git a/app/models/spree/shipment.rb b/app/models/spree/shipment.rb index 476658b13a..0b060c727c 100644 --- a/app/models/spree/shipment.rb +++ b/app/models/spree/shipment.rb @@ -346,7 +346,7 @@ module Spree def after_ship inventory_units.each(&:ship!) fee_adjustment.finalize! - send_shipped_email + send_shipped_email if order.send_shipment_email touch :shipped_at update_order_shipment_state end diff --git a/app/models/spree/user.rb b/app/models/spree/user.rb index fce0828b4c..56c0607262 100644 --- a/app/models/spree/user.rb +++ b/app/models/spree/user.rb @@ -42,6 +42,7 @@ module Spree has_many :credit_cards, dependent: :destroy has_many :report_rendering_options, class_name: "::ReportRenderingOptions", dependent: :destroy has_many :webhook_endpoints, dependent: :destroy + has_one :oidc_account, dependent: :destroy accepts_nested_attributes_for :enterprise_roles, allow_destroy: true accepts_nested_attributes_for :webhook_endpoints @@ -51,11 +52,6 @@ module Spree validates :email, 'valid_email_2/email': { mx: true }, if: :email_changed? validate :limit_owned_enterprises - validates :uid, uniqueness: true, if: lambda { uid.present? } - - # Same validation as in the openid_connect gem. - # This validator is totally outdated but we indirectly depend on it. - validates :uid, email: true, if: lambda { uid.present? } class DestroyWithOrdersError < StandardError; end @@ -63,10 +59,6 @@ module Spree User.admin.count > 0 end - def link_from_omniauth(auth) - update!(provider: auth.provider, uid: auth.uid) - end - # Whether a user has a role or not. def has_spree_role?(role_in_question) spree_roles.where(name: role_in_question.to_s).any? diff --git a/app/models/spree/variant.rb b/app/models/spree/variant.rb index f470f4bb51..add27c8ad2 100644 --- a/app/models/spree/variant.rb +++ b/app/models/spree/variant.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true require 'open_food_network/enterprise_fee_calculator' -require 'variant_units/variant_and_line_item_naming' -require 'concerns/variant_stock' require 'spree/localized_number' module Spree @@ -58,6 +56,7 @@ module Spree has_many :exchanges, through: :exchange_variants has_many :variant_overrides, dependent: :destroy has_many :inventory_items, dependent: :destroy + has_many :semantic_links, dependent: :delete_all localize_number :price, :weight @@ -71,7 +70,7 @@ module Spree %w(weight volume).include?(variant.product&.variant_unit) } - validates :unit_value, numericality: { greater_than: 0 } + validates :unit_value, numericality: { greater_than: 0 }, allow_blank: true validates :price, numericality: { greater_than_or_equal_to: 0 } validates :unit_description, presence: true, if: ->(variant) { @@ -169,11 +168,7 @@ module Spree end def tax_category - if self[:tax_category_id].nil? - TaxCategory.find_by(is_default: true) - else - TaxCategory.find(self[:tax_category_id]) - end + super || TaxCategory.find_by(is_default: true) end def price_with_fees(distributor, order_cycle) diff --git a/app/models/stripe_account.rb b/app/models/stripe_account.rb index a18d37b8ac..c0b507f316 100644 --- a/app/models/stripe_account.rb +++ b/app/models/stripe_account.rb @@ -14,9 +14,9 @@ class StripeAccount < ApplicationRecord return destroy if accounts.count > 1 destroy && Stripe::OAuth.deauthorize(stripe_user_id:) - rescue Stripe::OAuth::OAuthError + rescue Stripe::OAuth::OAuthError => e Bugsnag.notify( - RuntimeError.new("StripeDeauthorizeFailure"), + e, stripe_account: stripe_user_id, enterprise_id: ) diff --git a/app/queries/admin/enterprise_roles_query.rb b/app/queries/admin/enterprise_roles_query.rb new file mode 100644 index 0000000000..4828bb8ec1 --- /dev/null +++ b/app/queries/admin/enterprise_roles_query.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Admin + class EnterpriseRolesQuery + class << self + def query + enterprise_roles = query_enterprise_roles + users = query_users + enterprises = query_enterprises + + [enterprise_roles, users, enterprises] + end + + private + + def query_enterprise_roles + EnterpriseRole.joins(:user, :enterprise).order('spree_users.email ASC'). + pluck(:id, :user_id, :enterprise_id, 'spree_users.email', 'enterprises.name'). + map do |data| + id, user_id, enterprise_id, user_email, enterprise_name = data + + { id:, user_id:, enterprise_id:, user_email:, enterprise_name: } + end + end + + def query_users + Spree::User.order(:email).pluck(:id, :email).map do |data| + id, email = data + + { id:, email: } + end + end + + def query_enterprises + Enterprise.order(:name).pluck(:id, :name).map do |data| + id, name = data + + { id:, name: } + end + end + end + end +end diff --git a/app/queries/complete_orders_with_balance.rb b/app/queries/complete_orders_with_balance_query.rb similarity index 74% rename from app/queries/complete_orders_with_balance.rb rename to app/queries/complete_orders_with_balance_query.rb index 57223e5c6e..c6507f45bb 100644 --- a/app/queries/complete_orders_with_balance.rb +++ b/app/queries/complete_orders_with_balance_query.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true # Fetches complete orders of the specified user including their balance as a computed column -class CompleteOrdersWithBalance +class CompleteOrdersWithBalanceQuery def initialize(user) @user = user end - def query - OutstandingBalance.new(sorted_finalized_orders).query + def call + OutstandingBalanceQuery.new(sorted_finalized_orders).call end private diff --git a/app/queries/complete_visible_orders.rb b/app/queries/complete_visible_orders_query.rb similarity index 83% rename from app/queries/complete_visible_orders.rb rename to app/queries/complete_visible_orders_query.rb index 10eabe4f6d..22e427fec6 100644 --- a/app/queries/complete_visible_orders.rb +++ b/app/queries/complete_visible_orders_query.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -class CompleteVisibleOrders +class CompleteVisibleOrdersQuery def initialize(order_permissions) @order_permissions = order_permissions end - def query + def call order_permissions.visible_orders.complete end diff --git a/app/queries/customers_with_balance.rb b/app/queries/customers_with_balance_query.rb similarity index 88% rename from app/queries/customers_with_balance.rb rename to app/queries/customers_with_balance_query.rb index 623190ff4c..47d0fabca9 100644 --- a/app/queries/customers_with_balance.rb +++ b/app/queries/customers_with_balance_query.rb @@ -2,12 +2,12 @@ # Adds an aggregated 'balance_value' to each customer based on their order history # -class CustomersWithBalance +class CustomersWithBalanceQuery def initialize(customers) @customers = customers end - def query + def call @customers. joins(left_join_complete_orders). group("customers.id"). @@ -20,7 +20,7 @@ class CustomersWithBalance # The resulting orders are in states that belong after the checkout. Only these can be considered # for a customer's balance. def left_join_complete_orders - <<~SQL + <<~SQL.squish LEFT JOIN spree_orders ON spree_orders.customer_id = customers.id AND #{finalized_states.to_sql} SQL @@ -32,6 +32,6 @@ class CustomersWithBalance end def outstanding_balance_sum - "SUM(#{OutstandingBalance.new.statement})::float" + "SUM(#{OutstandingBalanceQuery.new.statement})::float" end end diff --git a/app/queries/outstanding_balance.rb b/app/queries/outstanding_balance_query.rb similarity index 92% rename from app/queries/outstanding_balance.rb rename to app/queries/outstanding_balance_query.rb index 637451b25c..c7d37fd62f 100644 --- a/app/queries/outstanding_balance.rb +++ b/app/queries/outstanding_balance_query.rb @@ -7,11 +7,11 @@ # Alternatively, you can get the SQL by calling #statement, which is suitable for more complex # cases. # -# See CompleteOrdersWithBalance or CustomersWithBalance as examples. +# See CompleteOrdersWithBalanceQuery or CustomersWithBalanceQuery as examples. # # Note this query object and `app/models/concerns/balance.rb` should implement the same behavior # until we find a better way. If you change one, please, change the other too. -class OutstandingBalance +class OutstandingBalanceQuery # All the states of a finished order but that shouldn't count towards the balance (the customer # didn't get the order for whatever reason). Note it does not include complete FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze @@ -22,14 +22,14 @@ class OutstandingBalance @relation = relation end - def query + def call relation.select("#{statement} AS balance_value") end # Arel doesn't support CASE statements until v7.1.0 so we'll have to wait with SQL literals # a little longer. See https://github.com/rails/arel/pull/400 for details. def statement - <<~SQL + <<~SQL.squish CASE WHEN "spree_orders"."state" IN #{non_fulfilled_states_group.to_sql} THEN "spree_orders"."payment_total" WHEN "spree_orders"."state" IS NOT NULL THEN "spree_orders"."payment_total" - "spree_orders"."total" ELSE 0 END diff --git a/app/queries/payments_requiring_action.rb b/app/queries/payments_requiring_action_query.rb similarity index 54% rename from app/queries/payments_requiring_action.rb rename to app/queries/payments_requiring_action_query.rb index ffe705e955..c8fe5eda23 100644 --- a/app/queries/payments_requiring_action.rb +++ b/app/queries/payments_requiring_action_query.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -class PaymentsRequiringAction +class PaymentsRequiringActionQuery def initialize(user) @user = user end - def query - Spree::Payment.joins(:order).where("spree_orders.user_id = ?", user.id). + def call + Spree::Payment.joins(:order).where(spree_orders: { user_id: user.id }). requires_authorization end diff --git a/app/reflexes/admin/connected_app_reflex.rb b/app/reflexes/admin/connected_app_reflex.rb new file mode 100644 index 0000000000..ecfb974d96 --- /dev/null +++ b/app/reflexes/admin/connected_app_reflex.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Admin + class ConnectedAppReflex < ApplicationReflex + def create + authorize! :admin, enterprise + + app = ConnectedApp.create!(enterprise_id: enterprise.id) + + # Avoid race condition by sending before enqueuing job: + broadcast_partial + + ConnectAppJob.perform_later( + app, current_user.spree_api_key, + channel: SessionChannel.for_request(request), + ) + morph :nothing + end + + def destroy + authorize! :admin, enterprise + + app = enterprise.connected_apps.first + app.destroy + + broadcast_partial + + WebhookDeliveryJob.perform_later( + app.data["destroy"], + "disconnect-app", + nil + ) + morph :nothing + end + + private + + def enterprise + @enterprise ||= Enterprise.find(element.dataset.enterprise_id) + end + + def broadcast_partial + selector = "#edit_enterprise_#{enterprise.id} #connected-app-discover-regen" + html = ApplicationController.render( + partial: "admin/enterprises/form/connected_apps", + locals: { enterprise: }, + ) + + # Avoid race condition by sending before enqueuing job: + cable_ready.morph(selector:, html:).broadcast + end + end +end diff --git a/app/reflexes/admin/orders_reflex.rb b/app/reflexes/admin/orders_reflex.rb index 765438d7cc..8e5f43eb65 100644 --- a/app/reflexes/admin/orders_reflex.rb +++ b/app/reflexes/admin/orders_reflex.rb @@ -5,11 +5,13 @@ module Admin before_reflex :authorize_order, only: [:capture, :ship] def capture - payment_capture = OrderCaptureService.new(@order) + payment_capture = Orders::CaptureService.new(@order) if payment_capture.call - morph dom_id(@order), render(partial: "spree/admin/orders/table_row", - locals: { order: @order.reload, success: true }) + cable_ready.replace(selector: dom_id(@order), + html: render(partial: "spree/admin/orders/table_row", + locals: { order: @order.reload, success: true })) + morph :nothing else flash[:error] = payment_capture.gateway_error || I18n.t(:payment_processing_failed) morph_admin_flashes @@ -17,7 +19,10 @@ module Admin end def ship + @order.send_shipment_email = false unless params[:send_shipment_email] if @order.ship + return set_param_for_controller if request.url.match?('edit') + morph dom_id(@order), render(partial: "spree/admin/orders/table_row", locals: { order: @order.reload, success: true }) else @@ -27,13 +32,20 @@ module Admin end def bulk_invoice(params) + visible_orders = editable_orders.where(id: params[:bulk_ids]).filter(&:invoiceable?) + if Spree::Config.enterprise_number_required_on_invoices? && + !all_distributors_can_invoice?(visible_orders) + render_business_number_required_error(visible_orders) + return + end + cable_ready.append( selector: "#orders-index", html: render(partial: "spree/admin/orders/bulk/invoice_modal") ).broadcast BulkInvoiceJob.perform_later( - params[:bulk_ids], + visible_orders.pluck(:id), "tmp/invoices/#{Time.zone.now.to_i}-#{SecureRandom.hex(2)}.pdf", channel: SessionChannel.for_request(request), current_user_id: current_user.id @@ -43,7 +55,7 @@ module Admin end def cancel_orders(params) - cancelled_orders = OrdersBulkCancelService.new(params, current_user).call + cancelled_orders = Orders::BulkCancelService.new(params, current_user).call cable_ready.dispatch_event(name: "modal:close") @@ -83,7 +95,8 @@ module Admin private def authorize_order - @order = Spree::Order.find_by(id: element.dataset[:id]) + id = element.dataset[:id] || params[:id] + @order = Spree::Order.find_by(id:) authorize! :admin, @order end @@ -96,5 +109,23 @@ module Admin def editable_orders Permissions::Order.new(current_user).editable_orders end + + def set_param_for_controller + params[:id] = @order.number + end + + def all_distributors_can_invoice?(orders) + distributor_ids = orders.map(&:distributor_id) + Enterprise.where(id: distributor_ids, abn: nil).empty? + end + + def render_business_number_required_error(orders) + distributor_ids = orders.map(&:distributor_id) + distributor_names = Enterprise.where(id: distributor_ids, abn: nil).pluck(:name) + + flash[:error] = I18n.t(:must_have_valid_business_number, + enterprise_name: distributor_names.join(", ")) + morph_admin_flashes + end end end diff --git a/app/reflexes/products_reflex.rb b/app/reflexes/products_reflex.rb index c75eddce24..b312c74205 100644 --- a/app/reflexes/products_reflex.rb +++ b/app/reflexes/products_reflex.rb @@ -6,20 +6,20 @@ class ProductsReflex < ApplicationReflex before_reflex :init_filters_params, :init_pagination_params def fetch - fetch_and_render_products + fetch_and_render_products_with_flash end def change_per_page @per_page = element.value.to_i @page = 1 - fetch_and_render_products + fetch_and_render_products_with_flash end def filter @page = 1 - fetch_and_render_products + fetch_and_render_products_with_flash end def clear_search @@ -28,7 +28,7 @@ class ProductsReflex < ApplicationReflex @category_id = nil @page = 1 - fetch_and_render_products + fetch_and_render_products_with_flash end def bulk_update @@ -38,13 +38,52 @@ class ProductsReflex < ApplicationReflex @products = product_set.collection # use instance variable mainly for testing if product_set.save - # flash[:success] = with_locale { I18n.t('.success') } - # morph_admin_flashes # ERROR: selector morph type has already been set + flash[:success] = I18n.t('admin.products_v3.bulk_update.success') elsif product_set.errors.present? @error_counts = { saved: product_set.saved_count, invalid: product_set.invalid.count } end - render_products_form + render_products_form_with_flash + end + + def delete_product + id = current_id_from_element(element) + product = product_finder(id).find_product + authorize! :delete, product + + if product.destroy + flash[:success] = I18n.t('admin.products_v3.delete_product.success') + else + flash[:error] = I18n.t('admin.products_v3.delete_product.error') + end + + fetch_and_render_products_with_flash + end + + def delete_variant + id = current_id_from_element(element) + variant = Spree::Variant.active.find(id) + authorize! :delete, variant + + if VariantDeleter.new.delete(variant) + flash[:success] = I18n.t('admin.products_v3.delete_variant.success') + else + flash[:error] = I18n.t('admin.products_v3.delete_variant.error') + end + + fetch_and_render_products_with_flash + end + + def edit_image + id = current_id_from_element(element) + product = product_finder(id).find_product + image = product.image + + image = Spree::Image.new(viewable: product) if product.image.blank? + + morph "#modal-component", + render(partial: "admin/products_v3/edit_image", + locals: { product:, image:, return_url: url }) end private @@ -64,7 +103,7 @@ class ProductsReflex < ApplicationReflex @per_page = element.dataset.perpage || params[:_per_page] || 15 end - def fetch_and_render_products + def fetch_and_render_products_with_flash fetch_products render_products end @@ -75,29 +114,30 @@ class ProductsReflex < ApplicationReflex html: render(partial: "admin/products_v3/content", locals: { products: @products, pagy: @pagy, search_term: @search_term, producer_options: producers, producer_id: @producer_id, - category_options: categories, category_id: @category_id }) - ).broadcast + category_options: categories, category_id: @category_id, + flashes: flash }) + ) cable_ready.replace_state( url: current_url, - ).broadcast_later + ) morph :nothing end - def render_products_form + def render_products_form_with_flash locals = { products: @products } locals[:error_counts] = @error_counts if @error_counts.present? + locals[:flashes] = flash if flash.any? cable_ready.replace( selector: "#products-form", html: render(partial: "admin/products_v3/table", locals:) - ).broadcast + ) morph :nothing - # dunno why this doesn't work. - # morph "#products-form", render(partial: "admin/products_v3/table", - # locals: { products: products }) + # dunno why this doesn't work. The HTML stops after the first `` element, wtf?! + # morph "#products-form", render(partial: "admin/products_v3/table", locals:) end def producers @@ -195,4 +235,12 @@ class ProductsReflex < ApplicationReflex params.permit(products: ::PermittedAttributes::Product.attributes) .to_h.with_indifferent_access end + + def product_finder(id) + ProductScopeQuery.new(current_user, { id: }) + end + + def current_id_from_element(element) + element.dataset.current_id + end end diff --git a/app/reflexes/user_reflex.rb b/app/reflexes/user_reflex.rb new file mode 100644 index 0000000000..d4b5f59be5 --- /dev/null +++ b/app/reflexes/user_reflex.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class UserReflex < ApplicationReflex + def accept_terms_of_services + current_user.update(terms_of_service_accepted_at: DateTime.now) + + morph "#banner-container", "" + end +end diff --git a/app/serializers/api/admin/customer_with_balance_serializer.rb b/app/serializers/api/admin/customer_with_balance_serializer.rb index fb043dda76..90f2726a10 100644 --- a/app/serializers/api/admin/customer_with_balance_serializer.rb +++ b/app/serializers/api/admin/customer_with_balance_serializer.rb @@ -3,8 +3,8 @@ module Api module Admin # This serializer relies on `object` to respond to `#balance_value`. That's done in - # `CustomersWithBalance` due to the fact that ActiveRecord maps the DB result set's columns to - # instance methods. This way, the `balance_value` alias on that class ends up being + # `CustomersWithBalanceQuery` due to the fact that ActiveRecord maps the DB result set's + # columns to instance methods. This way, the `balance_value` alias on that class ends up being # `object.balance_value` here. class CustomerWithBalanceSerializer < CustomerSerializer attributes :balance, :balance_status diff --git a/app/serializers/api/order_serializer.rb b/app/serializers/api/order_serializer.rb index 067d23b76d..b8fa04dff2 100644 --- a/app/serializers/api/order_serializer.rb +++ b/app/serializers/api/order_serializer.rb @@ -9,8 +9,8 @@ module Api has_many :payments, serializer: Api::PaymentSerializer - # This method relies on `balance_value` as a computed DB column. See `CompleteOrdersWithBalance` - # for reference. + # This method relies on `balance_value` as a computed DB column. + # See `CompleteOrdersWithBalanceQuery` for reference. def outstanding_balance -object.balance_value end diff --git a/app/serializers/invoice/line_item_serializer.rb b/app/serializers/invoice/line_item_serializer.rb index f3c5aa83a1..50e566850d 100644 --- a/app/serializers/invoice/line_item_serializer.rb +++ b/app/serializers/invoice/line_item_serializer.rb @@ -3,8 +3,16 @@ class Invoice class LineItemSerializer < ActiveModel::Serializer attributes :id, :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity, - :variant_id, :unit_price_price_and_unit, :unit_presentation + :variant_id, :unit_price_price_and_unit, :unit_presentation, + :enterprise_fee_additional_tax, :enterprise_fee_included_tax has_one :variant, serializer: Invoice::VariantSerializer - has_many :tax_rates, serializer: Invoice::TaxRateSerializer + + def enterprise_fee_additional_tax + EnterpriseFeeAdjustments.new(object.enterprise_fee_adjustments).total_additional_tax + end + + def enterprise_fee_included_tax + EnterpriseFeeAdjustments.new(object.enterprise_fee_adjustments).total_included_tax + end end end diff --git a/app/services/cap_quantity.rb b/app/services/cap_quantity.rb index c1bfefaf5a..27bb86302c 100644 --- a/app/services/cap_quantity.rb +++ b/app/services/cap_quantity.rb @@ -46,6 +46,7 @@ class CapQuantity end def available_variants_for - OrderCycleDistributedVariants.new(order.order_cycle, order.distributor).available_variants + OrderCycles::DistributedVariantsService.new(order.order_cycle, + order.distributor).available_variants end end diff --git a/app/services/cart_service.rb b/app/services/cart_service.rb index 97a53613b0..ce6bc85652 100644 --- a/app/services/cart_service.rb +++ b/app/services/cart_service.rb @@ -154,7 +154,7 @@ class CartService end def check_variant_available_under_distribution(variant) - return true if OrderCycleDistributedVariants.new(@order_cycle, @distributor) + return true if OrderCycles::DistributedVariantsService.new(@order_cycle, @distributor) .available_variants.include? variant errors.add(:base, I18n.t(:spree_order_populator_availability_error)) diff --git a/app/services/checkout/post_checkout_actions.rb b/app/services/checkout/post_checkout_actions.rb index 5e8071012d..5484a70c09 100644 --- a/app/services/checkout/post_checkout_actions.rb +++ b/app/services/checkout/post_checkout_actions.rb @@ -14,7 +14,7 @@ module Checkout def failure @order.updater.shipping_address_from_distributor - OrderCheckoutRestart.new(@order).call + Orders::CheckoutRestartService.new(@order).call end private diff --git a/app/services/create_order_cycle.rb b/app/services/create_order_cycle.rb deleted file mode 100644 index 50900f1c88..0000000000 --- a/app/services/create_order_cycle.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -# Creates an order cycle for the provided enterprise and selecting all the -# variants specified for both incoming and outgoing exchanges -class CreateOrderCycle - # Constructor - # - # @param enterprise [Enterprise] - # @param variants [Array] - def initialize(enterprise, variants) - @enterprise = enterprise - @variants = variants - end - - # Creates the order cycle - def call - incoming_exchange.order_cycle = order_cycle - incoming_exchange.variants << variants - - outgoing_exchange.order_cycle = order_cycle - outgoing_exchange.variants << variants - - order_cycle.exchanges << incoming_exchange - order_cycle.exchanges << outgoing_exchange - - order_cycle.save! - end - - private - - attr_reader :enterprise, :variants - - # Builds an order cycle for the next month, starting now - # - # @return [OrderCycle] - def order_cycle - @order_cycle ||= OrderCycle.new( - coordinator_id: enterprise.id, - name: 'Monthly order cycle', - orders_open_at: Time.zone.now, - orders_close_at: 1.month.from_now - ) - end - - # Builds an exchange with the enterprise both as sender and receiver - # - # @return [Exchange] - def incoming_exchange - @incoming_exchange ||= Exchange.new( - sender_id: enterprise.id, - receiver_id: enterprise.id, - incoming: true - ) - end - - # Builds an exchange with the enterprise both as sender and receiver - # - # @return [Exchange] - def outgoing_exchange - @outgoing_exchange ||= Exchange.new( - sender_id: enterprise.id, - receiver_id: enterprise.id, - pickup_time: '8 am', - incoming: false - ) - end -end diff --git a/app/services/customer_order_cancellation.rb b/app/services/customer_order_cancellation.rb deleted file mode 100644 index 2e3f51c6e6..0000000000 --- a/app/services/customer_order_cancellation.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class CustomerOrderCancellation - def initialize(order) - @order = order - end - - def call - return unless order.cancel - - Spree::OrderMailer.cancel_email_for_shop(order).deliver_later - end - - private - - attr_reader :order -end diff --git a/app/services/default_country.rb b/app/services/default_country.rb index 609535a1fc..0e07d5c68c 100644 --- a/app/services/default_country.rb +++ b/app/services/default_country.rb @@ -10,7 +10,10 @@ class DefaultCountry end def self.country - Spree::Country.cached_find_by(iso: ENV.fetch("DEFAULT_COUNTRY_CODE", - nil)) || Spree::Country.first + # Changing ENV requires restarting the process. + iso = ENV.fetch("DEFAULT_COUNTRY_CODE", nil) + + # When ENV changes on restart, this cache will be reset as well. + @country ||= Spree::Country.find_by(iso:) || Spree::Country.first end end diff --git a/app/services/order_adjustments_fetcher.rb b/app/services/order_adjustments_fetcher.rb deleted file mode 100644 index 6f236074b9..0000000000 --- a/app/services/order_adjustments_fetcher.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# This class allows orders with eager-loaded adjustment objects to calculate various adjustment -# types without triggering additional queries. -# -# For example; `order.adjustments.shipping.sum(:amount)` would normally trigger a new query -# regardless of whether or not adjustments have been preloaded, as `#shipping` is an adjustment -# scope, eg; `scope :shipping, where(originator_type: 'Spree::ShippingMethod')`. -# -# Here the adjustment scopes are moved to a shared module, and `adjustments.loaded?` is used to -# check if the objects have already been fetched and initialized. If they have, `order.adjustments` -# will be an Array, and we can select the required objects without hitting the database. If not, it -# will fetch the adjustments via their scopes as normal. - -class OrderAdjustmentsFetcher - include AdjustmentScopes - - def initialize(order) - @order = order - end - - def admin_and_handling_total - admin_and_handling_fees.map(&:amount).sum - end - - def payment_fee - sum_adjustments "payment_fee" - end - - def ship_total - sum_adjustments "shipping" - end - - private - - attr_reader :order - - def adjustments - order.all_adjustments - end - - def adjustments_eager_loaded? - adjustments.loaded? - end - - def sum_adjustments(scope) - collect_adjustments(scope).map(&:amount).sum - end - - def collect_adjustments(scope) - if adjustments_eager_loaded? - adjustment_scope = public_send("#{scope}_scope") - - # Adjustments are already loaded here, this block is using `Array#select` - adjustments.select do |adjustment| - match_by_scope(adjustment, adjustment_scope) && match_by_scope(adjustment, eligible_scope) - end - else - adjustments.where(nil).eligible.public_send scope - end - end - - def admin_and_handling_fees - if adjustments_eager_loaded? - adjustments.select do |adjustment| - match_by_scope(adjustment, eligible_scope) && - adjustment.originator_type == 'EnterpriseFee' && - adjustment.adjustable_type != 'Spree::LineItem' - end - else - adjustments.eligible. - where("originator_type = ? AND adjustable_type != ?", 'EnterpriseFee', 'Spree::LineItem') - end - end - - def match_by_scope(adjustment, scope) - adjustment.public_send(scope.keys.first) == scope.values.first - end -end diff --git a/app/services/order_available_payment_methods.rb b/app/services/order_available_payment_methods.rb deleted file mode 100644 index dc0784d909..0000000000 --- a/app/services/order_available_payment_methods.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -class OrderAvailablePaymentMethods - attr_reader :order, :customer - - delegate :distributor, - :order_cycle, - to: :order - - def initialize(order, customer = nil) - @order, @customer = order, customer - end - - def to_a - return [] if distributor.blank? - - payment_methods = payment_methods_before_tag_rules_applied - - applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor, - "FilterPaymentMethods", customer&.tag_list) - applicator.filter(payment_methods) - end - - private - - def payment_methods_before_tag_rules_applied - if order_cycle.nil? || order_cycle.simple? - distributor.payment_methods - else - distributor.payment_methods.where( - id: available_distributor_payment_methods_ids - ) - end.available.select(&:configured?).uniq - end - - def available_distributor_payment_methods_ids - order_cycle.distributor_payment_methods - .where(distributor_id: distributor.id) - .select(:payment_method_id) - end -end diff --git a/app/services/order_available_shipping_methods.rb b/app/services/order_available_shipping_methods.rb deleted file mode 100644 index 9b4bcf76ba..0000000000 --- a/app/services/order_available_shipping_methods.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -class OrderAvailableShippingMethods - attr_reader :order, :customer - - delegate :distributor, :order_cycle, to: :order - - def initialize(order, customer = nil) - @order, @customer = order, customer - end - - def to_a - return [] if distributor.blank? - - filter_by_category(tag_rules.filter(shipping_methods)) - end - - private - - def filter_by_category(methods) - return methods unless OpenFoodNetwork::FeatureToggle.enabled?(:match_shipping_categories, - distributor&.owner) - - required_category_ids = order.variants.pluck(:shipping_category_id).to_set - return methods if required_category_ids.empty? - - methods.select do |method| - provided_category_ids = method.shipping_categories.pluck(:id).to_set - required_category_ids.subset?(provided_category_ids) - end - end - - def shipping_methods - if order_cycle.nil? || order_cycle.simple? - distributor.shipping_methods - else - distributor.shipping_methods.where( - id: available_distributor_shipping_methods_ids - ) - end.frontend.to_a.uniq - end - - def available_distributor_shipping_methods_ids - order_cycle.distributor_shipping_methods - .where(distributor_id: distributor.id) - .select(:shipping_method_id) - end - - def tag_rules - OpenFoodNetwork::TagRuleApplicator.new( - distributor, "FilterShippingMethods", customer&.tag_list - ) - end -end diff --git a/app/services/order_capture_service.rb b/app/services/order_capture_service.rb deleted file mode 100644 index 78e69c9a1d..0000000000 --- a/app/services/order_capture_service.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -# Use `authorize! :admin order` before calling this service - -class OrderCaptureService - attr_reader :gateway_error - - def initialize(order) - @order = order - @gateway_error = nil - end - - def call - return false unless @order.payment_required? - return false unless (pending_payment = @order.pending_payments.first) - - pending_payment.capture! - rescue Spree::Core::GatewayError => e - @gateway_error = e - false - end -end diff --git a/app/services/order_cart_reset.rb b/app/services/order_cart_reset.rb deleted file mode 100644 index e84fd67e88..0000000000 --- a/app/services/order_cart_reset.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: false - -# Resets an order by verifying it's state and fixing any issues -class OrderCartReset - def initialize(order, distributor_id) - @order = order - @distributor ||= Enterprise.is_distributor.find_by(permalink: distributor_id) || - Enterprise.is_distributor.find(distributor_id) - end - - def reset_distributor - if order.distributor && order.distributor != distributor - order.empty! - order.set_order_cycle! nil - end - order.distributor = distributor - end - - def reset_other!(current_user, current_customer) - reset_user_and_customer(current_user) - reset_order_cycle(current_customer) - order.save! - end - - private - - attr_reader :order, :distributor, :current_user - - def reset_user_and_customer(current_user) - return unless current_user - - order.associate_user!(current_user) if order.user.blank? || order.email.blank? - end - - def reset_order_cycle(current_customer) - listed_order_cycles = Shop::OrderCyclesList.active_for(distributor, current_customer) - - if order_cycle_not_listed?(order.order_cycle, listed_order_cycles) - order.order_cycle = nil - order.empty! - end - - select_default_order_cycle(order, listed_order_cycles) - end - - def order_cycle_not_listed?(order_cycle, listed_order_cycles) - order_cycle.present? && !listed_order_cycles.include?(order_cycle) - end - - # If no OC is selected and there is only one in the list of OCs, selects it - def select_default_order_cycle(order, listed_order_cycles) - return unless order.order_cycle.blank? && listed_order_cycles.size == 1 - - order.order_cycle = listed_order_cycles.first - end -end diff --git a/app/services/order_checkout_restart.rb b/app/services/order_checkout_restart.rb deleted file mode 100644 index f43380636f..0000000000 --- a/app/services/order_checkout_restart.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -# Resets the passed order to cart state while clearing associated payments and shipments -class OrderCheckoutRestart - def initialize(order) - @order = order - end - - def call - return if order.cart? - - reset_state_to_cart - clear_shipments - clear_payments - - order.reload.update_order! - end - - private - - attr_reader :order - - def reset_state_to_cart - order.restart_checkout! - end - - def clear_shipments - order.shipments.with_state(:pending).destroy_all - end - - def clear_payments - order.payments.with_state(:checkout).destroy_all - end -end diff --git a/app/services/order_cycle_clone.rb b/app/services/order_cycle_clone.rb deleted file mode 100644 index fffe6a1277..0000000000 --- a/app/services/order_cycle_clone.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'order_management/subscriptions/proxy_order_syncer' - -class OrderCycleClone - def initialize(order_cycle) - @original_order_cycle = order_cycle - end - - def create - oc = @original_order_cycle.dup - oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name) - oc.orders_open_at = oc.orders_close_at = oc.mails_sent = oc.processed_at = nil - oc.coordinator_fee_ids = @original_order_cycle.coordinator_fee_ids - oc.preferred_product_selection_from_coordinator_inventory_only = - @original_order_cycle.preferred_product_selection_from_coordinator_inventory_only - oc.schedule_ids = @original_order_cycle.schedule_ids - oc.save! - @original_order_cycle.exchanges.each { |e| e.clone!(oc) } - oc.selected_distributor_payment_method_ids = selected_distributor_payment_method_ids - oc.selected_distributor_shipping_method_ids = selected_distributor_shipping_method_ids - sync_subscriptions - oc.reload - end - - private - - def selected_distributor_payment_method_ids - @original_order_cycle.attachable_distributor_payment_methods.map(&:id) & - @original_order_cycle.selected_distributor_payment_method_ids - end - - def selected_distributor_shipping_method_ids - @original_order_cycle.attachable_distributor_shipping_methods.map(&:id) & - @original_order_cycle.selected_distributor_shipping_method_ids - end - - def sync_subscriptions - return unless @original_order_cycle.schedule_ids.any? - - OrderManagement::Subscriptions::ProxyOrderSyncer.new( - Subscription.where(schedule_id: @original_order_cycle.schedule_ids) - ).sync! - end -end diff --git a/app/services/order_cycle_distributed_products.rb b/app/services/order_cycle_distributed_products.rb deleted file mode 100644 index afe47b6cf7..0000000000 --- a/app/services/order_cycle_distributed_products.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -# Returns a (paginatable) AR object for the products or variants in stock for a given shop and OC. -# The stock-checking includes on_demand and stock level overrides from variant_overrides. -class OrderCycleDistributedProducts - def initialize(distributor, order_cycle, customer) - @distributor = distributor - @order_cycle = order_cycle - @customer = customer - end - - def products_relation - Spree::Product.where(id: stocked_products).group("spree_products.id") - end - - def variants_relation - order_cycle. - variants_distributed_by(distributor). - merge(stocked_variants_and_overrides). - select("DISTINCT spree_variants.*") - end - - private - - attr_reader :distributor, :order_cycle, :customer - - def stocked_products - order_cycle. - variants_distributed_by(distributor). - merge(stocked_variants_and_overrides). - select("DISTINCT spree_variants.product_id") - end - - def stocked_variants_and_overrides - stocked_variants = Spree::Variant. - joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id - AND variant_overrides.hub_id = #{distributor.id}"). - joins(:stock_items). - where(query_stock_with_overrides) - - ProductTagRulesFilterer.new(distributor, customer, stocked_variants).call - end - - def query_stock_with_overrides - "( #{variant_not_overriden} AND ( #{variant_on_demand} OR #{variant_in_stock} ) ) - OR ( #{variant_overriden} AND ( #{override_on_demand} OR #{override_in_stock} ) ) - OR ( #{variant_overriden} AND ( #{override_on_demand_null} AND #{variant_on_demand} ) ) - OR ( #{variant_overriden} AND ( #{override_on_demand_null} - AND #{variant_not_on_demand} AND #{variant_in_stock} ) )" - end - - def variant_not_overriden - "variant_overrides.id IS NULL" - end - - def variant_overriden - "variant_overrides.id IS NOT NULL" - end - - def variant_in_stock - "spree_stock_items.count_on_hand > 0" - end - - def variant_on_demand - "spree_stock_items.backorderable IS TRUE" - end - - def variant_not_on_demand - "spree_stock_items.backorderable IS FALSE" - end - - def override_on_demand - "variant_overrides.on_demand IS TRUE" - end - - def override_in_stock - "variant_overrides.count_on_hand > 0" - end - - def override_on_demand_null - "variant_overrides.on_demand IS NULL" - end -end diff --git a/app/services/order_cycle_distributed_variants.rb b/app/services/order_cycle_distributed_variants.rb deleted file mode 100644 index 1b5c64c83e..0000000000 --- a/app/services/order_cycle_distributed_variants.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class OrderCycleDistributedVariants - def initialize(order_cycle, distributor) - @order_cycle = order_cycle - @distributor = distributor - end - - def distributes_order_variants?(order) - unavailable_order_variants(order).empty? - end - - def unavailable_order_variants(order) - order.line_item_variants - available_variants - end - - def available_variants - return [] unless @order_cycle - - @order_cycle.variants_distributed_by(@distributor) - end -end diff --git a/app/services/order_cycle_form.rb b/app/services/order_cycle_form.rb deleted file mode 100644 index c25c589ce5..0000000000 --- a/app/services/order_cycle_form.rb +++ /dev/null @@ -1,231 +0,0 @@ -# frozen_string_literal: true - -require 'open_food_network/permissions' -require 'open_food_network/order_cycle_form_applicator' -require 'order_management/subscriptions/proxy_order_syncer' - -class OrderCycleForm - def initialize(order_cycle, order_cycle_params, user) - @order_cycle = order_cycle - @order_cycle_params = order_cycle_params - @specified_params = order_cycle_params.keys - @user = user - @permissions = OpenFoodNetwork::Permissions.new(user) - @schedule_ids = order_cycle_params.delete(:schedule_ids) - @selected_distributor_payment_method_ids = order_cycle_params.delete( - :selected_distributor_payment_method_ids - ) - @selected_distributor_shipping_method_ids = order_cycle_params.delete( - :selected_distributor_shipping_method_ids - ) - end - - def save - schedule_ids = build_schedule_ids - order_cycle.assign_attributes(order_cycle_params) - return false unless order_cycle.valid? - - order_cycle.transaction do - order_cycle.save! - order_cycle.schedule_ids = schedule_ids if parameter_specified?(:schedule_ids) - order_cycle.save! - apply_exchange_changes - if can_update_selected_payment_or_shipping_methods? - attach_selected_distributor_payment_methods - attach_selected_distributor_shipping_methods - end - sync_subscriptions - true - end - rescue ActiveRecord::RecordInvalid => e - add_exception_to_order_cycle_errors(e) - false - end - - private - - attr_accessor :order_cycle, :order_cycle_params, :user, :permissions - - def add_exception_to_order_cycle_errors(exception) - error = exception.message.split(":").last.strip - order_cycle.errors.add(:base, error) if order_cycle.errors.to_a.exclude?(error) - end - - def apply_exchange_changes - return if exchanges_unchanged? - - OpenFoodNetwork::OrderCycleFormApplicator.new(order_cycle, user).go! - - # reload so outgoing exchanges are up-to-date for shipping/payment method validations - order_cycle.reload - end - - def attach_selected_distributor_payment_methods - return if @selected_distributor_payment_method_ids.nil? - - if distributor_only? - payment_method_ids = order_cycle.selected_distributor_payment_method_ids - payment_method_ids -= user_distributor_payment_method_ids - payment_method_ids += user_only_selected_distributor_payment_method_ids - order_cycle.selected_distributor_payment_method_ids = payment_method_ids - else - order_cycle.selected_distributor_payment_method_ids = selected_distributor_payment_method_ids - end - order_cycle.save! - end - - def attach_selected_distributor_shipping_methods - return if @selected_distributor_shipping_method_ids.nil? - - if distributor_only? - # A distributor can only update methods associated with their own - # enterprise, so we load all previously selected methods, and replace - # only the distributor's methods with their selection (not touching other - # distributor's methods). - shipping_method_ids = order_cycle.selected_distributor_shipping_method_ids - shipping_method_ids -= user_distributor_shipping_method_ids - shipping_method_ids += user_only_selected_distributor_shipping_method_ids - order_cycle.selected_distributor_shipping_method_ids = shipping_method_ids - else - order_cycle.selected_distributor_shipping_method_ids = - selected_distributor_shipping_method_ids - end - - order_cycle.save! - end - - def attachable_distributor_payment_method_ids - @attachable_distributor_payment_method_ids ||= - order_cycle.attachable_distributor_payment_methods.map(&:id) - end - - def attachable_distributor_shipping_method_ids - @attachable_distributor_shipping_method_ids ||= - order_cycle.attachable_distributor_shipping_methods.map(&:id) - end - - def exchanges_unchanged? - [:incoming_exchanges, :outgoing_exchanges].all? do |direction| - order_cycle_params[direction].nil? - end - end - - def selected_distributor_payment_method_ids - @selected_distributor_payment_method_ids = ( - attachable_distributor_payment_method_ids & - @selected_distributor_payment_method_ids.compact_blank.map(&:to_i) - ) - - if attachable_distributor_payment_method_ids.sort == - @selected_distributor_payment_method_ids.sort - @selected_distributor_payment_method_ids = [] - end - - @selected_distributor_payment_method_ids - end - - def user_only_selected_distributor_payment_method_ids - user_distributor_payment_method_ids.intersection(selected_distributor_payment_method_ids) - end - - def selected_distributor_shipping_method_ids - @selected_distributor_shipping_method_ids = ( - attachable_distributor_shipping_method_ids & - @selected_distributor_shipping_method_ids.compact_blank.map(&:to_i) - ) - - if attachable_distributor_shipping_method_ids.sort == - @selected_distributor_shipping_method_ids.sort - @selected_distributor_shipping_method_ids = [] - end - - @selected_distributor_shipping_method_ids - end - - def user_only_selected_distributor_shipping_method_ids - user_distributor_shipping_method_ids.intersection(selected_distributor_shipping_method_ids) - end - - def build_schedule_ids - return unless parameter_specified?(:schedule_ids) - - result = existing_schedule_ids - result |= (requested_schedule_ids & permitted_schedule_ids) # Add permitted and requested - # Remove permitted but not requested - result -= ((result & permitted_schedule_ids) - requested_schedule_ids) - result - end - - def sync_subscriptions - return unless parameter_specified?(:schedule_ids) - return unless schedule_sync_required? - - OrderManagement::Subscriptions::ProxyOrderSyncer.new(subscriptions_to_sync).sync! - end - - def schedule_sync_required? - removed_schedule_ids.any? || new_schedule_ids.any? - end - - def subscriptions_to_sync - Subscription.where(schedule_id: removed_schedule_ids + new_schedule_ids) - end - - def requested_schedule_ids - @schedule_ids.map(&:to_i) - end - - def parameter_specified?(key) - @specified_params.map(&:to_s).include?(key.to_s) - end - - def permitted_schedule_ids - Schedule.where(id: requested_schedule_ids | existing_schedule_ids) - .merge(permissions.editable_schedules).pluck(:id) - end - - def existing_schedule_ids - @existing_schedule_ids ||= order_cycle.persisted? ? order_cycle.schedule_ids : [] - end - - def removed_schedule_ids - existing_schedule_ids - order_cycle.schedule_ids - end - - def new_schedule_ids - @order_cycle.schedule_ids - existing_schedule_ids - end - - def can_update_selected_payment_or_shipping_methods? - @user.admin? || coordinator? || distributor? - end - - def coordinator? - @user.enterprises.include?(@order_cycle.coordinator) - end - - def distributor? - !user_distributors_ids.empty? - end - - def distributor_only? - distributor? && !@user.admin? && !coordinator? - end - - def user_distributors_ids - @user_distributors_ids ||= @user.enterprises.pluck(:id) - .intersection(@order_cycle.distributors.pluck(:id)) - end - - def user_distributor_payment_method_ids - @user_distributor_payment_method_ids ||= - DistributorPaymentMethod.where(distributor_id: user_distributors_ids) - .pluck(:id) - end - - def user_distributor_shipping_method_ids - @user_distributor_shipping_method_ids ||= - DistributorShippingMethod.where(distributor_id: user_distributors_ids) - .pluck(:id) - end -end diff --git a/app/services/order_cycle_warning.rb b/app/services/order_cycle_warning.rb deleted file mode 100644 index 7e7afad8d8..0000000000 --- a/app/services/order_cycle_warning.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class OrderCycleWarning - def initialize(current_user) - @current_user = current_user - end - - def call - distributors = active_distributors_not_ready_for_checkout - - return if distributors.empty? - - active_distributors_not_ready_for_checkout_message(distributors) - end - - private - - attr_reader :current_user - - def active_distributors_not_ready_for_checkout - ocs = OrderCycle.managed_by(current_user).active - distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq - Enterprise.where(id: distributors.map(&:id)).not_ready_for_checkout - end - - def active_distributors_not_ready_for_checkout_message(distributors) - distributor_names = distributors.map(&:name).join ', ' - - if distributors.count > 1 - I18n.t(:active_distributors_not_ready_for_checkout_message_plural, - distributor_names:) - else - I18n.t(:active_distributors_not_ready_for_checkout_message_singular, - distributor_names:) - end - end -end diff --git a/app/services/order_cycle_webhook_service.rb b/app/services/order_cycle_webhook_service.rb deleted file mode 100644 index f2d4df152a..0000000000 --- a/app/services/order_cycle_webhook_service.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -# Create a webhook payload for an order cycle event. -# The payload will be delivered asynchronously. -class OrderCycleWebhookService - def self.create_webhook_job(order_cycle, event) - webhook_payload = order_cycle - .slice(:id, :name, :orders_open_at, :orders_close_at, :coordinator_id) - .merge(coordinator_name: order_cycle.coordinator.name) - - # Endpoints for coordinator owner - webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints - - # Plus unique endpoints for distributor owners (ignore duplicates) - webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints) - - webhook_endpoints.each do |endpoint| - WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload) - end - end -end diff --git a/app/services/order_cycles/clone_service.rb b/app/services/order_cycles/clone_service.rb new file mode 100644 index 0000000000..56e8c79247 --- /dev/null +++ b/app/services/order_cycles/clone_service.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module OrderCycles + class CloneService + def initialize(order_cycle) + @original_order_cycle = order_cycle + end + + def create + oc = @original_order_cycle.dup + oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name) + oc.orders_open_at = oc.orders_close_at = oc.mails_sent = oc.processed_at = nil + oc.coordinator_fee_ids = @original_order_cycle.coordinator_fee_ids + oc.preferred_product_selection_from_coordinator_inventory_only = + @original_order_cycle.preferred_product_selection_from_coordinator_inventory_only + oc.schedule_ids = @original_order_cycle.schedule_ids + oc.save! + @original_order_cycle.exchanges.each { |e| e.clone!(oc) } + oc.selected_distributor_payment_method_ids = selected_distributor_payment_method_ids + oc.selected_distributor_shipping_method_ids = selected_distributor_shipping_method_ids + sync_subscriptions + oc.reload + end + + private + + def selected_distributor_payment_method_ids + @original_order_cycle.attachable_distributor_payment_methods.map(&:id) & + @original_order_cycle.selected_distributor_payment_method_ids + end + + def selected_distributor_shipping_method_ids + @original_order_cycle.attachable_distributor_shipping_methods.map(&:id) & + @original_order_cycle.selected_distributor_shipping_method_ids + end + + def sync_subscriptions + return unless @original_order_cycle.schedule_ids.any? + + OrderManagement::Subscriptions::ProxyOrderSyncer.new( + Subscription.where(schedule_id: @original_order_cycle.schedule_ids) + ).sync! + end + end +end diff --git a/app/services/order_cycles/distributed_products_service.rb b/app/services/order_cycles/distributed_products_service.rb new file mode 100644 index 0000000000..567478c7eb --- /dev/null +++ b/app/services/order_cycles/distributed_products_service.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +# Returns a (paginatable) AR object for the products or variants in stock for a given shop and OC. +# The stock-checking includes on_demand and stock level overrides from variant_overrides. + +module OrderCycles + class DistributedProductsService + def initialize(distributor, order_cycle, customer) + @distributor = distributor + @order_cycle = order_cycle + @customer = customer + end + + def products_relation + Spree::Product.where(id: stocked_products).group("spree_products.id") + end + + def variants_relation + order_cycle. + variants_distributed_by(distributor). + merge(stocked_variants_and_overrides). + select("DISTINCT spree_variants.*") + end + + private + + attr_reader :distributor, :order_cycle, :customer + + def stocked_products + order_cycle. + variants_distributed_by(distributor). + merge(stocked_variants_and_overrides). + select("DISTINCT spree_variants.product_id") + end + + def stocked_variants_and_overrides + stocked_variants = Spree::Variant. + joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id + AND variant_overrides.hub_id = #{distributor.id}"). + joins(:stock_items). + where(query_stock_with_overrides) + + ProductTagRulesFilterer.new(distributor, customer, stocked_variants).call + end + + def query_stock_with_overrides + "( #{variant_not_overriden} AND ( #{variant_on_demand} OR #{variant_in_stock} ) ) + OR ( #{variant_overriden} AND ( #{override_on_demand} OR #{override_in_stock} ) ) + OR ( #{variant_overriden} AND ( #{override_on_demand_null} AND #{variant_on_demand} ) ) + OR ( #{variant_overriden} AND ( #{override_on_demand_null} + AND #{variant_not_on_demand} AND #{variant_in_stock} ) )" + end + + def variant_not_overriden + "variant_overrides.id IS NULL" + end + + def variant_overriden + "variant_overrides.id IS NOT NULL" + end + + def variant_in_stock + "spree_stock_items.count_on_hand > 0" + end + + def variant_on_demand + "spree_stock_items.backorderable IS TRUE" + end + + def variant_not_on_demand + "spree_stock_items.backorderable IS FALSE" + end + + def override_on_demand + "variant_overrides.on_demand IS TRUE" + end + + def override_in_stock + "variant_overrides.count_on_hand > 0" + end + + def override_on_demand_null + "variant_overrides.on_demand IS NULL" + end + end +end diff --git a/app/services/order_cycles/distributed_variants_service.rb b/app/services/order_cycles/distributed_variants_service.rb new file mode 100644 index 0000000000..cf4bb1c03f --- /dev/null +++ b/app/services/order_cycles/distributed_variants_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module OrderCycles + class DistributedVariantsService + def initialize(order_cycle, distributor) + @order_cycle = order_cycle + @distributor = distributor + end + + def distributes_order_variants?(order) + unavailable_order_variants(order).empty? + end + + def unavailable_order_variants(order) + order.line_item_variants - available_variants + end + + def available_variants + return [] unless @order_cycle + + @order_cycle.variants_distributed_by(@distributor) + end + end +end diff --git a/app/services/order_cycles/form_service.rb b/app/services/order_cycles/form_service.rb new file mode 100644 index 0000000000..0b2f62d988 --- /dev/null +++ b/app/services/order_cycles/form_service.rb @@ -0,0 +1,233 @@ +# frozen_string_literal: true + +require 'open_food_network/permissions' +require 'open_food_network/order_cycle_form_applicator' + +module OrderCycles + class FormService + def initialize(order_cycle, order_cycle_params, user) + @order_cycle = order_cycle + @order_cycle_params = order_cycle_params + @specified_params = order_cycle_params.keys + @user = user + @permissions = OpenFoodNetwork::Permissions.new(user) + @schedule_ids = order_cycle_params.delete(:schedule_ids) + @selected_distributor_payment_method_ids = order_cycle_params.delete( + :selected_distributor_payment_method_ids + ) + @selected_distributor_shipping_method_ids = order_cycle_params.delete( + :selected_distributor_shipping_method_ids + ) + end + + def save + schedule_ids = build_schedule_ids + order_cycle.assign_attributes(order_cycle_params) + return false unless order_cycle.valid? + + order_cycle.transaction do + order_cycle.save! + order_cycle.schedule_ids = schedule_ids if parameter_specified?(:schedule_ids) + order_cycle.save! + apply_exchange_changes + if can_update_selected_payment_or_shipping_methods? + attach_selected_distributor_payment_methods + attach_selected_distributor_shipping_methods + end + sync_subscriptions + true + end + rescue ActiveRecord::RecordInvalid => e + add_exception_to_order_cycle_errors(e) + false + end + + private + + attr_accessor :order_cycle, :order_cycle_params, :user, :permissions + + def add_exception_to_order_cycle_errors(exception) + error = exception.message.split(":").last.strip + order_cycle.errors.add(:base, error) if order_cycle.errors.to_a.exclude?(error) + end + + def apply_exchange_changes + return if exchanges_unchanged? + + OpenFoodNetwork::OrderCycleFormApplicator.new(order_cycle, user).go! + + # reload so outgoing exchanges are up-to-date for shipping/payment method validations + order_cycle.reload + end + + def attach_selected_distributor_payment_methods + return if @selected_distributor_payment_method_ids.nil? + + if distributor_only? + payment_method_ids = order_cycle.selected_distributor_payment_method_ids + payment_method_ids -= user_distributor_payment_method_ids + payment_method_ids += user_only_selected_distributor_payment_method_ids + order_cycle.selected_distributor_payment_method_ids = payment_method_ids + else + order_cycle + .selected_distributor_payment_method_ids = selected_distributor_payment_method_ids + end + order_cycle.save! + end + + def attach_selected_distributor_shipping_methods + return if @selected_distributor_shipping_method_ids.nil? + + if distributor_only? + # A distributor can only update methods associated with their own + # enterprise, so we load all previously selected methods, and replace + # only the distributor's methods with their selection (not touching other + # distributor's methods). + shipping_method_ids = order_cycle.selected_distributor_shipping_method_ids + shipping_method_ids -= user_distributor_shipping_method_ids + shipping_method_ids += user_only_selected_distributor_shipping_method_ids + order_cycle.selected_distributor_shipping_method_ids = shipping_method_ids + else + order_cycle.selected_distributor_shipping_method_ids = + selected_distributor_shipping_method_ids + end + + order_cycle.save! + end + + def attachable_distributor_payment_method_ids + @attachable_distributor_payment_method_ids ||= + order_cycle.attachable_distributor_payment_methods.map(&:id) + end + + def attachable_distributor_shipping_method_ids + @attachable_distributor_shipping_method_ids ||= + order_cycle.attachable_distributor_shipping_methods.map(&:id) + end + + def exchanges_unchanged? + [:incoming_exchanges, :outgoing_exchanges].all? do |direction| + order_cycle_params[direction].nil? + end + end + + def selected_distributor_payment_method_ids + @selected_distributor_payment_method_ids = ( + attachable_distributor_payment_method_ids & + @selected_distributor_payment_method_ids.compact_blank.map(&:to_i) + ) + + if attachable_distributor_payment_method_ids.sort == + @selected_distributor_payment_method_ids.sort + @selected_distributor_payment_method_ids = [] + end + + @selected_distributor_payment_method_ids + end + + def user_only_selected_distributor_payment_method_ids + user_distributor_payment_method_ids.intersection(selected_distributor_payment_method_ids) + end + + def selected_distributor_shipping_method_ids + @selected_distributor_shipping_method_ids = ( + attachable_distributor_shipping_method_ids & + @selected_distributor_shipping_method_ids.compact_blank.map(&:to_i) + ) + + if attachable_distributor_shipping_method_ids.sort == + @selected_distributor_shipping_method_ids.sort + @selected_distributor_shipping_method_ids = [] + end + + @selected_distributor_shipping_method_ids + end + + def user_only_selected_distributor_shipping_method_ids + user_distributor_shipping_method_ids.intersection(selected_distributor_shipping_method_ids) + end + + def build_schedule_ids + return unless parameter_specified?(:schedule_ids) + + result = existing_schedule_ids + result |= (requested_schedule_ids & permitted_schedule_ids) # Add permitted and requested + # Remove permitted but not requested + result -= ((result & permitted_schedule_ids) - requested_schedule_ids) + result + end + + def sync_subscriptions + return unless parameter_specified?(:schedule_ids) + return unless schedule_sync_required? + + OrderManagement::Subscriptions::ProxyOrderSyncer.new(subscriptions_to_sync).sync! + end + + def schedule_sync_required? + removed_schedule_ids.any? || new_schedule_ids.any? + end + + def subscriptions_to_sync + Subscription.where(schedule_id: removed_schedule_ids + new_schedule_ids) + end + + def requested_schedule_ids + @schedule_ids.map(&:to_i) + end + + def parameter_specified?(key) + @specified_params.map(&:to_s).include?(key.to_s) + end + + def permitted_schedule_ids + Schedule.where(id: requested_schedule_ids | existing_schedule_ids) + .merge(permissions.editable_schedules).pluck(:id) + end + + def existing_schedule_ids + @existing_schedule_ids ||= order_cycle.persisted? ? order_cycle.schedule_ids : [] + end + + def removed_schedule_ids + existing_schedule_ids - order_cycle.schedule_ids + end + + def new_schedule_ids + @order_cycle.schedule_ids - existing_schedule_ids + end + + def can_update_selected_payment_or_shipping_methods? + @user.admin? || coordinator? || distributor? + end + + def coordinator? + @user.enterprises.include?(@order_cycle.coordinator) + end + + def distributor? + !user_distributors_ids.empty? + end + + def distributor_only? + distributor? && !@user.admin? && !coordinator? + end + + def user_distributors_ids + @user_distributors_ids ||= @user.enterprises.pluck(:id) + .intersection(@order_cycle.distributors.pluck(:id)) + end + + def user_distributor_payment_method_ids + @user_distributor_payment_method_ids ||= + DistributorPaymentMethod.where(distributor_id: user_distributors_ids) + .pluck(:id) + end + + def user_distributor_shipping_method_ids + @user_distributor_shipping_method_ids ||= + DistributorShippingMethod.where(distributor_id: user_distributors_ids) + .pluck(:id) + end + end +end diff --git a/app/services/order_cycles/warning_service.rb b/app/services/order_cycles/warning_service.rb new file mode 100644 index 0000000000..35859424d0 --- /dev/null +++ b/app/services/order_cycles/warning_service.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module OrderCycles + class WarningService + def initialize(current_user) + @current_user = current_user + end + + def call + distributors = active_distributors_not_ready_for_checkout + + return if distributors.empty? + + active_distributors_not_ready_for_checkout_message(distributors) + end + + private + + attr_reader :current_user + + def active_distributors_not_ready_for_checkout + ocs = OrderCycle.managed_by(current_user).active + distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq + Enterprise.where(id: distributors.map(&:id)).not_ready_for_checkout + end + + def active_distributors_not_ready_for_checkout_message(distributors) + distributor_names = distributors.map(&:name).join ', ' + + if distributors.count > 1 + I18n.t(:active_distributors_not_ready_for_checkout_message_plural, + distributor_names:) + else + I18n.t(:active_distributors_not_ready_for_checkout_message_singular, + distributor_names:) + end + end + end +end diff --git a/app/services/order_cycles/webhook_service.rb b/app/services/order_cycles/webhook_service.rb new file mode 100644 index 0000000000..f12c94ea1b --- /dev/null +++ b/app/services/order_cycles/webhook_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Create a webhook payload for an order cycle event. +# The payload will be delivered asynchronously. + +module OrderCycles + class WebhookService + def self.create_webhook_job(order_cycle, event) + webhook_payload = order_cycle + .slice(:id, :name, :orders_open_at, :orders_close_at, :coordinator_id) + .merge(coordinator_name: order_cycle.coordinator.name) + + # Endpoints for coordinator owner + webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints + + # Plus unique endpoints for distributor owners (ignore duplicates) + webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints) + + webhook_endpoints.each do |endpoint| + WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload) + end + end + end +end diff --git a/app/services/order_data_masker.rb b/app/services/order_data_masker.rb deleted file mode 100644 index e5ae198332..0000000000 --- a/app/services/order_data_masker.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class OrderDataMasker - def initialize(order) - @order = order - end - - def call - mask_customer_names unless customer_names_allowed? - mask_contact_data - end - - private - - attr_accessor :order - - def customer_names_allowed? - order.distributor.show_customer_names_to_suppliers - end - - def mask_customer_names - order.bill_address&.assign_attributes(firstname: I18n.t('admin.reports.hidden'), - lastname: "") - order.ship_address&.assign_attributes(firstname: I18n.t('admin.reports.hidden'), - lastname: "") - end - - def mask_contact_data - order.bill_address&.assign_attributes(phone: "", address1: "", address2: "", - city: "", zipcode: "", state: nil) - order.ship_address&.assign_attributes(phone: "", address1: "", address2: "", - city: "", zipcode: "", state: nil) - order.assign_attributes(email: I18n.t('admin.reports.hidden')) - end -end diff --git a/app/services/order_factory.rb b/app/services/order_factory.rb deleted file mode 100644 index 077dbac288..0000000000 --- a/app/services/order_factory.rb +++ /dev/null @@ -1,97 +0,0 @@ -# frozen_string_literal: true - -require 'open_food_network/scope_variant_to_hub' - -# Builds orders based on a set of attributes -# There are some idiosyncracies in the order creation process, -# and it is nice to have them dealt with in one place. - -class OrderFactory - def initialize(attrs, opts = {}) - @attrs = attrs.with_indifferent_access - @opts = opts.with_indifferent_access - end - - def create - create_order - set_user - build_line_items - set_addresses - create_shipment - set_shipping_method - create_payment - - @order - end - - private - - attr_reader :attrs, :opts - - def customer - @customer ||= Customer.find(attrs[:customer_id]) - end - - def shop - @shop ||= Enterprise.find(attrs[:distributor_id]) - end - - def create_order - @order = Spree::Order.create!(create_attrs) - end - - def create_attrs - create_attrs = attrs.slice(:customer_id, :order_cycle_id, :distributor_id) - create_attrs[:email] = customer.email - create_attrs - end - - def build_line_items - attrs[:line_items].each do |li| - next unless variant = Spree::Variant.find_by(id: li[:variant_id]) - - scoper.scope(variant) - li[:quantity] = stock_limited_quantity(variant.on_demand, variant.on_hand, li[:quantity]) - li[:price] = variant.price - build_item_from(li) - end - end - - def build_item_from(attrs) - @order.line_items.build( - attrs.merge(skip_stock_check: opts[:skip_stock_check]) - ) - end - - def set_user - @order.update_attribute(:user_id, customer.user_id) - end - - def set_addresses - @order.update(attrs.slice(:bill_address_attributes, :ship_address_attributes)) - end - - def create_shipment - @order.create_proposed_shipments - end - - def set_shipping_method - @order.select_shipping_method(attrs[:shipping_method_id]) - end - - def create_payment - @order.recreate_all_fees! - @order.payments.create(payment_method_id: attrs[:payment_method_id], - amount: @order.reload.total) - end - - def stock_limited_quantity(variant_on_demand, variant_on_hand, requested) - return requested if opts[:skip_stock_check] || variant_on_demand - - [variant_on_hand, requested].min - end - - def scoper - @scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(shop) - end -end diff --git a/app/services/order_fees_handler.rb b/app/services/order_fees_handler.rb deleted file mode 100644 index 47be2c9903..0000000000 --- a/app/services/order_fees_handler.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -class OrderFeesHandler - attr_reader :order - - delegate :distributor, :order_cycle, to: :order - - def initialize(order) - @order = order - end - - def recreate_all_fees! - # `with_lock` acquires an exclusive row lock on order so no other - # requests can update it until the transaction is commited. - # See https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/locking/pessimistic.rb#L69 - # and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE - order.with_lock do - EnterpriseFee.clear_all_adjustments order - - create_line_item_fees! - create_order_fees! - end - - tax_enterprise_fees! unless order.before_payment_state? - order.update_order! - end - - def create_line_item_fees! - order.line_items.includes(variant: :product).each do |line_item| - if provided_by_order_cycle? line_item - calculator.create_line_item_adjustments_for line_item - end - end - end - - def create_order_fees! - return unless order_cycle - - calculator.create_order_adjustments_for order - end - - def tax_enterprise_fees! - Spree::TaxRate.adjust(order, order.all_adjustments.enterprise_fee) - end - - def update_line_item_fees!(line_item) - line_item.adjustments.enterprise_fee.each do |fee| - fee.update_adjustment!(line_item, force: true) - end - end - - def update_order_fees! - order.adjustments.enterprise_fee.where(adjustable_type: 'Spree::Order').each do |fee| - fee.update_adjustment!(order, force: true) - end - end - - private - - def calculator - @calculator ||= OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle) - end - - def provided_by_order_cycle?(line_item) - @order_cycle_variant_ids ||= order_cycle&.variants&.map(&:id) || [] - @order_cycle_variant_ids.include? line_item.variant_id - end -end diff --git a/app/services/order_invoice_comparator.rb b/app/services/order_invoice_comparator.rb deleted file mode 100644 index d306a972ce..0000000000 --- a/app/services/order_invoice_comparator.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -class OrderInvoiceComparator - attr_reader :order - - def initialize(order) - @order = order - end - - def can_generate_new_invoice? - return true if invoices.empty? - - # We'll use a recursive BFS algorithm to find if the invoice is outdated - # the root will be the order - # On each node, we'll a list of relevant attributes that will be used on the comparison - different?(current_state_invoice, latest_invoice, invoice_generation_selector) - end - - def can_update_latest_invoice? - return false if invoices.empty? - - different?(current_state_invoice, latest_invoice, invoice_update_selector) - end - - private - - def different?(node1, node2, attributes_selector) - simple_values1, presenters1 = attributes_selector.call(node1) - simple_values2, presenters2 = attributes_selector.call(node2) - return true if simple_values1 != simple_values2 - - return true if presenters1.size != presenters2.size - - presenters1.zip(presenters2).any? do |presenter1, presenter2| - different?(presenter1, presenter2, attributes_selector) - end - end - - def invoice_generation_selector - values_selector(:invoice_generation_values) - end - - def invoice_update_selector - values_selector(:invoice_update_values) - end - - def values_selector(attribute) - proc do |node| - return [[], []] unless node.respond_to?(attribute) - - grouped = node.public_send(attribute).group_by(&grouper) - [grouped[:simple] || [], grouped[:presenters]&.flatten || []] - end - end - - def grouper - proc do |value| - if value.is_a?(Array) || value.class.to_s.starts_with?("Invoice::DataPresenter") - :presenters - else - :simple - end - end - end - - def current_state_invoice - @current_state_invoice ||= Invoice.new( - order:, - data: serialize_for_invoice, - date: Time.zone.today, - number: invoices.count + 1 - ).presenter - end - - def invoices - order.invoices - end - - def latest_invoice - @latest_invoice ||= invoices.first.presenter - end - - def serialize_for_invoice - InvoiceDataGenerator.new(order).serialize_for_invoice - end -end diff --git a/app/services/order_invoice_generator.rb b/app/services/order_invoice_generator.rb deleted file mode 100644 index a6d30b33d0..0000000000 --- a/app/services/order_invoice_generator.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -class OrderInvoiceGenerator - def initialize(order) - @order = order - end - - def generate_or_update_latest_invoice - if comparator.can_generate_new_invoice? - order.invoices.create!( - date: Time.zone.today, - number: order.invoices.count + 1, - data: invoice_data - ) - elsif comparator.can_update_latest_invoice? - order.invoices.latest.update!( - date: Time.zone.today, - data: invoice_data - ) - end - end - - private - - attr_reader :order - - def comparator - @comparator ||= OrderInvoiceComparator.new(order) - end - - def invoice_data - @invoice_data ||= InvoiceDataGenerator.new(order).generate - end -end diff --git a/app/services/order_payment_finder.rb b/app/services/order_payment_finder.rb deleted file mode 100644 index 54b19654a3..0000000000 --- a/app/services/order_payment_finder.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -class OrderPaymentFinder - def initialize(order) - @order = order - end - - def last_payment - last(@order.payments) - end - - def last_pending_payment - last(@order.pending_payments) - end - - private - - # `max_by` avoids additional database queries when payments are loaded - # already. There is usually only one payment and this shouldn't cause - # any overhead compared to `order(:created_at).last`. Using `last` - # without order is not deterministic. - # - # We are not using `updated_at` because all payments are touched when the - # order is updated and then all payments have the same `updated_at` value. - def last(payments) - payments.max_by(&:created_at) - end -end diff --git a/app/services/order_syncer.rb b/app/services/order_syncer.rb deleted file mode 100644 index 83ed767426..0000000000 --- a/app/services/order_syncer.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true - -# Responsible for ensuring that any updates to a Subscription are propagated to any -# orders belonging to that Subscription which have been instantiated -class OrderSyncer - attr_reader :order_update_issues - - def initialize(subscription) - @subscription = subscription - @order_update_issues = OrderUpdateIssues.new - @line_item_syncer = LineItemSyncer.new(subscription, order_update_issues) - end - - def sync! - orders_in_order_cycles_not_closed.all? do |order| - order.assign_attributes(customer_id:, email: customer&.email, - distributor_id: shop_id) - update_associations_for(order) - line_item_syncer.sync!(order) - order.update_order! - order.save - end - end - - private - - attr_reader :subscription, :line_item_syncer - - delegate :orders, :bill_address, :ship_address, :subscription_line_items, to: :subscription - delegate :shop_id, :customer, :customer_id, to: :subscription - delegate :shipping_method, :shipping_method_id, - :payment_method, :payment_method_id, to: :subscription - delegate :shipping_method_id_changed?, :shipping_method_id_was, to: :subscription - delegate :payment_method_id_changed?, :payment_method_id_was, to: :subscription - - def update_associations_for(order) - update_bill_address_for(order) if (bill_address.changes.keys & relevant_address_attrs).any? - update_shipment_for(order) if shipping_method_id_changed? - update_ship_address_for(order) - update_payment_for(order) if payment_method_id_changed? - end - - def orders_in_order_cycles_not_closed - return @orders_in_order_cycles_not_closed unless @orders_in_order_cycles_not_closed.nil? - - @orders_in_order_cycles_not_closed = orders.joins(:order_cycle). - merge(OrderCycle.not_closed).readonly(false) - end - - def update_bill_address_for(order) - unless addresses_match?(order.bill_address, bill_address) - return order_update_issues.add(order, I18n.t('bill_address')) - end - - order.bill_address.update(bill_address.attributes.slice(*relevant_address_attrs)) - end - - def update_payment_for(order) - payment = order.payments. - with_state('checkout').where(payment_method_id: payment_method_id_was).last - if payment - payment&.void_transaction! - order.payments.create(payment_method_id:, amount: order.reload.total) - else - unless order.payments.with_state('checkout').where(payment_method_id:).any? - order_update_issues.add(order, I18n.t('admin.payment_method')) - end - end - end - - def update_shipment_for(order) - return if pending_shipment_with?(order, shipping_method_id) # No need to do anything. - - if pending_shipment_with?(order, shipping_method_id_was) - order.select_shipping_method(shipping_method_id) - else - order_update_issues.add(order, I18n.t('admin.shipping_method')) - end - end - - def update_ship_address_for(order) - # The conditions here are to achieve the same behaviour in earlier versions of Spree, where - # switching from pick-up to delivery affects whether simultaneous changes to shipping address - # are ignored or not. - pickup_to_delivery = force_ship_address_required?(order) - if (!pickup_to_delivery || order.shipment.present?) && - (ship_address.changes.keys & relevant_address_attrs).any? - save_ship_address_in_order(order) - end - return unless !pickup_to_delivery || order.shipment.blank? - - order.updater.shipping_address_from_distributor - end - - def relevant_address_attrs - ["firstname", "lastname", "address1", "zipcode", "city", "state_id", "country_id", "phone"] - end - - def addresses_match?(order_address, subscription_address) - relevant_address_attrs.all? do |attr| - order_address[attr] == subscription_address.public_send("#{attr}_was") || - order_address[attr] == subscription_address[attr] - end - end - - def ship_address_updatable?(order) - return true if force_ship_address_required?(order) - return false unless order.shipping_method.require_ship_address? - return true if addresses_match?(order.ship_address, ship_address) - - order_update_issues.add(order, I18n.t('ship_address')) - false - end - - # This returns true when the shipping method on the subscription has changed - # to a delivery (ie. a shipping address is required) AND the existing shipping - # address on the order matches the shop's address - def force_ship_address_required?(order) - return false unless shipping_method.require_ship_address? - - distributor_address = order.address_from_distributor - relevant_address_attrs.all? do |attr| - order.ship_address[attr] == distributor_address[attr] - end - end - - def save_ship_address_in_order(order) - return unless ship_address_updatable?(order) - - order.ship_address.update(ship_address.attributes.slice(*relevant_address_attrs)) - end - - def pending_shipment_with?(order, shipping_method_id) - return false unless order.shipment.present? && order.shipment.state == "pending" - - order.shipping_method.id == shipping_method_id - end -end diff --git a/app/services/order_tax_adjustments_fetcher.rb b/app/services/order_tax_adjustments_fetcher.rb deleted file mode 100644 index 72a20a2257..0000000000 --- a/app/services/order_tax_adjustments_fetcher.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -# Collects Tax Adjustments related to an order, and returns a hash with a total for each rate. - -class OrderTaxAdjustmentsFetcher - def initialize(order) - @order = order - end - - def totals(tax_adjustments = order.all_adjustments.tax) - tax_adjustments.each_with_object({}) do |adjustment, hash| - tax_rate = adjustment.originator - hash[tax_rate] = hash[tax_rate].to_f + adjustment.amount - end - end - - private - - attr_reader :order -end diff --git a/app/services/order_update_issues.rb b/app/services/order_update_issues.rb deleted file mode 100644 index bcdf3c644e..0000000000 --- a/app/services/order_update_issues.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -# Wrapper for a hash of issues encountered by instances of OrderSyncer and LineItemSyncer -# Used to report issues to the user when they attempt to update a subscription - -class OrderUpdateIssues - def initialize - @issues = {} - end - - delegate :[], :keys, to: :issues - - def add(order, issue) - @issues[order.id] ||= [] - @issues[order.id] << issue - end - - private - - attr_reader :issues -end diff --git a/app/services/order_workflow.rb b/app/services/order_workflow.rb deleted file mode 100644 index 815f3e1ee4..0000000000 --- a/app/services/order_workflow.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -class OrderWorkflow - attr_reader :order - - def initialize(order) - @order = order - end - - def complete - advance_to_state("complete", advance_order_options) - end - - def complete! - advance_order!(advance_order_options) - end - - def next(options = {}) - result = advance_order_one_step - - after_transition_hook(options) - - result - end - - def advance_to_payment - return unless order.before_payment_state? - - advance_to_state("payment", advance_order_options) - end - - def advance_checkout(options = {}) - advance_to = order.before_payment_state? ? "payment" : "confirmation" - - advance_to_state(advance_to, advance_order_options.merge(options)) - end - - private - - def advance_order_options - shipping_method_id = order.shipping_method.id if order.shipping_method.present? - { "shipping_method_id" => shipping_method_id } - end - - def advance_to_state(target_state, options = {}) - until order.state == target_state - break unless order.next - - after_transition_hook(options) - end - - order.state == target_state - end - - def advance_order!(options) - until order.completed? - order.next! - after_transition_hook(options) - end - end - - def advance_order_one_step - tries ||= 3 - order.next - rescue ActiveRecord::StaleObjectError - retry unless (tries -= 1).zero? - false - end - - def after_transition_hook(options) - if order.state == "delivery" - order.select_shipping_method(options["shipping_method_id"]) - end - - persist_all_payments if order.state == "payment" - end - - # When a payment fails, the order state machine stays in 'payment' and rollbacks all transactions - # This rollback also reverts the payment state from 'failed', 'void' or 'invalid' to 'pending' - # Despite the rollback, the in-memory payment still has the correct state, so we persist it - def persist_all_payments - order.payments.each do |payment| - in_memory_payment_state = payment.state - if different_from_db_payment_state?(in_memory_payment_state, payment.id) - payment.reload.update(state: in_memory_payment_state) - end - end - end - - # Verifies if the in-memory payment state is different from the one stored in the database - # This is be done without reloading the payment so that in-memory data is not changed - def different_from_db_payment_state?(in_memory_payment_state, payment_id) - in_memory_payment_state != Spree::Payment.find(payment_id).state - end -end diff --git a/app/services/orders/available_payment_methods_service.rb b/app/services/orders/available_payment_methods_service.rb new file mode 100644 index 0000000000..86b58ffc14 --- /dev/null +++ b/app/services/orders/available_payment_methods_service.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'open_food_network/tag_rule_applicator' + +module Orders + class AvailablePaymentMethodsService + attr_reader :order, :customer + + delegate :distributor, + :order_cycle, + to: :order + + def initialize(order, customer = nil) + @order, @customer = order, customer + end + + def to_a + return [] if distributor.blank? + + payment_methods = payment_methods_before_tag_rules_applied + + applicator = OpenFoodNetwork::TagRuleApplicator + .new(distributor, "FilterPaymentMethods", customer&.tag_list) + applicator.filter(payment_methods) + end + + private + + def payment_methods_before_tag_rules_applied + if order_cycle.nil? || order_cycle.simple? + distributor.payment_methods + else + distributor.payment_methods.where( + id: available_distributor_payment_methods_ids + ) + end.available.select(&:configured?).uniq + end + + def available_distributor_payment_methods_ids + order_cycle.distributor_payment_methods + .where(distributor_id: distributor.id) + .select(:payment_method_id) + end + end +end diff --git a/app/services/orders/available_shipping_methods_service.rb b/app/services/orders/available_shipping_methods_service.rb new file mode 100644 index 0000000000..f264c2cca6 --- /dev/null +++ b/app/services/orders/available_shipping_methods_service.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'open_food_network/tag_rule_applicator' + +module Orders + class AvailableShippingMethodsService + attr_reader :order, :customer + + delegate :distributor, :order_cycle, to: :order + + def initialize(order, customer = nil) + @order, @customer = order, customer + end + + def to_a + return [] if distributor.blank? + + filter_by_category(tag_rules.filter(shipping_methods)) + end + + private + + def filter_by_category(methods) + return methods unless OpenFoodNetwork::FeatureToggle.enabled?(:match_shipping_categories, + distributor&.owner) + + required_category_ids = order.variants.pluck(:shipping_category_id).to_set + return methods if required_category_ids.empty? + + methods.select do |method| + provided_category_ids = method.shipping_categories.pluck(:id).to_set + required_category_ids.subset?(provided_category_ids) + end + end + + def shipping_methods + if order_cycle.nil? || order_cycle.simple? + distributor.shipping_methods + else + distributor.shipping_methods.where( + id: available_distributor_shipping_methods_ids + ) + end.frontend.to_a.uniq + end + + def available_distributor_shipping_methods_ids + order_cycle.distributor_shipping_methods + .where(distributor_id: distributor.id) + .select(:shipping_method_id) + end + + def tag_rules + OpenFoodNetwork::TagRuleApplicator.new( + distributor, "FilterShippingMethods", customer&.tag_list + ) + end + end +end diff --git a/app/services/orders/bulk_cancel_service.rb b/app/services/orders/bulk_cancel_service.rb new file mode 100644 index 0000000000..0469907bf8 --- /dev/null +++ b/app/services/orders/bulk_cancel_service.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Orders + class BulkCancelService + def initialize(params, current_user) + @order_ids = params[:bulk_ids] + @current_user = current_user + @send_cancellation_email = params[:send_cancellation_email] + @restock_items = params[:restock_items] + end + + def call + # rubocop:disable Rails/FindEach # .each returns an array, .find_each returns nil + editable_orders.where(id: @order_ids).each do |order| + order.send_cancellation_email = @send_cancellation_email + order.restock_items = @restock_items + order.cancel + end + # rubocop:enable Rails/FindEach + end + + private + + def editable_orders + Permissions::Order.new(@current_user).editable_orders + end + end +end diff --git a/app/services/orders/capture_service.rb b/app/services/orders/capture_service.rb new file mode 100644 index 0000000000..abdcbb9f0a --- /dev/null +++ b/app/services/orders/capture_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Use `authorize! :admin order` before calling this service + +module Orders + class CaptureService + attr_reader :gateway_error + + def initialize(order) + @order = order + @gateway_error = nil + end + + def call + return false unless @order.payment_required? + return false unless (pending_payment = @order.pending_payments.first) + + pending_payment.capture! + rescue Spree::Core::GatewayError => e + @gateway_error = e + false + end + end +end diff --git a/app/services/orders/cart_reset_service.rb b/app/services/orders/cart_reset_service.rb new file mode 100644 index 0000000000..418b386aa7 --- /dev/null +++ b/app/services/orders/cart_reset_service.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: false + +# Resets an order by verifying it's state and fixing any issues + +module Orders + class CartResetService + def initialize(order, distributor_id) + @order = order + @distributor ||= Enterprise.is_distributor.find_by(permalink: distributor_id) || + Enterprise.is_distributor.find(distributor_id) + end + + def reset_distributor + if order.distributor && order.distributor != distributor + order.empty! + order.set_order_cycle! nil + end + order.distributor = distributor + end + + def reset_other!(current_user, current_customer) + reset_user_and_customer(current_user) + reset_order_cycle(current_customer) + order.save! + end + + private + + attr_reader :order, :distributor, :current_user + + def reset_user_and_customer(current_user) + return unless current_user + + order.associate_user!(current_user) if order.user.blank? || order.email.blank? + end + + def reset_order_cycle(current_customer) + listed_order_cycles = Shop::OrderCyclesList.active_for(distributor, current_customer) + + if order_cycle_not_listed?(order.order_cycle, listed_order_cycles) + order.order_cycle = nil + order.empty! + end + + select_default_order_cycle(order, listed_order_cycles) + end + + def order_cycle_not_listed?(order_cycle, listed_order_cycles) + order_cycle.present? && listed_order_cycles.exclude?(order_cycle) + end + + # If no OC is selected and there is only one in the list of OCs, selects it + def select_default_order_cycle(order, listed_order_cycles) + return unless order.order_cycle.blank? && listed_order_cycles.size == 1 + + order.order_cycle = listed_order_cycles.first + end + end +end diff --git a/app/services/orders/checkout_restart_service.rb b/app/services/orders/checkout_restart_service.rb new file mode 100644 index 0000000000..793d8a7efe --- /dev/null +++ b/app/services/orders/checkout_restart_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Resets the passed order to cart state while clearing associated payments and shipments + +module Orders + class CheckoutRestartService + def initialize(order) + @order = order + end + + def call + return if order.cart? + + reset_state_to_cart + clear_shipments + clear_payments + + order.reload.update_order! + end + + private + + attr_reader :order + + def reset_state_to_cart + order.restart_checkout! + end + + def clear_shipments + order.shipments.with_state(:pending).destroy_all + end + + def clear_payments + order.payments.with_state(:checkout).destroy_all + end + end +end diff --git a/app/services/orders/compare_invoice_service.rb b/app/services/orders/compare_invoice_service.rb new file mode 100644 index 0000000000..797506d21c --- /dev/null +++ b/app/services/orders/compare_invoice_service.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +module Orders + class CompareInvoiceService + attr_reader :order + + def initialize(order) + @order = order + end + + def can_generate_new_invoice? + return true if invoices.empty? + + # We'll use a recursive BFS algorithm to find if the invoice is outdated + # the root will be the order + # On each node, we'll a list of relevant attributes that will be used on the comparison + different?(current_state_invoice, latest_invoice, invoice_generation_selector) + end + + def can_update_latest_invoice? + return false if invoices.empty? + + different?(current_state_invoice, latest_invoice, invoice_update_selector) + end + + private + + def different?(node1, node2, attributes_selector) + simple_values1, presenters1 = attributes_selector.call(node1) + simple_values2, presenters2 = attributes_selector.call(node2) + return true if simple_values1 != simple_values2 + + return true if presenters1.size != presenters2.size + + presenters1.zip(presenters2).any? do |presenter1, presenter2| + different?(presenter1, presenter2, attributes_selector) + end + end + + def invoice_generation_selector + values_selector(:invoice_generation_values) + end + + def invoice_update_selector + values_selector(:invoice_update_values) + end + + def values_selector(attribute) + proc do |node| + return [[], []] unless node.respond_to?(attribute) + + grouped = node.public_send(attribute).group_by(&grouper) + [grouped[:simple] || [], grouped[:presenters]&.flatten || []] + end + end + + def grouper + proc do |value| + if value.is_a?(Array) || value.class.to_s.starts_with?("Invoice::DataPresenter") + :presenters + else + :simple + end + end + end + + def current_state_invoice + @current_state_invoice ||= Invoice.new( + order:, + data: serialize_for_invoice, + date: Time.zone.today, + number: invoices.count + 1 + ).presenter + end + + def invoices + order.invoices + end + + def latest_invoice + @latest_invoice ||= invoices.first.presenter + end + + def serialize_for_invoice + InvoiceDataGenerator.new(order).serialize_for_invoice + end + end +end diff --git a/app/services/orders/customer_cancellation_service.rb b/app/services/orders/customer_cancellation_service.rb new file mode 100644 index 0000000000..7385139db0 --- /dev/null +++ b/app/services/orders/customer_cancellation_service.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Orders + class CustomerCancellationService + def initialize(order) + @order = order + end + + def call + return unless order.cancel + + Spree::OrderMailer.cancel_email_for_shop(order).deliver_later + end + + private + + attr_reader :order + end +end diff --git a/app/services/orders/factory_service.rb b/app/services/orders/factory_service.rb new file mode 100644 index 0000000000..eef377734b --- /dev/null +++ b/app/services/orders/factory_service.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'open_food_network/scope_variant_to_hub' + +# Builds orders based on a set of attributes +# There are some idiosyncracies in the order creation process, +# and it is nice to have them dealt with in one place. + +module Orders + class FactoryService + def initialize(attrs, opts = {}) + @attrs = attrs.with_indifferent_access + @opts = opts.with_indifferent_access + end + + def create + create_order + set_user + build_line_items + set_addresses + create_shipment + set_shipping_method + create_payment + + @order + end + + private + + attr_reader :attrs, :opts + + def customer + @customer ||= Customer.find(attrs[:customer_id]) + end + + def shop + @shop ||= Enterprise.find(attrs[:distributor_id]) + end + + def create_order + @order = Spree::Order.create!(create_attrs) + end + + def create_attrs + create_attrs = attrs.slice(:customer_id, :order_cycle_id, :distributor_id) + create_attrs[:email] = customer.email + create_attrs + end + + def build_line_items + attrs[:line_items].each do |li| + next unless variant = Spree::Variant.find_by(id: li[:variant_id]) + + scoper.scope(variant) + li[:quantity] = stock_limited_quantity(variant.on_demand, variant.on_hand, li[:quantity]) + li[:price] = variant.price + build_item_from(li) + end + end + + def build_item_from(attrs) + @order.line_items.build( + attrs.merge(skip_stock_check: opts[:skip_stock_check]) + ) + end + + def set_user + @order.update_attribute(:user_id, customer.user_id) + end + + def set_addresses + @order.update(attrs.slice(:bill_address_attributes, :ship_address_attributes)) + end + + def create_shipment + @order.create_proposed_shipments + end + + def set_shipping_method + @order.select_shipping_method(attrs[:shipping_method_id]) + end + + def create_payment + @order.recreate_all_fees! + @order.payments.create(payment_method_id: attrs[:payment_method_id], + amount: @order.reload.total) + end + + def stock_limited_quantity(variant_on_demand, variant_on_hand, requested) + return requested if opts[:skip_stock_check] || variant_on_demand + + [variant_on_hand, requested].min + end + + def scoper + @scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(shop) + end + end +end diff --git a/app/services/orders/fetch_adjustments_service.rb b/app/services/orders/fetch_adjustments_service.rb new file mode 100644 index 0000000000..8cb3d76c41 --- /dev/null +++ b/app/services/orders/fetch_adjustments_service.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +# This service allows orders with eager-loaded adjustment objects to calculate various adjustment +# types without triggering additional queries. +# +# For example; `order.adjustments.shipping.sum(:amount)` would normally trigger a new query +# regardless of whether or not adjustments have been preloaded, as `#shipping` is an adjustment +# scope, eg; `scope :shipping, where(originator_type: 'Spree::ShippingMethod')`. +# +# Here the adjustment scopes are moved to a shared module, and `adjustments.loaded?` is used to +# check if the objects have already been fetched and initialized. If they have, `order.adjustments` +# will be an Array, and we can select the required objects without hitting the database. If not, it +# will fetch the adjustments via their scopes as normal. + +module Orders + class FetchAdjustmentsService + include AdjustmentScopes + + def initialize(order) + @order = order + end + + def admin_and_handling_total + admin_and_handling_fees.map(&:amount).sum + end + + def payment_fee + sum_adjustments "payment_fee" + end + + def ship_total + sum_adjustments "shipping" + end + + private + + attr_reader :order + + def adjustments + order.all_adjustments + end + + def adjustments_eager_loaded? + adjustments.loaded? + end + + def sum_adjustments(scope) + collect_adjustments(scope).map(&:amount).sum + end + + def collect_adjustments(scope) + if adjustments_eager_loaded? + adjustment_scope = public_send("#{scope}_scope") + + # Adjustments are already loaded here, this block is using `Array#select` + adjustments.select do |adjustment| + match_by_scope(adjustment, adjustment_scope) && match_by_scope(adjustment, eligible_scope) + end + else + adjustments.where(nil).eligible.public_send scope + end + end + + def admin_and_handling_fees + if adjustments_eager_loaded? + adjustments.select do |adjustment| + match_by_scope(adjustment, eligible_scope) && + adjustment.originator_type == 'EnterpriseFee' && + adjustment.adjustable_type != 'Spree::LineItem' + end + else + adjustments.eligible. + where("originator_type = ? AND adjustable_type != ?", 'EnterpriseFee', 'Spree::LineItem') + end + end + + def match_by_scope(adjustment, scope) + adjustment.public_send(scope.keys.first) == scope.values.first + end + end +end diff --git a/app/services/orders/fetch_tax_adjustments_service.rb b/app/services/orders/fetch_tax_adjustments_service.rb new file mode 100644 index 0000000000..0aef4cc319 --- /dev/null +++ b/app/services/orders/fetch_tax_adjustments_service.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Collects Tax Adjustments related to an order, and returns a hash with a total for each rate. + +module Orders + class FetchTaxAdjustmentsService + def initialize(order) + @order = order + end + + def totals(tax_adjustments = order.all_adjustments.tax) + tax_adjustments.each_with_object({}) do |adjustment, hash| + tax_rate = adjustment.originator + hash[tax_rate] = hash[tax_rate].to_f + adjustment.amount + end + end + + private + + attr_reader :order + end +end diff --git a/app/services/orders/find_payment_service.rb b/app/services/orders/find_payment_service.rb new file mode 100644 index 0000000000..fe8eac2b5a --- /dev/null +++ b/app/services/orders/find_payment_service.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Orders + class FindPaymentService + def initialize(order) + @order = order + end + + def last_payment + last(@order.payments) + end + + def last_pending_payment + last(@order.pending_payments) + end + + private + + # `max_by` avoids additional database queries when payments are loaded + # already. There is usually only one payment and this shouldn't cause + # any overhead compared to `order(:created_at).last`. Using `last` + # without order is not deterministic. + # + # We are not using `updated_at` because all payments are touched when the + # order is updated and then all payments have the same `updated_at` value. + def last(payments) + payments.max_by(&:created_at) + end + end +end diff --git a/app/services/orders/generate_invoice_service.rb b/app/services/orders/generate_invoice_service.rb new file mode 100644 index 0000000000..99cf17f781 --- /dev/null +++ b/app/services/orders/generate_invoice_service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Orders + class GenerateInvoiceService + def initialize(order) + @order = order + end + + def generate_or_update_latest_invoice + if comparator.can_generate_new_invoice? + order.invoices.create!( + date: Time.zone.today, + number: total_invoices_created_by_distributor + 1, + data: invoice_data + ) + elsif comparator.can_update_latest_invoice? + order.invoices.latest.update!( + date: Time.zone.today, + data: invoice_data + ) + end + end + + private + + attr_reader :order + + def comparator + @comparator ||= Orders::CompareInvoiceService.new(order) + end + + def invoice_data + @invoice_data ||= InvoiceDataGenerator.new(order).generate + end + + def total_invoices_created_by_distributor + Invoice.joins(:order).where(order: { distributor: order.distributor }).count + end + end +end diff --git a/app/services/orders/handle_fees_service.rb b/app/services/orders/handle_fees_service.rb new file mode 100644 index 0000000000..09ee04f1a2 --- /dev/null +++ b/app/services/orders/handle_fees_service.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Orders + class HandleFeesService + attr_reader :order + + delegate :distributor, :order_cycle, to: :order + + def initialize(order) + @order = order + end + + def recreate_all_fees! + # `with_lock` acquires an exclusive row lock on order so no other + # requests can update it until the transaction is commited. + # See https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/locking/pessimistic.rb#L69 + # and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE + order.with_lock do + EnterpriseFee.clear_all_adjustments order + + create_line_item_fees! + create_order_fees! + end + + tax_enterprise_fees! unless order.before_payment_state? + order.update_order! + end + + def create_line_item_fees! + order.line_items.includes(variant: :product).each do |line_item| + if provided_by_order_cycle? line_item + calculator.create_line_item_adjustments_for line_item + end + end + end + + def create_order_fees! + return unless order_cycle + + calculator.create_order_adjustments_for order + end + + def tax_enterprise_fees! + Spree::TaxRate.adjust(order, order.all_adjustments.enterprise_fee) + end + + def update_line_item_fees!(line_item) + line_item.adjustments.enterprise_fee.each do |fee| + fee.update_adjustment!(line_item, force: true) + end + end + + def update_order_fees! + order.adjustments.enterprise_fee.where(adjustable_type: 'Spree::Order').each do |fee| + fee.update_adjustment!(order, force: true) + end + end + + private + + def calculator + @calculator ||= OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle) + end + + def provided_by_order_cycle?(line_item) + @order_cycle_variant_ids ||= order_cycle&.variants&.map(&:id) || [] + @order_cycle_variant_ids.include? line_item.variant_id + end + end +end diff --git a/app/services/orders/mask_data_service.rb b/app/services/orders/mask_data_service.rb new file mode 100644 index 0000000000..35cbc7482b --- /dev/null +++ b/app/services/orders/mask_data_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Orders + class MaskDataService + def initialize(order) + @order = order + end + + def call + mask_customer_names unless customer_names_allowed? + mask_contact_data + end + + private + + attr_accessor :order + + def customer_names_allowed? + order.distributor.show_customer_names_to_suppliers + end + + def mask_customer_names + order.bill_address&.assign_attributes(firstname: I18n.t('admin.reports.hidden'), + lastname: "") + order.ship_address&.assign_attributes(firstname: I18n.t('admin.reports.hidden'), + lastname: "") + end + + def mask_contact_data + order.bill_address&.assign_attributes(phone: "", address1: "", address2: "", + city: "", zipcode: "", state: nil) + order.ship_address&.assign_attributes(phone: "", address1: "", address2: "", + city: "", zipcode: "", state: nil) + order.assign_attributes(email: I18n.t('admin.reports.hidden')) + end + end +end diff --git a/app/services/orders/sync_service.rb b/app/services/orders/sync_service.rb new file mode 100644 index 0000000000..590e622209 --- /dev/null +++ b/app/services/orders/sync_service.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +# Responsible for ensuring that any updates to a Subscription are propagated to any +# orders belonging to that Subscription which have been instantiated + +module Orders + class SyncService + attr_reader :order_update_issues + + def initialize(subscription) + @subscription = subscription + @order_update_issues = Orders::UpdateIssuesService.new + @line_item_syncer = LineItemSyncer.new(subscription, order_update_issues) + end + + def sync! + orders_in_order_cycles_not_closed.all? do |order| + order.assign_attributes(customer_id:, email: customer&.email, + distributor_id: shop_id) + update_associations_for(order) + line_item_syncer.sync!(order) + order.update_order! + order.save + end + end + + private + + attr_reader :subscription, :line_item_syncer + + delegate :orders, :bill_address, :ship_address, :subscription_line_items, to: :subscription + delegate :shop_id, :customer, :customer_id, to: :subscription + delegate :shipping_method, :shipping_method_id, + :payment_method, :payment_method_id, to: :subscription + delegate :shipping_method_id_changed?, :shipping_method_id_was, to: :subscription + delegate :payment_method_id_changed?, :payment_method_id_was, to: :subscription + + def update_associations_for(order) + update_bill_address_for(order) if bill_address.changes.keys.intersect?(relevant_address_attrs) + update_shipment_for(order) if shipping_method_id_changed? + update_ship_address_for(order) + update_payment_for(order) if payment_method_id_changed? + end + + def orders_in_order_cycles_not_closed + return @orders_in_order_cycles_not_closed unless @orders_in_order_cycles_not_closed.nil? + + @orders_in_order_cycles_not_closed = orders.joins(:order_cycle). + merge(OrderCycle.not_closed).readonly(false) + end + + def update_bill_address_for(order) + unless addresses_match?(order.bill_address, bill_address) + return order_update_issues.add(order, I18n.t('bill_address')) + end + + order.bill_address.update(bill_address.attributes.slice(*relevant_address_attrs)) + end + + def update_payment_for(order) + payment = order.payments. + with_state('checkout').where(payment_method_id: payment_method_id_was).last + if payment + payment&.void_transaction! + order.payments.create(payment_method_id:, amount: order.reload.total) + else + unless order.payments.with_state('checkout').where(payment_method_id:).any? + order_update_issues.add(order, I18n.t('admin.payment_method')) + end + end + end + + def update_shipment_for(order) + return if pending_shipment_with?(order, shipping_method_id) # No need to do anything. + + if pending_shipment_with?(order, shipping_method_id_was) + order.select_shipping_method(shipping_method_id) + else + order_update_issues.add(order, I18n.t('admin.shipping_method')) + end + end + + def update_ship_address_for(order) + # The conditions here are to achieve the same behaviour in earlier versions of Spree, where + # switching from pick-up to delivery affects whether simultaneous changes to shipping address + # are ignored or not. + pickup_to_delivery = force_ship_address_required?(order) + if (!pickup_to_delivery || order.shipment.present?) && + ship_address.changes.keys.intersect?(relevant_address_attrs) + save_ship_address_in_order(order) + end + return unless !pickup_to_delivery || order.shipment.blank? + + order.updater.shipping_address_from_distributor + end + + def relevant_address_attrs + ["firstname", "lastname", "address1", "zipcode", "city", "state_id", "country_id", "phone"] + end + + def addresses_match?(order_address, subscription_address) + relevant_address_attrs.all? do |attr| + order_address[attr] == subscription_address.public_send("#{attr}_was") || + order_address[attr] == subscription_address[attr] + end + end + + def ship_address_updatable?(order) + return true if force_ship_address_required?(order) + return false unless order.shipping_method.require_ship_address? + return true if addresses_match?(order.ship_address, ship_address) + + order_update_issues.add(order, I18n.t('ship_address')) + false + end + + # This returns true when the shipping method on the subscription has changed + # to a delivery (ie. a shipping address is required) AND the existing shipping + # address on the order matches the shop's address + def force_ship_address_required?(order) + return false unless shipping_method.require_ship_address? + + distributor_address = order.address_from_distributor + relevant_address_attrs.all? do |attr| + order.ship_address[attr] == distributor_address[attr] + end + end + + def save_ship_address_in_order(order) + return unless ship_address_updatable?(order) + + order.ship_address.update(ship_address.attributes.slice(*relevant_address_attrs)) + end + + def pending_shipment_with?(order, shipping_method_id) + return false unless order.shipment.present? && order.shipment.state == "pending" + + order.shipping_method.id == shipping_method_id + end + end +end diff --git a/app/services/orders/update_issues_service.rb b/app/services/orders/update_issues_service.rb new file mode 100644 index 0000000000..0f0eba5277 --- /dev/null +++ b/app/services/orders/update_issues_service.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Wrapper for a hash of issues encountered by instances of Orders::SyncService and LineItemSyncer +# Used to report issues to the user when they attempt to update a subscription + +module Orders + class UpdateIssuesService + def initialize + @issues = {} + end + + delegate :[], :keys, to: :issues + + def add(order, issue) + @issues[order.id] ||= [] + @issues[order.id] << issue + end + + private + + attr_reader :issues + end +end diff --git a/app/services/orders/workflow_service.rb b/app/services/orders/workflow_service.rb new file mode 100644 index 0000000000..99eec1bd17 --- /dev/null +++ b/app/services/orders/workflow_service.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Orders + class WorkflowService + attr_reader :order + + def initialize(order) + @order = order + end + + def complete + advance_to_state("complete", advance_order_options) + end + + def complete! + advance_order!(advance_order_options) + end + + def next(options = {}) + result = advance_order_one_step + + after_transition_hook(options) + + result + end + + def advance_to_payment + return unless order.before_payment_state? + + advance_to_state("payment", advance_order_options) + end + + def advance_checkout(options = {}) + advance_to = order.before_payment_state? ? "payment" : "confirmation" + + advance_to_state(advance_to, advance_order_options.merge(options)) + end + + private + + def advance_order_options + shipping_method_id = order.shipping_method.id if order.shipping_method.present? + { "shipping_method_id" => shipping_method_id } + end + + def advance_to_state(target_state, options = {}) + until order.state == target_state + break unless order.next + + after_transition_hook(options) + end + + order.state == target_state + end + + def advance_order!(options) + until order.completed? + order.next! + after_transition_hook(options) + end + end + + def advance_order_one_step + tries ||= 3 + order.next + rescue ActiveRecord::StaleObjectError + retry unless (tries -= 1).zero? + false + end + + def after_transition_hook(options) + if order.state == "delivery" + order.select_shipping_method(options["shipping_method_id"]) + end + + persist_all_payments if order.state == "payment" + end + + # When a payment fails, the order state machine stays in 'payment' + # and rollbacks all transactions + # This rollback also reverts the payment state from 'failed', 'void' or 'invalid' to 'pending' + # Despite the rollback, the in-memory payment still has the correct state, so we persist it + def persist_all_payments + order.payments.each do |payment| + in_memory_payment_state = payment.state + if different_from_db_payment_state?(in_memory_payment_state, payment.id) + payment.reload.update(state: in_memory_payment_state) + end + end + end + + # Verifies if the in-memory payment state is different from the one stored in the database + # This is be done without reloading the payment so that in-memory data is not changed + def different_from_db_payment_state?(in_memory_payment_state, payment_id) + in_memory_payment_state != Spree::Payment.find(payment_id).state + end + end +end diff --git a/app/services/orders_bulk_cancel_service.rb b/app/services/orders_bulk_cancel_service.rb deleted file mode 100644 index a788183ffc..0000000000 --- a/app/services/orders_bulk_cancel_service.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class OrdersBulkCancelService - def initialize(params, current_user) - @order_ids = params[:bulk_ids] - @current_user = current_user - @send_cancellation_email = params[:send_cancellation_email] - @restock_items = params[:restock_items] - end - - def call - editable_orders.where(id: @order_ids).each do |order| - order.send_cancellation_email = @send_cancellation_email - order.restock_items = @restock_items - order.cancel - end - end - - private - - def editable_orders - Permissions::Order.new(@current_user).editable_orders - end -end diff --git a/app/services/permitted_attributes/product.rb b/app/services/permitted_attributes/product.rb index 96b9e18cf8..1adc8ff7d9 100644 --- a/app/services/permitted_attributes/product.rb +++ b/app/services/permitted_attributes/product.rb @@ -5,7 +5,8 @@ module PermittedAttributes def self.attributes [ :id, :name, :description, :supplier_id, :price, - :variant_unit, :variant_unit_scale, :unit_value, :unit_description, :variant_unit_name, + :variant_unit, :variant_unit_scale, :variant_unit_with_scale, :unit_value, + :unit_description, :variant_unit_name, :display_as, :sku, :group_buy, :group_buy_unit_size, :taxon_ids, :primary_taxon_id, :tax_category_id, :meta_keywords, :notes, :inherits_properties, diff --git a/app/services/place_proxy_order.rb b/app/services/place_proxy_order.rb index aaa88f620d..03bd379d9d 100644 --- a/app/services/place_proxy_order.rb +++ b/app/services/place_proxy_order.rb @@ -87,7 +87,7 @@ class PlaceProxyOrder end def move_to_completion - OrderWorkflow.new(order).complete! + Orders::WorkflowService.new(order).complete! end def send_placement_email diff --git a/app/services/process_payment_intent.rb b/app/services/process_payment_intent.rb index be6d4f233f..eb4429edbb 100644 --- a/app/services/process_payment_intent.rb +++ b/app/services/process_payment_intent.rb @@ -58,7 +58,7 @@ class ProcessPaymentIntent def process_payment return unless order.process_payments! - OrderWorkflow.new(order).complete + Orders::WorkflowService.new(order).complete end def ready_for_capture? diff --git a/app/services/products_renderer.rb b/app/services/products_renderer.rb index 99745ca451..5d0d794856 100644 --- a/app/services/products_renderer.rb +++ b/app/services/products_renderer.rb @@ -64,7 +64,7 @@ class ProductsRenderer end def distributed_products - OrderCycleDistributedProducts.new(distributor, order_cycle, customer) + OrderCycles::DistributedProductsService.new(distributor, order_cycle, customer) end def products_order @@ -89,10 +89,12 @@ class ProductsRenderer @variants_for_shop ||= begin scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) + # rubocop:disable Rails/FindEach # .each returns an array, .find_each returns nil distributed_products.variants_relation. includes(:default_price, :stock_locations, :product). where(product_id: products). each { |v| scoper.scope(v) } # Scope results with variant_overrides + # rubocop:enable Rails/FindEach end end diff --git a/app/services/sets/product_set.rb b/app/services/sets/product_set.rb index 362acae3f1..1adcdcb579 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -118,7 +118,7 @@ module Sets # Copy any variant errors to product variant&.errors&.each do |error| # The name is namespaced to avoid confusion with product attrs of same name. - product.errors.add("variant_#{error.attribute}".to_sym, error.message) + product.errors.add(:"variant_#{error.attribute}", error.message) end variant&.errors.blank? end diff --git a/app/services/shop/order_cycles_list.rb b/app/services/shop/order_cycles_list.rb index 7f3ab665a3..dd505c569e 100644 --- a/app/services/shop/order_cycles_list.rb +++ b/app/services/shop/order_cycles_list.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'open_food_network/tag_rule_applicator' + # Lists available order cycles for a given customer in a given distributor module Shop class OrderCyclesList @@ -10,8 +12,8 @@ module Shop def self.ready_for_checkout_for(distributor, customer) new(distributor, customer).call.select do |order_cycle| order = Spree::Order.new(distributor:, order_cycle:) - OrderAvailablePaymentMethods.new(order, customer).to_a.any? && - OrderAvailableShippingMethods.new(order, customer).to_a.any? + Orders::AvailablePaymentMethodsService.new(order, customer).to_a.any? && + Orders::AvailableShippingMethodsService.new(order, customer).to_a.any? end end diff --git a/app/services/variant_units/variant_and_line_item_naming.rb b/app/services/variant_units/variant_and_line_item_naming.rb index d0c72d4486..9b07413155 100644 --- a/app/services/variant_units/variant_and_line_item_naming.rb +++ b/app/services/variant_units/variant_and_line_item_naming.rb @@ -4,8 +4,6 @@ # It contains all of our logic for creating and naming option values (which are associated # with both models) and methods for printing human readable "names" for instances of these models. -require 'variant_units/option_value_namer' - module VariantUnits module VariantAndLineItemNaming def options_text diff --git a/app/services/weights_and_measures.rb b/app/services/weights_and_measures.rb index 7fd8528fe7..059075b7f8 100644 --- a/app/services/weights_and_measures.rb +++ b/app/services/weights_and_measures.rb @@ -20,6 +20,44 @@ class WeightsAndMeasures scales[product_scale.to_f]['system'] end + # @returns enumerable with label and value for select + def self.variant_unit_options + available_units_sorted.flat_map do |measurement, measurement_info| + measurement_info.filter_map do |scale, unit_info| + scale_clean = + ActiveSupport::NumberHelper.number_to_rounded(scale, precision: nil, + strip_insignificant_zeros: true) + [ + "#{I18n.t(measurement)} (#{unit_info['name']})", # Label (eg "Weight (g)") + "#{measurement}_#{scale_clean}", # Scale ID (eg "weight_1") + ] + end + end << + [ + I18n.t('items'), + 'items' + ] + end + + def self.available_units + Spree::Config.available_units.split(",") + end + + def self.available_units_sorted + self::UNITS.transform_values do |measurement_info| + # Filter to only include available units + measurement_info.filter do |_scale, unit_info| + available_units.include?(unit_info['name']) + end. + # Remove duplicates by name + uniq do |_scale, unit_info| + unit_info['name'] + end. + # Sort by unit number + sort.to_h + end + end + private UNITS = { @@ -29,10 +67,10 @@ class WeightsAndMeasures 1000.0 => { 'name' => 'kg', 'system' => 'metric' }, 1_000_000.0 => { 'name' => 'T', 'system' => 'metric' }, - 28.349523125 => { 'name' => 'oz', 'system' => 'imperial' }, 28.35 => { 'name' => 'oz', 'system' => 'imperial' }, - 453.59237 => { 'name' => 'lb', 'system' => 'imperial' }, + 28.349523125 => { 'name' => 'oz', 'system' => 'imperial' }, 453.6 => { 'name' => 'lb', 'system' => 'imperial' }, + 453.59237 => { 'name' => 'lb', 'system' => 'imperial' }, }, 'volume' => { 0.001 => { 'name' => 'mL', 'system' => 'metric' }, @@ -49,7 +87,7 @@ class WeightsAndMeasures return @units[@variant.product.variant_unit] if ignore_available_units @units[@variant.product.variant_unit]&.reject { |_scale, unit_info| - available_units.exclude?(unit_info['name']) + self.class.available_units.exclude?(unit_info['name']) } end @@ -67,8 +105,4 @@ class WeightsAndMeasures largest_unit end - - def available_units - Spree::Config.available_units.split(",") - end end diff --git a/app/views/admin/_terms_of_service_banner.html.haml b/app/views/admin/_terms_of_service_banner.html.haml new file mode 100644 index 0000000000..3adbba2f93 --- /dev/null +++ b/app/views/admin/_terms_of_service_banner.html.haml @@ -0,0 +1,8 @@ +#banner-container + .terms-of-service-banner.form-actions + .column-left + %p= t("admin.terms_of_service_have_been_updated_html", tos_link: link_to(t("admin.terms_of_service"), TermsOfServiceFile.current_url, target: "_blank")) + .column-right + %button{ data: { reflex: "click->user#accept_terms_of_services" } } + = t("admin.accept_terms_of_service") + diff --git a/app/views/admin/customers/index.html.haml b/app/views/admin/customers/index.html.haml index a463b2e0ed..ea65c31108 100644 --- a/app/views/admin/customers/index.html.haml +++ b/app/views/admin/customers/index.html.haml @@ -14,22 +14,22 @@ = admin_inject_shops(@shops, module: 'admin.customers') = admin_inject_available_countries(module: 'admin.customers') -%div{ ng: { controller: 'customersCtrl' } } +%div{ "ng-controller": 'customersCtrl' } .row.filters .sixteen.columns.alpha.omega .filter_select.five.columns.alpha - %label{ :for => 'quick_search', ng: {class: '{disabled: !shop_id}'} }=t('admin.quick_search') + %label{ for: 'quick_search', "ng-class": '{disabled: !shop_id}' }=t('admin.quick_search') %br - %input.fullwidth{ :type => "text", :id => 'quick_search', ng: { model: 'quickSearch', disabled: '!shop_id' }, :placeholder => t('.search_by_email') } + %input.fullwidth{ type: "text", id: 'quick_search', placeholder: t('.search_by_email'), "ng-model": 'quickSearch', "ng-disabled": '!shop_id' } .filter_select.four.columns - %label{ :for => 'hub_id', ng: { bind: "shop_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } } + %label{ for: 'hub_id', "ng-bind": "shop_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } %br - %input.ofn-select2.fullwidth#shop_id{ 'ng-model' => 'shop_id', name: 'shop_id', data: 'shops', on: { selecting: 'confirmRefresh' } } + %input.ofn-select2.fullwidth#shop_id{ "ng-model": 'shop_id', name: 'shop_id', data: 'shops', "on-selecting": 'confirmRefresh' } .seven.columns.omega   - %hr.divider{ ng: { show: "!RequestMonitor.loading && filteredCustomers.length > 0" } } + %hr.divider{ "ng-show": "!RequestMonitor.loading && filteredCustomers.length > 0" } - .row.controls{ ng: { show: "!RequestMonitor.loading && filteredCustomers.length > 0" } } + .row.controls{ "ng-show": "!RequestMonitor.loading && filteredCustomers.length > 0" } .thirteen.columns.alpha   %columns-dropdown{ action: "#{controller_name}_#{action_name}" } @@ -39,13 +39,13 @@ %h1 = t :loading_customers - .row.margin-bottom-50{ ng: { show: "!RequestMonitor.loading" } } + .row.margin-bottom-50{ "ng-show": "!RequestMonitor.loading" } %form{ name: "customers_form" } %h1#no_results{ 'ng-show' => '!RequestMonitor.loading && filteredCustomers.length == 0' } =t :no_customers_found %save-bar{ dirty: "customers_form.$dirty", persist: "false" } - %input.red{ type: "button", value: t(:save_changes), ng: { click: "submitAll(customers_form)" } } + %input.red{ type: "button", value: t(:save_changes), "ng-click": "submitAll(customers_form)" } %table.index#customers{ 'ng-show' => '!RequestMonitor.loading && filteredCustomers.length > 0' } %col.email{ width: "20%", 'ng-show' => 'columns.email.visible' } @@ -58,7 +58,7 @@ %col.balance{ width: "10%", 'ng-show' => 'columns.balance.visible' } %col.actions{ width: "10%"} %thead - %tr{ ng: { controller: "ColumnsCtrl" } } + %tr{ "ng-controller": "ColumnsCtrl" } -# %th.bulk -# %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } %th.email{ 'ng-show' => 'columns.email.visible' } @@ -81,12 +81,12 @@ %span{ 'ng-bind' => '::customer.email' } %span.guest-label{ 'ng-show' => 'customer.user_id == null' }= t('.guest_label') %td.first_name{ 'ng-show' => 'columns.first_name.visible'} - %input{ type: 'text', name: 'first_name', ng: { model: 'customer.first_name' }, 'obj-for-update' => 'customer', 'attr-for-update' => 'first_name'} + %input{ type: 'text', name: 'first_name', "obj-for-update": 'customer', "attr-for-update": 'first_name', "ng-model": 'customer.first_name' } %td.last_name{ 'ng-show' => 'columns.last_name.visible'} - %input{ type: 'text', name: 'last_name', ng: { model: 'customer.last_name' }, 'obj-for-update' => 'customer', 'attr-for-update' => 'last_name'} + %input{ type: 'text', name: 'last_name', "obj-for-update": 'customer', "attr-for-update": 'last_name', "ng-model": 'customer.last_name' } %td.code{ 'ng-show' => 'columns.code.visible' } - %input{ type: 'text', name: 'code', ng: {model: 'customer.code', change: 'checkForDuplicateCodes()'}, "obj-for-update" => "customer", "attr-for-update" => "code" } - %i.icon-warning-sign{ ng: {if: 'duplicate'} } + %input{ type: 'text', name: 'code', "obj-for-update": "customer", "attr-for-update": "code", "ng-model": 'customer.code', "ng-change": 'checkForDuplicateCodes()' } + %i.icon-warning-sign{ "ng-if": 'duplicate' } = t('.duplicate_code') %td.tags{ 'ng-show' => 'columns.tags.visible' } .tag_watcher{ 'obj-for-update' => "customer", "attr-for-update" => "tag_list"} @@ -101,6 +101,6 @@ %td.actions %a{ 'ng-click' => "deleteCustomer(customer)", :class => "delete-customer icon-trash no-text" } - %div.text-center{ ng: { show: "filteredCustomers.length > customerLimit" } } - %input{ type: 'button', value: t(:show_more), ng: { click: 'customerLimit = customerLimit + 20' } } - %input{ type: 'button', value: t(:show_all_with_more, num: '{{ filteredCustomers.length - customerLimit }}'), ng: { click: 'customerLimit = filteredCustomers.length' } } + %div.text-center{ "ng-show": "filteredCustomers.length > customerLimit" } + %input{ type: 'button', value: t(:show_more), "ng-click": 'customerLimit = customerLimit + 20' } + %input{ type: 'button', value: t(:show_all_with_more, num: '{{ filteredCustomers.length - customerLimit }}'), "ng-click": 'customerLimit = filteredCustomers.length' } diff --git a/app/views/admin/dfc_product_imports/index.html.haml b/app/views/admin/dfc_product_imports/index.html.haml new file mode 100644 index 0000000000..60a3f7a05a --- /dev/null +++ b/app/views/admin/dfc_product_imports/index.html.haml @@ -0,0 +1,7 @@ +- content_for :page_title do + #{t(".title")} + += render partial: 'spree/admin/shared/product_sub_menu' + +%p= t(".imported_products") += @count diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index e237704243..27b8c64403 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -29,20 +29,20 @@ %th.actions %tbody = enterprise_fee_set_form.ng_fields_for :collection do |f| - %tr{ ng: { repeat: 'enterprise_fee in enterprise_fees | filter:query' } } + %tr{ "ng-repeat": 'enterprise_fee in enterprise_fees | filter:query' } %td = f.ng_hidden_field :id - %ofn-select{ :id => angular_id(:enterprise_id), data: 'enterprises', ng: { model: 'enterprise_fee.enterprise_id' }, style: "width: 90%" } - %input{ type: "hidden", name: angular_name(:enterprise_id), ng: { value: "enterprise_fee.enterprise_id" } } + %ofn-select{ id: angular_id(:enterprise_id), data: 'enterprises', style: "width: 90%", "ng-model": 'enterprise_fee.enterprise_id' } + %input{ type: "hidden", name: angular_name(:enterprise_id), "ng-value": "enterprise_fee.enterprise_id" } %td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type' %td= f.ng_text_field :name, { placeholder: t('.name_placeholder') } %td - %ofn-select{ :id => angular_id(:tax_category_id), data: 'tax_categories', include_blank: true, ng: { model: 'enterprise_fee.tax_category_id' } } + %ofn-select{ id: angular_id(:tax_category_id), data: 'tax_categories', include_blank: true, "ng-model": 'enterprise_fee.tax_category_id' } %input{ type: "hidden", name: angular_name(:tax_category_id), 'watch-tax-category' => true } - %input{ type: "hidden", name: angular_name(:inherits_tax_category), ng: { value: "enterprise_fee.inherits_tax_category" } } + %input{ type: "hidden", name: angular_name(:inherits_tax_category), "ng-value": "enterprise_fee.inherits_tax_category" } %td - %ofn-select.calculator_type{ :id => angular_id(:calculator_type), ng: { model: 'enterprise_fee.calculator_type' }, value_attr: 'name', text_attr: 'description', data: 'calculators', 'spree-ensure-calculator-preferences-match-type' => true } - %input{ type: "hidden", name: angular_name(:calculator_type), ng: { value: "enterprise_fee.calculator_type" } } + %ofn-select.calculator_type{ id: angular_id(:calculator_type), value_attr: 'name', text_attr: 'description', data: 'calculators', "spree-ensure-calculator-preferences-match-type": true, "ng-model": 'enterprise_fee.calculator_type' } + %input{ type: "hidden", name: angular_name(:calculator_type), "ng-value": "enterprise_fee.calculator_type" } %td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'} %td.actions{'spree-delete-resource' => "1"} diff --git a/app/views/admin/enterprise_groups/_form.html.haml b/app/views/admin/enterprise_groups/_form.html.haml index 9e95a39879..a5a80a9871 100644 --- a/app/views/admin/enterprise_groups/_form.html.haml +++ b/app/views/admin/enterprise_groups/_form.html.haml @@ -1,7 +1,7 @@ = render 'spree/shared/error_messages', target: @enterprise = form_for [main_app, :admin, @enterprise_group] do |f| - .row{ ng: {app: 'admin.enterprise_groups', controller: 'enterpriseGroupCtrl'}, data: { controller: 'tabs-and-panels', "tabs-and-panels-class-name-value": "selected" } } + .row{ data: { controller: 'tabs-and-panels', "tabs-and-panels-class-name-value": "selected" }, "ng-app": 'admin.enterprise_groups', "ng-controller": 'enterpriseGroupCtrl' } .sixteen.columns.alpha .four.columns.alpha = render 'admin/shared/side_menu' diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml index e79da0b144..484bfe0cf4 100644 --- a/app/views/admin/enterprise_relationships/_form.html.haml +++ b/app/views/admin/enterprise_relationships/_form.html.haml @@ -8,7 +8,7 @@ %select.select2.fullwidth{id: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"} %td %label - %input{type: "checkbox", ng: {checked: "allPermissionsChecked()", click: "checkAllPermissions()"}} + %input{ type: "checkbox", "ng-checked": "allPermissionsChecked()", "ng-click": "checkAllPermissions()" } = t 'admin_enterprise_relationships_everything' %div{"ng-repeat" => "permission in EnterpriseRelationships.all_permissions"} %label diff --git a/app/views/admin/enterprise_relationships/_search_input.html.haml b/app/views/admin/enterprise_relationships/_search_input.html.haml index a85f201b91..b7f60be8c6 100644 --- a/app/views/admin/enterprise_relationships/_search_input.html.haml +++ b/app/views/admin/enterprise_relationships/_search_input.html.haml @@ -1,5 +1,5 @@ %input.search{"ng-model" => "query", "placeholder" => t(:admin_enterprise_relationships_seach_placeholder)} -%label{ng: {repeat: "permission in EnterpriseRelationships.all_permissions"}} - %input{type: "checkbox", ng: {click: "$parent.query = toggleKeyword($parent.query, permission)"}} +%label{ "ng-repeat": "permission in EnterpriseRelationships.all_permissions" } + %input{ type: "checkbox", "ng-click": "$parent.query = toggleKeyword($parent.query, permission)" } {{ EnterpriseRelationships.permission_presentation(permission) }} diff --git a/app/views/admin/enterprise_roles/_data.html.haml b/app/views/admin/enterprise_roles/_data.html.haml index d24fb584a7..a1b6b3472e 100644 --- a/app/views/admin/enterprise_roles/_data.html.haml +++ b/app/views/admin/enterprise_roles/_data.html.haml @@ -1,4 +1,3 @@ -= admin_inject_enterprise_roles(@enterprise_roles) -= admin_inject_users(@users) -= admin_inject_enterprises(@my_enterprises, @all_enterprises) - += admin_inject_json('ofn.admin', 'enterpriseRoles', @enterprise_roles) += admin_inject_json('ofn.admin', 'users', @users) += admin_inject_json('ofn.admin', 'my_enterprises', @my_enterprises) + admin_inject_json('ofn.admin', 'all_enterprises', @all_enterprises) diff --git a/app/views/admin/enterprises/_change_type_form.html.haml b/app/views/admin/enterprises/_change_type_form.html.haml index 5a71a1bb45..685ac3980d 100644 --- a/app/views/admin/enterprises/_change_type_form.html.haml +++ b/app/views/admin/enterprises/_change_type_form.html.haml @@ -3,13 +3,13 @@ = form_for @enterprise, url: main_app.register_admin_enterprise_path(@enterprise), html: { name: "change_type", id: "change_type", novalidate: true, "ng-app" => "admin.enterprises", "ng-controller"=> 'changeTypeFormCtrl' } do |change_type_form| -# Have to use hidden:'true' on this input rather than type:'hidden' as the latter seems to break ngPattern and therefore validation - %input{ hidden: "true", name: "sells", ng: { required: true, pattern: "/^(none|own|any)$/", model: 'sells', value: "sells"} } + %input{ hidden: "true", name: "sells", "ng-required": true, "ng-pattern": "/^(none|own|any)$/", "ng-model": 'sells', "ng-value": "sells" } .row .options.container - if @enterprise.is_primary_producer .basic_producer.option.six.columns.alpha - %a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } } + %a.full-width.button.selector{ "ng-click": "sells='none'", "ng-class": "{selected: sells=='none'}" } .top %h3= t('.producer_profile') %p= t('.connect_ofn') @@ -18,7 +18,7 @@ = t('.producer_description_text') .producer_shop.option.six.columns - %a.full-width.button.selector{ ng: { click: "sells='own'", class: "{selected: sells=='own'}" } } + %a.full-width.button.selector{ "ng-click": "sells='own'", "ng-class": "{selected: sells=='own'}" } .top %h3= t('.producer_shop') %p= t('.sell_your_produce') @@ -29,7 +29,7 @@ = t('.producer_shop_description_text2') .full_hub.option.six.columns.omega - %a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } } + %a.full-width.button.selector{ "ng-click": "sells='any'", "ng-class": "{selected: sells=='any'}" } .top %h3= t('.producer_hub') %p= t('.producer_hub_text') @@ -40,7 +40,7 @@ .two.columns.alpha   .shop_profile.option.six.columns - %a.full-width.button.selector{ ng: { click: "sells='none'", class: "{selected: sells=='none'}" } } + %a.full-width.button.selector{ "ng-click": "sells='none'", "ng-class": "{selected: sells=='none'}" } .top %h3= t('.profile') %p= t('.get_listing') @@ -49,7 +49,7 @@ = t('.profile_description_text') .full_hub.option.six.columns - %a.full-width.button.selector{ ng: { click: "sells='any'", class: "{selected: sells=='any'}" } } + %a.full-width.button.selector{ "ng-click": "sells='any'", "ng-class": "{selected: sells=='any'}" } .top %h3= t('.hub_shop') %p= t('.hub_shop_text') @@ -60,11 +60,11 @@ .row .sixteen.columns.alpha - %span.error{ ng: { show: "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } } + %span.error{ "ng-show": "(change_type.sells.$error.required || change_type.sells.$error.pattern) && submitted" } = t('.choose_option') - if @enterprise.sells == 'unspecified' - %input.button.big{ type: 'submit', value: t(:select_continue), ng: { click: "submit(change_type)" } } + %input.button.big{ type: 'submit', value: t(:select_continue), "ng-click": "submit(change_type)" } - else - %input.button.big{ type: 'submit', value: t('.change_now'), ng: { click: "submit(change_type)" } } + %input.button.big{ type: 'submit', value: t('.change_now'), "ng-click": "submit(change_type)" } %br   %hr diff --git a/app/views/admin/enterprises/_enterprise_user_index.html.haml b/app/views/admin/enterprises/_enterprise_user_index.html.haml index 46090da6af..030bc07de1 100644 --- a/app/views/admin/enterprises/_enterprise_user_index.html.haml +++ b/app/views/admin/enterprises/_enterprise_user_index.html.haml @@ -1,4 +1,4 @@ -%div{ ng: { controller: 'enterprisesCtrl' } } +%div{ "ng-controller": 'enterprisesCtrl' } .row{ 'ng-hide' => '!loaded' } .controls{ :class => "sixteen columns alpha", :style => "margin-bottom: 15px;" } .four.columns.alpha @@ -15,32 +15,32 @@ .row{ :class => "sixteen columns alpha", 'ng-show' => 'loaded && filteredEnterprises.length == 0'} %h1#no_results= t('.no_enterprises_found') - .row{ ng: { show: "loaded && filteredEnterprises.length > 0" } } + .row{ "ng-show": "loaded && filteredEnterprises.length > 0" } %table.index#enterprises - %col.name{ width: "28%", ng: { show: 'columns.name.visible' } } - %col.producer{ width: "18%", ng: { show: 'columns.producer.visible' }} - %col.package{ width: "18%", ng: { show: 'columns.package.visible' }} - %col.status{ width: "18%", ng: { show: 'columns.status.visible' }} - %col.manage{ width: "18%", ng: { show: 'columns.manage.visible' }} + %col.name{ width: "28%", "ng-show": 'columns.name.visible' } + %col.producer{ width: "18%", "ng-show": 'columns.producer.visible' } + %col.package{ width: "18%", "ng-show": 'columns.package.visible' } + %col.status{ width: "18%", "ng-show": 'columns.status.visible' } + %col.manage{ width: "18%", "ng-show": 'columns.manage.visible' } %thead - %tr{ ng: { controller: "ColumnsCtrl" } } - %th.name{ ng: { show: 'columns.name.visible' } }=t('admin.name') - %th.producer{ ng: { show: 'columns.producer.visible' } }=t('.producer?') - %th.package{ ng: { show: 'columns.package.visible' } }=t('.package') - %th.status{ ng: { show: 'columns.status.visible' } }=t('.status') - %th.manage{ ng: { show: 'columns.manage.visible' } }=t('.manage') - %tbody.panel-ctrl{ :id => "e_{{enterprise.id}}", object: "enterprise", ng: { repeat: "enterprise in filteredEnterprises = ( allEnterprises | filter:{ name: quickSearch } )" } } - %tr.enterprise{ ng: { class: { even: "'even'", odd: "'odd'"} } } - %td.name{ ng: { show: 'columns.name.visible' } } - %span{ ng: { bind: "::enterprise.name" } } - %td.producer.panel-toggle.text-center{ ng: { show: 'columns.producer.visible', class: "{error: enterprise.producerError}" }, name: "producer" } - %h5{ ng: { bind: "enterprise.producer" } } - %td.package.panel-toggle.text-center{ ng: { show: 'columns.package.visible', class: "{error: enterprise.packageError}" }, name: "package" } - %h5{ ng: { bind: "enterprise.package" } } - %td.status.panel-toggle.text-center{ ng: { show: 'columns.status.visible' }, name: "status" } - %i.icon-status{ ng: { class: "enterprise.status" } } - %td.manage{ ng: { show: 'columns.manage.visible' } } - %a.button.fullwidth{ ng: { href: '{{::enterprise.edit_path}}' } } + %tr{ "ng-controller": "ColumnsCtrl" } + %th.name{ "ng-show": 'columns.name.visible' }=t('admin.name') + %th.producer{ "ng-show": 'columns.producer.visible' }=t('.producer?') + %th.package{ "ng-show": 'columns.package.visible' }=t('.package') + %th.status{ "ng-show": 'columns.status.visible' }=t('.status') + %th.manage{ "ng-show": 'columns.manage.visible' }=t('.manage') + %tbody.panel-ctrl{ id: "e_{{enterprise.id}}", object: "enterprise", "ng-repeat": "enterprise in filteredEnterprises = ( allEnterprises | filter:{ name: quickSearch } )" } + %tr.enterprise{ "ng-class-even": "'even'", "ng-class-odd": "'odd'" } + %td.name{ "ng-show": 'columns.name.visible' } + %span{ "ng-bind": "::enterprise.name" } + %td.producer.panel-toggle.text-center{ name: "producer", "ng-show": 'columns.producer.visible', "ng-class": "{error: enterprise.producerError}" } + %h5{ "ng-bind": "enterprise.producer" } + %td.package.panel-toggle.text-center{ name: "package", "ng-show": 'columns.package.visible', "ng-class": "{error: enterprise.packageError}" } + %h5{ "ng-bind": "enterprise.package" } + %td.status.panel-toggle.text-center{ name: "status", "ng-show": 'columns.status.visible' } + %i.icon-status{ "ng-class": "enterprise.status" } + %td.manage{ "ng-show": 'columns.manage.visible' } + %a.button.fullwidth{ "ng-href": '{{::enterprise.edit_path}}' } = t('.manage_link') %i.icon-arrow-right diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index eff2a6ac1a..dcaae1a149 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -9,12 +9,6 @@ %fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }} %legend= t(".#{ item[:name] }.legend") - - when 'vouchers' - - if feature?(:vouchers, spree_current_user, @enterprise) - %fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }} - %legend= t(".#{ item[:form_name] || item[:name] }.legend") - = render "admin/enterprises/form/#{ item[:form_name] || item[:name] }", f: f - - else %fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }} %legend= t(".#{ item[:form_name] || item[:name] }.legend") diff --git a/app/views/admin/enterprises/_ng_form.html.haml b/app/views/admin/enterprises/_ng_form.html.haml index 18f29a5701..3ea83f8fbd 100644 --- a/app/views/admin/enterprises/_ng_form.html.haml +++ b/app/views/admin/enterprises/_ng_form.html.haml @@ -8,8 +8,8 @@ "ng-cloak" => true } do |f| %save-bar{ dirty: "enterprise_form.$dirty", persist: "true" } - %input.red{ type: "button", value: t(:update), ng: { click: "submit()", disabled: "!enterprise_form.$dirty" } } - %input{ type: "button", ng: { value: "enterprise_form.$dirty ? '#{t(:cancel)}' : '#{t(:close)}'", click: "cancel('#{main_app.admin_enterprises_path}')" } } + %input.red{ type: "button", value: t(:update), "ng-click": "submit()", "ng-disabled": "!enterprise_form.$dirty" } + %input{ type: "button", "ng-value": "enterprise_form.$dirty ? '#{t(:cancel)}' : '#{t(:close)}'", "ng-click": "cancel('#{main_app.admin_enterprises_path}')" } .row{ data: { controller: "tabs-and-panels", "tabs-and-panels-class-name-value": "selected" }} diff --git a/app/views/admin/enterprises/form/_connected_apps.html.haml b/app/views/admin/enterprises/form/_connected_apps.html.haml new file mode 100644 index 0000000000..c60a7da1a1 --- /dev/null +++ b/app/views/admin/enterprises/form/_connected_apps.html.haml @@ -0,0 +1,31 @@ +- enterprise ||= f.object +#connected-app-discover-regen + .connected-app__head + %div + %h3= t ".title" + %p= t ".tagline" + %div + - if enterprise.connected_apps.empty? + %button{ data: {reflex: "click->Admin::ConnectedApp#create", enterprise_id: enterprise.id} } + = t ".enable" + - elsif enterprise.connected_apps.connecting.present? + %button{ disabled: true } + %i.spinner.fa.fa-spin.fa-circle-o-notch +   + = t ".loading" + - else + %button{ data: {reflex: "click->Admin::ConnectedApp#destroy", enterprise_id: enterprise.id} } + = t ".disable" + + .connected-app__connection + - if enterprise.connected_apps.ready.present? + .connected-app__note + - link = enterprise.connected_apps[0].data["link"] + %p= t ".note" + %div + %a{ href: link, target: "_blank", class: "button secondary" } + = t ".link_label" + + %hr + .connected-app__description + = t ".description_html" diff --git a/app/views/admin/enterprises/form/_images.html.haml b/app/views/admin/enterprises/form/_images.html.haml index e305df260a..de1ab19c9f 100644 --- a/app/views/admin/enterprises/form/_images.html.haml +++ b/app/views/admin/enterprises/form/_images.html.haml @@ -2,12 +2,12 @@ .alpha.three.columns = f.label :logo %br - 100 x 100 pixels + = t('.logo_size') .omega.eight.columns - %img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.logo.thumb }}', if: 'Enterprise.logo' } } + %img{ class: 'image-field-group__preview-image', "ng-src": '{{ Enterprise.logo.thumb }}', "ng-if": 'Enterprise.logo' } %br = f.file_field :logo - %a.button.red{ href: '', ng: {click: 'removeLogo()', if: 'Enterprise.logo'} } + %a.button.red{ href: '', "ng-click": 'removeLogo()', "ng-if": 'Enterprise.logo' } = t('.remove_logo') .row.page-admin-enterprises-form__promo-image-field-group.image-field-group @@ -21,7 +21,7 @@ = t('.promo_image_note3') .omega.eight.columns - %img{ class: 'image-field-group__preview-image', ng: { src: '{{ Enterprise.promo_image.large }}', if: 'Enterprise.promo_image' } } + %img{ class: 'image-field-group__preview-image', "ng-src": '{{ Enterprise.promo_image.large }}', "ng-if": 'Enterprise.promo_image' } = f.file_field :promo_image - %a.button.red{ href: '', ng: {click: 'removePromoImage()', if: 'Enterprise.promo_image'} } + %a.button.red{ href: '', "ng-click": 'removePromoImage()', "ng-if": 'Enterprise.promo_image' } = t('.remove_promo_image') diff --git a/app/views/admin/enterprises/form/_shop_preferences.html.haml b/app/views/admin/enterprises/form/_shop_preferences.html.haml index be2d5d2045..616d631509 100644 --- a/app/views/admin/enterprises/form/_shop_preferences.html.haml +++ b/app/views/admin/enterprises/form/_shop_preferences.html.haml @@ -23,17 +23,13 @@ = radio_button :enterprise, :preferred_shopfront_product_sorting_method, :by_category, { 'ng-model' => 'Enterprise.preferred_shopfront_product_sorting_method' } = label :enterprise, :preferred_shopfront_product_sorting_method_by_category, t('.shopfront_sort_by_category') .eight.columns.omega - %textarea.fullwidth{ id: 'enterprise_preferred_shopfront_taxon_order', name: 'enterprise[preferred_shopfront_taxon_order]', rows: 6, - 'ofn-taxon-autocomplete' => '', 'multiple-selection' => 'true', placeholder: t('.shopfront_sort_by_category_placeholder'), - ng: { model: 'Enterprise.preferred_shopfront_taxon_order', readonly: "Enterprise.preferred_shopfront_product_sorting_method != 'by_category'" }} + %textarea.fullwidth{ id: 'enterprise_preferred_shopfront_taxon_order', name: 'enterprise[preferred_shopfront_taxon_order]', rows: 6, "ofn-taxon-autocomplete": '', "multiple-selection": 'true', placeholder: t('.shopfront_sort_by_category_placeholder'), "ng-model": 'Enterprise.preferred_shopfront_taxon_order', "ng-readonly": "Enterprise.preferred_shopfront_product_sorting_method != 'by_category'" } .row .three.columns.alpha = radio_button :enterprise, :preferred_shopfront_product_sorting_method, :by_producer, { 'ng-model' => 'Enterprise.preferred_shopfront_product_sorting_method' } = label :enterprise, :preferred_shopfront_product_sorting_method_by_producer, t('.shopfront_sort_by_producer') .eight.columns.omega - %textarea.fullwidth{ id: 'enterprise_preferred_shopfront_producer_order', name: 'enterprise[preferred_shopfront_producer_order]', rows: 6, - 'ofn-producer-autocomplete' => '', 'multiple-selection' => 'true', placeholder: t('.shopfront_sort_by_producer_placeholder'), - ng: { model: 'Enterprise.preferred_shopfront_producer_order', readonly: "Enterprise.preferred_shopfront_product_sorting_method != 'by_producer'" }} + %textarea.fullwidth{ id: 'enterprise_preferred_shopfront_producer_order', name: 'enterprise[preferred_shopfront_producer_order]', rows: 6, "ofn-producer-autocomplete": '', "multiple-selection": 'true', placeholder: t('.shopfront_sort_by_producer_placeholder'), "ng-model": 'Enterprise.preferred_shopfront_producer_order', "ng-readonly": "Enterprise.preferred_shopfront_product_sorting_method != 'by_producer'" } .row .three.columns.alpha @@ -56,7 +52,7 @@ = f.radio_button :require_login, true, "ng-model" => "Enterprise.require_login", "ng-value" => "true" = f.label :require_login, t('.shopfront_requires_login_true'), value: :true -.row{ng: {if: "!Enterprise.require_login"}} +.row{ "ng-if": "!Enterprise.require_login" } .three.columns.alpha %label= t '.allow_guest_orders' %div{'ofn-with-tip' => t('.allow_guest_orders_tip')} @@ -67,7 +63,7 @@ .five.columns.omega = f.radio_button :allow_guest_orders, false, "ng-model" => "Enterprise.allow_guest_orders", "ng-value" => "false" = f.label :allow_guest_orders, t('.allow_guest_orders_false'), value: :false - .row.warning{ng: {show: 'Enterprise.allow_guest_orders && Enterprise.allow_order_changes'}} + .row.warning{ "ng-show": 'Enterprise.allow_guest_orders && Enterprise.allow_order_changes' } .eight.columns.alpha.omega %i.icon-warning-sign = t '.recommend_require_login' diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml index 822ea3f1ff..875d09a8b8 100644 --- a/app/views/admin/enterprises/form/_tag_rules.html.haml +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -1,11 +1,11 @@ -.row{ ng: { controller: "TagRulesCtrl" } } +.row{ "ng-controller": "TagRulesCtrl" } .eleven.columns.alpha.omega - %ofn-sortable{ axis: "y", handle: ".header", items: '.customer_tag', position: "tagGroup.position", after: { sort: "updateRuleCounts()" } } - .no_tags{ ng: { show: "tagGroups.length == 0" } } + %ofn-sortable{ axis: "y", handle: ".header", items: '.customer_tag', position: "tagGroup.position", "after-sort": "updateRuleCounts()" } + .no_tags{ "ng-show": "tagGroups.length == 0" } = t('.no_tags_yet') = render 'admin/enterprises/form/tag_rules/default_rules' -# = render 'customer_tags' - .customer_tag{ id: "tg_{{tagGroup.position}}", ng: { repeat: "tagGroup in tagGroups" } } + .customer_tag{ id: "tg_{{tagGroup.position}}", "ng-repeat": "tagGroup in tagGroups" } .header %table %colgroup @@ -16,12 +16,12 @@ %h5 = t('.for_customers_tagged') %td - %tags-with-translation{ object: "tagGroup", max: 1, on: { tag: { added: "updateTagsRulesFor(tagGroup)", removed: "updateTagsRulesFor(tagGroup)" } } } + %tags-with-translation{ object: "tagGroup", max: 1, "on-tag-added": "updateTagsRulesFor(tagGroup)", "on-tag-removed": "updateTagsRulesFor(tagGroup)" } - .no_rules{ ng: { show: "tagGroup.rules.length == 0" } } + .no_rules{ "ng-show": "tagGroup.rules.length == 0" } = t('.no_rules_yet') - .tag_rule{ ng: { repeat: "rule in tagGroup.rules" } } + .tag_rule{ "ng-repeat": "rule in tagGroup.rules" } .add_rule.text-center %input.button.icon-plus{ type: 'button', value: t('.add_new_rule'), "add-new-rule-to" => "addNewRuleTo", "tag-group" => "tagGroup", "new-tag-rule-dialog" => true } .add_tag - %input.button.red.icon-plus{ type: 'button', value: t('.add_new_tag'), ng: { click: 'addNewTag()' } } + %input.button.red.icon-plus{ type: 'button', value: t('.add_new_tag'), "ng-click": 'addNewTag()' } diff --git a/app/views/admin/enterprises/form/_users.html.haml b/app/views/admin/enterprises/form/_users.html.haml index 8d33a27132..4951d09f8e 100644 --- a/app/views/admin/enterprises/form/_users.html.haml +++ b/app/views/admin/enterprises/form/_users.html.haml @@ -21,8 +21,8 @@ = render partial: 'admin/shared/whats_this_tooltip', locals: {tooltip_text: t('.contact_tip')} .eight.columns.omega - if full_permissions - %select.select2.fullwidth{id: 'receives_notifications_dropdown', name: 'receives_notifications', ng: {model: 'receivesNotifications', init: "receivesNotifications = '#{@enterprise.contact.id}'"}} - %option{ng: {repeat: 'user in Enterprise.users', selected: "user.id == #{@enterprise.contact.id}", hide: '!user.confirmed'}, value: '{{user.id}}'} + %select.select2.fullwidth{ id: 'receives_notifications_dropdown', name: 'receives_notifications', "ng-model": 'receivesNotifications', "ng-init": "receivesNotifications = '#{@enterprise.contact.id}'" } + %option{ value: '{{user.id}}', "ng-repeat": 'user in Enterprise.users', "ng-selected": "user.id == #{@enterprise.contact.id}", "ng-hide": '!user.confirmed' } {{user.email}} - else = @enterprise.contact.email @@ -41,16 +41,16 @@ - # Ignore this input in the submit = hidden_field_tag :ignored, nil, class: "select2 fullwidth", 'user-select' => 'newManager', 'ng-model' => 'newManager' %td.actions - %tr.animate-repeat{ id: "manager-{{manager.id}}", ng: { repeat: 'manager in Enterprise.users' }} + %tr.animate-repeat{ id: "manager-{{manager.id}}", "ng-repeat": 'manager in Enterprise.users' } %td = hidden_field_tag "enterprise[user_ids][]", nil, multiple: true, 'ng-value' => 'manager.id' {{ manager.email }} - %i.confirmation.confirmed.fa.fa-check-circle{ 'ofn-with-tip' => t('.email_confirmed'), ng: {show: 'manager.confirmed'} } - %i.confirmation.unconfirmed.fa.fa-exclamation-triangle{ 'ofn-with-tip' => t('.email_not_confirmed'), ng: {show: '!manager.confirmed'} } - %i.role.contact.fa.fa-envelope-o{ 'ofn-with-tip' => t('.contact'), ng: {show: 'manager.id == receivesNotifications'} } - %i.role.owner.fa.fa-star{ 'ofn-with-tip' => t('.owner'), ng: {show: 'manager.id == Enterprise.owner.id'} } + %i.confirmation.confirmed.fa.fa-check-circle{ "ofn-with-tip": t('.email_confirmed'), "ng-show": 'manager.confirmed' } + %i.confirmation.unconfirmed.fa.fa-exclamation-triangle{ "ofn-with-tip": t('.email_not_confirmed'), "ng-show": '!manager.confirmed' } + %i.role.contact.fa.fa-envelope-o{ "ofn-with-tip": t('.contact'), "ng-show": 'manager.id == receivesNotifications' } + %i.role.owner.fa.fa-star{ "ofn-with-tip": t('.owner'), "ng-show": 'manager.id == Enterprise.owner.id' } %td.actions - %a{ ng: {click: 'removeManager(manager)', class: "{disabled: manager.id == Enterprise.owner.id || manager.id == receivesNotifications}"}, :class => "icon-trash no-text" } + %a{ class: "icon-trash no-text", "ng-click": 'removeManager(manager)', "ng-class": "{disabled: manager.id == Enterprise.owner.id || manager.id == receivesNotifications}" } - else - @enterprise.users.each do |manager| diff --git a/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml b/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml index a58a71d8de..9bdb7570c6 100644 --- a/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml +++ b/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml @@ -8,9 +8,9 @@ %h5 = t('.by_default') %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "tag_rule_help_modal" } - .no_rules{ ng: { show: "defaultTagGroup.rules.length == 0" } } + .no_rules{ "ng-show": "defaultTagGroup.rules.length == 0" } = t('.no_rules_yet') - .tag_rule{ ng: { repeat: "rule in defaultTagGroup.rules" } } + .tag_rule{ "ng-repeat": "rule in defaultTagGroup.rules" } .add_rule.text-center %input.button.icon-plus{ type: 'button', value: t('.add_new_button'), "add-new-rule-to" => "addNewRuleTo", "tag-group" => "defaultTagGroup", "new-tag-rule-dialog" => true } diff --git a/app/views/admin/enterprises/new.html.haml b/app/views/admin/enterprises/new.html.haml index 26a34ad45f..a035288a55 100644 --- a/app/views/admin/enterprises/new.html.haml +++ b/app/views/admin/enterprises/new.html.haml @@ -16,5 +16,5 @@ = form_for [main_app, :admin, @enterprise], html: { "nav-check" => '', "nav-callback" => '' } do |f| .row - .twelve.columns.fullwidth_inputs{ ng: { controller: "NewEnterpriseController" } } + .twelve.columns.fullwidth_inputs{ "ng-controller": "NewEnterpriseController" } = render 'new_form', f: f, scope: 'admin.enterprises.form' diff --git a/app/views/admin/invoice_settings/edit.html.haml b/app/views/admin/invoice_settings/edit.html.haml index 0647c38920..ec5e7e6e65 100644 --- a/app/views/admin/invoice_settings/edit.html.haml +++ b/app/views/admin/invoice_settings/edit.html.haml @@ -10,10 +10,11 @@ = check_box_tag 'preferences[enable_invoices?]', '1', Spree::Config[:enable_invoices?] = label_tag nil, t('.enable_invoices?') - .field.align-center - = hidden_field_tag 'preferences[invoice_style2?]', '0' - = check_box_tag 'preferences[invoice_style2?]', '1', Spree::Config[:invoice_style2?] - = label_tag nil, t('.invoice_style2?') + - if ! OpenFoodNetwork::FeatureToggle.enabled?(:invoices, spree_current_user) + .field.align-center + = hidden_field_tag 'preferences[invoice_style2?]', '0' + = check_box_tag 'preferences[invoice_style2?]', '1', Spree::Config[:invoice_style2?] + = label_tag nil, t('.invoice_style2?') .field.align-center = hidden_field_tag 'preferences[enterprise_number_required_on_invoices?]', '0' diff --git a/app/views/admin/oidc_settings/index.html.haml b/app/views/admin/oidc_settings/index.html.haml index eed72c78d2..fac04aaec7 100644 --- a/app/views/admin/oidc_settings/index.html.haml +++ b/app/views/admin/oidc_settings/index.html.haml @@ -7,14 +7,26 @@ %h2= t(".connect") %br - - if spree_current_user.provider == 'openid_connect' && spree_current_user.uid.present? - = t(".already_connected") - = spree_current_user.uid + - if @account + = t(".connected", uid: @account.uid) %br %br = t(".view_account") = link_to t(".les_communs_link"), "#{ Devise.omniauth_configs[:openid_connect].options[:issuer] }/account" + %br + %br + = button_to t(".disconnect"), admin_oidc_setting_path(@account), method: :delete + + - if @account.refresh_token.blank? + %br + %br + %p= t(".note_expiry") + %br + %br + = button_to t(".refresh"), + Spree::Core::Engine.routes.url_helpers.spree_user_openid_connect_omniauth_authorize_path(auth_type: "login"), + data: { method: :post, "ujs-navigate": "false" } - else = t(".link_your_account") diff --git a/app/views/admin/order_cycles/_advanced_settings.html.haml b/app/views/admin/order_cycles/_advanced_settings.html.haml index 455329b68c..6c04ce73b9 100644 --- a/app/views/admin/order_cycles/_advanced_settings.html.haml +++ b/app/views/admin/order_cycles/_advanced_settings.html.haml @@ -22,7 +22,7 @@ .omega.eight.columns = f.check_box :automatic_notifications - .row{ "data-controller": "remote-toggle", "data-remote-toggle-selector-value": "#advanced_settings" } + .row{ "data-controller": "toggle-control", "data-toggle-control-selector-value": "#advanced_settings" } .sixteen.columns.alpha.omega.text-center %input{ type: 'submit', value: t('.save_reload') } - %a{ href: "#", "data-action": "click->remote-toggle#toggle" }= t(:close) + %a{ href: "#", "data-action": "click->toggle-control#toggleAdvancedSettings" }= t(:close) diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 94481b1f13..2eba9bc1ae 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -1,4 +1,4 @@ -%tr{ ng: { class: "'#{type} #{type}-{{ exchange.enterprise_id }}'" } } +%tr{ "ng-class": "'#{type} #{type}-{{ exchange.enterprise_id }}'" } %td{:class => "#{type}_name"} {{ enterprises[exchange.enterprise_id].name }} %td.products.panel-toggle.text-center{ name: "products" } {{ exchangeSelectedVariants(exchange) }} / {{ exchangeTotalVariants(exchange) }} @@ -7,7 +7,7 @@ %td.receival-details = text_field_tag 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', '', 'id' => 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', 'placeholder' => t('.receival_instructions_placeholder'), 'ng-model' => 'exchange.receival_instructions' - if type == 'distributor' - %td.tags.panel-toggle.text-center{ name: "tags", ng: { if: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } } + %td.tags.panel-toggle.text-center{ name: "tags", "ng-if": 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } {{ exchange.tags.length }} %td.collection-details = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'ng-init' => 'setPickupTimeFieldDirty($index, exchange.pickup_time)', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'required' => 'required', 'placeholder' => t('.pickup_time_placeholder'), 'ng-model' => 'exchange.pickup_time', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator', 'maxlength' => 35 @@ -16,7 +16,7 @@ = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => t('.pickup_instructions_placeholder'), 'ng-model' => 'exchange.pickup_instructions', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator' %span.icon-question-sign{'ofn-with-tip' => t('.pickup_instructions_tip')} %td.fees - %ol{ ng: { show: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } } + %ol{ "ng-show": 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } %li{'ng-repeat' => 'enterprise_fee in exchange.enterprise_fees'} = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', 'ng-model' => 'enterprise_fee.enterprise_id', 'ng-options' => 'enterprise.id as enterprise.name for enterprise in enterprisesWithFees()'} diff --git a/app/views/admin/order_cycles/_filters.html.haml b/app/views/admin/order_cycles/_filters.html.haml index a798f0ebe4..2013aed9f3 100644 --- a/app/views/admin/order_cycles/_filters.html.haml +++ b/app/views/admin/order_cycles/_filters.html.haml @@ -2,20 +2,20 @@ .filter.four.columns.alpha %label{ :for => 'query' }=t('admin.quick_search') %br - %input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query' }, placeholder: t(".search_by_order_cycle_name") } + %input.fullwidth{ type: "text", id: 'query', placeholder: t(".search_by_order_cycle_name"), "ng-model": 'query' } .filter_select.four.columns %label{ :for => 'involving_filter' }=t('.involving') %br - %input.ofn-select2.fullwidth{ id: 'involving_filter', type: 'number', blank: "{id: 0, name: '#{j t(".any_enterprise")}'}", data: 'enterprises', ng: { model: 'involvingFilter' } } + %input.ofn-select2.fullwidth{ id: 'involving_filter', type: 'number', blank: "{id: 0, name: '#{j t(".any_enterprise")}'}", data: 'enterprises', "ng-model": 'involvingFilter' } - if subscriptions_enabled? .filter_select.four.columns %label{ :for => 'schedule_filter' }=t('admin.order_cycles.index.schedule') %br - %input.ofn-select2.fullwidth{ id: 'schedule_filter', type: 'number', blank: "{id: 0, name: '#{j t(".any_schedule")}'}", data: 'schedules', ng: { model: 'scheduleFilter' } } + %input.ofn-select2.fullwidth{ id: 'schedule_filter', type: 'number', blank: "{id: 0, name: '#{j t(".any_schedule")}'}", data: 'schedules', "ng-model": 'scheduleFilter' } .one.columns   - else .five.columns   .filter_clear.three.columns.omega %label{ :for => 'clear_all_filters' } %br - %input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "#{t('admin.clear_all')}", ng: { click: "resetSelectFilters()" } } + %input.red.fullwidth{ type: 'button', id: 'clear_all_filters', value: "#{t('admin.clear_all')}", "ng-click": "resetSelectFilters()" } diff --git a/app/views/admin/order_cycles/_header.html.haml b/app/views/admin/order_cycles/_header.html.haml index 0d3f9fba8e..981726f631 100644 --- a/app/views/admin/order_cycles/_header.html.haml +++ b/app/views/admin/order_cycles/_header.html.haml @@ -1,35 +1,35 @@ %colgroup - %col{ ng: { show: 'columns.name.visible' } } - %col{ ng: { show: 'columns.schedules.visible' } } - %col{ ng: { show: 'columns.open.visible' }, style: 'width: 20%;' } - %col{ ng: { show: 'columns.close.visible' }, style: 'width: 20%;' } + %col{ "ng-show": 'columns.name.visible' } + %col{ "ng-show": 'columns.schedules.visible' } + %col{ style: 'width: 20%;', "ng-show": 'columns.open.visible' } + %col{ style: 'width: 20%;', "ng-show": 'columns.close.visible' } - unless simple_index - %col{ ng: { show: 'columns.producers.visible' } } - %col{ ng: { show: 'columns.coordinator.visible' } } - %col{ ng: { show: 'columns.shops.visible' } } - %col{ ng: { show: 'columns.products.visible' } } + %col{ "ng-show": 'columns.producers.visible' } + %col{ "ng-show": 'columns.coordinator.visible' } + %col{ "ng-show": 'columns.shops.visible' } + %col{ "ng-show": 'columns.products.visible' } %col{ style: 'width: 5%;' } %col{ style: 'width: 5%;' } %col{ style: 'width: 5%;' } %thead %tr - %th{ ng: { show: 'columns.name.visible' } } + %th{ "ng-show": 'columns.name.visible' } =t :name - %th{ ng: { show: 'columns.schedules.visible' } } + %th{ "ng-show": 'columns.schedules.visible' } =t('admin.order_cycles.index.schedules') - %th{ ng: { show: 'columns.open.visible' } } + %th{ "ng-show": 'columns.open.visible' } =t :open - %th{ ng: { show: 'columns.close.visible' } } + %th{ "ng-show": 'columns.close.visible' } =t :close - unless simple_index - %th{ ng: { show: 'columns.producers.visible' } } + %th{ "ng-show": 'columns.producers.visible' } =t :label_producers - %th{ ng: { show: 'columns.coordinator.visible' } } + %th{ "ng-show": 'columns.coordinator.visible' } =t :coordinator - %th{ ng: { show: 'columns.shops.visible' } } + %th{ "ng-show": 'columns.shops.visible' } =t :label_shops - %th{ ng: { show: 'columns.products.visible' } } + %th{ "ng-show": 'columns.products.visible' } =t :products %th.actions %th.actions diff --git a/app/views/admin/order_cycles/_loading_flash.html.haml b/app/views/admin/order_cycles/_loading_flash.html.haml index 64689ffe29..8d670b5d6e 100644 --- a/app/views/admin/order_cycles/_loading_flash.html.haml +++ b/app/views/admin/order_cycles/_loading_flash.html.haml @@ -1,6 +1,6 @@ -%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'RequestMonitor.loading' } } +%div.sixteen.columns.alpha.omega#loading{ "ng-cloak": true, "ng-if": 'RequestMonitor.loading' } = render partial: "components/admin_spinner" - %h1{ ng: { hide: 'orderCycles.length > 0' } } + %h1{ "ng-hide": 'orderCycles.length > 0' } = t('.loading_order_cycles') - %h1{ ng: { show: 'orderCycles.length > 0' } } + %h1{ "ng-show": 'orderCycles.length > 0' } = t('.loading') diff --git a/app/views/admin/order_cycles/_name_and_timing_form.html.haml b/app/views/admin/order_cycles/_name_and_timing_form.html.haml index 172a5763e2..e3bc8df191 100644 --- a/app/views/admin/order_cycles/_name_and_timing_form.html.haml +++ b/app/views/admin/order_cycles/_name_and_timing_form.html.haml @@ -35,11 +35,6 @@ = f.label :schedule_ids, t('admin.order_cycles.index.schedules') .six.columns - if viewing_as_coordinator_of?(@order_cycle) - %input.fullwidth.ofn-select2#schedule_ids{ name: 'order_cycle[schedule_ids]', - data: 'schedules', - multiple: 'true', - placeholder: t('admin.please_select'), - filter: '{viewing_as_coordinator: true}', - ng: { model: 'order_cycle.schedule_ids' } } + %input.fullwidth.ofn-select2#schedule_ids{ name: 'order_cycle[schedule_ids]', data: 'schedules', multiple: 'true', placeholder: t('admin.please_select'), filter: '{viewing_as_coordinator: true}', "ng-model": 'order_cycle.schedule_ids' } - else %schedule-list{ 'order-cycle' => 'order_cycle' } diff --git a/app/views/admin/order_cycles/_order_cycle_top_buttons.html.haml b/app/views/admin/order_cycles/_order_cycle_top_buttons.html.haml index 2cc89117b0..8d5cd0d6ed 100644 --- a/app/views/admin/order_cycles/_order_cycle_top_buttons.html.haml +++ b/app/views/admin/order_cycles/_order_cycle_top_buttons.html.haml @@ -1,8 +1,8 @@ - content_for :page_actions do - %li{ "data-controller": "remote-toggle", "data-remote-toggle-selector-value": "#advanced_settings" } - %button#toggle_settings{ "data-action": "click->remote-toggle#toggle" } + %li{ "data-controller": "toggle-control", "data-toggle-control-selector-value": "#advanced_settings" } + %button#toggle_settings{ "data-action": "click->toggle-control#toggleAdvancedSettings" } = t('.advanced_settings') - %i.icon-chevron-down{ "data-remote-toggle-target": "chevron" } + %i.icon-chevron-down{ "data-toggle-control-target": "chevron" } #advanced_settings{ style: "display: none" } = render partial: "/admin/order_cycles/advanced_settings" diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 4508858a1d..f331e4ca83 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -1,39 +1,39 @@ -%tr{ class: "order-cycle-{{orderCycle.id}} {{orderCycle.status}}", ng: { repeat: 'orderCycle in orderCycles | schedule:scheduleFilter | involving:involvingFilter | filter:{name: query} track by orderCycle.id' } } - %td.name{ ng: { show: 'columns.name.visible' } } - %input{ id: 'oc{{::orderCycle.id}}_name', name: 'oc{{::orderCycle.id}}[name]', type: 'text', ng: { model: 'orderCycle.name', disabled: '!orderCycle.viewing_as_coordinator' } } - %td.schedules{ ng: { show: 'columns.schedules.visible' } } - %span{ ng: { repeat: 'schedule in orderCycle.schedules'} } +%tr{ class: "order-cycle-{{orderCycle.id}} {{orderCycle.status}}", "ng-repeat": 'orderCycle in orderCycles | schedule:scheduleFilter | involving:involvingFilter | filter:{name: query} track by orderCycle.id' } + %td.name{ "ng-show": 'columns.name.visible' } + %input{ id: 'oc{{::orderCycle.id}}_name', name: 'oc{{::orderCycle.id}}[name]', type: 'text', "ng-model": 'orderCycle.name', "ng-disabled": '!orderCycle.viewing_as_coordinator' } + %td.schedules{ "ng-show": 'columns.schedules.visible' } + %span{ "ng-repeat": 'schedule in orderCycle.schedules' } %a{ 'schedule-dialog' => true, 'schedule-id' => '{{schedule.id}}' } {{ schedule.name + ($last ? '' : ',') }} - %span{ ng: { show: 'orderCycle.schedules.length == 0'}} None - %td.orders_open_at{ ng: { show: 'columns.open.visible' } } - %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at' }, data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, 'change-warning' => 'orderCycle' } - %input{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at'}, disabled: true } - %td.orders_close_at{ ng: { show: 'columns.close.visible' } } - %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at' }, data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, 'change-warning' => 'orderCycle' } - %input{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at'}, disabled: true } + %span{ "ng-show": 'orderCycle.schedules.length == 0' } None + %td.orders_open_at{ "ng-show": 'columns.open.visible' } + %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, "change-warning": 'orderCycle', "ng-if": 'orderCycle.viewing_as_coordinator', "ng-model": 'orderCycle.orders_open_at' } + %input{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', disabled: true, "ng-if": '!orderCycle.viewing_as_coordinator', "ng-model": 'orderCycle.orders_open_at' } + %td.orders_close_at{ "ng-show": 'columns.close.visible' } + %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, "change-warning": 'orderCycle', "ng-if": 'orderCycle.viewing_as_coordinator', "ng-model": 'orderCycle.orders_close_at' } + %input{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', disabled: true, "ng-if": '!orderCycle.viewing_as_coordinator', "ng-model": 'orderCycle.orders_close_at' } - unless simple_index - %td.producers{ ng: { show: 'columns.producers.visible' } } - %span{'ofn-with-tip' => '{{ orderCycle.producerNames }}', ng: { show: 'orderCycle.producers.length > 3' } } + %td.producers{ "ng-show": 'columns.producers.visible' } + %span{ "ofn-with-tip": '{{ orderCycle.producerNames }}', "ng-show": 'orderCycle.producers.length > 3' } {{ orderCycle.producers.length }} = t('.suppliers') - %span{ ng: { hide: 'orderCycle.producers.length > 3', bind: 'orderCycle.producerNames' } } - %td.coordinator{ ng: { show: 'columns.coordinator.visible', bind: { html: 'orderCycle.coordinator.name'} } } - %td.shops{ ng: { show: 'columns.shops.visible' } } - %span{'ofn-with-tip' => '{{ orderCycle.shopNames }}', ng: { show: 'orderCycle.shops.length > 3' } } + %span{ "ng-hide": 'orderCycle.producers.length > 3', "ng-bind": 'orderCycle.producerNames' } + %td.coordinator{ "ng-show": 'columns.coordinator.visible', "ng-bind-html": 'orderCycle.coordinator.name' } + %td.shops{ "ng-show": 'columns.shops.visible' } + %span{ "ofn-with-tip": '{{ orderCycle.shopNames }}', "ng-show": 'orderCycle.shops.length > 3' } {{ orderCycle.shops.length }} = t('.distributors') - %span{ ng: { hide: 'orderCycle.shops.length > 3', bind: 'orderCycle.shopNames' } } + %span{ "ng-hide": 'orderCycle.shops.length > 3', "ng-bind": 'orderCycle.shopNames' } - %td.products{ ng: { show: 'columns.products.visible' } } + %td.products{ "ng-show": 'columns.products.visible' } %span {{orderCycle.variant_count}} = t('.variants') %td.actions - %a.edit-order-cycle.icon-edit.no-text{ ng: { href: '{{orderCycle.edit_path}}'}, 'ofn-with-tip' => t(:edit) } - %td.actions{ ng: { if: 'orderCycle.viewing_as_coordinator' } } - %a.clone-order-cycle.icon-copy.no-text{ ng: { href: '{{orderCycle.clone_path}}'}, 'ofn-with-tip' => t(:clone) } - %td.actions{ ng: { if: 'orderCycle.deletable && orderCycle.viewing_as_coordinator' }} - %a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure), "ujs-navigate": "false" }, 'ofn-with-tip' => t(:remove) } + %a.edit-order-cycle.icon-edit.no-text{ "ofn-with-tip": t(:edit), "ng-href": '{{orderCycle.edit_path}}' } + %td.actions{ "ng-if": 'orderCycle.viewing_as_coordinator' } + %a.clone-order-cycle.icon-copy.no-text{ "ofn-with-tip": t(:clone), "ng-href": '{{orderCycle.clone_path}}' } + %td.actions{ "ng-if": 'orderCycle.deletable && orderCycle.viewing_as_coordinator' } + %a.delete-order-cycle.icon-trash.no-text{ data: { method: 'delete', confirm: t(:are_you_sure), "ujs-navigate": "false" }, "ofn-with-tip": t(:remove), "ng-href": '{{orderCycle.delete_path}}' } diff --git a/app/views/admin/order_cycles/_show_more.html.haml b/app/views/admin/order_cycles/_show_more.html.haml index 22191c4e38..80b7de3628 100644 --- a/app/views/admin/order_cycles/_show_more.html.haml +++ b/app/views/admin/order_cycles/_show_more.html.haml @@ -1,4 +1,4 @@ -.text-center.margin-bottom-50{ ng: { hide: "RequestMonitor.loading" } } - %input{ type: 'button', value: "#{t('admin.show_n_more', num: '30')} #{t(:days)}", ng: { click: 'showMore(30)' } } +.text-center.margin-bottom-50{ "ng-hide": "RequestMonitor.loading" } + %input{ type: 'button', value: "#{t('admin.show_n_more', num: '30')} #{t(:days)}", "ng-click": 'showMore(30)' } - %input{ type: 'button', value: "#{t('admin.show_n_more', num: '90')} #{t(:days)}", ng: { click: 'showMore(90)' } } + %input{ type: 'button', value: "#{t('admin.show_n_more', num: '90')} #{t(:days)}", "ng-click": 'showMore(90)' } diff --git a/app/views/admin/order_cycles/_simple_form.html.haml b/app/views/admin/order_cycles/_simple_form.html.haml index 8b8ac6f200..ff3fa25c6f 100644 --- a/app/views/admin/order_cycles/_simple_form.html.haml +++ b/app/views/admin/order_cycles/_simple_form.html.haml @@ -15,9 +15,9 @@ = label_tag t('.products') %table.exchanges - %tbody{ng: {repeat: "exchange in order_cycle.incoming_exchanges"}} + %tbody{ "ng-repeat": "exchange in order_cycle.incoming_exchanges" } %tr.products - %td{ ng: { include: "'admin/panels/exchange_products_simple.html'" } } + %td{ "ng-include": "'admin/panels/exchange_products_simple.html'" } %br = label_tag t('.tags') diff --git a/app/views/admin/order_cycles/edit.html.haml b/app/views/admin/order_cycles/edit.html.haml index 3a67c072bd..f6f3bc6f49 100644 --- a/app/views/admin/order_cycles/edit.html.haml +++ b/app/views/admin/order_cycles/edit.html.haml @@ -19,13 +19,13 @@ = form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f| %save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" } - %input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } + %input.red{ type: "button", value: t('.save'), "ng-click": "submit($event, null)", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } - if @order_cycle.simple? - %input.red{ type: "button", value: t('.save_and_back_to_list'), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } + %input.red{ type: "button", value: t('.save_and_back_to_list'), "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } - else - %input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } } - %input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } } + %input.red{ type: "button", value: t('.save_and_next'), "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" } + %input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" } - if @order_cycle.simple? = render 'simple_form', f: f diff --git a/app/views/admin/order_cycles/incoming.html.haml b/app/views/admin/order_cycles/incoming.html.haml index c9f40b9322..d4dbc1806b 100644 --- a/app/views/admin/order_cycles/incoming.html.haml +++ b/app/views/admin/order_cycles/incoming.html.haml @@ -9,10 +9,10 @@ = render 'wizard_progress' %save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" } - %input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } } - %input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } } + %input.red{ type: "button", value: t('.save'), "ng-click": "submit($event, null)", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input.red{ type: "button", value: t('.save_and_next'), "ng-click": "submit($event, '#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" } + %input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" } %fieldset.no-border-bottom %legend{ align: 'center'}= t('.incoming') diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index ed53a56ea3..db6c544286 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -14,19 +14,19 @@ = admin_inject_column_preferences module: 'admin.orderCycles' -%div{ ng: { controller: 'OrderCyclesCtrl' } } +%div{ "ng-controller": 'OrderCyclesCtrl' } = render 'admin/order_cycles/filters' %hr.divider - .row.controls{ ng: { show: "orderCycles.length > 0" } } + .row.controls{ "ng-show": "orderCycles.length > 0" } .thirteen.columns.alpha   %columns-dropdown{ action: "#{controller_name}_#{action_name}" } %form{ name: 'order_cycles_form' } %save-bar{ dirty: "order_cycles_form.$dirty", persist: "false" } - %input.red{ type: "button", value: t(:save_changes), ng: { click: "saveAll()", disabled: "!order_cycles_form.$dirty" } } - %table.index#listing_order_cycles{ ng: { show: 'orderCycles.length > 0' } } + %input.red{ type: "button", value: t(:save_changes), "ng-click": "saveAll()", "ng-disabled": "!order_cycles_form.$dirty" } + %table.index#listing_order_cycles{ "ng-show": 'orderCycles.length > 0' } = render 'admin/order_cycles/header' #, simple_index: simple_index %tbody = render 'admin/order_cycles/row' #, simple_index: simple_index diff --git a/app/views/admin/order_cycles/new.html.haml b/app/views/admin/order_cycles/new.html.haml index 6c35515d2f..6dd1c0b22b 100644 --- a/app/views/admin/order_cycles/new.html.haml +++ b/app/views/admin/order_cycles/new.html.haml @@ -9,8 +9,8 @@ %save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" } - if @order_cycle.simple? - custom_redirect_path = main_app.admin_order_cycles_path - %input.red{ type: "button", value: t('.create'), ng: { click: "submit($event, '#{custom_redirect_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } } + %input.red{ type: "button", value: t('.create'), "ng-click": "submit($event, '#{custom_redirect_path}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" } - if @order_cycle.simple? = render 'simple_form', f: f diff --git a/app/views/admin/order_cycles/outgoing.html.haml b/app/views/admin/order_cycles/outgoing.html.haml index d5b148f1cc..4812d36f7d 100644 --- a/app/views/admin/order_cycles/outgoing.html.haml +++ b/app/views/admin/order_cycles/outgoing.html.haml @@ -9,10 +9,10 @@ = render 'wizard_progress' %save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" } - %input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_checkout_options_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } } - %input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_checkout_options_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } } - %input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } } + %input.red{ type: "button", value: t('.save'), "ng-click": "submit($event, null)", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input.red{ type: "button", value: t('.save_and_next'), "ng-click": "submit($event, '#{main_app.admin_order_cycle_checkout_options_path(@order_cycle)}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" } + %input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_checkout_options_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" } + %input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" } %fieldset.no-border-bottom %legend{ align: 'center'}= t('.outgoing') @@ -27,7 +27,7 @@ %a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('outgoing')"} %span{'ng-show' => "OrderCycle.showProducts['outgoing']"}= t(:collapse_all) %span{'ng-hide' => "OrderCycle.showProducts['outgoing']"}= t(:expand_all) - %th{ ng: { if: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } } + %th{ "ng-if": 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } = t('.tags') %th= t('.delivery_details') %th= t('.fees') diff --git a/app/views/admin/product_import/_dfc_import_form.html.haml b/app/views/admin/product_import/_dfc_import_form.html.haml new file mode 100644 index 0000000000..3416a943e5 --- /dev/null +++ b/app/views/admin/product_import/_dfc_import_form.html.haml @@ -0,0 +1,16 @@ +%h3= t(".title") +%br + += form_with url: main_app.admin_dfc_product_imports_path, method: :get do |form| + = form.label :enterprise_id, t(".enterprise") + %span.required * + %br + = form.select :enterprise_id, options_from_collection_for_select(@producers, :id, :name, @producers.first&.id), { "data-controller": "tom-select", class: "primary" } + %br + %br + = form.label :catalog_url, t(".catalog_url") + %br + = form.text_field :catalog_url, size: 60 + %br + %br + = form.submit t(".import") diff --git a/app/views/admin/product_import/_entries_table.html.haml b/app/views/admin/product_import/_entries_table.html.haml index 6b0dcfdf6e..09be2df666 100644 --- a/app/views/admin/product_import/_entries_table.html.haml +++ b/app/views/admin/product_import/_entries_table.html.haml @@ -5,10 +5,10 @@ %th #{t('admin.product_import.import.line')} - @importer.table_headings.each do |heading| %th= heading - %tr{ng: {repeat: "(line_number, entry) in (entries | entriesFilterValid:'#{entries}')"}} + %tr{ "ng-repeat": "(line_number, entry) in (entries | entriesFilterValid:'#{entries}')" } %td - %i{ng: {class: "{'fa fa-warning error': (count(entry.errors) > 0), 'fa fa-check-circle success': (count(entry.errors) == 0)}"}} + %i{ "ng-class": "{'fa fa-warning error': (count(entry.errors) > 0), 'fa fa-check-circle success': (count(entry.errors) == 0)}" } %td {{line_number}} - %td{ng: {repeat: "(attribute, value) in entry.attributes", class: "{'invalid': attribute_invalid(attribute, line_number)}"}} + %td{ "ng-repeat": "(attribute, value) in entry.attributes", "ng-class": "{'invalid': attribute_invalid(attribute, line_number)}" } {{value}} diff --git a/app/views/admin/product_import/_errors_list.html.haml b/app/views/admin/product_import/_errors_list.html.haml index 787aabe76b..644f798bb1 100644 --- a/app/views/admin/product_import/_errors_list.html.haml +++ b/app/views/admin/product_import/_errors_list.html.haml @@ -1,9 +1,9 @@ -%div.import-errors{ng: {controller: 'ImportFeedbackCtrl', repeat: "(line_number, entry ) in (entries | entriesFilterValid:'invalid')"}} +%div.import-errors{ "ng-controller": 'ImportFeedbackCtrl', "ng-repeat": "(line_number, entry ) in (entries | entriesFilterValid:'invalid')" } %p.line %strong #{t('admin.product_import.import.item_line')} {{line_number}}: %span {{entry.attributes.name}} - %span{ng: {if: "entry.attributes.display_name"}} + %span{ "ng-if": "entry.attributes.display_name" } ( {{entry.attributes.display_name}} ) - %p.error{ng: {repeat: "(attribute, error) in entry.errors", show: "ignore_fields.indexOf(attribute) < 0" }} + %p.error{ "ng-repeat": "(attribute, error) in entry.errors", "ng-show": "ignore_fields.indexOf(attribute) < 0" }  -  {{error}} diff --git a/app/views/admin/product_import/_import_options.html.haml b/app/views/admin/product_import/_import_options.html.haml index c5c3e32c38..6e6144ffc9 100644 --- a/app/views/admin/product_import/_import_options.html.haml +++ b/app/views/admin/product_import/_import_options.html.haml @@ -4,7 +4,7 @@ - @importer.enterprises_index.each do |name, attrs| - if name and attrs[:id] and @importer.permission_by_id?(attrs[:id]) %div.panel-section.import-settings - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active}'}} + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active}' } %div.header-icon.success %i.fa.fa-check-circle %div.header-description diff --git a/app/views/admin/product_import/_import_review.html.haml b/app/views/admin/product_import/_import_review.html.haml index 90d8de9577..c6ff771a60 100644 --- a/app/views/admin/product_import/_import_review.html.haml +++ b/app/views/admin/product_import/_import_review.html.haml @@ -1,7 +1,7 @@ %h5= t('admin.product_import.import.validation_overview') %br -%div{ng: {controller: 'ImportFeedbackCtrl'}} +%div{ "ng-controller": 'ImportFeedbackCtrl' } - if @importer.product_field_errors? .alert-box.warning @@ -9,10 +9,10 @@ %em= @non_updatable_fields.keys.join(', ') + "." = t('.fields_ignored') - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"all"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"all"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"all")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"all")) == 0' } %div.header-icon.success %i.fa.fa-info-circle.info %div.header-count @@ -20,13 +20,13 @@ {{ count((entries | entriesFilterValid:"all")) }} %div.header-description = t('admin.product_import.import.entries_found') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"all")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"all")) == 0' } = render 'entries_table', entries: 'all' - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl', hide: 'count((entries | entriesFilterValid:"invalid")) == 0'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"invalid"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl', "ng-hide": 'count((entries | entriesFilterValid:"invalid")) == 0' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"invalid"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"invalid")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"invalid")) == 0' } %div.header-icon.error %i.fa.fa-warning %div.header-count @@ -34,15 +34,15 @@ {{ count((entries | entriesFilterValid:"invalid")) }} %div.header-description = t('admin.product_import.import.entries_with_errors') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"invalid")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"invalid")) == 0' } = render 'errors_list' %br = render 'entries_table', entries: 'invalid' - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl', hide: 'count((entries | entriesFilterValid:"create_product")) == 0'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"create_product"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl', "ng-hide": 'count((entries | entriesFilterValid:"create_product")) == 0' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"create_product"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"create_product")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"create_product")) == 0' } %div.header-icon.success %i.fa.fa-check-circle %div.header-count @@ -50,13 +50,13 @@ {{ count((entries | entriesFilterValid:"create_product")) }} %div.header-description = t('admin.product_import.import.products_to_create') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"create_product")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"create_product")) == 0' } = render 'entries_table', entries: 'create_product' - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl', hide: 'count((entries | entriesFilterValid:"update_product")) == 0'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"update_product"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl', "ng-hide": 'count((entries | entriesFilterValid:"update_product")) == 0' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"update_product"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"update_product")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"update_product")) == 0' } %div.header-icon.success %i.fa.fa-check-circle %div.header-count @@ -64,13 +64,13 @@ {{ count((entries | entriesFilterValid:"update_product")) }} %div.header-description = t('admin.product_import.import.products_to_update') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"update_product")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"update_product")) == 0' } = render 'entries_table', entries: 'update_product' - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl', hide: 'count((entries | entriesFilterValid:"create_inventory")) == 0'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"create_inventory"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl', "ng-hide": 'count((entries | entriesFilterValid:"create_inventory")) == 0' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"create_inventory"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"create_inventory")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"create_inventory")) == 0' } %div.header-icon.success %i.fa.fa-check-circle %div.header-count @@ -78,13 +78,13 @@ {{ count((entries | entriesFilterValid:"create_inventory")) }} %div.header-description = t('admin.product_import.import.inventory_to_create') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"create_inventory")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"create_inventory")) == 0' } = render 'entries_table', entries: 'create_inventory' - %div.panel-section{ng: {controller: 'DropdownPanelsCtrl', hide: 'count((entries | entriesFilterValid:"update_inventory")) == 0'}} - %div.panel-header{ng: {click: 'togglePanel()', class: '{active: active && count((entries | entriesFilterValid:"update_inventory"))}'}} + %div.panel-section{ "ng-controller": 'DropdownPanelsCtrl', "ng-hide": 'count((entries | entriesFilterValid:"update_inventory")) == 0' } + %div.panel-header{ "ng-click": 'togglePanel()', "ng-class": '{active: active && count((entries | entriesFilterValid:"update_inventory"))}' } %div.header-caret - %i{ng: {class: "{'icon-chevron-down': active, 'icon-chevron-right': !active}", hide: 'count((entries | entriesFilterValid:"update_inventory")) == 0'}} + %i{ "ng-class": "{'icon-chevron-down': active, 'icon-chevron-right': !active}", "ng-hide": 'count((entries | entriesFilterValid:"update_inventory")) == 0' } %div.header-icon.success %i.fa.fa-check-circle %div.header-count @@ -92,10 +92,10 @@ {{ count((entries | entriesFilterValid:"update_inventory")) }} %div.header-description = t('admin.product_import.import.inventory_to_update') - %div.panel-content{ng: {hide: '!active || count((entries | entriesFilterValid:"update_inventory")) == 0'}} + %div.panel-content{ "ng-hide": '!active || count((entries | entriesFilterValid:"update_inventory")) == 0' } = render 'entries_table', entries: 'update_inventory' - %div.panel-section{ng: {controller: 'ImportOptionsFormCtrl', hide: 'resetTotal == 0'}} + %div.panel-section{ "ng-controller": 'ImportOptionsFormCtrl', "ng-hide": 'resetTotal == 0' } %div.panel-header %div.header-caret %div.header-icon.info diff --git a/app/views/admin/product_import/_save_results.html.haml b/app/views/admin/product_import/_save_results.html.haml index 90cf847468..87af4141cf 100644 --- a/app/views/admin/product_import/_save_results.html.haml +++ b/app/views/admin/product_import/_save_results.html.haml @@ -4,31 +4,31 @@ %div.post-save-results - %p{ng: {show: 'updates.products_created'}} - %i.fa{ng: {class: "{'fa-info-circle': updates.products_created == 0, 'fa-check-circle': updates.products_created > 0}"}} + %p{ "ng-show": 'updates.products_created' } + %i.fa{ "ng-class": "{'fa-info-circle': updates.products_created == 0, 'fa-check-circle': updates.products_created > 0}" } %strong.created-count {{ updates.products_created }} = t('.products_created') - %p{ng: {show: 'updates.products_updated'}} - %i.fa{ng: {class: "{'fa-info-circle': updates.products_updated == 0, 'fa-check-circle': updates.products_updated > 0}"}} + %p{ "ng-show": 'updates.products_updated' } + %i.fa{ "ng-class": "{'fa-info-circle': updates.products_updated == 0, 'fa-check-circle': updates.products_updated > 0}" } %strong.updated-count {{ updates.products_updated }} = t('.products_updated') - %p{ng: {show: 'updates.inventory_created'}} - %i.fa{ng: {class: "{'fa-info-circle': updates.inventory_created == 0, 'fa-check-circle': updates.inventory_created > 0}"}} + %p{ "ng-show": 'updates.inventory_created' } + %i.fa{ "ng-class": "{'fa-info-circle': updates.inventory_created == 0, 'fa-check-circle': updates.inventory_created > 0}" } %strong.inv-created-count {{ updates.inventory_created }} = t('.inventory_created') - %p{ng: {show: 'updates.inventory_updated'}} - %i.fa{ng: {class: "{'fa-info-circle': updates.inventory_updated == 0, 'fa-check-circle': updates.inventory_updated > 0}"}} + %p{ "ng-show": 'updates.inventory_updated' } + %i.fa{ "ng-class": "{'fa-info-circle': updates.inventory_updated == 0, 'fa-check-circle': updates.inventory_updated > 0}" } %strong.inv-updated-count {{ updates.inventory_updated }} = t('.inventory_updated') - %p{ng: {show: 'updates.products_reset'}} + %p{ "ng-show": 'updates.products_reset' } %i.fa.fa-info-circle %strong.reset-count {{ updates.products_reset }} @@ -39,24 +39,24 @@ %br - %p{ng: {show: 'update_errors.length == 0'}} + %p{ "ng-show": 'update_errors.length == 0' } = t('.all_saved') - %div{ng: {show: 'update_errors.length > 0'}} + %div{ "ng-show": 'update_errors.length > 0' } %p {{ updated_total }} #{t('.some_saved')} %br %h5= t('.save_errors') - %p.save-error{ng: {repeat: 'error in update_errors'}} + %p.save-error{ "ng-repeat": 'error in update_errors' }  -  {{ error }} %br - %div{ng: {show: 'updated_total > 0'}} - %a.button.view{href: main_app.admin_inventory_path, ng: {show: 'updates.inventory_created > 0 || updates.inventory_updated > 0'}} + %div{ "ng-show": 'updated_total > 0' } + %a.button.view{ href: main_app.admin_inventory_path, "ng-show": 'updates.inventory_created > 0 || updates.inventory_updated > 0' } = t('.view_inventory') - %a.button.view{href: admin_products_path, ng: {show: 'updates.products_created > 0 || updates.products_updated > 0'}} + %a.button.view{ href: admin_products_path, "ng-show": 'updates.products_created > 0 || updates.products_updated > 0' } = t('.view_products') %a.button{href: main_app.admin_product_import_path} diff --git a/app/views/admin/product_import/_upload_form.html.haml b/app/views/admin/product_import/_upload_form.html.haml index cbe9cc9f9c..7477789f82 100644 --- a/app/views/admin/product_import/_upload_form.html.haml +++ b/app/views/admin/product_import/_upload_form.html.haml @@ -1,4 +1,4 @@ -%div{ng: {app: 'admin.productImport', controller: 'ImportOptionsFormCtrl', init: "initForm()"}} +%div{ "ng-app": 'admin.productImport', "ng-controller": 'ImportOptionsFormCtrl', "ng-init": "initForm()" } = form_tag main_app.admin_product_import_path, multipart: true, class: 'product-import' do diff --git a/app/views/admin/product_import/import.html.haml b/app/views/admin/product_import/import.html.haml index fe938b39d8..658d05abb1 100644 --- a/app/views/admin/product_import/import.html.haml +++ b/app/views/admin/product_import/import.html.haml @@ -4,7 +4,7 @@ = render partial: 'ams_data' = render partial: 'spree/admin/shared/product_sub_menu' -.import-wrapper{ng: {app: 'admin.productImport', controller: 'ImportFormCtrl'}} +.import-wrapper{ "ng-app": 'admin.productImport', "ng-controller": 'ImportFormCtrl' } - if @importer.item_count == 0 %h5 @@ -13,14 +13,14 @@ = t('.none_to_save') %br - else - .settings-section{ng: {show: 'step == "settings"'}} + .settings-section{ "ng-show": 'step == "settings"' } = render 'import_options' if @importer.table_headings %br - %a.button.proceed{href: '', ng: {click: 'confirmSettings()'}} + %a.button.proceed{ href: '', "ng-click": 'confirmSettings()' } = t('.import') %a.button{href: main_app.admin_product_import_path} #{t('admin.cancel')} - .progress-interface{ng: {show: 'step == "import"'}} + .progress-interface{ "ng-show": 'step == "import"' } %span.filename = @original_filename %span.percentage @@ -34,12 +34,12 @@ = render 'import_review' if @importer.table_headings - %div{ng: {controller: 'ImportFeedbackCtrl', show: 'count((entries | entriesFilterValid:"valid")) > 0'}} - %div{ng: {if: 'count((entries | entriesFilterValid:"invalid")) > 0'}} + %div{ "ng-controller": 'ImportFeedbackCtrl', "ng-show": 'count((entries | entriesFilterValid:"valid")) > 0' } + %div{ "ng-if": 'count((entries | entriesFilterValid:"invalid")) > 0' } %br %h5= t('admin.product_import.import.some_invalid_entries') %p= t('admin.product_import.import.fix_before_import') - %div{ng: {show: 'count((entries | entriesFilterValid:"invalid")) == 0'}} + %div{ "ng-show": 'count((entries | entriesFilterValid:"invalid")) == 0' } %br %h5= t('.no_errors') %p= t('.save_all_imported?') @@ -47,22 +47,22 @@ = hidden_field_tag :filepath, @filepath = hidden_field_tag "settings[import_into]", @import_into - %a.button.proceed{href: '', ng: {show: 'count((entries | entriesFilterValid:"invalid")) == 0', click: 'acceptResults()'}} + %a.button.proceed{ href: '', "ng-show": 'count((entries | entriesFilterValid:"invalid")) == 0', "ng-click": 'acceptResults()' } = t('.save') %a.button{href: main_app.admin_product_import_path}= t('admin.cancel') - %div{ng: {controller: 'ImportFeedbackCtrl', show: 'count((entries | entriesFilterValid:"valid")) == 0'}} + %div{ "ng-controller": 'ImportFeedbackCtrl', "ng-show": 'count((entries | entriesFilterValid:"valid")) == 0' } %br %a.button{href: main_app.admin_product_import_path}= t('admin.cancel') - .progress-interface{ng: {show: 'step == "save"'}} + .progress-interface{ "ng-show": 'step == "save"' } %span.filename #{t('.save_imported')} ({{ percentage.save }}) .progress-bar{} - %span.progress-track{ng: {style: "{'width': percentage.save }"}} + %span.progress-track{ "ng-style": "{'width': percentage.save }" } %p.red {{ exception }} - .save-results{ng: {show: 'step == "complete"'}} + .save-results{ "ng-show": 'step == "complete"' } = render 'save_results' diff --git a/app/views/admin/product_import/index.html.haml b/app/views/admin/product_import/index.html.haml index ef4c6ed9c3..15e413fcf9 100644 --- a/app/views/admin/product_import/index.html.haml +++ b/app/views/admin/product_import/index.html.haml @@ -13,3 +13,5 @@ %br = render 'upload_form' + += render 'dfc_import_form' if spree_current_user.oidc_account.present? diff --git a/app/views/admin/products_v3/_content.html.haml b/app/views/admin/products_v3/_content.html.haml index 0cda71ff9a..b805747b71 100644 --- a/app/views/admin/products_v3/_content.html.haml +++ b/app/views/admin/products_v3/_content.html.haml @@ -1,6 +1,7 @@ #products-content .container .sixteen.columns + = render partial: 'admin/shared/flashes', locals: { flashes: } if defined? flashes = render partial: 'filters', locals: { search_term: search_term, producer_id: producer_id, producer_options: producer_options, diff --git a/app/views/admin/products_v3/_delete_modal.html.haml b/app/views/admin/products_v3/_delete_modal.html.haml new file mode 100644 index 0000000000..40b6fbd32d --- /dev/null +++ b/app/views/admin/products_v3/_delete_modal.html.haml @@ -0,0 +1,16 @@ +-# object_type can be 'variant' or 'product' +- base_translation_key = ".delete_#{object_type}_modal" +- delete_modal = ConfirmModalComponent.new(id: "#{object_type}-delete-modal", + confirm_button_text: t("#{base_translation_key}.confirmation_text"), + cancel_button_text: t("#{base_translation_key}.cancellation_text"), + confirm_button_class: :red, + actions_alignment_class: 'justify-end', + confirm_reflexes: "click->products#delete_#{object_type}", + confirm_actions: "click->modal#close", + ) += render delete_modal do + %h2.margin-bottom-20.black-text + = t("#{base_translation_key}.heading") + %p + = t("#{base_translation_key}.prompt") + .margin-bottom-50 diff --git a/app/views/admin/products_v3/_edit_image.html.haml b/app/views/admin/products_v3/_edit_image.html.haml new file mode 100644 index 0000000000..57fccaac76 --- /dev/null +++ b/app/views/admin/products_v3/_edit_image.html.haml @@ -0,0 +1,17 @@ += render ModalComponent.new id: "#modal_edit_product_image_#{image.id}", instant: true, close_button: false, modal_class: :fit do + %h2= t(".title") + + -# Display image in the same way it appears in the shopfront popup + %p= image_tag image.persisted? ? image.url(:large) : Spree::Image.default_image_url(:large), width: 433, height: 433 + + -# Submit to controller, because StimulusReflex doesn't support file uploads + = form_for [:admin, product, image], + html: { multipart: true }, data: { controller: "form" } do |f| + %input{ type: :hidden, name: :return_url, value: return_url} + = f.hidden_field :viewable_id, value: product.id + + .modal-actions.justify-end + %input{ class: "secondary relaxed", type: 'button', value: t('.close'), "data-action": "click->modal#close" } + -# label.button provides a handy shortcut to open the file selector on click. Unfortunately this trick isn't keyboard accessible though.. + = f.label :attachment, t(".upload"), class: "button primary relaxed icon-upload-alt" + = f.file_field :attachment, accept: "image/*", style: "display: none", "data-action": "change->form#submit" diff --git a/app/views/admin/products_v3/_filters.html.haml b/app/views/admin/products_v3/_filters.html.haml index c1d61bf7bf..e413f481d3 100644 --- a/app/views/admin/products_v3/_filters.html.haml +++ b/app/views/admin/products_v3/_filters.html.haml @@ -5,10 +5,14 @@ - if producer_options.many? .producers = label_tag :producer_id, t('.producers.label') - = select_tag :producer_id, options_for_select(producer_options, producer_id), include_blank: t('.all_producers'), "data-controller": "tom-select", "data-tom-select-options-value": '{ "plugins": [] }', class: "fullwidth" + = select_tag :producer_id, options_for_select(producer_options, producer_id), + include_blank: t('.all_producers'), class: "fullwidth", + data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_producers')} .categories = label_tag :category_id, t('.categories.label') - = select_tag :category_id, options_for_select(category_options, category_id), include_blank: t('.all_categories'), "data-controller": "tom-select", "data-tom-select-options-value": '{ "plugins": [] }', class: "fullwidth" + = select_tag :category_id, options_for_select(category_options, category_id), + include_blank: t('.all_categories'), class: "fullwidth", + data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_categories')} .submit .search-button = button_tag t(".search"), class: "secondary icon-search relaxed" diff --git a/app/views/admin/products_v3/_product_row.html.haml b/app/views/admin/products_v3/_product_row.html.haml new file mode 100644 index 0000000000..1d2c68eca3 --- /dev/null +++ b/app/views/admin/products_v3/_product_row.html.haml @@ -0,0 +1,42 @@ +%td.with-image + %a.image-field{ href: admin_product_images_path(product), data: { controller: "modal", reflex: "click->products#edit_image", "current-id": product.id} } + = image_tag product.image&.url(:mini) || Spree::Image.default_image_url(:mini), width: 40, height: 40 + .button.secondary.mini= t('admin.products_page.image.edit') +%td.field.align-left.header + = f.hidden_field :id + = f.text_field :name, 'aria-label': t('admin.products_page.columns.name') + = error_message_on product, :name +%td.field + = f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') + = error_message_on product, :sku +%td.multi-field{ 'data-controller': 'toggle-control', 'data-toggle-control-match-value': 'items' } + = f.select :variant_unit_with_scale, + options_for_select(WeightsAndMeasures.variant_unit_options, product.variant_unit_with_scale), + {}, + class: "fullwidth no-input", + 'aria-label': t('admin.products_page.columns.unit_scale'), + data: { "controller": "tom-select", "tom-select-options-value": '{ "plugins": [] }', action: "change->toggle-control#displayIfMatch"} + .field + = f.text_field :variant_unit_name, 'aria-label': t('items'), 'data-toggle-control-target': 'control', style: (product.variant_unit == "items" ? "" : "display: none") + = error_message_on product, :variant_unit_name, 'data-toggle-control-target': 'control' +%td.align-right + -# empty +%td.align-right + -# empty +%td.align-right + -# empty +%td.align-left + .content= product.supplier&.name +%td.align-left + .content= product.primary_taxon&.name +%td.align-left +%td.align-left + .content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below) +%td.align-right + = render(VerticalEllipsisMenu::Component.new) do + = link_to t('admin.products_page.actions.edit'), edit_admin_product_path(product) + = link_to t('admin.products_page.actions.clone'), clone_admin_product_path(product) + %a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open", + "data-modal-link-target-value": "product-delete-modal", "class": "delete", + "data-modal-link-modal-dataset-value": {'data-current-id': product.id}.to_json } + = t('admin.products_page.actions.delete') diff --git a/app/views/admin/products_v3/_sort.html.haml b/app/views/admin/products_v3/_sort.html.haml index d3b4829f84..b89d926cef 100644 --- a/app/views/admin/products_v3/_sort.html.haml +++ b/app/views/admin/products_v3/_sort.html.haml @@ -6,4 +6,4 @@ = t(".pagination.clear_search") %form.with-dropdown = t(".pagination.per_page.show") - = select_tag :per_page, options_for_select([15, 25, 50, 100].collect{|i| [t('.pagination.per_page.per_page', num: i), i]}, pagy.items), data: { reflex: "change->products#change_per_page" } + = select_tag :per_page, options_for_select([15, 25, 50, 100].collect{|i| [t('.pagination.per_page.per_page', num: i), i]}, pagy.items), class: "no-input per-page", data: { reflex: "change->products#change_per_page", controller: "tom-select", "tom-select-options-value": '{ "plugins": [] }'} diff --git a/app/views/admin/products_v3/_table.html.haml b/app/views/admin/products_v3/_table.html.haml index 519738169c..3aa9df9ab6 100644 --- a/app/views/admin/products_v3/_table.html.haml +++ b/app/views/admin/products_v3/_table.html.haml @@ -1,38 +1,51 @@ = form_with url: bulk_update_admin_products_path, method: :patch, id: "products-form", builder: BulkFormBuilder, - html: {'data-reflex-serialize-form': true, 'data-reflex': 'submit->products#bulk_update', - 'data-controller': "bulk-form", 'data-bulk-form-disable-selector-value': "#sort,#filters", - 'data-bulk-form-error-value': defined?(error_counts), - } do |form| - %fieldset.form-actions{ class: ("hidden" unless defined?(error_counts)), 'data-bulk-form-target': "actions" } - .container - .status.eleven.columns - .modified_summary{ 'data-bulk-form-target': "changedSummary", 'data-translation-key': 'admin.products_v3.table.changed_summary'} - - if defined?(error_counts) - .error_summary - -# X products were saved correctly, but Y products could not be saved correctly. Please review errors and try again - = t('.error_summary.saved', count: error_counts[:saved]) + t('.error_summary.invalid', count: error_counts[:invalid]) - .form-buttons.five.columns - = form.submit t('.reset'), type: :reset, class: "medium", 'data-reflex': 'click->products#fetch' - = form.submit t('.save'), class: "medium" + html: { data: { reflex: 'submit->products#bulk_update', 'reflex-serialize-form': true, + controller: "bulk-form", 'bulk-form-disable-selector-value': "#sort,#filters", + 'bulk-form-error-value': defined?(error_counts), + } } do |form| + = render(partial: "admin/shared/flashes", locals: { flashes: }) if defined? flashes %table.products - %col{ width:"15%" } - %col{ width:"5%", style: "max-width:5em" } - %col{ width:"8%" } - %col{ width:"5%", style: "max-width:5em"} - %col{ width:"5%", style: "max-width:5em"} - %col{ width:"10%" }= # producer - %col{ width:"10%" } - %col{ width:"5%" } - %col{ width:"5%", style: "max-width:5em" } - %col{ width:"5%", style: "max-width:5em" } + %colgroup + %col{ width:"56" }= # Img (size + padding) + %col= # (grow to fill) Name + %col{ width:"8%"} + %col{ width:"8%"} + %col{ width:"5%"} + %col{ width:"5%"} + %col{ width:"10%"} + %col{ width:"15%"}= # Producer + %col{ width:"8%"} + %col{ width:"8%"} + %col{ width:"8%"} + %col{ width:"8%"}= # Actions %thead %tr + %td.form-actions-wrapper{ colspan: 12 } + .form-actions-wrapper2 + %fieldset.form-actions{ class: ("hidden" unless defined?(error_counts)), 'data-bulk-form-target': "actions" } + .container + .status + .modified_summary{ 'data-bulk-form-target': "changedSummary", 'data-translation-key': 'admin.products_v3.table.changed_summary'} + - if defined?(error_counts) + .error_summary + - if error_counts[:saved] > 0 + -# X products were saved correctly, but Y products could not be saved correctly. Please review errors and try again + = t('.error_summary.saved', count: error_counts[:saved]) + t('.error_summary.invalid', count: error_counts[:invalid]) + - else + -# Y products could not be saved correctly. Please review errors and try again + = t('.error_summary.invalid', count: error_counts[:invalid]) + .form-buttons + = form.submit t('.reset'), type: :reset, class: "medium", 'data-reflex': 'click->products#fetch' + = form.submit t('.save'), class: "medium" + %tr + %th.align-left= # image %th.align-left.with-input= t('admin.products_page.columns.name') - %th.align-right= t('admin.products_page.columns.sku') + %th.align-left.with-input= t('admin.products_page.columns.sku') + %th.align-left.with-input= t('admin.products_page.columns.unit_scale') %th.align-right= t('admin.products_page.columns.unit') - %th.align-right= t('admin.products_page.columns.price') - %th.align-right= t('admin.products_page.columns.on_hand') + %th.align-left.with-input= t('admin.products_page.columns.price') + %th.align-left.with-input= t('admin.products_page.columns.on_hand') %th.align-left= t('admin.products_page.columns.producer') %th.align-left= t('admin.products_page.columns.category') %th.align-left= t('admin.products_page.columns.tax_category') @@ -40,70 +53,26 @@ %th.align-right= t('admin.products_page.columns.actions') - products.each_with_index do |product, product_index| = form.fields_for("products", product, index: product_index) do |product_form| - %tbody.relaxed{ 'data-record-id': product_form.object.id } + %tbody.relaxed.naked_inputs{ data: { 'record-id': product_form.object.id, + controller: "nested-form", + action: 'nested-form:add->bulk-form#registerElements' } } %tr - %td.field.align-left.header - = product_form.hidden_field :id - = product_form.text_field :name, 'aria-label': t('admin.products_page.columns.name') - = error_message_on product, :name - %td.field - = product_form.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') - = error_message_on product, :sku - %td.align-right - .content - = product.variant_unit.upcase_first - / TODO: properly handle custom unit names - = WeightsAndMeasures::UNITS[product.variant_unit] && "(" + WeightsAndMeasures::UNITS[product.variant_unit][product.variant_unit_scale]["name"] + ")" - %td.align-right - -# empty - %td.align-right - -# empty - %td.align-left - .content= product.supplier&.name - %td.align-left - .content= product.primary_taxon&.name - %td.align-left - %td.align-left - .content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below) - %td.align-right - = render(VerticalEllipsisMenu::Component.new) do - = link_to t('admin.products_page.actions.edit'), edit_admin_product_path(product) - = link_to t('admin.products_page.actions.clone'), clone_admin_product_path(product) + = render partial: 'product_row', locals: { product:, f: product_form } - product.variants.each_with_index do |variant, variant_index| - = form.fields_for("products][#{product_index}][variants_attributes][", variant, variant_index:) do |variant_form| + = form.fields_for("products][#{product_index}][variants_attributes][", variant, index: variant_index) do |variant_form| %tr.condensed - %td.field - = variant_form.hidden_field :id - = variant_form.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: product.name - = error_message_on variant, :display_name - %td.field - = variant_form.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') - = error_message_on variant, :sku - %td.align-right - .content= variant.unit_to_display - %td.field - = variant_form.text_field :price, 'aria-label': t('admin.products_page.columns.price'), value: number_to_currency(variant.price, unit: '')&.strip # TODO: add a spec to prove that this formatting is necessary. If so, it should be in a shared form helper for currency inputs - = error_message_on variant, :price - %td.field.on-hand__wrapper{'data-controller': "popout"} - %button.on-hand__button{'data-popout-target': "button", 'aria-label': t('admin.products_page.columns.on_hand')} - = variant.on_demand ? t(:on_demand) : variant.on_hand - %div.on-hand__popout{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" } - .field - = variant_form.number_field :on_hand, 'aria-label': t('admin.products_page.columns.on_hand'), 'data-toggle-control-target': 'control', disabled: variant_form.object.on_demand - = error_message_on variant, :on_hand - .field.checkbox - = variant_form.label :on_demand do - = variant_form.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked' - = t(:on_demand) - %td.align-left - .content= variant.product.supplier&.name # same as product - %td.align-left - -# empty - %td.align-left - .content= variant.tax_category&.name || "None" # TODO: convert to dropdown, else translate hardcoded string. - %td.align-left - -# empty - %td.align-right - = render(VerticalEllipsisMenu::Component.new) do - = link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(product, variant) + = render partial: 'variant_row', locals: { variant:, f: variant_form } + + = form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", product.variants.build) do |new_variant_form| + %template{ 'data-nested-form-target': "template" } + %tr.condensed + = render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form } + + %tr{ 'data-nested-form-target': "target" } + %tr.condensed + %td + %td{ colspan: 11 } + %button.secondary.condensed.naked.icon-plus{ 'data-action': "nested-form#add", + 'aria-label': t('.new_variant') } + =t('.new_variant') diff --git a/app/views/admin/products_v3/_variant_row.html.haml b/app/views/admin/products_v3/_variant_row.html.haml new file mode 100644 index 0000000000..566bbab0e3 --- /dev/null +++ b/app/views/admin/products_v3/_variant_row.html.haml @@ -0,0 +1,49 @@ +%td + -# empty +%td.field + = f.hidden_field :id + = f.text_field :display_name, 'aria-label': t('admin.products_page.columns.name'), placeholder: variant.product.name + = error_message_on variant, :display_name +%td.field + = f.text_field :sku, 'aria-label': t('admin.products_page.columns.sku') + = error_message_on variant, :sku +%td + -# empty +- if variant.persisted? + %td.align-right + .content= variant.unit_to_display +- else # until unit component is developed, use a basic input just so we can create new records + %td.field + = f.number_field :unit_value, 'aria-label': t('admin.products_page.columns.unit') + = error_message_on variant, :unit_value +%td.field + = f.text_field :price, 'aria-label': t('admin.products_page.columns.price'), value: number_to_currency(variant.price, unit: '')&.strip # TODO: add a spec to prove that this formatting is necessary. If so, it should be in a shared form helper for currency inputs + = error_message_on variant, :price +%td.field.on-hand__wrapper{'data-controller': "popout"} + %button.on-hand__button{'data-popout-target': "button", 'aria-label': t('admin.products_page.columns.on_hand')} + = variant.on_demand ? t(:on_demand) : variant.on_hand + %div.on-hand__popout{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" } + .field + = f.number_field :on_hand, min: 0, 'aria-label': t('admin.products_page.columns.on_hand'), 'data-toggle-control-target': 'control', disabled: f.object.on_demand + = error_message_on variant, :on_hand + .field.checkbox + = f.label :on_demand do + = f.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked' + = t(:on_demand) +%td.align-left + .content= variant.product.supplier&.name # same as product +%td.align-left + -# empty +%td.align-left + .content= variant.tax_category&.name || "None" # TODO: convert to dropdown, else translate hardcoded string. +%td.align-left + -# empty +%td.align-right + - if variant.persisted? + = render(VerticalEllipsisMenu::Component.new) do + = link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant) + - if variant.product.variants.size > 1 + %a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open", + "data-modal-link-target-value": "variant-delete-modal", "class": "delete", + "data-modal-link-modal-dataset-value": {'data-current-id': variant.id}.to_json } + = t('admin.products_page.actions.delete') diff --git a/app/views/admin/products_v3/index.html.haml b/app/views/admin/products_v3/index.html.haml index 581dcc2605..52ca21def8 100644 --- a/app/views/admin/products_v3/index.html.haml +++ b/app/views/admin/products_v3/index.html.haml @@ -11,7 +11,13 @@ = render partial: 'spree/admin/shared/product_sub_menu' #products_v3_page{ "data-controller": "products" } - #loading-spinner.spinner-container{ "data-controller": "loading", "data-products-target": "loading" } - .spinner - = t('.loading') + + .spinner-overlay{ "data-controller": "loading", "data-products-target": "loading" } + .spinner-container + .spinner + = t('.loading') #products-content + - %w[product variant].each do |object_type| + = render partial: 'delete_modal', locals: { object_type: } + #modal-component + diff --git a/app/views/admin/reports/_rendering_options.html.haml b/app/views/admin/reports/_rendering_options.html.haml index dc00db24d0..cb954eb6eb 100644 --- a/app/views/admin/reports/_rendering_options.html.haml +++ b/app/views/admin/reports/_rendering_options.html.haml @@ -28,4 +28,3 @@ .alpha.two.columns= label_tag nil, t(:report_columns) .omega.fourteen.columns = render MultipleCheckedSelectComponent.new(name: "fields_to_show", options: @report.available_headers, selected: @rendering_options.options[:fields_to_show]) - \ No newline at end of file diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 8f334e467f..42f7ec8e05 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -1,11 +1,9 @@ - content_for :page_title do = @report_title -- content_for :minimal_js, true if @background_reports +- content_for :minimal_js, true -- options = @background_reports ? { data: { remote: "true" } } : {} - -= form_for @report.search, { url: url_for }.merge(options) do |f| += form_for @report.search, { url: url_for, data: { remote: "true" } } do |f| = hidden_field_tag "uuid", request.uuid %fieldset.no-border-bottom.print-hidden diff --git a/app/views/admin/shared/_angular_pagination.html.haml b/app/views/admin/shared/_angular_pagination.html.haml index 53a28af885..795e11a8af 100644 --- a/app/views/admin/shared/_angular_pagination.html.haml +++ b/app/views/admin/shared/_angular_pagination.html.haml @@ -1,4 +1,4 @@ -.pagination +.pagination{style: "clear: both;"} %button{'ng-click' => 'changePage(1)', 'ng-class' => "{'disabled': pagination.page == 1}", 'ng-disabled' => "pagination.page == 1"} = "«".html_safe = t(:first) diff --git a/app/views/admin/shared/_flashes.html.haml b/app/views/admin/shared/_flashes.html.haml index 57673b3f48..4e8b94dace 100644 --- a/app/views/admin/shared/_flashes.html.haml +++ b/app/views/admin/shared/_flashes.html.haml @@ -1,6 +1,8 @@ -#flashes +.flash-container - if defined? flashes - flashes.each do |type, msg| - .animate-show{"data-controller": "flash"} + .animate-show{"data-controller": "flash", "data-flash-auto-close-value": type == 'success'} .flash{type: "#{type}", class: "#{type}"} - %span= msg + .msg= msg + .actions + %button.secondary{"data-action": "click->flash#close"}= t('.dismiss') diff --git a/app/views/admin/shared/_side_menu.html.haml b/app/views/admin/shared/_side_menu.html.haml index 6240519f28..e900c233a8 100644 --- a/app/views/admin/shared/_side_menu.html.haml +++ b/app/views/admin/shared/_side_menu.html.haml @@ -1,13 +1,13 @@ .side_menu#side_menu - if @enterprise - enterprise_side_menu_items(@enterprise).each do |item| - - next if !item[:show] || (item[:name] == 'vouchers' && !feature?(:vouchers, spree_current_user, @enterprise)) - %a.menu_item{ href: item[:href] || "##{item[:name]}_panel", id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: item[:selected] } + - next if !item[:show] + %a.menu_item{ href: item[:href] || "##{item[:name]}_panel", data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab", test: "link_for_#{item[:name]}" }, class: item[:selected] } %i{ class: item[:icon_class] } %span= t(".enterprise.#{item[:name] }") - else - enterprise_group_side_menu_items.each do |item| - %a.menu_item{ href: "##{item[:name]}_panel", class: item[:selected], id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" } } + %a.menu_item{ href: "##{item[:name]}_panel", class: item[:selected], data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab", test: "link_for_#{item[:name]}" } } %i{ class: item[:icon_class] } %span= t(".enterprise_group.#{item[:name] }") diff --git a/app/views/admin/shared/_tooltip_button.html.haml b/app/views/admin/shared/_tooltip_button.html.haml index 06b37688cd..3eebca4671 100644 --- a/app/views/admin/shared/_tooltip_button.html.haml +++ b/app/views/admin/shared/_tooltip_button.html.haml @@ -1,5 +1,8 @@ %div{ "data-controller": "tooltip" } - %button{class: button_class, "data-reflex": button_reflex, "data-id": reflex_data_id, "data-tooltip-target": "element" } + - if local_assigns[:shipment] + %button{class: button_class,"data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "ship_order_#{reflex_data_id}", "data-id": reflex_data_id, "data-tooltip-target": "element" } + - else + %button{class: button_class, "data-reflex": button_reflex, "data-id": reflex_data_id, "data-tooltip-target": "element" } .tooltip-container .tooltip{"data-tooltip-target": "tooltip"} = sanitize tooltip_text diff --git a/app/views/admin/shared/_views_dropdown.html.haml b/app/views/admin/shared/_views_dropdown.html.haml index 9fe3df8521..8e1f299fa8 100644 --- a/app/views/admin/shared/_views_dropdown.html.haml +++ b/app/views/admin/shared/_views_dropdown.html.haml @@ -2,6 +2,6 @@ %span{ :class => 'icon-eye-open' }= "  #{t('admin.viewing', current_view_name: '{{ currentView().name }}')}".html_safe %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ ng: { repeat: "(viewKey, view) in views" }, toggle: { view: true }, 'close-on-click' => true } + %div.menu_item{ "close-on-click": true, "ng-repeat": "(viewKey, view) in views", "toggle-view": true } %span.check %span.name {{ view.name }} diff --git a/app/views/admin/subscriptions/_address.html.haml b/app/views/admin/subscriptions/_address.html.haml index f7bebb3ad2..4ec9516941 100644 --- a/app/views/admin/subscriptions/_address.html.haml +++ b/app/views/admin/subscriptions/_address.html.haml @@ -4,48 +4,48 @@ %legend{ align: 'center'}= t(:bill_address) .field %label{ for: 'bill_address_firstname'}= t(:first_name) - %input.fullwidth#bill_address_firstname{ name: 'bill_address_firstname', type: 'text', required: true, ng: { model: "subscription.bill_address.firstname" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_firstname.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['bill_address.firstname']", show: 'subscription_address_form.bill_address_firstname.$pristine' } } {{ error }} + %input.fullwidth#bill_address_firstname{ name: 'bill_address_firstname', type: 'text', required: true, "ng-model": "subscription.bill_address.firstname" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_firstname.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['bill_address.firstname']", "ng-show": 'subscription_address_form.bill_address_firstname.$pristine' } {{ error }} .field %label{ for: 'bill_address_lastname'}= t(:last_name) - %input.fullwidth#bill_address_lastname{ name: 'bill_address_lastname', type: 'text', required: true, ng: { model: "subscription.bill_address.lastname" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_lastname.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['bill_address.lastname']", show: 'subscription_address_form.bill_address_lastname.$pristine' } } {{ error }} + %input.fullwidth#bill_address_lastname{ name: 'bill_address_lastname', type: 'text', required: true, "ng-model": "subscription.bill_address.lastname" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_lastname.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['bill_address.lastname']", "ng-show": 'subscription_address_form.bill_address_lastname.$pristine' } {{ error }} .field %label{ for: 'bill_address_address1'}= t(:address) - %input.fullwidth#bill_address_address1{ name: 'bill_address_address1', type: 'text', required: true, ng: { model: "subscription.bill_address.address1" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_address1.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['bill_address.address1']", show: 'subscription_address_form.bill_address_address1.$pristine' } } {{ error }} + %input.fullwidth#bill_address_address1{ name: 'bill_address_address1', type: 'text', required: true, "ng-model": "subscription.bill_address.address1" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_address1.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['bill_address.address1']", "ng-show": 'subscription_address_form.bill_address_address1.$pristine' } {{ error }} .field %label{ for: 'bill_address_city'}= t(:suburb) - %input.fullwidth#bill_address_city{ name: 'bill_address_city', type: 'text', required: true, ng: { model: "subscription.bill_address.city" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_city.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.bill_address.city', show: 'subscription_address_form.bill_address_city.$pristine' } } {{ error }} + %input.fullwidth#bill_address_city{ name: 'bill_address_city', type: 'text', required: true, "ng-model": "subscription.bill_address.city" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_city.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.bill_address.city', "ng-show": 'subscription_address_form.bill_address_city.$pristine' } {{ error }} .field %label{ for: "bill_address_zipcode"}= t(:postcode) - %input.fullwidth#bill_address_zipcode{ name: 'bill_address_zipcode', type: 'text', required: true, ng: { model: "subscription.bill_address.zipcode" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_zipcode.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.bill_address.zipcode', show: 'subscription_address_form.bill_address_zipcode.$pristine' } } {{ error }} + %input.fullwidth#bill_address_zipcode{ name: 'bill_address_zipcode', type: 'text', required: true, "ng-model": "subscription.bill_address.zipcode" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_zipcode.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.bill_address.zipcode', "ng-show": 'subscription_address_form.bill_address_zipcode.$pristine' } {{ error }} .field %label{ for: "bill_address_phone"}= t(:phone) - %input.fullwidth#bill_address_phone{ name: 'bill_address_phone', type: 'text', required: true, ng: { model: "subscription.bill_address.phone" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_phone.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.bill_address.phone', show: 'subscription_address_form.bill_address_phone.$pristine' } } {{ error }} + %input.fullwidth#bill_address_phone{ name: 'bill_address_phone', type: 'text', required: true, "ng-model": "subscription.bill_address.phone" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_phone.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.bill_address.phone', "ng-show": 'subscription_address_form.bill_address_phone.$pristine' } {{ error }} .field %label{ for: "bill_address_country_id"}= t(:country) - %input.ofn-select2.fullwidth#bill_address_country_id{ name: 'bill_address_country_id', type: 'number', data: 'countries', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.bill_address.country_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_country_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.bill_address.country', show: 'subscription_address_form.bill_address_country_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#bill_address_country_id{ name: 'bill_address_country_id', type: 'number', data: 'countries', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.bill_address.country_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_country_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.bill_address.country', "ng-show": 'subscription_address_form.bill_address_country_id.$pristine' } {{ error }} .field %label{ for: "bill_address_state_id"}= t(:state) - %input.ofn-select2.fullwidth#bill_address_state_id{ name: 'bill_address_state_id', type: 'number', data: 'billStates', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.bill_address.state_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.bill_address_state_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.bill_address.state', show: 'subscription_address_form.bill_address_state_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#bill_address_state_id{ name: 'bill_address_state_id', type: 'number', data: 'billStates', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.bill_address.state_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.bill_address_state_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.bill_address.state', "ng-show": 'subscription_address_form.bill_address_state_id.$pristine' } {{ error }} .two.columns - %a.button.red.fullwidth{ href: 'javascript:void(0)', ng: { click: 'shipAddressFromBilling()' } } + %a.button.red.fullwidth{ href: 'javascript:void(0)', "ng-click": 'shipAddressFromBilling()' } = t('copy') %i.icon-chevron-right .seven.columns.omega @@ -53,41 +53,41 @@ %legend{ align: 'center'}= t(:ship_address) .field %label{ for: 'ship_address_firstname'}= t(:first_name) - %input.fullwidth#ship_address_firstname{ name: 'ship_address_firstname', type: 'text', required: true, ng: { model: "subscription.ship_address.firstname" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_firstname.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['ship_address.firstname']", show: 'subscription_address_form.ship_address_firstname.$pristine' } } {{ error }} + %input.fullwidth#ship_address_firstname{ name: 'ship_address_firstname', type: 'text', required: true, "ng-model": "subscription.ship_address.firstname" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_firstname.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['ship_address.firstname']", "ng-show": 'subscription_address_form.ship_address_firstname.$pristine' } {{ error }} .field %label{ for: 'ship_address_lastname'}= t(:last_name) - %input.fullwidth#ship_address_lastname{ name: 'ship_address_lastname', type: 'text', required: true, ng: { model: "subscription.ship_address.lastname" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_lastname.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['ship_address.lastname']", show: 'subscription_address_form.ship_address_lastname.$pristine' } } {{ error }} + %input.fullwidth#ship_address_lastname{ name: 'ship_address_lastname', type: 'text', required: true, "ng-model": "subscription.ship_address.lastname" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_lastname.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['ship_address.lastname']", "ng-show": 'subscription_address_form.ship_address_lastname.$pristine' } {{ error }} .field %label{ for: 'ship_address_address1'}= t(:address) - %input.fullwidth#ship_address_address1{ name: 'ship_address_address1', type: 'text', required: true, ng: { model: "subscription.ship_address.address1" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_address1.$error.required' } }= t(:error_required) - .error{ ng: { repeat: "error in errors['ship_address.address1']", show: 'subscription_address_form.ship_address_address1.$pristine' } } {{ error }} + %input.fullwidth#ship_address_address1{ name: 'ship_address_address1', type: 'text', required: true, "ng-model": "subscription.ship_address.address1" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_address1.$error.required' }= t(:error_required) + .error{ "ng-repeat": "error in errors['ship_address.address1']", "ng-show": 'subscription_address_form.ship_address_address1.$pristine' } {{ error }} .field %label{ for: 'ship_address_city'}= t(:suburb) - %input.fullwidth#ship_address_city{ name: 'ship_address_city', type: 'text', required: true, ng: { model: "subscription.ship_address.city" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_city.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.ship_address.city', show: 'subscription_address_form.ship_address_city.$pristine' } } {{ error }} + %input.fullwidth#ship_address_city{ name: 'ship_address_city', type: 'text', required: true, "ng-model": "subscription.ship_address.city" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_city.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.ship_address.city', "ng-show": 'subscription_address_form.ship_address_city.$pristine' } {{ error }} .field %label{ for: "ship_address_zipcode"}= t(:postcode) - %input.fullwidth#ship_address_zipcode{ name: 'ship_address_zipcode', type: 'text', required: true, ng: { model: "subscription.ship_address.zipcode" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_zipcode.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.ship_address.zipcode', show: 'subscription_address_form.ship_address_zipcode.$pristine' } } {{ error }} + %input.fullwidth#ship_address_zipcode{ name: 'ship_address_zipcode', type: 'text', required: true, "ng-model": "subscription.ship_address.zipcode" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_zipcode.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.ship_address.zipcode', "ng-show": 'subscription_address_form.ship_address_zipcode.$pristine' } {{ error }} .field %label{ for: "ship_address_phone"}= t(:phone) - %input.fullwidth#ship_address_phone{ name: 'ship_address_phone', type: 'text', required: true, ng: { model: "subscription.ship_address.phone" } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_phone.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.ship_address.phone', show: 'subscription_address_form.ship_address_phone.$pristine' } } {{ error }} + %input.fullwidth#ship_address_phone{ name: 'ship_address_phone', type: 'text', required: true, "ng-model": "subscription.ship_address.phone" } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_phone.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.ship_address.phone', "ng-show": 'subscription_address_form.ship_address_phone.$pristine' } {{ error }} .field %label{ for: "ship_address_country_id"}= t(:country) - %input.ofn-select2.fullwidth#ship_address_country_id{ name: 'ship_address_country_id', type: 'number', data: 'countries', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.ship_address.country_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_country_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.ship_address.country', show: 'subscription_address_form.ship_address_country_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#ship_address_country_id{ name: 'ship_address_country_id', type: 'number', data: 'countries', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.ship_address.country_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_country_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.ship_address.country', "ng-show": 'subscription_address_form.ship_address_country_id.$pristine' } {{ error }} .field %label{ for: "ship_address_state_id"}= t(:state) - %input.ofn-select2.fullwidth#ship_address_state_id{ name: 'ship_address_state_id', type: 'number', data: 'shipStates', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.ship_address.state_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_address_form.ship_address_state_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.ship_address.state', show: 'subscription_address_form.ship_address_state_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#ship_address_state_id{ name: 'ship_address_state_id', type: 'number', data: 'shipStates', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.ship_address.state_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_address_form.ship_address_state_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.ship_address.state', "ng-show": 'subscription_address_form.ship_address_state_id.$pristine' } {{ error }} diff --git a/app/views/admin/subscriptions/_autocomplete.html.haml b/app/views/admin/subscriptions/_autocomplete.html.haml index 784c77009e..91a699ff2d 100644 --- a/app/views/admin/subscriptions/_autocomplete.html.haml +++ b/app/views/admin/subscriptions/_autocomplete.html.haml @@ -9,12 +9,12 @@ %td.vertical-align-top .field = label_tag :add_variant_id, t('.name_or_sku') - %input#add_variant_id.variant_autocomplete.fullwidth{ type: 'number', ng: { model: 'newItem.variant_id' } } + %input#add_variant_id.variant_autocomplete.fullwidth{ type: 'number', "ng-model": 'newItem.variant_id' } %td.vertical-align-top .field = label_tag :add_quantity, t('.quantity') - %input#add_quantity.fullwidth{ type: 'number', min: 1, ng: { model: 'newItem.quantity' } } + %input#add_quantity.fullwidth{ type: 'number', min: 1, "ng-model": 'newItem.quantity' } %td .actions - %a.icon-plus.button.fullwidth{ href: 'javascript:void(0)', ng: { click: 'addSubscriptionLineItem()' } } + %a.icon-plus.button.fullwidth{ href: 'javascript:void(0)', "ng-click": 'addSubscriptionLineItem()' } = t('.add') diff --git a/app/views/admin/subscriptions/_controls.html.haml b/app/views/admin/subscriptions/_controls.html.haml index ece0c7eecb..ae94e78d54 100644 --- a/app/views/admin/subscriptions/_controls.html.haml +++ b/app/views/admin/subscriptions/_controls.html.haml @@ -1,5 +1,5 @@ -%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'shop_id && subscriptions.length > 0' } } -.controls.sixteen.columns.alpha.omega{ ng: { show: 'shop_id && subscriptions.length > 0' } } +%hr.divider.sixteen.columns.alpha.omega{ "ng-show": 'shop_id && subscriptions.length > 0' } +.controls.sixteen.columns.alpha.omega{ "ng-show": 'shop_id && subscriptions.length > 0' } .twelve.columns.alpha   .four.columns.omega diff --git a/app/views/admin/subscriptions/_details.html.haml b/app/views/admin/subscriptions/_details.html.haml index 9015d16024..e9df22b24e 100644 --- a/app/views/admin/subscriptions/_details.html.haml +++ b/app/views/admin/subscriptions/_details.html.haml @@ -3,42 +3,42 @@ .row .seven.columns.alpha.field %label{ for: 'customer_id'}= t('admin.customer') - %input.ofn-select2.fullwidth#customer_id{ name: 'customer_id', type: 'number', data: 'customers', text: 'email', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.customer_id', disabled: 'subscription.id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.customer_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.customer', show: 'subscription_details_form.customer_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#customer_id{ name: 'customer_id', type: 'number', data: 'customers', text: 'email', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.customer_id', "ng-disabled": 'subscription.id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_details_form.customer_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.customer', "ng-show": 'subscription_details_form.customer_id.$pristine' } {{ error }} .two.columns   .seven.columns.omega.field %label{ for: 'schedule_id'}= t('admin.schedule') - %input.ofn-select2.fullwidth#schedule_id{ name: 'schedule_id', type: 'number', data: 'schedules', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.schedule_id', disabled: 'subscription.id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.schedule_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.schedule', show: 'subscription_details_form.schedule_id.$pristine'} } {{ error }} + %input.ofn-select2.fullwidth#schedule_id{ name: 'schedule_id', type: 'number', data: 'schedules', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.schedule_id', "ng-disabled": 'subscription.id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_details_form.schedule_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.schedule', "ng-show": 'subscription_details_form.schedule_id.$pristine' } {{ error }} .row .seven.columns.alpha.field %label{ for: 'payment_method_id'} = t('admin.payment_method') %span.with-tip.icon-question-sign{ data: { powertip: "#{t('.allowed_payment_method_types_tip')}" } } - %input.ofn-select2.fullwidth#payment_method_id{ name: 'payment_method_id', type: 'number', data: 'paymentMethods', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.payment_method_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.payment_method_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.payment_method', show: 'subscription_details_form.payment_method_id.$pristine' } } {{ error }} - .error{ ng: { show: 'cardRequired && customer.$promise && customer.$resolved && !customer.allow_charges' } }= t('.charges_not_allowed') - .error{ ng: { show: 'cardRequired && customer.$promise && customer.$resolved && customer.allow_charges && !customer.default_card_present' } }= t('.no_default_card') - .error{ ng: { repeat: 'error in errors.credit_card', show: 'subscription_details_form.payment_method_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#payment_method_id{ name: 'payment_method_id', type: 'number', data: 'paymentMethods', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.payment_method_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_details_form.payment_method_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.payment_method', "ng-show": 'subscription_details_form.payment_method_id.$pristine' } {{ error }} + .error{ "ng-show": 'cardRequired && customer.$promise && customer.$resolved && !customer.allow_charges' }= t('.charges_not_allowed') + .error{ "ng-show": 'cardRequired && customer.$promise && customer.$resolved && customer.allow_charges && !customer.default_card_present' }= t('.no_default_card') + .error{ "ng-repeat": 'error in errors.credit_card', "ng-show": 'subscription_details_form.payment_method_id.$pristine' } {{ error }} .two.columns   .seven.columns.omega.field %label{ for: 'shipping_method_id'}= t('admin.shipping_method') - %input.ofn-select2.fullwidth#shipping_method_id{ name: 'shipping_method_id', type: 'number', data: 'shippingMethods', required: true, placeholder: t('admin.choose'), ng: { model: 'subscription.shipping_method_id' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.shipping_method_id.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.shipping_method', show: 'subscription_details_form.shipping_method_id.$pristine' } } {{ error }} + %input.ofn-select2.fullwidth#shipping_method_id{ name: 'shipping_method_id', type: 'number', data: 'shippingMethods', required: true, placeholder: t('admin.choose'), "ng-model": 'subscription.shipping_method_id' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_details_form.shipping_method_id.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.shipping_method', "ng-show": 'subscription_details_form.shipping_method_id.$pristine' } {{ error }} .row .seven.columns.alpha.field %label{ for: 'begins_at'}= t('admin.begins_at') - %input.fullwidth#begins_at{ name: 'begins_at', type: 'text', placeholder: "#{t('.begins_at_placeholder')}", data: { controller: "flatpickr" }, required: true, ng: { model: 'subscription.begins_at' } } - .error{ ng: { show: 'subscription_form.$submitted && subscription_details_form.begins_at.$error.required' } }= t(:error_required) - .error{ ng: { repeat: 'error in errors.begins_at', show: 'subscription_details_form.begins_at.$pristine' } } {{ error }} + %input.fullwidth#begins_at{ name: 'begins_at', type: 'text', placeholder: "#{t('.begins_at_placeholder')}", data: { controller: "flatpickr" }, required: true, "ng-model": 'subscription.begins_at' } + .error{ "ng-show": 'subscription_form.$submitted && subscription_details_form.begins_at.$error.required' }= t(:error_required) + .error{ "ng-repeat": 'error in errors.begins_at', "ng-show": 'subscription_details_form.begins_at.$pristine' } {{ error }} .two.columns   .seven.columns.omega.field %label{ for: 'ends_at'}= t('admin.ends_at') - %input.fullwidth#ends_at{ name: 'ends_at', type: 'text', placeholder: "#{t('.ends_at_placeholder')}", data: { controller: "flatpickr" }, ng: { model: 'subscription.ends_at' } } - .error{ ng: { repeat: 'error in errors.ends_at', show: 'subscription_details_form.ends_at.$pristine' } } {{ error }} + %input.fullwidth#ends_at{ name: 'ends_at', type: 'text', placeholder: "#{t('.ends_at_placeholder')}", data: { controller: "flatpickr" }, "ng-model": 'subscription.ends_at' } + .error{ "ng-repeat": 'error in errors.ends_at', "ng-show": 'subscription_details_form.ends_at.$pristine' } {{ error }} diff --git a/app/views/admin/subscriptions/_filters.html.haml b/app/views/admin/subscriptions/_filters.html.haml index 870b8ce43a..d931c06d2e 100644 --- a/app/views/admin/subscriptions/_filters.html.haml +++ b/app/views/admin/subscriptions/_filters.html.haml @@ -1,11 +1,11 @@ .row.filters .sixteen.columns.alpha.omega .filter_select.five.columns.alpha - %label{ :for => 'query', ng: {class: '{disabled: !shop_id}'} }=t('admin.quick_search') + %label{ for: 'query', "ng-class": '{disabled: !shop_id}' }=t('admin.quick_search') %br - %input.fullwidth{ :type => "text", :id => 'query', ng: { model: 'query', disabled: '!shop_id'}, :placeholder => "#{t('.query_placeholder')}" } + %input.fullwidth{ type: "text", id: 'query', placeholder: "#{t('.query_placeholder')}", "ng-model": 'query', "ng-disabled": '!shop_id' } .filter_select.four.columns - %label{ :for => 'shop_id', ng: { bind: "shop_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } } + %label{ for: 'shop_id', "ng-bind": "shop_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } %br %input.ofn-select2.fullwidth#shop_id{ 'ng-model' => 'shop_id', name: 'shop_id', data: 'shops' } .seven.columns.omega   diff --git a/app/views/admin/subscriptions/_form.html.haml b/app/views/admin/subscriptions/_form.html.haml index cfb4feddaf..84592a3bfb 100644 --- a/app/views/admin/subscriptions/_form.html.haml +++ b/app/views/admin/subscriptions/_form.html.haml @@ -1,27 +1,27 @@ -%form.margin-bottom-50{ name: 'subscription_form', novalidate: true, ng: { submit: 'save()' } } +%form.margin-bottom-50{ name: 'subscription_form', novalidate: true, "ng-submit": 'save()' } %save-bar{ persist: 'true' } - %div{ ng: { hide: 'subscription.id' } } - %a.button{ href: main_app.admin_subscriptions_path, ng: { show: "['details','review'].indexOf(view) >= 0" } }= t(:cancel) - %input{ type: "button", value: t(:back), ng: { click: 'back()', show: '!!backCallbacks[view]'} } - %input.red{ type: "button", value: t(:next), ng: { click: 'next()', show: '!!nextCallbacks[view]' } } - %input.red{ type: "submit", value: t('.create'), ng: { show: "view == 'review'" } } - %div{ ng: { show: 'subscription.id' } } + %div{ "ng-hide": 'subscription.id' } + %a.button{ href: main_app.admin_subscriptions_path, "ng-show": "['details','review'].indexOf(view) >= 0" }= t(:cancel) + %input{ type: "button", value: t(:back), "ng-click": 'back()', "ng-show": '!!backCallbacks[view]' } + %input.red{ type: "button", value: t(:next), "ng-click": 'next()', "ng-show": '!!nextCallbacks[view]' } + %input.red{ type: "submit", value: t('.create'), "ng-show": "view == 'review'" } + %div{ "ng-show": 'subscription.id' } %a.button{ href: main_app.admin_subscriptions_path }= t(:close) - %input.red{ type: "button", value: t(:review), ng: { click: "setView('review')", show: "view != 'review'" } } - %input.red{ type: "submit", value: t(:save_changes), ng: { disabled: 'subscription_form.$pristine' } } + %input.red{ type: "button", value: t(:review), "ng-click": "setView('review')", "ng-show": "view != 'review'" } + %input.red{ type: "submit", value: t(:save_changes), "ng-disabled": 'subscription_form.$pristine' } - .details{ ng: { show: "view == 'details'" } } - %ng-form{ name: 'subscription_details_form', ng: { controller: 'DetailsController' } } + .details{ "ng-show": "view == 'details'" } + %ng-form{ name: 'subscription_details_form', "ng-controller": 'DetailsController' } = render 'details' - .address{ ng: { show: "view == 'address'" } } - %ng-form{ name: 'subscription_address_form', ng: { controller: 'AddressController' } } + .address{ "ng-show": "view == 'address'" } + %ng-form{ name: 'subscription_address_form', "ng-controller": 'AddressController' } = render 'address' - .products{ ng: { show: "view == 'products'" } } - %ng-form{ name: 'subscription_products_form', ng: { controller: 'ProductsController' } } + .products{ "ng-show": "view == 'products'" } + %ng-form{ name: 'subscription_products_form', "ng-controller": 'ProductsController' } = render :partial => "spree/admin/variants/autocomplete", :formats => :js = render 'products' - .review{ ng: { show: "view == 'review'", controller: 'ReviewController' } } + .review{ "ng-show": "view == 'review'", "ng-controller": 'ReviewController' } = render 'review' diff --git a/app/views/admin/subscriptions/_loading_flash.html.haml b/app/views/admin/subscriptions/_loading_flash.html.haml index b4a85be210..2a53e71505 100644 --- a/app/views/admin/subscriptions/_loading_flash.html.haml +++ b/app/views/admin/subscriptions/_loading_flash.html.haml @@ -1,4 +1,4 @@ -%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'shop_id && RequestMonitor.loading' } } +%div.sixteen.columns.alpha.omega#loading{ "ng-cloak": true, "ng-if": 'shop_id && RequestMonitor.loading' } = render partial: "components/admin_spinner" %h1 = t('.loading') diff --git a/app/views/admin/subscriptions/_no_results.html.haml b/app/views/admin/subscriptions/_no_results.html.haml index 23ba6297fb..b7d461519b 100644 --- a/app/views/admin/subscriptions/_no_results.html.haml +++ b/app/views/admin/subscriptions/_no_results.html.haml @@ -1,7 +1,7 @@ -%div.margin-top-30.text-center{ ng: { show: 'shop_id && !RequestMonitor.loading && filteredSubscriptions.length == 0' } } - .no-results{ ng: { show: '!filtersApplied()' } } +%div.margin-top-30.text-center{ "ng-show": 'shop_id && !RequestMonitor.loading && filteredSubscriptions.length == 0' } + .no-results{ "ng-show": '!filtersApplied()' } %h1.margin-bottom-20=t('.no_subscriptions') %span.text-big=t('.why_dont_you_add_one') - .no-results{ ng: { show: 'filtersApplied()' } } + .no-results{ "ng-show": 'filtersApplied()' } %h1=t('.no_matching_subscriptions') diff --git a/app/views/admin/subscriptions/_order_update_issues_dialog.html.haml b/app/views/admin/subscriptions/_order_update_issues_dialog.html.haml index 0bd1e9a9ad..e7e1a0b203 100644 --- a/app/views/admin/subscriptions/_order_update_issues_dialog.html.haml +++ b/app/views/admin/subscriptions/_order_update_issues_dialog.html.haml @@ -2,7 +2,7 @@ #order_update_issues_dialog .message.clearfix.margin-bottom-30 .text=t("admin.subscriptions.order_update_issues_msg") - %div{ ng: { controller: 'OrderUpdateIssuesController' } } + %div{ "ng-controller": 'OrderUpdateIssuesController' } %table %col{ style: 'width: 30%' } %col{ style: 'width: 30%' } @@ -12,14 +12,14 @@ %th= t('admin.order_cycle') %th= t('admin.subscriptions.issue') %tbody - %tr.proxy_order{ :id => "ppo_{{proxyOrder.id}}", ng: { repeat: 'proxyOrder in options.proxyOrders' } } + %tr.proxy_order{ id: "ppo_{{proxyOrder.id}}", "ng-repeat": 'proxyOrder in options.proxyOrders' } %td - %a{ href: '{{::proxyOrder.edit_path}}', target: '_blank', ng: { bind: '::proxyOrder.number' } } + %a{ href: '{{::proxyOrder.edit_path}}', target: '_blank', "ng-bind": '::proxyOrder.number' } %td - %div{ ng: { bind: "::orderCycleName(proxyOrder.order_cycle_id)" } } - %div{ ng: { bind: "::orderCycleCloses(proxyOrder.order_cycle_id)" } } - %td.text-center{ ng: { bind: "proxyOrder.update_issues.join(', ')" } } + %div{ "ng-bind": "::orderCycleName(proxyOrder.order_cycle_id)" } + %div{ "ng-bind": "::orderCycleCloses(proxyOrder.order_cycle_id)" } + %td.text-center{ "ng-bind": "proxyOrder.update_issues.join(', ')" } .action-buttons.text-center - %button{ ng: { click: "close()" } } + %button{ "ng-click": "close()" } OK diff --git a/app/views/admin/subscriptions/_orders_panel.html.haml b/app/views/admin/subscriptions/_orders_panel.html.haml index d99ab196e8..fdf858e2fa 100644 --- a/app/views/admin/subscriptions/_orders_panel.html.haml +++ b/app/views/admin/subscriptions/_orders_panel.html.haml @@ -1,5 +1,5 @@ %script{ type: "text/ng-template", id: "admin/panels/proxy_orders.html" } - %form.margin-top-30{ name: 'subscription_form', ng: { controller: 'OrdersPanelController' } } + %form.margin-top-30{ name: 'subscription_form', "ng-controller": 'OrdersPanelController' } .row.subscription-orders .fourteen.columns.offset-by-one %table @@ -13,14 +13,14 @@ %th= t('total') %th.actions %tbody - %tr.proxy_order{ :id => "po_{{proxyOrder.id}}", ng: { repeat: 'proxyOrder in subscription.not_closed_proxy_orders' } } + %tr.proxy_order{ id: "po_{{proxyOrder.id}}", "ng-repeat": 'proxyOrder in subscription.not_closed_proxy_orders' } %td - %div{ ng: { bind: "::orderCycleName(proxyOrder.order_cycle_id)" } } - %div{ ng: { bind: "::orderCycleCloses(proxyOrder.order_cycle_id)" } } + %div{ "ng-bind": "::orderCycleName(proxyOrder.order_cycle_id)" } + %div{ "ng-bind": "::orderCycleCloses(proxyOrder.order_cycle_id)" } %td.text-center - %span.state{ ng: { class: "proxyOrder.state", bind: 'stateText(proxyOrder.state)' } } - %td.text-center{ ng: { bind: '(proxyOrder.total || subscription.estimatedTotal()) | localizeCurrency' } } + %span.state{ "ng-class": "proxyOrder.state", "ng-bind": 'stateText(proxyOrder.state)' } + %td.text-center{ "ng-bind": '(proxyOrder.total || subscription.estimatedTotal()) | localizeCurrency' } %td.actions %a.edit-order.icon-edit.no-text{ href: '{{::proxyOrder.edit_path}}', target: '_blank', 'ofn-with-tip' => t(:edit_order), confirm_order_edit: true } - %a.cancel-order.icon-remove.no-text{ href: 'javascript:void(0)', ng: { hide: "proxyOrder.state == 'canceled'", click: "cancelOrder(proxyOrder)" }, 'ofn-with-tip' => t(:cancel_order) } - %a.resume-order.icon-resume.no-text{ href: 'javascript:void(0)', ng: { show: "proxyOrder.state == 'canceled'", click: "resumeOrder(proxyOrder)" }, 'ofn-with-tip' => t(:resume_order) } + %a.cancel-order.icon-remove.no-text{ href: 'javascript:void(0)', "ofn-with-tip": t(:cancel_order), "ng-hide": "proxyOrder.state == 'canceled'", "ng-click": "cancelOrder(proxyOrder)" } + %a.resume-order.icon-resume.no-text{ href: 'javascript:void(0)', "ofn-with-tip": t(:resume_order), "ng-show": "proxyOrder.state == 'canceled'", "ng-click": "resumeOrder(proxyOrder)" } diff --git a/app/views/admin/subscriptions/_products.html.haml b/app/views/admin/subscriptions/_products.html.haml index 88d206e69d..44e62b7c9c 100644 --- a/app/views/admin/subscriptions/_products.html.haml +++ b/app/views/admin/subscriptions/_products.html.haml @@ -1,3 +1,3 @@ -%div{ ng: { controller: 'SubscriptionLineItemsController' } } +%div{ "ng-controller": 'SubscriptionLineItemsController' } = render 'autocomplete' = render 'subscription_line_items' diff --git a/app/views/admin/subscriptions/_products_panel.html.haml b/app/views/admin/subscriptions/_products_panel.html.haml index 330e2fe0c1..4781a3a007 100644 --- a/app/views/admin/subscriptions/_products_panel.html.haml +++ b/app/views/admin/subscriptions/_products_panel.html.haml @@ -1,15 +1,15 @@ = render :partial => "spree/admin/variants/autocomplete", :formats => :js %script{ type: "text/ng-template", id: "admin/panels/subscription_products.html" } - %form{ name: 'subscription_form', ng: { controller: 'ProductsPanelController' } } + %form{ name: 'subscription_form', "ng-controller": 'ProductsPanelController' } %div{ style: 'width: 90%; margin: auto;' } = render 'products' - %a.button.update.fullwidth{ ng: { class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } - %span{ ng: {hide: "saved() || saving" } } + %a.button.update.fullwidth{ "ng-class": "{disabled: saved() && !saving, saving: saving}", "ng-click": "save()" } + %span{ "ng-hide": "saved() || saving" } = t('.save') %i.icon-save - %span{ ng: {show: "saved() && !saving" } } + %span{ "ng-show": "saved() && !saving" } = t('.saved') %i.icon-ok-sign - %span{ ng: {show: "saving" } } + %span{ "ng-show": "saving" } = t('.saving') %i.icon-refresh diff --git a/app/views/admin/subscriptions/_review.html.haml b/app/views/admin/subscriptions/_review.html.haml index 9e5fcb7d55..3bf765df77 100644 --- a/app/views/admin/subscriptions/_review.html.haml +++ b/app/views/admin/subscriptions/_review.html.haml @@ -6,7 +6,7 @@ .five.columns.alpha %h3= t('.details') .eleven.columns.omega - %input#edit-details{ type: "button", value: t(:edit), ng: { click: "setView('details')" } } + %input#edit-details{ type: "button", value: t(:edit), "ng-click": "setView('details')" } .row .five.columns.alpha %strong= t('admin.customer') @@ -35,7 +35,7 @@ .five.columns.alpha %h3= t('.address') .eleven.columns.omega - %input#edit-address{ type: "button", value: t(:edit), ng: { click: "setView('address')" } } + %input#edit-address{ type: "button", value: t(:edit), "ng-click": "setView('address')" } .row .five.columns.alpha %strong= t('admin.bill_address') @@ -53,7 +53,7 @@ .five.columns.alpha %h3= t('.products') .eleven.columns.omega - %input#edit-products{ type: "button", value: t(:edit), ng: { click: "setView('products')" } } + %input#edit-products{ type: "button", value: t(:edit), "ng-click": "setView('products')" } .row %table#subscription-line-items.admin-subscription-review-subscription-line-items %colgroup @@ -69,10 +69,10 @@ %th.total %span= t(:total) %tbody - %tr.item{ id: "sli_{{$index}}", ng: { repeat: "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", class: { even: 'even', odd: 'odd' } } } + %tr.item{ id: "sli_{{$index}}", "ng-repeat": "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", "ng-class-even": 'even', "ng-class-odd": 'odd' } %td .description {{ item.description }} - .not-in-open-and-upcoming-order-cycles-warning{ ng: { if: '!item.in_open_and_upcoming_order_cycles' } } + .not-in-open-and-upcoming-order-cycles-warning{ "ng-if": '!item.in_open_and_upcoming_order_cycles' } = t(".no_open_or_upcoming_order_cycle") %td.price.align-center {{ item.price_estimate | localizeCurrency }} %td.quantity {{ item.quantity }} diff --git a/app/views/admin/subscriptions/_subscription_line_items.html.haml b/app/views/admin/subscriptions/_subscription_line_items.html.haml index 561cc76996..78afd26803 100644 --- a/app/views/admin/subscriptions/_subscription_line_items.html.haml +++ b/app/views/admin/subscriptions/_subscription_line_items.html.haml @@ -14,17 +14,17 @@ %span= t(:total) %th.orders-actions.actions %tbody - %tr.item{ id: "sli_{{$index}}", ng: { repeat: "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", class: { even: 'even', odd: 'odd' } } } + %tr.item{ id: "sli_{{$index}}", "ng-repeat": "item in subscription.subscription_line_items | filter:{ _destroy: '!true' }", "ng-class-even": 'even', "ng-class-odd": 'odd' } %td .description {{ item.description }} - .not-in-open-and-upcoming-order-cycles-warning{ ng: { if: '!item.in_open_and_upcoming_order_cycles' } } + .not-in-open-and-upcoming-order-cycles-warning{ "ng-if": '!item.in_open_and_upcoming_order_cycles' } = t(".not_in_open_and_upcoming_order_cycles_warning") %td.price.align-center {{ item.price_estimate | localizeCurrency }} %td.quantity - %input{ name: 'quantity', type: 'number', min: 0, ng: { model: 'item.quantity' } } + %input{ name: 'quantity', type: 'number', min: 0, "ng-model": 'item.quantity' } %td.total.align-center {{ (item.price_estimate * item.quantity) | localizeCurrency }} %td.actions - %a.delete-item.icon-trash.no-text{ ng: { click: 'removeSubscriptionLineItem(item)'}, :href => "javascript:void(0)" } + %a.delete-item.icon-trash.no-text{ href: "javascript:void(0)", "ng-click": 'removeSubscriptionLineItem(item)' } %tbody#subtotal.no-border-top %tr#subtotal-row %td{:colspan => "3"} @@ -34,7 +34,7 @@ %td.total.align-center %span#order_subtotal {{ subscription.estimatedSubtotal() | localizeCurrency }} %td.actions - %tbody#fees.no-border-top{ ng: { show: "subscription.estimatedFees() > 0" } } + %tbody#fees.no-border-top{ "ng-show": "subscription.estimatedFees() > 0" } %tr#fees-row %td{:colspan => "3"} %b diff --git a/app/views/admin/subscriptions/_table.html.haml b/app/views/admin/subscriptions/_table.html.haml index 4f45080694..d009af9065 100644 --- a/app/views/admin/subscriptions/_table.html.haml +++ b/app/views/admin/subscriptions/_table.html.haml @@ -1,7 +1,7 @@ = render 'products_panel' = render 'orders_panel' -%table.index#subscriptions{ ng: { cloak: true, show: 'shop_id && !RequestMonitor.loading && filteredSubscriptions.length > 0' } } +%table.index#subscriptions{ "ng-cloak": true, "ng-show": 'shop_id && !RequestMonitor.loading && filteredSubscriptions.length > 0' } %col.customer{ width: "20%", 'ng-show' => 'columns.customer.visible' } %col.schedule{ width: "20%", 'ng-show' => 'columns.schedule.visible' } %col.items{ width: "10%", 'ng-show' => 'columns.items.visible' } @@ -16,47 +16,47 @@ %tr -# %th.bulk -# %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } - %th.customer{ ng: { show: 'columns.customer.visible' } } + %th.customer{ "ng-show": 'columns.customer.visible' } = t('admin.customer') - %th.schedule{ ng: { show: 'columns.schedule.visible', } } + %th.schedule{ "ng-show": 'columns.schedule.visible' } = t('admin.schedule') - %th.items{ ng: { show: 'columns.items.visible', } } + %th.items{ "ng-show": 'columns.items.visible' } = t('admin.items') - %th.orders{ ng: { show: 'columns.orders.visible', } } + %th.orders{ "ng-show": 'columns.orders.visible' } = t('orders') - %th.status{ ng: { show: 'columns.state.visible', } } + %th.status{ "ng-show": 'columns.state.visible' } = t('admin.status_state') - %th.begins_on{ ng: { show: 'columns.begins_on.visible', } } + %th.begins_on{ "ng-show": 'columns.begins_on.visible' } = t('admin.begins_on') - %th.ends_on{ ng: { show: 'columns.ends_on.visible', } } + %th.ends_on{ "ng-show": 'columns.ends_on.visible' } = t('admin.ends_on') - %th.payment_method{ ng: { show: 'columns.payment_method.visible', } } + %th.payment_method{ "ng-show": 'columns.payment_method.visible' } = t('admin.payment_method') - %th.shipping_method{ ng: { show: 'columns.shipping_method.visible', } } + %th.shipping_method{ "ng-show": 'columns.shipping_method.visible' } = t('admin.shipping_method') %th.actions   - %tbody.panel-ctrl{ object: 'subscription', ng: { repeat: "subscription in subscriptions | filter:query as filteredSubscriptions track by subscription.id" } } - %tr.subscription{ :id => "so_{{subscription.id}}", ng: { class: { even: "'even'", odd: "'odd'" } } } - %td.customer{ ng: { show: 'columns.customer.visible'}} + %tbody.panel-ctrl{ object: 'subscription', "ng-repeat": "subscription in subscriptions | filter:query as filteredSubscriptions track by subscription.id" } + %tr.subscription{ id: "so_{{subscription.id}}", "ng-class-even": "'even'", "ng-class-odd": "'odd'" } + %td.customer{ "ng-show": 'columns.customer.visible' } %span{ "ng-bind": '::subscription.customer_email' } %br %span{ "ng-bind": '::subscription.customer_full_name' } - %td.schedule{ ng: { show: 'columns.schedule.visible', bind: '::subscription.schedule_name' } } - %td.items.panel-toggle{ name: 'products', ng: { show: 'columns.items.visible' } } - %h5{ ng: { bind: 'itemCount(subscription)' } } - %td.orders.panel-toggle{ name: 'orders', ng: { show: 'columns.orders.visible' } } - %h5{ ng: { bind: 'subscription.not_closed_proxy_orders.length' } } - %td.status{ ng: { show: 'columns.state.visible' } } - %span.state{ ng: { class: "subscription.state", bind: "'spree.subscription_state.' + subscription.state | t" } } - %td.begins_on{ ng: { show: 'columns.begins_on.visible', bind: '::subscription.begins_at' } } - %td.ends_on{ ng: { show: 'columns.ends_on.visible', bind: '::subscription.ends_at' } } - %td.payment_method{ ng: { show: 'columns.payment_method.visible', bind: '::paymentMethodsByID[subscription.payment_method_id].name' } } - %td.shipping_method{ ng: { show: 'columns.shipping_method.visible', bind: '::shippingMethodsByID[subscription.shipping_method_id].name' } } + %td.schedule{ "ng-show": 'columns.schedule.visible', "ng-bind": '::subscription.schedule_name' } + %td.items.panel-toggle{ name: 'products', "ng-show": 'columns.items.visible' } + %h5{ "ng-bind": 'itemCount(subscription)' } + %td.orders.panel-toggle{ name: 'orders', "ng-show": 'columns.orders.visible' } + %h5{ "ng-bind": 'subscription.not_closed_proxy_orders.length' } + %td.status{ "ng-show": 'columns.state.visible' } + %span.state{ "ng-class": "subscription.state", "ng-bind": "'spree.subscription_state.' + subscription.state | t" } + %td.begins_on{ "ng-show": 'columns.begins_on.visible', "ng-bind": '::subscription.begins_at' } + %td.ends_on{ "ng-show": 'columns.ends_on.visible', "ng-bind": '::subscription.ends_at' } + %td.payment_method{ "ng-show": 'columns.payment_method.visible', "ng-bind": '::paymentMethodsByID[subscription.payment_method_id].name' } + %td.shipping_method{ "ng-show": 'columns.shipping_method.visible', "ng-bind": '::shippingMethodsByID[subscription.shipping_method_id].name' } %td.actions - %a.edit-subscription.icon-edit.no-text{ ng: { href: '{{subscription.edit_path}}'}, 'ofn-with-tip' => t('.edit_subscription') } - %a.pause-subscription.icon-pause.no-text{ ng: { click: 'subscription.pause()', hide: '!!subscription.paused_at' }, 'ofn-with-tip' => t('.pause_subscription') , href: 'javascript:void(0)' } - %a.unpause-subscription.icon-play.no-text{ ng: { click: 'subscription.unpause()', show: '!!subscription.paused_at' }, 'ofn-with-tip' => t('.unpause_subscription') , href: 'javascript:void(0)' } - %a.cancel-subscription.icon-remove.no-text{ ng: { click: 'subscription.cancel()', hide: '!!subscription.canceled_at'}, 'ofn-with-tip' => t('.cancel_subscription') , href: 'javascript:void(0)' } + %a.edit-subscription.icon-edit.no-text{ "ofn-with-tip": t('.edit_subscription'), "ng-href": '{{subscription.edit_path}}' } + %a.pause-subscription.icon-pause.no-text{ "ofn-with-tip": t('.pause_subscription'), href: 'javascript:void(0)', "ng-click": 'subscription.pause()', "ng-hide": '!!subscription.paused_at' } + %a.unpause-subscription.icon-play.no-text{ "ofn-with-tip": t('.unpause_subscription'), href: 'javascript:void(0)', "ng-click": 'subscription.unpause()', "ng-show": '!!subscription.paused_at' } + %a.cancel-subscription.icon-remove.no-text{ "ofn-with-tip": t('.cancel_subscription'), href: 'javascript:void(0)', "ng-click": 'subscription.cancel()', "ng-hide": '!!subscription.canceled_at' } %tr.panel-row{ object: "subscription", panels: "{products: 'subscription_products', orders: 'proxy_orders'}" } diff --git a/app/views/admin/subscriptions/_wizard_progress.html.haml b/app/views/admin/subscriptions/_wizard_progress.html.haml index d8f381247b..cc58445dd8 100644 --- a/app/views/admin/subscriptions/_wizard_progress.html.haml +++ b/app/views/admin/subscriptions/_wizard_progress.html.haml @@ -1,3 +1,3 @@ %ul.wizard-progress - %li{ ng: { repeat: "step in ['details','address','products','review']", class: '{current: view==step}' } } + %li{ "ng-repeat": "step in ['details','address','products','review']", "ng-class": '{current: view==step}' } {{ stepTitleFor(step) }} diff --git a/app/views/admin/subscriptions/edit.html.haml b/app/views/admin/subscriptions/edit.html.haml index 51b4c40045..361c42556d 100644 --- a/app/views/admin/subscriptions/edit.html.haml +++ b/app/views/admin/subscriptions/edit.html.haml @@ -4,7 +4,7 @@ -# - content_for :page_actions do -# %li= button_link_to "Back to subscriptions list", main_app.admin_subscriptions_path, icon: 'icon-arrow-left' -%div{ ng: { app: 'admin.subscriptions', controller: 'SubscriptionController', cloak: true } } +%div{ "ng-app": 'admin.subscriptions', "ng-controller": 'SubscriptionController', "ng-cloak": true } = render 'data' = render 'order_update_issues_dialog' = render 'form' diff --git a/app/views/admin/subscriptions/index.html.haml b/app/views/admin/subscriptions/index.html.haml index 3e0ebebd90..7b48edb4b6 100644 --- a/app/views/admin/subscriptions/index.html.haml +++ b/app/views/admin/subscriptions/index.html.haml @@ -15,7 +15,7 @@ = render 'data' = render 'order_update_issues_dialog' -%div.margin-bottom-50{ ng: { controller: 'SubscriptionsController' } } +%div.margin-bottom-50{ "ng-controller": 'SubscriptionsController' } %save-bar{ dirty: "false", persist: "false" } = render 'filters' = render 'controls' diff --git a/app/views/admin/subscriptions/new.html.haml b/app/views/admin/subscriptions/new.html.haml index 6ac3749982..aab6173ec6 100644 --- a/app/views/admin/subscriptions/new.html.haml +++ b/app/views/admin/subscriptions/new.html.haml @@ -6,7 +6,7 @@ -# - content_for :page_actions do -# %li= button_link_to "Back to subscriptions list", main_app.admin_subscriptions_path, icon: 'icon-arrow-left' -%div{ ng: { app: 'admin.subscriptions', controller: 'SubscriptionController', cloak: true } } +%div{ "ng-app": 'admin.subscriptions', "ng-controller": 'SubscriptionController', "ng-cloak": true } = render 'data' = render 'wizard_progress' = render 'order_update_issues_dialog' diff --git a/app/views/admin/variant_overrides/_controls.html.haml b/app/views/admin/variant_overrides/_controls.html.haml index 1f407a4b35..c401717644 100644 --- a/app/views/admin/variant_overrides/_controls.html.haml +++ b/app/views/admin/variant_overrides/_controls.html.haml @@ -1,15 +1,15 @@ -%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'hub_id && products.length > 0' } } -.controls.sixteen.columns.alpha.omega{ ng: { show: 'hub_id && products.length > 0' } } +%hr.divider.sixteen.columns.alpha.omega{ "ng-show": 'hub_id && products.length > 0' } +.controls.sixteen.columns.alpha.omega{ "ng-show": 'hub_id && products.length > 0' } .eight.columns.alpha = render 'admin/shared/bulk_actions_dropdown' = render 'admin/shared/views_dropdown' - %span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.inventory.visible' } , data: { powertip: "#{t('admin.variant_overrides.index.inventory_powertip')}" } } - %span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.hidden.visible' } , data: { powertip: "#{t('admin.variant_overrides.index.hidden_powertip')}" } } - %span.text-big.with-tip.icon-question-sign{ ng: { show: 'views.new.visible' } , data: { powertip: "#{t('admin.variant_overrides.index.new_powertip')}" } } + %span.text-big.with-tip.icon-question-sign{ data: { powertip: "#{t('admin.variant_overrides.index.inventory_powertip')}" }, "ng-show": 'views.inventory.visible' } + %span.text-big.with-tip.icon-question-sign{ data: { powertip: "#{t('admin.variant_overrides.index.hidden_powertip')}" }, "ng-show": 'views.hidden.visible' } + %span.text-big.with-tip.icon-question-sign{ data: { powertip: "#{t('admin.variant_overrides.index.new_powertip')}" }, "ng-show": 'views.new.visible' } .four.columns   - .four.columns.omega{ ng: { show: 'views.new.visible' } } - %button.fullwidth{ type: 'button', ng: { click: "selectView('inventory')" } } + .four.columns.omega{ "ng-show": 'views.new.visible' } + %button.fullwidth{ type: 'button', "ng-click": "selectView('inventory')" } %i.icon-chevron-left = t('.back_to_my_inventory') - .four.columns.omega{ng: { show: 'views.inventory.visible' } } + .four.columns.omega{ "ng-show": 'views.inventory.visible' } %columns-dropdown{ action: "#{controller_name}_#{action_name}" } diff --git a/app/views/admin/variant_overrides/_filters.html.haml b/app/views/admin/variant_overrides/_filters.html.haml index bafb413ae5..3bb7d538b5 100644 --- a/app/views/admin/variant_overrides/_filters.html.haml +++ b/app/views/admin/variant_overrides/_filters.html.haml @@ -1,20 +1,20 @@ .filters.sixteen.columns.alpha.omega .filter.four.columns.alpha - %label{for: 'query', ng: {class: '{disabled: !hub_id}'} }=t('admin.quick_search') + %label{ for: 'query', "ng-class": '{disabled: !hub_id}' }=t('admin.quick_search') %br - %input.fullwidth{type: "text", id: 'query', ng: {model: 'query', disabled: '!hub_id'} } + %input.fullwidth{ type: "text", id: 'query', "ng-model": 'query', "ng-disabled": '!hub_id' } .filter_select.three.columns - %label{for: 'hub_id', ng: {bind: "hub_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } } + %label{ for: 'hub_id', "ng-bind": "hub_id ? '#{t('admin.shop')}' : '#{t('admin.variant_overrides.index.select_a_shop')}'" } %br - %select.select2.fullwidth#hub_id{name: 'hub_id', ng: {model: 'hub_id', options: 'hub.id as hub.name for (id, hub) in hubs' } } + %select.select2.fullwidth#hub_id{ name: 'hub_id', "ng-model": 'hub_id', "ng-options": 'hub.id as hub.name for (id, hub) in hubs' } .filter_select.three.columns - %label{for: 'producer_filter', ng: {class: '{disabled: !hub_id}'} }=t('admin.producer') + %label{ for: 'producer_filter', "ng-class": '{disabled: !hub_id}' }=t('admin.producer') %br - %input.ofn-select2.fullwidth{id: 'producer_filter', type: 'number', data: 'producers', blank: "{id: 0, name: '#{t(:all)}'}", ng: {model: 'producerFilter', disabled: '!hub_id' } } + %input.ofn-select2.fullwidth{ id: 'producer_filter', type: 'number', data: 'producers', blank: "{id: 0, name: '#{t(:all)}'}", "ng-model": 'producerFilter', "ng-disabled": '!hub_id' } .filter_select.three.columns - %label{ :for => 'import_date_filter', ng: {class: '{disabled: !hub_id}'} } #{t('admin.variant_overrides.index.import_date')} + %label{ for: 'import_date_filter', "ng-class": '{disabled: !hub_id}' } #{t('admin.variant_overrides.index.import_date')} %br - %select.fullwidth{id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'importDateFilter', options: 'date.id as date.name for date in import_dates', disabled: '!hub_id', init: "import_dates = #{@import_dates}"} } + %select.fullwidth{ id: 'import_date_filter', "ofn-select2-min-search": 5, "ng-model": 'importDateFilter', "ng-options": 'date.id as date.name for date in import_dates', "ng-disabled": '!hub_id', "ng-init": "import_dates = #{@import_dates}" } %options{value: '0', selected: 'selected'} #{t(:all)} -# .filter_select{ :class => "three columns" } -# %label{ :for => 'distributor_filter' }Hub @@ -27,4 +27,4 @@ .filter_clear.three.columns.omega %label{ :for => 'clear_all_filters' } %br - %input.red.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "#{t('admin.clear_all')}", ng: { click: "resetSelectFilters()", disabled: '!hub_id'} } + %input.red.fullwidth{ type: 'button', id: 'clear_all_filters', value: "#{t('admin.clear_all')}", "ng-click": "resetSelectFilters()", "ng-disabled": '!hub_id' } diff --git a/app/views/admin/variant_overrides/_hidden_products.html.haml b/app/views/admin/variant_overrides/_hidden_products.html.haml index 38f144b479..9871cdcb72 100644 --- a/app/views/admin/variant_overrides/_hidden_products.html.haml +++ b/app/views/admin/variant_overrides/_hidden_products.html.haml @@ -1,5 +1,5 @@ -%div{ ng: { show: 'views.hidden.visible' } } - %table#hidden-products{ ng: { show: 'filteredProducts.length > 0' } } +%div{ "ng-show": 'views.hidden.visible' } + %table#hidden-products{ "ng-show": 'filteredProducts.length > 0' } %col.producer{ width: "20%" } %col.product{ width: "20%" } %col.variant{ width: "20%" } @@ -10,13 +10,13 @@ %th.product=t('admin.product') %th.variant=t('admin.variant') %th.add=t('admin.variant_overrides.index.add') - %tbody{ ng: { repeat: 'product in filteredProducts | limitTo:productLimit' } } - %tr{ id: "v_{{variant.id}}", ng: { repeat: 'variant in product.variants | inventoryVariants:hub_id:views' } } - %td.producer{ ng: { bind: '::producersByID[product.producer_id].name'} } - %td.product{ ng: { bind: '::product.name'} } + %tbody{ "ng-repeat": 'product in filteredProducts | limitTo:productLimit' } + %tr{ id: "v_{{variant.id}}", "ng-repeat": 'variant in product.variants | inventoryVariants:hub_id:views' } + %td.producer{ "ng-bind": '::producersByID[product.producer_id].name' } + %td.product{ "ng-bind": '::product.name' } %td.variant - %span{ ng: { bind: '::variant.display_name || ""'} } - .variant-override-unit{ ng: { bind: '::variant.unit_to_display'} } + %span{ "ng-bind": '::variant.display_name || ""' } + .variant-override-unit{ "ng-bind": '::variant.unit_to_display' } %td.add - %button.fullwidth.icon-plus{ ng: { click: "setVisibility(hub_id,variant.id,true)" } } + %button.fullwidth.icon-plus{ "ng-click": "setVisibility(hub_id,variant.id,true)" } = t('admin.variant_overrides.index.add') diff --git a/app/views/admin/variant_overrides/_loading_flash.html.haml b/app/views/admin/variant_overrides/_loading_flash.html.haml index 0095c543e8..5765159f61 100644 --- a/app/views/admin/variant_overrides/_loading_flash.html.haml +++ b/app/views/admin/variant_overrides/_loading_flash.html.haml @@ -1,4 +1,4 @@ -%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'hub_id && products.length == 0 && RequestMonitor.loading' } } +%div.sixteen.columns.alpha.omega#loading{ "ng-cloak": true, "ng-if": 'hub_id && products.length == 0 && RequestMonitor.loading' } = render partial: "components/admin_spinner" %h1 = t('.loading_inventory') diff --git a/app/views/admin/variant_overrides/_new_products.html.haml b/app/views/admin/variant_overrides/_new_products.html.haml index 731535d06d..fb727c84e1 100644 --- a/app/views/admin/variant_overrides/_new_products.html.haml +++ b/app/views/admin/variant_overrides/_new_products.html.haml @@ -1,4 +1,4 @@ -%table#new-products{ ng: { show: 'views.new.visible && filteredProducts.length > 0' } } +%table#new-products{ "ng-show": 'views.new.visible && filteredProducts.length > 0' } %col.producer{ width: "20%" } %col.product{ width: "20%" } %col.variant{ width: "20%" } @@ -11,16 +11,16 @@ %th.variant=t('admin.variant') %th.add=t('admin.variant_overrides.index.add') %th.hide=t('admin.variant_overrides.index.hide') - %tbody{ ng: { repeat: 'product in filteredProducts | limitTo:productLimit' } } - %tr{ id: "v_{{variant.id}}", ng: { repeat: 'variant in product.variants | inventoryVariants:hub_id:views' } } - %td.producer{ ng: { bind: { html: '::producersByID[product.producer_id].name'} } } - %td.product{ ng: { bind: '::product.name'} } + %tbody{ "ng-repeat": 'product in filteredProducts | limitTo:productLimit' } + %tr{ id: "v_{{variant.id}}", "ng-repeat": 'variant in product.variants | inventoryVariants:hub_id:views' } + %td.producer{ "ng-bind-html": '::producersByID[product.producer_id].name' } + %td.product{ "ng-bind": '::product.name' } %td.variant - %span{ ng: { bind: '::variant.display_name || ""'} } - .variant-override-unit{ ng: { bind: '::variant.unit_to_display'} } + %span{ "ng-bind": '::variant.display_name || ""' } + .variant-override-unit{ "ng-bind": '::variant.unit_to_display' } %td.add - %button.fullwidth.icon-plus{ ng: { click: "setVisibility(hub_id,variant.id,true)" } } + %button.fullwidth.icon-plus{ "ng-click": "setVisibility(hub_id,variant.id,true)" } = t('admin.variant_overrides.index.add') %td.hide - %button.fullwidth.icon-remove{ ng: { click: "setVisibility(hub_id,variant.id,false)" } } + %button.fullwidth.icon-remove{ "ng-click": "setVisibility(hub_id,variant.id,false)" } = t('admin.variant_overrides.index.hide') diff --git a/app/views/admin/variant_overrides/_new_products_alert.html.haml b/app/views/admin/variant_overrides/_new_products_alert.html.haml index 385616d817..2f352e92ca 100644 --- a/app/views/admin/variant_overrides/_new_products_alert.html.haml +++ b/app/views/admin/variant_overrides/_new_products_alert.html.haml @@ -1,5 +1,3 @@ -%div{ ng: { show: '(newProductCount = (products | hubPermissions:hubPermissions:hub_id | newInventoryProducts:hub_id).length) > 0 && !views.new.visible && !alertDismissed' } } +%div{ "ng-show": '(newProductCount = (products | hubPermissions:hubPermissions:hub_id | newInventoryProducts:hub_id).length) > 0 && !views.new.visible && !alertDismissed' } %hr.divider.sixteen.columns.alpha.omega - %alert-row{ message: "#{t('admin.variant_overrides.index.new_products_alert_message', new_product_count: '{{ newProductCount }}')}", - dismissed: "alertDismissed", - button: { text: "#{t('admin.variant_overrides.index.review_now')}", action: "selectView('new')" } } + %alert-row{ message: "#{t('admin.variant_overrides.index.new_products_alert_message', new_product_count: '{{ newProductCount }}')}", dismissed: "alertDismissed", "button-text": "#{t('admin.variant_overrides.index.review_now')}", "button-action": "selectView('new')" } diff --git a/app/views/admin/variant_overrides/_no_results.html.haml b/app/views/admin/variant_overrides/_no_results.html.haml index 7ec4e68f0e..274424b32a 100644 --- a/app/views/admin/variant_overrides/_no_results.html.haml +++ b/app/views/admin/variant_overrides/_no_results.html.haml @@ -1,7 +1,7 @@ -%div.text-big.no-results{ ng: { show: 'hub_id && products.length > 0 && filteredProducts.length == 0' } } - %span{ ng: { show: 'views.inventory.visible && !filtersApplied()' } }=t('admin.variant_overrides.index.currently_empty') - %span{ ng: { show: 'views.inventory.visible && filtersApplied()' } }=t('admin.variant_overrides.index.no_matching_products') - %span{ ng: { show: 'views.hidden.visible && !filtersApplied()' } }=t('admin.variant_overrides.index.no_hidden_products') - %span{ ng: { show: 'views.hidden.visible && filtersApplied()' } }=t('admin.variant_overrides.index.no_matching_hidden_products') - %span{ ng: { show: 'views.new.visible && !filtersApplied()' } }=t('admin.variant_overrides.index.no_new_products') - %span{ ng: { show: 'views.new.visible && filtersApplied()' } }=t('admin.variant_overrides.index.no_matching_new_products') +%div.text-big.no-results{ "ng-show": 'hub_id && products.length > 0 && filteredProducts.length == 0' } + %span{ "ng-show": 'views.inventory.visible && !filtersApplied()' }=t('admin.variant_overrides.index.currently_empty') + %span{ "ng-show": 'views.inventory.visible && filtersApplied()' }=t('admin.variant_overrides.index.no_matching_products') + %span{ "ng-show": 'views.hidden.visible && !filtersApplied()' }=t('admin.variant_overrides.index.no_hidden_products') + %span{ "ng-show": 'views.hidden.visible && filtersApplied()' }=t('admin.variant_overrides.index.no_matching_hidden_products') + %span{ "ng-show": 'views.new.visible && !filtersApplied()' }=t('admin.variant_overrides.index.no_new_products') + %span{ "ng-show": 'views.new.visible && filtersApplied()' }=t('admin.variant_overrides.index.no_matching_new_products') diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index e8d239b803..a54960da50 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,32 +1,32 @@ -%form{ name: 'variant_overrides_form', ng: { show: "views.inventory.visible" } } +%form{ name: 'variant_overrides_form', "ng-show": "views.inventory.visible" } %save-bar{ dirty: "customers_form.$dirty", persist: "false" } - %input.red{ type: "button", value: t(:save_changes), ng: { click: "update()", disabled: "!variant_overrides_form.$dirty" } } + %input.red{ type: "button", value: t(:save_changes), "ng-click": "update()", "ng-disabled": "!variant_overrides_form.$dirty" } %table.index.bulk#variant-overrides - %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } - %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } - %col.sku{ width: "20%", ng: { show: 'columns.sku.visible' } } - %col.price{ width: "10%", ng: { show: 'columns.price.visible' } } - %col.on_hand{ width: "10%", ng: { show: 'columns.on_hand.visible' } } - %col.on_demand{ width: "10%", ng: { show: 'columns.on_demand.visible' } } - %col.reset{ width: "1%", ng: { show: 'columns.reset.visible' } } - %col.reset{ width: "15%", ng: { show: 'columns.reset.visible' } } - %col.inheritance{ width: "5%", ng: { show: 'columns.inheritance.visible' } } - %col.tags{ width: "30%", ng: { show: 'columns.tags.visible' } } - %col.visibility{ width: "10%", ng: { show: 'columns.visibility.visible' } } - %col.visibility{ width: "10%", ng: { show: 'columns.import_date.visible' } } + %col.producer{ width: "20%", "ng-show": 'columns.producer.visible' } + %col.product{ width: "20%", "ng-show": 'columns.product.visible' } + %col.sku{ width: "20%", "ng-show": 'columns.sku.visible' } + %col.price{ width: "10%", "ng-show": 'columns.price.visible' } + %col.on_hand{ width: "10%", "ng-show": 'columns.on_hand.visible' } + %col.on_demand{ width: "10%", "ng-show": 'columns.on_demand.visible' } + %col.reset{ width: "1%", "ng-show": 'columns.reset.visible' } + %col.reset{ width: "15%", "ng-show": 'columns.reset.visible' } + %col.inheritance{ width: "5%", "ng-show": 'columns.inheritance.visible' } + %col.tags{ width: "30%", "ng-show": 'columns.tags.visible' } + %col.visibility{ width: "10%", "ng-show": 'columns.visibility.visible' } + %col.visibility{ width: "10%", "ng-show": 'columns.import_date.visible' } %thead - %tr{ ng: { controller: "ColumnsCtrl" } } - %th.producer{ ng: { show: 'columns.producer.visible' } }=t('admin.producer') - %th.product{ ng: { show: 'columns.product.visible' } }=t('admin.product') - %th.sku{ ng: { show: 'columns.sku.visible' } }=t('admin.sku') - %th.price{ ng: { show: 'columns.price.visible' } }=t('admin.price') - %th.on_hand{ ng: { show: 'columns.on_hand.visible' } }=t('admin.on_hand') - %th.on_demand{ ng: { show: 'columns.on_demand.visible' } }=t('admin.on_demand?') - %th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } }=t('admin.variant_overrides.index.enable_reset?') - %th.inheritance{ ng: { show: 'columns.inheritance.visible' } }=t('admin.variant_overrides.index.inherit?') - %th.tags{ ng: { show: 'columns.tags.visible' } }=t('admin.tags') - %th.visibility{ ng: { show: 'columns.visibility.visible' } }=t('admin.variant_overrides.index.hide') - %th.import_date{ ng: { show: 'columns.import_date.visible' } }=t('admin.variant_overrides.index.import_date') - %tbody{ ng: {repeat: 'product in filteredProducts = (products | hubPermissions:hubPermissions:hub_id | inventoryProducts:hub_id:views | attrFilter:{producer_id:producerFilter} | importDate:hub_id:importDateFilter | filter:query) | limitTo:productLimit' } } + %tr{ "ng-controller": "ColumnsCtrl" } + %th.producer{ "ng-show": 'columns.producer.visible' }=t('admin.producer') + %th.product{ "ng-show": 'columns.product.visible' }=t('admin.product') + %th.sku{ "ng-show": 'columns.sku.visible' }=t('admin.sku') + %th.price{ "ng-show": 'columns.price.visible' }=t('admin.price') + %th.on_hand{ "ng-show": 'columns.on_hand.visible' }=t('admin.on_hand') + %th.on_demand{ "ng-show": 'columns.on_demand.visible' }=t('admin.on_demand?') + %th.reset{ colspan: 2, "ng-show": 'columns.reset.visible' }=t('admin.variant_overrides.index.enable_reset?') + %th.inheritance{ "ng-show": 'columns.inheritance.visible' }=t('admin.variant_overrides.index.inherit?') + %th.tags{ "ng-show": 'columns.tags.visible' }=t('admin.tags') + %th.visibility{ "ng-show": 'columns.visibility.visible' }=t('admin.variant_overrides.index.hide') + %th.import_date{ "ng-show": 'columns.import_date.visible' }=t('admin.variant_overrides.index.import_date') + %tbody{ "ng-repeat": 'product in filteredProducts = (products | hubPermissions:hubPermissions:hub_id | inventoryProducts:hub_id:views | attrFilter:{producer_id:producerFilter} | importDate:hub_id:importDateFilter | filter:query) | limitTo:productLimit' } = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' diff --git a/app/views/admin/variant_overrides/_products_product.html.haml b/app/views/admin/variant_overrides/_products_product.html.haml index 312273d4d2..188f332e26 100644 --- a/app/views/admin/variant_overrides/_products_product.html.haml +++ b/app/views/admin/variant_overrides/_products_product.html.haml @@ -1,12 +1,12 @@ %tr.product.even - %td.producer{ ng: { show: 'columns.producer.visible', bind: { html: '::producersByID[product.producer_id].name'} } } - %td.product{ ng: { show: 'columns.product.visible', bind: '::product.name'} } - %td.sku{ ng: { show: 'columns.sku.visible' } } - %td.price{ ng: { show: 'columns.price.visible' } } - %td.on_hand{ ng: { show: 'columns.on_hand.visible' } } - %td.on_demand{ ng: { show: 'columns.on_demand.visible' } } - %td.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } - %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } - %td.tags{ ng: { show: 'columns.tags.visible' } } - %td.visibility{ ng: { show: 'columns.visibility.visible' } } - %td.import_date{ ng: { show: 'columns.import_date.visible' } } + %td.producer{ "ng-show": 'columns.producer.visible', "ng-bind-html": '::producersByID[product.producer_id].name' } + %td.product{ "ng-show": 'columns.product.visible', "ng-bind": '::product.name' } + %td.sku{ "ng-show": 'columns.sku.visible' } + %td.price{ "ng-show": 'columns.price.visible' } + %td.on_hand{ "ng-show": 'columns.on_hand.visible' } + %td.on_demand{ "ng-show": 'columns.on_demand.visible' } + %td.reset{ colspan: 2, "ng-show": 'columns.reset.visible' } + %td.inheritance{ "ng-show": 'columns.inheritance.visible' } + %td.tags{ "ng-show": 'columns.tags.visible' } + %td.visibility{ "ng-show": 'columns.visibility.visible' } + %td.import_date{ "ng-show": 'columns.import_date.visible' } diff --git a/app/views/admin/variant_overrides/_products_variants.html.haml b/app/views/admin/variant_overrides/_products_variants.html.haml index 33f9fb9a61..43decfe33f 100644 --- a/app/views/admin/variant_overrides/_products_variants.html.haml +++ b/app/views/admin/variant_overrides/_products_variants.html.haml @@ -1,27 +1,27 @@ -%tr.variant{ id: "v_{{variant.id}}", ng: {repeat: 'variant in product.variants | inventoryVariants:hub_id:views'} } - %td.producer{ ng: { show: 'columns.producer.visible' } } - %td.product{ ng: { show: 'columns.product.visible' } } - %span{ ng: { bind: '::variant.display_name || ""'} } - .variant-override-unit{ ng: { bind: '::variant.unit_to_display'} } - %td.sku{ ng: { show: 'columns.sku.visible' } } - %input{name: 'variant-overrides-{{ variant.id }}-sku', type: 'text', ng: {model: 'variantOverrides[hub_id][variant.id].sku'}, placeholder: '{{ variant.sku }}', 'ofn-track-variant-override' => 'sku'} - %td.price{ ng: { show: 'columns.price.visible' } } - %input{name: 'variant-overrides-{{ variant.id }}-price', type: 'text', ng: {model: 'variantOverrides[hub_id][variant.id].price'}, placeholder: '{{ variant.price }}', 'ofn-track-variant-override' => 'price'} - %td.on_hand{ ng: { show: 'columns.on_hand.visible' } } - %input{name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', ng: { model: 'variantOverrides[hub_id][variant.id].count_on_hand', readonly: 'variantOverrides[hub_id][variant.id].on_demand != false' }, placeholder: '{{ countOnHandPlaceholder(variant, hub_id) }}', 'ofn-track-variant-override' => 'count_on_hand'} - %td.on_demand{ ng: { show: 'columns.on_demand.visible' } } - %select{ name: 'variant-overrides-{{ variant.id }}-on_demand', ng: { model: 'variantOverrides[hub_id][variant.id].on_demand', change: 'updateCountOnHand(variant, hub_id)', options: 'option.value as option.description for option in onDemandOptions' }, 'ofn-track-variant-override' => 'on_demand' } - %td.reset{ ng: { show: 'columns.reset.visible' } } - %input{name: 'variant-overrides-{{ variant.id }}-resettable', type: 'checkbox', ng: {model: 'variantOverrides[hub_id][variant.id].resettable'}, placeholder: '{{ variant.resettable }}', 'ofn-track-variant-override' => 'resettable'} - %td.reset{ ng: { show: 'columns.reset.visible' } } - %input{name: 'variant-overrides-{{ variant.id }}-default_stock', type: 'text', ng: {model: 'variantOverrides[hub_id][variant.id].default_stock'}, placeholder: '{{ variant.default_stock ? variant.default_stock : ("admin.variant_overrides.index.default_stock" | t)}}', 'ofn-track-variant-override' => 'default_stock'} - %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } - %input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-inherit', ng: { model: 'inherit' }, 'track-inheritance' => true } - %td.tags{ ng: { show: 'columns.tags.visible' } } +%tr.variant{ id: "v_{{variant.id}}", "ng-repeat": 'variant in product.variants | inventoryVariants:hub_id:views' } + %td.producer{ "ng-show": 'columns.producer.visible' } + %td.product{ "ng-show": 'columns.product.visible' } + %span{ "ng-bind": '::variant.display_name || ""' } + .variant-override-unit{ "ng-bind": '::variant.unit_to_display' } + %td.sku{ "ng-show": 'columns.sku.visible' } + %input{ name: 'variant-overrides-{{ variant.id }}-sku', type: 'text', placeholder: '{{ variant.sku }}', "ofn-track-variant-override": 'sku', "ng-model": 'variantOverrides[hub_id][variant.id].sku' } + %td.price{ "ng-show": 'columns.price.visible' } + %input{ name: 'variant-overrides-{{ variant.id }}-price', type: 'text', placeholder: '{{ variant.price }}', "ofn-track-variant-override": 'price', "ng-model": 'variantOverrides[hub_id][variant.id].price' } + %td.on_hand{ "ng-show": 'columns.on_hand.visible' } + %input{ name: 'variant-overrides-{{ variant.id }}-count_on_hand', type: 'text', placeholder: '{{ countOnHandPlaceholder(variant, hub_id) }}', "ofn-track-variant-override": 'count_on_hand', "ng-model": 'variantOverrides[hub_id][variant.id].count_on_hand', "ng-readonly": 'variantOverrides[hub_id][variant.id].on_demand != false' } + %td.on_demand{ "ng-show": 'columns.on_demand.visible' } + %select{ name: 'variant-overrides-{{ variant.id }}-on_demand', "ofn-track-variant-override": 'on_demand', "ng-model": 'variantOverrides[hub_id][variant.id].on_demand', "ng-change": 'updateCountOnHand(variant, hub_id)', "ng-options": 'option.value as option.description for option in onDemandOptions' } + %td.reset{ "ng-show": 'columns.reset.visible' } + %input{ name: 'variant-overrides-{{ variant.id }}-resettable', type: 'checkbox', placeholder: '{{ variant.resettable }}', "ofn-track-variant-override": 'resettable', "ng-model": 'variantOverrides[hub_id][variant.id].resettable' } + %td.reset{ "ng-show": 'columns.reset.visible' } + %input{ name: 'variant-overrides-{{ variant.id }}-default_stock', type: 'text', placeholder: '{{ variant.default_stock ? variant.default_stock : ("admin.variant_overrides.index.default_stock" | t)}}', "ofn-track-variant-override": 'default_stock', "ng-model": 'variantOverrides[hub_id][variant.id].default_stock' } + %td.inheritance{ "ng-show": 'columns.inheritance.visible' } + %input.field{ type: 'checkbox', name: 'variant-overrides-{{ variant.id }}-inherit', "track-inheritance": true, "ng-model": 'inherit' } + %td.tags{ "ng-show": 'columns.tags.visible' } .tag_watcher{ 'track-tag-list' => true } %tags_with_translation{ object: 'variantOverrides[hub_id][variant.id]', form: 'variant_overrides_form' } - %td.visibility{ ng: { show: 'columns.visibility.visible' } } - %button.icon-remove.fullwidth{ :type => 'button', ng: { click: "setVisibility(hub_id,variant.id,false)" } } + %td.visibility{ "ng-show": 'columns.visibility.visible' } + %button.icon-remove.fullwidth{ type: 'button', "ng-click": "setVisibility(hub_id,variant.id,false)" } = t('admin.variant_overrides.index.hide') - %td.import_date{ ng: { show: 'columns.import_date.visible' } } + %td.import_date{ "ng-show": 'columns.import_date.visible' } %span {{variantOverrides[hub_id][variant.id].import_date | date:"MMMM dd, yyyy HH:mm"}} diff --git a/app/views/admin/variant_overrides/_show_more.html.haml b/app/views/admin/variant_overrides/_show_more.html.haml index a6414a6d52..e232a512c2 100644 --- a/app/views/admin/variant_overrides/_show_more.html.haml +++ b/app/views/admin/variant_overrides/_show_more.html.haml @@ -1,4 +1,4 @@ -.text-center{ ng: { show: "filteredProducts.length > productLimit" } } - %input{ type: 'button', value: t(:show_more), ng: { click: 'productLimit = productLimit + 10' } } +.text-center{ "ng-show": "filteredProducts.length > productLimit" } + %input{ type: 'button', value: t(:show_more), "ng-click": 'productLimit = productLimit + 10' } - %input{ type: 'button', value: "#{t(:show_all)} ({{ filteredProducts.length - productLimit }} More)", ng: { click: 'productLimit = filteredProducts.length' } } + %input{ type: 'button', value: "#{t(:show_all)} ({{ filteredProducts.length - productLimit }} More)", "ng-click": 'productLimit = filteredProducts.length' } diff --git a/app/views/admin/variant_overrides/index.html.haml b/app/views/admin/variant_overrides/index.html.haml index 16beaad7b9..adfcc8f71c 100644 --- a/app/views/admin/variant_overrides/index.html.haml +++ b/app/views/admin/variant_overrides/index.html.haml @@ -1,13 +1,13 @@ = render 'admin/variant_overrides/header' = render 'admin/variant_overrides/data' -.margin-bottom-50{ ng: { app: 'admin.variantOverrides', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } } +.margin-bottom-50{ "ng-app": 'admin.variantOverrides', "ng-controller": 'AdminVariantOverridesCtrl', "ng-init": 'initialise()' } = render 'admin/variant_overrides/filters' = render 'admin/variant_overrides/new_products_alert' = render 'admin/variant_overrides/loading_flash' = render 'admin/variant_overrides/controls' = render 'admin/variant_overrides/no_results' - %div{ ng: { cloak: true, show: 'hub_id && filteredProducts.length > 0' } } + %div{ "ng-cloak": true, "ng-show": 'hub_id && filteredProducts.length > 0' } = render 'admin/variant_overrides/new_products' = render 'admin/variant_overrides/hidden_products' = render 'admin/variant_overrides/products' diff --git a/app/views/checkout/_details.html.haml b/app/views/checkout/_details.html.haml index c3518ed760..b837e52812 100644 --- a/app/views/checkout/_details.html.haml +++ b/app/views/checkout/_details.html.haml @@ -69,7 +69,7 @@ = f.check_box :save_bill_address = f.label :save_bill_address, t(:checkout_default_bill_address) - %div.checkout-substep{ "data-controller": "toggle shippingmethod" } + %div.checkout-substep{ "data-controller": "toggle-control shippingmethod" } - selected_shipping_method = @order.shipping_method&.id || params[:shipping_method_id] %div.checkout-title = t("checkout.step1.shipping_info.title") @@ -87,7 +87,7 @@ checked: ship_method_is_selected, name: "shipping_method_id", "data-requireAddress": shipping_method.require_ship_address, - "data-action": "toggle#toggle shippingmethod#selectShippingMethod", + "data-action": "toggle-control#toggleDisplay shippingmethod#selectShippingMethod", "data-toggle-show": shipping_method.require_ship_address = shipping_method_form.label shipping_method.id, shipping_method.name, {for: "shipping_method_" + shipping_method.id.to_s } %em.fees= payment_or_shipping_price(shipping_method, @order) @@ -104,7 +104,7 @@ = f.error_message_on :shipping_method, standalone: true - %div.checkout-input{ "data-toggle-target": "content", style: "display: #{display_ship_address ? 'block' : 'none'}" } + %div.checkout-input{ "data-toggle-control-target": "content", style: "display: #{display_ship_address ? 'block' : 'none'}" } = f.check_box :ship_address_same_as_billing, { id: "ship_address_same_as_billing", name: "ship_address_same_as_billing", "data-action": "shippingmethod#showHideShippingAddress", "data-shippingmethod-target": "shippingAddressCheckbox", checked: shipping_and_billing_match?(@order) }, 1, nil = f.label :ship_address_same_as_billing, t(:checkout_address_same), { for: "ship_address_same_as_billing" } @@ -142,7 +142,7 @@ = ship_address.select :state_id, states_for_country(ship_address_country), { selected: @order.ship_address&.state_id }, { "data-dependent-select-target": "select" } - if spree_current_user - %div.checkout-input{ "data-toggle-target": "content", style: "display: #{display_ship_address ? 'block' : 'none'}" } + %div.checkout-input{ "data-toggle-control-target": "content", style: "display: #{display_ship_address ? 'block' : 'none'}" } = f.check_box :save_ship_address = f.label :save_ship_address, t(:checkout_default_ship_address) diff --git a/app/views/checkout/_payment.html.haml b/app/views/checkout/_payment.html.haml index 38e237bb4d..656a246efe 100644 --- a/app/views/checkout/_payment.html.haml +++ b/app/views/checkout/_payment.html.haml @@ -1,5 +1,5 @@ .medium-6#checkout-payment-methods - - if feature?(:vouchers, spree_current_user, @order.distributor) && @order.distributor.vouchers.present? + - if @order.distributor.vouchers.present? %div.checkout-substep = render partial: "checkout/voucher_section", locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first } diff --git a/app/views/checkout/_voucher_section.html.haml b/app/views/checkout/_voucher_section.html.haml index df1837fb1d..0f7fb12f53 100644 --- a/app/views/checkout/_voucher_section.html.haml +++ b/app/views/checkout/_voucher_section.html.haml @@ -1,7 +1,7 @@ %div#voucher-section .checkout-title = t("checkout.step2.voucher.apply_voucher") - .checkout-input{"data-controller": "toggle-button-disabled"} + .checkout-input{"data-controller": "toggle-control"} = form_with url: voucher_adjustments_path, model: @order, method: :post, data: { remote: true } do |form| - if voucher_adjustment.present? .two-columns-inputs.voucher @@ -18,8 +18,8 @@ - else .two-columns-inputs %div.checkout-input - = form.text_field :voucher_code, value: params.dig(:order, :voucher_code), data: { action: "input->toggle-button-disabled#inputIsChanged" }, placeholder: t("checkout.step2.voucher.placeholder"), class: "voucher" + = form.text_field :voucher_code, value: params.dig(:order, :voucher_code), data: { action: "input->toggle-control#enableIfPresent" }, placeholder: t("checkout.step2.voucher.placeholder"), class: "voucher" = form.error_message_on :voucher_code %div.checkout-input - = form.submit t("checkout.step2.voucher.apply"), disabled: true, class: "button cancel voucher-button", "data-disable-with": false, data: { "toggle-button-disabled-target": "button" } + = form.submit t("checkout.step2.voucher.apply"), disabled: true, class: "button cancel voucher-button", "data-disable-with": false, data: { "toggle-control-target": "control" } diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index b9dab1817f..990c8387b5 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -1,5 +1,5 @@ - content_for(:title) do - = t :checkout_title + = checkout_page_title .darkswarm.footer-pad{"data-turbo": "true"} - content_for :order_cycle_form do diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index 628f4b8630..81949bf49a 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -17,9 +17,9 @@ - if @shopfront_layout == 'embedded' = render partial: 'shop/blocked_cookies' - .alert-box.changeable-orders-alert.info.animate-show{ ng: { show: "alert.visible && alert.html", cloak: true } } - %span{ ng: { bind: { html: "alert.html" } } } - %a.close{ ng: { click: "alert.close()" } } × + .alert-box.changeable-orders-alert.info.animate-show{ "ng-show": "alert.visible && alert.html", "ng-cloak": true } + %span{ "ng-bind-html": "alert.html" } + %a.close{ "ng-click": "alert.close()" } × - content_for :order_cycle_form do = render partial: "change_order_cycle" diff --git a/app/views/layouts/_signup_tab.html.haml b/app/views/layouts/_signup_tab.html.haml index 5d2af646e5..f25bf2024d 100644 --- a/app/views/layouts/_signup_tab.html.haml +++ b/app/views/layouts/_signup_tab.html.haml @@ -23,3 +23,4 @@ .row .large-12.columns = form.submit t(:action_signup), { class: "button primary", tabindex: 4 } + = form.invisible_captcha diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index d4aea36453..c44742a4b1 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -20,8 +20,9 @@ %tr %th = t :sku - %th - = t :supplier + - if @distributors_pickup_times.many? + %th + = t :supplier %th = t :product %th.text-right @@ -36,29 +37,31 @@ - @grouped_line_items.each_pair do |product_and_full_name, line_items| %tr %td - #{line_items.first.variant.sku} + = line_items.first.variant.sku + - if @distributors_pickup_times.many? + %td + = line_items.first.product.supplier.name %td - #{raw(line_items.first.product.supplier.name)} - %td - #{raw(product_and_full_name)} + = product_and_full_name %td.text-right - #{line_items.sum(&:quantity)} + = line_items.sum(&:quantity) %td.text-right - #{line_items.first.single_money} + = line_items.first.single_money %td.text-right - #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency) } + = Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency) %td.tax.text-right - #{Spree::Money.new(line_items.sum(&:included_tax), currency: line_items.first.currency) } + = Spree::Money.new(line_items.sum(&:included_tax), currency: line_items.first.currency) %tr.total-row %td - %td + - if @distributors_pickup_times.many? + %td %td %td %td %td.text-right - #{@total} + = @total %td.text-right - #{@tax_total} + = @tax_total - if @customer_line_items %p = t :producer_mail_order_customer_text @@ -67,8 +70,9 @@ %tr %th = t :sku - %th - = t :supplier + - if @distributors_pickup_times.many? + %th + = t :supplier %th = t :product %th.text-right @@ -81,32 +85,34 @@ - @customer_line_items.each do |line_item| %tr %td - #{line_item[:sku]} + = line_item[:sku] + - if @distributors_pickup_times.many? + %td + = line_item[:supplier_name] %td - #{raw(line_item[:supplier_name])} - %td - #{raw(line_item[:product_and_full_name])} + = line_item[:product_and_full_name] %td.text-right - #{line_item[:quantity]} + = line_item[:quantity] %td - #{raw(line_item[:first_name])} + = line_item[:first_name] %td - #{raw(line_item[:last_name])} + = line_item[:last_name] %p = t :producer_mail_text_after %p - #{t(:producer_mail_signoff)}, + = t(:producer_mail_signoff) + , %em %p - #{@coordinator.name} + = @coordinator.name %p %br - #{@coordinator.address.address1} + = @coordinator.address.address1 %br - #{@coordinator.address.city} + = @coordinator.address.city %br - #{@coordinator.address.zipcode} + = @coordinator.address.zipcode %p - #{@coordinator.phone} + = @coordinator.phone %p - #{@coordinator.contact.email} + = @coordinator.contact.email diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index 6e05619d07..644a3805b9 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -4,14 +4,14 @@ .row.vertical-align-middle .columns.small-12 %a.is_distributor{"ng-href" => "{{::producer.path}}", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", "data-is-link" => "true"} - %i{ng: {class: "::producer.producer_icon_font"}} + %i{ "ng-class": "::producer.producer_icon_font" } %span.margin-top %strong{"ng-bind" => "::producer.name"} %span.producer-name{"ng-if" => "::!producer.is_distributor" } .row.vertical-align-middle .columns.small-12 - %i{ng: {class: "::producer.producer_icon_font"}} + %i{ "ng-class": "::producer.producer_icon_font" } %span.margin-top %strong{"ng-bind" => "::producer.name"} diff --git a/app/views/registration/_modal.html.haml b/app/views/registration/_modal.html.haml index 5fa1da9296..798c1f23f9 100644 --- a/app/views/registration/_modal.html.haml +++ b/app/views/registration/_modal.html.haml @@ -1,10 +1,10 @@ %script{ type: "text/ng-template", id: "registration.html" } %div#registration-modal{"ng-controller" => "RegistrationCtrl"} - %div{ ng: { show: "currentStep() == 'introduction'" } } + %div{ "ng-show": "currentStep() == 'introduction'" } %ng-include{ src: "'registration/introduction.html'" } - %div{ ng: { repeat: 'step in steps', show: "currentStep() == step" } } + %div{ "ng-repeat": 'step in steps', "ng-show": "currentStep() == step" } %ng-include{ src: "'registration/'+ step + '.html'" } - %div{ ng: { show: "currentStep() == 'finished'" } } + %div{ "ng-show": "currentStep() == 'finished'" } %ng-include{ src: "'registration/finished.html'" } %a.close-reveal-modal{"ng-click" => "$close()"} diff --git a/app/views/registration/steps/_about.html.haml b/app/views/registration/steps/_about.html.haml index 65a688e54b..9a18dbd55a 100644 --- a/app/views/registration/steps/_about.html.haml +++ b/app/views/registration/steps/_about.html.haml @@ -7,49 +7,49 @@ %h2= t(".headline") %h5 = t(".message") - %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + %span{ "ng-class": "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } {{ enterprise.name }} - %form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('images', about)" } } + %form{ name: 'about', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "selectIfValid('images', about)" } .row .small-12.columns - .alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } } + .alert-box.info{ "ofn-inline-alert": true, "ng-show": "visible" } %h6{ "ng-bind" => "'registration.steps.about.success' | t:{enterprise: enterprise.name}" } %span= t(".registration_exit_message") - %a.close{ ng: { click: "close()" } } × + %a.close{ "ng-click": "close()" } × .small-12.large-8.columns .row .small-12.columns .field %label{ for: 'enterprise_description' }= t(".enterprise_description") - %input.chunky{ id: 'enterprise_description', placeholder: "{{'registration.steps.about.enterprise_description_placeholder' | t}}", ng: { model: 'enterprise.description' } } + %input.chunky{ id: 'enterprise_description', placeholder: "{{'registration.steps.about.enterprise_description_placeholder' | t}}", "ng-model": 'enterprise.description' } .row .small-12.columns .field %label{ for: 'enterprise_long_desc' }= t(".enterprise_long_desc") - %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "{{'registration.steps.about.enterprise_long_desc_placeholder' | t}}", ng: { model: 'enterprise.long_description' } } + %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "{{'registration.steps.about.enterprise_long_desc_placeholder' | t}}", "ng-model": 'enterprise.long_description' } %small{ "ng-bind" => "'registration.steps.about.enterprise_long_desc_length' | t:{num: enterprise.long_description.length}" } .small-12.large-4.columns .row .small-12.columns .field %label{ for: 'enterprise_abn' }= t(".enterprise_abn")+":" - %input.chunky{ id: 'enterprise_abn', placeholder: "{{'registration.steps.about.enterprise_abn_placeholder' | t}}", ng: { model: 'enterprise.abn' } } + %input.chunky{ id: 'enterprise_abn', placeholder: "{{'registration.steps.about.enterprise_abn_placeholder' | t}}", "ng-model": 'enterprise.abn' } .row .small-12.columns .field %label{ for: 'enterprise_acn' }= t(".enterprise_acn")+":" - %input.chunky{ id: 'enterprise_acn', placeholder: "{{'registration.steps.about.enterprise_acn_placeholder' | t}}", ng: { model: 'enterprise.acn' } } + %input.chunky{ id: 'enterprise_acn', placeholder: "{{'registration.steps.about.enterprise_acn_placeholder' | t}}", "ng-model": 'enterprise.acn' } .row .small-12.columns .field %label{ for: 'enterprise_charges_sales_tax' }= t(:charges_sales_tax) - %input{ id: 'enterprise_charges_sales_tax_true', type: 'radio', name: 'charges_sales_tax', value: 'true', required: true, ng: { model: 'enterprise.charges_sales_tax' } } + %input{ id: 'enterprise_charges_sales_tax_true', type: 'radio', name: 'charges_sales_tax', value: 'true', required: true, "ng-model": 'enterprise.charges_sales_tax' } %label{ for: 'enterprise_charges_sales_tax_true' } {{'say_yes' | t}} - %input{ id: 'enterprise_charges_sales_tax_false', type: 'radio', name: 'charges_sales_tax', value: 'false', required: true, ng: { model: 'enterprise.charges_sales_tax' } } + %input{ id: 'enterprise_charges_sales_tax_false', type: 'radio', name: 'charges_sales_tax', value: 'false', required: true, "ng-model": 'enterprise.charges_sales_tax' } %label{ for: 'enterprise_charges_sales_tax_false' } {{'say_no' | t}} - %span.error.small-12.columns{ ng: { show: "about.charges_sales_tax.$error.required && submitted" } } + %span.error.small-12.columns{ "ng-show": "about.charges_sales_tax.$error.required && submitted" } = t(".enterprise_tax_required") .row.buttons.pad-top diff --git a/app/views/registration/steps/_contact.html.haml b/app/views/registration/steps/_contact.html.haml index 370101ada3..2dea91fef5 100644 --- a/app/views/registration/steps/_contact.html.haml +++ b/app/views/registration/steps/_contact.html.haml @@ -7,30 +7,30 @@ %h2= t('registration.steps.introduction.registration_greeting') %h5{ "ng-bind" => "'registration.steps.contact.who_is_managing_enterprise' | t:{enterprise: enterprise.name}" } - %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('type',contact)" } } + %form{ name: 'contact', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "selectIfValid('type',contact)" } .row.content .small-12.medium-12.large-7.columns .row .small-12.columns.field %label{ for: 'enterprise_contact' }= t(".contact_field") - %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact_name', required: true, placeholder: "{{'registration.steps.contact.contact_field_placeholder' | t}}", ng: { model: 'enterprise.contact_name' } } - %span.error.small-12.columns{ ng: { show: "contact.contact_name.$error.required && submitted" } } + %input.chunky.small-12.columns{ id: 'enterprise_contact', name: 'contact_name', required: true, placeholder: "{{'registration.steps.contact.contact_field_placeholder' | t}}", "ng-model": 'enterprise.contact_name' } + %span.error.small-12.columns{ "ng-show": "contact.contact_name.$error.required && submitted" } = t(".contact_field_required") .row .small-12.columns.field %label{ for: 'enterprise_email_address' }= t("admin.enterprises.form.contact.email_address")+":" - %input.chunky.small-12.columns{ id: 'enterprise_email_address', name: 'email_address', type: 'email', placeholder: t('admin.enterprises.form.contact.email_address_placeholder'), ng: { model: 'enterprise.email_address' } } + %input.chunky.small-12.columns{ id: 'enterprise_email_address', name: 'email_address', type: 'email', placeholder: t('admin.enterprises.form.contact.email_address_placeholder'), "ng-model": 'enterprise.email_address' } .row .small-12.columns.field %label{ for: 'enterprise_phone' }= t(".phone_field")+":" - %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "{{'registration.steps.contact.phone_field_placeholder' | t}}", ng: { model: 'enterprise.phone' } } + %input.chunky.small-12.columns{ id: 'enterprise_phone', name: 'phone', placeholder: "{{'registration.steps.contact.phone_field_placeholder' | t}}", "ng-model": 'enterprise.phone' } .row .small-12.columns.field %label{ "for" => 'enterprise_whatsapp_phone', 'data-toggle' => "tooltip", 'title' => "{{'registration.steps.contact.whatsapp_phone_tooltip' | t}}" }= t(".whatsapp_phone_field")+":" - %input.chunky.small-12.columns{ id: 'enterprise_whatsapp_phone', name: 'whatsapp_phone', placeholder: "{{'registration.steps.contact.whatsapp_phone_field_placeholder' | t}}", ng: { model: 'enterprise.whatsapp_phone' } } + %input.chunky.small-12.columns{ id: 'enterprise_whatsapp_phone', name: 'whatsapp_phone', placeholder: "{{'registration.steps.contact.whatsapp_phone_field_placeholder' | t}}", "ng-model": 'enterprise.whatsapp_phone' } .small-12.medium-12.large-5.hide-for-small-only .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('details')" } } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", "ng-click": "select('details')" } %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/views/registration/steps/_details.html.haml b/app/views/registration/steps/_details.html.haml index 906b3a5745..45fb0e56b7 100644 --- a/app/views/registration/steps/_details.html.haml +++ b/app/views/registration/steps/_details.html.haml @@ -5,56 +5,56 @@ .small-12.columns %header %h2= t('.headline') - %h5{ ng: { if: "::enterprise.type != 'own'" } }= t(".enterprise") - %h5{ ng: { if: "::enterprise.type == 'own'" } }= t(".producer") - %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',details)" } } + %h5{ "ng-if": "::enterprise.type != 'own'" }= t(".enterprise") + %h5{ "ng-if": "::enterprise.type == 'own'" }= t(".producer") + %form{ name: 'details', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "selectIfValid('contact',details)" } .row .small-12.medium-9.large-12.columns.end .field - %label{ for: 'enterprise_name', ng: { if: "::enterprise.type != 'own'" } }= t(".enterprise_name_field") - %label{ for: 'enterprise_name', ng: { if: "::enterprise.type == 'own'" } }= t(".producer_name_field") - %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "{{'registration.steps.details.producer_name_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.name' } } - %span.error{ ng: { show: "details.name.$error.required && submitted" } } + %label{ for: 'enterprise_name', "ng-if": "::enterprise.type != 'own'" }= t(".enterprise_name_field") + %label{ for: 'enterprise_name', "ng-if": "::enterprise.type == 'own'" }= t(".producer_name_field") + %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "{{'registration.steps.details.producer_name_field_placeholder' | t}}", required: true, "ng-model": 'enterprise.name' } + %span.error{ "ng-show": "details.name.$error.required && submitted" } = t(".producer_name_field_error") .row .small-12.medium-9.large-6.columns .field %label{ for: 'enterprise_address' }= t(".address1_field") - %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "{{'registration.steps.details.address1_field_placeholder' | t}}", required: true, ng: { model: 'enterprise.address.address1' } } - %span.error{ ng: { show: "details.address1.$error.required && submitted" } } + %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "{{'registration.steps.details.address1_field_placeholder' | t}}", "ng-model": 'enterprise.address.address1' } + %span.error{ "ng-show": "details.address1.$error.required && submitted" } = t(".address1_field_error") .field %label{ for: 'enterprise_address2' }= t(".address2_field") - %input.chunky{ id: 'enterprise_address2', name: 'address2', required: false, placeholder: "", required: false, ng: { model: 'enterprise.address.address2' } } + %input.chunky{ id: 'enterprise_address2', name: 'address2', required: false, placeholder: "", "ng-model": 'enterprise.address.address2' } .small-12.medium-9.large-6.columns.end .row .small-12.medium-8.large-8.columns .field %label{ for: 'enterprise_city' }= t('.suburb_field') - %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "{{'registration.steps.details.suburb_field_placeholder' | t}}", ng: { model: 'enterprise.address.city' } } - %span.error{ ng: { show: "details.city.$error.required && submitted" } } + %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "{{'registration.steps.details.suburb_field_placeholder' | t}}", "ng-model": 'enterprise.address.city' } + %span.error{ "ng-show": "details.city.$error.required && submitted" } = t(".suburb_field_error") .small-12.medium-4.large-4.columns .field %label{ for: 'enterprise_zipcode' }= t(".postcode_field") - %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "{{'registration.steps.details.postcode_field_placeholder' | t}}", ng: { model: 'enterprise.address.zipcode' } } - %span.error{ ng: { show: "details.zipcode.$error.required && submitted" } } + %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "{{'registration.steps.details.postcode_field_placeholder' | t}}", "ng-model": 'enterprise.address.zipcode' } + %span.error{ "ng-show": "details.zipcode.$error.required && submitted" } = t(".postcode_field_error") .row .small-12.medium-8.large-8.columns .field %label{ for: 'enterprise_country' }= t(".country_field") - %select.chunky{ id: 'enterprise_country', name: 'country', required: true, ng: { init: "setDefaultCountry(#{DefaultCountry.id})", model: 'enterprise.country', options: 'c as c.name for c in countries' } } - %span.error{ ng: { show: "details.country.$error.required && submitted" } } + %select.chunky{ id: 'enterprise_country', name: 'country', required: true, "ng-init": "setDefaultCountry(#{DefaultCountry.id})", "ng-model": 'enterprise.country', "ng-options": 'c as c.name for c in countries' } + %span.error{ "ng-show": "details.country.$error.required && submitted" } = t(".country_field_error") .small-12.medium-4.large-4.columns .field %label{ for: 'enterprise_state' }= t(".state_field") - %select.chunky{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } - %span.error{ ng: { show: "details.state.$error.required && submitted" } } + %select.chunky{ id: 'enterprise_state', name: 'state', "ng-model": 'enterprise.address.state_id', "ng-options": 's.id as s.abbr for s in enterprise.country.states', "ng-show": 'countryHasStates()', "ng-required": 'countryHasStates()' } + %span.error{ "ng-show": "details.state.$error.required && submitted" } = t(".state_field_error") = render 'registration/steps/location_map' if using_google_maps? diff --git a/app/views/registration/steps/_images.html.haml b/app/views/registration/steps/_images.html.haml index b52c179f3c..9df31aaf96 100644 --- a/app/views/registration/steps/_images.html.haml +++ b/app/views/registration/steps/_images.html.haml @@ -1,5 +1,5 @@ %script{ type: "text/ng-template", id: "registration/images.html" } - .container#registration-images{ 'nv-file-drop' => true, uploader: "imageUploader", options:"{ alias: imageStep }", ng: { controller: "EnterpriseImageCtrl" } } + .container#registration-images{ "nv-file-drop": true, uploader: "imageUploader", options: "{ alias: imageStep }", "ng-controller": "EnterpriseImageCtrl" } %ng-include{ src: "'registration/steps.html'" } .row .small-12.columns @@ -7,17 +7,17 @@ %h2= t(".headline") %h5= t(".description") - %form{ name: 'images', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "select('social')" } } - .row{ ng: { repeat: 'image_step in imageSteps', show: "imageStep == image_step" } } + %form{ name: 'images', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "select('social')" } + .row{ "ng-repeat": 'image_step in imageSteps', "ng-show": "imageStep == image_step" } %ng-include{ src: "'registration/'+ image_step + '.html'" } - .row.buttons.pad-top{ ng: { if: "imageStep == 'logo'" } } + .row.buttons.pad-top{ "ng-if": "imageStep == 'logo'" } .small-12.columns - %input.button.secondary{ type: "button", value: t(".back"), ng: { click: "select('about')" } } + %input.button.secondary{ type: "button", value: t(".back"), "ng-click": "select('about')" }   - %input.button.primary.right{ type: "button", value: t(".continue"), ng: { click: "imageSelect('promo')" } } + %input.button.primary.right{ type: "button", value: t(".continue"), "ng-click": "imageSelect('promo')" } - .row.buttons.pad-top{ ng: { if: "imageStep == 'promo'" } } + .row.buttons.pad-top{ "ng-if": "imageStep == 'promo'" } .small-12.columns - %input.button.secondary{ type: "button", value: t(".back"), ng: { click: "imageSelect('logo')" } } + %input.button.secondary{ type: "button", value: t(".back"), "ng-click": "imageSelect('logo')" } %input.button.primary.right{ type: "submit", value: t(".continue") } diff --git a/app/views/registration/steps/_introduction.html.haml b/app/views/registration/steps/_introduction.html.haml index 90cbabff83..9b7286864b 100644 --- a/app/views/registration/steps/_introduction.html.haml +++ b/app/views/registration/steps/_introduction.html.haml @@ -44,8 +44,8 @@ #{t(:enterprise_tos_message)} = link_to_platform_terms %p.tos-checkbox - %input{ type: 'checkbox', name: 'accept_terms', id: 'accept_terms', ng: { model: "tos_accepted" } } + %input{ type: 'checkbox', name: 'accept_terms', id: 'accept_terms', "ng-model": "tos_accepted" } %label{for: "accept_terms"} #{t(:enterprise_tos_agree)} .small-12.medium-6.columns - %input.button.primary.left{ type: "button", value: "{{'registration.steps.introduction.registration_action' | t}}", ng: { click: "select('details')", disabled: "tos_required && !tos_accepted", model: "tos_accepted"} } + %input.button.primary.left{ type: "button", value: "{{'registration.steps.introduction.registration_action' | t}}", "ng-click": "select('details')", "ng-disabled": "tos_required && !tos_accepted", "ng-model": "tos_accepted" } diff --git a/app/views/registration/steps/_limit_reached.html.haml b/app/views/registration/steps/_limit_reached.html.haml index 7a486e5743..6f07dd80b9 100644 --- a/app/views/registration/steps/_limit_reached.html.haml +++ b/app/views/registration/steps/_limit_reached.html.haml @@ -14,4 +14,4 @@ .row .small-12.columns %hr - %input.button.primary{ type: "button", value: "{{'registration.steps.limit_reached.action' | t}}", ng: { click: "close()" } } + %input.button.primary{ type: "button", value: "{{'registration.steps.limit_reached.action' | t}}", "ng-click": "close()" } diff --git a/app/views/registration/steps/_location_map.html.haml b/app/views/registration/steps/_location_map.html.haml index 1d6baeb9ac..280ca55541 100644 --- a/app/views/registration/steps/_location_map.html.haml +++ b/app/views/registration/steps/_location_map.html.haml @@ -1,19 +1,19 @@ %div .center - %input.button.primary{ type: "button", value: "{{'registration.steps.details.locate_address' | t}}", ng: { click: "locateAddress()" } } - .center{ ng: {if: "latLong"}} + %input.button.primary{ type: "button", value: "{{'registration.steps.details.locate_address' | t}}", "ng-click": "locateAddress()" } + .center{ "ng-if": "latLong" } %strong {{'registration.steps.details.drag_pin' | t}} .small-12.medium-9.large-12.columns.end - .map-container--registration{id: "registration-map", ng: {if: "!!map"}} + .map-container--registration{ id: "registration-map", "ng-if": "!!map" } %ui-gmap-google-map{ center: "map.center", zoom: "map.zoom"} %ui-gmap-marker{idKey: 1, coords: "latLong", options: '{ draggable: true}'} - .center{ ng: {if: "latLong"}} + .center{ "ng-if": "latLong" } .field - %input{ type: 'checkbox', id: 'confirm_address', name: 'confirm_address', ng: { click: 'toggleAddressConfirmed()' } } + %input{ type: 'checkbox', id: 'confirm_address', name: 'confirm_address', "ng-click": 'toggleAddressConfirmed()' } %label{ for: 'confirm_address' } {{'registration.steps.details.confirm_address' | t}} - .small-12.medium-9.large-12.columns.end{ ng: {if: "latLong"}} + .small-12.medium-9.large-12.columns.end{ "ng-if": "latLong" } .field %strong {{'registration.steps.details.drag_map_marker' | t}} diff --git a/app/views/registration/steps/_logo.html.haml b/app/views/registration/steps/_logo.html.haml index 06de18ff4d..e79ba39fd1 100644 --- a/app/views/registration/steps/_logo.html.haml +++ b/app/views/registration/steps/_logo.html.haml @@ -37,10 +37,10 @@ .row.pad-top .small-12.columns.center #image-placeholder.logo - %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } - .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } + %img{ "ng-show": "imageSrc() && !imageUploader.isUploading", "ng-src": '{{ imageSrc() }}' } + .message{ "ng-hide": "imageSrc() || imageUploader.isUploading" } = t(".logo_placeholder") - .loading{ ng: { hide: "!imageUploader.isUploading" } } + .loading{ "ng-hide": "!imageUploader.isUploading" } = render partial: "components/spinner" %br/ = t("registration.steps.images.uploading") diff --git a/app/views/registration/steps/_promo.html.haml b/app/views/registration/steps/_promo.html.haml index 3a9d94db75..2b9ef89616 100644 --- a/app/views/registration/steps/_promo.html.haml +++ b/app/views/registration/steps/_promo.html.haml @@ -35,10 +35,10 @@ .row.pad-top .small-12.columns.center #image-placeholder.promo - %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } - .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } + %img{ "ng-show": "imageSrc() && !imageUploader.isUploading", "ng-src": '{{ imageSrc() }}' } + .message{ "ng-hide": "imageSrc() || imageUploader.isUploading" } = t(".promo_image_placeholder") - .loading{ ng: { hide: "!imageUploader.isUploading" } } + .loading{ "ng-hide": "!imageUploader.isUploading" } = render partial: "components/spinner" %br/ = t("registration.steps.images.uploading") diff --git a/app/views/registration/steps/_social.html.haml b/app/views/registration/steps/_social.html.haml index 9cd86ead1c..180d79019b 100644 --- a/app/views/registration/steps/_social.html.haml +++ b/app/views/registration/steps/_social.html.haml @@ -8,38 +8,38 @@ %h2= t(".enterprise_final_step") %h5{ "ng-bind" => "'registration.steps.social.enterprise_social_text' | t:{enterprise: enterprise.name}" } - %form{ name: 'social', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('finished',social)" } } + %form{ name: 'social', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "update('finished',social)" } .row.content .small-12.large-7.columns .row .small-12.columns .field %label{ for: 'enterprise_website' }= t(".website")+":" - %input.chunky{ id: 'enterprise_website', placeholder: "{{'registration.steps.social.website_placeholder' | t}}", ng: { model: 'enterprise.website' } } + %input.chunky{ id: 'enterprise_website', placeholder: "{{'registration.steps.social.website_placeholder' | t}}", "ng-model": 'enterprise.website' } .row .small-12.columns .field %label{ for: 'enterprise_facebook' }= t(".facebook")+":" - %input.chunky{ id: 'enterprise_facebook', placeholder: "{{'registration.steps.social.facebook_placeholder' | t}}", ng: { model: 'enterprise.facebook' } } + %input.chunky{ id: 'enterprise_facebook', placeholder: "{{'registration.steps.social.facebook_placeholder' | t}}", "ng-model": 'enterprise.facebook' } .row .small-12.columns .field %label{ for: 'enterprise_linkedin' }= t(".linkedin")+":" - %input.chunky{ id: 'enterprise_linkedin', placeholder: "{{'registration.steps.social.linkedin_placeholder' | t}}", ng: { model: 'enterprise.linkedin' } } + %input.chunky{ id: 'enterprise_linkedin', placeholder: "{{'registration.steps.social.linkedin_placeholder' | t}}", "ng-model": 'enterprise.linkedin' } .small-12.large-5.columns .row .small-12.columns .field %label{ for: 'enterprise_twitter' }= t(".twitter")+":" - %input.chunky{ id: 'enterprise_twitter', placeholder: "{{'registration.steps.social.twitter_placeholder' | t}}", ng: { model: 'enterprise.twitter' } } + %input.chunky{ id: 'enterprise_twitter', placeholder: "{{'registration.steps.social.twitter_placeholder' | t}}", "ng-model": 'enterprise.twitter' } .row .small-12.columns .field %label{ for: 'enterprise_instagram' }= t(".instagram")+":" - %input.chunky{ id: 'enterprise_instagram', placeholder: "{{'registration.steps.social.instagram_placeholder' | t}}", ng: { model: 'enterprise.instagram' } } + %input.chunky{ id: 'enterprise_instagram', placeholder: "{{'registration.steps.social.instagram_placeholder' | t}}", "ng-model": 'enterprise.instagram' } %span.error.small-12.columns#instagram-error{ style: "display: none" } .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('images')" } } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", "ng-click": "select('images')" } %input.button.primary.right{ type: "submit", value: "{{'continue' | t}}" } diff --git a/app/views/registration/steps/_steps.html.haml b/app/views/registration/steps/_steps.html.haml index 081cd97d98..44bd076de9 100644 --- a/app/views/registration/steps/_steps.html.haml +++ b/app/views/registration/steps/_steps.html.haml @@ -1,5 +1,5 @@ %script{ type: "text/ng-template", id: "registration/steps.html" } .row#progress-bar - .small-12.medium-2.columns.item{ ng: { repeat: 'step in steps', class: "{active: (currentStep() == step),'show-for-medium-up': (currentStep() != step)}" } } + .small-12.medium-2.columns.item{ "ng-repeat": 'step in steps', "ng-class": "{active: (currentStep() == step),'show-for-medium-up': (currentStep() != step)}" } {{ $index+1 + ". " + ('registration.steps.' + step + '.title' | t) }} diff --git a/app/views/registration/steps/_type.html.haml b/app/views/registration/steps/_type.html.haml index 01482fd160..654ddedded 100644 --- a/app/views/registration/steps/_type.html.haml +++ b/app/views/registration/steps/_type.html.haml @@ -10,24 +10,24 @@ %h4 = t(".question") - %form{ name: 'type', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "create(type)" } } - .row#enterprise-types{ 'data-equalizer' => true, ng: { if: "::enterprise.type != 'own'" } } + %form{ name: 'type', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "create(type)" } + .row#enterprise-types{ "data-equalizer": true, "ng-if": "::enterprise.type != 'own'" } .small-12.columns.field .row .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } - %a.btnpanel#producer-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = true", class: "{selected: enterprise.is_primary_producer}" } } + %a.btnpanel#producer-panel{ href: "#", "ng-click": "enterprise.is_primary_producer = true", "ng-class": "{selected: enterprise.is_primary_producer}" } %i.ofn-i_059-producer %h4= t(".yes_producer") .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } - %a.btnpanel#hub-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = false", class: "{selected: enterprise.is_primary_producer == false}" } } + %a.btnpanel#hub-panel{ href: "#", "ng-click": "enterprise.is_primary_producer = false", "ng-class": "{selected: enterprise.is_primary_producer == false}" } %i.ofn-i_063-hub %h4= t(".no_producer") .row .small-12.columns - %input.chunky{ id: 'enterprise_is_primary_producer', name: 'is_primary_producer', type: "hidden", required: true, ng: { model: 'enterprise.is_primary_producer' } } - %span.error{ ng: { show: "type.is_primary_producer.$error.required && submitted" } } + %input.chunky{ id: 'enterprise_is_primary_producer', name: 'is_primary_producer', type: "hidden", required: true, "ng-model": 'enterprise.is_primary_producer' } + %span.error{ "ng-show": "type.is_primary_producer.$error.required && submitted" } = t(".producer_field_error") .row .small-12.columns @@ -44,5 +44,5 @@ .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "{{'back' | t}}", ng: { click: "select('contact')" } } - %input.button.primary.right{ ng: { disabled: 'isDisabled' }, type: "submit", value: t(".create_profile") } + %input.button.secondary{ type: "button", value: "{{'back' | t}}", "ng-click": "select('contact')" } + %input.button.primary.right{ type: "submit", value: t(".create_profile"), "ng-disabled": 'isDisabled' } diff --git a/app/views/shared/_page_alert.html.haml b/app/views/shared/_page_alert.html.haml index d57ef18d99..b13a0710fa 100644 --- a/app/views/shared/_page_alert.html.haml +++ b/app/views/shared/_page_alert.html.haml @@ -1,6 +1,6 @@ - if ContentConfig.home_page_alert_html.present? .alert-cta - %h6= raw ContentConfig.home_page_alert_html + %h6= sanitize(ContentConfig.home_page_alert_html, scrubber: TrixScrubber.new) - else = render "shared/register_call" diff --git a/app/views/shared/menu/_alert.html.haml b/app/views/shared/menu/_alert.html.haml index e3eb53fe74..787c1258cc 100644 --- a/app/views/shared/menu/_alert.html.haml +++ b/app/views/shared/menu/_alert.html.haml @@ -1,4 +1,4 @@ .text-center.page-alert.fixed{ "ofn-page-alert" => true } .alert-box = render 'shared/page_alert' - %a.close{ ng: { click: "close()" } } × + %a.close{ "ng-click": "close()" } × diff --git a/app/views/shared/menu/_cart_sidebar.html.haml b/app/views/shared/menu/_cart_sidebar.html.haml index 423d4585cd..16c057888f 100644 --- a/app/views/shared/menu/_cart_sidebar.html.haml +++ b/app/views/shared/menu/_cart_sidebar.html.haml @@ -1,5 +1,5 @@ -.expanding-sidebar.cart-sidebar{ng: {controller: 'CartCtrl', class: "{'shown': showCartSidebar}"}} - .background{ng: {click: 'toggleCartSidebar()'}} +.expanding-sidebar.cart-sidebar{ "ng-controller": 'CartCtrl', "ng-class": "{'shown': showCartSidebar}" } + .background{ "ng-click": 'toggleCartSidebar()' } .sidebar = cache_with_locale "cart-header" do .cart-header @@ -7,7 +7,7 @@ = t('.items_in_cart_singular', num: "{{ Cart.total_item_count() }}") %span.title{"ng-show" => "Cart.line_items.length > 1"} = t('.items_in_cart_plural', num: "{{ Cart.total_item_count() }}") - %a.close{ng: {click: 'toggleCartSidebar()'}} + %a.close{ "ng-click": 'toggleCartSidebar()' } = t('.close') %i.ofn-i_009-close @@ -40,7 +40,7 @@ %p = t('.cart_empty') - %a.go-shopping.button.large.bright{ng: {show: "#{show_shopping_cta?}", href: "{{ CurrentHub.hub.id ? '#{main_app.shop_path}' : '#{main_app.shops_path}' }}"}} + %a.go-shopping.button.large.bright{ "ng-show": "#{show_shopping_cta?}", "ng-href": "{{ CurrentHub.hub.id ? '#{main_app.shop_path}' : '#{main_app.shops_path}' }}" } = t('.take_me_shopping') .sidebar-footer{"ng-show" => "Cart.line_items.length > 0"} @@ -52,8 +52,8 @@ %div.fullwidth %a.edit-cart.button.large.dark.left{href: main_app.cart_path, "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }"} - %div{ ng: { if: "Cart.dirty" } }= t(:cart_updating) - %div{ ng: { if: "!Cart.dirty && Cart.empty()" } }= t(:cart_empty) - %div{ ng: { if: "!Cart.dirty && !Cart.empty()" } }= t('.edit_cart') + %div{ "ng-if": "Cart.dirty" }= t(:cart_updating) + %div{ "ng-if": "!Cart.dirty && Cart.empty()" }= t(:cart_empty) + %div{ "ng-if": "!Cart.dirty && !Cart.empty()" }= t('.edit_cart') %a.checkout.button.large.bright.right{href: main_app.checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} = t '.checkout' diff --git a/app/views/shared/menu/_mobile_menu.html.haml b/app/views/shared/menu/_mobile_menu.html.haml index 0e25ee57b0..544cb65631 100644 --- a/app/views/shared/menu/_mobile_menu.html.haml +++ b/app/views/shared/menu/_mobile_menu.html.haml @@ -14,7 +14,7 @@ %section.right{"ng-cloak" => true} %span.cart-span{"ng-class" => "{ dirty: Cart.dirty || Cart.empty(), 'pure-dirty': Cart.dirty }"} - %a.icon{ng: {click: 'toggleCartSidebar()'}} + %a.icon{ "ng-click": 'toggleCartSidebar()' } %span = t '.cart' %span.count diff --git a/app/views/shared/menu/_offcanvas_menu.html.haml b/app/views/shared/menu/_offcanvas_menu.html.haml index 8c6456c647..967c854acd 100644 --- a/app/views/shared/menu/_offcanvas_menu.html.haml +++ b/app/views/shared/menu/_offcanvas_menu.html.haml @@ -1,4 +1,4 @@ -%aside.left-off-canvas-menu.show-for-medium-down{ ng: { controller: "OffcanvasCtrl" } } +%aside.left-off-canvas-menu.show-for-medium-down{ "ng-controller": "OffcanvasCtrl" } %ul.off-canvas-list = cache_with_locale [ContentConfig.cache_key, @white_label_logo] do %li.ofn-logo diff --git a/app/views/shop/messages/_customer_required.html.haml b/app/views/shop/messages/_customer_required.html.haml index 8943568258..844621ce28 100644 --- a/app/views/shop/messages/_customer_required.html.haml +++ b/app/views/shop/messages/_customer_required.html.haml @@ -8,6 +8,6 @@ %p = t('.require_login_link_html', login: ('' + t('.login') + '').html_safe) %p - = t('.require_login_2_html', contact: link_to(t('.contact'), '#contact'), enterprise: current_distributor.name) + = t('.require_login_2_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#activate" }), enterprise: current_distributor.name) - else - = t('.require_customer_html', contact: link_to(t('.contact'), '#contact'), enterprise: current_distributor.name) + = t('.require_customer_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#activate" }), enterprise: current_distributor.name) diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index 869b215c60..8a929af886 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -1,6 +1,6 @@ = cache_with_locale do - .filter-shopfront.taxon-selectors{ng: {show: 'supplied_taxons != null'}} + .filter-shopfront.taxon-selectors{ "ng-show": 'supplied_taxons != null' } %filter-selector{ 'selector-set' => "taxonSelectors", objects: "supplied_taxons", "active-selectors" => "activeTaxons"} - .filter-shopfront.property-selectors{ng: {show: 'supplied_properties != null'}} + .filter-shopfront.property-selectors{ "ng-show": 'supplied_properties != null' } %filter-selector{ 'selector-set' => "propertySelectors", objects: "supplied_properties", "active-selectors" => "activeProperties"} diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index e5a51a50cc..b40ea8255b 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -31,22 +31,22 @@ .sticky-shop-filters-container.thin-scroll-bar.hide-for-medium-down.large-2.columns %h5.filter-header = t(:products_filter_by) - %span{ng: {show: 'filtersCount()' }} + %span{ "ng-show": 'filtersCount()' } = "({{ filtersCount() }} #{t(:products_filter_selected)})" = render partial: "shop/products/filters" - .expanding-sidebar.shop-filters-sidebar.hide-for-large-up{ng: {show: 'showFilterSidebar', class: "{'shown': showFilterSidebar}"}} - .background{ng: {click: 'toggleFilterSidebar()'}} + .expanding-sidebar.shop-filters-sidebar.hide-for-large-up{ "ng-show": 'showFilterSidebar', "ng-class": "{'shown': showFilterSidebar}" } + .background{ "ng-click": 'toggleFilterSidebar()' } .sidebar %h5 = t(:products_filter_by) - %span{ng: {show: 'filtersCount()' }} + %span{ "ng-show": 'filtersCount()' } = "({{ filtersCount() }} #{t(:products_filter_selected)})" = render partial: "shop/products/filters" .sidebar-footer - %button.large.dark.left{type: 'button', ng: {click: 'clearFilters()'}} + %button.large.dark.left{ type: 'button', "ng-click": 'clearFilters()' } = t(:products_filter_clear) - %button.large.bright.right{type: 'button', ng: {click: 'toggleFilterSidebar()'}} + %button.large.bright.right{ type: 'button', "ng-click": 'toggleFilterSidebar()' } = t(:products_filter_done) diff --git a/app/views/shop/products/_search_feedback.haml b/app/views/shop/products/_search_feedback.haml index 3245c6bb50..eb71015376 100644 --- a/app/views/shop/products/_search_feedback.haml +++ b/app/views/shop/products/_search_feedback.haml @@ -10,7 +10,7 @@ %span.filter-label = t :products_results_for - %span{ ng: { hide: "!query"} } + %span{ "ng-hide": "!query" } %span.applied-search {{ query }} = render partial: 'shop/products/applied_filters_feedback' @@ -21,5 +21,5 @@ %p.no-results = t :products_no_results_html, query: "{{query}}".html_safe = render partial: 'shop/products/applied_filters_feedback' - %button.clear-search{type: 'button', ng: {click: 'clearAll()'}} + %button.clear-search{ type: 'button', "ng-click": 'clearAll()' } = t :products_clear_search diff --git a/app/views/shop/products/_searchbar.haml b/app/views/shop/products/_searchbar.haml index b7e6f98d57..d1f2b65879 100644 --- a/app/views/shop/products/_searchbar.haml +++ b/app/views/shop/products/_searchbar.haml @@ -8,11 +8,11 @@ placeholder: t(:products_search), "ng-debounce" => "200", "disable-enter-with-blur" => true} - %a.clear{type: 'button', ng: {show: 'query', click: 'clearQuery()'}, 'focus-search' => true} + %a.clear{ type: 'button', "focus-search": true, "ng-show": 'query', "ng-click": 'clearQuery()' } = image_pack_tag "icn-close.png" .hide-for-large-up - %button{type: 'button', ng: {click: 'toggleFilterSidebar()'}} + %button{ type: 'button', "ng-click": 'toggleFilterSidebar()' } = t(:products_filter_heading) - %span{ng: {show: 'filtersCount()' }} + %span{ "ng-show": 'filtersCount()' } ({{ filtersCount() }}) diff --git a/app/views/shop/products/_shop_variant_no_group_buy.html.haml b/app/views/shop/products/_shop_variant_no_group_buy.html.haml index c2f3e4131c..20fbb09901 100644 --- a/app/views/shop/products/_shop_variant_no_group_buy.html.haml +++ b/app/views/shop/products/_shop_variant_no_group_buy.html.haml @@ -1,23 +1,20 @@ = cache_with_locale do .small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::!variant.product.group_buy"} - .variant-quantity-inputs{ng: {if: "variant.line_item.quantity == 0"}} - %button.add-variant{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}} + .variant-quantity-inputs{ "ng-if": "variant.line_item.quantity == 0" } + %button.add-variant{ type: "button", "ng-click": "add(1)", "ng-disabled": "!canAdd(1)" } {{ "js.shopfront.variant.add_to_cart" | t }} - .variant-quantity-inputs{ng: {if: "variant.line_item.quantity != 0"}} - %button.variant-quantity{type: "button", ng: {click: "add(-1)", disabled: "!canAdd(-1)"}}> + .variant-quantity-inputs{ "ng-if": "variant.line_item.quantity != 0" } + %button.variant-quantity{ type: "button", "ng-click": "add(-1)", "ng-disabled": "!canAdd(-1)" }> -# U+FF0D Fullwidth Hyphen-Minus - - %input.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", - ng: {model: "variant.line_item.quantity", max: "Infinity"}}> - %button.variant-quantity{type: "button", ng: {click: "add(1)", disabled: "!canAdd(1)"}} + %input.variant-quantity{ type: "number", min: "0", max: "{{ available() }}", "ng-model": "variant.line_item.quantity", "ng-max": "Infinity" }> + %button.variant-quantity{ type: "button", "ng-click": "add(1)", "ng-disabled": "!canAdd(1)" } -# U+FF0B Fullwidth Plus Sign + - .variant-remaining-stock{ng: {if: "displayRemainingInStock()"}} + .variant-remaining-stock{ "ng-if": "displayRemainingInStock()" } {{ "js.shopfront.variant.remaining_in_stock" | t:{quantity: available()} }} - .variant-quantity-display{ng: {class: "{visible: variant.line_item.quantity}"}} + .variant-quantity-display{ "ng-class": "{visible: variant.line_item.quantity}" } {{ "js.shopfront.variant.quantity_in_cart" | t:{quantity: variant.line_item.quantity || 0} }} - %input{type: :hidden, - name: "variants[{{::variant.id}}]", - ng: {model: "variant.line_item.quantity"}} + %input{ type: :hidden, name: "variants[{{::variant.id}}]", "ng-model": "variant.line_item.quantity" } diff --git a/app/views/shop/products/_shop_variant_with_group_buy.html.haml b/app/views/shop/products/_shop_variant_with_group_buy.html.haml index fc5c6533b3..50b4812e5c 100644 --- a/app/views/shop/products/_shop_variant_with_group_buy.html.haml +++ b/app/views/shop/products/_shop_variant_with_group_buy.html.haml @@ -1,18 +1,14 @@ = cache_with_locale do .small-5.medium-3.large-3.columns.variant-quantity-column.text-right{"ng-if" => "::variant.product.group_buy"} - %button.add-variant{type: "button", ng: {if: "!variant.line_item.quantity", click: "addBulk(1)", disabled: "!canAdd(1)"}} + %button.add-variant{ type: "button", "ng-if": "!variant.line_item.quantity", "ng-click": "addBulk(1)", "ng-disabled": "!canAdd(1)" } {{ "js.shopfront.variant.add_to_cart" | t }} - %button.bulk-buy.variant-quantity{type: "button", ng: {if: "variant.line_item.quantity", click: "addBulk(0)"}}> + %button.bulk-buy.variant-quantity{ type: "button", "ng-if": "variant.line_item.quantity", "ng-click": "addBulk(0)" }> {{ variant.line_item.quantity }} - %button.bulk-buy.variant-quantity{type: "button", ng: {if: "variant.line_item.quantity", click: "addBulk(0)"}} + %button.bulk-buy.variant-quantity{ type: "button", "ng-if": "variant.line_item.quantity", "ng-click": "addBulk(0)" } {{ variant.line_item.max_quantity || "-" }} %br - .variant-quantity-display{ng: {class: "{visible: variant.line_item.quantity}"}} + .variant-quantity-display{ "ng-class": "{visible: variant.line_item.quantity}" } {{ "js.shopfront.variant.in_cart" | t }} - %input{type: :hidden, - name: "variants[{{::variant.id}}]", - ng: {model: "variant.line_item.quantity"}} - %input{type: :hidden, - name: "variants[{{::variant.id}}]", - ng: {model: "variant.line_item.max_quantity"}} + %input{ type: :hidden, name: "variants[{{::variant.id}}]", "ng-model": "variant.line_item.quantity" } + %input{ type: :hidden, name: "variants[{{::variant.id}}]", "ng-model": "variant.line_item.max_quantity" } diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index da625de3ee..8163d6e903 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -10,7 +10,7 @@ %h3 %a{"ng-click" => "triggerProductModal()", href: 'javascript:void(0)'} %span{"ng-bind" => "::product.name"} - .product-description{ng: {"bind-html": "::product.description_html", click: "triggerProductModal()", show: "product.description_html.length"}, "data-controller": "add-blank-to-link"} + .product-description{ "data-controller": "add-blank-to-link", "ng-bind-html": "::product.description_html", "ng-click": "triggerProductModal()", "ng-show": "product.description_html.length" } %div{ "ng-switch" => "enterprise.visible" } .product-producer = t :products_from diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 8497416f93..92d288a9f9 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -1,16 +1,16 @@ - if (@order&.distributor || current_distributor) == current_distributor - #shop-tabs{"data-controller": "tabs-and-panels shop-tabs", "data-tabs-and-panels-class-name-value": "selected"} + #shop-tabs{"data-controller": "tabs-and-panels", "data-action": "orderCycleSelected@window->tabs-and-panels#activateShopPanel", "data-tabs-and-panels-class-name-value": "selected"} .tab-buttons .flex.row .columns.small-12.large-8 - shop_tabs.each do |tab| .page - %a{ id: tab[:name], href: "##{tab[:name]}_panel", data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: ("selected" if tab[:default]) }=tab[:title] + %a{ href: "##{tab[:name]}_panel", data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab" }, class: ("selected" if tab[:default]) }=tab[:title] .columns.large-4.show-for-large-up = render partial: "shopping_shared/order_cycles" - shop_tabs.each do |tab| - %div{id: "#{tab[:name]}_panel", "data-tabs-and-panels-target": "panel #{'default' if tab[:default]}" } + %div{id: "#{tab[:name]}_panel", "data-tabs-and-panels-target": "panel #{'default' if tab[:default]} #{'shop' if tab[:shop]}" } .page-view - if tab[:custom] = render "shopping_shared/tabs/custom" diff --git a/app/views/shops/_hubs.html.haml b/app/views/shops/_hubs.html.haml index 23410b2505..5b9e1d3792 100644 --- a/app/views/shops/_hubs.html.haml +++ b/app/views/shops/_hubs.html.haml @@ -27,12 +27,12 @@ %a{href: "", "ng-click" => "showDistanceMatches()"} = t :hubs_distance_filter, location: "{{ nameMatchesFiltered[0].name }}" .more-controls - %span{ng: {show: "closed_shops_loading", cloak: true}} + %span{ "ng-show": "closed_shops_loading", "ng-cloak": true } = render partial: "components/spinner" - %span{ng: {if: "!show_closed", cloak: true}} - %a.button{href: "", ng: {click: "showClosedShops()"}} + %span{ "ng-if": "!show_closed", "ng-cloak": true } + %a.button{ href: "", "ng-click": "showClosedShops()" } = t '.show_closed_shops' - %span{ng: {if: "show_closed", cloak: true}} - %a.button{href: "", ng: {click: "hideClosedShops()"}} + %span{ "ng-if": "show_closed", "ng-cloak": true } + %a.button{ href: "", "ng-click": "hideClosedShops()" } = t '.hide_closed_shops' %a.button{href: main_app.map_path}= t '.show_on_map' diff --git a/app/views/shops/_skinny.html.haml b/app/views/shops/_skinny.html.haml index 33a2106fd5..2599bd7881 100644 --- a/app/views/shops/_skinny.html.haml +++ b/app/views/shops/_skinny.html.haml @@ -2,7 +2,7 @@ .row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}"} .columns.small-12.medium-5.large-5.skinny-head %a.hub{"ng-href" => "{{::hub.path}}", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub", "data-is-link" => "true"} - %i{ng: {class: "::hub.icon_font"}} + %i{ "ng-class": "::hub.icon_font" } %span.margin-top.hub-name-listing{"ng-bind" => "::hub.name | truncate:40"} .columns.small-4.medium-2.large-2 @@ -13,9 +13,9 @@ .columns.small-5.medium-3.large-3.text-right.no-wrap.flex.flex-align-center.flex-justify-end{"ng-if" => "::hub.active"} %a.hub.open_closed.flex.flex-align-center{"ng-href" => "{{::hub.path}}", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"} - %span{ ng: { if: "::current()" } } + %span{ "ng-if": "::current()" } %em= t :hubs_shopping_here - %span{ ng: { if: "::!current()" } } + %span{ "ng-if": "::!current()" } %span{"ng-bind" => "::hub.orders_close_at | sensible_timeframe"} %i.ofn-i_068-shop-reversed.show-for-medium-up %span{style: "margin-left: 0.5rem;"} @@ -23,9 +23,9 @@ .columns.small-5.medium-3.large-3.text-right.no-wrap.flex.flex-align-center.flex-justify-end{"ng-if" => "::!hub.active"} %a.hub.open_closed.flex{"ng-href" => "{{::hub.path}}", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"} - %span{ ng: { if: "::current()" } } + %span{ "ng-if": "::current()" } %em= t :hubs_shopping_here - %span{ ng: { if: "::!current()" } } + %span{ "ng-if": "::!current()" } = t :hubs_orders_closed %i.ofn-i_068-shop-reversed.show-for-medium-up %span{style: "margin-left: 0.5rem;"} @@ -34,7 +34,7 @@ .row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed"} .columns.small-12.medium-6.large-5.skinny-head %a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}"} - %i{ng: {class: "hub.icon_font"}} + %i{ "ng-class": "hub.icon_font" } %span.hub-name-listing{"ng-bind" => "::hub.name | truncate:40"} .columns.small-4.medium-2.large-2 @@ -43,5 +43,5 @@ %span.ellipsed{"ng-bind" => "::hub.address.state_name"} .columns.small-6.medium-3.large-4.text-right.no-wrap.flex.flex-align-center.flex-justify-end - %span{ ng: { if: "::!current()" } } + %span{ "ng-if": "::!current()" } %em= t :hubs_profile_only diff --git a/app/views/spree/admin/invoices/_invoices_table.html.haml b/app/views/spree/admin/invoices/_invoices_table.html.haml index 4c876319cd..d1b6c114eb 100644 --- a/app/views/spree/admin/invoices/_invoices_table.html.haml +++ b/app/views/spree/admin/invoices/_invoices_table.html.haml @@ -14,7 +14,7 @@ %td.align-center.created_at = invoice.presenter.display_date %td.align-center.label - = invoice.number + = invoice.display_number %td.align-center.label = invoice.presenter.total %td.align-center.label diff --git a/app/views/spree/admin/invoices/index.html.haml b/app/views/spree/admin/invoices/index.html.haml index c97e7bed1b..68a3ac356a 100644 --- a/app/views/spree/admin/invoices/index.html.haml +++ b/app/views/spree/admin/invoices/index.html.haml @@ -1,3 +1,7 @@ +- if show_generate_invoice_button?(@order) + .alert-box.warning + = t('.order_has_changed') + = render partial: 'spree/admin/shared/order_page_title' = render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Invoices' } @@ -7,7 +11,7 @@ - content_for :page_actions do - if show_generate_invoice_button?(@order) - %li= button_link_to t(:create_or_update_invoice), generate_admin_order_invoices_path(@order), :icon => 'icon-plus', data: { method: 'post' } + %li= generate_invoice_button(@order) = render partial: 'spree/admin/shared/order_links' %li= button_link_to t(:back_to_orders_list), admin_orders_path, :icon => 'icon-arrow-left' diff --git a/app/views/spree/admin/orders/_bulk_actions.html.haml b/app/views/spree/admin/orders/_bulk_actions.html.haml index 7616334c1f..74c032ed55 100644 --- a/app/views/spree/admin/orders/_bulk_actions.html.haml +++ b/app/views/spree/admin/orders/_bulk_actions.html.haml @@ -3,23 +3,22 @@ %span{ "data-controller": "checked-feedback", "data-checked-feedback-translation-value": "spree.admin.orders.index.selected" } = t("spree.admin.orders.index.selected", count: 0) - %div.plain.ofn-drop-down.disabled{ "data-checked-target": "disable", "data-controller": "dropdown", "data-action": "click->dropdown#toggle" } - %span{ class: 'icon-reorder' } - ="#{t('admin.actions')}".html_safe - %span - %i{ "data-dropdown-target": "arrow", "data-expanded-class": "icon-caret-up", "data-collapsed-class": "icon-caret-down" } - - %div.menu{ "data-dropdown-target": "menu" } - %div.menu_item - %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" } - = t('spree.admin.orders.index.resend_confirmation') - - if Spree::Config[:enable_invoices?] + %div.plain.ofn-drop-down.disabled{ "data-checked-target": "disable" } + %details{"data-controller": "dropdown"} + %summary + %span.icon-reorder + ="#{t('admin.actions')}".html_safe + %div.menu{"data-action": "click->dropdown#closeOnMenu"} %div.menu_item - %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" } - = t('spree.admin.orders.index.send_invoice') + %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" } + = t('spree.admin.orders.index.resend_confirmation') + - if Spree::Config[:enable_invoices?] + %div.menu_item + %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" } + = t('spree.admin.orders.index.send_invoice') + %div.menu_item + %span.name{ "data-controller": "bulk-actions", "data-action": "click->bulk-actions#perform", "data-bulk-actions-reflex-value": "Admin::Orders#bulk_invoice" } + = t('spree.admin.orders.index.print_invoices') %div.menu_item - %span.name{ "data-controller": "bulk-actions", "data-action": "click->bulk-actions#perform", "data-bulk-actions-reflex-value": "Admin::Orders#bulk_invoice" } - = t('spree.admin.orders.index.print_invoices') - %div.menu_item - %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" } - = t('spree.admin.orders.index.cancel_orders') + %span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" } + = t('spree.admin.orders.index.cancel_orders') diff --git a/app/views/spree/admin/orders/_invoice/_line_item_name.html.haml b/app/views/spree/admin/orders/_invoice/_line_item_name.html.haml index 015f48a54d..37dc28e3a1 100644 --- a/app/views/spree/admin/orders/_invoice/_line_item_name.html.haml +++ b/app/views/spree/admin/orders/_invoice/_line_item_name.html.haml @@ -1,6 +1,6 @@ %h5.inline-header - = "#{raw(line_item.variant.product.name)}" + = line_item.variant.product.name - unless line_item.variant.product.name.include? line_item.name_to_display - %span= "- #{raw(line_item.name_to_display)}" + %span= "- #{line_item.name_to_display}" - if line_item.unit_price_price_and_unit - = raw("(#{line_item.unit_price_price_and_unit})") \ No newline at end of file + = raw("(#{line_item.unit_price_price_and_unit})") diff --git a/app/views/spree/admin/orders/_invoice_table.html.haml b/app/views/spree/admin/orders/_invoice_table.html.haml index 7ed12adca1..04ac30e81a 100644 --- a/app/views/spree/admin/orders/_invoice_table.html.haml +++ b/app/views/spree/admin/orders/_invoice_table.html.haml @@ -16,7 +16,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= raw(item.variant.product.supplier.name) + %em= item.variant.product.supplier.name %td{:align => "right"} = item.quantity %td{:align => "right"} @@ -28,7 +28,7 @@ - taxable = adjustment.adjustable_type == "Spree::Shipment" ? adjustment.adjustable : adjustment %tr %td - %strong= "#{raw(adjustment.label)}" + %strong= adjustment.label %td{:align => "right"} 1 %td{:align => "right"} diff --git a/app/views/spree/admin/orders/_invoice_table2.html.haml b/app/views/spree/admin/orders/_invoice_table2.html.haml index ff24582867..789737bdad 100644 --- a/app/views/spree/admin/orders/_invoice_table2.html.haml +++ b/app/views/spree/admin/orders/_invoice_table2.html.haml @@ -19,7 +19,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= raw(item.variant.product.supplier.name) + %em= item.variant.product.supplier.name %td{:align => "right"} = item.quantity %td{:align => "right"} @@ -33,7 +33,7 @@ - checkout_adjustments_for(@order, exclude: [:line_item]).reverse_each do |adjustment| %tr %td - %strong= "#{raw(adjustment.label)}" + %strong= adjustment.label %td{:align => "right"} %td{:align => "right"} %td{:align => "right"} diff --git a/app/views/spree/admin/orders/_invoice_table4.html.haml b/app/views/spree/admin/orders/_invoice_table4.html.haml index ffd413a4c9..5382d3d8ce 100644 --- a/app/views/spree/admin/orders/_invoice_table4.html.haml +++ b/app/views/spree/admin/orders/_invoice_table4.html.haml @@ -22,7 +22,7 @@ = render 'spree/admin/orders/_invoice/line_item_name', line_item: item %br %small - %em= raw(item.variant.product.supplier.name) + %em= item.variant.product.supplier.name %td{:align => "right"} = item.quantity %td{:align => "right"} @@ -32,13 +32,13 @@ %td{:align => "right"} = item.display_amount_with_adjustments_without_taxes %td{:align => "right"} - = item.display_line_item_tax_rates + = @order.display_line_item_tax_rate(item) %td{:align => "right"} = item.display_amount_with_adjustments_and_with_taxes %tr %td - %strong= "#{t(:shipping)} " - = "( #{t(:invoice_shipping_type)} #{raw(@order.shipping_method.name)} )" + %strong= "#{@order.shipping_method.category} " + = "(#{@order.shipping_method.name})" %td{:align => "right"} %td{:align => "right"} %td{:align => "right"} @@ -51,7 +51,7 @@ - @order.checkout_adjustments(exclude: [:line_item, :shipment]).reverse_each do |adjustment| %tr %td - %strong= "#{raw(adjustment.label)}" + %strong= adjustment.label %td{:align => "right"} %td{:align => "right"} %td{:align => "right"} diff --git a/app/views/spree/admin/orders/_shipment.html.haml b/app/views/spree/admin/orders/_shipment.html.haml index f28ef2740f..599900c295 100644 --- a/app/views/spree/admin/orders/_shipment.html.haml +++ b/app/views/spree/admin/orders/_shipment.html.haml @@ -8,7 +8,10 @@ = Spree.t("shipment_states.#{shipment.state}") - if shipment.ready? and can? :update, shipment = "-" - = link_to t(:ship), '#', :class => 'ship button icon-arrow-right', :data => { 'shipment-number' => shipment.number } + %button{"class": "ship button icon-arrow-right","data-controller": "modal-link", + "data-action": "click->modal-link#open", "data-modal-link-target-value": dom_id(order, :ship)}= t(:ship) + %form + = render ShipOrderComponent.new(order: order) %table.stock-contents.index %colgroup diff --git a/app/views/spree/admin/orders/_table_row.html.haml b/app/views/spree/admin/orders/_table_row.html.haml index 9c0ffb5e17..cb0b579391 100644 --- a/app/views/spree/admin/orders/_table_row.html.haml +++ b/app/views/spree/admin/orders/_table_row.html.haml @@ -47,6 +47,9 @@ %i.success.icon-ok-sign{"data-controller": "ephemeral"} = render partial: 'admin/shared/tooltip', locals: {link_class: "icon_link with-tip icon-edit no-text" ,link: edit_admin_order_path(order), link_text: "", tooltip_text: t('spree.admin.orders.index.edit')} - if order.ready_to_ship? - = render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-road icon_link with-tip no-text", button_reflex: "click->Admin::OrdersReflex#ship", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.ship')} + %form + = render ShipOrderComponent.new(order: order) + = render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-road icon_link with-tip no-text", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.ship'), shipment: true} + - if order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any? = render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-capture icon_link no-text", button_reflex: "click->Admin::OrdersReflex#capture", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.capture')} diff --git a/app/views/spree/admin/orders/bulk/_invoice_modal.html.haml b/app/views/spree/admin/orders/bulk/_invoice_modal.html.haml index 07c2f30121..53f7982f25 100644 --- a/app/views/spree/admin/orders/bulk/_invoice_modal.html.haml +++ b/app/views/spree/admin/orders/bulk/_invoice_modal.html.haml @@ -1,6 +1,6 @@ %div{ id: "bulk_invoices_modal", "data-controller": "modal", "data-modal-instant-value": true, "data-action": "keyup@document->modal#closeIfEscapeKey" } .reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#remove" } - .reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" } + .reveal-modal.fade.tiny.modal-component{ "data-modal-target": "modal" } %div.fullwidth.align-center %h4.modal-title = t('js.admin.orders.index.compiling_invoices') diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 4216df3732..15053c326f 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -12,33 +12,33 @@ = admin_inject_column_preferences module: 'admin.lineItems' = admin_inject_available_units -%div{ ng: { controller: 'LineItemsCtrl' }, id: "table-filter" } +%div{ id: "table-filter", "ng-controller": 'LineItemsCtrl' } %fieldset %save-bar{ dirty: "bulk_order_form.$dirty", persist: "false" } - %input.red{ type: "button", value: "Save Changes", ng: { click: "submit()", disabled: "!bulk_order_form.$dirty" } } + %input.red{ type: "button", value: "Save Changes", "ng-click": "submit()", "ng-disabled": "!bulk_order_form.$dirty" } %legend{ align: 'center'}= t(:search) %div{ :class => "sixteen columns alpha" } .quick_search.three.columns.alpha %label{ for: 'quick_filter' } %br - %input.quick-search.fullwidth{ ng: {model: 'query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchResults()" } + %input.quick-search.fullwidth{ name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress": "$event.keyCode === 13 && fetchResults()", "ng-model": 'query' } .one.columns   .filter_select{ :class => "three columns" } %label{ :for => 'supplier_filter' } = t("admin.producer") %br - %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter' } } + %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', "min-search": 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", "on-selecting": "confirmRefresh", "ng-model": 'supplierFilter' } .filter_select{ :class => "three columns" } %label{ :for => 'distributor_filter' } = t("admin.shop") %br - %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter' } } + %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', "min-search": 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", "on-selecting": "confirmRefresh", "ng-model": 'distributorFilter' } .filter_select{ :class => "three columns" } %label{ :for => 'order_cycle_filter' } = t("admin.order_cycle") %br - %input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter' } } + %input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', "min-search": 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", "on-selecting": "confirmRefresh", "ng-model": 'orderCycleFilter' } .date_filter{class: "three columns"} %label = t("date_range") @@ -55,9 +55,9 @@ %a.button{'ng-click' => 'resetSelectFilters()', "id": "clear_filters_button" } = t(:clear_filters) - %hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'unitsVariantSelected()' } } + %hr.divider.sixteen.columns.alpha.omega{ "ng-show": 'unitsVariantSelected()' } - %div.sixteen.columns.alpha.omega#group_buy_calculation{ ng: { show: 'unitsVariantSelected()', cloak: true } } + %div.sixteen.columns.alpha.omega#group_buy_calculation{ "ng-show": 'unitsVariantSelected()', "ng-cloak": true } .one.columns.alpha .shared_resource.four.columns.alpha %input{ type: 'checkbox', :id => 'shared_resource', 'ng-model' => 'sharedResource'} @@ -102,7 +102,7 @@ %hr.divider.sixteen.columns.alpha.omega .clear - %div{ ng: { hide: 'RequestMonitor.loading || line_items.length == 0' }, style: "display: flex; justify-content: flex-start; column-gap: 10px; margin-bottom: 15px" } + %div{ style: "display: flex; justify-content: flex-start; column-gap: 10px; margin-bottom: 15px", "ng-hide": 'RequestMonitor.loading || line_items.length == 0' } -# This -20px is a hack to make the dropdowns align properly %div{ style: "margin-right: -20px;" } = render 'admin/shared/bulk_actions_dropdown' @@ -118,15 +118,15 @@ %h1 = t("admin.orders.bulk_management.loading") - %div{ class: "sixteen columns alpha", ng: { show: '!RequestMonitor.loading && filteredLineItems.length == 0', cloak: true } } + %div{ class: "sixteen columns alpha", "ng-show": '!RequestMonitor.loading && filteredLineItems.length == 0', "ng-cloak": true } %h1#no_results = t("admin.orders.bulk_management.no_results") - .margin-bottom-50{ ng: { hide: 'RequestMonitor.loading || filteredLineItems.length == 0', cloak: true } } + .margin-bottom-50{ "ng-hide": 'RequestMonitor.loading || filteredLineItems.length == 0', "ng-cloak": true } %form{ name: 'bulk_order_form' } - %table.index#listing_orders.bulk{ :class => "sixteen columns alpha", ng: { show: "initialized" } } + %table.index#listing_orders.bulk{ class: "sixteen columns alpha", "ng-show": "initialized" } %thead - %tr{ ng: { controller: "ColumnsCtrl" } } + %tr{ "ng-controller": "ColumnsCtrl" } %th.bulk %input{ :type => "checkbox", :name => 'toggle_bulk', 'ng-click' => 'toggleAllCheckboxes()', 'ng-checked' => "allBoxesChecked()" } %th.order_no{ 'ng-show' => 'columns.order_no.visible' } @@ -166,7 +166,7 @@ = "#{t('admin.price')} (#{Spree::Money.currency_symbol})" %th.actions - %tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } } + %tr.line_item{ "ng-repeat": "line_item in filteredLineItems = ( line_items | orderBy:sorting.predicate:sorting.reverse )", "ng-class-even": "'even'", "ng-class-odd": "'odd'", "ng-attr-id": "li_{{line_item.id}}" } %td.bulk %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true } %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} @@ -180,17 +180,17 @@ %td.variant{ 'ng-show' => 'columns.variant.visible' } %a{ :href => '#', 'ng-click' => "setSelectedUnitsVariant(line_item.units_product,line_item.units_variant)" } {{ line_item.units_variant.full_name }} %td.quantity{ 'ng-show' => 'columns.quantity.visible' } - %input.show-dirty{ :type => 'number', :name => 'quantity', :id => 'quantity', ng: { model: "line_item.quantity", change: "updateOnQuantity(line_item)", required: "true", class: '{"update-error": line_item.errors.quantity}' }, min: 1, step: 1 } - %span.error{ ng: { bind: 'line_item.errors.quantity' } } + %input.show-dirty{ type: 'number', name: 'quantity', id: 'quantity', min: 1, step: 1, "ng-model": "line_item.quantity", "ng-change": "updateOnQuantity(line_item)", "ng-required": "true", "ng-class": '{"update-error": line_item.errors.quantity}' } + %span.error{ "ng-bind": 'line_item.errors.quantity' } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} %td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } - %input.show-dirty{ type: 'number', step: 'any', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[0-9]*[.]?[0-9]+/' } - %span.error{ ng: { bind: 'line_item.errors.final_weight_volume' } } + %input.show-dirty{ type: 'number', step: 'any', name: 'final_weight_volume', id: 'final_weight_volume', min: 0, "ng-pattern": '/[0-9]*[.]?[0-9]+/', "ng-model": "line_item.final_weight_volume", "ng-readonly": "unitValueLessThanZero(line_item)", "ng-change": "weightAdjustedPrice(line_item)", "ng-required": "true", "ng-class": '{"update-error": line_item.errors.final_weight_volume}' } + %span.error{ "ng-bind": 'line_item.errors.final_weight_volume' } %td.price{ 'ng-show' => 'columns.price.visible' } - %input.show-dirty{ :type => 'text', :name => 'price', :id => 'price', :ng => { value: 'line_item.price * line_item.quantity | currency:""', readonly: "true", class: '{"update-error": line_item.errors.price}' } } - %span.error{ ng: { bind: 'line_item.errors.price' } } + %input.show-dirty{ type: 'text', name: 'price', id: 'price', "ng-value": 'line_item.price * line_item.quantity | currency:""', "ng-readonly": "true", "ng-class": '{"update-error": line_item.errors.price}' } + %span.error{ "ng-bind": 'line_item.errors.price' } %td.actions - %a{ ng: { href: "/admin/orders/{{line_item.order.number}}/edit" }, :class => "edit-order icon-edit no-text", 'confirm-link-click' => 'confirmRefresh()' } + %a{ class: "edit-order icon-edit no-text", "confirm-link-click": 'confirmRefresh()', "ng-href": "/admin/orders/{{line_item.order.number}}/edit" } %td.actions %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } diff --git a/app/views/spree/admin/orders/invoice4.html.haml b/app/views/spree/admin/orders/invoice4.html.haml index e65e7c10b9..bf7eee3679 100644 --- a/app/views/spree/admin/orders/invoice4.html.haml +++ b/app/views/spree/admin/orders/invoice4.html.haml @@ -45,7 +45,9 @@ %td{ :align => "left" } %br = "#{t :invoice_number}:" - = @order.invoice_number + = @order.display_number + - if @order.previous_invoice.present? + = "#{t :invoice_cancel_and_replace_invoice} #{ @order.previous_invoice.display_number}" %br = t :invoice_issued_on = l @order.invoice_date diff --git a/app/views/spree/admin/overview/_enterprises.html.haml b/app/views/spree/admin/overview/_enterprises.html.haml index e022623928..bef20ba29c 100644 --- a/app/views/spree/admin/overview/_enterprises.html.haml +++ b/app/views/spree/admin/overview/_enterprises.html.haml @@ -2,12 +2,12 @@ = render 'enterprises_header' - if @enterprises.empty? - %div.sixteen.columns.alpha.list-item.red + %div.sixteen.columns.alpha.list-item.warning %span.text.fifteen.columns.alpha = t "spree_admin_enterprises_none_text" %span.one.columns.omega %span.icon-remove-sign - %a.sixteen.columns.alpha.button.bottom.red{ href: "#{main_app.new_admin_enterprise_path}" } + %a.sixteen.columns.alpha.button.bottom.warning{ href: "#{main_app.new_admin_enterprise_path}" } = t "spree_admin_enterprises_none_create_a_new_enterprise" %span.icon-arrow-right @@ -27,6 +27,6 @@ %div.sixteen.columns.alpha.list = render partial: 'enterprise_row', collection: @enterprises, as: :enterprise - %a.sixteen.columns.alpha.button.bottom.blue{ href: "#{main_app.admin_enterprises_path}" } + %a.sixteen.columns.alpha.button.bottom{ href: "#{main_app.admin_enterprises_path}" } = t "spree_admin_overview_enterprises_footer" %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/_enterprises_header.html.haml b/app/views/spree/admin/overview/_enterprises_header.html.haml index a189fe40d6..760043dcfa 100644 --- a/app/views/spree/admin/overview/_enterprises_header.html.haml +++ b/app/views/spree/admin/overview/_enterprises_header.html.haml @@ -1,9 +1,9 @@ -%div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "red"}"} +%div.header.sixteen.columns.alpha{ :class => "#{@enterprises.count > 0 ? "" : "warning"}"} %h3.thirteen.columns.alpha = t "spree_admin_overview_enterprises_header" - if @enterprises.any? - if spree_current_user.can_own_more_enterprises? - %a.three.columns.omega.icon-plus.button.blue.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } + %a.three.columns.omega.icon-plus.button.white-bottom{ href: "#{main_app.new_admin_enterprise_path}" } = t "spree_admin_enterprises_create_new" - else %a{ "ofn-with-tip" => t('.ofn_with_tip') } diff --git a/app/views/spree/admin/overview/_order_cycles.html.haml b/app/views/spree/admin/overview/_order_cycles.html.haml index 3d26daa37f..642f28ce33 100644 --- a/app/views/spree/admin/overview/_order_cycles.html.haml +++ b/app/views/spree/admin/overview/_order_cycles.html.haml @@ -1,11 +1,11 @@ -- color_class = @order_cycle_count > 0 ? "blue" : "orange" -- icon_class = @order_cycle_count > 0 ? "icon-ok-sign" : "icon-warning-sign" +- color_class = "warning" unless @order_cycle_count.positive? +- icon_class = @order_cycle_count.positive? ? "icon-ok-sign" : "icon-warning-sign" %div.dashboard_item.seven.columns.omega#order_cycles %div.header.sixteen.columns.alpha{class: color_class} %h3.ten.columns.alpha = t ".order_cycles" - - if @order_cycle_count > 0 - %a.six.columns.omega.icon-plus.button.blue{ href: main_app.new_admin_order_cycle_path } + - if @order_cycle_count.positive? + %a.six.columns.omega.icon-plus.button{ href: main_app.new_admin_order_cycle_path } = t "spree_admin_enterprises_create_new" - else %a{ "ofn-with-tip" => t(".order_cycles_tip") } @@ -13,9 +13,9 @@ %div.sixteen.columns.alpha.list %div.sixteen.columns.alpha.list-item{class: color_class} %span.thirteen.columns.alpha - = t('.you_have_active', count: @order_cycle_count) + = t(".you_have_active", count: @order_cycle_count) %span.three.columns.omega %span{class: icon_class} - %a.sixteen.columns.alpha.button.bottom{ href: main_app.admin_order_cycles_path, class: color_class } - = t ".manage_order_cycles" - %span.icon-arrow-right + %a.sixteen.columns.alpha.button.bottom{ href: main_app.admin_order_cycles_path, class: color_class } + = t ".manage_order_cycles" + %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/_products.html.haml b/app/views/spree/admin/overview/_products.html.haml index c33c34c2f6..3f86329664 100644 --- a/app/views/spree/admin/overview/_products.html.haml +++ b/app/views/spree/admin/overview/_products.html.haml @@ -1,29 +1,26 @@ +- color_class = "warning" unless @product_count.positive? +- icon_class = @product_count.positive? ? "icon-ok-sign" : "icon-remove-sign" %div.dashboard_item.seven.columns.alpha#products - %div.header.sixteen.columns.alpha{ :class => "#{@product_count > 0 ? "" : "red"}"} + %div.header.sixteen.columns.alpha{class: color_class} %h3.ten.columns.alpha = t "products" - - if @product_count > 0 - %a.six.columns.omega.icon-plus.button.blue{ href: "#{new_admin_product_path}" } + - if @product_count.positive? + %a.six.columns.omega.icon-plus.button{ href: new_admin_product_path } = t "spree_admin_enterprises_create_new" - else - %a{ "ofn-with-tip" => "The products that you sell through the Open Food Network." } + %a{ "ofn-with-tip" => t(".products_tip") } = t "admin.whats_this" %div.sixteen.columns.alpha.list - - if @product_count > 0 - %div.sixteen.columns.alpha.list-item - %span.thirteen.columns.alpha - = t(".active_products", count: @product_count ) - %span.three.columns.omega - %span.icon-ok-sign - %a.sixteen.columns.alpha.button.bottom.blue{ href: "#{admin_products_path}" } - = t "spree_admin_enterprises_producers_manage_products" - %span.icon-arrow-right - - else - %div.sixteen.columns.alpha.list-item.red - %span.thirteen.columns.alpha - = t(".active_products", count: @product_count ) - %span.three.columns.omega - %span.icon-remove-sign - %a.sixteen.columns.alpha.button.bottom.red{ href: "#{new_admin_product_path}" } - = t "spree_admin_enterprises_create_new_product" - %span.icon-arrow-right + %div.sixteen.columns.alpha.list-item{class: color_class} + %span.thirteen.columns.alpha + = t(".active_products", count: @product_count) + %span.three.columns.omega + %span{class: icon_class} + - if @product_count.positive? + %a.sixteen.columns.alpha.button.bottom{ href: admin_products_path, class: color_class } + = t "spree_admin_enterprises_producers_manage_products" + %span.icon-arrow-right + - else + %a.sixteen.columns.alpha.button.bottom{ href: new_admin_product_path, class: color_class } + = t "spree_admin_enterprises_create_new_product" + %span.icon-arrow-right diff --git a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml index 4f72a14f9f..bc997ca13a 100644 --- a/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml +++ b/app/views/spree/admin/overview/multi_enterprise_dashboard.html.haml @@ -1,11 +1,10 @@ +- content_for :page_title do + = t('dashboard') + - content_for :page_actions do = render 'admin/shared/user_guide_link' - %div{ 'ng-app' => 'ofn.admin' } - %h1{ :style => 'margin-bottom: 30px' } - = t 'dashboard' - - if @enterprises.empty? = render partial: "enterprises" diff --git a/app/views/spree/admin/payment_methods/_providers.html.haml b/app/views/spree/admin/payment_methods/_providers.html.haml index b25a7b09c5..15bd4ef979 100644 --- a/app/views/spree/admin/payment_methods/_providers.html.haml +++ b/app/views/spree/admin/payment_methods/_providers.html.haml @@ -1,4 +1,4 @@ -#provider-settings{ ng: { controller: "ProvidersCtrl" } } +#provider-settings{ "ng-controller": "ProvidersCtrl" } .row .alpha.four.columns = label :payment_method, :type, t('.provider') diff --git a/app/views/spree/admin/payment_methods/_stripe_connect.html.haml b/app/views/spree/admin/payment_methods/_stripe_connect.html.haml index bc3ba014b4..28f1ad8f66 100644 --- a/app/views/spree/admin/payment_methods/_stripe_connect.html.haml +++ b/app/views/spree/admin/payment_methods/_stripe_connect.html.haml @@ -1,40 +1,37 @@ %fieldset.no-border-bottom#gateway_fields %legend{ align: "center"} = t(:provider_settings) - .preference-settings{ ng: { controller: "StripeController" } } + .preference-settings{ "ng-controller": "StripeController" } = fields_for :payment_method, @payment_method do |payment_method_form| .row .alpha.four.columns = payment_method_form.label :stripe_account_owner .omega.twelve.columns - if @stripe_account_holder.nil? || spree_current_user.enterprises.include?(@stripe_account_holder) - %input.ofn-select2.fullwidth#payment_method_preferred_enterprise_id{ type: 'number', - name: 'payment_method[preferred_enterprise_id]', - placeholder: t(".enterprise_select_placeholder"), - data: 'shops', ng: { model: 'paymentMethod.preferred_enterprise_id' } } + %input.ofn-select2.fullwidth#payment_method_preferred_enterprise_id{ type: 'number', name: 'payment_method[preferred_enterprise_id]', placeholder: t(".enterprise_select_placeholder"), data: 'shops', "ng-model": 'paymentMethod.preferred_enterprise_id' } - else %strong= Enterprise.find_by(id: @payment_method)&.name - #stripe-account-status{ ng: { show: "paymentMethod.preferred_enterprise_id" } } - .alert-box.warning{ ng: { hide: "stripe_account.status" } } + #stripe-account-status{ "ng-show": "paymentMethod.preferred_enterprise_id" } + .alert-box.warning{ "ng-hide": "stripe_account.status" } = t(".loading_account_information_msg") - .alert-box.error{ ng: { show: "stripe_account.status == 'stripe_disabled'" } } + .alert-box.error{ "ng-show": "stripe_account.status == 'stripe_disabled'" } = t(".stripe_disabled_msg") - .alert-box.error{ ng: { show: "stripe_account.status == 'request_failed'" } } + .alert-box.error{ "ng-show": "stripe_account.status == 'request_failed'" } = t(".request_failed_msg") - .alert-box.error{ ng: { show: "stripe_account.status == 'account_missing'" } } + .alert-box.error{ "ng-show": "stripe_account.status == 'account_missing'" } = t(".account_missing_msg") - %a.button{ ng: { href: "{{current_enterprise_stripe_path()}}" }, target: 'blank' } + %a.button{ target: 'blank', "ng-href": "{{current_enterprise_stripe_path()}}" } = t(".connect_one") %i.icon-chevron-right - .alert-box.error{ ng: { show: "stripe_account.status == 'access_revoked'" } } + .alert-box.error{ "ng-show": "stripe_account.status == 'access_revoked'" } = t(".access_revoked_msg") - .alert-box.ok{ ng: { show: "stripe_account.status == 'connected'" } } + .alert-box.ok{ "ng-show": "stripe_account.status == 'connected'" } .status %strong= t(".status") + ":" = t(".connected") diff --git a/app/views/spree/admin/payments/_list.html.haml b/app/views/spree/admin/payments/_list.html.haml index cbae3b95c6..975f171457 100644 --- a/app/views/spree/admin/payments/_list.html.haml +++ b/app/views/spree/admin/payments/_list.html.haml @@ -19,4 +19,4 @@ %td.actions - payment.actions.each do |action| = link_to_with_icon "icon-#{action}", Spree.t(action), fire_admin_order_payment_path(@order, payment, e: action), - no_text: true, data: { method: :put, "ujs-navigate": "false", action: action, disable_with: "" } + no_text: true, data: { method: :put, action: action, disable_with: "" } diff --git a/app/views/spree/admin/products/index.html.haml b/app/views/spree/admin/products/index.html.haml index b7c4a3853a..b78c73cb4f 100644 --- a/app/views/spree/admin/products/index.html.haml +++ b/app/views/spree/admin/products/index.html.haml @@ -2,7 +2,7 @@ = render 'spree/admin/products/index/data' = admin_inject_available_units -%div{ ng: { app: 'ofn.admin', controller: 'AdminProductEditCtrl', init: 'initialise()' } } +%div{ "ng-app": 'ofn.admin', "ng-controller": 'AdminProductEditCtrl', "ng-init": 'initialise()' } = render 'spree/admin/products/index/filters' %div{ 'ng-cloak' => true } diff --git a/app/views/spree/admin/products/index/_filters.html.haml b/app/views/spree/admin/products/index/_filters.html.haml index 98e186c7f1..57615eb09e 100644 --- a/app/views/spree/admin/products/index/_filters.html.haml +++ b/app/views/spree/admin/products/index/_filters.html.haml @@ -5,20 +5,20 @@ .quick_search.three.columns.alpha %label{ for: 'quick_filter' } %br - %input.quick-search.fullwidth{ ng: {model: 'q.query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchProducts()" } + %input.quick-search.fullwidth{ name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress": "$event.keyCode === 13 && fetchProducts()", "ng-model": 'q.query' } .one.columns   .filter_select.three.columns %label{ for: 'producer_filter' }= t 'producer' %br - %select.fullwidth{ id: 'producer_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.producerFilter', options: 'producer.id as producer.name for producer in producers'} } + %select.fullwidth{ id: 'producer_filter', "ofn-select2-min-search": 5, "ng-model": 'q.producerFilter', "ng-options": 'producer.id as producer.name for producer in producers' } .filter_select.three.columns %label{ for: 'category_filter' }= t 'category' %br - %select.fullwidth{ id: 'category_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.categoryFilter', options: 'taxon.id as taxon.name for taxon in taxons'} } + %select.fullwidth{ id: 'category_filter', "ofn-select2-min-search": 5, "ng-model": 'q.categoryFilter', "ng-options": 'taxon.id as taxon.name for taxon in taxons' } .filter_select.three.columns %label{ for: 'import_filter' }= t 'import_date' %br - %select.fullwidth{ id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.importDateFilter', init: "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}", options: 'date.id as date.name for date in importDates'} } + %select.fullwidth{ id: 'import_date_filter', "ofn-select2-min-search": 5, "ng-model": 'q.importDateFilter', "ng-init": "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}", "ng-options": 'date.id as date.name for date in importDates' } .filter_clear.three.columns.omega %label{ for: 'clear_all_filters' } diff --git a/app/views/spree/admin/products/index/_products.html.haml b/app/views/spree/admin/products/index/_products.html.haml index e0c6d76a2b..58b1c4133e 100644 --- a/app/views/spree/admin/products/index/_products.html.haml +++ b/app/views/spree/admin/products/index/_products.html.haml @@ -1,7 +1,7 @@ %div.sixteen.columns.alpha{ 'ng-hide' => 'RequestMonitor.loading || products.length == 0' } %form{ name: 'bulk_product_form', autocomplete: "off" } %save-bar{ dirty: "bulk_product_form.$dirty", persist: "false" } - %input.red{ type: "button", value: t(:save_changes), ng: { click: "submitProducts()", disabled: "!bulk_product_form.$dirty" } } + %input.red{ type: "button", value: t(:save_changes), "ng-click": "submitProducts()", "ng-disabled": "!bulk_product_form.$dirty" } %input{ type: "button", value: t(:close), 'ng-click' => "cancel('#{admin_products_path}')" } %table.index#listing_products.bulk diff --git a/app/views/spree/admin/products/index/_products_head.html.haml b/app/views/spree/admin/products/index/_products_head.html.haml index 09de5a1114..9e6d7e7065 100644 --- a/app/views/spree/admin/products/index/_products_head.html.haml +++ b/app/views/spree/admin/products/index/_products_head.html.haml @@ -1,24 +1,24 @@ %colgroup %col.actions - %col.image{ ng: { show: 'columns.image.visible' } } - %col.producer{ ng: { show: 'columns.producer.visible' } } - %col.sku{ ng: { show: 'columns.sku.visible' } } - %col.name{ ng: { show: 'columns.name.visible' } } - %col.unit{ ng: { show: 'columns.unit.visible' } } - %col.display_as{ ng: { show: 'columns.unit.visible' } } - %col.price{ ng: { show: 'columns.price.visible'} } - %col.on_hand{ ng: { show: 'columns.on_hand.visible' } } - %col.on_demand{ ng: { show: 'columns.on_demand.visible' } } - %col.category{ ng: { show: 'columns.category.visible' } } - %col.tax_category{ ng: { show: 'columns.tax_category.visible' } } - %col.inherits_properties{ ng: { show: 'columns.inherits_properties.visible' } } - %col.import_date{ ng: { show: 'columns.import_date.visible' } } + %col.image{ "ng-show": 'columns.image.visible' } + %col.producer{ "ng-show": 'columns.producer.visible' } + %col.sku{ "ng-show": 'columns.sku.visible' } + %col.name{ "ng-show": 'columns.name.visible' } + %col.unit{ "ng-show": 'columns.unit.visible' } + %col.display_as{ "ng-show": 'columns.unit.visible' } + %col.price{ "ng-show": 'columns.price.visible' } + %col.on_hand{ "ng-show": 'columns.on_hand.visible' } + %col.on_demand{ "ng-show": 'columns.on_demand.visible' } + %col.category{ "ng-show": 'columns.category.visible' } + %col.tax_category{ "ng-show": 'columns.tax_category.visible' } + %col.inherits_properties{ "ng-show": 'columns.inherits_properties.visible' } + %col.import_date{ "ng-show": 'columns.import_date.visible' } %col.actions %col.actions %col.actions %thead - %tr{ ng: { controller: "ColumnsCtrl" } } + %tr{ "ng-controller": "ColumnsCtrl" } %th.left-actions %a{ 'ng-click' => 'toggleShowAllVariants()', :style => 'color: red; cursor: pointer' } = t(:expand_all) diff --git a/app/views/spree/admin/products/index/_products_variant.html.haml b/app/views/spree/admin/products/index/_products_variant.html.haml index 45a7ea24c6..e66499f7c9 100644 --- a/app/views/spree/admin/products/index/_products_variant.html.haml +++ b/app/views/spree/admin/products/index/_products_variant.html.haml @@ -22,7 +22,7 @@ %input.field{ 'ng-model' => 'variant.on_demand', :name => 'variant_on_demand', 'ofn-track-variant' => 'on_demand', :type => 'checkbox' } %td{ 'ng-show' => 'columns.category.visible' } %td{ 'ng-show' => 'columns.tax_category.visible' } - %select.select2{ name: 'variant_tax_category_id', 'ofn-track-variant': 'tax_category_id', ng: { model: 'variant.tax_category_id', options: 'tax_category.id as tax_category.name for tax_category in tax_categories' } } + %select.select2{ name: 'variant_tax_category_id', "ofn-track-variant": 'tax_category_id', "ng-model": 'variant.tax_category_id', "ng-options": 'tax_category.id as tax_category.name for tax_category in tax_categories' } %option{ value: '' }= t(:none) %td{ 'ng-show' => 'columns.inherits_properties.visible' } %td{ 'ng-show' => 'columns.import_date.visible' } diff --git a/app/views/spree/admin/products/new.html.haml b/app/views/spree/admin/products/new.html.haml index 71710c1258..db7968a1da 100644 --- a/app/views/spree/admin/products/new.html.haml +++ b/app/views/spree/admin/products/new.html.haml @@ -54,7 +54,7 @@ %br/ = f.text_field :price, { "class": "fullwidth", "ng-model": "product.price", "ng-value": "'#{@product.price}'" } = f.error_message_on :price - .four.columns{ ng: { app: 'ofn.admin'}} + .four.columns{ "ng-app": 'ofn.admin' } = f.field_container :unit_price do %div{style: "display: flex"} = f.label :unit_price, t(".unit_price") diff --git a/app/views/spree/admin/shared/_order_links.html.haml b/app/views/spree/admin/shared/_order_links.html.haml index 6afd99bac2..9af75fffd8 100644 --- a/app/views/spree/admin/shared/_order_links.html.haml +++ b/app/views/spree/admin/shared/_order_links.html.haml @@ -1,14 +1,21 @@ %li.links-dropdown#links-dropdown - .ofn-drop-down{"data-controller": "dropdown", "data-action": "click->dropdown#toggle" } - %span - %i.icon-check - = I18n.t 'admin.actions' - %i{ "data-dropdown-target": "arrow", "data-expanded-class": "icon-caret-up", "data-collapsed-class": "icon-caret-down" } - %div.menu{"data-dropdown-target": "menu"} - - order_links(@order).each do |link| - %a.menu_item{ href: link[:url], target: link[:target] || "_self", data: { method: link[:method], "ujs-navigate": link[:method] ? "false" : "undefined", confirm: link[:confirm] } } - %span - %i{ class: link[:icon] } - %span=link[:name] + .ofn-drop-down + %details{"data-controller": "dropdown"} + %summary + %span + %i.icon-check + = I18n.t 'admin.actions' + %div.menu{"data-action": "click->dropdown#closeOnMenu"} + - order_links(@order).each do |link| + - if link[:name] == t(:ship_order) + %a.menu_item{ href: link[:url], target: link[:target] || "_self", data: { "modal-link-target-value": dom_id(@order, :ship), "action": "click->modal-link#open", "controller": "modal-link" } } + %span + %i{ class: link[:icon] } + %span=link[:name] + - else + %a.menu_item{ href: link[:url], target: link[:target] || "_self", data: { method: link[:method], "ujs-navigate": link[:method] ? "false" : "undefined", confirm: link[:confirm] } } + %span + %i{ class: link[:icon] } + %span=link[:name] = render 'spree/admin/shared/custom-confirm' diff --git a/app/views/spree/admin/shared/_product_sub_menu.html.haml b/app/views/spree/admin/shared/_product_sub_menu.html.haml index b89b16c764..6c222d49e7 100644 --- a/app/views/spree/admin/shared/_product_sub_menu.html.haml +++ b/app/views/spree/admin/shared/_product_sub_menu.html.haml @@ -1,6 +1,6 @@ - content_for :sub_menu do %ul#sub_nav.inline-menu - = tab :products + = tab :products, :products_v3 = tab :properties = tab :variant_overrides, url: main_app.admin_inventory_path, match_path: '/inventory' = tab :import, url: main_app.admin_product_import_path, match_path: '/product_import' diff --git a/app/views/spree/admin/shared/_status_message.html.haml b/app/views/spree/admin/shared/_status_message.html.haml index f9de868721..5f0ce5824b 100644 --- a/app/views/spree/admin/shared/_status_message.html.haml +++ b/app/views/spree/admin/shared/_status_message.html.haml @@ -1,2 +1,2 @@ -%h6{ id: "status-message", ng: { style: 'StatusMessage.statusMessage.style' } } +%h6{ id: "status-message", "ng-style": 'StatusMessage.statusMessage.style' } {{ StatusMessage.statusMessage.text || " " }} diff --git a/app/views/spree/admin/shared/_tabs.html.haml b/app/views/spree/admin/shared/_tabs.html.haml index ae2ad60a4d..019b45d948 100644 --- a/app/views/spree/admin/shared/_tabs.html.haml +++ b/app/views/spree/admin/shared/_tabs.html.haml @@ -1,5 +1,5 @@ = tab :overview, label: 'dashboard', url: spree.admin_dashboard_path, icon: 'icon-dashboard' -= tab :products, :properties, :inventory, :product_import, :images, :variants, :product_properties, :group_buy_options, :seo, url: admin_products_path, icon: 'icon-th-large' += tab :products, :properties, :inventory, :product_import, :images, :variants, :product_properties, :group_buy_options, :seo, :products_v3, :variant_overrides, url: admin_products_path, icon: 'icon-th-large' = tab :order_cycles, url: main_app.admin_order_cycles_path, icon: 'icon-refresh' = tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path, icon: 'icon-shopping-cart' = tab :reports, url: main_app.admin_reports_path, icon: 'icon-file' @@ -8,4 +8,4 @@ = tab :customers, url: main_app.admin_customers_path = tab :enterprise_groups, url: main_app.admin_enterprise_groups_path, label: 'groups' - if can? :admin, Spree::User - = tab(:users, url: spree.admin_users_path, icon: 'icon-user') + = tab(:users, :enterprise_roles, url: spree.admin_users_path, icon: 'icon-user') diff --git a/app/views/spree/admin/taxonomies/_form.html.haml b/app/views/spree/admin/taxonomies/_form.html.haml index 81927c7c1a..ac60030374 100644 --- a/app/views/spree/admin/taxonomies/_form.html.haml +++ b/app/views/spree/admin/taxonomies/_form.html.haml @@ -3,5 +3,5 @@ = f.label :name, t("spree.name") %span.required * %br/ - = error_message_on :taxonomy, :name, class: 'fullwidth title' + = error_message_on :taxonomy, :name = text_field :taxonomy, :name diff --git a/app/views/spree/admin/taxons/_form.html.haml b/app/views/spree/admin/taxons/_form.html.haml index a23a55be0f..621b5eadf8 100644 --- a/app/views/spree/admin/taxons/_form.html.haml +++ b/app/views/spree/admin/taxons/_form.html.haml @@ -1,31 +1,35 @@ .row .alpha.five.columns = f.field_container :name do - = f.label :name, t(:name) + = f.label :name, t(".name") %span.required * %br/ - = error_message_on :taxon, :name, class: 'fullwidth title' + = error_message_on :taxon, :name = text_field :taxon, :name, class: 'fullwidth' = f.field_container :permalink_part do - = f.label :permalink_part, t(:permalink) + = f.label :permalink_part, t(".permalink") %span.required * %br/ = @taxon.permalink.split("/")[0...-1].join("/") + "/" = text_field_tag :permalink_part, @permalink_part = f.field_container :meta_title do - = f.label :meta_title, t(:meta_title) + = f.label :meta_title, t(".meta_title") %br/ = f.text_field :meta_title, class: 'fullwidth', rows: 6 = f.field_container :meta_description do - = f.label :meta_description, t(:meta_description) + = f.label :meta_description, t(".meta_description") %br/ = f.text_field :meta_description, class: 'fullwidth', rows: 6 = f.field_container :meta_description do - = f.label :meta_keywords, t(:meta_keywords) + = f.label :meta_keywords, t(".meta_keywords") %br/ = f.text_field :meta_keywords, class: 'fullwidth', rows: 6 + = f.field_container :dfc_id do + = f.label :dfc_id, t(".dfc_id") + %br/ + = f.text_field :dfc_id, class: 'fullwidth', rows: 6 .omega.seven.columns = f.field_container :description do - = f.label :description, t(:description) + = f.label :description, t(".description") %br/ = f.text_area :description, class: 'fullwidth', rows: 6 diff --git a/app/views/spree/layouts/_admin_body.html.haml b/app/views/spree/layouts/_admin_body.html.haml index 4ca442a850..63ed3a5284 100644 --- a/app/views/spree/layouts/_admin_body.html.haml +++ b/app/views/spree/layouts/_admin_body.html.haml @@ -3,7 +3,7 @@ = yield :stripe_js #wrapper - .flash-container + #flashes = render partial: "admin/shared/flashes", locals: { flashes: flash } = render partial: "spree/layouts/admin/progress_spinner" @@ -59,6 +59,8 @@ %span= yield :sidebar_title = yield :sidebar + = render "admin/terms_of_service_banner" if tos_need_accepting? + %script = raw "Spree.api_key = \"#{spree_current_user.try(:spree_api_key).to_s}\";" diff --git a/app/views/spree/order_mailer/_order_summary.html.haml b/app/views/spree/order_mailer/_order_summary.html.haml index af0ba5433a..55e0e67cc3 100644 --- a/app/views/spree/order_mailer/_order_summary.html.haml +++ b/app/views/spree/order_mailer/_order_summary.html.haml @@ -20,7 +20,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= raw(item.variant.product.supplier.name) + %em= item.variant.product.supplier.name %td - if item.variant.sku.blank? \- @@ -43,7 +43,7 @@ - checkout_adjustments_for(@order, exclude: [:line_item]).reverse_each do |adjustment| %tr %td{align: "right", colspan: "3"} - = "#{raw(adjustment.label)}:" + = "#{adjustment.label}:" %td{align: "right"} = adjustment.display_amount %tr diff --git a/app/views/spree/orders/_bought.html.haml b/app/views/spree/orders/_bought.html.haml index f00a8b2dbe..5d80edbb05 100644 --- a/app/views/spree/orders/_bought.html.haml +++ b/app/views/spree/orders/_bought.html.haml @@ -1,18 +1,18 @@ -%tbody{ ng: { controller: 'EditBoughtOrderController' } } +%tbody{ "ng-controller": 'EditBoughtOrderController' } %tr - %td.toggle-bought{ colspan: 2, ng: { click: 'showBought=!showBought' } } + %td.toggle-bought{ colspan: 2, "ng-click": 'showBought=!showBought' } %h5.brick - %i{ ng: { class: "{ 'ofn-i_007-caret-right': !showBought, 'ofn-i_005-caret-down': showBought}"} } + %i{ "ng-class": "{ 'ofn-i_007-caret-right': !showBought, 'ofn-i_005-caret-down': showBought}" } = t(:orders_bought_items_notice, count: @order.finalised_line_items.to_a.sum(&:quantity)) %td.text-right{ colspan: 3 } - %a.edit-finalised.button.radius.expand.small{ href: changeable_orders_link_path, ng: { class: "{secondary: !showBought, primary: showBought}" } } + %a.edit-finalised.button.radius.expand.small{ href: changeable_orders_link_path, "ng-class": "{secondary: !showBought, primary: showBought}" } = t(:orders_bought_edit_button) %i.ofn-i_007-caret-right - @order.finalised_line_items.each do |line_item| - variant = line_item.variant - %tr.bought.line-item{class: "line-item-#{line_item.id} variant-#{variant.id}", ng: { show: 'showBought'} } + %tr.bought.line-item{ class: "line-item-#{line_item.id} variant-#{variant.id}", "ng-show": 'showBought' } %td.cart-item-description %div.item-thumb-image @@ -31,5 +31,5 @@ = line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil? %td.bought-item-delete.text-center - %a{ng: {click: "removeEnabled && deleteLineItem(#{line_item.id})"}} + %a{ "ng-click": "removeEnabled && deleteLineItem(#{line_item.id})" } %i.ofn-i_026-trash diff --git a/app/views/spree/orders/form/_update_buttons.html.haml b/app/views/spree/orders/form/_update_buttons.html.haml index 35b9ceaee4..be58dbbd4b 100644 --- a/app/views/spree/orders/form/_update_buttons.html.haml +++ b/app/views/spree/orders/form/_update_buttons.html.haml @@ -6,7 +6,7 @@ = t(:order_back_to_cart) - else .columns.small-12.medium-6 - = link_to "#{main_app.enterprise_shop_path(@order.distributor)}#/shop", class: "button expand" do + = link_to "#{main_app.enterprise_shop_path(@order.distributor)}#/shop_panel", class: "button expand" do = t(:order_back_to_store) .columns.small-12.medium-6 - if @order.distributor.website.present? @@ -23,5 +23,5 @@ .columns.small-12.medium-3 = button_tag :class => 'button primary expand', :id => 'update-button', "ng-disabled" => 'update_order_form.$pristine' do %i.ofn-i_051-check-big - %span{ ng: { show: 'update_order_form.$dirty' } }= t(:save_changes) - %span{ ng: { hide: 'update_order_form.$dirty' } }= t(:order_saved) + %span{ "ng-show": 'update_order_form.$dirty' }= t(:save_changes) + %span{ "ng-hide": 'update_order_form.$dirty' }= t(:order_saved) diff --git a/app/views/spree/shared/_line_item_name.html.haml b/app/views/spree/shared/_line_item_name.html.haml index 12a32772a4..8b1e75de55 100644 --- a/app/views/spree/shared/_line_item_name.html.haml +++ b/app/views/spree/shared/_line_item_name.html.haml @@ -1,6 +1,6 @@ %h5.inline-header - = "#{raw(line_item.product.name)}" + = "#{line_item.product.name}" - unless line_item.product.name.include? line_item.name_to_display - %span= "- #{raw(line_item.name_to_display)}" + %span= "- #{line_item.name_to_display}" - if line_item.options_text - = "(#{raw(line_item.options_text)})" + = "(#{line_item.options_text})" diff --git a/app/views/spree/users/_authorised_shops.html.haml b/app/views/spree/users/_authorised_shops.html.haml index 51fdacf3ef..4e83917699 100644 --- a/app/views/spree/users/_authorised_shops.html.haml +++ b/app/views/spree/users/_authorised_shops.html.haml @@ -5,13 +5,7 @@ %tr %th= t(".shop_name") %th= t(".allow_charges?") - %tr.customer{ id: "customer{{ customer.id }}", ng: { repeat: "customer in customers" } } - %td.shop{ ng: { bind: 'shopsByID[customer.enterprise_id].name' } } + %tr.customer{ id: "customer{{ customer.id }}", "ng-repeat": "customer in customers" } + %td.shop{ "ng-bind": 'shopsByID[customer.enterprise_id].name' } %td.allow_charges{ tooltip: "{{ hasOneDefaultSavedCards() ? null : \'" + t('.no_default_saved_cards_tooltip') + "\' }}" } - %input{ type: 'checkbox', - name: 'allow_charges', - ng: { model: 'customer.allow_charges', - change: 'customer.update()', - disabled: "!hasOneDefaultSavedCards()", - "true-value" => "true", - "false-value" => "false" } } + %input{ type: 'checkbox', name: 'allow_charges', "ng-model": 'customer.allow_charges', "ng-change": 'customer.update()', "ng-disabled": "!hasOneDefaultSavedCards()", "ng-true-value": "true", "ng-false-value": "false" } diff --git a/app/views/spree/users/_cards.html.haml b/app/views/spree/users/_cards.html.haml index 869b59242a..25ef6c87eb 100644 --- a/app/views/spree/users/_cards.html.haml +++ b/app/views/spree/users/_cards.html.haml @@ -7,18 +7,18 @@ %button.button.secondary.tiny.help-btn{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "saved_cards_modal" } %i.ofn-i_013-help - .saved_cards{ ng: { show: 'savedCreditCards.length > 0' } } + .saved_cards{ "ng-show": 'savedCreditCards.length > 0' } = render 'saved_cards' - .no_cards{ ng: { hide: 'savedCreditCards.length > 0' } } + .no_cards{ "ng-hide": 'savedCreditCards.length > 0' } = t(:you_have_no_saved_cards) - %button.button.primary{ ng: { click: 'showForm()', hide: 'CreditCard.visible' } } + %button.button.primary{ "ng-click": 'showForm()', "ng-hide": 'CreditCard.visible' } = t(:add_a_card) .small-12.medium-6.columns - .new_card{ ng: { show: 'CreditCard.visible', class: '{visible: CreditCard.visible}' } } + .new_card{ "ng-show": 'CreditCard.visible', "ng-class": '{visible: CreditCard.visible}' } %h3= t(:add_new_credit_card) = render 'new_card_form' - .authorised_shops{ ng: { controller: 'AuthorisedShopsCtrl', hide: 'CreditCard.visible || savedCreditCards.length == 0' } } + .authorised_shops{ "ng-controller": 'AuthorisedShopsCtrl', "ng-hide": 'CreditCard.visible || savedCreditCards.length == 0' } %h3 = t('.authorised_shops') = render 'authorised_shops' diff --git a/app/views/spree/users/_new_card_form.html.haml b/app/views/spree/users/_new_card_form.html.haml index 3d17afbf6a..72bfafd14f 100644 --- a/app/views/spree/users/_new_card_form.html.haml +++ b/app/views/spree/users/_new_card_form.html.haml @@ -4,24 +4,14 @@ %label = t(:first_name) -# Changing name not permitted by default (in checkout) - can be enabled by setting an allow_name_change variable in $scope - %input#first_name{ type: :text, - name: 'first_name', - required: true, - ng: { model: "secrets.first_name", - disabled: "!allow_name_change", - value: "order.bill_address.firstname"} } - %small.error{ ng: { show: 'new_card_form.$submitted && new_card_form.first_name.$error.required' } }= t(:error_required) + %input#first_name{ type: :text, name: 'first_name', required: true, "ng-model": "secrets.first_name", "ng-disabled": "!allow_name_change", "ng-value": "order.bill_address.firstname" } + %small.error{ "ng-show": 'new_card_form.$submitted && new_card_form.first_name.$error.required' }= t(:error_required) .small-6.columns %label = t(:last_name) - %input#last_name{type: :text, - name: "last_name", - required: true, - ng: { model: "secrets.last_name", - disabled: "!allow_name_change", - value: "order.bill_address.lastname" } } - %small.error{ ng: { show: 'new_card_form.$submitted && new_card_form.last_name.$error.required' } }= t(:error_required) + %input#last_name{ type: :text, name: "last_name", required: true, "ng-model": "secrets.last_name", "ng-disabled": "!allow_name_change", "ng-value": "order.bill_address.lastname" } + %small.error{ "ng-show": 'new_card_form.$submitted && new_card_form.last_name.$error.required' }= t(:error_required) .row .small-12.columns diff --git a/app/views/spree/users/_orders.html.haml b/app/views/spree/users/_orders.html.haml index a8c34b22f1..db548000ee 100644 --- a/app/views/spree/users/_orders.html.haml +++ b/app/views/spree/users/_orders.html.haml @@ -1,10 +1,10 @@ %script{ type: "text/ng-template", id: "account/orders.html" } .orders{"ng-controller" => "OrdersCtrl", "ng-cloak" => true} - .my-open-orders{ ng: { show: 'Orders.changeable.length > 0' } } + .my-open-orders{ "ng-show": 'Orders.changeable.length > 0' } %h3= t('.open_orders') = render 'open_orders' - .past-orders{ ng: { show: 'pastOrders.length > 0' } } + .past-orders{ "ng-show": 'pastOrders.length > 0' } %h3= t('.past_orders') = render 'past_orders' .message{"ng-if" => "Orders.all.length == 0", "ng-bind" => "::'you_have_no_orders_yet' | t"} diff --git a/app/views/spree/users/_saved_cards.html.haml b/app/views/spree/users/_saved_cards.html.haml index 853642762b..035d0849b1 100644 --- a/app/views/spree/users/_saved_cards.html.haml +++ b/app/views/spree/users/_saved_cards.html.haml @@ -5,12 +5,12 @@ %th= t(:card_expiry_date) %th= t('.default?') %th= t('.delete?') - %tr.card{ id: "card{{ card.id }}", ng: { repeat: "card in savedCreditCards" } } - %td.brand{ ng: { bind: '::card.brand' } } - %td.number{ ng: { bind: '::card.number' } } - %td.expiry{ ng: { bind: '::card.expiry' } } + %tr.card{ id: "card{{ card.id }}", "ng-repeat": "card in savedCreditCards" } + %td.brand{ "ng-bind": '::card.brand' } + %td.number{ "ng-bind": '::card.number' } + %td.expiry{ "ng-bind": '::card.expiry' } %td.is-default - %input{ type: 'radio', name: 'default_card', ng: { model: 'card.is_default', click: 'confirmSetDefault(card, $event)', value: "true"} } + %input{ type: 'radio', name: 'default_card', "ng-model": 'card.is_default', "ng-click": 'confirmSetDefault(card, $event)', "ng-value": "true" } %td.actions %button.tiny.alert.no-margin{ "ng-click": "deleteCard(card.id)" } = t(:delete) diff --git a/app/webpacker/channels/scoped_channel.js b/app/webpacker/channels/scoped_channel.js new file mode 100644 index 0000000000..359cf5c26d --- /dev/null +++ b/app/webpacker/channels/scoped_channel.js @@ -0,0 +1 @@ +// ScopedChannel is created with a specific ID in ../controllers/scoped_channel_controller.js diff --git a/app/webpacker/controllers/bulk_form_controller.js b/app/webpacker/controllers/bulk_form_controller.js index 91af33e9e0..1f2aa80f0e 100644 --- a/app/webpacker/controllers/bulk_form_controller.js +++ b/app/webpacker/controllers/bulk_form_controller.js @@ -1,6 +1,19 @@ import { Controller } from "stimulus"; -// Manages "changed" state for a form with multiple records +// Manage "changed" state for a form with multiple records +// +// When any elements are changed: +// - the element is marked ".changed" +// - "actions" element appears +// - "changedSummary" element is updated using I18n +// - "disableSelector" elements are disabled +// - The browser will warn if trying to leave the page +// +// Supported element types: +// - input[type=text] and similar +// - input[type=checkbox] +// - select (single) - including tom-select +// export default class BulkFormController extends Controller { static targets = ["actions", "changedSummary"]; static values = { @@ -13,18 +26,7 @@ export default class BulkFormController extends Controller { this.form = this.element; // Start listening for any changes within the form - // this.element.addEventListener('change', this.toggleChanged.bind(this)); // dunno why this doesn't work - for (const element of this.form.elements) { - element.addEventListener("input", this.toggleChanged.bind(this)); // immediately respond to any change - - // Set up a tree of fields according to their associated record - const recordContainer = element.closest("[data-record-id]"); // The JS could be more efficient if this data was added to each element. But I didn't want to pollute the HTML too much. - const recordId = recordContainer && recordContainer.dataset.recordId; - if (recordId) { - this.recordElements[recordId] ||= []; - this.recordElements[recordId].push(element); - } - } + this.#registerElements(this.form.elements); this.toggleFormChanged(); } @@ -35,6 +37,15 @@ export default class BulkFormController extends Controller { window.removeEventListener("beforeunload", this.preventLeavingBulkForm); } + // Register any new elements (may be called by another controller after dynamically adding fields) + registerElements() { + const registeredElements = Object.values(this.recordElements).flat(); + // Select only elements that haven't been registered yet + const newElements = Array.from(this.form.elements).filter(n => !registeredElements.includes(n)); + + this.#registerElements(newElements); + } + toggleChanged(e) { const element = e.target; element.classList.toggle("changed", this.#isChanged(element)); @@ -50,7 +61,7 @@ export default class BulkFormController extends Controller { const formChanged = changedRecordCount > 0 || this.errorValue; // Show actions - this.actionsTarget.classList.toggle("hidden", !formChanged); + this.hasActionsTarget && this.actionsTarget.classList.toggle("hidden", !formChanged); this.#disableOtherElements(formChanged); // like filters and sorting // Display number of records changed @@ -78,6 +89,20 @@ export default class BulkFormController extends Controller { // private + #registerElements(elements) { + for (const element of elements) { + element.addEventListener("input", this.toggleChanged.bind(this)); // immediately respond to any change + + // Set up a tree of fields according to their associated record + const recordContainer = element.closest("[data-record-id]"); // The JS could be more efficient if this data was added to each element. But I didn't want to pollute the HTML too much. + const recordId = recordContainer && recordContainer.dataset.recordId; + if (recordId) { + this.recordElements[recordId] ||= []; + this.recordElements[recordId].push(element); + } + } + } + #disableOtherElements(disable) { if (!this.hasDisableSelectorValue) return; @@ -101,6 +126,11 @@ export default class BulkFormController extends Controller { #isChanged(element) { if (element.type == "checkbox") { return element.defaultChecked !== undefined && element.checked != element.defaultChecked; + + } else if (element.type == "select-one") { + const defaultSelected = Array.from(element.options).find((opt)=>opt.hasAttribute('selected')); + return element.selectedOptions[0] != defaultSelected; + } else { return element.defaultValue !== undefined && element.value != element.defaultValue; } diff --git a/app/webpacker/controllers/checked_controller.js b/app/webpacker/controllers/checked_controller.js index 6317635fa4..acca8ec0dd 100644 --- a/app/webpacker/controllers/checked_controller.js +++ b/app/webpacker/controllers/checked_controller.js @@ -41,6 +41,13 @@ export default class extends Controller { return this.countValue === this.checkboxTargets.length; } + #closeDetails(elmnt) { + if (elmnt.getElementsByTagName('details').length == 0) + return; + + Array.from(elmnt.getElementsByTagName('details')).forEach((element) => element.open = false); + } + #toggleDisabled() { if (!this.hasDisableTarget) { return; @@ -48,6 +55,7 @@ export default class extends Controller { if (this.#checkedCount() === 0) { this.disableTargets.forEach((element) => element.classList.add("disabled")); + this.disableTargets.forEach(this.#closeDetails); } else { this.disableTargets.forEach((element) => element.classList.remove("disabled")); } diff --git a/app/webpacker/controllers/dropdown_controller.js b/app/webpacker/controllers/dropdown_controller.js index 19a3dc8b7a..844289e12c 100644 --- a/app/webpacker/controllers/dropdown_controller.js +++ b/app/webpacker/controllers/dropdown_controller.js @@ -1,44 +1,29 @@ import { Controller } from "stimulus"; export default class extends Controller { - static targets = ["arrow", "menu"]; connect() { - this.collapsedClasses = this.arrowTarget.dataset.collapsedClass.split(" "); - this.expandedClasses = this.arrowTarget.dataset.expandedClass.split(" "); - this.#hide(); - document.addEventListener("click", this.#onBodyClick.bind(this)); + document.body.addEventListener("click", this.#close.bind(this)); + this.element.addEventListener("click", this.#stopPropagation.bind(this)); } disconnect() { - document.removeEventListener("click", this.#onBodyClick); + document.removeEventListener("click", this.#close); + document.removeEventListener("click", this.#stopPropagation); } - toggle() { - if (this.element.classList.contains("disabled")) { - return; - } - if (this.menuTarget.classList.contains("hidden")) { - this.#show(); - } else { - this.#hide(); - } + closeOnMenu(event) { + this.#close(); + this.#stopPropagation(event); } - #onBodyClick(event) { - if (!this.element.contains(event.target)) { - this.#hide(); - } + // private + + #close(event) { + this.element.open = false; } - #show() { - this.menuTarget.classList.remove("hidden"); - this.arrowTarget.classList.remove(...this.collapsedClasses); - this.arrowTarget.classList.add(...this.expandedClasses); - } - #hide() { - this.menuTarget.classList.add("hidden"); - this.arrowTarget.classList.remove(...this.expandedClasses); - this.arrowTarget.classList.add(...this.collapsedClasses); + #stopPropagation(event) { + event.stopPropagation(); } } diff --git a/app/webpacker/controllers/flash_controller.js b/app/webpacker/controllers/flash_controller.js index c30c08bcab..84dfe7eaf9 100644 --- a/app/webpacker/controllers/flash_controller.js +++ b/app/webpacker/controllers/flash_controller.js @@ -5,7 +5,25 @@ document.addEventListener("turbolinks:before-cache", () => ); export default class extends Controller { - close() { + static values = { + autoClose: Boolean, + }; + + connect() { + if (this.autoCloseValue) { + setTimeout(this.close.bind(this), 5000); + } + } + + close(e) { + // Fade out + this.element.classList.remove("animate-show"); + this.element.classList.add("animate-hide-500"); + setTimeout(this.remove.bind(this), 500); + e && e.preventDefault(); // Prevent submitting if we're inside a form + } + + remove() { this.element.remove(); } } diff --git a/app/webpacker/controllers/form_controller.js b/app/webpacker/controllers/form_controller.js new file mode 100644 index 0000000000..aaac9db4ec --- /dev/null +++ b/app/webpacker/controllers/form_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "stimulus"; + +export default class FormController extends Controller { + submit() { + this.element.submit(); + } +} diff --git a/app/webpacker/controllers/index.js b/app/webpacker/controllers/index.js index 00245fdf88..4e31f9a433 100644 --- a/app/webpacker/controllers/index.js +++ b/app/webpacker/controllers/index.js @@ -6,12 +6,15 @@ import StimulusReflex from "stimulus_reflex"; import consumer from "../channels/consumer"; import controller from "../controllers/application_controller"; import CableReady from "cable_ready"; +import NestedForm from 'stimulus-rails-nested-form/dist/stimulus-rails-nested-form.umd.js' // the default module entry point is broken + const application = Application.start(); const context = require.context("controllers", true, /_controller\.js$/); const contextComponents = require.context("../../components", true, /_controller\.js$/); application.load(definitionsFromContext(context).concat(definitionsFromContext(contextComponents))); +application.register('nested-form', NestedForm); application.consumer = consumer; StimulusReflex.initialize(application, { controller, isolate: true }); StimulusReflex.debug = process.env.RAILS_ENV === "development"; diff --git a/app/webpacker/controllers/mixins/useSearchCustomer.js b/app/webpacker/controllers/mixins/useSearchCustomer.js index a7776e14fc..509b060315 100644 --- a/app/webpacker/controllers/mixins/useSearchCustomer.js +++ b/app/webpacker/controllers/mixins/useSearchCustomer.js @@ -9,7 +9,7 @@ export const useSearchCustomer = (controller) => { fetch("/admin/search/customers.json?" + new URLSearchParams(params)) .then((response) => response.json()) .then((json) => { - this.items = json; + this.items = [...this.items, ...json]; callback(json); }) .catch((error) => { diff --git a/app/webpacker/controllers/modal_link_controller.js b/app/webpacker/controllers/modal_link_controller.js index d4f58fb546..9eb765c35f 100644 --- a/app/webpacker/controllers/modal_link_controller.js +++ b/app/webpacker/controllers/modal_link_controller.js @@ -1,7 +1,7 @@ import { Controller } from "stimulus"; export default class extends Controller { - static values = { target: String }; + static values = { target: String, modalDataset: Object }; open() { let modal = document.getElementById(this.targetValue); @@ -12,6 +12,21 @@ export default class extends Controller { modalController.open(); } + setModalDataSetOnConfirm(event) { + try { + const modalId = this.targetValue; + const moodalConfirmButtonQuery = `#${modalId} #modal-confirm-button`; + const confirmButton = document.querySelector(moodalConfirmButtonQuery); + Object.keys(this.modalDatasetValue).forEach((datasetKey) => { + confirmButton.setAttribute(datasetKey, this.modalDatasetValue[datasetKey]); + }); + } catch (e) { + // In case of any type of error in setting the dataset value, stop the further actions i.e. opening the modal + event.stopImmediatePropagation(); + throw e; + } + } + getIdentifier() { return "modal"; } diff --git a/app/webpacker/controllers/popout_controller.js b/app/webpacker/controllers/popout_controller.js index 0e430ce1a9..287ed98771 100644 --- a/app/webpacker/controllers/popout_controller.js +++ b/app/webpacker/controllers/popout_controller.js @@ -10,7 +10,7 @@ export default class PopoutController extends Controller { // Show when click or down-arrow on button this.buttonTarget.addEventListener("click", this.show.bind(this)); - this.buttonTarget.addEventListener("keydown", this.showIfDownArrow.bind(this)); + this.buttonTarget.addEventListener("keydown", this.applyKeyAction.bind(this)); // Close when click or tab outside of dialog. Run async (don't block primary event handlers). this.closeIfOutsideBound = this.closeIfOutside.bind(this); // Store reference for removing listeners later. @@ -33,17 +33,33 @@ export default class PopoutController extends Controller { e.preventDefault(); } - showIfDownArrow(e) { - if (e.keyCode == 40) { + // Apply an appropriate action, behaving similar to a dropdown + // Shows the popout and applies the value where appropriate + applyKeyAction(e) { + if ([38, 40].includes(e.keyCode)) { + // Show if Up or Down arrow this.show(e); + } else if (e.key.match(/^[\d\w]$/)) { + // Show, and apply value if it's a digit or word character + this.show(e); + this.first_input.value = e.key; + // Notify of change + this.first_input.dispatchEvent(new Event("input")); } } close() { // Close if not already closed if (this.dialogTarget.style.display != "none") { + // Check every element for browser-side validation, before the fields get hidden. + if (!this.#enabledDisplayElements().every((element) => element.reportValidity())) { + // If any fail, don't close + return; + } + // Update button to represent any changes this.buttonTarget.innerText = this.#displayValue(); + this.buttonTarget.innerHTML ||= " "; // (with default space to help with styling) this.buttonTarget.classList.toggle("changed", this.#isChanged()); this.dialogTarget.style.display = "none"; diff --git a/app/webpacker/controllers/products_controller.js b/app/webpacker/controllers/products_controller.js index c8a1dc6963..6fe8662f50 100644 --- a/app/webpacker/controllers/products_controller.js +++ b/app/webpacker/controllers/products_controller.js @@ -2,6 +2,7 @@ import ApplicationController from "./application_controller"; export default class extends ApplicationController { static targets = ["loading"]; + static values = { currentId: Number }; connect() { super.connect(); diff --git a/app/webpacker/controllers/remote_toggle_controller.js b/app/webpacker/controllers/remote_toggle_controller.js deleted file mode 100644 index ad6944f837..0000000000 --- a/app/webpacker/controllers/remote_toggle_controller.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Controller } from "stimulus"; - -export default class extends Controller { - static targets = ["chevron"]; - static values = { selector: String }; - - toggle(event) { - if (this.hasChevronTarget) { - this.chevronTarget.classList.toggle("icon-chevron-down"); - this.chevronTarget.classList.toggle("icon-chevron-up"); - } - - const element = document.querySelector(this.selectorValue); - element.style.display = element.style.display === "none" ? "block" : "none"; - } -} diff --git a/app/webpacker/controllers/select_customer_controller.js b/app/webpacker/controllers/select_customer_controller.js index 98df1ecddd..d1ffaa4739 100644 --- a/app/webpacker/controllers/select_customer_controller.js +++ b/app/webpacker/controllers/select_customer_controller.js @@ -6,12 +6,13 @@ export default class extends TomSelectController { static values = { options: Object, distributor: Number }; connect() { + this.items = []; useSearchCustomer(this); useRenderCustomer(this); const options = { valueField: "id", labelField: "email", - searchField: ["email", "full_name", "last_name"], + searchField: ["email", "full_name", "first_name", "last_name"], load: this.load.bind(this), shouldLoad: (query) => query.length > 2, render: { @@ -20,7 +21,6 @@ export default class extends TomSelectController { }; super.connect(options); this.control.on("item_add", this.onItemSelect.bind(this)); - this.items = []; } onItemSelect(id, item) { @@ -38,9 +38,7 @@ export default class extends TomSelectController { ]; const attribute_wrapper = "#order_" + address + "_attributes_"; address_parts.forEach((part) => { - document.querySelector(attribute_wrapper + part).value = data - ? data[part] - : ""; + document.querySelector(attribute_wrapper + part).value = data ? data[part] : ""; }); this.setValueOnTomSelectController( document.querySelector(attribute_wrapper + "state_id"), @@ -51,9 +49,8 @@ export default class extends TomSelectController { data ? data.country_id : "" ); }); - $("#order_email").val(customer.email); - $("#user_id").val(customer.user_id); - $("#customer_id").val(customer.id); + document.querySelector("#order_email").value = customer.email; + document.querySelector("#customer_id").value = customer.id; } setValueOnTomSelectController = (element, value) => { diff --git a/app/webpacker/controllers/shop_tabs_controller.js b/app/webpacker/controllers/shop_tabs_controller.js deleted file mode 100644 index 86001f9570..0000000000 --- a/app/webpacker/controllers/shop_tabs_controller.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Controller } from "stimulus"; - -export default class extends Controller { - connect() { - window.addEventListener("orderCycleSelected", this.orderCycleSelected); - } - - disconnect() { - window.removeEventListener("orderCycleSelected", this.orderCycleSelected); - } - - orderCycleSelected = (event) => { - window.dispatchEvent( - new CustomEvent("tabs-and-panels:click", { - detail: { - tab: "shop", - panel: "shop_panel", - }, - }) - ); - }; -} diff --git a/app/webpacker/controllers/tabs_and_panels_controller.js b/app/webpacker/controllers/tabs_and_panels_controller.js index 050a4fff25..3436bbdd96 100644 --- a/app/webpacker/controllers/tabs_and_panels_controller.js +++ b/app/webpacker/controllers/tabs_and_panels_controller.js @@ -1,97 +1,54 @@ import { Controller } from "stimulus"; export default class extends Controller { - static targets = ["tab", "panel", "default"]; + static targets = ["tab", "panel", "default", "shop"]; static values = { className: String }; connect() { - // hide all active panel - this.panelTargets.forEach((panel) => { - panel.style.display = "none"; - }); - - // only display the default panel - this.defaultTarget.style.display = "block"; - - // Display panel specified in url anchor - const anchors = window.location.toString().split("#"); - let anchor = anchors.length > 1 ? anchors.pop() : ""; - - if (anchor != "") { - // Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel" :( - // strip the starting / if any - if (anchor[0] == "/") { - anchor = anchor.slice(1); - } - // Add _panel to the anchor to match the panel id if needed - if (!anchor.includes("_panel")) { - anchor = `${anchor}_panel`; - } - this.updateActivePanel(anchor); - - // tab - const tab_id = anchor.split("_panel").shift(); - this.updateActiveTab(tab_id); - } - - window.addEventListener("tabs-and-panels:click", (event) => { - this.simulateClick(event.detail.tab, event.detail.panel); - }); + this._activateFromWindowLocationOrDefaultPanelTarget(); window.addEventListener("popstate", (event) => { - const newPanelId = event.target.location.hash.replace("#/", ""); - const currentPanelId = this.currentActivePanel.id; - - if (newPanelId !== currentPanelId) { - const newTabId = newPanelId.split("_panel").shift(); - this.simulateClick(newTabId, newPanelId); - } + this._activateFromWindowLocationOrDefaultPanelTarget(); }); } - simulateClick(tab, panel) { - this.updateActivePanel(panel); - this.updateActiveTab(tab); - } - - changeActivePanel(event) { - this.updateActivePanel(`${event.currentTarget.id}_panel`); - } - - updateActivePanel(panel_id) { - const newActivePanel = this.panelTargets.find((panel) => panel.id == panel_id); - - if (newActivePanel === undefined) { - // No panel found - return; + _activateFromWindowLocationOrDefaultPanelTarget() { + // Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel" + const hashWithoutSlash = window.location.hash.replace("/", ""); + const tabWithSameHash = this.tabTargets.find((tab) => tab.hash == hashWithoutSlash); + if (hashWithoutSlash != "" && tabWithSameHash) { + this._activateByHash(tabWithSameHash.hash); + } else { + this._activateByHash(`#${this.defaultTarget.id}`); } - - this.currentActivePanel.style.display = "none"; - newActivePanel.style.display = "block"; } - changeActiveTab(event) { - this.currentActiveTab.classList.remove(`${this.classNameValue}`); - event.currentTarget.classList.add(`${this.classNameValue}`); + activate(event) { + this._activateByHash(event.currentTarget.hash); } - updateActiveTab(tab_id) { - const newActiveTab = this.tabTargets.find((tab) => tab.id == tab_id); - - if (newActiveTab === undefined) { - // No tab found - return; - } - - this.currentActiveTab.classList.remove(`${this.classNameValue}`); - newActiveTab.classList.add(`${this.classNameValue}`); + activateDefaultPanel() { + this._activateByHash(`#${this.defaultTarget.id}`); } - get currentActiveTab() { - return this.tabTargets.find((tab) => tab.classList.contains("selected")); + activateShopPanel() { + this._activateByHash(`#${this.shopTarget.id}`); } - get currentActivePanel() { - return this.panelTargets.find((panel) => panel.id == `${this.currentActiveTab.id}_panel`); + _activateByHash(hash) { + this.tabTargets.forEach((tab) => { + if (tab.hash == hash) { + tab.classList.add(this.classNameValue); + } else { + tab.classList.remove(this.classNameValue); + } + }); + this.panelTargets.forEach((panel) => { + if (panel.id == hash.replace("#", "")) { + panel.style.display = "block"; + } else { + panel.style.display = "none"; + } + }); } } diff --git a/app/webpacker/controllers/toggle_button_disabled_controller.js b/app/webpacker/controllers/toggle_button_disabled_controller.js deleted file mode 100644 index 7ef9233217..0000000000 --- a/app/webpacker/controllers/toggle_button_disabled_controller.js +++ /dev/null @@ -1,24 +0,0 @@ -import { Controller } from "stimulus"; - -// Since Rails 7 it adds "data-disabled-with" property to submit, you'll need to add -// 'data-disable-with="false' for this to function as expected, ie: -// -// -// -export default class extends Controller { - static targets = ["button"]; - - connect() { - if (this.hasButtonTarget) { - this.buttonTarget.disabled = true; - } - } - - inputIsChanged(e) { - if (e.target.value !== "") { - this.buttonTarget.disabled = false; - } else { - this.buttonTarget.disabled = true; - } - } -} diff --git a/app/webpacker/controllers/toggle_control_controller.js b/app/webpacker/controllers/toggle_control_controller.js index c9a64d9b29..6beea90ec2 100644 --- a/app/webpacker/controllers/toggle_control_controller.js +++ b/app/webpacker/controllers/toggle_control_controller.js @@ -1,27 +1,78 @@ import { Controller } from "stimulus"; +// Toggle state of a control based on a condition. +// +// 1. When an action occurs on an element, +// 2. The element's value is inspected, and +// 3. The related control(s) are changed state +// export default class extends Controller { - static targets = ["control"]; + static targets = ["control", "content", "chevron"]; + static values = { selector: String, match: String }; disableIfPresent(event) { - const input = event.currentTarget; - const disable = !!this.#inputValue(input); // Coerce value to boolean + const present = !!this.#inputValue(event.currentTarget); // Coerce value to boolean + this.#toggleDisabled(present); + } + + enableIfPresent(event) { + const present = !!this.#inputValue(event.currentTarget); // Coerce value to boolean + + this.#toggleDisabled(!present); + } + + // Display the "content" target if element has data-toggle-show="true" + // (TODO: why not use the "control" target?) + toggleDisplay(event) { + const input = event.currentTarget; + this.contentTargets.forEach((t) => { + t.style.display = input.dataset.toggleShow === "true" ? "block" : "none"; + }); + } + + // Toggle element specified by data-control-toggle-selector-value="" + // (TODO: give a more general name) + toggleAdvancedSettings(event) { + if (this.hasChevronTarget) { + this.chevronTarget.classList.toggle("icon-chevron-down"); + this.chevronTarget.classList.toggle("icon-chevron-up"); + } + + const element = document.querySelector(this.selectorValue); + element.style.display = element.style.display === "none" ? "block" : "none"; + } + + // Display the control if selected value matches value in data-toggle-match="" + displayIfMatch(event) { + const inputValue = this.#inputValue(event.currentTarget); + + this.#toggleDisplay(inputValue == this.matchValue); + } + + // private + + #toggleDisabled(disable) { this.controlTargets.forEach((target) => { target.disabled = disable; }); - // Focus when enabled + // Focus first when enabled if (!disable) { this.controlTargets[0].focus(); } } - //todo: can a new method disableIfBlank replace ButtonDisabledController? - //todo: can a new method toggleDisplay replace ToggleController? - //todo: can toggleDisplay with optional chevron-target replace RemoteToggleController? + #toggleDisplay(show) { + this.controlTargets.forEach((target) => { + target.style.display = (show ? "block" : "none"); + }); - // private + // Focus first when displayed + if (show) { + this.controlTargets[0].focus(); + } + } // Return input's value, but only if it would be submitted by a form // Radio buttons not supported (yet) diff --git a/app/webpacker/controllers/toggle_controller.js b/app/webpacker/controllers/toggle_controller.js deleted file mode 100644 index de87ad095d..0000000000 --- a/app/webpacker/controllers/toggle_controller.js +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller } from "stimulus"; - -export default class extends Controller { - static targets = ["content"]; - - toggle(event) { - const input = event.currentTarget; - this.contentTargets.forEach((t) => { - t.style.display = input.dataset.toggleShow === "true" ? "block" : "none"; - }); - } -} diff --git a/app/webpacker/controllers/tom_select_controller.js b/app/webpacker/controllers/tom_select_controller.js index 9cf4401fe6..2a3792d88e 100644 --- a/app/webpacker/controllers/tom_select_controller.js +++ b/app/webpacker/controllers/tom_select_controller.js @@ -2,26 +2,19 @@ import { Controller } from "stimulus"; import TomSelect from "tom-select/dist/esm/tom-select.complete"; export default class extends Controller { - static values = { options: Object }; - static defaults = { - maxItems: 1, - maxOptions: null, - plugins: ["dropdown_input"], - allowEmptyOption: true, - closeAfterSelect: true, - onItemAdd: function () { - this.setTextboxValue(""); - }, - }; + static values = { options: Object, placeholder: String }; connect(options = {}) { - if (this.#placeholder()) { - options.allowEmptyOption = false; - options.placeholder = this.#placeholder(); - } - this.control = new TomSelect(this.element, { - ...this.constructor.defaults, + maxItems: 1, + maxOptions: null, + plugins: ["dropdown_input"], + allowEmptyOption: true, // Show blank option (option with empty value) + closeAfterSelect: true, + placeholder: this.placeholderValue || this.#emptyOption(), + onItemAdd: function () { + this.setTextboxValue(""); + }, ...this.optionsValue, ...options, }); @@ -33,7 +26,7 @@ export default class extends Controller { // private - #placeholder() { + #emptyOption() { const optionsArray = [...this.element.options]; return optionsArray.find((option) => [null, ""].includes(option.value))?.text; } diff --git a/app/webpacker/css/admin/all.scss b/app/webpacker/css/admin/all.scss index 075fe06797..d9ad7c0008 100644 --- a/app/webpacker/css/admin/all.scss +++ b/app/webpacker/css/admin/all.scss @@ -83,6 +83,7 @@ @import "alert"; @import "animations"; @import "change_type_form"; +@import "connected_apps"; @import "customers"; @import "dashboard_item"; @import "dashboard-single-ent"; @@ -111,6 +112,7 @@ @import "side_menu"; @import "tables"; @import "tag_rules"; +@import "terms_of_service_banner"; @import "terms_of_service_files"; @import "validation"; @import "variant_overrides"; @@ -122,6 +124,5 @@ @import "~tom-select/src/scss/tom-select.default"; @import "components/tom_select"; -@import "app/components/help_modal_component/help_modal_component"; -@import "app/components/confirm_modal_component/confirm_modal_component"; +@import "app/components/modal_component/modal_component"; @import "app/webpacker/css/admin/trix.scss"; diff --git a/app/webpacker/css/admin/animations.scss b/app/webpacker/css/admin/animations.scss index 7b6e451078..6dffab1cfe 100644 --- a/app/webpacker/css/admin/animations.scss +++ b/app/webpacker/css/admin/animations.scss @@ -11,13 +11,23 @@ } @keyframes fade-out-hide { - 0% {opacity: 1; visibility: visible;} - 99% {opacity: 0; visibility: visible;} - 100% {opacity: 0; visibility: hidden;} + 0% { + opacity: 1; + visibility: visible; + } + 99% { + opacity: 0; + visibility: visible; + } + 100% { + opacity: 0; + visibility: hidden; + } } .animate-hide-500 { animation: fade-out-hide 0.5s; + opacity: 0; // Stay invisible after animation } // @-webkit-keyframes slideOutDown diff --git a/app/webpacker/css/admin/components/messages.scss b/app/webpacker/css/admin/components/messages.scss index 61dd751a55..f92498a599 100644 --- a/app/webpacker/css/admin/components/messages.scss +++ b/app/webpacker/css/admin/components/messages.scss @@ -57,6 +57,10 @@ &:nth-child(3) { padding: 20px; } + + .actions { + display: none; /* avoid adding new button on old design */ + } } } diff --git a/app/webpacker/css/admin/connected_apps.scss b/app/webpacker/css/admin/connected_apps.scss new file mode 100644 index 0000000000..c2cb2c9421 --- /dev/null +++ b/app/webpacker/css/admin/connected_apps.scss @@ -0,0 +1,41 @@ +#connected_apps_panel { + max-width: 615px; +} + +.connected-app__head { + display: flex; + justify-content: space-between; + margin-bottom: 1em; + + h3 { + margin-bottom: 1em; + } +} + +.connected-app__description { + p { + margin-bottom: 1em; + } +} + +.connected-app__note { + display: flex; + justify-content: space-between; + + background-color: $color-14; + border: none; + border-left: $border-radius solid $color-3; + border-radius: $border-radius; + box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.05), 0px 2px 2px rgba(0, 0, 0, 0.07); + margin: 2em 0; + padding: 0.5em 1em; + + align-items: center; + gap: 1em; + * { + flex-shrink: 0; + } + p { + flex-shrink: 1; + } +} diff --git a/app/webpacker/css/admin/dashboard_item.scss b/app/webpacker/css/admin/dashboard_item.scss index 67498fa352..7522b91836 100644 --- a/app/webpacker/css/admin/dashboard_item.scss +++ b/app/webpacker/css/admin/dashboard_item.scss @@ -18,7 +18,7 @@ div.dashboard_item { background-color: $spree-green; } - &.red { + &.warning { background-color: $color-warning; } @@ -39,7 +39,7 @@ div.dashboard_item { bottom: 5px; } - &.red { + &.warning { border-color: $color-warning; border-width: 3px; @@ -101,6 +101,10 @@ div.dashboard_item { max-height: 250px; overflow-y: auto; overflow-x: hidden; + + &:focus { + outline: none; + } } .list-title { @@ -121,7 +125,7 @@ div.dashboard_item { .list-item { border: solid $spree-blue; border-width: 0px 1px 0px 1px; - height: 38px; + height: 41px; span.alpha { font-weight: bold; @@ -160,13 +164,13 @@ div.dashboard_item { border: solid $color-warning; } - &.red { + &.warning { color: $color-warning; border: solid $color-warning; } &.orange, - &.red { + &.warning { border-width: 0px 3px 0px 3px; } @@ -224,7 +228,7 @@ div.dashboard_item { background-color: $spree-blue; } - &.red { + &.warning { background-color: $color-warning; } diff --git a/app/webpacker/css/admin/dropdown.scss b/app/webpacker/css/admin/dropdown.scss index 64c98e7ddf..ab3a61d5bb 100644 --- a/app/webpacker/css/admin/dropdown.scss +++ b/app/webpacker/css/admin/dropdown.scss @@ -47,6 +47,7 @@ &.disabled { opacity: 0.5; + pointer-events: none; &:hover { cursor: default; @@ -179,6 +180,34 @@ background-color: #ededed; } } + + > details { + // Override padding on ofn-drop-down-style + margin: -7px -15px; + padding: 7px 15px; + } + + > details > summary { + display: inline-block; + list-style: none; + width: auto; + text-transform: uppercase; + font-size: 85%; + font-weight: 600; + // Override padding on ofn-drop-down-style to increase clickable area + margin: -8px -15px; + padding: 8px 15px; + } + + > details > summary:after { + content: "\f0d7"; + font-family: FontAwesome; + } + + > details[open] > summary:after { + content: "\f0d8"; + font-family: FontAwesome; + } } .ofn-drop-down-v2 { diff --git a/app/webpacker/css/admin/globals/palette.scss b/app/webpacker/css/admin/globals/palette.scss index b591605326..5a64d064f7 100644 --- a/app/webpacker/css/admin/globals/palette.scss +++ b/app/webpacker/css/admin/globals/palette.scss @@ -13,6 +13,7 @@ $color-3: $spree-blue !default; // Light Blue $color-4: #6788a2 !default; // Dark Blue $color-5: #c60f13 !default; // Red $color-6: #ff9300 !default; // Yellow +$color-14: #f8f9fa !default; // Lighter grey $dark-grey: #333; $light-grey: #ddd; diff --git a/app/webpacker/css/admin/globals/variables.scss b/app/webpacker/css/admin/globals/variables.scss index 7ea08bbc7d..ef335f5492 100644 --- a/app/webpacker/css/admin/globals/variables.scss +++ b/app/webpacker/css/admin/globals/variables.scss @@ -149,3 +149,7 @@ $border-radius: 3px !default; $font-weight-bold: 600 !default; $font-weight-normal: 400 !default; + +// z-index +//-------------------------------------------------------------- +$tos-banner-z-index: 102; diff --git a/app/webpacker/css/admin/modals.scss b/app/webpacker/css/admin/modals.scss index b67c6e461a..20c27e9a67 100644 --- a/app/webpacker/css/admin/modals.scss +++ b/app/webpacker/css/admin/modals.scss @@ -16,7 +16,7 @@ dialog { display: none; position: absolute; z-index: 1005; - width: 100vw; + width: 100%; top: 0; border-radius: 0.4em; border: 0px none; @@ -75,8 +75,6 @@ dialog.full { top: 0; left: 0; height: 100%; - height: 100vh; - min-height: 100vh; max-width: none !important; margin-left: 0 !important; } @@ -161,7 +159,7 @@ dialog[open] { @media only screen and (max-width: 40em) { .reveal-modal, dialog { - min-height: 100vh; + min-height: 100%; } } @@ -170,6 +168,10 @@ dialog[open] { dialog { top: 6.25rem; } + .reveal-modal.fit, + dialog.fit { + width: fit-content; + } .reveal-modal.tiny, dialog.tiny { width: 30%; @@ -212,7 +214,7 @@ dialog[open] { } .reveal-modal.full, dialog.full { - width: 100vw; + width: 100%; max-width: 62.5rem; left: 0; right: 0; diff --git a/app/webpacker/css/admin/products_v3.scss b/app/webpacker/css/admin/products_v3.scss index f8d9a8272f..dff90a7b76 100644 --- a/app/webpacker/css/admin/products_v3.scss +++ b/app/webpacker/css/admin/products_v3.scss @@ -19,13 +19,18 @@ position: relative; } - // Form actions floats over other controls when active - .form-actions { - position: absolute; - top: -1em; - left: 0; - right: 0; - z-index: 1; // Ensure tom-select and .disabled-section are covered + #products-form { + .form-actions { + .container { + .status { + flex-grow: 1; // Fill space + } + + .form-buttons { + flex-shrink: 0; // Don't shrink + } + } + } } // Hopefully these rules will be moved to component(s). @@ -34,16 +39,45 @@ background-color: $color-tbl-bg; padding: 4px; + padding-top: 0; // Hide border because .form-actions is there. It is added with th padding instead. border-collapse: separate; // This is needed for the outer padding. Also should be helpful to give more flexibility of borders between rows. // Additional horizontal padding to align with input contents - thead th.with-input { - padding-left: $padding-tbl-cell + $hpadding-txt; - padding-right: $padding-tbl-cell + $hpadding-txt; + thead { + position: sticky; + top: 0; + z-index: 1; // TODO: Cover .popout and .vertical-ellipsis-menu, but only when sticky + box-shadow: $box-shadow; + + // Form actions replaces other controls when active + // It is part of the table header, to allow for sticky stacking when scrolling. + .form-actions-wrapper { + padding: 0; + overflow: visible; + background-color: white; + } + .form-actions-wrapper2 { + position: relative; + // Stretch to cover table side borders + left: -4px; + width: calc(100% + 8px); + background-color: white; + + padding: 4px 0; + } + + th.with-input { + // Additional padding to line up with content of input + padding-left: $padding-tbl-cell + $hpadding-txt; + + &.align-right { + padding-right: $padding-tbl-cell + $hpadding-txt; + } + } } // Row hover - tr:hover { + tbody tr:hover { td { background-color: $light-grey; position: relative; @@ -58,6 +92,16 @@ height: 100%; } } + + // "Naked" inputs. Row hover helps reveal them. + input:not([type="checkbox"]), .ts-control { + background-color: $color-tbl-cell-bg; + } + + // Reveal naked button text when any part of row is hovered + button.naked { + color: $color-link; + } } th, @@ -75,12 +119,14 @@ } th { + padding-top: $padding-tbl-cell + 4px; // Increase padding to create a top border // Clip long content in headers, but allow wrapping overflow: hidden; text-overflow: clip; // If colums are so small that headers are clipping, ellipsis are more of a hindrance } td { + position: relative; // Ensure that overflowing content is covered by the following cell. We don't use overflow: hidden because that messes with the popover. background-color: $color-tbl-cell-bg; } @@ -96,6 +142,10 @@ td { border-bottom: 2px solid $color-tbl-bg; + + &.with-image { + padding: 8px; + } } tr:first-child td { @@ -116,29 +166,20 @@ .field { padding: 0; - margin-bottom: 0.75em; + } + .multi-field { + // Allow wrap with small gap + display: flex; + flex-wrap: wrap; + gap: 3px; } label { margin: 0; } - // "Naked" inputs. Row hover helps reveal them. - input:not([type="checkbox"]) { - background-color: $color-tbl-cell-bg; - height: auto; - font-size: inherit; - font-weight: inherit; - - &:not(:focus):not(.changed):not([disabled]) { - border-color: transparent; - } - } - - .field_with_errors { - input { - border-color: $color-error; - } + .ts-control { + z-index: 0; // Avoid hovering over thead } } @@ -162,45 +203,40 @@ } #sort { - margin-bottom: 1em; + margin-top: 3px; // Helps even up with .form-actions when visible + margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; - } - #sort { line-height: $btn-relaxed-height; height: $btn-relaxed-height; + &.disabled-section { + display: none; + } + .with-dropdown { display: flex; justify-content: space-between; align-items: center; gap: 10px; } + .per-page { + width: 10em; + } } #filters { - display: grid; - grid-template-columns: repeat(6, 1fr); - grid-template-rows: 1fr; - grid-column-gap: 20px; + display: flex; + column-gap: 20px; align-items: end; - margin-bottom: 20px; + margin-bottom: 1rem; .query { - grid-column: 1 / span 3; - } + flex-grow: 1; + min-width: 15em; - .producers, - .categories { - } - - .submit { - grid-column: 6 / span 1; - } - - .query { .search-input { width: 100%; position: relative; @@ -234,10 +270,7 @@ .producers, .categories { - select { - width: 150px; - height: $btn-relaxed-height; - } + width: 15em; } .submit { @@ -327,8 +360,8 @@ &__popout { position: absolute; - top: -1em; - left: -1em; + top: -0.6em; + left: -0.2em; z-index: 1; // Cover below row when hover width: 9em; @@ -338,8 +371,12 @@ border-radius: $border-radius; box-shadow: 0px 0px 8px 0px rgba($near-black, 0.25); - .field:last-child { - margin-bottom: 0; + .field{ + margin-bottom: 0.75em; + + &:last-child { + margin-bottom: 0; + } } input[disabled] { @@ -347,4 +384,30 @@ } } } + + a.image-field { + position: relative; + + img { + display: block; + border-radius: $border-radius; + } + + .button { + display: none; // to be shown on hover + position: absolute; + bottom: 0; + left: 0; + } + + &:hover, + &:focus { + .button.secondary { + display: block; + background-color: $color-btn-secondary-hover-bg; + color: $color-btn-hover-bg; + border-color: $color-btn-hover-border; + } + } + } } diff --git a/app/webpacker/css/admin/side_menu.scss b/app/webpacker/css/admin/side_menu.scss index 398787b7b8..f746c45cda 100644 --- a/app/webpacker/css/admin/side_menu.scss +++ b/app/webpacker/css/admin/side_menu.scss @@ -2,6 +2,9 @@ border-right: 2px solid #f6f6f6; border-top: 2px solid #f6f6f6; + /* Reserve space for the save bar to avoid hidden menu items. */ + margin-bottom: 2em; + .menu_item { display: block; padding: 8px 15px; diff --git a/app/webpacker/css/admin/terms_of_service_banner.scss b/app/webpacker/css/admin/terms_of_service_banner.scss new file mode 100644 index 0000000000..ca23f9b763 --- /dev/null +++ b/app/webpacker/css/admin/terms_of_service_banner.scss @@ -0,0 +1,27 @@ +#banner-container { + position: fixed; + bottom: 0px; + left: 0; + width: 100%; + z-index: $tos-banner-z-index; + + .terms-of-service-banner { + padding: 18px; + text-align: center; + font-size: 120%; + color: white; + font-weight: 600; + margin-top: 0; + background-color: rgba($color-notice, 0.8); + display: flex; + + .column-left { + width: 70%; + } + + .column-right { + width: 30%; + text-align: center; + } + } +} diff --git a/app/webpacker/css/admin_v3/all.scss b/app/webpacker/css/admin_v3/all.scss index 3ee85908f9..7ee27d28ac 100644 --- a/app/webpacker/css/admin_v3/all.scss +++ b/app/webpacker/css/admin_v3/all.scss @@ -56,7 +56,7 @@ @import "../admin/components/dialogs"; @import "../admin/components/input"; @import "../admin/components/jquery_dialog"; -@import "../admin/components/messages"; +@import "components/messages"; // admin_v3 @import "components/navigation"; // admin_v3 @import "../admin/components/ng-cloak"; @import "../admin/components/page_actions"; @@ -87,6 +87,7 @@ @import "../admin/alert"; @import "../admin/animations"; @import "pages/change_type_form"; // admin_v3 +@import "../admin/connected_apps"; @import "../admin/customers"; @import "dashboard/dashboard_item"; // admin_v3 @import "pages/dashboard-single-ent"; // admin_v3 @@ -127,7 +128,8 @@ @import "~tom-select/src/scss/tom-select.default"; @import "components/tom_select"; // admin_v3 -@import "app/components/help_modal_component/help_modal_component"; -@import "app/components/confirm_modal_component/confirm_modal_component"; +@import "app/components/modal_component/modal_component"; @import "app/components/vertical_ellipsis_menu/component"; // admin_v3 and only V3 @import "app/webpacker/css/admin/trix.scss"; + +@import "terms_of_service_banner"; // admin_v3 diff --git a/app/webpacker/css/admin_v3/components/buttons.scss b/app/webpacker/css/admin_v3/components/buttons.scss index 13ecab6d7b..f213dd7bc3 100644 --- a/app/webpacker/css/admin_v3/components/buttons.scss +++ b/app/webpacker/css/admin_v3/components/buttons.scss @@ -1,3 +1,6 @@ +// Button styles +// Design reference: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Design-styleguide%3A-links-and-buttons + @mixin disabled-button() { &:disabled, &:disabled:hover { @@ -47,27 +50,38 @@ button:not(.plain):not(.trix-button), color: $color-btn-hover-text; } - &.fullwidth { - width: 100%; - text-align: center; - } - + // --- Colours --- &.secondary { - background-color: transparent; - border: 1px solid $color-btn-bg; - color: $color-btn-bg; + background-color: $color-btn-secondary-bg; + color: $color-link; &:hover { - background-color: $color-11; - border: 1px solid $color-btn-hover-bg; - color: $color-btn-hover-bg; + background-color: $color-btn-secondary-hover-bg; } - &:active, &:focus { - background-color: $color-11; - border: 1px solid $color-4; - color: $color-4; + background-color: $color-btn-secondary-hover-bg; + border-color: $color-link-focus; + color: $color-link-focus; + } + + // "Naked" variation: text appears on hover + &.naked { + border: none; + color: transparent; + + &:before { + color: $color-link; + } + + &:hover { + background-color: $color-btn-secondary-bg; + color: $color-link; + } + &:focus { + background-color: $color-btn-secondary-hover-bg; + color: $color-link-focus; + } } } @@ -90,6 +104,13 @@ button:not(.plain):not(.trix-button), } } + // --- Sizes --- + &.mini { + line-height: 18px; + height: auto; // DC: I don't like fixed heights. + padding: 0 6px; + } + &.condensed { line-height: $btn-condensed-height - 2px; // remove 2px to compensate for border height: $btn-condensed-height; @@ -108,15 +129,9 @@ button:not(.plain):not(.trix-button), text-transform: uppercase; } - &.red { - background-color: $color-btn-red-bg; - border-color: $color-btn-red-bg; - @include disabled-button(); // required for specifity - - &:hover { - background-color: $color-btn-red-hover-bg; - border-color: $color-btn-red-hover-bg; - } + &.fullwidth { + width: 100%; + text-align: center; } .badge { @@ -142,6 +157,7 @@ button:not(.plain):not(.trix-button), } } +// --- Reset buttons --- input[type="reset"] { // Reset button looks like a link, but has a border the same as buttons when active. background: none; diff --git a/app/webpacker/css/admin_v3/components/dropdown.scss b/app/webpacker/css/admin_v3/components/dropdown.scss index 6a93884aeb..8a101d1c8d 100644 --- a/app/webpacker/css/admin_v3/components/dropdown.scss +++ b/app/webpacker/css/admin_v3/components/dropdown.scss @@ -47,6 +47,7 @@ &.disabled { opacity: 0.5; + pointer-events: none; &:hover { cursor: default; @@ -179,6 +180,32 @@ background-color: #ededed; } } + + > details { + margin: -7px -15px; + padding: 7px 15px; + } + + > details > summary { + display: inline-block; + list-style: none; + width: auto; + text-transform: uppercase; + font-size: 85%; + font-weight: 600; + margin: -8px -15px; + padding: 8px 15px; + } + + > details > summary:after { + content: "\f0d7"; + font-family: FontAwesome; + } + + > details[open] > summary:after { + content: "\f0d8"; + font-family: FontAwesome; + } } .ofn-drop-down-v2 { diff --git a/app/webpacker/css/admin_v3/components/messages.scss b/app/webpacker/css/admin_v3/components/messages.scss new file mode 100644 index 0000000000..1c07461075 --- /dev/null +++ b/app/webpacker/css/admin_v3/components/messages.scss @@ -0,0 +1,80 @@ +.errorExplanation { + padding: 10px; + border: 1px solid very-light($color-error, 12); + background-color: very-light($color-error, 6); + border-radius: 3px; + color: $color-error; + margin-bottom: 15px; + + h2 { + font-size: 140%; + color: $color-error; + margin-bottom: 5px; + } + + p { + padding: 10px 0; + } + + ul { + list-style-position: inside; + + li { + font-weight: $font-weight-bold; + } + } +} + +.flash-container { + position: fixed; + left: 0; + bottom: 0; + width: 100%; + z-index: $flash-message-z-index; + display: flex; + justify-content: center; + + .flash { + position: relative; + display: flex; + align-items: center; + bottom: 3.5rem; + padding: 0.25rem; + min-width: 24rem; + max-width: 48.25em; + font-weight: 600; + background-color: $light-grey; + border-radius: $border-radius; + box-shadow: $box-shadow; + + &.success { + color: $color-flash-success-text; + background-color: $color-flash-success-bg; + } + &.notice { + color: $color-flash-notice-text; + background-color: $color-flash-notice-bg; + border-left: $border-radius solid $red; + } + &.error { + color: $color-flash-error-text; + background-color: $color-flash-error-bg; + } + + .msg { + flex-grow: 1; + margin: 0.25rem 0.75rem; + } + } +} + +.notice:not(.flash) { + padding: 1rem; + margin-bottom: 1.5rem; + background-color: $spree-light-blue; + border-radius: $border-radius; + + a { + font-weight: bold; + } +} diff --git a/app/webpacker/css/admin_v3/components/navigation.scss b/app/webpacker/css/admin_v3/components/navigation.scss index ecefc92369..4ef5d5a06c 100644 --- a/app/webpacker/css/admin_v3/components/navigation.scss +++ b/app/webpacker/css/admin_v3/components/navigation.scss @@ -61,7 +61,7 @@ nav.menu { } #admin-menu { - @include defaultBoxShadow; + box-shadow: $box-shadow; li { .dropdown { diff --git a/app/webpacker/css/admin_v3/components/pagination.scss b/app/webpacker/css/admin_v3/components/pagination.scss index 82642f54a6..98df96f06e 100644 --- a/app/webpacker/css/admin_v3/components/pagination.scss +++ b/app/webpacker/css/admin_v3/components/pagination.scss @@ -20,7 +20,7 @@ display: inline-block; text-align: center; background-color: $color-1; - @include defaultBoxShadow; + box-shadow: $box-shadow; border-radius: 4px; border: none; color: $color-8; @@ -77,7 +77,7 @@ background-color: $white; color: $near-black; - box-shadow: $color-btn-shadow; + box-shadow: $box-shadow; &.active { color: $white; diff --git a/app/webpacker/css/admin_v3/components/select2.scss b/app/webpacker/css/admin_v3/components/select2.scss index 5751802972..d42fc66d6b 100644 --- a/app/webpacker/css/admin_v3/components/select2.scss +++ b/app/webpacker/css/admin_v3/components/select2.scss @@ -11,7 +11,6 @@ box-shadow: none !important; @include border-radius($border-radius); color: $color-1 !important; - font-size: 90%; height: 31px; line-height: inherit !important; padding: 5px 15px 7px; @@ -23,7 +22,6 @@ .select2-search-choice-close { background-image: none !important; - font-size: 100% !important; @extend .icon-remove; @extend [class^="icon-"], :before; margin-top: 2px; @@ -54,7 +52,6 @@ .select2-search { @extend .icon-search; - font-size: 100%; color: darken($color-border, 15); padding: 0 9px 0 0; @@ -72,7 +69,6 @@ padding: 6px 0 6px 25px; margin: 5px 0 0 5px; font-family: $base-font-family; - font-size: 90%; box-shadow: none; background-image: none; } @@ -102,8 +98,6 @@ padding-left: 0 !important; li { - font-size: 85% !important; - &.select2-highlighted { .select2-result-label { &, @@ -117,7 +111,6 @@ color: $color-body-text; min-height: 22px; clear: both; - overflow: auto; } &.select2-no-results, @@ -160,7 +153,6 @@ border: none; box-shadow: none; color: $color-1 !important; - font-size: 85%; &:hover { background-color: $color-btn-hover-bg; @@ -168,7 +160,6 @@ .select2-search-choice-close { background-image: none !important; - font-size: 85% !important; @extend .icon-remove; @extend [class^="icon-"], :before; margin-left: 2px; @@ -248,7 +239,6 @@ label .select2-container { top: 0; margin: 0; padding: 0; - font-size: 100% !important; } } } diff --git a/app/webpacker/css/admin_v3/components/spinner.scss b/app/webpacker/css/admin_v3/components/spinner.scss index d6694b3ed2..fd7235ffe3 100644 --- a/app/webpacker/css/admin_v3/components/spinner.scss +++ b/app/webpacker/css/admin_v3/components/spinner.scss @@ -1,16 +1,23 @@ -.spinner-container { +.spinner-overlay { position: absolute; - width: 100%; + left: 0; + right: 0; height: 100%; min-height: 200px; + background: rgba(255, 255, 255, 0.8); + z-index: 2; +} + +.spinner-container { + position: fixed; + left: 0; + right: 0; display: flex; - justify-content: flex-start; + justify-content: center; align-items: center; flex-direction: column; gap: 40px; font-size: 24px; - background: rgba(255, 255, 255, 0.8); - z-index: 2; &.hidden { display: none; diff --git a/app/webpacker/css/admin_v3/components/tom_select.scss b/app/webpacker/css/admin_v3/components/tom_select.scss index cf250c5b94..ddae48cd71 100644 --- a/app/webpacker/css/admin_v3/components/tom_select.scss +++ b/app/webpacker/css/admin_v3/components/tom_select.scss @@ -4,11 +4,12 @@ &.input-active { .ts-control { - border-color: $lighter-grey; + border-color: $orient; + background-color: $lighter-grey; input { &::placeholder { - color: $light-grey; + color: $medium-light-grey; } } } @@ -22,24 +23,47 @@ @include border-radius($border-radius); input { + font-size: $body-font-size; + height: auto; + &::placeholder { color: $near-black; } } + + .item { + margin-top: 2px; // Ensure it lines up with the input. I have no idea why it's necessary. + } } .ts-dropdown { - @include border-radius($border-radius); + // Unstyled, we style the dropdown-content instead + margin: 0; + background: none; border: none; box-shadow: none; + + min-width: 10em; // Ensure content doesn't wrap too much + // We could consider always stretching to fit with width: max-content; + } + + .ts-dropdown-content { + margin-top: 4px; color: $near-black; + background-color: $color-body-bg; + @include border-radius($border-radius); + box-shadow: $shadow-dropdown; + + max-height: 21em; // Show up to 8 items without scrolling .option { - &.selected { - border-left: 2px solid $orient; - } + padding: 8px; + border-left: 3px solid transparent; + line-height: 20px; + &.selected, &.active { + border-left: 3px solid $orient; background-color: $lighter-grey; color: $near-black; } @@ -47,21 +71,134 @@ } } -.plugin-dropdown_input .dropdown-input { - border-color: $lighter-grey; +.plugin-dropdown_input { + .ts-dropdown { + // Cover up the control with the input + top: 0; + } - &:focus { - border-color: $orient; + .dropdown-input-wrap::after { + position: absolute; + content: "\f077"; // chevron-up + font-family: FontAwesome; + border: none; + top: 0.7em; + right: 0.7em; + font-size: 13px; + } + + .dropdown-input { + background-color: $color-body-bg; + border: 1px solid $lighter-grey; + box-shadow: none; + padding: 0.6em 0.75em; + + &:focus { + border: 1px solid $orient; + } } } -// For the "single" tom_select, be as clause as native select boxes +// For the "single" tom_select, be as close as possible to native select boxes // without too many options .ts-wrapper.single { max-height: 40px; max-width: 100%; - &.input-active .ts-control { - cursor: pointer; + .ts-control { + padding: 0.5em 0.75em; + padding-right: 1em !important; // ts has a clever variable-based rule here, but it doesn't seem to work right. + overflow: hidden; + + // Icon: Override TS icon with icon-chevron-down + &::after { + content: "\f078"; + font-family: FontAwesome; + border: none; + top: 1em; + } + &:not(.rtl)::after { + right: 1.9em; + } + + .item { + margin-right: 1em; + // Hide overflow with an ellipsis if a width has been set + overflow: hidden; + text-overflow: ellipsis; + text-wrap: nowrap; + } + } + + &.dropdown-active .ts-control { + &::after { + content: "\f077"; // chevron-up + } + } +} + +// 'no-input' mode, like a native select (hide text input). +.ts-wrapper.single.no-input { + .ts-control input { + // Hide input, while keeping it focusable for keyboard events. + height: 0; + } + + .ts-dropdown { + position: absolute; + top: 0; // we don't need to see the currently selected option, because it's visible in the dropdown + } + + .ts-dropdown-content { + margin-top: 0; + } +} + +// Appears just like other text on the page. +// See table.products tr:hover for example of revealing them +.naked_inputs .ts-wrapper { + height: auto; + min-height: 0; + + .ts-control { + padding: ($vpadding-txt - 1px) ($hpadding-txt - 1px); // Minus 1px for border + + height: auto; + min-height: 0; + font-size: inherit; + font-weight: inherit; + border-color: transparent; + line-height: 22px; + background-color: $color-body-bg; + + &::after { + display: none; // hide until hover + top: 0.8em; + right: 1em; + font-size: 0.75em; + } + + .item { + margin-top: 0; + margin-right: 0; // full width until hover + } + + &:hover { + &::after { + display: block; + } + .item { + margin-right: 1em; + } + } + } +} + +// Display as "changed" if sibling select is marked as changed. +select.changed + .ts-wrapper { + &.single, &.multi { + .ts-control { + border-color: $color-txt-changed-brd; + } } } diff --git a/app/webpacker/css/admin_v3/dashboard/dashboard_item.scss b/app/webpacker/css/admin_v3/dashboard/dashboard_item.scss index 3316704ef1..f52b935619 100644 --- a/app/webpacker/css/admin_v3/dashboard/dashboard_item.scss +++ b/app/webpacker/css/admin_v3/dashboard/dashboard_item.scss @@ -14,15 +14,7 @@ div.dashboard_item { padding: 0px 6px; border-radius: 10px; - &.green { - background-color: $spree-green; - } - - &.red { - background-color: $color-warning; - } - - &.orange { + &.warning { background-color: $color-warning; } } @@ -39,16 +31,7 @@ div.dashboard_item { bottom: 5px; } - &.red { - border-color: $color-warning; - border-width: 3px; - - h3 { - color: $color-warning; - } - } - - &.orange { + &.warning { border-color: $color-warning; border-width: 3px; @@ -102,6 +85,10 @@ div.dashboard_item { max-height: 250px; overflow-y: auto; overflow-x: hidden; + + &:focus { + outline: thin dotted; + } } .list-title { @@ -122,7 +109,7 @@ div.dashboard_item { .list-item { border: solid $spree-blue; border-width: 0px 1px 0px 1px; - height: 38px; + height: 41px; span.alpha { font-weight: bold; @@ -156,18 +143,9 @@ div.dashboard_item { font-size: 30px; } - &.orange { + &.warning { color: $color-warning; border: solid $color-warning; - } - - &.red { - color: $color-warning; - border: solid $color-warning; - } - - &.orange, - &.red { border-width: 0px 3px 0px 3px; } @@ -200,13 +178,6 @@ div.dashboard_item { .icon-ok-sign { color: #fff; } - - .text-icon { - &.green { - color: $spree-green; - background-color: #fff; - } - } } } } @@ -217,20 +188,19 @@ div.dashboard_item { font-weight: bold; text-align: center; - &.orange { + &.warning { background-color: $color-warning; - } + border-color: $color-warning; - &.blue { - background-color: $spree-blue; - } + &:focus { + border-color: $color-btn-hover-bg; + } - &.red { - background-color: $color-warning; } &:hover { background-color: $color-btn-hover-bg; + border-color: $color-btn-hover-bg; } &.bottom { diff --git a/app/webpacker/css/admin_v3/globals/palette.scss b/app/webpacker/css/admin_v3/globals/palette.scss index 59b3c88c3a..225b00b368 100644 --- a/app/webpacker/css/admin_v3/globals/palette.scss +++ b/app/webpacker/css/admin_v3/globals/palette.scss @@ -11,6 +11,7 @@ $yellow: #ff9300 !default; // Yellow $mystic: #d9e8eb !default; // Mystic $lighter-grey: #f8f9fa !default; // Lighter grey $light-grey: #eff1f2 !default; // Light grey (Porcelain) +$medium-light-grey: #babdbe !default; // Silver Sand $medium-grey: #919191 !default; // Medium grey $dark-grey: #2e3132 !default; // Dark Grey $near-black: #191c1d !default; // Near-black (Shark) @@ -34,3 +35,4 @@ $color-10: $orient; $color-11: $mystic; $color-12: $fair-pink; $color-13: $roof-terracotta; +$color-14: $lighter-grey; diff --git a/app/webpacker/css/admin_v3/globals/variables.scss b/app/webpacker/css/admin_v3/globals/variables.scss index 7eecf26ea8..bbd8fde650 100644 --- a/app/webpacker/css/admin_v3/globals/variables.scss +++ b/app/webpacker/css/admin_v3/globals/variables.scss @@ -12,7 +12,7 @@ $base-font-family: "Open Sans", "Helvetica Neue", "Helvetica", Helvetica, Arial, // Body base colors $color-body-bg: $white !default; $color-body-text: $near-black !default; -$color-headers: $dark-blue !default; +$color-headers: $near-black !default; $color-link: $teal !default; $color-link-hover: $orient !default; $color-link-active: $dark-blue !default; @@ -25,6 +25,12 @@ $color-success: $green !default; $color-notice: $yellow !default; $color-warning: $red !default; $color-error: $red !default; +$color-flash-success-text: $white !default; +$color-flash-success-bg: $near-black !default; +$color-flash-notice-text: $color-body-text !default; +$color-flash-notice-bg: $fair-pink !default; +$color-flash-error-text: $white !default; +$color-flash-error-bg: $color-error !default; // Table styles $color-tbl-bg: $light-grey !default; @@ -34,13 +40,12 @@ $color-tbl-thead-txt: $color-headers !default; $color-tbl-thead-bg: $light-grey !default; $color-tbl-border: $pale-blue !default; $padding-tbl-cell: 12px; -$padding-tbl-cell-condensed: 10px 12px; +$padding-tbl-cell-condensed: 4px 12px; $padding-tbl-cell-relaxed: 12px 12px; // Button colors $color-btn-bg: $teal !default; $color-btn-text: $white !default; -$color-btn-shadow: 0px 1px 0px rgba(0, 0, 0, 0.05), 0px 2px 2px rgba(0, 0, 0, 0.07) !default; $color-btn-hover-bg: $orient !default; $color-btn-hover-text: $white !default; $color-btn-hover-border: $dark-blue !default; @@ -48,6 +53,8 @@ $color-btn-disabled-bg: $medium-grey !default; $color-btn-disabled-text: $lighter-grey !default; $color-btn-red-bg: $red !default; $color-btn-red-hover-bg: $roof-terracotta !default; +$color-btn-secondary-bg: $white !default; +$color-btn-secondary-hover-bg: $mystic !default; // Actions colors $color-action-edit-bg: very-light($teal) !default; @@ -67,6 +74,9 @@ $color-action-save-brd: very-light($color-success, 20 ) !default; $color-action-mail-bg: very-light($color-success, 5 ) !default; $color-action-mail-brd: very-light($color-success, 20 ) !default; +// Dropdown styles +$shadow-dropdown: 0px 0px 8px 0px rgba($near-black, 0.25); + // Select2 select field colors $color-sel-bg: $lighter-grey !default; $color-sel-hover-bg: $lighter-grey !default; @@ -161,6 +171,7 @@ $h2-size: $h3-size + 2 !default; $h1-size: $h2-size + 2 !default; $border-radius: 4px !default; +$box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.05), 0px 2px 2px rgba(0, 0, 0, 0.07) !default; $font-weight-bold: 600 !default; $font-weight-normal: 400 !default; @@ -168,3 +179,8 @@ $font-weight-normal: 400 !default; $btn-relaxed-height: 40px !default; $btn-regular-height: 32px !default; $btn-condensed-height: 26px !default; + +// z-index +//-------------------------------------------------------------- +$tos-banner-z-index: 1001; +$flash-message-z-index: 1000; diff --git a/app/webpacker/css/admin_v3/mixins.scss b/app/webpacker/css/admin_v3/mixins.scss index 5301558d32..c2471a52ba 100644 --- a/app/webpacker/css/admin_v3/mixins.scss +++ b/app/webpacker/css/admin_v3/mixins.scss @@ -1,7 +1,3 @@ -@mixin defaultBoxShadow { - box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.05), 0px 2px 2px rgba(0, 0, 0, 0.07); -} - @mixin arrowDown { content: " "; display: block; diff --git a/app/webpacker/css/admin_v3/pages/change_type_form.scss b/app/webpacker/css/admin_v3/pages/change_type_form.scss index 0bd7c92560..0aed7a5663 100644 --- a/app/webpacker/css/admin_v3/pages/change_type_form.scss +++ b/app/webpacker/css/admin_v3/pages/change_type_form.scss @@ -74,7 +74,7 @@ &:hover { &:after { - border-top-color: $spree-green; + border-top-color: $orient; } } diff --git a/app/webpacker/css/admin_v3/shared/forms.scss b/app/webpacker/css/admin_v3/shared/forms.scss index 8a37545a29..c802a29c97 100644 --- a/app/webpacker/css/admin_v3/shared/forms.scss +++ b/app/webpacker/css/admin_v3/shared/forms.scss @@ -13,14 +13,25 @@ input[type="date"], input[type="datetime"], input[type="time"], input[type="number"], -textarea, -fieldset { +textarea { @include border-radius($border-radius); - padding: $vpadding-txt $hpadding-txt; + padding: ($vpadding-txt - 1px) ($hpadding-txt - 1px); // Minus 1px for border border: 1px solid $lighter-grey; color: $color-txt-text; - font-size: 90%; background-color: $lighter-grey; + font-size: 14px; + line-height: 22px; + + + // Appears just like other text on the page. + // See table.products tr:hover for example of revealing them + .naked_inputs & { + background-color: inherit; + height: auto; + font-size: inherit; + font-weight: inherit; + border-color: transparent; + } &:focus { outline: none; @@ -266,11 +277,11 @@ fieldset { } .form-actions { - @include defaultBoxShadow; + box-shadow: $box-shadow; background-color: $fair-pink; border: none; - border-left: 4px solid $red; - border-radius: 4px; + border-left: $border-radius solid $red; + border-radius: $border-radius; margin: 0.5em 0; padding: 0; diff --git a/app/webpacker/css/admin_v3/shared/layout.scss b/app/webpacker/css/admin_v3/shared/layout.scss index addd363521..7950b4b711 100644 --- a/app/webpacker/css/admin_v3/shared/layout.scss +++ b/app/webpacker/css/admin_v3/shared/layout.scss @@ -11,10 +11,6 @@ padding: 15px 0; margin-top: 25px; - h1 { - color: $near-black; - } - .ofn-drop-down { border: 0; background-color: $spree-blue; @@ -135,3 +131,9 @@ display: none; } } + +// Text Colors +.black-text { + color: $near-black +} +//------------ diff --git a/app/webpacker/css/admin_v3/shared/typography.scss b/app/webpacker/css/admin_v3/shared/typography.scss index a716aeeeaf..84822b39da 100644 --- a/app/webpacker/css/admin_v3/shared/typography.scss +++ b/app/webpacker/css/admin_v3/shared/typography.scss @@ -45,6 +45,7 @@ b { } // links +// Design reference: https://github.com/openfoodfoundation/openfoodnetwork/wiki/Design-styleguide%3A-links-and-buttons //-------------------------------------------------------------- a:not(.button) { color: $color-link; diff --git a/app/webpacker/css/admin_v3/terms_of_service_banner.scss b/app/webpacker/css/admin_v3/terms_of_service_banner.scss new file mode 100644 index 0000000000..70daaebb4d --- /dev/null +++ b/app/webpacker/css/admin_v3/terms_of_service_banner.scss @@ -0,0 +1,25 @@ +#banner-container { + position: fixed; + bottom: 0px; + left: 0; + width: 100%; + z-index: $tos-banner-z-index; + padding: 0 1.5%; + + .terms-of-service-banner { + display: flex; + + .column-left { + width: 70%; + font-size: 1rem; + font-weight: bold; + padding: 0.75em 1em; + } + + .column-right { + width: 30%; + padding: 0.5em 1em; + text-align: right; + } + } +} diff --git a/app/webpacker/css/darkswarm/all.scss b/app/webpacker/css/darkswarm/all.scss index a78b2c30e2..cbab208f24 100644 --- a/app/webpacker/css/darkswarm/all.scss +++ b/app/webpacker/css/darkswarm/all.scss @@ -77,5 +77,4 @@ ofn-modal { @import "../shared/question-mark-icon"; @import '../admin/shared/scroll_bar'; -@import 'app/components/help_modal_component/help_modal_component'; -@import 'app/components/confirm_modal_component/confirm_modal_component'; +@import 'app/components/modal_component/modal_component'; diff --git a/app/webpacker/js/hotkeys.js b/app/webpacker/js/hotkeys.js new file mode 100644 index 0000000000..9b14d66cf4 --- /dev/null +++ b/app/webpacker/js/hotkeys.js @@ -0,0 +1,21 @@ +import hotkeys from "hotkeys-js"; + +// Enable hotkeys on form elements +hotkeys.filter = function (event) { + var tagName = (event.target || event.srcElement).tagName; + hotkeys.setScope(/^(INPUT|TEXTAREA|SELECT|BUTTON)$/.test(tagName) ? "input" : "other"); + return true; +}; + +// Submit form +// Although 'enter' will submit the form in many cases, it doesn't cover elements such +// as select and textarea. This shortcut is a standard used across many major websites. +hotkeys("ctrl+enter, command+enter", function (event, handler) { + const form = event.target.form; + + // Simulate a click on the first available submit button. This seems to be the most robust option, + // ensuring that event handlers are handled first (eg for StimulusReflex). If there's no submit + // button, nothing happens (eg for Angular forms). + const submit = form && form.querySelector('input[type="submit"], button[type="submit"]'); + submit && submit.click(); +}); diff --git a/app/webpacker/packs/admin.js b/app/webpacker/packs/admin.js index 2980fdf0de..81735d3040 100644 --- a/app/webpacker/packs/admin.js +++ b/app/webpacker/packs/admin.js @@ -1,6 +1,7 @@ import "controllers"; import "channels"; import "@hotwired/turbo"; +import "../js/hotkeys"; import "../js/mrujs"; import "../js/matomo"; import "../js/moment"; diff --git a/app/webpacker/packs/application.js b/app/webpacker/packs/application.js index d565a6e818..5ee6a3b066 100644 --- a/app/webpacker/packs/application.js +++ b/app/webpacker/packs/application.js @@ -1,5 +1,6 @@ import "controllers"; import "@hotwired/turbo"; +import "../js/hotkeys"; import "../js/mrujs"; import "../js/matomo"; import "../js/moment"; diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 0000000000..671f09c3ee --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +load File.expand_path("spring", __dir__) +require 'bundler/setup' +load Gem.bin_path('rubocop', 'rubocop') diff --git a/bin/spring b/bin/spring index 37f429ba02..a81373ab6e 100755 --- a/bin/spring +++ b/bin/spring @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -# This file loads Spring without using loading other gems in the Gemfile, in order to be fast. +# This file loads Spring without loading other gems in the Gemfile in order to be fast. # It gets overwritten when you run the `spring binstub` command. if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"]) diff --git a/bin/update b/bin/update deleted file mode 100755 index 58bfaed518..0000000000 --- a/bin/update +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = File.expand_path('..', __dir__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') - - puts "\n== Updating database ==" - system! 'bin/rails db:migrate' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/config/application.rb b/config/application.rb index 116fb5970a..09d8fb5abe 100644 --- a/config/application.rb +++ b/config/application.rb @@ -9,8 +9,8 @@ require "rails" "action_mailer/railtie", "active_job/railtie", "action_cable/engine", - #"action_mailbox/engine", - #"action_text/engine", + # "action_mailbox/engine", + # "action_text/engine", "rails/test_unit/railtie", "sprockets/railtie" # Disable this after migrating to Webpacker ].each do |railtie| @@ -150,6 +150,7 @@ module Openfoodnetwork module ::Reporting; end Rails.application.reloader.to_prepare do next if defined?(::Reporting) && defined?(::Reporting::Errors) + loader = Zeitwerk::Loader.new loader.push_dir("#{Rails.root}/lib/reporting", namespace: ::Reporting) loader.enable_reloading @@ -227,7 +228,9 @@ module Openfoodnetwork config.action_view.form_with_generates_remote_forms = false config.active_record.cache_versioning = false config.active_record.has_many_inversing = false - config.active_record.yaml_column_permitted_classes = [BigDecimal, Symbol] + config.active_record.yaml_column_permitted_classes = [BigDecimal, Symbol, Time, + ActiveSupport::TimeWithZone, + ActiveSupport::TimeZone] config.active_support.escape_html_entities_in_json = true @@ -246,6 +249,7 @@ module Openfoodnetwork config.active_storage.content_types_to_serve_as_binary -= ["image/svg+xml"] config.active_storage.variable_content_types += ["image/svg+xml"] config.active_storage.url_options = config.action_controller.default_url_options + config.active_storage.variant_processor = :mini_magick config.exceptions_app = self.routes diff --git a/config/environments/development.rb b/config/environments/development.rb index 84dc8c9958..3dcbe29076 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -16,7 +16,6 @@ Openfoodnetwork::Application.configure do # :file_store is used by default when no cache store is specifically configured. if !!ENV["PROFILE"] || !!ENV["DEV_CACHING"] config.cache_store = :redis_cache_store, { - driver: :hiredis, url: ENV.fetch("OFN_REDIS_URL", "redis://localhost:6379/1"), expires_in: 90.minutes } diff --git a/config/environments/production.rb b/config/environments/production.rb index 6fde13c85e..d6877e67e9 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -43,7 +43,6 @@ Openfoodnetwork::Application.configure do # Use a different cache store in production config.cache_store = :redis_cache_store, { - driver: :hiredis, url: ENV.fetch("OFN_REDIS_URL", "redis://localhost:6380/0"), reconnect_attempts: 1 } diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 6fde13c85e..d6877e67e9 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -43,7 +43,6 @@ Openfoodnetwork::Application.configure do # Use a different cache store in production config.cache_store = :redis_cache_store, { - driver: :hiredis, url: ENV.fetch("OFN_REDIS_URL", "redis://localhost:6380/0"), reconnect_attempts: 1 } diff --git a/config/environments/test.rb b/config/environments/test.rb index e5cc306c18..cfba7d0307 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -15,7 +15,6 @@ Openfoodnetwork::Application.configure do # Separate cache stores when running in parallel config.cache_store = :redis_cache_store, { - driver: :hiredis, # Unique database number to avoid conflict with others url: ENV.fetch("OFN_REDIS_TEST_URL", "redis://localhost:6379/3"), reconnect_attempts: 1 diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index ab762473db..5c3c0ca585 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -145,7 +145,13 @@ end if ENV["OPENID_APP_ID"].present? && ENV["OPENID_APP_SECRET"].present? Devise.setup do |config| - protocol = Rails.env.development? ? "http://" : "https://" + site = if Rails.env.development? + # The lescommuns server accepts localhost:3000 as valid. + # So you can test in development. + "http://localhost:3000" + else + "https://#{ENV["SITE_URL"]}" + end config.omniauth :openid_connect, { name: :openid_connect, issuer: "https://login.lescommuns.org/auth/realms/data-food-consortium", @@ -158,7 +164,7 @@ if ENV["OPENID_APP_ID"].present? && ENV["OPENID_APP_SECRET"].present? client_options: { identifier: ENV["OPENID_APP_ID"], secret: ENV["OPENID_APP_SECRET"], - redirect_uri: "#{protocol}#{ENV["SITE_URL"]}/user/spree_user/auth/openid_connect/callback", + redirect_uri: "#{site}/user/spree_user/auth/openid_connect/callback", jwks_uri: 'https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/certs' } } diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb new file mode 100644 index 0000000000..ec536959f1 --- /dev/null +++ b/config/initializers/haml.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Haml 6 treats only a small set of standard boolean attributes as such. +# Other attributes are added in full. But AngularJS distinguishes between +#
+# and +#
+# +# The latter raises errors. +# +# Adding to these attributes is officially supported: +# - https://github.com/haml/haml/releases/tag/v6.2.2 +# +Haml::BOOLEAN_ATTRIBUTES.push( + *%w[ + mailto + new-tag-rule-dialog + ng-cloak + ng-transclude + offcanvas + ofn-disable-enter + question-mark-with-tooltip-animation + scroll-after-load + textangular-links-target-blank + ] +) diff --git a/config/initializers/rswag_api.rb b/config/initializers/rswag_api.rb index 15e2107512..59911cef65 100644 --- a/config/initializers/rswag_api.rb +++ b/config/initializers/rswag_api.rb @@ -5,7 +5,7 @@ Rswag::Api.configure do |config| # This is used by the Swagger middleware to serve requests for API descriptions # NOTE: If you're using rswag-specs to generate Swagger, you'll need to ensure # that it's configured to generate files in the same folder - config.swagger_root = Rails.root.join("swagger").to_s + config.openapi_root = Rails.root.join("swagger").to_s # Inject a lamda function to alter the returned Swagger prior to serialization # The function will have access to the rack env for the current request diff --git a/config/initializers/rswag_ui.rb b/config/initializers/rswag_ui.rb index bcb7c95cbe..e8f8d4096b 100644 --- a/config/initializers/rswag_ui.rb +++ b/config/initializers/rswag_ui.rb @@ -6,11 +6,11 @@ Rswag::Ui.configure do |config| # host) to the corresponding endpoint and the second is a title that will be # displayed in the document selector. # NOTE: If you're using rspec-api to expose Swagger files - # (under swagger_root) as JSON or YAML endpoints, then the list below should + # (under openapi_root) as JSON or YAML endpoints, then the list below should # correspond to the relative paths for those endpoints. - config.swagger_endpoint 'v1.yaml', 'API V1 Docs' - config.swagger_endpoint 'dfc.yaml', 'OFN DFC API Docs' + config.openapi_endpoint 'dfc.yaml', 'OFN DFC API Docs' + config.openapi_endpoint 'v1.yaml', 'API V1 Docs' # Add Basic Auth in case your API is private # config.basic_auth_enabled = true diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb index 09d946a4c3..19c782762a 100644 --- a/config/initializers/stripe.rb +++ b/config/initializers/stripe.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Add some additional properties, to allow us to access these # properties from the object, rather than calling from ENV directly. # This is mostly useful for stubbing when testing, but also feels @@ -15,4 +17,4 @@ Rails.application.reloader.to_prepare do Stripe.publishable_key = ENV['STRIPE_INSTANCE_PUBLISHABLE_KEY'] Stripe.client_id = ENV['STRIPE_CLIENT_ID'] Stripe.endpoint_secret = ENV['STRIPE_ENDPOINT_SECRET'] -end \ No newline at end of file +end diff --git a/config/locales/ar.yml b/config/locales/ar.yml index c00ddd3365..618c451135 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -334,7 +334,6 @@ ar: cancel_order: "الغاء الطلب" confirm_send_invoice: "سيتم إرسال فاتورة لهذا الطلب إلى العميل. هل أنت متأكد أنك تريد المتابعة؟" confirm_resend_order_confirmation: "هل أنت متأكد من أنك تريد إعادة إرسال رسالة تأكيد الطلب؟" - must_have_valid_business_number: "يجب أن يكون %{enterprise_name} على ABN صالح قبل إرسال الفواتير." invoice: "فاتورة" active: "نشط" cancelled: "ألغيت" @@ -473,6 +472,9 @@ ar: actions: edit: تعديل clone: استنساخ + delete: حذف + image: + edit: تعديل adjustments: skipped_changing_canceled_order: "لا يمكنك تغيير الطلب الذي تم إلغاؤه." begins_at: يبدأ عند @@ -698,6 +700,10 @@ ar: categories: label: التصنيفات search: بحث + table: + new_variant: نوع جديد + edit_image: + close: عودة product_import: title: استيراد المنتج file_not_found: لم يتم العثور على الملف أو تعذر فتحه @@ -739,6 +745,9 @@ ar: product_categories: فئات المنتجات tax_categories: فئات الضرائب shipping_categories: فئات الشحن + dfc_import_form: + enterprise: "الشركة" + import: "استيراد" import: review: مراجعة import: استيراد @@ -796,6 +805,7 @@ ar: view_products: الذهاب إلى صفحة المنتجات view_inventory: انتقل إلى صفحة المخزون product_headings: + distributor: الموزع producer: المنتج sku: SKU name: الاسم @@ -1112,6 +1122,8 @@ ar: active: نشيط؟ add_new: اضف جديد no_voucher_yet: لا قسائم حتى الآن + connected_apps: + loading: "جار التحميل" actions: edit_profile: الإعدادات properties: الخصائص @@ -1461,7 +1473,6 @@ ar: index: title: "إعدادات OIDC" connect: "اربط حسابك" - already_connected: "حسابك مرتبط بالفعل بحساب تفويض DFC هذا:" les_communs_link: "خادم معرف Les Communs" link_your_account: "تحتاج أولاً إلى ربط حسابك بموفر التفويض الذي تستخدمه DFC (Les Communs Open ID Connect)." link_account_button: "اربط حسابك في Les Communs OIDC" @@ -1777,6 +1788,8 @@ ar: invoice_tax_total: "إجمالي ضريبة السلع والخدمات:" tax_invoice: "فاتورة ضريبية" tax_total: "إجمالي الضريبة (%{rate}):" + invoice_shipping_category_delivery: "توصيل" + invoice_shipping_category_pickup: "استلام" total_excl_tax: "المجموع (باستثناء الضرائب):" total_incl_tax: "إجمالي (بما في ذلك الضرائب):" abn: "ABN:" @@ -3602,8 +3615,6 @@ ar: start_date: "تاريخ البدء" successfully_removed: "تمت الإزالة بنجاح" taxonomy_edit: "التصنيف" - taxonomy_tree_error: "خطأ شجرة التصنيف" - taxonomy_tree_instruction: "تعليمات شجرة التصنيف" tree: "شجرة" updating: "تحديث" your_order_is_empty_add_product: "طلبك فارغ ، يرجى البحث عن المنتج أعلاه وإضافته" @@ -4176,6 +4187,11 @@ ar: email: "البريد الإلكتروني" total: "المجموع" billing_address_name: "الاسم" + taxons: + form: + name: الاسم + permalink: الرابط الثابت + description: وصف general_settings: edit: legal_settings: "الإعدادات القانونية" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 60f3364ab1..8d862ea74a 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -58,6 +58,8 @@ ca: orders_close_at: Data de tancament variant_override: count_on_hand: "Disponibles" + enterprise: + white_label_logo_link: "Enllaç del logotip utilitzat a botiga" errors: models: spree/user: @@ -85,6 +87,7 @@ ca: blank: "no es pot deixar en blanc" errors: messages: + content_type_invalid: "té un tipus de contingut no vàlid" file_size_out_of_range: "la mida %{file_size} no és entre el rang necessari" limit_out_of_range: "el total està fora del marge" image_metadata_missing: "no és una imatge vàlida" @@ -100,6 +103,9 @@ ca: internal_server_error: title: "Ens sap greu, però alguna cosa no ha funcionat (505)" message_html: "Si us plau, torna a provar

Pot ser un problema temporal. Si us plau, clica al botó de tornar enrere per a tornar a la pantalla anterior o torna a l'Inici i prova una altra vegada.

Ho estem intentant

Si has vist aquest problema abans, probablement ja estem assabentats i estem treballant per a solucionar-lo. Registrem tots els errors que apareixen.

Contacte de suport

Si el problema persisteix o és urgent, si us plau informa'ns. Busca com fer-ho a la pàgina Open Food Network Local.

Ens ajuda molt que ens proporcionis tants detalls com sigui possible de què estaves fent quan es va aparèixer aquest error.

" + unprocessable_entity: + message_html: "

El canvi que volies va ser rebutjat. Potser heu intentat canviar alguna cosa a la qual no teniu accés.

Tornar a l'inici

" + stimulus_reflex_error: "Ens sap greu, però alguna cosa ha anat malament.\n\n Pot ser que sigui un problema temporal, així que si us plau, torna-ho a provar o torna a carregar la pàgina.\n Enregistrem tots els errors i és possible que estiguem treballant en una solució.\n Si el problema persisteix o és urgent, poseu-vos en contacte amb nosaltres." stripe: error_code: incorrect_number: "El número de la targeta és incorrecte." @@ -259,6 +265,10 @@ ca: integer_array_validator: not_array_error: "ha de ser una matriu" invalid_element_error: "ha de contenir només nombres enters vàlids" + report_job: + report_failed: | + Aquest informe ha fallat. Pot ser massa gran per processar-lo. + Ho analitzarem, però si el problema persisteix, si us plau, aviseu-nos. enterprise_mailer: confirmation_instructions: subject: "Sisplau, confirma l'adreça electrònica d'%{enterprise}" @@ -278,6 +288,13 @@ ca: order_cycle: subject: "Informe del cicle de comanda per %{producer}" provider_settings: "Configuració del proveïdor" + report_mailer: + report_ready: + subject: "Informe a punt" + heading: "Informe a punt per descarregar" + intro: | + L'enllaç següent caducarà al cap d'una setmana. + link_label: "%{name}" shipment_mailer: shipped_email: dear_customer: "Benvolguda consumidora:" @@ -341,7 +358,6 @@ ca: cancel_order: "Cancel·lar Comanda" confirm_send_invoice: " S'enviarà al client una factura per a aquesta comanda Està segur de continuar?" confirm_resend_order_confirmation: "Segur que vol reenviar el correu de confirmació de la comanda?" - must_have_valid_business_number: "%{enterprise_name} ha de tenir un NIF vàlid abans de poder enviar les factures." invoice: "Factura" invoices: "Factures" file: "Arxiu" @@ -450,8 +466,11 @@ ca: filters: categories: title: Categories + selected_categories: "%{count} categories seleccionades" producers: title: Productors + selected_producers: "%{count} productors seleccionats" + per_page: "%{count} elements per pàgina" colums: Columnes columns: name: Nom @@ -480,6 +499,9 @@ ca: actions: edit: Editar clone: Clonar + delete: Suprimir + image: + edit: Editar adjustments: skipped_changing_canceled_order: "No podeu canviar una comanda cancel·lada." begins_at: Comença a @@ -544,6 +566,9 @@ ca: has_n_rules: "té %{num} regles" unsaved_confirm_leave: "Hi ha canvis no desats canviats en aquesta pàgina. Continua sense desar?" available_units: "Unitats disponibles" + terms_of_service_have_been_updated_html: "Les condicions de servei de Katuma/-OFN s'han actualitzat: %{tos_link}" + terms_of_service: Llegeix les condicions del servei + accept_terms_of_service: Accepteu les condicions del servei shopfront_settings: embedded_shopfront_settings: "Configuració de botiga incrustada" enable_embedded_shopfronts: "Habilita les botigues incrustades" @@ -696,12 +721,30 @@ ca: index: header: title: Edició de productes en bloc + loading: Carregant els vostres productes + delete_modal: + delete_product_modal: + heading: "Suprimeix el producte" + prompt: "Això l'eliminarà permanentment de la vostra llista." + confirmation_text: "Suprimeix el producte" + cancellation_text: "Conserveu el producte" + delete_variant_modal: + heading: "Suprimeix la variant" + prompt: "Això l'eliminarà permanentment de la vostra llista." + confirmation_text: "Suprimeix la variant" + cancellation_text: "Mantenir la variant" sort: pagination: + total_html: "%{total} productes trobats pels vostres criteris de cerca. Mostrant de %{from} a %{to} ." + per_page: + show: Mostra + per_page: "%{num} per pàgina" clear_search: Esborra la cerca filters: search_products: Cerca de productes + search_for_producers: Cercar productors all_producers: Tots els productors + search_for_categories: Cercar categories all_categories: Totes les categories producers: label: Productors @@ -711,10 +754,20 @@ ca: no_products: no_products_found: No hem trobat productes table: - save: Guardar canvis + error_summary: + saved: + one: "%{count} producte ha estat guardat correctament, però" + other: "%{count} productes han estat guardats correctament, però " + invalid: + one: "El producte %{count} no s'ha pogut desar. Reviseu els errors i torneu-ho a provar." + other: "%{count} productes no s'han pogut desar. Reviseu els errors i torneu-ho a provar." reset: Descartar canvis + save: Guardar canvis + new_variant: Nova variant bulk_update: - success: "Els productes s'han actualitzat amb èxit" + success: Canvis guardats + edit_image: + close: Enrere product_import: title: Importació de productes file_not_found: No s'ha trobat el fitxer o no s'ha pogut obrir @@ -756,6 +809,9 @@ ca: product_categories: Tipus de productes tax_categories: Tipus d'impostos shipping_categories: Tipus d'enviament + dfc_import_form: + enterprise: "Organització" + import: "Importa" import: review: Revisa import: Importa @@ -813,6 +869,7 @@ ca: view_products: Anar a la pàgina de Productes view_inventory: Anar a la pàgina d'Inventari product_headings: + distributor: Distribuïdora producer: Productora sku: Número de referència (SKU) name: Nom @@ -1125,6 +1182,9 @@ ca: remove_logo: "Eliminar el logo" remove_logo_confirm: "Esteu segur de voler eliminar el logo?" remove_logo_success: "Logo eliminat" + white_label_logo_link_label: "Enllaç del logotip utilitzat a botiga" + connected_apps: + loading: "S'està carregant" actions: edit_profile: Configuració properties: Propietats @@ -1461,6 +1521,7 @@ ca: generate_report: "Generació d'informe" on_screen: "A la pantalla" display: Mostra + header_row: Fila de capçalera packing: name: "Informes de repartiment" subscriptions: @@ -1563,6 +1624,7 @@ ca: back: Enrere save: Desa voucher_amount: Quantitat + percentage_rate: Percentatge (%) controllers: enterprises: stripe_connect_cancelled: "S'ha cancel·lat la connexió a Stripe" @@ -1664,6 +1726,7 @@ ca: message_html: "Accepto el %{terms_and_conditions_link} del venedor i la plataforma %{tos_link}." terms_and_conditions: "Termes i condicions" submit: Finalitzar comanda + cancel: Tornar a mètodes de pagament errors: invalid_email: "siusplau, introduïu una adreça de correu electrònic vàlida" voucher_not_found: No trobat @@ -1740,8 +1803,11 @@ ca: invoice_tax_total: "Total IVA:" tax_invoice: "FACTURA D'IMPOSTOS" tax_total: "Impost total (%{rate}):" + invoice_shipping_category_delivery: "Lliurament " + invoice_shipping_category_pickup: "Recollida" total_excl_tax: "Total (impostos exclòs):" total_incl_tax: "Total (impost inclòs):" + total_all_tax: "Impost total:" abn: "NIF:" acn: "IVA intracomunitari:" invoice_issued_on: "Factura emesa el:" @@ -1955,6 +2021,7 @@ ca: order_hub_info: Informació del grup order_back_to_store: Tornar a la botiga order_back_to_cart: Tornar a la cistella + order_back_to_website: Tornar a la teva pàgina web bom_tip: "Utilitzeu aquesta pàgina per alterar les quantitats de productes en diverses comandes. Els productes també es poden eliminar de les comandes completament, si es requereix." unsaved_changes_warning: "Hi ha canvis sense desar i es perdran si continueu." unsaved_changes_error: "Els camps amb vora vermella contenen errors." @@ -2492,7 +2559,7 @@ ca: admin_enterprise_groups_web_twitter: "p. ex: @horta_josepribes" admin_enterprise_groups_web_website_placeholder: "p. ex.: www.hortajosepribes.coop" admin_order_cycles: "Cicles de comanda de l'Admin" - open: "Obert" + open: "Obre" close: "Tanca" create: "Crear" search: "Cerca" @@ -3096,6 +3163,7 @@ ca: processing: "processant" void: "pendent" invalid: "invàlid" + quantity_unavailable: "Estoc disponible insuficient. L'article de la línia no s'ha desat!" quantity_unchanged: "Quantitat sense canvis respecte a l’import anterior." resend_user_email_confirmation: resend: "Reenviar" @@ -3425,11 +3493,11 @@ ca: server_error: "Error del servidor" shipping_method_names: UPS Ground: "UPS Ground" + pick_up: "Recollida a la granja" + delivery: "Signat, segellat, lliurat" start_date: "Data d'inici" successfully_removed: "S'ha suprimit correctament" taxonomy_edit: "Edició de taxonomia" - taxonomy_tree_error: "Error d'arbre de taxonomia" - taxonomy_tree_instruction: "Instrucció d'arbre de taxonomia" tree: "Arbre" updating: "Actualitzant" your_order_is_empty_add_product: "La comanda està buida, si us plau cerca i afegeix un producte a dalt" @@ -3482,6 +3550,7 @@ ca: credit_card: "Targeta de crèdit" new_payment: "Nou pagament" capture: "Pagat" + capture_and_complete_order: "Capturar i completar comanda" void: "Pendent" login: "Inicia sessió" password: "Contrasenya" @@ -3508,6 +3577,7 @@ ca: display_currency: "Moneda de visualització" choose_currency: "Escull la moneda" mail_method_settings: "Configuració del mètode de correu" + mail_settings_notice_html: "Els canvis fets aquí seran temporals només per a la depuració i es poden revertir en el futur.
Es poden fer canvis permanents actualitzant els secrets de la instància i proporcionant-los mitjançant ofn-install . Poseu-vos en contacte amb l'equip global OFN per obtenir més detalls." general: "General" enable_mail_delivery: "Habilita el lliurament de correu" send_mails_as: "Enviar missatges com" @@ -3717,6 +3787,7 @@ ca: orders: add_product: cannot_add_item_to_canceled_order: "No es pot afegir l'article a la comanda cancel·lada" + include_out_of_stock_variants: "Inclou variants sense estoc disponible" index: listing_orders: "Llistat comandes" new_order: "Nova comanda" @@ -3734,7 +3805,13 @@ ca: results_found: "%{number} Resultats trobats." viewing: "Veient %{start} a %{end}." print_invoices: "Imprimir factures" + cancel_orders: "Cancel·lar comandes" resend_confirmation: "Reenviar Confirmació" + send_invoice: "Enviar factures" + selected: + zero: "No s'ha seleccionat cap comanda" + one: "1 comanda seleccionada" + other: "%{count} comandes seleccionades" sortable_header: payment_state: "Estat del pagament" shipment_state: "Estat de la Tramesa" @@ -3994,6 +4071,11 @@ ca: email: "Correu electrònic" total: "Total" billing_address_name: "Nom" + taxons: + form: + name: Nom + permalink: Enllaç permanent + description: Descripció general_settings: edit: legal_settings: "Paràmetres legals" @@ -4268,6 +4350,8 @@ ca: one: "1 any" other: "%{count} anys" components: + multiple_checked_select: + filter_placeholder: "Opcions de filtre" search_input: placeholder: Cerca selector_with_filter: diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 5f93d4a3d7..9aefe37d48 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -291,6 +291,10 @@ cy: integer_array_validator: not_array_error: "rhaid iddo fod yn arae" invalid_element_error: "rhaid iddo gynnwys cyfanrifau dilys yn unig" + report_job: + report_failed: | + Methodd yr adroddiad hwn. Hwyrach ei fod yn rhy fawr i’w brosesu. + Byddwn yn ymchwilio i hyn, ond cofiwch gysylltu os bydd y broblem yn parhau. enterprise_mailer: confirmation_instructions: subject: "Cadarnhewch y cyfeiriad e-bost ar gyfer %{enterprise}" @@ -380,7 +384,6 @@ cy: cancel_order: "Canslo Archeb" confirm_send_invoice: "Anfonir anfoneb ar gyfer yr archeb hon at y cwsmer. Ydych chi'n siŵr eich bod chi eisiau parhau?" confirm_resend_order_confirmation: "Ydych chi'n siŵr eich bod chi eisiau ail-anfon e-bost cadarnhau archeb?" - must_have_valid_business_number: "Noder rhif eich cwmni yma." invoice: "Anfoneb" invoices: "Anfonebau" file: "Ffeil" @@ -523,6 +526,9 @@ cy: actions: edit: Golygu clone: Clôn + delete: Dileu + image: + edit: Golygu adjustments: skipped_changing_canceled_order: "Nid yw'n bosib newid archeb a ganslwyd" begins_at: Yn dechrau am @@ -587,6 +593,9 @@ cy: has_n_rules: "%{num} rheolau'n berthnasol" unsaved_confirm_leave: "Mae yna newidiadau heb eu cadw ar y dudalen hon. Parhau heb eu cadw?" available_units: "Unedau sydd ar gael" + terms_of_service_have_been_updated_html: "Diweddarwyd Telerau Gwasanaeth y Rhwydwaith Bwyd Agored: %{tos_link}" + terms_of_service: Darllen y Telerau Gwasanaeth + accept_terms_of_service: Derbyn y Telerau Gwasanaeth shopfront_settings: embedded_shopfront_settings: "Mewnosodwyd gosodiadau blaen siop " enable_embedded_shopfronts: "Galluogi blaenau siopau a fewnosodwyd" @@ -740,6 +749,17 @@ cy: header: title: Cynhyrchion Golygu Swmp loading: Yn llwytho eich cynnyrch + delete_modal: + delete_product_modal: + heading: "Dileu cynnyrch" + prompt: "Caiff ei ddileu’n barhaol o’ch rhestr." + confirmation_text: "Dileu cynnyrch" + cancellation_text: "Cadw cynnyrch" + delete_variant_modal: + heading: "Dileu amrywiolyn" + prompt: "Caiff ei ddileu’n barhaol o’ch rhestr." + confirmation_text: "Dileu amrywiolyn" + cancellation_text: "Cadw amrywiolyn" sort: pagination: total_html: "%{total}Cafwyd hyd i cynnyrch ar gyfer eich meini prawf chwilio. Yn dangos %{from} i %{to}." @@ -749,7 +769,9 @@ cy: clear_search: Clirio'r chwiliad filters: search_products: Chwilio am gynnyrch + search_for_producers: Chwilio am gynhyrchwyr all_producers: Pob cynhyrchydd + search_for_categories: Chwilio am gategorïau all_categories: Pob categori producers: label: Prynu bwyd @@ -764,10 +786,25 @@ cy: changed_summary: one: "Addaswyd %{count} cynnyrch" other: "Addaswyd %{count} cynnyrch" - save: Cadw’r newidiadau + error_summary: + saved: + one: "Cadwyd %{count} cynnyrch yn iawn, ond" + other: "Cadwyd %{count} cynnyrch yn iawn, ond" reset: Hepgor y newidiadau + save: Cadw’r newidiadau + new_variant: Amrywiolyn newydd bulk_update: - success: "Llwyddwyd i ddiweddaru’r cynnyrch" + success: Cadwyd y newidiadau + edit_image: + title: Golygu llun y cynnyrch + close: Yn ôl + upload: Lanlwytho llun + delete_product: + success: Llwyddwyd i ddileu’r cynnyrch + error: Yn methu dileu’r cynnyrch + delete_variant: + success: Llwyddwyd i ddileu’r amrywiolyn + error: Yn methu dileu’r amrywiolyn product_import: title: Mewnforio Cynnyrch file_not_found: Ni ddaethpwyd o hyd i ffeil neu ni ellid ei hagor @@ -809,6 +846,9 @@ cy: product_categories: Categorïau Cynnyrch tax_categories: Categorïau Treth shipping_categories: Categorïau Cludo + dfc_import_form: + enterprise: "Menter" + import: "Mewnforio" import: review: Adolygu import: Mewnforio @@ -866,6 +906,7 @@ cy: view_products: Ewch i'r Dudalen Cynhyrchion view_inventory: Ewch i Dudalen y Stocrestr product_headings: + distributor: Dosbarthwr producer: Cynhyrchydd sku: Cod y Cynnyrch name: Enw @@ -909,6 +950,7 @@ cy: orders: edit: order_sure_want_to: Ydych chi'n siŵr eich bod chi am %{event} yr archeb hwn? + voucher_tax_included_in_price: "%{label} (treth wedi ei chynnwys yn y daleb)" invoice_email_sent: 'Anfonwyd e-bost anfonebu' order_email_resent: 'Ail-anfonwyd e-bost archebu' bulk_management: @@ -1011,6 +1053,7 @@ cy: images: legend: "Delweddau" logo: Logo + logo_size: "300 x 300 picsel" promo_image_placeholder: 'Mae''r ddelwedd hon i''w gweld yn ''Amdanom Ni''' promo_image_note1: 'NODER:' promo_image_note2: Bydd unrhyw ddelwedd hyrwyddo a lanlwythir yma'n cael ei thocio i 1200 x 260. @@ -1195,6 +1238,12 @@ cy: create_custom_tab: "Creu tab pwrpasol yn ffrynt y siop" custom_tab_title: "Teitl y tab pwrpasol" custom_tab_content: "Cynnwys y tab pwrpasol" + connected_apps: + legend: "Apiau cysylltiedig" + enable: "Caniatáu rhannu data" + disable: "Rhoi’r gorau i rannu" + loading: "Yn llwytho" + link_label: "Rheoli’r rhestru" actions: edit_profile: Gosodiadau properties: Manylion @@ -1424,6 +1473,8 @@ cy: has_no_payment_methods: "nid oes gan %{enterprise} unrhyw ddulliau talu" has_no_shipping_methods: "nid oes gan %{enterprise} unrhyw ddulliau anfon" has_no_enterprise_fees: "nid oes gan %{enterprise} unrhyw ffioedd menter" + flashes: + dismiss: Anwybyddu side_menu: enterprise: primary_details: "Manylion sylfaenol" @@ -1444,6 +1495,7 @@ cy: users: "Defnyddwyr" vouchers: Talebau white_label: "Label gwyn" + connected_apps: "Apiau cysylltiedig" enterprise_group: primary_details: "Manylion sylfaenol" users: "Defnyddwyr" @@ -1564,7 +1616,6 @@ cy: index: title: "OIDC Settings" connect: "Cysylltu eich Cyfrif" - already_connected: "Your account is already linked to this DFC authorization account:" les_communs_link: "Les Communs Open ID server" link_your_account: "You need first to link your account with the authorization provider used by DFC (Les Communs Open ID Connect)." link_account_button: "Link your Les Communs OIDC Account" @@ -1870,16 +1921,22 @@ cy: invoice_column_price: "Pris" invoice_column_item: "Eitem" invoice_column_qty: "Nifer" + invoice_column_weight_volume: "Pwysau / Cyfaint" invoice_column_unit_price_with_taxes: "Pris yr uned (gan gynnwys treth)" invoice_column_unit_price_without_taxes: "Pris yr uned (heb dreth)" invoice_column_price_with_taxes: "Cyfanswm pris (gan gynnwys treth)" invoice_column_price_without_taxes: "Cyfanswm pris (heb dreth)" + invoice_column_price_per_unit_without_taxes: "Pris fesul uned (heb dreth)" invoice_column_tax_rate: "Cyfradd dreth" invoice_tax_total: "Cyfanswm GST:" + invoice_cancel_and_replace_invoice: "Yn canslo ac yn disodli’r anfoneb" tax_invoice: "ANFONEB TRETH" tax_total: "Cyfanswm treth (%{rate}):" + invoice_shipping_category_delivery: "Dosbarthu" + invoice_shipping_category_pickup: "Casglu" total_excl_tax: "Cyfanswm (heb dreth):" total_incl_tax: "Cyfanswm (gan gynnwys treth):" + total_all_tax: "Cyfanswm treth:" abn: "Rhif y Cwmni:" acn: "Rhif yr Elusen" invoice_issued_on: "Cyhoeddwyd yr anfoneb ar:" @@ -2096,6 +2153,9 @@ cy: order_back_to_store: Yn ôl i'r siop order_back_to_cart: Yn ôl i'r basged order_back_to_website: Yn ôl i’r Wefan + checkout_details_title: Manylion talu + checkout_payment_title: Taliad + checkout_summary_title: Crynodeb wrth dalu bom_tip: "Defnyddiwch y dudalen hon i newid nifer cynnyrch ar draws sawl archeb. Gellir hefyd dileu cynnyrch o archebion yn gyfan gwbl, pe dymunir." unsaved_changes_warning: "Mae newidiadau heb eu cadw yn bodoli a byddant yn cael eu colli os byddwch yn parhau." unsaved_changes_error: "Mae'r meysydd â ffiniau coch yn cynnwys gwallau." @@ -2958,6 +3018,8 @@ cy: report_header_transaction_fee: Ffi Trafodiad (dim treth) report_header_total_untaxable_admin: Cyfanswm addasiadau gweinyddol na ellir eu trethu (dim treth) report_header_total_taxable_admin: Cyfanswm addasiadau gweinyddol trethadwy (sy'n cynnwys treth) + report_header_voucher_label: Label taleb + report_header_voucher_amount: "Swm y daleb (%{currency_symbol})" report_line_cost_of_produce: Cost y cynnyrch report_line_line_items: eitemau llinell report_header_last_completed_order_date: Dyddiad yr archeb ddiweddaraf a gwblhawyd @@ -3275,6 +3337,7 @@ cy: processing: "yn prosesu" void: "di-rym" invalid: "annilys" + quantity_unavailable: "Dim digon o stoc ar gael. Heb gadw’r eitem llinell!" quantity_unchanged: "Ni newidiwyd y nifer/maint o'r nifer/maint blaenorol." cancel_the_order_html: "Trwy hyn byddwch yn canslo'r archeb presennol.
Ydych chi'n siwr eich bod am wneud hyn?" cancel_the_order_send_cancelation_email: "Anfon ebost canslo at y cwsmer" @@ -3670,8 +3733,6 @@ cy: start_date: "Dyddiad cychwyn" successfully_removed: "Llwyddwyd i ddileu" taxonomy_edit: "Golygu tacsonomi" - taxonomy_tree_error: "Gwall - coeden tacsonomi" - taxonomy_tree_instruction: "Cyfarwyddyd - coeden tacsonomi" tree: "Coeden" updating: "Yn diweddaru" your_order_is_empty_add_product: "Mae eich archeb yn wag, chwiliwch am ac ychwanegwch gynnyrch uchod" @@ -3724,6 +3785,7 @@ cy: credit_card: "Cerdyn credyd" new_payment: "Taliad Newydd" capture: "Cipio" + capture_and_complete_order: "Cofnodi a chwblhau’r archeb" void: "Di-rym" login: "Mewngofnodi" password: "Cyfrinair" @@ -4005,6 +4067,7 @@ cy: from: "O" to: "Bil i" shipping: "Yn anfon" + order_number: "Rhif archeb" invoice_number: "Rhif anfoneb" payments_list: date_time: "Dyddiad / amser" @@ -4022,6 +4085,9 @@ cy: line_item_adjustments: "Addasiadau Eitemau Llinell" order_adjustments: "Addasiadau i archeb" order_total: "Cyfanswm yr archeb" + invoices: + index: + order_has_changed: "Mae’r archeb wedi newid ers diweddaru’r anfoneb ddiwethaf. Hwyrach nad yw’r anfoneb a ddangosir yma’n gyfredol bellach." overview: enterprises_header: ofn_with_tip: Cynhyrchwyr a / neu Hybiau yw mentrau a nhw yw'r uned sefydliadol sylfaenol yn y Open Food Network. @@ -4030,6 +4096,7 @@ cy: has_no_payment_methods: "does dim dulliau talu" has_no_shipping_methods: "does dim dulliau anfon nwyddau" products: + products_tip: "Y cynnyrch rydych chi’n eu gwerthu trwy’r Rhwydwaith Bwyd Agored." active_products: zero: "Nid oes gennych unrhyw gynnyrch gweithredol." one: "Mae gennych chi un cynnyrch gweithredol" @@ -4182,6 +4249,8 @@ cy: bulk_unit_size: Maint uned swmp display_as: display_as: Arddangos fel + clone: + success: Mae’r cynnyrch wedi’i glonio reports: table: select_and_search: "Dewis hidlwyr a chlicio ar %{option} i gael mynediad i'ch data." @@ -4251,6 +4320,11 @@ cy: email: "E-bost" total: "Cyfanswm" billing_address_name: "Enw" + taxons: + form: + name: Enw + permalink: Permalink + description: Disgrifiad general_settings: edit: legal_settings: "Gosodiadau Cyfreithiol" diff --git a/config/locales/de_CH.yml b/config/locales/de_CH.yml index 0aa1e4c2a7..0d0c8501fc 100644 --- a/config/locales/de_CH.yml +++ b/config/locales/de_CH.yml @@ -6,7 +6,11 @@ de_CH: spree/shipping_method: Lieferoption attributes: spree/order/ship_address: - phone: "Telefonnummer (optional)" + address1: "Lieferadresse Straße + Hausnummer" + address2: "Lieferadresse Adresszusatz" + city: "Lieferadresse Ort" + country: "Lieferadresse Land" + phone: "Telefonnummer" firstname: "Vorname" lastname: "Nachname" spree/user: @@ -328,7 +332,6 @@ de_CH: cancel_order: "Bestellung stornieren" confirm_send_invoice: "Eine Rechnung für diese Bestellung wird an den Kunden geschickt. Sind Sie sicher, dass sie fortfahren möchten?" confirm_resend_order_confirmation: "Sind Sie sicher, dass Sie die E-Mail-Bestätigung erneut senden wollen?" - must_have_valid_business_number: "%{enterprise_name} sollte eine gültige Steuer-ID-Nummer haben, bevor die Rechnung gesendet wird." invoice: "Rechnung" active: "Aktiv" cancelled: "Storniert" @@ -464,6 +467,9 @@ de_CH: actions: edit: Bearbeiten clone: Duplizieren + delete: Löschen + image: + edit: Bearbeiten adjustments: skipped_changing_canceled_order: "Eine stornierte Bestellung kann nicht geändert werden." begins_at: Beginnt @@ -684,6 +690,12 @@ de_CH: categories: label: Lieferkategorien search: Suche + no_products: + no_products_found: Keine Produkte gefunden. + table: + new_variant: Neue Produktvariante + edit_image: + close: Zurück product_import: title: Produkte importieren file_not_found: Die Datei konnte nicht gefunden oder nicht geöffnet werden. @@ -723,6 +735,9 @@ de_CH: product_categories: Produktkategorien tax_categories: Steuerkategorien shipping_categories: Lieferkategorien + dfc_import_form: + enterprise: "Unternehmen" + import: "Importieren" import: review: Überprüfung import: Importieren @@ -780,6 +795,7 @@ de_CH: view_products: Zur Produktseite view_inventory: Zur Katalogseite product_headings: + distributor: Verteilstelle producer: Produzent sku: Artikelnummer name: Name @@ -868,7 +884,7 @@ de_CH: legend: "Adresse" business_details: legend: "Geschäftsdetails" - abn: USt-IdNr. + abn: MWST nr. abn_placeholder: z. B. DE999999999 acn: St.-Nr. acn_placeholder: z. B. 93815/08152 @@ -1083,6 +1099,8 @@ de_CH: rate: Steuersatz customers: Kunde active: Aktiv? + connected_apps: + loading: "Wird geladen ..." actions: edit_profile: Einstellungen properties: Eigenschaften @@ -1549,10 +1567,11 @@ de_CH: message_html: "Sie haben bereits eine Bestellung für diesen Bestellzyklus. Überprüfen Sie den %{cart}, um die Artikel zu sehen, die Sie zuvor bestellt haben. Sie können Artikel auch stornieren, solange der Bestellzyklus geöffnet ist." step1: contact_information: + title: Kontakt Information email: label: E-Mail-Adresse phone: - label: Telefonnummer (optional) + label: Telefonnummer billing_address: title: Rechnungsadresse first_name: @@ -1700,9 +1719,11 @@ de_CH: invoice_tax_total: "Umsatzsteuersumme:" tax_invoice: "RECHNUNG" tax_total: "davon Steuern (%{rate}):" + invoice_shipping_category_delivery: "Lieferung" + invoice_shipping_category_pickup: "Abholen" total_excl_tax: "Netto (zzgl. Steuern):" total_incl_tax: "GESAMT (inkl. Steuern):" - abn: "USt-IdNr.:" + abn: "MWST nr." acn: "St.-Nr.:" invoice_issued_on: "Rechnungsdatum:" date_of_transaction: "Bestelldatum:" @@ -2310,7 +2331,7 @@ de_CH: contact_field: "Hauptansprechpartner" contact_field_placeholder: "Vorname Nachname" contact_field_required: "Bitte geben Sie einen Hauptansprechpartner ein." - phone_field: "Telefonnummer (optional)" + phone_field: "Telefonnummer" phone_field_placeholder: "z. B. +49 40 123 456" type: title: "Art des Profils" @@ -2333,7 +2354,7 @@ de_CH: enterprise_long_desc: "Ausführliche Beschreibung (empfohlen)" enterprise_long_desc_placeholder: "Erzählen Sie hier die Geschichte Ihres Unternehmens - was macht Sie anders und wunderbar? Wir empfehlen, die Beschreibung unter 600 Zeichen oder 150 Wörtern zu halten." enterprise_long_desc_length: "%{num} Zeichen/bis zu 600 empfohlen" - enterprise_abn: "USt-IdNr. (optional für Rechnungserstellung)" + enterprise_abn: "MWST nr. (optional für Rechnungserstellung)" enterprise_abn_placeholder: "z. B. DE999999999" enterprise_acn: "St.-Nr. (optional für Rechnungserstellung)" enterprise_acn_placeholder: "z. B. 93815/08152" @@ -3402,8 +3423,6 @@ de_CH: start_date: "Anfangsdatum" successfully_removed: "Erfolgreich gelöscht" taxonomy_edit: "Kategorien bearbeiten" - taxonomy_tree_error: "Fehler in Struktur der Kategorien" - taxonomy_tree_instruction: "Anleitung zur Struktur der Kategorien" tree: "Struktur" updating: "Aktualisierung" your_order_is_empty_add_product: "Ihre Bestellung ist leer. Suchen Sie oben ein Produkt und fügen Sie es hinzu." @@ -3955,6 +3974,11 @@ de_CH: email: "E-Mail-Adresse" total: "Gesamt" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Beschreibung general_settings: edit: legal_settings: "Rechtliche Einstellungen" diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index b01596f27a..ec8ad69aa6 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -387,7 +387,6 @@ de_DE: cancel_order: "Bestellung stornieren" confirm_send_invoice: "Eine Rechnung für diese Bestellung wird an den Kunden geschickt. Sind Sie sicher, dass sie fortfahren möchten?" confirm_resend_order_confirmation: "Sind Sie sicher, dass Sie die E-Mail-Bestätigung erneut senden wollen?" - must_have_valid_business_number: "%{enterprise_name} muss eine gültige Umsatzsteueridentifikationsnummer (USt-IdNr.) haben, bevor Rechnungen gesendet werden können." invoice: "Rechnung" invoices: "Rechnungen" file: "Datei" @@ -530,6 +529,9 @@ de_DE: actions: edit: Bearbeiten clone: Duplizieren + delete: Löschen + image: + edit: Bearbeiten adjustments: skipped_changing_canceled_order: "Eine stornierte Bestellung kann nicht geändert werden." begins_at: Beginnt @@ -594,6 +596,9 @@ de_DE: has_n_rules: "hat %{num} Regeln" unsaved_confirm_leave: "Es gibt ungespeicherte Änderungen auf dieser Seite. Möchten Sie ohne Speichern fortfahren?" available_units: "Verfügbare Einheiten" + terms_of_service_have_been_updated_html: "Die Nutzungsbedingungen des Open Food Network wurden aktualisiert: %{tos_link} Bitte stimmen Sie den neuen Bedingungen zu, bevor Sie die Plattform weiter verwenden." + terms_of_service: Nutzungsbedingungen lesen + accept_terms_of_service: Nutzungsbedingungen zustimmen shopfront_settings: embedded_shopfront_settings: "Einstellungen für eingebettete Gruppen" enable_embedded_shopfronts: "Eingebettete Gruppen erlauben" @@ -778,10 +783,13 @@ de_DE: invalid: one: "%{count} Produkt konnte nicht gespeichert werden. Bitte überprüfen Sie die Fehler und versuchen Sie es erneut." other: "%{count} Produkte konnten nicht gespeichert werden. Bitte überprüfen Sie die Fehler und versuchen Sie es erneut." - save: Änderungen speichern reset: Änderungen verwerfen + save: Änderungen speichern + new_variant: Neue Produktvariante bulk_update: - success: "Produkte erfolgreich aktualisiert" + success: Die Änderungen wurden gespeichert. + edit_image: + close: Zurück product_import: title: Produkte importieren file_not_found: Die Datei konnte nicht gefunden oder nicht geöffnet werden. @@ -823,6 +831,9 @@ de_DE: product_categories: Produktkategorien tax_categories: Steuerkategorien shipping_categories: Lieferkategorien + dfc_import_form: + enterprise: "Unternehmen" + import: "Importieren" import: review: Überprüfung import: Importieren @@ -880,6 +891,7 @@ de_DE: view_products: Zur Produktseite view_inventory: Zur Katalogseite product_headings: + distributor: Verteilstelle producer: Produzent sku: Artikelnummer name: Name @@ -1212,6 +1224,9 @@ de_DE: create_custom_tab: "Benutzerdefinierten Reiter im Online-Shop hinzufügen" custom_tab_title: "Titel des Reiters" custom_tab_content: "Inhalt des Reiters" + connected_apps: + legend: "Verknüpfte Apps" + loading: "Wird geladen ..." actions: edit_profile: Einstellungen properties: Eigenschaften @@ -1237,7 +1252,7 @@ de_DE: sell_your_produce: Verkaufen Sie Ihre eigenen Produkte producer_shop_description_text: Verkaufen Sie Ihre eigenen Produkte direkt an Ihre Kunden durch Ihren eigenen Produzentenladen im Open Food Network. producer_shop_description_text2: Ein Produzentenladen ist nur für Ihre eigenen Produkte bestimmt. Wenn Sie nur oder auch Produkte anderer verkaufen möchten, wählen Sie "Laden". - producer_hub: Produzentenladen + producer_hub: Laden producer_hub_text: Verkaufen Sie eigene Produkte und Produkte anderer producer_hub_description_text: Verkaufen Sie eigene Produkte und Produkte anderer Produzenten oder Läden im Open Food Network. Mit Ihrem Laden sind Sie ein zentraler Bestandteil Ihres lokalen Lebensmittelsystems. profile: Profil @@ -1461,6 +1476,7 @@ de_DE: users: "Benutzer" vouchers: Gutscheine white_label: "OFN verbergen" + connected_apps: "Verknüpfte Apps" enterprise_group: primary_details: "Unternehmen" users: "Benutzer" @@ -1581,7 +1597,6 @@ de_DE: index: title: "OIDC-Einstellungen" connect: "Verbinden Sie Ihr Benutzerkonto" - already_connected: "Ihr Benutzerkonto ist bereits mit diesem DFC-Autorisierungskonto verbunden:" les_communs_link: "Les Communs Open ID-Server" link_your_account: "Sie müssen Ihr Benutzerkonto zunächst mit dem von DFC (Les Communs Open ID Connect) verwendeten Autorisierungsanbieter verbinden." link_account_button: "Verbinden Sie Ihr Les Communs OIDC-Konto" @@ -1835,8 +1850,8 @@ de_DE: shared: mailers: powered_by: - open_food_network: "Open Food Network" - powered_html: "Ermöglicht durch das %{open_food_network}." + open_food_network: "E-Mail" + powered_html: "Ermöglicht durch das Open Food Network. Schicken Sie uns eine %{open_food_network}." menu: cart: cart: "Warenkorb/Kasse" @@ -1903,7 +1918,8 @@ de_DE: invoice_tax_total: "Umsatzsteuersumme:" tax_invoice: "RECHNUNG" tax_total: "davon Steuern (%{rate}):" - invoice_shipping_type: "Lieferoption:" + invoice_shipping_category_delivery: "Lieferoptionen" + invoice_shipping_category_pickup: "Abholen" total_excl_tax: "Netto (zzgl. Steuern):" total_incl_tax: "GESAMT (inkl. Steuern):" total_all_tax: "Steuern gesamt:" @@ -2219,7 +2235,7 @@ de_DE: shopping_contact_address: "Adresse" shopping_contact_web: "Kontakt" shopping_contact_social: "Folgen" - shopping_groups_part_of: "Ist Mitglied der Gruppe(n):" + shopping_groups_part_of: "ist Mitglied der Gruppe(n):" shopping_producers_of_hub: "Produzenten bei %{hub}:" enterprises_next_closing: "Nächster Bestellschluss" enterprises_currently_open: "Bestellzyklus ist geöffnet" @@ -3058,7 +3074,7 @@ de_DE: enterprise_fees_update_notice: Ihre Gebühren wurden aktualisiert. enterprise_register_package_error: "Bitte wählen Sie ein Paket." enterprise_register_error: "Die Registrierung für %{enterprise} konnte nicht abgeschlossen werden." - enterprise_register_success_notice: "Herzlichen Glückwünsch! Die Registrierung für %{enterprise} ist abgeschlossen!" + enterprise_register_success_notice: "Herzlichen Glückwunsch! Die Registrierung für %{enterprise} ist abgeschlossen!" enterprise_bulk_update_success_notice: "Unternehmen wurden erfolgreich aktualisiert." enterprise_bulk_update_error: 'Das Speichern der Änderungen ist fehlgeschlagen.' enterprise_shop_show_error: "Der gesuchte Laden existiert nicht oder ist im Open Food Network inaktiv. Bitte schauen Sie nach anderen Läden!" @@ -3199,7 +3215,7 @@ de_DE: producer_shop_text2: > Ein Produzentenladen ist nur für Ihre eigenen Produkte bestimmt. Wenn Sie Produkte anderer verkaufen möchten, wählen Sie "Laden". - producer_hub: Produzentenladen + producer_hub: Laden producer_hub_text1: > Verkaufen Sie eigene Produkte und Produkte anderer Produzenten oder Läden im Open Food Network. Mit Ihrem Laden sind Sie ein zentraler Bestandteil @@ -3659,8 +3675,6 @@ de_DE: start_date: "Anfangsdatum" successfully_removed: "Erfolgreich gelöscht" taxonomy_edit: "Kategorien bearbeiten" - taxonomy_tree_error: "Fehler in Struktur der Kategorien" - taxonomy_tree_instruction: "Anleitung zur Struktur der Kategorien" tree: "Struktur" updating: "Aktualisierung" your_order_is_empty_add_product: "Ihre Bestellung ist leer. Suchen Sie oben ein Produkt und fügen Sie es hinzu." @@ -4242,6 +4256,11 @@ de_DE: email: "E-Mail-Adresse" total: "Gesamt" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Beschreibung general_settings: edit: legal_settings: "Rechtliche Einstellungen" @@ -4354,7 +4373,7 @@ de_DE: picked_up_subject: "Abholbenachrichtigung" test_mailer: test_email: - greeting: "Herzlichen Glückwünsch!" + greeting: "Herzlichen Glückwunsch!" message: "Wenn Sie diese E-Mail erhalten haben, sind Ihre E-Mail-Einstellungen korrekt." subject: "Test-E-Mail" order_state: diff --git a/config/locales/el.yml b/config/locales/el.yml index c67d4ea495..97d367a68e 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -252,7 +252,6 @@ el: cancel_order: "Ακύρωση παραγγελίας" confirm_send_invoice: "Θα αποσταλεί ένα τιμολόγιο στον πελάτη για αυτή την παραγγελία,θέλετε να συνεχίσετε?" confirm_resend_order_confirmation: "Είσαστε βέβαιος ότι θέλετε να ξαναστείλετε email επιβεβαίωσης παραγγελίας?" - must_have_valid_business_number: "%{επωνυμία_επιχείρησης} πρέπει να έχει έγκυρο ABN για να αποσταλούν τα τιμολόγια." invoice: "Τιμολόγιο" more: "Περισσότερα" say_no: "Όχι" @@ -381,6 +380,9 @@ el: actions: edit: Επεξεργασία clone: Κλώνος + delete: Διαγραφή + image: + edit: Επεξεργασία adjustments: skipped_changing_canceled_order: "Δεν μπορείτε να κάνετε αλλαγή ακυρωμένης παραγγελίας." begins_at: Ξεκινάει από @@ -587,6 +589,8 @@ el: producers: label: Παραγωγοί search: Αναζήτηση + edit_image: + close: Πίσω product_import: title: Είσαγωγή Προϊόντος file_not_found: Το αρχείο δεν βρέθηκε ή δεν μπόρεσε να φορτωθεί @@ -626,6 +630,9 @@ el: product_categories: Κατηγορίες προϊόντος tax_categories: Κατηγορίες Φορολόγησης shipping_categories: Κατηγορίες Μεταφορικών + dfc_import_form: + enterprise: "Επιχείρηση" + import: "Εισαγωγή" import: review: Αναθεώρηση import: Εισαγωγή @@ -683,6 +690,7 @@ el: view_products: Μεταβείτε στη σελίδα προϊόντων view_inventory: Μετάβαση στη σελίδα αποθέματος product_headings: + distributor: Διανομέας producer: Παραγωγός sku: SKU name: Όνομα @@ -3041,8 +3049,6 @@ el: start_date: "Ημερομηνία έναρξης" successfully_removed: "Καταργήθηκε επιτυχώς" taxonomy_edit: "Επεξεργασία ταξινόμησης" - taxonomy_tree_error: "Σφάλμα δέντρου ταξινόμησης" - taxonomy_tree_instruction: "Οδηγίες δέντρων ταξινομίας" tree: "Δέντρο" updating: "Ενημέρωση" your_order_is_empty_add_product: "Η παραγγελία σας είναι κενή, αναζητήστε και προσθέστε ένα προϊόν παραπάνω" @@ -3588,6 +3594,10 @@ el: email: "Email" total: "Σύνολο" billing_address_name: "Όνομα" + taxons: + form: + name: Όνομα + description: Περιγραφή general_settings: edit: legal_settings: "Νομικές ρυθμίσεις" diff --git a/config/locales/en.yml b/config/locales/en.yml index 7a51352b98..70e19ded48 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -99,6 +99,10 @@ en: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Inheriting the tax categeory requires a per-item calculator." + spree/image: + attributes: + attachment: + integrity_error: "failed to load. Please check to ensure the file is not corrupt, and try again." spree/user: attributes: email: @@ -433,7 +437,7 @@ en: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." + must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be used." invoice: "Invoice" invoices: "Invoices" file: "File" @@ -555,6 +559,7 @@ en: colums: Columns columns: name: Name + unit_scale: Unit scale unit: Unit price: Price producer: Producer @@ -580,6 +585,9 @@ en: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." # Common properties / models @@ -653,6 +661,9 @@ en: available_units: "Available Units" + terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}" + terms_of_service: Read Terms of Service + accept_terms_of_service: Accept Terms of Service shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -746,6 +757,10 @@ en: user_guide: User Guide map: Map + dfc_product_imports: + index: + title: "Importing a DFC product catalog" + imported_products: "Imported products:" enterprise_fees: index: title: "Enterprise Fees" @@ -815,6 +830,17 @@ en: header: title: Bulk Edit Products loading: Loading your products + delete_modal: + delete_product_modal: + heading: "Delete product" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete product" + cancellation_text: "Keep product" + delete_variant_modal: + heading: "Delete variant" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete variant" + cancellation_text: "Keep variant" sort: pagination: total_html: "%{total} products found for your search criteria. Showing %{from} to %{to}." @@ -824,7 +850,9 @@ en: clear_search: Clear search filters: search_products: Search for products + search_for_producers: Search for producers all_producers: All producers + search_for_categories: Search for categories all_categories: All categories producers: label: Producers @@ -849,10 +877,21 @@ en: invalid: one: "%{count} product could not be saved. Please review the errors and try again." other: "%{count} products could not be saved. Please review the errors and try again." - save: Save changes reset: Discard changes - bulk_update: # TODO: fix these - success: "Products successfully updated" #TODO: add count + save: Save changes + new_variant: New variant + bulk_update: + success: Changes saved + edit_image: + title: Edit product photo + close: Back + upload: Upload photo + delete_product: + success: Successfully deleted the product + error: Unable to delete the product + delete_variant: + success: Successfully deleted the variant + error: Unable to delete the variant product_import: title: Product Import file_not_found: File not found or could not be opened @@ -894,6 +933,11 @@ en: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + title: "Import from DFC catalog" + enterprise: "Enterprise" + catalog_url: "DFC catalog URL" + import: "Import" import: review: Review import: Import @@ -951,6 +995,7 @@ en: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -1098,6 +1143,7 @@ en: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'This image is displayed in "About Us"' promo_image_note1: 'PLEASE NOTE:' promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. @@ -1279,6 +1325,29 @@ en: create_custom_tab: "Create custom tab in shopfront" custom_tab_title: "Title for custom tab" custom_tab_content: "Content for custom tab" + connected_apps: + legend: "Connected apps" + title: "Discover Regenerative" + tagline: "Allow Discover Regenerative to publish your enterprise information." + enable: "Allow data sharing" + disable: "Stop sharing" + loading: "Loading" + note: | + Your Open Food Network account is connected to Discover Regenerative. + Add or update information on your Discover Regenerative listing here. + link_label: "Manage listing" + description_html: | +

+ Eligible producers can showcase their regenerative credentials, + farming practices and more through a profile listing. + Simplifying how buyers can find regenerative produce and connect + with producers of interest. +

+

+ Learn more about Discover Regenerative + +

actions: edit_profile: Settings properties: Properties @@ -1510,6 +1579,8 @@ en: has_no_payment_methods: "%{enterprise} has no payment methods" has_no_shipping_methods: "%{enterprise} has no shipping methods" has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + flashes: + dismiss: Dismiss side_menu: enterprise: primary_details: "Primary Details" @@ -1530,6 +1601,7 @@ en: users: "Users" vouchers: Vouchers white_label: "White Label" + connected_apps: "Connected apps" enterprise_group: primary_details: "Primary Details" users: "Users" @@ -1650,10 +1722,15 @@ en: index: title: "OIDC Settings" connect: "Connect Your Account" - already_connected: "Your account is already linked to this DFC authorization account:" + disconnect: "Disconnect" + connected: "Your account is linked to %{uid}." les_communs_link: "Les Communs Open ID server" link_your_account: "You need first to link your account with the authorization provider used by DFC (Les Communs Open ID Connect)." link_account_button: "Link your Les Communs OIDC Account" + note_expiry: | + Tokens to access connected apps have expired. Please refresh your + account connection to keep all integrations working. + refresh: "Refresh authorisation" view_account: "To view your account, see:" subscriptions: index: @@ -1991,9 +2068,11 @@ en: invoice_column_price_per_unit_without_taxes: "Price Per unit (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "GST Total:" + invoice_cancel_and_replace_invoice: "cancels and replaces invoice" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" - invoice_shipping_type: "Type:" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" total_all_tax: "Total tax:" @@ -2227,6 +2306,9 @@ en: order_back_to_store: Back To Store 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 bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." @@ -3126,6 +3208,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_header_transaction_fee: Transaction Fee (no tax) report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + report_header_voucher_label: Voucher Label + report_header_voucher_amount: "Voucher Amount (%{currency_symbol})" report_line_cost_of_produce: Cost of produce report_line_line_items: line items report_header_last_completed_order_date: Last completed order date @@ -3805,8 +3889,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" + taxonomy_tree_error: "There was an error updating the taxonomy tree." + taxonomy_tree_instruction: "Right-click on an item to add, rename, remove or edit." tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" @@ -4119,6 +4203,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using add_product: cannot_add_item_to_canceled_order: "Cannot add item to canceled order" include_out_of_stock_variants: "Include variants with no available stock" + shipment: + mark_as_shipped_message_html: "This will mark the order as Shipped.
Are you sure you want to proceed?" + mark_as_shipped_label_message: "Send a shipment/pick up notification email to the customer." index: listing_orders: "Listing Orders" new_order: "New Order" @@ -4177,6 +4264,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using line_item_adjustments: "Line Item Adjustments" order_adjustments: "Order Adjustments" order_total: "Order Total" + invoices: + index: + order_has_changed: "The order has changed since the last invoice update. The invoice shown here might not be up-to-date anymore." overview: enterprises_header: ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. @@ -4185,6 +4275,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using has_no_payment_methods: "has no payment methods" has_no_shipping_methods: "has no shipping methods" products: + products_tip: "The products that you sell through the Open Food Network." active_products: zero: "You don't have any active products." one: "You have one active product" @@ -4337,6 +4428,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using bulk_unit_size: Bulk unit size display_as: display_as: Display As + clone: + success: Product cloned reports: table: select_and_search: "Select filters and click on %{option} to access your data." @@ -4406,6 +4499,15 @@ See the %{link} to find out more about %{sitename}'s features and to start using email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + meta_title: Meta Title + meta_description: Meta Description + meta_keywords: Meta Keywords + description: Description + dfc_id: DFC URI general_settings: edit: legal_settings: "Legal Settings" @@ -4701,3 +4803,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using pagination: next: Next previous: Previous + + + # Gem to prevent bot form submissions + invisible_captcha: + sentence_for_humans: "Please leave empty" + timestamp_error_message: "Please try again after 5 seconds." diff --git a/config/locales/en_AU.yml b/config/locales/en_AU.yml index 7ff2e2a49e..e36a51f9d2 100644 --- a/config/locales/en_AU.yml +++ b/config/locales/en_AU.yml @@ -211,7 +211,6 @@ en_AU: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -343,6 +342,9 @@ en_AU: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -549,6 +551,10 @@ en_AU: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -588,6 +594,9 @@ en_AU: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -645,6 +654,7 @@ en_AU: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -925,6 +935,8 @@ en_AU: rate: Rate customers: Customer active: Active? + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1485,6 +1497,8 @@ en_AU: invoice_tax_total: "GST Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "ABN:" @@ -3612,6 +3626,11 @@ en_AU: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_BE.yml b/config/locales/en_BE.yml index fb229c1a62..be82f1371c 100644 --- a/config/locales/en_BE.yml +++ b/config/locales/en_BE.yml @@ -197,7 +197,6 @@ en_BE: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -317,6 +316,9 @@ en_BE: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -510,6 +512,10 @@ en_BE: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -546,6 +552,9 @@ en_BE: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -603,6 +612,7 @@ en_BE: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -863,6 +873,8 @@ en_BE: vouchers: rate: Rate customers: Customer + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1370,6 +1382,8 @@ en_BE: invoice_tax_total: "GST Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "ABN:" @@ -3274,6 +3288,10 @@ en_BE: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index fe6636895b..217f311200 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -1,5 +1,8 @@ en_CA: language_name: "English" + time: + formats: + long: "%B %d, %Y %-I:%M %p" activerecord: models: spree/product: Product @@ -75,6 +78,10 @@ en_CA: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Inheriting the tax category requires a per-item calculator" + spree/image: + attributes: + attachment: + integrity_error: "failed to load. Please check to ensure the file is not corrupt, and try again." spree/user: attributes: email: @@ -384,7 +391,7 @@ en_CA: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a Business Number before invoices can be sent." + must_have_valid_business_number: "%{enterprise_name} must have a valid business number before invoices can be used." invoice: "Invoice" invoices: "Invoices" file: "File" @@ -527,6 +534,9 @@ en_CA: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -591,6 +601,9 @@ en_CA: has_n_rules: "has %{num} rules" unsaved_confirm_leave: "There are unsaved changes on this page. Continue without saving?" available_units: "Available Units" + terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}" + terms_of_service: Read Terms of Service + accept_terms_of_service: Accept Terms of Service shopfront_settings: embedded_shopfront_settings: "Embedded Group Settings" enable_embedded_shopfronts: "Enable Embedded Groups" @@ -744,6 +757,17 @@ en_CA: header: title: Bulk Edit Products loading: Loading your products + delete_modal: + delete_product_modal: + heading: "Delete product" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete product" + cancellation_text: "Keep product" + delete_variant_modal: + heading: "Delete variant" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete variant" + cancellation_text: "Keep variant" sort: pagination: total_html: "%{total} products found for your search criteria. Showing %{from} to %{to}." @@ -753,7 +777,9 @@ en_CA: clear_search: Clear search filters: search_products: Search for products + search_for_producers: Search for producers all_producers: All producers + search_for_categories: Search for categories all_categories: All categories producers: label: Producers @@ -768,10 +794,28 @@ en_CA: changed_summary: one: "%{count} product modified." other: "%{count} products modified." - save: Save changes + error_summary: + saved: + one: "%{count} product was saved correctly, but" + other: "%{count}products were saved correctly, but" + invalid: + one: "%{count} product could not be saved. Please review the errors and try again." + other: "%{count} products could not be saved. Please review the errors and try again." reset: Discard changes + save: Save changes + new_variant: New variant bulk_update: - success: "Products successfully updated" + success: Changes saved + edit_image: + title: Edit product photo + close: Back + upload: Upload photo + delete_product: + success: Successfully deleted the product + error: Unable to delete the product + delete_variant: + success: Successfully deleted the variant + error: Unable to delete the variant product_import: title: Product Import file_not_found: File not found or could not be opened @@ -813,6 +857,9 @@ en_CA: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -870,6 +917,7 @@ en_CA: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -1016,6 +1064,7 @@ en_CA: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'This image is displayed in "About Us"' promo_image_note1: 'PLEASE NOTE:' promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. @@ -1200,6 +1249,29 @@ en_CA: create_custom_tab: "Create custom tab in shopfront" custom_tab_title: "Title for custom tab" custom_tab_content: "Content for custom tab" + connected_apps: + legend: "Connected apps" + title: "Discover Regenerative" + tagline: "Allow Discover Regenerative to publish your enterprise information." + enable: "Allow data sharing" + disable: "Stop sharing" + loading: "Loading" + note: | + Your Open Food Network account is connected to Discover Regenerative. + Add or update information on your Discover Regenerative listing here. + link_label: "Manage listing" + description_html: | +

+ Eligible producers can showcase their regenerative credentials, + farming practices, and more through a profile listing. + This simplifies how buyers can find regenerative products and connect + with producers of interest. +

+

+ Learn more about Discover Regenerative + +

actions: edit_profile: Settings properties: Properties @@ -1429,6 +1501,8 @@ en_CA: has_no_payment_methods: "%{enterprise} has no payment methods" has_no_shipping_methods: "%{enterprise} has no shipping methods" has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + flashes: + dismiss: Dismiss side_menu: enterprise: primary_details: "Primary Details" @@ -1449,6 +1523,7 @@ en_CA: users: "Users" vouchers: Vouchers white_label: "White Label" + connected_apps: "Connected apps" enterprise_group: primary_details: "Primary Details" users: "Users" @@ -1568,7 +1643,6 @@ en_CA: index: title: "OIDC Settings" connect: "Connect Your Account" - already_connected: "Your account is already linked to this DFC authorization account:" les_communs_link: "Les Communs Open ID server" link_your_account: "First you need to link your account with the authorization provider used by DFC (Les Communs Open ID Connect)." link_account_button: "Link your Les Communs OIDC Account" @@ -1880,16 +1954,22 @@ en_CA: invoice_column_price: "Price" invoice_column_item: "Item" invoice_column_qty: "Qty" + invoice_column_weight_volume: "Weight/Volume" invoice_column_unit_price_with_taxes: "Unit price (Incl. tax)" invoice_column_unit_price_without_taxes: "Unit price (Excl. tax)" invoice_column_price_with_taxes: "Total price (Incl. tax)" invoice_column_price_without_taxes: "Total price (Excl. tax)" + invoice_column_price_per_unit_without_taxes: "Price per Unit (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "Sales Tax:" + invoice_cancel_and_replace_invoice: "cancels and replaces invoice" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total " + total_all_tax: "Total tax:" abn: "Business Number:" acn: "Business Number:" invoice_issued_on: "Invoice issued on:" @@ -2106,6 +2186,9 @@ en_CA: order_back_to_store: Back To Store 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 bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." unsaved_changes_error: "Fields with red borders contain errors." @@ -2968,6 +3051,8 @@ en_CA: report_header_transaction_fee: Transaction Fee (no tax) report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + report_header_voucher_label: Voucher Label + report_header_voucher_amount: "Voucher Amount (%{currency_symbol})" report_line_cost_of_produce: Cost of produce report_line_line_items: line items report_header_last_completed_order_date: Last completed order date @@ -3282,6 +3367,7 @@ en_CA: processing: "processing" void: "void" invalid: "invalid" + quantity_unavailable: "Insufficient stock available. Item not saved." quantity_unchanged: "Quantity unchanged from previous amount." cancel_the_order_html: "This will cancel the current order.
Are you sure you want to proceed?" cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer." @@ -3632,8 +3718,6 @@ en_CA: start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" @@ -3686,6 +3770,7 @@ en_CA: credit_card: "Credit Card" new_payment: "New Payment" capture: "Capture" + capture_and_complete_order: "Capture and complete order" void: "Void" login: "Login" password: "Password" @@ -3927,6 +4012,9 @@ en_CA: add_product: cannot_add_item_to_canceled_order: "You cannot add an item to a cancelled order." include_out_of_stock_variants: "Include variants with no available stock" + shipment: + mark_as_shipped_message_html: "This will mark the order as shipped.
Are you sure you want to proceed?" + mark_as_shipped_label_message: "Send a shipment/pick up notification email to the customer." index: listing_orders: "Listing Orders" new_order: "New Order" @@ -3985,6 +4073,9 @@ en_CA: line_item_adjustments: "Line Item Adjustments" order_adjustments: "Order Adjustments" order_total: "Order Total" + invoices: + index: + order_has_changed: "The order has changed since the last invoice update. The invoice shown here might not be up-to-date anymore. " overview: enterprises_header: ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. @@ -3993,6 +4084,7 @@ en_CA: has_no_payment_methods: "has no payment methods" has_no_shipping_methods: "has no shipping methods" products: + products_tip: "The products that you sell through the Open Food Network." active_products: zero: "You don't have any active products." one: "You have one active product" @@ -4145,6 +4237,8 @@ en_CA: bulk_unit_size: Bulk unit size display_as: display_as: Display As + clone: + success: Product cloned reports: table: select_and_search: "Select filters and click on %{option} to access your data." @@ -4214,6 +4308,15 @@ en_CA: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + meta_title: Meta Title + meta_description: Meta Description + meta_keywords: Meta Keywords + description: Description + dfc_id: DFC URI general_settings: edit: legal_settings: "Legal Settings" @@ -4509,3 +4612,6 @@ en_CA: pagination: next: Next previous: Previous + invisible_captcha: + sentence_for_humans: "Please leave empty" + timestamp_error_message: "Please try again after 5 seconds." diff --git a/config/locales/en_DE.yml b/config/locales/en_DE.yml index 993a4ec845..e75e9957e4 100644 --- a/config/locales/en_DE.yml +++ b/config/locales/en_DE.yml @@ -200,7 +200,6 @@ en_DE: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -322,6 +321,9 @@ en_DE: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -515,6 +517,10 @@ en_DE: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -553,6 +559,9 @@ en_DE: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -610,6 +619,7 @@ en_DE: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -871,6 +881,8 @@ en_DE: vouchers: rate: Rate customers: Customer + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1380,6 +1392,8 @@ en_DE: invoice_tax_total: "GST Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "ABN:" @@ -3289,6 +3303,10 @@ en_DE: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index 1d8bf10057..f433c2202b 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -78,6 +78,10 @@ en_FR: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Inheriting the tax categeory requires a per-item calculator." + spree/image: + attributes: + attachment: + integrity_error: "failed to load. Please check to ensure the file is not corrupt, and try again." spree/user: attributes: email: @@ -387,7 +391,7 @@ en_FR: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." + must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be used." invoice: "Invoice" invoices: "Invoices" file: "File" @@ -505,6 +509,7 @@ en_FR: colums: Columns columns: name: Name + unit_scale: Unit scale unit: Unit price: Price producer: Producer @@ -530,6 +535,9 @@ en_FR: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -594,6 +602,9 @@ en_FR: has_n_rules: "has %{num} rules" unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" available_units: "Available Units" + terms_of_service_have_been_updated_html: "CoopCircuits Terms of Service have been updated: %{tos_link}" + terms_of_service: Read Terms of Service + accept_terms_of_service: Accept Terms of Service shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -747,6 +758,17 @@ en_FR: header: title: Bulk Edit Products loading: Loading your products + delete_modal: + delete_product_modal: + heading: "Delete product" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete product" + cancellation_text: "Keep product" + delete_variant_modal: + heading: "Delete variant" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete variant" + cancellation_text: "Keep variant" sort: pagination: total_html: "%{total} products found for your search criteria. Showing %{from} to %{to}." @@ -756,7 +778,9 @@ en_FR: clear_search: Clear search filters: search_products: Search for products + search_for_producers: Search for producers all_producers: All producers + search_for_categories: Search for categories all_categories: All categories producers: label: Producers @@ -778,10 +802,21 @@ en_FR: invalid: one: "%{count} product could not be saved. Please review the errors and try again." other: "%{count} products could not be saved. Please review the errors and try again." - save: Save changes reset: Discard changes + save: Save changes + new_variant: New variant bulk_update: - success: "Products successfully updated" + success: Changes saved + edit_image: + title: Edit product photo + close: Back + upload: Upload photo + delete_product: + success: Successfully deleted the product + error: Unable to delete the product + delete_variant: + success: Successfully deleted the variant + error: Unable to delete the variant product_import: title: Product Import file_not_found: File not found or could not be opened @@ -823,6 +858,9 @@ en_FR: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -880,6 +918,7 @@ en_FR: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -1026,6 +1065,7 @@ en_FR: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'This image is displayed in "About Us"' promo_image_note1: 'PLEASE NOTE:' promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. @@ -1210,6 +1250,29 @@ en_FR: create_custom_tab: "Create custom tab in shopfront" custom_tab_title: "Title for custom tab" custom_tab_content: "Content for custom tab" + connected_apps: + legend: "Connected apps" + title: "Discover Regenerative" + tagline: "Allow Discover Regenerative to publish your enterprise information." + enable: "Allow data sharing" + disable: "Stop sharing" + loading: "Loading" + note: | + Your Open Food Network account is connected to Discover Regenerative. + Add or update information on your Discover Regenerative listing here. + link_label: "Manage listing" + description_html: | +

+ Eligible producers can showcase their regenerative credentials, + farming practices and more through a profile listing. + Simplifying how buyers can find regenerative produce and connect + with producers of interest. +

+

+ Learn more about Discover Regenerative + +

actions: edit_profile: Settings properties: Properties @@ -1439,6 +1502,8 @@ en_FR: has_no_payment_methods: "%{enterprise} has no payment methods" has_no_shipping_methods: "%{enterprise} has no shipping methods" has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + flashes: + dismiss: Dismiss side_menu: enterprise: primary_details: "Primary Details" @@ -1459,6 +1524,7 @@ en_FR: users: "Users" vouchers: Vouchers white_label: "White Label" + connected_apps: "Connected apps" enterprise_group: primary_details: "Primary Details" users: "Users" @@ -1578,10 +1644,15 @@ en_FR: index: title: "OIDC Settings" connect: "Connect Your Account" - already_connected: "Your account is already linked to this authorization account:" + disconnect: "Disconnect" + connected: "Your account is linked to %{uid}." les_communs_link: "Les Communs Open ID server" link_your_account: "You need first to link your account with the authorization provider used by Les Communs Open ID Connect." link_account_button: "Link your Les Communs OIDC Account" + note_expiry: | + Tokens to access connected apps have expired. Please refresh your + account connection to keep all integrations working. + refresh: "Refresh authorisation" view_account: "To view your account, see:" subscriptions: index: @@ -1898,9 +1969,11 @@ en_FR: invoice_column_price_per_unit_without_taxes: "Price Per unit (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "GST Total:" + invoice_cancel_and_replace_invoice: "cancels and replaces invoice" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" - invoice_shipping_type: "Type:" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" total_all_tax: "Total tax:" @@ -2120,6 +2193,9 @@ en_FR: order_back_to_store: Back To Store 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 bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." unsaved_changes_error: "Fields with red borders contain errors." @@ -2982,6 +3058,8 @@ en_FR: report_header_transaction_fee: Transaction Fee (no tax) report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + report_header_voucher_label: Voucher Label + report_header_voucher_amount: "Voucher Amount (%{currency_symbol})" report_line_cost_of_produce: Cost of produce report_line_line_items: line items report_header_last_completed_order_date: Last completed order date @@ -3648,8 +3726,8 @@ en_FR: start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" + taxonomy_tree_error: "There was an error updating the taxonomy tree." + taxonomy_tree_instruction: "Right-click on an item to add, rename, remove or edit." tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" @@ -3944,6 +4022,9 @@ en_FR: add_product: cannot_add_item_to_canceled_order: "Cannot add item to canceled order" include_out_of_stock_variants: "Include variants with no available stock" + shipment: + mark_as_shipped_message_html: "This will mark the order as Shipped.
Are you sure you want to proceed?" + mark_as_shipped_label_message: "Send a shipment/pick up notification email to the customer." index: listing_orders: "Listing Orders" new_order: "New Order" @@ -4002,6 +4083,9 @@ en_FR: line_item_adjustments: "Line Item Adjustments" order_adjustments: "Order Adjustments" order_total: "Order Total" + invoices: + index: + order_has_changed: "The order has changed since the last invoice update. The invoice shown here might not be up-to-date anymore." overview: enterprises_header: ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. @@ -4010,6 +4094,7 @@ en_FR: has_no_payment_methods: "has no payment methods" has_no_shipping_methods: "has no shipping methods" products: + products_tip: "The products that you sell through CoopCircuits." active_products: zero: "You don't have any active products." one: "You have one active product" @@ -4162,6 +4247,8 @@ en_FR: bulk_unit_size: Bulk unit size display_as: display_as: Display As + clone: + success: Product cloned reports: table: select_and_search: "Select filters and click on %{option} to access your data." @@ -4231,6 +4318,15 @@ en_FR: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + meta_title: Meta Title + meta_description: Meta Description + meta_keywords: Meta Keywords + description: Description + dfc_id: DFC URI general_settings: edit: legal_settings: "Legal Settings" @@ -4526,3 +4622,6 @@ en_FR: pagination: next: Next previous: Previous + invisible_captcha: + sentence_for_humans: "Please leave empty" + timestamp_error_message: "Please try again after 5 seconds." diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index bdb2239f89..7917fab67f 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -291,6 +291,10 @@ en_GB: integer_array_validator: not_array_error: "must be an array" invalid_element_error: "must contain only valid integers" + report_job: + report_failed: | + This report failed. It may be too big to process. + We will look into it but please let us know if the problem persists. enterprise_mailer: confirmation_instructions: subject: "Please confirm the email address for %{enterprise}" @@ -380,7 +384,6 @@ en_GB: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "Please enter your company number." invoice: "Invoice" invoices: "Invoices" file: "File" @@ -523,6 +526,9 @@ en_GB: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -587,6 +593,9 @@ en_GB: has_n_rules: "has %{num} rules" unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" available_units: "Available Units" + terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}" + terms_of_service: Read Terms of Service + accept_terms_of_service: Accept Terms of Service shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -740,6 +749,16 @@ en_GB: header: title: Bulk Edit Products loading: Loading your products + delete_modal: + delete_product_modal: + heading: "Delete product" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete product" + cancellation_text: "Keep product" + delete_variant_modal: + heading: "Delete variant" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete variant" sort: pagination: total_html: "%{total}productsfound for your search criteria. Showing %{from}to%{to}." @@ -749,7 +768,9 @@ en_GB: clear_search: Clear search filters: search_products: Search for products + search_for_producers: Search for producers all_producers: All producers + search_for_categories: Search for categories all_categories: All categories producers: label: Buy @@ -764,10 +785,25 @@ en_GB: changed_summary: one: "%{count}product modified." other: "%{count}products modified." - save: Save changes + error_summary: + saved: + one: "%{count}product was saved correctly, but " + other: "%{count}products were saved correctly, but " reset: Discard changes + save: Save changes + new_variant: New variant bulk_update: - success: "Products successfully updated" + success: Changes saved + edit_image: + title: Edit product photo + close: Back + upload: Upload photo + delete_product: + success: Successfully deleted the product + error: Unable to delete the product + delete_variant: + success: Successfully deleted the variant + error: Unable to delete the variant product_import: title: Product Import file_not_found: File not found or could not be opened @@ -809,6 +845,9 @@ en_GB: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -866,6 +905,7 @@ en_GB: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: Product Code name: Name @@ -909,6 +949,7 @@ en_GB: orders: edit: order_sure_want_to: Are you sure you want to %{event} this order? + voucher_tax_included_in_price: "%{label}(tax included in voucher)" invoice_email_sent: 'Invoice email has been sent' order_email_resent: 'Order email has been resent' bulk_management: @@ -1011,6 +1052,7 @@ en_GB: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'This image is displayed in "About Us"' promo_image_note1: 'PLEASE NOTE:' promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. @@ -1195,6 +1237,29 @@ en_GB: create_custom_tab: "Create custom tab in shopfront" custom_tab_title: "Title for custom tab" custom_tab_content: "Content for custom tab" + connected_apps: + legend: "Connected apps" + title: "Discover Regenerative" + tagline: "Allow Discover Regenerative to publish your enterprise information." + enable: "Allow data sharing" + disable: "Stop sharing" + loading: "Loading" + note: | + Your Open Food Network account is connected to Discover Regenerative. + Add or update information on your Discover Regenerative listing here. + link_label: "Manage listing" + description_html: | +

+ Eligible producers can showcase their regenerative credentials, + farming practices and more through a profile listing. + Simplifying how buyers can find regenerative produce and connect + with producers of interest. +

+

+ Learn more about Discover Regenerative + +

actions: edit_profile: Settings properties: Properties @@ -1424,6 +1489,8 @@ en_GB: has_no_payment_methods: "%{enterprise} has no payment methods" has_no_shipping_methods: "%{enterprise} has no shipping methods" has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + flashes: + dismiss: Dismiss side_menu: enterprise: primary_details: "Primary Details" @@ -1444,6 +1511,7 @@ en_GB: users: "Users" vouchers: Vouchers white_label: "White Label" + connected_apps: "Connected apps" enterprise_group: primary_details: "Primary Details" users: "Users" @@ -1563,7 +1631,6 @@ en_GB: index: title: "OIDC Settings" connect: "Connect Your Account" - already_connected: "Your account is already linked to this DFC authorisation account:" les_communs_link: "Les Communs Open ID server" link_your_account: "You need first to link your account with the authorisation provider used by DFC (Les Communs Open ID Connect)." link_account_button: "Link your Les Communs OIDC Account" @@ -1875,16 +1942,22 @@ en_GB: invoice_column_price: "Price" invoice_column_item: "Item" invoice_column_qty: "Qty" + invoice_column_weight_volume: "Weight / VOL." invoice_column_unit_price_with_taxes: "Unit price (Incl. tax)" invoice_column_unit_price_without_taxes: "Unit price (Excl. tax)" invoice_column_price_with_taxes: "Total price (Incl. tax)" invoice_column_price_without_taxes: "Total price (Excl. tax)" + invoice_column_price_per_unit_without_taxes: "Price Per unit (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "VAT Total:" + invoice_cancel_and_replace_invoice: "cancels and replaces invoice" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" + total_all_tax: "Total tax:" abn: "Company Number:" acn: "Charity Number:" invoice_issued_on: "Invoice issued on:" @@ -2042,12 +2115,12 @@ en_GB: brandstory_part5_strong: "And what’s just as important as the software itself are the values which underpin it. " brandstory_part6: "If you sell good food - as a farmer, farmer’s market, food co-op, or food hub- then choose software that aligns with your values to build food systems for people and planet, not profit. By working collectively rather than competitively, we share the costs of developing new software, and we ensure that our project is resilient!" system_headline: "Selling with us - 3 easy steps" - system_step1: "1. Open your shop" - system_step1_text: "• Add your shop name\n• Enter a description and contact details\n• Add photos and social media details " + system_step1: "1. Open Your Shop" + system_step1_text: "Add your shop name, a description and some contact details." system_step2: "2. Add Your Products" - system_step2_text: "• Create your products \n• Set prices, stock levels, descriptions and images\n• Connect with other local producers to stock their products (optional)" + system_step2_text: "Create products, set stock levels and open an order cycle." system_step3: "3. Plan Your Deliveries" - system_step3_text: "• Set up payment methods \n• Create pick-up and/or delivery options\n• Create subscriptions for regular orders" + system_step3_text: "Set up payment methods and add your collection and delivery options." cta_headline: "The sustainable food network of our future." cta_label: "Start shopping" stats_headline: "Join over 15k shoppers and producers in creating a new food system" @@ -2101,6 +2174,8 @@ en_GB: order_back_to_store: Back To Store order_back_to_cart: Back To Cart order_back_to_website: Back To Website + checkout_details_title: Checkout Details + checkout_payment_title: Checkout Payment bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." unsaved_changes_error: "Fields with red borders contain errors." @@ -2963,6 +3038,8 @@ en_GB: report_header_transaction_fee: Transaction Fee (no tax) report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + report_header_voucher_label: Voucher Label + report_header_voucher_amount: "Voucher Amount (%{currency_symbol})" report_line_cost_of_produce: Cost of produce report_line_line_items: line items report_header_last_completed_order_date: Last completed order date @@ -3284,6 +3361,7 @@ en_GB: processing: "processing" void: "void" invalid: "invalid" + quantity_unavailable: "Insufficient stock available. Line item unsaved!" quantity_unchanged: "Quantity unchanged from previous amount." cancel_the_order_html: "This will cancel the current order.
Are you sure you want to proceed?" cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer" @@ -3634,8 +3712,6 @@ en_GB: start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" @@ -3688,6 +3764,7 @@ en_GB: credit_card: "Credit Card" new_payment: "New Payment" capture: "Capture" + capture_and_complete_order: "Capture and complete order" void: "Void" login: "Sign in" password: "Password" @@ -3987,6 +4064,9 @@ en_GB: line_item_adjustments: "Line Item Adjustments" order_adjustments: "Order Adjustments" order_total: "Order Total" + invoices: + index: + order_has_changed: "The order has changed since the last invoice update. The invoice shown here might not be up-to-date anymore." overview: enterprises_header: ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. @@ -3995,6 +4075,7 @@ en_GB: has_no_payment_methods: "has no payment methods" has_no_shipping_methods: "has no shipping methods" products: + products_tip: "The products that you sell through the Open Food Network." active_products: zero: "You don't have any active products." one: "You have one active product" @@ -4147,6 +4228,8 @@ en_GB: bulk_unit_size: Bulk unit size display_as: display_as: Display As + clone: + success: Product cloned reports: table: select_and_search: "Select filters and click on %{option} to access your data." @@ -4216,6 +4299,11 @@ en_GB: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_IE.yml b/config/locales/en_IE.yml index c389ffc5a1..2ec7d875a2 100644 --- a/config/locales/en_IE.yml +++ b/config/locales/en_IE.yml @@ -1,5 +1,8 @@ en_IE: language_name: "English" + time: + formats: + long: "%B %d, %Y %-l:%M %p" activerecord: models: spree/product: Product @@ -75,6 +78,10 @@ en_IE: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Inheriting the tax categeory requires a per-item calculator." + spree/image: + attributes: + attachment: + integrity_error: "failed to load. Please check to ensure the file is not corrupt, and try again." spree/user: attributes: email: @@ -384,7 +391,7 @@ en_IE: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "Please enter your company number." + must_have_valid_business_number: "%{enterprise_name} must have a company number before invoices can be used." invoice: "Invoice" invoices: "Invoices" file: "File" @@ -502,6 +509,7 @@ en_IE: colums: Columns columns: name: Name + unit_scale: Unit scale unit: Unit price: Price producer: Producer @@ -527,6 +535,9 @@ en_IE: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -591,6 +602,9 @@ en_IE: has_n_rules: "has %{num} rules" unsaved_confirm_leave: "There are unsaved changed on this page. Continue without saving?" available_units: "Available Units" + terms_of_service_have_been_updated_html: "Open Food Network's Terms of Service have been updated: %{tos_link}" + terms_of_service: Read Terms of Service + accept_terms_of_service: Accept Terms of Service shopfront_settings: embedded_shopfront_settings: "Embedded Shopfront Settings" enable_embedded_shopfronts: "Enable Embedded Shopfronts" @@ -615,7 +629,7 @@ en_IE: title: "Invoice Settings" enable_invoices?: "Enable Invoices?" invoice_style2?: "Use the alternative invoice model that includes total tax breakdown per rate and tax rate info per item (not yet suitable for countries displaying prices excluding tax)" - enterprise_number_required_on_invoices?: "Require an ABN to generate an invoice?" + enterprise_number_required_on_invoices?: "Require a company number to generate an invoice?" stripe_connect_settings: edit: title: "Stripe Connect" @@ -744,6 +758,17 @@ en_IE: header: title: Bulk Edit Products loading: Loading your products + delete_modal: + delete_product_modal: + heading: "Delete product" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete product" + cancellation_text: "Keep product" + delete_variant_modal: + heading: "Delete variant" + prompt: "This will permanently remove it from your list." + confirmation_text: "Delete variant" + cancellation_text: "Keep variant" sort: pagination: total_html: "%{total} products found for your search criteria. Showing %{from} to %{to}." @@ -753,7 +778,9 @@ en_IE: clear_search: Clear search filters: search_products: Search for products + search_for_producers: Search for producers all_producers: All producers + search_for_categories: Search for categories all_categories: All categories producers: label: Producers @@ -775,10 +802,21 @@ en_IE: invalid: one: "%{count} product could not be saved. Please review the errors and try again." other: "%{count} products could not be saved. Please review the errors and try again." - save: Save changes reset: Discard changes + save: Save changes + new_variant: New variant bulk_update: - success: "Products successfully updated" + success: Changes saved + edit_image: + title: Edit product photo + close: Back + upload: Upload photo + delete_product: + success: Successfully deleted the product + error: Unable to delete the product + delete_variant: + success: Successfully deleted the variant + error: Unable to delete the variant product_import: title: Product Import file_not_found: File not found or could not be opened @@ -820,6 +858,9 @@ en_IE: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -877,6 +918,7 @@ en_IE: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: Product Code name: Name @@ -1023,6 +1065,7 @@ en_IE: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'This image is displayed in "About Us"' promo_image_note1: 'PLEASE NOTE:' promo_image_note2: Any promo image uploaded here will be cropped to 1200 x 260. @@ -1207,6 +1250,29 @@ en_IE: create_custom_tab: "Create custom tab in shopfront" custom_tab_title: "Title for custom tab" custom_tab_content: "Content for custom tab" + connected_apps: + legend: "Connected apps" + title: "Discover Regenerative" + tagline: "Allow Discover Regenerative to publish your enterprise information." + enable: "Allow data sharing" + disable: "Stop sharing" + loading: "Loading" + note: | + Your Open Food Network account is connected to Discover Regenerative. + Add or update information on your Discover Regenerative listing here. + link_label: "Manage listing" + description_html: | +

+ Eligible producers can showcase their regenerative credentials, + farming practices and more through a profile listing. + Simplifying how buyers can find regenerative produce and connect + with producers of interest. +

+

+ Learn more about Discover Regenerative + +

actions: edit_profile: Settings properties: Properties @@ -1436,6 +1502,8 @@ en_IE: has_no_payment_methods: "%{enterprise} has no payment methods" has_no_shipping_methods: "%{enterprise} has no shipping methods" has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + flashes: + dismiss: Dismiss side_menu: enterprise: primary_details: "Primary Details" @@ -1456,6 +1524,7 @@ en_IE: users: "Users" vouchers: Vouchers white_label: "White Label" + connected_apps: "Connected apps" enterprise_group: primary_details: "Primary Details" users: "Users" @@ -1575,10 +1644,15 @@ en_IE: index: title: "OIDC Settings" connect: "Connect Your Account" - already_connected: "Your account is already linked to this DFC authorization account:" + disconnect: "Disconnect" + connected: "Your account is linked to %{uid}." les_communs_link: "Les Communs Open ID server" link_your_account: "You need first to link your account with the authorization provider used by DFC (Les Communs Open ID Connect)." link_account_button: "Link your Les Communs OIDC Account" + note_expiry: | + Tokens to access connected apps have expired. Please refresh your + account connection to keep all integrations working. + refresh: "Refresh authorisation" view_account: "To view your account, see:" subscriptions: index: @@ -1895,9 +1969,11 @@ en_IE: invoice_column_price_per_unit_without_taxes: "Price Per unit (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "VAT Total:" + invoice_cancel_and_replace_invoice: "cancels and replaces invoice" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" - invoice_shipping_type: "Type:" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" total_all_tax: "Total tax:" @@ -1916,8 +1992,8 @@ en_IE: menu_4_url: "/groups" menu_5_title: "About" menu_5_url: "https://about.openfoodnetwork.ie" - menu_6_title: "Blog" - menu_6_url: "https://about.openfoodnetwork.ie/blog" + menu_6_title: "Resources" + menu_6_url: "https://about.openfoodnetwork.ie/resources" menu_7_title: "Support" menu_7_url: "https://about.openfoodnetwork.ie/support" logo: "Logo (640x130)" @@ -2117,6 +2193,9 @@ en_IE: order_back_to_store: Back To Store 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 bom_tip: "Use this page to alter product quantities across multiple orders. Products may also be removed from orders entirely, if required." unsaved_changes_warning: "Unsaved changes exist and will be lost if you continue." unsaved_changes_error: "Fields with red borders contain errors." @@ -2979,6 +3058,8 @@ en_IE: report_header_transaction_fee: Transaction Fee (no tax) report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax) report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive) + report_header_voucher_label: Voucher Label + report_header_voucher_amount: "Voucher Amount (%{currency_symbol})" report_line_cost_of_produce: Cost of produce report_line_line_items: line items report_header_last_completed_order_date: Last completed order date @@ -3300,6 +3381,7 @@ en_IE: processing: "processing" void: "void" invalid: "invalid" + quantity_unavailable: "Insufficient stock available. Line item unsaved!" quantity_unchanged: "Quantity unchanged from previous amount." cancel_the_order_html: "This will cancel the current order.
Are you sure you want to proceed?" cancel_the_order_send_cancelation_email: "Send a cancellation email to the customer" @@ -3650,8 +3732,8 @@ en_IE: start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" + taxonomy_tree_error: "There was an error updating the taxonomy tree." + taxonomy_tree_instruction: "Right-click on an item to add, rename, remove or edit." tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your order is empty, please search for and add a product above" @@ -3704,6 +3786,7 @@ en_IE: credit_card: "Credit Card" new_payment: "New Payment" capture: "Capture" + capture_and_complete_order: "Capture and complete order" void: "Void" login: "Login" password: "Password" @@ -3945,6 +4028,9 @@ en_IE: add_product: cannot_add_item_to_canceled_order: "Cannot add item to canceled order" include_out_of_stock_variants: "Include variants with no available stock" + shipment: + mark_as_shipped_message_html: "This will mark the order as Shipped.
Are you sure you want to proceed?" + mark_as_shipped_label_message: "Send a shipment/pick up notification email to the customer." index: listing_orders: "Listing Orders" new_order: "New Order" @@ -4003,6 +4089,9 @@ en_IE: line_item_adjustments: "Line Item Adjustments" order_adjustments: "Order Adjustments" order_total: "Order Total" + invoices: + index: + order_has_changed: "The order has changed since the last invoice update. The invoice shown here might not be up-to-date anymore." overview: enterprises_header: ofn_with_tip: Enterprises are Producers and/or Hubs and are the basic unit of organisation within the Open Food Network. @@ -4011,6 +4100,7 @@ en_IE: has_no_payment_methods: "has no payment methods" has_no_shipping_methods: "has no shipping methods" products: + products_tip: "The products that you sell through the Open Food Network." active_products: zero: "You don't have any active products." one: "You have one active product" @@ -4163,6 +4253,8 @@ en_IE: bulk_unit_size: Bulk unit size display_as: display_as: Display As + clone: + success: Product cloned reports: table: select_and_search: "Select filters and click on %{option} to access your data." @@ -4232,6 +4324,15 @@ en_IE: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + meta_title: Meta Title + meta_description: Meta Description + meta_keywords: Meta Keywords + description: Description + dfc_id: DFC URI general_settings: edit: legal_settings: "Legal Settings" @@ -4527,3 +4628,6 @@ en_IE: pagination: next: Next previous: Previous + invisible_captcha: + sentence_for_humans: "Please leave empty" + timestamp_error_message: "Please try again after 5 seconds." diff --git a/config/locales/en_IN.yml b/config/locales/en_IN.yml index 02e85add1c..e1101b2857 100644 --- a/config/locales/en_IN.yml +++ b/config/locales/en_IN.yml @@ -193,9 +193,9 @@ en_IN: title: "Open Food Network India" welcome_to: "Welcome to " site_meta_description: "The Open Food Network India software platform allows farmers to sell produce online, at a price that works for them. It has been built specifically for selling food so it can handle tricky measures or stock levels that only food has - a dozen eggs, a bunch of parsley, a whole chicken that varies in weight…" - search_by_name: Search by name, town, State or postcode... - producers_join: India producers are now welcome to join Open Food Network India. - charges_sales_tax: Charges VAT? + search_by_name: Search by name, town, state or PIN code... + producers_join: Indian producers are now welcome to join Open Food Network India. + charges_sales_tax: Charges GST? print_invoice: "Print Invoice" print_ticket: "Print Ticket" select_ticket_printer: "Select printer for tickets" @@ -207,7 +207,6 @@ en_IN: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid ABN before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -330,6 +329,9 @@ en_IN: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -533,6 +535,10 @@ en_IN: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -572,6 +578,9 @@ en_IN: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -629,6 +638,7 @@ en_IN: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: Product Code name: Name @@ -897,6 +907,8 @@ en_IN: rate: Rate customers: Customer active: Active? + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1437,6 +1449,8 @@ en_IN: invoice_tax_total: "VAT Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "Company Number:" @@ -1485,8 +1499,8 @@ en_IN: city: City city_placeholder: eg. Newcastle state: State - postcode: Postcode - postcode_placeholder: eg. 3070 + postcode: PIN Code + postcode_placeholder: eg. 400081 suburb: Suburb country: Country unauthorized: Unauthorized @@ -1580,14 +1594,14 @@ en_IN: cookies_policy_link: "cookies policy" cookies_accept_button: "Accept Cookies" home_shop: Shop Now - brandstory_headline: "Growing Local Food Online" - brandstory_intro: "THE online community helping you to build a successful food enterprise" - brandstory_part1: "The Open Food Network software platform allows farmers to sell produce online, at a price that works for them. It has been built specifically for selling food so it can handle tricky measures or stock levels that only food has - a dozen eggs, a bunch of parsley, a whole chicken that varies in weight…" - brandstory_part2: "Food Producers, Farmer Producer Organizations (FPO), or Farmer Producer Companies (FPC) can create an online shop, collect payments, and sell through other shops on this platform." - brandstory_part3: "Wholesalers, Farmer Producer Organizations (FPO), or Farmer Producer Companies (FPC) can integrate OFN with their existing systems and manage buying groups to supply customers with their produce through our national network of food hubs and shops." - brandstory_part4: "Farmer Producer Organizations (FPO), or Farmer Producer Companies (FPC) can bring together producers in their local area to create virtual farmers’ markets, building a resilient local food economy." - brandstory_part5_strong: "And what’s just as important as the software itself are the values which underpin it. " - brandstory_part6: "If you sell good food - as a farmer, farmer’s market, food co-op, or food hub- then choose software that aligns with your values to build food systems for people and planet, not profit. By working collectively rather than competitively, we share the costs of developing new software, and we ensure that our project is resilient!" + brandstory_headline: "Buy Organic & Local Food." + brandstory_intro: "Search for local farmers and food producers." + brandstory_part1: "Choose from thousands of farmers and food producers who produce food using sustainable farming methods without any harmful chemicals." + brandstory_part2: "When you buy your food from local farmers, you support the local economy." + brandstory_part3: "If you are a farmer or food producer and would like to sell your produce online without paying any commission, register on Open Food Network, India today and start selling. Best B2B or B2C online marketplace developed for the farmers." + brandstory_part4: "This software is developed by a Not-For-Profit organization to help farmers sell their produce online." + brandstory_part5_strong: "We call it Open Foor Network India, or OFN India." + brandstory_part6: "If you sell good food - as a farmer, farmers market, food co-op, or food hub- then OFN India is your best option." system_headline: "Selling on OFN - 3 easy steps" system_step1: "1. Create new account" system_step1_text: "Set up your enterprise with a name, description, photos, contact details and social media links." @@ -1870,7 +1884,7 @@ en_IN: sell_hubs_detail: "Set up a profile for your food enterprise or organisation on the OFN. At any time you can upgrade your profile to a multi-producer shop." sell_groups_detail: "Set up a tailored directory of enterprises (producers and other food enterprises) for your region or for your organisation." sell_user_guide: "Find out more in our user guide." - sell_listing_price: "Listing a profile on OFN India is free. Plans for shops and hubs start from as little as ₹100 per month. For more detail on pricing visit https://about.openfoodindia.org/pricing/ ." + sell_listing_price: "Listing a profile on OFN India is free. Plans for shops and hubs start from as little as ₹375 per month. For more detail on pricing visit https://about.openfoodindia.org/pricing/ ." sell_embed: "We collectively budget for new feature development from the international OFN community. This way the huge cost of good software development can be shared. If you want a new feature, chances are someone in France, South Africa, Australia, India or Brazil might want it too! Use the Community Forum to suggest features you'd like to see." sell_ask_services: "Ask us about OFN services." shops_title: Shops @@ -2005,18 +2019,18 @@ en_IN: producer: "Great! First we need to know a little bit about your farm:" enterprise_name_field: "Enterprise Name:" producer_name_field: "Farm Name:" - producer_name_field_placeholder: "e.g. Charlie's Chilli Farm" + producer_name_field_placeholder: "e.g. Prashant Patil's Farm" producer_name_field_error: "Sorry, this name has already been taken. Please try another." address1_field: "Address line 1:" - address1_field_placeholder: "e.g. 123 Apple Drive" + address1_field_placeholder: "e.g. Lokmanya Tilak Road" address1_field_error: "Please enter an address" address2_field: "Address line 2:" suburb_field: "Town:" - suburb_field_placeholder: "eg. Taunton" + suburb_field_placeholder: "eg. Solapur" suburb_field_error: "Please enter a postal town" - postcode_field: "Postcode:" - postcode_field_placeholder: "eg. TA1 TAA" - postcode_field_error: "Postcode required" + postcode_field: "PIN Code:" + postcode_field_placeholder: "eg. 400081" + postcode_field_error: "PIN Code required" state_field: "State:" state_field_error: "State Required" country_field: "Country:" @@ -2083,7 +2097,7 @@ en_IN: enterprise_final_step: "Final step!" enterprise_social_text: "How can people find %{enterprise} online?" website: "Website" - website_placeholder: "eg. openfoodnetwork.org.au" + website_placeholder: "eg. openfoodnetwork.in" facebook: "Facebook" facebook_placeholder: "eg. www.facebook.com/PageNameHere" linkedin: "LinkedIn" @@ -3523,6 +3537,10 @@ en_IN: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_NZ.yml b/config/locales/en_NZ.yml index 9cf954d5ef..765448476d 100644 --- a/config/locales/en_NZ.yml +++ b/config/locales/en_NZ.yml @@ -329,7 +329,6 @@ en_NZ: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid GST number before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -465,6 +464,9 @@ en_NZ: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -686,6 +688,10 @@ en_NZ: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -725,6 +731,9 @@ en_NZ: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -782,6 +791,7 @@ en_NZ: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -1088,6 +1098,8 @@ en_NZ: rate: Rate customers: Customer active: Active? + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1705,6 +1717,8 @@ en_NZ: invoice_tax_total: "GST Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "GST No.:" @@ -3871,6 +3885,11 @@ en_NZ: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_PH.yml b/config/locales/en_PH.yml index ca7a199432..2809b9821f 100644 --- a/config/locales/en_PH.yml +++ b/config/locales/en_PH.yml @@ -206,7 +206,6 @@ en_PH: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "%{enterprise_name} must have a valid TIN before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -329,6 +328,9 @@ en_PH: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -525,6 +527,10 @@ en_PH: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -563,6 +569,9 @@ en_PH: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -620,6 +629,7 @@ en_PH: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -886,6 +896,8 @@ en_PH: vouchers: rate: Rate customers: Customer + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1416,6 +1428,8 @@ en_PH: invoice_tax_total: "VAT Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "TIN:" @@ -3457,6 +3471,10 @@ en_PH: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index 9b23d64d37..5ef675891d 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -307,7 +307,6 @@ en_US: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: " %{enterprise_name}must have a business identifier, Tax ID or W9 before invoices can be sent." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -443,6 +442,9 @@ en_US: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit adjustments: skipped_changing_canceled_order: "You can't change a cancelled order." begins_at: Begins At @@ -664,6 +666,10 @@ en_US: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -703,6 +709,9 @@ en_US: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -760,6 +769,7 @@ en_US: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -1051,6 +1061,8 @@ en_US: rate: Rate customers: Customer active: Active? + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1657,6 +1669,8 @@ en_US: invoice_tax_total: "Tax total:" tax_invoice: "INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "License, Certification, or Business ID:" @@ -3340,8 +3354,6 @@ en_US: start_date: "Start date" successfully_removed: "Successfully Removed" taxonomy_edit: "Taxonomy edit" - taxonomy_tree_error: "Taxonomy tree error" - taxonomy_tree_instruction: "Taxonomy tree instruction" tree: "Tree" updating: "Updating" your_order_is_empty_add_product: "Your cart is empty, please search for and add a product above" @@ -3893,6 +3905,11 @@ en_US: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/en_ZA.yml b/config/locales/en_ZA.yml index 368cb0d49f..d9865ca8e6 100644 --- a/config/locales/en_ZA.yml +++ b/config/locales/en_ZA.yml @@ -206,7 +206,6 @@ en_ZA: cancel_order: "Cancel Order" confirm_send_invoice: "An invoice for this order will be sent to the customer. Are you sure you want to continue?" confirm_resend_order_confirmation: "Are you sure you want to resend the order confirmation email?" - must_have_valid_business_number: "Please enter your company number." invoice: "Invoice" active: "Active" cancelled: "Cancelled" @@ -329,6 +328,9 @@ en_ZA: actions: edit: Edit clone: Clone + delete: Delete + image: + edit: Edit begins_at: Begins At begins_on: Begins On customer: Customer @@ -529,6 +531,10 @@ en_ZA: categories: label: Categories search: Search + table: + new_variant: New variant + edit_image: + close: Back product_import: title: Product Import file_not_found: File not found or could not be opened @@ -567,6 +573,9 @@ en_ZA: product_categories: Product Categories tax_categories: Tax Categories shipping_categories: Shipping Categories + dfc_import_form: + enterprise: "Enterprise" + import: "Import" import: review: Review import: Import @@ -624,6 +633,7 @@ en_ZA: view_products: Go To Products Page view_inventory: Go To Inventory Page product_headings: + distributor: Distributor producer: Producer sku: SKU name: Name @@ -888,6 +898,8 @@ en_ZA: vouchers: rate: Rate customers: Customer + connected_apps: + loading: "Loading" actions: edit_profile: Settings properties: Properties @@ -1418,6 +1430,8 @@ en_ZA: invoice_tax_total: "VAT Total:" tax_invoice: "TAX INVOICE" tax_total: "Total tax (%{rate}):" + invoice_shipping_category_delivery: "Delivery" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" abn: "Company Number:" @@ -3350,6 +3364,10 @@ en_ZA: email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + description: Description general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/es.yml b/config/locales/es.yml index 1cf3c71853..7bb5c36599 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -338,7 +338,6 @@ es: cancel_order: "Cancelar pedido" confirm_send_invoice: "Una factura para esta orde se envió al cliente. ¿Está seguro que quiere continuar?" confirm_resend_order_confirmation: "¿Estás seguro que quieres reenviar el correo de confirmación del pedido?" - must_have_valid_business_number: "%{enterprise_name} debe tener un NIF válido antes de que las facturas se puedan enviar." invoice: "Factura" active: "Activo" cancelled: "Cancelado" @@ -474,6 +473,9 @@ es: actions: edit: Editar clone: Duplicar + delete: Borrar + image: + edit: Editar adjustments: skipped_changing_canceled_order: "No puede cambiar un pedido cancelado." begins_at: Empieza en @@ -702,10 +704,11 @@ es: no_products_found: No se encontraron productos import_products: Importar varios productos table: - save: Guardar Cambios reset: Desechar Cambios - bulk_update: - success: "Los productos se actualizaron correctamente" + save: Guardar Cambios + new_variant: Nueva variante + edit_image: + close: Atrás product_import: title: Importación de productos file_not_found: Archivo no encontrado o no se pudo abrir @@ -745,6 +748,9 @@ es: product_categories: Categorías de Producto tax_categories: Categorías de impuestos shipping_categories: Categorías de envío + dfc_import_form: + enterprise: "Organización" + import: "Importar" import: review: Revisión import: Importar @@ -802,6 +808,7 @@ es: view_products: Ir a la página de productos view_inventory: Ir a la página de inventario product_headings: + distributor: Distribuidora producer: Productora sku: SKU name: Nombre @@ -1119,6 +1126,8 @@ es: remove_logo: "Eliminar el logo" remove_logo_confirm: "¿Está seguro de que desea eliminar este logo?" remove_logo_success: "Logo eliminado" + connected_apps: + loading: "Cargando" actions: edit_profile: Configuración properties: Propiedades @@ -1758,6 +1767,8 @@ es: invoice_tax_total: "IVA Total:" tax_invoice: "FACTURA DE IMPUESTOS" tax_total: "Total impuestos (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Recoger" total_excl_tax: "Total (Sin Impuestos):" total_incl_tax: "Total (Impuestos incl.)" abn: "NIF:" @@ -3484,8 +3495,6 @@ es: start_date: "Fecha de inicio" successfully_removed: "Eliminado con éxito" taxonomy_edit: "editar taxonomía" - taxonomy_tree_error: "Error del árbol de taxonomía" - taxonomy_tree_instruction: "Instrucción del árbol de taxonomía" tree: "Árbol" updating: "Actualizando" your_order_is_empty_add_product: "Su pedido está vacío, busque y añada un producto arriba" @@ -4047,6 +4056,11 @@ es: email: "Email" total: "Total" billing_address_name: "Nombre" + taxons: + form: + name: Nombre + permalink: Enlace permanente + description: Descripción general_settings: edit: legal_settings: "Configuraciones legales" diff --git a/config/locales/es_CO.yml b/config/locales/es_CO.yml index d26a0bddc4..8f0bc6c24e 100644 --- a/config/locales/es_CO.yml +++ b/config/locales/es_CO.yml @@ -226,7 +226,6 @@ es_CO: cancel_order: "Cancelar pedido" confirm_send_invoice: "Al cliente se enviará una factura por este pedido. ¿Desea continuar?" confirm_resend_order_confirmation: "¿Estás seguro que quieres reenviar el correo de confirmación del pedido?" - must_have_valid_business_number: "%{enterprise_name} debe tener un NIT válido antes de que las facturas se puedan enviar." invoice: "Factura" active: "Activo" cancelled: "Cancelado" @@ -353,6 +352,9 @@ es_CO: actions: edit: Editar clone: Duplicar + delete: Borrar + image: + edit: Editar begins_at: Empieza en begins_on: Comienza en customer: Cliente @@ -553,6 +555,10 @@ es_CO: categories: label: Categorías search: Buscar + table: + new_variant: Nueva variante + edit_image: + close: Atrás product_import: title: Importe de producto file_not_found: Archivo no encontrado o no se pudo abrir @@ -592,6 +598,9 @@ es_CO: product_categories: Categorías de producto tax_categories: Categorías de impuestos shipping_categories: Categorías de envío + dfc_import_form: + enterprise: "Organización" + import: "Importar" import: review: Revisión import: Importar @@ -649,6 +658,7 @@ es_CO: view_products: Ir a la página de productos view_inventory: Ir a la página de inventario product_headings: + distributor: Distribuidor producer: Productor sku: SKU name: Nombre @@ -920,6 +930,8 @@ es_CO: vouchers: rate: Impuesto customers: Cliente + connected_apps: + loading: "Cargando" actions: edit_profile: Configuración properties: Propiedades @@ -1465,6 +1477,8 @@ es_CO: invoice_tax_total: "Total IVA:" tax_invoice: "FACTURA DE IMPUESTOS" tax_total: "Total impuestos (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Recoger" total_excl_tax: "Total (sin impuestos):" total_incl_tax: "Total (Impuestos incl.)" abn: "NIT:" @@ -3583,6 +3597,10 @@ es_CO: email: "Correo electrónico" total: "Total" billing_address_name: "Nombre" + taxons: + form: + name: Nombre + description: Descripción general_settings: edit: legal_settings: "Configuraciones legales" diff --git a/config/locales/es_CR.yml b/config/locales/es_CR.yml index d9ac04bc3e..84817dcefc 100644 --- a/config/locales/es_CR.yml +++ b/config/locales/es_CR.yml @@ -329,7 +329,6 @@ es_CR: cancel_order: "Cancelar pedido" confirm_send_invoice: "Al cliente se enviará una factura por este pedido. ¿Desea continuar?" confirm_resend_order_confirmation: "¿Estás seguro que quieres reenviar el correo de confirmación del pedido?" - must_have_valid_business_number: "%{enterprise_name} debe tener un NIF válido antes de que las facturas se puedan enviar." invoice: "Factura" active: "Activo" cancelled: "Cancelado" @@ -465,6 +464,9 @@ es_CR: actions: edit: Editar clone: Duplicar + delete: Borrar + image: + edit: Editar adjustments: skipped_changing_canceled_order: "No se puede cambiar una orden cancelada" begins_at: Empieza en @@ -690,6 +692,10 @@ es_CR: categories: label: Categorías search: Buscar + table: + new_variant: Nueva variante + edit_image: + close: Atrás product_import: title: Importe de producto file_not_found: Archivo no encontrado o no se pudo abrir @@ -729,6 +735,9 @@ es_CR: product_categories: Categorías de producto tax_categories: Categorías de impuestos shipping_categories: Categorías de envío + dfc_import_form: + enterprise: "Organización" + import: "Importar" import: review: Revisión import: Importar @@ -786,6 +795,7 @@ es_CR: view_products: Ir a la página de productos view_inventory: Ir a la página de inventario product_headings: + distributor: Distribuidor producer: Productor sku: SKU name: Nombre @@ -1095,6 +1105,8 @@ es_CR: rate: Impuesto customers: Cliente active: Activo? + connected_apps: + loading: "Cargando" actions: edit_profile: Configuración properties: Propiedades @@ -1714,6 +1726,8 @@ es_CR: invoice_tax_total: "Total IVA:" tax_invoice: "FACTURA DE IMPUESTOS" tax_total: "Total impuestos (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Recoger" total_excl_tax: "Total (sin impuestos):" total_incl_tax: "Total (Impuestos incl.)" abn: "NIF:" @@ -3920,6 +3934,11 @@ es_CR: email: "Correo electrónico" total: "Total" billing_address_name: "Nombre" + taxons: + form: + name: Nombre + permalink: Enlace permanente + description: Descripción general_settings: edit: legal_settings: "Configuraciones legales" diff --git a/config/locales/es_US.yml b/config/locales/es_US.yml index 4c5d1ac6a3..14ba901734 100644 --- a/config/locales/es_US.yml +++ b/config/locales/es_US.yml @@ -306,7 +306,6 @@ es_US: cancel_order: "Cancelar pedido" confirm_send_invoice: "Una factura para esta orde se envió al cliente. ¿Está seguro que quiere continuar?" confirm_resend_order_confirmation: "¿Estás seguro que quieres reenviar el correo de confirmación del pedido?" - must_have_valid_business_number: "%{enterprise_name} debe tener un NIF válido antes de que las facturas se puedan enviar." invoice: "Factura" active: "Activo" cancelled: "Cancelado" @@ -442,6 +441,9 @@ es_US: actions: edit: Editar clone: Duplicar + delete: Borrar + image: + edit: Editar adjustments: skipped_changing_canceled_order: "No puede modificar un pedido cancelado." begins_at: Empieza en @@ -662,6 +664,10 @@ es_US: categories: label: Categorías search: Buscar + table: + new_variant: Nueva variante + edit_image: + close: Atrás product_import: title: Importación de productos file_not_found: Archivo no encontrado o no se pudo abrir @@ -701,6 +707,9 @@ es_US: product_categories: Categorías de Producto tax_categories: Categorías de impuestos shipping_categories: Categorías de envío + dfc_import_form: + enterprise: "Organización" + import: "Importar" import: review: Revisión import: Importar @@ -758,6 +767,7 @@ es_US: view_products: Ir a la página de productos view_inventory: Ir a la página de inventario product_headings: + distributor: Distribuidor producer: Productora sku: SKU name: Nombre @@ -1050,6 +1060,8 @@ es_US: rate: Impuesto customers: Consumidora active: ¿Activo? + connected_apps: + loading: "Cargando" actions: edit_profile: Configuración properties: Propiedades @@ -1618,6 +1630,8 @@ es_US: invoice_tax_total: "IVA Total:" tax_invoice: "FACTURA DE IMPUESTOS" tax_total: "Total impuestos (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Recoger" total_excl_tax: "Total (Sin Impuestos):" total_incl_tax: "Total (Impuestos incl.)" abn: "NIF:" @@ -3839,6 +3853,11 @@ es_US: email: "Email" total: "Total" billing_address_name: "Nombre" + taxons: + form: + name: Nombre + permalink: Enlace permanente + description: Descripción general_settings: edit: legal_settings: "Configuraciones legales" diff --git a/config/locales/fil_PH.yml b/config/locales/fil_PH.yml index 358c00a155..2399d5de45 100644 --- a/config/locales/fil_PH.yml +++ b/config/locales/fil_PH.yml @@ -206,7 +206,6 @@ fil_PH: cancel_order: "kanselahin ang order" confirm_send_invoice: "ang invoice para sa order na ito ay ipapadala sa customer. nais mo bang magpatuloy?" confirm_resend_order_confirmation: "nais mo bang ipadala muli ang order confirmation email?" - must_have_valid_business_number: "%{enterprise_name}ay dapat may valid na TIN bago makapagpadala ng mga invoice" invoice: "invoice" active: "aktibo" cancelled: "kanselado" @@ -329,6 +328,9 @@ fil_PH: actions: edit: i-edit clone: Gayahin + delete: Tanggalin + image: + edit: i-edit begins_at: nagsimula sa begins_on: nagsisimula sa customer: Customer @@ -526,6 +528,10 @@ fil_PH: categories: label: mga kategorya search: hanapin + table: + new_variant: bagong uri + edit_image: + close: bumalik product_import: title: paglipat ng produkto file_not_found: 'ang file ay hindi mahanap o mabuksan ' @@ -564,6 +570,9 @@ fil_PH: product_categories: kategorya ng produkto tax_categories: kategorya ng tax shipping_categories: mga kategorya ng pagpapadala + dfc_import_form: + enterprise: "Enterprise" + import: "ilipat" import: review: suriin import: i-import @@ -621,6 +630,7 @@ fil_PH: view_products: pumunta sa pahina ng mga produkto view_inventory: pumunta sa pahina ng mga imbentaryo product_headings: + distributor: Distributor producer: Producer sku: SKU name: Pangalan @@ -888,6 +898,8 @@ fil_PH: vouchers: rate: antas customers: Customer + connected_apps: + loading: "naglo-load" actions: edit_profile: Settings properties: mga katangian @@ -1418,6 +1430,8 @@ fil_PH: invoice_tax_total: "kabuuang GST:" tax_invoice: "TAX INVOICE" tax_total: "Kabuuang Tax (%{rate}):" + invoice_shipping_category_delivery: "pagdeliver" + invoice_shipping_category_pickup: "Pickup" total_excl_tax: "Kabuuan (Hindi kasama ang tax):" total_incl_tax: "Kabuuan (Kasama ang tax):" abn: "TIN:" @@ -3469,6 +3483,10 @@ fil_PH: email: "Email" total: "kabuuan" billing_address_name: "Pangalan" + taxons: + form: + name: Pangalan + description: paglalarawan general_settings: edit: legal_settings: "Legal Settings" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 0f833aced0..ae5adfccf4 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1,5 +1,8 @@ fr: language_name: "Français" + time: + formats: + long: "%e %b %Y, %-k:%M" activerecord: models: spree/product: Produit @@ -75,6 +78,10 @@ fr: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Déterminer la catégorie de taxe requiert l'utilisation du calculateur par article. " + spree/image: + attributes: + attachment: + integrity_error: "Echec du chargement. Merci de vérifier que le fichier n'est pas endommagé et réessayer." spree/user: attributes: email: @@ -303,7 +310,7 @@ fr: email_registered: "va, dès que vous aurez choisi un type d'entreprise, être créé sur" email_userguide_html: "Le Guide Utilisateur est là pour vous accompagner dans la prise en main de la plateforme. Vous y trouverez les informations détaillées de paramétrage de votre entreprise, de vos produits si vous êtes producteur, et éventuellement de votre boutique. Pour accéder au guide en français, cliquez ici : %{link}" userguide: "Guide utilisateur" - email_admin_html: "Pour gérez votre entreprise, rendez-vous sur l’%{link}. Si vous choisissez CoopCircuits pour organiser vos ventes, l’utilisation est gratuite les 3 premiers mois pour vous permettre de tester notre solution. Au-delà, vous devrez choisir une de nos offres pour continuer à utiliser le service (voir rubrique “nos offres” sur notre site internet)." + email_admin_html: "Pour gérer votre entreprise, rendez-vous sur l’%{link}. Si vous choisissez CoopCircuits pour organiser vos ventes, l’utilisation est gratuite les 3 premiers mois pour vous permettre de tester notre solution. Au-delà, vous devrez choisir une de nos offres pour continuer à utiliser le service (voir rubrique “nos offres” sur notre site internet)." admin_panel: "interface d'administration" email_community_html: "Nous avons aussi un forum de discussion en ligne pour échanger avec la communauté sur des questions liées à la plateforme ou aux défis de la gestion d'un circuit court. Nous vous invitons à y participer ! %{link}" join_community: "Accéder au forum" @@ -383,7 +390,7 @@ fr: cancel_order: "Annuler la commande" confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" - must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." + must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées. " invoice: "Facture" invoices: "Factures" file: "fichier" @@ -501,6 +508,7 @@ fr: colums: Colonnes columns: name: Nom + unit_scale: Échelle d'unité unit: Unité price: Prix producer: Producteur @@ -526,6 +534,9 @@ fr: actions: edit: Modifier clone: Dupliquer + delete: Supprimer + image: + edit: Modifier adjustments: skipped_changing_canceled_order: "Vous ne pouvez pas modifier une commande annulée." begins_at: Commence @@ -590,6 +601,9 @@ fr: has_n_rules: "a %{num} règles" unsaved_confirm_leave: "Des modifications n'ont pas été sauvegardées et seront perdues si vous quittez la page. Souhaitez-vous quitter la page?" available_units: "Unités disponibles" + terms_of_service_have_been_updated_html: "Les conditions d'utilisation de CoopCircuits ont été mises à jour : %{tos_link}" + terms_of_service: Lire les conditions d'utilisation + accept_terms_of_service: Accepter les conditions d'utilisation shopfront_settings: embedded_shopfront_settings: "Paramètres Boutiques Intégrées" enable_embedded_shopfronts: "Autoriser l'intégration des boutiques" @@ -743,16 +757,29 @@ fr: header: title: Gestion du catalogue produits loading: Vos produits sont en cours de chargement + delete_modal: + delete_product_modal: + heading: "Supprimer produit" + prompt: "Cette action va supprimer ceci de façon permanente de votre liste." + confirmation_text: "Supprimer produit" + cancellation_text: "Conserver le produit" + delete_variant_modal: + heading: "Supprimer la variante" + prompt: "Cette action va supprimer ceci de façon permanente de votre liste." + confirmation_text: "Supprimer la variante" + cancellation_text: "Conserver la variante" sort: pagination: - total_html: "%{total} produits trouvés selon vos critères de recherche. Montrer %{from} à %{to}." + total_html: "%{total} produits trouvés selon vos critères de recherche. Résultats %{from} à %{to}." per_page: show: Montrer per_page: "%{num} par page" clear_search: Annuler la recherche filters: search_products: Chercher des produits + search_for_producers: Rechercher des producteurs all_producers: Tous les producteurs + search_for_categories: Rechercher des catégories all_categories: Toutes les catégories producers: label: Producteurs @@ -765,20 +792,31 @@ fr: no_products_found_for_search: Aucun produit trouvé avec vos critères de recherche table: changed_summary: - one: "%{count} produits modifiés" + one: "%{count} produit modifié" other: "%{count} produits modifiés" error_summary: saved: one: "%{count} le produit a été sauvegardé correctement, mais " other: "%{count} les produits ont été sauvegardés correctement, mais" invalid: - one: "Le produit %{count} n'a pas pu être enregistré. Veuillez examiner les erreurs et réessayer." - many: "Les produits %{count} n'ont pas pu être enregistrés. Veuillez examiner les erreurs et réessayer." - other: "Les produits %{count} n'ont pas pu être enregistrés. Veuillez examiner les erreurs et réessayer." - save: Sauvegarder les changements + one: "%{count} produit n'a pas pu être sauvegardé. Veuillez examiner les erreurs et réessayer." + many: "%{count} produits n'ont pas pu être sauvegardés. Veuillez examiner les erreurs et réessayer." + other: "%{count} produits n'ont pas pu être sauvegardés. Veuillez examiner les erreurs et réessayer." reset: Annuler les changements + save: Sauvegarder les changements + new_variant: Nouvelle variante bulk_update: - success: "Produits mis à jour" + success: Changements sauvegardés + edit_image: + title: Modifier la photo du produit + close: Retour + upload: Télécharger la photo + delete_product: + success: Le produit a bien été supprimé + error: Le produit n'a pas pu être supprimé + delete_variant: + success: La variante a bien été supprimée + error: La variante n'a pas pu être supprimée product_import: title: Import liste produits file_not_found: Fichier non trouvé ou impossible à ouvrir @@ -820,6 +858,9 @@ fr: product_categories: Catégorie Produit tax_categories: TVA applicable shipping_categories: Conditions de transport + dfc_import_form: + enterprise: "Entreprise" + import: "Importer" import: review: Vérifier import: Importer @@ -877,6 +918,7 @@ fr: view_products: Aller à la page Produits view_inventory: Aller à la page Catalogues Boutiques product_headings: + distributor: Boutique producer: Producteur sku: Référence Produit name: Nom @@ -920,7 +962,7 @@ fr: orders: edit: order_sure_want_to: Êtes-vous sûr de vouloir %{event} cette commande ? - voucher_tax_included_in_price: "%{label} (taxe inclue dans le bon de promotion)" + voucher_tax_included_in_price: "%{label} (taxe inclue dans le bon de réduction)" invoice_email_sent: 'L''email de facturation a bien été envoyé' order_email_resent: 'L''email de commande a de nouveau été envoyé' bulk_management: @@ -1023,6 +1065,7 @@ fr: images: legend: "Images" logo: Logo + logo_size: "300 x 300 pixels" promo_image_placeholder: 'Cette image est affichée dans "A propos"' promo_image_note1: 'ATTENTION:' promo_image_note2: Votre bannière doit mesurer 1200 x 260, toute image non conforme sera rognée. @@ -1184,8 +1227,8 @@ fr: email_confirmed: "Email confirmé" email_not_confirmed: "Email non confirmé" vouchers: - legend: Bon de réduction - voucher_code: Code promo + legend: Bons de réduction + voucher_code: Code de réduction rate: Taux label: Nom purpose: Raison @@ -1208,6 +1251,29 @@ fr: create_custom_tab: "Créer un onglet personnalisé dans la boutique" custom_tab_title: "Titre de l'onglet personnalisé" custom_tab_content: "Contenu de l'onglet personnalisé" + connected_apps: + legend: "Applications connectées" + title: "Discover Regenerative" + tagline: "Autoriser Discover Regenerative à publier les informations de votre entreprise" + enable: "Autoriser le partage de données" + disable: "Arrêter le partage" + loading: "Chargement en cours" + note: | + Votre compte Open Food Network est connecté à Discover Regenerative. + Ajouter et mettre à jour les informations sur votre liste Discover Regenerative ici. + link_label: "Gérer la liste" + description_html: | +

+ Les producteurs éligibles peuvent mettre en avant leurs références Regenerative, + pratiques agricoles et plus à travers le listing du profil. + Simplifier comment les acheteurs peuvent trouver les produits regeneratives et se connecter + avec des producteurs. +

+

+ En savoir plus sur Discover Regenerative + +

actions: edit_profile: Paramètres properties: Labels / propriétés @@ -1427,7 +1493,7 @@ fr: resume: could_not_resume_the_order: La commande n'a pas pu être reprise select2: - minimal_search_length: Entrez %{count} ou caractères + minimal_search_length: Entrez %{count} ou plus de caractères searching: Recherche... no_matches: Pas de correspondance trouvée shared: @@ -1437,6 +1503,8 @@ fr: has_no_payment_methods: "%{enterprise} n'a pas défini de méthode de paiement" has_no_shipping_methods: "%{enterprise} n'a pas défini de méthode de livraison" has_no_enterprise_fees: "%{enterprise} n'a pas défini de marges et commissions" + flashes: + dismiss: Masquer side_menu: enterprise: primary_details: "Informations de base" @@ -1455,8 +1523,9 @@ fr: tag_rules: "Règles de tag" shop_preferences: "Préférences boutique" users: "Gestionnaires" - vouchers: Bon de réduction + vouchers: Bons de réduction white_label: "Marque blanche" + connected_apps: "Applications connectées" enterprise_group: primary_details: "Informations de base" users: "Gestionnaires" @@ -1578,10 +1647,15 @@ fr: index: title: "OIDC" connect: "Connecter votre compte" - already_connected: "Votre compte est déjà relié avec cette autorisation :" + disconnect: "Déconnecter" + connected: "Votre compte est relié à %{uid}." les_communs_link: "Serveur OIDC Les Communs" link_your_account: "Vous devez d'abord vous connecter avec le serveur des Communs." link_account_button: "Relier votre compte OIDC Les Communs" + note_expiry: | + Les approbations permettant d'accéder aux applications connectées ont expiré. Veuillez rafraîchir + la connexion à votre compte pour que toutes les intégrations fonctionnent. + refresh: "Rafraichir l'autorisation" view_account: "Pour voir votre compte, consultez :" subscriptions: index: @@ -1683,9 +1757,9 @@ fr: legend: Nouveau bon de réduction back: Retour save: Sauvegarder - voucher_code: Code promo + voucher_code: Code de réduction voucher_amount: Montant - voucher_type: Type de promo + voucher_type: Type de réduction flat_rate: fixe percentage_rate: Pourcentage (%) controllers: @@ -1900,9 +1974,11 @@ fr: invoice_column_price_per_unit_without_taxes: "Prix par unité (hors taxe)" invoice_column_tax_rate: "TVA applicable" invoice_tax_total: "Total TVA :" + invoice_cancel_and_replace_invoice: "annule et remplace la facture" tax_invoice: "FACTURE" tax_total: "Total taxe (%{rate}) :" - invoice_shipping_type: "Type :" + invoice_shipping_category_delivery: "Livraison" + invoice_shipping_category_pickup: "Retrait" total_excl_tax: "Total HT :" total_incl_tax: "Total TTC :" total_all_tax: "total taxe" @@ -2122,6 +2198,9 @@ fr: order_back_to_store: Retour à la boutique order_back_to_cart: Retour au panier order_back_to_website: Retour vers le site + checkout_details_title: Détails de la commande + checkout_payment_title: Paiement de la commande + checkout_summary_title: Résumé de la commande bom_tip: "Utilisez cette page pour modifier les quantités sur plusieurs commandes à la fois. Les produits peuvent aussi être supprimés des commandes si nécessaire." unsaved_changes_warning: "Des modifications n'ont pas été enregistrées et seront perdues si vous continuez." unsaved_changes_error: "Les champs entourés en rouge contiennent des erreurs." @@ -2984,6 +3063,8 @@ fr: report_header_transaction_fee: Frais de Transaction (TVA non incluse) report_header_total_untaxable_admin: Total ajustements non taxables report_header_total_taxable_admin: Total ajustments soumis à TVA (inclut TVA) + report_header_voucher_label: Label de la réduction + report_header_voucher_amount: "Montant de la réduction (%{currency_symbol})" report_line_cost_of_produce: 'Coût des produits hors ' report_line_line_items: produits report_header_last_completed_order_date: Date dernière commande @@ -3702,8 +3783,8 @@ fr: start_date: "Date de début" successfully_removed: "Supprimé avec succès" taxonomy_edit: "Modifier la taxonomie" - taxonomy_tree_error: "Erreur de l'arbre de toxonomie" - taxonomy_tree_instruction: "Consignes pour l'arbre de taxonomie" + taxonomy_tree_error: "Erreur lors de la mise à jour de l'arbre de taxonomie." + taxonomy_tree_instruction: "Faites un clic droit sur un article pour ajouter, renommer, supprimer ou modifier." tree: "Arbre" updating: "Mettre à jour" your_order_is_empty_add_product: "Votre commande est vide, veuillez ajouter des produits" @@ -3998,6 +4079,9 @@ fr: add_product: cannot_add_item_to_canceled_order: "Il n'est pas possible d'ajouter des produits sur une commande annulée" include_out_of_stock_variants: "Inclure les variantes sans stock" + shipment: + mark_as_shipped_message_html: "Cette action marque la commande comme livrée.
Etes-vous certain de vouloir faire ceci? " + mark_as_shipped_label_message: "Envoyer une notification de livraison / distribution par e-mail au client" index: listing_orders: "Liste des commandes" new_order: "Nouvelle commande" @@ -4056,6 +4140,9 @@ fr: line_item_adjustments: "Ajustements sur la ligne produit" order_adjustments: "Ajustements sur la commande" order_total: "Total Commande" + invoices: + index: + order_has_changed: "La commande a changé depuis la dernière mise à jour de la facture. La facture affichée ici risque donc de ne pas être à jour." overview: enterprises_header: ofn_with_tip: Les Entreprises sont les entités qui organisent des circuits courts et utilisent pour cela CoopCircuits. @@ -4064,6 +4151,7 @@ fr: has_no_payment_methods: "n'a pas de méthode de paiement" has_no_shipping_methods: "n'a pas de méthode de livraison" products: + products_tip: "Les produits que vous vendez via CoopCircuits." active_products: zero: "Vous n'avez aucun produit actif." one: "Vous avez un produit actif" @@ -4216,6 +4304,8 @@ fr: bulk_unit_size: Quantité totale du lot display_as: display_as: Unité affichée + clone: + success: Produit dupliqué reports: table: select_and_search: "Sélectionner les filtres et cliquer sur %{option} pour accéder aux données." @@ -4285,6 +4375,15 @@ fr: email: "Email" total: "Total" billing_address_name: "Nom" + taxons: + form: + name: Nom + permalink: Permalien + meta_title: Méta titre + meta_description: Méta description + meta_keywords: Méta mots clés + description: Description + dfc_id: URI DFC general_settings: edit: legal_settings: "Configuration légales" @@ -4594,3 +4693,6 @@ fr: pagination: next: Suivant previous: Précédent + 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." diff --git a/config/locales/fr_BE.yml b/config/locales/fr_BE.yml index 0d4afaa5b1..26a6f97f4f 100644 --- a/config/locales/fr_BE.yml +++ b/config/locales/fr_BE.yml @@ -327,7 +327,6 @@ fr_BE: cancel_order: "Annuler la commande" confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" - must_have_valid_business_number: "%{enterprise_name} doit avoir un numéro d'entreprise valide avant que les factures puissent être envoyées." invoice: "Facture" active: "Actif" cancelled: "Annulée" @@ -463,6 +462,9 @@ fr_BE: actions: edit: Modifier clone: Dupliquer + delete: Supprimer + image: + edit: Modifier adjustments: skipped_changing_canceled_order: "Vous ne pouvez pas modifier une commande annulée." begins_at: Commence @@ -537,6 +539,7 @@ fr_BE: show: title: "Fichiers des conditions d'utilisation" no_files: "Aucune condition d'utilisation n'a encore été téléchargée." + current_terms_html: "Voir le %{tos_link} actuel. Heure de téléchargement : %{datetime} ." terms_of_service: "Conditions d'utilisation" delete: "Supprimer le fichier" confirm_delete: "Voulez-vous vraiment supprimer le fichier actuel des conditions d'utilisation ?" @@ -684,6 +687,10 @@ fr_BE: categories: label: Les catégories search: Rechercher + table: + new_variant: Nouvelle variante + edit_image: + close: Retour product_import: title: Import liste produits file_not_found: Fichier non trouvé ou impossible à ouvrir @@ -705,7 +712,7 @@ fr_BE: none_saved: n'a pu sauvegarder aucun produit :-( line_number: "Ligne %{number} :" encoding_error: "Veuillez vérifier la langage de votre fichier source et l'enregistrer avec un encodage UTF-8" - unexpected_error: "L'import du produit à provoqué une erreur à l'ouverture du fichier: %{error_message}" + unexpected_error: "L'import du produit a provoqué une erreur à l'ouverture du fichier: %{error_message}" malformed_csv: "L'import du produit a rencontré une erreur a l'ouverture du fichier:%{error_message}" index: notice: "Remarque" @@ -725,6 +732,9 @@ fr_BE: product_categories: Catégorie Produit tax_categories: TVA applicable shipping_categories: Condition de transport + dfc_import_form: + enterprise: "Entreprise" + import: "Importer" import: review: Vérifier import: Importer @@ -782,6 +792,7 @@ fr_BE: view_products: Aller à la page Produits view_inventory: Aller à la page Catalogues Comptoir product_headings: + distributor: Comptoir producer: Producteur·trice sku: Référence produit (sku) name: Nom @@ -870,6 +881,7 @@ fr_BE: legend: "Adresse" business_details: legend: "Juridique" + upload: 'télécharger' abn: Numéro d'entreprise abn_placeholder: 'ex: 000 000 000' acn: n° TVA @@ -877,6 +889,7 @@ fr_BE: display_invoice_logo: Afficher le logo sur la facture invoice_text: Ajouter une mention spécifique en bas des factures terms_and_conditions: "Termes et conditions" + uploaded_on: "téléchargé à" reset_form: "Vider les champs" business_address_legend: "Adresse de facturation" invoice_item_sorting_legend: "Ordre d'affichage sur facture" @@ -1000,7 +1013,7 @@ fr_BE: enable_subscriptions_false: "Désactivé" enable_subscriptions_true: "Activé" customer_names_in_reports: "Nom de client dans le rapport" - customer_names_tip: "Autoriser vos fourniseur à voir les noms des clients dans le rapport " + customer_names_tip: "Autoriser vos fourniseurs à voir les noms des clients dans le rapport " customer_names_false: "Désactivé" customer_names_true: "Activé" shopfront_message: "Message d'accueil comptoir ouvert" @@ -1099,6 +1112,8 @@ fr_BE: create_custom_tab: "Créer un onglet personnalisé dans la vitrine" custom_tab_title: "Titre de l'onglet personnalisé" custom_tab_content: "Contenu de l'onglet personnalisé" + connected_apps: + loading: "Chargement en cours" actions: edit_profile: Paramètres properties: Labels / propriétés @@ -1305,7 +1320,7 @@ fr_BE: cancel: Annuler proceed: Continuer status: - open: ouvrir + open: ouvert closed: fermé producer_properties: index: @@ -1735,6 +1750,8 @@ fr_BE: invoice_tax_total: "Total TVA :" tax_invoice: "FACTURE" tax_total: "Total taxe (%{rate}) :" + invoice_shipping_category_delivery: "Livraison" + invoice_shipping_category_pickup: "Retrait" total_excl_tax: "Total HT :" total_incl_tax: "Total TTC :" abn: "Numéro d'entreprise : " @@ -1931,6 +1948,7 @@ fr_BE: order_not_paid: NON RÉGLÉ order_total: Total commande order_payment: "Payer via:" + no_payment_required: "Aucun paiement requis" order_billing_address: Adresse de facturation order_delivery_on: Livraison prévue order_delivery_address: Adresse de livraison @@ -2216,6 +2234,7 @@ fr_BE: orders_could_not_cancel: "Désolé, la commande n'a pas pu être annulée" orders_cannot_remove_the_final_item: "Impossible de supprimer le dernier produit d'une commande, si vous souhaitez supprimer l'ensemble des produits, veuillez annuler la commande." orders_bought_items_notice: + one: "Un article supplémentaire est déjà confirmé pour ce cycle de commande" few: "%{count} produits ajoutés ont été confirmés pour ce cycle de vente." many: "%{count} produits ajoutés ont été confirmés pour ce cycle de vente." other: "%{count} produits ajoutés ont été confirmés pour ce cycle de vente." @@ -2503,6 +2522,7 @@ fr_BE: calculator: "Calculateur" calculator_values: "Valeurs applicables" calculator_settings_warning: "Si vous changez le type de calculatrice, vous devez d'abord enregistrer avant de pouvoir modifier les paramètres de la calculatrice" + calculator_preferred_unit_error: "doit être en kg ou en lb" flat_percent_per_item: "Pourcentage net" flat_rate_per_item: "Montant fixe (par article)" flat_rate_per_order: "Montant fixe (par commande)" @@ -2792,6 +2812,7 @@ fr_BE: report_header_transaction_fee: Frais de Transaction (TVA non incluse) report_header_total_untaxable_admin: Total ajustements non taxables report_header_total_taxable_admin: Total ajustments soumis à TVA (inclut TVA) + report_line_line_items: Ces articles equals: "Egal" contains: "contient" discount: "Réduction" @@ -2821,6 +2842,7 @@ fr_BE: payment_processing_failed: "Le paiement n' a pu être effectué , merci de vérifier les données rentrées" payment_method_not_supported: "Ce mode de paiement n'est pas possible. Veuillez en choisir un autre." payment_updated: "Paiement mis à jour " + action_required: "Action requise" tag_rules: "Règles de tag" enterprise_fee_whole_order: Commande totale enterprise_fee_by_name: "%{name} frais par %{role} %{enterprise_name}" @@ -2834,6 +2856,7 @@ fr_BE: enterprise_name_error: "est déjà utilisé. Si vous êtes le gérant de cette entreprise et que vous souhaitez demander le transfert du compte, ou bien si vous souhaitez distribuer les produits de cette entreprise, merci de contacter le manager actuel du profil à %{email}." enterprise_owner_error: "^ %{email} ne peut pas créer de nouvelles entreprises (limite actuelle : %{enterprise_limit} entreprises )." enterprise_role_uniqueness_error: "^Ce rôle existe déjà." + enterprise_terms_and_conditions_type_error: "Seuls les PDF sont autorisés" inventory_item_visibility_error: doit être vrai ou faux product_importer_file_error: "erreur : aucun document importé" product_importer_spreadsheet_error: "impossible de traiter le fichier : type de fichier invalide" @@ -2892,6 +2915,7 @@ fr_BE: resolve_errors: Veuillez corriger les erreurs suivantes more_items: "+ %{count} en plus" default_card_updated: La carte bancaire par défaut a été mise à jour + default_card_voids_auth: Changer votre carte par défaut supprimera les autorisations existantes des magasins pour la facturer. Vous pouvez réautoriser les magasins après avoir mis à jour la carte par défaut. Souhaitez-vous changer la carte par défaut ? cart: add_to_cart_failed: > Il y a eu un problème pour ajouter ce produit au panier. Il est peut-être @@ -2928,6 +2952,7 @@ fr_BE: terms_and_conditions_info: title: "Mise à jour des CGU & CGV" message_1: "Les CGV représentent le contrat entre vous, le vendeur, et l'acheteur. Ajouter un fichier ici permet à vos acheteurs de valider ce contrat lors de leur commande sous la forme d'une case à cocher avant la validation du paiement. Pour rappel, la mise en place de CGV est une obligation réglementaire." + message_2: "Les acheteurs ne seront tenus d’accepter les conditions générales qu’une seule fois. Cependant, si vous modifiez vos conditions générales, les acheteurs devront à nouveau les accepter avant de pouvoir passer à la caisse." terms_and_conditions_warning: title: "Mise à jour des CGU & CGV" message_1: "Tous vos acheteurs devront les accepter une fois à la validation de la commande. Si vous mettez à jour le fichier, tous vos acheteurs devront les accepter à nouveau lors du paiement." @@ -3043,6 +3068,8 @@ fr_BE: select_all_variants: "Sélectionner toutes %{total_number_of_variants}les variantes" variants_loaded: " %{num_of_variants_loaded}de %{total_number_of_variants}variantes chargées" loading_variants: "Chargement des variantes" + no_variants: "Aucune variante disponible pour ce produit (masquée via les paramètres d'inventaire)." + some_variants_hidden: "(Certaines variantes peuvent être masquées via les paramètres d'inventaire)" tag_rules: shipping_method_tagged_top: "Méthodes d'expédition étiquetées" shipping_method_tagged_bottom: "sont:" @@ -3064,7 +3091,7 @@ fr_BE: compiling_invoices: "Compile les factures" bulk_invoice_created: "Factures créées" bulk_invoice_failed: "La création des factures a échoué" - please_wait: "Veuillez attendre que le PDF soit disponible avant de fermé ce dialogue." + please_wait: "Veuillez attendre que le PDF soit disponible avant de fermer ce dialogue." order_state: address: "adresse" adjustments: "ajustements" @@ -3098,6 +3125,7 @@ fr_BE: processing: "en traitement" void: "faire un avoir" invalid: "invalide" + quantity_unavailable: "Stock disponible insuffisant. Cet article n'a pas été enregistré!" quantity_unchanged: "Quantité inchangée par rapport au montant précédent." cancel_the_order_html: "Cela annulera la commande en cours.
Êtes-vous sur de vouloir continuer?" cancel_the_order_send_cancelation_email: "Envoyer un e-mail d'annulation au client" @@ -3226,6 +3254,11 @@ fr_BE: signup_or_login: "Commencez par vous inscrire (ou connexion)" have_an_account: "Déjà inscrit?" action_login: "Se connecter." + inflections: + item: + one: "article" + many: "articles" + other: "articles" producers: signup: start_free_profile: "Commencez par créer votre profil entreprise, et présentez votre formule quand vous êtes prêt !" @@ -3497,6 +3530,7 @@ fr_BE: inventory_error_flash_for_insufficient_quantity: "Une donnée n'est plus disponible sur votre carte." inventory: Catalogue boutique zipcode: Code postal + weight: Poids (par kg ou lb) error_user_destroy_with_orders: "Les utilisateurs avec une commande finalisée ne peuvent pas être supprimés" cannot_create_payment_without_payment_methods: "Vous ne pouvez pas créer un paiement pour une commande sans qu'aucun mode de paiement ne soit défini." please_define_payment_methods: "Veuillez d'abord définir certains modes de paiement." @@ -3506,6 +3540,9 @@ fr_BE: payment_method: "Méthode de paiement" payment_processing_failed: "Le paiement n' a pu être effectué , merci de vérifier les données rentrées" sku: "Référence produit" + there_are_no_items_for_this_order: "Il n'y a aucun article pour cette commande." + order_populator: + out_of_stock: '%{item} est en rupture de stock.' actions: update: "Mettre à jour" cancel: "Annuler" @@ -3762,6 +3799,7 @@ fr_BE: paypal: no_payment_via_admin_backend: Les payement paypal ne savent pas etre prit dans le Backoffice products: + image_upload_error: "Veuillez télécharger l'image au format JPG, PNG, GIF, SVG ou WEBP." new: title: "Nouveau Produit" new_product: "Nouveau Produit" @@ -3874,6 +3912,11 @@ fr_BE: email: "Email" total: "Total" billing_address_name: "Nom" + taxons: + form: + name: Nom + permalink: permalien + description: Description general_settings: edit: legal_settings: "Configuration légales" @@ -3937,9 +3980,9 @@ fr_BE: dont_cancel: "Si vous avez changer d'avis ou souhaiter annuler cette commande contacter nous %{email}" order_summary_canceled_html: "Résumé de la commande #%{number} [ANNULEE]" details: "Voici les détails que vous avez commander:" - unpaid_order: "Votre commande n'était pas encore payer donc aucun remboursement na été fait." - paid_order: "Votre commande était payer donc %{distributor} vous a rembourser le montant totale. " - credit_order: "Votre commande était payer donc vous avez eter crediter." + unpaid_order: "Votre commande n'était pas encore payée donc aucun remboursement n'a été effectué." + paid_order: "Votre commande était payée donc %{distributor} vous a remboursé le montant total. " + credit_order: "Votre commande était payée donc vous avez été credité." subject: "Annulation de Commande" cancel_email_for_shop: greeting: "Chère %{name}" @@ -4017,6 +4060,7 @@ fr_BE: past_orders: Commandes Passées transactions: transaction_history: Historique des Transactions + authorisation_required: Autorisation requise open_orders: order: Commander shop: Faire mes courses diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 8ec8c6d354..c40bcf4646 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -380,7 +380,6 @@ fr_CA: cancel_order: "Annuler la commande" confirm_send_invoice: "La facture de cette commande va être transmise à l'acheteur. Etes-vous sûr de vouloir continuer ?" confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" - must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." invoice: "Facture" invoices: "Factures" file: "fichier" @@ -523,6 +522,9 @@ fr_CA: actions: edit: Modifier clone: Dupliquer + delete: Supprimer + image: + edit: Modifier adjustments: skipped_changing_canceled_order: "Vous ne pouvez pas modifier une commande annulée." begins_at: Commence à @@ -764,10 +766,11 @@ fr_CA: changed_summary: one: "%{count} produits modifiés" other: "%{count} produits modifiés" - save: Sauvegarder les changements reset: Annuler les changements - bulk_update: - success: "Produits mis à jour" + save: Sauvegarder les changements + new_variant: Nouvelle variante + edit_image: + close: Retour product_import: title: import produit file_not_found: Fichier non trouvé ou impossible à ouvrir @@ -809,6 +812,9 @@ fr_CA: product_categories: Catégorie Produit tax_categories: Taxe applicable shipping_categories: Condition de transport + dfc_import_form: + enterprise: "Entreprise" + import: "Importer" import: review: Vérifier import: Importer @@ -866,6 +872,7 @@ fr_CA: view_products: Aller à la page Produits view_inventory: Aller à la page Catalogues Boutiques product_headings: + distributor: Hub-distributeur producer: Producteur sku: Référence Produit name: Nom @@ -1197,6 +1204,9 @@ fr_CA: create_custom_tab: "Créer un onglet personnalisé dans la boutique" custom_tab_title: "Titre de l'onglet personnalisé" custom_tab_content: "Contenu de l'onglet personnalisé" + connected_apps: + legend: "Connected apps https://n8n.openfoodnetwork.org.uk/webhook-test/regen/connect-enterprise/can" + loading: "Chargement en cours" actions: edit_profile: Paramètres properties: Labels / propriétés @@ -1446,6 +1456,7 @@ fr_CA: users: "Utilisateurs" vouchers: Bon de réduction white_label: "Marque blanche" + connected_apps: "Connected apps https://n8n.openfoodnetwork.org.uk/webhook-test/regen/connect-enterprise/can" enterprise_group: primary_details: "Informations de base" users: "Utilisateurs" @@ -1567,7 +1578,6 @@ fr_CA: index: title: "OIDC" connect: "Connecter votre compte" - already_connected: "Votre compte est déjà relié avec cette autorisation :" les_communs_link: "Serveur OIDC Les Communs" link_your_account: "Vous devez d'abord vous connecter avec le serveur des Communs." link_account_button: "Relier votre compte OIDC Les Communs" @@ -1888,6 +1898,8 @@ fr_CA: invoice_tax_total: "Total Taxe :" tax_invoice: "FACTURE" tax_total: "Total taxe (%{rate}) :" + invoice_shipping_category_delivery: "Livraison" + invoice_shipping_category_pickup: "Retrait" total_excl_tax: "Total HT :" total_incl_tax: "Total TTC :" abn: "SIRET" @@ -3669,8 +3681,6 @@ fr_CA: start_date: "Date de début" successfully_removed: "Supprimé avec succès" taxonomy_edit: "Modifier la taxonomie" - taxonomy_tree_error: "Erreur de l'arbre de toxonomie" - taxonomy_tree_instruction: "Consignes pour l'arbre de taxonomie" tree: "Arbre" updating: "Mettre à jour" your_order_is_empty_add_product: "Votre commande est vide, veuillez ajouter des produits" @@ -4251,6 +4261,11 @@ fr_CA: email: "Email" total: "Total" billing_address_name: "Nom" + taxons: + form: + name: Nom + permalink: Nom pour URL (sans espace) + description: Description general_settings: edit: legal_settings: "Configuration légales" diff --git a/config/locales/fr_CH.yml b/config/locales/fr_CH.yml index e572397cf1..a17ba60ecd 100644 --- a/config/locales/fr_CH.yml +++ b/config/locales/fr_CH.yml @@ -328,7 +328,6 @@ fr_CH: cancel_order: "Annuler la commande" confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" - must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." invoice: "Facture" active: "Actif" cancelled: "Annulée" @@ -464,6 +463,9 @@ fr_CH: actions: edit: Modifier clone: Dupliquer + delete: Supprimer + image: + edit: Modifier adjustments: skipped_changing_canceled_order: "Vous ne pouvez pas modifier une commande annulée." begins_at: Commence @@ -684,6 +686,10 @@ fr_CH: categories: label: Conditions de transport search: Rechercher + table: + new_variant: Nouvelle variante + edit_image: + close: Retour product_import: title: Import liste produits file_not_found: Fichier non trouvé ou impossible à ouvrir @@ -723,6 +729,9 @@ fr_CH: product_categories: Catégorie Produit tax_categories: TVA applicable shipping_categories: Conditions de transport + dfc_import_form: + enterprise: "Entreprise" + import: "Importer" import: review: Vérifier import: Importer @@ -780,6 +789,7 @@ fr_CH: view_products: Aller à la page Produits view_inventory: Aller à la page Catalogues Boutiques product_headings: + distributor: Distributeur producer: Producteur sku: Référence Produit name: Nom @@ -1083,6 +1093,8 @@ fr_CH: rate: Taux customers: Client active: Active ? + connected_apps: + loading: "Chargement en cours" actions: edit_profile: Paramètres properties: Labels / propriétés @@ -1700,6 +1712,8 @@ fr_CH: invoice_tax_total: "Total TVA :" tax_invoice: "FACTURE" tax_total: "Total taxe (%{rate}) :" + invoice_shipping_category_delivery: "Livraison" + invoice_shipping_category_pickup: "Retrait" total_excl_tax: "Total HT :" total_incl_tax: "Total TTC :" abn: "SIRET" @@ -3446,8 +3460,6 @@ fr_CH: start_date: "Date de début" successfully_removed: "Supprimé avec succès" taxonomy_edit: "Modifier la taxonomie" - taxonomy_tree_error: "Erreur de l'arbre de toxonomie" - taxonomy_tree_instruction: "Consignes pour l'arbre de taxonomie" tree: "Arbre" updating: "Mettre à jour" your_order_is_empty_add_product: "Votre commande est vide, veuillez ajouter des produits" @@ -3999,6 +4011,11 @@ fr_CH: email: "Email" total: "Total" billing_address_name: "Produit/Variante" + taxons: + form: + name: Produit/Variante + permalink: Permalien + description: Description general_settings: edit: legal_settings: "Configuration légales" diff --git a/config/locales/fr_CM.yml b/config/locales/fr_CM.yml index ba66296ee4..13ba93bf9d 100644 --- a/config/locales/fr_CM.yml +++ b/config/locales/fr_CM.yml @@ -265,7 +265,6 @@ fr_CM: cancel_order: "Annuler la commande" confirm_send_invoice: "La facture de cette commande va être transmise au client. Etes-vous sûr de vouloir continuer ?" confirm_resend_order_confirmation: "Etes-vous sûr de vouloir renvoyer le mail de confirmation de commande ?" - must_have_valid_business_number: "%{enterprise_name} doit avoir un SIRET valide avant que les factures puissent être envoyées." invoice: "Facture" active: "Actif" cancelled: "Annulée" @@ -401,6 +400,9 @@ fr_CM: actions: edit: Modifier clone: Dupliquer + delete: Supprimer + image: + edit: Modifier adjustments: skipped_changing_canceled_order: "Vous ne pouvez pas modifier une commande annulée." begins_at: Commence @@ -621,6 +623,10 @@ fr_CM: categories: label: Conditions de transport search: Rechercher + table: + new_variant: Nouvelle variante + edit_image: + close: Retour product_import: title: Import liste produits file_not_found: Fichier non trouvé ou impossible à ouvrir @@ -660,6 +666,9 @@ fr_CM: product_categories: Catégorie Produit tax_categories: TVA applicable shipping_categories: Conditions de transport + dfc_import_form: + enterprise: "Entreprise" + import: "Importer" import: review: Vérifier import: Importer @@ -717,6 +726,7 @@ fr_CM: view_products: Aller à la page Produits view_inventory: Aller à la page Catalogues Boutiques product_headings: + distributor: Distributeur producer: Producteur sku: Référence Produit name: Produit/Variante @@ -1007,6 +1017,8 @@ fr_CM: rate: Taux customers: Client active: Active ? + connected_apps: + loading: "Chargement en cours" actions: edit_profile: Paramètres properties: Labels / propriétés @@ -1610,6 +1622,8 @@ fr_CM: invoice_tax_total: "Total TVA :" tax_invoice: "FACTURE" tax_total: "Total taxe (%{rate}) :" + invoice_shipping_category_delivery: "Livraison" + invoice_shipping_category_pickup: "Retrait" total_excl_tax: "Total HT :" total_incl_tax: "Total TTC :" abn: "SIRET" @@ -3340,8 +3354,6 @@ fr_CM: start_date: "Date de début" successfully_removed: "Supprimé avec succès" taxonomy_edit: "Modifier la taxonomie" - taxonomy_tree_error: "Erreur de l'arbre de toxonomie" - taxonomy_tree_instruction: "Consignes pour l'arbre de taxonomie" tree: "Arbre" updating: "Mettre à jour" your_order_is_empty_add_product: "Votre commande est vide, veuillez ajouter des produits" @@ -3893,6 +3905,11 @@ fr_CM: email: "Email" total: "Total" billing_address_name: "Produit/Variante" + taxons: + form: + name: Produit/Variante + permalink: Permalien + description: Description general_settings: edit: legal_settings: "Configuration légales" diff --git a/config/locales/hi.yml b/config/locales/hi.yml index ab6b2576ab..cf84a4eaa9 100644 --- a/config/locales/hi.yml +++ b/config/locales/hi.yml @@ -387,7 +387,6 @@ hi: cancel_order: "ऑर्डर रद्द करें" confirm_send_invoice: "इस ऑर्डर के लिए एक इनवॉइस ग्राहक को भेजा जाएगा। क्या आप वाकई जारी रखना चाहते हैं?" confirm_resend_order_confirmation: "क्या आप वाकई ऑर्डर पुष्टिकरण ईमेल फिर से भेजना चाहते हैं?" - must_have_valid_business_number: "इनवॉइस भेजे जाने से पहले %{enterprise_name} के पास मान्य ABN होना चाहिए।" invoice: "इनवॉइस" invoices: "इनवॉइस" file: "फ़ाइल" @@ -530,6 +529,9 @@ hi: actions: edit: एडिट करें clone: क्लोन + delete: हटाएं + image: + edit: एडिट करें adjustments: skipped_changing_canceled_order: "आप रद्द किए गए ऑर्डर को बदल नहीं सकते।" begins_at: को शुरू होता है @@ -778,10 +780,11 @@ hi: invalid: one: "%{count} उत्पाद सेव नहीं किया जा सका। कृपया त्रुटियों को रिव्यू करें और फिर से प्रयास करें।" other: "%{count} उत्पाद सेव नहीं किए जा सके। कृपया त्रुटियों को रिव्यू करें और फिर से प्रयास करें।" - save: परिवर्तन सेव करें reset: परिवर्तनों को अस्वीकार करें - bulk_update: - success: "उत्पाद सफलतापूर्वक अपडेट किए गए" + save: परिवर्तन सेव करें + new_variant: नया वैरिएंट + edit_image: + close: वापस जाएं product_import: title: उत्पाद इम्पोर्ट करें file_not_found: फ़ाइल नहीं मिली या खोली नहीं जा सकी @@ -823,6 +826,9 @@ hi: product_categories: उत्पाद श्रेणियां tax_categories: टैक्स श्रेणियां shipping_categories: शिपिंग श्रेणियां + dfc_import_form: + enterprise: "एंटरप्राइज़" + import: "इम्पोर्ट करें" import: review: रिव्यू import: इम्पोर्ट करें @@ -880,6 +886,7 @@ hi: view_products: उत्पाडों के पेज पर जाएं view_inventory: इन्वेंट्री पेज पर जाएं product_headings: + distributor: वितरक producer: उत्पादक sku: SKU name: नाम @@ -1211,6 +1218,8 @@ hi: create_custom_tab: "शॉपफ्रन्ट में कस्टम टैब बनाएं" custom_tab_title: "कस्टम टैब के लिए शीर्षक" custom_tab_content: "कस्टम टैब के लिए सामग्री" + connected_apps: + loading: "लोड हो रहा" actions: edit_profile: सेटिंग्स properties: प्रॉपर्टीज़ @@ -1580,7 +1589,6 @@ hi: index: title: "OIDC सेटिंग्स" connect: "अपना अकाउंट कनेक्ट करें" - already_connected: "आपका अकाउंट पहले से ही इस DFC प्राधिकरण अकाउंट से जुड़ा हुआ है:" les_communs_link: "Les Communs ओपन ID सर्वर" link_your_account: "आपको सबसे पहले अपने अकाउंट को DFC (Les Communs Open ID Connect) द्वारा उपयोग किए जाने वाले प्राधिकरण प्रदाता के साथ लिंक करने की जरूरत है।" link_account_button: "अपने Les Communs OIDC अकाउंट को लिंक करें" @@ -1902,7 +1910,8 @@ hi: invoice_tax_total: "GST कुल:" tax_invoice: "टैक्स इनवॉइस" tax_total: "कुल टैक्स (%{rate} ):" - invoice_shipping_type: "टाइप करें:" + invoice_shipping_category_delivery: "डिलिवरी" + invoice_shipping_category_pickup: "पिकअप" total_excl_tax: "कुल (टैक्स को छोड़कर):" total_incl_tax: "कुल (टैक्स सहित):" total_all_tax: "कुल टैक्स:" @@ -1982,7 +1991,7 @@ hi: label_groups: "ग्रुप्स" label_about: "परिचय" label_blog: "ब्लॉग" - label_support: "समर्थन" + label_support: "सपोर्ट" label_shopping: "शॉपिंग" label_login: "लॉग-इन करें" label_logout: "लॉग-आउट करें" @@ -2054,14 +2063,14 @@ hi: cookies_policy_link: "कुकीज़ नीति" cookies_accept_button: "कुकीज़ स्वीकार करें" home_shop: अभी खरीदें - brandstory_headline: "कृषि उत्पाद, स्थानीय और ऑनलाईन!" - brandstory_intro: "कभी-कभी सिस्टम को ठीक करने का सबसे अच्छा तरीका एक नया सिस्टम शुरू करना होता है…" - brandstory_part1: "ओपन फूड नेटवर्क इंडिया सॉफ्टवेयर प्लेटफॉर्म किसानों को लाभदायक कीमतों पर अपनी उपज ऑनलाइन बेचने की सुविधा देता है। यह सॉफ़्टवेयर खाद्यान्न बेचने के लिए डिज़ाइन किया गया है ताकि यह विभिन्न मापों या स्टॉक स्तरों को संभाल सके जो केवल कृषि उत्पादों में आम हैं। जैसे एक दर्जन अंडे, धनिया का एक गुच्छा, या एक पूरे चिकन का वजन जो अलग-अलग हो सकता है।" - brandstory_part2: "खाद्यान्न उत्पादक, Farmer Producer Organizations (FPO), या Farmer Producer Companies (FPC) अपनी खुद की ऑनलाइन दुकान बना सकते हैं, ऑनलाइन पेमेंट ले सकते है, या इसी वेबसाइट पर अन्य दुकानों के माध्यम से बेच भी सकते हैं।" + brandstory_headline: "जैविक और स्थानीय कृषि उत्पाद।" + brandstory_intro: "स्थानीय किसानों और खाद्य उत्पादकों को खोजें।" + brandstory_part1: "उन हजारों किसानों और खाद्य उत्पादकों में से चुनें जो बिना किसी हानिकारक रसायनों के टिकाऊ खेती के तरीकों का उपयोग करके भोजन का उत्पादन करते हैं।" + brandstory_part2: "जब आप अपना भोजन स्थानीय किसानों से खरीदते हैं, तो आप स्थानीय अर्थव्यवस्था का समर्थन करते हैं।" brandstory_part3: "थोक विक्रेता, Farmer Producer Organizations (FPO), या Farmer Producer Companies (FPC), OFN को अपने द्वारा उपयोग किए जाने वाले सॉफ़्टवेयर के साथ integrate कर सकते हैं और ऑनलाइन खाद्य केंद्रों और दुकानों के हमारे राष्ट्रीय नेटवर्क के माध्यम से ग्राहकों को अपने उत्पादों की आपूर्ति करने के लिए खरीद समूह बना सकते हैं।" - brandstory_part4: "Farmer Producer Organizations (FPO), या Farmer Producer Companies (FPC) एक ऑनलाइन किसान बाजार बनाने के लिए अपने स्थानीय क्षेत्र में उत्पादकों को एक साथ ला सकते हैं, जिससे एक मजबूत स्थानीय खाद्य अर्थव्यवस्था बन सकती है।" - brandstory_part5_strong: "हम इसे Open Food Network कहते हैं।" - brandstory_part6: "यदि आप अच्छा खाद्य बेचते हैं - एक किसान, किसान बाजार, खाद्य सहकारी समिति, या खाद्य केंद्र के रूप में - तो ऐसा सॉफ़्टवेयर चुनें जो लोगों और पृथ्वी के लिए खाद्य प्रणाली बनाने के आपके मूल्यों के अनुरूप हो, न कि लाभ के लिए। प्रतिस्पर्धात्मक रूप से बजाय सामूहिक रूप से काम करके, हम नए सॉफ़्टवेयर विकसित करने की लागत साझा करते हैं, और हम यह सुनिश्चित करते हैं कि हमारी परियोजना लचीली है!" + brandstory_part4: "यह सॉफ्टवेयर किसानों को अपनी उपज ऑनलाइन बेचने में मदद करने के लिए एक गैर-लाभकारी संगठन द्वारा विकसित किया गया है।" + brandstory_part5_strong: "हम इसे Open Food Network India या OFN India कहते हैं।" + brandstory_part6: "यदि आप अच्छा भोजन बेचते हैं - एक किसान, किसान बाज़ार, खाद्य सहकारी संस्था या खाद्य केंद्र के रूप में - तो ओएफएन इंडिया आपके लिए सबसे अच्छा विकल्प है।" system_headline: "OFN पर कैसे बेचें - 3 आसान स्टेप्स।" system_step1: "1. एक नया खाता बनाएं" system_step1_text: "• अपने कृषि व्यवसाय या दुकान का नाम, विवरण और संपर्क विवरण दर्ज करें • फ़ोटो और सोशल मीडिया विवरण दर्ज करें" @@ -2352,12 +2361,12 @@ hi: sell_hubs_detail: "OFN पर अपने खाद्यान्न एंटरप्राइज़ या संगठन के लिए एक प्रोफ़ाइल सेट करें। किसी भी समय आप अपनी प्रोफ़ाइल को मल्टी-उत्पादक शॉप में अपग्रेड कर सकते हैं।" sell_groups_detail: "अपने क्षेत्र के लिए या अपने संगठन के लिए एंटरप्राइजों (उत्पादकों और दूसरे खाद्यान्नों के एंटरप्राइज़) की एक अनुकूलित निर्देशिका सेट करें।" sell_user_guide: "हमारी यूज़र गाइड में और जानें।" - sell_listing_price: "OFN पर लिस्टिंग फ्री है। OFN पर शॉप खोलना और चलाना ₹50,000 की मासिक बिक्री तक फ्री है। यदि आप ज्यादा बिक्री करते हैं तो आप बिक्री के 1% से 3% के बीच अपना सामुदायिक योगदान चुन सकते हैं। मूल्य निर्धारण के बारे में ज्यादा जानकारी के लिए शीर्ष मेन्यू में अबाउट लिंक के माध्यम से सॉफ़्टवेयर प्लेटफ़ॉर्म अनुभाग पर जाएं।" + sell_listing_price: "OFN India पर लिस्टिंग फ्री है। दुकानों और हब्स के लिए योजनाएं कम से कम ₹375 प्रति माह से शुरू होती हैं। मूल्य निर्धारण के बारे में अधिक जानकारी के लिए https://about.openfoodindia.org/pricing/ पर जाएं।" sell_embed: "हम आपकी अपनी अनुकूलित वेबसाइट में एक OFN शॉप भी एम्बेड कर सकते हैं या आपके क्षेत्र के लिए एक अनुकूलित स्थानीय खाद्यान्न नेटवर्क वेबसाइट बना सकते हैं।" sell_ask_services: "OFN सेवाओं के बारे में हमसे पूछें।" shops_title: दुकानें shops_headline: खरीदारी, पूरी तरह से बदली हुई। - shops_text: अनाज साइकल में उगता है, किसान साइकल में कटाई करते हैं, और हम अनाज को साइकल में ऑर्डर करते हैं। अगर आपको कोई ऑर्डर साइकल बंद मिलता है, तो जल्द ही वापस चेक करें। + shops_text: खाद्य फसलें एक विशिष्ट अवधि में उगती हैं, किसान एक विशिष्ट अवधि में फसल काटते हैं और हम भी एक विशिष्ट अवधि (सायकल) में खाद्य पदार्थो का ऑर्डर देते हैं। यदि जिस स्टोर पर आप जाना चाहते हैं उसका ऑर्डर चक्र बंद है, तो बाद में पुनः प्रयास करें। shops_signup_title: हब के रूप में साइन अप करें shops_signup_headline: खाद्यान्न हब्स, असीमित। shops_signup_motivation: आपका मॉडल जो भी हो, हम आपका समर्थन करते हैं। आप जैसे भी बदलते हैं, हम आपके साथ हैं। हम गैर-लाभकारी, स्वतंत्र और ओपन-सोर्स हैं। हम वे सॉफ़्टवेयर पार्टनर हैं जिनके बारे में आपने सपने देखे हैं। @@ -2489,18 +2498,18 @@ hi: producer: "वूट! सबसे पहले हमें आपके फार्म/खेत के बारे में थोड़ा बहुत जानने कि जरूरत है:" enterprise_name_field: "एंटरप्राइज़ का नाम:" producer_name_field: "फार्म का नाम:" - producer_name_field_placeholder: "उदाहरण प्रकाश सिंह जी का जबरदस्त फार्म" + producer_name_field_placeholder: "उदाहरण प्रकाश सिंह जी का फार्म" producer_name_field_error: "कृपया अपने एंटरप्राइज़ के लिए एक यूनीक नाम चुनें" address1_field: "पता पंक्ति 1:" address1_field_placeholder: "उदाहरण 123 सरदार वल्लभ पटेल रोड" address1_field_error: "कृपया पता एंटर करें" address2_field: "पता पंक्ति 2:" suburb_field: "उपनगर:" - suburb_field_placeholder: "उदाहरण नॉर्थकोट" + suburb_field_placeholder: "उदाहरण कोल्हापुर" suburb_field_error: "कृपया एक उपनगर में प्रवेश करें" - postcode_field: "पोस्टकोड:" - postcode_field_placeholder: "उदाहरण 3070" - postcode_field_error: "पोस्टकोड आवश्यक" + postcode_field: "पिनकोड:" + postcode_field_placeholder: "उदाहरण 400081" + postcode_field_error: "पिनकोड आवश्यक" state_field: "राज्य:" state_field_error: "राज्य आवश्यक" country_field: "देश:" @@ -2520,7 +2529,7 @@ hi: whatsapp_phone_field: "WhatsApp फ़ोन नंबर" whatsapp_phone_tooltip: "यह नंबर आपकी सार्वजनिक प्रोफ़ाइल में प्रदर्शित होगा जिसे WhatsApp लिंक के रूप में खोला जाएगा।" phone_field_placeholder: "उदाहरण (03) 1234 5678" - whatsapp_phone_field_placeholder: "उदाहरण +61 4 1234 5678" + whatsapp_phone_field_placeholder: "उदाहरण +91 4 1234 5678" type: title: "टाइप करें" headline: "%{enterprise} जोड़ने का अंतिम चरण!" @@ -2564,7 +2573,7 @@ hi: logo_placeholder: "एक बार अपलोड होते ही आपका लोगो रिव्यू के लिए यहां दिखाई देगा" promo: select_promo_image: "चरण 3. प्रोमो छवि चुनें" - promo_image_tip: "टिप: एक बैनर के रूप में दिखाया गया है, वरीयता प्राप्त1200×260px है" + promo_image_tip: "टिप: एक बैनर के रूप में दिखाया गया है, वरीयता प्राप्त 1200×260px है" promo_image_label: "प्रोमो छवि चुनें" promo_image_drag: "अपने प्रोमो को यहां खींचें और छोड़ें" review_promo_image: "चरण 4. अपने प्रोमो बैनर को रिव्यू करें" @@ -2586,8 +2595,8 @@ hi: instagram_placeholder: "उदाहरण @instagram_handle" limit_reached: headline: "अरे नहीं!" - message: "आप सीमा तक पहुँच गए हैं!" - text: "आप उन एंटरप्राइजिस की संख्या की सीमा तक पहुँच गए हैं, जिनका आपको मालिक होने की अनुमति है" + message: "आप limit तक पहुँच गए हैं!" + text: "आप उन एंटरप्राइजिस की संख्या की limit तक पहुँच गए हैं, जितनोंकी आपको मालिक होने की अनुमति है" action: "होमपेज पर वापस लौटें" finished: headline: "समाप्त हुआ!" @@ -3651,8 +3660,6 @@ hi: start_date: "शुरू होने की तिथि" successfully_removed: "सफलतापूर्वक हटाया गया" taxonomy_edit: "टैक्सोनॉमी एडिट करें" - taxonomy_tree_error: "टैक्सोनॉमी ट्री त्रुटि" - taxonomy_tree_instruction: "टैक्सोनॉमी ट्री निर्देश" tree: "ट्री" updating: "अपडेट किया जा रहा है" your_order_is_empty_add_product: "आपका ऑर्डर खाली है, कृपया ऊपर दिए गए उत्पाद को सर्च करें और जोड़ें" @@ -4234,6 +4241,11 @@ hi: email: "ईमेल" total: "कुल" billing_address_name: "नाम" + taxons: + form: + name: नाम + permalink: पर्मलिंक + description: विवरण general_settings: edit: legal_settings: "कानूनी सेटिंग्स" diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 0686c9ac3f..a6ef6eb41e 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -6,9 +6,22 @@ hu: spree/shipping_method: Szállítási Mód attributes: spree/order/ship_address: + address1: "Szállítási cím (utca és házszám)" + address2: "Szállítási cím 2. sor" + city: "Szállítási cím város" + country: "Szállítási cím ország" phone: "Telefonszám" firstname: "Keresztnév" lastname: "Vezetéknév" + zipcode: "Szállítási cím irányítószám" + spree/order/bill_address: + address1: "Számlázási cím (Utca és Házszám)" + zipcode: "Számlázási cím Irányítószám" + city: "Számlázási cím Város" + country: "Számlázási cím Ország" + firstname: "Számlázási név Keresztnév" + lastname: "Számlázási cím Vezetéknév" + phone: Vásárlói telefonszám spree/user: password: "Jelszó" password_confirmation: "Jelszó megerősítése" @@ -34,6 +47,7 @@ hu: shipping_category_id: "Szállítási mód" variant_unit: "Változatos egység" variant_unit_name: "Változat egység neve" + unit_value: "Egység értéke" spree/credit_card: base: "Hitelkártya" number: "Szám" @@ -253,6 +267,10 @@ hu: order_cycle: subject: "Rendelési ciklus jelentés a következőhöz: %{producer}" provider_settings: "Szolgáltató beállításai" + report_mailer: + report_ready: + subject: "A jelentés kész" + heading: "A jelentés letöltésre kész" shipment_mailer: shipped_email: dear_customer: "Tisztelt Ügyfelünk," @@ -316,7 +334,6 @@ hu: cancel_order: "Megrendelés visszavonása" confirm_send_invoice: "Erről a megrendelésről számlát küldünk a vásárlónak. Biztos, hogy akarod folytatni?" confirm_resend_order_confirmation: "Biztos, hogy újra el akarja küldeni a rendelést visszaigazoló e-mailt?" - must_have_valid_business_number: "%{enterprise_name}-nak érvényes adószámmal kell rendelkeznie a számlák küldéséhez." invoice: "Számla" active: "Aktív" cancelled: "Törölve" @@ -452,6 +469,9 @@ hu: actions: edit: Szerkesztés clone: Klón + delete: Töröl + image: + edit: Szerkesztés adjustments: skipped_changing_canceled_order: "A törölt rendelést nem módosíthatja." begins_at: 'Kezdete:' @@ -668,6 +688,13 @@ hu: index: header: title: Termékek tömeges szerkesztése + delete_modal: + delete_product_modal: + heading: "Termék törlése" + confirmation_text: "Termék törlése" + delete_variant_modal: + heading: "Változat törlése" + confirmation_text: "Változat törlése" sort: pagination: clear_search: Keresés törlése @@ -677,6 +704,23 @@ hu: categories: label: Kategóriák search: Keresés + table: + error_summary: + saved: + one: "a termék helyesen lett elmentve, de" + other: "a termékek helyesen lettek elmentve, de" + save: Változások mentése + new_variant: Új variáns + bulk_update: + success: Változtatások mentve + edit_image: + close: Vissza + delete_product: + success: Sikeresen törölte a terméket + error: A terméket nem lehet törölni + delete_variant: + success: Sikeresen törölte a terméket + error: A változatot nem lehet törölni product_import: title: Termék importálása file_not_found: A fájl nem található vagy nem nyitható meg @@ -716,6 +760,9 @@ hu: product_categories: termék kategóriák tax_categories: Adókategóriák shipping_categories: Szállítási módok + dfc_import_form: + enterprise: "Vállalkozás" + import: "Importálás" import: review: Felülvizsgálat import: Importálás @@ -773,6 +820,7 @@ hu: view_products: Ugrás a Termékek oldalra view_inventory: Ugrás a Leltár oldalra product_headings: + distributor: Elosztó producer: Termelő sku: SKU name: Név @@ -1078,6 +1126,8 @@ hu: rate: Mérték customers: Vevő active: Aktív? + connected_apps: + loading: "Betöltés" actions: edit_profile: Beállítások properties: Tulajdonságok @@ -1705,6 +1755,8 @@ hu: invoice_tax_total: "GST összesen:" tax_invoice: "ADÓSZÁMLA" tax_total: "Teljes adó (%{rate}):" + invoice_shipping_category_delivery: "Szállítás" + invoice_shipping_category_pickup: "Átvétel" total_excl_tax: "Összesen (adó nélkül):" total_incl_tax: "Összesen (adóval együtt):" abn: "LÉGI ÚTON SZÁLLÍTOTT:" @@ -3096,6 +3148,7 @@ hu: processing: "feldolgozás" void: "üres" invalid: "érvénytelen" + quantity_unavailable: "Nem áll rendelkezésre elegendő készlet. A tétel nem menthető!" quantity_unchanged: "A mennyiség nem változott az előző mennyiséghez képest." cancel_the_order_html: "Ez megszakítja a vásárlási folyamatot. Biztosan folytatja?" cancel_the_order_send_cancelation_email: "A törlés elküldése a vásárlónak, emailben." @@ -3434,8 +3487,6 @@ hu: start_date: "Kezdő dátum" successfully_removed: "Sikeresen eltávolítva" taxonomy_edit: "Termék kategória szerkesztés" - taxonomy_tree_error: "Termék kategória fa hiba" - taxonomy_tree_instruction: "Termék kategória fa" tree: "Fa" updating: "Frissítés" your_order_is_empty_add_product: "Rendelése üres, kérjük keressen és adjon hozzá egy terméket fent" @@ -3991,6 +4042,11 @@ hu: email: "Email" total: "Összesen" billing_address_name: "Név" + taxons: + form: + name: Név + permalink: Permalink + description: Leírás general_settings: edit: legal_settings: "Jogi beállítások" diff --git a/config/locales/it.yml b/config/locales/it.yml index 4054df082c..12e29ab077 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -355,7 +355,6 @@ it: cancel_order: "Cancella Ordine" confirm_send_invoice: "Verrà mandata al cliente una mail con la conferma di quest'ordine. Sei sicuro di voler continuare?" confirm_resend_order_confirmation: "Sei sicuro di voler inviare di nuovo l'email di conferma per l'ordine?" - must_have_valid_business_number: "%{enterprise_name} deve avere una partita iva valida per poter emettere fattura." invoice: "Conferma d'ordine" active: "Attivo" cancelled: "Annullato" @@ -494,6 +493,9 @@ it: actions: edit: Modifica clone: Duplica + delete: Annulla + image: + edit: Modifica adjustments: skipped_changing_canceled_order: "Non puoi modificare una richiesta annullata" begins_at: Inizia a @@ -719,6 +721,10 @@ it: categories: label: categorie search: Cerca + table: + new_variant: Nuova variante + edit_image: + close: Indietro product_import: title: Importa prodotto file_not_found: Documento non trovato o non disponibile @@ -760,6 +766,9 @@ it: product_categories: Categorie Prodotti tax_categories: Categorie imposte shipping_categories: Categorie Spedizioni + dfc_import_form: + enterprise: "Azienda" + import: "Importazione" import: review: Revisione import: Importazione @@ -817,6 +826,7 @@ it: view_products: Vai alla Pagina dei Prodotti view_inventory: Vai alla Pagina dell'Inventario product_headings: + distributor: Distributore producer: Produttore sku: Codice ID name: Nome @@ -1125,6 +1135,8 @@ it: rate: Tariffa customers: Cliente active: Attivo? + connected_apps: + loading: "Caricamento" actions: edit_profile: Impostazioni properties: Proprietà @@ -1473,7 +1485,6 @@ it: index: title: "Impoostazioni" connect: "Collega il tuo profilo" - already_connected: "Il tuo profilo è già collegato a questo profilo di autorizzazione DFC" view_account: "Per vedere il tuo profilo, vedi:" subscriptions: index: @@ -1766,6 +1777,8 @@ it: invoice_tax_total: "IVA totale" tax_invoice: "Conferma d'ordine" tax_total: "Totale IVA %{rate}:" + invoice_shipping_category_delivery: "Consegna" + invoice_shipping_category_pickup: "Prelievo" total_excl_tax: "Totale (IVA escl.):" total_incl_tax: "Totale (IVA incl.):" abn: "CF:" @@ -3515,8 +3528,6 @@ it: start_date: "Data inizio" successfully_removed: "Rimosso con successo" taxonomy_edit: "Modifica tassonomia" - taxonomy_tree_error: "Errore schema tassonomia" - taxonomy_tree_instruction: "Istruzioni schema tassonomia" tree: "Schema" updating: "In aggiornamento" your_order_is_empty_add_product: "Il tuo ordine è vuoto, cerca e aggiungi un prodotto qui sopra" @@ -4086,6 +4097,11 @@ it: email: "Email" total: "Totale" billing_address_name: "Nome" + taxons: + form: + name: Nome + permalink: Permalink + description: Descrizione general_settings: edit: legal_settings: "Impostazioni Legali" diff --git a/config/locales/it_CH.yml b/config/locales/it_CH.yml index 78272fb157..6341d5b4a0 100644 --- a/config/locales/it_CH.yml +++ b/config/locales/it_CH.yml @@ -306,7 +306,6 @@ it_CH: cancel_order: "Cancella Ordine" confirm_send_invoice: "Verrà mandata al cliente una mail con la conferma di quest'ordine. Sei sicuro di voler continuare?" confirm_resend_order_confirmation: "Sei sicuro di voler inviare di nuovo l'email di conferma per l'ordine?" - must_have_valid_business_number: "%{enterprise_name} deve avere una partita iva valida per poter emettere fattura." invoice: "Conferma d'ordine" active: "Attivo" cancelled: "Annullato" @@ -442,6 +441,9 @@ it_CH: actions: edit: Modifica clone: Duplica + delete: Annulla + image: + edit: Modifica adjustments: skipped_changing_canceled_order: "Non puoi modificare una richiesta annullata" begins_at: Inizia a @@ -662,6 +664,10 @@ it_CH: categories: label: categorie search: Cerca + table: + new_variant: Nuova variante + edit_image: + close: Indietro product_import: title: Importa prodotto file_not_found: Documento non trovato o non disponibile @@ -701,6 +707,9 @@ it_CH: product_categories: Categorie Prodotti tax_categories: Categorie imposte shipping_categories: Categorie Spedizioni + dfc_import_form: + enterprise: "Azienda" + import: "Importazione" import: review: Revisione import: Importazione @@ -758,6 +767,7 @@ it_CH: view_products: Vai alla Pagina dei Prodotti view_inventory: Vai alla Pagina dell'Inventario product_headings: + distributor: Distributore producer: Produttore sku: Codice ID name: Nome @@ -1057,6 +1067,8 @@ it_CH: rate: Tariffa customers: Cliente active: Attivo? + connected_apps: + loading: "Caricamento" actions: edit_profile: Impostazioni properties: Proprietà @@ -1665,6 +1677,8 @@ it_CH: invoice_tax_total: "IVA totale" tax_invoice: "Conferma d'ordine" tax_total: "Totale tasse (%{rate}):" + invoice_shipping_category_delivery: "Consegna" + invoice_shipping_category_pickup: "Prelievo" total_excl_tax: "Totale (Tasse escl.):" total_incl_tax: "Totale (Tasse incl.):" abn: "CF:" @@ -3375,8 +3389,6 @@ it_CH: start_date: "Data inizio" successfully_removed: "Rimosso con successo" taxonomy_edit: "Modifica tassonomia" - taxonomy_tree_error: "Errore schema tassonomia" - taxonomy_tree_instruction: "Istruzioni schema tassonomia" tree: "Schema" updating: "In aggiornamento" your_order_is_empty_add_product: "Il tuo ordine è vuoto, cerca e aggiungi un prodotto qui sopra" @@ -3928,6 +3940,11 @@ it_CH: email: "Email" total: "Totale" billing_address_name: "Nome" + taxons: + form: + name: Nome + permalink: Permalink + description: Descrizione general_settings: edit: legal_settings: "Impostazioni Legali" diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3892a1be8e..e70899ed8e 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -329,7 +329,6 @@ ko: cancel_order: "주문 취소" confirm_send_invoice: "이 주문에 대한 청구서가 고객님께 보내질 예정입니다. 계속해서 진행하시겠습니까?" confirm_resend_order_confirmation: "주문 확인 이메일을 재전송하기를 원하시는 것이 확실합니까?" - must_have_valid_business_number: "%{enterprise_name}는 청구서가 보내기 전에 사용가능한 호주 사업 번호를 가져야 합니다. " invoice: "청구서" active: "활성화" cancelled: "취소됨" @@ -465,6 +464,9 @@ ko: actions: edit: 편집 clone: 복사 + delete: 삭제 + image: + edit: 편집 adjustments: skipped_changing_canceled_order: "취소된 명령을 바꿀 수 없습니다." begins_at: 시작 시간 @@ -685,6 +687,10 @@ ko: categories: label: 카테고리 search: 탐색 + table: + new_variant: 새로운 변경사항 + edit_image: + close: 뒤로 product_import: title: 제품 가져오기 file_not_found: 파일을 찾을 수 없거나 열 수 없습니다. @@ -724,6 +730,9 @@ ko: product_categories: 제품 카테고리 tax_categories: 세금 카테고리 shipping_categories: 배송 카테고리 + dfc_import_form: + enterprise: "회사" + import: "수입" import: review: 리뷰 import: 수입 @@ -781,6 +790,7 @@ ko: view_products: 제품 페이지로 이동 view_inventory: 인벤토리 페이지로 이동 product_headings: + distributor: 유통업체 producer: 제공자 sku: 재고 관리 단위 name: 이름 @@ -1077,6 +1087,8 @@ ko: rate: 비율 customers: 고객 active: 활성화 하시겠습니까? + connected_apps: + loading: "로딩 중" actions: edit_profile: 설정 properties: 특성 @@ -1694,6 +1706,8 @@ ko: invoice_tax_total: "물품·용역소비세 총합 :" tax_invoice: "세금 계산서" tax_total: "총 세금 (%{rate}) :" + invoice_shipping_category_delivery: "배달" + invoice_shipping_category_pickup: "직접 수령" total_excl_tax: "총합 (세금 별도)" total_incl_tax: "총합 (세금 포함) :" abn: "호주 비즈니스 번호 :" @@ -3341,8 +3355,6 @@ ko: start_date: "시작 날짜" successfully_removed: "성공적으로 제거됨" taxonomy_edit: "분류법 편집" - taxonomy_tree_error: "분류법 트리 오류" - taxonomy_tree_instruction: "분류법 트리 소개" tree: "트리" updating: "업데이트 중" your_order_is_empty_add_product: "주문이 비어 있습니다. 위의 제품을 검색하여 추가하십시오." @@ -3895,6 +3907,11 @@ ko: email: "이메일" total: "총합" billing_address_name: "이름" + taxons: + form: + name: 이름 + permalink: 하이퍼링크 + description: 설명 general_settings: edit: legal_settings: "법적 설정" diff --git a/config/locales/ml.yml b/config/locales/ml.yml new file mode 100644 index 0000000000..78d98f1fa5 --- /dev/null +++ b/config/locales/ml.yml @@ -0,0 +1,4570 @@ +ml: + language_name: "മലയാളം" + time: + formats: + long: "%B %d , %Y %-l:%M %p" + activerecord: + models: + spree/product: ഉൽപ്പന്നം + spree/shipping_method: ഷിപ്പിംഗ് രീതി + attributes: + spree/order/ship_address: + address1: "ഷിപ്പിംഗ് വിലാസം (തെരുവ് + വീട് നമ്പർ)" + address2: "ഷിപ്പിംഗ് വിലാസം വരി 2" + city: "ഷിപ്പിംഗ് വിലാസം നഗരം" + country: "ഷിപ്പിംഗ് വിലാസം രാജ്യം" + phone: "ഫോൺ നമ്പർ" + firstname: "ഒന്നാം പേര് " + lastname: "പേരിന്റെ അവസാന ഭാഗം" + zipcode: "ഷിപ്പിംഗ് വിലാസത്തിന്റെ പിൻകോഡ്" + spree/order/bill_address: + address1: "ബില്ലിംഗ് വിലാസം (തെരുവ് + വീട്ടു നമ്പർ)" + zipcode: "ബില്ലിംഗ് വിലാസത്തിലെ പിൻകോഡ്" + city: "ബില്ലിംഗ് വിലാസത്തിലെ നഗരം" + country: "ബില്ലിംഗ് വിലാസത്തിലെ രാജ്യം" + firstname: "ബില്ലിംഗ് വിലാസത്തിലെ ഒന്നാം പേര്" + lastname: "ബില്ലിംഗ് വിലാസത്തിലെ അവസാന നാമം" + phone: ഉപഭോക്തൃ ഫോൺ + spree/user: + password: "പാസ്സ്‌വേർഡ്" + password_confirmation: "പാസ്സ്‌വേർഡ് സ്ഥിരീകരണം" + reset_password_token: പാസ്സ്‌വേർഡ് ടോക്കൺ റീസെറ്റ് ചെയ്യുക + enterprise_fee: + fee_type: ഫീസ് തരം + spree/order: + payment_state: പേയ്മെന്റ് സ്റ്റേറ്റ് + shipment_state: ഷിപ്പിംഗ് സ്റ്റേറ്റ് + completed_at: പൂർത്തിയാക്കിയത് + number: നമ്പർ + state: സ്റ്റേറ്റ് + email: ഉപഭോക്താവിന്റെ ഇ-മെയിൽ + spree/payment: + amount: തുക + state: സ്റ്റേറ്റ് + source: ഉറവിടം + spree/product: + name: "ഉത്പന്നത്തിന്റെ പേര്" + price: "വില" + primary_taxon: "ഉൽപ്പന്ന വിഭാഗം" + supplier: "വിതരണക്കാരൻ" + shipping_category_id: "ഷിപ്പിംഗ് വിഭാഗം" + variant_unit: "വേരിയന്റ് യൂണിറ്റ്" + variant_unit_name: "വേരിയന്റ് യൂണിറ്റിന്റെ പേര്" + unit_value: "യൂണിറ്റ് മൂല്യം" + spree/credit_card: + base: "ക്രെഡിറ്റ് കാർഡ്" + number: "നമ്പർ" + month: "മാസം" + verification_value: "സ്ഥിരീകരണ മൂല്യം" + year: "വർഷം" + order_cycle: + orders_close_at: അവസാന തീയതി + variant_override: + count_on_hand: "കയ്യിൽ" + spree/payment_method/calculator: + preferred_flat_percent: "കാൽക്കുലേറ്റർ ഫ്ലാറ്റ് ശതമാനം:" + preferred_amount: "കാൽക്കുലേറ്റർ തുക:" + preferred_first_item: "കാൽക്കുലേറ്റർ ആദ്യ ഇനം:" + preferred_additional_item: "കാൽക്കുലേറ്റർ അധിക ഇനത്തിന്റെ വില:" + preferred_max_items: "കാൽക്കുലേറ്റർ പരമാവധി ഇനങ്ങൾ:" + preferred_minimal_amount: "കാൽക്കുലേറ്റർ ഏറ്റവും കുറഞ്ഞ തുക:" + preferred_normal_amount: "കാൽക്കുലേറ്റർ സാധാരണ തുക:" + preferred_discount_amount: "കാൽക്കുലേറ്റർ കിഴിവ് തുക:" + preferred_unit_from_list: "പട്ടികയിൽ നിന്നുള്ള കാൽക്കുലേറ്റർ യൂണിറ്റ്:" + preferred_per_unit: "ഓരോ യൂണിറ്റിനും ഉള്ള കാൽക്കുലേറ്റർ:" + enterprise: + white_label_logo_link: "കടയുടെ മുൻവശത്ത് ഉപയോഗിക്കുന്ന ലോഗോയ്ക്കുള്ള ലിങ്ക്" + errors: + models: + enterprise_fee: + inherit_tax_requires_per_item_calculator: "നികുതി വിഭാഗം അവകാശമായി ലഭിക്കുന്നതിന് ഓരോ ഇനത്തിനും കാൽക്കുലേറ്റർ ആവശ്യമാണ്." + spree/user: + attributes: + email: + taken: "ഈ ഈമെയിലുമായി ബന്ധപ്പെടുത്തിയ ഒരു അക്കൗണ്ട് ഇതിനകം ഉണ്ട്. ദയവായി ലോഗിൻ ചെയ്യുക അല്ലെങ്കിൽ നിങ്ങളുടെ പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുക." + reset_password_token: + invalid: അസാധുവാണ് + spree/order: + no_card: വില ഈടാക്കാൻ അംഗീകൃത ക്രെഡിറ്റ് കാർഡുകളൊന്നും ലഭ്യമല്ല + spree/credit_card: + attributes: + base: + card_expired: "കാലാവധി കഴിഞ്ഞു" + order_cycle: + attributes: + orders_close_at: + after_orders_open_at: തുറന്ന തീയതിക്ക് ശേഷമായിരിക്കണം + variant_override: + count_on_hand: + using_producer_stock_settings_but_count_on_hand_set: "പ്രൊഡ്യൂസർ സ്റ്റോക്ക് ക്രമീകരണങ്ങൾ ഉപയോഗിക്കുന്നതിനാൽ ശൂന്യമായിരിക്കണം" + on_demand_but_count_on_hand_set: "ആവശ്യകത കൂടിയിരുന്നാൽ ശൂന്യമായിരിക്കണം" + limited_stock_but_no_count_on_hand: "സ്റ്റോക്ക് പരിമിതമായതിനാൽ വ്യക്തമാക്കണം" + messages: + confirmation: "%{attribute} എന്നതുമായി പൊരുത്തപ്പെടുന്നില്ല" + blank: "ശൂന്യമായിരിക്കാൻ കഴിയില്ല" + too_short: "വളരെ ചെറുതാണ് (കുറഞ്ഞത് %{count} അക്ഷരങ്ങൾ ഉണ്ടാകണം)" + errors: + messages: + content_type_invalid: "ഒരു അസാധുവായ ഉള്ളടക്കം ഉണ്ട്" + file_size_out_of_range: "%{file_size} വലുപ്പം ആവശ്യമായ ശ്രേണിയ്‌ക്കിടയിലല്ല" + limit_out_of_range: "ആകെ എണ്ണം പരിധിക്ക് പുറത്താണ്" + image_metadata_missing: "ചിത്രം സാധുതയുള്ളതല്ല" + dimension_min_inclusion: "%{width} x %{height} പിക്സലിനു തുല്യമോ വലുതോ ആയിരിക്കണം." + dimension_max_inclusion: "%{width} x %{height} പിക്സലിനു തുല്യമോ കുറവോ ആയിരിക്കണം." + dimension_width_inclusion: "%{min} പിക്സലിനും %{max} പിക്സലിനും ഇടയിൽ വീതി ഉൾപ്പെടുത്തിയിട്ടില്ല." + dimension_height_inclusion: "%{min} പിക്സലിനും %{max} പിക്സലിനും ഇടയിൽ ഉയരം ഉൾപ്പെടുത്തിയിട്ടില്ല." + dimension_width_greater_than_or_equal_to: "വീതി %{length} പിക്സലിനു തുല്യമോ വലുതോ ആയിരിക്കണം." + dimension_height_greater_than_or_equal_to: "ഉയരം %{length} പിക്സലിനു തുല്യമോ വലുതോ ആയിരിക്കണം." + dimension_width_less_than_or_equal_to: "വീതി %{length} പിക്സലിനു തുല്യമോ കുറവോ ആയിരിക്കണം." + dimension_height_less_than_or_equal_to: "ഉയരം %{length} പിക്സലിനു തുല്യമോ കുറവോ ആയിരിക്കണം." + dimension_width_equal_to: "വീതി %{length} പിക്സലിന് തുല്യമായിരിക്കണം." + dimension_height_equal_to: "ഉയരം %{length} പിക്സലിന് തുല്യമായിരിക്കണം." + aspect_ratio_not_square: "ഒരു ചതുര ചിത്രം ആയിരിക്കണം" + aspect_ratio_not_portrait: "ഒരു ഛായാചിത്രം ആയിരിക്കണം" + aspect_ratio_not_landscape: "ഒരു ഭൂപ്രകൃതി ചിത്രമായിരിക്കണം" + aspect_ratio_is_not: "%{aspect_ratio} എന്ന വീക്ഷണാനുപാതം ഉണ്ടായിരിക്കണം" + aspect_ratio_unknown: "വീക്ഷണാനുപാതം അജ്ഞാതമാണ് " + image_not_processable: "ചിത്രം സാധുതയുള്ളതല്ല" + not_found: + title: "നിങ്ങൾ തിരയുന്ന പേജ് നിലവിലില്ല (404)" + message_html: "ദയവായി വീണ്ടും ശ്രമിക്കുക

ഇതൊരു താൽക്കാലിക പ്രശ്നമായിരിക്കാം. മുൻപുണ്ടായിരുന്ന സ്‌ക്രീനിലേക്ക് മടങ്ങാൻ ബാക്ക് ബട്ടൺ ക്ലിക്ക് ചെയ്യുക അല്ലെങ്കിൽ ഹോം പേജ് ലേക്ക് തിരികെ പോയി വീണ്ടും ശ്രമിക്കുക.

പിന്തുണയ്ക്കായി ബന്ധപ്പെടുക

പ്രശ്‌നം നിലനിൽക്കുന്നുണ്ടെങ്കിലോ അത്യാവശ്യമോ ആണെങ്കിൽ, അതിനെക്കുറിച്ച് ഞങ്ങളോട് പറയുക. ആഗോള ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ലോക്കൽ പേജിൽ നിന്ന് ഞങ്ങളുടെ ബന്ധപ്പെടുന്നതിനുള്ള വിശദാംശങ്ങൾ കണ്ടെത്തുക.

നഷ്‌ടമായ പേജ് എന്തിനെക്കുറിച്ചാണെന്ന് നിങ്ങൾക്ക് കഴിയുന്നത്ര വിശദാംശങ്ങൾ നൽകാൻ കഴിയുമെങ്കിൽ അത് ശരിക്കും ഞങ്ങളെ സഹായിക്കും.

" + internal_server_error: + title: "ക്ഷമിക്കണം, എന്തോ തകരാറ് സംഭവിച്ചു (500)" + message_html: "ദയവായി വീണ്ടും ശ്രമിക്കുക

ഇതൊരു താൽക്കാലിക പ്രശ്നമായിരിക്കാം. മുൻപുണ്ടായിരുന്ന സ്‌ക്രീനിലേക്ക് മടങ്ങാൻ ബാക്ക് ബട്ടണിൽ ക്ലിക്ക് ചെയ്യുക അല്ലെങ്കിൽ ഹോം പേജ് ലേക്ക് തിരികെ പോയി വീണ്ടും ശ്രമിക്കുക.

ഞങ്ങൾ പ്രശ്നം പരിഹരിച്ചുകൊണ്ടിരിക്കുകയാണ്

നിങ്ങൾ ഈ പ്രശ്നം മുമ്പ് കണ്ടിട്ടുണ്ടെങ്കിൽ, ഞങ്ങൾ മിക്കവാറും ഇതിനെക്കുറിച്ച് ഇതിനകം തന്നെ അറിയുകയും ഒരു പരിഹാരത്തിനായി പ്രവർത്തിച്ചുകൊണ്ടിരിക്കുകയും ആകാം. വരുന്ന എല്ലാ പിശകുകളും ഞങ്ങൾ രേഖപ്പെടുത്തുന്നുണ്ട്.

പിന്തുണയ്ക്കായി ബന്ധപ്പെടുക

പ്രശ്‌നം നിലനിൽക്കുന്നുണ്ടെങ്കിലോ അത്യാവശ്യമോ ആണെങ്കിൽ, അതിനെക്കുറിച്ച് ഞങ്ങളോട് പറയുക. ആഗോള ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ലോക്കൽ പേജിൽ നിന്ന് ഞങ്ങളെ ബന്ധപ്പെടുന്നതിനുള്ള വിശദാംശങ്ങൾ കണ്ടെത്തുക.

ഈ തകരാറ് സംഭവിച്ചപ്പോൾ നിങ്ങൾ എന്തുചെയ്യുകയായിരുന്നു എന്നതിനെക്കുറിച്ച് നിങ്ങൾക്ക് കഴിയുന്നത്ര വിശദാംശങ്ങൾ നൽകാൻ കഴിയുമെങ്കിൽ അത് ഞങ്ങളെ സഹായിക്കും.

" + unprocessable_entity: + title: "നിങ്ങൾ ആഗ്രഹിച്ച മാറ്റം നിരസിക്കപ്പെട്ടു (422)" + message_html: "

നിങ്ങൾ ആഗ്രഹിച്ച മാറ്റം നിരസിക്കപ്പെട്ടു. നിങ്ങൾക്ക് അനുമതി ഇല്ലാത്ത എന്തെങ്കിലും മാറ്റാൻ നിങ്ങൾ ശ്രമിച്ചിരിക്കാം.

ഹോം പേജിലേക്ക് തിരിച്ചുപോകുക

" + stimulus_reflex_error: "ക്ഷമിക്കണം, എന്തോ കുഴപ്പം സംഭവിച്ചു.\n\n ഇതൊരു താൽക്കാലിക പ്രശ്നമായിരിക്കാം, അതിനാൽ ദയവായി വീണ്ടും ശ്രമിക്കുക അല്ലെങ്കിൽ പേജ് റീലോഡ് ചെയ്യുക.\n ഞങ്ങൾ എല്ലാ പ്രശ്നങ്ങളും രേഖപ്പെടുത്തുന്നുണ്ട്, ചിലപ്പോൾ ഒരു പരിഹാരത്തിനായി പ്രവർത്തിച്ചുകൊണ്ടിരിക്കുകയും ആകാം\n പ്രശ്നം നിലനിൽക്കുന്നുവെങ്കിലോ അല്ലെങ്കിൽ ആത്യാവശ്യമോ ആണെങ്കിൽ, ദയവായി ഞങ്ങളുമായി ബന്ധപ്പെടുക. " + stripe: + error_code: + incorrect_number: "കാർഡ് നമ്പർ തെറ്റാണ്." + invalid_number: "കാർഡ് നമ്പർ ഒരു സാധുവായ ക്രെഡിറ്റ് കാർഡ് നമ്പറല്ല." + invalid_expiry_month: "കാർഡിന്റെ കാലഹരണ മാസം അസാധുവാണ്." + invalid_expiry_year: "കാർഡിന്റെ കാലഹരണ വർഷം അസാധുവാണ്." + invalid_cvc: "കാർഡിന്റെ സുരക്ഷാ കോഡ് അസാധുവാണ്." + expired_card: "കാർഡ് കാലഹരണപ്പെട്ടു." + incorrect_cvc: "കാർഡിന്റെ സുരക്ഷാ കോഡ് തെറ്റാണ്." + incorrect_zip: "കാർഡിന്റെ സിപ് കോഡ് നിർണ്ണയം പരാജയപ്പെട്ടു." + card_declined: "കാർഡ് നിരസിക്കപ്പെട്ടു." + missing: "നിരക്ക് ഈടാക്കാൻ കഴിയുന്ന ഒരു കാർഡുംഉപഭോക്താവിന് ഇല്ല." + processing_error: "കാർഡ് പ്രോസസ്സ് ചെയ്യുമ്പോൾ ഒരു തകരാറ് സംഭവിച്ചു." + rate_limit: "അഭ്യർത്ഥനകൾ എപിഐ-യിൽ വളരെ വേഗത്തിൽ വന്നതിനാൽ ഒരു തകരാറ് സംഭവിച്ചു. നിങ്ങൾ സ്ഥിരമായി ഈ തകരാറ് നേരിടുകയാണെങ്കിൽ ഞങ്ങളെ അറിയിക്കുക." + authentication_required: "ഈ ഇടപാടിന്റെ ആധികാരികത ഉറപ്പിക്കേണ്ടത് ആവശ്യമായതിനാൽ കാർഡ് നിരസിച്ചു." + approve_with_id: "പേയ്‌മെന്റ് അംഗീകരിക്കാനാവില്ല." + call_issuer: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + card_not_supported: "കാർഡ് ഇത്തരത്തിലുള്ള വാങ്ങലുകൾ പിന്തുണയ്ക്കുന്നില്ല." + card_velocity_exceeded: "ഉപഭോക്താവിന്റെ കാർഡിൽ ലഭ്യമായ ബാലൻസ് അല്ലെങ്കിൽ ക്രെഡിറ്റ് പരിധി കവിഞ്ഞു." + currency_not_supported: "കാർഡ് നിർദ്ദിഷ്ട കറൻസിയെ പിന്തുണയ്ക്കുന്നില്ല." + do_not_honor: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + do_not_try_again: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + duplicate_transaction: "സമാനമായ തുകയും ക്രെഡിറ്റ് കാർഡ് വിവരങ്ങളും ഉള്ള ഒരു ഇടപാട് ഈയിടെ സമർപ്പിച്ചു." + fraudulent: "രേഖകൾ തട്ടിപ്പാണെന്ന് സംശയിക്കുന്നതിനാൽ പേയ്‌മെന്റ് നിരസിക്കപ്പെട്ടു." + generic_decline: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + incorrect_pin: "നൽകിയ പിൻ തെറ്റാണ്. ഒരു കാർഡ് റീഡർ ഉപയോഗിച്ച് നടത്തുന്ന ഇടപാടുകൾക്ക് മാത്രമേ ഈ നിരസിക്കൽ കോഡ് ബാധകമാകൂ." + insufficient_funds: "വാങ്ങൽ പൂർത്തിയാക്കാൻ കാർഡിൽ മതിയായ ഫണ്ടില്ല." + invalid_account: "കാർഡ് അല്ലെങ്കിൽ കാർഡ് ബന്ധിപ്പിച്ചിട്ടുള്ള അക്കൗണ്ട് അസാധുവാണ്." + invalid_amount: "പേയ്മെന്റ് തുക അസാധുവാണ്, അല്ലെങ്കിൽ അനുവദനീയമായ തുകയേക്കാൾ കൂടുതലാണ്." + invalid_pin: "നൽകിയ പിൻ തെറ്റാണ്. ഒരു കാർഡ് റീഡർ ഉപയോഗിച്ച് നടത്തുന്ന ഇടപാടുകൾക്ക് മാത്രമേ ഈ നിരസിക്കൽ കോഡ് ബാധകമാകൂ." + issuer_not_available: "കാർഡ് ഇഷ്യൂവറെ ബന്ധപ്പെടാൻ കഴിയാത്തതിനാൽ ഇടപാടിന് അംഗീകാരം നൽകാനായില്ല." + lost_card: "കാർഡ് നഷ്ടപ്പെട്ടതായി റിപ്പോർട്ട് ചെയ്തതിനാൽ ഇടപാട് നിരസിക്കപ്പെട്ടു." + merchant_blacklist: "രേഖകൾ ഉപയോക്താവിന്റെ ബ്ലോക്ക് ലിസ്റ്റിലെ മൂല്യവുമായി പൊരുത്തപ്പെടുന്നതിനാൽ പേയ്‌മെന്റ് നിരസിക്കപ്പെട്ടു." + new_account_information_available: "കാർഡ് അല്ലെങ്കിൽ കാർഡ് ബന്ധിപ്പിച്ചിട്ടുള്ള അക്കൗണ്ട് അസാധുവാണ്." + no_action_taken: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + not_permitted: "പേയ്മെന്റ് അനുവദനീയമല്ല." + offline_pin_required: "ഒരു പിൻ ആവശ്യമുള്ളതിനാൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + online_or_offline_pin_required: "ഒരു പിൻ ആവശ്യമുള്ളതിനാൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + pickup_card: "ഈ പേയ്‌മെന്റ് നടത്താൻ കാർഡ് ഉപയോഗിക്കാൻ കഴിയില്ല (ഇത് നഷ്‌ടപ്പെട്ടതായോ മോഷ്‌ടിക്കപ്പെട്ടതായോ റിപ്പോർട്ടുചെയ്യപ്പെട്ടിരിക്കാൻ സാധ്യതയുണ്ട്)." + pin_try_exceeded: "അനുവദനീയമായ പിൻ രേഖപ്പെടുത്തലുകളുടെ എണ്ണം കവിഞ്ഞു." + reenter_transaction: "അജ്ഞാതമായ കാരണത്താൽ ഇഷ്യൂവറിന് ഇടപാട് പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല." + restricted_card: "ഈ ഇടപാട് നടത്താൻ കാർഡ് ഉപയോഗിക്കാൻ കഴിയില്ല (ഇത് നഷ്‌ടപ്പെട്ടതായോ മോഷ്‌ടിക്കപ്പെട്ടതായോ റിപ്പോർട്ടുചെയ്യപ്പെട്ടിരിക്കാൻ സാധ്യതയുണ്ട്)." + revocation_of_all_authorizations: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + revocation_of_authorization: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + security_violation: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + service_not_allowed: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + stolen_card: "കാർഡ് മോഷ്ടിക്കപ്പെട്ടതായി റിപ്പോർട്ട് ചെയ്തിട്ടുള്ളതിനാൽ ഇടപാട് നിരസിക്കപ്പെട്ടു." + stop_payment_order: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + testmode_decline: "ഒരു സ്ട്രൈപ്പ് ടെസ്റ്റ് കാർഡ് നമ്പർ ഉപയോഗിച്ചു." + transaction_not_allowed: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + try_again_later: "അജ്ഞാതമായ കാരണത്താൽ കാർഡ് നിരസിക്കപ്പെട്ടു." + withdrawal_count_limit_exceeded: "ഉപഭോക്താവിന്റെ കാർഡിൽ ലഭ്യമായ ബാലൻസ് അല്ലെങ്കിൽ ക്രെഡിറ്റ് പരിധി കവിഞ്ഞു." + activemodel: + errors: + messages: + inclusion: "പട്ടികയിൽ ഉൾപ്പെടുത്തിയിട്ടില്ല" + models: + order_management/subscriptions/validator: + attributes: + subscription_line_items: + at_least_one_product: "^ദയവായി ഒരു ഉൽപ്പന്നമെങ്കിലും ചേർക്കുക" + not_available: "തിരഞ്ഞെടുത്ത ഷെഡ്യൂളിൽ നിന്ന് %{name} ലഭ്യമല്ല" + ends_at: + after_begins_at: "ആരംഭിക്കുന്നതിന് ശേഷം ആയിരിക്കണം" + customer: + does_not_belong_to_shop: "%{shop} -ൽ ഉൾപ്പെടുന്നില്ല" + schedule: + not_coordinated_by_shop: "%{shop} ഏകോപിപ്പിച്ചിട്ടില്ല" + payment_method: + not_available_to_shop: "%{shop} -ന് ലഭ്യമല്ല" + invalid_type: "ക്യാഷ് അല്ലെങ്കിൽ സ്ട്രൈപ്പ് രീതി ആയിരിക്കണം" + charges_not_allowed: "^ക്രെഡിറ്റ് കാർഡ് നിരക്കുകൾക്ക് ഈ ഉപഭോക്താവ് അനുമതി നൽകിയിട്ടില്ല." + no_default_card: "^ഈ ഉപഭോക്താവിന് ഡിഫോൽറ്റ് കാർഡ് ലഭ്യമല്ല" + shipping_method: + not_available_to_shop: "%{shop} -ന് ലഭ്യമല്ല" + card_details: "കാർഡ് വിശദാംശങ്ങൾ" + card_type: "കാർഡ് തരം" + cardholder_name: "കാർഡ് ഉടമയുടെ പേര്" + community_forum_url: "കമ്മ്യൂണിറ്റി ഫോറം യുആർഎൽ" + customer_instructions: "ഉപഭോക്തൃ നിർദ്ദേശങ്ങൾ" + additional_information: "അധിക വിവരം" + devise: + passwords: + spree_user: + cannot_be_blank: "ഉപയോക്തൃ പാസ്‌വേഡ് നൽകാതിരിക്കാൻ കഴിയില്ല. ദയവായി ഒരു പാസ്‌വേഡ് നൽകുക." + confirmations: + send_instructions: "കുറച്ച് മിനിറ്റിനുള്ളിൽ നിങ്ങളുടെ അക്കൗണ്ട് എങ്ങനെ സ്ഥിരീകരിക്കാം എന്നതിനെക്കുറിച്ചുള്ള നിർദ്ദേശങ്ങളടങ്ങിയ ഒരു ഇമെയിൽ നിങ്ങൾക്ക് ലഭിക്കും." + failed_to_send: "നിങ്ങളുടെ സ്ഥിരീകരണ ഇമെയിൽ അയയ്‌ക്കുമ്പോൾ ഒരു തകരാറ് സംഭവിച്ചു." + resend_confirmation_email: "സ്ഥിരീകരണ ഇമെയിൽ വീണ്ടും അയയ്ക്കുക." + confirmed: "നിങ്ങളുടെ ഇമെയിൽ സ്ഥിരീകരിച്ചതിന് നന്ദി! നിങ്ങൾക്ക് ഇപ്പോൾ ലോഗിൻ ചെയ്യാം." + not_confirmed: "നിങ്ങളുടെ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കാൻ കഴിഞ്ഞില്ല. ഒരുപക്ഷേ നിങ്ങൾ ഇതിനകം ഈ ഘട്ടം പൂർത്തിയാക്കിയിട്ടുണ്ടോ?" + user_confirmations: + spree_user: + send_instructions: "കുറച്ച് മിനിറ്റിനുള്ളിൽ നിങ്ങളുടെ അക്കൗണ്ട് എങ്ങനെ സ്ഥിരീകരിക്കാം എന്നതിനെക്കുറിച്ചുള്ള നിർദ്ദേശങ്ങളടങ്ങിയ ഒരു ഇമെയിൽ നിങ്ങൾക്ക് ലഭിക്കും." + confirmation_sent: "ഇമെയിൽ സ്ഥിരീകരണം അയച്ചു" + confirmation_not_sent: "സ്ഥിരീകരണ ഇമെയിൽ അയയ്ക്കുന്നതിൽ തകരാറ്" + user_registrations: + spree_user: + signed_up_but_unconfirmed: "സ്ഥിരീകരണ ലിങ്കുള്ള ഒരു സന്ദേശം നിങ്ങളുടെ ഇമെയിൽ വിലാസത്തിലേക്ക് അയച്ചു. നിങ്ങളുടെ അക്കൗണ്ട് സജീവമാക്കാൻ ലിങ്ക് തുറക്കുക." + unknown_error: "നിങ്ങളുടെ അക്കൗണ്ട് സൃഷ്ടിക്കുമ്പോൾ എന്തോ തകരാറ് സംഭവിച്ചു. നിങ്ങളുടെ ഇമെയിൽ വിലാസം പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക." + failure: + disabled: "നിങ്ങളുടെ അക്കൗണ്ട് പ്രവർത്തനരഹിതമാക്കി. ഈ പ്രശ്നം പരിഹരിക്കാൻ അഡ്മിനിസ്ട്രേറ്ററെ ബന്ധപ്പെടുക." + invalid: | + അസാധുവായ ഇമെയിൽ അല്ലെങ്കിൽ പാസ്സ്‌വേർഡ്. + നിങ്ങൾ കഴിഞ്ഞ തവണ ഗസ്റ്റ് മോഡിൽ ആയിരുന്നോ? ഒരുപക്ഷേ നിങ്ങൾ ഒരു അക്കൗണ്ട് സൃഷ്‌ടിക്കുകയോ പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുകയോ ചെയ്യേണ്ടതുണ്ട്. + unconfirmed: "തുടരുന്നതിന് മുമ്പ് നിങ്ങളുടെ അക്കൗണ്ട് സ്ഥിരീകരിക്കേണ്ടതുണ്ട്." + already_registered: "ഈ ഇമെയിൽ വിലാസം ഇതിനകം രജിസ്റ്റർ ചെയ്തിട്ടുണ്ട്. തുടരാൻ ലോഗിൻ ചെയ്യുക അല്ലെങ്കിൽ തിരികെ പോയി മറ്റൊരു ഇമെയിൽ വിലാസം ഉപയോഗിക്കുക." + success: + logged_in_succesfully: "വിജയകരമായി ലോഗിൻ ചെയ്തു" + sessions: + signed_out: "വിജയകരമായി സൈൻ ഔട്ട് ചെയ്‌തു." + already_signed_out: "വിജയകരമായി സൈൻ ഔട്ട് ചെയ്‌തു." + user_passwords: + spree_user: + updated_not_active: "നിങ്ങളുടെ പാസ്‌വേഡ് പുനഃസജ്ജീകരിച്ചു, എന്നാൽ നിങ്ങളുടെ ഇമെയിൽ ഇതുവരെ സ്ഥിരീകരിച്ചിട്ടില്ല." + updated: "നിങ്ങളുടെ പാസ്സ്‌വേർഡ് വിജയകരമായി മാറ്റി. നിങ്ങൾ ഇപ്പോൾ സൈൻ ഇൻ ചെയ്‌തു." + send_instructions: "കുറച്ച് മിനിറ്റിനുള്ളിൽ നിങ്ങളുടെ അക്കൗണ്ട് എങ്ങനെ സ്ഥിരീകരിക്കാം എന്നതിനെക്കുറിച്ചുള്ള നിർദ്ദേശങ്ങളടങ്ങിയ ഒരു ഇമെയിൽ നിങ്ങൾക്ക് ലഭിക്കും." + oidc: + failure: "%{error}: മൂലം സൈൻ ഇൻ ചെയ്യാൻ കഴിഞ്ഞില്ല " + home_page_alert_html: "ഹോം പേജ് അലേർട്ട് എച്ടിഎംഎൽ" + hub_signup_case_studies_html: "ഹബ് സൈൻഅപ്പ് കേസ് പഠനങ്ങൾ എച്ടിഎംഎൽ" + hub_signup_detail_html: "ഹബ് സൈൻഅപ്പ് വിശദാംശങ്ങൾ എച്ടിഎംഎൽ" + hub_signup_pricing_table_html: "ഹബ് സൈൻഅപ്പ് വില പട്ടിക എച്ടിഎംഎൽ" + group_signup_case_studies_html: "ഗ്രൂപ്പ് സൈൻഅപ്പ് കേസ് പഠനങ്ങൾ എച്ടിഎംഎൽ" + group_signup_detail_html: "ഗ്രൂപ്പ് സൈൻ അപ്പ് വിശദാംശങ്ങൾ എച്ടിഎംഎൽ" + group_signup_pricing_table_html: "ഗ്രൂപ്പ് സൈൻഅപ്പ് വില പട്ടിക എച്ടിഎംഎൽ" + item_description: "ഇനത്തെ കുറിച്ചുള്ള വിശദീകരണം" + menu_1_icon_name: "മെനു 1 ഐക്കൺ പേര്" + menu_2_icon_name: "മെനു 2 ഐക്കൺ പേര്" + menu_3_icon_name: "മെനു 3 ഐക്കൺ പേര്" + menu_4_icon_name: "മെനു 4 ഐക്കൺ പേര്" + menu_5_icon_name: "മെനു 5 ഐക്കൺ പേര്" + menu_6_icon_name: "മെനു 6 ഐക്കൺ പേര്" + menu_7_icon_name: "മെനു 7 ഐക്കൺ പേര്" + models: + order_cycle: + cloned_order_cycle_name: "%{order_cycle} -ന്റെ പകർപ്പ്" + tax_rate: + included_in_price: "വിലയിൽ ഉൾപ്പെടുത്തിയിട്ടുണ്ട്" + open_street_map_enabled: "റോഡ് മാപ് പ്രവർത്തനക്ഷമമാക്കുക" + open_street_map_default_latitude: "റോഡ് മാപ് സ്ഥിര അക്ഷാംശം തുറക്കുക" + open_street_map_default_longitude: "റോഡ് മാപ് സ്ഥിര രേഖാംശം തുറക്കുക" + open_street_map_provider_name: "റോഡ് മാപ് ദാതാവിന്റെ പേര് തുറക്കുക" + open_street_map_provider_options: "റോഡ് മാപ് പ്രൊവൈഡർ ഓപ്ഷനുകൾ തുറക്കുക" + producer_signup_case_studies_html: "പ്രൊഡ്യൂസർ സൈൻഅപ്പ് കേസ് സ്റ്റഡീസ് എച്ടിഎംഎൽ" + producer_signup_detail_html: "പ്രൊഡ്യൂസർ സൈൻഅപ്പ് വിശദാംശങ്ങൾ എച്ടിഎംഎൽ" + producer_signup_pricing_table_html: "പ്രൊഡ്യൂസർ സൈൻഅപ്പ് പ്രൈസിംഗ് ടേബിൾ എച്ടിഎംഎൽ" + producers_social: "നിർമാതാക്കളുടെ സാമൂഹ്യം" + resume_order: "ഓർഡർ പുനരാരംഭിക്കുക" + sku: "എസ്കെയു" + subtotal: "ആകെത്തുക" + tax_rate: "നികുതി നിരക്ക്" + validators: + date_time_string_validator: + not_string_error: "ഒരു ശൃംഖല ആയിരിക്കണം" + invalid_format_error: "സാധുതയുള്ളതായിരിക്കണം" + integer_array_validator: + not_array_error: "ക്രമീകരിച്ചത് ആയിരിക്കണം" + invalid_element_error: "സാധുവായ പൂർണ്ണസംഖ്യകൾ മാത്രം അടങ്ങിയിരിക്കണം" + report_job: + report_failed: | + ഈ റിപ്പോർട്ട് പരാജയപ്പെട്ടു. ഇത് പ്രോസസ്സ് ചെയ്യാൻ സാധിക്കാത്തവിധം വലുതായിരിക്കാം. + ഞങ്ങൾ അത് പരിശോധിക്കും എന്നാൽ പ്രശ്നം നിലനിൽക്കുകയാണെങ്കിൽ ദയവായി ഞങ്ങളെ അറിയിക്കുക. + enterprise_mailer: + confirmation_instructions: + subject: "%{enterprise} -ന് വേണ്ടി ഇമെയിൽ വിലാസം ദയവായി സ്ഥിരീകരിക്കുക" + welcome: + subject: "%{enterprise} ഇപ്പോൾ %{sitename} -ലാണ്" + email_welcome: "സ്വാഗതം" + email_registered: "ഇപ്പോൾ ഇതിന്റെ ഭാഗമാണ്" + email_userguide_html: "നിങ്ങളുടെ പ്രൊഡ്യൂസർ അല്ലെങ്കിൽ ഹബ് സജ്ജീകരിക്കുന്നതിനുള്ള വിശദമായ ഉപയോക്തൃ ഗൈഡ് ഇവിടെയുണ്ട്: %{link}" + userguide: "ഫുഡ് നെറ്റ്‌വർക്ക് ഉപയോക്തൃ ഗൈഡ് തുറക്കുക" + email_admin_html: "%{link} -ലേക്ക് ലോഗിൻ ചെയ്തോ അല്ലെങ്കിൽ ഹോംപേജിന്റെ മുകളിൽ വലതുവശത്തുള്ള കോഗിൽ ക്ലിക്കുചെയ്‌ത് അഡ്മിനിസ്ട്രേഷൻ തിരഞ്ഞെടുത്തോ നിങ്ങൾക്ക് നിങ്ങളുടെ അക്കൗണ്ട് നിയന്ത്രിക്കാനാകും." + admin_panel: "അഡ്മിൻ പാനൽ" + email_community_html: "ഒരു ഫുഡ് എന്റർപ്രൈസ് നേരിടുന്ന വെല്ലുവിളികളും ഓഎഫ്എൻ സോഫ്‌റ്റ്‌വെയറുമായി ബന്ധപ്പെട്ട കമ്മ്യൂണിറ്റി ചർച്ചകൾക്കുമായി ഞങ്ങൾക്ക് ഒരു ഓൺലൈൻ ഫോറവും ഉണ്ട്. അതിൽ ചേരാൻ നിങ്ങളെ ക്ഷണിക്കുന്നു. ഞങ്ങൾ നിരന്തരം വളർന്നുകൊണ്ടിരിക്കുന്നു, ഈ ഫോറത്തിലെ നിങ്ങളുടെ നിർദ്ദേശങ്ങൾ അടുത്ത സംഭവങ്ങൾ രൂപപ്പെടുത്തും. %{link}" + join_community: "കമ്മ്യൂണിറ്റിയിൽ ചേരുക" + invite_manager: + subject: "%{enterprise} നിങ്ങളെ മാനേജരാകാൻ ക്ഷണിച്ചു" + producer_mailer: + order_cycle: + subject: "%{producer}-നുള്ള ഓർഡർ സൈക്കിൾ റിപ്പോർട്ട് " + provider_settings: "ദാതാവിന്റെ ക്രമീകരണങ്ങൾ" + report_mailer: + report_ready: + subject: "റിപ്പോർട്ട് തയ്യാറാണ്" + heading: "റിപ്പോർട്ട് ഡൗൺലോഡ് ചെയ്യാൻ തയ്യാറാണ്" + intro: | + ചുവടെയുള്ള ലിങ്ക് ഒരാഴ്ചയ്ക്ക് ശേഷം കാലഹരണപ്പെടും. + link_label: "%{name}" + shipment_mailer: + shipped_email: + dear_customer: "പ്രിയ ഉപഭോക്താവേ," + instructions: "നിങ്ങളുടെ ഓർഡർ അയച്ചുകഴിഞ്ഞു" + shipment_summary: "ഷിപ്പിംഗ് സംഗ്രഹം" + subject: "ഷിപ്പ്മെന്റ് അറിയിപ്പ്" + thanks: "നിങ്ങളുടെ ഇടപാടിന് നന്ദി." + track_information: "ട്രാക്കിംഗ് വിവരങ്ങൾ: %{tracking}" + track_link: "ട്രാക്കിംഗ് ലിങ്ക്: %{url}" + subscription_mailer: + placement_summary_email: + subject: അടുത്തിടെ നൽകിയ സബ്‌സ്‌ക്രിപ്‌ഷൻ ഓർഡറുകളുടെ ഒരു സംഗ്രഹം + greeting: "ഹലോ %{name} ," + intro: "%{shop} -ൽ നിന്ന് ഇപ്പോൾ നൽകിയിട്ടുള്ള സബ്‌സ്‌ക്രിപ്‌ഷൻ ഓർഡറുകളുടെ ഒരു സംഗ്രഹം ചുവടെയുണ്ട്." + confirmation_summary_email: + subject: അടുത്തിടെ സ്ഥിരീകരിച്ച സബ്സ്ക്രിപ്ഷൻ ഓർഡറുകളുടെ ഒരു സംഗ്രഹം + greeting: "ഹലോ %{name} ," + intro: "%{shop} -ൽ നിന്ന് ഇപ്പോൾ അന്തിമമാക്കിയ സബ്‌സ്‌ക്രിപ്‌ഷൻ ഓർഡറുകളുടെ ഒരു സംഗ്രഹം ചുവടെയുണ്ട്." + summary_overview: + total: സ്വയമേവയുള്ള പ്രോസസ്സിംഗിനായി ആകെ %{count} സബ്‌സ്‌ക്രിപ്‌ഷനുകൾ അടയാളപ്പെടുത്തി. + success_zero: ഇവയിൽ ഒന്നും വിജയകരമായി പ്രോസസ്സ് ചെയ്തിട്ടില്ല. + success_some: ഇതിൽ, %{count} വിജയകരമായി പ്രോസസ്സ് ചെയ്തു. + success_all: എല്ലാം വിജയകരമായി പ്രോസസ്സ് ചെയ്തു. + issues: നേരിട്ട പ്രശ്നങ്ങളുടെ വിശദാംശങ്ങൾ ചുവടെ നൽകിയിരിക്കുന്നു. + summary_detail: + no_message_provided: തകരാറ് സന്ദേശം നൽകിയിട്ടില്ല + changes: + title: മതിയായ സ്റ്റോക്കില്ല (%{count} ഓർഡറുകൾ) + explainer: ഈ ഓർഡറുകൾ പ്രോസസ്സ് ചെയ്തുവെങ്കിലും ആവശ്യപ്പെട്ട ചില ഇനങ്ങൾക്ക് മതിയായ സ്റ്റോക്ക് ഇല്ല + empty: + title: സ്റ്റോക്കില്ല (%{count} ഓർഡറുകൾ) + explainer: ആവശ്യപ്പെട്ട ഇനങ്ങൾക്കൊന്നും സ്റ്റോക്ക് ലഭ്യമല്ലാത്തതിനാൽ ഈ ഓർഡറുകൾ പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല + complete: + title: ഇതിനകം പ്രോസസ്സ് ചെയ്തു (%{count} ഓർഡറുകൾ) + explainer: ഈ ഓർഡറുകൾ ഇതിനകം പൂർത്തിയായതായി അടയാളപ്പെടുത്തി, അതിനാൽ അവ വിട്ടുകളഞ്ഞു + processing: + title: തകരാറ് നേരിട്ടു (%{count} ഓർഡറുകൾ) + explainer: ഒരു തകരാറ് കാരണം ഈ ഓർഡറുകളുടെ സ്വയമേവയുള്ള പ്രോസസ്സിംഗ് പരാജയപ്പെട്ടു. സാധ്യമായ ഇടങ്ങളിൽ തകരാറ് പട്ടികപ്പെടുത്തിയിട്ടുണ്ട്. + failed_payment: + title: പണം കൊടുക്കൽ പരാജയപ്പെട്ടു (%{count} ഓർഡറുകൾ) + explainer: ഒരു തകരാറ് കാരണം ഈ ഓർഡറുകൾക്കുള്ള ഓട്ടോമാറ്റിക് പേയ്മെന്റ് പ്രോസസ്സിംഗ് പരാജയപ്പെട്ടു. സാധ്യമായ ഇടങ്ങളിൽ തകരാറ് പട്ടികപ്പെടുത്തിയിട്ടുണ്ട്. + other: + title: മറ്റ് പരാജയം (%{count} ഓർഡറുകൾ) + explainer: അജ്ഞാതമായ ഒരു കാരണത്താൽ ഈ ഓർഡറുകളുടെ ഓട്ടോമാറ്റിക് പ്രോസസ്സിംഗ് പരാജയപ്പെട്ടു. ഇത് ഇത് സംഭവിക്കാൻ പാടില്ലാത്തതാണ്, നിങ്ങൾ ഇത് കാണുകയാണെങ്കിൽ ദയവായി ഞങ്ങളുമായി ബന്ധപ്പെടുക. + home: "ഒഎഫ്എൻ" + title: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക്" + welcome_to: "-ലേക്ക് സ്വാഗതം" + site_meta_description: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഇന്ത്യ സോഫ്റ്റ്‌വെയർ പ്ലാറ്റ്‌ഫോം, കർഷകരെ ന്യായമായ വിലയ്ക്ക് ഉൽപ്പന്നങ്ങൾ ഓൺലൈനിൽ വിൽക്കാൻ അനുവദിക്കുന്നു. ഇത് ആഹാരസാധനങ്ങൾ വിൽക്കുന്നതിനായി പ്രത്യേകം നിർമ്മിച്ചതാണ്, അതിനാൽ അതിൽ മാത്രം ഉള്ള തന്ത്രപരമായ അളവുകളോ സ്റ്റോക്ക് ലെവലുകളോ കൈകാര്യം ചെയ്യാൻ ഇതിന് കഴിയും - ഉദാഹരണത്തിന്, ഒരു ഡസൻ മുട്ടകൾ, ഒരു കെട്ട് ചീര, ഭാരത്തിൽ വ്യത്യാസമുള്ള ഒരു മുഴുവൻ കോഴി…" + search_by_name: പേര്, നഗരം, സംസ്ഥാനം അല്ലെങ്കിൽ പിൻ കോഡ് എന്നിവ പ്രകാരം തിരയുക... + producers_join: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ ചേരാൻ ഇന്ത്യൻ കർഷകരെ ഇപ്പോൾ സ്വാഗതം ചെയ്യുന്നു. + charges_sales_tax: ജി എസ് ടി ഈടാക്കുന്നുണ്ടോ? + business_address: "വ്യാപാര മേൽവിലാസം" + print_invoice: "ഇൻവോയ്സ് പ്രിന്റ് ചെയ്യുക" + print_ticket: "ടിക്കറ്റ് പ്രിന്റ് ചെയ്യുക" + select_ticket_printer: "ടിക്കറ്റുകൾക്കായി പ്രിന്റർ തിരഞ്ഞെടുക്കുക" + send_invoice: "ഇൻവോയ്സ് അയയ്ക്കുക" + resend_confirmation: "സ്ഥിരീകരണം വീണ്ടും അയയ്ക്കുക" + view_order: "ഓർഡർ കാണുക" + edit_order: "ഓർഡർ എഡിറ്റ് ചെയ്യുക" + ship_order: "ഓർഡർ അയക്കുക" + cancel_order: "ഓർഡർ റദ്ദാക്കുക" + confirm_send_invoice: "ഈ ഓർഡറിന്റെ ഇൻവോയ്സ് ഉപഭോക്താവിന് അയയ്ക്കും. നിങ്ങൾക്ക് തുടരണമെന്നുള്ളത് തീർച്ചയാണോ?" + confirm_resend_order_confirmation: "ഓർഡർ സ്ഥിരീകരണ ഇമെയിൽ വീണ്ടും അയയ്‌ക്കണമെന്നത് തീർച്ചയാണോ?" + invoice: "ഇൻവോയ്സ്" + invoices: "ഇൻവോയ്സുകൾ" + file: "ഫയൽ" + active: "സജീവം" + download: "ഡൗൺലോഡ്" + cancelled: "റദ്ദാക്കി" + more: "കൂടുതൽ" + say_no: "ഇല്ല" + say_yes: "അതെ" + ongoing: നടന്നുകൊണ്ടിരിക്കുന്നു + bill_address: ബില്ലിംഗ് വിലാസം + ship_address: ഷിപ്പിംഗ് വിലാസം + sort_order_cycles_on_shopfront_by: "ഓർഡർ സൈക്കിളുകൾ ഷോപ്പ്ഫ്രണ്ട് പ്രകാരം തിരഞ്ഞെടുക്കുക" + required_fields: ആവശ്യമുള്ള ഫീൽഡുകൾ ഒരു നക്ഷത്രചിഹ്നം ഉപയോഗിച്ച് സൂചിപ്പിച്ചിരിക്കുന്നു + select_continue: തിരഞ്ഞെടുത്ത് തുടരുക + remove: നീക്കം ചെയ്യുക + collapse_all: എല്ലാം സങ്കോചിപ്പിക്കുക + expand_all: എല്ലാം വികസിപ്പിക്കുക + loading: ലോഡിംഗ്... + show_more: കൂടുതൽ കാണിക്കുക + show_all: എല്ലാം കാണിക്കൂ + show_all_with_more: "എല്ലാം കാണിക്കുക (%{num} കൂടുതൽ)" + cancel: റദ്ദാക്കുക + edit: എഡിറ്റ് ചെയ്യുക + clone: ക്ലോൺ + distributors: വിതരണക്കാർ + distribution: വിതരണം + order_cycles: ഓർഡർ സൈക്കിളുകൾ + bulk_order_management: ബൾക്ക് ഓർഡർ മാനേജ്മെന്റ് + enterprises: എൻറ്റർപ്രൈസിസ് + enterprise_groups: ഗ്രൂപ്പുകൾ + reports: റിപ്പോർട്ടുകൾ + listing_reports: റിപ്പോർട്ടുകളുടെ പട്ടിക + variant_overrides: ചരക്കുപട്ടിക + import: ഇറക്കുമതി ചെയ്യുക + spree_products: സ്പ്രി ഉൽപ്പന്നങ്ങൾ + all: എല്ലാം + current: നിലവിലുള്ളത് + available: ലഭ്യമാണ് + dashboard: ഡാഷ്ബോർഡ് + undefined: നിർവചിക്കാത്തത് + unused: ഉപയോഗിക്കാത്തത് + admin_and_handling: അഡ്മിൻ & കൈകാര്യംചെയ്യൽ + profile: പ്രൊഫൈൽ + supplier_only: വിതരണക്കാരൻ മാത്രം + has_shopfront: ഷോപ്പ്ഫ്രണ്ട് ഉണ്ട് + weight: ഭാരം + volume: വ്യാപ്തം + items: ഇനങ്ങൾ + summary: സംഗ്രഹം + detailed: വിശദമായത് + updated: അപ്ഡേറ്റ് ചെയ്തു + 'yes': "അതെ" + 'no': "ഇല്ല" + y: 'വൈ' + n: 'എൻ' + powered_by: പ്രായോജകർ + blocked_cookies_alert: "ഈ ഷോപ്പ്ഫ്രണ്ട് ഉപയോഗിക്കുന്നതിന് ആവശ്യമായ കുക്കീസ് നിങ്ങളുടെ ബ്രൗസർ ബ്ലോക്ക് ചെയ്യുന്നുണ്ടാകാം. കുക്കീസ് അനുവദിക്കുന്നതിനും പേജ് റീലോഡ് ചെയ്യുന്നതിനും താഴെ ക്ലിക്ക് ചെയ്യുക." + allow_cookies: "കുക്കീസ് അനുവദിക്കുക" + none: ഒന്നുമില്ല + notes: കുറിപ്പുകൾ + error: തകരാറ് + voucher: വൗച്ചർ + processing_payment: "പേയ്‌മെന്റ് പ്രോസസ്സ് ചെയ്യുന്നു..." + no_pending_payments: "തീർപ്പാക്കാത്ത പേയ്‌മെന്റുകളൊന്നുമില്ല" + invalid_payment_state: "അസാധുവായ പേയ്‌മെന്റ് അവസ്ഥ: %{state}" + filter_results: ഫിൽട്ടർ ഫലങ്ങൾ + clear_filters: ഫിൽട്ടറുകൾ മായ്ക്കുക + quantity: അളവ് + pick_up: പിക്ക്അപ്പ് + ok: ശരി + copy: പകർത്തുക + change_my_password: "എന്റെ പാസ്‌വേഡ് മാറ്റുക" + update_password: "പാസ്‌വേഡ് അപ്‌ഡേറ്റ് ചെയ്യുക" + password_confirmation: പാസ്‌വേഡ് സ്ഥിരീകരണം + reset_password_token: പാസ്സ്‌വേർഡ് ടോക്കൺ റീസെറ്റ് ചെയ്യുക + expired: കാലഹരണപ്പെട്ടു, പുതിയൊരെണ്ണം അഭ്യർത്ഥിക്കുക + back_to_payments_list: "പേയ്‌മെന്റ് ലിസ്റ്റിലേക്ക് മടങ്ങുക" + maestro_or_solo_cards: "മാസ്ട്രോ/സോളോ കാർഡുകൾ" + backordered: "ബാക്ക്ഓർഡേർഡ്‌" + on_hand: "കയ്യിൽ" + on hand: "കയ്യിൽ" + ship: "അയക്കൽ" + shipping_category: "അയക്കൽ വിഭാഗം" + height: "ഉയരം" + width: "വീതി" + depth: "ആഴം" + payment_could_not_process: "പേയ്മെന്റ് പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല" + payment_could_not_complete: "പേയ്‌മെന്റ് പൂർത്തിയാക്കാനായില്ല" + actions: + create_and_add_another: "ഉണ്ടാക്കുക, മറ്റൊന്ന് ചേർക്കുക" + create: "ഉണ്ടാക്കുക" + cancel: "റദ്ദാക്കുക" + resume: "പുനരാരംഭിക്കുക" + save: "സേവ് ചെയ്യുക" + edit: "എഡിറ്റ് ചെയ്യുക" + update: "അപ്ഡേറ്റ് ചെയ്യുക" + delete: "ഡിലീറ്റ് ചെയ്യുക" + add: "ചേർക്കുക" + cut: "കട്ട് " + paste: "പേസ്റ്റ്" + destroy: "നശിപ്പിക്കുക" + rename: "പേരുമാറ്റുക" + admin: + products_page: + title: ഉൽപ്പന്നങ്ങൾ + filters: + categories: + title: വിഭാഗങ്ങൾ + selected_categories: "%{count} വിഭാഗങ്ങൾ തിരഞ്ഞെടുത്തു" + producers: + title: പ്രൊഡ്യൂസേഴ്‌സ് + selected_producers: "%{count} പ്രൊഡ്യൂസേഴ്‌സിനെ തിരഞ്ഞെടുത്തു" + per_page: "ഓരോ പേജിലും %{count} ഇനങ്ങൾ" + colums: നിരകൾ + columns: + name: പേര് + unit: യൂണിറ്റ് + price: വില + producer: പ്രൊഡ്യൂസർ + category: വിഭാഗം + sku: എസ്.കെ.യു + on_hand: "കയ്യിൽ" + on_demand: "ആവശ്യപ്പെടുന്നതനുസരിച്ച്" + tax_category: "നികുതി വിഭാഗം" + inherits_properties: "അനന്തരാവകാശമായി ലഭിച്ച സ്വത്തുക്കൾ?" + import_date: "ഇറക്കുമതി തീയതി" + actions: പ്രവർത്തനങ്ങൾ + columns_selector: + unit: യൂണിറ്റ് + price: വില + producer: പ്രൊഡ്യൂസർ + category: വിഭാഗം + sku: എസ്.കെ.യു + on_hand: "കയ്യിൽ" + on_demand: "ആവശ്യപ്പെടുന്നതനുസരിച്ച്" + tax_category: "നികുതി വിഭാഗം" + inherits_properties: "അനന്തരാവകാശമായി ലഭിച്ച സ്വത്തുക്കൾ?" + import_date: "ഇറക്കുമതി തീയതി" + actions: + edit: എഡിറ്റ് ചെയ്യുക + clone: ക്ലോൺ + delete: ഡിലീറ്റ് ചെയ്യുക + image: + edit: എഡിറ്റ് ചെയ്യുക + adjustments: + skipped_changing_canceled_order: "നിങ്ങൾക്ക് റദ്ദാക്കിയ ഓർഡർ മാറ്റാൻ കഴിയില്ല." + begins_at: ആരംഭിക്കുന്നത് + begins_on: -ൽ ആരംഭിക്കുന്നു + bill_address: "ബിൽ വിലാസം" + ship_address: "അയക്കേണ്ട വിലാസം" + customer: ഉപഭോക്താവ് + date: തീയതി + email: ഇമെയിൽ + ends_at: അവസാനിക്കുന്നത് + ends_on: -ൽ അവസാനിക്കുന്നു + name: പേര് + first_name: പേരിന്റെ ആദ്യഭാഗം + last_name: പേരിന്റെ അവസാന ഭാഗം + on_hand: കയ്യിൽ + on_demand: ആവശ്യപ്പെടുന്നതനുസരിച്ച് + on_demand?: ആവശ്യപ്പെടുന്നതനുസരിച്ച്? + order_cycle: ഓർഡർ സൈക്കിൾ + payment: പേയ്മെന്റ് + payment_method: പണമടയ്ക്കൽ രീതി + phone: ഫോൺ + price: വില + producer: പ്രൊഡ്യൂസർ + image: ചിത്രം + product: ഉൽപ്പന്നം + quantity: അളവ് + schedule: പട്ടിക + shipping: ഷിപ്പിംഗ് + shipping_method: ഷിപ്പിംഗ് രീതി + shop: ഷോപ്പ് + sku: എസ്.കെ.യു + status_state: സ്റ്റേറ്റ് + tags: ടാഗുകൾ + variant: വേരിയന്റ് + weight: ഭാരം + volume: വ്യാപ്തം + items: ഇനങ്ങൾ + select_all: എല്ലാം തിരഞ്ഞെടുക്കുക + quick_search: ദ്രുത തിരയൽ + clear_all: എല്ലാം മായ്ക്കുക + start_date: "ആരംഭിക്കുന്ന തീയതി" + end_date: "അവസാനിക്കുന്ന തീയതി" + unsaved_changes: "ചില മാറ്റങ്ങൾ നിങ്ങൾ സേവ് ചെയ്തിട്ടില്ല" + form_invalid: "ഫോമിൽ നഷ്‌ടമായതോ അസാധുവായതോ ആയ ഫീൽഡുകൾ ഉണ്ട്" + clear_filters: ഫിൽട്ടറുകൾ മായ്ക്കുക + clear: ക്ലിയർ + save: സേവ് ചെയ്യുക + cancel: റദ്ദാക്കുക + back: തിരികെ + show_more: കൂടുതൽ കാണിക്കുക + show_n_more: '%{num} കൂടുതൽ കാണിക്കുക' + choose: "തിരഞ്ഞെടുക്കുക..." + please_select: ദയവായി തിരഞ്ഞെടുക്കുക... + column_save_as_default: സ്ഥിരസ്ഥിതിയായി സംരക്ഷിക്കുക + columns: നിരകൾ + actions: പ്രവർത്തനങ്ങൾ + viewing: "കാണുന്നത്: %{current_view_name}" + description: വിവരണം + whats_this: എന്താണിത്? + tag_has_rules: "ഈ ടാഗിനായി നിലവിലുള്ള നിയമങ്ങൾ: %{num}" + has_one_rule: "ഒരു നിയമമുണ്ട്" + has_n_rules: "%{num} നിയമങ്ങളുണ്ട്" + unsaved_confirm_leave: "ഈ പേജിൽ സേവ് ചെയ്യാത്ത മാറ്റങ്ങളുണ്ട്. സേവ് ചെയ്യാതെ തുടരണോ?" + available_units: "ലഭ്യമായ യൂണിറ്റുകൾ" + terms_of_service_have_been_updated_html: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിന്റെ സേവന നിബന്ധനകൾ അപ്‌ഡേറ്റ് ചെയ്‌തു: %{tos_link}" + terms_of_service: സേവന നിബന്ധനകൾ വായിക്കുക + accept_terms_of_service: സേവന നിബന്ധനകൾ അംഗീകരിക്കുക + shopfront_settings: + embedded_shopfront_settings: "ഉൾച്ചേർത്ത ഷോപ്പ്ഫ്രണ്ട് ക്രമീകരണങ്ങൾ" + enable_embedded_shopfronts: "എംബഡഡ് ഷോപ്പ്ഫ്രണ്ടുകൾ പ്രവർത്തനക്ഷമമാക്കുക" + embedded_shopfronts_whitelist: "ബാഹ്യ ഡൊമെയ്‌നുകളുടെ വൈറ്റ്‌ലിസ്റ്റ്" + terms_of_service_files: + create: + select_file: "ദയവായി ആദ്യം ഒരു ഫയൽ തിരഞ്ഞെടുക്കുക." + show: + title: "സേവന നിബന്ധന ഫയലുകൾ" + no_files: "സേവന നിബന്ധനകളൊന്നും ഇതുവരെ അപ്‌ലോഡ് ചെയ്തിട്ടില്ല." + current_terms_html: "നിലവിലെ %{tos_link} കാണുക. അപ്‌ലോഡ് സമയം: %{datetime}." + terms_of_service: "സേവന നിബന്ധനകൾ" + delete: "ഫയൽ ഡിലീറ്റ് ചെയ്യുക" + confirm_delete: "നിലവിലെ സേവന നിബന്ധനകളുടെ ഫയൽ ഡിലീറ്റ് ചെയ്യണമെന്ന് തീർച്ചയാണോ?" + attachment: "അറ്റാച്ച്മെന്റ്" + create_terms_of_service: "സേവന നിബന്ധനകളുടെ ഫയൽ സൃഷ്ടിക്കുക" + number_localization: + number_localization_settings: "നമ്പർ ലോകലൈസേഷൻ ക്രമീകരണങ്ങൾ" + enable_localized_number: "അന്തർദേശീയ ആയിരം/ദശാംശം സെപ്പറേറ്റർ ലോജിക് ഉപയോഗിക്കുക" + invoice_settings: + edit: + title: "ഇൻവോയ്സ് ക്രമീകരണങ്ങൾ" + enable_invoices?: "ഇൻവോയ്‌സുകൾ പ്രവർത്തനക്ഷമമാക്കണോ?" + invoice_style2?: "ഓരോ ഇനത്തിനുമുള്ള നികുതി നിരക്ക് വിവരവും, ഒരു നിരക്കിന് മൊത്തം നികുതി വിശദാംശങ്ങളും ഉൾപ്പെടുന്ന ഇതര ഇൻവോയ്സ് മോഡൽ ഉപയോഗിക്കുക (നികുതി ഒഴികെയുള്ള വിലകൾ പ്രദർശിപ്പിക്കുന്ന രാജ്യങ്ങൾക്ക് ഇതുവരെ അനുയോജ്യമല്ല)" + enterprise_number_required_on_invoices?: "ഇൻവോയ്സ് സൃഷ്ടിക്കാൻ ഒരു എബിഎൻ ആവശ്യമുണ്ടോ?" + stripe_connect_settings: + edit: + title: "സ്ട്രൈപ്പ് കണക്ട്" + settings: "ക്രമീകരണങ്ങൾ" + stripe_connect_enabled: സ്ട്രൈപ്പ് കണക്ട് ഉപയോഗിച്ച് പേയ്‌മെന്റുകൾ സ്വീകരിക്കാൻ ഷോപ്പുകളെ പ്രാപ്‌തമാക്കണോ? + no_api_key_msg: ഈ എന്റർപ്രൈസസിന് സ്ട്രൈപ്പ് അക്കൗണ്ട് നിലവിലില്ല. + configuration_explanation_html: സ്ട്രൈപ്പ് കണക്റ്റ് ഇന്റഗ്രേഷൻ കോൺഫിഗർ ചെയ്യുന്നതിനുള്ള വിശദമായ നിർദ്ദേശങ്ങൾക്ക്, ദയവായി ഈ ഗൈഡ് പരിശോധിക്കുക. + status: സ്ഥിതി + ok: ശരി + instance_secret_key: അടിയന്തര രഹസ്യ കീ + account_id: അക്കൗണ്ട് ഐഡി + business_name: ബിസിനസ്സ് പേര് + charges_enabled: കൂലി നിരക്കുകൾ പ്രവർത്തനക്ഷമമാക്കി + charges_enabled_warning: "മുന്നറിയിപ്പ്: നിങ്ങളുടെ അക്കൗണ്ടിന് കൂലി നിരക്കുകൾ പ്രവർത്തനക്ഷമമാക്കിയിട്ടില്ല" + auth_fail_error: നിങ്ങൾ നൽകിയ എപിഐ കീ അസാധുവാണ് + empty_api_key_error_html: സ്ട്രൈപ്പ് എപിഐ കീ നൽകിയിട്ടില്ല. നിങ്ങളുടെ എപിഐ കീ സജ്ജമാക്കാൻ, ഈ നിർദ്ദേശങ്ങൾ പാലിക്കുക + matomo_settings: + edit: + title: "മറ്റോമോ സെറ്റിങ്‌സ്" + matomo_url: "മറ്റോമോ യുആർഎൽ" + matomo_site_id: "മറ്റോമോ സൈറ്റ് ഐഡി" + matomo_tag_manager_url: "മറ്റോമോ ടാഗ് മാനേജർ യുആർഎൽ" + info_html: "മറ്റോമോ ഒരു വെബ്, മൊബൈൽ അനലിറ്റിക്സ് ആപ്ലിക്കേഷനാണ്. നിങ്ങൾക്ക് ഒന്നുകിൽ മറ്റോമോ ഓൺ-പ്രിമൈസസ് ഹോസ്റ്റ് ചെയ്യാം അല്ലെങ്കിൽ ക്ലൗഡ്-ഹോസ്‌റ്റഡ് സേവനം ഉപയോഗിക്കാം. കൂടുതൽ വിവരങ്ങൾക്ക് matomo.org കാണുക." + config_instructions_html: "ഇവിടെ നിങ്ങൾക്ക് ഓഎഫ്എൻ മറ്റോമോ സംയോജനം ക്രമീകരിക്കാം. താഴെയുള്ള മറ്റോമോ യുആർഎൽ, ഉപയോക്തൃ ട്രാക്കിംഗ് വിവരങ്ങൾ അയയ്‌ക്കുന്ന മറ്റോമോ ഇൻസ്റ്റൻസിലേക്ക് നയിക്കുന്നു; ഇത് ശൂന്യമായി വിടുകയാണെങ്കിൽ, മറ്റോമോ ഉപയോക്തൃ ട്രാക്കിംഗ് പ്രവർത്തനരഹിതമാകും. സൈറ്റ് ഐഡി ഫീൽഡ് നിർബന്ധമല്ല, എന്നാൽ നിങ്ങൾ ഒരു മറ്റോമോ ഇൻസ്റ്റൻസിൽ ഒന്നിലധികം വെബ്‌സൈറ്റുകൾ ട്രാക്കുചെയ്യുകയാണെങ്കിൽ അത് ഉപയോഗപ്രദമാണ്; ഇത് മറ്റോമോ ഇൻസ്റ്റൻസ് കൺസോളിൽ കാണാം." + config_instructions_tag_manager_html: "മറ്റോമോ ടാഗ് മാനേജർ യുആർഎൽ സജ്ജീകരിക്കുന്നത് മറ്റോമോ ടാഗ് മാനേജർ സംവിധാനം പ്രവർത്തനക്ഷമമാക്കുന്നു. അനലിറ്റിക്സ് ഇവന്റുകൾ സജ്ജീകരിക്കാൻ ഈ ഉപകരണം നിങ്ങളെ അനുവദിക്കുന്നു. മറ്റോമോ ടാഗ് മാനേജർ യുആർഎൽ, മറ്റോമോ ടാഗ് മാനേജറിന്റെ ഇൻസ്റ്റാൾ കോഡ് വിഭാഗത്തിൽ നിന്ന് പകർത്തിയതാണ്. ഈ ഓപ്‌ഷനുകൾ യുആർഎൽ മാറ്റുമെന്നതിനാൽ നിങ്ങൾ ശരിയായ കണ്ടെയ്‌നറും എൻവയോണ്മെന്റും തിരഞ്ഞെടുത്തുവെന്ന് ഉറപ്പാക്കുക." + customers: + index: + new_customer: "പുതിയ ഉപഭോക്താവ്" + code: കോഡ് + duplicate_code: "ഈ കോഡ് ഇതിനകം ഉപയോഗിച്ചു." + bill_address: "ബില്ലിംഗ് വിലാസം" + ship_address: "ഷിപ്പിംഗ് വിലാസം" + balance: "മിച്ചം" + update_address_success: "വിലാസം വിജയകരമായി അപ്ഡേറ്റ് ചെയ്തു." + update_address_error: "ക്ഷമിക്കണം! ദയവായി ആവശ്യമായ എല്ലാ ഫീൽഡുകളിലും വിവരങ്ങൾ ചേർക്കുക!" + edit_bill_address: "ബില്ലിംഗ് വിലാസം തിരുത്തുക" + edit_ship_address: "ഷിപ്പിംഗ് വിലാസം തിരുത്തുക" + required_fileds: "ആവശ്യമുള്ള ഫീൽഡുകൾ ഒരു നക്ഷത്രചിഹ്നം ഉപയോഗിച്ച് സൂചിപ്പിച്ചിരിക്കുന്നു" + select_country: "രാജ്യം തിരഞ്ഞെടുക്കുക" + select_state: "സംസ്ഥാനം തിരഞ്ഞെടുക്കുക" + edit: "എഡിറ്റ് ചെയ്യുക" + update_address: "വിലാസം അപ്ഡേറ്റ് ചെയ്യുക" + confirm_delete: "ഡിലീറ്റ് ചെയ്യണമെന്ന് ഉറപ്പാണോ?" + search_by_email: "ഇമെയിൽ/കോഡ് വഴി തിരയുക..." + guest_label: "ഗസ്റ്റ് ചെക്ക്ഔട്ട്" + credit_owed: "കടപ്പെട്ടിരിക്കുന്നു" + balance_due: "മിച്ച കടം" + destroy: + has_associated_subscriptions: "ഡിലീറ്റ് പരാജയപ്പെട്ടു: ഈ ഉപഭോക്താവിന് സജീവമായ സബ്‌സ്‌ക്രിപ്‌ഷനുകളുണ്ട്. ആദ്യം അവ റദ്ദാക്കുക." + contents: + edit: + title: ഉള്ളടക്കം + header: തലക്കെട്ട് + home_page: ഹോം പേജ് + producer_signup_page: പ്രൊഡ്യൂസർ സൈൻഅപ്പ് പേജ് + hub_signup_page: ഹബ് സൈൻഅപ്പ് പേജ് + group_signup_page: ഗ്രൂപ്പ് സൈൻഅപ്പ് പേജ് + main_links: പ്രധാന മെനു ലിങ്കുകൾ + footer_and_external_links: അടിക്കുറിപ്പും ബാഹ്യ ലിങ്കുകളും + your_content: നിങ്ങളുടെ ഉള്ളടക്കം + user_guide: ഉപയോക്തൃ ഗൈഡ് + map: മാപ്പ് + enterprise_fees: + index: + title: "എന്റർപ്രൈസ് ഫീസ്" + enterprise: "എന്റർപ്രൈസ്" + fee_type: "ഫീസ് തരം" + name: "പേര്" + tax_category: "നികുതി വിഭാഗം" + calculator: "കാൽക്കുലേറ്റർ" + calculator_values: "കാൽക്കുലേറ്റർ മൂല്യങ്ങൾ" + search: "തിരയുക" + name_placeholder: "ഉദാ: പാക്കിംഗ് ഫീസ്" + enterprise_groups: + index: + new_button: പുതിയ എന്റർപ്രൈസ് ഗ്രൂപ്പ് + form_primary_details: + primary_details: "പ്രാഥമിക വിവരങ്ങൾ" + form_users: + users: "ഉപയോക്താക്കൾ" + form_about: + about: "കുറിച്ച്" + form_images: + images: "ചിത്രങ്ങൾ" + form_address: + contact: "ബന്ധപ്പെടുക" + form_web: + web: "വെബ് ഉറവിടങ്ങൾ" + enterprise_roles: + form: + manages: നിർവ്വഹണം + enterprise_role: + manages: നിർവ്വഹണം + products: + unit_name_placeholder: 'ഉദാ: കുലകൾ' + index: + unit: യൂണിറ്റ് + display_as: പ്രദർശന മാർഗ്ഗം + category: വിഭാഗം + tax_category: നികുതി വിഭാഗം + inherits_properties?: സ്വത്തുക്കൾ അവകാശമാക്കുന്നുണ്ടോ? + av_on: "Av. ഓൺ" + import_date: ഇറക്കുമതി ചെയ്തത് + upload_an_image: ഒരു ചിത്രം അപ്‌ലോഡ് ചെയ്യുക + seo: + product_search_keywords: "ഉൽപ്പന്നം തിരയുന്നതിനുള്ള കീവേഡുകൾ" + product_search_tip: "കടകളിൽ നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ തിരയാൻ സഹായിക്കുന്നതിന് വാക്കുകൾ ടൈപ്പ് ചെയ്യുക. ഓരോ കീവേർഡിനും ഇടയിൽ ശൂന്യസ്ഥലം നൽകണം." + seo_tip: "വെബിൽ നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ തിരയാൻ സഹായിക്കുന്നതിന് വാക്കുകൾ ടൈപ്പ് ചെയ്യുക. ഓരോ കീവേർഡിനും ഇടയിൽ ശൂന്യസ്ഥലം നൽകണം." + search: "തിരയുക" + properties: + property_name: "പ്രോപർറ്റിയുടെ പേര്" + inherited_property: "പാരമ്പര്യ സ്വത്ത്" + variants: + infinity: "അനന്തത" + to_order_tip: "ഓർഡർ ചെയ്യാൻ തയ്യാറാക്കിയ ഇനങ്ങൾക്ക് സെറ്റ് സ്റ്റോക്ക് ലെവൽ ഇല്ല, ഉദാഹരണത്തിന്, ഓർഡർ അനുസരിച്ച് തയ്യാറാക്കുന്ന ബ്രഡ് പാക്കറ്റുകൾ." + back_to_products_list: "ഉൽപ്പന്നങ്ങളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + editing_product: "ഉൽപ്പന്നം തിരുത്തുന്നു" + tabs: + product_details: "ഉൽപ്പന്നത്തിന്റെ വിവരം" + group_buy_options: "ഗ്രൂപ്പ് വാങ്ങൽ ഓപ്ഷനുകൾ" + images: "ചിത്രങ്ങൾ" + variants: "വകഭേദങ്ങൾ" + product_properties: "ഉൽപ്പന്ന സവിശേഷതകൾ" + products_v3: + index: + header: + title: ഉൽപ്പന്നങ്ങൾ മൊത്തമായി തിരുത്തുക + loading: നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ ലോഡ് ചെയ്യുന്നു + sort: + pagination: + total_html: "നിങ്ങളുടെ തിരയൽ മാനദണ്ഡങ്ങൾക്കനുസരിച്ച് %{total} ഉൽപ്പന്നങ്ങൾ കണ്ടെത്തി. %{from} മുതൽ %{to} വരെ കാണിക്കുന്നു." + per_page: + show: കാണിക്കുക + per_page: "ഓരോ പേജിലും %{num}" + clear_search: തിരയൽ മായ്‌ക്കുക + filters: + search_products: ഉൽപ്പന്നങ്ങൾക്കായി തിരയുക + all_producers: എല്ലാ പ്രൊഡ്യൂസേഴ്‌സും + all_categories: എല്ലാ വിഭാഗങ്ങളും + producers: + label: പ്രൊഡ്യൂസഴ്സ് + categories: + label: വിഭാഗങ്ങൾ + search: തിരയുക + no_products: + no_products_found: ഉൽപ്പന്നങ്ങളൊന്നും കണ്ടെത്തിയില്ല + import_products: ഒന്നിലധികം ഉൽപ്പന്നങ്ങൾ ഇറക്കുമതി ചെയ്യുക + no_products_found_for_search: നിങ്ങളുടെ തിരയൽ മാനദണ്ഡങ്ങൾക്കനുസരിച്ച് ഉൽപ്പന്നങ്ങളൊന്നും കണ്ടെത്തിയില്ല + table: + changed_summary: + one: "%{count} ഉൽപ്പന്നം പരിഷ്‌ക്കരിച്ചു." + other: "%{count} ഉൽപ്പന്നങ്ങൾ പരിഷ്‌ക്കരിച്ചു." + error_summary: + saved: + one: "%{count} ഉൽപ്പന്നം ശരിയായി സേവ് ചെയ്തു, പക്ഷേ" + other: "%{count} ഉൽപ്പന്നങ്ങൾ ശരിയായി സേവ് ചെയ്തു, പക്ഷേ" + invalid: + one: "%{count} ഉൽപ്പന്നം സംരക്ഷിക്കാൻ കഴിഞ്ഞില്ല. പിശകുകൾ അവലോകനം ചെയ്‌ത് വീണ്ടും ശ്രമിക്കുക." + other: "%{count} ഉൽപ്പന്നങ്ങൾ സേവ് ചെയ്യാൻ കഴിഞ്ഞില്ല. തകരാറുകൾ അവലോകനം ചെയ്‌ത് വീണ്ടും ശ്രമിക്കുക." + reset: മാറ്റങ്ങൾ ഉപേക്ഷിക്കുക + save: മാറ്റങ്ങൾ സേവ് ചെയ്യുക + new_variant: പുതിയ വേരിയന്റ് + bulk_update: + success: മാറ്റങ്ങൾ സേവ് ചെയ്തു. + edit_image: + close: തിരികെ + product_import: + title: ഉൽപ്പന്ന ഇറക്കുമതി + file_not_found: ഫയൽ കണ്ടെത്തിയില്ല അല്ലെങ്കിൽ തുറക്കാൻ കഴിഞ്ഞില്ല + no_data: സ്‌പ്രെഡ്‌ഷീറ്റിൽ ഡാറ്റയൊന്നും കണ്ടെത്തിയില്ല + confirm_reset: "ഇതിനായുള്ള എല്ലാ ഉൽപ്പന്നങ്ങളുടെയും സ്റ്റോക്ക് ലെവൽ പൂജ്യമായി സജ്ജമാക്കും\n അപ്‌ലോഡ് ചെയ്ത ഫയലിൽ ഇല്ലാത്ത എന്റർപ്രൈസ്" + model: + no_file: "തകരാറ്: ഒരു ഫയലും അപ്‌ലോഡ് ചെയ്‌തിട്ടില്ല" + could_not_process: "ഫയൽ പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല: അസാധുവായ ഫയൽ തരം" + incorrect_value: തെറ്റായ മൂല്യം + conditional_blank: യൂണിറ്റ്_ടൈപ്പ് ശൂന്യമായിരിക്കാൻ കഴിയില്ല + no_product: ഡാറ്റാബേസിലെ ഏതെങ്കിലും ഉൽപ്പന്നങ്ങളുമായി പൊരുത്തപ്പെടുന്നില്ല + not_found: ഡാറ്റാബേസിൽ കണ്ടെത്തിയില്ല + category_not_found: അനുവദനീയമായ വിഭാഗങ്ങളുമായി പൊരുത്തപ്പെടുന്നില്ല. ഉൽപ്പന്ന ഇറക്കുമതി പേജിൽ നിന്ന് തിരഞ്ഞെടുക്കാനുള്ള ശരിയായ വിഭാഗങ്ങൾ കാണുക, അല്ലെങ്കിൽ അക്ഷരത്തെറ്റ് ഇല്ലെന്ന് ഉറപ്പുവരുത്തുക. + not_updatable: ഉൽപ്പന്ന ഇറക്കുമതി വഴി നിലവിലുള്ള ഉൽപ്പന്നങ്ങളിൽ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല + values_must_be_same: ഒരേ പേരിലുള്ള ഉൽപ്പന്നങ്ങൾക്ക് സമാനമായിരിക്കണം + blank: ശൂന്യമായിരിക്കാൻ കഴിയില്ല + products_no_permission: ഈ എന്റർപ്രൈസസിനായി ഉൽപ്പന്നങ്ങൾ നിയന്ത്രിക്കാൻ നിങ്ങൾക്ക് അനുമതിയില്ല + inventory_no_permission: ഈ പ്രൊഡ്യൂസറിനായി ചരക്കുപട്ടിക സൃഷ്ടിക്കാൻ നിങ്ങൾക്ക് അനുമതിയില്ല + none_saved: ഉൽപ്പന്നങ്ങളൊന്നും വിജയകരമായി സേവ് ചെയ്തില്ല + line_number: "ലൈൻ %{number} :" + encoding_error: "നിങ്ങളുടെ സോഴ്സ് ഫയലിന്റെ ഭാഷാ ക്രമീകരണം പരിശോധിച്ച് അത് യുടിഎഫ്-8 എൻകോഡിംഗിൽ സേവ് ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പുവരുത്തുക" + unexpected_error: "ഫയൽ തുറക്കുന്ന സമയം ഉൽപ്പന്ന ഇറക്കുമതിയിൽ ഒരു അപ്രതീക്ഷിത തകരാറ് നേരിട്ടു: %{error_message}" + malformed_csv: "ഉൽപ്പന്ന ഇറക്കുമതി നടക്കുമ്പോൾ തെറ്റായ സി.എസ്.വി അഭിമുഖീകരിച്ചു: %{error_message}" + index: + notice: "ശ്രദ്ധിക്കുക" + beta_notice: "ഈ ഫീച്ചർ ഇപ്പോഴും ബീറ്റയിലാണ്: ഇത് ഉപയോഗിക്കുമ്പോൾ നിങ്ങൾക്ക് ചില തകരാറുകൾ അനുഭവപ്പെട്ടേക്കാം. പിന്തുണയ്ക്കുവേണ്ടി ബന്ധപ്പെടാൻ മടിക്കേണ്ട." + select_file: അപ്‌ലോഡ് ചെയ്യാൻ ഒരു സ്‌പ്രെഡ്‌ഷീറ്റ് തിരഞ്ഞെടുക്കുക + spreadsheet: സ്പ്രെഡ്ഷീറ്റ് + choose_import_type: ഇറക്കുമതി തരം തിരഞ്ഞെടുക്കുക + import_into: ഇറക്കുമതി തരം + product_list: ഉൽപ്പന്ന ലിസ്റ്റ് + inventories: ചരക്കുകൾ + import: ഇറക്കുമതി ചെയ്യുക + upload: അപ്‌ലോഡ് ചെയ്യുക + csv_templates: സി.എസ്.വി ടെംപ്ലേറ്റുകൾ + product_list_template: ഉൽപ്പന്ന ലിസ്റ്റ് ടെംപ്ലേറ്റ് ഡൗൺലോഡ് ചെയ്യുക + inventory_template: ചരക്കുപട്ടിക ടെംപ്ലേറ്റ് ഡൗൺലോഡ് ചെയ്യുക + category_values: ലഭ്യമായ വിഭാഗ മൂല്യങ്ങൾ + product_categories: ഉൽപ്പന്ന വിഭാഗങ്ങൾ + tax_categories: നികുതി വിഭാഗങ്ങൾ + shipping_categories: ഷിപ്പിംഗ് വിഭാഗങ്ങൾ + dfc_import_form: + enterprise: "എന്റർപ്രൈസ്" + import: "ഇറക്കുമതി ചെയ്യുക" + import: + review: അവലോകനം + import: ഇറക്കുമതി ചെയ്യുക + save: രക്ഷിക്കും + results: ഫലങ്ങൾ + save_imported: ഇറക്കുമതി ചെയ്ത ഉൽപ്പന്നങ്ങൾ സംരക്ഷിക്കുക + no_valid_entries: സാധുവായ രേഖപ്പെടുത്തലുകളൊന്നും കണ്ടെത്തിയില്ല + none_to_save: സേവ് ചെയ്യാൻ കഴിയുന്ന രേഖപ്പെടുത്തലുകളൊന്നുമില്ല + some_invalid_entries: ഇറക്കുമതി ചെയ്ത ഫയലിൽ അസാധുവായ രേഖപ്പെടുത്തലുകൾ അടങ്ങിയിരിക്കുന്നു + fix_before_import: ഈ തകരാറുകൾ പരിഹരിച്ച് ഫയൽ വീണ്ടും ഇറക്കുമതി ചെയ്യാൻ ശ്രമിക്കുക + save_valid?: സാധുവായ രേഖപ്പെടുത്തലുകൾ ഇപ്പോൾ സേവ് ചെയ്യുകയും മറ്റുള്ളവ ഉപേക്ഷിക്കുകയും ചെയ്യണോ? + no_errors: തകരാറുകളൊന്നും കണ്ടെത്തിയില്ല! + save_all_imported?: ഇറക്കുമതി ചെയ്ത എല്ലാ ഉൽപ്പന്നങ്ങളും സേവ് ചെയ്യണോ? + options_and_defaults: ഇമ്പോർട്ട് ഓപ്‌ഷനുകളും ഡിഫോൾട്ടുകളും + no_permission: ഈ എന്റർപ്രൈസ് മാനേജ് ചെയ്യാൻ നിങ്ങൾക്ക് അനുമതിയില്ല + not_found: എന്റർപ്രൈസ് ഡാറ്റാബേസിൽ കണ്ടെത്താൻ കഴിഞ്ഞില്ല + no_name: പേരില്ല + blank_enterprise: ചില ഉൽപ്പന്നങ്ങൾക്ക് ഒരു എന്റർപ്രൈസ് വ്യതിരിക്തത ഇല്ല + reset_absent?: ഇല്ലാത്ത ഉൽപ്പന്നങ്ങൾ പുനഃസജ്ജമാക്കുക + reset_absent_tip: ഫയലിൽ ഇല്ലാത്ത എല്ലാ ഉൽപ്പന്നങ്ങൾക്കും സ്റ്റോക്ക് പൂജ്യമായി സജ്ജമാക്കുക + overwrite_all: എല്ലാം തിരുത്തിയെഴുതുക + overwrite_empty: ശൂന്യമാണെങ്കിൽ തിരുത്തിയെഴുതുക + default_stock: സ്റ്റോക്ക് ലെവൽ സജ്ജമാക്കുക + default_tax_cat: നികുതി വിഭാഗം സജ്ജമാക്കുക + default_shipping_cat: ഷിപ്പിംഗ് വിഭാഗം സജ്ജമാക്കുക + default_available_date: ലഭ്യമായ തീയതി സജ്ജമാക്കുക + validation_overview: ഇറക്കുമതി നിർണ്ണയ അവലോകനം + entries_found: ഇറക്കുമതി ചെയ്ത ഫയലിൽ രേഖപ്പെടുത്തലുകൾ കണ്ടെത്തി + entries_with_errors: ഇനങ്ങളിൽ തകരാറുകൾ ഉണ്ട് അതിനാൽ ഇറക്കുമതി ചെയ്യില്ല + products_to_create: ഉൽപ്പന്നങ്ങൾ സൃഷ്ടിക്കും + products_to_update: ഉൽപ്പന്നങ്ങൾ അപ്ഡേറ്റ് ചെയ്യും + inventory_to_create: ചരക്കുപ്പട്ടിക ഇനങ്ങൾ സൃഷ്ടിക്കും + inventory_to_update: ചരക്കുപ്പട്ടിക ഇനങ്ങൾ അപ്ഡേറ്റ് ചെയ്യും + products_to_reset: നിലവിലുള്ള ഉൽപ്പന്നങ്ങളുടെ സ്റ്റോക്ക് പൂജ്യത്തിലേക്ക് പുനഃസജ്ജമാക്കും + inventory_to_reset: നിലവിലുള്ള ചരക്കുപ്പട്ടിക ഇനങ്ങളുടെ സ്റ്റോക്ക് പൂജ്യത്തിലേക്ക് പുനഃസജ്ജമാക്കും + line: രേഖ + item_line: ഇനം രേഖ + import_review: + not_updatable_tip: "നിലവിലുള്ള ഉൽപ്പന്നങ്ങൾക്കായി ബൾക്ക് ഇമ്പോർട്ട് വഴി ഇനിപ്പറയുന്ന ഫീൽഡുകൾ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല:" + fields_ignored: ഇറക്കുമതി ചെയ്ത ഉൽപ്പന്നങ്ങൾ സേവ് ചെയ്യപ്പെടുമ്പോൾ ഈ ഫീൽഡുകൾ അവഗണിക്കപ്പെടും. + entries_table: + not_updatable: നിലവിലുള്ള ഉൽപ്പന്നങ്ങളുടെ ബൾക്ക് ഇറക്കുമതി വഴി ഈ ഫീൽഡ് അപ്‌ഡേറ്റ് ചെയ്യാനാകില്ല + save_results: + final_results: അന്തിമ ഫലങ്ങൾ ഇറക്കുമതി ചെയ്യുക + products_created: ഉൽപ്പന്നങ്ങൾ സൃഷ്ടിച്ചു + products_updated: ഉൽപ്പന്നങ്ങൾ അപ്ഡേറ്റ് ചെയ്തു + inventory_created: ചരക്കുപ്പട്ടിക ഇനങ്ങൾ സൃഷ്ടിച്ചു + inventory_updated: ചരക്കുപ്പട്ടിക ഇനങ്ങൾ അപ്ഡേറ്റ് ചെയ്തു + products_reset: ഉൽപ്പന്നങ്ങളുടെ സ്റ്റോക്ക് ലെവൽ പൂജ്യത്തിലേക്ക് റീസെറ്റ് ചെയ്തു + inventory_reset: ചരക്കുപ്പട്ടിക ഇനങ്ങളുടെ സ്റ്റോക്ക് ലെവൽ പൂജ്യത്തിലേക്ക് റീസെറ്റ് ചെയ്തു + all_saved: "എല്ലാ ഇനങ്ങളും വിജയകരമായി സേവ് ചെയ്തു" + some_saved: "ഇനങ്ങൾ വിജയകരമായി സേവ് ചെയ്തു" + save_errors: തകരാറുകൾ സേവ് ചെയ്യുക + import_again: മറ്റൊരു ഫയൽ അപ്‌ലോഡ് ചെയ്യുക + view_products: ഉൽപ്പന്നങ്ങളുടെ പേജിലേക്ക് പോകുക + view_inventory: ചരക്കുപ്പട്ടിക പേജിലേക്ക് പോകുക + product_headings: + distributor: വിതരണക്കാരൻ + producer: പ്രൊഡ്യൂസർ + sku: എസ്.കെ.യു + name: പേര് + display_name: പ്രദർശന നാമം + category: വിഭാഗം + description: വിവരണം + units: യൂണിറ്റുകൾ + unit_type: യൂണിറ്റ് തരം + variant_unit_name: വേരിയന്റ് യൂണിറ്റിന്റെ പേര് + price: വില + on_hand: കയ്യിൽ + on_demand: ആവശ്യപ്പെടുന്നതനുസരിച്ച് + shipping_category: ഷിപ്പിംഗ് വിഭാഗം + tax_category: നികുതി വിഭാഗം + variant_overrides: + loading_flash: + loading_inventory: ചരക്കുപ്പട്ടിക ലോഡുചെയ്യുന്നു + index: + title: ഇൻവെന്ററി + description: നിങ്ങളുടെ സംരംഭങ്ങൾക്കായുള്ള ചരക്കുപ്പട്ടികകൾ നിയന്ത്രിക്കാൻ ഈ പേജ് ഉപയോഗിക്കുക. ഇവിടെ സജ്ജീകരിച്ചിരിക്കുന്ന ഉൽപ്പന്ന വിശദാംശങ്ങൾ 'ഉൽപ്പന്നങ്ങൾ' പേജിൽ സജ്ജീകരിച്ചിരിക്കുന്നവയെ അസാധുവാക്കും + enable_reset?: സ്റ്റോക്ക് റീസെറ്റ് പ്രവർത്തനക്ഷമമാക്കണോ? + default_stock: "സ്ഥിരസ്ഥിതി സ്റ്റോക്ക്" + inherit?: അനന്തരാവകാശമോ? + add: ചേർക്കുക + hide: മറയ്ക്കുക + import_date: ഇറക്കുമതി ചെയ്തത് + select_a_shop: ഒരു ഷോപ്പ് തിരഞ്ഞെടുക്കുക + review_now: ഇപ്പോൾ അവലോകനം ചെയ്യുക + new_products_alert_message: നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് ചേർക്കാൻ %{new_product_count} പുതിയ ഉൽപ്പന്നങ്ങൾ ലഭ്യമാണ്. + currently_empty: നിങ്ങളുടെ ചരക്കുപ്പട്ടിക നിലവിൽ ശൂന്യമാണ് + no_matching_products: നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിൽ പൊരുത്തപ്പെടുന്ന ഉൽപ്പന്നങ്ങളൊന്നും കണ്ടെത്തിയില്ല + no_hidden_products: ഈ ചരക്കുപ്പട്ടികയിൽ നിന്ന് ഉൽപ്പന്നങ്ങളൊന്നും മറച്ചിട്ടില്ല + no_matching_hidden_products: നിങ്ങളുടെ തിരയൽ മാനദണ്ഡങ്ങളുമായി പൊരുത്തപ്പെടുന്ന മറച്ചുവച്ചിരിക്കുന്ന ഉൽപ്പന്നങ്ങളൊന്നുമില്ല + no_new_products: ഈ ചരക്കുപ്പട്ടികയിലേക്ക് ചേർക്കാൻ പുതിയ ഉൽപ്പന്നങ്ങളൊന്നും ലഭ്യമല്ല + no_matching_new_products: നിങ്ങളുടെ തിരയൽ മാനദണ്ഡങ്ങളുമായി പൊരുത്തപ്പെടുന്ന പുതിയ ഉൽപ്പന്നങ്ങളൊന്നുമില്ല + inventory_powertip: ഇത് നിങ്ങളുടെ ഉൽപ്പന്നങ്ങളുടെ ചരക്കുപ്പട്ടികയാണ്. നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് ഉൽപ്പന്നങ്ങൾ ചേർക്കുന്നതിന്, ഡ്രോപ്പ്ഡൗണിൽ നിന്ന് 'പുതിയ ഉൽപ്പന്നങ്ങൾ' തിരഞ്ഞെടുക്കുക. + hidden_powertip: ഈ ഉൽപ്പന്നങ്ങൾ നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിൽ നിന്ന് മറച്ചിരിക്കുന്നു, നിങ്ങളുടെ ഷോപ്പിലേക്ക് ചേർക്കാൻ ലഭ്യമല്ല. നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് ഒരു ഉൽപ്പന്നം ചേർക്കാൻ 'ചേർക്കുക' ക്ലിക്ക് ചെയ്യാം. + new_powertip: ഈ ഉൽപ്പന്നങ്ങൾ നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് ചേർക്കാൻ ലഭ്യമാണ്. നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് ഒരു ഉൽപ്പന്നം ചേർക്കുന്നതിന് 'ചേർക്കുക' അല്ലെങ്കിൽ കാഴ്ചയിൽ നിന്ന് മറയ്ക്കാൻ 'മറയ്ക്കുക' ക്ലിക്കുചെയ്യുക. നിങ്ങൾക്ക് പിന്നീട് ഇത് മാറ്റാൻ സാധിക്കും! + controls: + back_to_my_inventory: എന്റെ ചരക്കുപ്പട്ടികയിലേക്ക് മടങ്ങുക + orders: + edit: + order_sure_want_to: ഈ ഓർഡർ %{event} ചെയ്യണമെന്ന് തീർച്ചയാണോ? + voucher_tax_included_in_price: "%{label} (വൗച്ചറിൽ നികുതി ഉൾപ്പെടുത്തിയിട്ടുണ്ട്)" + invoice_email_sent: 'ഇൻവോയ്സ് ഇമെയിൽ അയച്ചു' + order_email_resent: 'ഓർഡർ ഇമെയിൽ വീണ്ടും അയച്ചു' + bulk_management: + tip: "ഒന്നിലധികം ഓർഡറുകളിലുടനീളം ഉൽപ്പന്നത്തിന്റെ അളവ് മാറ്റാൻ ഈ പേജ് ഉപയോഗിക്കുക. ആവശ്യമെങ്കിൽ, ഓർഡറുകളിൽ നിന്ന് ഉൽപ്പന്നങ്ങൾ പൂർണ്ണമായും നീക്കം ചെയ്യാം." + shared: "പങ്കിട്ട വിഭവം?" + order_no: "ഓർഡർ നമ്പർ." + order_date: "പൂർത്തിയാക്കിയത്" + max: "പരമാവധി" + product_unit: "ഉൽപ്പന്നം: യൂണിറ്റ്" + weight_volume: "ഭാരം/വ്യാപ്തം (ഗ്രാം)" + ask: "ചോദിക്കണോ?" + page_title: "ബൾക്ക് ഓർഡർ മാനേജ്മെന്റ്" + actions_delete: "തിരഞ്ഞെടുത്തത് ഡിലീറ്റ് ചെയ്യുക" + loading: "ഓർഡറുകൾ ലോഡുചെയ്യുന്നു" + no_results: "ഓർഡറുകളൊന്നും കണ്ടെത്തിയില്ല." + group_buy_unit_size: "ഗ്രൂപ്പ് വാങ്ങൽ - യൂണിറ്റ് വലുപ്പം" + total_qtt_ordered: "ഓർഡർ ചെയ്ത ആകെ അളവ്" + max_qtt_ordered: "ഓർഡർ ചെയ്ത പരമാവധി അളവ് " + current_fulfilled_units: "നിലവിലുള്ള പൂർത്തിയാക്കപ്പെട്ട യൂണിറ്റുകൾ" + max_fulfilled_units: "പരമാവധി പൂർത്തിയാക്കപ്പെട്ട യൂണിറ്റുകൾ" + order_error: "നിങ്ങൾക്ക് ഓർഡറുകൾ അപ്‌ഡേറ്റ് ചെയ്യുന്നതിന് മുമ്പ് ചില തകരാറുകൾ പരിഹരിക്കേണ്ടതുണ്ട്.\n ചുവന്ന ബോർഡറുകളുള്ള എല്ലാ ഫീൽഡുകളിലും തകരാറുകൾ ഉണ്ട്." + variants_without_unit_value: "മുന്നറിയിപ്പ്: ചില വകഭേദങ്ങൾക്ക് യൂണിറ്റ് മൂല്യമില്ല" + all: "എല്ലാം" + select_variant: "ഒരു വകഭേദം തിരഞ്ഞെടുക്കുക" + note: + note_label: "കുറിപ്പ്:" + no_note_present: "കുറിപ്പൊന്നും നൽകിയിട്ടില്ല." + enterprise: + select_outgoing_oc_products_from: ഇതിൽ നിന്ന് പുറത്തേക്കുപോകുന്ന ഒസി ഉൽപ്പന്നങ്ങൾ തിരഞ്ഞെടുക്കുക + enterprises: + index: + title: എന്റർപ്രൈസസ് + new_enterprise: പുതിയ എന്റർപ്രൈസ് + producer?: "പ്രൊഡ്യൂസർ?" + package: പാക്കേജ് + status: സ്ഥിതി + manage: കൈകാര്യം ചെയ്യുക + form: + about_us: + legend: "കുറിച്ച്" + desc_short: ഹൃസ്വ വിവരണം + desc_short_placeholder: ഒന്നോ രണ്ടോ വാക്യങ്ങളിൽ നിങ്ങളുടെ സംരംഭത്തെക്കുറിച്ച് ഞങ്ങളോട് പറയുക + desc_long: ഞങ്ങളേക്കുറിച്ച് + desc_long_placeholder: നിങ്ങളെക്കുറിച്ച് ഉപഭോക്താക്കളോട് പറയുക. ഈ വിവരം നിങ്ങളുടെ പൊതു പ്രൊഫൈലിൽ ദൃശ്യമാകും. + address: + legend: "വിലാസം" + business_details: + legend: "ബിസിനസ്സ് വിശദാംശങ്ങൾ" + upload: 'അപ്‌ലോഡ് ചെയ്യുക ' + abn: എബിഎൻ + abn_placeholder: ഉദാ. 99 123 456 789 + acn: എ.സി.എൻ + acn_placeholder: ഉദാ. 123 456 789 + display_invoice_logo: ഇൻവോയ്സുകളിൽ ലോഗോ പ്രദർശിപ്പിക്കുക + invoice_text: ഇൻവോയ്‌സുകളുടെ അവസാനം ഇഷ്‌ടാനുസൃതമാക്കിയ വാചകം ചേർക്കുക + terms_and_conditions: "ഉപാധികളും നിബന്ധനകളും" + remove_terms_and_conditions: "ഫയൽ നീക്കം ചെയ്യുക" + uploaded_on: "അപ്‌ലോഡ് ചെയ്തത്" + reset_form: "ഫോം പുനഃസജ്ജമാക്കുക" + business_address_legend: "വ്യാപാര മേൽവിലാസം" + invoice_item_sorting_legend: "ഇൻവോയ്സ് ഇനം തരം തിരിക്കുന്നു" + sort_items_by_supplier?: വിതരണക്കാർ പ്രകാരം ഇനങ്ങൾ തരം തിരിക്കണോ? + sort_items_by_supplier_tip: "പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, വിതരണക്കാരുടെ പേര് അനുസരിച്ച് ഇനങ്ങൾ തരം തിരിക്കും." + enabled: പ്രവർത്തനക്ഷമമാക്കുക + disabled: പ്രവർത്തനരഹിതമാക്കുക + business_address: + company_legal_name: കമ്പനിയുടെ നിയമപരമായ പേര് + company_placeholder: ഉദാഹരണം Inc. + address1: നിയമപരമായ വിലാസം + address1_placeholder: 123 ഹൈ സ്ട്രീറ്റ്. + address2: വിലാസം (തുടർച്ച) + legal_phone_number: നിയമപരമായ ഫോൺ നമ്പർ + phone_placeholder: "98 123 4565" + select_country: "രാജ്യം തിരഞ്ഞെടുക്കുക" + select_state: "സംസ്ഥാനം തിരഞ്ഞെടുക്കുക" + contact: + legend: "ബന്ധപ്പെടുക" + name: പേര് + name_placeholder: ഉദാ. ഗുസ്താവ് പ്ലം + email_address: പൊതു ഇമെയിൽ വിലാസം + email_address_placeholder: ഉദാ. enquiries@fresh-food.com + email_address_tip: "ഈ ഇമെയിൽ വിലാസം നിങ്ങളുടെ പൊതു പ്രൊഫൈലിൽ പ്രദർശിപ്പിക്കും" + phone: ഫോൺ + phone_placeholder: ഉദാ. 98 7654 3210 + whatsapp_phone: വാട്ട്സ്ആപ്പ് ഫോൺ നമ്പർ + whatsapp_phone_placeholder: ഉദാ. +61 4 9876 5432 + whatsapp_phone_tip: "വാട്ട്‌സ്ആപ്പ് ലിങ്കായി തുറക്കുന്നതിന്, ഈ നമ്പർ നിങ്ങളുടെ പൊതു പ്രൊഫൈലിൽ പ്രദർശിപ്പിക്കും." + website: വെബ്സൈറ്റ് + website_placeholder: ഉദാ. www.truffles.com + enterprise_fees: + legend: "എന്റർപ്രൈസ് ഫീസ്" + name: പേര് + fee_type: ഫീസ് തരം + manage_fees: എന്റർപ്രൈസ് ഫീസ് കൈകാര്യം ചെയ്യുക + no_fees_yet: നിങ്ങൾക്ക് ഇതുവരെ എന്റർപ്രൈസ് ഫീസുകളൊന്നുമില്ല. + create_button: ഇപ്പോൾത്തന്നെ ഒന്ന് ഉണ്ടാക്കുക + enterprise_permissions: + legend: "എന്റർപ്രൈസ് അനുമതികൾ" + enterprise_relationships: എന്റർപ്രൈസ് ബന്ധങ്ങൾ + images: + legend: "ചിത്രങ്ങൾ" + logo: ലോഗോ + promo_image_placeholder: '"ഞങ്ങളെക്കുറിച്ച്" എന്നതിൽ ഈ ചിത്രം പ്രദർശിപ്പിച്ചിരിക്കുന്നു' + promo_image_note1: 'ദയവായി ശ്രദ്ധിക്കുക:' + promo_image_note2: ഇവിടെ അപ്‌ലോഡ് ചെയ്യുന്ന ഏത് പ്രൊമോ ചിത്രത്തിന്റെയും വലിപ്പം 1200 x 260 ആക്കി മാറ്റും. + promo_image_note3: ഒരു എന്റർപ്രൈസിന്റെ പ്രൊഫൈൽ പേജിന്റെയും പോപ്പ്-അപ്പുകളുടെയും മുകളിൽ പ്രൊമോ ചിത്രം പ്രദർശിപ്പിക്കും. + remove_logo: "ചിത്രം നീക്കം ചെയ്യുക" + remove_promo_image: "ചിത്രം നീക്കം ചെയ്യുക" + inventory_settings: + legend: "ചരക്കുപ്പട്ടിക ക്രമീകരണങ്ങൾ" + text1: സ്റ്റോക്ക് ലെവലുകളും വിലകളും കൈകാര്യം ചെയ്യാൻ നിങ്ങൾക്ക് ഇത് തിരഞ്ഞെടുക്കാം - + inventory: ചരക്കുപ്പട്ടിക + text2: > + നിങ്ങൾ ഇൻവെന്ററി ടൂൾ ഉപയോഗിക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ വിതരണക്കാർ ചേർത്ത + പുതിയ ഉൽപ്പന്നങ്ങൾ സ്റ്റോക്ക് ചെയ്യുന്നതിന് മുമ്പ് നിങ്ങളുടെ ചരക്കുപ്പട്ടികയിലേക്ക് + ചേർക്കേണ്ടതുണ്ടോ എന്ന് നിങ്ങൾക്ക് തീരുമാനിക്കാം. നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ + നിയന്ത്രിക്കാൻ നിങ്ങളുടെ ചരക്കുപ്പട്ടിക ഉപയോഗിക്കുന്നില്ലെങ്കിൽ, ചുവടെയുള്ള + 'ശുപാർശ ചെയ്‌തത്' എന്ന ഓപ്ഷൻ നിങ്ങൾ തിരഞ്ഞെടുക്കണം: + preferred_product_selection_from_inventory_only_yes: പുതിയ ഉൽപ്പന്നങ്ങൾ എന്റെ ഷോപ്പ് ഫ്രണ്ടിൽ ഇടാം (ശുപാർശ ചെയ്‌തത്) + preferred_product_selection_from_inventory_only_no: പുതിയ ഉൽപ്പന്നങ്ങൾ എന്റെ ഷോപ്പ് ഫ്രണ്ടിൽ ഇടുന്നതിന് മുമ്പ് എന്റെ ചരക്കുപ്പട്ടികയിലേക്ക് ചേർക്കണം + payment_methods: + legend: "പേയ്മെന്റ് രീതികൾ" + name: പേര് + applies: ബാധകമാണോ? + manage: പേയ്‌മെന്റ് രീതികൾ കൈകാര്യം ചെയ്യുക + no_method_yet: നിങ്ങൾക്ക് ഇതുവരെ പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല. + create_button: പുതിയ പേയ്‌മെന്റ് രീതി ഉണ്ടാക്കുക + create_one_button: ഇപ്പോൾത്തന്നെ ഒന്ന് ഉണ്ടാക്കുക + primary_details: + legend: "പ്രാഥമിക വിശദാംശങ്ങൾ" + name: പേര് + name_placeholder: ഉദാ. പ്രൊഫസർ പ്ലമ്മിന്റെ ബയോഡൈനാമിക് ട്രഫിൾസ് + groups: ഗ്രൂപ്പുകൾ + groups_tip: നിങ്ങൾ അംഗമായ ഏതെങ്കിലും ഗ്രൂപ്പുകളോ പ്രദേശങ്ങളോ തിരഞ്ഞെടുക്കുക. ഇത് നിങ്ങളുടെ എന്റർപ്രൈസ് കണ്ടെത്താൻ ഉപഭോക്താക്കളെ സഹായിക്കും. + groups_placeholder: ലഭ്യമായ ഗ്രൂപ്പുകൾ തിരയാൻ ടൈപ്പ് ചെയ്യാൻ തുടങ്ങൂ... + primary_producer: പ്രാഥമിക പ്രൊഡ്യൂസർ? + primary_producer_tip: നിങ്ങൾ ആഹാരത്തിന്റെ പ്രാഥമിക പ്രൊഡ്യൂസർ ആണെങ്കിൽ 'പ്രൊഡ്യൂസർ' തിരഞ്ഞെടുക്കുക. + producer: പ്രൊഡ്യൂസർ + any: ഏതെങ്കിലും + none: ഒന്നുമില്ല + own: സ്വന്തം + sells: വിൽക്കുന്നു + sells_tip: "ഒന്നുമില്ല - എന്റർപ്രൈസ് ഉപഭോക്താക്കൾക്ക് നേരിട്ട് വിൽക്കില്ല.
സ്വന്തം - എന്റർപ്രൈസ് ഉപഭോക്താക്കൾക്ക് സ്വന്തം ഉൽപ്പന്നങ്ങൾ വിൽക്കുന്നു.
ഏതെങ്കിലും - എന്റർപ്രൈസസിന് സ്വന്തം അല്ലെങ്കിൽ മറ്റ് സംരംഭങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ വിൽക്കാൻ കഴിയും.
" + visible_in_search: തിരയലിൽ ദൃശ്യമാണോ? + visible_in_search_tip: "കടകൾ ഇങ്ങനെ ആയിരിക്കും
1. പൊതുവായി ദൃശ്യമായത്, ഒഎഫ്എൻ മാപ്പിലും ലിസ്റ്റിംഗുകളിലും ദൃശ്യമാകുന്നു.
2. മാപ്പുകളിലും ലിസ്റ്റിംഗുകളിലും മറച്ചിരിക്കുന്നു, എന്നാൽ മറ്റ് ഷോപ്പുകൾ പരാമർശിക്കുകയും അവരുടെ പ്രൊഫൈലിൽ ലിങ്ക് ചെയ്യുകയും ചെയ്യുന്നു.
3. പൂർണ്ണമായും മറച്ചിരിക്കുന്നു." + visible: പൊതു + not_visible: മറച്ചിരിക്കുന്നു + hidden: എല്ലാ റഫറൻസുകളും മറയ്ക്കുക + properties: + legend: "പ്രോപ്പർട്ടികൾ" + permalink: + permalink: പെർമലിങ്ക് (സ്‌പെയ്‌സുകളില്ല) + permalink_tip: "നിങ്ങളുടെ ഷോപ്പിലേക്ക് യുആർഎൽ സൃഷ്‌ടിക്കാൻ ഈ പെർമലിങ്ക് ഉപയോഗിക്കുന്നു: %{link} your-shop-name/shop" + link_to_front: ഷോപ്പ് ഫ്രണ്ടിലേക്കുള്ള ലിങ്ക് + link_to_front_tip: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ നിങ്ങളുടെ ഷോപ്പ് ഫ്രണ്ടിലേക്കുള്ള നേരിട്ടുള്ള ലിങ്ക്. + ofn_uid: ഒഎഫ്എൻ യുഐഡി + ofn_uid_tip: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ എന്റർപ്രൈസ് തിരിച്ചറിയാൻ ഉപയോഗിക്കുന്ന തനത് ഐഡി. + shipping_methods: + legend: "ഷിപ്പിംഗ് രീതികൾ" + name: "പേര്" + applies: "സജീവമാണോ?" + manage: "ഷിപ്പിംഗ് രീതികൾ നിയന്ത്രിക്കുക" + create_button: "പുതിയ ഷിപ്പിംഗ് രീതി ഉണ്ടാക്കുക" + create_one_button: "ഇപ്പോൾത്തന്നെ ഒന്ന് ഉണ്ടാക്കുക" + no_method_yet: "നിങ്ങൾക്ക് ഇതുവരെ ഷിപ്പിംഗ് രീതികളൊന്നുമില്ല." + shop_preferences: + legend: "ഷോപ്പ് മുൻഗണനകൾ" + shopfront_requires_login: "എല്ലാവർക്കും കാണാവുന്ന ഷോപ്പ്ഫ്രണ്ട്?" + shopfront_requires_login_tip: "ഷോപ്പ്ഫ്രണ്ട് കാണാൻ ഉപഭോക്താക്കൾ ലോഗിൻ ചെയ്യണമോ അതോ എല്ലാവർക്കും ദൃശ്യമാക്കണോ എന്ന് തിരഞ്ഞെടുക്കുക." + shopfront_requires_login_false: "പൊതു" + shopfront_requires_login_true: "രജിസ്റ്റർ ചെയ്ത ഉപഭോക്താക്കൾക്ക് മാത്രം ദൃശ്യമാണ്" + recommend_require_login: "ഓർഡറുകൾ മാറ്റാൻ കഴിയുമ്പോൾ ലോഗിൻ ചെയ്യാൻ ഉപയോക്താക്കളോട് ആവശ്യപ്പെടാൻ ഞങ്ങൾ ശുപാർശ ചെയ്യുന്നു." + allow_guest_orders: "ഗസ്റ്റ് ഓർഡറുകൾ" + allow_guest_orders_tip: "ഗസ്റ്റ് ആയി ചെക്ക്ഔട്ട് അനുവദിക്കുക അല്ലെങ്കിൽ ഉപയോക്താവിനോട് രജിസ്റ്റർ ചെയ്യാൻ ആവശ്യപ്പെടുക." + allow_guest_orders_false: "ഓർഡർ ചെയ്യാൻ ലോഗിൻ ചെയ്യേണ്ടതുണ്ട്" + allow_guest_orders_true: "ഗസ്റ്റ് ചെക്ക്ഔട്ട് അനുവദിക്കുക" + allow_order_changes: "ഓർഡറുകൾ മാറ്റുക" + allow_order_changes_tip: "ഓർഡർ സൈക്കിൾ തുറന്നിരിക്കുന്നിടത്തോളം കാലം അവരുടെ ഓർഡർ മാറ്റാൻ ഉപഭോക്താക്കളെ അനുവദിക്കുക." + allow_order_changes_false: "നൽകിയ ഓർഡറുകൾ മാറ്റാനോ റദ്ദാക്കാനോ കഴിയില്ല" + allow_order_changes_true: "ഓർഡർ സൈക്കിൾ തുറന്നിരിക്കുന്ന സമയത്ത് ഉപഭോക്താക്കൾക്ക് ഓർഡറുകൾ മാറ്റാനും റദ്ദാക്കാനും കഴിയും" + enable_subscriptions: "സബ്സ്ക്രിപ്ഷനുകൾ" + enable_subscriptions_tip: "സബ്‌സ്‌ക്രിപ്‌ഷൻ പ്രവർത്തനം പ്രവർത്തനക്ഷമമാക്കണോ?" + enable_subscriptions_false: "പ്രവർത്തനരഹിതമാക്കി" + enable_subscriptions_true: "പ്രവർത്തനക്ഷമമാക്കി" + customer_names_in_reports: "റിപ്പോർട്ടുകളിലെ ഉപഭോക്തൃ പേരുകൾ" + customer_names_tip: "റിപ്പോർട്ടുകളിൽ നിങ്ങളുടെ ഉപഭോക്താക്കളുടെ പേരുകൾ കാണാൻ നിങ്ങളുടെ വിതരണക്കാരെ പ്രാപ്തമാക്കുക" + customer_names_false: "പ്രവർത്തനരഹിതമാക്കി" + customer_names_true: "പ്രവർത്തനക്ഷമമാക്കി" + shopfront_message: "ഷോപ്പ്ഫ്രണ്ട് സന്ദേശം" + shopfront_message_placeholder: > + ഉപഭോക്താക്കളെ സ്വാഗതം ചെയ്യുന്നതിനും നിങ്ങളുമായി എങ്ങനെ ഷോപ്പിംഗ് നടത്താമെന്ന് + വിശദീകരിക്കുന്നതിനുമുള്ള ഒരു ഓപ്‌ഷണൽ സന്ദേശം. ഇവിടെ മൂലവാക്യം നൽകിയാൽ, + ഉപഭോക്താക്കൾ ആദ്യം നിങ്ങളുടെ ഷോപ്പ് ഫ്രണ്ടിൽ എത്തുമ്പോൾ അത് ഹോം ടാബിൽ + പ്രദർശിപ്പിക്കും. + shopfront_message_link_tooltip: "ലിങ്ക് ചേർക്കുക / എഡിറ്റ് ചെയ്യുക" + shopfront_message_link_prompt: "ചേർക്കുന്നതിന് ദയവായി ഒരു യുആർഎൽ നൽകുക" + shopfront_closed_message: "ഷോപ്പ്ഫ്രണ്ട് അടച്ച സന്ദേശം" + shopfront_closed_message_placeholder: > + നിങ്ങളുടെ ഷോപ്പ് എന്തിനാണ് അടച്ചിരിക്കുന്നത് എന്നതിനെക്കുറിച്ചും/അല്ലെങ്കിൽ + ഉപഭോക്താക്കൾക്ക് അത് വീണ്ടും എന്ന് തുറക്കുമെന്ന് പ്രതീക്ഷിക്കാമെന്നതിനെക്കുറിച്ചും + കൂടുതൽ വിശദീകരണം നൽകുന്ന ഒരു സന്ദേശം. നിങ്ങൾക്ക് സജീവമായ ഓർഡർ സൈക്കിളുകൾ + ഇല്ലാത്തപ്പോൾ (അതായത്, ഷോപ്പ് അടച്ചിരിക്കുന്നു) മാത്രമേ ഇത് നിങ്ങളുടെ + ഷോപ്പിൽ പ്രദർശിപ്പിക്കുകയുള്ളൂ. + shopfront_category_ordering: "ഷോപ്പ്ഫ്രണ്ട് വിഭാഗം ഓർഡറിംഗ്" + shopfront_category_ordering_note: "(മുകളിൽ നിന്ന് താഴേക്ക്)" + open_date: "തുറക്കുന്ന തീയതി" + close_date: "അവസാന തീയതി" + display_ordering_in_shopfront: "ഷോപ്പ്ഫ്രണ്ടിൽ ഓർഡർ ചെയ്യൽ പ്രദർശിപ്പിക്കുക:" + shopfront_sort_by_category: "വിഭാഗം പ്രകാരം" + shopfront_sort_by_producer: "പ്രൊഡ്യൂസർ വഴി" + shopfront_sort_by_category_placeholder: "വിഭാഗം" + shopfront_sort_by_producer_placeholder: "നിർമ്മാതാവ്" + display_remaining_stock: "സ്റ്റോക്ക് കുറവാണെങ്കിൽ ബാക്കിയുള്ള സ്റ്റോക്ക് ഷോപ്പ്ഫ്രണ്ടിൽ പ്രദർശിപ്പിക്കുക" + display_remaining_stock_tip: "മൂന്നോ അതിലധികമോ ഇനങ്ങൾ മാത്രം ശേഷിക്കുമ്പോൾ ഉപഭോക്താക്കളെ അറിയിക്കുക." + enabled: "പ്രവർത്തനക്ഷമമാക്കി" + disabled: "പ്രവർത്തനരഹിതമാക്കി" + social: + legend: "സാമൂഹികം" + twitter_placeholder: "ഉദാ. @the_prof" + instagram_placeholder: "ഉദാ. the_prof" + facebook_placeholder: "ഉദാ. www.facebook.com/PageNameHere" + linkedin_placeholder: "ഉദാ. www.linkedin.com/in/YourNameHere" + stripe_connect: + connect_with_stripe: "സ്ട്രൈപ്പുമായി ബന്ധിപ്പിക്കുക" + stripe_connect_intro: "ക്രെഡിറ്റ് കാർഡ് ഉപയോഗിച്ച് പേയ്‌മെന്റുകൾ സ്വീകരിക്കുന്നതിന്, നിങ്ങളുടെ സ്ട്രൈപ്പ് അക്കൗണ്ട് ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് കണക്‌റ്റ് ചെയ്യേണ്ടതുണ്ട്. ആരംഭിക്കുന്നതിന് വലതുവശത്തുള്ള ബട്ടൺ ഉപയോഗിക്കുക." + stripe_account_connected: "സ്ട്രൈപ്പ് അക്കൗണ്ട് കണക്‌റ്റ് ചെയ്‌തു." + disconnect: "അക്കൗണ്ട് വിച്ഛേദിക്കുക" + confirm_modal: + title: സ്ട്രൈപ്പുമായി ബന്ധിപ്പിക്കുക + part1: ഉപഭോക്താക്കളിൽ നിന്ന് ക്രെഡിറ്റ് കാർഡ് പേയ്‌മെന്റുകൾ സ്വീകരിക്കാൻ ഒഎഫ്എൻ-ലെ ഷോപ്പുകളെ അനുവദിക്കുന്ന ഒരു പേയ്‌മെന്റ് പ്രോസസ്സിംഗ് സേവനമാണ് സ്ട്രൈപ്പ്. + part2: ഈ ഫീച്ചർ ഉപയോഗിക്കുന്നതിന്, നിങ്ങളുടെ സ്ട്രൈപ്പ് അക്കൗണ്ട് ഒഎഫ്എൻ-ലേക്ക് കണക്‌റ്റ് ചെയ്യണം. ചുവടെയുള്ള 'ഞാൻ അംഗീകരിക്കുന്നു' എന്ന ബട്ടൺ ക്ലിക്ക് ചെയ്യുന്നത് നിങ്ങൾക്ക് നിലവിലുള്ള ഒരു സ്ട്രൈപ്പ് അക്കൗണ്ട് കണക്റ്റുചെയ്യാൻ കഴിയുന്ന സ്ട്രൈപ്പ് വെബ്‌സൈറ്റിലേക്ക് റീഡയറക്‌ട് ചെയ്യും, അതല്ലെങ്കിൽ നിങ്ങൾക്ക് അക്കൗണ്ട് ഇല്ലെങ്കിൽ പുതിയൊരെണ്ണം ഉണ്ടാക്കാനും കഴിയും. + part3: ഇത് നിങ്ങളുടെ പേരിൽ ഉപഭോക്താക്കളിൽ നിന്ന് ക്രെഡിറ്റ് കാർഡ് പേയ്‌മെന്റുകൾ സ്വീകരിക്കാൻ ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിനെ അനുവദിക്കും. നിങ്ങളുടെ സ്വന്തം സ്ട്രൈപ്പ് അക്കൗണ്ട് പരിപാലിക്കേണ്ടതും സ്ട്രൈപ്പ് ചാർജുകൾ അടയ്ക്കേണ്ടതും ഏതെങ്കിലും ചാർജ്ബാക്കുകളും ഉപഭോക്തൃ സേവനവും നിങ്ങൾ തന്നെ കൈകാര്യം ചെയ്യേണ്ടതാണെന്ന കാര്യം ശ്രദ്ധിക്കുക. + i_agree: ഞാൻ അംഗീകരിക്കുന്നു + cancel: റദ്ദാക്കുക + tag_rules: + legend: "ടാഗ് നിയമങ്ങൾ" + default_rules: + by_default: സ്ഥിരസ്ഥിതിയായി + no_rules_yet: സ്ഥിരസ്ഥിതി നിയമങ്ങളൊന്നും ഇതുവരെ ബാധകമല്ല + add_new_button: '+ ഒരു പുതിയ സ്ഥിരസ്ഥിതി നിയമം ചേർക്കുക' + no_tags_yet: ഈ എന്റർപ്രൈസസിന് ഇതുവരെ ടാഗുകളൊന്നും ബാധകമല്ല + no_rules_yet: ഈ ടാഗിന് ഇതുവരെ നിയമങ്ങളൊന്നും ബാധകമല്ല + for_customers_tagged: 'ടാഗ് ചെയ്ത ഉപഭോക്താക്കൾക്കായി:' + add_new_rule: '+ ഒരു പുതിയ നിയമം ചേർക്കുക' + add_new_tag: '+ ഒരു പുതിയ ടാഗ് ചേർക്കുക' + users: + legend: "ഉപയോക്താക്കൾ" + email_confirmation_notice_html: "ഇമെയിൽ സ്ഥിരീകരണം തീർച്ചപ്പെടുത്തിയിട്ടില്ല. ഞങ്ങൾ ഒരു സ്ഥിരീകരണ ഇമെയിൽ %{email} ലേക്ക് അയച്ചു." + resend: വീണ്ടും അയയ്ക്കുക + owner: 'ഉടമ' + contact: "ബന്ധപ്പെടുക" + contact_tip: "ഓർഡറുകൾക്കും അറിയിപ്പുകൾക്കുമായി എന്റർപ്രൈസ് ഇമെയിലുകൾ സ്വീകരിക്കുന്ന മാനേജർ. സ്ഥിരീകരിച്ച ഇമെയിൽ വിലാസം ഉണ്ടായിരിക്കണം." + owner_tip: ഈ എന്റർപ്രൈസസിന്റെ ഉത്തരവാദിത്തമുള്ള പ്രാഥമിക ഉപയോക്താവ്. + notifications: അറിയിപ്പുകൾ + notifications_tip: ഓർഡറുകൾ സംബന്ധിച്ച അറിയിപ്പുകൾ ഈ ഇമെയിൽ വിലാസത്തിലേക്ക് അയയ്‌ക്കും. + notifications_placeholder: ഉദാ. gustav@truffles.com + notifications_note: 'ശ്രദ്ധിക്കുക: ഉപയോഗിക്കുന്നതിന് മുമ്പ് ഒരു പുതിയ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കേണ്ടതുണ്ട്' + managers: മാനേജർമാർ + managers_tip: ഈ എന്റർപ്രൈസ് മാനേജ് ചെയ്യാൻ അനുമതിയുള്ള മറ്റ് ഉപയോക്താക്കൾ. + invite_manager: "മാനേജരെ ക്ഷണിക്കുക" + invite_manager_tip: "രജിസ്റ്റർ ചെയ്യാത്ത ഒരു ഉപയോക്താവിനെ സൈൻ അപ്പ് ചെയ്യാനും ഈ എന്റർപ്രൈസിന്റെ മാനേജരാകാനും ക്ഷണിക്കുക." + add_unregistered_user: "രജിസ്റ്റർ ചെയ്യാത്ത ഒരു ഉപയോക്താവിനെ ചേർക്കുക" + email_confirmed: "ഇമെയിൽ സ്ഥിരീകരിച്ചു" + email_not_confirmed: "ഇമെയിൽ സ്ഥിരീകരിച്ചിട്ടില്ല" + vouchers: + legend: വൗച്ചറുകൾ + voucher_code: വൗച്ചർ കോഡ് + rate: നിരക്ക് + label: ലേബൽ + purpose: ഉദ്ദേശം + expiry: കാലാവധിയാകൽ + use_limit: ഉപയോഗിക്കുക/പരിമിതപ്പെടുത്തുക + customers: ഉപഭോക്താവ് + net_value: മൊത്തം തുക + active: സജീവമാണോ? + add_new: പുതിയത് ചേർക്കുക + no_voucher_yet: ഇതുവരെ വൗച്ചറുകളൊന്നുമില്ല + white_label: + legend: "വൈറ്റ് ലേബൽ" + hide_ofn_navigation: "ഒഎഫ്എൻ നാവിഗേഷൻ മറയ്ക്കുക" + upload_logo: "ഷോപ്പ്ഫ്രണ്ടിൽ ലോഗോ ഉപയോഗിക്കുന്നു" + remove_logo: "ലോഗോ നീക്കം ചെയ്യുക" + remove_logo_confirm: "ഈ ലോഗോ നീക്കം ചെയ്യണമെന്ന് തീർച്ചയാണോ?" + remove_logo_success: "ലോഗോ നീക്കം ചെയ്തു" + white_label_logo_link_label: "കടയുടെ മുൻവശത്ത് ഉപയോഗിക്കുന്ന ലോഗോയ്ക്കുള്ള ലിങ്ക്" + hide_groups_tab: "ഷോപ്പ്ഫ്രണ്ടിൽ ഗ്രൂപ്പുകളുടെ ടാബ് മറയ്ക്കുക" + create_custom_tab: "ഷോപ്പ്ഫ്രണ്ടിൽ ഇഷ്‌ടാനുസൃത ടാബ് സൃഷ്‌ടിക്കുക" + custom_tab_title: "ഇഷ്‌ടാനുസൃത ടാബിനുള്ള ശീർഷകം" + custom_tab_content: "ഇഷ്‌ടാനുസൃത ടാബിനുള്ള ഉള്ളടക്കം" + connected_apps: + legend: "ബന്ധിപ്പിച്ച ആപ്പുകൾ" + title: "റീജനറേറ്റീവ് കണ്ടെത്തുക" + loading: "ലോഡിംഗ്" + actions: + edit_profile: ക്രമീകരണങ്ങൾ + properties: പ്രോപ്പർട്ടികൾ + payment_methods: പേയ്മെന്റ് രീതികൾ + payment_methods_tip: ഈ എന്റർപ്രൈസസിന് പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല + shipping_methods: ഷിപ്പിംഗ് രീതികൾ + shipping_methods_tip: ഈ സംരംഭത്തിന് ഷിപ്പിംഗ് രീതികളുണ്ട് + enterprise_fees: എന്റർപ്രൈസ് ഫീസ് + enterprise_fees_tip: ഈ എന്റർപ്രൈസസിന് ഫീസില്ല + admin_index: + name: പേര് + role: പങ്ക് + sells: വിൽക്കുന്നു + visible: ദൃശ്യമാണോ? + owner: ഉടമ + producer: നിർമ്മാതാവ് + change_type_form: + producer_profile: പ്രൊഡ്യൂസർ പ്രൊഫൈൽ + connect_ofn: ഒഎഫ്എൻ വഴി ബന്ധിപ്പിക്കുക + always_free: എപ്പോഴും സൗജന്യം + producer_description_text: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ ചേർക്കുക, നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ അവരുടെ സ്റ്റോറുകളിൽ സ്റ്റോക്ക് ചെയ്യാൻ ഹബ്ബുകളെ അനുവദിക്കുന്നു. + producer_shop: പ്രൊഡ്യൂസർ ഷോപ്പ് + sell_your_produce: നിങ്ങളുടെ സ്വന്തം ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + producer_shop_description_text: നിങ്ങളുടെ സ്വന്തം ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഷോപ്പ്ഫ്രണ്ട് വഴി നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ ഉപഭോക്താക്കൾക്ക് നേരിട്ട് വിൽക്കുക. + producer_shop_description_text2: ഒരു പ്രൊഡ്യൂസർ ഷോപ്പ് നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾക്ക് മാത്രമുള്ളതാണ്, സൈറ്റിന് പുറത്ത് ഉൽപ്പാദിപ്പിച്ച ഉൽപ്പന്നങ്ങൾ വിൽക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, 'പ്രൊഡ്യൂസർ ഹബ്' തിരഞ്ഞെടുക്കുക. + producer_hub: പ്രൊഡ്യൂസർ ഹബ് + producer_hub_text: തന്നിൽ നിന്നും മറ്റുള്ളവരിൽ നിന്നും ഉള്ള ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + producer_hub_description_text: നിങ്ങളുടെ പ്രാദേശിക ഭക്ഷണ സമ്പ്രദായത്തിന്റെ നട്ടെല്ലാണ് നിങ്ങളുടെ എൻറ്റർപ്രൈസ്. ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ ഷോപ്പ്ഫ്രണ്ട് വഴി നിങ്ങൾക്ക് നിങ്ങളുടെ സ്വന്തം ഉൽപ്പന്നങ്ങളും മറ്റ് സംരംഭങ്ങളിൽ നിന്ന് സമാഹരിച്ച ഉൽപ്പന്നങ്ങളും വിൽക്കാൻ കഴിയും. + profile: പ്രൊഫൈൽ മാത്രം + get_listing: ഒരു ലിസ്റ്റിംഗ് നേടുക + profile_description_text: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ ആളുകൾക്ക് നിങ്ങളെ കണ്ടെത്താനും ബന്ധപ്പെടാനും കഴിയും. നിങ്ങളുടെ എന്റർപ്രൈസ് മാപ്പിൽ ദൃശ്യമാകും, കൂടാതെ ലിസ്റ്റിംഗുകളിൽ തിരയാനും കഴിയും. + hub_shop: ഹബ് ഷോപ്പ് + hub_shop_text: മറ്റുള്ളവരിൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + hub_shop_description_text: നിങ്ങളുടെ പ്രാദേശിക ഭക്ഷണ സമ്പ്രദായത്തിന്റെ നട്ടെല്ലാണ് നിങ്ങളുടെ എൻറ്റർപ്രൈസ്. നിങ്ങൾക്ക് മറ്റ് സംരംഭങ്ങളിൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ സമാഹരിക്കുകയും ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ നിങ്ങളുടെ ഷോപ്പ് വഴി വിൽക്കുകയും ചെയ്യാം. + choose_option: മുകളിലുള്ള ഓപ്ഷനുകളിലൊന്ന് ദയവായി തിരഞ്ഞെടുക്കുക. + change_now: ഇപ്പോൾ മാറ്റുക + enterprise_user_index: + loading_enterprises: എന്റർപ്രൈസുകൾ ലോഡ് ചെയ്യുന്നു + no_enterprises_found: എൻറ്റർപ്രൈസുകളൊന്നും കണ്ടെത്തിയില്ല. + search_placeholder: പേര് പ്രകാരം തിരയുക + manage: കൈകാര്യം ചെയ്യുക + manage_link: ക്രമീകരണങ്ങൾ + producer?: "പ്രൊഡ്യൂസർ?" + package: "പാക്കേജ്" + status: "സ്ഥിതി" + new_form: + owner: ഉടമ + owner_tip: ഈ എന്റർപ്രൈസസിന്റെ ഉത്തരവാദിത്തമുള്ള പ്രാഥമിക ഉപയോക്താവ്. + i_am_producer: ഞാൻ ഒരു പ്രൊഡ്യൂസർ ആണ് + contact_name: ബന്ധപ്പെടാനുള്ള പേര് + edit: + editing: 'ക്രമീകരണങ്ങൾ:' + back_link: എന്റർപ്രൈസസ് ലിസ്റ്റിലേക്ക് മടങ്ങുക + new: + title: പുതിയ എന്റർപ്രൈസ് + back_link: എന്റർപ്രൈസസ് ലിസ്റ്റിലേക്ക് മടങ്ങുക + welcome: + welcome_title: ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് സ്വാഗതം! + welcome_text: നിങ്ങൾ വിജയകരമായി സൃഷ്ടിച്ചു + next_step: അടുത്ത പടി + choose_starting_point: 'നിങ്ങളുടെ പാക്കേജ് തിരഞ്ഞെടുക്കുക:' + profile: 'പ്രൊഫൈൽ' + producer_profile: 'പ്രൊഡ്യൂസർ പ്രൊഫൈൽ' + invite_manager: + user_already_exists: "ഉപയോക്താവ് ഇതിനകം നിലവിലുണ്ട്" + error: "എന്തോ തകരാറ് സംഭവിച്ചു" + order_cycles: + loading_flash: + loading_order_cycles: ഓർഡർ സൈക്കിളുകൾ ലോഡുചെയ്യുന്നു + loading: ലോഡുചെയ്യുന്നു... + new: + create: "സൃഷ്ടിക്കാൻ" + cancel: "റദ്ദാക്കുക" + back_to_list: "പട്ടികയിലേക്ക് മടങ്ങുക" + create: + success: 'നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ ഉണ്ടാക്കി.' + update: + success: 'നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ അപ്ഡേറ്റ് ചെയ്തു.' + clone: + success: "നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ %{name} ക്ലോൺ ചെയ്തു." + notify_producers: + success: 'പ്രൊഡ്യൂസേഴ്സിന് അയയ്‌ക്കേണ്ട ഇമെയിലുകൾ അയയ്‌ക്കുന്നതിനായി ക്യൂവിലാണ്.' + edit: + save: "സേവ് ചെയ്യുക" + save_and_next: "സേവ് ചെയ്ത് അടുത്തതിലേക്ക് പോകുക" + next: "അടുത്തത്" + cancel: "റദ്ദാക്കുക" + back_to_list: "പട്ടികയിലേക്ക് മടങ്ങുക" + save_and_back_to_list: "സേവ് ചെയ്ത് ലിസ്റ്റിലേക്ക് മടങ്ങുക" + choose_products_from: "ഇതിൽ നിന്ന് ഉൽപ്പന്നങ്ങൾ തിരഞ്ഞെടുക്കുക:" + re_notify_producers: പ്രൊഡ്യൂസേഴ്സിനെ വീണ്ടും അറിയിക്കുക + notify_producers_tip: ഇത് ഓരോ പ്രൊഡ്യൂസേഴ്സിനും അവരുടെ ഓർഡറുകളുടെ ലിസ്റ്റുമായി ഒരു ഇമെയിൽ അയയ്ക്കും. + incoming: + incoming: "ഇൻകമിംഗ്" + supplier: "വിതരണക്കാരൻ" + products: "ഉൽപ്പന്നങ്ങൾ" + receival_details: "സ്വീകരിക്കൽ വിശദാംശങ്ങൾ" + fees: "ഫീസ്" + save: "സേവ് ചെയ്യുക" + save_and_next: "സേവ് ചെയ്ത് അടുത്തതിലേക്ക് പോകുക" + next: "അടുത്തത്" + cancel: "റദ്ദാക്കുക" + back_to_list: "പട്ടികയിലേക്ക് മടങ്ങുക" + outgoing: + outgoing: "ഔട്ട്ഗോയിംഗ്" + distributor: "വിതരണക്കാരൻ" + products: "ഉൽപ്പന്നങ്ങൾ" + tags: "ടാഗുകൾ" + delivery_details: "ഡെലിവറി വിശദാംശങ്ങൾ" + fees: "ഫീസ്" + next: "അടുത്തത്" + previous: "മുന്നിലത്തേത്" + save: "സേവ് ചെയ്യുക" + save_and_next: "സേവ് ചെയ്ത് അടുത്തതിലേക്ക് പോകുക" + cancel: "റദ്ദാക്കുക" + back_to_list: "പട്ടികയിലേക്ക് മടങ്ങുക" + checkout_options: + back_end: "ബാക്ക് ഓഫീസ് മാത്രം" + cancel: "റദ്ദാക്കുക" + checkout_options: "ചെക്ക്ഔട്ട് ഓപ്ഷനുകൾ" + distributor: "വിതരണക്കാരൻ" + no_payment_methods: ഈ ഓർഡർ സൈക്കിളിലെ ഓരോ വിതരണക്കാരനും കുറഞ്ഞത് ഒരു പേയ്‌മെന്റ് രീതി ആവശ്യമാണ്. + no_shipping_methods: ഈ ഓർഡർ സൈക്കിളിലെ ഓരോ വിതരണക്കാരനും കുറഞ്ഞത് ഒരു ഷിപ്പിംഗ് രീതി ആവശ്യമാണ്. + payment_methods: "പേയ്മെന്റ് രീതികൾ" + save: "സേവ് ചെയ്യുക" + save_and_back_to_list: "സേവ് ചെയ്ത് ലിസ്റ്റിലേക്ക് മടങ്ങുക" + select_all: "എല്ലാം തിരഞ്ഞെടുക്കുക" + shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + wizard_progress: + edit: "1. പൊതുവായ ക്രമീകരണങ്ങൾ" + incoming: "2. ഇൻകമിംഗ് ഉൽപ്പന്നങ്ങൾ" + outgoing: "3. ഔട്ട്ഗോയിംഗ് ഉൽപ്പന്നങ്ങൾ" + checkout_options: "4. ചെക്ക്ഔട്ട് ഓപ്ഷനുകൾ" + exchange_form: + pickup_time_tip: ഈ ഒസിയിൽ നിന്നുള്ള ഓർഡറുകൾ ഉപഭോക്താവിന് തയ്യാറാകുമ്പോൾ + pickup_instructions_placeholder: "പിക്കപ്പ് നിർദ്ദേശങ്ങൾ" + pickup_instructions_tip: ഒരു ഓർഡർ പൂർത്തിയാക്കിയ ശേഷം ഉപഭോക്താക്കൾക്ക് ഈ നിർദ്ദേശങ്ങൾ കാണിക്കും + pickup_time_placeholder: "തയ്യാറാണോ (അതായത്. തീയതി / സമയം)" + receival_instructions_placeholder: "സ്വീകരിക്കൽ നിർദ്ദേശങ്ങൾ" + add_fee: 'ഫീസ് ചേർക്കുക' + remove: 'നീക്കം ചെയ്യുക' + selected: 'തിരഞ്ഞെടുത്തു' + add_exchange_form: + add_supplier: 'വിതരണക്കാരനെ ചേർക്കുക' + add_distributor: 'വിതരണക്കാരനെ ചേർക്കുക' + advanced_settings: + automatic_notifications: യാന്ത്രിക അറിയിപ്പുകൾ + automatic_notifications_tip: ഓർഡർ സൈക്കിളുകൾ അവസാനിക്കുമ്പോൾ ഇമെയിലുകൾ വഴി പ്രൊഡ്യൂസേഴ്സിനെ അവരുടെ ഓർഡറുകൾ സ്വയമേവ അറിയിക്കുക + title: വിപുലമായ ക്രമീകരണങ്ങൾ + choose_product_tip: നിങ്ങൾക്ക് ഇൻകമിംഗ്, ഔട്ട്‌ഗോയിംഗ് ഉൽപ്പന്നങ്ങൾ %{inventory} ന്റെ ചരക്കുപ്പട്ടികയിലേക്ക് മാത്രം പരിമിതപ്പെടുത്താം. + preferred_product_selection_from_coordinator_inventory_only_here: കോർഡിനേറ്ററുടെ ചരക്കുപ്പട്ടിക മാത്രം + preferred_product_selection_from_coordinator_inventory_only_all: ലഭ്യമായ എല്ലാ ഉൽപ്പന്നങ്ങളും + save_reload: പേജ് സേവ് ചെയ്ത് വീണ്ടും ലോഡുചെയ്യുക + order_cycle_top_buttons: + advanced_settings: "വിപുലമായ ക്രമീകരണങ്ങൾ" + coordinator_fees: + add: കോർഡിനേറ്റർ ഫീസ് ചേർക്കുക + filters: + search_by_order_cycle_name: "ഓർഡർ സൈക്കിൾ പേര് പ്രകാരം തിരയുക..." + involving: "ഉൾപ്പെടുന്നു" + any_enterprise: "ഏതെങ്കിലും എന്റർപ്രൈസ്" + any_schedule: "ഏതെങ്കിലും ഷെഡ്യൂൾ" + form: + general_settings: "പൊതുവായ ക്രമീകരണങ്ങൾ" + incoming: ഇൻകമിംഗ് + supplier: വിതരണക്കാരൻ + products: ഉൽപ്പന്നങ്ങൾ + receival_details: സ്വീകരിക്കൽ വിശദാംശങ്ങൾ + fees: ഫീസ് + outgoing: ഔട്ട്ഗോയിംഗ് + distributor: വിതരണക്കാരൻ + tags: ടാഗുകൾ + add_a_tag: ഒരു ടാഗ് ചേർക്കുക + delivery_details: പിക്കപ്പ് / ഡെലിവറി വിശദാംശങ്ങൾ + index: + schedule: ഷെഡ്യൂൾ + schedules: ഷെഡ്യൂളുകൾ + new_schedule: പുതിയ ഷെഡ്യൂൾ + new_schedule_tooltip: ഒരു സബ്‌സ്‌ക്രിപ്‌ഷൻ ഓർഡർ നൽകുന്ന ആവൃത്തി + name_and_timing_form: + name: പേര് + orders_open: ഓർഡറുകൾ തുറക്കുന്നത് + coordinator: കോർഡിനേറ്റർ + orders_close: ഓർഡറുകൾ അവസാനിക്കുന്നത് + row: + suppliers: വിതരണക്കാർ + distributors: വിതരണക്കാർ + variants: വകഭേദങ്ങൾ + simple_form: + ready_for: തയ്യാറാണ് + ready_for_placeholder: തീയതി / സമയം + customer_instructions: ഉപഭോക്തൃ നിർദ്ദേശങ്ങൾ + customer_instructions_placeholder: പിക്ക്-അപ്പ് അല്ലെങ്കിൽ ഡെലിവറി കുറിപ്പുകൾ + products: ഉൽപ്പന്നങ്ങൾ + fees: ഫീസ് + tags: ടാഗുകൾ + destroy_errors: + orders_present: ആ ഓർഡർ സൈക്കിൾ ഒരു ഉപഭോക്താവ് തിരഞ്ഞെടുത്തതിനാൽ അത് ഇല്ലാതാക്കാൻ കഴിയില്ല. ഉപഭോക്താക്കൾ ഇത് ആക്‌സസ് ചെയ്യുന്നതിൽ നിന്ന് തടയാൻ, അത് ക്ലോസ് ചെയ്യുക. + schedule_present: ആ ഓർഡർ സൈക്കിൾ ഒരു ഷെഡ്യൂളുമായി ലിങ്ക് ചെയ്‌തിരിക്കുന്നതിനാൽ അത് ഇല്ലാതാക്കാൻ കഴിയില്ല. ആദ്യം ഷെഡ്യൂൾ അൺലിങ്ക് ചെയ്യുക അല്ലെങ്കിൽ ഇല്ലാതാക്കുക. + bulk_update: + no_data: ഹോ, എന്തോ തകരാറ് സംഭവിച്ചു. ഓർഡർ സൈക്കിൾ ഡാറ്റയൊന്നും കണ്ടെത്തിയില്ല. + date_warning: + msg: ഈ ഓർഡർ സൈക്കിൾ %{n} ഓപ്പൺ സബ്‌സ്‌ക്രിപ്‌ഷൻ ഓർഡറുകളിലേക്ക് ലിങ്ക് ചെയ്‌തിരിക്കുന്നു. ഇപ്പോൾ ഈ തീയതി മാറ്റുന്നത് ഇതിനകം നൽകിയിട്ടുള്ള ഒരു ഓർഡറിനെയും ബാധിക്കില്ല, എന്നാൽ സാധ്യമെങ്കിൽ അത് ഒഴിവാക്കേണ്ടതാണ്. നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ? + cancel: റദ്ദാക്കുക + proceed: തുടരുക + status: + undated: തീയതിയില്ലാത്ത + upcoming: വരാനിരിക്കുന്ന + open: തുറക്കുക + closed: അടച്ചു + producer_properties: + index: + title: പ്രൊഡ്യൂസർ പ്രോപ്പർട്ടികൾ + proxy_orders: + cancel: + could_not_cancel_the_order: ഓർഡർ റദ്ദാക്കാൻ കഴിഞ്ഞില്ല + resume: + could_not_resume_the_order: ഓർഡർ പുനരാരംഭിക്കാനായില്ല + select2: + minimal_search_length: ദയവായി %{count} അല്ലെങ്കിൽ കൂടുതൽ പ്രതീകങ്ങൾ നൽകുക + searching: തിരയുന്നു... + no_matches: പൊരുത്തങ്ങളൊന്നും കണ്ടെത്തിയില്ല + shared: + user_guide_link: + user_guide: ഉപയോക്തൃ ഗൈഡ് + enterprises_hubs_tabs: + has_no_payment_methods: "%{enterprise} -ന് പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല" + has_no_shipping_methods: "%{enterprise} -ന് ഷിപ്പിംഗ് രീതികളൊന്നുമില്ല" + has_no_enterprise_fees: "%{enterprise} -ന് എന്റർപ്രൈസ് ഫീസ് ഇല്ല" + flashes: + dismiss: ബഹിഷ്കരിക്കുക + side_menu: + enterprise: + primary_details: "പ്രാഥമിക വിശദാംശങ്ങൾ" + address: "വിലാസം" + contact: "ബന്ധപ്പെടുക" + social: "സാമൂഹികം" + about: "കുറിച്ച്" + business_details: "ബിസിനസ്സ് വിശദാംശങ്ങൾ" + images: "ചിത്രങ്ങൾ" + properties: "പ്രോപ്പർട്ടികൾ" + shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + payment_methods: "പേയ്മെന്റ് രീതികൾ" + enterprise_fees: "എന്റർപ്രൈസ് ഫീസ്" + enterprise_permissions: "എന്റർപ്രൈസ് അനുമതികൾ" + inventory_settings: "ചരക്കുപ്പട്ടിക ക്രമീകരണങ്ങൾ" + tag_rules: "നിയമങ്ങൾ കൂട്ടിയോജിപ്പിക്കുക" + shop_preferences: "ഷോപ്പ് മുൻഗണനകൾ" + users: "ഉപയോക്താക്കൾ" + vouchers: വൗച്ചറുകൾ + white_label: "വൈറ്റ് ലേബൽ" + connected_apps: "ബന്ധിപ്പിച്ച ആപ്പുകൾ" + enterprise_group: + primary_details: "പ്രാഥമിക വിശദാംശങ്ങൾ" + users: "ഉപയോക്താക്കൾ" + about: "കുറിച്ച്" + images: "ചിത്രങ്ങൾ" + contact: "ബന്ധപ്പെടുക" + web: "വെബ് ഉറവിടങ്ങൾ" + enterprise_issues: + create_new: പുതിയത് ഉണ്ടാക്കുക + resend_email: ഇമെയിൽ വീണ്ടും അയക്കുക + has_no_payment_methods: "%{enterprise} -ന് നിലവിൽ പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല" + has_no_shipping_methods: "%{enterprise} -ന് നിലവിൽ ഷിപ്പിംഗ് രീതികളൊന്നുമില്ല" + email_confirmation: "ഇമെയിൽ സ്ഥിരീകരണം നടന്നിട്ടില്ല. ഞങ്ങൾ ഒരു സ്ഥിരീകരണ ഇമെയിൽ %{email} -ലേക്ക് അയച്ചു." + not_visible: "%{enterprise} ദൃശ്യമല്ല, അതിനാൽ മാപ്പിലോ തിരയലുകളിലോ കണ്ടെത്താൻ കഴിയില്ല" + reports: + deprecated: "ഈ റിപ്പോർട്ട് നിരാകരിച്ചതാണ്, ഭാവിയിലെ റിലീസിൽ ഇത് നീക്കം ചെയ്യപ്പെടും." + hidden: മറച്ചിരിക്കുന്നു + unitsize: യൂണിറ്റ് സൈസ് + total: ആകെ + total_items: ആകെ ഇനങ്ങൾ + total_by_customer: കസ്റ്റമർ മുഖേന ആകെ + total_by_supplier: വിതരണക്കാരൻ മുഖേന ആകെ + supplier_totals: ഓർഡർ സൈക്കിൾ സപ്ലയർ ആകെ + percentage: "%{value} %" + supplier_totals_by_distributor: വിതരണക്കാരൻ മുഖേന ഓർഡർ സൈക്കിൾ സപ്ലയർ ആകെ + totals_by_supplier: സപ്ലയർ മുഖേന ഓർഡർ സൈക്കിൾ ഡിസ്ട്രിബ്യൂട്ടർ ആകെ + customer_totals: ഓർഡർ സൈക്കിൾ ഉപഭോക്താക്കൾ ആകെ + all_products: എല്ലാ ഉൽപ്പന്നങ്ങളും + inventory: ചരക്കുപട്ടിക (കൈയിൽ) + lettuce_share: ലെറ്റൂസ് ഷെയർ + payment_methods: പേയ്‌മെന്റ് രീതികളുടെ റിപ്പോർട്ട് + delivery: ഡെലിവറി റിപ്പോർട്ട് + sales_tax_totals_by_producer: പ്രൊഡ്യൂസർ മുഖേനയുള്ള വിൽപ്പന നികുതിയുടെ ആകെത്തുക + sales_tax_totals_by_order: ഓർഡർ പ്രകാരമുള്ള വിൽപ്പന നികുതിയുടെ ആകെത്തുക + tax_types: നികുതി തരങ്ങൾ + tax_rates: നികുതി നിരക്കുകൾ + pack_by_customer: ഉപഭോക്താവിനാൽ പായ്ക്ക് ചെയ്യുക + pack_by_supplier: വിതരണക്കാരൻ വഴി പായ്ക്ക് ചെയ്യുക + pack_by_product: ഉൽപ്പന്നം അനുസരിച്ച് പായ്ക്ക് ചെയ്യുക + download: + button: "റിപ്പോർട്ട് ഡൗൺലോഡ് ചെയ്യുക" + show: + report_taking_longer: > + ക്ഷമിക്കണം, ഈ റിപ്പോർട്ട് പ്രോസസ്സ് ചെയ്യാൻ വളരെയധികം സമയമെടുത്തു. അതിൽ + ധാരാളം ഡാറ്റ അടങ്ങിയിരിക്കാം അല്ലെങ്കിൽ ഞങ്ങൾ മറ്റ് റിപ്പോർട്ടുകളിൽ തിരക്കിലാണ്. + നിങ്ങൾക്ക് പിന്നീട് വീണ്ടും ശ്രമിക്കാവുന്നതാണ്. + report_taking_longer_html: > + ഈ റിപ്പോർട്ട് പ്രോസസ്സ് ചെയ്യാൻ കൂടുതൽ സമയമെടുക്കുന്നു. അതിൽ ധാരാളം ഡാറ്റ + അടങ്ങിയിരിക്കാം അല്ലെങ്കിൽ ഞങ്ങൾ മറ്റ് റിപ്പോർട്ടുകളിൽ തിരക്കിലാണ്. അത് + പൂർത്തിയായിക്കഴിഞ്ഞാൽ, ഞങ്ങൾ നിങ്ങളെ ഇമെയിൽ വഴി അറിയിക്കും. + report_link_label: റിപ്പോർട്ട് ഡൗൺലോഡ് ചെയ്യുക (ലഭ്യമാകുമ്പോൾ) + revenues_by_hub: + name: ഹബ് വഴിയുള്ള വരുമാനം + description: ഹബ് വഴിയുള്ള വരുമാനം + orders_and_distributors: + name: ഓർഡറുകളും വിതരണക്കാരും + description: വിതരണക്കാരുടെ വിശദാംശങ്ങളുള്ള ഓർഡറുകൾ + bulk_coop: + name: ബൾക്ക് കോ-ഓപ്പ് + description: ബൾക്ക് സഹകരണ ഓർഡറുകൾക്കായുള്ള റിപ്പോർട്ടുകൾ + payments: + name: പേയ്മെന്റ് റിപ്പോർട്ടുകൾ + description: പേയ്‌മെന്റുകൾക്കുള്ള റിപ്പോർട്ടുകൾ + orders_and_fulfillment: + name: ഓർഡറുകളും പൂർത്തീകരണ റിപ്പോർട്ടുകളും + customers: + name: ഉപഭോക്താക്കൾ + products_and_inventory: + name: ഉൽപ്പന്നങ്ങളും ചരക്കുപട്ടികയും + users_and_enterprises: + name: ഉപയോക്താക്കളും സംരംഭങ്ങളും + description: എന്റർപ്രൈസ് ഉടമസ്ഥതയും സ്ഥിതിയും + order_cycle_management: + name: ഓർഡർ സൈക്കിൾ മാനേജ്മെന്റ് + sales_tax: + name: വില്പന നികുതി + xero_invoices: + name: സീറോ ഇൻവോയ്‌സുകൾ + description: സീറോയിലേക്ക് ഇറക്കുമതി ചെയ്യുന്നതിനുള്ള ഇൻവോയ്‌സുകൾ + enterprise_fee_summary: + name: "എന്റർപ്രൈസ് ഫീസ് സംഗ്രഹം" + description: "എന്റർപ്രൈസ് ഫീസ് ശേഖരിച്ചതിന്റെ സംഗ്രഹം" + enterprise_fees_with_tax_report_by_order: "ഓർഡർ പ്രകാരം നികുതി റിപ്പോർട്ടോടുകൂടിയ എന്റർപ്രൈസ് ഫീസ്" + enterprise_fees_with_tax_report_by_producer: "പ്രൊഡ്യൂസർ പ്രകാരം നികുതി റിപ്പോർട്ടോടുകൂടിയ എന്റർപ്രൈസ് ഫീസ്" + errors: + no_report_type: "ഒരു റിപ്പോർട്ട് തരം വ്യക്തമാക്കുക" + report_not_found: "റിപ്പോർട്ട് കണ്ടെത്തിയില്ല" + missing_ransack_params: "അഭ്യർത്ഥനയിൽ റാൻസാക് സെർച്ച് പാരാമുകൾ നൽകുക" + hidden_field: "<മറച്ചിരിക്കുന്നു>" + summary_row: + total: "ആകെ" + table: + select_and_search: "നിങ്ങളുടെ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ ഫിൽട്ടറുകൾ തിരഞ്ഞെടുത്ത് %{option} ക്ലിക്ക് ചെയ്യുക." + headings: + hub: "ഹബ്" + customer_code: "കോഡ്" + first_name: "പേരിന്റെ ആദ്യഭാഗം" + last_name: "പേരിന്റെ അവസാന ഭാഗം" + supplier: "വിതരണക്കാരൻ" + product: "ഉൽപ്പന്നം" + variant: "വേരിയന്റ്" + quantity: "അളവ്" + is_temperature_controlled: "ടെമ്പ് കൺട്രോൾഡ്?" + temp_controlled: "ടെമ്പ് കൺട്രോൾഡ്?" + price: "വില" + rendering_options: + generate_report: "റിപ്പോർട്ട് സൃഷ്ടിക്കുക" + on_screen: "സ്‌ക്രീനിൽ" + spreadsheet: "സ്‌പ്രെഡ്‌ഷീറ്റ് (എക്‌സൽ, ഓപ്പൺ ഓഫീസ്..)" + display: പ്രദർശിപ്പിക്കുക + summary_row: സംഗ്രഹ വരി + header_row: തലക്കെട്ട് വരി + raw_data: അസംസ്കൃത ഡാറ്റ + formatted_data: ഫോർമാറ്റ് ചെയ്ത ഡാറ്റ + packing: + name: "പാക്കിംഗ് റിപ്പോർട്ടുകൾ" + oidc_settings: + index: + title: "ഓഐഡിസി ക്രമീകരണങ്ങൾ" + connect: "നിങ്ങളുടെ അക്കൗണ്ട് ബന്ധിപ്പിക്കുക" + les_communs_link: "ലെസ് കമ്മ്യൂണ്സ് ഓപ്പൺ ഐഡി സെർവർ" + link_your_account: "ആദ്യം ഡിഎഫ്സി (ലെസ് കമ്മ്യൂണ്സ് ഓപ്പൺ ഐഡി കണക്ട്) ഉപയോഗിക്കുന്ന അംഗീകൃത ദാതാവുമായി നിങ്ങളുടെ അക്കൗണ്ട് ലിങ്ക് ചെയ്യേണ്ടത് ആവശ്യമാണ്." + link_account_button: "നിങ്ങളുടെ ലെസ് കമ്മ്യൂണ്സ് ഓപ്പൺ ഐഡി കണക്ട് അക്കൗണ്ട് ലിങ്ക് ചെയ്യുക" + view_account: "നിങ്ങളുടെ അക്കൗണ്ട് കാണുന്നതിന്, നോക്കുക:" + subscriptions: + index: + title: "സബ്സ്ക്രിപ്ഷനുകൾ" + new: "പുതിയ സബ്സ്ക്രിപ്ഷൻ" + issue: "ഇഷ്യൂ" + new: + title: "പുതിയ സബ്സ്ക്രിപ്ഷൻ" + edit: + title: "സബ്സ്ക്രിപ്ഷൻ എഡിറ്റ് ചെയ്യുക" + table: + edit_subscription: സബ്സ്ക്രിപ്ഷൻ എഡിറ്റ് ചെയ്യുക + pause_subscription: സബ്‌സ്‌ക്രിപ്‌ഷൻ താൽക്കാലികമായി നിർത്തുക + unpause_subscription: സബ്സ്ക്രിപ്ഷൻ ഇടവേള അവസാനിപ്പിക്കുക + cancel_subscription: സബ്‌സ്‌ക്രിപ്‌ഷൻ റദ്ദാക്കുക + filters: + query_placeholder: "ഇമെയിൽ പ്രകാരം തിരയുക..." + setup_explanation: + title: "സബ്സ്ക്രിപ്ഷനുകൾ" + just_a_few_more_steps: 'ആരംഭിക്കുന്നതിന് മുമ്പ് കുറച്ച് ഘട്ടങ്ങൾ കൂടി:' + enable_subscriptions: "നിങ്ങളുടെ ഷോപ്പുകളിലൊന്നിലെങ്കിലും സബ്‌സ്‌ക്രിപ്‌ഷനുകൾ പ്രവർത്തനക്ഷമമാക്കുക" + enable_subscriptions_step_1_html: 1. %{enterprises_link} പേജിലേക്ക് പോയി നിങ്ങളുടെ ഷോപ്പ് കണ്ടെത്തി "മാനേജ് ചെയ്യുക" ക്ലിക്ക് ചെയ്യുക + enable_subscriptions_step_2: 2. "ഷോപ്പ് മുൻഗണനകൾ" എന്നതിന് കീഴിൽ, സബ്സ്ക്രിപ്ഷൻ ഓപ്ഷൻ പ്രവർത്തനക്ഷമമാക്കുക + set_up_shipping_and_payment_methods_html: '%{shipping_link} , %{payment_link} രീതികൾ സജ്ജീകരിക്കുക' + set_up_shipping_and_payment_methods_note_html: സബ്സ്ക്രിപ്ഷനുകൾക്കൊപ്പം ക്യാഷ്, സ്ട്രൈപ്പ് പേയ്‌മെന്റ് രീതികൾ മാത്രമേ ചെയ്യാവൂ
എന്നത് ശ്രദ്ധിക്കുക + ensure_at_least_one_customer_html: കുറഞ്ഞത് ഒരു %{customer_link} നിലവിലുണ്ടെന്ന് ഉറപ്പാക്കുക + create_at_least_one_schedule: കുറഞ്ഞത് ഒരു ഷെഡ്യൂളെങ്കിലും സൃഷ്ടിക്കുക + create_at_least_one_schedule_step_1_html: 1. %{order_cycles_link} പേജിലേക്ക് പോകുക + create_at_least_one_schedule_step_2: 2. നിങ്ങൾ ഇതിനകം അങ്ങനെ ചെയ്തിട്ടില്ലെങ്കിൽ ഒരു ഓർഡർ സൈക്കിൾ സൃഷ്ടിക്കുക + create_at_least_one_schedule_step_3: 3. '+ പുതിയ ഷെഡ്യൂൾ' ക്ലിക്ക് ചെയ്ത് ഫോം പൂരിപ്പിക്കുക + once_you_are_done_you_can_html: നിങ്ങൾ ചെയ്തുകഴിഞ്ഞാൽ, നിങ്ങൾക്ക് %{reload_this_page_link} കഴിയും + reload_this_page: ഈ പേജ് വീണ്ടും ലോഡുചെയ്യുക + form: + create: "സബ്സ്ക്രിപ്ഷൻ സൃഷ്ടിക്കുക" + steps: + details: 1. അടിസ്ഥാന വിവരങ്ങൾ + address: 2. വിലാസം + products: 3. ഉൽപ്പന്നങ്ങൾ ചേർക്കുക + review: 4. അവലോകനം & സേവ് ചെയ്യുക + subscription_line_items: + this_is_an_estimate: | + പ്രദർശിപ്പിച്ച വിലകൾ ഒരു എസ്റ്റിമേറ്റ് മാത്രമാണ്, സബ്‌സ്‌ക്രിപ്‌ഷൻ മാറുന്ന സമയത്ത് വില കണക്കാക്കുന്നു. + നിങ്ങൾ വിലകളോ ഫീസോ മാറ്റുകയാണെങ്കിൽ, ഓർഡറുകൾ അപ്‌ഡേറ്റ് ചെയ്യപ്പെടും, എന്നാൽ സബ്‌സ്‌ക്രിപ്‌ഷൻ പഴയ മൂല്യങ്ങൾ പ്രദർശിപ്പിക്കും. + not_in_open_and_upcoming_order_cycles_warning: "ഈ ഉൽപ്പന്നത്തിന് ഓപ്പൺ അല്ലെങ്കിൽ വരാനിരിക്കുന്ന ഓർഡർ സൈക്കിളുകളൊന്നുമില്ല." + autocomplete: + name_or_sku: "പേര് അല്ലെങ്കിൽ എസ്കെയു " + quantity: "അളവ്" + add: "ചേർക്കുക" + details: + details: വിശദാംശങ്ങൾ + invalid_error: ക്ഷമിക്കണം! ദയവായി ആവശ്യമായ എല്ലാ ഫീൽഡുകളിലും വിവരങ്ങൾ ചേർക്കുക! + allowed_payment_method_types_tip: ഇപ്പോൾ പണവും സ്ട്രൈപ്പും പേയ്‌മെന്റ് രീതികൾ മാത്രമേ ഉപയോഗിക്കാവൂ + credit_card: ക്രെഡിറ്റ് കാർഡ് + charges_not_allowed: ഈ ഉപഭോക്താവിന് ചാർജുകൾ അനുവദനീയമല്ല + no_default_card: ചാർജ് ചെയ്യാൻ ഉപഭോക്താവിന് കാർഡുകളൊന്നും ലഭ്യമല്ല + card_ok: ചാർജ് ചെയ്യാൻ ഉപഭോക്താവിന് ഒരു കാർഡ് ലഭ്യമാണ് + begins_at_placeholder: "ഒരു തീയതി തിരഞ്ഞെടുക്കുക" + ends_at_placeholder: "ഓപ്ഷണൽ" + loading_flash: + loading: സബ്‌സ്‌ക്രിപ്‌ഷനുകൾ ലോഡുചെയ്യുന്നു + review: + details: വിശദാംശങ്ങൾ + address: വിലാസം + products: ഉൽപ്പന്നങ്ങൾ + no_open_or_upcoming_order_cycle: "വരാനിരിക്കുന്ന ഓർഡർ സൈക്കിൾ ഇല്ല" + products_panel: + save: "സേവ്" + saving: "സേവ് ചെയ്യുന്നു" + saved: "സേവ് ചെയ്തു" + product_already_in_order: ഈ ഉൽപ്പന്നം ഇതിനകം ഓർഡറിൽ ചേർത്തിട്ടുണ്ട്. അളവ് നേരിട്ട് എഡിറ്റ് ചെയ്യുക. + stock: + insufficient_stock: "ആവശ്യത്തിന് സ്റ്റോക്ക് ലഭ്യമല്ല" + out_of_stock: "സ്റ്റോക്കില്ല" + orders: + number: നമ്പർ + confirm_edit: ഈ ഓർഡർ എഡിറ്റ് ചെയ്യണമെന്ന് തീർച്ചയാണോ? അങ്ങനെ ചെയ്യുന്നത് ഭാവിയിൽ സബ്‌സ്‌ക്രിപ്‌ഷനിലേക്കുള്ള മാറ്റങ്ങൾ സ്വയമേവ സമന്വയിപ്പിക്കുന്നത് കൂടുതൽ ബുദ്ധിമുട്ടാക്കിയേക്കാം. + confirm_cancel_msg: "ഈ സബ്‌സ്‌ക്രിപ്‌ഷൻ റദ്ദാക്കണമെന്ന് തീർച്ചയാണോ? ഈ പ്രവർത്തനം പഴയപടിയാക്കാനാകില്ല." + cancel_failure_msg: "ക്ഷമിക്കണം, റദ്ദാക്കൽ പരാജയപ്പെട്ടു!" + confirm_pause_msg: "ഈ സബ്‌സ്‌ക്രിപ്‌ഷൻ താൽക്കാലികമായി നിർത്തണമെന്ന് തീർച്ചയാണോ?" + pause_failure_msg: "ക്ഷമിക്കണം, താൽക്കാലികമായി നിർത്തുന്നത് പരാജയപ്പെട്ടു!" + confirm_unpause_msg: "ഈ സബ്‌സ്‌ക്രിപ്‌ഷന്റെ ഷെഡ്യൂളിൽ നിങ്ങൾക്ക് ഒരു ഓപ്പൺ ഓർഡർ സൈക്കിൾ ഉണ്ടെങ്കിൽ, ഈ ഉപഭോക്താവിനായി ഒരു ഓർഡർ സൃഷ്‌ടിക്കും. ഈ സബ്‌സ്‌ക്രിപ്‌ഷൻ ഇടവേള അവസാനിപ്പിക്കണമെന്നത് തീർച്ചയാണോ?" + unpause_failure_msg: "ക്ഷമിക്കണം, ഇടവേള അവസാനിപ്പിക്കുന്നത് പരാജയപ്പെട്ടു!" + confirm_cancel_open_orders_msg: "ഈ സബ്‌സ്‌ക്രിപ്‌ഷന്റെ ചില ഓർഡറുകൾ നിലവിൽ ഉണ്ട്. ഓർഡർ നൽകുമെന്ന് ഉപഭോക്താവിനെ ഇതിനകം അറിയിച്ചിട്ടുണ്ട്. ഈ ഓർഡർ(കൾ) റദ്ദാക്കണോ അതോ സൂക്ഷിക്കണോ?" + resume_canceled_orders_msg: "ഈ സബ്‌സ്‌ക്രിപ്‌ഷനുള്ള ചില ഓർഡറുകൾ ഇപ്പോൾ പുനരാരംഭിക്കാനാകും. ഓർഡറുകൾ എന്ന ഡ്രോപ്പ്ഡൗണിൽ നിന്ന് നിങ്ങൾക്ക് അവ പുനരാരംഭിക്കാം." + yes_cancel_them: അവ റദ്ദാക്കുക + no_keep_them: അവ സൂക്ഷിക്കുക + yes_i_am_sure: അതെ, എനിക്ക് ഉറപ്പാണ് + number: "നമ്പർ" + order_update_issues_msg: ചില ഓർഡറുകൾ സ്വയമേവ അപ്‌ഡേറ്റ് ചെയ്യാൻ കഴിഞ്ഞില്ല, അവ കരകൃതമായി എഡിറ്റ് ചെയ്‌തതുകൊണ്ടാകാം. ചുവടെ ലിസ്‌റ്റ് ചെയ്‌തിരിക്കുന്ന പ്രശ്‌നങ്ങൾ അവലോകനം ചെയ്‌ത് ആവശ്യമെങ്കിൽ വ്യക്തിഗത ഓർഡറുകളിൽ എന്തെങ്കിലും ക്രമീകരണങ്ങൾ വരുത്തുക. + no_results: + no_subscriptions: ഇതുവരെ സബ്‌സ്‌ക്രിപ്‌ഷനുകളൊന്നുമില്ല... + why_dont_you_add_one: എന്തുകൊണ്ടാണ് നിങ്ങൾ ഒരെണ്ണം ചേർക്കാത്തത്? :) + no_matching_subscriptions: പൊരുത്തപ്പെടുന്ന സബ്‌സ്‌ക്രിപ്‌ഷനുകളൊന്നും കണ്ടെത്തിയില്ല + schedules: + destroy: + associated_subscriptions_error: അനുബന്ധ സബ്‌സ്‌ക്രിപ്‌ഷനുകൾ ഉള്ളതിനാൽ ഈ ഷെഡ്യൂൾ ഇല്ലാതാക്കാൻ കഴിയില്ല + vouchers: + new: + legend: പുതിയ വൗച്ചർ + back: തിരികെ + save: സേവ് + voucher_code: വൗച്ചർ കോഡ് + voucher_amount: തുക + voucher_type: വൗച്ചർ തരം + flat_rate: ഫ്ലാറ്റ് + percentage_rate: ശതമാനം (%) + controllers: + enterprises: + stripe_connect_cancelled: "സ്ട്രൈപ്പിലേക്കുള്ള കണക്ഷൻ റദ്ദാക്കി" + stripe_connect_success: "സ്ട്രൈപ്പ് അക്കൗണ്ട് കണക്റ്റ് ചെയ്തു" + stripe_connect_fail: ക്ഷമിക്കണം, നിങ്ങളുടെ സ്ട്രൈപ്പ് അക്കൗണ്ടിന്റെ കണക്ഷൻ പരാജയപ്പെട്ടു + stripe_connect_settings: + resource: സ്ട്രൈപ്പ് കണക്റ്റ് കോൺഫിഗറേഷൻ + resend_confirmation_emails_feedback: + one: "ഒരു ഓർഡറിനായി സ്ഥിരീകരണ ഇമെയിൽ അയച്ചു." + other: "%{count}ഓർഡറുകൾക്ക് സ്ഥിരീകരണ ഇമെയിലുകൾ അയച്ചിട്ടുണ്ട്." + send_invoice_feedback: + one: "ഒരു ഓർഡറിനായി ഇൻവോയ്സ് ഇമെയിൽ അയച്ചു." + other: "%{count} ഓർഡറുകൾക്ക് ഇൻവോയ്സ് ഇമെയിലുകൾ അയച്ചിട്ടുണ്ട്." + api: + unknown_error: "എന്തോ തകരാറ് സംഭവിച്ചു. ഞങ്ങളുടെ ടീമിനെ അറിയിച്ചിട്ടുണ്ട്." + invalid_api_key: "അസാധുവായ എപിഐ കീ (%{key}) വ്യക്തമായിട്ടുണ്ട്." + unauthorized: "ആ പ്രവർത്തനം നടത്താൻ നിങ്ങൾക്ക് അധികാരമില്ല." + unpermitted_parameters: "ഈ അഭ്യർത്ഥനയിൽ പാരാമീറ്ററുകൾ അനുവദനീയമല്ല: %{params}" + missing_parameter: "ആവശ്യമായ പാരാമീറ്റർ കാണുന്നില്ല അല്ലെങ്കിൽ ശൂന്യമാണ്: %{param}" + invalid_resource: "അസാധുവായ റീസോർസ്. തകരാറുകൾ പരിഹരിച്ച് വീണ്ടും ശ്രമിക്കുക." + resource_not_found: "നിങ്ങൾ തിരയുന്ന റീസോർസ് കണ്ടെത്താനായില്ല." + enterprise_logo: + destroy_attachment_does_not_exist: "ലോഗോ നിലവിലില്ല" + enterprise_promo_image: + destroy_attachment_does_not_exist: "പ്രമോ ചിത്രം നിലവിലില്ല" + enterprise_terms_and_conditions: + destroy_attachment_does_not_exist: "നിബന്ധനകളും വ്യവസ്ഥകളും ഫയൽ നിലവിലില്ല" + orders: + failed_to_update: "ഓർഡർ അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു" + query_param: + error: + title: അസാധുവായ അന്വേഷണ പാരാമീറ്റർ + extra_fields: "പിന്തുണയ്ക്കാത്ത ഫീൽഡുകൾ: %{fields}" + checkout: + failed: "ചെക്ക്ഔട്ട് പരാജയപ്പെട്ടു. ദയവായി ഞങ്ങളെ അറിയിക്കുക, അതുവഴി ഞങ്ങൾക്ക് നിങ്ങളുടെ ഓർഡർ പ്രോസസ്സ് ചെയ്യാൻ കഴിയും." + payment_cancelled_due_to_stock: "പേയ്‌മെന്റ് റദ്ദാക്കി: സ്റ്റോക്ക് പ്രശ്‌നങ്ങൾ കാരണം ചെക്ക്ഔട്ട് പൂർത്തിയാക്കാനായില്ല." + order_not_loaded: "ചെക്ക്ഔട്ട് പ്രോസസ്സിംഗിനായി സാധുതയുള്ള ഒരു ഓർഡർ കണ്ടെത്തിയില്ല" + your_details_without_number: നിങ്ങളുടെ വിശദാംശങ്ങൾ + payment_method_without_number: പണമടയ്ക്കൽ രീതി + order_summary_without_number: ഓർഡർ സംഗ്രഹം + already_ordered: + cart: "കാർട്ട്" + message_html: "ഈ ഓർഡർ സൈക്കിളിനായി നിങ്ങൾക്ക് ഇതിനകം ഒരു ഓർഡർ ഉണ്ട്. നിങ്ങൾ മുമ്പ് ഓർഡർ ചെയ്ത ഇനങ്ങൾ കാണാൻ %{cart} പരിശോധിക്കുക. ഓർഡർ സൈക്കിൾ തുറന്നിരിക്കുന്നിടത്തോളം നിങ്ങൾക്ക് ഇനങ്ങൾ റദ്ദാക്കാനും കഴിയും." + step1: + contact_information: + title: ബന്ധപ്പെടാനുള്ള വിവരങ്ങൾ + email: + label: ഇമെയിൽ + phone: + label: ഫോൺ നമ്പർ + billing_address: + title: ബില്ലിംഗ് വിലാസം + first_name: + label: പേരിന്റെ ആദ്യഭാഗം + last_name: + label: പേരിന്റെ അവസാന ഭാഗം + address: + address1: + label: വിലാസം (തെരുവ് + വീടിന്റെ നമ്പർ) + address2: + label: അധിക വിലാസ വിവരം (ഓപ്ഷണൽ) + city: + label: നഗരം + state_id: + label: സ്റ്റേറ്റ് + zipcode: + label: പിൻ കോഡ് + country_id: + label: രാജ്യം + shipping_info: + title: ഷിപ്പിംഗ് വിവരം + submit: അടുത്തത് - പേയ്മെന്റ് രീതി + cancel: എഡിറ്റ് ബാസ്കറ്റിലേക്ക് മടങ്ങുക + step2: + payment_method: + title: പണമടയ്ക്കൽ രീതി + form: + card_number: + label: കാർഡ് നമ്പർ + placeholder: 'ഉദാ: 4242 4242 4242 4242' + card_verification_value: + label: സി.വി.സി + card_month: + label: മാസം + card_year: + label: വർഷം + stripe: + use_saved_card: സേവ് ചെയ്ത കാർഡ് ഉപയോഗിക്കുക + use_new_card: നിങ്ങളുടെ കാർഡ് ഐഡന്റിഫയറുകൾ നൽകുക + save_card: ഭാവിയിലെ ഉപയോഗത്തിനായി കാർഡ് സേവ് ചെയ്യുക + create_new_card: അല്ലെങ്കിൽ പുതിയ കാർഡ് വിശദാംശങ്ങൾ ചുവടെ നൽകുക + explaination: അന്തിമ ചെലവുകൾ ഉൾപ്പെടുന്ന അടുത്ത ഘട്ടത്തിൽ നിങ്ങൾക്ക് ഓർഡർ അവലോകനം ചെയ്യാനും സ്ഥിരീകരിക്കാനും കഴിയും. + submit: അടുത്തത് - ഓർഡർ സംഗ്രഹം + cancel: നിങ്ങളുടെ വിശദാംശങ്ങളിലേക്ക് മടങ്ങുക + voucher: + voucher: "%{voucher_amount} വൗച്ചർ" + apply_voucher: വൗച്ചർ പ്രയോഗിക്കുക + apply: പ്രയോഗിക്കുക + placeholder: വൗച്ചർ കോഡ് നൽകുക + remove_code: കോഡ് നീക്കം ചെയ്യുക + confirm_delete: വൗച്ചർ നീക്കം ചെയ്യണമെന്ന് തീർച്ചയാണോ? + warning_forfeit_remaining_amount: "ശ്രദ്ധിക്കുക: നിങ്ങളുടെ ഓർഡറിന്റെ ആകെ തുക നിങ്ങളുടെ വൗച്ചറിനേക്കാൾ കുറവാണെങ്കിൽ, നിങ്ങൾക്ക് ശേഷിക്കുന്ന മൂല്യം ചെലവഴിക്കാൻ കഴിഞ്ഞേക്കില്ല." + step3: + delivery_details: + title: ഡെലിവറി വിശദാംശങ്ങൾ + edit: എഡിറ്റ് ചെയ്യുക + address: ഡെലിവറി വിലാസം + instructions: നിർദ്ദേശങ്ങൾ + payment_method: + title: പണമടയ്ക്കൽ രീതി + edit: എഡിറ്റ് ചെയ്യുക + instructions: നിർദ്ദേശങ്ങൾ + order: + title: ഓർഡർ വിശദാംശങ്ങൾ + edit: എഡിറ്റ് ചെയ്യുക + terms_and_conditions: + message_html: "ഞാൻ വിൽപ്പനക്കാരന്റെ %{terms_and_conditions_link} അംഗീകരിക്കുന്നു." + link_text: "ഉപാധികളും നിബന്ധനകളും" + platform_terms_of_service: + message_html: "ഞാൻ പ്ലാറ്റ്‌ഫോം %{tos_link} അംഗീകരിക്കുന്നു." + all_terms_and_conditions: + message_html: "വിൽപ്പനക്കാരന്റെ %{terms_and_conditions_link} , പ്ലാറ്റ്‌ഫോം %{tos_link} എന്നിവ ഞാൻ അംഗീകരിക്കുന്നു." + terms_and_conditions: "ഉപാധികളും നിബന്ധനകളും" + submit: ഓർഡർ പൂർത്തിയാക്കുക + cancel: പേയ്‌മെന്റ് രീതിയിലേക്ക് മടങ്ങുക + errors: + saving_failed: "സേവ് ചെയ്യുന്നത് പരാജയപ്പെട്ടു, ഹൈലൈറ്റ് ചെയ്ത ഫീൽഡുകൾ അപ്ഡേറ്റ് ചെയ്യുക." + terms_not_accepted: ദയവായി നിബന്ധനകളും വ്യവസ്ഥകളും അംഗീകരിക്കുക + required: ഫീൽഡ് ശൂന്യമാക്കാൻ കഴിയില്ല + invalid_number: "ദയവായി സാധുതയുള്ള ഒരു ഫോൺ നമ്പർ നൽകുക" + invalid_email: "സാധുതയുള്ള ഒരു ഇമെയിൽ വിലാസം നൽകുക" + select_a_shipping_method: ഒരു ഷിപ്പിംഗ് രീതി തിരഞ്ഞെടുക്കുക + select_a_payment_method: ഒരു പേയ്‌മെന്റ് രീതി തിരഞ്ഞെടുക്കുക + no_shipping_methods_available: ഷിപ്പിംഗ് ഓപ്ഷനുകൾ ഇല്ലാത്തതിനാൽ ചെക്ക്ഔട്ട് സാധ്യമല്ല. കട ഉടമയുമായി ബന്ധപ്പെടുക. + voucher_not_found: കണ്ടെത്തിയില്ല + add_voucher_error: വൗച്ചർ ചേർക്കുമ്പോൾ ഒരു തകരാറ് ഉണ്ടായി + shops: + hubs: + show_closed_shops: "അടച്ച കടകൾ കാണിക്കുക" + hide_closed_shops: "അടഞ്ഞുകിടക്കുന്ന കടകൾ മറയ്ക്കുക" + show_on_map: "മാപ്പിൽ എല്ലാം കാണിക്കുക" + shared: + mailers: + powered_by: + open_food_network: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക്" + powered_html: "താങ്കൾക്ക് ഷോപ്പിംഗ് അനുഭവം നൽകുന്നത് %{open_food_network} ആണ്." + menu: + cart: + cart: "കാർട്ട്" + cart_sidebar: + checkout: "ചെക്ക് ഔട്ട്" + edit_cart: "കാർട്ട് എഡിറ്റ് ചെയ്യുക" + items_in_cart_singular: "നിങ്ങളുടെ കാർട്ടിലെ %{num} ഇനം" + items_in_cart_plural: "നിങ്ങളുടെ കാർട്ടിലെ %{num} ഇനങ്ങൾ" + close: "അടയ്ക്കുക" + cart_empty: "നിങ്ങളുടെ കാർട്ട് ശൂന്യമാണ്" + take_me_shopping: "എന്നെ ഷോപ്പിംഗിന് കൊണ്ടുപോകൂ!" + signed_in: + profile: "പ്രൊഫൈൽ" + mobile_menu: + cart: "കാർട്ട്" + register_call: + selling_on_ofn: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ കയറാൻ താൽപ്പര്യമുണ്ടോ?" + register: "ഇവിടെ രജിസ്റ്റർ ചെയ്യുക" + footer: + footer_secure: "സുരക്ഷിതവും വിശ്വസ്തവും." + footer_secure_text: "നിങ്ങളുടെ ഷോപ്പിംഗ്, പേയ്‌മെന്റ് വിവരങ്ങൾ സ്വകാര്യമായി സൂക്ഷിക്കാൻ ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് എല്ലായിടത്തും എസ്.എസ്.എൽ എൻക്രിപ്ഷൻ (2048 ബിറ്റ് ആർ.എസ്.എ) ഉപയോഗിക്കുന്നു. ഞങ്ങളുടെ സെർവറുകൾ നിങ്ങളുടെ ക്രെഡിറ്റ് കാർഡ് വിശദാംശങ്ങൾ ശേഖരിക്കുന്നില്ല, പേയ്‌മെന്റുകൾ പ്രോസസ്സ് ചെയ്യുന്നത് പിസിഐ-കംപ്ലയിന്റ് സേവനങ്ങളാണ്." + footer_contact_headline: "ബന്ധം പുലർത്തുക" + footer_contact_email: "ഞങ്ങൾക്ക് ഇമെയിൽ ചെയ്യുക" + footer_nav_headline: "നാവിഗേറ്റ് ചെയ്യുക" + footer_join_headline: "ഞങ്ങൾക്കൊപ്പം ചേരുക" + footer_join_body: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ ഒരു ലിസ്‌റ്റിംഗ്, ഷോപ്പ് അല്ലെങ്കിൽ ഗ്രൂപ്പ് ഡയറക്‌ടറി സൃഷ്‌ടിക്കുക." + footer_join_cta: "എന്നോട് കൂടുതൽ പറയൂ!" + footer_legal_call: "വായിക്കുക" + footer_legal_visit: "ഞങ്ങളെ ഇതിൽ കണ്ടെത്തൂ" + footer_legal_text_html: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഒരു സ്വതന്ത്ര ഓപ്പൺ സോഴ്‌സ് സോഫ്റ്റ്‌വെയർ പ്ലാറ്റ്‌ഫോമാണ്. ഞങ്ങളുടെ ഉള്ളടക്കം %{content_license} ഉപയോഗിച്ചും ഞങ്ങളുടെ കോഡ് %{code_license} ഉപയോഗിച്ചും ലൈസൻസുള്ളതാണ്." + footer_data_text_with_privacy_policy_html: "നിങ്ങളുടെ ഡാറ്റ ഞങ്ങൾ നന്നായി പരിപാലിക്കുന്നു. ഞങ്ങളുടെ %{privacy_policy} , %{cookies_policy} എന്നിവ കാണുക" + footer_data_text_without_privacy_policy_html: "നിങ്ങളുടെ ഡാറ്റ ഞങ്ങൾ നന്നായി പരിപാലിക്കുന്നു. ഞങ്ങളുടെ %{cookies_policy} കാണുക" + footer_data_privacy_policy: "സ്വകാര്യതാ നയം" + footer_data_cookies_policy: "കുക്കീസ് നയം" + shop: + messages: + customer_required: + login: "ലോഗിൻ" + contact: "ബന്ധപ്പെടുക" + require_customer_login: "അംഗീകൃത ഉപഭോക്താക്കൾക്ക് മാത്രമേ ഈ ഷോപ്പിൽ പ്രവേശിക്കാൻ കഴിയൂ." + require_login_link_html: "നിങ്ങൾ ഇതിനകം അംഗീകൃത ഉപഭോക്താവാണെങ്കിൽ, തുടരാൻ %{login} ചെയ്യുക." + require_login_2_html: "ഇവിടെ ഷോപ്പിംഗ് തുടങ്ങണോ? ദയവായി, ചേരുന്നതിനെക്കുറിച്ച് %{contact} %{enterprise} -ൽ ചോദിക്കുക." + require_customer_html: "നിങ്ങൾക്ക് ഇവിടെ ഷോപ്പിംഗ് ആരംഭിക്കാൻ താൽപ്പര്യമുണ്ടെങ്കിൽ, ദയവായി ചേരുന്നതിനെക്കുറിച്ച് %{contact}%{enterprise} -ൽ ചോദിക്കുക." + select_oc: + select_oc_html: "ഏതൊക്കെ ഉൽപ്പന്നങ്ങൾ ലഭ്യമാണെന്ന് കാണുന്നതിന്, നിങ്ങളുടെ ഓർഡർ എപ്പോൾ വേണമെന്ന് തിരഞ്ഞെടുക്കുക." + products: + summary: + bulk: "ബൾക്ക്" + card_could_not_be_updated: കാർഡ് അപ്ഡേറ്റ് ചെയ്യാൻ കഴിഞ്ഞില്ല + card_could_not_be_saved: കാർഡ് സേവ് ചെയ്യാൻ കഴിഞ്ഞില്ല + spree_gateway_error_flash_for_checkout: "നിങ്ങളുടെ പേയ്‌മെന്റ് വിവരങ്ങളിൽ ഒരു പ്രശ്‌നമുണ്ടായി: %{error}" + invoice_billing_address: "ബില്ലിംഗ് വിലാസം:" + invoice_column_tax: "ജി.എസ്.ടി" + invoice_column_price: "വില" + invoice_column_item: "ഇനം" + invoice_column_qty: "അളവ്" + invoice_column_weight_volume: "ഭാരം / വ്യാപ്തം" + invoice_column_unit_price_with_taxes: "യൂണിറ്റ് വില (നികുതി ഉൾപ്പെടെ)" + invoice_column_unit_price_without_taxes: "യൂണിറ്റ് വില (നികുതി ഒഴികെ)" + invoice_column_price_with_taxes: "മൊത്തം വില (നികുതി ഉൾപ്പെടെ)" + invoice_column_price_without_taxes: "മൊത്തം വില (നികുതി ഒഴികെ)" + invoice_column_price_per_unit_without_taxes: "ഓരോ യൂണിറ്റിനും വില (നികുതി ഒഴികെ)" + invoice_column_tax_rate: "നികുതി നിരക്ക്" + invoice_tax_total: "ജി.എസ്.ടി ആകെ:" + invoice_cancel_and_replace_invoice: "ഇൻവോയ്സ് റദ്ദാക്കുകയും പകരം വയ്ക്കുകയും ചെയ്യുന്നു" + tax_invoice: "നികുതി ഇൻവോയ്സ്" + tax_total: "മൊത്തം നികുതി (%{rate}):" + invoice_shipping_category_delivery: "ഡെലിവറി" + invoice_shipping_category_pickup: "പിക്ക്അപ്പ്" + total_excl_tax: "ആകെ (നികുതി ഒഴികെ):" + total_incl_tax: "ആകെ (നികുതി ഉൾപ്പെടെ):" + total_all_tax: "മൊത്തം നികുതി:" + abn: "എബിഎൻ:" + acn: "എസിഎൻ:" + invoice_issued_on: "ഇൻവോയ്സ് ഇഷ്യൂ ചെയ്തത്:" + order_number: "ഓർഡർ നമ്പർ:" + date_of_transaction: "ഇടപാട് തീയതി:" + menu_1_title: "കടകൾ" + menu_1_url: "/കടകൾ" + menu_2_title: "മാപ്പ്" + menu_2_url: "/മാപ്പ്" + menu_3_title: "പ്രൊഡ്യൂസഴ്സ്" + menu_3_url: "/പ്രൊഡ്യൂസഴ്സ്" + menu_4_title: "ഗ്രൂപ്പുകൾ" + menu_4_url: "/ഗ്രൂപ്പുകൾ" + menu_5_title: "കുറിച്ച്" + menu_5_url: "https://about.openfoodnetwork.org.au/" + menu_6_title: "ബന്ധിപ്പിക്കുക" + menu_6_url: "https://openfoodnetwork.org/au/connect/" + menu_7_title: "പഠിക്കുക" + menu_7_url: "https://openfoodnetwork.org/au/learn/" + logo: "ലോഗോ (640x130)" + logo_mobile: "മൊബൈൽ ലോഗോ (75x26)" + logo_mobile_svg: "മൊബൈൽ ലോഗോ (എസ് വി ജി)" + home_hero: "ഹീറോ ചിത്രം" + home_show_stats: "സ്ഥിതിവിവരക്കണക്കുകൾ കാണിക്കുക" + footer_logo: "ലോഗോ (220x76)" + footer_facebook_url: "ഫേസ്ബുക് യുആർഎൽ" + footer_twitter_url: "ട്വിറ്റർ യുആർഎൽ" + footer_instagram_url: "ഇൻസ്റ്റാഗ്രാം യുആർഎൽ" + footer_linkedin_url: "ലിങ്ക്ഡ്ഇൻ യുആർഎൽ" + footer_googleplus_url: "ഗൂഗിൾ പ്ലസ് യുആർഎൽ" + footer_pinterest_url: "പിന്റെരെസ്റ് യുആർഎൽ" + footer_email: "ഇമെയിൽ" + footer_links_md: "ലിങ്കുകൾ" + footer_about_url: "കുറിച്ച് യുആർഎൽ" + user_guide_link: "ഉപയോക്തൃ ഗൈഡ് ലിങ്ക്" + name: പേര് + first_name: പേരിന്റെ ആദ്യഭാഗം + last_name: പേരിന്റെ അവസാന ഭാഗം + email: ഇമെയിൽ + phone: ഫോൺ + next: അടുത്തത് + address: വിലാസം + address_placeholder: ഉദാ. 123 ഹൈ സ്ട്രീറ്റ് + address2: വിലാസം (തുടർച്ച) + city: നഗരം + city_placeholder: ഉദാ. നോർത്ത്കോട്ടെ + latitude: അക്ഷാംശം + latitude_placeholder: ഉദാ. -37.4713077 + latitude_longitude_tip: മാപ്പിൽ നിങ്ങളുടെ എന്റർപ്രൈസ് പ്രദർശിപ്പിക്കുന്നതിന് അക്ഷാംശവും രേഖാംശവും ആവശ്യമാണ്. + longitude: രേഖാംശം + longitude_placeholder: ഉദാ. 144.7851531 + use_geocoder: വിലാസത്തിൽ നിന്ന് സ്വയമേവ അക്ഷാംശവും രേഖാംശവും കണക്കാക്കണോ? + state: സ്റ്റേറ്റ് + postcode: പിൻ കോഡ് + postcode_placeholder: ഉദാ. 3070 + suburb: നഗരപ്രാന്തം + country: രാജ്യം + unauthorized: അനധികൃതം + terms_of_service: "സേവന നിബന്ധനകൾ" + on_demand: ആവശ്യപ്പെടുന്നതനുസരിച്ച് + not_allowed: അനുവദനീയമല്ല + no_shipping: ഷിപ്പിംഗ് രീതികളൊന്നുമില്ല + no_payment: പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല + no_shipping_or_payment: ഷിപ്പിംഗ് അല്ലെങ്കിൽ പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല + unconfirmed: സ്ഥിരീകരിച്ചിട്ടില്ല + days: ദിവസങ്ങൾ + authorization_failure: "പ്രവേശനാനുമതി പരാജയപ്പെട്ടു" + description: "വിവരണം" + label_shop: "കട" + label_shops: "കടകൾ" + label_map: "മാപ്പ്" + label_producer: "പ്രൊഡ്യൂസർ" + label_producers: "പ്രൊഡ്യൂസേഴ്‌സ്" + label_groups: "ഗ്രൂപ്പുകൾ" + label_about: "കുറിച്ച്" + label_blog: "ബ്ലോഗ്" + label_support: "പിന്തുണ" + label_shopping: "ഷോപ്പിംഗ്" + label_login: "ലോഗിൻ" + label_logout: "ലോഗൗട്ട്" + label_signup: "സൈൻ അപ്പ് ചെയ്യുക" + label_administration: "ഭരണകൂടം" + label_admin: "അഡ്മിൻ" + label_account: "അക്കൗണ്ട്" + label_more: "കൂടുതൽ കാണിക്കുക" + label_less: "കുറച്ചുമാത്രം കാണിക്കുക" + label_notices: "അറിയിപ്പുകൾ" + cart_items: "ഇനങ്ങൾ" + cart_headline: "നിങ്ങളുടെ ഷോപ്പിംഗ് കാർട്ട്" + total: "ആകെ" + cart_updating: "കാർട്ട് അപ്ഡേറ്റ് ചെയ്യുന്നു..." + cart_empty: "കാർട്ട് ശൂന്യം" + cart_edit: "നിങ്ങളുടെ കാർട്ട് എഡിറ്റ് ചെയ്യുക" + item: "ഇനം" + qty: "അളവ്" + card_number: കാർഡ് നമ്പർ + card_securitycode: "സുരക്ഷാ കോഡ്" + card_expiry_date: കാലഹരണപ്പെടുന്ന തീയതി + card_masked_digit: "എക്സ്" + card_expiry_abbreviation: "Exp" + new_credit_card: "പുതിയ ക്രെഡിറ്റ് കാർഡ്" + my_credit_cards: എന്റെ ക്രെഡിറ്റ് കാർഡുകൾ + add_new_credit_card: പുതിയ ക്രെഡിറ്റ് കാർഡ് ചേർക്കുക + saved_cards: സേവ് ചെയ്യപ്പെട്ട കാർഡുകൾ + add_a_card: ഒരു കാർഡ് ചേർക്കുക + add_card: കാർഡ് ചേർക്കുക + you_have_no_saved_cards: നിങ്ങൾ ഇതുവരെ കാർഡുകളൊന്നും സേവ് ചെയ്തിട്ടില്ല + saving_credit_card: ക്രെഡിറ്റ് കാർഡ് സേവ് ചെയ്യുന്നു... + card_has_been_removed: "നിങ്ങളുടെ കാർഡ് നീക്കം ചെയ്‌തു (നമ്പർ: %{number})" + card_could_not_be_removed: ക്ഷമിക്കണം, കാർഡ് നീക്കം ചെയ്യാൻ കഴിഞ്ഞില്ല + invalid_credit_card: "അസാധുവായ ക്രെഡിറ്റ് കാർഡ്" + legal: + cookies_policy: + header: "ഞങ്ങൾ എങ്ങനെ കുക്കികൾ ഉപയോഗിക്കുന്നു" + desc_part_1: "നിങ്ങൾ ചില വെബ്‌സൈറ്റുകൾ സന്ദർശിക്കുമ്പോൾ നിങ്ങളുടെ കമ്പ്യൂട്ടറിൽ സംഭരിക്കുന്ന വളരെ ചെറിയ ടെക്‌സ്‌റ്റ് ഫയലുകളാണ് കുക്കികൾ." + desc_part_2: "ഓ.എഫ്.എൻ -ൽ ഞങ്ങൾ നിങ്ങളുടെ സ്വകാര്യതയെ പൂർണ്ണമായും മാനിക്കുന്നു. ഓൺലൈനായി ആഹാരം വിൽക്കുന്നതിനോ വാങ്ങുന്നതിനോ ഉള്ള സേവനം നിങ്ങൾക്ക് എത്തിക്കുന്നതിന് ആവശ്യമായ കുക്കികൾ മാത്രമാണ് ഞങ്ങൾ ഉപയോഗിക്കുന്നത്. നിങ്ങളുടെ ഡാറ്റ ഒന്നും തന്നെ ഞങ്ങൾ വിൽക്കില്ല. ആവാസവ്യവസ്ഥയ്ക്ക് ഉപയോഗപ്രദമായ (ഹ്രസ്വ ആഹാര സംവിധാനങ്ങൾക്കുള്ള ലോജിസ്റ്റിക് സേവനങ്ങൾ പോലെ) പുതിയ സഹകരണ സേവനങ്ങൾ നിർമ്മിക്കുന്നതിന് നിങ്ങളുടെ ഡാറ്റയിൽ ചിലത് പങ്കിടാൻ ഭാവിയിൽ ഞങ്ങൾ നിങ്ങളോട് നിർദ്ദേശിച്ചേക്കാം, എന്നാൽ ഞങ്ങൾ ഇതുവരെ അത്രത്തോളം എത്തിയിട്ടില്ല, നിങ്ങളുടെ മുൻ‌കൂർ അനുമതി ഇല്ലാതെ ഞങ്ങൾ അത് ചെയ്യില്ല :-)" + desc_part_3: "നിങ്ങൾ സേവനത്തിലേക്ക് 'ലോഗിൻ' ചെയ്‌താൽ നിങ്ങൾ ആരാണെന്ന് ഓർമ്മിക്കുന്നതിനോ അല്ലെങ്കിൽ നിങ്ങൾ ലോഗിൻ ചെയ്‌തിട്ടില്ലെങ്കിൽപ്പോലും നിങ്ങളുടെ കാർട്ടിൽ ഇട്ടിരിക്കുന്ന ഇനങ്ങൾ ഓർമ്മിക്കുന്നതിനോ ഞങ്ങൾ പ്രധാനമായും കുക്കികൾ ഉപയോഗിക്കുന്നു. നിങ്ങൾ വെബ്‌സൈറ്റിൽ ക്ലിക്ക് ചെയ്യാതെ നാവിഗേറ്റ് ചെയ്യുന്നത് തുടരുകയാണെങ്കിൽ \"കുക്കികൾ സ്വീകരിക്കുക\" വെബ്‌സൈറ്റിന്റെ പ്രവർത്തനത്തിന് അത്യന്താപേക്ഷിതമായ കുക്കികൾ സംഭരിക്കുന്നതിന് നിങ്ങൾ ഞങ്ങൾക്ക് സമ്മതം നൽകുന്നുവെന്ന് ഞങ്ങൾ അനുമാനിക്കുന്നു. ഞങ്ങൾ ഉപയോഗിക്കുന്ന കുക്കികളുടെ ലിസ്റ്റ് ഇതാ!" + essential_cookies: "അവശ്യ കുക്കികൾ" + essential_cookies_desc: "ഞങ്ങളുടെ വെബ്‌സൈറ്റിന്റെ പ്രവർത്തനത്തിന് ഇനിപ്പറയുന്ന കുക്കികൾ കർശനമായി ആവശ്യമാണ്." + essential_cookies_note: "മിക്ക കുക്കികളിലും ഒരു വിശിഷ്ടമായ ഐഡന്റിഫയർ മാത്രമേ അടങ്ങിയിട്ടുള്ളൂ, എന്നാൽ മറ്റ് ഡാറ്റയില്ല, അതിനാൽ നിങ്ങളുടെ ഇമെയിൽ വിലാസവും പാസ്‌വേഡും ഒരിക്കലും അവയിൽ ഉണ്ടാകില്ല, അതൊരിക്കലും വെളിപ്പെടുകയും ഇല്ല." + cookie_domain: "സജ്ജീകരിച്ചത്:" + cookie_session_desc: "പേജ് സന്ദർശനങ്ങൾക്കിടയിൽ ഉപയോക്താക്കളെ ഓർമ്മിക്കാൻ വെബ്‌സൈറ്റിനെ അനുവദിക്കാൻ ഉപയോഗിക്കുന്നു, ഉദാഹരണത്തിന്, നിങ്ങളുടെ കാർട്ടിലെ ഇനങ്ങൾ ഓർമ്മിക്കുന്നത്." + cookie_consent_desc: "കുക്കികൾ സംഭരിക്കുന്നതിനുള്ള ഉപയോക്തൃ സമ്മതത്തിന്റെ സ്ഥിതി നിലനിർത്താൻ ഉപയോഗിക്കുന്നു" + cookie_remember_me_desc: "ഉപയോക്താവ് അയാളെ ഓർത്തിരിക്കാൻ വെബ്‌സൈറ്റിനോട് അഭ്യർത്ഥിച്ചിട്ടുണ്ടെങ്കിൽ ഉപയോഗിക്കുന്നു. 12 ദിവസത്തിന് ശേഷം ഈ കുക്കി സ്വയമേവ ഇല്ലാതാക്കപ്പെടും. ഒരു ഉപയോക്താവെന്ന നിലയിൽ ആ കുക്കി ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, നിങ്ങൾ ലോഗ്ഔട്ട് ചെയ്താൽ മാത്രം മതി. നിങ്ങളുടെ കമ്പ്യൂട്ടറിൽ ആ കുക്കി ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നില്ലെങ്കിൽ, ലോഗിൻ ചെയ്യുമ്പോൾ \"എന്നെ ഓർമ്മിക്കുക\" ചെക്ക്ബോക്സിൽ ക്ലിക്ക് ചെയ്യരുത്." + cookie_openstreemap_desc: "ഞങ്ങളുടെ സൗഹൃദ ഓപ്പൺ സോഴ്‌സ് മാപ്പിംഗ് ദാതാവ് (ഓപ്പൺ സ്ട്രീറ്റ്മാപ്പ്) അവരുടെ സേവനങ്ങളുടെ ദുരുപയോഗം തടയുന്നതിന്, ഒരു നിശ്ചിത കാലയളവിൽ വളരെയധികം അഭ്യർത്ഥനകൾ സ്വീകരിക്കുന്നില്ലെന്ന് ഉറപ്പാക്കാൻ ഉപയോഗിക്കുന്നു." + cookie_stripe_desc: "തട്ടിപ്പ് കണ്ടെത്തുന്നതിനായി ഞങ്ങളുടെ പേയ്‌മെന്റ് പ്രോസസ്സർ സ്ട്രൈപ്പ് ശേഖരിച്ച ഡാറ്റ https://stripe.com/cookies-policy/legal. എല്ലാ ഷോപ്പുകളും സ്ട്രൈപ്പ് ഒരു പേയ്‌മെന്റ് രീതിയായി ഉപയോഗിക്കുന്നില്ല, എന്നാൽ ഇത് എല്ലാ പേജുകളിലും പ്രയോഗിക്കുന്നത് വഞ്ചന തടയുന്നതിനുള്ള ഒരു നല്ല സമ്പ്രദായമാണ്. സാധാരണയായി ഞങ്ങളുടെ പേജുകളിൽ ഏതൊക്കെ അവരുടെ എപിഐ-യുമായി ഇടപഴകുന്നു എന്നതിന്റെ ഒരു ചിത്രം സ്ട്രൈപ്പ് നിർമ്മിക്കുകയും തുടർന്ന് അസാധാരണമായ എന്തെങ്കിലും ഉണ്ടെങ്കിൽ ഫ്ലാഗ് ചെയ്യുകയും ചെയ്യും. അതിനാൽ സ്ട്രൈപ്പ് കുക്കി സജ്ജീകരിക്കുന്നത് ഒരു ഉപയോക്താവിന് ഒരു പേയ്‌മെന്റ് രീതി നൽകുന്നതിനേക്കാൾ വിശാലമായ സേവനമാണ്. ഇത് നീക്കം ചെയ്യുന്നത് സേവനത്തിന്റെ സുരക്ഷയെ തന്നെ ബാധിക്കും. https://stripe.com/privacy എന്നതിൽ നിങ്ങൾക്ക് സ്ട്രൈപ്പിനെക്കുറിച്ച് കൂടുതലറിയാനും അതിന്റെ സ്വകാര്യതാ നയം വായിക്കാനും കഴിയും." + statistics_cookies: "സ്ഥിതിവിവരക്കണക്ക് കുക്കികൾ" + statistics_cookies_desc: "ഇനിപ്പറയുന്നവ കർശനമായി ആവശ്യമില്ല, എന്നാൽ ഉപയോക്തൃ പെരുമാറ്റം വിശകലനം ചെയ്യുന്നതിനും നിങ്ങൾ ഏറ്റവും കൂടുതൽ ഉപയോഗിക്കുന്നതോ ഉപയോഗിക്കാത്തതോ ആയ ഫീച്ചറുകൾ തിരിച്ചറിയുന്നതിനും ഉപയോക്തൃ അനുഭവ പ്രശ്‌നങ്ങൾ മനസ്സിലാക്കുന്നതിനും ഞങ്ങളെ അനുവദിച്ചുകൊണ്ട് മികച്ച ഉപയോക്തൃ അനുഭവം നിങ്ങൾക്ക് നൽകാൻ സഹായിക്കുന്നു." + statistics_cookies_matomo_desc_html: "പ്ലാറ്റ്‌ഫോം ഉപയോഗ ഡാറ്റ ശേഖരിക്കുന്നതിനും വിശകലനം ചെയ്യുന്നതിനും, ജിഡിപിആർ അനുസരിച്ചുള്ളതും നിങ്ങളുടെ സ്വകാര്യത പരിരക്ഷിക്കുന്നതുമായ ഒരു ഓപ്പൺ സോഴ്‌സ് അനലിറ്റിക്‌സ് ഉപകരണമായ മറ്റോമോ(എക്സ് പിവിക്) ഞങ്ങൾ ഉപയോഗിക്കുന്നു." + statistics_cookies_matomo_optout: "നിങ്ങൾക്ക് മറ്റോമോ അനലിറ്റിക്‌സ് ഒഴിവാക്കണോ? ഞങ്ങൾ വ്യക്തിഗത വിവരങ്ങളൊന്നും ശേഖരിക്കുന്നില്ല, ഞങ്ങളുടെ സേവനം മെച്ചപ്പെടുത്താൻ മറ്റോമോ ഞങ്ങളെ സഹായിക്കുന്നു, എന്നാൽ നിങ്ങളുടെ ഇഷ്ടം ഞങ്ങൾ മാനിക്കുന്നു :-)" + cookie_matomo_basics_desc: "സ്ഥിതിവിവരക്കണക്കുകൾ ശേഖരിക്കാൻ മറ്റോമോ ഫസ്റ്റ് പാർട്ടി കുക്കികൾ." + cookie_matomo_heatmap_desc: "മറ്റോമോ ഹീറ്റ്മാപ് & സെഷൻ റെക്കോർഡിങ് കുക്കി." + cookie_matomo_ignore_desc: "ട്രാക്ക് ചെയ്യപ്പെടുന്നതിൽ നിന്ന് ഉപയോക്താവിനെ ഒഴിവാക്കാൻ ഉപയോഗിക്കുന്ന കുക്കി." + disabling_cookies_header: "കുക്കികൾ പ്രവർത്തനരഹിതമാക്കുന്നതിനുള്ള മുന്നറിയിപ്പ്" + disabling_cookies_desc: "ഒരു ഉപയോക്താവെന്ന നിലയിൽ, നിങ്ങളുടെ ബ്രൗസറിന്റെ ക്രമീകരണ നിയന്ത്രണത്തിലൂടെ നിങ്ങൾക്ക് ആവശ്യമുള്ളപ്പോഴെല്ലാം ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിന്റെയോ മറ്റേതെങ്കിലും വെബ്‌സൈറ്റ് കുക്കികളോ അനുവദിക്കാനോ തടയാനോ ഇല്ലാതാക്കാനോ കഴിയും. ഓരോ ബ്രൗസറിനും ഓരോ ഓപ്പറേറ്റീവ് ഉണ്ട്. ലിങ്കുകൾ ഇതാ:" + disabling_cookies_firefox_link: "https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences" + disabling_cookies_chrome_link: "https://support.google.com/chrome/answer/95647" + disabling_cookies_ie_link: "https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/en-ww/cookies/" + disabling_cookies_note: "എന്നാൽ ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഉപയോഗിക്കുന്ന അവശ്യ കുക്കികൾ നിങ്ങൾ ഇല്ലാതാക്കുകയോ പരിഷ്‌ക്കരിക്കുകയോ ചെയ്‌താൽ, വെബ്‌സൈറ്റ് പ്രവർത്തിക്കില്ല എന്ന് അറിഞ്ഞിരിക്കുക, ഉദാഹരണത്തിന് ചെക്ക്ഔട്ട് ചെയ്യാനോ നിങ്ങളുടെ കാർട്ടിലേക്ക് ഒന്നും ചേർക്കാനോ നിങ്ങൾക്ക് കഴിയില്ല." + cookies_banner: + cookies_usage: "നിങ്ങളുടെ നാവിഗേഷൻ സുഗമവും സുരക്ഷിതവുമാക്കുന്നതിനും, ഞങ്ങൾ ഓഫർ ചെയ്യുന്ന ഫീച്ചറുകൾ മെച്ചപ്പെടുത്തുന്നതിന് നിങ്ങൾ അത് എങ്ങനെ ഉപയോഗിക്കുന്നുവെന്ന് മനസ്സിലാക്കുന്നതിനും ഈ സൈറ്റ് കുക്കികൾ ഉപയോഗിക്കുന്നു." + cookies_definition: "നിങ്ങൾ ചില വെബ്‌സൈറ്റുകൾ സന്ദർശിക്കുമ്പോൾ നിങ്ങളുടെ കമ്പ്യൂട്ടറിൽ സംഭരിക്കുന്ന വളരെ ചെറിയ ടെക്‌സ്‌റ്റ് ഫയലുകളാണ് കുക്കികൾ." + cookies_desc: "ഓൺലൈനായി ആഹാരസാധനങ്ങൾ വിൽക്കുന്നതിനോ വാങ്ങുന്നതിനോ ഉള്ള സേവനം നിങ്ങൾക്ക് എത്തിക്കുന്നതിന് ആവശ്യമായ കുക്കികൾ മാത്രമാണ് ഞങ്ങൾ ഉപയോഗിക്കുന്നത്. നിങ്ങളുടെ ഡാറ്റ ഒന്നുംതന്നെ ഞങ്ങൾ വിൽക്കില്ല. നിങ്ങൾ സേവനത്തിലേക്ക് 'ലോഗിൻ' ചെയ്‌താൽ നിങ്ങൾ ആരാണെന്ന് ഓർമ്മിക്കുന്നതിനോ അല്ലെങ്കിൽ നിങ്ങൾ ലോഗിൻ ചെയ്‌തിട്ടില്ലെങ്കിൽപ്പോലും നിങ്ങളുടെ കാർട്ടിൽ ഇട്ടിരിക്കുന്ന ഇനങ്ങൾ ഓർമ്മിക്കുന്നതിനോ ഞങ്ങൾ പ്രധാനമായും കുക്കികൾ ഉപയോഗിക്കുന്നു. നിങ്ങൾ വെബ്‌സൈറ്റിൽ \"കുക്കികൾ സ്വീകരിക്കുക\" എന്ന ടാബ് ക്ലിക്ക് ചെയ്യാതെ നാവിഗേറ്റ് ചെയ്യുന്നത് തുടരുകയാണെങ്കിൽ വെബ്‌സൈറ്റിന്റെ പ്രവർത്തനത്തിന് അത്യന്താപേക്ഷിതമായ കുക്കികൾ സംഭരിക്കുന്നതിന് നിങ്ങൾ ഞങ്ങൾക്ക് സമ്മതം നൽകുന്നുവെന്ന് ഞങ്ങൾ അനുമാനിക്കുന്നു." + cookies_policy_link_desc: "നിങ്ങൾക്ക് കൂടുതലറിയണമെങ്കിൽ, പരിശോധിക്കുക" + cookies_policy_link: "കുക്കീസ് നയം" + cookies_accept_button: "കുക്കികൾ സ്വീകരിക്കുക" + home_shop: ഇപ്പോൾ ഷോപ്പുചെയ്യുക + brandstory_headline: "ഓർഗാനിക് & ലോക്കൽ ഫുഡ് വാങ്ങുക." + brandstory_intro: "പ്രാദേശിക കർഷകരെയും ഭക്ഷ്യ ഉൽപ്പാദകരെയും തിരയുക." + brandstory_part1: "ദോഷകരമായ രാസവസ്തുക്കൾ ഇല്ലാതെ സുസ്ഥിരമായ കൃഷിരീതികൾ ഉപയോഗിച്ച് ഭക്ഷണം ഉത്പാദിപ്പിക്കുന്ന ആയിരക്കണക്കിന് കർഷകരിൽ നിന്നും ഭക്ഷ്യ ഉൽപാദകരിൽ നിന്നും തിരഞ്ഞെടുക്കുക." + brandstory_part2: "പ്രാദേശിക കർഷകരിൽ നിന്ന് നിങ്ങളുടെ ഭക്ഷണം വാങ്ങുമ്പോൾ, നിങ്ങൾ പ്രാദേശിക സമ്പദ്‌വ്യവസ്ഥയെ പിന്തുണയ്ക്കുന്നു." + brandstory_part3: "മൊത്തക്കച്ചവടക്കാർ, ഫാർമർ പ്രൊഡ്യൂസർ ഓർഗനൈസേഷനുകൾ (എഫ്‌പി‌ഒ), അല്ലെങ്കിൽ ഫാർമർ പ്രൊഡ്യൂസർ കമ്പനികൾ (എഫ്‌പിസി) എന്നിവയ്‌ക്ക് അവരുടെ നിലവിലുള്ള സംവിധാനങ്ങളുമായി ഓഎഫ്എൻ സമന്വയിപ്പിക്കാനും ഞങ്ങളുടെ ദേശീയ ഭക്ഷ്യ ഹബ്ബുകളും ഷോപ്പുകളും വഴി ഉപഭോക്താക്കൾക്ക് അവരുടെ ഉൽപ്പന്നങ്ങൾ വിതരണം ചെയ്യുന്നതിനായി ഉപഭോക്തൃ ഗ്രൂപ്പുകളെ നിയന്ത്രിക്കാനും കഴിയും." + brandstory_part4: "കർഷകർക്ക് അവരുടെ ഉൽപന്നങ്ങൾ ഓൺലൈനിൽ വിൽക്കാൻ സഹായിക്കുന്നതിന് ലാഭേച്ഛയില്ലാതെ പ്രവർത്തിക്കുന്ന ഒരു സ്ഥാപനമാണ് ഈ സോഫ്റ്റ്‌വെയർ വികസിപ്പിച്ചിരിക്കുന്നത്." + brandstory_part5_strong: "ഞങ്ങൾ അതിനെ \"ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഇന്ത്യ\" അല്ലെങ്കിൽ \"OFN ഇന്ത്യ\" എന്ന് വിളിക്കുന്നു." + brandstory_part6: "നിങ്ങൾ നല്ല ഭക്ഷണം വിൽക്കുകയാണെങ്കിൽ - ഒരു കർഷകൻ, കർഷക വിപണി, ഫുഡ് കോ-ഓപ്പ് അല്ലെങ്കിൽ ഫുഡ് ഹബ്ബ് എന്ന നിലയിൽ- OFN ഇന്ത്യയാണ് നിങ്ങളുടെ ഏറ്റവും മികച്ച ഓപ്ഷൻ." + system_headline: "ഓഎഫ്എൻ -ൽ സാധനങ്ങൾ വിൽക്കണോ- 3 എളുപ്പ ഘട്ടങ്ങൾ" + system_step1: "1. പുതിയ അക്കൗണ്ട് ഉണ്ടാക്കുക" + system_step1_text: "പേര്, വിവരണം, ഫോട്ടോകൾ, ബന്ധപ്പെടാനുള്ള വിശദാംശങ്ങൾ, സോഷ്യൽ മീഡിയ ലിങ്കുകൾ എന്നിവ ഉപയോഗിച്ച് നിങ്ങളുടെ എന്റർപ്രൈസ് സജ്ജീകരിക്കുക." + system_step2: "2. നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ കൂട്ടിച്ചേർക്കുക" + system_step2_text: "നിങ്ങളുടെ ഷോപ്പിലേക്ക് ഉൽപ്പന്നങ്ങൾ കൂട്ടിച്ചേർക്കുക - നിങ്ങളുടെ സ്വന്തമോ കൂടാതെ/അല്ലെങ്കിൽ നിങ്ങൾക്ക് ചുറ്റുമുള്ള മറ്റ് പ്രൊഡ്യൂസർമാരിൽ നിന്ന്. ചിത്രങ്ങൾ, വിവരണങ്ങൾ, വിലകൾ, സ്റ്റോക്ക് ലെവലുകൾ, എങ്ങനെവേണമെങ്കിലും ഉള്ള തൂക്കങ്ങൾ അളവുകൾ എന്നിവ സജ്ജമാക്കുക." + system_step3: "3. നിങ്ങളുടെ ഡെലിവറികൾ ആസൂത്രണം ചെയ്യുക" + system_step3_text: "പേയ്‌മെന്റ് രീതികൾ സജ്ജീകരിക്കുക. ഒന്നിലധികം പിക്ക്-അപ്പ് പോയിന്റുകളും ഡെലിവറി വിശദാംശങ്ങളും രൂപപ്പെടുത്തുക. ആവർത്തിച്ചുള്ള ഓർഡറുകളും പതിവ് വിതരണങ്ങളും ഉണ്ടാക്കുക." + cta_headline: "ഭാവിയിലെ സുസ്ഥിര ഭക്ഷ്യ ശൃംഖല." + cta_label: "ഞാൻ തയാറാണ്" + stats_headline: "ഞങ്ങൾ ഒരു പുതിയ ആഹാര സമ്പ്രദായം സൃഷ്ടിക്കുകയാണ്." + stats_producers: "ഭക്ഷ്യ ഉത്പാദകർ" + stats_shops: "ഫുഡ് ഷോപ്പുകൾ" + stats_shoppers: "ആഹാര ഉപഭോക്താക്കൾ" + stats_orders: "ആഹാര ഓർഡറുകൾ" + checkout_title: ചെക്ക് ഔട്ട് + checkout_now: ഇപ്പോൾ ചെക്ക്ഔട്ട് ചെയ്യുക + checkout_order_ready: ഓർഡർ തയ്യാറാണ് + checkout_hide: മറയ്ക്കുക + checkout_expand: വികസിപ്പിക്കുക + checkout_headline: "ശരി, ചെക്ക്ഔട്ടിന് തയ്യാറാണോ?" + checkout_as_guest: "അതിഥിയായി ചെക്ക്ഔട്ട് ചെയ്യുക" + checkout_details: "നിങ്ങളുടെ വിശദാംശങ്ങൾ" + checkout_billing: "ബിൽ വിവരങ്ങൾ" + checkout_default_bill_address: "സ്ഥിരസ്ഥിതി ബില്ലിംഗ് വിലാസമായി സേവ് ചെയ്യുക" + checkout_shipping: ഷിപ്പിംഗ് വിവരം + checkout_default_ship_address: "സ്ഥിരസ്ഥിതി ഷിപ്പിംഗ് വിലാസമായി സേവ് ചെയ്യുക" + checkout_method_free: സൗജന്യം + checkout_address_same: ഷിപ്പിംഗ് വിലാസവും ബില്ലിംഗ് വിലാസവും ഒന്നുതന്നെയാണോ? + checkout_ready_for: "തയ്യാറാണ്:" + checkout_instructions: "എന്തെങ്കിലും അഭിപ്രായങ്ങളോ പ്രത്യേക നിർദ്ദേശങ്ങളോ ഉണ്ടോ?" + checkout_payment: പേയ്മെന്റ് + checkout_send: ഇപ്പോൾ ഓർഡർ ചെയ്യുക + checkout_your_order: നിങ്ങളുടെ ഓർഡർ + checkout_cart_total: കാർട്ട് മൊത്തം + checkout_shipping_price: ഷിപ്പിംഗ് + checkout_total_price: ആകെ + checkout_back_to_cart: "കാർട്ടിലേക്ക് മടങ്ങുക" + cost_currency: "ചെലവ് കറൻസി" + order_paid: പണം നൽകി + order_not_paid: പണം നൽകിയിട്ടില്ല + order_total: ആകെ ഓർഡർ + order_payment: "ഇതുവഴി പണമടയ്ക്കുന്നു:" + no_payment_required: "പണം നൽകേണ്ടതില്ല" + order_billing_address: ബില്ലിംഗ് വിലാസം + order_delivery_on: ഡെലിവറി തീയതി + order_delivery_address: ഡെലിവറി വിലാസം + order_delivery_time: ഡെലിവറി സമയം + order_special_instructions: "നിങ്ങളുടെ കുറിപ്പുകൾ:" + order_pickup_time: ശേഖരണത്തിന് തയ്യാറാണ് + order_pickup_instructions: ശേഖരണ നിർദ്ദേശങ്ങൾ + order_produce: ഉൽപ്പന്നം + order_amount_paid: അടച്ച തുക + order_total_price: ആകെ + order_balance_due: മിച്ച കടം + order_includes_tax: (നികുതി ഉൾപ്പെടുന്നു) + order_payment_paypal_successful: പേയ്യ്പാൽ വഴിയുള്ള നിങ്ങളുടെ പേയ്‌മെന്റ് വിജയകരമായി പ്രോസസ്സ് ചെയ്തു. + order_hub_info: ഹബ് വിവരം + order_back_to_store: സ്റ്റോറിലേക്ക് മടങ്ങുക + order_back_to_cart: കാർട്ടിലേക്ക് മടങ്ങുക + order_back_to_website: വെബ്‌സൈറ്റിലേക്ക് മടങ്ങുക + checkout_details_title: ചെക്ക്ഔട്ട് വിശദാംശങ്ങൾ + checkout_payment_title: ചെക്ക്ഔട്ട് പേയ്മെന്റ് + checkout_summary_title: ചെക്ക്ഔട്ട് സംഗ്രഹം + bom_tip: "ഒന്നിലധികം ഓർഡറുകളിലുടനീളം ഉൽപ്പന്നത്തിന്റെ അളവ് മാറ്റാൻ ഈ പേജ് ഉപയോഗിക്കുക. ആവശ്യമെങ്കിൽ, ഓർഡറുകളിൽ നിന്ന് ഉൽപ്പന്നങ്ങൾ പൂർണ്ണമായും നീക്കം ചെയ്യാം." + unsaved_changes_warning: "സേവ് ചെയ്യാത്ത മാറ്റങ്ങൾ നിലവിലുണ്ട്, നിങ്ങൾ തുടരുകയാണെങ്കിൽ അത് നഷ്‌ടമാകും." + unsaved_changes_error: "ചുവന്ന ബോർഡറുകളുള്ള ഫീൽഡുകളിൽ തെറ്റുകൾ ഉണ്ട്." + products: "ഉൽപ്പന്നങ്ങൾ" + products_in: "%{oc} -ൽ" + products_at: "%{distributor} -ൽ" + products_elsewhere: "ഉൽപ്പന്നങ്ങൾ മറ്റൊരിടത്ത് കണ്ടെത്തി" + email_confirmed: "നിങ്ങളുടെ ഇമെയിൽ വിലാസം സ്ഥിരീകരിച്ചതിന് നന്ദി." + email_confirmation_activate_account: "നിങ്ങളുടെ പുതിയ അക്കൗണ്ട് സജീവമാക്കുന്നതിന് മുമ്പ്, ഞങ്ങൾക്ക് നിങ്ങളുടെ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കേണ്ടതുണ്ട്." + email_confirmation_greeting: "ഹായ്, %{contact}!" + email_confirmation_profile_created: "%{name} -ന് വേണ്ടിയുള്ള ഒരു പ്രൊഫൈൽ വിജയകരമായി സൃഷ്ടിച്ചു! നിങ്ങളുടെ പ്രൊഫൈൽ സജീവമാക്കുന്നതിന് ഞങ്ങൾക്ക് ഈ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കേണ്ടതുണ്ട്." + email_confirmation_click_link: "നിങ്ങളുടെ ഇമെയിൽ സ്ഥിരീകരിക്കുന്നതിനും നിങ്ങളുടെ പ്രൊഫൈൽ സജ്ജീകരിക്കുന്നത് തുടരുന്നതിനും ചുവടെയുള്ള ലിങ്കിൽ ക്ലിക്കുചെയ്യുക." + email_confirmation_link_label: "ഈ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കുക »" + email_confirmation_help_html: "നിങ്ങളുടെ ഇമെയിൽ സ്ഥിരീകരിച്ച ശേഷം ഈ എന്റർപ്രൈസസിനായി നിങ്ങളുടെ അഡ്മിനിസ്ട്രേഷൻ അക്കൗണ്ട് ആക്സസ് ചെയ്യാൻ കഴിയും. %{sitename}-ന്റെ സവിശേഷതകളെ കുറിച്ച് കൂടുതൽ അറിയുന്നതിനും നിങ്ങളുടെ പ്രൊഫൈലോ ഓൺലൈൻ സ്റ്റോറോ ഉപയോഗിച്ച് തുടങ്ങുന്നതിനും %{link} കാണുക." + email_confirmation_notice_unexpected: "നിങ്ങൾ %{sitename} എന്നതിൽ സൈൻ അപ്പ് ചെയ്‌തതിനാലോ അല്ലെങ്കിൽ നിങ്ങൾക്ക് അറിയാവുന്ന ആരെങ്കിലും സൈൻ അപ്പ് ചെയ്യാൻ ക്ഷണിച്ചതിനാലോ നിങ്ങൾക്ക് ഈ സന്ദേശം ലഭിച്ചു. എന്തുകൊണ്ടാണ് നിങ്ങൾക്ക് ഈ ഇമെയിൽ ലഭിക്കുന്നതെന്ന് മനസ്സിലാകുന്നില്ലെങ്കിൽ, ദയവായി %{contact} ലേക്ക് എഴുതുക." + email_social: "ഞങ്ങളുമായി ബന്ധപ്പെടുക:" + email_contact: "ഞങ്ങൾക്ക് ഇമെയിൽ ചെയ്യുക:" + email_signoff: "ആശംസകൾ," + email_signature: "%{sitename} ടീം" + email_confirm_customer_greeting: "ഹായ് %{name} ," + email_confirm_customer_intro_html: "%{distributor} -ഇവിടെ ഷോപ്പിംഗ് നടത്തിയതിന് നന്ദി!" + email_confirm_customer_number_html: "# %{number} ഓർഡർ സ്ഥിരീകരണം" + email_confirm_customer_details_html: "%{distributor} -ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ വിശദാംശങ്ങൾ ഇതാ:" + email_confirm_customer_signoff: "വിശ്വസ്തതയോടെ," + email_confirm_shop_greeting: "ഹായ് %{name} ," + email_confirm_shop_order_html: "നന്നായി ചെയ്തു! %{distributor} നിങ്ങൾക്ക് ഒരു പുതിയ ഓർഡർ ഉണ്ട്!" + email_confirm_shop_number_html: "# %{number} ഓർഡർ സ്ഥിരീകരണം " + email_order_summary_item: "ഇനം" + email_order_summary_quantity: "അളവ്" + email_order_summary_sku: "എസ്.കെ.യു" + email_order_summary_price: "വില" + email_order_summary_subtotal: "അകെ തുക:" + email_order_summary_total: "ആകെ:" + email_order_summary_includes_tax: "(നികുതി ഉൾപ്പെടുന്നു):" + email_payment_paid: പണം നൽകി + email_payment_not_paid: പണം നൽകിയിട്ടില്ല + email_payment_description: ചെക്ക്ഔട്ടിലെ പേയ്മെന്റ് വിവരണം + email_payment_summary: പേയ്മെന്റ് സംഗ്രഹം + email_payment_method: "ഇതുവഴി പണമടയ്ക്കുന്നു:" + email_so_placement_intro_html: "നിങ്ങൾക്ക് %{distributor} -ന്റെ അടുത്തുനിന്ന് ഒരു പുതിയ ഓർഡർ ഉണ്ട്" + email_so_placement_details_html: "%{distributor} '-ന്റെ അടുത്തുനിന്നുള്ള നിങ്ങളുടെ ഓർഡറിന്റെ വിശദാംശങ്ങൾ ഇതാ:" + email_so_placement_changes: "നിർഭാഗ്യവശാൽ, നിങ്ങൾ അഭ്യർത്ഥിച്ച എല്ലാ ഉൽപ്പന്നങ്ങളും ലഭ്യമല്ല. നിങ്ങൾ അഭ്യർത്ഥിച്ച യഥാർത്ഥ അളവുകൾ ചുവടെ ക്രോസ്-ഔട്ട് ആയി കാണപ്പെടുന്നു." + email_so_payment_success_intro_html: "%{distributor} -ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡറിനായി ഒരു ഓട്ടോമാറ്റിക് പേയ്‌മെന്റ് പ്രോസസ്സ് ചെയ്തു." + email_so_placement_explainer_html: "ഈ ഓർഡർ നിങ്ങൾക്കായി സ്വയമേവ സൃഷ്ടിച്ചതാണ്." + email_so_edit_true_html: "നിങ്ങൾക്ക് %{orders_close_at} -ൽ ഓർഡറുകൾ സ്വീകരിക്കുന്നത് അവസാനിക്കുന്നത് വരെ മാറ്റങ്ങൾ വരുത്താം." + email_so_edit_false_html: "നിങ്ങൾക്ക് എപ്പോൾ വേണമെങ്കിലും ഈ ഓർഡറിന്റെ വിശദാംശങ്ങൾ കാണാൻ കഴിയും." + email_so_contact_distributor_html: "നിങ്ങൾക്ക് എന്തെങ്കിലും ചോദ്യങ്ങളുണ്ടെങ്കിൽ, നിങ്ങൾക്ക് %{email} വഴി %{distributor} -നെ ബന്ധപ്പെടാം." + email_so_contact_distributor_to_change_order_html: "ഈ ഓർഡർ നിങ്ങൾക്കായി സ്വയമേവ സൃഷ്ടിച്ചതാണ്. ഓർഡറുകൾ സ്വീകരിക്കുന്നത് അവസാനിക്കുന്ന %{orders_close_at} തീയതി വരെ%{email} ഉപയോഗിച്ചുകൊണ്ട് %{distributor} -നെ ബന്ധപ്പെട്ടുകൊണ്ട് നിങ്ങൾക്ക് ഓർഡറിൽ മാറ്റങ്ങൾ വരുത്താം." + email_so_confirmation_intro_html: "%{distributor} -ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ ഇപ്പോൾ സ്ഥിരീകരിച്ചു" + email_so_confirmation_explainer_html: "ഈ ഓർഡർ നിങ്ങൾക്കായി സ്വയമേവ ഉണ്ടാക്കിയതാണ്, അത് ഇപ്പോൾ അന്തിമമായിക്കഴിഞ്ഞു." + email_so_confirmation_details_html: "%{distributor} ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡറിനെ കുറിച്ച് നിങ്ങൾ അറിയേണ്ടതെല്ലാം ഇതാ:" + email_so_empty_intro_html: "ഞങ്ങൾ %{distributor} -ൽ നിന്ന് ഒരു പുതിയ ഓർഡർ നൽകാൻ ശ്രമിച്ചു, പക്ഷേ ചില തകരാറുകൾ ഉണ്ടായിരുന്നു..." + email_so_empty_explainer_html: "നിർഭാഗ്യവശാൽ, നിങ്ങൾ ഓർഡർ ചെയ്ത ഉൽപ്പന്നങ്ങളൊന്നും ലഭ്യമല്ല, അതിനാൽ ഓർഡർ നൽകിയിട്ടില്ല. നിങ്ങൾ അഭ്യർത്ഥിച്ച യഥാർത്ഥ അളവുകൾ ചുവടെ ക്രോസ്-ഔട്ട് ആയി കാണപ്പെടുന്നു." + email_so_empty_details_html: "%{distributor} -ൽ നിന്ന് സ്ഥാപിക്കാനാകാതെപോയ ഓർഡറിന്റെ വിശദാംശങ്ങൾ ഇതാ:" + email_so_failed_payment_intro_html: "ഞങ്ങൾ ഒരു പേയ്‌മെന്റ് പ്രോസസ്സ് ചെയ്യാൻ ശ്രമിച്ചു, പക്ഷേ ചില തകരാറുകൾ ഉണ്ടായിരുന്നു..." + email_so_failed_payment_explainer_html: "നിങ്ങളുടെ ക്രെഡിറ്റ് കാർഡിലെ ഒരു പ്രശ്നം കാരണം %{distributor} -ൽ നിന്നുള്ള നിങ്ങളുടെ സബ്‌സ്‌ക്രിപ്‌ഷന്റെ പേയ്‌മെന്റ് പരാജയപ്പെട്ടു. ഈ പരാജയപ്പെട്ട പേയ്‌മെന്റിനെക്കുറിച്ച് %{distributor} -നെ അറിയിച്ചു." + email_so_failed_payment_details_html: "പേയ്‌മെന്റ് ഗേറ്റ്‌വേ നൽകിയ പരാജയത്തിന്റെ വിശദാംശങ്ങൾ ഇതാ:" + email_shipping_delivery_details: ഡെലിവറി വിശദാംശങ്ങൾ + email_shipping_delivery_time: "ഡെലിവറി:" + email_shipping_delivery_address: "ഡെലിവറി വിലാസം:" + email_shipping_collection_details: ശേഖരണ വിശദാംശങ്ങൾ + email_shipping_collection_time: "ശേഖരണത്തിന് തയ്യാറാണ്:" + email_shipping_collection_instructions: "ശേഖരണ നിർദ്ദേശങ്ങൾ:" + email_special_instructions: "നിങ്ങളുടെ കുറിപ്പുകൾ:" + email_signup_greeting: ഹലോ! + email_signup_welcome: "%{sitename} -ലേക്ക് സ്വാഗതം!" + email_signup_confirmed_email: "നിങ്ങളുടെ ഇമെയിൽ സ്ഥിരീകരിച്ചതിന് നന്ദി." + email_signup_shop_html: "നിങ്ങൾക്ക് ഇപ്പോൾ %{link} -ൽ ലോഗിൻ ചെയ്യാം." + email_signup_text: "നെറ്റ്‌വർക്കിൽ ചേർന്നതിന് നന്ദി. നിങ്ങളൊരു ഉപഭോക്താവാണെങ്കിൽ, അതിശയകരമായ നിരവധി കർഷകരെയും അതിശയകരമായ ഫുഡ് ഹബ്ബുകളും രുചികരമായ ആഹാരത്തെയും പരിചയപ്പെടുത്താൻ ഞങ്ങൾ ആഗ്രഹിക്കുന്നു! നിങ്ങളൊരു പ്രൊഡ്യൂസറോ ഭക്ഷ്യ സംരംഭകനോ ആണെങ്കിൽ, നിങ്ങളെ നെറ്റ്‌വർക്കിന്റെ ഭാഗമാക്കുന്നതിൽ ഞങ്ങൾക്ക് സന്തോഷമുണ്ട്." + email_signup_help_html: "നിങ്ങളുടെ എല്ലാ ചോദ്യങ്ങളും ഫീഡ്‌ബാക്കും ഞങ്ങൾ സ്വാഗതം ചെയ്യുന്നു; നിങ്ങൾക്ക് സൈറ്റിലെ ഫീഡ്‌ബാക്ക് അയയ്‌ക്കുക ബട്ടൺ ഉപയോഗിക്കാം അല്ലെങ്കിൽ %{email} എന്ന വിലാസത്തിൽ ഞങ്ങൾക്ക് ഇമെയിൽ ചെയ്യാം" + invite_email: + greeting: "ഹലോ!" + invited_to_manage: "%{instance} ൽ %{enterprise} മാനേജ് ചെയ്യാൻ നിങ്ങളെ ക്ഷണിച്ചിരിക്കുന്നു." + confirm_your_email: "സ്ഥിരീകരണ ലിങ്കുള്ള ഒരു ഇമെയിൽ നിങ്ങൾക്ക് ലഭിച്ചിരിക്കണം അല്ലെങ്കിൽ ഉടൻ ലഭിക്കും. നിങ്ങളുടെ ഇമെയിൽ സ്ഥിരീകരിക്കുന്നത് വരെ നിങ്ങൾക്ക് %{enterprise} -ന്റെ പ്രൊഫൈൽ ആക്‌സസ് ചെയ്യാൻ കഴിയില്ല." + set_a_password: "എന്റർപ്രൈസ് നിയന്ത്രിക്കാൻ കഴിയുന്നതിന് മുമ്പ് ഒരു പാസ്‌വേഡ് സജ്ജീകരിക്കാൻ നിങ്ങളോട് ആവശ്യപ്പെടും." + mistakenly_sent: "എന്തുകൊണ്ടാണ് നിങ്ങൾക്ക് ഈ ഇമെയിൽ ലഭിച്ചതെന്ന് ഉറപ്പില്ലേ? കൂടുതൽ വിവരങ്ങൾക്ക് ദയവായി %{owner_email} എന്ന വിലാസത്തിൽ ബന്ധപ്പെടുക." + producer_mail_greeting: "പ്രിയ" + producer_mail_text_before: "ഓർഡർ സൈക്കിളിനെക്കുറിച്ചുള്ള ഒരു അപ്‌ഡേറ്റ് ചുവടെ കണ്ടെത്തുക:" + producer_mail_order_text: "നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾക്കായുള്ള ഓർഡറുകളുടെ ഒരു സംഗ്രഹം ഇതാ:" + producer_mail_delivery_instructions: "സ്റ്റോക്ക് പിക്കപ്പ്/ഡെലിവറി നിർദ്ദേശങ്ങൾ:" + producer_mail_signoff: "നന്ദിയും ആശംസകളും" + producer_mail_order_customer_text: "ഉപഭോക്താക്കൾ കൂട്ടമായി ഉണ്ടാക്കിയ ഓർഡറുകളുടെ ഒരു സംഗ്രഹം ഇതാ" + shopping_oc_closed: ഓർഡറുകൾ സ്വീകരിക്കുന്നില്ല + shopping_oc_closed_description: "അടുത്ത ഓർഡർ സ്വീകരിക്കുന്നത് വരെ കാത്തിരിക്കുക (അല്ലെങ്കിൽ വൈകിയ ഓർഡറുകൾ സ്വീകരിക്കാൻ കഴിയുമോ എന്നറിയാൻ ഞങ്ങളെ നേരിട്ട് ബന്ധപ്പെടുക)" + shopping_oc_last_closed: "അവസാന ഓർഡർ സൈക്കിൾ %{distance_of_time} മുമ്പ് അവസാനിച്ചു" + shopping_oc_next_open: "അടുത്ത ഓർഡർ സൈക്കിൾ %{distance_of_time} -ൽ ആരംഭിക്കും" + shopping_oc_select: "തിരഞ്ഞെടുക്കുക..." + shopping_tabs_home: "ഹോം" + shopping_tabs_shop: "കട" + shopping_tabs_about: "കുറിച്ച്" + shopping_tabs_producers: "പ്രൊഡ്യൂസേഴ്‌സ്" + shopping_tabs_contact: "ബന്ധപ്പെടുക" + shopping_tabs_groups: "ഗ്രൂപ്പുകൾ" + shopping_contact_address: "വിലാസം" + shopping_contact_web: "ബന്ധപ്പെടുക" + shopping_contact_social: "ഫോളോ ചെയ്യുക" + shopping_groups_part_of: "ഭാഗമാണ്:" + shopping_producers_of_hub: "%{hub} -ന്റെ പ്രൊഡ്യൂസേഴ്‌സ്:" + enterprises_next_closing: "അടുത്ത ഓർഡർ ക്ലോസിംഗ്" + enterprises_currently_open: "ഓർഡറുകൾ നിലവിൽ സ്വീകരിക്കുന്നു" + enterprises_ready_for: "തയ്യാറാണ്" + enterprises_choose: "നിങ്ങളുടെ ഓർഡർ എപ്പോൾ വേണമെന്ന് തിരഞ്ഞെടുക്കുക:" + maps_open: "തുറന്നിരിക്കുന്നു" + maps_closed: "അടച്ചു" + map_title: "മാപ്പ്" + hubs_buy: "ഇതിനായി ഷോപ്പുചെയ്യുക:" + hubs_shopping_here: "ഇവിടെ ഷോപ്പിംഗ് ചെയ്യുക" + hubs_orders_closed: "ഓർഡറുകൾ അടച്ചു" + hubs_profile_only: "പ്രൊഫൈൽ മാത്രം" + hubs_delivery_options: "ഡെലിവറി ഓപ്ഷനുകൾ" + hubs_pickup: "പിക്ക്അപ്പ്" + hubs_delivery: "ഡെലിവറി" + hubs_producers: "ഞങ്ങളുടെ പ്രൊഡ്യൂസേഴ്‌സ്" + hubs_filter_by: "ഇതനുസരിച്ച് ഫിൽട്ടർ ചെയ്യുക" + hubs_filter_type: "ഇനം" + hubs_filter_delivery: "ഡെലിവറി" + hubs_filter_property: "സാധനം" + hubs_matches: "ഇതാണോ നിങ്ങൾ അർത്ഥമാക്കുന്നത്?" + hubs_intro: നിങ്ങളുടെ പ്രാദേശിക പ്രദേശത്ത് ഷോപ്പുചെയ്യുക + hubs_distance: ഏറ്റവും അടുത്തത് + hubs_distance_filter: "%{location} -ന്റെ സമീപമുള്ള ഷോപ്പുകൾ എന്നെ കാണിക്കൂ" + shop_changeable_orders_alert_html: + one: %{shop}/%{order} -ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ അവലോകനത്തിനായി ലഭ്യമാണ്. നിങ്ങൾക്ക് %{oc_close} വരെ അതിൽ മാറ്റങ്ങൾ വരുത്താം. + few: നിങ്ങൾക്ക് %{shop} - -ൽ നിന്നുള്ള %{count} ഓർഡറുകൾ നിലവിൽ അവലോകനത്തിനായി ലഭ്യമാണ്. നിങ്ങൾക്ക് %{oc_close} വരെ അതിൽ മാറ്റങ്ങൾ വരുത്താം. + many: നിങ്ങൾക്ക് %{shop} -ൽ നിന്നുള്ള %{count}ഓർഡറുകൾ നിലവിൽ അവലോകനത്തിനായി ലഭ്യമാണ്. നിങ്ങൾക്ക് %{oc_close} വരെ അതിൽ മാറ്റങ്ങൾ വരുത്താം. + other: നിങ്ങൾക്ക് %{shop} -ൽ നിന്നുള്ള %{count} ഓർഡറുകൾ നിലവിൽ അവലോകനത്തിനായി ലഭ്യമാണ്. നിങ്ങൾക്ക് %{oc_close} വരെ അതിൽ മാറ്റങ്ങൾ വരുത്താം. + orders_changeable_orders_alert_html: ഈ ഓർഡർ സ്ഥിരീകരിച്ചു, എന്നാൽ നിങ്ങൾക്ക് %{oc_close} വരെ അതിൽ മാറ്റങ്ങൾ വരുത്താം. + products_clear: ക്ലിയർ + products_showing: "കാണിക്കുന്നു:" + products_results_for: "ഇതിനായുള്ള ഫലങ്ങൾ" + products_or: "അഥവാ" + products_and: "ഒപ്പം" + products_filters_in: "ഇൻ" + products_with: കൂടെ + products_search: "തിരയുക..." + products_filter_by: "ഇതനുസരിച്ച് ഫിൽട്ടർ ചെയ്യുക" + products_filter_selected: "തിരഞ്ഞെടുത്തു" + products_filter_heading: "ഫിൽട്ടറുകൾ" + products_filter_clear: "ക്ലിയർ" + products_filter_done: "ചെയ്തു" + products_loading: "ഉൽപ്പന്നങ്ങൾ ലോഡുചെയ്യുന്നു..." + products_updating_cart: "കാർട്ട് അപ്ഡേറ്റ് ചെയ്യുന്നു..." + products_cart_empty: "കാർട്ട് ശൂന്യം" + products_edit_cart: "നിങ്ങളുടെ കാർട്ട് എഡിറ്റ് ചെയ്യുക" + products_from: നിന്ന് + products_change: "സേവ് ചെയ്യാൻ മാറ്റങ്ങളൊന്നുമില്ല." + products_update_error: "ഇനിപ്പറയുന്ന തകരാറ്(കൾ) മൂലം സേവ് ചെയ്യുന്നത് പരാജയപ്പെട്ടു:" + products_update_error_msg: "സേവ് ചെയ്യുന്നത് പരാജയപ്പെട്ടു." + products_update_error_data: "അസാധുവായ ഡാറ്റ മൂലം സേവ് ചെയ്യുന്നത് പരാജയപ്പെട്ടു:" + products_changes_saved: "മാറ്റങ്ങൾ സേവ് ചെയ്തു." + products_no_results_html: "ക്ഷമിക്കണം, %{query} എന്നതിന് ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല" + products_clear_search: "തിരയൽ മായ്‌ക്കുക" + search_no_results_html: "ക്ഷമിക്കണം, %{query} ന് ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല. മറ്റൊരു തിരയൽ പരീക്ഷിക്കണോ?" + components_profiles_popover: "പ്രൊഫൈലുകൾക്ക് ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ ഷോപ്പ് ഫ്രണ്ട് ഇല്ല, എന്നാൽ മറ്റെവിടെയെങ്കിലും സ്വന്തം അല്ലെങ്കിൽ ഓൺലൈൻ ഷോപ്പ് ഉണ്ടായിരിക്കാം" + components_profiles_show: "പ്രൊഫൈലുകൾ കാണിക്കുക" + components_filters_nofilters: "ഫിൽട്ടറുകൾ ഇല്ല" + components_filters_clearfilters: "എല്ലാ ഫിൽട്ടറുകളും മായ്‌ക്കുക" + groups_title: ഗ്രൂപ്പുകൾ + groups_headline: ഗ്രൂപ്പുകൾ / പ്രദേശങ്ങൾ + groups_text: "ഓരോ പ്രൊഡ്യൂസറും അതുല്യരാണ്. ഓരോ ബിസിനസ്സും വ്യത്യസ്തമായ എന്തെങ്കിലും വാഗ്ദാനം ചെയ്യുന്നു. ലൊക്കേഷൻ, കർഷക വിപണി അല്ലെങ്കിൽ തത്ത്വചിന്ത എന്നിങ്ങനെ പൊതുവായ എന്തെങ്കിലും പങ്കിടുന്ന പ്രൊഡ്യൂസേഴ്‌സ്, ഹബുകൾ, വിതരണക്കാർ എന്നിവരുടെ കൂട്ടായ്മകളാണ് ഞങ്ങളുടെ ഗ്രൂപ്പുകൾ. ഇത് നിങ്ങളുടെ ഷോപ്പിംഗ് അനുഭവം എളുപ്പമാക്കുന്നു. അതിനാൽ നിങ്ങൾക്കായി പ്രത്യേകം തയ്യാറാക്കിയ ഞങ്ങളുടെ ഗ്രൂപ്പുകൾ പരിശോധിക്കുക." + groups_search: "പേര് അല്ലെങ്കിൽ കീവേഡ് തിരയുക" + groups_no_groups: "ഗ്രൂപ്പുകളൊന്നും കണ്ടെത്തിയില്ല" + groups_about: "ഞങ്ങളേക്കുറിച്ച്" + groups_producers: "ഞങ്ങളുടെ പ്രൊഡ്യൂസേഴ്‌സ്" + groups_hubs: "ഞങ്ങളുടെ ഹബ്ബുകൾ" + groups_contact_web: ബന്ധപ്പെടുക + groups_contact_social: പിന്തുടരുക + groups_contact_address: വിലാസം + groups_contact_email: ഞങ്ങൾക്ക് ഇമെയിൽ ചെയ്യുക + groups_contact_website: ഞങ്ങളുടെ വെബ്സൈറ്റ് സന്ദർശിക്കുക + groups_contact_facebook: ഞങ്ങളെ ഫേസ്ബുക്കിൽ ഫോളോ ചെയ്യുക + groups_signup_title: ഒരു ഗ്രൂപ്പായി സൈൻ അപ്പ് ചെയ്യുക + groups_signup_headline: ഗ്രൂപ്പുകൾ സൈൻ അപ്പ് + groups_signup_intro: "സഹകരണ വിപണനത്തിനുള്ള ഒരു അത്ഭുതകരമായ പ്ലാറ്റ്‌ഫോമാണ് ഞങ്ങൾ, നിങ്ങളുടെ അംഗങ്ങൾക്കും ഓഹരി ഉടമകൾക്കും പുതിയ വിപണികളിലെത്താനുള്ള എളുപ്പവഴി. ഞങ്ങൾ ലാഭരഹിതവും താങ്ങാനാവുന്നതും ലളിതവുമാണ്." + groups_signup_email: ഞങ്ങൾക്ക് ഇമെയിൽ ചെയ്യുക + groups_signup_motivation1: ഞങ്ങൾ ഭക്ഷ്യ സമ്പ്രദായങ്ങളെ ന്യായമായി പരിവർത്തനം ചെയ്യുന്നു. + groups_signup_motivation2: അതാണ് ഞങ്ങളുടെ പ്രചോദനം.. ഞങ്ങൾ ഓപ്പൺ സോഴ്‌സ് കോഡ് അടിസ്ഥാനമാക്കി പ്രവർത്തിക്കുന്ന, ലാഭേച്ഛയില്ലാത്ത ഒരു ആഗോള സംസ്ഥാപനം ആണ്. ഞങ്ങൾ ന്യായമായി പ്രവർത്തിക്കുന്നു. നിങ്ങൾക്ക് എല്ലായ്പ്പോഴും ഞങ്ങളെ വിശ്വസിക്കാം. + groups_signup_motivation3: നിങ്ങൾക്ക് വലിയ ആശയങ്ങളുണ്ടെന്ന് ഞങ്ങൾക്കറിയാം, സഹായിക്കാൻ ഞങ്ങൾ ആഗ്രഹിക്കുന്നു. ഞങ്ങളുടെ അറിവും നെറ്റ്‌വർക്കുകളും ഉറവിടങ്ങളും ഞങ്ങൾ പങ്കിടും. ഒറ്റപ്പെടൽ ഒരു മാറ്റവും സൃഷ്ടിക്കില്ലെന്ന് ഞങ്ങൾക്കറിയാം, അതിനാൽ ഞങ്ങൾ നിങ്ങളുടെ പങ്കാളികളാകും. + groups_signup_motivation4: നിങ്ങൾ എവിടെയായിരുന്നാലും ഞങ്ങൾ നിങ്ങളെ കണ്ടുമുട്ടും. + groups_signup_motivation5: നിങ്ങൾ ഫുഡ് ഹബ്ബുകൾ, പ്രൊഡ്യൂസേഴ്‌സ്, അല്ലെങ്കിൽ വിതരണക്കാർ, ഒരു വ്യവസായ സ്ഥാപനം അല്ലെങ്കിൽ ഒരു പ്രാദേശിക ഗവൺമെന്റ് എന്നിവയുടെ ഒരു സഖ്യം ആയിരിക്കാം. + groups_signup_motivation6: നിങ്ങളുടെ പ്രാദേശിക ആഹാര പ്രസ്ഥാനത്തിൽ നിങ്ങളുടെ പങ്ക് എന്തായാലും, സഹായിക്കാൻ ഞങ്ങൾ തയ്യാറാണ്. എന്നിരുന്നാലും, നിങ്ങളുടെ ലോകത്തിന്റെ ഭാഗത്ത് ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് എങ്ങനെയിരിക്കും അല്ലെങ്കിൽ എന്താണ് ചെയ്യുന്നതെന്നറിഞ്ഞ് നിങ്ങൾ ആശ്ചര്യപ്പെടും, നമുക്ക് സംസാരിച്ചാലോ. + groups_signup_motivation7: ഞങ്ങൾ ആഹാര ചലനങ്ങളെ കൂടുതൽ യുക്തിസഹമാക്കുന്നു. + groups_signup_motivation8: നിങ്ങളുടെ നെറ്റ്‌വർക്കുകൾ സജീവമാക്കുകയും പ്രവർത്തനക്ഷമമാക്കുകയും ചെയ്യേണ്ടതുണ്ട്, സംഭാഷണത്തിനും പ്രവർത്തനത്തിനുമുള്ള ഒരു പ്ലാറ്റ്ഫോം ഞങ്ങൾ വാഗ്ദാനം ചെയ്യുന്നു. നിങ്ങൾക്ക് യഥാർത്ഥ ഇടപെടൽ ആവശ്യമാണ്. എല്ലാ ഉപയോക്താക്കളിലേക്കും എല്ലാ പങ്കാളികളിലേക്കും എല്ലാ മേഖലകളിലേക്കും എത്തിച്ചേരാൻ ഞങ്ങൾ സഹായിക്കും. + groups_signup_motivation9: നിങ്ങൾക്ക് റിസോഴ്‌സിംഗ് ആവശ്യമാണ്. ഞങ്ങളുടെ എല്ലാ അനുഭവങ്ങളും ഞങ്ങൾ പങ്കുവയ്ക്കും. നിങ്ങളുടെ സഹകരണം വേണം. സമചിന്താഗതിക്കാരുടെ ഒരു ആഗോള ശൃംഖലയിലേക്ക് നിങ്ങളെ ഞങ്ങൾ ബന്ധിപ്പിക്കുന്നതാണ് നല്ലത്. + groups_signup_pricing: ഗ്രൂപ്പ് അക്കൗണ്ട് + groups_signup_studies: കേസ് പഠനങ്ങൾ + groups_signup_contact: ചർച്ചയ്ക്ക് തയ്യാറാണോ? + groups_signup_contact_text: "ഓഎഫ്എൻ-ന് നിങ്ങൾക്കായി എന്തുചെയ്യാനാകുമെന്ന് കണ്ടെത്താൻ ബന്ധപ്പെടുക:" + groups_signup_detail: "വിശദാംശം ഇതാ." + login_invalid: "അസാധുവായ ഇമെയിൽ അല്ലെങ്കിൽ പാസ്സ്‌വേർഡ്" + producers_about: ഞങ്ങളേക്കുറിച്ച് + producers_buy: ഇത് വാങ്ങുക + producers_contact: ബന്ധപ്പെടുക + producers_contact_phone: വിളിക്കുക + producers_contact_social: ഫോളോ ചെയ്യുക + producers_buy_at_html: "%{enterprise} -ൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ ഇവിടെ വാങ്ങുക:" + producers_filter: ഇതനുസരിച്ച് ഫിൽട്ടർ ചെയ്യുക + producers_filter_type: ഇനം + producers_filter_property: സാധനം + producers_title: പ്രൊഡ്യൂസേഴ്‌സ് + producers_headline: പ്രാദേശിക പ്രൊഡ്യൂസേഴ്‌സിനെ കണ്ടെത്തുക + producers_signup_title: ഒരു പ്രൊഡ്യൂസറായി സൈൻ അപ്പ് ചെയ്യുക + producers_signup_headline: ഭക്ഷ്യ ഉൽപാദകർ, ശാക്തീകരിക്കപ്പെട്ടു. + producers_signup_motivation: നിങ്ങളുടെ ആഹാരം വിൽക്കുകയും, വൈവിധ്യമാർന്ന പുതിയ വിപണികളിലേക്ക് നിങ്ങളുടെ കഥകൾ പറയുകയും ചെയ്യുക. ഓരോ ചെലവുകളിലും സമയവും പണവും ലാഭിക്കുക. അപകടസാധ്യതയില്ലാതെ ഞങ്ങൾ നവീകരണത്തെ പിന്തുണയ്ക്കുന്നു. ഞങ്ങൾ ഈ കളിക്കളത്തെ സമനിലയിലാക്കി. + producers_signup_send: ഇപ്പോൾ ചേരുക + producers_signup_enterprise: എന്റർപ്രൈസ് അക്കൗണ്ടുകൾ + producers_signup_studies: ഞങ്ങളുടെ പ്രൊഡ്യൂസേഴ്‌സിൽ നിന്നുള്ള കഥകൾ. + producers_signup_cta_headline: ഇപ്പോൾ ചേരുക! + producers_signup_cta_action: ഇപ്പോൾ ചേരുക + producers_signup_detail: വിശദാംശം ഇതാ. + producer: പ്രൊഡ്യൂസർ + products_item: ഇനം + products_description: വിവരണം + products_variant: വേരിയന്റ് + products_quantity: അളവ് + products_available: ലഭ്യമാണോ? + products_producer: "പ്രൊഡ്യൂസർ" + products_price: "വില" + name_or_sku: "പേര് അല്ലെങ്കിൽ എസ്കെയു " + register_title: രജിസ്റ്റർ ചെയ്യുക + sell_title: "രജിസ്റ്റർ ചെയ്യുക" + sell_headline: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് നേടൂ!" + sell_motivation: "നിങ്ങളുടെ മനോഹരമായ ആഹാരസാധനം പ്രദർശിപ്പിക്കുക." + sell_producers: "പ്രൊഡ്യൂസേഴ്‌സ്" + sell_hubs: "ഹബ്ബുകൾ" + sell_groups: "ഗ്രൂപ്പുകൾ" + sell_producers_detail: "മിനിറ്റുകൾക്കുള്ളിൽ ഓഎഫ്എൻ-ൽ നിങ്ങളുടെ ബിസിനസ്സിനായി ഒരു പ്രൊഫൈൽ സജ്ജീകരിക്കുക. ഏത് സമയത്തും നിങ്ങൾക്ക് നിങ്ങളുടെ പ്രൊഫൈൽ ഒരു ഓൺലൈൻ സ്റ്റോറിലേക്ക് അപ്‌ഗ്രേഡ് ചെയ്യാനും നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ നേരിട്ട് ഉപഭോക്താക്കൾക്ക് വിൽക്കാനും കഴിയും." + sell_hubs_detail: "ഓഎഫ്എൻ-ൽ നിങ്ങളുടെ ഫുഡ് എന്റർപ്രൈസസിനോ ഓർഗനൈസേഷനോ വേണ്ടി ഒരു പ്രൊഫൈൽ സജ്ജീകരിക്കുക. ഏത് സമയത്തും നിങ്ങൾക്ക് നിങ്ങളുടെ പ്രൊഫൈൽ ഒരു മൾട്ടി പ്രൊഡ്യൂസർ ഷോപ്പിലേക്ക് അപ്‌ഗ്രേഡ് ചെയ്യാം." + sell_groups_detail: "നിങ്ങളുടെ പ്രദേശത്തിനോ നിങ്ങളുടെ സ്ഥാപനത്തിനോ വേണ്ടി എന്റർപ്രൈസസിന്റെ (നിർമ്മാതാക്കളും മറ്റ് ഭക്ഷ്യ സംരംഭങ്ങളും) ഒരു പ്രത്യേക ഡയറക്ടറി സജ്ജീകരിക്കുക." + sell_user_guide: "ഞങ്ങളുടെ ഉപയോക്തൃ ഗൈഡിൽ കൂടുതൽ കണ്ടെത്തുക." + sell_listing_price: "ഓഎഫ്എൻ-ൽ ലിസ്റ്റിംഗ് സൗജന്യമാണ്. ഓഎഫ്എൻ-ൽ ഒരു ഷോപ്പ് തുറക്കുന്നതും പ്രവർത്തിപ്പിക്കുന്നതും പ്രതിമാസ വിൽപ്പനയുടെ $500 വരെ സൗജന്യമാണ്. നിങ്ങൾ കൂടുതൽ വിൽക്കുകയാണെങ്കിൽ, വിൽപ്പനയുടെ 1% മുതൽ 3% വരെ നിങ്ങളുടെ കമ്മ്യൂണിറ്റി വരിസംഖ്യ തിരഞ്ഞെടുക്കാം. വിലനിർണ്ണയത്തെക്കുറിച്ചുള്ള കൂടുതൽ വിശദാംശങ്ങൾക്ക് മുകളിലെ മെനുവിലെ വിവര ലിങ്ക് വഴി സോഫ്‌റ്റ്‌വെയർ പ്ലാറ്റ്‌ഫോം വിഭാഗം സന്ദർശിക്കുക." + sell_embed: "നിങ്ങളുടെ സ്വന്തം ഇഷ്‌ടാനുസൃതമാക്കിയ വെബ്‌സൈറ്റിൽ ഞങ്ങൾക്ക് ഒരു ഓഎഫ്എൻ ഷോപ്പ് ഉൾപ്പെടുത്താം അല്ലെങ്കിൽ നിങ്ങളുടെ പ്രദേശത്തിനായി ഒരു ഇഷ്‌ടാനുസൃത പ്രാദേശിക ഫുഡ് നെറ്റ്‌വർക്ക് വെബ്‌സൈറ്റ് നിർമ്മിക്കാനും കഴിയും." + sell_ask_services: "ഓഎഫ്എൻ സേവനങ്ങളെക്കുറിച്ച് ഞങ്ങളോട് ചോദിക്കുക." + shops_title: കടകൾ + shops_headline: ഷോപ്പിംഗ്, രൂപാന്തരപ്പെട്ടു. + shops_text: ആഹാരം ഒരു കാലചക്രത്തിൽ വളരുന്നു, കർഷകർ കാലചക്രങ്ങളിൽ വിളവെടുക്കുന്നു, നമ്മൾ കാലചക്രങ്ങളിൽ ആഹാരം ഓർഡർ ചെയ്യുന്നു. ഒരു ഓർഡർ സൈക്കിൾ അടച്ചതായി നിങ്ങൾ കണ്ടെത്തുകയാണെങ്കിൽ, പിന്നീട് വീണ്ടും പരിശോധിക്കുക. + shops_signup_title: ഒരു ഹബ്ബായി സൈൻ അപ്പ് ചെയ്യുക + shops_signup_headline: ഭക്ഷണ കേന്ദ്രങ്ങൾ, പരിധിയില്ലാത്തത്. + shops_signup_motivation: നിങ്ങളുടെ മാതൃക എന്തായാലും ഞങ്ങൾ നിങ്ങളെ പിന്തുണയ്ക്കുന്നു. നിങ്ങൾ മാറിയാലും ഞങ്ങൾ നിങ്ങളോടൊപ്പമുണ്ട്. ഞങ്ങൾ ലാഭേച്ഛയില്ലാത്തവരും സ്വതന്ത്രരും ഓപ്പൺ സോഴ്‌സ് ചെയ്യുന്നവരുമാണ്. നിങ്ങൾ സ്വപ്നം കണ്ട സോഫ്റ്റ്‌വെയർ പങ്കാളികൾ ഞങ്ങളാണ്. + shops_signup_action: ഇപ്പോൾ ചേരുക + shops_signup_pricing: എന്റർപ്രൈസ് അക്കൗണ്ടുകൾ + shops_signup_stories: ഞങ്ങളുടെ ഹബ്ബുകളിൽ നിന്നുള്ള കഥകൾ. + shops_signup_help: സഹായിക്കാൻ ഞങ്ങൾ തയ്യാറാണ്. + shops_signup_help_text: നിങ്ങൾക്ക് ഒരു മികച്ച പ്രതിഫലം ആവശ്യമാണ്. നിങ്ങൾക്ക് പുതിയ ഉപഭോക്താക്കളേയും ലോജിസ്റ്റിക്സ് പങ്കാളികളെയും ആവശ്യമുണ്ട്. മൊത്തവ്യാപാരം, ചില്ലറ വിൽപ്പന, ആഹാരം എന്നിവയിലുടനീളം നിങ്ങളുടെ കഥ പറയേണ്ടതുണ്ട്. + shops_signup_detail: വിശദാംശം ഇതാ. + orders: "ഓർഡറുകൾ" + orders_fees: "ഫീസ്..." + orders_edit_title: "ഷോപ്പിംഗ് കാർട്ട്" + orders_edit_headline: "നിങ്ങളുടെ ഷോപ്പിംഗ് കാർട്ട്" + orders_edit_time: "ഓർഡർ തയ്യാറാണ്" + orders_edit_continue: "ഷോപ്പിംഗ് തുടരുക" + orders_edit_checkout: "ചെക്ക് ഔട്ട്" + orders_form_empty_cart: "കാർട്ട് ശൂന്യമാണ്" + orders_form_update_cart: "അപ്ഡേറ്റ് ചെയ്യുക" + orders_form_subtotal: "പ്രൊഡ്യൂസ് ആകെ" + orders_form_total: "ആകെ" + orders_oc_expired_headline: "ഈ ഓർഡർ സൈക്കിളിനുള്ള ഓർഡറുകൾ അവസാനിച്ചു" + orders_oc_expired_text: "ക്ഷമിക്കണം, ഈ ഓർഡർ സൈക്കിളിനുള്ള ഓർഡറുകൾ %{time} മുമ്പ് അടച്ചു! വൈകിയുള്ള ഓർഡറുകൾ സ്വീകരിക്കാനാകുമോ എന്നറിയാൻ നിങ്ങളുടെ ഹബ്ബുമായി നേരിട്ട് ബന്ധപ്പെടുക." + orders_oc_expired_text_others_html: "ക്ഷമിക്കണം, ഈ ഓർഡർ സൈക്കിളിനുള്ള ഓർഡറുകൾ %{time} മുമ്പ് അടച്ചു! വൈകിയുള്ള ഓർഡറുകൾ %{link} സ്വീകരിക്കാൻ കഴിയുമോ എന്നറിയാൻ നിങ്ങളുടെ ഹബ്ബുമായി നേരിട്ട് ബന്ധപ്പെടുക." + orders_oc_expired_text_link: "അല്ലെങ്കിൽ ഈ ഹബ്ബിൽ ലഭ്യമായ മറ്റ് ഓർഡർ സൈക്കിളുകൾ കാണുക" + orders_oc_expired_email: "ഇമെയിൽ:" + orders_oc_expired_phone: "ഫോൺ:" + orders_show_title: "ഓർഡർ സ്ഥിരീകരണം" + orders_show_time: "ഓർഡർ തയ്യാറാകുന്ന സമയം" + orders_show_order_number: "ഓർഡർ # %{number}" + orders_show_cancelled: "റദ്ദാക്കി" + orders_show_confirmed: "സ്ഥിരീകരിച്ചു" + orders_your_order_has_been_cancelled: "നിങ്ങളുടെ ഓർഡർ റദ്ദാക്കി" + orders_could_not_cancel: "ക്ഷമിക്കണം, ഓർഡർ റദ്ദാക്കാൻ കഴിഞ്ഞില്ല" + orders_cannot_remove_the_final_item: "ഒരു ഓർഡറിൽ നിന്ന് അന്തിമ ഇനം നീക്കംചെയ്യാൻ കഴിയില്ല, പകരം ഓർഡർ റദ്ദാക്കുക." + orders_bought_items_notice: + one: "ഈ ഓർഡർ സൈക്കിളിനായി ഒരു അധിക ഇനം ഇതിനകം സ്ഥിരീകരിച്ചു" + few: "ഈ ഓർഡർ സൈക്കിളിനായി ഇതിനകം %{count} അധിക ഇനങ്ങൾ സ്ഥിരീകരിച്ചു." + many: "ഈ ഓർഡർ സൈക്കിളിനായി ഇതിനകം %{count} അധിക ഇനങ്ങൾ സ്ഥിരീകരിച്ചു." + other: "ഈ ഓർഡർ സൈക്കിളിനായി ഇതിനകം %{count} അധിക ഇനങ്ങൾ സ്ഥിരീകരിച്ചു." + orders_bought_edit_button: "സ്ഥിരീകരിച്ച ഇനങ്ങൾ എഡിറ്റ് ചെയ്യുക" + orders_bought_already_confirmed: "* ഇതിനകം സ്ഥിരീകരിച്ചു" + orders_confirm_cancel: "ഈ ഓർഡർ റദ്ദാക്കണമെന്ന് തീർച്ചയാണോ?" + order_processed_successfully: "നിങ്ങളുടെ ഓർഡർ വിജയകരമായി പ്രോസസ്സ് ചെയ്തു" + products_cart_distributor_choice: "നിങ്ങളുടെ ഓർഡറിന്റെ വിതരണക്കാരൻ:" + products_cart_distributor_change: "നിങ്ങളുടെ കാർട്ടിൽ ഈ ഉൽപ്പന്നം ചേർക്കുകയാണെങ്കിൽ, ഈ ഓർഡറിനായുള്ള നിങ്ങളുടെ വിതരണക്കാരനെ %{name} എന്നതിലേക്ക് മാറ്റും." + products_cart_distributor_is: "ഈ ഓർഡറിന്റെ നിങ്ങളുടെ വിതരണക്കാരൻ %{name} ആണ് ." + products_distributor_error: "മറ്റൊരു വിതരണക്കാരനുമായി ഷോപ്പിംഗ് നടത്തുന്നതിന് മുമ്പ് ദയവായി %{link} -ൽ ഓർഡർ പൂർത്തിയാക്കുക." + products_oc: "നിങ്ങളുടെ ഓർഡറിനായി ഓർഡർ സൈക്കിൾ:" + products_oc_change: "നിങ്ങളുടെ കാർട്ടിൽ ഈ ഉൽപ്പന്നം ചേർക്കുകയാണെങ്കിൽ, ഈ ഓർഡറിനായുള്ള നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ %{name} ആയി മാറും." + products_oc_is: "ഈ ഓർഡറിനായുള്ള നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ %{name} ആണ്." + products_oc_error: "മറ്റൊരു ഓർഡർ സൈക്കിളിൽ ഷോപ്പിംഗ് ചെയ്യുന്നതിന് മുമ്പ് ദയവായി %{link} ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ പൂർത്തിയാക്കുക." + products_oc_current: "നിങ്ങളുടെ നിലവിലെ ഓർഡർ സൈക്കിൾ" + products_max_quantity: പരമാവധി അളവ് + products_distributor: വിതരണക്കാരൻ + products_distributor_info: നിങ്ങളുടെ ഓർഡറിനായി ഒരു വിതരണക്കാരനെ തിരഞ്ഞെടുക്കുമ്പോൾ, അവരുടെ വിലാസവും പിക്കപ്പ് സമയവും ഇവിടെ പ്രദർശിപ്പിക്കും. + password: പാസ്സ്‌വേർഡ് + remember_me: എന്നെ ഓർമ്മിക്കുക + are_you_sure: "നിങ്ങൾക്ക് ഉറപ്പാണോ?" + orders_open: "ഓർഡറുകൾ തുറന്നിരിക്കുന്നു" + closing: "അടയ്ക്കുന്നു" + going_back_to_home_page: "നിങ്ങളെ ഹോം പേജിലേക്ക് തിരികെ കൊണ്ടുപോകുന്നു" + creating: ഉണ്ടാക്കുന്നു + updating: അപ്ഡേറ്റ് ചെയ്യുന്നു + failed_to_create_enterprise: "നിങ്ങളുടെ എന്റർപ്രൈസ് സൃഷ്ടിക്കുന്നതിൽ പരാജയപ്പെട്ടു." + failed_to_create_enterprise_unknown: "നിങ്ങളുടെ എന്റർപ്രൈസ് സൃഷ്ടിക്കുന്നതിൽ പരാജയപ്പെട്ടു.\n എല്ലാ ഫീൽഡുകളും പൂർണ്ണമായും പൂരിപ്പിച്ചിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക." + failed_to_update_enterprise_unknown: "നിങ്ങളുടെ എന്റർപ്രൈസ് അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു.\n എല്ലാ ഫീൽഡുകളും പൂർണ്ണമായും പൂരിപ്പിച്ചിട്ടുണ്ടെന്ന് ഉറപ്പാക്കുക." + enterprise_confirm_delete_message: " ഈ എന്റർപ്രൈസ് നൽകുന്ന %{product} ഇത് ഇല്ലാതാക്കും. നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + order_not_saved_yet: "നിങ്ങളുടെ ഓർഡർ ഇതുവരെ സേവ് ചെയ്തിട്ടില്ല. പൂർത്തിയാക്കാൻ ഞങ്ങൾക്ക് കുറച്ച് നിമിഷങ്ങൾ തരൂ!" + filter_by: "ഇതനുസരിച്ച് ഫിൽട്ടർ ചെയ്യുക" + hide_filters: "ഫിൽട്ടറുകൾ മറയ്ക്കുക" + one_filter_applied: "1 ഫിൽട്ടർ പ്രയോഗിച്ചു" + x_filters_applied: "ഫിൽട്ടറുകൾ പ്രയോഗിച്ചു" + submitting_order: "നിങ്ങളുടെ ഓർഡർ സമർപ്പിക്കുന്നു: ദയവായി കാത്തിരിക്കുക" + confirm_hub_change: "നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് നിങ്ങൾ തിരഞ്ഞെടുത്ത ഹബ് മാറ്റുകയും നിങ്ങളുടെ ഷോപ്പിംഗ് കാർട്ടിലെ എല്ലാ ഇനങ്ങളും നീക്കം ചെയ്യുകയും ചെയ്യും." + confirm_oc_change: "നിങ്ങൾക്ക് ഉറപ്പാണോ? ഇത് നിങ്ങൾ തിരഞ്ഞെടുത്ത ഓർഡർ സൈക്കിളിനെ മാറ്റുകയും നിങ്ങളുടെ ഷോപ്പിംഗ് കാർട്ടിലെ എല്ലാ ഇനങ്ങളും നീക്കം ചെയ്യുകയും ചെയ്യും." + location_placeholder: "ഒരു ലൊക്കേഷനിൽ ടൈപ്പ് ചെയ്യുക..." + error_required: "ശൂന്യമായിരിക്കാൻ കഴിയില്ല" + error_number: "നമ്പർ ആയിരിക്കണം" + error_email: "ഇമെയിൽ വിലാസം ആയിരിക്കണം" + error_not_found_in_database: "%{name} ഡാറ്റാബേസിൽ കണ്ടെത്തിയില്ല" + error_not_primary_producer: "ഒരു പ്രൊഡ്യൂസർ എന്ന നിലയിൽ %{name} പ്രവർത്തനക്ഷമമാക്കിയിട്ടില്ല" + error_no_permission_for_enterprise: "\"%{name}\" ഈ എന്റർപ്രൈസിനായുള്ള ഉൽപ്പന്നങ്ങൾ നിയന്ത്രിക്കാൻ നിങ്ങൾക്ക് അനുമതിയില്ല" + item_handling_fees: "ഇനം കൈകാര്യം ചെയ്യുന്നതിനുള്ള ഫീസ് (ഇനത്തിന്റെ ആകെത്തുകയിൽ ഉൾപ്പെടുന്നു)" + january: "ജനുവരി" + february: "ഫെബ്രുവരി" + march: "മാർച്ച്" + april: "ഏപ്രിൽ" + may: "മെയ്" + june: "ജൂൺ" + july: "ജൂലൈ" + august: "ഓഗസ്റ്റ്" + september: "സെപ്റ്റംബർ" + october: "ഒക്ടോബർ" + november: "നവംബർ" + december: "ഡിസംബർ" + email_not_found: "ഇമെയിൽ വിലാസം കണ്ടെത്തിയില്ല" + email_unconfirmed: "നിങ്ങളുടെ പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുന്നതിന് മുമ്പ് നിങ്ങളുടെ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കേണ്ടതുണ്ട്." + email_required: "നിങ്ങൾ ഒരു ഇമെയിൽ വിലാസം നൽകണം" + logging_in: "ഒരു നിമിഷം കാത്തിരിക്കൂ, നിങ്ങൾ ലോഗിൻ ചെയ്യുന്നു" + signup_email: "നിങ്ങളുടെ ഇമെയിൽ" + choose_password: "ഒരു പാസ്‌വേഡ് തിരഞ്ഞെടുക്കുക" + confirm_password: "പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക" + action_signup: "ഇപ്പോൾ സൈൻ അപ്പ് ചെയ്യുക" + forgot_password: "പാസ്‌വേഡ് മറന്നോ?" + password_reset_sent: "നിങ്ങളുടെ പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുന്നതിനുള്ള നിർദ്ദേശങ്ങളടങ്ങിയ ഒരു ഇമെയിൽ അയച്ചു!" + reset_password: "പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുക" + update_and_recalculate_fees: "അപ്ഡേറ്റ് ചെയ്യുകയും ഫീസ് വീണ്ടും കണക്കാക്കുകയും ചെയ്യുക" + registration: + steps: + introduction: + registration_greeting: "ഹേയ്!" + registration_intro: "നിങ്ങളുടെ പ്രൊഡ്യൂസർ അല്ലെങ്കിൽ ഹബ്ബിനായി നിങ്ങൾക്ക് ഇപ്പോൾ ഒരു പ്രൊഫൈൽ സൃഷ്ടിക്കാൻ കഴിയും" + registration_checklist: "എനിക്ക് എന്താണ് വേണ്ടത്?" + registration_time: "5-10 മിനിറ്റുകൾ" + registration_enterprise_address: "എന്റർപ്രൈസ് വിലാസം" + registration_contact_details: "പ്രാഥമിക കോൺടാക്റ്റ് വിശദാംശങ്ങൾ" + registration_logo: "നിങ്ങളുടെ ലോഗോ ചിത്രം" + registration_promo_image: "നിങ്ങളുടെ പ്രൊഫൈലിനായി ലാൻഡ്‌സ്‌കേപ്പ് ചിത്രം" + registration_about_us: "'ഞങ്ങളെക്കുറിച്ച്' വാചകം" + registration_outcome_headline: "എന്താണ് എനിക്ക് കിട്ടുക?" + registration_outcome1_html: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ നിങ്ങളെ കണ്ടെത്താനും ബന്ധപ്പെടാനും നിങ്ങളുടെ പ്രൊഫൈൽ ആളുകളെ സഹായിക്കുന്നു." + registration_outcome2: "നിങ്ങളുടെ സാമൂഹികവും ഓൺലൈൻ സാന്നിധ്യവുമായുള്ള കണക്ഷനുകൾ വർദ്ധിപ്പിക്കാൻ സഹായിക്കുന്നതിന് നിങ്ങളുടെ എന്റർപ്രൈസസിന്റെ കഥ പറയാൻ ഈ ഇടം ഉപയോഗിക്കുക." + registration_outcome3: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ വ്യാപാരം നടത്തുന്നതിനോ അല്ലെങ്കിൽ ഒരു ഓൺലൈൻ സ്റ്റോർ തുറക്കുന്നതിനോ ഉള്ള ആദ്യപടി കൂടിയാണിത്." + registration_action: "നമുക്ക് തുടങ്ങാം!" + details: + title: "വിശദാംശങ്ങൾ" + headline: "നമുക്ക് തുടങ്ങാം" + enterprise: "ആദ്യം ഞങ്ങൾക്ക് നിങ്ങളുടെ എന്റർപ്രൈസിനെക്കുറിച്ച് കുറച്ച് അറിയേണ്ടതുണ്ട്:" + producer: "ആദ്യം ഞങ്ങൾ നിങ്ങളുടെ ഫാമിനെക്കുറിച്ച് കുറച്ച് അറിയേണ്ടതുണ്ട്:" + enterprise_name_field: "എന്റർപ്രൈസ് പേര്:" + producer_name_field: "ഫാമിന്റെ പേര്:" + producer_name_field_placeholder: "ഉദാ: ചാർലിയുടെ ആകർഷണീയമായ ഫാം" + producer_name_field_error: "നിങ്ങളുടെ എന്റർപ്രൈസസിനായി ഒരു നല്ല പേര് തിരഞ്ഞെടുക്കുക" + address1_field: "അഡ്രസ് ലൈൻ 1:" + address1_field_placeholder: "ഉദാ 123 ക്രാൻബെറി ഡ്രൈവ്" + address1_field_error: "ദയവായി ഒരു വിലാസം നൽകുക" + address2_field: "അഡ്രസ് ലൈൻ 2:" + suburb_field: "നഗരപ്രാന്തം:" + suburb_field_placeholder: "ഉദാ നോർത്ത്കോട്ടെ" + suburb_field_error: "ദയവായി ഒരു പ്രാന്തപ്രദേശം നൽകുക" + postcode_field: "പിൻ കോഡ്:" + postcode_field_placeholder: "ഉദാ 3070" + postcode_field_error: "പിൻ കോഡ് ആവശ്യമാണ്" + state_field: "സ്റ്റേറ്റ്:" + state_field_error: "സംസ്ഥാനം ആവശ്യമാണ്" + country_field: "രാജ്യം:" + country_field_error: "ദയവായി ഒരു രാജ്യം തിരഞ്ഞെടുക്കുക" + map_location: "മാപ്പ് ലൊക്കേഷൻ" + locate_address: "മാപ്പിൽ വിലാസം കണ്ടെത്തുക" + drag_pin: "കൃത്യമല്ലെങ്കിൽ പിൻ ശരിയായ സ്ഥലത്തേക്ക് വലിച്ചിടുക." + confirm_address: "മാപ്പിൽ എന്റർപ്രൈസിന്റെ സൂചിപ്പിച്ച സ്ഥാനം ശരിയാണെന്ന് ഞാൻ സ്ഥിരീകരിക്കുന്നു." + drag_map_marker: "ഗ്രാമീണ മേഖലകളിൽ പ്രവർത്തിക്കുന്ന നിരവധി പ്രൊഡ്യൂസേഴ്‌സ് കാരണം, ഭൂപടങ്ങളുടെ കൃത്യത എപ്പോഴും മെച്ചപ്പെടുത്തുന്നു. മുകളിലെ മാപ്പുമായി ഇടപഴകുന്നതിലൂടെ, പിൻ അമർത്തിപ്പിടിക്കുന്നതിന് ക്ലിക്ക് ചെയ്യുകയോ ടാപ്പുചെയ്യുകയോ ചെയ്‌ത്, നിങ്ങളുടെ അറിവിന്റെ അടിസ്ഥാനത്തിൽ കൂടുതൽ കൃത്യമായ ലൊക്കേഷനിലേക്ക് വലിച്ചിടുന്നതിലൂടെ, നിങ്ങൾ എവിടെയാണെന്ന് നന്നായി മനസ്സിലാക്കാൻ ഞങ്ങളെ സഹായിക്കുക." + contact: + title: "ബന്ധപ്പെടുക" + who_is_managing_enterprise: "%{enterprise} ആരാണ് കൈകാര്യം ചെയ്യുന്നത്?" + contact_field: "പ്രാഥമിക കോൺടാക്റ്റ്" + contact_field_placeholder: "ബന്ധപ്പെടാനുള്ള പേര്" + contact_field_required: "നിങ്ങൾ ഒരു പ്രാഥമിക കോൺടാക്റ്റ് നൽകേണ്ടതുണ്ട്." + phone_field: "ഫോൺ നമ്പർ" + whatsapp_phone_field: "വാട്ട്സ്ആപ്പ് ഫോൺ നമ്പർ" + whatsapp_phone_tooltip: "വാട്ട്‌സ്ആപ്പ് ലിങ്കായി തുറക്കുന്നതിന്, ഈ നമ്പർ നിങ്ങളുടെ പൊതു പ്രൊഫൈലിൽ പ്രദർശിപ്പിക്കും." + phone_field_placeholder: "ഉദാ. (03) 1234 5678" + whatsapp_phone_field_placeholder: "ഉദാ. +61 4 1234 5678" + type: + title: "ഇനം" + headline: "%{enterprise} ചേർക്കുന്നതിനുള്ള അവസാന ഘട്ടം !" + question: "നിങ്ങൾ ഒരു പ്രൊഡ്യൂസർ ആണോ?" + yes_producer: "അതെ, ഞാൻ ഒരു പ്രൊഡ്യൂസർ ആണ്" + no_producer: "അല്ല, ഞാൻ ഒരു പ്രൊഡ്യൂസർ അല്ല" + producer_field_error: "ഒന്ന് തിരഞ്ഞെടുക്കുക. നിങ്ങൾ പ്രൊഡ്യൂസർ ആണോ?" + yes_producer_help: "പ്രൊഡ്യൂസേഴ്‌സ് കഴിക്കാനും/അല്ലെങ്കിൽ കുടിക്കാനും സ്വാദിഷ്ടമായ കാര്യങ്ങൾ ഉണ്ടാക്കുന്നു. നിങ്ങൾ അത് കൃഷി ചെയ്താലും, വളർത്തിയാലും, പാകം ചെയ്താലും, ചുട്ടാലും, പുളിപ്പിച്ചാലും അല്ലെങ്കിൽ വാർത്തെടുത്താലും നിങ്ങൾ ഒരു പ്രൊഡ്യൂസർ ആണ് ." + no_producer_help: "നിങ്ങൾ ഒരു പ്രൊഡ്യൂസർ അല്ലെങ്കിൽ , നിങ്ങൾ ആഹാരം വിൽക്കുകയും വിതരണം ചെയ്യുകയും ചെയ്യുന്ന ഒരാളായിരിക്കാം. നിങ്ങൾ ഒരു ഹബ്, കൂപ്പ്, വാങ്ങൽ ഗ്രൂപ്പ്, ചില്ലറ വ്യാപാരി, മൊത്തവ്യാപാരി അല്ലെങ്കിൽ മറ്റെന്തെങ്കിലും ആയിരിക്കാം." + create_profile: "പ്രൊഫൈൽ സൃഷ്ടിക്കുക" + about: + title: "കുറിച്ച്" + headline: "കൊള്ളാം!" + message: "ഇനി നമുക്ക് അതിനെ കുറിച്ചുള്ള വിശദാംശങ്ങൾ നോക്കാം" + success: "വിജയം! ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് %{enterprise} ചേർത്തു" + registration_exit_message: "ഏത് ഘട്ടത്തിലും നിങ്ങൾ ഈ വിസാർഡിൽ നിന്ന് പുറത്തുകടക്കുകയാണെങ്കിൽ, അഡ്മിൻ ഇന്റർഫേസിലേക്ക് പോയി നിങ്ങളുടെ പ്രൊഫൈൽ സൃഷ്ടിക്കുന്നത് തുടരാം." + enterprise_description: "ഹൃസ്വ വിവരണം" + enterprise_description_placeholder: "നിങ്ങളുടെ എന്റർപ്രൈസ് വിവരിക്കുന്ന ഒരു ചെറിയ വാചകം" + enterprise_long_desc: "നീണ്ട വിവരണം" + enterprise_long_desc_placeholder: "നിങ്ങളുടെ എന്റർപ്രൈസസിന്റെ കഥ പറയാനുള്ള നിങ്ങളുടെ അവസരമാണിത് - നിങ്ങളെ വ്യത്യസ്തനും അതിശയകരവുമാക്കുന്നത് എന്താണ്? നിങ്ങളുടെ വിവരണം 600 അക്ഷരങ്ങളിൽ താഴെയോ 150 വാക്കുകളിലോ നിലനിർത്താൻ ഞങ്ങൾ നിർദ്ദേശിക്കുന്നു." + enterprise_long_desc_length: "%{num} പ്രതീകങ്ങൾ / 600 വരെ ശുപാർശ ചെയ്യുന്നു" + enterprise_abn: "എബിഎൻ" + enterprise_abn_placeholder: "ഉദാ. 99 123 456 789" + enterprise_acn: "എ.സി.എൻ" + enterprise_acn_placeholder: "ഉദാ. 123 456 789" + enterprise_tax_required: "നിങ്ങൾ ഒരെണ്ണം തിരഞ്ഞെടുക്കേണ്ടതുണ്ട്." + images: + title: "ചിത്രങ്ങൾ" + headline: "നന്ദി!" + description: "ചില മനോഹരമായ ചിത്രങ്ങൾ അപ്‌ലോഡ് ചെയ്യാം, അങ്ങനെ നിങ്ങളുടെ പ്രൊഫൈൽ മികച്ചതായി കാണപ്പെടും! :)" + uploading: "അപ്‌ലോഡ് ചെയ്യുന്നു..." + continue: "തുടരുക" + back: "തിരികെ" + logo: + select_logo: "ഘട്ടം 1. ലോഗോ ഇമേജ് തിരഞ്ഞെടുക്കുക" + logo_tip: "നുറുങ്ങ്: ചതുര ചിത്രങ്ങൾ മികച്ച രീതിയിൽ പ്രവർത്തിക്കും, കുറഞ്ഞത് 300×300px" + logo_label: "ഒരു ലോഗോ ചിത്രം തിരഞ്ഞെടുക്കുക" + logo_drag: "നിങ്ങളുടെ ലോഗോ ഇവിടെ വലിച്ചിടുക" + review_logo: "ഘട്ടം 2. നിങ്ങളുടെ ലോഗോ അവലോകനം ചെയ്യുക" + review_logo_tip: "നുറുങ്ങ്: മികച്ച ഫലങ്ങൾക്കായി, നിങ്ങളുടെ ലോഗോ ലഭ്യമായ സ്ഥലത്ത് നിറഞ്ഞിരിക്കണം" + logo_placeholder: "ഒരിക്കൽ അപ്‌ലോഡ് ചെയ്‌താൽ അവലോകനത്തിനായി നിങ്ങളുടെ ലോഗോ ഇവിടെ ദൃശ്യമാകും" + promo: + select_promo_image: "ഘട്ടം 3. പ്രമോ ചിത്രം തിരഞ്ഞെടുക്കുക" + promo_image_tip: "നുറുങ്ങ്: ഒരു ബാനറായി കാണിച്ചിരിക്കുന്നു, ശുപാർശ ചെയ്യുന്ന വലുപ്പം 1200×260px ആണ്" + promo_image_label: "ഒരു പ്രമോ ചിത്രം തിരഞ്ഞെടുക്കുക" + promo_image_drag: "നിങ്ങളുടെ പ്രമോ ഇവിടെ വലിച്ചിടുക" + review_promo_image: "ഘട്ടം 4. നിങ്ങളുടെ പ്രൊമോ ബാനർ അവലോകനം ചെയ്യുക" + review_promo_image_tip: "നുറുങ്ങ്: മികച്ച ഫലങ്ങൾക്കായി, നിങ്ങളുടെ പ്രൊമോ ഇമേജ് ലഭ്യമായ സ്ഥലത്ത് നിറഞ്ഞിരിക്കണം" + promo_image_placeholder: "ഒരിക്കൽ അപ്‌ലോഡ് ചെയ്‌താൽ അവലോകനത്തിനായി നിങ്ങളുടെ ലോഗോ ഇവിടെ ദൃശ്യമാകും" + social: + title: "സാമൂഹികം" + enterprise_final_step: "അവസാന ഘട്ടം!" + enterprise_social_text: "ആളുകൾക്ക് എങ്ങനെ %{enterprise} ഓൺലൈനിൽ കണ്ടെത്താനാകും?" + website: "വെബ്സൈറ്റ്" + website_placeholder: "ഉദാ. openfoodnetwork.org.au" + facebook: "ഫേസ്ബുക്ക്" + facebook_placeholder: "ഉദാ. www.facebook.com/PageNameHere" + linkedin: "ലിങ്ക്ഡ്ഇൻ" + linkedin_placeholder: "ഉദാ. www.linkedin.com/YourNameHere" + twitter: "ട്വിറ്റർ" + twitter_placeholder: "ഉദാ. @twitter_handle" + instagram: "ഇൻസ്റ്റാഗ്രാം" + instagram_placeholder: "ഉദാ. @instagram_handle" + limit_reached: + headline: "അയ്യോ!" + message: "നിങ്ങളുടെ അനുവദനീയമായ പരിധി എത്തി!" + text: "നിങ്ങൾക്ക് സ്വന്തമാക്കാൻ അനുവാദമുള്ള എന്റർപ്രൈസസിന്റെ പരിധിയിൽ നിങ്ങൾ എത്തിയിരിക്കുന്നു" + action: "ഹോംപേജിലേക്ക് മടങ്ങുക" + finished: + headline: "പൂർത്തിയായി!" + thanks: "%{enterprise} ന്റെ വിശദാംശങ്ങൾ പൂരിപ്പിച്ചതിന് നന്ദി." + login: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് ലോഗിൻ ചെയ്‌ത് അഡ്മിനിലേക്ക് പോയി ഏത് ഘട്ടത്തിലും നിങ്ങളുടെ എന്റർപ്രൈസ് മാറ്റാനോ അപ്‌ഡേറ്റ് ചെയ്യാനോ കഴിയും." + action: "എന്റർപ്രൈസ് ഡാഷ്‌ബോർഡിലേക്ക് പോകുക" + back: "തിരികെ" + continue: "തുടരുക" + action_or: "അഥവാ" + enterprise_limit: എന്റർപ്രൈസ് പരിധി + shipping_method_destroy_error: "%{number} എന്ന ഓർഡറിൽ പരാമർശിച്ചിരിക്കുന്നതിനാൽ ആ ഷിപ്പിംഗ് രീതി ഇല്ലാതാക്കാൻ കഴിയില്ല." + fees: "ഫീസ്" + fee_name: "ഫീസ് പേര്" + fee_owner: "ഫീസ് ഉടമ" + item_cost: "ഇനത്തിന്റെ വില" + bulk: "ബൾക്ക്" + shop_variant_quantity_min: "കുറഞ്ഞത്" + shop_variant_quantity_max: "പരമാവധി" + contact: "ബന്ധപ്പെടുക" + follow: "ഫോളോ ചെയ്യുക" + shop_for_products_html: "%{enterprise}-ൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ ഇവിടെ വാങ്ങുക:" + change_shop: "ഷോപ്പ് ഇതിലേക്ക് മാറ്റുക:" + shop_at: "ഇപ്പോൾ ഇവിടെ ഷോപ്പുചെയ്യുക:" + admin_fee: "അഡ്മിൻ ഫീസ്" + sales_fee: "വിൽപ്പന ഫീസ്" + packing_fee: "പാക്കിംഗ് ഫീസ്" + transport_fee: "ട്രാൻസ്‌പോർട് ഫീസ്" + fundraising_fee: "ധനസമാഹരണ ഫീസ്" + price_graph: "വില ഗ്രാഫ്" + included_tax: "നികുതി ഉൾപ്പെടുത്തിയിട്ടുണ്ട്" + tax: "നികുതി" + tax_amount_included: "%{amount} (ഉൾപ്പെടുന്നു)" + remove_tax: "നികുതി നീക്കം ചെയ്യുക" + balance: "മിച്ചം" + transaction: "ഇടപാട്" + transaction_date: "തീയതി" + payment_state: "പേയ്മെന്റ് സ്ഥിതി" + shipping_state: "ഷിപ്പിംഗ് സ്ഥിതി" + value: "മൂല്യം" + balance_due: "ബാക്കി" + credit: "ക്രെഡിറ്റ്" + Paid: "പണം നൽകി" + Ready: "തയ്യാറാണ്" + not_visible: ദൃശ്യമല്ല + you_have_no_orders_yet: "നിങ്ങൾക്ക് ഇതുവരെ ഓർഡറുകളൊന്നുമില്ല" + show_only_complete_orders: "പൂർണ്ണമായ ഓർഡറുകൾ മാത്രം കാണിക്കുക" + successfully_created: '%{resource} വിജയകരമായി സൃഷ്‌ടിച്ചു!' + successfully_removed: '%{resource} വിജയകരമായി നീക്കം ചെയ്‌തു!' + successfully_updated: '%{resource} വിജയകരമായി അപ്‌ഡേറ്റ് ചെയ്‌തു!' + running_balance: "റണ്ണിംഗ് ബാലൻസ്" + outstanding_balance: "കുടിശ്ശിക" + admin_enterprise_relationships: "എന്റർപ്രൈസ് അനുമതികൾ" + admin_enterprise_relationships_everything: "എല്ലാം" + admin_enterprise_relationships_permits: "അനുമതികൾ" + admin_enterprise_relationships_seach_placeholder: "തിരയുക" + admin_enterprise_relationships_button_create: "സൃഷ്ടിക്കാൻ" + admin_enterprise_relationships_to: "വരെ" + admin_enterprise_groups: "എന്റർപ്രൈസ് ഗ്രൂപ്പുകൾ" + admin_enterprise_groups_name: "പേര്" + admin_enterprise_groups_owner: "ഉടമ" + admin_enterprise_groups_on_front_page: "ഒന്നാം പേജിൽ?" + admin_enterprise_groups_enterprise: "സംരംഭങ്ങൾ" + admin_enterprise_groups_data_powertip: "ഈ ഗ്രൂപ്പിന്റെ ഉത്തരവാദിത്തമുള്ള പ്രാഥമിക ഉപയോക്താവ്." + admin_enterprise_groups_data_powertip_logo: "ഇതാണ് ഗ്രൂപ്പിന്റെ ലോഗോ" + admin_enterprise_groups_data_powertip_promo_image: "ഗ്രൂപ്പ് പ്രൊഫൈലിന്റെ മുകളിൽ ഈ ചിത്രം പ്രദർശിപ്പിച്ചിരിക്കുന്നു" + admin_enterprise_groups_contact_phone_placeholder: "ഉദാ. 98 7654 3210" + admin_enterprise_groups_contact_address1_placeholder: "ഉദാ. 123 ഹൈ സ്ട്രീറ്റ്" + admin_enterprise_groups_contact_city: "നഗരപ്രാന്തം" + admin_enterprise_groups_contact_city_placeholder: "ഉദാ. നോർത്ത്കോട്ടെ" + admin_enterprise_groups_contact_zipcode: "പിൻ കോഡ്" + admin_enterprise_groups_contact_zipcode_placeholder: "ഉദാ. 3070" + admin_enterprise_groups_contact_state_id: "സ്റ്റേറ്റ്" + admin_enterprise_groups_contact_country_id: "രാജ്യം" + admin_enterprise_groups_web_twitter: "ഉദാ. @the_prof" + admin_enterprise_groups_web_website_placeholder: "ഉദാ. www.truffles.com" + admin_order_cycles: "അഡ്മിൻ ഓർഡർ സൈക്കിളുകൾ" + open: "തുറന്നിരിക്കുന്നു" + close: "അടയ്ക്കുക" + create: "സൃഷ്ടിക്കാൻ" + search: "തിരയുക" + supplier: "വിതരണക്കാരൻ" + product_name: "ഉത്പന്നത്തിന്റെ പേര്" + product_description: "ഉൽപ്പന്ന വിവരണം" + permalink: "പെർമലിങ്ക്" + shipping_categories: "ഷിപ്പിംഗ് വിഭാഗങ്ങൾ" + units: "യൂണിറ്റ് വലിപ്പം" + coordinator: "കോർഡിനേറ്റർ" + distributor: "വിതരണക്കാരൻ" + enterprise_fees: "എന്റർപ്രൈസ് ഫീസ്" + process_my_order: "എന്റെ ഓർഡർ പ്രോസസ്സ് ചെയ്യുക" + delivery_instructions: ഡെലിവറി നിർദ്ദേശങ്ങൾ + delivery_method: വിതരണ സംവിധാനം + fee_type: "ഫീസ് തരം" + tax_category: "നികുതി വിഭാഗം" + display: "പ്രദർശിപ്പിക്കുക" + tags: "ടാഗുകൾ" + calculator: "കാൽക്കുലേറ്റർ" + calculator_values: "കാൽക്കുലേറ്റർ മൂല്യങ്ങൾ" + calculator_settings_warning: "നിങ്ങൾ കാൽക്കുലേറ്റർ തരം മാറ്റുകയാണെങ്കിൽ, കാൽക്കുലേറ്റർ ക്രമീകരണങ്ങൾ എഡിറ്റുചെയ്യുന്നതിന് മുമ്പ് നിങ്ങൾ ആദ്യം സേവ് ചെയ്യണം" + calculator_preferred_unit_error: "കിലോ അല്ലെങ്കിൽ പൗണ്ട് ആയിരിക്കണം" + calculator_preferred_value_error: "അസാധുവായ ഇൻപുട്ട്. ദയവായി നമ്പറുകൾ മാത്രം ഉപയോഗിക്കുക. ഉദാഹരണത്തിന്: 10, 5.5, -20" + flat_percent_per_item: "ശതമാനം (ഓരോ ഇനത്തിനും)" + flat_rate_per_item: "നിരക്ക് (ഓരോ ഇനത്തിനും)" + flat_rate_per_order: "നിരക്ക് (ഓരോ ഓർഡറിനും)" + flexible_rate: "ഫ്ലെക്സിബിൾ റേറ്റ്" + price_sack: "വില " + new_order_cycles: "പുതിയ ഓർഡർ സൈക്കിളുകൾ" + new_order_cycle: "പുതിയ ഓർഡർ സൈക്കിൾ" + new_order_cycle_tooltip: "ഒരു നിശ്ചിത സമയത്തേക്ക് കട തുറക്കുക" + select_a_coordinator_for_your_order_cycle: "നിങ്ങളുടെ ഓർഡർ സൈക്കിളിനായി ഒരു കോർഡിനേറ്ററെ തിരഞ്ഞെടുക്കുക" + notify_producers: 'പ്രൊഡ്യൂസേഴ്സിനെ അറിയിക്കുക' + edit_order_cycle: "ഓർഡർ സൈക്കിൾ എഡിറ്റ് ചെയ്യുക" + roles: "കർത്തവ്യങ്ങൾ" + update: "അപ്ഡേറ്റ് ചെയ്യുക" + delete: ഇല്ലാതാക്കുക + add_producer_property: "പ്രൊഡ്യൂസർ പ്രോപ്പർട്ടി ചേർക്കുക" + in_progress: "പുരോഗതിയിൽ" + started_at: "ആരംഭിച്ചത്" + queued: "ക്യൂവിൽ" + scheduled_for: "ഇതിനായി ഷെഡ്യൂൾ ചെയ്തിട്ടുണ്ട്" + customers: "ഉപഭോക്താക്കൾ" + please_select_hub: "ദയവായി ഒരു ഹബ് തിരഞ്ഞെടുക്കുക" + loading_customers: "ഉപഭോക്താക്കളെ ലോഡുചെയ്യുന്നു" + no_customers_found: "ഉപഭോക്താക്കളെ കണ്ടെത്തിയില്ല" + go: "പോകൂ" + hub: "ഹബ്" + product: "ഉൽപ്പന്നം" + price: "വില" + review: "അവലോകനം" + save_changes: "മാറ്റങ്ങൾ സേവ് ചെയ്യുക" + order_saved: "ഓർഡർ സേവ് ചെയ്തു" + no_products: ഉൽപ്പന്നങ്ങളൊന്നുമില്ല + spree_admin_overview_enterprises_header: "എന്റെ സംരംഭങ്ങൾ" + spree_admin_overview_enterprises_footer: "എന്റെ എന്റർപ്രൈസുകൾ നിയന്ത്രിക്കുക" + spree_admin_enterprises_hubs_name: "പേര്" + spree_admin_enterprises_create_new: "പുതിയത് സൃഷ്‌ടിക്കുക" + spree_admin_enterprises_shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + spree_admin_enterprises_fees: "എന്റർപ്രൈസ് ഫീസ്" + spree_admin_enterprises_none_create_a_new_enterprise: "ഒരു പുതിയ എന്റർപ്രൈസ് സൃഷ്ടിക്കുക" + spree_admin_enterprises_none_text: "നിങ്ങൾക്ക് ഇതുവരെ സംരംഭങ്ങളൊന്നുമില്ല" + spree_admin_enterprises_tabs_hubs: "ഹബ്ബുകൾ" + spree_admin_enterprises_producers_manage_products: "ഉൽപ്പന്നങ്ങൾ കൈകാര്യം ചെയ്യുക" + spree_admin_enterprises_create_new_product: "ഒരു പുതിയ ഉൽപ്പന്നം സൃഷ്‌ടിക്കുക" + spree_admin_single_enterprise_alert_mail_confirmation: "ഇതിനായുള്ള ഇമെയിൽ വിലാസം ദയവായി സ്ഥിരീകരിക്കുക" + spree_admin_single_enterprise_alert_mail_sent: "ഞങ്ങൾ ഇതിലേക്ക് ഒരു ഇമെയിൽ അയച്ചു-" + spree_admin_overview_action_required: "നടപടി ആവശ്യമാണ്" + spree_admin_overview_check_your_inbox: "കൂടുതൽ നിർദ്ദേശങ്ങൾക്കായി നിങ്ങളുടെ ഇൻബോക്സ് പരിശോധിക്കുക. നന്ദി!" + spree_admin_unit_value: യൂണിറ്റ് മൂല്യം + spree_admin_unit_description: യൂണിറ്റ് വിവരണം + spree_admin_variant_unit: വേരിയന്റ് യൂണിറ്റ് + spree_admin_variant_unit_scale: വേരിയന്റ് യൂണിറ്റ് സ്കെയിൽ + spree_admin_supplier: വിതരണക്കാരൻ + spree_admin_product_category: ഉൽപ്പന്ന വിഭാഗം + spree_admin_variant_unit_name: വേരിയന്റ് യൂണിറ്റിന്റെ പേര് + unit_name: "യൂണിറ്റിന്റെ പേര്" + change_package: "പാക്കേജ് മാറ്റുക" + spree_admin_single_enterprise_hint: "സൂചന: നിങ്ങളെ കണ്ടെത്താൻ ആളുകളെ അനുവദിക്കുന്നതിന്, ചുവടെ നിങ്ങളുടെ ദൃശ്യപരത ഓണാക്കുക" + spree_admin_eg_pickup_from_school: "ഉദാ. 'പ്രൈമറി സ്കൂളിൽ നിന്ന് പിക്കപ്പ് ചെയ്യുക'" + spree_admin_eg_collect_your_order: "ഉദാ. 'ദയവായി 123 ഇമാജിനറി സ്ട്രീറ്റ്, നോർത്ത്കോട്ട്, 3070-ൽ നിന്ന് നിങ്ങളുടെ ഓർഡർ ശേഖരിക്കുക'" + spree_classification_primary_taxon_error: "ടാക്സോൺ %{taxon} എന്നത് %{product} -ന്റെ പ്രാഥമിക ടാക്സോണാണ്, അത് ഇല്ലാതാക്കാൻ കഴിയില്ല" + spree_order_availability_error: "ഡിസ്ട്രിബ്യൂട്ടർക്കോ അല്ലെങ്കിൽ ഓർഡർ സൈക്കിളിനോ നിങ്ങളുടെ കാർട്ടിലെ ഉൽപ്പന്നങ്ങൾ വിതരണം ചെയ്യാൻ കഴിയില്ല" + spree_order_populator_error: "നിങ്ങളുടെ കാർട്ടിലെ എല്ലാ ഉൽപ്പന്നങ്ങളും വിതരണം ചെയ്യാൻ ആ വിതരണക്കാരനോ ഓർഡർ സൈക്കിളിനോ കഴിയില്ല. ദയവായി മറ്റൊന്ന് തിരഞ്ഞെടുക്കുക." + spree_order_cycle_error: "ഈ ഓർഡറിനായി ഒരു ഓർഡർ സൈക്കിൾ തിരഞ്ഞെടുക്കുക." + spree_order_populator_availability_error: "തിരഞ്ഞെടുത്ത വിതരണക്കാരിൽ നിന്നോ ഓർഡർ സൈക്കിളിൽ നിന്നോ ആ ഉൽപ്പന്നം ലഭ്യമല്ല." + spree_distributors_error: "കുറഞ്ഞത് ഒരു ഹബ്ബെങ്കിലും തിരഞ്ഞെടുക്കണം" + spree_user_enterprise_limit_error: "^ %{email} ന് കൂടുതൽ സംരംഭങ്ങൾ സ്വന്തമാക്കാൻ അനുവാദമില്ല (പരിധി %{enterprise_limit})." + spree_variant_product_error: കുറഞ്ഞത് ഒരു വേരിയന്റെങ്കിലും ഉണ്ടായിരിക്കണം + your_profil_live: "നിങ്ങളുടെ പ്രൊഫൈൽ തത്സമയം" + see: "കാണുക" + live: "തത്സമയം" + manage: "കൈകാര്യം ചെയ്യുക" + resend: "വീണ്ടും അയയ്ക്കുക" + add_and_manage_products: "ഉൽപ്പന്നങ്ങൾ ചേർക്കുക & കൈകാര്യം ചെയ്യുക" + add_and_manage_order_cycles: "ഓർഡർ സൈക്കിളുകൾ ചേർക്കുക & കൈകാര്യം ചെയ്യുക" + manage_order_cycles: "ഓർഡർ സൈക്കിളുകൾ കൈകാര്യം ചെയ്യുക" + manage_products: "ഉൽപ്പന്നങ്ങൾ കൈകാര്യം ചെയ്യുക" + edit_profile_details: "പ്രൊഫൈൽ വിശദാംശങ്ങൾ എഡിറ്റ് ചെയ്യുക" + edit_profile_details_etc: "നിങ്ങളുടെ പ്രൊഫൈൽ വിവരണം, ചിത്രങ്ങൾ മുതലായവ മാറ്റുക." + order_cycle: "ഓർഡർ സൈക്കിൾ" + enterprise_relationships: "എന്റർപ്രൈസ് അനുമതികൾ" + first_name_begins_with: "ആദ്യ നാമം ആരംഭിക്കുന്നത്" + last_name_begins_with: "അവസാന നാമം ആരംഭിക്കുന്നത്" + shipping_method: "ഷിപ്പിംഗ് രീതി" + new_order: "പുതിയ ഓർഡർ" + enterprise_tos_link: "എന്റർപ്രൈസ് സേവന നിബന്ധനകൾ ലിങ്ക്" + enterprise_tos_message: "ഞങ്ങളുടെ ലക്ഷ്യങ്ങളും മൂല്യങ്ങളും പങ്കിടുന്ന ആളുകളുമായി പ്രവർത്തിക്കാൻ ഞങ്ങൾ ആഗ്രഹിക്കുന്നു. അതിനാൽ, പുതിയ സംരംഭങ്ങൾ ഞങ്ങളോട് യോജിക്കാൻ ആവശ്യപ്പെടുന്നത്-" + enterprise_tos_agree: "മുകളിലുള്ള സേവന നിബന്ധനകൾ ഞാൻ അംഗീകരിക്കുന്നു" + tax_settings: "നികുതി ക്രമീകരണങ്ങൾ" + products_require_tax_category: "ഉൽപ്പന്നങ്ങൾക്ക് നികുതി വിഭാഗം ആവശ്യമാണ്" + admin_shared_address_1: "വിലാസം" + admin_shared_address_2: "വിലാസം (തുടർച്ച)" + admin_share_city: "നഗരം" + admin_share_zipcode: "പിൻ കോഡ്" + admin_share_country: "രാജ്യം" + admin_share_state: "സ്റ്റേറ്റ്" + hub_sidebar_hubs: "ഹബ്ബുകൾ" + hub_sidebar_none_available: "ഒന്നും ലഭ്യമല്ല" + hub_sidebar_manage: "കൈകാര്യം ചെയ്യുക" + hub_sidebar_at_least: "കുറഞ്ഞത് ഒരു ഹബ്ബെങ്കിലും തിരഞ്ഞെടുക്കണം" + hub_sidebar_blue: "നീല" + hub_sidebar_red: "ചുവപ്പ്" + order_cycles_closed_for_hub: "നിങ്ങൾ തിരഞ്ഞെടുത്ത ഹബ് ഓർഡറുകൾക്കായി താൽക്കാലികമായി അടച്ചിരിക്കുന്നു. ദയവായി പിന്നീട് വീണ്ടും ശ്രമിക്കുക." + report_customers_distributor: "വിതരണക്കാരൻ" + report_customers_hub: "ഹബ്" + report_customers_supplier: "വിതരണക്കാരൻ" + report_customers_cycle: "ഓർഡർ സൈക്കിൾ" + report_customers_type: "റിപ്പോർട്ട് ഇനം" + report_customers_csv: "സി എസ് വി ഫയൽ ഡൗൺലോഡ് ചെയ്യുക" + report_customers: ഉപഭോക്താവ് + report_producers: "പ്രൊഡ്യൂസേഴ്‌സ്" + report_type: "റിപ്പോർട്ട് ഇനം" + report_hubs: "ഹബ്ബുകൾ" + report_payment: "പേയ്മെന്റ് രീതികൾ" + report_distributor: "വിതരണക്കാരൻ" + report_payment_by: 'തരം തിരിച്ചുള്ള പേയ്‌മെന്റുകൾ' + report_itemised_payment: 'ഇനം തിരിച്ചുള്ള മൊത്തം പേയ്‌മെന്റുകൾ' + report_payment_totals: 'ആകെ പേയ്മെന്റ്' + report_all: 'എല്ലാം' + report_order_cycle: "ഓർഡർ സൈക്കിൾ" + report_hide_columns: മറയ്‌ക്കാനുള്ള നിരകൾ + report_columns: നിരകൾ + report_enterprises: "സംരംഭങ്ങൾ" + report_enterprise_fee: "ഫീസ് പേരുകൾ" + report_users: "ഉപയോക്താക്കൾ" + report_tax_rates: നികുതി നിരക്കുകൾ + report_tax_types: നികുതി തരങ്ങൾ + report_filters: റിപ്പോർട്ട് ഫിൽട്ടറുകൾ + report_print: റിപ്പോർട്ട് പ്രിന്റ് ചെയ്യുക + report_render_options: റെൻഡറിംഗ് ഓപ്ഷനുകൾ + report_header_ofn_uid: ഒഎഫ്എൻ യുഐഡി + report_header_order_cycle: ഓർഡർ സൈക്കിൾ + report_header_user: ഉപയോക്താവ് + report_header_email: ഇമെയിൽ + report_header_status: സ്ഥിതി + report_header_comments: അഭിപ്രായങ്ങൾ + report_header_first_name: പേരിന്റെ ആദ്യഭാഗം + report_header_last_name: പേരിന്റെ അവസാന ഭാഗം + report_header_suburb: നഗരപ്രാന്തം + report_header_phone: ഫോൺ + report_header_address: വിലാസം + report_header_billing_address: ബില്ലിംഗ് വിലാസം + report_header_relationship: ബന്ധം + report_header_hub: ഹബ് + report_header_hub_address: ഹബ് വിലാസം + report_header_to_hub: ഹബ്ബിലേക്ക് + report_header_hub_code: ഹബ് കോഡ് + report_header_hub_id: ഹബ് ഐഡി + report_header_hub_business_number: "ഹബ് ബിസിനസ് നമ്പർ" + report_header_hub_legal_name: "ഹബ്ബിന്റെ നിയമപരമായ പേര്" + report_header_hub_contact_name: "ഹബ് കോൺടാക്റ്റ് പേര്" + report_header_hub_email: "ഹബ് പൊതു ഇമെയിൽ" + report_header_hub_owner_email: ഹബ് ഉടമയുടെ ഇമെയിൽ + report_header_hub_phone: "ഹബ് ഫോൺ നമ്പർ" + report_header_hub_address_line1: "ഹബ് വിലാസം ലൈൻ 1" + report_header_hub_address_line2: "ഹബ് വിലാസം ലൈൻ 2" + report_header_hub_address_city: "ഹബ് നഗരപ്രാന്തം" + report_header_hub_address_zipcode: "ഹബ് പിൻകോഡ്" + report_header_hub_address_state_name: "ഹബ് സ്റ്റേറ്റ്" + report_header_code: കോഡ് + report_header_paid: പണം നൽകിയോ? + report_header_delivery: ഡെലിവറി? + report_header_shipping: ഷിപ്പിംഗ് + report_header_shipping_method: ഷിപ്പിംഗ് രീതി + report_header_shipping_instructions: ഷിപ്പിംഗ് നിർദ്ദേശങ്ങൾ + report_header_ship_street: ഷിപ്പിംഗ് തെരുവ് + report_header_ship_street_2: ഷിപ്പിംഗ് തെരുവ് 2 + report_header_ship_city: ഷിപ്പിംഗ് നഗരം + report_header_ship_postcode: ഷിപ്പിംഗ് പിൻകോഡ് + report_header_ship_state: ഷിപ്പിംഗ് സ്റ്റേറ്റ് + report_header_billing_street: ബില്ലിംഗ് സ്ട്രീറ്റ് + report_header_billing_street_2: ബില്ലിംഗ് സ്ട്രീറ്റ് 2 + report_header_billing_street_3: ബില്ലിംഗ് സ്ട്രീറ്റ് 3 + report_header_billing_street_4: ബില്ലിംഗ് സ്ട്രീറ്റ് 4 + report_header_billing_city: ബില്ലിംഗ് സിറ്റി + report_header_billing_postcode: ബില്ലിംഗ് പിൻ കോഡ് + report_header_billing_state: ബില്ലിംഗ് സ്റ്റേറ്റ് + report_header_incoming_transport: ഇൻകമിംഗ് ട്രാൻസ്പോർട്ട് + report_header_special_instructions: പ്രത്യേക നിർദ്ദേശങ്ങൾ + report_header_order_number: ഓർഡർ നമ്പർ + report_header_date: തീയതി + report_header_confirmation_date: സ്ഥിരീകരണ തീയതി + report_header_tags: ടാഗുകൾ + report_header_items: ഇനങ്ങൾ + report_header_items_total: "ഇനങ്ങൾ ആകെ %{currency_symbol}" + report_header_taxable_items_total: "നികുതി ചുമത്താവുന്ന ഇനങ്ങളുടെ ആകെത്തുക (%{currency_symbol})" + report_header_sales_tax: "വിൽപ്പന നികുതി (%{currency_symbol})" + report_header_delivery_charge: "ഡെലിവറി ചാർജ് (%{currency_symbol})" + report_header_tax: "നികുതി" + report_header_tax_on_delivery: "ഡെലിവറിയ്ക്കുള്ള നികുതി (%{currency_symbol})" + report_header_tax_on_fees: "ഫീസിനുള്ള നികുതി (%{currency_symbol})" + report_header_tax_category: "നികുതി വിഭാഗം" + report_header_tax_rate_name: "നികുതി നിരക്ക് പേര്" + report_header_tax_rate: "നികുതി നിരക്ക്" + report_header_total_tax: "മൊത്തം നികുതി (%{currency_symbol})" + report_header_total_excl_tax: "ആകെ നികുതി ഒഴികെ (%{currency_symbol})" + report_header_total_incl_tax: "മൊത്തം നികുതി ഉൾപ്പെടെ (%{currency_symbol})" + report_header_total_orders: "ഓർഡറുകളുടെ ആകെ എണ്ണം" + report_header_enterprise: എന്റർപ്രൈസ് + report_header_enterprise_fee_name: പേര് + report_header_enterprise_fee_type: ഇനം + report_header_enterprise_fee_owner: ഉടമ + report_header_customer: ഉപഭോക്താവ് + report_header_customer_first_name: പേരിന്റെ ആദ്യഭാഗം + report_header_customer_last_name: പേരിന്റെ അവസാന ഭാഗം + report_header_customer_code: ഉപഭോക്തൃ കോഡ് + report_header_product: ഉൽപ്പന്നം + report_header_product_properties: ഉൽപ്പന്ന സവിശേഷതകൾ + report_header_product_tax_category: ഉൽപ്പന്ന നികുതി വിഭാഗം + report_header_quantity: അളവ് + report_header_max_quantity: പരമാവധി അളവ് + report_header_variant: വേരിയന്റ് + report_header_variant_value: വേരിയന്റ് മൂല്യം + report_header_variant_unit: വേരിയന്റ് യൂണിറ്റ് + report_header_total_available: ആകെ ലഭ്യമായത് + report_header_unallocated: അനുവദിച്ചിട്ടില്ലാത്തത് + report_header_max_quantity_excess: പരമാവധി അളവിൽ അധികമുള്ളത് + report_header_taxons: നികുതികൾ + report_header_supplier: വിതരണക്കാരൻ + report_header_producer: പ്രൊഡ്യൂസർ + report_header_producer_suburb: പ്രൊഡ്യൂസർ നഗരപ്രാന്തം + report_header_producer_tax_status: പ്രൊഡ്യൂസർ ടാക്സ് സ്ഥിതി + report_header_producer_charges_sales_tax?: ജിഎസ്ടി/വാറ്റ് രജിസ്റ്റർ ചെയ്തത് + report_header_unit: യൂണിറ്റ് + report_header_group_buy_unit_quantity: ഗ്രൂപ്പ് വാങ്ങൽ യൂണിറ്റ് അളവ് + report_header_cost: ചെലവ് + report_header_shipping_cost: ഷിപ്പിംഗ് കൂലി + report_header_curr_cost_per_unit: ഓരോ യൂണിറ്റിനും ഉള്ള ചെലവ് + report_header_total_shipping_cost: മൊത്തം ഷിപ്പിംഗ് ചെലവ് + report_header_payment_method: പണമടയ്ക്കൽ രീതി + report_header_sells: വിൽക്കുന്നു + report_header_visible: ദൃശ്യമാണ് + report_header_price: വില + report_header_unit_size: യൂണിറ്റ് വലിപ്പം + report_header_distributor: വിതരണക്കാരൻ + report_header_distributor_address: വിതരണക്കാരന്റെ വിലാസം + report_header_distributor_city: വിതരണക്കാരന്റെ നഗരം + report_header_distributor_postcode: വിതരണക്കാരന്റെ പിൻകോഡ് + report_header_distributor_tax_status: വിതരണക്കാരന്റെ നികുതി നില + report_header_delivery_address: ഡെലിവറി വിലാസം + report_header_delivery_postcode: ഡെലിവറി പിൻ കോഡ് + report_header_bulk_unit_size: ബൾക്ക് യൂണിറ്റ് വലുപ്പം + report_header_weight: ഭാരം + report_header_final_weight_volume: ഫൈനൽ (ഭാരം/വോളിയം) + report_header_height: ഉയരം + report_header_width: വീതി + report_header_depth: ആഴം + report_header_sum_total: ആകെ തുക + report_header_date_of_order: ഓർഡർ തീയതി + report_header_amount_owing: കുടിശ്ശിക തുക + report_header_amount_paid: അടച്ച തുക + report_header_units_required: ആവശ്യമായ യൂണിറ്റുകൾ + report_header_remainder: ബാക്കിയുള്ളത് + report_header_order_date: ഓർഡർ തീയതി + report_header_order_id: ഓർഡർ ഐഡി + report_header_item_name: ഇനത്തിന്റെ പേര് + report_header_temp_controlled_items: താപനില നിയന്ത്രിത ഇനങ്ങൾ? + report_header_customer_name: ഉപഭോക്താവിന്റെ പേര് + report_header_customer_email: ഉപഭോക്തൃ ഇമെയിൽ + report_header_customer_phone: ഉപഭോക്തൃ ഫോൺ + report_header_customer_city: ഉപഭോക്തൃ നഗരം + report_header_payment_state: പേയ്മെന്റ് സ്റ്റേറ്റ് + report_header_payment_type: പേയ്മെന്റ് തരം + report_header_item_price: "ഇനം (%{currency})" + report_header_item_fees_price: "ഇനം + ഫീസ് (%{currency})" + report_header_admin_handling_fees: "അഡ്മിൻ & കൈകാര്യം ചെയ്യൽ (%{currency})" + report_header_ship_price: "ഷിപ്പിംഗ് (%{currency})" + report_header_pay_fee_price: "ഫീസ് അടയ്ക്കുക (%{currency})" + report_header_total_price: "ആകെ (%{currency})" + report_header_product_total_price: "ഉൽപ്പന്ന ആകെത്തുക (%{currency})" + report_header_shipping_total_price: "ഷിപ്പിംഗ് ആകെ (%{currency})" + report_header_outstanding_balance_price: "ബാക്കിയുള്ള തുക (%{currency})" + report_header_eft_price: "ഇ എഫ് ടി (%{currency})" + report_header_paypal_price: "പേപാൽ (%{currency})" + report_header_sku: എസ്.കെ.യു + report_header_amount: തുക + report_header_balance: മിച്ചം + report_header_total_cost: "മൊത്തം ചെലവ്" + report_header_total_ordered: ആകെ ഓർഡർ ചെയ്തത് + report_header_total_max: ആകെ പരമാവധി + report_header_total_units: ആകെ യൂണിറ്റുകൾ + report_header_sum_max_total: "പരമാവധി ആകെ തുക" + report_header_total_excl_vat: "ആകെ ഒഴികെ. നികുതി (%{currency_symbol})" + report_header_total_incl_vat: "മൊത്തം നികുതി ഉൾപ്പെടെ (%{currency_symbol})" + report_header_temp_controlled: ടെമ്പ് കൺട്രോൾഡ്? + report_header_is_producer: പ്രൊഡ്യൂസർ? + report_header_not_confirmed: സ്ഥിരീകരിച്ചില്ല + report_header_gst_on_income: വരുമാനത്തിൽ ജി.എസ്.ടി + report_header_gst_free_income: ജിഎസ്ടി സൗജന്യ വരുമാനം + report_header_total_untaxable_produce: നികുതി നൽകേണ്ടാത്ത മൊത്തം ഉൽപ്പന്നങ്ങൾ (നികുതി ഇല്ല) + report_header_total_taxable_produce: നികുതി നൽകേണ്ട മൊത്തം ഉൽപ്പന്നങ്ങൾ (നികുതി ഉൾപ്പെടെ) + report_header_total_untaxable_fees: നികുതി നൽകാത്ത മൊത്തം ഫീസ് (നികുതി ഇല്ല) + report_header_total_taxable_fees: നികുതി നൽകേണ്ട മൊത്തം ഫീസ് (നികുതി ഉൾപ്പെടെ) + report_header_delivery_shipping_cost: ഡെലിവറി ഷിപ്പിംഗ് ചെലവ് (നികുതി ഉൾപ്പെടെ) + report_header_transaction_fee: ഇടപാട് ഫീസ് (നികുതി ഇല്ല) + report_header_total_untaxable_admin: മൊത്തം നികുതി നൽകാത്ത അഡ്മിൻ ക്രമീകരണങ്ങൾ (നികുതി ഇല്ല) + report_header_total_taxable_admin: നികുതി നൽകേണ്ട മൊത്തം അഡ്മിൻ ക്രമീകരണങ്ങൾ (നികുതി ഉൾപ്പെടെ) + report_line_cost_of_produce: ഉല്പന്ന ചെലവ് + report_line_line_items: ലൈൻ ഇനങ്ങൾ + report_header_last_completed_order_date: അവസാനം പൂർത്തിയാക്കിയ ഓർഡർ തീയതി + report_xero_configuration: സീറോ കോൺഫിഗറേഷൻ + initial_invoice_number: "പ്രാരംഭ ഇൻവോയ്സ് നമ്പർ" + invoice_date: "രസീത് തീയതി" + due_date: "അവസാന തീയതി" + account_code: "അക്കൗണ്ട് കോഡ്" + equals: "തുല്യമായത്" + contains: "അടങ്ങിയിട്ടുള്ളത്" + discount: "കിഴിവ്" + filter_products: "ഉൽപ്പന്നങ്ങൾ ഫിൽട്ടർ ചെയ്യുക" + delete_product_variant: "അവസാന വേരിയന്റ് ഇല്ലാതാക്കാൻ കഴിയില്ല!" + progress: "പുരോഗതി" + saving: "സേവ് ചെയ്യുന്നു.." + success: "വിജയം" + failure: "പരാജയം" + unsaved_changes_confirmation: "സേവ് ചെയ്യാത്ത മാറ്റങ്ങൾ നഷ്ടപ്പെടും. എന്തായാലും തുടരണോ?" + one_product_unsaved: "ഒരു ഉൽപ്പന്നത്തിലേക്കുള്ള മാറ്റങ്ങൾ സേവ് ചെയ്തിട്ടില്ല" + products_unsaved: "%{n} ഉൽപ്പന്നങ്ങളിലേക്കുള്ള മാറ്റങ്ങൾ സേവ് ചെയ്തിട്ടില്ല." + is_already_manager: "ഇതിനകം ഒരു മാനേജരാണ്!" + no_change_to_save: "സേവ് ചെയ്യാൻ മാറ്റങ്ങളൊന്നുമില്ല" + user_invited: "ഈ എന്റർപ്രൈസ് മാനേജ് ചെയ്യാൻ %{email} ക്ഷണിച്ചു" + add_manager: "നിലവിലുള്ള ഒരു ഉപയോക്താവിനെ ചേർക്കുക" + users: "ഉപയോക്താക്കൾ" + about: "കുറിച്ച്" + images: "ചിത്രങ്ങൾ" + web: "വെബ്" + primary_details: "പ്രാഥമിക വിശദാംശങ്ങൾ" + social: "സാമൂഹികം" + shipping: "ഷിപ്പിംഗ്" + shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + payment_methods: "പേയ്മെന്റ് രീതികൾ" + payment_method_fee: "ഇടപാട് ഫീസ്" + payment_processing_failed: "പേയ്‌മെന്റ് പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല, നിങ്ങൾ നൽകിയ വിശദാംശങ്ങൾ പരിശോധിക്കുക" + payment_method_not_supported: "ആ പേയ്‌മെന്റ് രീതി പിന്തുണയ്ക്കുന്നില്ല. ദയവായി മറ്റൊന്ന് തിരഞ്ഞെടുക്കുക." + payment_updated: "പേയ്മെന്റ് അപ്ഡേറ്റ് ചെയ്തു" + cannot_perform_operation: "പേയ്മെന്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല" + action_required: "നടപടി ആവശ്യമാണ്" + tag_rules: "നിയമങ്ങൾ ടാഗ് ചെയ്യുക" + enterprise_fee_whole_order: മുഴുവൻ ഓർഡർ + enterprise_fee_by_name: " %{role} %{enterprise_name} മുഖേനയുള്ള %{name}ഫീസ്" + validation_msg_relationship_already_established: "^ആ ബന്ധം ഇതിനകം സ്ഥാപിതമാണ്." + validation_msg_at_least_one_hub: "^കുറഞ്ഞത് ഒരു ഹബ്ബെങ്കിലും തിരഞ്ഞെടുക്കണം" + validation_msg_tax_category_cant_be_blank: "^നികുതി വിഭാഗം ശൂന്യമാക്കാൻ കഴിയില്ല" + validation_msg_is_associated_with_an_exising_customer: "നിലവിലുള്ള ഒരു ഉപഭോക്താവുമായി ഇത് ബന്ധപ്പെട്ടിരിക്കുന്നു" + content_configuration_pricing_table: "(TODO: വിലനിർണ്ണയ പട്ടിക)" + content_configuration_case_studies: "(TODO: കേസ് സ്റ്റഡീസ്)" + content_configuration_detail: "(TODO: വിശദാംശങ്ങൾ)" + enterprise_name_error: "ഇതിനകം എടുത്തുകഴിഞ്ഞു. ഇത് നിങ്ങളുടെ എന്റർപ്രൈസ് ആണെങ്കിൽ നിങ്ങൾ ഉടമസ്ഥാവകാശം ക്ലെയിം ചെയ്യാൻ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, അല്ലെങ്കിൽ ഈ എന്റർപ്രൈസുമായി വ്യാപാരം ചെയ്യാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, ഈ പ്രൊഫൈലിന്റെ നിലവിലെ മാനേജരെ %{email} -ൽ ബന്ധപ്പെടുക." + enterprise_owner_error: "^ %{email} ന് കൂടുതൽ സംരംഭങ്ങൾ സ്വന്തമാക്കാൻ അനുവാദമില്ല (പരിധി %{enterprise_limit})." + enterprise_role_uniqueness_error: "^ആ ജോലി ഇതിനകം നിലവിലുണ്ട്." + enterprise_terms_and_conditions_type_error: "പിഡിഎഫ്-കൾ മാത്രമേ അനുവദിക്കൂ" + inventory_item_visibility_error: ശരിയോ തെറ്റോ ആയിരിക്കണം + product_importer_file_error: "തകരാറ്: ഒരു ഫയലും അപ്‌ലോഡ് ചെയ്‌തിട്ടില്ല" + product_importer_spreadsheet_error: "ഫയൽ പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല: അസാധുവായ ഫയൽ തരം" + product_importer_products_save_error: ഉൽപ്പന്നങ്ങളൊന്നും വിജയകരമായി സേവ് ചെയ്തില്ല + product_import_file_not_found_notice: 'ഫയൽ കണ്ടെത്തിയില്ല അല്ലെങ്കിൽ തുറക്കാൻ കഴിഞ്ഞില്ല' + product_import_no_data_in_spreadsheet_notice: 'സ്‌പ്രെഡ്‌ഷീറ്റിൽ ഡാറ്റയൊന്നും കണ്ടെത്തിയില്ല' + order_choosing_hub_notice: നിങ്ങളുടെ ഹബ് തിരഞ്ഞെടുത്തു. + order_cycle_selecting_notice: നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ തിരഞ്ഞെടുത്തു. + adjustments_tax_rate_error: "^ഈ ക്രമീകരണത്തിനുള്ള നികുതി നിരക്ക് ശരിയാണോയെന്ന് പരിശോധിക്കുക." + active_distributors_not_ready_for_checkout_message_singular: >- + %{distributor_names} എന്ന ഹബ് ഒരു സജീവ ഓർഡർ സൈക്കിളിൽ ലിസ്‌റ്റ് ചെയ്‌തിരിക്കുന്നു, + എന്നാൽ സാധുവായ ഷിപ്പിംഗ്, പേയ്‌മെന്റ് രീതികൾ ഇല്ല. നിങ്ങൾ ഇവ സജ്ജീകരിക്കുന്നത് + വരെ, ഉപഭോക്താക്കൾക്ക് ഈ ഹബ്ബിൽ ഷോപ്പിംഗ് നടത്താൻ കഴിയില്ല. + active_distributors_not_ready_for_checkout_message_plural: >- + %{distributor_names} എന്ന ഹബുകൾ ഒരു സജീവ ഓർഡർ സൈക്കിളിൽ ലിസ്‌റ്റ് ചെയ്‌തിട്ടുണ്ട്, + എന്നാൽ സാധുവായ ഷിപ്പിംഗ്, പേയ്‌മെന്റ് രീതികൾ ഇല്ല. നിങ്ങൾ ഇവ സജ്ജീകരിക്കുന്നത് + വരെ, ഉപഭോക്താക്കൾക്ക് ഈ ഹബ്ബുകളിൽ ഷോപ്പിംഗ് നടത്താൻ കഴിയില്ല. + enterprise_fees_update_notice: നിങ്ങളുടെ എന്റർപ്രൈസ് ഫീസ് അപ്ഡേറ്റ് ചെയ്തു. + enterprise_register_package_error: "ദയവായി ഒരു പാക്കേജ് തിരഞ്ഞെടുക്കുക" + enterprise_register_error: "%{enterprise} -നുള്ള രജിസ്ട്രേഷൻ പൂർത്തിയാക്കാൻ കഴിഞ്ഞില്ല" + enterprise_register_success_notice: "അഭിനന്ദനങ്ങൾ! %{enterprise} -നുള്ള രജിസ്‌ട്രേഷൻ പൂർത്തിയായി!" + enterprise_bulk_update_success_notice: "എന്റർപ്രൈസസ് വിജയകരമായി അപ്ഡേറ്റ് ചെയ്തു" + enterprise_bulk_update_error: 'അപ്ഡേറ്റ് പരാജയപ്പെട്ടു' + enterprise_shop_show_error: "നിങ്ങൾ തിരയുന്ന ഷോപ്പ് ഓഎഫ്എൻ-ൽ നിലവിലില്ല അല്ലെങ്കിൽ നിഷ്‌ക്രിയമാണ്. ദയവായി മറ്റ് കടകൾ പരിശോധിക്കുക." + order_cycles_bulk_update_notice: 'ഓർഡർ സൈക്കിളുകൾ അപ്ഡേറ്റ് ചെയ്തു.' + order_cycles_no_permission_to_coordinate_error: "ഒരു ഓർഡർ സൈക്കിൾ ഏകോപിപ്പിക്കാൻ നിങ്ങളുടെ സംരംഭങ്ങൾക്കൊന്നും അനുമതിയില്ല" + order_cycles_no_permission_to_create_error: "ആ എന്റർപ്രൈസ് ഏകോപിപ്പിച്ച ഒരു ഓർഡർ സൈക്കിൾ സൃഷ്ടിക്കാൻ നിങ്ങൾക്ക് അനുമതിയില്ല" + order_cycle_closed: "നിങ്ങൾ തിരഞ്ഞെടുത്ത ഓർഡർ സൈക്കിൾ ഇപ്പോൾ അടച്ചു. ദയവായി വീണ്ടും ശ്രമിക്കുക!" + back_to_orders_list: "ഓർഡർ ലിസ്റ്റിലേക്ക് മടങ്ങുക" + no_orders_found: "ഓർഡറുകളൊന്നും കണ്ടെത്തിയില്ല" + order_information: "ഓർഡർ വിവരം" + new_payment: "പുതിയ പേയ്മെന്റ്" + create_or_update_invoice: "ഇൻവോയ്സ് സൃഷ്ടിക്കുക അല്ലെങ്കിൽ അപ്ഡേറ്റ് ചെയ്യുക" + date_completed: "പൂർത്തിയായ തീയതി" + amount: "തുക" + invoice_number: "ഇൻവോയ്സ് നമ്പർ" + invoice_file: "ഫയൽ" + invalid_url: "'%{url}' എന്ന യുആർഎൽ അസാധുവാണ്" + state_names: + ready: തയ്യാറാണ് + pending: തീർപ്പാക്കാത്തത് + shipped: ഷിപ് ചെയ്തു + js: + saving: 'സേവ് ചെയ്യുന്നു..' + changes_saved: 'മാറ്റങ്ങൾ സേവ് ചെയ്തു.' + authorising: "അധികാരപ്പെടുത്തുന്നു..." + save_changes_first: ആദ്യം മാറ്റങ്ങൾ സേവ് ചെയ്യുക. + all_changes_saved: എല്ലാ മാറ്റങ്ങളും സേവ് ചെയ്തു + unsaved_changes: നിങ്ങൾക്ക് സേവ് ചെയ്യാത്ത മാറ്റങ്ങളുണ്ട് + all_changes_saved_successfully: എല്ലാ മാറ്റങ്ങളും വിജയകരമായി സേവ് ചെയ്തു + oh_no: "അയ്യോ! നിങ്ങളുടെ മാറ്റങ്ങൾ സേവ് ചെയ്യാൻ എനിക്ക് കഴിഞ്ഞില്ല." + unauthorized: "ഈ പേജ് ആക്സസ് ചെയ്യാൻ നിങ്ങൾക്ക് അധികാരമില്ല." + error: തകരാറ് + unavailable: ലഭ്യമല്ല + profile: പ്രൊഫൈൽ + hub: ഹബ് + shop: കട + choose: തിരഞ്ഞെടുക്കുക + resolve_errors: ഇനിപ്പറയുന്ന തകരാറുകൾ പരിഹരിക്കുക + more_items: "+ %{count} കൂടുതൽ" + default_card_updated: ഡിഫോൾട്ട് കാർഡ് അപ്ഡേറ്റ് ചെയ്തു + default_card_voids_auth: നിങ്ങളുടെ ഡിഫോൾട്ട് കാർഡ് മാറ്റുന്നത്, അത് ചാർജ് ചെയ്യുന്നതിനുള്ള കടകളുടെ നിലവിലുള്ള അംഗീകാരങ്ങൾ നീക്കം ചെയ്യും. ഡിഫോൾട്ട് കാർഡ് അപ്ഡേറ്റ് ചെയ്തതിന് ശേഷം നിങ്ങൾക്ക് ഷോപ്പുകൾക്ക് വീണ്ടും അംഗീകാരം നൽകാം. ഡിഫോൾട്ട് കാർഡ് മാറ്റാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ? + cart: + add_to_cart_failed: > + കാർട്ടിലേക്ക് ഈ ഉൽപ്പന്നം ചേർക്കുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായി. ഒരുപക്ഷേ അത് + ലഭ്യമല്ലാതാവുകയോ കട പൂട്ടുകയോ ചെയ്തേക്കാം. + admin: + unit_price_tooltip: "വ്യത്യസ്ത ഉൽപ്പന്നങ്ങളും പാക്കേജിംഗ് വലുപ്പങ്ങളും തമ്മിലുള്ള വിലകൾ എളുപ്പത്തിൽ താരതമ്യം ചെയ്യാൻ നിങ്ങളുടെ ഉപഭോക്താക്കളെ അനുവദിക്കുന്നതിലൂടെ യൂണിറ്റ് വില സുതാര്യത വർദ്ധിപ്പിക്കുന്നു. ശ്രദ്ധിക്കുക, കടയുടെ മുൻവശത്ത് പ്രദർശിപ്പിക്കുന്ന അവസാന യൂണിറ്റ് വിലയിൽ നികുതിയും ഫീസും ഉൾപ്പെടുന്നതിനാൽ വ്യത്യാസമുണ്ടാകാം." + enterprise_limit_reached: "ഓരോ അക്കൗണ്ടിനും എന്റർപ്രൈസസിന്റെ സ്റ്റാൻഡേർഡ് പരിധിയിൽ നിങ്ങൾ എത്തിയിരിക്കുന്നു. നിങ്ങൾക്ക് ഇത് വർദ്ധിപ്പിക്കണമെങ്കിൽ %{contact_email} -ലേക്ക് എഴുതുക." + deleting_item_will_cancel_order: "ഈ പ്രവർത്തനം ഒന്നോ അതിലധികമോ ശൂന്യമായ ഓർഡറുകൾക്ക് കാരണമാകും, അത് റദ്ദാക്കപ്പെടും. നിങ്ങൾക്ക് തുടരാൻ താൽപ്പര്യമുണ്ടോ?" + modals: + got_it: "മനസ്സിലായി" + confirm: "സ്ഥിരീകരിക്കുക" + close: "അടയ്ക്കുക" + continue: "തുടരുക" + cancel: "റദ്ദാക്കുക" + invite: "ക്ഷണിക്കുക" + invite_title: "രജിസ്റ്റർ ചെയ്യാത്ത ഒരു ഉപയോക്താവിനെ ക്ഷണിക്കുക" + tag_rule_help: + title: നിയമങ്ങൾ കൂട്ടിയോജിപ്പിക്കുക + overview: അവലോകനം + overview_text: > + ഏതൊക്കെ ഇനങ്ങൾ ദൃശ്യമാകുമെന്നോ അല്ലെങ്കിൽ ഏതൊക്കെ ഉപയോക്താക്കൾക്ക് ദൃശ്യമാകുമെന്നോ + വിവരിക്കുന്നതിനുള്ള ഒരു മാർഗം ടാഗ് റൂൾസ് നൽകുന്നു. ഇനങ്ങൾ ഷിപ്പിംഗ് + രീതികൾ, പേയ്‌മെന്റ് രീതികൾ, ഉൽപ്പന്നങ്ങൾ, ഓർഡർ സൈക്കിളുകൾ എന്നിവ ആകാം. + by_default_rules: "'സ്വതവേ...' നിയമങ്ങൾ" + by_default_rules_text: > + ഇനങ്ങൾ ഡിഫോൾട്ടായി ദൃശ്യമാകാത്ത വിധം മറയ്ക്കാൻ ഡിഫോൾട്ട് നിയമങ്ങൾ നിങ്ങളെ + അനുവദിക്കുന്നു. പ്രത്യേക ടാഗുകളുള്ള ഉപഭോക്താക്കൾക്കുള്ള സ്ഥിരമല്ലാത്ത + നിയമങ്ങളാൽ ഈ സ്വഭാവം അസാധുവാക്കാവുന്നതാണ്. + customer_tagged_rules: "'ഉപഭോക്താക്കൾ ടാഗ് ചെയ്‌ത...' നിയമങ്ങൾ" + customer_tagged_rules_text: > + ഒരു നിർദ്ദിഷ്‌ട ഉപഭോക്തൃ ടാഗുമായി ബന്ധപ്പെട്ട നിയമങ്ങൾ സൃഷ്‌ടിക്കുന്നതിലൂടെ, + നിർദ്ദിഷ്‌ട ടാഗ് ഉള്ള ഉപഭോക്താക്കൾക്കായി നിങ്ങൾക്ക് ഡിഫോൾട്ട് സ്വഭാവം + (അത് ഇനങ്ങൾ കാണിക്കാനോ മറയ്‌ക്കാനോ ആകട്ടെ) അസാധുവാക്കാനാകും. + terms_and_conditions_info: + title: "നിബന്ധനകളും വ്യവസ്ഥകളും അപ്‌ലോഡ് ചെയ്യുന്നു" + message_1: "വിൽപ്പനക്കാരനും വാങ്ങുന്നയാളും തമ്മിലുള്ള കരാറാണ് നിബന്ധനകളും വ്യവസ്ഥകളും എന്നതിലുള്ളത്. നിങ്ങൾ ഇവിടെ ഒരു ഫയൽ അപ്‌ലോഡ് ചെയ്യുകയാണെങ്കിൽ, ചെക്ക്ഔട്ട് പൂർത്തിയാക്കുന്നതിന് ഷോപ്പർമാർ നിങ്ങളുടെ നിബന്ധനകളും വ്യവസ്ഥകളും അംഗീകരിക്കണം. വാങ്ങുന്നയാൾക്ക് ഇത് ചെക്ക്ഔട്ടിൽ ഒരു ചെക്ക്ബോക്സായി ദൃശ്യമാകും, അത് ചെക്ക്ഔട്ടുമായി മുന്നോട്ട് പോകുന്നതിന് പരിശോധിക്കേണ്ടതാണ്. ദേശീയ നിയമനിർമ്മാണത്തിന് അനുസൃതമായി നിബന്ധനകളും വ്യവസ്ഥകളും അപ്‌ലോഡ് ചെയ്യാൻ ഞങ്ങൾ ശുപാർശ ചെയ്യുന്നു." + message_2: "ഉപഭോക്താക്കൾ ഒരിക്കൽ മാത്രം നിബന്ധനകളും വ്യവസ്ഥകളും അംഗീകരിച്ചാൽ മതിയാകും. എന്നിരുന്നാലും, നിങ്ങൾ നിബന്ധനകളും വ്യവസ്ഥകളും മാറ്റുകയാണെങ്കിൽ ഉപഭോക്താക്കൾ വീണ്ടും അവ അംഗീകരിക്കേണ്ടതുണ്ട്." + terms_and_conditions_warning: + title: "നിബന്ധനകളും വ്യവസ്ഥകളും അപ്‌ലോഡ് ചെയ്യുന്നു" + message_1: "നിങ്ങളുടെ എല്ലാ ഉപഭോക്താക്കളും ചെക്ക്ഔട്ട് ചെയ്യുമ്പോൾ ഒരിക്കൽ അവ സമ്മതിക്കേണ്ടി വരും. നിങ്ങൾ ഫയൽ അപ്ഡേറ്റ് ചെയ്യുകയാണെങ്കിൽ, ചെക്ക്ഔട്ടിൽ നിങ്ങളുടെ എല്ലാ ഉപഭോക്താക്കളും അവ വീണ്ടും അംഗീകരിക്കേണ്ടതുണ്ട്." + message_2: "സബ്‌സ്‌ക്രിപ്‌ഷനുള്ള ഉപഭോക്താക്കൾക്കായി, നിങ്ങൾ അവർക്ക് ഇപ്പോൾ നിബന്ധനകളും വ്യവസ്ഥകളും (അല്ലെങ്കിൽ അവയിലെ മാറ്റങ്ങൾ) ഇമെയിൽ ചെയ്യേണ്ടതുണ്ട്, ഈ പുതിയ നിബന്ധനകളെയും വ്യവസ്ഥകളെയും കുറിച്ച് അവരെ ഒന്നും അറിയിക്കില്ല." + business_address_info: + message: "കമ്പനിയുടെ നിയമപരമായ പേര്, നിയമപരമായ വിലാസം, നിയമപരമായ ഫോൺ നമ്പർ എന്നിവ അവരുടെ പൊതു വ്യാപാര വിവരങ്ങളിലേക്ക് വ്യത്യസ്‌ത വിശദാംശങ്ങളോടെ രജിസ്റ്റർ ചെയ്‌ത നിയമപരമായ സ്ഥാപനത്തിൽ നിന്ന് ഇൻവോയ്‌സ് ചെയ്യുന്ന ബിസിനസുകൾക്കായി ഉപയോഗിക്കുന്നു. ഈ വിശദാംശങ്ങൾ ഇൻവോയ്സുകളിൽ മാത്രമേ ഉപയോഗിക്കൂ. ഈ വിശദാംശങ്ങൾ ശൂന്യമാണെങ്കിൽ നിങ്ങളുടെ പൊതു പേരും വിലാസവും ഫോൺ നമ്പറും ഇൻവോയ്സുകളിൽ ഉപയോഗിക്കും." + panels: + save: സേവ് + saved: സേവ് ചെയ്തു + saving: സേവ് ചെയ്യുന്നു + enterprise_package: + hub_profile: ഹബ് പ്രൊഫൈൽ + hub_profile_cost: "ചെലവ്: എപ്പോഴും സൗജന്യം" + hub_profile_text1: > + ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിൽ ആളുകൾക്ക് നിങ്ങളെ കണ്ടെത്താനും ബന്ധപ്പെടാനും + കഴിയും. നിങ്ങളുടെ എന്റർപ്രൈസ് മാപ്പിൽ ദൃശ്യമാകും, കൂടാതെ ലിസ്റ്റിംഗുകളിൽ + തിരയാനും കഴിയും. + hub_profile_text2: > + ഒരു പ്രൊഫൈൽ ഉണ്ടായിരിക്കുന്നതും ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലൂടെ നിങ്ങളുടെ + പ്രാദേശിക ആഹാര സംവിധാനത്തിൽ കണക്ഷനുകൾ ഉണ്ടാക്കുന്നതും എല്ലായ്പ്പോഴും + സൗജന്യമായിരിക്കും. + hub_shop: ഹബ് ഷോപ്പ് + hub_shop_text1: > + നിങ്ങളുടെ പ്രാദേശിക ആഹാര സമ്പ്രദായത്തിന്റെ നട്ടെല്ലാണ് നിങ്ങളുടെ സംരംഭം. + നിങ്ങൾക്ക് മറ്റ് സംരംഭങ്ങളിൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ സമാഹരിക്കുകയും ഓപ്പൺ + ഫുഡ് നെറ്റ്‌വർക്കിലെ നിങ്ങളുടെ ഷോപ്പ് വഴി വിൽക്കുകയും ചെയ്യാം. + hub_shop_text2: > + ഹബുകൾക്ക് പല രൂപങ്ങൾ എടുക്കാം, അവ ഒരു ഫുഡ് സഹകരണ സ്ഥാപനം, ഒരു വാങ്ങൽ + ഗ്രൂപ്പ്, ഒരു വെജി-ബോക്സ് പ്രോഗ്രാം അല്ലെങ്കിൽ ഒരു പ്രാദേശിക പലചരക്ക് + കട എന്നിവ ആകാം. + hub_shop_text3: > + നിങ്ങളുടെ സ്വന്തം ഉൽപ്പന്നങ്ങൾ വിൽക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, + ഈ എന്റർപ്രൈസ് ഒരു പ്രൊഡ്യൂസർ ആയി മാറേണ്ടതുണ്ട്. + choose_package: ദയവായി ഒരു പാക്കേജ് തിരഞ്ഞെടുക്കുക + choose_package_text1: > + ഇടതുവശത്തുള്ള ഓപ്ഷനുകളിൽ നിന്ന് ഒരു പാക്കേജ് തിരഞ്ഞെടുക്കുന്നത് വരെ + നിങ്ങളുടെ എന്റർപ്രൈസ് പൂർണ്ണമായും സജീവമാകില്ല. + choose_package_text2: > + ഓരോ പാക്കേജിനെക്കുറിച്ചും കൂടുതൽ വിശദമായ വിവരങ്ങൾ കാണുന്നതിന് ഒരു ഓപ്‌ഷനിൽ + ക്ലിക്ക് ചെയ്യുക, നിങ്ങൾ പൂർത്തിയാക്കുമ്പോൾ ചുവന്ന സേവ് ബട്ടൺ അമർത്തുക! + profile_only: പ്രൊഫൈൽ മാത്രം + profile_only_cost: "ചെലവ്: എപ്പോഴും സൗജന്യം" + profile_only_text1: > + ഒരു പ്രൊഫൈൽ നിങ്ങളെ മറ്റുള്ളവർക്ക് ദൃശ്യവും കോൺടാക്റ്റ് ചെയ്യാവുന്നതുമാക്കുന്നു, + നിങ്ങളുടെ കഥ പങ്കിടാനുള്ള ഒരു മാർഗമാണിത്. + profile_only_text2: > + നിങ്ങൾ ആഹാരം ഉൽപ്പാദിപ്പിക്കുന്നതിൽ ശ്രദ്ധ കേന്ദ്രീകരിക്കുകയും അത് മറ്റൊരാൾക്ക് + വിൽക്കുന്ന ജോലി ഉപേക്ഷിക്കുകയും ചെയ്യുകയാണെങ്കിൽ, നിങ്ങൾക്ക് ഓപ്പൺ ഫുഡ് + നെറ്റ്‌വർക്കിൽ ഒരു ഷോപ്പ് ആവശ്യമില്ല. + profile_only_text3: > + ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ ചേർക്കുക, നിങ്ങളുടെ + ഉൽപ്പന്നങ്ങൾ അവരുടെ സ്റ്റോറുകളിൽ സ്റ്റോക്ക് ചെയ്യാൻ ഹബുകളെ അനുവദിക്കുന്നു. + producer_shop: പ്രൊഡ്യൂസർ ഷോപ്പ് + producer_shop_text1: > + നിങ്ങളുടെ സ്വന്തം ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് ഷോപ്പ് ഫ്രണ്ട് വഴി നിങ്ങളുടെ + ഉൽപ്പന്നങ്ങൾ ഉപഭോക്താക്കൾക്ക് നേരിട്ട് വിൽക്കുക. + producer_shop_text2: > + ഒരു പ്രൊഡ്യൂസർ ഷോപ്പ് നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾക്ക് മാത്രമുള്ളതാണ്, സൈറ്റിന് + പുറത്ത് ഉൽപ്പാദിപ്പിച്ച ഉൽപ്പന്നങ്ങൾ വിൽക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, + ദയവായി 'പ്രൊഡ്യൂസർ ഹബ്' തിരഞ്ഞെടുക്കുക. + producer_hub: പ്രൊഡ്യൂസർ ഹബ് + producer_hub_text1: > + നിങ്ങളുടെ പ്രാദേശിക ആഹാര സമ്പ്രദായത്തിന്റെ നട്ടെല്ലാണ് നിങ്ങളുടെ സംരംഭം. + ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ ഷോപ്പ് ഫ്രണ്ട് വഴി നിങ്ങൾക്ക് നിങ്ങളുടെ സ്വന്തം + ഉൽപ്പന്നങ്ങളും മറ്റ് സംരംഭങ്ങളിൽ നിന്ന് സമാഹരിച്ച ഉൽപ്പന്നങ്ങളും വിൽക്കാൻ + കഴിയും. + producer_hub_text2: > + പ്രൊഡ്യൂസർ ഹബ്‌സിന് പല രൂപങ്ങൾ എടുക്കാം, അവ ഒരു സിഎസ്‌എ, വെജി-ബോക്‌സ് + പ്രോഗ്രാം, അല്ലെങ്കിൽ റൂഫ്‌ടോപ്പ് ഗാർഡൻ ഉള്ള ആഹാര സഹകരണ സ്ഥാപനം എന്നിങ്ങനെ. + producer_hub_text3: > + ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്ക് കഴിയുന്നത്ര ഹബ് മോഡലുകളെ പിന്തുണയ്‌ക്കാൻ ലക്ഷ്യമിടുന്നു, + അതിനാൽ നിങ്ങളുടെ സാഹചര്യം പ്രശ്‌നമല്ല, നിങ്ങളുടെ സ്ഥാപനമോ പ്രാദേശിക + ഭക്ഷണ ബിസിനസോ പ്രവർത്തിപ്പിക്കുന്നതിന് ആവശ്യമായ സൗകര്യങ്ങൾ നൽകാൻ ഞങ്ങൾ + ആഗ്രഹിക്കുന്നു. + get_listing: ഒരു ലിസ്റ്റിംഗ് നേടുക + always_free: എപ്പോഴും സൗജന്യം + sell_produce_others: മറ്റുള്ളവരിൽ നിന്നുള്ള ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + sell_own_produce: നിങ്ങളുടെ സ്വന്തം ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + sell_both: തന്നിൽ നിന്നും മറ്റുള്ളവരിൽ നിന്നും ഉള്ള ഉൽപ്പന്നങ്ങൾ വിൽക്കുക + enterprise_producer: + producer: പ്രൊഡ്യൂസർ + producer_text1: > + പ്രൊഡ്യൂസേഴ്‌സ് കഴിക്കാനും/അല്ലെങ്കിൽ കുടിക്കാനും സ്വാദിഷ്ടമായ കാര്യങ്ങൾ + ഉണ്ടാക്കുന്നു. നിങ്ങൾ അത് കൃഷി ചെയ്താലും, വളർത്തിയാലും, പാകം ചെയ്താലും, + ചുട്ടാലും, പുളിപ്പിച്ചാലും അല്ലെങ്കിൽ വാർത്തെടുത്താലും നിങ്ങൾ ഒരു പ്രൊഡ്യൂസർ + ആണ്. + producer_text2: > + മറ്റ് സംരംഭങ്ങളിൽ നിന്നുള്ള ആഹാരം സമാഹരിച്ച് ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ + ഒരു ഷോപ്പ് വഴി വിൽക്കുന്നത് പോലുള്ള മറ്റ് പ്രവർത്തനങ്ങളും പ്രൊഡ്യൂസർമാർക്ക് + ചെയ്യാൻ കഴിയും. + non_producer: നോൺ പ്രൊഡ്യൂസർ + non_producer_text1: > + ഉൽപ്പാദകരല്ലാത്തവർ സ്വയം ഒരു ഭക്ഷണവും ഉൽപ്പാദിപ്പിക്കുന്നില്ല, അതായത് + ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലൂടെ വിൽപ്പനയ്‌ക്കായി സ്വന്തം ഉൽപ്പന്നങ്ങൾ സൃഷ്ടിക്കാൻ + അവർക്ക് കഴിയില്ല. + non_producer_text2: > + പകരം, ഉൽപ്പാദകരല്ലാത്തവർ ഉത്പാദകരെ ഉപഭോക്താക്കളുമായി ബന്ധിപ്പിക്കുന്നതിൽ + വൈദഗ്ദ്ധ്യം നേടുന്നു, അത് സമാഹരിക്കുകയോ ഗ്രേഡിംഗ് ചെയ്യുകയോ പാക്കുചെയ്യുകയോ + ഭക്ഷണം വിൽക്കുകയോ വിതരണം ചെയ്യുകയോ മൂലമാകാം. + producer_desc: ആഹാര ഉത്പാദകർ + producer_example: ഉദാ. ഗ്രോവേഴ്സ്, ബേക്കേഴ്സ്, ബ്രൂവേഴ്സ്, മേക്കേഴ്സ് + non_producer_desc: മറ്റെല്ലാ ഭക്ഷ്യ സംരംഭങ്ങളും + non_producer_example: ഉദാ. പലചരക്ക് കടകൾ, ഭക്ഷ്യ സഹകരണ സ്ഥാപനങ്ങൾ, വാങ്ങൽ ഗ്രൂപ്പുകൾ + enterprise_status: + status_title: "%{name} സജ്ജീകരിച്ചു, മുന്നോട്ടുപോകാൻ തയ്യാറാണ്!" + severity: തീവ്രത + description: വിവരണം + resolve: പരിഹരിക്കുക + exchange_products: + load_more_variants: "കൂടുതൽ വേരിയന്റുകൾ ലോഡ് ചെയ്യുക" + load_all_variants: "എല്ലാ വേരിയന്റുകളും ലോഡ് ചെയ്യുക" + select_all_variants: "%{total_number_of_variants} വേരിയന്റുകളും തിരഞ്ഞെടുക്കുക" + variants_loaded: "%{num_of_variants_loaded} ൽ %{total_number_of_variants} വേരിയന്റുകൾ ലോഡ് ചെയ്തു" + loading_variants: "വേരിയന്റുകൾ ലോഡുചെയ്യുന്നു" + no_variants: "ഈ ഉൽപ്പന്നത്തിന് വേരിയന്റൊന്നും ലഭ്യമല്ല (ഇൻവെന്ററി ക്രമീകരണങ്ങൾ വഴി മറച്ചിരിക്കുന്നു)." + some_variants_hidden: "(ചില വേരിയന്റുകൾ ഇൻവെന്ററി ക്രമീകരണങ്ങൾ വഴി മറച്ചിരിക്കാം)" + tag_rules: + shipping_method_tagged_top: "ഷിപ്പിംഗ് രീതികൾ ടാഗ് ചെയ്‌തു" + shipping_method_tagged_bottom: "ഇവ:" + payment_method_tagged_top: "പേയ്‌മെന്റ് രീതികൾ ടാഗ് ചെയ്‌തു" + payment_method_tagged_bottom: "ഇവ:" + order_cycle_tagged_top: "ഓർഡർ സൈക്കിളുകൾ ടാഗ് ചെയ്‌തു" + order_cycle_tagged_bottom: "ഇവ:" + inventory_tagged_top: "ഇൻവെന്ററി വേരിയന്റുകൾ ടാഗ് ചെയ്‌തു" + inventory_tagged_bottom: "ഇവ:" + new_tag_rule_dialog: + select_rule_type: "ഒരു റൂൾ തരം തിരഞ്ഞെടുക്കുക:" + add_rule: "നിയമം ചേർക്കുക" + enterprise_fees: + inherit_from_product: "ഉൽപ്പന്നത്തിൽ നിന്ന് അവകാശം നേടുക" + orders: + index: + per_page: "ഓരോ പേജിലും %{results}" + view_file: "ഫയൽ കാണുക" + compiling_invoices: "ഇൻവോയ്സുകൾ സമാഹരിക്കുന്നു" + bulk_invoice_created: "ബൾക്ക് ഇൻവോയ്സ് സൃഷ്ടിച്ചു" + bulk_invoice_failed: "ബൾക്ക് ഇൻവോയ്സ് സൃഷ്ടിക്കുന്നതിൽ പരാജയപ്പെട്ടു" + please_wait: "ഈ മോഡൽ അടയ്ക്കുന്നതിന് മുമ്പ് പിഡിഎഫ് തയ്യാറാകുന്നത് വരെ കാത്തിരിക്കുക." + order_state: + address: "വിലാസം" + adjustments: "മാറ്റംവരുത്തലുകൾ" + awaiting_return: "തിരിച്ചുവരവിനായി കാത്തിരിക്കുന്നു" + canceled: "റദ്ദാക്കി" + cart: "കാർട്ട്" + complete: "പൂർത്തിയായത്" + confirm: "സ്ഥിരീകരിക്കുക" + delivery: "ഡെലിവറി" + paused: "താൽക്കാലികമായി നിർത്തി" + payment: "പേയ്മെന്റ്" + pending: "തീർച്ചപ്പെടാത്ത" + resumed: "പുനരാരംഭിച്ചു" + returned: "തിരിച്ചയച്ചു" + confirmation: "സ്ഥിരീകരണം" + shipment_states: + backorder: "ബാക്ക്ഓർഡർ" + partial: "ഭാഗികമായ" + pending: "തീർച്ചപ്പെടാത്ത" + ready: "തയ്യാർ" + shipped: "അയച്ചു" + canceled: "റദ്ദാക്കി" + payment_states: + balance_due: "ബാക്കി" + completed: "പൂർത്തിയാക്കിയത്" + checkout: "ചെക്ക് ഔട്ട്" + credit_owed: "കടബാധ്യത" + failed: "പരാജയപ്പെട്ടു" + paid: "പണം നൽകി" + pending: "തീർച്ചപ്പെടാത്ത" + requires_authorization: "അനുമതി ആവശ്യമാണ്" + processing: "പ്രോസസ്സിംഗ്" + void: "ശൂന്യം" + invalid: "അസാധുവാണ്" + quantity_unavailable: "ആവശ്യത്തിന് സ്റ്റോക്ക് ലഭ്യമല്ല. ലൈൻ ഇനം സേവ് ചെയ്തിട്ടില്ല!" + quantity_unchanged: "മുൻ അളവിൽ നിന്ന് മാറ്റമില്ല." + cancel_the_order_html: "ഇത് നിലവിലെ ഓർഡർ റദ്ദാക്കും.
നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + cancel_the_order_send_cancelation_email: "ഉപഭോക്താവിന് ഒരു റദ്ദാക്കൽ ഇമെയിൽ അയയ്ക്കുക" + restock_item: "റീസ്റ്റോക്ക് ഇനങ്ങൾ: ഈ ഇനം സ്റ്റോക്കിലേക്ക് തിരികെ നൽകുക" + restock_items: "റീസ്റ്റോക്ക് ഇനങ്ങൾ: എല്ലാ ഇനങ്ങളും സ്റ്റോക്കിലേക്ക് തിരികെ നൽകുക" + delete_line_items_html: + one: "ഇത് ഓർഡറിൽ നിന്ന് ഒരു വരി ഇനം ഇല്ലാതാക്കും.
നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + other: "ഇത് ഓർഡറിൽ നിന്ന് %{count} ലൈൻ ഇനങ്ങൾ ഇല്ലാതാക്കും.
നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + resend_user_email_confirmation: + resend: "വീണ്ടും അയയ്ക്കുക" + sending: "വീണ്ടും അയയ്‌ക്കുക..." + done: "വീണ്ടും അയച്ചു ✓" + failed: "വീണ്ടും അയയ്‌ക്കാനായില്ല ✗" + order_cycles: + schedules: + adding_a_new_schedule: "ഒരു പുതിയ ഷെഡ്യൂൾ ചേർക്കുന്നു" + updating_a_schedule: "ഒരു ഷെഡ്യൂൾ അപ്ഡേറ്റ് ചെയ്യുന്നു" + create_schedule: "ഷെഡ്യൂൾ സൃഷ്ടിക്കുക" + update_schedule: "ഷെഡ്യൂൾ അപ്ഡേറ്റ് ചെയ്യുക" + delete_schedule: "ഷെഡ്യൂൾ ഡിലീറ്റ് ചെയ്യുക" + schedule_name_placeholder: "ഷെഡ്യൂൾ പേര്" + created_schedule: "ഷെഡ്യൂൾ സൃഷ്ടിച്ചു" + updated_schedule: "ഷെഡ്യൂൾ അപ്ഡേറ്റ് ചെയ്തു" + deleted_schedule: "ഷെഡ്യൂൾ ഡിലീറ്റ് ചെയ്തു" + name_required_error: "ദയവായി ഈ ഷെഡ്യൂളിന് ഒരു പേര് നൽകുക" + no_order_cycles_error: "ദയവായി ഒരു ഓർഡർ സൈക്കിളെങ്കിലും തിരഞ്ഞെടുക്കുക (വലിച്ചിടുക)" + available: "ലഭ്യമാണ്" + selected: "തിരഞ്ഞെടുത്തത്" + customers: + index: + add_customer: "ഉപഭോക്താവിനെ ചേർക്കുക" + add_a_new_customer_for: "%{shop_name} -നായി ഒരു പുതിയ ഉപഭോക്താവിനെ ചേർക്കുക" + customer_placeholder: "customer@example.org" + valid_email_error: "സാധുതയുള്ള ഒരു ഇമെയിൽ വിലാസം നൽകുക" + subscriptions: + error_saving: "സബ്സ്ക്രിപ്ഷൻ സേവ് ചെയ്യുന്നതിൽ തകരാറ് സംഭവിച്ചു" + new: + please_select_a_shop: "ദയവായി ഒരു ഷോപ്പ് തിരഞ്ഞെടുക്കുക" + enterprises: + form: + images: + removed_logo_successfully: "ലോഗോ വിജയകരമായി നീക്കം ചെയ്തു" + immediate_logo_removal_warning: "നിങ്ങൾ സ്ഥിരീകരിച്ചതിന് ശേഷം ഉടൻ തന്നെ ലോഗോ നീക്കം ചെയ്യപ്പെടും." + removed_promo_image_successfully: "പ്രമോ ചിത്രം വിജയകരമായി നീക്കം ചെയ്തു" + immediate_promo_image_removal_warning: "നിങ്ങൾ സ്ഥിരീകരിച്ചതിന് ശേഷം ഉടൻ തന്നെ പ്രമോ ചിത്രം നീക്കം ചെയ്യപ്പെടും." + immediate_terms_and_conditions_removal_warning: "നിങ്ങൾ സ്ഥിരീകരിച്ചതിന് ശേഷം ഉടൻ തന്നെ നിബന്ധനകളും വ്യവസ്ഥകളും എന്ന ഫയൽ നീക്കം ചെയ്യപ്പെടും." + removed_terms_and_conditions_successfully: "നിബന്ധനകളും വ്യവസ്ഥകളും എന്ന ഫയൽ വിജയകരമായി നീക്കം ചെയ്തു" + insufficient_stock: "മതിയായ സ്റ്റോക്ക് ലഭ്യമല്ല, %{on_hand} മാത്രം ശേഷിക്കുന്നു" + out_of_stock: + reduced_stock_available: കുറഞ്ഞ സ്റ്റോക്ക് ലഭ്യമാണ് + out_of_stock_text: > + നിങ്ങൾ ഷോപ്പിംഗ് നടത്തുമ്പോൾ, നിങ്ങളുടെ കാർട്ടിലെ ഒന്നോ അതിലധികമോ ഉൽപ്പന്നങ്ങളുടെ + സ്റ്റോക്ക് ലെവലുകൾ കുറഞ്ഞു. എന്താണ് മാറിയതെന്ന് ഇവിടെ പറയുന്നു: + now_out_of_stock: ഇത് ഇപ്പോൾ സ്റ്റോക്കില്ല. + only_n_remainging: "ഇപ്പോൾ %{num} മാത്രമേ ശേഷിക്കുന്നുള്ളൂ." + shopfront: + variant: + add_to_cart: "ചേർക്കുക" + in_cart: "കാർട്ടിൽ" + quantity_in_cart: "കാർട്ടിൽ %{quantity}" + remaining_in_stock: "%{quantity} മാത്രം ശേഷിക്കുന്നു" + bulk_buy_modal: + min_quantity: "കുറഞ്ഞ അളവ്" + max_quantity: "പരമാവധി അളവ്" + price_breakdown: "വില വിശ്ലേഷണം" + unit_price_tooltip: "ഇതാണ് ഈ ഉൽപ്പന്നത്തിന്റെ യൂണിറ്റ് വില. പാക്കേജിംഗ് വലുപ്പത്തിലും ഭാരത്തിലും നിന്ന് സ്വതന്ത്രമായി ഉൽപ്പന്നങ്ങളുടെ വില താരതമ്യം ചെയ്യാൻ ഇത് നിങ്ങളെ അനുവദിക്കുന്നു." + variants: + on_demand: + 'yes': "ആവശ്യപ്പെടുന്നതനുസരിച്ച്" + variant_overrides: + on_demand: + use_producer_settings: "പ്രൊഡ്യൂസർ സ്റ്റോക്ക് ക്രമീകരണങ്ങൾ ഉപയോഗിക്കുക" + 'yes': "അതെ" + 'no': "ഇല്ല" + inventory_products: "ഇൻവെന്ററി ഉൽപ്പന്നങ്ങൾ" + hidden_products: "മറഞ്ഞിരിക്കുന്ന ഉൽപ്പന്നങ്ങൾ" + new_products: "പുതിയ ഉൽപ്പന്നങ്ങൾ" + reset_stock_levels: സ്റ്റോക്ക് ലെവലുകൾ ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജമാക്കുക + changes_to: -എന്നതിലേക്കുള്ള മാറ്റങ്ങൾ + one_override: ഒന്ന് അസാധുവാക്കുക + overrides: അസാധുവാക്കുന്നു + remain_unsaved: സേവ് ചെയ്യാതെ തുടരുന്നു. + no_changes_to_save: സേവ് ചെയ്യാൻ മാറ്റങ്ങളൊന്നുമില്ല.' + no_authorisation: "ആ മാറ്റങ്ങൾ സേവ് ചെയ്യാൻ എനിക്ക് അംഗീകാരം നേടാനായില്ല, അതിനാൽ അവ സേവ് ചെയ്യപ്പെട്ടിട്ടില്ല." + some_trouble: "%{errors} സേവ് ചെയ്യുന്നതിൽ എനിക്ക് കുറച്ച് പ്രശ്നമുണ്ടായിരുന്നു: " + changing_on_hand_stock: ഹാൻഡ് സ്റ്റോക്ക് ലെവൽ മാറുന്നു... + stock_reset: സ്റ്റോക്കുകൾ ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യുന്നു. + tag_rules: + show_hide_variants: 'എന്റെ ഷോപ്പ് ഫ്രണ്ടിൽ വേരിയന്റുകൾ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക' + show_hide_shipping: 'ചെക്ക്ഔട്ടിൽ ഷിപ്പിംഗ് രീതികൾ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക' + show_hide_payment: 'ചെക്ക്ഔട്ടിൽ പേയ്മെന്റ് രീതികൾ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക' + show_hide_order_cycles: 'എന്റെ ഷോപ്പ് ഫ്രണ്ടിൽ ഓർഡർ സൈക്കിളുകൾ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക' + visible: ദൃശ്യമാണ് + not_visible: ദൃശ്യമല്ല + services: + unsaved_changes_message: സേവ് ചെയ്യപ്പെടാത്ത മാറ്റങ്ങൾ നിലവിൽ നിലവിലുണ്ട്, ഇപ്പോൾ സേവ് ചെയ്യണോ അതോ അവഗണിക്കണോ? + save: സേവ് ചെയ്യുക + ignore: അവഗണിക്കുക + add_to_order_cycle: "ഓർഡർ സൈക്കിളിലേക്ക് ചേർക്കുക" + manage_products: "ഉൽപ്പന്നങ്ങൾ കൈകാര്യം ചെയ്യുക" + edit_profile: "പ്രൊഫൈൽ എഡിറ്റ് ചെയ്യുക" + add_products_to_inventory: "ഇൻവെന്ററിയിലേക്ക് ഉൽപ്പന്നങ്ങൾ ചേർക്കുക" + resources: + could_not_delete_customer: 'ഉപഭോക്താവിനെ ഇല്ലാതാക്കാൻ കഴിഞ്ഞില്ല' + product_import: + confirmation: | + ഇതിനായുള്ള എല്ലാ ഉൽപ്പന്നങ്ങളുടെയും സ്റ്റോക്ക് ലെവൽ പൂജ്യമായി സജ്ജമാക്കും + അപ്‌ലോഡ് ചെയ്ത ഫയലിൽ ഇല്ലാത്ത എന്റർപ്രൈസ്. + order_cycles: + create_failure: "ഓർഡർ സൈക്കിൾ സൃഷ്ടിക്കുന്നതിൽ പരാജയപ്പെട്ടു" + update_success: 'നിങ്ങളുടെ ഓർഡർ സൈക്കിൾ അപ്ഡേറ്റ് ചെയ്തു.' + update_failure: "ഓർഡർ സൈക്കിൾ അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു" + no_distributors: ഈ ഓർഡർ സൈക്കിളിൽ വിതരണക്കാരില്ല. നിങ്ങൾ ഒരെണ്ണം ചേർക്കുന്നത് വരെ ഈ ഓർഡർ സൈക്കിൾ ഉപഭോക്താക്കൾക്ക് ദൃശ്യമാകില്ല. ഈ ഓർഡർ സൈക്കിൾ സേവ് ചെയ്യുന്നത് തുടരാൻ നിങ്ങൾക്ക് താൽപ്പര്യമുണ്ടോ?' + enterprises: + producer: "പ്രൊഡ്യൂസർ" + non_producer: "നോൺ പ്രൊഡ്യൂസർ" + customers: + select_shop: 'ആദ്യം ഒരു ഷോപ്പ് തിരഞ്ഞെടുക്കുക' + could_not_create: ക്ഷമിക്കണം! സൃഷ്ടിക്കാൻ കഴിഞ്ഞില്ല + subscriptions: + closes: അടയ്ക്കുന്നു + closed: അടച്ചു + close_date_not_set: അടയ്ക്കുന്ന തീയതി സജ്ജീകരിച്ചിട്ടില്ല + spree: + users: + order: "ഓർഡർ ചെയ്യുക" + registration: + welcome_to_ofn: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലേക്ക് സ്വാഗതം!" + signup_or_login: "സൈൻ അപ്പ് ചെയ്തുകൊണ്ട് ആരംഭിക്കുക (അല്ലെങ്കിൽ ലോഗിൻ ചെയ്യുക)" + have_an_account: "ഇതിനകം ഒരു അക്കൗണ്ട് ഉണ്ടോ?" + action_login: "ഇപ്പോൾ ലോഗിൻ ചെയ്യുക." + stripe_elements: + unknown_error_from_stripe: | + ഞങ്ങളുടെ പേയ്‌മെന്റ് ഗേറ്റ്‌വേയിൽ നിങ്ങളുടെ കാർഡ് സജ്ജീകരിക്കുന്നതിൽ ഒരു തകരാറുണ്ടായി. + പേജ് പുതുക്കിയ ശേഷം വീണ്ടും ശ്രമിക്കുക, ഇത് രണ്ടാം തവണയും പരാജയപ്പെടുകയാണെങ്കിൽ, + പിന്തുണയ്ക്കായി ഞങ്ങളെ ബന്ധപ്പെടുക. + inflections: + each: + one: "ഓരോന്നും" + other: "ഓരോന്നും" + bunch: + one: "കുല" + other: "കൂട്ടങ്ങൾ" + pack: + one: "പായ്ക്ക്" + other: "പാക്കുകൾ" + box: + one: "പെട്ടി" + other: "പെട്ടികൾ" + bottle: + one: "കുപ്പി" + other: "കുപ്പികൾ" + jar: + one: "ഭരണി" + other: "ജാറുകൾ" + head: + one: "തല" + other: "ഹെഡ്സ്" + bag: + one: "ബാഗ്" + other: "ബാഗുകൾ" + loaf: + one: "അപ്പം" + other: "ബ്രെഡ്‌ഡുകൾ" + single: + one: "സിംഗിൾ" + other: "സിംഗിൾസ്" + tub: + one: "ടബ്" + other: "ട്യൂബുകൾ" + punnet: + one: "പന്നറ്റ്" + other: "പഴക്കൂടകൾ" + packet: + one: "പാക്കറ്റ്" + other: "പാക്കറ്റുകൾ" + item: + one: "ഇനം" + other: "ഇനങ്ങൾ" + dozen: + one: "ഡസൻ" + other: "ഡസനുകൾ" + unit: + one: "യൂണിറ്റ്" + other: "യൂണിറ്റുകൾ" + serve: + one: "സേവിക്കുക" + other: "സേവിക്കുന്നു" + tray: + one: "ട്രേ" + other: "ട്രേകൾ" + piece: + one: "കഷണം" + other: "കഷണങ്ങൾ" + pot: + one: "കലം" + other: "പാത്രങ്ങൾ" + bundle: + one: "ബണ്ടിൽ" + other: "ബണ്ടിലുകൾ" + flask: + one: "ഫ്ലാസ്ക്" + other: "ഫ്ലാസ്കുകൾ" + basket: + one: "കൊട്ടയിൽ" + other: "കൊട്ടകൾ" + sack: + one: "ചാക്ക്" + other: "ചാക്കുകൾ" + producers: + signup: + start_free_profile: "ഒരു സൗജന്യ പ്രൊഫൈലിൽ ആരംഭിക്കുക, നിങ്ങൾ തയ്യാറാകുമ്പോൾ വികസിപ്പിക്കുക!" + order_management: + reports: + bulk_coop: + filters: + bulk_coop_allocation: "ബൾക്ക് കോ-ഓപ് അലോക്കേഷൻ" + bulk_coop_customer_payments: "ബൾക്ക് കോ-ഓപ്പ് ഉപഭോക്തൃ പേയ്‌മെന്റുകൾ" + bulk_coop_packing_sheets: "ബൾക്ക് കോ-ഓപ്പ് പാക്കിംഗ് ഷീറ്റുകൾ" + bulk_coop_supplier_report: "ബൾക്ക് കോ-ഓപ്പ് സപ്ലയർ റിപ്പോർട്ട്" + enterprise_fee_summaries: + filters: + date_range: "തീയതി പരിധി" + report_format_csv: "സി.എസ്.വി ഫയൽ ആയി ഡൗൺലോഡ് ചെയ്യുക" + generate_report: "റിപ്പോർട്ട് സൃഷ്ടിക്കുക" + report: + none: "ഒന്നുമില്ല" + select_and_search: "നിങ്ങളുടെ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ ഫിൽട്ടറുകൾ തിരഞ്ഞെടുത്ത് റിപ്പോർട്ട് സൃഷ്ടിക്കുക എന്നതിൽ ക്ലിക്ക് ചെയ്യുക." + enterprise_fee_summary: + date_end_before_start_error: "ആരംഭിച്ചതിന് ശേഷമായിരിക്കണം" + parameter_not_allowed_error: "ഈ റിപ്പോർട്ടിനായി തിരഞ്ഞെടുത്ത ഒന്നോ അതിലധികമോ ഫിൽട്ടറുകൾ ഉപയോഗിക്കാൻ നിങ്ങൾക്ക് അധികാരമില്ല." + fee_calculated_on_transfer_through_all: "എല്ലാം" + fee_calculated_on_transfer_through_entire_orders: "മുഴുവൻ ഓർഡറുകളും %{distributor} വഴി" + tax_category_various: "വിവിധ" + fee_type: + payment_method: "പേയ്മെന്റ് ഇടപാട്" + shipping_method: "ഷിപ്മെന്റ്" + fee_placements: + supplier: "ഇൻകമിംഗ്" + distributor: "ഔട്ട്ഗോയിംഗ്" + coordinator: "കോർഡിനേറ്റർ" + tax_category_name: + shipping_instance_rate: "പ്ലാറ്റ്ഫോം നിരക്ക്" + formats: + csv: + header: + fee_type: "ഫീസ് തരം" + enterprise_name: "എന്റർപ്രൈസ് ഉടമ" + fee_name: "ഫീസ് പേര്" + customer_name: "ഉപഭോക്താവ്" + fee_placement: "ഫീസ് പ്ലേസ്മെന്റ്" + fee_calculated_on_transfer_through_name: "കൈമാറ്റം ചെയ്യുമ്പോൾ ഉള്ള ഫീസ് കണക്ക്" + tax_category_name: "നികുതി വിഭാഗം" + total_amount: "$$ ആകെ" + html: + header: + fee_type: "ഫീസ് തരം" + enterprise_name: "എന്റർപ്രൈസ് ഉടമ" + fee_name: "ഫീസ് പേര്" + customer_name: "ഉപഭോക്താവ്" + fee_placement: "ഫീസ് പ്ലേസ്മെന്റ്" + fee_calculated_on_transfer_through_name: "കൈമാറ്റം ചെയ്യുമ്പോൾ ഉള്ള ഫീസ് കണക്ക്" + tax_category_name: "നികുതി വിഭാഗം" + total_amount: "$$ ആകെ" + invalid_filter_parameters: "ഈ റിപ്പോർട്ടിനായി നിങ്ങൾ തിരഞ്ഞെടുത്ത ഫിൽട്ടറുകൾ അസാധുവാണ്." + report: + none: "ഒന്നുമില്ല" + order: "ഓർഡർ ചെയ്യുക" + order_details: "ഓർഡർ വിശദാംശങ്ങൾ" + customer_details: "ഉപഭോക്തൃ വിശദാംശങ്ങൾ" + adjustments: "മാറ്റംവരുത്തലുകൾ" + payments: "പേയ്മെന്റുകൾ" + return_authorizations: "റിട്ടേൺ അനുമതികൾ" + credit_owed: "കടപ്പെട്ടിരിക്കുന്നു" + new_adjustment: "പുതിയ മാറ്റംവരുത്തലുകൾ" + payment: "പേയ്മെന്റ്" + payment_method: "പണമടയ്ക്കൽ രീതി" + shipment: "ഷിപ്മെന്റ്" + shipment_inc_vat: "വാറ്റ് ഉൾപ്പെടെയുള്ള ഷിപ്മെന്റ്" + shipping_tax_rate: "ഷിപ്പിംഗ് നികുതി നിരക്ക്" + category: "വിഭാഗം" + import_date: "ഇറക്കുമതി തീയതി" + delivery: "ഡെലിവറി" + temperature_controlled: "താപ നിയന്ത്രിതം" + new_product: "പുതിയ ഉൽപ്പന്നം" + administration: "ഭരണകൂടം" + logged_in_as: "ലോഗിൻ ചെയ്തത്" + account: "അക്കൗണ്ട്" + logout: "ലോഗൗട്ട്" + date_range: "തീയതി പരിധി" + status: "സ്ഥിതി" + new: "പുതിയത്" + start: "ആരംഭിക്കുക" + end: "അവസാനിപ്പിക്കുക" + stop: "നിർത്തുക" + first: "ആദ്യം" + previous: "മുന്നിലത്തേത്" + last: "അവസാനത്തെത്" + webhook_endpoints: + create: + success: വെബ്ഹൂക് എൻഡ്‌പോയിന്റ് വിജയകരമായി സൃഷ്‌ടിച്ചു + error: വെബ്ഹൂക് എൻഡ്‌പോയിന്റ് സൃഷ്‌ടിക്കുന്നതിൽ പരാജയപ്പെട്ടു + destroy: + success: വെബ്ഹൂക് എൻഡ്‌പോയിന്റ് വിജയകരമായി ഇല്ലാതാക്കി + error: വെബ്ഹൂക് എൻഡ്‌പോയിന്റ് ഇല്ലാതാക്കുന്നതിൽ പരാജയപ്പെട്ടു + spree: + order_updated: "ഓർഡർ അപ്ഡേറ്റ് ചെയ്തു" + add_country: "രാജ്യം ചേർക്കുക" + add_state: "സ്റ്റേറ്റ് ചേർക്കുക" + adjustment: "മാറ്റംവരുത്തലുകൾ" + all: "എല്ലാം" + associated_adjustment_closed: "അനുബന്ധ മാറ്റംവരുത്തലുകൾ അടച്ചു" + back_to_adjustments_list: "മാറ്റംവരുത്തലുകളിലേക്ക് മടങ്ങുക" + back_to_users_list: "ഉപയോക്താക്കളിലേക്ക് മടങ്ങുക" + back_to_zones_list: "സോണുകളിലേക്ക് മടങ്ങുക" + card_code: "കാർഡ് കോഡ്" + card_number: "കാർഡ് നമ്പർ" + category: "വിഭാഗം" + created_successfully: "വിജയകരമായി സൃഷ്ടിച്ചു" + credit: "ക്രെഡിറ്റ്" + editing_tax_category: "നികുതി വിഭാഗം എഡിറ്റുചെയ്യുന്നു" + editing_tax_rate: "നികുതി നിരക്ക് എഡിറ്റുചെയ്യുന്നു" + editing_zone: "എഡിറ്റിംഗ് സോൺ" + expiration: "കാലഹരണപ്പെടൽ" + invalid_payment_provider: "പേയ്‌മെന്റ് ദാതാവ് അസാധുവാണ്" + items_cannot_be_shipped: "സാധനങ്ങൾ അയക്കാൻ കഴിയില്ല" + gateway_config_unavailable: "ഗേറ്റ്‌വേ കോൺഫിഗറേഷൻ ലഭ്യമല്ല" + gateway_error: "പേയ്‌മെന്റ് പരാജയപ്പെട്ടു" + more: "കൂടുതൽ" + new_adjustment: "പുതിയ മാറ്റംവരുത്തൽ" + new_tax_category: "പുതിയ നികുതി വിഭാഗം" + new_taxon: "പുതിയ ടാക്സൺ" + new_user: "പുതിയ ഉപയോക്താവ്" + no_pending_payments: "തീർപ്പാക്കാത്ത പേയ്‌മെന്റുകളൊന്നുമില്ല" + none: "ഒന്നുമില്ല" + not_found: "കണ്ടെത്തിയില്ല" + notice_messages: + variant_deleted: "വേരിയന്റ് ഇല്ലാതാക്കി" + payment_method_not_supported: "പേയ്‌മെന്റ് രീതി പിന്തുണയ്ക്കുന്നില്ല" + resend_authorization_email: "അനുമതി ഇമെയിൽ വീണ്ടും അയയ്ക്കുക" + rma_credit: "ആർഎംഎ ക്രെഡിറ്റ്" + refund: "റീഫണ്ട്" + server_error: "സെർവർ തകരാർ" + shipping_method_names: + UPS Ground: "യുപിഎസ് ഗ്രൗണ്ട്" + pick_up: "ഫാമിൽ നിന്നും പിക്കപ്പ് ചെയ്യുക" + delivery: "ഒപ്പിട്ടു, സീൽ ചെയ്തു, എത്തിച്ചു" + start_date: "ആരംഭിക്കുന്ന തീയതി" + successfully_removed: "വിജയകരമായി നീക്കം ചെയ്തു" + taxonomy_edit: "ടാക്സോണമി എഡിറ്റ്" + tree: "ട്രീ" + updating: "അപ്ഡേറ്റ് ചെയ്യുന്നു" + your_order_is_empty_add_product: "നിങ്ങളുടെ ഓർഡർ ശൂന്യമാണ്, മുകളിൽ ഒരു ഉൽപ്പന്നം തിരയുകയും ചേർക്കുകയും ചെയ്യുക" + add_product: "ഉൽപ്പന്നം ചേർക്കുക" + name_or_sku: "പേര് അല്ലെങ്കിൽ എസ്കെയു (ഉൽപ്പന്നത്തിന്റെ പേരിന്റെ ആദ്യ 4 പ്രതീകങ്ങളെങ്കിലും നൽകുക)" + resend: "വീണ്ടും അയയ്ക്കുക" + back_to_orders_list: "ഓർഡർ ലിസ്റ്റിലേക്ക് മടങ്ങുക" + back_to_payments_list: "പേയ്‌മെന്റ് ലിസ്റ്റിലേക്ക് മടങ്ങുക" + return_authorizations: "റിട്ടേൺ അനുമതികൾ" + cannot_create_returns: "ഈ ഓർഡറിന് ഷിപ്പ് ചെയ്‌ത യൂണിറ്റുകൾ ഇല്ലാത്തതിനാൽ റിട്ടേണുകൾ സൃഷ്‌ടിക്കാനാവില്ല." + select_stock: "സ്റ്റോക്ക് തിരഞ്ഞെടുക്കുക" + location: "സ്ഥാനം" + count_on_hand: "കൈയിൽ എണ്ണുക" + quantity: "അളവ്" + on_demand: "ആവശ്യപ്പെടുന്നതനുസരിച്ച്" + on_hand: "കയ്യിൽ" + package_from: "പാക്കേജ് എവിടെ നിന്നും-" + item_description: "ഇനത്തെ കുറിച്ചുള്ള വിശദീകരണം" + price: "വില" + total: "ആകെ" + edit: "എഡിറ്റ് ചെയ്യുക" + split: "രണ്ടായി പിരിക്കുക" + delete: "ഇല്ലാതാക്കുക" + cannot_set_shipping_method_without_address: "ഉപഭോക്തൃ വിശദാംശങ്ങൾ നൽകുന്നതുവരെ ഷിപ്പിംഗ് രീതി സജ്ജീകരിക്കാൻ കഴിയില്ല." + no_tracking_present: "ട്രാക്കിംഗ് വിശദാംശങ്ങളൊന്നും നൽകിയിട്ടില്ല." + tracking: "ട്രാക്കിംഗ്" + tracking_number: "ട്രാക്കിംഗ് നമ്പർ" + order_total: "ഓർഡർ ആകെ" + customer_details: "ഉപഭോക്തൃ വിശദാംശങ്ങൾ" + customer_details_updated: "ഉപഭോക്തൃ വിശദാംശങ്ങൾ അപ്ഡേറ്റ് ചെയ്തു" + customer_search: "ഉപഭോക്തൃ തിരയൽ" + choose_a_customer: "ഒരു ഉപഭോക്താവിനെ തിരഞ്ഞെടുക്കുക" + account: "അക്കൗണ്ട്" + billing_address: "ബില്ലിംഗ് വിലാസം" + shipping_address: "ഷിപ്പിംഗ് വിലാസം" + first_name: "ഒന്നാം പേര് " + last_name: "പേരിന്റെ അവസാന ഭാഗം" + street_address: "സ്ട്രീറ്റ് വിലാസം" + street_address_2: "വിലാസം (തുടർച്ച)" + city: "നഗരം" + zip: "പിൻകോഡ്" + country: "രാജ്യം" + state: "സ്റ്റേറ്റ്" + phone: "ഫോൺ" + update: "അപ്ഡേറ്റ് ചെയ്യുക" + use_billing_address: "ബില്ലിംഗ് വിലാസം ഉപയോഗിക്കുക" + adjustments: "മാറ്റംവരുത്തലുകൾ" + continue: "തുടരുക" + fill_in_customer_info: "ഉപഭോക്തൃ വിവരങ്ങൾ പൂരിപ്പിക്കുക" + credit_card: "ക്രെഡിറ്റ് കാർഡ്" + new_payment: "പുതിയ പേയ്മെന്റ്" + capture: "ക്യാപ്‌ചർ" + capture_and_complete_order: "ക്യാപ്‌ചർ ചെയ്‌ത് ഓർഡർ പൂർത്തിയാക്കുക" + void: "ശൂന്യം" + login: "ലോഗിൻ" + password: "പാസ്സ്‌വേർഡ്" + signature: "കയ്യൊപ്പ്" + solution: "പരിഹാരം" + landing_page: "ലാൻഡിംഗ് പേജ്" + server: "സെർവർ" + test_mode: "ടെസ്റ്റ് മോഡ്" + logourl: "ലോഗോ യുആർഎൽ" + are_you_sure_delete: "ഈ റെക്കോർഡ് ഇല്ലാതാക്കണമെന്ന് തീർച്ചയാണോ?" + confirm_delete: "ഇല്ലാതാക്കൽ സ്ഥിരീകരിക്കുക" + configurations: "കോൺഫിഗറേഷനുകൾ" + general_settings: "പൊതുവായ ക്രമീകരണങ്ങൾ" + site_name: "സൈറ്റിന്റെ പേര്" + site_url: "സൈറ്റ് യുആർഎൽ" + default_seo_title: "ഡിഫോൾട്ട് എസ്ഇഓ ശീർഷകം" + default_meta_description: "ഡിഫോൾട്ട് മെറ്റാ വിവരണം" + default_meta_keywords: "ഡിഫോൾട്ട് മെറ്റാ കീവേഡുകൾ" + currency_decimal_mark: "കറൻസി ഡെസിമൽ മാർക്ക്" + currency_settings: "കറൻസി ക്രമീകരണങ്ങൾ" + currency_symbol_position: '"രൂപയിലുള്ള തുകയ്ക്ക് മുൻപാണോ അതോ ശേഷമാണോ കറൻസി ചിഹ്നം" ഇടേണ്ടത്?' + currency_thousands_separator: "കറൻസി സെപ്പറേറ്റർ ചിഹ്നം" + hide_cents: "പൈസ മറയ്ക്കുക" + display_currency: "കറൻസി പ്രദർശിപ്പിക്കുക" + choose_currency: "കറൻസി തിരഞ്ഞെടുക്കുക" + mail_method_settings: "മെയിൽ രീതി ക്രമീകരണങ്ങൾ" + mail_settings_notice_html: "ഡീബഗ്ഗിംഗിനായി മാത്രം ഇവിടെ വരുത്തിയ മാറ്റങ്ങൾ താൽക്കാലികമായിരിക്കും, ഭാവിയിൽ അത് പഴയപടിയായേക്കാം.
ഇൻസ്‌റ്റൻസിന്റെ രഹസ്യങ്ങൾ അപ്‌ഡേറ്റ് ചെയ്‌ത് ഓഫ്-ഇൻസ്റ്റാൾ ഉപയോഗിച്ച് പ്രൊവിഷൻ ചെയ്‌ത് ശാശ്വതമായ മാറ്റങ്ങൾ വരുത്താനാകും. കൂടുതൽ വിവരങ്ങൾക്ക് ഓഎഫ്എൻ ഗ്ലോബൽ ടീമിനെ സമീപിക്കുക." + general: "പൊതുവായത്" + enable_mail_delivery: "മെയിൽ ഡെലിവറി പ്രവർത്തനക്ഷമമാക്കുക" + send_mails_as: "മെയിലുകൾ ഇങ്ങനെ അയയ്ക്കുക" + smtp_send_all_emails_as_from_following_address: "ഇനിപ്പറയുന്ന വിലാസത്തിൽ നിന്ന് എല്ലാ മെയിലുകളും അയയ്ക്കുക." + send_copy_of_all_mails_to: "എല്ലാ മെയിലുകളുടെയും പകർപ്പ് ഇതിലേക്ക് അയയ്ക്കുക" + smtp_send_copy_to_this_addresses: "ഈ വിലാസത്തിലേക്ക് എല്ലാ ഔട്ട്‌ഗോയിംഗ് മെയിലുകളുടെയും ഒരു പകർപ്പ് അയയ്ക്കുന്നു. ഒന്നിലധികം വിലാസങ്ങൾ കോമ ഉപയോഗിച്ച് വേർതിരിക്കുക." + tax_categories: "നികുതി വിഭാഗങ്ങൾ" + listing_tax_categories: "നികുതി വിഭാഗങ്ങളുടെ പട്ടിക" + back_to_tax_categories_list: "നികുതി വിഭാഗങ്ങളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + tax rate: "നികുതി നിരക്കുകൾ" + new_tax_rate: "പുതിയ നികുതി നിരക്ക്" + tax_category: "നികുതി വിഭാഗം" + tax_rates: "നികുതി നിരക്കുകൾ" + rate: "നിരക്ക്" + tax_rate_amount_explanation: "കണക്കുകൂട്ടലുകളെ സഹായിക്കുന്നതിനുള്ള ഒരു ദശാംശ തുകയാണ് നികുതി നിരക്കുകൾ, (അതായത് നികുതി നിരക്ക് 5% ആണെങ്കിൽ 0.05 നൽകുക)" + included_in_price: "വിലയിൽ ഉൾപ്പെടുത്തിയിട്ടുണ്ട്" + show_rate_in_label: "ലേബലിൽ നിരക്ക് കാണിക്കുക" + back_to_tax_rates_list: "നികുതി നിരക്കുകളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + tax_settings: "നികുതി ക്രമീകരണങ്ങൾ" + zones: "സോണുകൾ" + new_zone: "പുതിയ സോൺ" + default_tax: "സ്ഥിരസ്ഥിതി നികുതി" + default_tax_zone: "സ്ഥിരസ്ഥിതി നികുതി സോൺ" + country_based: "രാജ്യം അടിസ്ഥാനമാക്കിയുള്ളത്" + state_based: "സംസ്ഥാനം അടിസ്ഥാനമാക്കിയുള്ളത്" + countries: "രാജ്യങ്ങൾ" + listing_countries: "രാജ്യങ്ങളുടെ പട്ടിക" + iso_name: "ഐഎസ്ഓ നാമം" + states_required: "ആവശ്യമുള്ള സംസ്ഥാനങ്ങൾ" + editing_country: "രാജ്യം എഡിറ്റ് ചെയ്യുന്നു" + back_to_countries_list: "രാജ്യങ്ങളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + states: "സംസ്ഥാനങ്ങൾ" + abbreviation: "ചുരുക്കെഴുത്ത്" + new_state: "പുതിയ സംസ്ഥാനം" + payment_methods: "പേയ്മെന്റ് രീതികൾ" + taxonomies: "ടാക്സോണമികൾ" + new_taxonomy: "പുതിയ ടാക്സോണമി" + back_to_taxonomies_list: "ടാക്‌സോണമി ലിസ്റ്റിലേക്ക് മടങ്ങുക" + shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + shipping_method: "ഷിപ്പിംഗ് രീതി" + shipment: "കയറ്റുമതി" + payment: "പേയ്മെന്റ്" + status: "സ്ഥിതി" + shipping_categories: "ഷിപ്പിംഗ് വിഭാഗങ്ങൾ" + new_shipping_category: "പുതിയ ഷിപ്പിംഗ് വിഭാഗം" + back_to_shipping_categories: "ഷിപ്പിംഗ് വിഭാഗങ്ങളിലേക്ക് മടങ്ങുക" + editing_shipping_category: "ഷിപ്പിംഗ് വിഭാഗം എഡിറ്റുചെയ്യുന്നു" + name: "പേര്" + description: "വിവരണം" + type: "ഇനം" + default: "സ്ഥിരസ്ഥിതി" + calculator: "കാൽക്കുലേറ്റർ" + zone: "സോൺ" + display: "പ്രദർശിപ്പിക്കുക" + environment: "പരിസ്ഥിതി" + active: "സജീവമാണ്" + nore: "കൂടുതൽ" + no_results: "ഫലങ്ങളൊന്നുമില്ല" + create: "ഉണ്ടാക്കുക" + loading: "ലോഡിംഗ്" + flat_percent: "ശതമാനം" + per_kg: "കിലോയ്ക്ക്" + amount: "തുക" + currency: "കറൻസി" + first_item: "ആദ്യ ഇനത്തിന്റെ വില" + additional_item: "അധിക ഇനത്തിന്റെ വില" + max_items: "പരമാവധി ഇനങ്ങൾ" + minimal_amount: "കുറഞ്ഞ തുക" + normal_amount: "സാധാരണ തുക" + discount_amount: "കിഴിവ് തുക" + no_images_found: "ചിത്രങ്ങളൊന്നും കണ്ടെത്തിയില്ല" + new_image: "പുതിയ ചിത്രം" + filename: "ഫയലിന്റെ പേര്" + alt_text: "ഇതര വാചകം" + thumbnail: "ലഘുചിത്രം" + back_to_images_list: "ചിത്രങ്ങളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + email: ഇമെയിൽ + account_updated: "അക്കൗണ്ട് അപ്ഡേറ്റ് ചെയ്തു!" + email_updated: "പുതിയ ഇമെയിൽ സ്ഥിരീകരിച്ചുകഴിഞ്ഞാൽ അക്കൗണ്ട് അപ്‌ഡേറ്റ് ചെയ്യും." + show_api_key_view_toggled: "ഷോ എപിഐ കീ വീക്ഷണം മാറ്റി!" + my_account: "എന്റെ അക്കൗണ്ട്" + date: "തീയതി" + time: "സമയം" + inventory_error_flash_for_insufficient_quantity: "നിങ്ങളുടെ കാർട്ടിലെ ഒരു ഇനം ലഭ്യമല്ലാതായി." + inventory: ഇൻവെന്ററി + zipcode: പിൻ കോഡ് + weight: ഭാരം (കിലോ അല്ലെങ്കിൽ പൗണ്ട്) + error_user_destroy_with_orders: "പൂർത്തിയാക്കിയ ഓർഡറുകൾ ഉള്ള ഉപയോക്താക്കളെ ഡിലീറ്റ് ചെയ്യാൻ പാടില്ല" + cannot_create_payment_without_payment_methods: "പേയ്‌മെന്റ് രീതികളൊന്നും നിർവചിക്കാതെ നിങ്ങൾക്ക് ഒരു ഓർഡറിനായി പേയ്‌മെന്റ് ഉണ്ടാക്കാനാവില്ല." + please_define_payment_methods: "ദയവായി ആദ്യം ചില പേയ്മെന്റ് രീതികൾ നിർവ്വചിക്കുക." + options: "ഓപ്ഷനുകൾ" + has_no_shipped_units: "ഷിപ്പ് ചെയ്ത യൂണിറ്റുകൾ ഇല്ല" + successfully_created: '%{resource} വിജയകരമായി സൃഷ്‌ടിച്ചു!' + successfully_updated: '%{resource} വിജയകരമായി അപ്‌ഡേറ്റ് ചെയ്‌തു!' + payment_method: "പണമടയ്ക്കൽ രീതി" + payment_processing_failed: "പേയ്‌മെന്റ് പ്രോസസ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല, നിങ്ങൾ നൽകിയ വിശദാംശങ്ങൾ പരിശോധിക്കുക" + not_available: "ബാധകമല്ല" + sku: "എസ്.കെ.യു" + there_are_no_items_for_this_order: "ഈ ഓർഡറിന് ഇനങ്ങളൊന്നുമില്ല." + order_populator: + out_of_stock: '%{item} സ്റ്റോക്കില്ല.' + actions: + update: "അപ്ഡേറ്റ് ചെയ്യുക" + cancel: "റദ്ദാക്കുക" + shared: + error_messages: + errors_prohibited_this_record_from_being_saved: + one: "1 തകരാർ ഈ റെക്കോർഡ് സേവ് ചെയ്യുന്നത് തടഞ്ഞു:" + few: "%{count} തകരാറുകൾ ഈ റെക്കോർഡ് സേവ് ചെയ്യുന്നത് തടഞ്ഞു:" + many: "%{count} തകരാറുകൾ ഈ റെക്കോർഡ് സേവ് ചെയ്യുന്നത് തടഞ്ഞു:" + other: "%{count} തകരാറുകൾ ഈ റെക്കോർഡ് സേവ് ചെയ്യുന്നത് തടഞ്ഞു:" + there_were_problems_with_the_following_fields: "ഇനിപ്പറയുന്ന ഫീൽഡുകളിൽ പ്രശ്‌നങ്ങളുണ്ടായി" + payments_list: + date_time: "തീയതി / സമയം" + amount: "തുക" + payment_method: "പണമടയ്ക്കൽ രീതി" + payment_state: "പേയ്മെന്റ് സ്റ്റേറ്റ്" + errors: + messages: + included_price_validation: "നിങ്ങൾ ഒരു ഡിഫോൾട്ട് ടാക്സ് സോൺ സജ്ജീകരിച്ചിട്ടില്ലെങ്കിൽ തിരഞ്ഞെടുക്കാൻ കഴിയില്ല" + blank: "ശൂന്യമായിരിക്കാൻ കഴിയില്ല" + invalid_instagram_url: "ഉപയോക്തൃ നാമം മാത്രമായിരിക്കണം ഉദാ. പ്രൊഫ" + layouts: + admin: + login_nav: + header: + store: സ്റ്റോർ + validation: + must_be_int: "ഒരു പൂർണ്ണസംഖ്യ ആയിരിക്കണം" + admin: + mail_methods: + send_testmail: "ടെസ്റ്റ് ഇമെയിൽ അയയ്ക്കുക" + testmail: + delivery_success: "ടെസ്റ്റ് ഇമെയിൽ അയച്ചു." + error: "ടെസ്റ്റ് ഇമെയിൽ അയയ്‌ക്കാൻ ശ്രമിക്കുമ്പോൾ ഒരു തകരാറ്‌ സംഭവിച്ചു." + unit_price_tooltip: "വ്യത്യസ്ത ഉൽപ്പന്നങ്ങളും പാക്കേജിംഗ് വലുപ്പങ്ങളും തമ്മിലുള്ള വിലകൾ എളുപ്പത്തിൽ താരതമ്യം ചെയ്യാൻ നിങ്ങളുടെ ഉപഭോക്താക്കളെ അനുവദിക്കുന്നതിലൂടെ യൂണിറ്റ് വില സുതാര്യത വർദ്ധിപ്പിക്കുന്നു. ശ്രദ്ധിക്കുക, കടയുടെ മുൻവശത്ത് പ്രദർശിപ്പിക്കുന്ന അവസാന യൂണിറ്റ് വിലയിൽ നികുതിയും ഫീസും ഉൾപ്പെടുന്നതിനാൽ വ്യത്യാസമുണ്ടാകാം." + subscriptions: + number: "നമ്പർ" + tab: + dashboard: "ഡാഷ്ബോർഡ്" + orders: "ഓർഡറുകൾ" + bulk_order_management: "ബൾക്ക് ഓർഡർ മാനേജ്മെന്റ്" + subscriptions: "സബ്സ്ക്രിപ്ഷനുകൾ" + products: "ഉൽപ്പന്നങ്ങൾ" + option_types: "ഓപ്ഷൻ തരങ്ങൾ" + properties: "പ്രോപ്പർട്ടികൾ" + variant_overrides: "ഇൻവെന്ററി" + reports: "റിപ്പോർട്ടുകൾ" + configuration: "കോൺഫിഗറേഷൻ" + users: "ഉപയോക്താക്കൾ" + roles: "കർത്തവ്യങ്ങൾ" + order_cycles: "ഓർഡർ സൈക്കിളുകൾ" + enterprises: "സംരംഭങ്ങൾ" + enterprise_relationships: "അനുമതികൾ" + customers: "ഉപഭോക്താക്കൾ" + groups: "ഗ്രൂപ്പുകൾ" + oidc_settings: "ഓഐഡിസി ക്രമീകരണങ്ങൾ" + product_properties: + index: + inherits_properties_checkbox_hint: "%{supplier} ൽ നിന്ന് പ്രോപ്പർട്ടികൾ അവകാശമാക്കണോ? (മുകളിൽ അസാധുവായില്ലെങ്കിൽ)" + add_product_properties: "ഉൽപ്പന്ന ഗുണവിശേഷങ്ങൾ ചേർക്കുക" + properties: + index: + properties: "ഗുണവിശേഷങ്ങൾ" + new_property: "പുതിയ പ്രോപ്പർട്ടി" + name: "പേര്" + presentation: "അവതരണം" + new: + new_property: "പുതിയ പ്രോപ്പർട്ടി" + edit: + editing_property: "പ്രോപ്പർട്ടി എഡിറ്റ് ചെയ്യുന്നു" + back_to_properties_list: "പ്രോപ്പർട്ടീസ് ലിസ്റ്റിലേക്ക് മടങ്ങുക" + form: + name: "പേര്" + presentation: "അവതരണം" + return_authorizations: + index: + new_return_authorization: "പുതിയ റിട്ടേൺ അനുമതി" + return_authorizations: "റിട്ടേൺ അനുമതികൾ" + back_to_orders_list: "ഓർഡർ ലിസ്റ്റിലേക്ക് മടങ്ങുക" + rma_number: "ആർഎംഎ നമ്പർ" + status: "സ്ഥിതി" + amount: "തുക" + cannot_create_returns: "ഈ ഓർഡറിന് ഷിപ്പ് ചെയ്‌ത യൂണിറ്റുകൾ ഇല്ലാത്തതിനാൽ റിട്ടേണുകൾ സൃഷ്‌ടിക്കാനാവില്ല." + continue: "തുടരുക" + new: + new_return_authorization: "പുതിയ റിട്ടേൺ അനുമതി" + back_to_return_authorizations_list: "റിട്ടേൺ അനുമതി ലിസ്റ്റിലേക്ക് മടങ്ങുക" + continue: "തുടരുക" + edit: + receive: "സ്വീകരിക്കുക" + are_you_sure: "നിങ്ങൾക്ക് ഉറപ്പാണോ?" + return_authorization: "റിട്ടേൺ അനുമതി" + form: + product: "ഉൽപ്പന്നം" + quantity_shipped: "അളവ് അയച്ചു" + quantity_returned: "അളവ് തിരിച്ചയച്ചു" + return_quantity: "തിരിച്ചയച്ച അളവ്" + amount: "തുക" + rma_value: "ആർഎംഎ മൂല്യം" + reason: "കാരണം" + stock_location: "സ്റ്റോക്ക് സ്ഥാനം" + states: + authorized: "അധികാരപ്പെടുത്തിയത്" + received: "ലഭിച്ചു" + canceled: "റദ്ദാക്കി" + line_items: + index: + results_found: "%{number} ഫലങ്ങൾ കണ്ടെത്തി." + viewing: "%{start} മുതൽ %{end} വരെ കാണുന്നു." + orders: + add_product: + cannot_add_item_to_canceled_order: "റദ്ദാക്കിയ ഓർഡറിലേക്ക് ഇനം ചേർക്കാൻ കഴിയില്ല" + include_out_of_stock_variants: "സ്റ്റോക്ക് ലഭ്യമല്ലാത്ത വേരിയന്റുകൾ ഉൾപ്പെടുത്തുക" + index: + listing_orders: "ഓർഡറുകളുടെ പട്ടിക" + new_order: "പുതിയ ഓർഡർ" + capture: "പിടിച്ചെടുക്കുക" + ship: "കപ്പൽ" + edit: "എഡിറ്റ് ചെയ്യുക" + order_not_updated: "ഓർഡർ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിഞ്ഞില്ല" + note: "കുറിപ്പ്" + first: "ആദ്യം" + last: "അവസാനത്തെത്" + previous: "മുന്നിലത്തേത്" + next: "അടുത്തത്" + loading: "ലോഡിംഗ്" + no_orders_found: "ഓർഡറുകളൊന്നും കണ്ടെത്തിയില്ല" + results_found: "%{number} ഫലങ്ങൾ കണ്ടെത്തി." + viewing: "%{start} മുതൽ %{end} വരെ കാണുന്നു." + print_invoices: "ഇൻവോയ്സുകൾ അച്ചടിക്കുക" + cancel_orders: "ഓർഡറുകൾ റദ്ദാക്കുക" + resend_confirmation: "സ്ഥിരീകരണം അയയ്ക്കുക" + resend_confirmation_confirm_html: "ഇത് സ്ഥിരീകരണ ഇമെയിൽ ഉപഭോക്താവിന് വീണ്ടും അയയ്ക്കും.
നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + send_invoice: "ഇൻവോയ്‌സുകൾ അയയ്‌ക്കുക" + send_invoice_confirm_html: "തിരഞ്ഞെടുത്ത എല്ലാ പൂർണ്ണമായ ഓർഡറുകൾക്കും ഇത് ഉപഭോക്തൃ ഇൻവോയ്‌സുകൾ ഇമെയിൽ ചെയ്യും.
നിങ്ങൾക്ക് തുടരണമെന്ന് തീർച്ചയാണോ?" + selected: + zero: "ഓർഡർ ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല" + one: "1 ഓർഡർ തിരഞ്ഞെടുത്തു" + other: "%{count} ഓർഡറുകൾ തിരഞ്ഞെടുത്തു" + sortable_header: + payment_state: "പേയ്മെന്റ് സ്റ്റേറ്റ്" + shipment_state: "ഷിപ്പിംഗ് സ്റ്റേറ്റ്" + completed_at: "പൂർത്തിയാക്കിയത്" + number: "നമ്പർ" + state: "സ്റ്റേറ്റ്" + email: "ഉപഭോക്താവിന്റെ ഇ-മെയിൽ" + invoice: + issued_on: "പ്രസിദ്ധീകരിച്ചത്" + tax_invoice: "നികുതി ഇൻവോയ്സ്" + code: "കോഡ്" + from: "നിന്ന്" + to: "-നുള്ള ബിൽ" + shipping: "ഷിപ്പിംഗ്" + order_number: "ഓർഡർ നമ്പർ" + invoice_number: "ഇൻവോയ്സ് നമ്പർ" + payments_list: + date_time: "തീയതി / സമയം" + payment_method: "പണമടയ്ക്കൽ രീതി" + payment_state: "പേയ്മെന്റ് സ്റ്റേറ്റ്" + amount: "തുക" + note: + note_label: "കുറിപ്പ്:" + no_note_present: "കുറിപ്പൊന്നും നൽകിയിട്ടില്ല." + form: + distribution_fields: + title: "വിതരണം" + distributor: "വിതരണക്കാരൻ:" + order_cycle: "ഓർഡർ സൈക്കിൾ:" + line_item_adjustments: "ലൈൻ ഇനം മാറ്റംവരുത്തലുകൾ" + order_adjustments: "ഓർഡർ മാറ്റംവരുത്തലുകൾ" + order_total: "ഓർഡർ ആകെ" + overview: + enterprises_header: + ofn_with_tip: എന്റർപ്രൈസസ് എന്നത് പ്രൊഡ്യൂസർമാർ കൂടാതെ/അല്ലെങ്കിൽ ഹബുകൾ ആണ്, മാത്രമല്ല അത് ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലെ ഓർഗനൈസേഷന്റെ അടിസ്ഥാന യൂണിറ്റുമാണ്. + enterprise_row: + has_no_enterprise_fees: "എന്റർപ്രൈസ് ഫീസ് ഇല്ല" + has_no_payment_methods: "പേയ്‌മെന്റ് രീതികളൊന്നുമില്ല" + has_no_shipping_methods: "ഷിപ്പിംഗ് രീതികളൊന്നുമില്ല" + products: + products_tip: "ഓപ്പൺ ഫുഡ് നെറ്റ്‌വർക്കിലൂടെ നിങ്ങൾ വിൽക്കുന്ന ഉൽപ്പന്നങ്ങൾ." + active_products: + zero: "നിങ്ങൾക്ക് സജീവ ഉൽപ്പന്നങ്ങളൊന്നുമില്ല." + one: "നിങ്ങൾക്ക് ഒരു സജീവ ഉൽപ്പന്നമുണ്ട്" + few: "നിങ്ങൾക്ക് %{count} സജീവ ഉൽപ്പന്നങ്ങളുണ്ട്" + many: "നിങ്ങൾക്ക് %{count} സജീവ ഉൽപ്പന്നങ്ങളുണ്ട്" + other: "നിങ്ങൾക്ക് %{count} സജീവ ഉൽപ്പന്നങ്ങളുണ്ട്" + order_cycles: + order_cycles: "ഓർഡർ സൈക്കിളുകൾ" + order_cycles_tip: "നിങ്ങളുടെ ഉൽപ്പന്നങ്ങൾ ഉപഭോക്താക്കൾക്ക് എപ്പോൾ, എവിടെയാണ് ലഭ്യമാകുന്നതെന്ന് ഓർഡർ സൈക്കിളുകൾ നിർണ്ണയിക്കുന്നു." + you_have_active: + zero: "നിങ്ങൾക്ക് സജീവമായ ഓർഡർ സൈക്കിളുകളൊന്നുമില്ല." + one: "നിങ്ങൾക്ക് ഒരു സജീവ ഓർഡർ സൈക്കിൾ ഉണ്ട്." + few: "നിങ്ങൾക്ക് %{count} സജീവമായ ഓർഡർ സൈക്കിളുകൾ ഉണ്ട്." + many: "നിങ്ങൾക്ക് %{count} സജീവമായ ഓർഡർ സൈക്കിളുകൾ ഉണ്ട്." + other: "നിങ്ങൾക്ക് %{count} സജീവമായ ഓർഡർ സൈക്കിളുകൾ ഉണ്ട്." + manage_order_cycles: "ഓർഡർ സൈക്കിളുകൾ നിയന്ത്രിക്കുക" + version: + view_all_releases: എല്ലാ റിലീസുകളും കാണുക + shipping_methods: + index: + shipping_methods: "ഷിപ്പിംഗ് രീതികൾ" + new_shipping_method: "പുതിയ ഷിപ്പിംഗ് രീതി" + name: "പേര്" + products_distributor: "വിതരണക്കാരൻ" + zone: "സോൺ" + calculator: "കാൽക്കുലേറ്റർ" + display: "പ്രദർശിപ്പിക്കുക" + both: "ചെക്ക്ഔട്ടും ബാക്ക് ഓഫീസും" + back_end: "ബാക്ക് ഓഫീസ് മാത്രം" + no_shipping_methods_found: "ഷിപ്പിംഗ് രീതികളൊന്നും കണ്ടെത്തിയില്ല" + new: + new_shipping_method: "പുതിയ ഷിപ്പിംഗ് രീതി" + back_to_shipping_methods_list: "ഷിപ്പിംഗ് രീതികളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + edit: + editing_shipping_method: "ഷിപ്പിംഗ് രീതി എഡിറ്റുചെയ്യുന്നു" + new: "പുതിയത്" + back_to_shipping_methods_list: "ഷിപ്പിംഗ് രീതികളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + form: + categories: "വിഭാഗങ്ങൾ" + tax_category: "നികുതി വിഭാഗം" + zones: "സോണുകൾ" + both: "ചെക്ക്ഔട്ടും ബാക്ക് ഓഫീസും" + back_end: "ബാക്ക് ഓഫീസ് മാത്രം" + deactivation_warning: "ഒരു ഷിപ്പിംഗ് രീതി ഡീ-ആക്ടിവേറ്റ് ചെയ്യുന്നത് നിങ്ങളുടെ ലിസ്റ്റിൽ നിന്ന് ഷിപ്പിംഗ് രീതി അപ്രത്യക്ഷമാക്കും. പകരമായി, 'ഡിസ്‌പ്ലേ' എന്ന ഓപ്‌ഷൻ 'ബാക്ക് ഓഫീസ് മാത്രം' ആയി സജ്ജീകരിച്ച് നിങ്ങൾക്ക് ചെക്ക്ഔട്ട് പേജിൽ നിന്ന് ഒരു ഷിപ്പിംഗ് രീതി മറയ്ക്കാം." + payment_methods: + index: + payment_methods: "പേയ്മെന്റ് രീതികൾ" + new_payment_method: "പുതിയ പേയ്‌മെന്റ് രീതി" + name: "പേര്" + products_distributor: "വിതരണക്കാരൻ" + provider: "ദാതാവ്" + environment: "പരിസ്ഥിതി" + display: "പ്രദർശിപ്പിക്കുക" + active: "സജീവമാണ്" + both: "രണ്ടും" + back_end: "ബാക്ക് ഓഫീസ് മാത്രം" + active_yes: "അതെ" + active_no: "ഇല്ല" + no_payment_methods_found: "പേയ്‌മെന്റ് രീതികളൊന്നും കണ്ടെത്തിയില്ല" + new: + new_payment_method: "പുതിയ പേയ്‌മെന്റ് രീതി" + back_to_payment_methods_list: "പേയ്‌മെന്റ് രീതികളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + edit: + new: "പുതിയത്" + editing_payment_method: "പേയ്‌മെന്റ് രീതി എഡിറ്റുചെയ്യുന്നു" + back_to_payment_methods_list: "പേയ്‌മെന്റ് രീതികളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + stripe_connect: + enterprise_select_placeholder: തിരഞ്ഞെടുക്കുക... + loading_account_information_msg: സ്ട്രൈപ്പിൽ നിന്ന് അക്കൗണ്ട് വിവരങ്ങൾ ലോഡുചെയ്യുന്നു, ദയവായി കാത്തിരിക്കൂ... + stripe_disabled_msg: സ്ട്രൈപ്പ് പേയ്‌മെന്റുകൾ സിസ്റ്റം അഡ്മിനിസ്ട്രേറ്റർ പ്രവർത്തനരഹിതമാക്കി. + request_failed_msg: ക്ഷമിക്കണം. സ്ട്രൈപ്പ് ഉപയോഗിച്ച് അക്കൗണ്ട് വിശദാംശങ്ങൾ പരിശോധിക്കാൻ ശ്രമിക്കുമ്പോൾ എന്തോ തകരാർ സംഭവിച്ചു... + account_missing_msg: ഈ എന്റർപ്രൈസസിന് സ്ട്രൈപ്പ് അക്കൗണ്ട് നിലവിലില്ല. + connect_one: ഒന്ന് ബന്ധിപ്പിക്കുക + access_revoked_msg: ഈ സ്‌ട്രൈപ്പ് അക്കൗണ്ടിലേക്കുള്ള ആക്‌സസ് റദ്ദാക്കി, നിങ്ങളുടെ അക്കൗണ്ട് വീണ്ടും കണക്‌റ്റ് ചെയ്യുക. + status: സ്ഥിതി + connected: ബന്ധിപ്പിച്ചു + account_id: അക്കൗണ്ട് ഐഡി + business_name: ബിസിനസ്സ് പേര് + charges_enabled: ചാർജുകൾ പ്രവർത്തനക്ഷമമാക്കി + form: + name: "പേര്" + description: "വിവരണം" + environment: "പരിസ്ഥിതി" + display: "പ്രദർശിപ്പിക്കുക" + active: "സജീവമാണ്" + active_yes: "അതെ" + active_no: "ഇല്ല" + both: "ചെക്ക്ഔട്ടും ബാക്ക് ഓഫീസും" + back_end: "ബാക്ക് ഓഫീസ് മാത്രം" + tags: "ടാഗുകൾ" + deactivation_warning: "പേയ്‌മെന്റ് രീതി ഡീ-ആക്ടിവേറ്റ് ചെയ്യുന്നത് നിങ്ങളുടെ ലിസ്റ്റിൽ നിന്ന് പേയ്‌മെന്റ് രീതി അപ്രത്യക്ഷമാക്കും. പകരമായി, 'ഡിസ്‌പ്ലേ'എന്ന ഓപ്‌ഷൻ 'ബാക്ക് ഓഫീസ് മാത്രം' ആയി സജ്ജീകരിച്ച് നിങ്ങൾക്ക് ചെക്ക്ഔട്ട് പേജിൽ നിന്ന് ഒരു പേയ്‌മെന്റ് രീതി മറയ്ക്കാം." + providers: + provider: "ദാതാവ്" + check: "പണം/ഇഎഫ്ടി/ മുതലായവ (ഓട്ടോമാറ്റിക് മൂല്യനിർണ്ണയം ആവശ്യമില്ലാത്ത പേയ്‌മെന്റുകൾ)" + pin: "പേയ്‌മെന്റുകൾ പിൻ ചെയ്യുക" + paypalexpress: "പേപാൽ എക്സ്പ്രസ്" + stripeconnect: "സ്‌ട്രൈപ്പ്" + stripesca: "സ്‌ട്രൈപ്പ് എസ്സിഎ" + payments: + source_forms: + stripe: + error_saving_payment: പേയ്‌മെന്റ് സംരക്ഷിക്കുന്നതിൽ തകരാറ്‌ + submitting_payment: പേയ്‌മെന്റ് സമർപ്പിക്കുന്നു... + paypal: + no_payment_via_admin_backend: പേപാൽ പേയ്‌മെന്റുകൾ ബാക്ക് ഓഫീസിൽ ക്യാപ്‌ചർ ചെയ്യാൻ കഴിയില്ല + products: + image_upload_error: "ദയവായി JPG, PNG, GIF, SVG അല്ലെങ്കിൽ WEBP ഫോർമാറ്റിൽ ചിത്രം അപ്‌ലോഡ് ചെയ്യുക." + image_not_processable: "ഇമേജ് അറ്റാച്ച്‌മെന്റ് ഒരു സാധുവായ ചിത്രമല്ല." + new: + title: "പുതിയ ഉൽപ്പന്നം" + new_product: "പുതിയ ഉൽപ്പന്നം" + supplier: "വിതരണക്കാരൻ" + supplier_select_placeholder: "ഒരു വിതരണക്കാരനെ തിരഞ്ഞെടുക്കുക" + product_name: "ഉത്പന്നത്തിന്റെ പേര്" + units: "യൂണിറ്റ് വലിപ്പം" + value: "മൂല്യം" + unit_name: "യൂണിറ്റിന്റെ പേര്" + price: "വില" + unit_price: "യൂണിറ്റ് വില" + unit_price_legend: "ഇനത്തിന്റെ വിലയെ അടിസ്ഥാനമാക്കി കണക്കാക്കുന്നു" + on_hand: "കയ്യിൽ" + on_demand: "ആവശ്യപ്പെടുന്നതനുസരിച്ച്" + product_description: "ഉൽപ്പന്ന വിവരണം" + image: "ചിത്രം" + unit_name_placeholder: 'ഉദാ. കുലകൾ' + index: + header: + title: ഉൽപ്പന്നങ്ങൾ മൊത്തമായി തിരുത്തുക + indicators: + title: ഉൽപ്പന്നങ്ങൾ ലോഡുചെയ്യുന്നു + no_products: "ഇതുവരെ ഉൽപ്പന്നങ്ങളൊന്നുമില്ല. എന്തുകൊണ്ടാണ് നിങ്ങൾ ഒന്നും ചേർക്കാത്തത്?" + no_results: "ക്ഷമിക്കണം, ഫലങ്ങളൊന്നും പൊരുത്തപ്പെടുന്നില്ല" + products_head: + name: പേര് + unit: യൂണിറ്റ് + display_as: പ്രദർശന മാർഗ്ഗം + category: വിഭാഗം + tax_category: നികുതി വിഭാഗം + inherits_properties?: സ്വത്തുക്കൾ അവകാശമാക്കുന്നുണ്ടോ? + av_on: "Av. ഓൺ" + import_date: "ഇറക്കുമതി തീയതി" + products_variant: + variant_has_n_overrides: "ഈ വേരിയന്റിന് %{n} തിരുത്തൽ(കൾ) ഉണ്ട്" + new_variant: "പുതിയ വേരിയന്റ്" + product_name: ഉത്പന്നത്തിന്റെ പേര് + primary_taxon_form: + product_category: ഉൽപ്പന്ന വിഭാഗം + group_buy_form: + group_buy: "ഗ്രൂപ്പ് വാങ്ങൽ?" + bulk_unit_size: ബൾക്ക് യൂണിറ്റ് വലിപ്പം + display_as: + display_as: പ്രദർശന മാർഗ്ഗം + clone: + success: ഉൽപ്പന്നം ക്ലോൺ ചെയ്തു + reports: + table: + select_and_search: "നിങ്ങളുടെ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ ഫിൽട്ടറുകൾ തിരഞ്ഞെടുത്ത് %{option} ക്ലിക്ക് ചെയ്യുക." + customer_names_message: + customer_names_tip: "നിങ്ങൾ വിതരണം ചെയ്ത ഓർഡറുകൾക്കായി ഉപഭോക്തൃ പേരുകൾ മറച്ചിട്ടുണ്ടെങ്കിൽ, നിങ്ങൾക്ക് വിതരണക്കാരനെ ബന്ധപ്പെടുകയും ഉപഭോക്തൃ പേരുകൾ കാണാൻ വിതരണക്കാരെ അനുവദിക്കുന്നതിന് അവരുടെ ഷോപ്പ് മുൻഗണനകൾ അപ്‌ഡേറ്റ് ചെയ്യാൻ കഴിയുമോ എന്ന് ചോദിക്കുകയും ചെയ്യാം." + products_and_inventory: + all_products: + message: "റിപ്പോർട്ടുചെയ്ത സ്റ്റോക്ക് ലെവലുകൾ വിതരണക്കാരന്റെ ഉൽപ്പന്ന ലിസ്റ്റുകളിൽ നിന്ന് മാത്രമാണെന്ന കാര്യം ശ്രദ്ധിക്കുക. നിങ്ങളുടെ സ്റ്റോക്ക് അളവ് നിയന്ത്രിക്കാൻ നിങ്ങൾ ഇൻവെന്ററി ഉപയോഗിക്കുകയാണെങ്കിൽ ഈ റിപ്പോർട്ടിൽ ഈ മൂല്യങ്ങൾ അവഗണിക്കപ്പെടും." + users: + index: + listing_users: "ഉപയോക്താക്കളെ ലിസ്റ്റുചെയ്യുന്നു" + new_user: "പുതിയ ഉപയോക്താവ്" + user: "ഉപയോക്താവ്" + enterprise_limit: "എന്റർപ്രൈസ് പരിധി" + search: "തിരയുക" + email: "ഇമെയിൽ" + edit: + editing_user: "ഉപയോക്താവ് എഡിറ്റ് ചെയ്യുന്നു" + back_to_users_list: "ഉപയോക്താക്കളുടെ പട്ടികയിലേക്ക് മടങ്ങുക" + general_settings: "പൊതുവായ ക്രമീകരണങ്ങൾ" + form: + disabled: "പ്രവർത്തനരഹിതമാണോ?" + email: "ഇമെയിൽ" + roles: "കർത്തവ്യങ്ങൾ" + enterprise_limit: "എന്റർപ്രൈസ് പരിധി" + confirm_password: "പാസ്സ്‌വേർഡ് സ്ഥിരീകരിക്കുക" + password: "പാസ്സ്‌വേർഡ്" + locale: "ഭാഷ" + email_confirmation: + confirmation_pending: "ഇമെയിൽ സ്ഥിരീകരണം തീർച്ചപ്പെടുത്തിയിട്ടില്ല. ഞങ്ങൾ ഒരു സ്ഥിരീകരണ ഇമെയിൽ %{address} ലേക്ക് അയച്ചു." + variants: + index: + sku: "എസ്.കെ.യു" + price: "വില" + options: "ഓപ്ഷനുകൾ" + no_results: "ഫലങ്ങളൊന്നുമില്ല" + option_types: "ഓപ്ഷൻ തരങ്ങൾ" + option_values: "ഓപ്ഷൻ മൂല്യങ്ങൾ" + and: "ഒപ്പം" + new_variant: "പുതിയ വേരിയന്റ്" + show_active: "സജീവമായവ കാണിക്കുക" + show_deleted: "ഇല്ലാതാക്കിയവ കാണിക്കുക" + new: + new_variant: "പുതിയ വേരിയന്റ്" + form: + sku: "എസ്.കെ.യു" + price: "വില" + unit_price: "യൂണിറ്റ് വില" + display_as: "പ്രദർശന മാർഗ്ഗം" + display_name: "പ്രദർശന നാമം" + display_as_placeholder: 'ഉദാ. 2 കി.ഗ്രാം' + display_name_placeholder: 'ഉദാ. തക്കാളി' + autocomplete: + out_of_stock: "സ്റ്റോക്കില്ല" + producer_name: "പ്രൊഡ്യൂസർ" + unit: "യൂണിറ്റ്" + shared: + configuration_menu: + terms_of_service: "സേവന നിബന്ധനകൾ" + sortable_header: + name: "പേര്" + number: "നമ്പർ" + completed_at: "പൂർത്തിയാക്കിയത്" + state: "സ്റ്റേറ്റ്" + payment_state: "പേയ്മെന്റ് സ്റ്റേറ്റ്" + shipment_state: "ഷിപ്പിംഗ് സ്റ്റേറ്റ്" + email: "ഇമെയിൽ" + total: "ആകെ" + billing_address_name: "പേര്" + taxons: + form: + name: പേര് + permalink: പെർമലിങ്ക് + description: വിവരണം + general_settings: + edit: + legal_settings: "നിയമ ക്രമീകരണങ്ങൾ" + cookies_consent_banner_toggle: "കുക്കികളുടെ സമ്മത ബാനർ പ്രദർശിപ്പിക്കുക" + privacy_policy_url: "സ്വകാര്യതാ നയ യുആർഎൽ" + enterprises_require_tos: "സംരംഭങ്ങൾ സേവന നിബന്ധനകൾ അംഗീകരിക്കണം" + shoppers_require_tos: "ഷോപ്പർമാർ സേവന നിബന്ധനകൾ അംഗീകരിക്കണം" + cookies_policy_matomo_section: "കുക്കി നയ പേജിൽ മറ്റോമോ വിഭാഗം പ്രദർശിപ്പിക്കുക" + footer_tos_url: "സേവന നിബന്ധനകൾ യുആർഎൽ" + checkout: + payment: + stripe: + choose_one: ഒന്ന് തിരഞ്ഞെടുക്കുക + enter_new_card: ഒരു പുതിയ കാർഡിനായി വിശദാംശങ്ങൾ നൽകുക + used_saved_card: "സേവ് ചെയ്തിട്ടുള്ള കാർഡ് ഉപയോഗിക്കുക:" + or_enter_new_card: "അല്ലെങ്കിൽ, ഒരു പുതിയ കാർഡിന്റെ വിശദാംശങ്ങൾ നൽകുക:" + remember_this_card: ഈ കാർഡ് ഓർമിച്ചിരിക്കണോ? + stripe_sca: + choose_one: ഒന്ന് തിരഞ്ഞെടുക്കുക + enter_new_card: ഒരു പുതിയ കാർഡിനായി വിശദാംശങ്ങൾ നൽകുക + used_saved_card: "സേവ് ചെയ്തിട്ടുള്ള കാർഡ് ഉപയോഗിക്കുക:" + or_enter_new_card: "അല്ലെങ്കിൽ, ഒരു പുതിയ കാർഡിന്റെ വിശദാംശങ്ങൾ നൽകുക:" + remember_this_card: ഈ കാർഡ് ഓർമിച്ചിരിക്കണോ? + date_picker: + flatpickr_date_format: "Y-m-d" + flatpickr_datetime_format: "Y-m-d H:i" + today: "ഇന്ന്" + now: "ഇപ്പോൾ" + close: "അടയ്ക്കുക" + orders: + error_flash_for_unavailable_items: "നിങ്ങളുടെ കാർട്ടിലെ ഒരു ഇനം ലഭ്യമല്ലാതായി. തിരഞ്ഞെടുത്ത അളവുകൾ തിരുത്തുക." + edit: + login_to_view_order: "നിങ്ങളുടെ ഓർഡർ കാണുന്നതിന് ദയവായി ലോഗിൻ ചെയ്യുക." + bought: + item: "ഈ ഓർഡർ സൈക്കിളിൽ ഇതിനകം ഓർഡർ ചെയ്തിട്ടുണ്ട്" + line_item: + insufficient_stock: "മതിയായ സ്റ്റോക്ക് ലഭ്യമല്ല, %{on_hand} മാത്രം ശേഷിക്കുന്നു" + out_of_stock: "സ്റ്റോക്കില്ല" + unavailable_item: "നിലവിൽ ലഭ്യമല്ല" + shipment_states: + backorder: ബാക്ക്ഓർഡർ + partial: ഭാഗികമായ + pending: തീർച്ചപ്പെടാത്ത + ready: തയ്യാർ + shipped: അയച്ചു + canceled: റദ്ദാക്കി + payment_states: + balance_due: ബാക്കി + completed: പൂർത്തിയാക്കിയത് + checkout: ചെക്ക് ഔട്ട് + credit_owed: കടബാധ്യത + failed: പരാജയപ്പെട്ടു + paid: പണം നൽകി + pending: തീർച്ചപ്പെടാത്ത + processing: പ്രോസസ്സിംഗ് + requires_authorization: "അനുമതി ആവശ്യമാണ്" + void: ശൂന്യം + invalid: അസാധുവാണ് + authorise: അധികാരപ്പെടുത്തുക + order_mailer: + cancel_email: + customer_greeting: "പ്രിയ %{name}," + instructions_html: "%{distributor} -ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ റദ്ദാക്കി. നിങ്ങളുടെ രേഖകൾക്കായി ഈ റദ്ദാക്കൽ വിവരം സൂക്ഷിക്കുക." + dont_cancel: "നിങ്ങൾ മനസ്സ് മാറ്റുകയോ ഈ ഓർഡർ റദ്ദാക്കാൻ ആഗ്രഹിക്കുന്നില്ലെങ്കിലോ ദയവായി %{email} -ൽ ബന്ധപ്പെടുക " + order_summary_canceled_html: "ഓർഡർ സംഗ്രഹം #%{number} [റദ്ദാക്കി]" + details: "നിങ്ങൾ ഓർഡർ ചെയ്തതിന്റെ വിശദാംശങ്ങൾ ഇതാ:" + unpaid_order: "നിങ്ങളുടെ ഓർഡറിന് പണമടച്ചിട്ടില്ലാത്തതിനാൽ റീഫണ്ട് നൽകിയിട്ടില്ല" + paid_order: "നിങ്ങളുടെ ഓർഡറിന് പണമടച്ചിട്ടുള്ളതിനാൽ %{distributor} മുഴുവൻ തുകയും റീഫണ്ട് ചെയ്തു" + credit_order: "നിങ്ങളുടെ ഓർഡറിന് പണമടച്ചിട്ടുള്ളതിനാൽ നിങ്ങളുടെ അക്കൗണ്ടിൽ ക്രെഡിറ്റ് ചെയ്യപ്പെട്ടു" + subject: "ഓർഡർ റദ്ദാക്കൽ" + cancel_email_for_shop: + greeting: "പ്രിയ %{name}," + subject: "ഓർഡർ റദ്ദാക്കൽ" + intro: "ഒരു ഉപഭോക്താവ് അവരുടെ ഓർഡർ # %{number} റദ്ദാക്കി." + view_cancelled_order: "റദ്ദാക്കിയ ഓർഡർ കാണുക" + confirm_email: + subject: "ഓർഡർ സ്ഥിരീകരണം" + invoice_email: + hi: "ഹായ് %{name}" + invoice_attached_text: നിങ്ങളുടെ സമീപകാല ഓർഡറിന്റെ ഇൻവോയ്സ് ഇവിടെ കൊടുത്തിരിക്കുന്നു, കാണുക + user_mailer: + reset_password_instructions: + request_sent_text: | + നിങ്ങളുടെ പാസ്‌വേഡ് പുനഃസജ്ജമാക്കാനുള്ള അഭ്യർത്ഥന നടത്തി. + നിങ്ങളല്ല ഈ അഭ്യർത്ഥന നടത്തിയതെങ്കിൽ, ഈ ഇമെയിൽ അവഗണിക്കുക. + link_text: > + നിങ്ങൾ ഈ അഭ്യർത്ഥന നടത്തിയെങ്കിൽ താഴെയുള്ള ലിങ്കിൽ ക്ലിക്ക് ചെയ്യുക: + issue_text: | + മുകളിലുള്ള യുആർഎൽ പ്രവർത്തിക്കുന്നില്ലെങ്കിൽ, അത് നിങ്ങളുടെ ബ്രൗസറിൽ പകർത്താൻ ശ്രമിക്കുക. + എന്തെങ്കിലും പ്രശ്നങ്ങൾ ഉണ്ടെങ്കിൽ ഞങ്ങളുമായി ബന്ധപ്പെടുക. + subject: "പാസ്‌വേഡ് പുനഃസജ്ജമാക്കുന്നതിനുള്ള നിർദ്ദേശങ്ങൾ " + confirmation_instructions: + subject: "നിങ്ങളുടെ ഓഎഫ്എൻ അക്കൗണ്ട് സ്ഥിരീകരിക്കുക" + payment_mailer: + authorize_payment: + subject: "ഓഎഫ്എൻ-ൽ %{distributor} -ലേക്കുള്ള നിങ്ങളുടെ പേയ്‌മെന്റ് അംഗീകരിക്കുക" + instructions: "%{distributor} -ക്കുള്ള നിങ്ങളുടെ %{amount}പേയ്‌മെന്റിന് അധിക പ്രാമാണീകരണം ആവശ്യമാണ്. നിങ്ങളുടെ പേയ്‌മെന്റ് അംഗീകരിക്കുന്നതിന് ഇനിപ്പറയുന്ന യുആർഎൽ സന്ദർശിക്കുക:" + authorization_required: + subject: "ഒരു പേയ്‌മെന്റിന് ഉപഭോക്താവിന്റെ അനുമതി ആവശ്യമാണ്" + message: "%{order_number} എന്ന ഓർഡറിന്റെ പേയ്‌മെന്റിന് ഉപഭോക്താവിൽ നിന്ന് അധിക അനുമതി ആവശ്യമാണ്. ഉപഭോക്താവിന് ഇമെയിൽ വഴി അറിയിപ്പ് ലഭിച്ചു, അത് അംഗീകരിക്കപ്പെടുന്നതുവരെ പേയ്‌മെന്റ് തീർച്ചപ്പെടുത്തിയിട്ടില്ലെന്ന് ദൃശ്യമാകും." + shipment_mailer: + shipped_email: + dear_customer: "പ്രിയ ഉപഭോക്താവേ," + instructions: "%{distributor} ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ അയച്ചു" + shipment_summary: "ഷിപ്പിംഗ് സംഗ്രഹം" + subject: "ഷിപ്പ്മെന്റ് അറിയിപ്പ്" + thanks: "നിങ്ങളുടെ ബിസിനസ്സിന് നന്ദി." + track_information: "ട്രാക്കിംഗ് വിവരങ്ങൾ: %{tracking}" + track_link: "ട്രാക്കിംഗ് ലിങ്ക്: %{url}" + picked_up_instructions: "%{distributor} ൽ നിന്നുള്ള നിങ്ങളുടെ ഓർഡർ പിക്ക് ചെയ്തു" + picked_up_subject: "പിക്കപ്പ് അറിയിപ്പ്" + test_mailer: + test_email: + greeting: "അഭിനന്ദനങ്ങൾ!" + message: "നിങ്ങൾക്ക് ഈ ഇമെയിൽ ലഭിച്ചിട്ടുണ്ടെങ്കിൽ, നിങ്ങളുടെ ഇമെയിൽ ക്രമീകരണം ശരിയാണ്." + subject: "ടെസ്റ്റ് മെയിൽ" + order_state: + address: വിലാസം + adjustments: മാറ്റംവരുത്തലുകൾ + awaiting_return: തിരിച്ചുവരവിനായി കാത്തിരിക്കുന്നു + canceled: റദ്ദാക്കി + cart: കാർട്ട് + confirmation: "സ്ഥിരീകരണം" + complete: പൂർത്തിയായത് + confirm: സ്ഥിരീകരിക്കുക + delivery: ഡെലിവറി + paused: താൽക്കാലികമായി നിർത്തി + payment: പേയ്മെന്റ് + pending: തീർച്ചപ്പെടാത്ത + resumed: പുനരാരംഭിച്ചു + returned: തിരിച്ചയച്ചു + subscription_state: + active: സജീവം + pending: തീർച്ചപ്പെടാത്ത + ended: അവസാനിച്ചു + paused: താൽക്കാലികമായി നിർത്തി + canceled: റദ്ദാക്കി + paypal: + already_refunded: "ഈ പേയ്‌മെന്റ് റീഫണ്ട് ചെയ്‌തു, അതിൽ തുടർ നടപടികളൊന്നും സ്വീകരിക്കാനാകില്ല." + no_payment_via_admin_backend: "നിങ്ങൾക്ക് ഇപ്പോൾ അഡ്‌മിൻ ബാക്കെൻഡ് വഴി പേയ്പാൽ അക്കൗണ്ടുകൾ ചാർജ് ചെയ്യാൻ കഴിയില്ല." + transaction: "പേപാൽ ഇടപാട്" + payer_id: "പേയർ ഐഡി" + transaction_id: "ഇടപാട് ഐഡി" + token: "ടോക്കൺ" + refund: "റീഫണ്ട്" + refund_amount: "തുക" + original_amount: "യഥാർത്ഥ തുക: %{amount}" + refund_successful: "പേയ്പാൽ റീഫണ്ട് വിജയകരമായി" + refund_unsuccessful: "പേയ്പാൽ റീഫണ്ട് പരാജയപ്പെട്ടു" + actions: + refund: "റീഫണ്ട്" + flash: + cancel: "പേയ്പാൽ ഉപയോഗിക്കാൻ താൽപ്പര്യമില്ലേ? കുഴപ്പമില്ല." + connection_failed: "പേയ്പാൽ-ലേക്ക് ബന്ധിപ്പിക്കാൻ കഴിഞ്ഞില്ല." + generic_error: "പേയ്പാൽ പരാജയപ്പെട്ടു. %{reasons}" + users: + api_keys: + regenerate_key: "കീ വീണ്ടും ഉണ്ടാക്കുക" + title: എപിഐ കീ + webhook_endpoints: + title: വെബ്‌ഹുക്ക് എൻഡ്‌പോയിന്റുകൾ + description: സിസ്റ്റത്തിലെ ഇവന്റുകൾ ബാഹ്യ സിസ്റ്റങ്ങളിലേക്ക് വെബ്‌ഹുക്കുകളെ ട്രിഗർ ചെയ്‌തേക്കാം. + event_types: + order_cycle_opened: ഓർഡർ സൈക്കിൾ തുറന്നു + event_type: + header: ഇവന്റ് തരം + url: + header: എൻഡ്‌പോയിന്റ് യുആർഎൽ + create_placeholder: റിമോട്ട് വെബ്ഹുക്ക് എൻഡ് പോയിന്റിന്റെ യുആർഎൽ നൽകുക + developer_settings: + title: ഡെവലപ്പർ ക്രമീകരണങ്ങൾ + form: + account_settings: അക്കൗണ്ട് ക്രമീകരണങ്ങൾ + show: + tabs: + developer_settings: ഡെവലപ്പർ ക്രമീകരണങ്ങൾ + orders: ഓർഡറുകൾ + cards: ക്രെഡിറ്റ് കാർഡുകൾ + transactions: ഇടപാടുകൾ + settings: അക്കൗണ്ട് ക്രമീകരണങ്ങൾ + unconfirmed_email: "%{unconfirmed_email} -ന്റെ ഇമെയിൽ സ്ഥിരീകരണം നടന്നിട്ടില്ല. പുതിയ ഇമെയിൽ സ്ഥിരീകരിച്ചുകഴിഞ്ഞാൽ നിങ്ങളുടെ ഇമെയിൽ വിലാസം അപ്ഡേറ്റ് ചെയ്യപ്പെടും." + orders: + open_orders: ആരംഭിച്ച ഓർഡറുകൾ + past_orders: കഴിഞ്ഞ ഓർഡറുകൾ + transactions: + transaction_history: ഇടപാട് ചരിത്രം + authorisation_required: അംഗീകാരം ആവശ്യമാണ് + authorise: അധികാരപ്പെടുത്തുക + open_orders: + order: ഓർഡർ ചെയ്യുക + shop: കട + changes_allowed_until: -വരെ മാറ്റങ്ങൾ അനുവദിച്ചിരിക്കുന്നു + items: ഇനങ്ങൾ + total: ആകെ + edit: എഡിറ്റ് ചെയ്യുക + cancel: റദ്ദാക്കുക + closed: അടച്ചു + until: വരെ + past_orders: + order: ഓർഡർ ചെയ്യുക + shop: കട + completed_at: പൂർത്തിയാക്കിയത് + items: ഇനങ്ങൾ + total: ആകെ + paid?: പണം നൽകിയോ? + status: സ്ഥിതി + completed: പൂർത്തിയായി + cancelled: റദ്ദാക്കി + saved_cards: + default?: സ്ഥിരസ്ഥിതി? + delete?: ഇല്ലാതാക്കുക? + cards: + authorised_shops: അംഗീകൃത കടകൾ + authorised_shops_agreement: നിങ്ങൾക്കുള്ള ഏതൊരു സബ്‌സ്‌ക്രിപ്‌ഷനും (അതായത്. ആവർത്തിച്ചുള്ള ഓർഡറുകൾ) നിങ്ങളുടെ ഡിഫോൾട്ട് ക്രെഡിറ്റ് കാർഡ് ചാർജ് ചെയ്യാൻ അനുവദിച്ചിരിക്കുന്ന ഷോപ്പുകളുടെ ലിസ്‌റ്റാണിത്. നിങ്ങളുടെ കാർഡ് വിശദാംശങ്ങൾ സുരക്ഷിതമായി സൂക്ഷിക്കും, കട ഉടമകളുമായി പങ്കിടില്ല. നിങ്ങളിൽ നിന്ന് നിരക്ക് ഈടാക്കുമ്പോൾ എല്ലായ്പ്പോഴും നിങ്ങളെ അറിയിക്കും. ഒരു ഷോപ്പിനായി ബോക്‌സ് ചെക്ക് ചെയ്യുന്നതിലൂടെ, ആ ഷോപ്പിൽ നിങ്ങൾ സൃഷ്‌ടിക്കുന്ന ഏതൊരു സബ്‌സ്‌ക്രിപ്‌ഷന്റെയും നിബന്ധനകൾക്ക് അനുസൃതമായി പേയ്‌മെന്റുകൾ എടുക്കുന്നതിന് നിങ്ങളുടെ കാർഡ് ഇഷ്യൂ ചെയ്‌ത ധനകാര്യ സ്ഥാപനത്തിന് നിർദ്ദേശങ്ങൾ അയയ്‌ക്കാൻ ആ ഷോപ്പിനെ അധികാരപ്പെടുത്താൻ നിങ്ങൾ സമ്മതിക്കുന്നു. + saved_cards_popover: പിന്നീടുള്ള ഉപയോഗത്തിനായി സേവ് ചെയ്യാൻ നിങ്ങൾ തിരഞ്ഞെടുത്ത കാർഡുകളുടെ പട്ടികയാണിത്. നിങ്ങൾ ഒരു ഓർഡർ ചെക്ക്ഔട്ട് ചെയ്യുമ്പോൾ നിങ്ങളുടെ 'ഡിഫോൾട്ട്' സ്വയമേവ തിരഞ്ഞെടുക്കപ്പെടും, നിങ്ങൾ അങ്ങനെ ചെയ്യാൻ അനുവദിച്ചിട്ടുള്ള ഏതെങ്കിലും കടകളിൽ നിന്ന് ചാർജുകൾ ഈടാക്കാം (വലത് കാണുക). + authorised_shops: + shop_name: "കടയുടെ പേര്" + allow_charges?: "ഡിഫോൾട്ട് കാർഡിലേക്ക് ചാർജുകൾ അനുവദിക്കണോ?" + no_default_saved_cards_tooltip: ചാർജുകൾ അനുവദിക്കുന്നതിന് നിങ്ങൾ ഒരു ക്രെഡിറ്റ് കാർഡ് ഡിഫോൾട്ടായി അടയാളപ്പെടുത്തേണ്ടതുണ്ട്. + localized_number: + invalid_format: ഒരു അസാധുവായ ഫോർമാറ്റ് ഉണ്ട്. ദയവായി ഒരു നമ്പർ നൽകുക. + api: + invalid_api_key: "അസാധുവായ എപിഐ കീ ( %{key} ) വ്യക്തമായിട്ടുണ്ട്." + unauthorized: "ആ പ്രവർത്തനം നടത്താൻ നിങ്ങൾക്ക് അധികാരമില്ല." + invalid_resource: "അസാധുവായ റീസോർസ്. തകരാറുകൾ പരിഹരിച്ച് വീണ്ടും ശ്രമിക്കുക." + resource_not_found: "നിങ്ങൾ തിരയുന്ന റീസോർസ് കണ്ടെത്താനായില്ല." + access: "എപിഐ ആക്സസ്" + key: "കീ" + clear_key: "കീ മായ്ക്കുക" + regenerate_key: "കീ വീണ്ടും ഉണ്ടാക്കുക" + no_key: "കീ ഇല്ല" + generate_key: "എപിഐ കീ ഉണ്ടാക്കുക" + key_generated: "കീ ഉണ്ടാക്കി" + key_cleared: "കീ മായ്ച്ചു" + shipment: + cannot_ready: "കയറ്റുമതി തയ്യാറാക്കാൻ കഴിയില്ല." + invalid_taxonomy_id: "ടാക്സോണമി ഐഡി അസാധുവാണ്." + toggle_api_key_view: "ഉപയോക്താവിനായി എപിഐ കീ കാണിക്കുക" + activerecord: + models: + spree/payment: + one: പേയ്മെന്റ് + other: പേയ്മെന്റുകൾ + unit: യൂണിറ്റ് + per_unit: യൂണിറ്റിന് + datetime: + distance_in_words: + about_x_hours: + one: ഏകദേശം 1 മണിക്കൂർ + other: ഏകദേശം %{count} മണിക്കൂറുകൾ + about_x_months: + one: ഏകദേശം 1 മാസം + other: ഏകദേശം %{count} മാസങ്ങൾ + about_x_years: + one: ഏകദേശം 1 വർഷം + other: ഏകദേശം %{count} വർഷങ്ങൾ + almost_x_years: + one: ഏകദേശം 1 വർഷം + other: '%{count}വർഷങ്ങളോളം' + half_a_minute: അര മിനിറ്റ് + less_than_x_seconds: + one: 1 സെക്കൻഡിൽ കുറവ് + other: '%{count} സെക്കൻഡിൽ കുറവ്' + less_than_x_minutes: + one: ഒരു മിനിറ്റിൽ താഴെ + other: '%{count} മിനിറ്റിൽ കുറവ്' + over_x_years: + one: 1 വർഷത്തിൽ കൂടുതൽ + other: '%{count} വർഷത്തിൽ കൂടുതൽ' + x_seconds: + one: "1 സെക്കൻഡ്" + other: "%{count} സെക്കൻഡുകൾ" + x_minutes: + one: "1 മിനിറ്റ്" + other: "%{count} മിനിറ്റുകൾ" + x_days: + one: "1 ദിവസം" + other: "%{count} ദിവസങ്ങൾ" + x_months: + one: "1 മാസം" + other: "%{count} മാസങ്ങൾ" + x_years: + one: "1 വർഷം" + other: "%{count} വർഷങ്ങൾ" + components: + multiple_checked_select: + filter_placeholder: "ഫിൽട്ടർ ഓപ്ഷനുകൾ" + search_input: + placeholder: തിരയുക + selector_with_filter: + selected_items: "%{count} തിരഞ്ഞെടുത്തു" + search_placeholder: തിരയുക + pagination: + next: അടുത്തത് + previous: മുന്നിലത്തേത് diff --git a/config/locales/mr.yml b/config/locales/mr.yml index 9d1ad2acb6..1c09d8dfb1 100644 --- a/config/locales/mr.yml +++ b/config/locales/mr.yml @@ -236,7 +236,7 @@ mr: signed_up_but_unconfirmed: "पुष्टीकरण दुव्यासह एक संदेश तुमच्या ईमेल पत्त्यावर पाठविला गेला आहे. तुमचे खाते सक्रिय करण्यासाठी कृपया लिंक उघडा." unknown_error: "तुमचे खाते तयार करताना काहीतरी चूक झाली. तुमचा ईमेल पत्ता तपासा आणि पुन्हा प्रयत्न करा." failure: - disabled: "तुमचे खाते अक्षम केले गेले आहे. या समस्येचे निराकरण करण्यासाठी कृपया ॲडमिनिस्ट्रेटरशी संपर्क साधा." + disabled: "तुमचे खाते डिसेबल केले गेले आहे. या समस्येचे निराकरण करण्यासाठी कृपया ॲडमिनिस्ट्रेटरशी संपर्क साधा." invalid: | चुकीचा इमेल किंवा पासवर्ड शब्द. तुम्ही मागील वेळी गेस्ट होता का? कदाचित तुम्हाला एखादे खाते तयार करावे लागेल किंवा तुमचा पासवर्ड रीसेट करावा लागेल. @@ -372,7 +372,7 @@ mr: title: "ओपन फूड नेटवर्क" welcome_to: "आपले स्वागत आहे" site_meta_description: "ओपन फूड नेटवर्क इंडिया सॉफ्टवेअर प्लॅटफॉर्म, शेतकऱ्यांना फायदेशीर किमतीत त्यांचे उत्पादन ऑनलाइन विकण्याची क्षमता देते. हे सॉफ्टवेअर अन्नधान्यं विकण्यासाठीच बनवले गेले आहे जेणेकरून ते विभिन्नं मापं किंवा स्टॉकची लेव्हल हाताळू शकते जी फक्त कृषी उत्पादनांमध्येच असते. उदा. डझनभर अंडी, कोथिंबिरीची जुडी, किंवा एक अख्ख चिकन ज्याचे वजन वेगवेगळे असू शकते." - search_by_name: नाव किंवा उपनगरानुसार शोधा... + search_by_name: 'नांव, उपनगर किंवा पिन कोडनुसार शोधा ... ' producers_join: ओपन फूड नेटवर्कमध्ये सामील होण्यासाठी भारतीय उत्पादकांचे आता स्वागत आहे. charges_sales_tax: GST आकारणार? business_address: "व्यवसायाचा पत्ता" @@ -387,7 +387,6 @@ mr: cancel_order: "ऑर्डर रद्द करा" confirm_send_invoice: "या ऑर्डरचे इन्व्हॉईस ग्राहकाला पाठवले जाईल. तुम्ही पुढे जाऊ इच्छिता?" confirm_resend_order_confirmation: "तुम्हाला खात्री आहे की तुम्ही ऑर्डर पुष्टीकरण ईमेल पुन्हा पाठवू इच्छिता?" - must_have_valid_business_number: "इनव्हॉइस पाठवण्यापूर्वी %{enterprise_name} कडे वैध ABN असणे आवश्यक आहे." invoice: "इनव्हॉइस" invoices: "इनव्हॉइसेस" file: "फाईल" @@ -530,6 +529,8 @@ mr: actions: edit: सुधारणे clone: क्लोन + image: + edit: सुधारित करा adjustments: skipped_changing_canceled_order: "तुम्ही रद्द केलेली ऑर्डर बदलू शकत नाही." begins_at: वाजता सुरू होते @@ -642,7 +643,7 @@ mr: matomo_site_id: "Matomo साइट आयडी" matomo_tag_manager_url: "Matomo टॅग व्यवस्थापक URL" info_html: "Matomo हे वेब आणि मोबाइल ॲनालिटिक्स ॲप्लिकेशन आहे. तुम्ही एकतर Matomo ऑन-प्रिमाइसेस होस्ट करू शकता किंवा क्लाउड-होस्टेड सेवा वापरू शकता. अधिक माहितीसाठी matomo.org पहा." - config_instructions_html: "येथे तुम्ही OFN Matomo एकत्रीकरण कॉन्फिगर करू शकता. खालील Matomo URL ने Matomo instance कडे निर्देश केला पाहिजे जेथे वापरकर्त्याची ट्रॅकिंग माहिती पाठविली जाईल; ते रिक्त ठेवल्यास, Matomo वापरकर्ता ट्रॅकिंग अक्षम केले जाईल. साइट आयडी फील्ड अनिवार्य नाही परंतु जर तुम्ही एकाच Matomo instanceवर एकापेक्षा जास्त वेबसाइटचा ट्रॅक करत असाल तर ते उपयुक्त आहे; ते Matomo उदाहरण कन्सोलवर आढळू शकते." + config_instructions_html: "येथे तुम्ही OFN Matomo एकत्रीकरण कॉन्फिगर करू शकता. खालील Matomo URL ने Matomo instance कडे निर्देश केला पाहिजे जेथे वापरकर्त्याची ट्रॅकिंग माहिती पाठविली जाईल; ते रिक्त ठेवल्यास, Matomo वापरकर्ता ट्रॅकिंग डिसेबल केले जाईल. साइट आयडी फील्ड अनिवार्य नाही परंतु जर तुम्ही एकाच Matomo instanceवर एकापेक्षा जास्त वेबसाइटचा ट्रॅक करत असाल तर ते उपयुक्त आहे; ते Matomo उदाहरण कन्सोलवर आढळू शकते." config_instructions_tag_manager_html: "Matomo Tag Manager URL सेट केल्याने Matomo Tag Manager सक्षम होतो. हे साधन तुम्हाला अ‍ॅनॅलिटिक्स इव्हेंट सेट करण्याची परवानगी देते. Matomo Tag Manager URL ही Matomo Tag Manager च्या Install Code विभागातून कॉपी केली आहे. तुम्ही योग्य कंटेनर आणि वातावरण निवडल्याची खात्री करा कारण हे पर्याय URL बदलतात." customers: index: @@ -778,10 +779,8 @@ mr: invalid: one: "%{count} उत्पादन जतन करणे शक्य नाही. कृपया त्रुटींचे पुनरावलोकन करा आणि पुन्हा प्रयत्न करा." other: "%{count} उत्पादने जतन करणे शक्य नाही. कृपया एरर्सचे पुनरावलोकन करा आणि पुन्हा प्रयत्न करा." - save: बदल जतन करा reset: बदल जतन करू नका - bulk_update: - success: "उत्पादने यशस्वीरित्या अद्यतनित केली" + save: बदल जतन करा product_import: title: उत्पादन आयात file_not_found: फाइल सापडली नाही किंवा उघडता आली नाही @@ -823,6 +822,9 @@ mr: product_categories: उत्पादन श्रेणी tax_categories: कर श्रेणी shipping_categories: शिपिंग श्रेण्या + dfc_import_form: + enterprise: "एंटरप्राइझ" + import: "Import करा" import: review: पुनरावलोकन करा import: आयात करा @@ -992,7 +994,7 @@ mr: sort_items_by_supplier?: पुरवठादारानुसार वस्तूंचे वर्गीकरण करायचे? sort_items_by_supplier_tip: "सक्षम केल्यावर, पुरवठादाराच्या नावानुसार वस्तूंचे वर्गीकरण केले जाईल." enabled: सक्रिय करा - disabled: निष्क्रिय करा + disabled: डिसेबल करा business_address: company_legal_name: कंपनीचे कायदेशीर नाव company_placeholder: उदाहरण Inc. @@ -1109,11 +1111,11 @@ mr: allow_order_changes_true: "ऑर्डर सायकल चालू असताना ग्राहक ऑर्डर बदलू/रद्द करू शकतात" enable_subscriptions: "सबस्क्रिप्शन्स" enable_subscriptions_tip: "सबस्क्रिप्शन्स सक्षम करायची?" - enable_subscriptions_false: "निष्क्रिय केले" + enable_subscriptions_false: "डिसेबल्ड आहे" enable_subscriptions_true: "सक्रिय केले" customer_names_in_reports: "रिपोर्टस् मधील ग्राहकांची नावे" customer_names_tip: "रिपोर्टस् मध्ये तुमच्या ग्राहकांची नावे पाहण्यासाठी तुमच्या पुरवठादारांना सक्षम करा" - customer_names_false: "निष्क्रिय केले" + customer_names_false: "डिसेबल्ड आहे" customer_names_true: "सक्रिय केले" shopfront_message: "शॉपफ्रंट संदेश" shopfront_message_placeholder: > @@ -1140,7 +1142,7 @@ mr: display_remaining_stock: "उपलब्धता कमी असल्यास उपलब्ध स्टॉक दुकानात प्रदर्शित करा" display_remaining_stock_tip: "फक्त 3 किंवा त्यापेक्षा कमी वस्तू शिल्लक असल्यास खरेदीदारांना सूचित करा. " enabled: "सक्रिय केले" - disabled: "अक्षम" + disabled: "डिसेबल्ड आहे" social: legend: "सामाजिक" twitter_placeholder: "उदा. @the_prof" @@ -1577,7 +1579,6 @@ mr: index: title: "OIDC सेटिंग्ज" connect: "तुमचे खाते कनेक्ट करा" - already_connected: "तुमचे खाते आधीपासून या DFC अधिकृतता खात्याशी जोडलेले आहे:" les_communs_link: "Les Communs Open ID सर्व्हर" link_your_account: "DFC (Les Communs Open ID Connect) द्वारे वापरल्या जाणार्‍या अधिकृतता प्रदात्याशी तुम्ही प्रथम तुमचे खाते लिंक करणे आवश्यक आहे." link_account_button: "तुमचे Les Communs OIDC खाते लिंक करा" @@ -1842,7 +1843,6 @@ mr: invoice_tax_total: "GST एकूण:" tax_invoice: "टॅक्स इन्व्हॉइस" tax_total: "एकूण कर (%{rate}):" - invoice_shipping_type: "प्रकार:" total_excl_tax: "एकूण (कर वगळून):" total_incl_tax: "एकूण (करासह):" total_all_tax: "एकूण कर:" @@ -1852,13 +1852,13 @@ mr: order_number: "ऑर्डर क्रमांक:" date_of_transaction: "व्यवहाराची तारीख:" menu_1_title: "दुकाने" - menu_1_url: "/शॉप्स" + menu_1_url: "/shops" menu_2_title: "नकाशा" - menu_2_url: "/मॅप्स" + menu_2_url: "/map" menu_3_title: "उत्पादक" - menu_3_url: "/उत्पादक" + menu_3_url: "/producers" menu_4_title: "ग्रुप्स" - menu_4_url: "/ग्रुप्स" + menu_4_url: "/groups" menu_5_title: "बद्दल" menu_5_url: "https://about.openfoodnetwork.in" menu_6_title: "किंमत" @@ -1994,14 +1994,14 @@ mr: cookies_policy_link: "कुकीज धोरण" cookies_accept_button: "कुकीज स्वीकारा" home_shop: खरेदी करा - brandstory_headline: "कृषि उत्पादन, स्थानिक आणि ऑनलाईन!" - brandstory_intro: "कधी कधी सिस्टमला ठीक करण्याचा सर्वोत्तम मार्ग म्हणजे नवीन सिस्टम सुरू करणे…" - brandstory_part1: "ओपन फूड नेटवर्क इंडिया सॉफ्टवेअर प्लॅटफॉर्म, शेतकऱ्यांना फायदेशीर किमतीत त्यांचे उत्पादन ऑनलाइन विकण्याची क्षमता देते. हे सॉफ्टवेअर अन्नधान्यं विकण्यासाठीच बनवले गेले आहे जेणेकरून ते विभिन्नं मापं किंवा स्टॉकची लेव्हल हाताळू शकते जी फक्त कृषी उत्पादनांमध्येच असते. उदा. डझनभर अंडी, कोथिंबिरीची जुडी, किंवा एक अख्ख चिकन ज्याचे वजन वेगवेगळे असू शकते." + brandstory_headline: "सेंद्रिय आणि स्थानिक अन्न खरेदी करा." + brandstory_intro: "स्थानिक शेतकरी आणि अन्न उत्पादक शोधा." + brandstory_part1: "हजारो शेतकरी आणि अन्न उत्पादकांमधून निवडा जे कोणत्याही हानिकारक रसायनांशिवाय शाश्वत शेती पद्धती वापरून अन्न तयार करतात." brandstory_part2: "अन्नधान्य उत्पादक, Farmer Producer Organizations (FPO), किंवा Farmer Producer Companies (FPC) स्वतःचे ऑनलाइन शॉप तयार करू शकतात, पेमेंट गोळा करू शकतात, किंवा ह्याच वेबसाइटवरील इतर दुकानांमधूनही विक्री करू शकतात." - brandstory_part3: "घाऊक विक्रेते, Farmer Producer Organizations (FPO), किंवा Farmer Producer Companies (FPC) ते वापरत असलेल्या सॉफ्टवेअरशी OFN integrate करू शकतात आणि खरेदी गट तयार करून ग्राहकांना ऑनलाईन अन्नधान्य केंद्रे आणि आमच्या दुकानांच्या राष्ट्रीय नेटवर्कद्वारे त्यांच्या उत्पादनांचा पुरवठा करू शकतात." - brandstory_part4: "Farmer Producer Organizations (FPO), किंवा Farmer Producer Companies (FPC) त्यांच्या स्थानिक क्षेत्रातील उत्पादकांना एकत्र आणून शेतकर्‍यांची एक ऑनलाइन बाजारपेठ तयार करू शकतात, एक मजबुत स्थानिक अन्न अर्थव्यवस्था तयार करू शकतात." - brandstory_part5_strong: "आणि सॉफ्टवेअर इतकंच महत्त्वाचं आहे जितकी ती मूल्ये आहेत ज्यांच्या आधारावर ते उभं आहे." - brandstory_part6: "तुम्ही शेतकरी असाल किंवा शेतकरी बाजार, शेतकरी कॉ-ऑपेराटीव्ह सोसायटी, किंवा फूड हब असाल आणि चांगले अन्नधान्य विकत असाल, तर नफा नव्हे तर लोकांसाठी आणि पृथ्वीसाठी, एक अन्न प्रणाली तयार करण्यासाठी असे सॉफ्टवेअर निवडा जे तुमच्या मूल्यांशी संलग्न आहे. आम्ही स्पर्धात्मक न राहता एकत्रितपणे काम करून, नवीन सॉफ्टवेअर विकसित करण्याचा खर्च वाटून घेतो आणि आमचा प्रकल्प यशस्वी होईल ह्याची खात्री बाळगतो." + brandstory_part3: "जेव्हा तुम्ही स्थानिक शेतकर्‍यांकडून तुमचे अन्न खरेदी करता तेव्हा तुम्ही स्थानिक अर्थव्यवस्थेला आधार देता." + brandstory_part4: "शेतकर्‍यांना त्यांचे उत्पादन ऑनलाइन विकण्यास मदत करण्यासाठी हे सॉफ्टवेअर ना नफा ना तोटा तत्वावर चालणाऱ्या संस्थेने विकसित केले आहे." + brandstory_part5_strong: "आम्ही त्याला ओपन फूड नेटवर्क इंडिया किंवा ओएफएन इंडिया म्हणतो." + brandstory_part6: "जर तुम्ही शेतकरी, शेतकरी बाजार, फूड को-ऑप किंवा फूड हब म्हणून चांगले अन्न विकत असाल- तर OFN India हा तुमचा सर्वोत्तम पर्याय आहे." system_headline: "OFN वर विक्री कशी कराल - ३ सोप्या स्टेप्स." system_step1: "1. नविन अकाउंट बनवा" system_step1_text: "• तुमच्या कृषि उद्योगाचे किंवा दुकानाचे नाव, वर्णन आणि संपर्क तपशील प्रविष्ट करा • फोटो आणि सोशल मीडिया तपशील प्रविष्ट करा" @@ -2292,12 +2292,12 @@ mr: sell_hubs_detail: "तुमच्या फूड एंटरप्राइझ किंवा OFN वर संस्थेसाठी प्रोफाइल सेट करा. तुम्ही तुमचे प्रोफाईल कधीही मल्टी- उत्पादक शॉपमध्ये अपग्रेड करू शकता." sell_groups_detail: "खास तुमच्या विभागासाठी किंवा तुमच्या संस्थेसाठी एंटरप्राइजेसची (उत्पादक आणि इतर फूड एंटरप्राइजेस) डिरेक्टरी तयार करा." sell_user_guide: "आमच्या वापरकर्ता मार्गदर्शकामध्ये अधिक शोधा." - sell_listing_price: "OFN वर लिस्टिंग विनामूल्य आहे. OFN वर शॉप उघडणे आणि चालवणे हे मासिक विक्री $500 पर्यंत विनामूल्य आहे. तुम्ही अधिक विक्री केल्यास तुम्ही तुमच्या विक्रीच्या 1% आणि 3% दरम्यान तुमचे समुदाय योगदान निवडू शकता. किंमतीच्या अधिक तपशीलासाठी वरच्या मेनूमधील बद्दल लिंकद्वारे सॉफ्टवेअर प्लॅटफॉर्म विभागाला भेट द्या." + sell_listing_price: "OFN India वर लिस्टिंग विनामूल्य आहे. दुकाने आणि हब्स साठी योजना दरमहा ₹375 पासून सुरू होतात. किंमतीबद्दल अधिक माहितीसाठी https://about.openfoodindia.org/pricing/ ला भेट द्या." sell_embed: "आम्ही तुमच्या स्वतःच्या वेबसाइटमध्येदेखील OFN शॉप एम्बेड करू शकतो किंवा तुमच्या विभागासाठी खास बनवलेली स्थानिक खाद्य नेटवर्क वेबसाइट तयार करू शकतो." sell_ask_services: "OFN सेवांबद्दल आमच्याकडे चौकशी करा. " shops_title: शॉप्स shops_headline: खरेदी, आमूलाग्र बदललेली. - shops_text: 'अन्न विशिष्ट कालावधीत वाढते, शेतकरी विशिष्ट कालावधीत कापणी करतात आणि तसेच आम्हीही विशिष्ट कालावधी (सायकल) मध्ये खाद्य ऑर्डर करतो. तुम्हाला ऑर्डर सायकल बंद असल्याचे आढळल्यास, लवकरच पुन्हा प्रयत्न करा. ' + shops_text: अन्नधान्य एका विशिष्ट कालावधीत वाढते, शेतकरी विशिष्ट कालावधीत कापणी करतात आणि तसेच आपणही विशिष्ट कालावधी (सायकल) मध्ये खाद्य ऑर्डर करतो. जर तुम्हाला हवे असलेल्या दुकानाची ऑर्डर सायकल बंद असल्याचे आढळल्यास, तर थोड्या वेळाने पुन्हा प्रयत्न करा. shops_signup_title: हब म्हणून साइन अप करा shops_signup_headline: अन्नधान्य हब, अमर्यादित. shops_signup_motivation: 'तुमचे मॉडेल काहीही असो, आम्ही तुम्हाला सहकार्य करतो. तुम्ही बदललात तरी आम्ही तुमच्या सोबत राहू. आम्ही ना-नफा, स्वतंत्र आणि मुक्त स्रोत आहोत. आम्ही अगदी तुमच्या मनाजोगे सॉफ्टवेअर भागीदार आहोत. ' @@ -2439,8 +2439,8 @@ mr: suburb_field_placeholder: "उदा. रत्नागिरी" suburb_field_error: "कृपया उपनगर प्रविष्ट करा" postcode_field: "पिनकोड:" - postcode_field_placeholder: "उदा. 3070" - postcode_field_error: "पोस्टकोड आवश्यक आहे" + postcode_field_placeholder: "उदा. 400081" + postcode_field_error: "पिनकोड आवश्यक आहे" state_field: "राज्य:" state_field_error: "राज्य आवश्यक" country_field: "देश:" @@ -2480,7 +2480,7 @@ mr: enterprise_description: "संक्षिप्त वर्णन" enterprise_description_placeholder: "तुमच्या एंटरप्राइझचे वर्णन करणारे एक संक्षिप्त वाक्य" enterprise_long_desc: "तपशीलवार वर्णन" - enterprise_long_desc_placeholder: "तुमच्या एंटरप्राइझची कथा सांगण्याची ही संधी आहे - तुम्ही कशामुळे वेगळे आणि विलक्षण आहात ? आमचा सल्ला आहे की तुमचे वर्णन 600 वर्ण किंवा 150 शब्दांपेक्षा कमी ठेवा." + enterprise_long_desc_placeholder: "तुमच्या एंटरप्राइझची कथा सांगण्याची ही संधी आहे - तुम्ही कशामुळे वेगळे आणि विलक्षण आहात ? आमचा सल्ला आहे की तुमचे वर्णन 600 अक्षरं किंवा 150 शब्दांपेक्षा कमी ठेवा." enterprise_long_desc_length: "%{num} / 600 वर्णांपर्यंत लिहू शकता. " enterprise_abn: "ABN" enterprise_abn_placeholder: "उदा. 99 123 456 789" @@ -2504,7 +2504,7 @@ mr: logo_placeholder: "एकदा अपलोड केल्यानंतर तुमचा लोगो पुनरावलोकनासाठी येथे दिसेल" promo: select_promo_image: "पायरी 3. प्रोमो इमेज निवडा" - promo_image_tip: "टीप: बॅनर म्हणून दर्शविले जाते, शक्यतो आकार 1200×260px असावा" + promo_image_tip: "टीप: बॅनर म्हणून दर्शविले जाते, शक्यतो आकार 1200×260px असावा" promo_image_label: "प्रोमो इमेज निवडा" promo_image_drag: "तुमचा प्रोमो येथे ड्रॅग आणि ड्रॉप करा" review_promo_image: "पायरी 4. तुमच्या प्रोमो बॅनरचे पुनरावलोकन करा" @@ -3591,8 +3591,6 @@ mr: start_date: "प्रारंभ तारीख" successfully_removed: "यशस्वीरित्या काढले" taxonomy_edit: "वर्गीकरण संपादन" - taxonomy_tree_error: "वर्गीकरण वृक्ष त्रुटी" - taxonomy_tree_instruction: "वर्गीकरण वृक्ष सूचना" tree: "वृक्ष" updating: "अपडेट करत आहे" your_order_is_empty_add_product: "तुमची ऑर्डर रिकामी आहे, कृपया उत्पादन शोधा आणि वर समाविष्ट करा" @@ -4022,7 +4020,7 @@ mr: stripe_connect: enterprise_select_placeholder: निवडा... loading_account_information_msg: Stripe वरून खाते माहिती लोड करत आहे, कृपया प्रतीक्षा करा... - stripe_disabled_msg: Stripe पेमेंट सिस्टम ॲडमिनिस्ट्रेटरद्वारे निष्क्रिय केले गेले आहे. + stripe_disabled_msg: Stripe पेमेंट सिस्टम ॲडमिनिस्ट्रेटरद्वारे डिसेबल केले गेले आहे. request_failed_msg: क्षमस्व. Stripe सह खाते तपशील सत्यापित करण्याचा प्रयत्न करताना काहीतरी चूक झाली... account_missing_msg: या एंटरप्राइझसाठी कोणतेही Stripe खाते अस्तित्वात नाही. connect_one: एक कनेक्ट करा @@ -4126,7 +4124,7 @@ mr: back_to_users_list: "वापरकर्त्यांच्या यादीकडे परत" general_settings: "सामान्य सेटिंग्ज" form: - disabled: "अक्षम?" + disabled: "डिसेबल्ड आहे?" email: "ईमेल" roles: "भूमिका" enterprise_limit: "एंटरप्राइझ मर्यादा" @@ -4174,6 +4172,9 @@ mr: email: "ईमेल" total: "एकूण" billing_address_name: "नाव" + taxons: + form: + name: नाव general_settings: edit: legal_settings: "कायदेशीर सेटिंग्ज" diff --git a/config/locales/nb.yml b/config/locales/nb.yml index 1f839f5808..3b4c61176c 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -78,6 +78,10 @@ nb: models: enterprise_fee: inherit_tax_requires_per_item_calculator: "Å arve skattekategorien krever en varekalkulator." + spree/image: + attributes: + attachment: + integrity_error: "kunne ikke laste. Kontroller at filen ikke er korrupt, og prøv igjen." spree/user: attributes: email: @@ -387,7 +391,7 @@ nb: cancel_order: "Avbryt Bestilling" confirm_send_invoice: "En faktura for denne bestillingen vil bli sendt til kunden. Er du sikker på at du vil fortsette?" confirm_resend_order_confirmation: "Er du sikker på at du vil sende ordrebekreftelse via epost på nytt?" - must_have_valid_business_number: "%{enterprise_name} må ha et gyldig ORG nr. før fakturaer kan sendes." + must_have_valid_business_number: "%{enterprise_name} må ha et gyldig ABN før faktura kan brukes." invoice: "Faktura" invoices: "Fakturaer" file: "Fil" @@ -530,6 +534,9 @@ nb: actions: edit: Rediger clone: Klon + delete: Slett + image: + edit: Rediger adjustments: skipped_changing_canceled_order: "Du kan ikke endre en avbrutt bestilling." begins_at: Begynner på @@ -594,6 +601,9 @@ nb: has_n_rules: "har %{num} regler" unsaved_confirm_leave: "Det finnes ulagrede endringer på denne siden. Fortsett uten å lagre?" available_units: "Tilgjengelige Enheter" + terms_of_service_have_been_updated_html: "Open Food Networks Vilkår for bruk er oppdatert: %{tos_link}" + terms_of_service: Les Vilkår for bruk + accept_terms_of_service: Godta Vilkårene for bruk shopfront_settings: embedded_shopfront_settings: "Innstillinger Innebygd Nettbutikk" enable_embedded_shopfronts: "Aktiver Innebygd Nettbutikk" @@ -747,6 +757,17 @@ nb: header: title: Endre produkter i bulk loading: Laster inn produktene dine + delete_modal: + delete_product_modal: + heading: "Slett produkt" + prompt: "Dette vil fjerne den permanent fra listen din." + confirmation_text: "Slett produkt" + cancellation_text: "Behold produkt" + delete_variant_modal: + heading: "Slett variant" + prompt: "Dette vil fjerne den permanent fra listen din." + confirmation_text: "Slett variant" + cancellation_text: "Behold variant" sort: pagination: total_html: "%{total} produkter funnet for søkekriteriene dine. Viser %{from} til %{to} ." @@ -756,7 +777,9 @@ nb: clear_search: Tøm søk filters: search_products: Søk etter produkter + search_for_producers: Søk etter produsenter all_producers: Alle produsenter + search_for_categories: Søk etter kategorier all_categories: Alle kategorier producers: label: Produsenter @@ -778,10 +801,21 @@ nb: invalid: one: "%{count} produkt kunne ikke lagres. Se gjennom feilene og prøv igjen." other: "%{count} produkter kunne ikke lagres. Se gjennom feilene og prøv igjen." - save: Lagre endringer reset: Forkaste endringer + save: Lagre endringer + new_variant: Ny variant bulk_update: - success: "Produktene er oppdatert" + success: Endringer lagret + edit_image: + title: Rediger produktbilde + close: Tilbake + upload: Last opp bilde + delete_product: + success: Produktet ble slettet + error: Kan ikke slette produktet + delete_variant: + success: Varianten ble slettet + error: Kan ikke slette varianten product_import: title: Produktimport file_not_found: Filen ble ikke funnet eller kunne ikke åpnes @@ -823,6 +857,9 @@ nb: product_categories: Produktkategorier tax_categories: Avgiftskategorier shipping_categories: Fraktkategorier + dfc_import_form: + enterprise: "Bedrift" + import: "Import" import: review: Anmeldelse import: Import @@ -880,6 +917,7 @@ nb: view_products: Gå til produktside view_inventory: Gå til varelager product_headings: + distributor: Distributør producer: Produsent sku: SKU name: Navn @@ -1026,6 +1064,7 @@ nb: images: legend: "Bilder" logo: Logo + logo_size: "300 x 300 piksler" promo_image_placeholder: 'Dette bildet vises i "Om Oss"' promo_image_note1: 'VENNLIGST MERK:' promo_image_note2: Ethvert promobilde lastet opp her vil bli kuttet til 1200x260. @@ -1210,6 +1249,29 @@ nb: create_custom_tab: "Lag egendefinert fane i butikk" custom_tab_title: "Tittel for egendefinert fane" custom_tab_content: "Innhold for egendefinert fane" + connected_apps: + legend: "Tilkoblede apper" + title: "Oppdag Regenerativ" + tagline: "Tillat Discover Regenerativ å publisere bedriftsinformasjonen din." + enable: "Tillat datadeling" + disable: "Stopp deling" + loading: "Laster" + note: | + Open Food Network-kontoen din er koblet til Discover Regenerativ. + Legg til eller oppdater informasjon om Discover Regenerativ-oppføringen din her. + link_label: "Administrer oppføring" + description_html: | +

+ Kvalifiserte produsenter kan vise frem sin regenerative legitimasjon, + oppdrettspraksis og mer gjennom en profiloppføring. + Forenkler hvordan kunder kan finne regenerative produkter og koble sammen + med produsenter av interesse. +

+

+ Lær mer om Discover Regenerativ + +

actions: edit_profile: Innstillinger properties: Egenskaper @@ -1439,6 +1501,8 @@ nb: has_no_payment_methods: "%{enterprise} har ingen betalingsmetoder" has_no_shipping_methods: "%{enterprise} har ingen fraktmetoder" has_no_enterprise_fees: "%{enterprise} har ingen bedriftsavgifter" + flashes: + dismiss: Lukk side_menu: enterprise: primary_details: "Primærdetaljer" @@ -1459,6 +1523,7 @@ nb: users: "Brukere" vouchers: Kuponger white_label: "Blank" + connected_apps: "Tilkoblede apper" enterprise_group: primary_details: "Primærdetaljer" users: "Brukere" @@ -1578,10 +1643,15 @@ nb: index: title: "OIDC-innstillinger" connect: "Koble til kontoen din" - already_connected: "Kontoen din er allerede knyttet til denne DFC-autorisasjonskontoen:" + disconnect: "Koble fra" + connected: "Kontoen din er knyttet til %{uid} ." les_communs_link: "Les Communs Open ID server" link_your_account: "Du må først koble kontoen din til autorisasjonsleverandøren som brukes av DFC (Les Communs Open ID Connect)." link_account_button: "Koble til Les Communs OIDC-kontoen din" + note_expiry: | + Tokens for å få tilgang til tilkoblede apper har utløpt. Vennligst oppdater din + kontotilkobling for å holde alle integrasjoner i gang. + refresh: "Oppdater autorisasjonen" view_account: "For å se kontoen din, se:" subscriptions: index: @@ -1898,9 +1968,11 @@ nb: invoice_column_price_per_unit_without_taxes: "Pris per enhet (ekskl. mva)" invoice_column_tax_rate: "Avgiftsrate" invoice_tax_total: "MVA Totalt:" + invoice_cancel_and_replace_invoice: "kansellerer og erstatter faktura" tax_invoice: "AVGIFTSFAKTURA" tax_total: "Totalavgift (%{rate}):" - invoice_shipping_type: "Type:" + invoice_shipping_category_delivery: "Levering" + invoice_shipping_category_pickup: "Henting" total_excl_tax: "Sum (Eks. avgift):" total_incl_tax: "Sum (Inkl. avgift):" total_all_tax: "Total skatt:" @@ -2120,6 +2192,9 @@ nb: order_back_to_store: Tilbake til butikken order_back_to_cart: Tilbake til handlekurven order_back_to_website: Tilbake til nettsiden + checkout_details_title: Kassedetaljer + checkout_payment_title: Betaling i kassen + checkout_summary_title: Kassesammendrag bom_tip: "Bruk denne siden for å endre produktmengder på tvers av flere bestillinger. Produkter kan også fjernes fra bestillinger helt hvis påkrevd." unsaved_changes_warning: "Ulagrede endringer finnes og vil gå tapt hvis du fortsetter." unsaved_changes_error: "Felt med røde kanter inneholder feil." @@ -2982,6 +3057,8 @@ nb: report_header_transaction_fee: Transaksjonsgebyr (ingen skatt) report_header_total_untaxable_admin: Samlede avgiftsfrie administreringsjusteringer (ingen skatt) report_header_total_taxable_admin: Sum skattepliktige adminintreringsjusteringer (inklusive skatt) + report_header_voucher_label: Kupongetikett + report_header_voucher_amount: "Kupongbeløp ( %{currency_symbol} )" report_line_cost_of_produce: Produktkostnad report_line_line_items: linjeelementer report_header_last_completed_order_date: Siste fullførte bestillingsdato @@ -3645,8 +3722,6 @@ nb: start_date: "Startdato" successfully_removed: "Fjernet OK" taxonomy_edit: "Rediger kategori" - taxonomy_tree_error: "Feil i kategoritre" - taxonomy_tree_instruction: "Instruksjon for kategoritre" tree: "Tre" updating: "Oppdaterer" your_order_is_empty_add_product: "Bestillingen din er tom, vennligst søk etter og legg til et produkt over" @@ -3941,6 +4016,9 @@ nb: add_product: cannot_add_item_to_canceled_order: "Kan ikke legge varen til den avbrutte bestillingen" include_out_of_stock_variants: "Inkluder varianter uten tilgjengelig lager" + shipment: + mark_as_shipped_message_html: "Dette vil merke bestillingen som sendt.
Er du sikker på at du vil fortsette?" + mark_as_shipped_label_message: "Send en e-post om forsendelse/hentingsvarsling til kunden." index: listing_orders: "Lister opp bestillinger" new_order: "Ny bestilling" @@ -3999,6 +4077,9 @@ nb: line_item_adjustments: "Artikkeljustering" order_adjustments: "Bestillingsjusteringer" order_total: "Bestilling Totalt" + invoices: + index: + order_has_changed: "Ordren er endret siden siste fakturaoppdatering. Fakturaen som vises her er kanskje ikke oppdatert lenger." overview: enterprises_header: ofn_with_tip: Bedrifter er Produsenter og / eller Hubs og er den grunnleggende organisasjonsenheten innen Open Food Network. @@ -4007,6 +4088,7 @@ nb: has_no_payment_methods: "har ingen betalingsmåter" has_no_shipping_methods: "har ingen leveringmetoder" products: + products_tip: "Produktene du selger gjennom Open Food Network." active_products: zero: "Du har ingen aktive produkter." one: "Du har ett aktivt produkt" @@ -4159,6 +4241,8 @@ nb: bulk_unit_size: Bulk enhetsstørrelse display_as: display_as: Vis som + clone: + success: Produkt klonet reports: table: select_and_search: "Velg filtre og klikk på %{option} for å få tilgang til dine data." @@ -4228,6 +4312,15 @@ nb: email: "Epost" total: "Total" billing_address_name: "Navn" + taxons: + form: + name: Navn + permalink: Permalink + meta_title: Metatittel + meta_description: Metabeskrivelse + meta_keywords: Meta nøkkelord + description: Beskrivelse + dfc_id: DFC URI general_settings: edit: legal_settings: "Juridiske innstillinger" @@ -4523,3 +4616,6 @@ nb: pagination: next: Neste previous: Tidligere + invisible_captcha: + sentence_for_humans: "Vennligst la stå tomt" + timestamp_error_message: "Vennligst prøv igjen etter 5 sekunder." diff --git a/config/locales/nl_BE.yml b/config/locales/nl_BE.yml index 9924b1bc51..190328f301 100644 --- a/config/locales/nl_BE.yml +++ b/config/locales/nl_BE.yml @@ -211,7 +211,6 @@ nl_BE: cancel_order: "Bestelling annuleren" confirm_send_invoice: "Een factuur voor deze bestelling wordt naar de klant gestuurd. Weet u zeker dat u wilt doorgaan?" confirm_resend_order_confirmation: "Weet u zeker dat u de orderbevestiging opnieuw wilt verzenden?" - must_have_valid_business_number: "%{enterprise_name}moet een geldige ABN hebben voordat facturen kunnen worden verzonden." invoice: "Factuur" active: "Aktief" cancelled: "Geannuleerd" @@ -338,6 +337,9 @@ nl_BE: actions: edit: bewerking clone: Kloon + delete: 'Uitwissen ' + image: + edit: bewerking begins_at: Begint Bij begins_on: Begint Op customer: Klant @@ -542,6 +544,10 @@ nl_BE: categories: label: Categorieën search: Zoeken + table: + new_variant: Nieuw variant + edit_image: + close: achterstevoren product_import: title: Importeren van producten file_not_found: Bestand niet gevonden of kon niet worden geopend @@ -581,6 +587,9 @@ nl_BE: product_categories: Productcategorieën tax_categories: Belastingcategorieën shipping_categories: Verzendingscategorieën + dfc_import_form: + enterprise: "Onderneming" + import: "Importeren" import: review: Beoordeling import: Importeren @@ -638,6 +647,7 @@ nl_BE: view_products: Ga naar de pagina Producten view_inventory: Ga naar de inventarispagina product_headings: + distributor: Distributeur producer: Producent sku: SKU name: Naam @@ -911,6 +921,8 @@ nl_BE: rate: Percentage customers: Klant active: Actief? + connected_apps: + loading: "Aan het opladen" actions: edit_profile: Instellingen properties: Eigenschappen @@ -1458,6 +1470,8 @@ nl_BE: invoice_tax_total: "Totaal BTW:" tax_invoice: "FACTUUR" tax_total: "BTW Totaal (%{rate}):" + invoice_shipping_category_delivery: "Levering" + invoice_shipping_category_pickup: "Ophaling" total_excl_tax: "Totaal (Excl. BTW):" total_incl_tax: "Totaal (Incl. BTW):" abn: "Bedrijfsnummer : " @@ -3482,6 +3496,11 @@ nl_BE: email: "E-mail" total: "Totaal" billing_address_name: "Naam" + taxons: + form: + name: Naam + permalink: Permalink + description: Beschrijving general_settings: edit: legal_settings: "De legale instelling" diff --git a/config/locales/pa.yml b/config/locales/pa.yml new file mode 100644 index 0000000000..7f8357ae48 --- /dev/null +++ b/config/locales/pa.yml @@ -0,0 +1,4396 @@ +pa: + language_name: "ਪੰਜਬੀ" + activerecord: + models: + spree/product: ਉਤਪਾਦ + spree/shipping_method: ਸ਼ਿਪਿੰਗ ਦਾ ਤਰੀਕਾ + attributes: + spree/order/ship_address: + address1: "ਸ਼ਿਪਿੰਗ ਪਤਾ (ਗਲੀ + ਘਰ ਦਾ ਨੰਬਰ)" + address2: "ਸ਼ਿਪਿੰਗ ਪਤੇ ਦੀ ਲਾਈਨ 2" + city: "ਸ਼ਿਪਿੰਗ ਪਤੇ ਦਾ ਸ਼ਹਿਰ" + country: "ਸ਼ਿਪਿੰਗ ਪਤੇ ਦਾ ਦੇਸ਼" + phone: "ਫੋਨ ਨੰਬਰ" + firstname: "ਪਹਿਲਾ ਨਾਂ" + lastname: "ਆਖਰੀ ਨਾਂ" + zipcode: "ਸ਼ਿਪਿੰਗ ਪਤੇ ਦਾ ਪਿਨਕੋਡ" + spree/order/bill_address: + address1: "ਬਿਲਿੰਗ ਪਤਾ (ਗਲੀ + ਘਰ ਦਾ ਨੰਬਰ)" + zipcode: "ਬਿਲਿੰਗ ਪਤੇ ਦਾ ਪਿਨਕੋਡ" + city: "ਬਿਲਿੰਗ ਪਤੇ ਦਾ ਸ਼ਹਿਰ" + country: "ਬਿਲਿੰਗ ਪਤੇ ਦਾ ਦੇਸ਼" + firstname: "ਬਿਲਿੰਗ ਪਤੇ ਤੇ ਪਹਿਲਾ ਨਾਂ" + lastname: "ਬਿਲਿੰਗ ਪਤੇ ਤੇ ਆਖਰੀ ਨਾਂ" + phone: ਗਾਹਕ ਫੋਨ + spree/user: + password: "ਪਾਸਵਰਡ" + password_confirmation: "ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ" + reset_password_token: ਪਾਸਵਰਡ ਟੋਕਨ ਨੂੰ ਰੀਸੈਟ ਕਰੋ + enterprise_fee: + fee_type: ਫੀਸ ਦੀ ਕਿਸਮ + spree/order: + payment_state: ਭੁਗਤਾਨ ਸਥਿਤੀ + shipment_state: ਸ਼ਿਪਮੈਂਟ ਦੀ ਸਥਿਤੀ + completed_at: ਇਸ ਸਮੇਂ ਪੂਰਾ ਹੋਇਆ + number: ਨੰਬਰ + state: ਸਥਿਤੀ + email: ਗਾਹਕ ਦਾ ਈਮੇਲ + spree/payment: + amount: ਰਕਮ + state: ਸਥਿਤੀ + source: ਸਰੋਤ + spree/product: + name: "ਉਤਪਾਦ ਦਾ ਨਾਂ" + price: "ਕੀਮਤ" + primary_taxon: "ਉਤਪਾਦ ਸ਼੍ਰੇਣੀ" + supplier: "ਸਪਲਾਇਰ" + shipping_category_id: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ" + variant_unit: "ਵੇਰੀਐਂਟ ਯੂਨਿਟ" + variant_unit_name: "ਵੇਰੀਐਂਟ ਯੂਨਿਟ ਦਾ ਨਾਮ" + unit_value: "ਯੂਨਿਟ ਵੈਲਯੂ" + spree/credit_card: + base: "ਕਰੇਡਿਟ ਕਾਰਡ" + number: "ਨੰਬਰ" + month: "ਮਹੀਨਾ" + verification_value: "ਪੁਸ਼ਟੀਕਰਨ ਵੈਲਯੂ" + year: "ਸਾਲ" + order_cycle: + orders_close_at: ਸਮਾਪਤੀ ਮਿਤੀ + variant_override: + count_on_hand: "ਹੱਥ ਵਿਚ" + spree/payment_method/calculator: + preferred_flat_percent: "ਫਲੈਟ ਪ੍ਰਤੀਸ਼ਤ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_amount: "ਰਾਸ਼ੀ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_first_item: "ਪਹਿਲੀ ਆਈਟਮ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_additional_item: "ਵਾਧੂ ਆਈਟਮ ਦੀ ਲਾਗਤ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_max_items: "ਅਧਿਕਤਮ ਆਈਟਮਾਂ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_minimal_amount: "ਨਿਮਨਤਮ ਰਾਸ਼ੀ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_normal_amount: "ਆਮ ਰਾਸ਼ੀ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_discount_amount: "ਛੁੱਟ ਦੀ ਰਾਸ਼ੀ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_unit_from_list: "ਸੂਚੀ ਤੋਂ ਯੂਨਿਟ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + preferred_per_unit: "ਪ੍ਰਤੀ ਯੂਨਿਟ ਦਾ ਕੈਲਕੁਲੇਟਰ:" + enterprise: + white_label_logo_link: "ਸ਼ੌਪਫਰੰਟ ਵਿੱਚ ਵਰਤੇ ਗਏ ਲੋਗੋ ਦਾ ਲਿੰਕ" + errors: + models: + enterprise_fee: + inherit_tax_requires_per_item_calculator: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਪ੍ਰਤੀ-ਆਈਟਮ ਕੈਲਕੁਲੇਟਰ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।" + spree/user: + attributes: + email: + taken: "ਇਸ ਈਮੇਲ ਲਈ ਪਹਿਲਾਂ ਹੀ ਇੱਕ ਖਾਤਾ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਲੌਗਇਨ ਕਰੋ ਜਾਂ ਆਪਣਾ ਪਾਸਵਰਡ ਰੀਸੈਟ ਕਰੋ।" + reset_password_token: + invalid: ਅਵੈਧ ਹੈ + spree/order: + no_card: ਚਾਰਜ ਕਰਨ ਲਈ ਕੋਈ ਅਧਿਕਾਰਤ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਉਪਲਬਧ ਨਹੀਂ ਹਨ + spree/credit_card: + attributes: + base: + card_expired: "ਮਿਆਦ ਖਤਮ ਹੋ ਗਈ ਹੈ" + order_cycle: + attributes: + orders_close_at: + after_orders_open_at: ਖੁੱਲਣ ਦੀ ਮਿਤੀ ਤੋਂ ਬਾਅਦ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + variant_override: + count_on_hand: + using_producer_stock_settings_but_count_on_hand_set: "ਖਾਲੀ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ ਕਿਉਂਕਿ ਉਤਪਾਦਕ ਸਟਾਕ ਸੈਟਿੰਗਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਹੈ" + on_demand_but_count_on_hand_set: "ਜੇ ਡਿਮਾਂਡ ਤੇ ਹੋਵੇ ਤਾਂ ਖਾਲੀ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + limited_stock_but_no_count_on_hand: "ਸੀਮਤ ਸਟਾਕ ਨੂੰ ਮਜਬੂਰ ਕਰਨ ਕਰਕੇ ਨਿਰਧਾਰਤ ਕੀਤਾ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ" + messages: + confirmation: "%{attribute} ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ" + blank: "ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ" + too_short: "ਬਹੁਤ ਛੋਟਾ (ਨਿਊਨਤਮ %{count} ਅੱਖਰ)" + errors: + messages: + content_type_invalid: "ਅਵੈਧ ਸਮੱਗਰੀ ਦੀ ਕਿਸਮ ਸ਼ਾਮਲ ਹੈ" + file_size_out_of_range: "ਸਾਈਜ਼ %{file_size} ਲੋੜੀਂਦੀ ਸੀਮਾ ਵਿੱਚਕਾਰ ਨਹੀਂ ਹੈ" + limit_out_of_range: "ਕੁੱਲ ਗਿਣਤੀ ਸੀਮਾ ਤੋਂ ਬਾਹਰ ਹੈ" + image_metadata_missing: "ਇਹ ਇੱਕ ਵੈਧ ਫੋਟੋ ਨਹੀਂ ਹੈ" + dimension_min_inclusion: "%{width} x %{height} ਪਿਕਸਲ ਤੋਂ ਵੱਧ ਜਾਂ ਬਰਾਬਰ ਦਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।" + dimension_max_inclusion: "%{width} x %{height} ਪਿਕਸਲ ਤੋਂ ਘੱਟ ਜਾਂ ਬਰਾਬਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।" + dimension_width_inclusion: "ਚੌੜਾਈ %{min} ਅਤੇ %{max} ਪਿਕਸਲਾਂ ਵਿੱਚਕਾਰ ਸ਼ਾਮਲ ਨਹੀਂ ਹੈ।" + dimension_height_inclusion: "ਲੰਬਾਈ %{min} ਅਤੇ %{max} ਪਿਕਸਲਾਂ ਵਿੱਚਕਾਰ ਸ਼ਾਮਲ ਨਹੀਂ ਹੈ।" + dimension_width_greater_than_or_equal_to: "ਚੌੜਾਈ %{length} ਪਿਕਸਲ ਤੋਂ ਵੱਧ ਜਾਂ ਬਰਾਬਰ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" + dimension_height_greater_than_or_equal_to: "ਲੰਬਾਈ %{length} ਪਿਕਸਲ ਤੋਂ ਵੱਧ ਜਾਂ ਬਰਾਬਰ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" + dimension_width_less_than_or_equal_to: "ਚੌੜਾਈ %{length} ਪਿਕਸਲ ਤੋਂ ਘੱਟ ਜਾਂ ਬਰਾਬਰ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" + dimension_height_less_than_or_equal_to: "ਲੰਬਾਈ %{length} ਪਿਕਸਲ ਤੋਂ ਘੱਟ ਜਾਂ ਬਰਾਬਰ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" + dimension_width_equal_to: "width must be equal to %{length} pixel." + dimension_height_equal_to: "ਲੰਬਾਈ %{length} ਪਿਕਸਲ ਦੇ ਬਰਾਬਰ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" + aspect_ratio_not_square: "ਇੱਕ ਚੌਰਸ ਫੋਟੋ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ" + aspect_ratio_not_portrait: "ਇੱਕ ਪੋਰਟਰੇਟ ਫੋਟੋ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ" + aspect_ratio_not_landscape: "ਇੱਕ ਲੈਂਡਸਕੇਪ ਫੋਟੋ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ" + aspect_ratio_is_not: "ਅਸਪੈਕਟ ਅਨੁਪਾਤ %{aspect ratio} ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + aspect_ratio_unknown: "ਇੱਕ ਅਗਿਆਤ ਅਸਪੈਕਟ ਅਨੁਪਾਤ ਹੈ" + image_not_processable: "ਇਹ ਇੱਕ ਵੈਧ ਫੋਟੋ ਨਹੀਂ ਹੈ" + not_found: + title: "ਜਿਹੜਾ ਪੰਨਾ ਤੁਸੀਂ ਲੱਭ ਰਹੇ ਸੀ ਉਹ ਮੌਜੂਦ ਨਹੀਂ ਹੈ (404)" + message_html: "ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ

ਇਹ ਇੱਕ ਅਸਥਾਈ ਸਮੱਸਿਆ ਹੋ ਸਕਦੀ ਹੈ। ਪਿਛਲੀ ਸਕ੍ਰੀਨ ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ ਬੈਕ ਬਟਨ ਤੇ ਕਲਿੱਕ ਕਰੋ ਜਾਂ ਹੋਮ ਤੇ ਵਾਪਸ ਜਾਓ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।

ਸਹਾਇਤਾ ਨਾਲ ਸੰਪਰਕ ਕਰੋ

ਜੇਕਰ ਸਮੱਸਿਆ ਬਣੀ ਰਹਿੰਦੀ ਹੈ ਜਾਂ ਜ਼ਰੂਰੀ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਨੂੰ ਇੱਸ ਦੇ ਬਾਰੇ ਵਿੱਚ ਦੱਸੋ। ਗਲੋਬਲ ਓਪਨ ਫੂਡ ਨੈੱਟਵਰਕ ਦੇ ਲੋਕਲ ਪੰਨੇ ਤੋਂ ਸਾਡੇ ਸੰਪਰਕ ਵੇਰਵੇ ਪ੍ਰਾਪਤ ਕਰੋ

ਤੁਹਾਡਾ ਸਾਨੂੰ ਗੁੰਮ ਹੋਏ ਪੰਨੇ ਬਾਰੇ ਜਿੰਨਾ ਸੰਭਵ ਹੋ ਸਕੇ ਉਨ੍ਹੇਂ ਜਿਆਦਾ ਵੇਰਵੇ ਪ੍ਰਦਾਨ ਕਰਨਾ ਅਸਲ ਵਿੱਚ ਸਾਡੀ ਵਧੇਰੀ ਮਦਦ ਕਰਦਾ ਹੈ।

" + internal_server_error: + title: "ਸਾਨੂੰ ਅਫਸੋਸ ਹੈ, ਪਰ ਕੁਝ ਗਲਤ ਹੋਇਆ ਹੈ (500)" + message_html: "ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ

ਇਹ ਇੱਕ ਅਸਥਾਈ ਸਮੱਸਿਆ ਹੋ ਸਕਦੀ ਹੈ। ਪਿਛਲੀ ਸਕ੍ਰੀਨ ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ ਬੈਕ ਬਟਨ ਤੇ ਕਲਿੱਕ ਕਰੋ ਜਾਂ ਹੋਮ ਤੇ ਵਾਪਿਸ ਜਾਓ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।

ਅਸੀਂ ਇਸ ਤੇ ਕੰਮ ਕਰ ਰਹੇ ਹਾਂ

ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਸਮੱਸਿਆ ਨੂੰ ਪਹਿਲਾਂ ਵੇਖਿਆ ਹੈ, ਤਾਂ ਅਸੀਂ ਸ਼ਾਇਦ ਇਸ ਬਾਰੇ ਪਹਿਲਾਂ ਹੀ ਜਾਣਦੇ ਹਾਂ ਅਤੇ ਇਸ ਨੂੰ ਠੀਕ ਕਰਨ ਲਈ ਕੰਮ ਕਰ ਰਹੇ ਹਾਂ। ਅਸੀਂ ਉਨ੍ਹਾਂ ਸਾਰੀਆਂ ਤਰੁੱਟੀਆਂ ਨੂੰ ਰਿਕਾਰਡ ਕਰਦੇ ਹਾਂ ਜੋ ਅਸੀਂ ਨੋਟਿਸ ਕਰਦੇ ਹਾਂ।

ਸਹਾਇਤਾ ਨਾਲ ਸੰਪਰਕ ਕਰੋ

ਜੇਕਰ ਸਮੱਸਿਆ ਬਣੀ ਰਹਿੰਦੀ ਹੈ ਜਾਂ ਜ਼ਰੂਰੀ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਨੂੰ ਉਸ ਬਾਰੇ ਦੱਸੋ। ਗਲੋਬਲ ਓਪਨ ਫੂਡ ਨੈੱਟਵਰਕ ਦੇ ਲੋਕਲ ਪੰਨੇ ਤੋਂ ਸਾਡੇ ਸੰਪਰਕ ਵੇਰਵੇ ਪ੍ਰਾਪਤ ਕਰੋ

< p>ਤੁਹਾਡਾ ਸਾਨੂੰ ਗੁੰਮ ਹੋਏ ਪੰਨੇ ਬਾਰੇ ਜਿੰਨਾ ਸੰਭਵ ਹੋ ਸਕੇ ਉਨ੍ਹੇਂ ਜਿਆਦਾ ਵੇਰਵੇ ਪ੍ਰਦਾਨ ਕਰਨਾ ਅਸਲ ਵਿੱਚ ਸਾਡੀ ਵਧੇਰੀ ਮਦਦ ਕਰਦਾ ਹੈ।

" + unprocessable_entity: + title: "ਜੋ ਬਦਲਾਅ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਸੀ ਉਹ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ (422)" + message_html: "

ਜੋ ਬਦਲਾਅ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਸੀ ਉਹ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਤੁਸੀਂ ਕਿਸੇ ਅਜਿਹੀ ਚੀਜ਼ ਨੂੰ ਬਦਲਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਹੋਵੇ ਜਿਸ ਤੱਕ ਤੁਹਾਡੀ ਪਹੁੰਚ ਨਹੀਂ ਹੈ।

ਹੋਮ ਤੇ ਵਾਪਸ ਜਾਓ

" + stimulus_reflex_error: "ਸਾਨੂੰ ਅਫਸੋਸ ਹੈ, ਪਰ ਕੁਝ ਗਲਤ ਹੋਇਆ ਹੈ।\\n\\nਇਹ ਇੱਕ ਅਸਥਾਈ ਸਮੱਸਿਆ ਹੋ ਸਕਦੀ ਹੈ, ਇਸ ਲਈ ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜਾਂ ਪੰਨੇ ਨੂੰ ਦੋਬਾਰਾ ਲੋਡ ਕਰੋ।\\nਅਸੀਂ ਸਾਰੀਆਂ ਤਰੁੱਟੀਆਂ ਨੂੰ ਰਿਕਾਰਡ ਕਰਦੇ ਹਾਂ ਅਤੇ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਅਸੀਂ ਉਨਾਂ ਨੂੰ ਠੀਕ ਕਰਨ ਤੇ ਕੰਮ ਰਹੇ ਹੋਈਏ।\\nਜੇਕਰ ਸਮੱਸਿਆ ਬਣੀ ਰਹਿੰਦੀ ਹੈ ਜਾਂ ਜ਼ਰੂਰੀ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਡੇ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।" + stripe: + error_code: + incorrect_number: "ਕਾਰਡ ਨੰਬਰ ਗਲਤ ਹੈ।" + invalid_number: "ਕਾਰਡ ਨੰਬਰ ਇੱਕ ਵੈਧ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ ਨਹੀਂ ਹੈ।" + invalid_expiry_month: "ਕਾਰਡ ਦੀ ਮਿਆਦ ਖ਼ਤਮ ਹੋਣ ਦਾ ਮਹੀਨਾ ਅਵੈਧ ਹੈ।" + invalid_expiry_year: "ਕਾਰਡ ਦੀ ਮਿਆਦ ਖ਼ਤਮ ਹੋਣ ਦਾ ਸਾਲ ਅਵੈਧ ਹੈ।" + invalid_cvc: "ਕਾਰਡ ਦਾ ਸੁਰੱਖਿਆ ਕੋਡ ਅਵੈਧ ਹੈ।" + expired_card: "ਕਾਰਡ ਦੀ ਮਿਆਦ ਖ਼ਤਮ ਹੋ ਗਈ ਹੈ।" + incorrect_cvc: "ਕਾਰਡ ਦਾ ਸੁਰੱਖਿਆ ਕੋਡ ਗਲਤ ਹੈ।" + incorrect_zip: "ਕਾਰਡ ਦੇ ਪਿੰਨ ਕੋਡ ਦੀ ਤਸਦੀਕ ਅਸਫਲ ਰਹੀ।" + card_declined: "ਕਾਰਡ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ।" + missing: "ਜਿਸ ਗਾਹਕ ਤੋਂ ਸ਼ੁਲਕ ਲੀਤਾ ਜਾ ਰਿਹਾ ਹੈ ਉਸਦਾ ਕੋਈ ਕਾਰਡ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।" + processing_error: "ਕਾਰਡ ਦੀ ਪ੍ਰੋਸਸਸਿੰਗ ਕਰਦੇ ਸਮੇਂ ਇੱਕ ਗਲਤੀ ਹੋਈ।" + rate_limit: "API ਤੇ ਬਹੁਤ ਤੇਜ਼ੀ ਨਾਲ ਆਉਣ ਵਾਲੀਆਂ ਬੇਨਤੀਆਂ ਕਾਰਨ ਇੱਕ ਗਲਤੀ ਹੋਈ। ਕਿਰਪਾ ਕਰਕੇ ਸਾਨੂੰ ਦੱਸੋ ਜੇਕਰ ਤੁਹਾਨੂੰ ਇਸ ਗਲਤੀ ਦਾ ਸਾਹਮਣਾ ਲਗਾਤਾਰ ਕਰਨਾ ਪੈਂਦਾ ਹੈ।" + authentication_required: "ਕਾਰਡ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਸੀ ਕਿਉਂਕਿ ਲੈਣ-ਦੇਣ ਲਈ ਪ੍ਰਮਾਣਿਕਤਾ ਦੀ ਲੋੜ ਹੈ।" + approve_with_id: "ਭੁਗਤਾਨ ਅਧਿਕਾਰਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।" + call_issuer: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + card_not_supported: "ਕਾਰਡ ਇਸ ਕਿਸਮ ਦੀ ਖਰੀਦ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ।" + card_velocity_exceeded: "ਗਾਹਕ ਨੇ ਆਪਣੇ ਕਾਰਡ ਉਤੇ ਉਪਲਬਧ ਬਕਾਇਆ ਰਾਸ਼ੀ ਜਾਂ ਕ੍ਰੈਡਿਟ ਸੀਮਾ ਨੂੰ ਪਾਰ ਕਰ ਲਿਆ ਹੈ।" + currency_not_supported: "ਕਾਰਡ ਨਿਰਧਾਰਿਤ ਕਰੰਸੀ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ।" + do_not_honor: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + do_not_try_again: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + duplicate_transaction: "ਲੈਣ-ਦੇਣ ਦੀ ਉਸੇ ਰਕਮ ਅਤੇ ਉਸੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਤੇ ਹਾਲ ਹੀ ਵਿੱਚ ਜਮ੍ਹਾਂ ਕੀਤਾ ਗਿਆ ਸੀ।" + fraudulent: "ਭੁਗਤਾਨ ਅਸਵੀਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਸਟ੍ਰਾਈਪ ਨੂੰ ਸ਼ੱਕ ਹੈ ਕਿ ਇਹ ਧੋਖਾਧੜੀ ਹੋ ਸਕਦੀ ਹੈ।" + generic_decline: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + incorrect_pin: "ਦਰਜ ਕੀਤਾ ਪਿੰਨ ਗਲਤ ਹੈ। ਇਹ ਅਸਵੀਕਾਰ ਕੀਤਾ ਕੋਡ ਸਿਰਫ ਕਾਰਡ ਰੀਡਰ ਦੁਆਰਾ ਕੀਤੇ ਗਏ ਭੁਗਤਾਨਾਂ ਤੇ ਲਾਗੂ ਹੁੰਦਾ ਹੈ।" + insufficient_funds: "ਖਰੀਦ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਕਾਰਡ ਉਤੇ ਲੋੜੀਂਦੇ ਫੰਡ ਨਹੀਂ ਹਨ।" + invalid_account: "ਇਹ ਕਾਰਡ, ਜਾਂ ਖਾਤਾ ਜਿਸ ਨਾਲ ਕਾਰਡ ਲਿੰਕ ਕੀਤਾ ਗਿਆ ਹੈ, ਅਵੈਧ ਹੈ।" + invalid_amount: "ਭੁਗਤਾਨ ਦੀ ਰਕਮ ਅਵੈਧ ਹੈ, ਜਾਂ ਮਨਜ਼ੂਰ ਕੀਤੀ ਰਕਮ ਤੋਂ ਵੱਧ ਹੈ।" + invalid_pin: "ਦਰਜ ਕੀਤਾ ਪਿੰਨ ਗਲਤ ਹੈ। ਇਹ ਅਸਵੀਕਾਰ ਕੀਤਾ ਕੋਡ ਸਿਰਫ ਕਾਰਡ ਰੀਡਰ ਦੁਆਰਾ ਕੀਤੇ ਗਏ ਭੁਗਤਾਨਾਂ ਤੇ ਲਾਗੂ ਹੁੰਦਾ ਹੈ।" + issuer_not_available: "ਕਾਰਡ ਦੇ ਜਾਰੀਕਰਤਾ ਨਾਲ ਸੰਪਰਕ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ, ਇਸਲਈ ਭੁਗਤਾਨ ਅਧਿਕਾਰਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।" + lost_card: "ਭੁਗਤਾਨ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਕਾਰਡ ਗੁੰਮ ਹੋਣ ਦੀ ਰਿਪੋਰਟ ਕੀਤੀ ਗਈ ਹੈ।" + merchant_blacklist: "ਭੁਗਤਾਨ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਇਹ ਸਟ੍ਰਾਈਪ ਉਪਭੋਗਤਾ ਦੀ ਬਲਾਕ ਸੂਚੀ ਵਿੱਚ ਦਿੱਤੀ ਵੈਲਯੂ ਨਾਲ ਮੇਲ ਖਾਂਦਾ ਹੈ।" + new_account_information_available: "ਇਹ ਕਾਰਡ, ਜਾਂ ਖਾਤਾ ਜਿਸ ਨਾਲ ਕਾਰਡ ਲਿੰਕ ਕੀਤਾ ਗਿਆ ਹੈ, ਅਵੈਧ ਹੈ।" + no_action_taken: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + not_permitted: "ਭੁਗਤਾਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।" + offline_pin_required: "ਕਾਰਡ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਇਸਨੂੰ ਇੱਕ ਪਿੰਨ ਦੀ ਲੋੜ ਹੈ।" + online_or_offline_pin_required: "ਕਾਰਡ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਇਸਨੂੰ ਇੱਕ ਪਿੰਨ ਦੀ ਲੋੜ ਹੈ।" + pickup_card: "ਇਸ ਭੁਗਤਾਨ ਲਈ ਕਾਰਡ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ (ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਸਦੀ ਗੁੰਮ ਜਾਂ ਚੋਰੀ ਹੋਣ ਦੀ ਰਿਪੋਰਟ ਕੀਤੀ ਗਈ ਹੈ)।" + pin_try_exceeded: "ਪਿੰਨ ਭਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ਾਂ ਦੀ ਮਨਜ਼ੂਰਸ਼ੁਦਾ ਸੰਖਿਆ ਤੋਂ ਵਾਧੂ ਹੋ ਗਈ ਹੈ।" + reenter_transaction: "ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਜਾਰੀਕਰਤਾ ਦੁਆਰਾ ਭੁਗਤਾਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ।" + restricted_card: "ਇਸ ਭੁਗਤਾਨ ਲਈ ਕਾਰਡ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ (ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਸਦੀ ਗੁੰਮ ਜਾਂ ਚੋਰੀ ਹੋਣ ਦੀ ਰਿਪੋਰਟ ਕੀਤੀ ਗਈ ਹੈ)।" + revocation_of_all_authorizations: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + revocation_of_authorization: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + security_violation: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + service_not_allowed: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + stolen_card: "ਭੁਗਤਾਨ ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ ਕਿਉਂਕਿ ਕਾਰਡ ਚੋਰੀ ਹੋਣ ਦੀ ਰਿਪੋਰਟ ਕੀਤੀ ਗਈ ਹੈ।" + stop_payment_order: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + testmode_decline: "ਸਟ੍ਰਾਈਪ ਦੇ ਇੱਕ ਟੈਸਟ ਕਾਰਡ ਨੰਬਰ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਗਈ ਸੀ।" + transaction_not_allowed: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + try_again_later: "ਕਾਰਡ ਨੂੰ ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ।" + withdrawal_count_limit_exceeded: "ਗਾਹਕ ਨੇ ਆਪਣੇ ਕਾਰਡ ਉਤੇ ਉਪਲਬਧ ਬਕਾਇਆ ਰਾਸ਼ੀ ਜਾਂ ਕ੍ਰੈਡਿਟ ਸੀਮਾ ਨੂੰ ਪਾਰ ਕਰ ਲਿਆ ਹੈ।" + activemodel: + errors: + messages: + inclusion: "ਸੂਚੀ ਵਿੱਚ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤਾ ਗਿਆ" + models: + order_management/subscriptions/validator: + attributes: + subscription_line_items: + at_least_one_product: "^ਕਿਰਪਾ ਕਰਕੇ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਉਤਪਾਦ ਜੋੜੋ" + not_available: "^%{name} ਚੁਣੀ ਗਈ ਅਨੁਸੁਚੀ ਤੋਂ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + ends_at: + after_begins_at: "ਸ਼ੁਰੂ ਕਰਨ ਤੋਂ ਬਾਅਦ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + customer: + does_not_belong_to_shop: "%{shop} ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੈ" + schedule: + not_coordinated_by_shop: "%{shop} ਦੁਆਰਾ ਤਾਲਮੇਲ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ" + payment_method: + not_available_to_shop: "%{shop} ਲਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + invalid_type: "ਨਕਦੀ ਜਾਂ ਸਟ੍ਰਾਈਪ ਵਿਧੀ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ" + charges_not_allowed: "^ਇਸ ਗਾਹਕ ਦੁਆਰਾ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਤੇ ਪੈਸੇ ਕੱਟੇ ਜਾਣ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ" + no_default_card: "^ਇਸ ਗਾਹਕ ਲਈ ਕੋਈ ਡਿਫੌਲਟ ਕਾਰਡ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + shipping_method: + not_available_to_shop: "%{shop} ਲਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + card_details: "ਕਾਰਡ ਦੇ ਵੇਰਵੇ" + card_type: "ਕਾਰਡ ਦੀ ਕਿਸਮ" + cardholder_name: "ਕਾਰਡ ਧਾਰਕ ਦਾ ਨਾਮ" + community_forum_url: "ਕਮਿਊਨਿਟੀ ਫੋਰਮ URL" + customer_instructions: "ਗਾਹਕ ਨਿਰਦੇਸ਼" + additional_information: "ਵਧੀਕ ਜਾਣਕਾਰੀ" + devise: + passwords: + spree_user: + cannot_be_blank: "ਉਪਭੋਗਤਾ ਪਾਸਵਰਡ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਪਾਸਵਰਡ ਦਰਜ ਕਰੋ।" + confirmations: + send_instructions: "ਕੁਝ ਮਿੰਟਾਂ ਵਿੱਚ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਨਿਰਦੇਸ਼ਾਂ ਵਾਲਾ ਇੱਕ ਈਮੇਲ ਪ੍ਰਾਪਤ ਹੋਵੇਗੀ।" + failed_to_send: "ਤੁਹਾਡੀ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਭੇਜਣ ਵੇਲੇ ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ।" + resend_confirmation_email: "ਪੁਸ਼ਟੀ ਈਮੇਲ ਮੁੜ ਤੋਂ ਭੇਜੋ" + confirmed: "ਤੁਹਾਡੀ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਤੁਹਾਡਾ ਧੰਨਵਾਦ! ਤੁਸੀਂ ਹੁਣ ਲੌਗ ਇਨ ਕਰ ਸਕਦੇ ਹੋ।" + not_confirmed: "ਤੁਹਾਡੇ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਤੁਸੀਂ ਪਹਿਲਾਂ ਹੀ ਇਹ ਪੜਾਅ ਪੂਰਾ ਕਰ ਲਿਆ ਹੈ?" + user_confirmations: + spree_user: + send_instructions: "ਕੁਝ ਮਿੰਟਾਂ ਵਿੱਚ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਨਿਰਦੇਸ਼ਾਂ ਵਾਲਾ ਇੱਕ ਈਮੇਲ ਪ੍ਰਾਪਤ ਹੋਵੇਗੀ।" + confirmation_sent: "ਈਮੇਲ ਪੁਸ਼ਟੀਕਰਣ ਭੇਜਿਆ ਗਿਆ" + confirmation_not_sent: "ਪੁਸ਼ਟੀਕਰਣ ਈਮੇਲ ਭੇਜਣ ਵਿੱਚ ਗਲਤੀ" + user_registrations: + spree_user: + signed_up_but_unconfirmed: "ਤੁਹਾਡੇ ਈਮੇਲ ਪਤੇ ਉਤੇ ਪੁਸ਼ਟੀਕਰਨ ਲਿੰਕ ਵਾਲਾ ਇੱਕ ਸੁਨੇਹਾ ਭੇਜਿਆ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਖਾਤੇ ਨੂੰ ਅਕਟੀਵੇਟ ਕਰਨ ਲਈ ਇਸ ਲਿੰਕ ਨੂੰ ਖੋਲ੍ਹੋ।" + unknown_error: "ਤੁਹਾਡਾ ਖਾਤਾ ਨੂੰ ਬਣਾਉਣ ਸਮੇਂ ਕੁਝ ਗਲਤ ਹੋਇਆ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਈਮੇਲ ਪਤੇ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + failure: + disabled: "ਤੁਹਾਡਾ ਖਾਤਾ ਡਿਸੇਬਲ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਮੁੱਦੇ ਨੂੰ ਹੱਲ ਕਰਨ ਲਈ ਕਿਸੇ ਪ੍ਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।" + unconfirmed: "ਅੱਗੇ ਵਧਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ।" + already_registered: "ਅੱਗੇ ਵਧਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨੀ ਪਵੇਗੀ।" + success: + logged_in_succesfully: "ਸਫਲਤਾਪੂਰਵਕ ਲੌਗਇਨ ਕੀਤਾ ਗਿਆ" + sessions: + signed_out: "ਸਫਲਤਾਪੂਰਵਕ ਸਾਈਨ ਆਉਟ ਕੀਤਾ ਗਿਆ।" + already_signed_out: "ਸਫਲਤਾਪੂਰਵਕ ਸਾਈਨ ਆਉਟ ਕੀਤਾ ਗਿਆ।" + user_passwords: + spree_user: + updated_not_active: "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਰੀਸੈਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ, ਪਰ ਤੁਹਾਡੀ ਈਮੇਲ ਦੀ ਅਜੇ ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋਈ ਹੈ।" + updated: "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਸਫਲਤਾਪੂਰਵਕ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਤੁਸੀਂ ਹੁਣ ਸਾਈਨ ਇਨ ਹੋ।" + send_instructions: "ਕੁਝ ਮਿੰਟਾਂ ਵਿੱਚ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਨਿਰਦੇਸ਼ਾਂ ਵਾਲਾ ਇੱਕ ਈਮੇਲ ਪ੍ਰਾਪਤ ਹੋਵੇਗੀ।" + oidc: + failure: "ਸਾਈਨ ਇਨ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ: %{error}" + home_page_alert_html: "ਹੋਮ ਪੇਜ ਚੇਤਾਵਨੀ HTML" + hub_signup_case_studies_html: "ਹੱਬ ਸਾਈਨਅਪ ਕੇਸ ਸਟੱਡੀਜ਼ HTML" + hub_signup_detail_html: "ਹੱਬ ਸਾਈਨਅਪ ਵੇਰਵੇ HTML" + hub_signup_pricing_table_html: "ਹੱਬ ਸਾਈਨਅੱਪ ਕੀਮਤ ਨਿਰਧਾਰਨ ਸਾਰਣੀ HTML" + group_signup_case_studies_html: "ਗਰੁੱਪ ਸਾਈਨਅੱਪ ਕੇਸ ਸਟੱਡੀਜ਼ HTML" + group_signup_detail_html: "ਗਰੁੱਪ ਸਾਈਨਅਪ ਵੇਰਵੇ HTML" + group_signup_pricing_table_html: "ਗਰੁੱਪ ਸਾਈਨਅੱਪ ਕੀਮਤ ਨਿਰਧਾਰਨ ਸਾਰਣੀ HTML" + item_description: "ਆਈਟਮ ਦਾ ਵੇਰਵਾ" + menu_1_icon_name: "ਮੇਨਯੁ 1 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_2_icon_name: "ਮੇਨਯੁ 2 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_3_icon_name: "ਮੇਨਯੁ 3 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_4_icon_name: "ਮੇਨਯੁ 4 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_5_icon_name: "ਮੇਨਯੁ 5 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_6_icon_name: "ਮੇਨਯੁ 6 ਆਈਕਨ ਦਾ ਨਾਮ" + menu_7_icon_name: "ਮੇਨਯੁ 7 ਆਈਕਨ ਦਾ ਨਾਮ" + models: + order_cycle: + cloned_order_cycle_name: "%{order_cycle} ਦੀ ਕਾਪੀ" + tax_rate: + included_in_price: "ਕੀਮਤ ਵਿੱਚ ਸ਼ਾਮਲ" + open_street_map_enabled: "ਓਪਨ ਸਟ੍ਰੀਟ ਮੈਪ ਸਮਰਥਿਤ" + open_street_map_default_latitude: "ਓਪਨ ਸਟ੍ਰੀਟ ਮੈਪ ਡਿਫੌਲਟ ਲੈਟਿਟ੍ਯੂਡ" + open_street_map_default_longitude: "ਓਪਨ ਸਟ੍ਰੀਟ ਮੈਪ ਡਿਫੌਲਟ ਲੌਨਜੀਟੂਡ" + open_street_map_provider_name: "ਓਪਨ ਸਟ੍ਰੀਟ ਮੈਪ ਪ੍ਰਦਾਤਾ ਦਾ ਨਾਂ" + open_street_map_provider_options: "ਓਪਨ ਸਟ੍ਰੀਟ ਮੈਪ ਪ੍ਰਦਾਤਾ ਵਿਕਲਪ" + producer_signup_case_studies_html: "ਉਤਪਾਦਕ ਸਾਈਨਅਪ ਕੇਸ ਸਟੱਡੀਜ਼ HTML" + producer_signup_detail_html: "ਉਤਪਾਦਕ ਸਾਈਨਅੱਪ ਵੇਰਵੇ HTML" + producer_signup_pricing_table_html: "ਉਤਪਾਦਕ ਸਾਈਨਅੱਪ ਕੀਮਤ ਨਿਰਧਾਰਨ ਸਾਰਣੀ HTML" + producers_social: "ਉਤਪਾਦਕਾਂ ਦਾ ਸੋਸ਼ਲ" + resume_order: "ਆਰਡਰ ਮੁੜ ਸ਼ੁਰੂ ਕਰੋ" + sku: "SKU" + subtotal: "ਸਬ ਟੋਟਲ" + tax_rate: "ਟੈਕਸ ਦੀ ਦਰ" + validators: + date_time_string_validator: + not_string_error: "ਇੱਕ ਸਟ੍ਰਿੰਗ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ" + invalid_format_error: "ਵੈਧ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + integer_array_validator: + not_array_error: "ਇੱਕ ਐਰੇ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + invalid_element_error: "ਸਿਰਫ਼ ਵੈਧ ਪੂਰਨ ਅੰਕ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ" + report_job: + report_failed: | + ਇਹ ਰਿਪੋਰਟ ਅਸਫਲ ਰਹੀ। ਇਹ ਸੰਸਾਧਨ ਕਰਨ ਲਈ ਬਹੁਤ ਵੱਡਾ ਹੋ ਸਕਦੀ ਹੈ। ਅਸੀਂ ਇਸ ਦੀ ਜਾਂਚ ਕਰਾਂਗੇ, ਪਰ ਜੇਕਰ ਸਮੱਸਿਆ ਬਣੀ ਰਹਿੰਦੀ ਹੈ ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਨੂੰ ਦੱਸੋ। + enterprise_mailer: + confirmation_instructions: + subject: "ਕਿਰਪਾ ਕਰਕੇ %{enterprise} ਲਈ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + welcome: + subject: "%{enterprise} ਹੁਣ %{sitename} ਉਤੇ ਹੈ" + email_welcome: "ਜੀ ਆਇਆਂ ਨੂੰ" + email_registered: "ਹੁਣ ਇਸ ਦਾ ਹਿੱਸਾ ਹੈ" + email_userguide_html: "ਤੁਹਾਡੇ ਉਤਪਾਦਕ ਜਾਂ ਹੱਬ ਨੂੰ ਸਥਾਪਤ ਕਰਨ ਲਈ ਵਿਸਤ੍ਰਿਤ ਸਮਰਥਨ ਨਾਲ ਇੱਕ ਯੂਜ਼ਰ ਗਾਈਡ ਇੱਥੇ ਉਪਲਬਧ ਹੈ:  %{link}" + userguide: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਯੂਜ਼ਰ ਗਾਈਡ" + email_admin_html: "ਤੁਸੀਂ %{link} ਤੇ ਲੌਗ ਇਨ ਕਰਕੇ ਜਾਂ ਹੋਮ ਪੇਜ ਦੇ ਉਪਰ ਸੱਜੇ ਕੋਗ ਤੇ ਕਲਿੱਕ ਕਰਕੇ, ਅਤੇ ਅਡਮਿਨਿਸਟ੍ਰੇਸ਼ਨ ਦੀ ਚੋਣ ਕਰਕੇ ਆਪਣੇ ਖਾਤੇ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹੋ।" + admin_panel: "ਐਡਮਿਨ ਪੈਨਲ" + email_community_html: "ਸਾਡੇ ਕੋਲ OFN ਸੌਫਟਵੇਅਰ ਅਤੇ ਫੂਡ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਚਲਾਉਣ ਦੀਆਂ ਵਿਲੱਖਣ ਚੁਣੌਤੀਆਂ ਨਾਲ ਸਬੰਧਤ ਭਾਈਚਾਰਕ ਚਰਚਾ ਲਈ ਇੱਕ ਔਨਲਾਈਨ ਪਲੇਟਫਾਰਮ ਵੀ ਹੈ। ਤੁਹਾਨੂੰ ਇਸ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਣ ਲਈ ਉਤਸ਼ਾਹਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਅਸੀਂ ਲਗਾਤਾਰ ਵਿਕਸਿਤ ਹੋ ਰਹੇ ਹਾਂ ਅਤੇ ਇਸ ਪਲੇਟਫਾਰਮ ਤੇ ਤਿਹਾਡੇ ਦ੍ਵਾਰਾ ਦਿੱਤੇ ਸੁਝਾਅ ਇਹ ਨਿਰਧਾਰਤ ਕਰਨਗੇ ਕਿ ਅੱਗੇ ਕੀ ਹੁੰਦਾ ਹੈ। %{link }" + join_community: "ਭਾਈਚਾਰੇ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ" + invite_manager: + subject: "%{enterprise} ਨੇ ਤੁਹਾਨੂੰ ਪ੍ਰਬੰਧਕ ਬਣਨ ਲਈ ਸੱਦਾ ਦਿੱਤਾ ਹੈ" + producer_mailer: + order_cycle: + subject: "%{producer} ਲਈ ਆਰਡਰ ਸਾਈਕਲ ਰਿਪੋਰਟ" + provider_settings: "ਪ੍ਰਦਾਤਾ ਸੈਟਿੰਗਾਂ" + report_mailer: + report_ready: + subject: "ਰਿਪੋਰਟ ਤਿਆਰ ਹੈ" + heading: "ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਰਿਪੋਰਟ ਤਿਆਰ ਹੈ" + intro: | + ਹੇਠਾਂ ਦਿੱਤੇ ਲਿੰਕ ਦੀ ਮਿਆਦ ਇੱਕ ਹਫ਼ਤੇ ਬਾਅਦ ਖਤਮ ਹੋ ਜਾਵੇਗੀ। + link_label: "\"%{name}\"" + shipment_mailer: + shipped_email: + dear_customer: "ਪਿਆਰੇ ਗਾਹਕ," + instructions: "ਤੁਹਾਡਾ ਆਰਡਰ ਭੇਜ ਦਿੱਤਾ ਗਿਆ ਹੈ" + shipment_summary: "ਸ਼ਿਪਮੈਂਟ ਸੰਖੇਪ" + subject: "ਸ਼ਿਪਮੈਂਟ ਬਾਰੇ ਸੂਚਨਾ" + thanks: "ਤੁਹਾਡੇ ਕਾਰੋਬਾਰ ਦੇਣ ਲਈ ਧੰਨਵਾਦ।" + track_information: "ਟਰੈਕਿੰਗ ਜਾਣਕਾਰੀ: %{tracking}" + track_link: "ਟਰੈਕਿੰਗ ਲਿੰਕ: %{url}" + subscription_mailer: + placement_summary_email: + subject: ਹਾਲ ਹੀ ਵਿੱਚ ਦਿੱਤੇ ਗਏ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰਾਂ ਦਾ ਸੰਖੇਪ + greeting: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ %{name}," + intro: "ਹੇਠਾਂ ਉਹਨਾਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰਾਂ ਦਾ ਸੰਖੇਪ ਹੈ ਜੋ ਹਾਲ ਹੀ ਵਿੱਚ %{shop} ਲਈ ਦਿੱਤੇ ਗਏ ਹਨ।" + confirmation_summary_email: + subject: ਹਾਲ ਹੀ ਵਿੱਚ ਪੁਸ਼ਟੀ ਕੀਤੇ ਗਏ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰਾਂ ਦਾ ਸੰਖੇਪ + greeting: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ %{name}," + intro: "ਹੇਠਾਂ ਉਹਨਾਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰਾਂ ਦਾ ਸੰਖੇਪ ਹੈ ਜਿਹਨਾਂ ਨੂੰ %{shop} ਲਈ ਹਾਲ ਹੀ ਵਿੱਚ ਅੰਤਿਮ ਰੂਪ ਦਿੱਤਾ ਗਿਆ ਹੈ।" + summary_overview: + total: ਕੁੱਲ %{count} ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਨੂੰ ਸਵੈਚਾਲਿਤ ਪ੍ਰੋਸਸਸਿੰਗ ਲਈ ਚਿੰਨ੍ਹਿਤ ਕੀਤਾ ਗਿਆ ਸੀ। + success_zero: ਇਹਨਾਂ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਸਫਲਤਾਪੂਰਵਕ ਪ੍ਰੋਸਸ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਸੀ। + success_some: ਇਹਨਾਂ ਵਿੱਚੋਂ %{count} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਪ੍ਰੋਸੈਸ ਕੀਤਾ ਗਿਆ ਸੀ। + success_all: ਸਭ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਪ੍ਰੋਸੱਸ ਕੀਤੀ ਗਿਆ। + issues: ਸਾਹਮਣੇ ਆਈਆਂ ਸਮੱਸਿਆਵਾਂ ਦੇ ਵੇਰਵੇ ਹੇਠਾਂ ਦਿੱਤੇ ਗਏ ਹਨ। + summary_detail: + no_message_provided: ਗਲਤੀ ਦਾ ਕੋਈ ਵੀ ਸੁਨੇਹਾ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ + changes: + title: ਨਾਕਾਫ਼ੀ ਸਟਾਕ (%{count} ਆਰਡਰ) + explainer: ਇਹਨਾਂ ਆਰਡਰਾਂ ਨੂੰ ਪ੍ਰੋਸੱਸ ਕੀਤਾ ਗਿਆ ਸੀ ਪਰ ਕੁਝ ਬੇਨਤੀ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਲਈ ਨਾਕਾਫ਼ੀ ਸਟਾਕ ਉਪਲਬਧ ਸੀ + empty: + title: ਕੋਈ ਸਟਾਕ ਨਹੀਂ (%{count} ਆਰਡਰ) + explainer: ਇਹਨਾਂ ਆਰਡਰਾਂ ਨੂੰ ਪ੍ਰੋਸੱਸ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਕਿਸੇ ਵੀ ਬੇਨਤੀ ਕੀਤੀ ਆਈਟਮ ਲਈ ਕੋਈ ਸਟਾਕ ਉਪਲਬਧ ਨਹੀਂ ਸੀ + complete: + title: ਪਹਿਲਾਂ ਹੀ ਪ੍ਰੋਸੱਸ ਕੀਤਾ ਹੋਇਆ (%{count} ਆਰਡਰ) + explainer: ਇਹਨਾਂ ਆਰਡਰਾਂ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਪੂਰੇ ਹੋਏ ਵਜੋਂ ਮਾਰਕ ਕੀਤਾ ਗਿਆ ਸੀ, ਅਤੇ ਇਸਲਈ ਉਹਨਾਂ ਨੂੰ ਅਛੂਤੇ ਛੱਡ ਦਿੱਤਾ ਗਿਆ ਸੀ + processing: + title: ਗਲਤੀ ਦਾ ਸਾਹਮਣਾ ਕਰਨਾ ਪਿਆ (%{count} ਆਰਡਰ) + explainer: ਇੱਕ ਗਲਤੀ ਦੇ ਕਾਰਨ ਇਹਨਾਂ ਆਰਡਰਾਂ ਦੀ ਸਵੈਚਾਲਿਤ ਪ੍ਰੋਸੈਸਿੰਗ ਅਸਫਲ ਰਹੀ। ਜਿੱਥੇ ਸੰਭਵ ਹੈ ਉਥੇ ਗਲਤੀ ਸੂਚੀਬੱਧ ਕੀਤੀ ਗਈ। + failed_payment: + title: ਅਸਫਲ ਭੁਗਤਾਨ (%{count} ਆਰਡਰ) + explainer: ਇੱਕ ਗਲਤੀ ਦੇ ਕਾਰਨ ਇਹਨਾਂ ਆਰਡਰਾਂ ਲਈ ਭੁਗਤਾਨ ਦੀ ਸਵੈਚਲਿਤ ਪ੍ਰੋਸਸਸਿੰਗ ਅਸਫਲ ਰਹੀ। ਜਿੱਥੇ ਸੰਭਵ ਹੈ ਉਥੇ ਗਲਤੀ ਸੂਚੀਬੱਧ ਕੀਤੀ ਗਈ। + other: + title: ਹੋਰ ਅਸਫਲਤਾ (%{count} ਆਰਡਰ) + explainer: ਕਿਸੇ ਅਣਜਾਣ ਕਾਰਨ ਕਰਕੇ ਇਹਨਾਂ ਆਰਡਰਾਂ ਦੀ ਸਵੈਚਲਿਤ ਪ੍ਰੋਸਸਸਿੰਗ ਅਸਫਲ ਰਹੀ। ਅਜਿਹਾ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ, ਜੇਕਰ ਤੁਸੀਂ ਇਹ ਵੇਖ ਰਹੇ ਹੋ ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਡੇ ਨਾਲ ਸੰਪਰਕ ਕਰੋ। + home: "OFN" + title: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ" + welcome_to: "ਤੁਹਾਡਾ ਸਵਾਗਤ ਹੈ" + site_meta_description: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਇੰਡੀਆ ਸਾਫਟਵੇਅਰ ਪਲੇਟਫਾਰਮ ਕਿਸਾਨਾਂ ਨੂੰ ਔਨਲਾਈਨ ਉਤਪਾਦ ਵੇਚਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ, ਉਸ ਕੀਮਤ ਤੇ ਜੋ ਉਹਨਾਂ ਲਈ ਸਹੀ ਹਨ। ਇਹ ਖਾਸ ਤੌਰ ਉਤੇ ਭੋਜਨ ਉਤਪਾਦ ਵੇਚਣ ਲਈ ਬਣਾਇਆ ਗਿਆ ਹੈ ਤਾਂ ਜੋ ਇਹ ਔਖੇ ਉਪਾਵਾਂ ਜਾਂ ਸਟਾਕ ਦੇ ਪੱਧਰਾਂ ਨੂੰ ਸੰਭਾਲ ਸਕੇ ਜੋ ਸਿਰਫ ਭੋਜਨ ਵਿੱਚ ਹੁੰਦੇ ਹਨ - ਇੱਕ ਦਰਜਨ ਅੰਡੇ, ਅਜਵਾਇਣ ਦਾ ਇੱਕ ਗੁੱਛਾ, ਇੱਕ ਪੂਰਾ ਚਿਕਨ ਜੋ ਭਾਰ ਵਿੱਚ ਵੱਖਰੇ ਹੁੰਦੇ ਹਨ..." + search_by_name: ਨਾਂ, ਸ਼ਹਿਰ, ਰਾਜ ਜਾਂ ਪਿੰਨ ਕੋਡ ਦੁਆਰਾ ਖੋਜ ਕਰੋ... + producers_join: ਭਾਰਤੀ ਉਤਪਾਦਕਾਂ ਦਾ ਹੁਣ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਣ ਲਈ ਸਵਾਗਤ ਹੈ। + charges_sales_tax: ਜੀਐਸਟੀ ਲਾਉਂਦੇ ਹਨ? + business_address: "ਕਾਰੋਬਾਰੀ ਪਤਾ" + print_invoice: "ਇਨਵੌਇਸ ਪ੍ਰਿੰਟ" + print_ticket: "ਟਿਕਟ ਪ੍ਰਿੰਟ ਕਰੋ" + select_ticket_printer: "ਟਿਕਟਾਂ ਲਈ ਪ੍ਰਿੰਟਰ ਚੁਣੋ" + send_invoice: "ਇਨਵੌਇਸ ਭੇਜੋ" + resend_confirmation: "ਪੁਸ਼ਟੀ ਮੁੜ ਭੇਜੋ" + view_order: "ਆਰਡਰ ਵੇਖੋ" + edit_order: "ਆਰਡਰ ਸੰਪਾਦਿਤ ਕਰੋ" + ship_order: "ਆਰਡਰ ਭੇਜੋ" + cancel_order: "ਆਰਡਰ ਰੱਦ ਕਰੋ" + confirm_send_invoice: "ਇਸ ਆਰਡਰ ਲਈ ਗਾਹਕ ਨੂੰ ਇੱਕ ਇਨਵੌਇਸ ਭੇਜਿਆ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + confirm_resend_order_confirmation: "ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਆਰਡਰ ਦੇ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਨੂੰ ਦੁਬਾਰਾ ਭੇਜਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + invoice: "ਇਨਵੌਇਸ" + invoices: "ਇਨਵੌਇਸ" + file: "ਫਾਇਲ" + active: "ਸਕ੍ਰਿਅ" + download: "ਡਾਊਨਲੋਡ" + cancelled: "ਰੱਦ ਕੀਤਾ ਗਿਆ" + more: "ਹੋਰ" + say_no: "ਨਹੀਂ" + say_yes: "ਹਾਂ" + ongoing: ਜਾਰੀ + bill_address: ਬਿਲਿੰਗ ਪਤਾ + ship_address: ਸ਼ਿਪਿੰਗ ਪਤਾ + sort_order_cycles_on_shopfront_by: "ਸ਼ਾਪਫ੍ਰੰਟ ਤੇ ਇਸਦੇ ਅਧਾਰ ਤੇ ਆਰਡਰ ਸਾਈਕਲ ਨੂੰ ਕ੍ਰਮਬੱਧ ਕਰੋ" + required_fields: ਲੋੜੀਂਦੇ ਖੇਤਰਾਂ ਨੂੰ ਤਾਰੇ ਨਾਲ ਚਿੰਨ੍ਹਿਤ ਕੀਤਾ ਗਿਆ ਹੈ + select_continue: ਚੁਣੋ ਅਤੇ ਜਾਰੀ ਰੱਖੋ + remove: Remove + collapse_all: ਸਾਰੇ ਨੂੰ ਛੋਟਾ ਕਰੋ + expand_all: ਸਾਰੇ ਨੂੰ ਵੱਡਾ ਕਰੋ + loading: ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ... + show_more: ਹੋਰ ਵਿਖਾਓ + show_all: ਸਾਰੇ ਵਿਖਾਓ + show_all_with_more: "ਸਾਰੇ ਵਿਖਾਓ (%{num} ਹੋਰ)" + cancel: ਰੱਦ ਕਰੋ + edit: ਸੰਪਾਦਿਤ ਕਰੋ + clone: ਕਲੋਨ + distributors: ਵਿਤਰਕ + distribution: ਵਿਤਰਣ + order_cycles: ਆਰਡਰ ਸਾਈਕਲ + bulk_order_management: ਥੋਕ ਆਰਡਰ ਪ੍ਰਬੰਧਨ + enterprises: ਐਂਟਰਪ੍ਰਾਈਜ਼ + enterprise_groups: ਸਮੂਹ + reports: ਰਿਪੋਰਟਾਂ + listing_reports: ਲਿਸਟ ਕੀਤੀਆਂ ਰਿਪੋਰਟਾਂ + variant_overrides: ਇਨਵੇਂਟਰੀ + import: ਇਮਪੋਰਟ + spree_products: ਸ੍ਪ੍ਰੀ ਉਤਪਾਦ + all: ਸਾਰੇ + current: ਮੌਜੂਦਾ + available: ਉਪਲੱਬਧ + dashboard: ਡੈਸ਼ਬੋਰਡ + undefined: ਅਪਰਿਭਾਸ਼ਿਤ + unused: ਅਣਵਰਤਿਆ + admin_and_handling: ਪ੍ਰਸ਼ਾਸਨ ਅਤੇ ਸੰਚਾਲਨ + profile: ਪ੍ਰੋਫਾਈਲ + supplier_only: ਸਿਰਫ਼ ਸਪਲਾਇਰ + has_shopfront: ਸ਼ਾਪਫਰੰਟ ਹੈ + weight: ਭਾਰ + volume: ਮਾਤਰਾ + items: ਵਸਤੂਆਂ + summary: ਸੰਖੇਪ + detailed: ਵਿਸਤ੍ਰਿਤ + updated: ਅੱਪਡੇਟ ਕੀਤਾ + 'yes': "ਹਾਂ" + 'no': "ਨਹੀਂ" + y: 'ਹਾਂ' + n: 'ਨਹੀਂ' + powered_by: ਇਨਹਾਂ ਦੁਆਰਾ ਸੰਚਾਲਿਤ + blocked_cookies_alert: "ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਤੁਹਾਡਾ ਬ੍ਰਾਊਜ਼ਰ ਇਸ ਸ਼ਾਪਫ੍ਰੰਟ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਜ਼ਰੂਰੀ ਕੂਕੀਜ਼ ਨੂੰ ਬਲੌਕ ਕਰ ਰਿਹਾ ਹੈ। ਕੂਕੀਜ਼ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਅਤੇ ਪੰਨੇ ਨੂੰ ਦੁਬਾਰਾ ਲੋਡ ਕਰਨ ਲਈ ਹੇਠਾਂ ਕਲਿੱਕ ਕਰੋ।" + allow_cookies: "ਕੂਕੀਜ਼ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ" + none: ਕੋਈ ਨਹੀਂ + notes: ਨੋਟ + error: ਗਲਤੀ + voucher: ਵਾਊਚਰ + processing_payment: "ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਕੀਤੀ ਜਾ ਰਿਹਾ ਹੈ..." + no_pending_payments: "ਕੋਈ ਬਕਾਇਆ ਭੁਗਤਾਨ ਨਹੀਂ ਹੈ" + invalid_payment_state: "ਅਵੈਧ ਭੁਗਤਾਨ ਸਥਿਤੀ: %{state}" + filter_results: ਨਤੀਜੇ ਫਿਲਟਰ ਕਰੋ + clear_filters: ਫਿਲਟਰ ਸਾਫ਼ ਕਰੋ + quantity: ਮਾਤਰਾ + pick_up: ਪਿਕ ਅਪ + ok: ਠੀਕ ਹੈ + copy: ਕਾਪੀ + change_my_password: "ਮੇਰਾ ਪਾਸਵਰਡ ਬਦਲੋ" + update_password: "ਪਾਸਵਰਡ ਅੱਪਡੇਟ ਕਰੋ" + password_confirmation: ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ + reset_password_token: ਪਾਸਵਰਡ ਟੋਕਨ ਨੂੰ ਰੀਸੈਟ ਕਰੋ + expired: ਮਿਆਦ ਪੁੱਗ ਗਈ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਨਵੇਂ ਲਈ ਬੇਨਤੀ ਕਰੋ + back_to_payments_list: "ਭੁਗਤਾਨ ਸੂਚੀ ਤੇ ਵਾਪਸ" + maestro_or_solo_cards: "ਮਾਏਸਟ੍ਰੋ/ਸੋਲੋ ਕਾਰਡ" + backordered: "ਬੈਕਆਰਡਰ ਕੀਤਾ ਗਿਆ" + on_hand: "ਹੱਥ ਵਿਚ" + on hand: "ਹੱਥ ਵਿਚ" + ship: "ਸ਼ਿਪ" + shipping_category: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ" + height: "ਉਚਾਈ" + width: "ਚੌੜਾਈ" + depth: "ਡੂੰਘਾਈ" + payment_could_not_process: "ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਿਆ" + payment_could_not_complete: "ਭੁਗਤਾਨ ਪੂਰਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ" + actions: + create_and_add_another: "ਬਣਾਓ ਅਤੇ ਦੂਜਾ ਜੋੜੋ" + create: "ਬਣਾਓ" + cancel: "ਰੱਦ ਕਰੋ" + resume: "ਮੁੜ ਤੋਂ ਸ਼ੁਰੂ ਕਰੋ" + save: "ਸੇਵ ਕਰੋ" + edit: "ਸੰਪਾਦਿਤ ਕਰੋ" + update: "ਅੱਪਡੇਟ" + delete: "ਹਟਾਓ" + add: "ਜੋੜੋ" + cut: "ਕੱਟੋ" + paste: "ਚਿਪਕਾਓ" + destroy: "ਨਾਸ਼ ਕਰੋ" + rename: "ਨਾਮ ਬਦਲੋ" + admin: + products_page: + title: ਉਤਪਾਦ + filters: + categories: + title: ਸ਼੍ਰੇਣੀਆਂ + selected_categories: "%{count} ਸ਼੍ਰੇਣੀਆਂ ਚੁਣੀਆਂ ਗਈਆਂ" + producers: + title: ਉਤਪਾਦਕ + selected_producers: "%{count} ਉਤਪਾਦਕ ਚੁਣੇ ਗਏ" + per_page: "ਪ੍ਰਤੀ ਪੰਨਾ %{count} ਆਈਟਮਾਂ" + colums: ਕਾਲਮ + columns: + name: ਨਾਮ + unit: ਯੂਨਿਟ + price: ਕੀਮਤ + producer: ਉਤਪਾਦਕ + category: ਸ਼੍ਰੇਣੀ + sku: SKU + on_hand: "ਹੱਥ ਵਿਚ" + on_demand: "ਡਿਮਾਂਡ ਤੇ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + inherits_properties: "ਪ੍ਰਾਪਰਟੀਜ਼ ਅਪਣਾਉਂਦੇ ਹਨ?" + import_date: "ਇਮਪੋਰਟ ਮਿਤੀ" + actions:
+ columns_selector: + unit: ਯੂਨਿਟ + price: ਕੀਮਤ + producer: ਉਤਪਾਦਕ + category: ਸ਼੍ਰੇਣੀ + sku: SKU + on_hand: "ਹੱਥ ਵਿਚ" + on_demand: "ਡਿਮਾਂਡ ਤੇ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + inherits_properties: "ਪ੍ਰਾਪਰਟੀਜ਼ ਅਪਣਾਉਂਦੇ ਹਨ?" + import_date: "ਇਮਪੋਰਟ ਮਿਤੀ" + actions: + edit: ਸੰਪਾਦਿਤ ਕਰੋ + clone: ਕਲੋਨ + delete: ਹਟਾਓ + image: + edit: ਸੰਪਾਦਿਤ ਕਰੋ + adjustments: + skipped_changing_canceled_order: "ਤੁਸੀਂ ਰੱਦ ਕੀਤੇ ਆਰਡਰ ਨੂੰ ਬਦਲ ਨਹੀਂ ਸਕਦੇ ਹੋ।" + begins_at: ਇਸਤੇ ਸ਼ੁਰੂ ਹੋਵੇਗਾ + begins_on: ਇਸਤੇ ਸ਼ੁਰੂ ਹੋਵੇਗਾ + bill_address: "ਬਿੱਲ ਪਤਾ" + ship_address: "ਸ਼ਿਪ ਪਤਾ" + customer: ਗਾਹਕ + date: ਮਿਤੀ + email: ਈਮੇਲ + ends_at: ਇਸਤੇ ਖਤਮ ਹੋਵੇਗਾ + ends_on: ਇਸਤੇ ਖਤਮ ਹੋਵੇਗਾ + name: ਨਾਂ + first_name: ਪਹਿਲਾ ਨਾਂ + last_name: ਆਖਰੀ ਨਾਂ + on_hand: ਹੱਥ ਵਿਚ + on_demand: ਡਿਮਾਂਡ ਤੇ + on_demand?: ਡਿਮਾਂਡ ਤੇ? + order_cycle: ਆਰਡਰ ਸਾਈਕਲ + payment: ਭੁਗਤਾਨ + payment_method: ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ + phone: ਫੋਨ + price: ਕੀਮਤ + producer: ਉਤਪਾਦਕ + image: ਤਸਵੀਰ + product: ਉਤਪਾਦ + quantity: ਮਾਤਰਾ + schedule: ਸਮਾਸੂਚੀ + shipping: ਸ਼ਿਪਿੰਗ + shipping_method: ਸ਼ਿਪਿੰਗ ਦਾ ਤਰੀਕਾ + shop: ਸ਼ਾਪ + sku: SKU + status_state: ਸਥਿਤੀ + tags: ਟੈਗ + variant: ਵੇਰੀਐਂਟ + weight: ਭਾਰ + volume: ਮਾਤਰਾ + items: ਵਸਤੂਆਂ + select_all: ਸਭ ਨੂੰ ਚੁਣੋ + quick_search: ਤੇਜ਼ ਖੋਜ + clear_all: ਸਾਰੇ ਮਿਟਾਓ + start_date: "ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਮਿਤੀ" + end_date: "ਸਮਾਪਤੀ ਦੀ ਮਿਤੀ" + unsaved_changes: "ਤੁਹਾਡੇ ਕੋਲ ਸੇਵ ਨਾ ਕੀਤੀਆਂ ਤਬਦੀਲੀਆਂ ਹਨ" + form_invalid: "ਫਾਰਮ ਵਿੱਚ ਗੁੰਮ ਜਾਂ ਅਵੈਧ ਖੇਤਰ ਹਨ" + clear_filters: ਫਿਲਟਰ ਮਿਟਾਓ + clear: ਮਿਟਾਓ + save: ਸੇਵ ਕਰੋ + cancel: ਰੱਦ ਕਰੋ + back: ਵਾਪਸ + show_more: ਹੋਰ ਵਿਖਾਓ + choose: "ਚੁਣੋ..." + please_select: ਕਿਰਪਾ ਕਰਕੇ ਚੁਣੋ... + column_save_as_default: ਡਿਫੌਲਟ ਦੇ ਤੌਰ ਤੇ ਸੇਵ ਕਰੋ + columns: ਕਾਲਮ + actions: ਸੰਪਾਦਿਤ ਕਰੋ + viewing: "ਵੇਖ ਰਹੇ ਹਾਂ: %{current_view_name}" + description: ਵਰਣਨ + whats_this: ਇਹ ਕੀ ਹੈ? + tag_has_rules: "ਇਸ ਟੈਗ ਲਈ ਮੌਜੂਦਾ ਨਿਯਮ: %{num}" + has_one_rule: "ਇੱਕ ਨਿਯਮ ਹੈ" + has_n_rules: "ਇਸ ਵਿੱਚ %{num} ਨਿਯਮ ਹਨ" + unsaved_confirm_leave: "ਇਸ ਪੇਜ ਵਿੱਚ ਕੁਝ ਤਬਦੀਲੀਆਂ ਨੂੰ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਸੇਵ ਕੀਤੇ ਬਿਨਾਂ ਜਾਰੀ ਰੱਖੀਏ?" + available_units: "ਉਪਲੱਬਧ ਯੂਨਿਟਾਂ" + shopfront_settings: + embedded_shopfront_settings: "ਸ਼ਾਪਫਰੰਟ ਦੀਆਂ ਏਮਬੈਡ ਕੀਤੀਆਂ ਸੈਟਿੰਗਾਂ" + enable_embedded_shopfronts: "ਏਮਬੈਡਡ ਸ਼ਾਪਫਰੰਟਸ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ" + embedded_shopfronts_whitelist: "ਬਾਹਰੀ ਡੋਮੇਨ ਵਾਈਟਲਿਸਟ" + terms_of_service_files: + create: + select_file: "ਕਿਰਪਾ ਕਰਕੇ ਪਹਿਲਾਂ ਇੱਕ ਫਾਈਲ ਚੁਣੋ।" + show: + title: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੀਆਂ ਫਾਈਲਾਂ" + no_files: "ਸੇਵਾ ਦੀਆਂ ਕੋਈ ਸ਼ਰਤਾਂ ਹਾਲੇ ਤੱਕ ਅੱਪਲੋਡ ਨਹੀਂ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ।" + current_terms_html: "ਮੌਜੂਦਾ %{tos_link} ਵੇਖੋ। ਅੱਪਲੋਡ ਸਮਾਂ: %{datetime}।" + terms_of_service: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ" + delete: "ਫਾਇਲ ਹਟਾਓ" + confirm_delete: "ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਮੌਜੂਦਾ ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੀਆਂ ਫਾਈਲਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + attachment: "ਅਟੈਚਮੈਂਟ" + create_terms_of_service: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੀ ਫਾਈਲ ਬਣਾਓ" + number_localization: + number_localization_settings: "ਨੰਬਰ ਸਥਾਨੀਕਰਨ ਸੈਟਿੰਗਾਂ" + enable_localized_number: "ਅੰਤਰਰਾਸ਼ਟਰੀ ਹਜ਼ਾਰ/ਦਸ਼ਮਲਵ ਵਿਭਾਜਕ ਤਰਕ ਦੀ ਵਰਤੋਂ ਕਰੋ" + invoice_settings: + edit: + title: "ਇਨਵੌਇਸ ਸੈਟਿੰਗਾਂ" + enable_invoices?: "ਇਨਵੌਇਸ ਸਮਰੱਥ ਕਰੋ?" + invoice_style2?: "ਵਿਕਲਪਿਕ ਇਨਵੌਇਸ ਮਾਡਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ ਜਿਸ ਵਿੱਚ ਪ੍ਰਤੀ ਦਰ ਅਤੇ ਪ੍ਰਤੀ ਆਈਟਮ ਟੈਕਸ ਦਰ ਦੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੁੰਦੀ ਹੈ (ਅਜੇ ਤੱਕ ਉਹਨਾਂ ਦੇਸ਼ਾਂ ਲਈ ਸਮਰਥਿਤ ਨਹੀਂ ਜੋ ਟੈਕਸ-ਮੁਕਤ ਕੀਮਤਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰਦੇ ਹਨ)" + enterprise_number_required_on_invoices?: "ਇੱਕ ਇਨਵੌਇਸ ਬਣਾਉਣ ਲਈ ABN ਦੀ ਲੋੜ ਹੈ?" + stripe_connect_settings: + edit: + title: "ਸਟ੍ਰਾਈਪ ਕਨੈਕਟ" + settings: "ਸੈਟਿੰਗਾਂ" + stripe_connect_enabled: ਸ਼ੋਪਾਂ ਨੂੰ ਸਟ੍ਰਾਈਪ ਕਨੈਕਟ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਭੁਗਤਾਨ ਸਵੀਕਾਰ ਕਰਨ ਲਈ ਸਮਰੱਥ ਬਣਾਓ? + no_api_key_msg: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਕੋਈ ਸਟ੍ਰਾਈਪ ਖਾਤਾ ਨਹੀਂ ਹੈ। + configuration_explanation_html: ਸਟ੍ਰਾਈਪ ਕਨੈਕਟ ਏਕੀਕਰਣ ਨੂੰ ਕੌਂਫਿਗਰ ਕਰਨ ਬਾਰੇ ਵਿਸਤ੍ਰਿਤ ਨਿਰਦੇਸ਼ਾਂ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਗਾਈਡ ਤੋਂ ਸਲਾਹ ਲਓ। + status: ਸਥਿਤੀ + ok: ਠੀਕ ਹੈ + instance_secret_key: ਇੰਸਟੈਂਸ ਸਿਕ੍ਰੇਟ ਕੀ + account_id: ਖਾਤਾ ਆਈ.ਡੀ + business_name: ਕਾਰੋਬਾਰ ਦਾ ਨਾਮ + charges_enabled: ਚਾਰਜ ਸਮਰੱਥ ਕੀਤੇ ਗਏ + charges_enabled_warning: "ਚੇਤਾਵਨੀ: ਤੁਹਾਡੇ ਖਾਤੇ ਲਈ ਚਾਰਜ ਸਮਰੱਥ ਨਹੀਂ ਹਨ" + auth_fail_error: ਤੁਹਾਡੇ ਵੱਲੋਂ ਪ੍ਰਦਾਨ ਕੀਤੀ API ਕੁੰਜੀ ਅਵੈਧ ਹੈ + empty_api_key_error_html: ਕੋਈ ਸਟ੍ਰਾਈਪ API ਕੁੰਜੀ ਪ੍ਰਦਾਨ ਨਹੀਂ ਕੀਤੀ ਗਈ ਹੈ। ਆਪਣੇ API ਨੂੰ ਸੇਟ ਕਰਨ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਇਹਨਾਂ ਹਦਾਇਤਾਂ ਦੀ ਪਾਲਣਾ ਕਰੋ + matomo_settings: + edit: + title: "ਮੈਟੋਮੋ ਸੈਟਿੰਗਾਂ" + matomo_url: "ਮੈਟੋਮੋ URL" + matomo_site_id: "ਮੈਟੋਮੋ ਸਾਈਟ ਆਈਡੀ" + matomo_tag_manager_url: "ਮੈਟੋਮੋ ਟੈਗ ਮੈਨੇਜਰ URL" + info_html: "ਮੈਟੋਮੋ ਇੱਕ ਵੈਬ ਅਤੇ ਮੋਬਾਈਲ ਐਨਾਲਿਟਿਕਸ ਐਪ੍ਲੀਕੇਸ਼ਨ ਹੈ। ਤੁਸੀਂ ਜਾਂ ਤਾਂਮੈਟੋਮੋ ਦੇ ਔਨ-ਪ੍ਰੀਮਾਇਸੇਸ ਹੋਸਟ ਬਣਾ ਸਕਦੇ ਹੋ ਜਾਂ ਕਲਾਉਡ-ਹੋਸਟੇਡ ਸੇਵਾ ਦਾ ਉਪਯੋਗ ਕਰ ਸਕਦੇ ਹੋ। ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ matomo.org ਵੇਖੋ।" + config_instructions_html: "ਇੱਥੇ ਤੁਸੀਂ OFN ਮੈਟੋਮੋ ਏਕੀਕਰਣ ਨੂੰ ਕੌਂਫਿਗਰ ਕਰ ਸਕਦੇ ਹੋ। ਹੇਠਾਂ ਦਿੱਤੇ ਮੈਟੋਮੋ URL ਨੂੰ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜਿੱਥੇ ਉਪਭੋਗਤਾ ਦੀ ਟਰੈਕਿੰਗ ਜਾਣਕਾਰੀ ਭੇਜੀ ਜਾਵੇਗੀ; ਜੇਕਰ ਇਸਨੂੰ ਖਾਲੀ ਛੱਡ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਮੈਟੋਮੋ ਉਪਭੋਗਤਾ ਟਰੈਕਿੰਗ ਨੂੰ ਅਯੋਗ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਸਾਈਟ ਆਈਡੀ ਫ਼ੀਲਡ ਲਾਜ਼ਮੀ ਨਹੀਂ ਹੈ ਪਰ ਉਪਯੋਗੀ ਹੋ ਸਕਦੀ ਹੈ, ਜੇ ਤੁਸੀਂ ਇੱਕੋ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਤੇ ਇੱਕ ਤੋਂ ਵੱਧ ਵੈਬਸਾਈਟਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਰਹੇ ਹੋਵੋ; ਇਸਨੂੰ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਕੰਸੋਲ ਉਤੇ ਲੱਭਿਆ ਜਾ ਸਕਦਾ ਹੈ।" + config_instructions_tag_manager_html: "ਇੱਥੇ ਤੁਸੀਂ OFN ਮੈਟੋਮੋ ਏਕੀਕਰਣ ਨੂੰ ਕੌਂਫਿਗਰ ਕਰ ਸਕਦੇ ਹੋ। ਹੇਠਾਂ ਦਿੱਤੇ ਮੈਟੋਮੋ URL ਨੂੰ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜਿੱਥੇ ਉਪਭੋਗਤਾ ਦੀ ਟਰੈਕਿੰਗ ਜਾਣਕਾਰੀ ਭੇਜੀ ਜਾਵੇਗੀ; ਜੇਕਰ ਇਸਨੂੰ ਖਾਲੀ ਛੱਡ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਮੈਟੋਮੋ ਉਪਭੋਗਤਾ ਟਰੈਕਿੰਗ ਨੂੰ ਅਯੋਗ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਸਾਈਟ ਆਈਡੀ ਫ਼ੀਲਡ ਲਾਜ਼ਮੀ ਨਹੀਂ ਹੈ ਪਰ ਉਪਯੋਗੀ ਹੋ ਸਕਦੀ ਹੈ, ਜੇ ਤੁਸੀਂ ਇੱਕੋ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਤੇ ਇੱਕ ਤੋਂ ਵੱਧ ਵੈਬਸਾਈਟਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਰਹੇ ਹੋਵੋ; ਇਸਨੂੰ ਮੈਟੋਮੋ ਇੰਸਟੈਂਸ ਕੰਸੋਲ ਉਤੇ ਲੱਭਿਆ ਜਾ ਸਕਦਾ ਹੈ।" + customers: + index: + new_customer: "ਨਵਾਂ ਗਾਹਕ" + code: ਕੋਡ + duplicate_code: "ਇਹ ਕੋਡ ਪਹਿਲਾਂ ਹੀ ਵਰਤਿਆ ਜਾ ਚੁੱਕਿਆ ਹੈ।" + bill_address: "ਬਿਲਿੰਗ ਪਤਾ" + ship_address: "ਸ਼ਿਪਿੰਗ ਪਤਾ" + balance: "ਬਾਕੀ ਰਕਮ" + update_address_success: "ਪਤਾ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ।" + update_address_error: "ਮਾਫ਼ ਕਰਨਾ! ਕਿਰਪਾ ਕਰਕੇ ਸਾਰੇ ਲੋੜੀਂਦੇ ਖੇਤਰਾਂ ਨੂੰ ਭਰੋ!" + edit_bill_address: "ਬਿਲਿੰਗ ਪਤਾ ਸੰਪਾਦਿਤ ਕਰੋ" + edit_ship_address: "ਸ਼ਿਪਿੰਗ ਪਤਾ ਸੰਪਾਦਿਤ ਕਰੋ" + required_fileds: "ਲੋੜੀਂਦੇ ਖੇਤਰਾਂ ਨੂੰ ਤਾਰੇ ਨਾਲ ਚਿੰਨ੍ਹਿਤ ਕੀਤਾ ਗਿਆ ਹੈ" + select_country: "ਦੇਸ਼ ਚੁਣੋ" + select_state: "ਰਾਜ ਚੁਣੋ" + edit: "ਸੰਪਾਦਿਤ ਕਰੋ" + update_address: "ਪਤਾ ਅੱਪਡੇਟ ਕਰੋ" + confirm_delete: "ਯਕੀਨੀ ਤੌਰ ਤੇ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + search_by_email: "ਈਮੇਲ/ਕੋਡ ਦੁਆਰਾ ਖੋਜੋ..." + guest_label: "ਗੈਸਟ ਚੈਕਆਉਟ" + credit_owed: "ਬਕਾਇਆ ਕਰਜ਼ਾ" + balance_due: "ਬਕਾਇਆ ਰਕਮ" + destroy: + has_associated_subscriptions: "ਹਟਾਉਣਾ ਅਸਫਲ ਰਿਹਾ: ਇਸ ਗਾਹਕ ਕੋਲ ਕਿਰਿਆਸ਼ੀਲ ਗਾਹਕੀਆਂ ਹਨ। ਪਹਿਲਾਂ ਉਹਨਾਂ ਨੂੰ ਰੱਦ ਕਰੋ।" + contents: + edit: + title: ਕੰਟੇਂਟ + header: ਹੈਡਰ + home_page: ਹੋਮ ਪੇਜ + producer_signup_page: ਉਤਪਾਦਕ ਸਾਈਨਅੱਪ ਪੇਜ + hub_signup_page: ਹੱਬ ਸਾਈਨਅੱਪ ਪੇਜ + group_signup_page: ਸਮੂਹ ਸਾਈਨਅਪ ਪੇਜ + main_links: ਮੁੱਖ ਮੇਨਯੁ ਦੇ ਲਿੰਕ + footer_and_external_links: ਫੁੱਟਰ ਅਤੇ ਬਾਹਰੀ ਲਿੰਕ + your_content: ਤੁਹਾਡਾ ਕੰਟੇਂਟ + user_guide: ਉਪਭੋਗਤਾ ਗਾਈਡ + map: ਮੈਪ + enterprise_fees: + index: + title: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ" + enterprise: "ਐਂਟਰਪ੍ਰਾਈਜ਼" + fee_type: "ਫੀਸ ਦੀ ਕਿਸਮ" + name: "ਨਾਮ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + calculator: "ਕੈਲਕੁਲੇਟਰ" + calculator_values: "ਕੈਲਕੁਲੇਟਰ ਵੈਲਯੂ" + search: "ਖੋਜੋ" + name_placeholder: "ਜਿਵੇਂ - ਪੈਕਿੰਗ ਫ਼ੀਸ" + enterprise_groups: + index: + new_button: ਨਵਾਂ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸਮੂਹ + form_primary_details: + primary_details: "ਪ੍ਰਾਥਮਿਕ ਵੇਰਵੇ" + form_users: + users: "ਉਪਭੋਗਤਾ" + form_about: + about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + form_images: + images: "ਫੋਟੋ" + form_address: + contact: "ਸੰਪਰਕ" + form_web: + web: "ਵੈਬ ਸੰਸਾਧਨ" + enterprise_roles: + form: + manages: ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ + enterprise_role: + manages: ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ + products: + unit_name_placeholder: 'ਜਿਵੇਂ ਕਿ ਗੁੱਛੇ''' + index: + unit: ਯੂਨਿਟ + display_as: ਇਸ ਤਰ੍ਹਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ + category: ਸ਼੍ਰੇਣੀ + tax_category: ਟੈਕਸ ਸ਼੍ਰੇਣੀ + inherits_properties?: ਪ੍ਰਾਪਰਟੀਜ਼ ਅਪਣਾਉਂਦੇ ਹਨ? + av_on: "ਏਵੀ. ਆਨ" + import_date: ਇਮਪੋਰਟ ਕੀਤੇ ਗਏ + upload_an_image: ਇੱਕ ਫੋਟੋ ਅੱਪਲੋਡ ਕਰੋ + seo: + product_search_keywords: "ਉਤਪਾਦ ਖੋਜ ਕੀਵਰਡ" + product_search_tip: "ਸ਼ਾਪ ਵਿੱਚ ਆਪਣੇ ਉਤਪਾਦ ਲੱਭਣ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਨ ਲਈ ਸ਼ਬਦ ਟਾਈਪ ਕਰੋ। ਹਰੇਕ ਕੀਵਰਡ ਨੂੰ ਵੱਖ ਕਰਨ ਲਈ ਉਹਨਾਂ ਵਿਚਕਾਰ ਖਾਲੀ ਥਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ।" + seo_tip: "ਵੈਬ ਤੇ ਆਪਣੇ ਉਤਪਾਦਾਂ ਨੂੰ ਲੱਭਣ ਵਿੱਚ ਮਦਦ ਲਈ ਸ਼ਬਦ ਟਾਈਪ ਕਰੋ। ਹਰੇਕ ਕੀਵਰਡ ਨੂੰ ਵੱਖ ਕਰਨ ਲਈ ਉਹਨਾਂ ਵਿਚਕਾਰ ਖਾਲੀ ਥਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ।" + search: "ਖੋਜੋ" + properties: + property_name: "ਪ੍ਰਾਪਰਟੀ ਦਾ ਨਾਮ" + inherited_property: "ਅਪਣਾਈ ਗਈ ਪ੍ਰਾਪਰਟੀ" + variants: + infinity: "ਇਨਫਿਨਿਟੀ" + to_order_tip: "ਆਰਡਰ ਉਤੇ ਬਣੀਆਂ ਆਈਟਮਾਂ ਵਿੱਚ ਸਟਾਕ ਦੇ ਪੱਧਰ ਨਿਰਧਾਰਤ ਨਹੀਂ ਹੁੰਦੇ, ਜਿਵੇਂ ਕਿ ਆਰਡਰ ਤੇ ਬਣਾਈ ਗਈ ਤਾਜ਼ਾ ਬ੍ਰੈਡ।" + back_to_products_list: "ਉਤਪਾਦਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਿਸ" + editing_product: "ਉਤਪਾਦ ਦਾ ਸੰਪਾਦਨ" + tabs: + product_details: "ਉਤਪਾਦ ਵੇਰਵੇ" + group_buy_options: "ਸਮੂਹ ਖਰੀਦ ਵਿਕਲਪ" + images: "ਫੋਟੋ" + variants: "ਵੇਰੀਐਂਟਸ" + product_properties: "ਉਤਪਾਦ ਦੀ ਪਰੌਪਰਟੀਆਂ" + products_v3: + index: + header: + title: ਥੋਕ ਸੰਪਾਦਿਤ ਉਤਪਾਦ + loading: ਤੁਹਾਡੇ ਉਤਪਾਦ ਲੋਡ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ + sort: + pagination: + total_html: "ਤੁਹਾਡੇ ਖੋਜ ਮਾਪਦੰਡ ਲਈ %{total} ਉਤਪਾਦ ਮਿਲੇ ਹਨ। %{from} ਤੋਂ %{to} ਦਿਖਾ ਰਹੇ ਹਨ।" + per_page: + show: ਵਿਖਾਓ + per_page: "%{num} ਪ੍ਰਤੀ ਪੇਜ" + clear_search: ਖੋਜ ਮਿਟਾਓ + filters: + search_products: ਉਤਪਾਦਾਂ ਦੀ ਖੋਜ ਕਰੋ + all_producers: ਸਾਰੇ ਉਤਪਾਦਕ + all_categories: ਸਾਰੀਆਂ ਸ਼੍ਰੇਣੀਆਂ + producers: + label: ਉਤਪਾਦਕ + categories: + label: ਸ਼੍ਰੇਣੀਆਂ + search: ਖੋਜੋ + no_products: + no_products_found: ਕੋਈ ਉਤਪਾਦ ਨਹੀਂ ਲੱਭੇ + import_products: ਇੱਕ ਤੋਂ ਜ਼ਿਆਦਾ ਉਤਪਾਦ ਇਮਪੋਰਟ ਕਰੋ + no_products_found_for_search: ਤੁਹਾਡੇ ਖੋਜ ਮਾਪਦੰਡਾਂ ਦੇ ਅਨੁਸਾਰ ਕੋਈ ਉਤਪਾਦ ਨਹੀਂ ਮਿਲੇ + table: + reset: ਤਬਦੀਲੀਆਂ ਤਿਆਗੋ + save: ਤਬਦੀਲੀਆਂ ਨੂੰ ਸੇਵ ਕਰੋ + new_variant: ਨਵਾਂ ਵੇਰੀਐਂਟ + bulk_update: + success: ਬਦਲਾਵ ਸੇਵ ਕੀਤੇ ਗਏ।' + edit_image: + close: ਵਾਪਸ + product_import: + title: ਉਤਪਾਦ ਦਾ ਇਮਪੋਰਟ + file_not_found: ਫ਼ਾਈਲ ਨਹੀਂ ਮਿਲੀ ਜਾਂ ਖੋਲ੍ਹੀ ਨਹੀਂ ਜਾ ਸਕੀ + no_data: ਸਪ੍ਰੈਡਸ਼ੀਟ ਵਿੱਚ ਕੋਈ ਡਾਟਾ ਨਹੀਂ ਮਿਲਿਆ + confirm_reset: "ਇਹ ਇਸ \\n ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਸਾਰੇ ਉਤਪਾਦਾਂ ਤੇ ਸਟਾਕ ਪੱਧਰ ਨੂੰ ਜ਼ੀਰੋ ਤੇ ਸੇਟ ਕਰੇਗਾ ਜੋ ਅੱਪਲੋਡ ਕੀਤੀ ਫਾਈਲ ਵਿੱਚ ਮੌਜੂਦ ਨਹੀਂ ਹਨ" + model: + no_file: "ਗਲਤੀ: ਕੋਈ ਫ਼ਾਈਲ ਅੱਪਲੋਡ ਨਹੀਂ ਕੀਤੀ ਗਈ" + could_not_process: "ਫਾਇਲ ਸੰਸਾਧਿਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ: ਅਵੈਧ ਫਾਇਲ ਕਿਸਮ" + incorrect_value: ਗਲਤ ਵਲਯੂ + conditional_blank: ਜੇਕਰ unit_type ਖਾਲੀ ਹੈ ਤਾਂ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ + no_product: ਡੇਟਾਬੇਸ ਵਿੱਚ ਕਿਸੇ ਉਤਪਾਦ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ + not_found: ਡੇਟਾਬੇਸ ਵਿੱਚ ਨਹੀਂ ਮਿਲਿਆ + category_not_found: ਮਨਜ਼ੂਰਸ਼ੁਦਾ ਸ਼੍ਰੇਣੀਆਂ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ। ਉਤਪਾਦ ਇਮਪੋਰਟ ਪੇਜ ਤੇ ਚੁਣਨ ਲਈ ਸਹੀ ਸ਼੍ਰੇਣੀਆਂ ਵੇਖੋ, ਜਾਂ ਜਾਂਚ ਕਰੋ ਕਿ ਕੋਈ ਗਲਤ ਸਪੈਲਿੰਗ ਤਾਂ ਨਹੀਂ ਹੈ। + not_updatable: ਉਤਪਾਦ ਇਮਪੋਰਟ ਰਾਹੀਂ ਮੌਜੂਦਾ ਉਤਪਾਦਾਂ ਤੇ ਅਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ + values_must_be_same: ਸਮਾਨ ਨਾਮ ਵਾਲੇ ਉਤਪਾਦਾਂ ਲਈ ਇੱਕੋ ਜਿਹਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + blank: ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ + products_no_permission: ਤੁਹਾਡੇ ਕੋਲ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ + inventory_no_permission: ਤੁਹਾਡੇ ਕੋਲ ਇਸ ਉਤਪਾਦਕ ਲਈ ਇਨਵੇਂਟਰੀ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ + none_saved: ਕਿਸੇ ਵੀ ਉਤਪਾਦ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਗਿਆ + line_number: "ਲਾਈਨ %{number}:" + encoding_error: "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੀ ਸਰੋਤ ਫਾਈਲ ਦੀ ਭਾਸ਼ਾ ਸੈਟਿੰਗਾਂ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਇਹ UTF-8 ਇੰਕੋਡਿੰਗ ਨਾਲ ਸੇਵ ਕੀਤੀ ਗਈ ਹੈ।" + unexpected_error: "ਫਾਈਲ ਖੋਲ੍ਹਣ ਦੌਰਾਨ ਉਤਪਾਦ ਇਮਪੋਰਟ ਵਿੱਚ ਇੱਕ ਅਣਕਿਆਸੀ ਗਲਤੀ ਆਈ: %{error_message}" + malformed_csv: "ਉਤਪਾਦ ਇਮਪੋਰਟ ਨੂੰ ਇੱਕ ਨੁਕਸਦਾਰ CSV ਦਾ ਸਾਹਮਣਾ ਕਰਨਾ ਪਿਆ: %{error_message}" + index: + notice: "ਨੋਟਿਸ" + beta_notice: "ਇਹ ਫ਼ੀਚਰ ਅਜੇ ਵੀ ਬੀਟਾ ਵਿੱਚ ਹੈ: ਤੁਸੀਂ ਇਸਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਕੁਝ ਤਰੁੱਟੀਆਂ ਦਾ ਅਨੁਭਵ ਕਰ ਸਕਦੇ ਹੋ। ਕਿਰਪਾ ਕਰਕੇ ਸਮਰਥਨ ਨਾਲ ਸੰਪਰਕ ਕਰਨ ਵਿੱਚ ਸੰਕੋਚ ਨਾ ਕਰੋ।" + select_file: ਅੱਪਲੋਡ ਕਰਨ ਲਈ ਇੱਕ ਸਪ੍ਰੈਡਸ਼ੀਟ ਚੁਣੋ + spreadsheet: ਸਪ੍ਰੈਡਸ਼ੀਟ + choose_import_type: ਇਮਪੋਰਟ ਦੀ ਕਿਸਮ ਚੁਣੋ + import_into: ਇਮਪੋਰਟ ਦੀ ਕਿਸਮ + product_list: ਉਤਪਾਦਾਂ ਦੀ ਸੂਚੀ + inventories: ਇਨਵੇਂਟਰੀਆਂ + import: ਇਮਪੋਰਟ + upload: ਅੱਪਲੋਡ ਕਰੋ + csv_templates: CSV ਟੈਮਪਲੇਟ + product_list_template: ਉਤਪਾਦ ਸੂਚੀ ਦਾ ਟੈਮਪਲੇਟ ਡਾਊਨਲੋਡ ਕਰੋ + inventory_template: ਇਨਵੈਂਟਰੀ ਟੈਂਪਲੇਟ ਡਾਊਨਲੋਡ ਕਰੋ + category_values: ਉਪਲਬਧ ਸ਼੍ਰੇਣੀਆਂ ਦੇ ਵੈਲਯੂ + product_categories: ਉਤਪਾਦ ਸ਼੍ਰੇਣੀਆਂ + tax_categories: ਟੈਕਸ ਸ਼੍ਰੇਣੀਆਂ + shipping_categories: ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀਆਂ + dfc_import_form: + enterprise: "ਐਂਟਰਪ੍ਰਾਈਜ਼" + import: "ਇਮਪੋਰਟ" + import: + review: ਸਮੀਖਿਆ + import: ਇਮਪੋਰਟ + save: ਸੇਵ ਕਰੋ + results: ਨਤੀਜੇ + save_imported: ਇਮਪੋਰਟ ਕੀਤੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸੇਵ ਕਰੋ + no_valid_entries: ਕੋਈ ਵੈਧ ਐਂਟਰੀਆਂ ਨਹੀਂ ਮਿਲੀਆਂ + none_to_save: ਸੇਵ ਕਰਨ ਲਈ ਕੋਈ ਐਂਟਰੀਆਂ ਨਹੀਂ ਹਨ + some_invalid_entries: ਇਮਪੋਰਟ ਕੀਤੀ ਫਾਈਲ ਵਿੱਚ ਅਵੈਧ ਐਂਟਰੀਆਂ ਹਨ + fix_before_import: ਕਿਰਪਾ ਕਰਕੇ ਇਹਨਾਂ ਤਰੁੱਟੀਆਂ ਨੂੰ ਠੀਕ ਕਰੋ ਅਤੇ ਫਾਈਲ ਨੂੰ ਦੁਬਾਰਾ ਇਮਪੋਰਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ + save_valid?: ਹੁਣ ਲਈ ਵੈਧ ਐਂਟਰੀਆਂ ਨੂੰ ਸੇਵ ਕਰੋ ਅਤੇ ਬਾਕੀਆਂ ਨੂੰ ਤਿਆਗ ਦਿਓ? + no_errors: ਕੋਈ ਵੀ ਤਰੁੱਟੀਆਂ ਨਹੀਂ ਮਿਲੀ! + save_all_imported?: ਇਮਪੋਰਟ ਕੀਤੇ ਸਾਰੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸੇਵ ਕਰੋ? + options_and_defaults: ਇਮਪੋਰਟ ਵਿਕਲਪ ਅਤੇ ਡਿਫੌਲਟ + no_permission: ਤੁਹਾਡੇ ਕੋਲ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ + not_found: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਡੇਟਾਬੇਸ ਵਿੱਚ ਨਹੀਂ ਲੱਭਿਆ ਜਾ ਸਕਿਆ + no_name: ਕੋਈ ਨਾਂ ਨਹੀਂ + blank_enterprise: ਕੁਝ ਉਤਪਾਦਾਂ ਲਈ ਕੋਈ ਵੀ ਪਰਿਭਾਸ਼ਿਤ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨਹੀਂ ਹੈ + reset_absent?: ਗੈਰਹਾਜ਼ਰ ਉਤਪਾਦਾਂ ਨੂੰ ਰੀਸੈਟ ਕਰੋ + reset_absent_tip: ਫਾਈਲ ਵਿੱਚ ਮੌਜੂਦ ਨਾ ਹੋਣ ਵਾਲੇ ਸਾਰੇ ਉਤਪਾਦਾਂ ਲਈ ਸਟਾਕ ਨੂੰ ਜ਼ੀਰੋ ਤੇ ਸੇਟ ਕਰੋ + overwrite_all: ਸਭ ਨੂੰ ਓਵਰਰਾਈਟ ਕਰੋ + overwrite_empty: ਜੇਕਰ ਖਾਲੀ ਹੈ ਤਾਂ ਓਵਰਰਾਈਟ ਕਰੋ + default_stock: ਸਟਾਕ ਦਾ ਪੱਧਰ ਸੇਟ ਕਰੋ + default_tax_cat: ਟੈਕਸ ਸ਼੍ਰੇਣੀ ਸੇਟ ਕਰੋ + default_shipping_cat: ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ ਸੇਟ ਕਰੋ + default_available_date: ਉਪਲਬਧ ਮਿਤੀ ਸੇਟ ਕਰੋ + validation_overview: ਇਮਪੋਰਟ ਪ੍ਰਮਾਣਿਕਤਾ ਦੀ ਸੰਖੇਪ ਜਾਣਕਾਰੀ + entries_found: ਇੰਪੋਰਟ ਕੀਤੀ ਫਾਈਲ ਵਿੱਚ ਐਂਟਰੀਆਂ ਮਿਲੀਆਂ + entries_with_errors: ਆਈਟਮਾਂ ਵਿੱਚ ਤਰੁੱਟੀਆਂ ਹਨ ਅਤੇ ਇਮਪੋਰਟ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ + products_to_create: ਉਤਪਾਦ ਬਣਾਏ ਜਾਣਗੇ + products_to_update: ਉਤਪਾਦ ਅੱਪਡੇਟ ਕੀਤੇ ਜਾਣਗੇ + inventory_to_create: ਇਨਵੇਂਟਰੀ ਆਈਟਮਾਂ ਬਣਾਈਆਂ ਜਾਣਗੀਆਂ + inventory_to_update: ਇਨਵੇਂਟਰੀ ਆਈਟਮਾਂ ਅਪਡੇਟ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ + products_to_reset: ਮੌਜੂਦਾ ਉਤਪਾਦਾਂ ਦਾ ਸਟਾਕ ਨੂੰ ਜ਼ੀਰੋ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਵੇਗਾ + inventory_to_reset: ਮੌਜੂਦਾ ਇਨਵੇਂਟਰੀ ਆਈਟਮਾਂ ਦਾ ਸਟਾਕ ਨੂੰ ਜ਼ੀਰੋ ਤੇ ਰੀਸੈਟ ਹੋ ਜਾਵੇਗਾ + line: ਲਾਈਨ + item_line: ਆਈਟਮ ਲਾਈਨ + import_review: + not_updatable_tip: "ਮੌਜੂਦਾ ਉਤਪਾਦਾਂ ਲਈ ਥੋਕ ਇਮਪੋਰਟ ਰਾਹੀਂ ਹੇਠਾਂ ਦਿੱਤੇ ਖੇਤਰਾਂ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ:" + fields_ignored: ਜਦੋਂ ਇਮਪੋਰਟ ਕੀਤੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸੇਵ ਕੀਤੇ ਜਾਣ ਤੇ ਇਹਨਾਂ ਖੇਤਰਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕੀਤਾ ਜਾਵੇਗਾ। + entries_table: + not_updatable: ਮੌਜੂਦਾ ਉਤਪਾਦਾਂ ਤੇ ਥੋਕ ਇਮਪੋਰਟ ਦੁਆਰਾ ਇਸ ਖੇਤਰ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ + save_results: + final_results: ਅੰਤਿਮ ਨਤੀਜੇ ਇਮਪੋਰਟ ਕਰੋ + products_created: ਉਤਪਾਦ ਬਣਾਏ ਗਏ + products_updated: ਉਤਪਾਦ ਅੱਪਡੇਟ ਕੀਤੇ ਗਏ + inventory_created: ਇਨਵੇਂਟਰੀ ਆਈਟਮਾਂ ਬਣਾਈਆਂ ਗਈਆਂ + inventory_updated: ਇਨਵੇਂਟਰੀ ਆਈਟਮਾਂ ਅਪਡੇਟ ਕੀਤੀਆਂ ਗਈਆਂ + products_reset: Products had stock level reset to zero + inventory_reset: ਇਨਵੇਂਟਰੀ ਦਾ ਸਟਾਕ ਪੱਧਰ ਜ਼ੀਰੋ ਤੇ ਰੀਸੈਟ ਕੀਤਾ ਗਿਆ ਸੀ + all_saved: "ਸਾਰੀਆਂ ਆਈਟਮਾਂ ਸਫਲਤਾਪੂਰਵਕ ਸੇਵ ਕੀਤੀਆਂ ਗਈਆਂ" + some_saved: "ਆਈਟਮਾਂ ਸਫਲਤਾਪੂਰਵਕ ਸੇਵ ਕੀਤੀਆਂ ਗਈਆਂ" + save_errors: ਤਰੁੱਟੀਆਂ ਸੇਵ ਕਰੋ + import_again: ਕੋਈ ਹੋਰ ਫ਼ਾਈਲ ਅੱਪਲੋਡ ਕਰੋ + view_products: ਉਤਪਾਦ ਪੇਜ ਤੇ ਜਾਓ + view_inventory: Go To Inventory Page + product_headings: + distributor: ਵਿਤਰਕ + producer: ਉਤਪਾਦਕ + sku: SKU + name: ਨਾਮ + display_name: ਡਿਸਪਲੇ ਕੀਤੇ ਜਾਣ ਵਾਲਾ ਨਾਂ + category: ਸ਼੍ਰੇਣੀ + description: ਵਰਣਨ + units: ਯੂਨਿਟਾਂ + unit_type: ਯੂਨਿਟ ਦੀ ਕਿਸਮ + variant_unit_name: ਵੇਰੀਐਂਟ ਯੂਨਿਟ ਦਾ ਨਾਮ + price: ਕੀਮਤ + on_hand: ਹੱਥ ਵਿਚ + on_demand: ਡਿਮਾਂਡ ਤੇ + shipping_category: ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ + tax_category: ਟੈਕਸ ਸ਼੍ਰੇਣੀ + variant_overrides: + loading_flash: + loading_inventory: ਇਨਵੇਂਟਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ + index: + title: ਇਨਵੇਂਟਰੀ + description: ਆਪਣੇ ਇੰਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਇਨਵੇਂਟਰੀ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਸ ਪੇਜ ਦੀ ਵਰਤੋਂ ਕਰੋ। ਇੱਥੇ ਸੇਟ ਕੀਤਾ ਕੋਈ ਵੀ ਉਤਪਾਦ ਵੇਰਵਾ 'ਉਤਪਾਦ' ਪੇਜ ਉਤੇ ਸੇਟ ਕੀਤੇ ਵਰਣਨ ਨੂੰ ਓਵਰਰਾਈਡ ਕਰ ਦੇਵੇਗਾ + enable_reset?: ਸਟਾਕ ਰੀਸੇਟ ਸਮਰੱਥ ਕਰੀਏ? + default_stock: "ਡਿਫੌਲਟ ਸਟਾਕ" + inherit?: ਅਪਣਾਈਏ? + add: ਜੋੜੋ + hide: ਲੁਕਾਓ + import_date: ਇਮਪੋਰਟ ਕੀਤੇ ਗਏ + select_a_shop: ਇੱਕ ਸ਼ਾਪ ਚੁਣੋ + review_now: ਹੁਣੇ ਸਮੀਖਿਆ ਕਰੋ + new_products_alert_message: ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਲਈ %{new_product_count} ਨਵੇਂ ਉਤਪਾਦ ਉਪਲਬਧ ਹਨ। + currently_empty: ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਇਸ ਸਮੇਂ ਖਾਲੀ ਹੈ + no_matching_products: ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਕੋਈ ਮੇਲ ਖਾਂਦੇ ਉਤਪਾਦ ਨਹੀਂ ਮਿਲੇ + no_hidden_products: ਇਸ ਇਨਵੇਂਟਰੀ ਤੋਂ ਕੋਈ ਉਤਪਾਦ ਲੁਕਾਇਆ ਨਹੀਂ ਗਿਆ ਹੈ + no_matching_hidden_products: ਕੋਈ ਵੀ ਲੁਕਵੇਂ ਉਤਪਾਦ ਤੁਹਾਡੇ ਖੋਜ ਮਾਪਦੰਡ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ + no_new_products: ਇਸ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਕੋਈ ਨਵੇਂ ਉਤਪਾਦ ਉਪਲਬਧ ਨਹੀਂ ਹਨ + no_matching_new_products: ਕੋਈ ਵੀ ਨਵਾਂ ਉਤਪਾਦ ਤੁਹਾਡੇ ਖੋਜ ਮਾਪਦੰਡ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ + inventory_powertip: ਇਹ ਤੁਹਾਡੇ ਉਤਪਾਦਾਂ ਦੀ ਇਨਵੇਂਟਰੀ ਹੈ। ਆਪਣੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਉਤਪਾਦ ਜੋੜਨ ਲਈ, ਵਿਊਇੰਗ ਡ੍ਰੌਪਡਾਉਨ ਵਿੱਚੋਂ 'ਨਵੇਂ ਉਤਪਾਦ' ਚੁਣੋ। + hidden_powertip: ਇਹ ਉਤਪਾਦ ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਤੋਂ ਲੁਕਾਏ ਗਏ ਹਨ ਅਤੇ ਤੁਹਾਡੀ ਸ਼ਾਪ ਵਿੱਚ ਜੋੜਣ ਲਈ ਉਪਲਬਧ ਨਹੀਂ ਹੋਣਗੇ। ਤੁਸੀਂ ਆਪਣੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਉਤਪਾਦ ਜੋੜਨ ਲਈ 'ਜੋੜੋ' ਉਤੇ ਕਲਿੱਕ ਕਰ ਸਕਦੇ ਹੋ। + new_powertip: ਇਹ ਉਤਪਾਦ ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਜੋੜੇ ਜਾਣ ਲਈ ਉਪਲਬਧ ਹਨ। ਆਪਣੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਕਿਸੇ ਉਤਪਾਦ ਨੂੰ ਜੋੜਨ ਲਈ 'ਜੋੜੋ' 'ਤੇ ਕਲਿੱਕ ਕਰੋ, ਜਾਂ ਇਸਨੂੰ ਵੇਖੇ ਜਾਣ ਤੋਂ ਛੁਪਾਉਣ ਲਈ 'ਲੁਕਾਓ' ਉਤੇ ਕਲਿੱਕ ਕਰੋ। ਤੁਸੀਂ ਬਾਅਦ ਵਿੱਚ ਕਦੇ ਵੀ ਆਪਣਾ ਮਨ ਬਦਲ ਸਕਦੇ ਹੋ! + controls: + back_to_my_inventory: ਮੇਰੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਵਾਪਸ ਜਾਓ + orders: + edit: + order_sure_want_to: ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਇਸ ਆਰਡਰ ਨੂੰ %{event} ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? + voucher_tax_included_in_price: "%{label} (ਵਾਊਚਰ ਵਿੱਚ ਟੈਕਸ ਸ਼ਾਮਲ)" + invoice_email_sent: 'ਇਨਵੌਇਸ ਈਮੇਲ ਭੇਜ ਦਿੱਤੀ ਗਈ ਹੈ''' + order_email_resent: 'ਆਰਡਰ ਈਮੇਲ ਦੁਬਾਰਾ ਭੇਜ ਦਿੱਤੀ ਗਈ ਹੈ''' + bulk_management: + tip: "ਬਹੁਤ ਸਾਰੇ ਆਰਡਰਾਂ ਵਿੱਚ ਉਤਪਾਦ ਦੀ ਮਾਤਰਾ ਨੂੰ ਬਦਲਣ ਲਈ ਇਸ ਪੇਜ ਦੀ ਵਰਤੋਂ ਕਰੋ। ਲੋੜ ਪੈਣ ਤੇ ਉਤਪਾਦਾਂ ਨੂੰ ਆਰਡਰਾਂ ਤੋਂ ਪੂਰੀ ਤਰ੍ਹਾਂ ਹਟਾਇਆ ਜਾ ਸਕਦਾ ਹੈ।" + shared: "ਸੰਸਾਧਨ ਸਾਂਝਾ ਕੀਤਾ?" + order_no: "ਆਰਡਰ ਨੰਬਰ" + order_date: "ਤੇ ਪੂਰਾ ਹੋਇਆ" + max: "ਅਧਿਕਤਮ" + product_unit: "ਉਤਪਾਦ: ਯੂਨਿਟ" + weight_volume: "ਭਾਰ/ਮਾਤਰਾ (ਗ੍ਰਾਮ)" + ask: "ਪੁੱਛੋ?" + page_title: "ਥੋਕ ਆਰਡਰ ਪ੍ਰਬੰਧਨ" + actions_delete: "ਚੁਣੇ ਗਏ ਨੂੰ ਹਟਾਓ" + loading: "ਆਰਡਰ ਲੋਡ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ" + no_results: "ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਮਿਲਿਆ।" + group_buy_unit_size: "ਸਮੂਹ ਖਰੀਦ ਯੂਨਿਟ ਦਾ ਸਾਈਜ਼" + total_qtt_ordered: "ਆਰਡਰ ਕੀਤੀ ਕੁੱਲ ਮਾਤਰਾ" + max_qtt_ordered: "ਆਰਡਰ ਕੀਤੀ ਗਈ ਅਧਿਕਤਮ ਮਾਤਰਾ" + current_fulfilled_units: "ਮੌਜੂਦਾ ਪੂਰੀਆਂ ਕੀਤੀਆਂ ਯੂਨਿਟਾਂ" + max_fulfilled_units: "ਅਧਿਕਤਮ ਮੁਕੰਮਲ ਹੋਈਆਂ ਯੂਨਿਟਾਂ" + order_error: "ਤੁਹਾਡੇ ਵੱਲੋਂ ਆਰਡਰ ਅੱਪਡੇਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕੁਝ ਤਰੁੱਟੀਆਂ ਨੂੰ ਹੱਲ ਕੀਤਾ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ।\\nਲਾਲ ਕਿਨਾਰਿਆਂ ਵਾਲੇ ਕਿਸੇ ਵੀ ਖੇਤਰ ਵਿੱਚ ਤਰੁੱਟੀਆਂ ਹੁੰਦੀਆਂ ਹਨ।" + variants_without_unit_value: "ਚੇਤਾਵਨੀ: ਕੁਝ ਵੇਰੀਐਂਟਸ ਦੀ ਕੋਈ ਯੂਨਿਟ ਵੈਲਯੂ ਨਹੀਂ ਹੁੰਦੀ" + all: "ਸਾਰੇ" + select_variant: "ਇੱਕ ਵੇਰੀਐਂਟ ਚੁਣੋ" + note: + note_label: "ਨੋਟ:" + no_note_present: "ਕੋਈ ਨੋਟ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ।" + enterprise: + select_outgoing_oc_products_from: ਇਥੋਂ ਬਾਹਰ ਜਾਣ ਵਾਲੇ OC ਉਤਪਾਦਾਂ ਦੀ ਚੋਣ ਕਰੋ + enterprises: + index: + title: ਐਂਟਰਪ੍ਰਾਈਜ਼ + new_enterprise: ਐਂਟਰਪ੍ਰਾਈਜ਼ਜ਼ + producer?: "ਉਤਪਾਦਕ?" + package: ਪੈਕੇਜ + status: ਸਥਿਤੀ + manage: ਪ੍ਰਬੰਧਿਤ ਕਰੋ + form: + about_us: + legend: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + desc_short: ਸੰਖੇਪ ਵਰਣਨ + desc_short_placeholder: ਇੱਕ ਜਾਂ ਦੋ ਵਾਕਾਂ ਵਿੱਚ ਸਾਨੂੰ ਆਪਣੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਬਾਰੇ ਦੱਸੋ + desc_long: ਸਾਡੇ ਬਾਰੇ ਵਿੱਚ + desc_long_placeholder: ਗਾਹਕਾਂ ਨੂੰ ਆਪਣੇ ਬਾਰੇ ਦੱਸੋ। ਇਹ ਜਾਣਕਾਰੀ ਤੁਹਾਡੇ ਜਨਤਕ ਪ੍ਰੋਫਾਈਲ ਤੇ ਵਿਖਾਈ ਦਿੰਦੀ ਹੈ। + address: + legend: "ਪਤਾ" + business_details: + legend: "ਕਾਰੋਬਾਰੀ ਵੇਰਵੇ" + upload: 'ਅੱਪਲੋਡ''' + abn: ABN + abn_placeholder: ਜਿਵੇਂ - 99 123 456 789 + acn: ACN + acn_placeholder: ਜਿਵੇਂ - 99 123 456 789 + display_invoice_logo: ਇਨਵੌਇਸ ਤੇ ਲੋਗੋ ਡਿਸਪਲੇ ਕਰੋ + invoice_text: ਇਨਵੌਇਸ ਦੇ ਅੰਤ ਵਿੱਚ ਅਨੁਕੂਲਿਤ ਟੈਕਸਟ ਸ਼ਾਮਲ ਕਰੋ + terms_and_conditions: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ" + remove_terms_and_conditions: "ਫਾਇਲ ਹਟਾਓ" + uploaded_on: "ਇਸਤੇ ਅੱਪਲੋਡ ਕੀਤਾ ਗਿਆ" + reset_form: "ਫਾਰਮ ਰੀਸੈਟ ਕਰੋ" + business_address_legend: "ਕਾਰੋਬਾਰੀ ਪਤਾ" + invoice_item_sorting_legend: "ਇਨਵੌਇਸ ਆਈਟਮ ਦੀ ਛਾਂਟੀ" + sort_items_by_supplier?: ਸਪਲਾਇਰ ਦੁਆਰਾ ਆਈਟਮਾਂ ਨੂੰ ਕ੍ਰਮਬੱਧ ਕਰੋ? + sort_items_by_supplier_tip: "ਜਦੋਂ ਸਮਰੱਥ ਹੋਦਾ ਹੈ, ਤਾਂ ਆਈਟਮਾਂ ਨੂੰ ਸਪਲਾਇਰ ਦੇ ਨਾਂ ਦੁਆਰਾ ਛਾਂਟਿਆ ਜਾਣਗੀਆਂ।" + enabled: ਸਮਰੱਥ ਕਰੋ + disabled: ਅਸਮਰੱਥ ਕਰੋ + business_address: + company_legal_name: ਕੰਪਨੀ ਦਾ ਕਾਨੂੰਨੀ ਨਾਂ + company_placeholder: ਜਿਵੇਂ - Inc. + address1: ਕਾਨੂੰਨੀ ਪਤਾ + address1_placeholder: 123 ਹਾਈ ਸੇਂਟ + address2: ਪਤਾ (ਜਾਰੀ) + legal_phone_number: ਕਾਨੂੰਨੀ ਫੋਨ ਨੰਬਰ + phone_placeholder: "98 123 4565" + select_country: "ਦੇਸ਼ ਚੁਣੋ" + select_state: "ਰਾਜ ਚੁਣੋ" + contact: + legend: "ਸੰਪਰਕ" + name: ਨਾਮ + name_placeholder: ਜਿਵੇਂ - ਗੁਸਤਾਵ ਪ੍ਲਮ + email_address: ਪਬਲਿਕ ਈਮੇਲ ਪਤਾ + email_address_placeholder: ਜਿਵੇਂ ਕਿ inquiries@fresh-food.com + email_address_tip: "ਇਹ ਈਮੇਲ ਐਡਰੈੱਸ ਤੁਹਾਡੇ ਪਬਲਿਕ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਦਿਸੇਗਾ।" + phone: ਫੋਨ + phone_placeholder: ਜਿਵੇਂ - 98 7654 3210 + whatsapp_phone: WhatsApp ਫੋਨ ਨੰਬਰ + whatsapp_phone_placeholder: ਜਿਵੇਂ - +61 4 9876 5432 + whatsapp_phone_tip: "ਇਹ ਨੰਬਰ ਤੁਹਾਡੇ ਪਬਲਿਕ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ WhatsApp ਲਿੰਕ ਦੇ ਰੂਪ ਵਿੱਚ ਖੋਲ੍ਹਣ ਲਈ ਵਿਖਾਇਆ ਜਾਵੇਗਾ।" + website: ਵੈਬਸਾਈਟ + website_placeholder: ਜਿਵੇਂ ਕਿ www.tuffles.com + enterprise_fees: + legend: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ" + name: ਨਾਮ + fee_type: ਫੀਸ ਦੀ ਕਿਸਮ + manage_fees: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ + no_fees_yet: ਤੁਹਾਡੀ ਹਾਲੇ ਕੋਈ ਵੀ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ ਨਹੀਂ ਹੈ। + create_button: ਹੁਣੇ ਇੱਕ ਬਣਾਓ + enterprise_permissions: + legend: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਅਨੁਮਤੀਆਂ" + enterprise_relationships: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸਬੰਧ + images: + legend: "ਫੋਟੋ" + logo: ਲੋਗੋ + promo_image_placeholder: 'ਇਹ ਫੋਟੋ "ਸਾਡੇ ਬਾਰੇ ਵਿੱਚ" ਪ੍ਰਦਰਸ਼ਿਤ ਹੈ' + promo_image_note1: 'ਕਿਰਪਾ ਕਰਕੇ ਨੋਟ ਕਰੋ:''' + promo_image_note2: ਇੱਥੇ ਅੱਪਲੋਡ ਕੀਤੀ ਕਿਸੇ ਵੀ ਪ੍ਰੋਮੋ ਦੀ ਫੋਟੋ ਨੂੰ 1200 x 260 ਵਿੱਚ ਕੱਟਿਆ ਜਾਵੇਗਾ। + promo_image_note3: ਪ੍ਰੋਮੋ ਫੋਟੋ ਕਿਸੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਪ੍ਰੋਫਾਈਲ ਪੇਜ ਦੇ ਉਪਰ ਦੀ ਔਰ ਅਤੇ ਪੌਪ-ਅਪ ਵਿੱਚ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। + remove_logo: "ਫੋਟੋ ਹਟਾਓ" + remove_promo_image: "ਫੋਟੋ ਹਟਾਓ" + inventory_settings: + legend: "ਇਨਵੇਂਟਰੀ ਸੈਟਿੰਗਾਂ" + text1: ਤੁਸੀਂ ਆਪਣੀ ਇਨਵੇਂਟਰੀ ਰਾਹੀਂ ਸਟਾਕ ਦੇ ਪੱਧਰ ਅਤੇ ਕੀਮਤਾਂ ਦੇ ਪ੍ਰਬੰਧਨ ਦਾ ਵਿਕਲਪ ਚੁਣ ਸਕਦੇ ਹੋ + inventory: ਇਨਵੇਂਟਰੀ + text2: > + ਜੇਕਰ ਤੁਸੀਂ ਇਨਵੇਂਟਰੀ ਟੂਲ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਚੁਣ ਸਕਦੇ ਹੋ ਕਿ + ਤੁਹਾਡੇ ਸਪਲਾਇਰਾਂ ਦੁਆਰਾ ਸ਼ਾਮਲ ਕੀਤੇ ਗਏ ਨਵੇਂ ਉਤਪਾਦਾਂ ਨੂੰ ਸਟਾਕ ਕੀਤੇ ਜਾਣ ਤੋਂ + ਪਹਿਲਾਂ ਤੁਹਾਡੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਲੋੜ ਹੈ ਜਾਂ ਨਹੀਂ। ਜੇਕਰ ਤੁਸੀਂ + ਆਪਣੇ ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਆਪਣੀ ਇਨਵੇਂਟਰੀ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰ ਰਹੇ ਹੋ + ਤਾਂ ਤੁਹਾਨੂੰ ਹੇਠਾਂ 'ਸਿਫ਼ਾਰਸ਼ੀ' ਵਿਕਲਪ ਦੀ ਚੋਣ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ: + preferred_product_selection_from_inventory_only_yes: ਨਵੇਂ ਉਤਪਾਦ ਮੇਰੇ ਸ਼ੌਪਫ੍ਰੰਟ ਵਿੱਚ ਰੱਖੇ ਜਾ ਸਕਦੇ ਹਨ (ਸਿਫਾਰਿਸ਼ ਕੀਤੀ ਗਈ) + preferred_product_selection_from_inventory_only_no: ਮੇਰੇ ਸ਼ੌਪਫਰੰਟ ਵਿੱਚ ਰੱਖੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਨਵੇਂ ਉਤਪਾਦ ਮੇਰੀ ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤੇ ਜਾਣੇ ਚਾਹੀਦੇ ਹਨ। + payment_methods: + legend: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + name: ਨਾਮ + applies: ਲਾਗੂ ਹੁੰਦਾ ਹੈ? + manage: ਭੁਗਤਾਨ ਦੇ ਢੰਗਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ + no_method_yet: ਤੁਹਾਡੇ ਕੋਲ ਅਜੇ ਤੱਕ ਕੋਈ ਭੁਗਤਾਨ ਦਾ ਢੰਗ ਨਹੀਂ ਹੈ। + create_button: ਭੁਗਤਾਨ ਦਾ ਨਵਾਂ ਢੰਗ ਬਣਾਓ + create_one_button: ਹੁਣੇ ਇੱਕ ਬਣਾਓ + primary_details: + legend: "ਪ੍ਰਾਥਮਿਕ ਵੇਰਵੇ" + name: ਨਾਮ + name_placeholder: ਜਿਵੇਂ - ਪ੍ਰੋਫੈਸਰ ਪਲੱਮ ਦੇ ਬਾਇਓਡਾਇਨਾਮਿਕ ਟਰਫਲਜ਼ + groups: ਸਮੂਹ + groups_tip: ਕੋਈ ਵੀ ਸਮੂਹ ਜਾਂ ਖੇਤਰ ਚੁਣੋ ਜਿਸ ਦੇ ਤੁਸੀਂ ਮੈਂਬਰ ਹੋ। ਇਹ ਗਾਹਕਾਂ ਨੂੰ ਤੁਹਾਡੇ ਉਦਯੋਗ ਨੂੰ ਲੱਭਣ ਵਿੱਚ ਮਦਦ ਕਰੇਗਾ। + groups_placeholder: ਉਪਲਬਧ ਸਮੂਹਾਂ ਨੂੰ ਖੋਜਣ ਲਈ ਟਾਈਪ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ... + primary_producer: ਮੁੱਖ ਉਤਪਾਦਕ? + primary_producer_tip: ਜੇਕਰ ਤੁਸੀਂ ਭੋਜਨ ਦੇ ਮੁੱਖ ਉਤਪਾਦਕ ਹੋ ਤਾਂ 'ਉਤਪਾਦਕ' ਚੁਣੋ। + producer: ਉਤਪਾਦਕ + any: ਕੋਈ ਵੀ + none: ਕੋਈ ਨਹੀਂ + own: ਆਪਣਾ + sells: ਵੇਚਦਾ ਹੈ + sells_tip: "ਕੋਈ ਨਹੀਂ - ਐਂਟਰਪ੍ਰਾਈਜ਼ ਗਾਹਕਾਂ ਨੂੰ ਸਿੱਧੇ ਤੌਰ ਤੇ ਨਹੀਂ ਵੇਚਦਾ।
ਆਪਣਾ - ਐਂਟਰਪ੍ਰਾਈਜ਼ ਗਾਹਕਾਂ ਨੂੰ ਆਪਣੇ ਉਤਪਾਦ ਵੇਚਦਾ ਹੈ।
ਕੋਈ ਵੀ - ਐਂਟਰਪ੍ਰਾਈਜ਼ ਆਪਣੇ ਜਾਂ ਹੋਰ ਉਦਯੋਗਾਂ ਦੇ ਉਤਪਾਦ ਵੇਚ ਸਕਦਾ ਹੈ।
" + visible_in_search: ਖੋਜ ਵਿੱਚ ਦਿਸਦਾ ਹੈ? + visible_in_search_tip: "\"ਸ਼ਾਪਾਂ
1. ਪਬਲਿਕ ਤੌਰ ਤੇ OFN ਦੇ ਨਕਸ਼ੇ ਅਤੇ ਲਿਸਟਿੰਗਜ਼ ਤੇ ਵਿਖਾਈ ਦੇ ਸਕਦੀਆਂ ਹਨ।
2. ਨਕਸ਼ਿਆਂ ਅਤੇ ਲਿਸਟਿੰਗਜ਼ ਤੇ ਲੁਕੀਆਂ ਹੋਈਆਂ ਹਨ ਪਰ ਦੂਜੀਆਂ ਸ਼ਾਪਾਂ ਦੁਆਰਾ ਹਵਾਲਾ ਦਿੱਤੀਆਂ ਗਈਆਂ ਹਨ ਅਤੇ ਉਹਨਾਂ ਦੇ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਲਿੰਕ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ।
3. ਪੂਰੀ ਤਰ੍ਹਾਂ ਛੁਪੀਆਂ ਹੋ ਸਕਦੀਆਂ ਹਨ।\"" + visible: ਪਬਲਿਕ + not_visible: ਲੁਕੀਆ ਹੋਇਆ + hidden: ਸਾਰੇ ਹਵਾਲੇ ਲੁਕਾਓ + properties: + legend: "ਪ੍ਰਾਪਰਟੀਜ਼" + permalink: + permalink: ਸਥਾਈ ਲਿੰਕ (ਕੋਈ ਖਾਲੀ ਥਾਂ ਨਹੀਂ) + permalink_tip: "ਇਹ ਸਥਾਈ ਲਿੰਕ ਤੁਹਾਡੀ ਦੁਕਾਨ ਦਾ URL ਬਣਾਉਣ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ: %{link}ਤੁਹਾਡੀ-ਸ਼ਾਪ-ਦਾ ਨਾਮ/ਸ਼ਾਪ" + link_to_front: ਸ਼ਾਪਫਰੰਟ ਨਾਲ ਲਿੰਕ ਕਰੋ + link_to_front_tip: ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਤੁਹਾਡੇ ਸ਼ੌਪਫਰੰਟ ਦਾ ਸਿੱਧਾ ਲਿੰਕ। + ofn_uid: OFN UID + ofn_uid_tip: ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਇੰਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਪਛਾਣ ਕਰਨ ਲਈ ਵਰਤੋਂ ਕੀਤੀ ਜਾਣ ਵਾਲੀ ਵਿਸ਼ੇਸ਼ ਆਈਡੀ। + shipping_methods: + legend: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + name: "ਨਾਮ" + applies: "ਕਿਰਿਆਸ਼ੀਲ?" + manage: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ" + create_button: "ਸ਼ਿਪਿੰਗ ਦੇ ਨਵੇਂ ਢੰਗ ਬਣਾਓ" + create_one_button: "ਹੁਣੇ ਇੱਕ ਬਣਾਓ" + no_method_yet: "ਤੁਹਾਡੇ ਕੋਲ ਹਾਲੇ ਤੱਕ ਕੋਈ ਵੀ ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ ਨਹੀਂ ਹਨ।" + shop_preferences: + legend: "ਸ਼ਾਪ ਤਰਜੀਹਾਂ" + shopfront_requires_login: "ਇੱਕ ਪਬਲਿਕ ਤੌਰ ਉਤੇ ਵਿਖਾਈ ਦੇਣ ਵਾਲੀ ਸ਼ਾਪ?" + shopfront_requires_login_tip: "ਚੁਣੋ ਕਿ ਕੀ ਗਾਹਕਾਂ ਨੂੰ ਸ਼ਾਪਫ੍ਰੰਟ ਵੇਖਣ ਲਈ ਲੌਗਇਨ ਕਰਨਾ ਪਵੇਗਾ ਜਾਂ ਇਹ ਸਾਰਿਆਂ ਨੂੰ ਖਾਈ ਵਿਖਾਈ ਦਵੇਗੀ।" + shopfront_requires_login_false: "ਪਬਲਿਕ" + shopfront_requires_login_true: "ਸਿਰਫ਼ ਰਜਿਸਟਰਡ ਗਾਹਕਾਂ ਨੂੰ ਵਿਖੇਗਾ" + recommend_require_login: "ਆਰਡਰ ਬਦਲੇ ਜਾਣ ਲਈ ਅਸੀਂ ਉਪਭੋਗਤਾਵਾਂ ਦੇ ਲੌਗਇਨ ਕਰਨ ਦੀ ਸਿਫਾਰਸ਼ ਕਰਦੇ ਹਾਂ।" + allow_guest_orders: "ਗੈਸਟ ਆਰਡਰ" + allow_guest_orders_tip: "ਇੱਕ ਗੈਸਟ ਵਜੋਂ ਚੈਕਆਉਟ ਦੀ ਆਗਿਆ ਦਿਓ ਜਾਂ ਇੱਕ ਰਜਿਸਟਰਡ ਉਪਭੋਗਤਾ ਦੀ ਲੋੜ ਹੈ।" + allow_guest_orders_false: "ਆਰਡਰ ਕਰਨ ਲਈ ਲੌਗਇਨ ਦੀ ਲੋੜ ਹੈ" + allow_guest_orders_true: "ਗੈਸਟ ਚੈਕਆਉਟ ਦੀ ਆਗਿਆ ਦਿਓ" + allow_order_changes: "ਆਰਡਰ ਬਦਲੋ" + allow_order_changes_tip: "ਜਦ ਤੱਕ ਆਰਡਰ ਸਾਈਕਲ ਖੁੱਲਾ ਹੈ, ਗਾਹਕਾਂ ਨੂੰ ਆਪਣਾ ਆਰਡਰ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿਓ।" + allow_order_changes_false: "ਕੀਤੇ ਗਏ ਆਰਡਰ ਨੂੰ ਬਦਲਿਆ/ਰੱਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ" + allow_order_changes_true: "ਆਰਡਰ ਸਾਈਕਲ ਖੁੱਲੇ ਹੋਣ ਤੇ ਗਾਹਕ ਆਰਡਰ ਬਦਲ/ਰੱਦ ਕਰ ਸਕਦੇ ਹਨ" + enable_subscriptions: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + enable_subscriptions_tip: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਕਾਰਜਕੁਸ਼ਲਤਾ ਨੂੰ ਸਮਰੱਥ ਕਰੋ?" + enable_subscriptions_false: "ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + enable_subscriptions_true: "ਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + customer_names_in_reports: "ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਗਾਹਕ ਦੇ ਨਾਮ" + customer_names_tip: "ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਆਪਣੇ ਗਾਹਕਾਂ ਦੇ ਨਾ ਵੇਖਣ ਲਈ ਆਪਣੇ ਸਪਲਾਇਰਾਂ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ" + customer_names_false: "ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + customer_names_true: "ਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + shopfront_message: "ਸ਼ਾਪਫਰੰਟ ਸੰਦੇਸ਼" + shopfront_message_placeholder: > + ਗਾਹਕਾਂ ਦਾ ਸੁਆਗਤ ਕਰਨ ਅਤੇ ਤੁਹਾਡੇ ਨਾਲ ਖਰੀਦਦਾਰੀ ਕਰਨ ਦਾ ਢੰਗ ਦੱਸਣ ਲਈ ਇੱਕ ਵਿਕਲਪਿਕ + ਸੰਦੇਸ਼। ਜੇਕਰ ਟੈਕਸਟ ਇੱਥੇ ਦਰਜ ਕੀਤਾ ਗਿਆ ਹੈ ਤਾਂ ਉਹ ਹੋਮ ਟੈਬ ਵਿੱਚ ਪ੍ਰਦਰਸ਼ਿਤ + ਹੋਵੇਗਾ ਜਦੋਂ ਗਾਹਕ ਪਹਿਲੀ ਵਾਰ ਤੁਹਾਡੇ ਸ਼ਾਪ ਦੇ ਸਾਹਮਣੇ ਆਉਂਦੇ ਹਨ। + shopfront_message_link_tooltip: "ਲਿੰਕ ਪਾਓ / ਸੰਪਾਦਿਤ ਕਰੋ" + shopfront_message_link_prompt: "ਸਮਿਲਿਤ ਕਰਨ ਲਈ ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ URL ਦਾਖਲ ਕਰੋ" + shopfront_closed_message: "ਸ਼ਾਪਫ੍ਰੰਟ ਦੇ ਬੰਦ ਹੋਣ ਦਾ ਸੰਦੇਸ਼" + shopfront_closed_message_placeholder: > + ਇੱਕ ਸੰਦੇਸ਼ ਜੋ ਇਸ ਬਾਰੇ ਵਧੇਰੇ ਵਿਸਤ੍ਰਿਤ ਵਿਆਖਿਆ ਪ੍ਰਦਾਨ ਕਰਦਾ ਹੈ ਕਿ ਤੁਹਾਡੀ + ਸ਼ਾਪ ਕਿਉਂ ਬੰਦ ਹੈ ਅਤੇ/ਜਾਂ ਗਾਹਕ ਇਸ ਦੇ ਦੁਬਾਰਾ ਖੁੱਲ੍ਹਣ ਦੀ ਉਮੀਦ ਕਦੋਂ ਕਰ ਸਕਦੇ + ਹਨ। ਇਹ ਤੁਹਾਡੀ ਸ਼ਾਪ ਤੇ ਉਦੋਂ ਹੀ ਪ੍ਰਦਰਸ਼ਿਤ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ + ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਨਹੀਂ ਹੁੰਦਾ ਹੈ (ਯਾਨੀ ਜਦੋਂ ਸ਼ਾਪ ਬੰਦ ਹੈ)। + shopfront_category_ordering: "ਸ਼ਾਪਫਰੰਟ ਸ਼੍ਰੇਣੀ ਦੇ ਅਧਾਰ ਤੇ ਆਰਡਰ" + shopfront_category_ordering_note: "(ਉਪਰ ਤੋਂ ਥੱਲੇ)" + open_date: "ਖੁੱਲਣ ਦੀ ਮਿਤੀ" + close_date: "ਬੰਦ ਹੋਣ ਦੀ ਮਿਤੀ" + display_ordering_in_shopfront: "ਸ਼ਾਪਫਰੰਟ ਵਿੱਚ ਆਰਡਰ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ:" + shopfront_sort_by_category: "ਸ਼੍ਰੇਣੀ ਦੁਆਰਾ" + shopfront_sort_by_producer: "ਉਤਪਾਦਕ ਦੁਆਰਾ" + shopfront_sort_by_category_placeholder: "ਸ਼੍ਰੇਣੀ" + shopfront_sort_by_producer_placeholder: "ਉਤਪਾਦਕ" + display_remaining_stock: "ਜੇਕਰ ਹੱਥ ਵਿੱਚ ਸਟਾਕ ਘੱਟ ਹੈ ਤਾਂ ਬਾਕੀ ਬਚੇ ਸਟਾਕ ਨੂੰ ਸ਼ਾਪਫਰੰਟ ਤੇ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ" + display_remaining_stock_tip: "ਜਦੋਂ ਸਿਰਫ 3 ਜਾਂ ਉਸਤੋਂ ਘੱਟ ਚੀਜ਼ਾਂ ਬਚੀਆਂ ਹੋਣ ਤਾਂ ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਸੂਚਿਤ ਕਰੋ।" + enabled: "ਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + disabled: "ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + social: + legend: "ਸਮਾਜਿਕ" + twitter_placeholder: "ਜਿਵੇਂ - @the_prof" + instagram_placeholder: "ਜਿਵੇਂ -the_prof" + facebook_placeholder: "ਜਿਵੇਂ- www.facebook.com/PageNameHere" + linkedin_placeholder: "ਜਿਵੇਂ - www.linkedin.com/in/YourNameHere" + stripe_connect: + connect_with_stripe: "ਸਟ੍ਰਾਈਪ ਨਾਲ ਕਨੇਕਟ ਕਰੋ" + stripe_connect_intro: "ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਭੁਗਤਾਨ ਸਵੀਕਾਰ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਆਪਣੇ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਨਾਲ ਕਨੇਕਟ ਕਰਨ ਦੀ ਲੋੜ ਹੋਵੇਗੀ। ਸ਼ੁਰੂਆਤ ਕਰਨ ਲਈ ਸੱਜੇ ਪਾਸੇ ਦਿੱਤੇ ਗਏ ਬਟਨ ਦੀ ਵਰਤੋਂ ਕਰੋ।" + stripe_account_connected: "ਸਟ੍ਰਾਈਪ ਖਾਤਾ ਕਨੇਕਟ ਹੈ।" + disconnect: "ਖਾਤਾ ਡਿਸਕਕਨੇਟ ਕਰੋ" + confirm_modal: + title: ਸਟ੍ਰਾਈਪ ਨਾਲ ਕਨੇਕਟ ਕਰੋ + part1: ਸਟ੍ਰਾਈਪ ਇੱਕ ਭੁਗਤਾਨ ਪ੍ਰੋਸੈਸਿੰਗ ਸੇਵਾ ਹੈ ਜੋ OFN ਉਤੇ ਸ਼ਾਪਾਂ ਨੂੰ ਗਾਹਕਾਂ ਤੋਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਭੁਗਤਾਨ ਸਵੀਕਾਰ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ। + part2: ਇਸ ਫ਼ੀਚਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਆਪਣੇ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਨੂੰ OFN ਨਾਲ ਕਨੇਕਟ ਕਰਨਾ ਹੋਵੇਗਾ। ਹੇਠਾਂ 'ਮੈਂ ਸਹਿਮਤ ਹਾਂ' ਉਤੇ ਕਲਿੱਕ ਕਰਨ ਨਾਲ ਤੁਸੀਂ ਸਟ੍ਰਾਈਪ ਵੈਬਸਾਈਟ ਤੇ ਰੀਡਾਇਰੈਕਟ ਹੋ ਜਾਵੋਗੇ, ਜਿੱਥੇ ਤੁਸੀਂ ਮੌਜੂਦਾ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਨੂੰ ਕਨੇਕਟ ਕਰ ਸਕਦੇ ਹੋ, ਜਾਂ ਜੇਕਰ ਤੁਹਾਡੇ ਕੋਲ ਪਹਿਲਾਂ ਤੋਂ ਕੋਈ ਖਾਤਾ ਨਹੀਂ ਹੈ ਤਾਂ ਤੁਸੀਂ ਇੱਕ ਨਵਾਂ ਖਾਤਾ ਬਣਾ ਸਕਦੇ ਹੋ। + part3: ਇਹ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਨੂੰ ਤੁਹਾਡੀ ਤਰਫੋਂ ਗਾਹਕਾਂ ਤੋਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਭੁਗਤਾਨ ਸਵੀਕਾਰ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਵੇਗਾ। ਕਿਰਪਾ ਕਰਕੇ ਨੋਟ ਕਰੋ ਕਿ ਤੁਹਾਨੂੰ ਆਪਣੇ ਖੁਦ ਦੇ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਨੂੰ ਕਾਇਮ ਰੱਖਣ, ਫੀਸਾਂ ਦਾ ਭੁਗਤਾਨ ਕਰਨ ਅਤੇ ਕਿਸੇ ਵੀ ਚਾਰਜਬੈਕ ਅਤੇ ਗਾਹਕ ਸੇਵਾ ਨੂੰ ਖੁਦ ਸੰਭਾਲਣ ਦੀ ਲੋੜ ਹੋਵੇਗੀ। + i_agree: ਮੈਂ ਸਹਿਮਤ ਹਾਂ + cancel: ਰੱਦ ਕਰੋ + tag_rules: + legend: "ਟੈਗ ਨਿਯਮ" + default_rules: + by_default: ਡਿਫੌਲਟ ਦਵਾਰਾ + no_rules_yet: ਅਜੇ ਤੱਕ ਕੋਈ ਡਿਫੌਲਟ ਨਿਯਮ ਲਾਗੂ ਨਹੀਂ ਹੁੰਦੇ + add_new_button: '+ ਇੱਕ ਨਵਾਂ ਡਿਫੌਲਟ ਨਿਯਮ ਜੋੜੋ''' + no_tags_yet: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਤੇ ਅਜੇ ਤੱਕ ਕੋਈ ਟੈਗ ਲਾਗੂ ਨਹੀਂ ਹਨ + no_rules_yet: ਇਸ ਟੈਗ ਤੇ ਅਜੇ ਤੱਕ ਕੋਈ ਨਿਯਮ ਲਾਗੂ ਨਹੀਂ ਹਨ + for_customers_tagged: 'ਟੈਗ ਕੀਤੇ ਗਾਹਕਾਂ ਲਈ:''' + add_new_rule: '+ ਇੱਕ ਨਵਾਂ ਨਿਯਮ ਜੋੜੋ''' + add_new_tag: '+ ਇੱਕ ਨਵਾਂ ਟੈਗ ਜੋੜੋ''' + users: + legend: "ਉਪਭੋਗਤਾ" + email_confirmation_notice_html: "ਈਮੇਲ ਪੁਸ਼ਟੀਕਰਨ ਲੰਬਿਤ ਹੈ। ਅਸੀਂ %{email} ਨੂੰ ਇੱਕ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਭੇਜੀ ਹੈ।" + resend: ਦੁਬਾਰਾ ਭੇਜੋ + owner: 'ਮਾਲਕ''' + contact: "ਸੰਪਰਕ" + contact_tip: "ਪ੍ਰਬੰਧਕ ਜੋ ਆਰਡਰਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਲਈ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਈਮੇਲ ਪ੍ਰਾਪਤ ਕਰੇਗਾ। ਉਦੇ ਕੋਲ ਇੱਕ ਪੁਸ਼ਟੀ ਕੀਤਾ ਈਮੇਲ ਪਤਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।" + owner_tip: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਜ਼ਿੰਮੇਵਾਰ ਪ੍ਰਾਥਮਿਕ ਉਪਭੋਗਤਾ। + notifications: ਸੂਚਨਾਵਾਂ + notifications_tip: ਆਰਡਰਾਂ ਬਾਰੇ ਸੂਚਨਾਵਾਂ ਇਸ ਈਮੇਲ ਪਤੇ ਉਤੇ ਭੇਜੀਆਂ ਜਾਣਗੀਆਂ। + notifications_placeholder: ਜਿਵੇਂ - gustav@truffles.com + notifications_note: 'ਨੋਟ: ਵਰਤਣ ਤੋਂ ਪਹਿਲਾਂ ਇੱਕ ਨਵੇਂ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਹੋ ਸਕਦੀ ਹੈ''' + managers: ਪ੍ਰਬੰਧਕ + managers_tip: ਦੂਜੇ ਉਪਭੋਗਤਾ ਜਿਹਨਾਂ ਕੋਲ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਹੈ। + invite_manager: "ਪ੍ਰਬੰਧਕ ਨੂੰ ਸੱਦਾ ਦਿਓ" + invite_manager_tip: "ਕਿਸੇ ਗੈਰ-ਰਜਿਸਟਰਡ ਉਪਭੋਗਤਾ ਨੂੰ ਸਾਈਨ ਅੱਪ ਕਰਨ ਅਤੇ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਪ੍ਰਬੰਧਕ ਬਣਨ ਲਈ ਸੱਦਾ ਦਿਓ।" + add_unregistered_user: "ਇੱਕ ਗੈਰ-ਰਜਿਸਟਰਡ ਉਪਭੋਗਤਾ ਜੋੜੋ" + email_confirmed: "ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਹੋਈ" + email_not_confirmed: "ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋਈ" + vouchers: + legend: ਵਾਊਚਰ + voucher_code: ਵਾਊਚਰ ਕੋਡ + rate: ਦਰ + label: ਲੇਬਲ + purpose: ਉਦੇਸ਼ + expiry: ਸਮਾਪਤੀ + use_limit: ਵਰਤੋਂ/ਸੀਮਾ + customers: ਗਾਹਕ + net_value: ਸ਼ੁੱਧ ਮੁੱਲ + active: ਕਿਰਿਆਸ਼ੀਲ? + add_new: ਨਵਾਂ ਜੋੜੋ + no_voucher_yet: ਅਜੇ ਤੱਕ ਕੋਈ ਵਾਊਚਰ ਨਹੀਂ + white_label: + legend: "ਚਿੱਟਾ ਲੇਬਲ" + hide_ofn_navigation: "OFN ਨੈਵੀਗੇਸ਼ਨ ਲੁਕਾਓ" + upload_logo: "ਸ਼ੋਪਫ੍ਰੰਟ ਵਿੱਚ ਵਰਤਿਆ ਗਿਆ ਲੋਗੋ" + remove_logo: "ਲੋਗੋ ਹਟਾਓ" + remove_logo_confirm: "ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ ਤੇ ਇਸ ਲੋਗੋ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + remove_logo_success: "ਲੋਗੋ ਹਟਾਇਆ ਗਿਆ" + white_label_logo_link_label: "ਸ਼ੌਪਫਰੰਟ ਵਿੱਚ ਵਰਤੇ ਗਏ ਲੋਗੋ ਦਾ ਲਿੰਕ" + hide_groups_tab: "ਸ਼ਾਪਫਰੰਟ ਵਿੱਚ ਸਮੂਹ ਟੈਬ ਨੂੰ ਲੁਕਾਓ" + create_custom_tab: "ਸ਼ਾਪਫ੍ਰੰਟ ਵਿੱਚ ਕਸਟਮ ਟੈਬ ਬਣਾਓ" + custom_tab_title: "ਕਸਟਮ ਟੈਬ ਲਈ ਸਿਰਲੇਖ" + custom_tab_content: "ਕਸਟਮ ਟੈਬ ਲਈ ਕੰਟੇਂਟ" + connected_apps: + loading: "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + actions: + edit_profile: ਸੈਟਿੰਗਾਂ + properties: ਪ੍ਰਾਪਰਟੀਜ਼ + payment_methods: ਭੁਗਤਾਨ ਦੇ ਢੰਗ + payment_methods_tip: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਕੋਲ ਭੁਗਤਾਨ ਦੇ ਕੋਈ ਢੰਗ ਨਹੀਂ ਹਨ + shipping_methods: ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ + shipping_methods_tip: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਕੋਲ ਸ਼ਿਪਿੰਗ ਦੇ ਕੋਈ ਢੰਗ ਨਹੀਂ ਹਨ + enterprise_fees: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ + enterprise_fees_tip: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਕੋਈ ਫ਼ੀਸ ਨਹੀਂ ਹੈ + admin_index: + name: ਨਾਮ + role: ਭੂਮਿਕਾ + sells: ਵੇਚਦਾ ਹੈ + visible: ਦਿਸਣਯੋਗ? + owner: ਮਾਲਕ' + producer: ਉਤਪਾਦਕ + change_type_form: + producer_profile: ਉਤਪਾਦਕ ਪ੍ਰੋਫਾਈਲ' + connect_ofn: OFN ਦੁਆਰਾ ਕਨੇਕਟ ਕਰੋ + always_free: ਹਮੇਸ਼ਾ ਮੁਫ਼ਤ + producer_description_text: ਆਪਣੇ ਉਤਪਾਦਾਂ ਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਨਾਲ ਜੋੜੋ, ਹੱਬ ਨੂੰ ਉਹਨਾਂ ਦੇ ਸਟੋਰਾਂ ਵਿੱਚ ਤੁਹਾਡੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸਟਾਕ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੇ ਹੋਏ। + producer_shop: ਉਤਪਾਦਕ ਸ਼ਾਪ + sell_your_produce: ਆਪਣੀ ਖੁਦ ਦੀ ਪੈਦਾਵਾਰ ਵੇਚੋ + producer_shop_description_text: ਆਪਣੇ ਖੁਦ ਦੇ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਸ਼ਾਪਫਰੰਟ ਰਾਹੀਂ ਗਾਹਕਾਂ ਨੂੰ ਆਪਣੇ ਉਤਪਾਦ ਸਿੱਧੇ ਤੌਰ ਤੇ ਵੇਚੋ। + producer_shop_description_text2: ਇੱਕ ਉਤਪਾਦਕ ਸ਼ਾਪ ਸਿਰਫ਼ ਤੁਹਾਡੇ ਪੈਦਾਵਾਰ ਲਈ ਹੈ, ਜੇਕਰ ਤੁਸੀਂ ਕਿਥੇ ਹੋਰ ਉਗਾਏ/ਉਤਪਾਦਿਤ ਉਤਪਾਦ ਵੇਚਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ 'ਉਤਪਾਦਕ ਹੱਬ' ਦੀ ਚੋਣ ਕਰੋ। + producer_hub: ਉਤਪਾਦਕ ਹੱਬ + producer_hub_text: ਆਪਣੇ ਅਤੇ ਦੂਜਿਆਂ ਦੀ ਪੈਦਾਵਾਰ ਵੇਚੋ + producer_hub_description_text: ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਪ੍ਰਣਾਲੀ ਦੀ ਰੀੜ੍ਹ ਦੀ ਹੱਡੀ ਹੈ। ਤੁਸੀਂ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਆਪਣੇ ਸ਼ੌਪਫਰੰਟ ਰਾਹੀਂ ਆਪਣੀ ਖੁਦ ਦੀ ਪੈਦਾਵਾਰ ਦੇ ਨਾਲ-ਨਾਲ ਦੂਜੇ ਉਦਯੋਗਾਂ ਤੋਂ ਇਕੱਠੀ ਕੀਤੀ ਉਪਜ ਵੀ ਵੇਚ ਸਕਦੇ ਹੋ। + profile: ਸਿਰਫ਼ ਪ੍ਰੋਫਾਈਲ + get_listing: ਇੱਕ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰੋ + profile_description_text: ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਲੋਕ ਤੁਹਾਨੂੰ ਲੱਭ ਅਤੇ ਸੰਪਰਕ ਕਰ ਸਕਦੇ ਹਨ। ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਮੈਪ ਤੇ ਵਿਖਾਈ ਦੇਵੇਗਾ, ਅਤੇ ਸੂਚੀਆਂ ਵਿੱਚ ਖੋਜਣ ਯੋਗ ਹੋਵੇਗਾ। + hub_shop: ਹੱਬ ਸ਼ਾਪ + hub_shop_text: ਦੂਜਿਆਂ ਦੇ ਉਤਪਾਦ ਵੇਚੋ + hub_shop_description_text: ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਪ੍ਰਣਾਲੀ ਦੀ ਰੀੜ੍ਹ ਦੀ ਹੱਡੀ ਹੈ। ਤੁਸੀਂ ਦੂਜਿਆਂ ਉਦਯੋਗਾਂ ਦੀ ਪੈਦਾਵਾਰ ਇਕੱਠੇ ਕਰਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਆਪਣੀ ਸ਼ਾਪ ਰਾਹੀਂ ਵੇਚ ਸਕਦੇ ਹੋ। + choose_option: ਕਿਰਪਾ ਕਰਕੇ ਉਪਰੋਕਤ ਵਿਕਲਪਾਂ ਵਿੱਚੋਂ ਇੱਕ ਦੀ ਚੋਣ ਕਰੋ। + change_now: ਹੁਣੇ ਬਦਲੋ + enterprise_user_index: + loading_enterprises: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲੋਡ ਕਰ ਰਹੇ ਹਨ + no_enterprises_found: ਕੋਈ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨਹੀਂ ਮਿਲੇ। + search_placeholder: ਨਾਂ ਦੁਆਰਾ ਖੋਜ ਕਰੋ + manage: ਪ੍ਰਬੰਧਿਤ ਕਰੋ + manage_link: ਸੈਟਿੰਗਾਂ + producer?: "ਉਤਪਾਦਕ?" + package: "ਪੈਕੇਜ" + status: "ਸਥਿਤੀ" + new_form: + owner: ਮਾਲਕ' + owner_tip: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਜ਼ਿੰਮੇਵਾਰ ਪ੍ਰਾਥਮਿਕ ਉਪਭੋਗਤਾ। + i_am_producer: ਮੈਂ ਇੱਕ ਉਤਪਾਦਕ ਹਾਂ + contact_name: ਸੰਪਰਕ ਨਾਮ + edit: + editing: 'ਸੈਟਿੰਗਾਂ:''' + back_link: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੂਚੀ ਤੇ ਵਾਪਸ ਜਾਓ + new: + title: ਐਂਟਰਪ੍ਰਾਈਜ਼ਜ਼ + back_link: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੂਚੀ ਤੇ ਵਾਪਸ ਜਾਓ + welcome: + welcome_title: ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਵਿੱਚ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ! + welcome_text: ਤੁਸੀਂ ਸਫਲਤਾਪੂਰਵਕ ਇਹ ਬਣਾ ਲਿਆ ਹੈ + next_step: ਅਗਲਾ ਕਦਮ + choose_starting_point: 'ਆਪਣਾ ਪੈਕੇਜ ਚੁਣੋ:''' + profile: 'ਪ੍ਰੋਫਾਈਲ' + producer_profile: 'ਉਤਪਾਦਕ ਪ੍ਰੋਫਾਈਲ''' + invite_manager: + user_already_exists: "ਉਪਭੋਗਤਾ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ" + error: "ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ" + order_cycles: + loading_flash: + loading_order_cycles: ਆਰਡਰ ਸਾਈਕਲ ਲੋਡ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ + loading: ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ... + new: + create: "ਬਣਾਓ" + cancel: "ਰੱਦ ਕਰੋ" + back_to_list: "ਸੂਚੀ ਤੇ ਵਾਪਸ" + create: + success: 'ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ ਬਣਾਇਆ ਗਿਆ।''' + update: + success: 'ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ ਅੱਪਡੇਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।''' + clone: + success: "ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ %{name} ਕਲੋਨ ਕੀਤਾ ਗਿਆ ਹੈ।" + notify_producers: + success: 'ਨਿਰਮਾਤਾਵਾਂ ਨੂੰ ਭੇਜੀਆਂ ਜਾਣ ਵਾਲੀਆਂ ਈਮੇਲਾਂ ਨੂੰ ਭੇਜਣ ਲਈ ਕਤਾਰਬੱਧ ਕੀਤਾ ਗਿਆ ਹੈ।''' + edit: + save: "ਸੇਵ ਕਰੋ" + save_and_next: "ਸੇਵ ਕਰੋ ਅਤੇ ਅਗਲਾ" + next: "ਅਗਲਾ" + cancel: "ਰੱਦ ਕਰੋ" + back_to_list: "ਸੂਚੀ ਤੇ ਵਾਪਸ" + save_and_back_to_list: "ਸੇਵ ਕਰੋ ਅਤੇ ਸੂਚੀ ਵਿੱਚ ਵਾਪਸ" + choose_products_from: "ਇਥੋਂ ਉਤਪਾਦ ਚੁਣੋ:" + re_notify_producers: ਉਤਪਾਦਕਾਂ ਨੂੰ ਦੁਬਾਰਾ ਸੂਚਿਤ ਕਰੋ + notify_producers_tip: ਇਹ ਹਰੇਕ ਉਤਪਾਦਕ ਨੂੰ ਉਹਨਾਂ ਦੇ ਆਰਡਰ ਦੀ ਸੂਚੀ ਦੇ ਨਾਲ ਇੱਕ ਈਮੇਲ ਭੇਜੇਗਾ। + incoming: + incoming: "ਅੰਦਰ ਆਉਣ ਵਾਲੇ" + supplier: "ਸਪਲਾਇਰ" + products: "ਉਤਪਾਦ" + receival_details: "ਪ੍ਰਾਪਤੀ ਵੇਰਵੇ" + fees: "ਫ਼ੀਸ" + save: "ਸੇਵ ਕਰੋ" + save_and_next: "ਸੇਵ ਕਰੋ ਅਤੇ ਅਗਲਾ" + next: "ਅਗਲਾ" + cancel: "ਰੱਦ ਕਰੋ" + back_to_list: "ਸੂਚੀ ਤੇ ਵਾਪਸ" + outgoing: + outgoing: "ਬਾਹਰ ਜਾਣ ਵਾਲੇ" + distributor: "ਵਿਤਰਕ" + products: "ਉਤਪਾਦ" + tags: "ਟੈਗ" + delivery_details: "ਡਿਲਿਵਰੀ ਵੇਰਵੇ" + fees: "ਫ਼ੀਸ" + next: "ਅਗਲਾ" + previous: "ਪਿਛਲਾ" + save: "ਸੇਵ ਕਰੋ" + save_and_next: "ਸੇਵ ਕਰੋ ਅਤੇ ਅਗਲਾ" + cancel: "ਰੱਦ ਕਰੋ" + back_to_list: "ਸੂਚੀ ਤੇ ਵਾਪਸ" + checkout_options: + back_end: "ਸਿਰਫ ਬੈਕ ਆਫਿਸ" + cancel: "ਰੱਦ ਕਰੋ" + checkout_options: "ਚੈਕਆਊਟ ਵਿਕਲਪ" + distributor: "ਵਿਤਰਕ" + no_payment_methods: ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਤੇ ਹਰੇਕ ਵਿਤਰਕ ਨੂੰ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। + no_shipping_methods: ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਤੇ ਹਰੇਕ ਵਿਤਰਕ ਨੂੰ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। + payment_methods: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + save: "ਸੇਵ ਕਰੋ" + save_and_back_to_list: "ਸੇਵ ਕਰੋ ਅਤੇ ਸੂਚੀ ਵਿੱਚ ਵਾਪਸ" + select_all: "ਸਭ ਨੂੰ ਚੁਣੋ" + shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + wizard_progress: + edit: "1. ਆਮ ਸੈਟਿੰਗਾਂ" + incoming: "2. ਅੰਦਰ ਆਉਣ ਵਾਲੇ ਉਤਪਾਦ" + outgoing: "3. ਬਾਹਰ ਜਾਣ ਵਾਲੇ ਉਤਪਾਦ" + checkout_options: "4. ਚੈਕਆਊਟ ਵਿਕਲਪ" + exchange_form: + pickup_time_tip: ਜਦੋਂ ਇਸ OC ਤੋਂ ਆਰਡਰ ਗਾਹਕ ਲਈ ਤਿਆਰ ਹੋ ਜਾਣਗੇ + pickup_instructions_placeholder: "ਪਿਕ-ਅੱਪ ਹਦਾਇਤਾਂ" + pickup_instructions_tip: ਇਹ ਹਦਾਇਤਾਂ ਗਾਹਕਾਂ ਨੂੰ ਆਰਡਰ ਪੂਰਾ ਕਰਨ ਤੋਂ ਬਾਅਦ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ + pickup_time_placeholder: "ਇਸ ਲਈ ਤਿਆਰ (ਜਿਵੇਂ ਕਿ ਮਿਤੀ / ਸਮਾਂ)" + receival_instructions_placeholder: "ਪ੍ਰਾਪਤੀ ਨਿਰਦੇਸ਼" + add_fee: 'ਫ਼ੀਸ ਜੋੜੋ''' + remove: 'Remove' + selected: 'ਚੁਣੇ ਗਏ''' + add_exchange_form: + add_supplier: 'ਸਪਲਾਇਰ ਜੋੜੋ''' + add_distributor: 'ਵਿਤਰਕ ਜੋੜੋ''' + advanced_settings: + automatic_notifications: ਸਵੈਚਲਿਤ ਸੂਚਨਾਵਾਂ + automatic_notifications_tip: ਆਰਡਰ ਸਾਈਕਲ ਦੇ ਬੰਦ ਹੋਣ ਤੇ ਉਤਪਾਦਕਾਂ ਨੂੰ ਉਹਨਾਂ ਦੇ ਆਰਡਰਾਂ ਬਾਰੇ ਈਮੇਲ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ ਤੇ ਸੂਚਿਤ ਕਰੋ + title: ਉਨਤ ਸੈਟਿੰਗਾਂ + choose_product_tip: ਤੁਸੀਂ ਆਉਣ ਵਾਲੇ ਅਤੇ ਜਾਣ ਵਾਲੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸਿਰਫ਼ %{inventory} ਦੀ ਇਨਵੇਂਟਰੀ ਤੱਕ ਸੀਮਤ ਕਰ ਸਕਦੇ ਹੋ। + preferred_product_selection_from_coordinator_inventory_only_here: ਸਿਰਫ਼ ਕੋਆਰਡੀਨੇਟਰ ਦੀ ਇਨਵੇਂਟਰੀ + preferred_product_selection_from_coordinator_inventory_only_all: ਸਾਰੇ ਉਪਲਬਧ ਉਤਪਾਦ + save_reload: ਪੇਜ ਨੂੰ ਸੇਵ ਅਤੇ ਦੁਬਾਰਾ ਲੋਡ ਕਰੋ + order_cycle_top_buttons: + advanced_settings: "ਤੁਸੀਂ ਆਉਣ ਵਾਲੇ ਅਤੇ ਜਾਣ ਵਾਲੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸਿਰਫ਼ %{inventory} ਦੀ ਇਨਵੇਂਟਰੀ ਤੱਕ ਸੀਮਤ ਕਰ ਸਕਦੇ ਹੋ।" + coordinator_fees: + add: ਕੋਆਰਡੀਨੇਟਰ ਫੀਸ ਜੋੜੋ + filters: + search_by_order_cycle_name: "ਆਰਡਰ ਸਾਈਕਲ ਦੇ ਨਾਮ ਦੁਆਰਾ ਖੋਜੋ..." + involving: "ਸੰਮਲਿਤ" + any_enterprise: "ਕੋਈ ਵੀ ਐਂਟਰਪ੍ਰਾਈਜ਼" + any_schedule: "ਕੋਈ ਵੀ ਸ਼ੈਡਿਊਲ" + form: + general_settings: "ਆਮ ਸੈਟਿੰਗਾਂ" + incoming: ਅੰਦਰ ਆਉਣ ਵਾਲੇ + supplier: ਸਪਲਾਇਰ + products: ਉਤਪਾਦ + receival_details: ਪ੍ਰਾਪਤੀ ਵੇਰਵੇ + fees: ਫ਼ੀਸ + outgoing: ਬਾਹਰ ਜਾਣ ਵਾਲੇ + distributor: ਵਿਤਰਕ + tags: ਟੈਗ + add_a_tag: ਟੈਗ ਜੋੜੋ + delivery_details: ਪਿਕਅੱਪ / ਡਿਲਿਵਰੀ ਵੇਰਵੇ + index: + schedule: ਸਮਾਸੂਚੀ + schedules: ਸ਼ੈਡਿਊਲ + new_schedule: ਨਵਾਂ ਸ਼ੈਡਿਊਲ + new_schedule_tooltip: ਉਹ ਬਾਰੰਬਾਰਤਾ ਜਿਸ ਨਾਲ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰ ਦਿੱਤੇ ਜਾਂਦੇ ਹਨ + name_and_timing_form: + name: ਨਾਮ + orders_open: ਆਰਡਰ ਖੋਲ੍ਹਣ ਦਾ ਸਮਾਂ + coordinator: ਕੋਆਰਡੀਨੇਟਰ + orders_close: ਆਰਡਰ ਬੰਦ ਹੋ ਗਏ ਹਨ + row: + suppliers: ਸਪਲਾਇਰ + distributors: ਵਿਤਰਕ + variants: ਵੇਰੀਐਂਟਸ + simple_form: + ready_for: ਲਈ ਤਿਆਰ + ready_for_placeholder: ਮਿਤੀ/ਸਮਾਂ + customer_instructions: ਗਾਹਕ ਨਿਰਦੇਸ਼ + customer_instructions_placeholder: ਪਿਕ-ਅੱਪ ਜਾਂ ਡਿਲਿਵਰੀ ਨੋਟ + products: ਉਤਪਾਦ + fees: ਫ਼ੀਸ + tags: ਟੈਗ + destroy_errors: + orders_present: ਉਸ ਆਰਡਰ ਸਾਈਕਲ ਨੂੰ ਇੱਕ ਗਾਹਕ ਦੁਆਰਾ ਚੁਣਿਆ ਗਿਆ ਹੈ ਅਤੇ ਇਸਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ ਹੈ। ਗਾਹਕਾਂ ਨੂੰ ਇਸ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਤੋਂ ਰੋਕਣ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਬੰਦ ਕਰੋ। + schedule_present: ਉਹ ਆਰਡਰ ਸਾਈਕਲ ਇੱਕ ਸ਼ੈਡਿਊਲ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਅਤੇ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਪਹਿਲਾਂ ਸ਼ੈਡਿਊਲ ਤੋਂ ਲਿੰਕ ਤੋਂ ਹਟਾਓ ਜਾਂ ਡਿਲੀਟ ਕਰੋ। + bulk_update: + no_data: ਹਮ, ਕੁਝ ਗੜਬੜ ਹੋ ਗਈ। ਕੋਈ ਆਰਡਰ ਸਾਈਕਲ ਡੇਟਾ ਨਹੀਂ ਮਿਲਿਆ। + date_warning: + msg: ਇਹ ਆਰਡਰ ਸਾਈਕਲ %{n} ਓਪਨ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਆਰਡਰ ਨਾਲ ਜੁੜੀ ਹੋਈ ਹੈ। ਇਸ ਮਿਤੀ ਨੂੰ ਹੁਣ ਬਦਲਣ ਨਾਲ ਕਿਸੇ ਵੀ ਪਹਿਲਾਂ ਤੋਂ ਕੀਤੇ ਆਰਡਰ ਤੇ ਕੋਈ ਅਸਰ ਨਹੀਂ ਪਵੇਗਾ, ਪਰ ਜੇ ਸੰਭਵ ਹੋਵੇ ਤਾਂ ਇਸਤੋਂ ਬਚਣਾ ਚਾਹੀਦਾ ਹੈ। ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ ਉਤੇ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ? + cancel: ਰੱਦ ਕਰੋ + proceed: ਅੱਗੇ ਵਧੋ + status: + undated: ਮਿਤੀ ਤੋਂ ਬਿਨਾਂ + upcoming: ਜਲਦ ਆਉਣ ਵਾਲਾ + open: ਖੁਲਿਆ ਹੋਇਆ + closed: ਬੰਦ ਕੀਤਾ ਹੋਇਆ + producer_properties: + index: + title: ਉਤਪਾਦਕ ਪ੍ਰਾਪਰਟੀਜ਼ + proxy_orders: + cancel: + could_not_cancel_the_order: ਆਰਡਰ ਰੱਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ + resume: + could_not_resume_the_order: ਆਰਡਰ ਮੁੜ ਸ਼ੁਰੂ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ + select2: + minimal_search_length: ਕਿਰਪਾ ਕਰਕੇ %{count} ਜਾਂ ਜ਼ਿਆਦਾ ਅੱਖਰ ਦਾਖਲ ਕਰੋ + searching: ਖੋਜ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ... + no_matches: ਕੋਈ ਮੇਲ ਨਹੀਂ ਮਿਲਿਆ + shared: + user_guide_link: + user_guide: ਉਪਭੋਗਤਾ ਗਾਈਡ + enterprises_hubs_tabs: + has_no_payment_methods: "%{enterprise} ਦੇ ਕੋਲ ਕੋਈ ਭੁਗਤਾਨ ਤੇ ਤਰੀਕੇ ਨਹੀਂ ਹਨ" + has_no_shipping_methods: "%{enterprise} ਦੇ ਕੋਲ ਕੋਈ ਸ਼ਿਪਿੰਗ ਤੇ ਤਰੀਕੇ ਨਹੀਂ ਹਨ" + has_no_enterprise_fees: "%{enterprise} has no enterprise fees" + side_menu: + enterprise: + primary_details: "ਪ੍ਰਾਥਮਿਕ ਵੇਰਵੇ" + address: "ਪਤਾ" + contact: "ਸੰਪਰਕ" + social: "ਸਮਾਜਿਕ" + about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + business_details: "ਕਾਰੋਬਾਰੀ ਵੇਰਵੇ" + images: "ਫੋਟੋ" + properties: "ਪ੍ਰਾਪਰਟੀਜ਼" + shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + payment_methods: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + enterprise_fees: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ" + enterprise_permissions: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਅਨੁਮਤੀਆਂ" + inventory_settings: "ਇਨਵੇਂਟਰੀ ਸੈਟਿੰਗਾਂ" + tag_rules: "ਟੈਗ ਨਿਯਮ" + shop_preferences: "ਸ਼ਾਪ ਤਰਜੀਹਾਂ" + users: "ਉਪਭੋਗਤਾ" + vouchers: ਵਾਊਚਰ + white_label: "ਚਿੱਟਾ ਲੇਬਲ" + enterprise_group: + primary_details: "ਪ੍ਰਾਥਮਿਕ ਵੇਰਵੇ" + users: "ਉਪਭੋਗਤਾ" + about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + images: "ਫੋਟੋ" + contact: "ਸੰਪਰਕ" + web: "ਵੈਬ ਸੰਸਾਧਨ" + enterprise_issues: + create_new: ਨਵਾਂ ਬਣਾਓ + resend_email: ਈਮੇਲ ਦੁਬਾਰਾ ਭੇਜੋ + has_no_payment_methods: "%{enterprise} ਕੋਲ ਇਸ ਵੇਲੇ ਕੋਈ ਭੁਗਤਾਨ ਵਿਧੀ ਨਹੀਂ ਹੈ" + has_no_shipping_methods: "%{enterprise} ਕੋਲ ਵਰਤਮਾਨ ਵਿੱਚ ਕੋਈ ਵੀ ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ ਨਹੀਂ ਹਨ" + email_confirmation: "ਈਮੇਲ ਪੁਸ਼ਟੀਕਰਨ ਲੰਬਿਤ ਹੈ। ਅਸੀਂ %{email} ਨੂੰ ਇੱਕ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਭੇਜੀ ਹੈ।" + not_visible: "%{enterprise} ਦਿਖਾਈ ਨਹੀਂ ਦੇ ਰਿਹਾ ਹੈ ਅਤੇ ਇਸ ਲਈ ਮੈਪ ਤੇ ਜਾਂ ਖੋਜਾਂ ਵਿੱਚ ਨਹੀਂ ਲੱਭਿਆ ਜਾ ਸਕਦਾ" + reports: + deprecated: "ਇਹ ਰਿਪੋਰਟ ਬਰਤਰਫ਼ ਕੀਤੀ ਗਈ ਹੈ ਅਤੇ ਭਵਿੱਖ ਵਿੱਚ ਰੀਲੀਜ਼ ਵਿੱਚ ਹਟਾ ਦਿੱਤੀ ਜਾਵੇਗੀ।" + hidden: ਲੁਕਿਆ ਹੋਇਆ + unitsize: ਯੂਨਿਟ ਦਾ ਸਾਈਜ਼ + total: ਕੁੱਲ + total_items: ਕੁੱਲ ਆਈਟਮਾਂ + total_by_customer: ਗਾਹਕ ਦੁਆਰਾ ਕੁੱਲ + total_by_supplier: ਸਪਲਾਇਰ ਦੁਆਰਾ ਕੁੱਲ + supplier_totals: ਆਰਡਰ ਸਾਈਕਲ ਸਪਲਾਇਰ ਕੁੱਲ + percentage: "%{value} %" + supplier_totals_by_distributor: ਵਿਤਰਕ ਦੁਆਰਾ ਆਰਡਰ ਸਾਈਕਲ ਸਪਲਾਇਰ ਦਾ ਕੁੱਲ ਜੋੜ + totals_by_supplier: ਸਪਲਾਇਰ ਦੁਆਰਾ ਆਰਡਰ ਸਾਈਕਲ ਵਿਤਰਕ ਦਾ ਕੁੱਲ ਜੋੜ + customer_totals: ਆਰਡਰ ਸਾਈਕਲ ਗਾਹਕ ਕੁੱਲ ਜੋੜ + all_products: ਸਾਰੇ ਉਤਪਾਦ + inventory: ਇਨਵੇਂਟਰੀ (ਹੱਥ ਵਿਚ) + lettuce_share: ਲੈਟਯੂਸਸ਼ੇਅਰ + payment_methods: ਭੁਗਤਾਨ ਦੇ ਤਰੀਕੇ ਦੀ ਰਿਪੋਰਟ + delivery: ਡਿਲਿਵਰੀ ਰਿਪੋਰਟ + sales_tax_totals_by_producer: ਉਤਪਾਦਕ ਦੁਆਰਾ ਕੁੱਲ ਸੇਲ੍ਸ ਟੈਕਸ + sales_tax_totals_by_order: ਆਰਡਰ ਦੁਆਰਾ ਕੁੱਲ ਸੇਲ੍ਸ ਟੈਕਸ + tax_types: ਟੈਕਸ ਦੀਆਂ ਕਿਸਮਾਂ + tax_rates: ਟੈਕਸ ਦੀਆਂ ਦਰਾਂ + pack_by_customer: ਗਾਹਕ ਦੇ ਅਨੁਸਾਰ ਪੈਕ + pack_by_supplier: ਸਪਲਾਇਰ ਦੇ ਅਨੁਸਾਰ ਪੈਕ + pack_by_product: ਉਤਪਾਦ ਦੇ ਅਨੁਸਾਰ ਪੈਕ + download: + button: "ਰਿਪੋਰਟ ਡਾਊਨਲੋਡ ਕਰੋ" + show: + report_taking_longer: > + ਮਾਫ਼ ਕਰਨਾ, ਇਸ ਰਿਪੋਰਟ ਨੂੰ ਸੰਸਾਧਿਤ ਕਰਨ ਵਿੱਚ ਬਹੁਤ ਸਮਾਂ ਲੱਗਾ। ਇਸ ਵਿੱਚ ਬਹੁਤ + ਸਾਰਾ ਡੇਟਾ ਹੋ ਸਕਦਾ ਹੈ ਜਾਂ ਅਸੀਂ ਹੋਰ ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਰੁੱਝੇ ਹੋਏ ਹਾਂ। ਤੁਸੀਂ ਬਾਅਦ + ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰ ਸਕਦੇ ਹੋ। + report_taking_longer_html: > + ਇਸ ਰਿਪੋਰਟ ਨੂੰ ਸੰਸਾਧਿਤ ਹੋਣ ਵਿੱਚ ਜ਼ਿਆਦਾ ਸਮਾਂ ਲੱਗ ਰਿਹਾ ਹੈ। ਇਸ ਵਿੱਚ ਬਹੁਤ ਸਾਰਾ + ਡੇਟਾ ਹੋ ਸਕਦਾ ਹੈ ਜਾਂ ਅਸੀਂ ਹੋਰ ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਰੁੱਝੇ ਹੋਏ ਹਾਂ। ਇੱਕ ਵਾਰ ਜਦੋਂ + ਇਹ ਪੂਰਾ ਹੋ ਜਾਂਦਾ ਹੈ, ਅਸੀਂ ਤੁਹਾਨੂੰ ਈਮੇਲ ਰਾਹੀਂ ਸੂਚਿਤ ਕਰਾਂਗੇ। + report_link_label: ਰਿਪੋਰਟ ਡਾਊਨਲੋਡ ਕਰੋ (ਜਦੋਂ ਉਪਲਬਧ ਹੋਵੇ) + revenues_by_hub: + name: ਹੱਬ ਦੁਆਰਾ ਰੇਵਨ੍ਯੂ + description: ਹੱਬ ਦੁਆਰਾ ਰੇਵਨ੍ਯੂ + orders_and_distributors: + name: ਆਰਡਰ ਅਤੇ ਵਿਤਰਕ + description: ਵਿਤਰਕ ਵੇਰਵਿਆਂ ਦੇ ਨਾਲ ਆਰਡਰ + bulk_coop: + name: ਥੋਕ ਸਹਿਕਾਰਤਾ + description: ਥੋਕ ਸਹਿਕਾਰਤਾ ਆਰਡਰਾਂ ਲਈ ਰਿਪੋਰਟਾਂ + payments: + name: ਭੁਗਤਾਨ ਰਿਪੋਰਟਾਂ + description: ਭੁਗਤਾਨਾਂ ਲਈ ਰਿਪੋਰਟਾਂ + orders_and_fulfillment: + name: ਆਰਡਰ ਅਤੇ ਪੂਰਤੀ ਰਿਪੋਰਟਾਂ + customers: + name: ਗਾਹਕ + products_and_inventory: + name: ਉਤਪਾਦ ਅਤੇ ਇਨਵੇਂਟਰੀ + users_and_enterprises: + name: ਉਪਭੋਗਤਾ ਅਤੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ + description: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਮਲਕੀਅਤ ਅਤੇ ਸਥਿਤੀ + order_cycle_management: + name: ਆਰਡਰ ਸਾਈਕਲ ਪ੍ਰਬੰਧਨ + sales_tax: + name: ਸੇਲ੍ਸ ਟੈਕਸ + xero_invoices: + name: Xero ਦੇ ਇਨਵੌਇਸ + description: Xero ਵਿੱਚ ਇਮਪੋਰਟ ਲਈ ਇਨਵੌਇਸ + enterprise_fee_summary: + name: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ ਦਾ ਸਾਰ" + description: "ਇਕੱਠੀ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫੀਸਾਂ ਦਾ ਸਾਰ" + enterprise_fees_with_tax_report_by_order: "ਆਰਡਰ ਦੁਆਰਾ ਟੈਕਸ ਰਿਪੋਰਟ ਨਾਲ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫੀਸ" + enterprise_fees_with_tax_report_by_producer: "ਉਤਪਾਦਕ ਦੁਆਰਾ ਟੈਕਸ ਰਿਪੋਰਟ ਨਾਲ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫੀਸ" + errors: + no_report_type: "ਕਿਰਪਾ ਕਰਕੇ ਰਿਪੋਰਟ ਦੀ ਕਿਸਮ ਨਿਰਦੇਸ਼ਿਤ ਕਰੋ" + report_not_found: "ਰਿਪੋਰਟ ਨਹੀਂ ਮਿਲੀ" + missing_ransack_params: "ਕਿਰਪਾ ਕਰਕੇ ਬੇਨਤੀ ਵਿੱਚ ਰੈਨਸੈਕ ਖੋਜ ਪੇਰਾਮੀਟਰ ਦੀ ਸਪਲਾਈ ਕਰੋ" + hidden_field: "<ਲੁਕਿਆ ਹੋਇਆ >" + summary_row: + total: "ਕੁੱਲ" + table: + select_and_search: "ਫਿਲਟਰ ਚੁਣੋ ਅਤੇ ਆਪਣੇ ਡੇਟਾ ਤੱਕ ਪਹੁੰਚਣ ਲਈ %{option} ਤੇ ਕਲਿੱਕ ਕਰੋ।" + headings: + hub: "ਹੱਬ" + customer_code: "ਕੋਡ" + first_name: "ਪਹਿਲਾ ਨਾਂ" + last_name: "ਆਖਰੀ ਨਾਂ" + supplier: "ਸਪਲਾਇਰ" + product: "ਉਤਪਾਦ" + variant: "ਵੇਰੀਐਂਟ" + quantity: "ਮਾਤਰਾ" + is_temperature_controlled: "ਤਾਪਮਾਨ ਦਵਾਰਾ ਨਿਯੰਤ੍ਰਿਤ?" + temp_controlled: "ਤਾਪਮਾਨ ਦਵਾਰਾ ਨਿਯੰਤ੍ਰਿਤ?" + price: "ਕੀਮਤ" + rendering_options: + generate_report: "ਰਿਪੋਰਟ ਤਿਆਰ ਕਰੋ" + on_screen: "ਸਕ੍ਰੀਨ ਤੇ" + spreadsheet: "ਸਪ੍ਰੈਡਸ਼ੀਟ (ਐਕਸਲ, ਓਪਨਆਫਿਸ..)" + display: ਡਿਸਪਲੇ + summary_row: ਸਾਰਾਂਸ਼ ਕਤਾਰ + header_row: ਹੈਡਰ ਕਤਾਰ + raw_data: ਕੱਚਾ ਡੇਟਾ + formatted_data: ਫਾਰਮੈਟ ਕੀਤਾ ਡੇਟਾ + packing: + name: "ਪੈਕਿੰਗ ਦੀਆਂ ਰਿਪੋਰਟਾਂ" + oidc_settings: + index: + title: "OIDC ਸੈਟਿੰਗਾਂ" + connect: "ਆਪਣਾ ਖਾਤਾ ਕਨੈਕਟ ਕਰੋ" + les_communs_link: "ਲੇਸ ਕਮਿਊਨਜ਼ ਓਪਨ ਆਈਡੀ ਸਰਵਰ" + link_your_account: "ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ ਆਪਣੇ ਖਾਤੇ ਨੂੰ DFC (ਲੇਸ ਕਮਿਊਨਸ ਓਪਨ ਆਈਡੀ ਕਨੈਕਟ) ਦੁਆਰਾ ਵਰਤੇ ਗਏ ਅਧਿਕਾਰ ਪ੍ਰਦਾਤਾ ਨਾਲ ਲਿੰਕ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।" + link_account_button: "ਆਪਣੇ ਲੇਸ ਕਮਿਊਨਸ OIDC ਖਾਤੇ ਨੂੰ ਲਿੰਕ ਕਰੋ" + view_account: "ਆਪਣੇ ਖਾਤੇ ਨੂੰ ਵੇਖਣ ਲਈ, ਇੱਥੇ ਵੇਖੋ:" + subscriptions: + index: + title: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + new: "ਨਵੀਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + issue: "ਮੁੱਦੇ" + new: + title: "ਨਵੀਂ ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + edit: + title: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਸੰਪਾਦਿਤ ਕਰੋ" + table: + edit_subscription: ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਸੰਪਾਦਿਤ ਕਰੋ + pause_subscription: ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਵਿਰਾਮ ਦਿਓ + unpause_subscription: ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਮੁੜ ਕੇ ਸ਼ੁਰੂ ਕਰੋ + cancel_subscription: ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਰੱਦ ਕਰੋ + filters: + query_placeholder: "\"ਈਮੇਲ ਦੁਆਰਾ ਖੋਜੋ...\"" + setup_explanation: + title: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + just_a_few_more_steps: 'ਸ਼ੁਰੂ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕੁਝ ਹੋਰ ਕਦਮ:''' + enable_subscriptions: "ਤੁਹਾਡੀਆਂ ਸ਼ਾਪਾਂ ਵਿੱਚੋਂ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਲਈ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ" + enable_subscriptions_step_1_html: 1. %{enterprises_link} ਪੇਜ ਤੇ ਜਾਓ, ਆਪਣੀ ਸ਼ਾਪ ਲੱਭੋ, ਅਤੇ "ਪ੍ਰਬੰਧਿਤ ਕਰੋ" ਉਤੇ ਕਲਿੱਕ ਕਰੋ। + enable_subscriptions_step_2: 2. "ਸ਼ਾਪ ਤਰਜੀਹਾਂ" ਦੇ ਤਹਿਤ, ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਵਿਕਲਪ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ + set_up_shipping_and_payment_methods_html: '%{shipping_link} ਅਤੇ %{payment_link} ਦੇ ਤਰੀਕਿਆਂ ਨੂੰ ਸੇਟਅੱਪ ਕਰੋ' + set_up_shipping_and_payment_methods_note_html: ਨੋਟ ਕਰੋ ਕਿ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨਾਲ ਸਿਰਫ਼ ਨਕਦ ਅਤੇ ਸਟ੍ਰਾਈਪ ਭੁਗਤਾਨ ਦੇ ਢੰਗ
ਵਰਤੀਆਂ ਜਾ ਸਕਦੀਆਂ ਹਨ + ensure_at_least_one_customer_html: ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਘੱਟੋ-ਘੱਟ ਇੱਕ %{customer_link} ਮੌਜੂਦ ਹੈ + create_at_least_one_schedule: ਘੱਟੋ-ਘੱਟ ਇੱਕ ਸ਼ੈਡਿਊਲ ਬਣਾਓ + create_at_least_one_schedule_step_1_html: 1. %{order_cycles_link} ਪੇਜ ਤੇ ਜਾਓ + create_at_least_one_schedule_step_2: 2. ਜੇਕਰ ਤੁਸੀਂ ਪਹਿਲਾਂ ਹੀ ਅਜਿਹਾ ਨਹੀਂ ਕੀਤਾ ਹੈ ਤਾਂ ਇੱਕ ਆਰਡਰ ਸਾਈਕਲ ਬਣਾਓ + create_at_least_one_schedule_step_3: 3. '+ ਨਵੇਂ ਸ਼ਡਿਊਲ' ਉਤੇ ਕਲਿੱਕ ਕਰੋ, ਅਤੇ ਫਾਰਮ ਭਰੋ + once_you_are_done_you_can_html: ਇੱਕ ਵਾਰ ਜਦੋਂ ਤੁਸੀਂ ਪੂਰਾ ਕਰ ਲੈਂਦੇ ਹੋ, ਤੁਸੀਂ %{reload_this_page_link} ਕਰ ਸਕਦੇ ਹੋ + reload_this_page: ਇਸ ਪੇਜ ਨੂੰ ਮੁੜ ਲੋਡ ਕਰੋ + form: + create: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਬਣਾਓ" + steps: + details: 1. ਬੁਨਿਆਦੀ ਵੇਰਵੇ + address: 2. ਪਤਾ + products: 3. ਉਤਪਾਦ ਜੋੜੋ + review: 4. ਸਮੀਖਿਆ ਕਰੋ ਅਤੇ ਸੇਵ ਕਰੋ + subscription_line_items: + this_is_an_estimate: | + ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਮਤਾਂ ਸਿਰਫ ਇੱਕ ਅਨੁਮਾਨ ਹਨ ਅਤੇ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਬਦਲਣ ਦੇ ਸਮੇਂ ਦੀ ਗਣਨਾ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਕੀਮਤਾਂ ਜਾਂ ਫ਼ੀਸਾਂ ਨੂੰ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਆਰਡਰ ਅੱਪਡੇਟ ਕੀਤੇ ਜਾਣਗੇ, ਪਰ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਅਜੇ ਵੀ ਪੁਰਾਣੇ ਮੁੱਲਾਂ ਨੂੰ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੇਗੀ। + not_in_open_and_upcoming_order_cycles_warning: "ਇਸ ਉਤਪਾਦ ਲਈ ਕੋਈ ਓਪਨ ਜਾਂ ਆਗਾਮੀ ਆਰਡਰ ਸਾਈਕਲ ਨਹੀਂ ਹਨ।" + autocomplete: + name_or_sku: "ਨਾਂ ਜਾਂ SKU" + quantity: "ਮਾਤਰਾ" + add: "ਜੋੜੋ" + details: + details: ਮਾਤਰਾ + invalid_error: ਓਹ! ਕਿਰਪਾ ਕਰਕੇ ਸਾਰੇ ਲੋੜੀਂਦੇ ਖੇਤਰਾਂ ਨੂੰ ਭਰੋ... + allowed_payment_method_types_tip: ਇਸ ਸਮੇਂ ਸਿਰਫ਼ ਨਕਦ ਅਤੇ ਸਟ੍ਰਾਈਪ ਭੁਗਤਾਨ ਦੇ ਢੰਗਾਂ ਦੀ ਵਰਤੋਂ ਹੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ + credit_card: ਕਰੇਡਿਟ ਕਾਰਡ + charges_not_allowed: ਇਸ ਗਾਹਕ ਦੁਆਰਾ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਤੇ ਪੈਸੇ ਕੱਟੇ ਜਾਣ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ + no_default_card: ਗਾਹਕ ਕੋਲ ਚਾਰਜ ਕਰਨ ਲਈ ਕੋਈ ਕਾਰਡ ਉਪਲਬਧ ਨਹੀਂ ਹਨ + card_ok: ਗਾਹਕ ਕੋਲ ਚਾਰਜ ਕਰਨ ਲਈ ਇੱਕ ਕਾਰਡ ਉਪਲਬਧ ਹੈ + begins_at_placeholder: "ਇੱਕ ਮਿਤੀ ਚੁਣੋ" + ends_at_placeholder: "ਵਿਕਲਪਿਕ" + loading_flash: + loading: ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਲੋਡ ਹੋ ਰਹੀ ਹੈ + review: + details: ਮਾਤਰਾ + address: ਪਤਾ + products: ਉਤਪਾਦ + no_open_or_upcoming_order_cycle: "ਕੋਈ ਆਗਾਮੀ ਆਰਡਰ ਸਾਈਕਲ ਨਹੀਂ" + products_panel: + save: "ਸੇਵ ਕਰੋ" + saving: "ਬਚਤ ਕਰ ਰਹੇ ਹਾਂ" + saved: "ਸੇਵ ਕੀਤਾ ਗਿਆ" + product_already_in_order: ਇਸ ਉਤਪਾਦ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਆਰਡਰ ਵਿੱਚ ਜੋੜਿਆ ਜਾ ਚੁੱਕਾ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਮਾਤਰਾ ਨੂੰ ਸਿੱਧਾ ਸੰਪਾਦਿਤ ਕਰੋ। + stock: + insufficient_stock: "ਨਾਕਾਫ਼ੀ ਸਟਾਕ ਉਪਲਬਧ" + out_of_stock: "ਸਟਾਕ ਵਿੱਚ ਨਹੀਂ ਹੈਂ" + orders: + number: ਨੰਬਰ + confirm_edit: ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇਸ ਆਰਡਰ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਅਜਿਹਾ ਕਰਨ ਨਾਲ ਭਵਿੱਖ ਵਿੱਚ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ ਤੇ ਸਿੰਕ ਕਰਨਾ ਜ਼ਿਆਦਾ ਮੁਸ਼ਕਲ ਹੋ ਸਕਦਾ ਹੈ। + confirm_cancel_msg: "ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇਸ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਰੱਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਸ ਕਾਰਵਾਈ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਲਿੱਤਾ ਜਾ ਸਕਦਾ।" + cancel_failure_msg: "Sorry, cancellation failed!" + confirm_pause_msg: "Are you sure you want to pause this subscription?" + pause_failure_msg: "ਮੁਆਫ਼ ਕਰਨਾ, ਵਿਰਾਮ ਅਸਫਲ ਰਿਹਾ!" + confirm_unpause_msg: "ਜੇਕਰ ਤੁਹਾਡੇ ਕੋਲ ਇਸ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਦੇ ਸ਼ਡਿਊਲ ਵਿੱਚ ਇੱਕ ਖੁੱਲਾ ਆਰਡਰ ਸਾਈਕਲ ਹੈ, ਤਾਂ ਇਸ ਗਾਹਕ ਲਈ ਇੱਕ ਆਰਡਰ ਬਣਾਇਆ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਇਸ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਬੰਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?" + unpause_failure_msg: "ਮਾਫ਼ ਕਰਨਾ, ਵਿਰਾਮ ਤੋਂ ਬਾਹਰ ਕੱਢਣਾ ਅਸਫਲ ਰਿਹਾ!" + confirm_cancel_open_orders_msg: "ਇਸ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਲਈ ਕੁਝ ਆਰਡਰ ਇਸ ਸਮੇਂ ਖੁੱਲ੍ਹੇ ਹਨ। ਗਾਹਕ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਸੂਚਿਤ ਕੀਤਾ ਗਿਆ ਹੈ ਕਿ ਆਰਡਰ ਲਿੱਤਾ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਇਸ ਆਰਡਰ (ਇਹਨਾਂ ਆਰਡਰਾਂ) ਨੂੰ ਰੱਦ ਕਰਨਾ ਚਾਹੋਗੇ ਜਾਂ ਉਹਨਾਂ ਨੂੰ ਰੱਖਣਾ ਚਾਹੋਗੇ?" + resume_canceled_orders_msg: "ਇਸ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਲਈ ਕੁਝ ਆਰਡਰ ਹੁਣੇ ਮੁੜ ਤੋਂ ਸ਼ੁਰੂ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਤੁਸੀਂ ਉਹਨਾਂ ਨੂੰ ਆਰਡਰ ਡ੍ਰੌਪਡਾਊਨ ਤੋਂ ਮੁੜ-ਸ਼ੁਰੂ ਕਰ ਸਕਦੇ ਹੋ।" + yes_cancel_them: ਉਹਨਾਂ ਨੂੰ ਰੱਦ ਕਰੋ + no_keep_them: ਉਹਨਾਂ ਨੂੰ ਰੱਖੋ + yes_i_am_sure: ਹਾਂ, ਮੈਨੂੰ ਯਕੀਨ ਹੈ + number: "ਨੰਬਰ" + order_update_issues_msg: ਕੁਝ ਆਰਡਰ ਸਵੈਚਲਿਤ ਤੌਰ ਤੇ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ, ਇਹ ਸਭ ਤੋਂ ਵੱਧ ਸੰਭਾਵਨਾ ਹੈ ਕਿਉਂਕਿ ਉਹਨਾਂ ਨੂੰ ਹੱਥੀਂ ਸੰਪਾਦਿਤ ਕੀਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਹੇਠਾਂ ਸੂਚੀਬੱਧ ਮੁੱਦਿਆਂ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਅਤੇ ਲੋੜ ਪੈਣ ਤੇ ਵਿਅਕਤੀਗਤ ਆਰਡਰਾਂ ਲਈ ਕੋਈ ਵੀ ਸਮਾਯੋਜਨ ਕਰੋ। + no_results: + no_subscriptions: ਅਜੇ ਤੱਕ ਕੋਈ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨਹੀਂ... + why_dont_you_add_one: ਤੁਸੀਂ ਇੱਕ ਕਿਉਂ ਨਹੀਂ ਜੋੜਦੇ? :) + no_matching_subscriptions: ਕੋਈ ਮੇਲ ਖਾਂਦੀ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨਹੀਂ ਮਿਲੀ + schedules: + destroy: + associated_subscriptions_error: ਇਸ ਸ਼ਡਿਊਲ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ ਹੈ ਕਿਉਂਕਿ ਇਸ ਵਿੱਚ ਸੰਬੰਧਿਤ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਹਨ + vouchers: + new: + legend: ਨਵਾਂ ਵਾਊਚਰ + back: ਵਾਪਸ + save: ਸੇਵ ਕਰੋ + voucher_code: ਵਾਊਚਰ ਕੋਡ + voucher_amount: ਰਕਮ + voucher_type: ਵਾਊਚਰ ਦੀ ਕਿਸਮ + flat_rate: ਫਲੈਟ + percentage_rate: ਪ੍ਰਤੀਸ਼ਤ (%) + controllers: + enterprises: + stripe_connect_cancelled: "ਸਟ੍ਰਾਈਪ ਨਾਲ ਕਨੇਕਸ਼ਨ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ" + stripe_connect_success: "ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਸਫ਼ਲਤਾਪੂਰਵਕ ਕਨੇਕਟ ਕੀਤਾ ਗਿਆ" + stripe_connect_fail: ਮਾਫ਼ ਕਰਨਾ, ਤੁਹਾਡੇ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਦਾ ਕਨੇਕਸ਼ਨ ਅਸਫਲ ਰਿਹਾ + stripe_connect_settings: + resource: ਸਟ੍ਰਾਈਪ ਕਨੇਕਟ ਕੌਂਫਿਗਰੇਸ਼ਨ + api: + unknown_error: "ਕੁਝ ਗੜਬੜ ਹੋ ਗਈ ਹੈ। ਸਾਡੀ ਟੀਮ ਨੂੰ ਸੂਚਿਤ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।" + invalid_api_key: "ਅਵੈਧ API ਕੁੰਜੀ (%{key}) ਨਿਰਧਾਰਤ ਕੀਤੀ ਗਈ।" + unauthorized: "ਤੁਸੀਂ ਉਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ।" + unpermitted_parameters: "ਇਸ ਬੇਨਤੀ ਵਿੱਚ ਪੈਰਾਮੀਟਰਾਂ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ: %{params}" + missing_parameter: "ਇੱਕ ਲੋੜੀਂਦਾ ਪੈਰਾਮੀਟਰ ਗੁੰਮ ਜਾਂ ਖਾਲੀ ਹੈ: %{param}" + invalid_resource: "ਅਵੈਧ ਸਰੋਤ। ਕਿਰਪਾ ਕਰਕੇ ਗਲਤੀਆਂ ਠੀਕ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + resource_not_found: "ਜਿਸ ਸਰੋਤ ਨੂੰ ਤੁਸੀਂ ਲੱਭ ਰਹੇ ਸੀ, ਉਹ ਲੱਭਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।" + enterprise_logo: + destroy_attachment_does_not_exist: "ਲੋਗੋ ਮੌਜੂਦ ਨਹੀਂ ਹੈ" + enterprise_promo_image: + destroy_attachment_does_not_exist: "ਪ੍ਰੋਮੋ ਫੋਟੋ ਮੌਜੂਦ ਨਹੀਂ ਹੈ" + enterprise_terms_and_conditions: + destroy_attachment_does_not_exist: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਦੀ ਫ਼ਾਈਲ ਮੌਜੂਦ ਨਹੀਂ ਹੈ" + orders: + failed_to_update: "ਆਰਡਰ ਅੱਪਡੇਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲ" + query_param: + error: + title: ਅਵੈਧ ਪੁੱਛਗਿੱਛ ਪੈਰਾਮੀਟਰ + extra_fields: "ਅਸਮਰਥਿਤ ਖੇਤਰ: %{fields}" + checkout: + failed: "ਚੈਕਆਊਟ ਅਸਫਲ ਰਿਹਾ। ਕਿਰਪਾ ਕਰਕੇ ਸਾਨੂੰ ਦੱਸੋ ਤਾਂ ਜੋ ਅਸੀਂ ਤੁਹਾਡੇ ਆਰਡਰ ਨੂੰ ਸੰਸਾਧਿਤ ਕਰ ਸਕੀਏ।" + payment_cancelled_due_to_stock: "ਭੁਗਤਾਨ ਰੱਦ ਕੀਤਾ ਗਿਆ: ਸਟਾਕ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਕਾਰਨ ਚੈਕਆਉਟ ਪੂਰਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।" + order_not_loaded: "ਚੈਕਆਉਟ ਸੰਸਾਧਨ ਲਈ ਕੋਈ ਵੈਧ ਆਰਡਰ ਨਹੀਂ ਮਿਲਿਆ" + your_details_without_number: ਤੁਹਾਡੇ ਵੇਰਵੇ + payment_method_without_number: ਭੁਗਤਾਨੇ ਦੇ ਢੰਗ + order_summary_without_number: ਆਰਡਰ ਸੰਖੇਪ + already_ordered: + cart: "ਕਾਰਟ" + message_html: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ ਤੁਹਾਡੇ ਕੋਲ ਪਹਿਲਾਂ ਹੀ ਇੱਕ ਆਰਡਰ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਪਹਿਲਾਂ ਆਰਡਰ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਵੇਖਣ ਲਈ %{cart} ਨੂੰ ਚੈਕ ਕਰੋ। ਜਦੋਂ ਤੱਕ ਆਰਡਰ ਸਾਈਕਲ ਖੁੱਲ੍ਹਾ ਹੈ, ਤੁਸੀਂ ਆਈਟਮਾਂ ਨੂੰ ਰੱਦ ਵੀ ਕਰ ਸਕਦੇ ਹੋ।" + step1: + contact_information: + title: ਸੰਪਰਕ ਜਾਣਕਾਰੀ + email: + label: ਈਮੇਲ + phone: + label: ਫੋਨ ਨੰਬਰ + billing_address: + title: ਬਿਲਿੰਗ ਪਤਾ + first_name: + label: ਪਹਿਲਾ ਨਾਂ + last_name: + label: ਆਖਰੀ ਨਾਂ + address: + address1: + label: पता (गली + मकान नंबर) + address2: + label: ਵਾਧੂ ਪਤੇ ਦੀ ਜਾਣਕਾਰੀ (ਵਿਕਲਪਿਕ) + city: + label: ਸ਼ਹਿਰ + state_id: + label: ਸਥਿਤੀ + zipcode: + label: ਪਿੰਨ ਕੋਡ + country_id: + label: ਦੇਸ਼ + shipping_info: + title: ਸ਼ਿਪਿੰਗ ਜਾਣਕਾਰੀ + submit: ਅਗਲਾ - ਭੁਗਤਾਨ ਦਾ ਢੰਗ + cancel: ਬਾਸਕੇਟ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ ਉਤੇ ਵਾਪਸ + step2: + payment_method: + title: ਭੁਗਤਾਨੇ ਦੇ ਢੰਗ + form: + card_number: + label: ਕਾਰਡ ਨੰਬਰ + placeholder: ਜਿਵੇਂ ਕਿ 4242 4242 4242 4242 + card_verification_value: + label: CVC + card_month: + label: ਮਹੀਨਾ + card_year: + label: ਸਾਲ + stripe: + use_saved_card: ਸੇਵ ਕੀਤੇ ਕਾਰਡਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ + use_new_card: ਆਪਣੇ ਕਾਰਡ ਦੀ ਪਛਾਣ ਭਰੋ + save_card: ਭਵਿੱਖ ਦੀ ਵਰਤੋਂ ਲਈ ਕਾਰਡ ਸੇਵ ਕਰੋ + create_new_card: ਜਾਂ ਹੇਠਾਂ ਨਵੇਂ ਕਾਰਡ ਦੇ ਵੇਰਵੇ ਭਰੋ + explaination: ਤੁਸੀਂ ਅਗਲੇ ਪੜਾਅ ਵਿੱਚ ਆਪਣੇ ਆਰਡਰ ਦੀ ਸਮੀਖਿਆ ਅਤੇ ਪੁਸ਼ਟੀ ਕਰ ਸਕਦੇ ਹੋ ਜਿਸ ਵਿੱਚ ਅੰਤਿਮ ਲਾਗਤਾਂ ਸ਼ਾਮਲ ਹਨ। + submit: ਆਰਡਰ ਸੰਖੇਪ + cancel: ਤੁਹਾਡੇ ਵੇਰਵਿਆਂ ਤੇ ਵਾਪਸ + voucher: + voucher: "%{voucher_amount} ਵਾਊਚਰ" + apply_voucher: ਵਾਊਚਰ ਲਾਗੂ ਕਰੋ + apply: ਲਾਗੂ ਕਰੋ + placeholder: ਵਾਊਚਰ ਕੋਡ ਦਾਖਲ ਕਰੋ + remove_code: ਕੋਡ ਹਟਾਓ + confirm_delete: ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਵਾਉਚਰ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ? + warning_forfeit_remaining_amount: "ਨੋਟ: ਜੇਕਰ ਤੁਹਾਡੇ ਆਰਡਰ ਦੀ ਕੁੱਲ ਰਕਮ ਤੁਹਾਡੇ ਵਾਊਚਰ ਤੋਂ ਘੱਟ ਹੈ ਤਾਂ ਤੁਸੀਂ ਬਾਕੀ ਮੁੱਲ ਨੂੰ ਖਰਚ ਕਰਨ ਦੇ ਯੋਗ ਨਹੀਂ ਹੋ ਸਕੋਗੇ।" + step3: + delivery_details: + title: ਡਿਲਿਵਰੀ ਵੇਰਵੇ + edit: ਸੰਪਾਦਿਤ ਕਰੋ + address: ਡਿਲਿਵਰੀ ਪਤਾ + instructions: ਹਦਾਇਤਾਂ + payment_method: + title: ਭੁਗਤਾਨੇ ਦੇ ਢੰਗ + edit: ਸੰਪਾਦਿਤ ਕਰੋ + instructions: ਹਦਾਇਤਾਂ + order: + title: ਆਰਡਰ ਵੇਰਵੇ + edit: ਸੰਪਾਦਿਤ ਕਰੋ + terms_and_conditions: + message_html: "ਮੈਂ ਵਿਕਰੇਤਾ ਦੇ %{terms_and_conditions_link} ਨਾਲ ਸਹਿਮਤ ਹਾਂ।" + link_text: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ" + platform_terms_of_service: + message_html: "ਮੈਂ ਪਲੇਟਫਾਰਮ %{tos_link} ਨਾਲ ਸਹਿਮਤ ਹਾਂ।" + all_terms_and_conditions: + message_html: "ਮੈਂ ਵਿਕਰੇਤਾ ਦੇ %{terms_and_conditions_link} ਅਤੇ ਪਲੇਟਫਾਰਮ %{tos_link} ਨਾਲ ਸਹਿਮਤ ਹਾਂ।" + terms_and_conditions: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ" + submit: ਪੂਰਾ ਆਰਡਰ + cancel: ਭੁਗਤਾਨ ਦੇ ਤਰੀਕੇ ਤੇ ਵਾਪਸ + errors: + saving_failed: "ਸੇਵ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ, ਕਿਰਪਾ ਕਰਕੇ ਹਾਈਲਾਈਟ ਕੀਤੇ ਖੇਤਰਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ।" + terms_not_accepted: ਕਿਰਪਾ ਕਰਕੇ ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਸਵੀਕਾਰ ਕਰੋ + required: ਖੇਤਰ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ + invalid_number: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਵੈਧ ਫੋਨ ਨੰਬਰ ਭਰੋ" + invalid_email: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਵੈਧ ਈਮੇਲ ਪਤਾ ਭਰੋ" + select_a_shipping_method: ਇੱਕ ਸ਼ਿਪਿੰਗ ਦਾ ਢੰਗ ਚੁਣੋ + select_a_payment_method: ਇੱਕ ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ ਚੁਣੋ + no_shipping_methods_available: ਸ਼ਿਪਿੰਗ ਵਿਕਲਪਾਂ ਦੀ ਅਣਹੋਂਦ ਕਾਰਨ ਚੈਕਆਉਟ ਸੰਭਵ ਨਹੀਂ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਸ਼ਾਪ ਦੇ ਮਾਲਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ। + voucher_not_found: ਨਹੀਂ ਲਭਿਆ + add_voucher_error: ਵਾਊਚਰ ਜੋੜਦੇ ਸਮੇਂ ਇੱਕ ਤਰੁੱਟੀ ਉਤਪੰਨ ਹੋਈ + shops: + hubs: + show_closed_shops: "ਬੰਦ ਸ਼ਾਪਾਂ ਵਿਖਾਓ" + hide_closed_shops: "ਬੰਦ ਸ਼ਾਪਾਂ ਲੁਕਾਓ" + show_on_map: "ਮੈਪ ਤੇ ਸਭ ਵਿਖਾਓ" + shared: + mailers: + powered_by: + open_food_network: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ" + powered_html: "ਤੁਹਾਡਾ ਖਰੀਦਦਾਰੀ ਅਨੁਭਵ %{open_food_network} ਦੁਆਰਾ ਸੰਚਾਲਿਤ ਹੈ।" + menu: + cart: + cart: "ਕਾਰਟ" + cart_sidebar: + checkout: "ਚੈਕਆਊਟ" + edit_cart: "ਕਾਰਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ" + items_in_cart_singular: "ਤੁਹਾਡੀ ਕਾਰਟ ਵਿੱਚ %{num} ਆਈਟਮ ਹੈ" + items_in_cart_plural: "ਤੁਹਾਡੀ ਕਾਰਟ ਵਿੱਚ %{num} ਆਈਟਮਾਂ ਹਨ" + close: "ਬੰਦ" + cart_empty: "ਤੁਹਾਡਾ ਕਾਰਟ ਖਾਲੀ ਹੈ" + take_me_shopping: "ਮੈਨੂੰ ਖਰੀਦਦਾਰੀ ਕਰਨ ਲਈ ਲੈ ਚਲੋ!" + signed_in: + profile: "ਪ੍ਰੋਫਾਈਲ" + mobile_menu: + cart: "ਕਾਰਟ" + register_call: + selling_on_ofn: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਆਉਣ ਵਿੱਚ ਦਿਲਚਸਪੀ ਰੱਖਦੇ ਹੋ?" + register: "ਇੱਥੇ ਰਜਿਸਟਰ ਕਰੋ" + footer: + footer_secure: "ਸੁਰੱਖਿਅਤ ਅਤੇ ਭਰੋਸੇਮੰਦ." + footer_secure_text: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੁਹਾਡੀ ਖਰੀਦਦਾਰੀ ਅਤੇ ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ ਨੂੰ ਗੁਪਤ ਰੱਖਣ ਲਈ ਹਰ ਥਾਂ SSL ਐਨਕ੍ਰਿਪਸ਼ਨ (2048 ਬਿੱਟ RSA) ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਸਾਡੇ ਸਰਵਰ ਤੁਹਾਡੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਦੇ ਵੇਰਵਿਆਂ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕਰਦੇ ਹਨ ਅਤੇ ਭੁਗਤਾਨਾਂ ਨੂੰ PCI-ਅਨੁਕੂਲ ਸੇਵਾਵਾਂ ਦੁਆਰਾ ਸੰਸਾਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।" + footer_contact_headline: "ਸੰਪਰਕ ਵਿੱਚ ਰਹੋ" + footer_contact_email: "ਸਾਨੂੰ ਈਮੇਲ ਕਰੋ" + footer_nav_headline: "ਨੇਵੀਗੇਟ" + footer_join_headline: "ਸਾਡੇ ਨਾਲ ਜੁੜੋ" + footer_join_body: "ਓਪਨ ਫੂਡ ਨੈੱਟਵਰਕ ਉਤੇ ਇੱਕ ਸੂਚੀ, ਸ਼ਾਪ ਜਾਂ ਸਮੂਹ ਡਾਇਰੈਕਟਰੀ ਬਣਾਓ।" + footer_join_cta: "ਮੈਨੂੰ ਹੋਰ ਦੱਸੋ!" + footer_legal_call: "ਸਾਡਾ ਇਹ ਪੜ੍ਹੋ" + footer_legal_visit: "ਸਾਨੂੰ ਇਥੇ ਲੱਭੋ" + footer_legal_text_html: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਇੱਕ ਮੁਫਤ ਅਤੇ ਓਪਨ ਸੋਰਸ ਸੌਫਟਵੇਅਰ ਪਲੇਟਫਾਰਮ ਹੈ। ਸਾਡੇ ਕੰਟੇਂਟ ਨੂੰ %{content_license} ਅਤੇ ਸਾਡੇ ਕੋਡ ਨੂੰ %{code_license} ਤੋਂ ਲਾਇਸੈਂਸ ਪ੍ਰਾਪਤ ਹੈ।" + footer_data_text_with_privacy_policy_html: "ਅਸੀਂ ਤੁਹਾਡੇ ਡੇਟਾ ਦੀ ਚੰਗੀ ਤਰ੍ਹਾਂ ਦੇਖਭਾਲ ਕਰਦੇ ਹਾਂ। ਸਾਡੀ %{privacy_policy} ਅਤੇ %{cookies_policy}\" ਵੇਖੋ।" + footer_data_text_without_privacy_policy_html: "ਅਸੀਂ ਤੁਹਾਡੇ ਡੇਟਾ ਦੀ ਚੰਗੀ ਤਰ੍ਹਾਂ ਦੇਖਭਾਲ ਕਰਦੇ ਹਾਂ। ਸਾਡੀ %{cookies_policy} ਵੇਖੋ" + footer_data_privacy_policy: "ਪ੍ਰਾਈਵੇਸੀ ਪਾਲਿਸੀ" + footer_data_cookies_policy: "ਕੂਕੀਜ਼ ਪਾਲਿਸੀ" + shop: + messages: + customer_required: + login: "ਲਾਗਇਨ" + contact: "ਸੰਪਰਕ" + require_customer_login: "ਸਿਰਫ਼ ਮਨਜ਼ੂਰਸ਼ੁਦਾ ਗਾਹਕ ਹੀ ਇਸ ਸ਼ਾਪ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੇ ਹਨ।" + require_login_link_html: "ਜੇ ਤੁਸੀਂ ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਮਨਜ਼ੂਰਸ਼ੁਦਾ ਗਾਹਕ ਹੋ, ਤਾਂ ਅੱਗੇ ਵਧਣ ਲਈ %{login} ਕਰੋ।" + require_login_2_html: "ਇੱਥੇ ਖਰੀਦਦਾਰੀ ਸ਼ੁਰੂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਕਿਰਪਾ ਕਰਕੇ %{contact} %{enterprise} ਅਤੇ ਸ਼ਾਮਲ ਹੋਣ ਬਾਰੇ ਪੁੱਛੋ।" + require_customer_html: "ਜੇ ਤੁਸੀਂ ਇੱਥੇ ਖਰੀਦਦਾਰੀ ਸ਼ੁਰੂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸ਼ਾਮਲ ਹੋਣ ਬਾਰੇ ਪੁੱਛਣ ਲਈ %{contact} %{enterprise} ਨੂੰ ਪੁੱਛੋ।" + products: + summary: + bulk: "ਥੋਕ" + card_could_not_be_updated: ਕਾਰਡ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ + card_could_not_be_saved: ਕਾਰਡ ਨੂੰ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ + spree_gateway_error_flash_for_checkout: "ਤੁਹਾਡੀ ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ ਵਿੱਚ ਇੱਕ ਸਮੱਸਿਆ ਸੀ: %{error}" + invoice_billing_address: "ਬਿਲਿੰਗ ਪਤਾ:" + invoice_column_tax: "ਜੀਐਸਟੀ" + invoice_column_price: "ਕੀਮਤ" + invoice_column_item: "ਆਈਟਮ" + invoice_column_qty: "ਮਾਤਰਾ" + invoice_column_weight_volume: "ਵਜ਼ਨ / ਮਾਤਰਾ" + invoice_column_unit_price_with_taxes: "ਯੂਨਿਟ ਕੀਮਤ (ਟੈਕਸ ਸਮੇਤ)" + invoice_column_unit_price_without_taxes: "ਯੂਨਿਟ ਕੀਮਤ (ਟੈਕਸ ਛੱਡ ਕੇ)" + invoice_column_price_with_taxes: "ਕੁੱਲ ਕੀਮਤ (ਟੈਕਸ ਸਮੇਤ)" + invoice_column_price_without_taxes: "ਕੁੱਲ ਕੀਮਤ (ਟੈਕਸ ਛੱਡ ਕੇ)" + invoice_column_price_per_unit_without_taxes: "ਪ੍ਰਤੀ ਯੂਨਿਟ ਕੀਮਤ (ਟੈਕਸ ਛੱਡ ਕੇ)" + invoice_column_tax_rate: "ਟੈਕਸ ਦੀ ਦਰ" + invoice_tax_total: "ਜੀਐਸਟੀ ਕੁੱਲ:" + tax_invoice: "ਟੈਕਸ ਇਨਵੌਇਸ" + tax_total: "ਕੁੱਲ ਟੈਕਸ (%{rate}):" + invoice_shipping_category_delivery: "ਡਿਲਿਵਰੀ" + invoice_shipping_category_pickup: "ਪਿਕਅੱਪ" + total_excl_tax: "ਕੁੱਲ (ਟੈਕਸ ਛੱਡ ਕੇ):" + total_incl_tax: "ਕੁੱਲ (ਟੈਕਸ ਸਮੇਤ):" + total_all_tax: "ਕੁੱਲ ਟੈਕਸ:" + abn: "ABN:" + acn: "ACN:" + invoice_issued_on: "ਇਨਵੌਇਸ ਇਸ ਮਿਤੀ ਨੂੰ ਜਾਰੀ ਕੀਤਾ ਗਿਆ:" + order_number: "ਆਰਡਰ ਨੰਬਰ:" + date_of_transaction: "ਲੈਣ-ਦੇਣ ਦੀ ਮਿਤੀ:" + menu_1_title: "ਸ਼ਾਪਾਂ" + menu_1_url: "/ਸ਼ਾਪਾਂ" + menu_2_title: "ਮੈਪ" + menu_2_url: "/ਮੈਪ" + menu_3_title: "ਉਤਪਾਦਕ" + menu_3_url: "ਉਤਪਾਦਕ" + menu_4_title: "ਸਮੂਹ" + menu_4_url: "/ਸਮੂਹ" + menu_5_title: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + menu_5_url: "https://about.openfoodnetwork.org.au/" + menu_6_title: "ਕਨੈਕਟ" + menu_6_url: "https://openfoodnetwork.org/au/connect/" + menu_7_title: "ਸਿੱਖੋ" + menu_7_url: "https://openfoodnetwork.org/au/learn/" + logo: "ਲੋਗੋ (640x130)" + logo_mobile: "ਮੋਬਾਈਲ ਲੋਗੋ (75x26)" + logo_mobile_svg: "ਮੋਬਾਈਲ ਲੋਗੋ (SVG)" + home_hero: "ਹੀਰੋ ਫੋਟੋ" + home_show_stats: "ਅੰਕੜੇ ਵਿਖਾਓ" + footer_logo: "ਲੋਗੋ (220x76)" + footer_facebook_url: "ਫੇਸਬੁੱਕ URL" + footer_twitter_url: "ਟਵਿੱਟਰ URL" + footer_instagram_url: "ਇੰਸਟਾਗ੍ਰਾਮ URL" + footer_linkedin_url: "ਲਿੰਕਡਇਨ URL" + footer_googleplus_url: "ਗੂਗਲ ਪਲੱਸ URL" + footer_pinterest_url: "ਪਿਨਟਰੈਸਟ URL" + footer_email: "ਈਮੇਲ" + footer_links_md: "ਲਿੰਕ" + footer_about_url: "URL ਦੇ ਬਾਰੇ ਵਿੱਚ" + user_guide_link: "ਉਪਭੋਗਤਾ ਗਾਈਡ ਲਿੰਕ" + name: ਨਾਮ + first_name: ਪਹਿਲਾ ਨਾਂ + last_name: ਆਖਰੀ ਨਾਂ + email: ਈਮੇਲ + phone: ਫੋਨ + next: ਅਗਲਾ + address: ਪਤਾ + address_placeholder: ਜਿਵੇਂ ਕਿ 123 ਹਾਈ ਸਟਰੀਟ + address2: ਪਤਾ (ਜਾਰੀ) + city: ਸ਼ਹਿਰ + city_placeholder: ਜਿਵੇਂ ਕਿ ਨੌਰਥਕੋਟ + latitude: ਲੈਟਿਟ੍ਯੂਡ + latitude_placeholder: ਜਿਵੇਂ ਕਿ -37.4713077 + latitude_longitude_tip: ਤੁਹਾਡੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਨਕਸ਼ੇ ਤੇ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰਨ ਲਈ ਲੈਟਿਟ੍ਯੂਡ ਅਤੇ ਲੌਨਜੀਟੂਡ ਦੀ ਲੋੜ ਹੈ। + longitude: ਲੌਨਜੀਟੂਡ + longitude_placeholder: ਜਿਵੇਂ ਕਿ - 144.7851531 + use_geocoder: ਪਤੇ ਤੋਂ ਸਵੈਚਲਿਤ ਤੌਰ ਤੇ ਲੈਟਿਟ੍ਯੂਡ ਅਤੇ ਲੌਨਜੀਟੂਡ ਦੀ ਗਣਨਾ ਕਰੋ? + state: ਸਥਿਤੀ + postcode: ਪਿੰਨ ਕੋਡ + postcode_placeholder: ਜਿਵੇਂ ਕਿ 3070 + suburb: ਉਪਨਗਰ + country: ਦੇਸ਼ + unauthorized: ਅਣਅਧਿਕਾਰਤ + terms_of_service: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ" + on_demand: ਡਿਮਾਂਡ ਤੇ + not_allowed: ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ + no_shipping: ਸ਼ਿਪਿੰਗ ਦਾ ਕੋਈ ਢੰਗ ਨਹੀਂ + no_payment: ਭੁਗਤਾਨ ਦਾ ਕੋਈ ਢੰਗ ਨਹੀਂ + no_shipping_or_payment: ਸ਼ਿਪਿੰਗ ਜਾਂ ਭੁਗਤਾਨ ਦਾ ਕੋਈ ਢੰਗ ਨਹੀਂ + unconfirmed: ਅਪੁਸ਼ਟ + days: ਦਿਨ + authorization_failure: "ਅਧਿਕਾਰੀਕਰਨ ਅਸਫਲਤਾ" + description: "ਵਰਣਨ" + label_shop: "ਸ਼ਾਪ" + label_shops: "ਸ਼ਾਪਾਂ" + label_map: "ਮੈਪ" + label_producer: "ਉਤਪਾਦਕ" + label_producers: "ਉਤਪਾਦਕ" + label_groups: "ਸਮੂਹ" + label_about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + label_blog: "ਬਲੌਗ" + label_support: "ਸਮਰਥਨ" + label_shopping: "ਸ਼ਾਪਿੰਗ" + label_login: "ਲਾਗਇਨ" + label_logout: "ਲਾਗਆਊਟ" + label_signup: "ਸਾਇਨ ਅਪ" + label_administration: "ਪ੍ਰਸ਼ਾਸਨ" + label_admin: "ਪ੍ਰਸ਼ਾਸਕ" + label_account: "ਖਾਤਾ" + label_more: "ਹੋਰ ਵਿਖਾਓ" + label_less: "ਘੱਟ ਵਿਖਾਓ" + label_notices: "ਨੋਟਿਸ" + cart_items: "ਆਈਟਮਾਂ" + cart_headline: "ਤੁਹਾਡਾ ਸ਼ਾਪਿੰਗ ਕਾਰਟ" + total: "ਕੁੱਲ" + cart_updating: "ਕਾਰਟ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." + cart_empty: "ਕਾਰਟ ਖਾਲੀ ਹੈ" + cart_edit: "ਆਪਣੇ ਕਾਰਟ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ" + item: "ਆਈਟਮ" + qty: "ਮਾਤਰਾ" + card_number: ਕਾਰਡ ਨੰਬਰ + card_securitycode: "ਸੁਰੱਖਿਆ ਕੋਡ" + card_expiry_date: ਮਿਆਦ ਪੁੱਗਣ ਦੀ ਮਿਤੀ + card_masked_digit: "X" + card_expiry_abbreviation: "Exp" + new_credit_card: "ਨਵਾਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ" + my_credit_cards: ਮੇਰੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ + add_new_credit_card: ਨਵਾਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਜੋੜੋ + saved_cards: ਸੇਵ ਕੀਤੇ ਕਾਰਡ + add_a_card: ਇੱਕ ਕਾਰਡ ਜੋੜੋ + add_card: ਕਾਰਡ ਜੋੜੋ + you_have_no_saved_cards: ਤੁਸੀਂ ਅਜੇ ਤੱਕ ਕੋਈ ਵੀ ਕਾਰਡ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਹੈ + saving_credit_card: ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਸੇਵ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ... + card_has_been_removed: "ਤੁਹਾਡਾ ਕਾਰਡ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ (ਨੰਬਰ: %{number})" + card_could_not_be_removed: ਮਾਫ਼ ਕਰਨਾ, ਕਾਰਡ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ + invalid_credit_card: "ਅਵੈਧ ਕ੍ਰੈਡਿਟ ਕਾਰਡ" + legal: + cookies_policy: + header: "ਅਸੀਂ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਕਿਵੇਂ ਕਰਦੇ ਹਾਂ" + desc_part_1: "ਕੂਕੀਜ਼ ਬਹੁਤ ਛੋਟੀਆਂ ਟੈਕਸਟ ਫਾਈਲਾਂ ਹੁੰਦੀਆਂ ਹਨ ਜੋ ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਉਤੇ ਸਟੋਰ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ ਜਦੋਂ ਤੁਸੀਂ ਕੁਝ ਵੈਬਸਾਈਟਾਂ ਤੇ ਜਾਂਦੇ ਹੋ।" + desc_part_2: "OFN ਵਿੱਚ ਅਸੀਂ ਤੁਹਾਡੀ ਗੋਪਨੀਯਤਾ ਦਾ ਪੂਰਾ ਸਤਿਕਾਰ ਕਰਦੇ ਹਾਂ। ਅਸੀਂ ਸਿਰਫ਼ ਉਹਨਾਂ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਾਂ ਜੋ ਤੁਹਾਨੂੰ ਆਨਲਾਈਨ ਭੋਜਨ ਵੇਚਣ/ਖਰੀਦਣ ਦੀ ਸੇਵਾ ਪ੍ਰਦਾਨ ਕਰਨ ਲਈ ਜ਼ਰੂਰੀ ਹਨ। ਅਸੀਂ ਤੁਹਾਡਾ ਕੋਈ ਵੀ ਡੇਟਾ ਨਹੀਂ ਵੇਚਦੇ। ਅਸੀਂ ਭਵਿੱਖ ਵਿੱਚ ਤੁਹਾਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦਾ ਪ੍ਰਸਤਾਵ ਦੇ ਸਕਦੇ ਹਾਂ। ਨਵੀਆਂ ਕਾਮਨਜ਼ ਸੇਵਾਵਾਂ ਬਣਾਉਣ ਲਈ ਤੁਹਾਡੇ ਕੁਝ ਡੇਟਾ ਜੋ ਈਕੋਸਿਸਟਮ ਲਈ ਲਾਭਦਾਇਕ ਹੋ ਸਕਦੀਆਂ ਹਨ (ਜਿਵੇਂ ਕਿ ਛੋਟੇ ਭੋਜਨ ਪ੍ਰਣਾਲੀਆਂ ਲਈ ਲੌਜਿਸਟਿਕ ਸੇਵਾਵਾਂ) ਪਰ ਅਸੀਂ ਅਜੇ ਉਥੇ ਨਹੀਂ ਹਾਂ, ਅਤੇ ਅਸੀਂ ਤੁਹਾਡੇ ਅਧਿਕਾਰ ਤੋਂ ਬਿਨਾਂ ਅਜਿਹਾ ਨਹੀਂ ਕਰਾਂਗੇ :-)" + desc_part_3: "ਅਸੀਂ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਮੁੱਖ ਤੌਰ ਤੇ ਇਹ ਯਾਦ ਰੱਖਣ ਲਈ ਕਰਦੇ ਹਾਂ ਕਿ ਤੁਸੀਂ ਕੌਣ ਹੋ ਜੇ ਤੁਸੀਂ ਸੇਵਾ ਵਿੱਚ 'ਲੌਗ ਇਨ' ਕਰਦੇ ਹੋ, ਜਾਂ ਤੁਹਾਡੇ ਦੁਆਰਾ ਲੌਗਇਨ ਨਾ ਹੋਣ ਤੇ ਵੀ ਤੁਹਾਡੇ ਕਾਰਟ ਵਿੱਚ ਪਾਈਆਂ ਗਈਆਂ ਚੀਜ਼ਾਂ ਨੂੰ ਯਾਦ ਰੱਖਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ। ਜੇਕਰ ਤੁਸੀਂ ਬਿਨਾਂ \"ਕੂਕੀਜ਼ ਸਵੀਕਾਰ ਹਨ\" ਤੇ ਕਲਿੱਕ ਕੀਤੇ ਵੈਬਸਾਈਟ ਉਤੇ ਨੈਵੀਗੇਟ ਕਰਦੇ ਰਹਿੰਦੇ ਹੋ, ਤਾਂ ਅਸੀਂ ਮੰਨਦੇ ਹਾਂ ਕਿ ਤੁਸੀਂ ਸਾਨੂੰ ਉਹਨਾਂ ਕੂਕੀਜ਼ ਨੂੰ ਸਟੋਰ ਕਰਨ ਲਈ ਸਹਿਮਤੀ ਦੇ ਰਹੇ ਹੋ ਜੋ ਵੈਬਸਾਈਟ ਦੇ ਕੰਮਕਾਜ ਲਈ ਜ਼ਰੂਰੀ ਹਨ। ਇੱਥੇ ਕੂਕੀਜ਼ ਦੀ ਉਹ ਸੂਚੀ ਦਿੱਤੀ ਗਈ ਹੈ ਜੋ ਅਸੀਂ ਵਰਤਦੇ ਹਾਂ!" + essential_cookies: "ਜ਼ਰੂਰੀ ਕੂਕੀਜ਼" + essential_cookies_desc: "ਸਾਡੀ ਵੈਬਸਾਈਟ ਦੇ ਸੰਚਾਲਨ ਲਈ ਹੇਠਾਂ ਦਿੱਤੀਆਂ ਕੂਕੀਜ਼ ਬਹੁਤ ਜ਼ਰੂਰੀ ਹਨ।" + essential_cookies_note: "ਜ਼ਿਆਦਾਤਰ ਕੂਕੀਜ਼ ਵਿੱਚ ਸਿਰਫ਼ ਇੱਕ ਵਿਲੱਖਣ ਪਛਾਣਕਰਤਾ ਹੁੰਦਾ ਹੈ, ਪਰ ਕੋਈ ਹੋਰ ਡੇਟਾ ਨਹੀਂ, ਇਸਲਈ ਤੁਹਾਡਾ ਈਮੇਲ ਪਤਾ ਅਤੇ ਪਾਸਵਰਡ ਕਦੇ ਵੀ ਉਹਨਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਨਹੀਂ ਹੁੰਦਾ ਅਤੇ ਕਦੇ ਵੀ ਸਾਹਮਣੇ ਨਹੀਂ ਆਉਂਦਾ।" + cookie_domain: "ਇਸ ਦੁਆਰਾ ਸੇਟ ਕਰੋ:" + cookie_session_desc: "ਵੇਬਸਾਈਟ ਨੂੰ ਪੇਜ ਉਤੇ ਵਿਜ਼ਿਟ ਦੇ ਵਿਚਕਾਰ ਉਪਭੋਗਤਾਵਾਂ ਨੂੰ ਯਾਦ ਰੱਖਣ ਦੀ ਆਗਿਆ ਦੇਣ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ, ਉਦਾਹਰਨ ਲਈ, ਤੁਹਾਡੀ ਕਾਰਟ ਵਿੱਚ ਆਈਟਮਾਂ ਨੂੰ ਯਾਦ ਰੱਖਣ ਲਈ।" + cookie_consent_desc: "ਕੂਕੀਜ਼ ਨੂੰ ਸਟੋਰ ਕਰਨ ਲਈ ਉਪਭੋਗਤਾ ਦੀ ਸਹਿਮਤੀ ਦੀ ਸਥਿਤੀ ਨੂੰ ਕਾਇਮ ਰੱਖਣ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ" + cookie_remember_me_desc: "ਜੇਕਰ ਉਪਭੋਗਤਾ ਨੇ ਵੈਬਸਾਈਟ ਨੂੰ ਉਸਨੂੰ ਯਾਦ ਰੱਖਣ ਲਈ ਬੇਨਤੀ ਕੀਤੀ ਹੈ ਤਾਂ ਇਸਨੂੰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ। ਇਹ ਕੂਕੀ 12 ਦਿਨਾਂ ਬਾਅਦ ਆਪਣੇ ਆਪ ਮਿਟਾ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ। ਜੇਕਰ ਇੱਕ ਉਪਭੋਗਤਾ ਦੇ ਰੂਪ ਵਿੱਚ ਤੁਸੀਂ ਚਾਹੁੰਦੇ ਹੋ ਕਿ ਕੂਕੀ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇ, ਤਾਂ ਤੁਹਾਨੂੰ ਸਿਰਫ਼ ਲੌਗਆਊਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਨਹੀਂ ਚਾਹੁੰਦੇ ਕਿ ਕੂਕੀ ਨੂੰ ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਉਤੇ ਇੰਸਟਾਲ ਕੀਤਾ ਜਾਵੇ ਤਾਂ ਤੁਹਾਨੂੰ ਲੌਗਇਨ ਕਰਨ ਵੇਲੇ \"ਮੈਨੂੰ ਯਾਦ ਰੱਖੋ\" ਚੈਕਬਾਕਸ ਤੇ ਚੈਕ ਦਾ ਨਿਸ਼ਾਨ ਨਹੀਂ ਲਾਉਣਾ ਚਾਹੀਦਾ।" + cookie_openstreemap_desc: "ਸਾਡੇ ਦੋਸਤਾਨਾ ਓਪਨ ਸੋਰਸ ਮੈਪਿੰਗ ਪ੍ਰਦਾਤਾ (OpenStreetMap) ਦੁਆਰਾ ਇਹ ਯਕੀਨੀ ਬਣਾਉਣ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ ਕਿ ਇੱਕ ਦਿੱਤੇ ਸਮੇਂ ਦੇ ਦੌਰਾਨ ਉਹ ਬਹੁਤ ਸਾਰੀਆਂ ਬੇਨਤੀਆਂ ਪ੍ਰਾਪਤ ਨਾ ਕਰਨ, ਤਾਂ ਜੋ ਉਹਨਾਂ ਦੀਆਂ ਸੇਵਾਵਾਂ ਦੀ ਦੁਰਵਰਤੋਂ ਨੂੰ ਰੋਕਿਆ ਜਾ ਸਕੇ।" + cookie_stripe_desc: "ਧੋਖਾਧੜੀ ਦਾ ਪਤਾ ਲਗਾਉਣ ਲਈ ਸਾਡੇ ਭੁਗਤਾਨ ਪ੍ਰੋਸੈਸਰ ਸਟ੍ਰਾਈਪ ਦੁਆਰਾ ਇਕੱਤਰ ਕੀਤਾ ਗਿਆ ਡੇਟਾ https://stripe.com/cookies-policy/legal। ਸਾਰੀਆਂ ਸ਼ਾਪਾਂ ਸਟ੍ਰਾਈਪ ਨੂੰ ਭੁਗਤਾਨ ਵਿਧੀ ਵਜੋਂ ਨਹੀਂ ਵਰਤਦੀਆਂ ਹਨ, ਪਰ ਧੋਖਾਧੜੀ ਨੂੰ ਰੋਕਣ ਲਈ ਇਸਨੂੰ ਸਾਰੇ ਪੰਨਿਆਂ ਤੇ ਲਾਗੂ ਕਰਨਾ ਇੱਕ ਚੰਗਾ ਅਭਿਆਸ ਹੈ। ਸ਼ਾਇਦ ਸਟ੍ਰਾਈਪ ਇੱਕ ਤਸਵੀਰ ਬਣਾਉਂਦਾ ਹੈ ਕਿ ਸਾਡੇ ਕਿਹੜੇ ਪੰਨੇ ਆਮ ਤੌਰ ਤੇ ਉਹਨਾਂ ਦੇ API ਨਾਲ ਇੰਟਰੈਕਟ ਕਰਦੇ ਹਨ ਅਤੇ ਫਿਰ ਕਿਸੇ ਵੀ ਅਸਧਾਰਨ ਚੀਜ਼ਾਂ ਨੂੰ ਫਲੈਗ ਕਰਦੇ ਹਨ। ਇਸ ਲਈ ਸਟ੍ਰਾਈਪ ਕੂਕੀ ਨੂੰ ਸੈਟ ਕਰਨਾ ਇੱਕ ਉਪਭੋਗਤਾ ਲਈ ਭੁਗਤਾਨ ਵਿਧੀ ਪ੍ਰਦਾਨ ਕਰਨ ਨਾਲੋਂ ਵਧੇਰੇ ਵਿਆਪਕ ਹੈ। ਇਸਨੂੰ ਹਟਾਉਣਾ ਸੰਭਵ ਹੈ। ਇਸਨੂੰ ਹਟਾਉਣ ਨਾਲ ਸੁਰੱਖਿਆ ਦੀ ਸੇਵਾ ਪ੍ਰਭਾਵਿਤ ਹੋ ਸਕਦੀ ਹੈ। ਤੁਸੀਂ ਸਟ੍ਰਾਈਪ ਬਾਰੇ ਹੋਰ ਜਾਣ ਸਕਦੇ ਅਤੇ ਇਸਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਨੂੰ https://stripe.com/privacy ਤੇ ਪੜ੍ਹ ਸਕਦੇ ਹੋ।" + statistics_cookies: "ਸਟੈਟਿਸਟਿਕਸ ਕੂਕੀਜ਼" + statistics_cookies_desc: "ਹੇਠ ਦਿੱਤੇ ਸਖਤੀ ਨਾਲ ਜ਼ਰੂਰੀ ਨਹੀਂ ਹਨ, ਪਰ ਤੁਹਾਡੇ ਦਵਾਰਾ ਅਨੁਮਤੀ ਦੇਣ ਨਾਲ ਇਹ ਸਾਨੂੰ ਉਪਭੋਗਤਾ ਵਿਵਹਾਰ ਦਾ ਵਿਸ਼ਲੇਸ਼ਣ ਕਰਨ, ਪਛਾਣ ਕਰਨ ਲਈ ਕਿ ਤੁਹਾਨੂੰ ਸਭ ਤੋਂ ਵੱਧ ਕਿਹੜੀਆਂ ਸੁਵਿਧਾਵਾਂ ਸਬ ਤੋਂ ਵਧੀਆ ਲੱਗਦੀਆਂ ਹਨ, ਜਾਂ ਨਹੀਂ ਲੱਗਦੇ ਹਨ, ਉਪਭੋਗਤਾ ਅਨੁਭਵ ਦੀਆਂ ਸਮੱਸਿਆਵਾਂ ਨੂੰ ਸਮਝਣ, ਆਦਿ ਵਿੱਚ ਮਦਦ ਕਰਦੇ ਹਨ, ਜਿਦੇ ਨਾਲ ਅਸੀਂ ਇੱਕ ਬੇਹਤਰ ਉਪਭੋਗਤਾ ਅਨੁਭਵ ਪ੍ਰਦਾਨ ਕਰਨ ਵਿੱਚ ਸਕਸ਼ਮ ਬਣਾਉਂਦੇ ਹਨ।" + statistics_cookies_matomo_desc_html: "ਪਲੇਟਫਾਰਮ ਦੀ ਵਰਤੋਂ ਦੇ ਡੇਟਾ ਨੂੰ ਇਕੱਤਰ ਕਰਨ ਅਤੇ ਵਿਸ਼ਲੇਸ਼ਣ ਕਰਨ ਲਈ, ਅਸੀਂ Matomo (ex Piwik) ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਾਂ, ਜੋ ਕਿ ਇੱਕ ਓਪਨ ਸੋਰਸ ਵਿਸ਼ਲੇਸ਼ਣ ਟੂਲ ਹੈ ਜੋ GDPR ਅਨੁਕੂਲ ਹੈ ਅਤੇ ਤੁਹਾਡੀ ਗੋਪਨੀਯਤਾ ਦੀ ਰੱਖਿਆ ਕਰਦਾ ਹੈ।" + statistics_cookies_matomo_optout: "ਕੀ ਤੁਸੀਂ ਮਾਟੋਮੋ ਵਿਸ਼ਲੇਸ਼ਕੀ ਤੋਂ ਬਾਹਰ ਨਿਕਲਣਾ ਚਾਹੁੰਦੇ ਹੋ? ਅਸੀਂ ਕੋਈ ਨਿੱਜੀ ਡਾਟਾ ਇਕੱਠਾ ਨਹੀਂ ਕਰਦੇ, ਅਤੇ ਮਾਟੋਮੋ ਸਾਡੀ ਸੇਵਾ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਵਿੱਚ ਸਾਡੀ ਮਦਦ ਕਰਦਾ ਹੈ, ਪਰ ਅਸੀਂ ਤੁਹਾਡੀ ਪਸੰਦ ਦਾ ਸਨਮਾਨ ਕਰਦੇ ਹਾਂ :-)" + cookie_matomo_heatmap_desc: "ਡਾਟਾ ਇਕੱਠਾ ਕਰਨ ਲਈ ਮਾਟੋਮੋ ਦੀ ਪਹਿਲੀ ਪਾਰਟੀ ਕੂਕੀਜ਼।" + cookie_matomo_ignore_desc: "ਕੁਕੀ ਜਿਦੀ ਵਰਤੋਂ ਉਪਭੋਗਤਾ ਨੂੰ ਟਰੈਕ ਕੀਤੇ ਜਾਣ ਤੋਂ ਰੋਕਣ ਲਈ ਕੀਤੀ ਜਾਂਦੀ ਹੈ।" + disabling_cookies_header: "ਕੂਕੀਜ਼ ਨੂੰ ਅਯੋਗ ਕਰਨ ਵੇਲੇ ਚੇਤਾਵਨੀ" + disabling_cookies_desc: "ਇੱਕ ਉਪਭੋਗਤਾ ਦੇ ਤੌਰ ਤੇ ਤੁਸੀਂ ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਦੇ ਸੈਟਿੰਗ ਕੰਟਰੋਲ ਦੁਆਰਾ ਜਦੋਂ ਵੀ ਚਾਹੋ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਜਾਂ ਕਿਸੇ ਹੋਰ ਵੈਬਸਾਈਟ ਤੋਂ ਕੂਕੀਜ਼ ਨੂੰ ਇਜਾਜ਼ਤ ਦੇ ਸਕਦੇ ਹੋ, ਬਲੌਕ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ। ਹਰੇਕ ਬ੍ਰਾਊਜ਼ਰ ਦਾ ਵੱਖਰਾ ਆਪਰੇਟਿਵ ਹੁੰਦਾ ਹੈ। ਇੱਥੇ ਲਿੰਕ ਦਿੱਤੇ ਗਏ ਹਨ:" + disabling_cookies_firefox_link: "https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences" + disabling_cookies_ie_link: "https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies" + disabling_cookies_safari_link: "https://www.apple.com/legal/privacy/en-ww/cookies/" + disabling_cookies_note: "ਪਰ ਧਿਆਨ ਰੱਖੋ ਕਿ ਜੇਕਰ ਤੁਸੀਂ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਦੁਆਰਾ ਵਰਤੀਆਂ ਜਾਣ ਵਾਲੀਆਂ ਜ਼ਰੂਰੀ ਕੂਕੀਜ਼ ਨੂੰ ਹਟਾਉਂਦੇ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਦੇ ਹੋ, ਤਾਂ ਵੈਬਸਾਈਟ ਕੰਮ ਨਹੀਂ ਕਰੇਗੀ, ਉਦਾਹਰਨ ਲਈ ਤੁਸੀਂ ਆਪਣੇ ਕਾਰਟ ਵਿੱਚ ਨਾ ਤਾਂ ਕੁਜ ਜੋੜ ਸਕੋਗੇ ਅਤੇ ਨਾ ਹੀ ਚੈਕਆਉਟ ਕਰ ਸਕੋਗੇ।" + cookies_banner: + cookies_usage: "ਇਹ ਸਾਈਟ ਤੁਹਾਡੀ ਨੈਵੀਗੇਸ਼ਨ ਨੂੰ ਪਰੇਸ਼ਾਨੀ-ਮੁਕਤ ਅਤੇ ਸੁਰੱਖਿਅਤ ਬਣਾਉਣ ਲਈ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਕਰਦੀ ਹੈ, ਅਤੇ ਨਾਲ ਹੀ ਸਾਨੂੰ ਇਹ ਸਮਝਾ ਕੇ ਕੀ ਤੁਸੀ ਇਸਦਾ ਉਪਯੋਗ ਕਿਵੇਂ ਕਰਦੇ ਹੋ, ਸਾਡੇ ਦੁਆਰਾ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਣ ਵਾਲਿਆਂ ਸੁਵਿਧਾਵਾਂ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਵਿੱਚ ਸਾਡੀ ਮਦਦ ਕਰਦੀ ਹੈ।" + cookies_definition: "ਕੂਕੀਜ਼ ਬਹੁਤ ਛੋਟੀਆਂ ਟੈਕਸਟ ਫਾਈਲਾਂ ਹੁੰਦੀਆਂ ਹਨ ਜੋ ਤੁਹਾਡੇ ਕੰਪਿਊਟਰ ਉਤੇ ਸਟੋਰ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ ਜਦੋਂ ਤੁਸੀਂ ਕੁਝ ਵੈਬਸਾਈਟਾਂ ਤੇ ਜਾਂਦੇ ਹੋ।" + cookies_desc: "ਅਸੀਂ ਸਿਰਫ਼ ਉਹਨਾਂ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਾਂ ਜੋ ਤੁਹਾਨੂੰ ਆਨਲਾਈਨ ਭੋਜਨ ਵੇਚਣ/ਖਰੀਦਣ ਦੀ ਸੇਵਾ ਪ੍ਰਦਾਨ ਕਰਨ ਲਈ ਜ਼ਰੂਰੀ ਹਨ। ਅਸੀਂ ਤੁਹਾਡਾ ਕੋਈ ਵੀ ਡੇਟਾ ਵੇਚਦੇ ਨਹੀਂ ਹਾਂ। ਅਸੀਂ ਕੂਕੀਜ਼ ਦੀ ਵਰਤੋਂ ਮੁੱਖ ਤੌਰ ਤੇ ਇਹ ਯਾਦ ਰੱਖਣ ਲਈ ਕਰਦੇ ਹਾਂ ਕਿ ਜੇਕਰ ਤੁਸੀਂ ਸੇਵਾ ਵਿੱਚ 'ਲਾਗ ਇਨ' ਕਰਦੇ ਹੋ ਤਾਂ ਤੁਸੀਂ ਕੌਣ ਹੋ, ਜਾਂ ਤੁਹਾਡੇ ਦੁਆਰਾ ਆਪਣੀ ਕਾਰਟ ਵਿੱਚ ਰੱਖੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਯਾਦ ਰੱਖਣ ਲਈ, ਉਦੋਂ ਵੀ ਜਦੋਂ ਤੁਸੀਂ ਲਾਗਇਨ ਨਾ ਹੋਵੋ। ਜੇਕਰ ਤੁਸੀਂ \"ਕੂਕੀਜ਼ ਸਵੀਕਾਰ ਹਨ\" ਉਤੇ ਕਲਿੱਕ ਕੀਤੇ ਬਿਨਾਂ ਵੈਬਸਾਈਟ ਰਾਹੀਂ ਨੈਵੀਗੇਟ ਕਰਨਾ ਜਾਰੀ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਅਸੀਂ ਇਹ ਮੰਨ ਕੇ ਚਲਦੇ ਹਾਂ ਕਿ ਤੁਸੀਂ ਸਾਨੂੰ ਉਹਨਾਂ ਕੂਕੀਜ਼ ਨੂੰ ਸਟੋਰ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇ ਰਹੇ ਹੋ ਜੋ ਵੈਬਸਾਈਟ ਦੇ ਕੰਮ ਕਰਨ ਲਈ ਜ਼ਰੂਰੀ ਹਨ।" + cookies_policy_link_desc: "ਜੇ ਤੁਸੀਂ ਹੋਰ ਜਾਣਨਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਚੈਕ ਕਰੋ ਸਾਡੀ" + cookies_policy_link: "ਕੂਕੀਜ਼ ਪਾਲਿਸੀ" + cookies_accept_button: "ਕੂਕੀਜ਼ ਸਵੀਕਾਰ ਹਨ" + home_shop: ਹੁਣੇ ਖਰੀਦੋ + brandstory_headline: "ਆਰਗੈਨਿਕ ਅਤੇ ਸਥਾਨਕ ਭੋਜਨ ਖਰੀਦੋ।" + brandstory_intro: "Search for local farmers and food producers." + brandstory_part1: "ਹਜ਼ਾਰਾਂ ਕਿਸਾਨਾਂ ਅਤੇ ਭੋਜਨ ਉਤਪਾਦਕਾਂ ਵਿੱਚੋਂ ਚੁਣੋ ਜੋ ਬਿਨਾਂ ਕਿਸੇ ਨੁਕਸਾਨਦੇਹ ਰਸਾਇਣਾਂ ਦੇ ਟਿਕਾਊ ਖੇਤੀ ਵਿਧੀਆਂ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਭੋਜਨ ਪੈਦਾ ਕਰਦੇ ਹਨ।" + brandstory_part2: "ਫਾਰਮਰਜ਼, ਭੋਜਨ ਉਤਪਾਦਕ, ਫਾਰਮਰ ਪ੍ਰੋਡਿਊਸਰ ਆਰਗੇਨਾਈਜ਼ੇਸ਼ਨਾਂ (FPO), ਜਾਂ ਫਾਰਮਰ ਪ੍ਰੋਡਿਊਸਰ ਕੰਪਨੀਆਂ (FPC) ਇਸ ਪਲੇਟਫਾਰਮ ਉਤੇ ਇੱਕ ਔਨਲਾਈਨ ਸ਼ਾਪ ਬਣਾ ਸਕਦੇ ਹਨ, ਭੁਗਤਾਨ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹਨ ਅਤੇ ਹੋਰ ਸ਼ਾਪਾਂ ਰਾਹੀਂ ਵੇਚ ਸਕਦੇ ਹਨ।" + brandstory_part3: "ਜਦੋਂ ਤੁਸੀਂ ਸਥਾਨਕ ਕਿਸਾਨਾਂ ਤੋਂ ਆਪਣਾ ਭੋਜਨ ਖਰੀਦਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਸਥਾਨਕ ਆਰਥਿਕਤਾ ਦਾ ਸਮਰਥਨ ਕਰਦੇ ਹੋ।" + brandstory_part4: "ਇਹ ਸਾਫਟਵੇਅਰ ਇੱਕ ਗੈਰ-ਲਾਭਕਾਰੀ ਸੰਸਥਾ ਦੁਆਰਾ ਤਿਆਰ ਕੀਤਾ ਗਿਆ ਹੈ ਤਾਂ ਜੋ ਕਿਸਾਨਾਂ ਨੂੰ ਉਹਨਾਂ ਦੀ ਉਪਜ ਆਨਲਾਈਨ ਵੇਚਣ ਵਿੱਚ ਮਦਦ ਕੀਤੀ ਜਾ ਸਕੇ।" + brandstory_part5_strong: "ਅਸੀਂ ਇਸਨੂੰ ਓਪਨ ਫੂਡ ਨੈੱਟਵਰਕ ਇੰਡੀਆ ਜਾਂ OFN ਇੰਡੀਆ ਕਹਿੰਦੇ ਹਾਂ।" + brandstory_part6: "ਜੇਕਰ ਤੁਸੀਂ ਚੰਗਾ ਭੋਜਨ ਵੇਚਦੇ ਹੋ - ਇੱਕ ਕਿਸਾਨ, ਕਿਸਾਨ ਬਜ਼ਾਰ, ਫੂਡ ਕੋ-ਆਪ, ਜਾਂ ਫੂਡ ਹੱਬ- ਦੇ ਰੂਪ ਵਿੱਚ- ਤਾਂ OFN ਇੰਡੀਆ ਤੁਹਾਡਾ ਸਭ ਤੋਂ ਵਧੀਆ ਵਿਕਲਪ ਹੈ।" + system_headline: "OFN ਉਤੇ ਵੇਚਣਾ - 3 ਆਸਾਨ ਕਦਮ" + system_step1: "1. ਨਵਾਂ ਖਾਤਾ ਬਣਾਓ" + system_step1_text: "ਨਾਂ, ਵਰਣਨ, ਫੋਟੋਆਂ, ਸੰਪਰਕ ਵੇਰਵਿਆਂ ਅਤੇ ਸੋਸ਼ਲ ਮੀਡੀਆ ਲਿੰਕਾਂ ਦੇ ਨਾਲ ਆਪਣੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਸੇਟ ਅਪ ਕਰੋ।" + system_step2: "2. ਆਪਣੇ ਉਤਪਾਦ ਜੋੜੋ" + system_step2_text: "ਆਪਣੀ ਸ਼ਾਪ ਵਿੱਚ ਉਤਪਾਦ ਸ਼ਾਮਲ ਕਰੋ - ਤੁਹਾਡੇ ਆਪਣੇ ਅਤੇ/ਜਾਂ ਤੁਹਾਡੇ ਆਲੇ-ਦੁਆਲੇ ਦੇ ਹੋਰ ਉਤਪਾਦਕਾਂ ਦੇ। ਫੋਟੋ, ਵਰਣਨ, ਕੀਮਤਾਂ, ਸਟਾਕ ਦੇ ਪੱਧਰ ਅਤੇ ਲਚਕਦਾਰ ਵਜ਼ਨ ਅਤੇ ਮਾਪ ਸੇਟ ਕਰੋ।" + system_step3: "3. ਆਪਣੀਆਂ ਡਿਲੀਵਰੀ ਦੀ ਯੋਜਨਾ ਬਣਾਓ" + system_step3_text: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਸੇਟ ਅਪ ਕਰੋ। ਕਈ ਪਿਕ-ਅੱਪ ਪੁਆਇੰਟ ਅਤੇ ਡਿਲੀਵਰੀ ਵੇਰਵੇ ਬਣਾਓ। ਆਵਰਤੀ ਆਰਡਰ ਅਤੇ ਨਿਯਮਤ ਵੰਡਣ ਦੀ ਪ੍ਰਕ੍ਰਿਆ ਬਣਾਓ।" + cta_headline: "ਭਵਿੱਖ ਦਾ ਟਿਕਾਊ ਭੋਜਨ ਨੈਟਵਰਕ।" + cta_label: "ਮੈਂ ਤਿਆਰ ਹਾਂ" + stats_headline: "ਅਸੀਂ ਇੱਕ ਨਵੀਂ ਭੋਜਨ ਪ੍ਰਣਾਲੀ ਬਣਾ ਰਹੇ ਹਾਂ।" + stats_producers: "ਭੋਜਨ ਉਤਪਾਦਕ" + stats_shops: "ਭੋਜਨ ਦੀਆਂ ਸ਼ਾਪਾਂ" + stats_shoppers: "ਭੋਜਨ ਖਰੀਦਦਾਰ" + stats_orders: "ਭੋਜਨ ਆਰਡਰ" + checkout_title: ਚੈਕਆਊਟ + checkout_now: ਹੁਣੇ ਚੈਕਆਉਟ ਕਰੋ + checkout_order_ready: ਇਹਨਾਂ ਲਈ ਆਰਡਰ ਤਿਆਰ ਹੈ + checkout_hide: ਲੁਕਾਓ + checkout_expand: ਫੈਲਾਓ + checkout_headline: "ਠੀਕ ਹੈ, ਚੈਕਆਉਟ ਲਈ ਤਿਆਰ ਹੋ?" + checkout_as_guest: "ਗੈਸਟ ਦੇ ਤੌਰ ਤੇ ਚੈਕਆਉਟ ਕਰੋ" + checkout_details: "ਤੁਹਾਡੇ ਵੇਰਵੇ" + checkout_billing: "ਬਿਲਿੰਗ ਜਾਣਕਾਰੀ" + checkout_default_bill_address: "ਡਿਫੌਲਟ ਬਿਲਿੰਗ ਪਤੇ ਵਜੋਂ ਸੁਰੱਖਿਅਤ ਕਰੋ" + checkout_shipping: ਸ਼ਿਪਿੰਗ ਜਾਣਕਾਰੀ + checkout_default_ship_address: "ਡਿਫੌਲਟ ਬਿਲਿੰਗ ਪਤੇ ਵਜੋਂ ਸੁਰੱਖਿਅਤ ਕਰੋ" + checkout_method_free: ਮੁਫ਼ਤ + checkout_address_same: ਕੀ ਸ਼ਿਪਿੰਗ ਪਤਾ ਅਤੇ ਬਿਲਿੰਗ ਪਤਾ ਇੱਕੋ ਹੈ? + checkout_ready_for: "ਇਸ ਲਈ ਤਿਆਰ:" + checkout_instructions: "ਕੋਈ ਟਿੱਪਣੀਆਂ ਜਾਂ ਵਿਸ਼ੇਸ਼ ਹਦਾਇਤਾਂ?" + checkout_payment: ਭੁਗਤਾਨ + checkout_send: ਹੁਣੇ ਆਰਡਰ ਕਰੋ + checkout_your_order: ਤੁਹਾਡਾ ਆਰਡਰ + checkout_cart_total: ਕਾਰਟ ਦੀ ਕੁੱਲ ਰਕਮ + checkout_shipping_price: ਸ਼ਿਪਿੰਗ + checkout_total_price: ਕੁੱਲ + checkout_back_to_cart: "ਕਾਰਟ ਤੇ ਵਾਪਸ" + cost_currency: "ਲਾਗਤ ਦੀ ਕਰੰਸੀ" + order_paid: ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ + order_not_paid: ਭੁਗਤਾਨ ਨਹੀਂ ਕੀਤਾ ਗਿਆ + order_total: ਕੁੱਲ ਆਰਡਰ + order_payment: "ਇਸ ਰਾਹੀਂ ਭੁਗਤਾਨ:" + no_payment_required: "ਕਿਸੇ ਭੁਗਤਾਨ ਦੀ ਲੋੜ ਨਹੀਂ" + order_billing_address: ਬਿਲਿੰਗ ਪਤਾ + order_delivery_on: ਡਿਲਿਵਰੀ ਦਾ ਦਿਨ + order_delivery_address: ਡਿਲਿਵਰੀ ਪਤਾ + order_delivery_time: ਡਿਲਿਵਰੀ ਦਾ ਸਮਾਂ + order_special_instructions: "ਤੁਹਾਡੇ ਨੋਟ:" + order_pickup_time: ਕਲੇਕਟ ਕਰਨ ਲਈ ਤਿਆਰ + order_pickup_instructions: ਕਲੇਕਸ਼ਨ ਦੇ ਨਿਰਦੇਸ਼ + order_produce: ਉਤਪਾਦਨ + order_amount_paid: ਭੁਗਤਾਨ ਕੀਤੀ ਰਕਮ + order_total_price: ਕੁੱਲ + order_balance_due: ਬਕਾਇਆ ਰਕਮ + order_includes_tax: (ਟੈਕਸ ਸ਼ਾਮਲ ਹੈ) + order_payment_paypal_successful: ਪੇਪਾਲ ਦੁਆਰਾ ਤੁਹਾਡਾ ਭੁਗਤਾਨ ਸਫਲਤਾਪੂਰਵਕ ਸੰਸਾਧਿਤ ਹੋ ਗਿਆ ਹੈ। + order_hub_info: ਹੱਬ ਦੀ ਜਾਣਕਾਰੀ + order_back_to_store: ਸਟੋਰ ਤੇ ਵਾਪਸ + order_back_to_cart: ਕਾਰਟ ਤੇ ਵਾਪਸ + order_back_to_website: ਵੈਬਸਾਈਟ ਤੇ ਵਾਪਸ + bom_tip: "ਬਹੁਤ ਸਾਰੇ ਆਰਡਰਾਂ ਵਿੱਚ ਉਤਪਾਦ ਦੀ ਮਾਤਰਾ ਨੂੰ ਬਦਲਣ ਲਈ ਇਸ ਪੇਜ ਦੀ ਵਰਤੋਂ ਕਰੋ। ਲੋੜ ਪੈਣ ਤੇ ਉਤਪਾਦਾਂ ਨੂੰ ਆਰਡਰਾਂ ਤੋਂ ਪੂਰੀ ਤਰ੍ਹਾਂ ਹਟਾਇਆ ਜਾ ਸਕਦਾ ਹੈ।" + unsaved_changes_warning: "ਬਿਨਾ ਸੇਵ ਕੀਤੀਆਂ ਤਬਦੀਲੀਆਂ ਮੌਜੂਦ ਹਨ ਅਤੇ ਜੇਕਰ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਦੇ ਹੋ ਤਾਂ ਗੁੰਮ ਹੋ ਜਾਣਗੀਆਂ।" + unsaved_changes_error: "ਲਾਲ ਕਿਨਾਰਿਆਂ ਵਾਲੇ ਖੇਤਰਾਂ ਵਿੱਚ ਤਰੁੱਟੀਆਂ ਹਨ।" + products: "ਉਤਪਾਦ" + products_in: "%{oc} ਵਿੱਚ" + products_at: "%{ਡਿਸਟ੍ਰੀਬਿਊਟਰ} ਤੇ" + products_elsewhere: "ਕਿਤੇ ਹੋਰ ਲੱਭੇ ਉਤਪਾਦ" + email_confirmed: "ਆਪਣੇ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਤੁਹਾਡਾ ਧੰਨਵਾਦ।" + email_confirmation_activate_account: "ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਅਸੀਂ ਤੁਹਾਡੇ ਨਵੇਂ ਖਾਤੇ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਕਰ ਸਕੀਏ, ਸਾਨੂੰ ਤੁਹਾਡੇ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।" + email_confirmation_greeting: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ, %{contact}!" + email_confirmation_profile_created: "%{name} ਲਈ ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਬਣਾਇਆ ਗਿਆ ਹੈ! ਤੁਹਾਡੇ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਕਰਨ ਲਈ ਸਾਨੂੰ ਇਸ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।" + email_confirmation_click_link: "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੀ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਅਤੇ ਆਪਣੀ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਸੇਟ ਕਰਨਾ ਜਾਰੀ ਰੱਖਣ ਲਈ ਹੇਠਾਂ ਦਿੱਤੇ ਲਿੰਕ ਉਤੇ ਕਲਿੱਕ ਕਰੋ।" + email_confirmation_link_label: "ਇਸ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ »" + email_confirmation_help_html: "ਆਪਣੀ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਤੋਂ ਬਾਅਦ ਤੁਸੀਂ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਆਪਣੇ ਪ੍ਰਬੰਧਨ ਖਾਤੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੇ ਹੋ। %{sitename} ਦੇ ਫ਼ੀਚਰ ਬਾਰੇ ਹੋਰ ਜਾਣਨ ਲਈ ਅਤੇ ਆਪਣੇ ਪ੍ਰੋਫਾਈਲ ਜਾਂ ਔਨਲਾਈਨ ਸਟੋਰ ਦੀ ਵਰਤੋਂ ਸ਼ੁਰੂ ਕਰਨ ਲਈ %{link} ਨੂੰ ਵੇਖੋ।" + email_confirmation_notice_unexpected: "ਤੁਹਾਨੂੰ ਇਹ ਸੁਨੇਹਾ ਇਸ ਲਈ ਪ੍ਰਾਪਤ ਹੋਇਆ ਹੈ ਕਿਉਂਕਿ ਤੁਸੀਂ %{sitename} ਤੇ ਸਾਈਨ ਅੱਪ ਕੀਤਾ ਸੀ, ਜਾਂ ਕਿਸੇ ਅਜਿਹੇ ਵਿਅਕਤੀ ਦੁਆਰਾ ਸਾਈਨ ਅੱਪ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਸੱਦਾ ਦਿੱਤਾ ਗਿਆ ਸੀ ਜਿਸਨੂੰ ਤੁਸੀਂ ਸ਼ਾਇਦ ਜਾਣਦੇ ਹੋ। ਜੇਕਰ ਤੁਸੀਂ ਇਹ ਸਮਝ ਨਹੀਂ ਪਾ ਰਹੇ ਹੋ ਕਿ ਤੁਸੀਂ ਇਹ ਈਮੇਲ ਕਿਉਂ ਪ੍ਰਾਪਤ ਕੀਤਾ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ %{contact} ਨੂੰ ਲਿਖੋ।" + email_social: "ਸਾਡੇ ਨਾਲ ਜੁੜੋ:" + email_contact: "ਸਾਨੂੰ ਈਮੇਲ ਕਰੋ:" + email_signoff: "ਚੀਅਰਸ," + email_signature: "%{sitename} ਟੀਮ" + email_confirm_customer_greeting: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ %{name}," + email_confirm_customer_intro_html: "%{distributor} ਤੋਂ ਖਰੀਦਦਾਰੀ ਲਈ ਧੰਨਵਾਦ!" + email_confirm_customer_number_html: "ਆਰਡਰ ਪੁਸ਼ਟਿਕਰਨ #%{number}" + email_confirm_customer_details_html: "%{distributor} ਤੋਂ ਤੁਹਾਡੇ ਆਰਡਰ ਵੇਰਵੇ ਇੱਥੇ ਹਨ:" + email_confirm_customer_signoff: "ਸ਼ੁਭਕਾਮਨਾਵਾਂ," + email_confirm_shop_greeting: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ %{name}," + email_confirm_shop_order_html: "ਸ਼ਾਬਾਸ਼! ਤੁਹਾਡੇ ਕੋਲ %{distributor} ਲਈ ਇੱਕ ਨਵਾਂ ਆਰਡਰ ਹੈ!" + email_confirm_shop_number_html: "ਆਰਡਰ ਪੁਸ਼ਟਿਕਰਨ #%{number}" + email_order_summary_item: "ਆਈਟਮ" + email_order_summary_quantity: "ਮਾਤਰਾ" + email_order_summary_sku: "SKU" + email_order_summary_price: "ਕੀਮਤ" + email_order_summary_subtotal: "ਉਪ-ਕੁੱਲ:" + email_order_summary_total: "ਕੁੱਲ" + email_order_summary_includes_tax: "(ਟੈਕਸ ਸਮੇਤ):" + email_payment_paid: ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ + email_payment_not_paid: ਭੁਗਤਾਨ ਨਹੀਂ ਕੀਤਾ ਗਿਆ + email_payment_description: ਚੈਕਆਉਟ ਤੇ ਭੁਗਤਾਨ ਦਾ ਵੇਰਵਾ + email_payment_summary: ਭੁਗਤਾਨ ਸੰਖੇਪ + email_payment_method: "ਇਸ ਰਾਹੀਂ ਭੁਗਤਾਨ:" + email_so_placement_intro_html: "ਤੁਹਾਡੇ ਕੋਲ %{distributor} ਨਾਲ ਇੱਕ ਨਵਾਂ ਆਰਡਰ ਹੈ" + email_so_placement_details_html: "%{distributor} ਲਈ ਤੁਹਾਡੇ ਆਰਡਰ ਦੇ ਵੇਰਵੇ ਇੱਥੇ ਦਿੱਤੇ ਗਏ ਹਨ:" + email_so_placement_changes: "ਬਦਕਿਸਮਤੀ ਨਾਲ, ਤੁਹਾਡੇ ਦੁਆਰਾ ਬੇਨਤੀ ਕੀਤੇ ਸਾਰੇ ਉਤਪਾਦ ਉਪਲਬਧ ਨਹੀਂ ਸਨ। ਤੁਹਾਡੇ ਦੁਆਰਾ ਬੇਨਤੀ ਕੀਤੀ ਅਸਲ ਮਾਤਰਾਵਾਂ ਹੇਠਾਂ ਕਟਿਆਂ ਹੋਇਆਂ ਵਿਖਾਈਆਂ ਗਈਆਂ ਹਨ।" + email_so_payment_success_intro_html: "%{distributor} ਤੋਂ ਤੁਹਾਡੇ ਆਰਡਰ ਲਈ ਇੱਕ ਸਵੈਚਾਲਿਤ ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।" + email_so_placement_explainer_html: "ਇਹ ਆਰਡਰ ਤੁਹਾਡੇ ਲਈ ਸਵੈਚਾਲਿਤ ਢੰਗ ਨਾਲ ਬਣਾਇਆ ਗਿਆ ਸੀ।" + email_so_edit_true_html: "ਜਦੋ ਤਕ %{orders_close_at} ਤੇ ਆਰਡਰ ਬੰਦ ਨਹੀਂ ਹੋ ਜਾਂਦੇ, ਤਦ ਤਕ ਤੁਸੀਂ ਵਿੱਚ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ।" + email_so_edit_false_html: "ਤੁਸੀਂ ਕਿਸੀ ਵੀ ਸਮੇਂ ਤੇ ਇਸ ਆਰਡਰ ਦੇ ਵੇਰਵੇ ਵੇਖ ਸਕਦੇ ਹੋ।" + email_so_contact_distributor_html: "ਜੇਕਰ ਤੁਹਾਡੇ ਕੋਈ ਸਵਾਲ ਹਨ ਤਾ ਤੁਸੀਂ %{email} ਦੇ ਦਵਾਰਾ %{distributor} ਨਾਲ ਸੰਪਰਕ ਕਰ ਸਕਦੇ ਹੋ।" + email_so_contact_distributor_to_change_order_html: "ਇਹ ਆਰਡਰ ਤੁਹਾਡੇ ਲਈ ਸਵੈਚਾਲਿਤ ਢੰਗ ਨਾਲ ਬਣਾਇਆ ਗਿਆ ਸੀ। ਤੁਸੀਂ %{email} ਦੇ ਦਵਾਰਾ %{distributor} ਨਾਲ ਸੰਪਰਕ ਕਰਕੇ%{orders_close_at} ਦੇ ਬੰਦ ਹੋਣ ਤਕ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ।" + email_so_confirmation_intro_html: "%{distributor} ਦੇ ਨਾਲ ਤੁਹਾਡੇ ਆਰਡਰ ਦੀ ਪੁਸ਼ਟੀ ਹੁਣ ਹੋ ਗਈ ਹੈ" + email_so_confirmation_explainer_html: "ਇਹ ਆਰਡਰ ਤੁਹਾਡੇ ਲਈ ਸਵੈਚਲਿਤ ਤੌਰ ਤੇ ਦਿੱਤਾ ਗਿਆ ਸੀ, ਅਤੇ ਹੁਣ ਇਸਨੂੰ ਅੰਤਿਮ ਰੂਪ ਦੇ ਦਿੱਤਾ ਗਿਆ ਹੈ." + email_so_confirmation_details_html: "%{distributor} ਤੋਂ ਤੁਹਾਡੇ ਆਰਡਰ ਬਾਰੇ ਤੁਹਾਨੂੰ ਜੋ ਕੁਜ ਜਾਣਨ ਦੀ ਲੋੜ ਹੈ, ਉਹ ਸਬ ਇਥੇ ਦਿੱਤਾ ਗਿਆ ਹੈ:" + email_so_empty_intro_html: "ਅਸੀਂ %{distributor} ਨੂੰ ਇੱਕ ਨਵਾਂ ਆਰਡਰ ਦੇਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ, ਪਰ ਕੁਝ ਸਮੱਸਿਆਵਾਂ ਆਈਆਂ..." + email_so_empty_explainer_html: "ਬਦਕਿਸਮਤੀ ਨਾਲ, ਤੁਹਾਡੇ ਦੁਆਰਾ ਆਰਡਰ ਕੀਤੇ ਗਏ ਉਤਪਾਦਾਂ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਉਪਲਬਧ ਨਹੀਂ ਸੀ, ਇਸਲਈ ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਤੁਹਾਡੇ ਦੁਆਰਾ ਬੇਨਤੀ ਕੀਤੀ ਅਸਲ ਮਾਤਰਾ ਹੇਠਾਂ ਕੱਟ ਕੇ ਵਿਖਾਈਆਂ ਗਈਆਂ ਹਨ।" + email_so_empty_details_html: "%{distributor} ਕੇ ਕੋਲ ਨਾ ਕੀਤੇ ਗਏ ਆਰਡਰ ਦੇ ਵੇਰਵੇ ਇੱਥੇ ਦਿੱਤੇ ਗਏ ਹਨ:" + email_so_failed_payment_intro_html: "ਅਸੀਂ ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ, ਪਰ ਕੁਝ ਸਮੱਸਿਆਵਾਂ ਆਈਆਂ..." + email_so_failed_payment_explainer_html: "ਤੁਹਾਡੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨਾਲ ਸਮੱਸਿਆ ਦੇ ਕਾਰਨ %{distributor} ਨਾਲ ਤੁਹਾਡੀ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਲਈ ਭੁਗਤਾਨ ਅਸਫਲ ਰਿਹਾ। %{distributor} ਨੂੰ ਇਸ ਅਸਫਲ ਭੁਗਤਾਨ ਬਾਰੇ ਸੂਚਿਤ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।" + email_so_failed_payment_details_html: "ਭੁਗਤਾਨ ਗੇਟਵੇ ਦੁਆਰਾ ਪ੍ਰਦਾਨ ਕੀਤੀ ਗਈ ਅਸਫਲਤਾ ਦੇ ਵੇਰਵੇ ਇੱਥੇ ਦਿੱਤੇ ਗਏ ਹਨ:" + email_shipping_delivery_details: ਡਿਲਿਵਰੀ ਵੇਰਵੇ + email_shipping_delivery_time: "ਡਿਲਿਵਰੀ ਦਾ ਦਿਨ:" + email_shipping_delivery_address: "ਡਿਲਿਵਰੀ ਪਤਾ" + email_shipping_collection_details: ਕਲੇਕਸ਼ਨ ਦੇ ਵੇਰਵੇ + email_shipping_collection_time: "ਕਲੇਕਟ ਕਰਨ ਲਈ ਤਿਆਰ" + email_shipping_collection_instructions: "ਕਲੇਕਸ਼ਨ ਦੇ ਨਿਰਦੇਸ਼" + email_special_instructions: "ਤੁਹਾਡੇ ਨੋਟ:" + email_signup_greeting: ਸਤ ਸ੍ਰੀ ਅਕਾਲ! + email_signup_welcome: "%{sitename} ਵਿੱਚ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!" + email_signup_confirmed_email: "ਤੁਹਾਡੀ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਧੰਨਵਾਦ।" + email_signup_shop_html: "ਤੁਸੀਂ ਹੁਣ %{link} ਤੇ ਲਾਗਇਨ ਕਰ ਸਕਦੇ ਹੋ।" + email_signup_text: "ਨੈਟਵਰਕ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਣ ਲਈ ਧੰਨਵਾਦ। ਜੇਕਰ ਤੁਸੀਂ ਇੱਕ ਗਾਹਕ ਹੋ, ਤਾਂ ਅਸੀਂ ਤੁਹਾਨੂੰ ਬਹੁਤ ਸਾਰੇ ਸ਼ਾਨਦਾਰ ਕਿਸਾਨਾਂ, ਵਧੀਆ ਫੂਡ ਹੱਬਾਂ ਅਤੇ ਸੁਆਦੀ ਭੋਜਨ ਨਾਲ ਜਾਣੂ ਕਰਵਾਉਣ ਲਈ ਉਤਸੁਕ ਹਾਂ! ਜੇਕਰ ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਜਾਂ ਭੋਜਨ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਹੋ, ਤਾਂ ਅਸੀਂ ਤੁਹਾਨੂੰ ਇਸ ਨੈਟਵਰਕ ਦੇ ਇੱਕ ਹਿੱਸਾ ਬਣਾਉਣ ਲਈ ਉਤਸ਼ਾਹਿਤ ਹਾਂ।" + email_signup_help_html: "ਅਸੀਂ ਤੁਹਾਡੇ ਸਾਰੇ ਸਵਾਲਾਂ ਅਤੇ ਫੀਡਬੈਕ ਦਾ ਸੁਆਗਤ ਕਰਦੇ ਹਾਂ; ਤੁਸੀਂ ਸਾਈਟ ਉਤੇ ਫੀਡਬੈਕ ਭੇਜੋ ਬਟਨ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਸਾਨੂੰ %{email} ਤੇ ਈਮੇਲ ਕਰ ਸਕਦੇ ਹੋ" + invite_email: + greeting: "ਸਤ ਸ੍ਰੀ ਅਕਾਲ!" + invited_to_manage: "ਤੁਹਾਨੂੰ %{instance} ਉਤੇ %{enterprise} ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਸੱਦਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।" + confirm_your_email: "ਤੁਹਾਨੂੰ ਇੱਕ ਪੁਸ਼ਟੀਕਰਨ ਲਿੰਕ ਦੇ ਨਾਲ ਇੱਕ ਈਮੇਲ ਪ੍ਰਾਪਤ ਹੋਇਆ ਹੋਵੇਗਾ ਜਾਂ ਜਲਦੀ ਹੀ ਪ੍ਰਾਪਤ ਹੋਵੇਗਾ। ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਆਪਣੀ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕਰ ਲੈਂਦੇ, ਤੁਸੀਂ %{enterprise} ਦੇ ਪ੍ਰੋਫਾਈਲ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਪਾਉਂਗੇ।" + set_a_password: "ਫਿਰ ਤੁਹਾਨੂੰ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੇ ਯੋਗ ਹੋਣ ਤੋਂ ਪਹਿਲਾਂ ਇੱਕ ਪਾਸਵਰਡ ਸੇਟ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਵੇਗਾ।" + producer_mail_greeting: "ਪਿਆਰੇ" + producer_mail_text_before: "ਕਿਰਪਾ ਹੇਠਾਂ ਆਰਡਰ ਸਾਈਕਲ ਦੇ ਬਾਰੇ ਵਿੱਚ ਇੱਕ ਅੱਪਡੇਟ ਵੇਖੋ, ਜੋ ਕਿ ਤਿਆਰ ਹੈ ਇਸ ਲਈ:" + producer_mail_order_text: "ਇਹ ਰਿਹਾ ਤੁਹਾਡੇ ਉਤਪਾਦਾਂ ਦੇ ਆਰਡਰ ਦਾ ਸੰਖੇਪ:" + producer_mail_delivery_instructions: "ਸਟਾਕ ਪਿਕਅਪ/ਡਿਲਿਵਰੀ ਨਿਰਦੇਸ਼:" + producer_mail_signoff: "ਧੰਨਵਾਦ ਅਤੇ ਸ਼ੁੱਭਕਾਮਨਾਵਾਂ" + producer_mail_order_customer_text: "ਇੱਥੇ ਗਾਹਕਾਂ ਦੁਆਰਾ ਸਮੂਹ ਕੀਤੇ ਗਏ ਆਰਡਰਾਂ ਦਾ ਸਾਰ ਹੈ" + shopping_oc_closed: ਆਰਡਰ ਬੰਦ ਹਨ + shopping_oc_closed_description: "ਕਿਰਪਾ ਕਰਕੇ ਅਗਲਾ ਸਾਈਕਲ ਖੁੱਲ੍ਹਣ ਤੱਕ ਇੰਤਜ਼ਾਰ ਕਰੋ (ਜਾਂ ਇਹ ਵੇਖਣ ਲਈ ਸਾਡੇ ਨਾਲ ਸਿੱਧਾ ਸੰਪਰਕ ਕਰੋ ਕਿ ਕੀ ਅਸੀਂ ਕਿਸੇ ਲੇਟ ਆਰਡਰ ਨੂੰ ਸਵੀਕਾਰ ਕਰ ਸਕਦੇ ਹਾਂ)" + shopping_oc_last_closed: "ਆਖਰੀ ਸਾਈਕਲ %{distance_of_time} ਪਹਿਲਾਂ ਬੰਦ ਹੋ ਗਈ ਸੀ" + shopping_oc_next_open: "ਅਗਲੀ ਸਾਈਕਲ %{distance_of_time} ਵਿੱਚ ਖੁਲੇਗੀ" + shopping_oc_select: "ਚੁਣੋ..." + shopping_tabs_home: "ਹੋਮ" + shopping_tabs_shop: "ਸ਼ਾਪ" + shopping_tabs_about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + shopping_tabs_producers: "ਉਤਪਾਦਕ" + shopping_tabs_contact: "ਸੰਪਰਕ" + shopping_tabs_groups: "ਸਮੂਹ" + shopping_contact_address: "ਪਤਾ" + shopping_contact_web: "ਸੰਪਰਕ" + shopping_contact_social: "ਫਾਲੋ ਕਰੋ" + shopping_groups_part_of: "ਇਸ ਦਾ ਹਿੱਸਾ ਹੈ:" + shopping_producers_of_hub: "%{hub} ਦੇ ਉਤਪਾਦਕ:" + enterprises_next_closing: "ਅਗਲਾ ਆਰਡਰ ਸਮਾਪਤ ਹੋ ਰਿਹਾ ਹੈ" + enterprises_currently_open: "ਆਰਡਰ ਇਸ ਵੇਲੇ ਖੁੱਲ੍ਹੇ ਹਨ" + enterprises_ready_for: "ਲਈ ਤਿਆਰ" + enterprises_choose: "ਚੁਣੋ ਜਦੋਂ ਤੁਸੀਂ ਆਪਣਾ ਆਰਡਰ ਚਾਹੁੰਦੇ ਹੋ:" + maps_open: "ਖੁਲਾ ਹੈ" + maps_closed: "ਬੰਦ ਹੈ" + map_title: "ਮੈਪ" + hubs_buy: "ਇਹਨਾਂ ਲਈ ਖਰੀਦੋ:" + hubs_shopping_here: "ਇੱਥੇ ਖਰੀਦਦਾਰੀ ਕਰ ਰਹੇ ਹਾਂ" + hubs_orders_closed: "ਆਰਡਰ ਬੰਦ ਹ" + hubs_profile_only: "ਸਿਰਫ਼ ਪ੍ਰੋਫਾਈਲ" + hubs_delivery_options: "ਡਿਲਿਵਰੀ ਵਿਕਲਪ" + hubs_pickup: "ਪਿਕਅੱਪ" + hubs_delivery: "ਡਿਲਿਵਰੀ" + hubs_producers: "ਸਾਡੇ ਉਤਪਾਦਕ" + hubs_filter_by: "ਇਸਦੇ ਅਨੁਸਾਰ ਫਿਲਟਰ ਕਰੋ" + hubs_filter_type: "ਕਿਸਮ" + hubs_filter_delivery: "ਡਿਲਿਵਰੀ" + hubs_filter_property: "ਪ੍ਰਾਪਰਟੀ" + hubs_matches: "ਕੀ ਤੁਹਾਡਾ ਮਤਲਬ ਸੀ?" + hubs_intro: ਆਪਣੇ ਸਥਾਨਕ ਖੇਤਰ ਵਿੱਚ ਖਰੀਦਦਾਰੀ ਕਰੋ + hubs_distance: ਇਸਦੇ ਸਭ ਤੋਂ ਨੇੜੇ + hubs_distance_filter: "ਮੈਨੂੰ %{location} ਦੇ ਨੇੜੇ ਦੀਆਂ ਸ਼ਾਪਾਂ ਵਿਖਾਓ" + shop_changeable_orders_alert_html: + one: %{shop} / %{order} ਵਾਲਾ ਤੁਹਾਡਾ ਆਰਡਰ ਸਮੀਖਿਆ ਲਈ ਖੁੱਲ੍ਹਾ ਹੈ। ਤੁਸੀਂ %{oc_close} ਤੱਕ ਬਦਲਾਅ ਕਰ ਸਕਦੇ ਹੋ। + few: ਤੁਹਾਡੇ ਕੋਲ %{count} ਇਸ %{shop} ਦੇ ਨਾਲ ਜੋ ਆਰਡਰ ਹਨ, ਅਤੇ ਉਹ ਅਜੇ ਵੀ ਸਮੀਖਿਆ ਲਈ ਖੁੱਲ੍ਹੇ ਹਨ। ਤੁਸੀਂ %{oc_close} ਤੱਕ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ। + many: ਤੁਹਾਡੇ ਕੋਲ %{count} ਇਸ %{shop} ਦੇ ਨਾਲ ਜੋ ਆਰਡਰ ਹਨ, ਅਤੇ ਉਹ ਅਜੇ ਵੀ ਸਮੀਖਿਆ ਲਈ ਖੁੱਲ੍ਹੇ ਹਨ। ਤੁਸੀਂ %{oc_close} ਤੱਕ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ। + other: ਤੁਹਾਡੇ ਕੋਲ %{count} ਇਸ %{shop} ਦੇ ਨਾਲ ਜੋ ਆਰਡਰ ਹਨ, ਅਤੇ ਉਹ ਅਜੇ ਵੀ ਸਮੀਖਿਆ ਲਈ ਖੁੱਲ੍ਹੇ ਹਨ। ਤੁਸੀਂ %{oc_close} ਤੱਕ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ। + orders_changeable_orders_alert_html: ਇਸ ਆਰਡਰ ਦੀ ਪੁਸ਼ਟੀ ਹੋ ਚੁੱਕੀ ਹੈ, ਪਰ ਤੁਸੀਂ %{oc_close} ਤੱਕ ਬਦਲਾਵ ਕਰ ਸਕਦੇ ਹੋ। + products_clear: ਮਿਟਾਓ + products_showing: "ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ:" + products_results_for: "ਇਸ ਲਈ ਨਤੀਜੇ" + products_or: "ਜਾਂ" + products_and: "ਅਤੇ" + products_filters_in: "ਵਿੱਚ" + products_with: ਨਾਲ + products_search: "ਖੋਜੋ..." + products_filter_by: "ਇਸਦੇ ਅਨੁਸਾਰ ਫਿਲਟਰ ਕਰੋ" + products_filter_selected: "ਚੁਣੇ ਗਏ'" + products_filter_heading: "ਫਿਲਟਰ" + products_filter_clear: "ਮਿਟਾਓ" + products_filter_done: "ਪੂਰਾ ਹੋਇਆ" + products_loading: "ਉਤਪਾਦ ਲੋਡ ਹੋ ਰਹੇ ਹਨ..." + products_updating_cart: "ਕਾਰਟ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." + products_cart_empty: "ਕਾਰਟ ਖਾਲੀ ਹੈ" + products_edit_cart: "ਆਪਣੇ ਕਾਰਟ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ" + products_from: ਤੋਂ + products_change: "ਸੇਵ ਕਰਨ ਲਈ ਕੋਈ ਬਦਲਾਅ ਨਹੀਂ ਹਨ।" + products_update_error: "ਹੇਠਾਂ ਦਿੱਤੀਆਂ ਗਲਤੀ (ਗਲਤੀਆਂ) ਦੇ ਕਾਰਨ ਸੇਵ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ:" + products_update_error_msg: "ਸੇਵ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ।" + products_update_error_data: "ਅਵੈਧ ਡੇਟਾ ਦੇ ਕਾਰਨ ਸੇਵ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ:" + products_changes_saved: "ਬਦਲਾਵਾਂ ਨੂੰ ਸੇਵ ਕੀਤਾ ਗਿਆ।" + products_no_results_html: "ਮਾਫ਼ ਕਰਨਾ, %{query} ਲਈ ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ" + products_clear_search: "ਖੋਜ ਮਿਟਾਓ" + search_no_results_html: "ਮਾਫ਼ ਕਰਨਾ, %{query} ਲਈ ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ। ਹੋਰ ਖੋਜ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ?" + components_profiles_popover: "ਪ੍ਰੋਫਾਈਲਾਂ ਦਾ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਕੋਈ ਸ਼ਾਪਫਰੰਟ ਨਹੀਂ ਹੈ, ਪਰ ਕਿਤੇ ਹੋਰ ਉਹਨਾਂ ਦੀ ਆਪਣੀ ਭੌਤਿਕ ਜਾਂ ਔਨਲਾਈਨ ਸ਼ਾਪ ਹੋ ਸਕਦੀ ਹੈ" + components_profiles_show: "ਪ੍ਰੋਫਾਈਲ ਵਿਖਾਓ" + components_filters_nofilters: "ਕੋਈ ਫਿਲਟਰ ਨਹੀਂ" + components_filters_clearfilters: "ਸਾਰੇ ਫਿਲਟਰ ਸਾਫ਼ ਕਰੋ" + groups_title: ਸਮੂਹ + groups_headline: ਸਮੂਹ/ਖੇਤਰ + groups_text: "ਹਰ ਉਤਪਾਦਕ ਵਿਲੱਖਣ ਹੁੰਦਾ ਹੈ। ਹਰ ਕਾਰੋਬਾਰ ਕੋਲ ਕੁਜ ਵੱਖਰਾ ਪੇਸ਼ ਕਰਨ ਲਈ ਹੁੰਦਾ ਹੈ। ਸਾਡੇ ਸਮੂਹ ਉਤਪਾਦਕਾਂ, ਹੱਬਾਂ ਅਤੇ ਵਿਤਰਕਾਂ ਦੇ ਸਮੂਹ ਹਨ ਜੋ ਸਥਾਨ, ਕਿਸਾਨ ਬਾਜ਼ਾਰ ਜਾਂ ਫਲਸਫੇ ਵਰਗੀਆਂ ਸਾਂਝੀਆਂ ਚੀਜ਼ਾਂ ਨੂੰ ਸਾਂਝਾ ਕਰਦੇ ਹਨ। ਇਹ ਤੁਹਾਡੇ ਖਰੀਦਦਾਰੀ ਅਨੁਭਵ ਨੂੰ ਆਸਾਨ ਬਣਾਉਂਦਾ ਹੈ। ਇਸ ਲਈ ਸਾਡੇ ਸਮੂਹਾਂ ਦੀ ਪੜਚੋਲ ਕਰੋ ਅਤੇ ਤੁਹਾਡੇ ਲਈ ਬਣਾਈ ਚੀਜਾਂ ਦਾ ਅਨੰਦ ਲਵੋ।" + groups_search: "ਨਾਂ ਜਾਂ ਸ਼ਬਦ ਲੱਭੋ" + groups_no_groups: "ਕੋਈ ਸਮੂਹ ਨਹੀਂ ਲੱਭਿਆ" + groups_about: "ਸਾਡੇ ਬਾਰੇ ਵਿੱਚ" + groups_producers: "ਸਾਡੇ ਉਤਪਾਦਕ" + groups_hubs: "ਸਾਡੇ ਹੱਬ" + groups_contact_web: ਸੰਪਰਕ + groups_contact_social: ਫਾਲੋ ਕਰੋ + groups_contact_address: ਪਤਾ + groups_contact_email: ਸਾਨੂੰ ਈਮੇਲ ਕਰੋ + groups_contact_website: ਸਾਡੀ ਵੈਬਸਾਈਟ ਤੇ ਆਓ + groups_contact_facebook: ਫੇਸਬੁੱਕ ਤੇ ਸਾਨੂੰ ਫੋਲੋ ਕਰੋ + groups_signup_title: ਇੱਕ ਸਮੂਹ ਵਜੋਂ ਸਾਈਨ ਅੱਪ ਕਰੋ + groups_signup_headline: ਸਮੂਹ ਸਾਈਨ ਅੱਪ + groups_signup_intro: "ਅਸੀਂ ਸਹਿਯੋਗੀ ਮਾਰਕੀਟਿੰਗ ਲਈ ਇੱਕ ਸ਼ਾਨਦਾਰ ਪਲੇਟਫਾਰਮ ਹਾਂ, ਤੁਹਾਡੇ ਮੈਂਬਰਾਂ ਅਤੇ ਹਿੱਸੇਦਾਰਾਂ ਲਈ ਨਵੇਂ ਬਾਜ਼ਾਰਾਂ ਤੱਕ ਪਹੁੰਚਣ ਦਾ ਸਭ ਤੋਂ ਆਸਾਨ ਤਰੀਕਾ। ਅਸੀਂ ਗੈਰ-ਲਾਭਕਾਰੀ, ਕਿਫਾਇਤੀ ਅਤੇ ਸਰਲ ਹਾਂ।" + groups_signup_email: ਸਾਨੂੰ ਈਮੇਲ ਕਰੋ + groups_signup_motivation1: ਅਸੀਂ ਭੋਜਨ ਪ੍ਰਣਾਲੀਆਂ ਨੂੰ ਨਿਰਪੱਖ ਢੰਗ ਨਾਲ ਬਦਲਦੇ ਹਾਂ। + groups_signup_motivation2: ਇਸ ਲਈ ਅਸੀਂ ਹਰ ਰੋਜ਼ ਮੰਜੇ ਤੋਂ ਉਠਦੇ ਹਾਂ। ਅਸੀਂ ਓਪਨ ਸੋਰਸ ਕੋਡ ਦੇ ਆਧਾਰ ਉਤੇ ਇੱਕ ਗਲੋਬਲ ਗੈਰ-ਮੁਨਾਫ਼ਾ ਸੰਸਥਾ ਹਾਂ। ਅਸੀਂ ਨਿਰਪੱਖ ਖੇਡਦੇ ਹਾਂ। ਤੁਸੀਂ ਹਮੇਸ਼ਾ ਸਾਡੇ ਉਤੇ ਭਰੋਸਾ ਕਰ ਸਕਦੇ ਹੋ। + groups_signup_motivation3: ਅਸੀਂ ਜਾਣਦੇ ਹਾਂ ਕਿ ਤੁਹਾਡੇ ਕੋਲ ਵੱਡੇ ਵਿਚਾਰ ਹਨ, ਅਤੇ ਅਸੀਂ ਮਦਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹਾਂ। ਅਸੀਂ ਆਪਣਾ ਗਿਆਨ, ਨੈਟਵਰਕ ਅਤੇ ਸਰੋਤ ਸਾਂਝੇ ਕਰਾਂਗੇ। ਅਸੀਂ ਜਾਣਦੇ ਹਾਂ ਕਿ ਕੱਲੇ ਰਹਿਣ ਨਾਲ ਬਦਲਾਵ ਨਹੀਂ ਲਿਆਯਾ ਜਾ ਸਕਦਾ, ਇਸ ਲਈ ਅਸੀਂ ਤੁਹਾਡੇ ਨਾਲ ਭਾਈਵਾਲੀ ਕਰਾਂਗੇ। + groups_signup_motivation4: ਅਸੀਂ ਤੁਹਾਨੂੰ ਉਥੇ ਹੀ ਮਿਲਦੇ ਹਾਂ ਜਿੱਥੇ ਤੁਸੀਂ ਹੋ। + groups_signup_motivation5: ਤੁਸੀਂ ਫੂਡ ਹੱਬ, ਉਤਪਾਦਕਾਂ, ਜਾਂ ਵਿਤਰਕਾਂ, ਦਾ ਗਠਜੋੜ ਜਾਂ ਇੱਕ ਉਦਯੋਗ ਸੰਸਥਾ, ਜਾਂ ਇੱਕ ਸਥਾਨਕ ਸਰਕਾਰ ਹੋ ਸਕਦੇ ਹੋ। + groups_signup_motivation6: ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਅੰਦੋਲਨ ਵਿੱਚ ਤੁਹਾਡੀ ਭੂਮਿਕਾ ਜੋ ਵੀ ਹੋਵੇ, ਅਸੀਂ ਮਦਦ ਕਰਨ ਲਈ ਤਿਆਰ ਹਾਂ। ਹਾਲਾਂਕਿ ਤੁਸੀਂ ਹੈਰਾਨ ਹੋਵੋਗੇ ਕਿ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੁਹਾਡੀ ਹਿੱਸੇ ਦੀ ਦੁਨੀਆਂ ਵਿੱਚ ਕੀ ਕਰ ਰਿਹਾ ਹੈ ਜਾਂ ਕਿਹੋ ਜਿਹਾ ਵਿਖਾਈ ਦੇਵੇਗਾ, ਆਓ ਫਿਰ ਗੱਲਬਾਤ ਸ਼ੁਰੂ ਕਰੀਏ। + groups_signup_motivation7: ਅਸੀਂ ਭੋਜਨ ਦੀਆਂ ਗਤੀਵਿਧੀਆਂ ਨੂੰ ਵਧੇਰੇ ਅਰਥਪੂਰਨ ਬਣਾਉਂਦੇ ਹਾਂ। + groups_signup_motivation8: ਤੁਹਾਨੂੰ ਆਪਣੇ ਨੈਟਵਰਕਾਂ ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਅਤੇ ਸਮਰੱਥ ਕਰਨ ਦੀ ਲੋੜ ਹੈ, ਅਸੀਂ ਗੱਲਬਾਤ ਅਤੇ ਕਾਰਵਾਈ ਲਈ ਇੱਕ ਪਲੇਟਫਾਰਮ ਪੇਸ਼ ਕਰਦੇ ਹਾਂ। ਤੁਹਾਨੂੰ ਅਸਲ ਸ਼ਮੂਲੀਅਤ ਦੀ ਲੋੜ ਹੈ। ਅਸੀਂ ਸਾਰੇ ਖਿਡਾਰੀਆਂ, ਸਾਰੇ ਹਿੱਸੇਦਾਰਾਂ, ਸਾਰੇ ਖੇਤਰਾਂ ਤੱਕ ਪਹੁੰਚਣ ਵਿੱਚ ਮਦਦ ਕਰਾਂਗੇ। + groups_signup_motivation9: ਤੁਹਾਨੂੰ ਸੰਸਾਧਨਾਂ ਦੀ ਲੋੜ ਹੈ। ਅਸੀਂ ਆਪਣੇ ਸਾਰੇ ਤਜ਼ਰਬੇ ਨੂੰ ਕੰਮ ਤੇ ਲਾਵਾਂਗੇ। ਤੁਹਾਡੇ ਸਹਿਯੋਗ ਦੀ ਲੋੜ ਹੈ। ਅਸੀਂ ਤੁਹਾਨੂੰ ਸਾਥੀਆਂ ਦੇ ਗਲੋਬਲ ਨੈਟਵਰਕ ਨਾਲ ਬਿਹਤਰ ਢੰਗ ਨਾਲ ਜੋੜਾਂਗੇ। + groups_signup_pricing: ਸਮੂਹ ਖਾਤਾ + groups_signup_studies: ਕੇਸ ਸਟੱਡੀਜ਼ + groups_signup_contact: ਚਰਚਾ ਕਰਨ ਲਈ ਤਿਆਰ ਹੋ? + groups_signup_contact_text: "OFN ਤੁਹਾਡੇ ਲਈ ਕੀ ਕਰ ਸਕਦਾ ਹੈ, ਇਹ ਜਾਣਨ ਲਈ ਸੰਪਰਕ ਕਰੋ:" + groups_signup_detail: "ਇੱਥੇ ਵੇਰਵਾ ਹੈ." + login_invalid: "ਅਵੈਧ ਈਮੇਲ ਜਾਂ ਪਾਸਵਰਡ" + producers_about: ਸਾਡੇ ਬਾਰੇ + producers_buy: ਇਹਨਾਂ ਲਈ ਖਰੀਦਦਾਰੀ ਕਰੋ + producers_contact: ਸੰਪਰਕ + producers_contact_phone: ਕਾਲ ਕਰੋ + producers_contact_social: ਫਾਲੋ ਕਰੋ + producers_buy_at_html: "%{enterprise} ਦੇ ਉਤਪਾਦਾਂ ਲਈ ਇੱਥੇ ਖਰੀਦਦਾਰੀ ਕਰੋ:" + producers_filter: ਇਸਦੇ ਅਨੁਸਾਰ ਫਿਲਟਰ ਕਰੋ + producers_filter_type: ਕਿਸਮ + producers_filter_property: ਪ੍ਰਾਪਰਟੀ + producers_title: ਉਤਪਾਦਕ + producers_headline: ਸਥਾਨਕ ਉਤਪਾਦਕਾਂ ਨੂੰ ਲੱਭੋ + producers_signup_title: ਇੱਕ ਉਤਪਾਦਕ ਦੇ ਤੌਰ ਤੇ ਸਾਈਨ ਅੱਪ ਕਰੋ + producers_signup_headline: ਭੋਜਨ ਉਤਪਾਦਕ, ਅਧਿਕਾਰਤ। + producers_signup_motivation: ਆਪਣਾ ਭੋਜਨ ਵੇਚੋ ਅਤੇ ਵਿਭਿੰਨ ਨਵੇਂ ਬਾਜ਼ਾਰਾਂ ਨੂੰ ਆਪਣੀਆਂ ਕਹਾਣੀਆਂ ਦੱਸੋ। ਹਰ ਓਵਰਹੈਡ ਤੇ ਸਮਾਂ ਅਤੇ ਪੈਸਾ ਬਚਾਓ। ਅਸੀਂ ਬਿਨਾਂ ਜੋਖਮ ਦੇ ਨਵੀਨਤਾ ਦਾ ਸਮਰਥਨ ਕਰਦੇ ਹਾਂ। ਅਸੀਂ ਖੇਡ ਦੇ ਮੈਦਾਨ ਨੂੰ ਸਾਰਿਆਂ ਲਈ ਬਰਾਬਰ ਕਰ ਦਿੱਤਾ ਹੈ। + producers_signup_send: ਹੁਣੇ ਸ਼ਾਮਲ ਹੋਵੋ + producers_signup_enterprise: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੇ ਖਾਤੇ + producers_signup_studies: ਸਾਡੇ ਉਤਪਾਦਕ ਦੀਆਂ ਕਹਾਣੀਆਂ। + producers_signup_cta_headline: ਹੁਣੇ ਸ਼ਾਮਲ ਹੋਵੋ! + producers_signup_cta_action: ਹੁਣੇ ਸ਼ਾਮਲ ਹੋਵੋ + producers_signup_detail: ਇੱਥੇ ਵੇਰਵਾ ਹੈ. + producer: ਉਤਪਾਦਕ + products_item: ਆਈਟਮ + products_description: ਵਰਣਨ + products_variant: ਵੇਰੀਐਂਟ + products_quantity: ਮਾਤਰਾ + products_available: ਉਪਲੱਬਧ? + products_producer: "ਉਤਪਾਦਕ" + products_price: "ਕੀਮਤ" + name_or_sku: "ਨਾਂ ਜਾਂ SKU" + register_title: ਰਜਿਸਟਰ + sell_title: "ਰਜਿਸਟਰ" + sell_headline: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਪ੍ਰਾਪਤ ਕਰੋ!" + sell_motivation: "ਆਪਣੀ ਸਭ ਤੋਂ ਵਧੀਆ ਭੋਜਨ ਸਮੱਗਰੀ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ।" + sell_producers: "ਉਤਪਾਦਕ" + sell_hubs: "ਹੱਬ" + sell_groups: "ਸਮੂਹ" + sell_producers_detail: "ਆਪਣੇ ਕਾਰੋਬਾਰ ਲਈ OFN ਤੇ ਕੁਝ ਹੀ ਮਿੰਟਾਂ ਵਿੱਚ ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਸੇਟ ਕਰੋ। ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੇ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਇੱਕ ਔਨਲਾਈਨ ਸਟੋਰ ਵਿੱਚ ਅੱਪਗ੍ਰੇਡ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਆਪਣੇ ਉਤਪਾਦ ਸਿੱਧੇ ਗਾਹਕਾਂ ਨੂੰ ਵੇਚ ਸਕਦੇ ਹੋ।" + sell_hubs_detail: "OFN ਤੇ ਆਪਣੇ ਫੂਡ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਜਾਂ ਸੰਸਥਾ ਲਈ ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਸੇਟ ਕਰੋ। ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਮਲਟੀ-ਪ੍ਰੋਡਿਊਸਰ ਦੀ ਸ਼ਾਪ ਤੇ ਅੱਪਗ੍ਰੇਡ ਕਰ ਸਕਦੇ ਹੋ।" + sell_groups_detail: "ਆਪਣੇ ਖੇਤਰ ਜਾਂ ਆਪਣੀ ਸੰਸਥਾ ਲਈ ਐਂਟਰਪ੍ਰਾਈਜ਼ਾਂ (ਉਤਪਾਦਕਾਂ ਅਤੇ ਹੋਰ ਭੋਜਨ ਐਂਟਰਪ੍ਰਾਈਜ਼) ਦੀ ਇੱਕ ਅਨੁਕੂਲਿਤ ਡਾਇਰੈਕਟਰੀ ਸਥਾਪਤ ਕਰੋ।" + sell_user_guide: "ਸਾਡੀ ਉਪਭੋਗਤਾ ਗਾਈਡ ਵਿੱਚ ਹੋਰ ਜਾਣੋ।" + sell_listing_price: "OFN ਤੇ ਲਿਸਟਿੰਗ ਕਰਨਾ ਮੁਫ਼ਤ ਹੈ। ਇਹ $500 ਤੱਕ ਦੀ ਮਹੀਨਾਵਾਰ ਵਿਕਰੀ ਦੇ ਨਾਲ OFN ਉਤੇ ਇੱਕ ਸ਼ਾਪ ਖੋਲ੍ਹਣ ਅਤੇ ਚਲਾਉਣ ਲਈ ਮੁਫ਼ਤ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਇਸਤੋਂ ਜ਼ਿਆਦਾ ਵੇਚਦੇ ਹੋ ਤਾਂ ਤੁਸੀਂ ਵਿਕਰੀ ਦੇ 1% ਤੋਂ 3% ਵਿਚਕਾਰ ਆਪਣਾ ਕਮਿਊਨਿਟੀ ਯੋਗਦਾਨ ਚੁਣ ਸਕਦੇ ਹੋ। ਕੀਮਤ ਬਾਰੇ ਹੋਰ ਜਾਨਣ ਲਈ ਸੌਫਟਵੇਅਰ ਪਲੇਟਫਾਰਮ ਸੈਕਸ਼ਨ ਤੇ \"ਸਾਡੇ ਬਾਰੇ\" ਵਾਲੇ ਲਿੰਕ ਰਾਹੀਂ ਜਾਓ।" + sell_embed: "ਅਸੀਂ ਇੱਕ OFN ਸ਼ਾਪ ਨੂੰ ਤੁਹਾਡੀ ਆਪਣੀ ਕਸਟਮਾਈਜ਼ ਕੀਤੀ ਵੈਬਸਾਈਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹਾਂ ਜਾਂ ਤੁਹਾਡੇ ਖੇਤਰ ਲਈ ਇੱਕ ਕਸਟਮਾਈਜ਼ ਕੀਤੀ ਸਥਾਨਕ ਫੂਡ ਨੈਟਵਰਕ ਵੈਬਸਾਈਟ ਬਣਾ ਸਕਦੇ ਹਾਂ।" + sell_ask_services: "OFN ਸੇਵਾਵਾਂ ਬਾਰੇ ਸਾਨੂੰ ਪੁੱਛੋ।" + shops_title: ਸ਼ਾਪਾਂ + shops_headline: ਖਰੀਦਦਾਰੀ, ਪਰਿਵਰਤਿਤ + shops_text: ਭੋਜਨ ਸਾਈਕਲਾਂ ਵਿੱਚ ਵਧਦਾ ਹੈ, ਕਿਸਾਨ ਸਾਈਕਲਾਂ ਵਿੱਚ ਵਾਢੀ ਕਰਦੇ ਹਨ, ਅਤੇ ਅਸੀਂ ਸਾਈਕਲਾਂ ਵਿੱਚ ਭੋਜਨ ਆਰਡਰ ਕਰਦੇ ਹਾਂ। ਜੇਕਰ ਤੁਹਾਨੂੰ ਕੋਈ ਆਰਡਰ ਸਾਈਕਲ ਬੰਦ ਮਿਲਦਾ ਹੈ, ਤਾਂ ਜਲਦ ਹੀ ਦੁਬਾਰਾ ਜਾਂਚ ਕਰਿਓ। + shops_signup_title: ਇੱਕ ਹੱਬ ਦੇ ਰੂਪ ਵਿੱਚ ਸਾਈਨ ਅਪ ਕਰੋ + shops_signup_headline: ਫੂਡ ਹੱਬ, ਅਸੀਮਤ। + shops_signup_motivation: ਤੁਹਾਡਾ ਮਾਡਲ ਜੋ ਵੀ ਹੋਵੇ, ਅਸੀਂ ਤੁਹਾਡਾ ਸਮਰਥਨ ਕਰਦੇ ਹਾਂ। ਭਾਵੇਂ ਤੁਸੀਂ ਕਿੰਨੇ ਵੀ ਬਦਲਾਵ ਕਰੋ, ਅਸੀਂ ਤੁਹਾਡੇ ਨਾਲ ਹਾਂ। ਅਸੀਂ ਗੈਰ-ਮੁਨਾਫ਼ਾ, ਸੁਤੰਤਰ ਅਤੇ ਖੁਲੇ-ਸਰੋਤ ਹਾਂ। ਅਸੀਂ ਉਹ ਸੌਫਟਵੇਅਰ ਪਾਰਟਨਰ ਹਾਂ ਜਿਨ੍ਹਾਂ ਦਾ ਤੁਸੀਂ ਸੁਪਨਾ ਵੇਖਿਆ ਹੈ। + shops_signup_action: ਹੁਣੇ ਸ਼ਾਮਲ ਹੋਵੋ + shops_signup_pricing: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੇ ਖਾਤੇ + shops_signup_stories: ਸਾਡੇ ਹੱਬ ਤੋਂ ਕਹਾਣੀਆਂ। + shops_signup_help: ਅਸੀਂ ਮਦਦ ਕਰਨ ਲਈ ਤਿਆਰ ਹਾਂ। + shops_signup_help_text: ਤੁਹਾਨੂੰ ਇੱਕ ਬਿਹਤਰ ਰਿਟਰਨ ਦੀ ਲੋੜ ਹੈ। ਤੁਹਾਨੂੰ ਨਵੇਂ ਖਰੀਦਦਾਰਾਂ ਅਤੇ ਲੌਜਿਸਟਿਕ ਭਾਈਵਾਲਾਂ ਦੀ ਲੋੜ ਹੈ। ਤੁਹਾਨੂੰ ਆਪਣੀ ਕਹਾਣੀ ਥੋਕ, ਖੁਦਰਾ ਅਤੇ ਰਸੋਈ ਦੇ ਮੇਜ਼ ਉਤੇ ਦੱਸੇ ਜਾਣੀ ਚਾਹੀਦੀ ਹੈ। + shops_signup_detail: ਇੱਥੇ ਵੇਰਵਾ ਹੈ. + orders: "ਆਰਡਰ" + orders_fees: "ਫ਼ੀਸ..." + orders_edit_title: "ਖਰੀਦਦਾਰੀ ਕਾਰਟ" + orders_edit_headline: "ਤੁਹਾਡਾ ਸ਼ਾਪਿੰਗ ਕਾਰਟ" + orders_edit_time: "ਇਹਨਾਂ ਲਈ ਆਰਡਰ ਤਿਆਰ ਹੈ" + orders_edit_continue: "ਖਰੀਦਦਾਰੀ ਜਾਰੀ ਰੱਖੋ" + orders_edit_checkout: "ਚੈਕਆਊਟ" + orders_form_empty_cart: "ਖਾਲੀ ਕਾਰਟ" + orders_form_update_cart: "ਅੱਪਡੇਟ" + orders_form_subtotal: "ਉਤਪਾਦ ਉਪ-ਕੁਲ" + orders_form_total: "ਕੁੱਲ" + orders_oc_expired_headline: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ ਆਰਡਰ ਬੰਦ ਹੋ ਗਏ ਹਨ" + orders_oc_expired_text: "ਮਾਫ਼ ਕਰਨਾ, ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ ਆਰਡਰ %{time} ਪਹਿਲਾਂ ਬੰਦ ਹੋ ਗਏ ਹਨ! ਕਿਰਪਾ ਕਰਕੇ ਇਹ ਵੇਖਣ ਲਈ ਸਿੱਧੇ ਆਪਣੇ ਹੱਬ ਨਾਲ ਸੰਪਰਕ ਕਰੋ ਕਿ ਕਿ ਉਹ ਲੇਟ ਆਰਡਰ ਸਵੀਕਾਰ ਕਰ ਸਕਦੇ ਹਨ।" + orders_oc_expired_text_others_html: "ਮਾਫ਼ ਕਰਨਾ, ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ ਆਰਡਰ %{time} ਪਹਿਲਾਂ ਬੰਦ ਹੋ ਗਏ ਹਨ! ਕਿਰਪਾ ਕਰਕੇ ਇਹ ਵੇਖਣ ਲਈ ਸਿੱਧੇ ਆਪਣੇ ਹੱਬ ਨਾਲ ਸੰਪਰਕ ਕਰੋ ਕਿ ਕੀ ਉਹ ਲੇਟ ਆਰਡਰ %{link} ਨੂੰ ਸਵੀਕਾਰ ਕਰ ਸਕਦੇ ਹਨ।" + orders_oc_expired_text_link: "ਜਾਂ ਇਸ ਹੱਬ ਤੇ ਉਪਲਬਧ ਹੋਰ ਆਰਡਰ ਸਾਈਕਲ ਵੇਖੋ" + orders_oc_expired_email: "ਈਮੇਲ:" + orders_oc_expired_phone: "ਫੋਨ:" + orders_show_title: "ਆਰਡਰ ਦੀ ਪੁਸ਼ਟੀ" + orders_show_time: "ਇਸ ਤੇ ਆਰਡਰ ਤਿਆਰ ਹੈ" + orders_show_order_number: "ਆਰਡਰ #%{number}" + orders_show_cancelled: "ਰੱਦ ਕੀਤਾ ਗਿਆ" + orders_show_confirmed: "ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ" + orders_your_order_has_been_cancelled: "ਤੁਹਾਡਾ ਆਰਡਰ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ" + orders_could_not_cancel: "ਮਾਫ਼ ਕਰਨਾ, ਆਰਡਰ ਰੱਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ" + orders_cannot_remove_the_final_item: "ਕਿਸੇ ਆਰਡਰ ਤੋਂ ਅੰਤਿਮ ਆਈਟਮ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਕਿਰਪਾ ਕਰਕੇ ਇਸਦੀ ਬਜਾਏ ਆਰਡਰ ਨੂੰ ਰੱਦ ਕਰੋ।" + orders_bought_items_notice: + one: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ ਇੱਕ ਵਾਧੂ ਆਈਟਮ ਦੀ ਪਹਿਲਾਂ ਹੀ ਪੁਸ਼ਟੀ ਕੀਤੀ ਜਾ ਚੁਕੀ ਹੈ" + few: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ %{count} ਵਾਧੂ ਆਈਟਮਾਂ ਦੀ ਪੁਸ਼ਟੀ ਪਹਿਲਾਂ ਹੀ ਹੋ ਚੁੱਕੀ ਹੈ" + many: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ %{count} ਵਾਧੂ ਆਈਟਮਾਂ ਦੀ ਪੁਸ਼ਟੀ ਪਹਿਲਾਂ ਹੀ ਹੋ ਚੁੱਕੀ ਹੈ" + other: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਲਈ %{count} ਵਾਧੂ ਆਈਟਮਾਂ ਦੀ ਪੁਸ਼ਟੀ ਪਹਿਲਾਂ ਹੀ ਹੋ ਚੁੱਕੀ ਹੈ" + orders_bought_edit_button: "ਪੁਸ਼ਟੀ ਕੀਤੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰੋ" + orders_bought_already_confirmed: "* ਪਹਿਲਾਂ ਹੀ ਪੁਸ਼ਟੀ ਹੋ ਚੁਕੀ ਹੈ" + orders_confirm_cancel: "ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਇਸ ਆਰਡਰ ਨੂੰ ਰੱਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?" + order_processed_successfully: "ਤੁਹਾਡੇ ਆਰਡਰ ਦੀ ਸਫਲਤਾਪੂਰਵਕ ਸੰਸਾਧਿਤ ਕੀਤੀ ਗਈ ਹੈ" + products_cart_distributor_choice: "ਤੁਹਾਡੇ ਆਰਡਰ ਲਈ ਵਿਤਰਕ:" + products_cart_distributor_change: "ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਉਤਪਾਦ ਨੂੰ ਆਪਣੀ ਕਾਰਟ ਵਿੱਚ ਜੋੜਦੇ ਹੋ ਤਾਂ ਇਸ ਆਰਡਰ ਲਈ ਤੁਹਾਡੇ ਵਿਤਰਕ ਨੂੰ %{name} ਵਿੱਚ ਬਦਲ ਦਿੱਤਾ ਜਾਵੇਗਾ।" + products_cart_distributor_is: "ਇਸ ਆਰਡਰ ਲਈ ਤੁਹਾਡਾ ਵਿਤਰਕ %{name} ਹੈ।" + products_distributor_error: "ਕਿਰਪਾ ਕਰਕੇ ਕਿਸੇ ਹੋਰ ਵਿਤਰਕ ਨਾਲ ਖਰੀਦਦਾਰੀ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ %{link} ਉਤੇ ਆਪਣਾ ਆਰਡਰ ਪੂਰਾ ਕਰੋ।" + products_oc: "ਤੁਹਾਡੇ ਆਰਡਰ ਲਈ ਆਰਡਰ ਸਾਈਕਲ:" + products_oc_change: "ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਉਤਪਾਦ ਨੂੰ ਆਪਣੀ ਕਾਰਟ ਵਿੱਚ ਜੋੜਦੇ ਹੋ ਤਾਂ ਇਸ ਆਰਡਰ ਲਈ ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ %{name} ਵਿੱਚ ਬਦਲ ਦਿੱਤਾ ਜਾਵੇਗਾ।" + products_oc_is: "ਇਸ ਆਰਡਰ ਲਈ ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ %{name} ਹੈ।" + products_oc_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਵੱਖਰੇ ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਖਰੀਦਦਾਰੀ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ %{link} ਤੋਂ ਆਪਣਾ ਆਰਡਰ ਪੂਰਾ ਕਰੋ।" + products_oc_current: "ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਆਰਡਰ ਸਾਈਕਲ" + products_max_quantity: ਅਧਿਕਤਮ ਮਾਤਰਾ + products_distributor: ਵਿਤਰਕ + products_distributor_info: ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੇ ਆਰਡਰ ਲਈ ਇੱਕ ਵਿਤਰਕ ਚੁਣਦੇ ਹੋ, ਤਾਂ ਉਹਨਾਂ ਦਾ ਪਤਾ ਅਤੇ ਪਿਕਅੱਪ ਸਮਾਂ ਇੱਥੇ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤਾ ਜਾਵੇਗਾ। + password: ਪਾਸਵਰਡ + remember_me: ਮੈਨੂੰ ਯਾਦ ਰੱਖੋ + are_you_sure: "ਕੀ ਤੁਹਾਨੂੰ ਯਕੀਨ ਹੈ?" + orders_open: "ਆਰਡਰ ਖੁੱਲ੍ਹੇ ਹਨ" + closing: "ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ" + going_back_to_home_page: "ਤੁਹਾਨੂੰ ਹੋਮ ਪੇਜ ਉਤੇ ਵਾਪਸ ਲੈ ਜਾ ਰਹੇ ਹਾਂ" + creating: ਬਣਾ ਰਹੇ ਹਨ + updating: ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ + failed_to_create_enterprise: "ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ ਰਿਹਾ।" + failed_to_create_enterprise_unknown: "ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ।\\nਕਿਰਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਸਾਰੇ ਖੇਤਰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਭਰੇ ਹੋਏ ਹਨ।" + failed_to_update_enterprise_unknown: "ਤੁਹਾਡੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਅਪਡੇਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲ।\\nਕਿਰਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਸਾਰੇ ਖੇਤਰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਭਰੇ ਹੋਏ ਹਨ।" + enterprise_confirm_delete_message: "ਇਹ ਉਸ %{product} ਨੂੰ ਵੀ ਮਿਟਾ ਦੇਵੇਗਾ ਜੋ ਇਹ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸਪਲਾਈ ਕਰਦਾ ਹੈ। ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਇਸ ਨੂੰ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + order_not_saved_yet: "ਤੁਹਾਡਾ ਆਰਡਰ ਅਜੇ ਤੱਕ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਸਾਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਕੁਝ ਸਕਿੰਟ ਦਿਓ!" + filter_by: "ਇਸਦੇ ਅਨੁਸਾਰ ਫਿਲਟਰ ਕਰੋ" + hide_filters: "ਫਿਲਟਰ ਲੁਕਾਓ" + one_filter_applied: "1 ਫਿਲਟਰ ਲਾਗੂ ਕੀਤਾ ਗਿਆ" + x_filters_applied: "ਫਿਲਟਰ ਲਾਗੂ ਕੀਤੇ ਗਏ" + submitting_order: "ਤੁਹਾਡਾ ਆਰਡਰ ਜਮ੍ਹਾਂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ: ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ" + confirm_hub_change: "ਕੀ ਤੁਸੀਂ ਯਕੀਨਨ ਹੋ? ਇਹ ਤੁਹਾਡੇ ਚੁਣੇ ਹੋਏ ਹੱਬ ਨੂੰ ਬਦਲ ਦੇਵੇਗਾ ਅਤੇ ਤੁਹਾਡੇ ਖਰੀਦਦਾਰੀ ਦੇ ਕਾਰਟ ਵਿੱਚ ਰੱਖੀ ਕਿਸੇ ਵੀ ਆਈਟਮ ਨੂੰ ਹਟਾ ਦੇਵੇਗਾ।" + confirm_oc_change: "ਕੀ ਤੁਸੀਂ ਯਕੀਨਨ ਹੋ? ਇਹ ਤੁਹਾਡੇ ਚੁਣੇ ਹੋਏ ਆਰਡਰ ਸਾਈਕਲ ਨੂੰ ਬਦਲ ਦੇਵੇਗਾ ਅਤੇ ਤੁਹਾਡੀ ਖਰੀਦਦਾਰੀ ਦੇ ਕਾਰਟ ਵਿੱਚ ਰੱਖੇ ਕਿਸੇ ਵੀ ਆਈਟਮ ਨੂੰ ਹਟਾ ਦੇਵੇਗਾ।" + location_placeholder: "ਕੋਈ ਲੋਕੇਸ਼ਨ ਟਾਈਪ ਕਰੋ..." + error_required: "ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ" + error_number: "ਨੰਬਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + error_email: "ਈਮੇਲ ਪਤਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + error_not_found_in_database: "%{name} ਡਾਟਾਬੇਸ ਵਿੱਚ ਨਹੀਂ ਮਿਲਿਆ" + error_not_primary_producer: "%{name} ਨੂੰ ਇੱਕ ਉਤਪਾਦਕ ਵਜੋਂ ਸਮਰੱਥ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ" + error_no_permission_for_enterprise: "\\\"%{name}\\\": ਤੁਹਾਨੂੰ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ" + item_handling_fees: "ਆਈਟਮ ਹੈਂਡਲਿੰਗ ਫੀਸ (ਆਈਟਮ ਦੇ ਕੁੱਲ ਵਿੱਚ ਸ਼ਾਮਲ ਹੈ)" + january: "ਜਨਵਰੀ" + february: "ਫਰਵਰੀ" + march: "ਮਾਰਚ" + april: "ਅਪ੍ਰੈਲ" + may: "ਮਈ" + june: "ਜੂਨ" + july: "ਜੁਲਾਈ" + august: "ਅਗਸਤ" + september: "ਸਤੰਬਰ" + october: "ਅਕਤੂਬਰ" + november: "ਨਵੰਬਰ" + december: "ਦਸੰਬਰ" + email_not_found: "ਈਮੇਲ ਪਤਾ ਨਹੀਂ ਮਿਲਿਆ" + email_unconfirmed: "ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਰੀਸੈਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਆਪਣੇ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨੀ ਪਵੇਗੀ।" + email_required: "ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਪਤਾ ਦੇਣਾ ਚਾਹੀਦਾ ਹੈ" + logging_in: "ਇੱਕ ਪਲ ਰੁਕੋ, ਅਸੀਂ ਤੁਹਾਨੂੰ ਲੌਗਇਨ ਕਰ ਰਹੇ ਹਾਂ" + signup_email: "ਤੁਹਾਡਾ ਈਮੇਲ" + choose_password: "ਇੱਕ ਪਾਸਵਰਡ ਚੁਨੋ" + confirm_password: "ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + action_signup: "ਹੁਣੇ ਸਾਈਨ ਅੱਪ ਕਰੋ" + forgot_password: "ਪਾਸਵਰਡ ਭੁੱਲ ਗਏ?" + password_reset_sent: "ਤੁਹਾਡੇ ਪਾਸਵਰਡ ਨੂੰ ਰੀਸੈਟ ਕਰਨ ਦੀਆਂ ਹਦਾਇਤਾਂ ਵਾਲੀ ਇੱਕ ਈਮੇਲ ਭੇਜੀ ਗਈ ਹੈ!" + reset_password: "ਪਾਸਵਰਡ ਰੀਸੈਟ ਕਰੋ" + update_and_recalculate_fees: "ਅਪਡੇਟ ਕਰੋ ਅਤੇ ਫੀਸਾਂ ਦੀ ਮੁੜ ਗਣਨਾ ਕਰੋ" + registration: + steps: + introduction: + registration_greeting: "ਸਤ ਸ੍ਰੀ ਅਕਾਲ!" + registration_intro: "ਹੁਣ ਤੁਸੀਂ ਆਪਣੇ ਉਤਪਾਦਕ ਜਾਂ ਹੱਬ ਲਈ ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕਦੇ ਹੋ" + registration_checklist: "ਮੈਨੂੰ ਕੀ ਚਾਹੀਦਾ ਹੈ?" + registration_time: "5-10 ਮਿੰਟ" + registration_enterprise_address: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਪਤਾ" + registration_contact_details: "ਪ੍ਰਾਥਮਿਕ ਸੰਪਰਕ ਵੇਰਵੇ" + registration_logo: "ਤੁਹਾਡੇ ਲੋਗੋ ਦੀ ਫੋਟੋ" + registration_promo_image: "ਤੁਹਾਡੀ ਪ੍ਰੋਫ਼ਾਈਲ ਲਈ ਲੈਂਡਸਕੇਪ ਫੋਟੋ" + registration_about_us: "'ਸਾਡੇ ਬਾਰੇ ਵਿੱਚ' ਦਾ ਟੈਕਸਟ" + registration_outcome_headline: "ਮੈਨੂੰ ਕੀ ਮਿਲਦਾ ਹੈ?" + registration_outcome1_html: "ਤੁਹਾਡੀ ਪ੍ਰੋਫਾਈਲ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਤੁਹਾਨੂੰ ਲੱਭਣ ਅਤੇ ਸੰਪਰਕ ਕਰਨ ਵਿੱਚ ਲੋਕਾਂ ਦੀ ਮਦਦ ਕਰਦੀ ਹੈ।" + registration_outcome2: "ਤੁਹਾਡੀ ਸਮਾਜਿਕ ਅਤੇ ਔਨਲਾਈਨ ਮੌਜੂਦਗੀ ਲਈ ਕਨੇਕਸ਼ਨਾਂ ਨੂੰ ਵਧਾਉਣ ਵਿੱਚ ਮਦਦ ਕਰਨ ਲਈ, ਆਪਣੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਕਹਾਣੀ ਦੱਸਣ ਲਈ ਇਸ ਥਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ।" + registration_outcome3: "ਇਹ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਵਪਾਰ ਕਰਨ, ਜਾਂ ਔਨਲਾਈਨ ਸਟੋਰ ਖੋਲ੍ਹਣ ਵੱਲ ਪਹਿਲਾ ਕਦਮ ਹੈ।" + registration_action: "ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ!" + details: + title: "ਮਾਤਰਾ" + headline: "ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ" + enterprise: "ਵਾਹ! ਪਹਿਲਾਂ ਸਾਨੂੰ ਤੁਹਾਡੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਬਾਰੇ ਥੋੜ੍ਹਾ ਜਿਹਾ ਜਾਣਨ ਦੀ ਲੋੜ ਹੈ:" + producer: "ਵਾਹ! ਪਹਿਲਾਂ ਸਾਨੂੰ ਤੁਹਾਡੇ ਖੇਤ ਬਾਰੇ ਥੋੜ੍ਹਾ ਜਿਹਾ ਜਾਣਨ ਦੀ ਲੋੜ ਹੈ:" + enterprise_name_field: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਨਾਮ:" + producer_name_field: "ਖੇਤ ਦਾ ਨਾਮ:" + producer_name_field_placeholder: "ਜਿਵੇਂ ਕਿ ਚਾਰਲੀ ਦਾ ਸ਼ਾਨਦਾਰ ਫਾਰਮ" + producer_name_field_error: "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਉੱਦਮ ਲਈ ਇੱਕ ਵਿਲੱਖਣ ਨਾਮ ਚੁਣੋ" + address1_field: "ਪਤਾ ਲਾਈਨ 1:" + address1_field_placeholder: "ਜਿਵੇਂ ਕਿ 123 ਕਰੈਨਬੇਰੀ ਡਰਾਈਵ" + address1_field_error: "ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਪਤਾ ਦਰਜ ਕਰੋ" + address2_field: "ਪਤਾ ਲਾਈਨ 2:" + suburb_field: "ਉਪਨਗਰ:" + suburb_field_placeholder: "ਜਿਵੇਂ ਕਿ ਨੌਰਥਕੋਟ" + suburb_field_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਉਪਨਗਰ ਦਾਖਲ ਕਰੋ" + postcode_field: "ਪਿਨਕੋਡ:" + postcode_field_placeholder: "ਜਿਵੇਂ ਕਿ 3070" + postcode_field_error: "ਪਿਨਕੋਡ ਲੋੜੀਂਦਾ ਹੈ" + state_field: "ਰਾਜ:" + state_field_error: "ਰਾਜ ਲੋੜੀਂਦਾ ਹੈ" + country_field: "ਦੇਸ਼:" + country_field_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਦੇਸ਼ ਚੁਣੋ" + map_location: "ਮੈਪ ਲੋਕੇਸ਼ਨ" + locate_address: "ਮੈਪ ਉਤੇ ਪਤਾ ਲੱਭੋ" + drag_pin: "ਜੇਕਰ ਸਹੀ ਨਹੀਂ ਹੈ ਤਾਂ ਪਿੰਨ ਨੂੰ ਸਹੀ ਥਾਂ ਤੇ ਡ੍ਰੈਗ ਕਰੋ ਅਤੇ ਡਰੌਪ ਕਰੋ।" + confirm_address: "ਮੈਂ ਪੁਸ਼ਟੀ ਕਰਦਾ ਹਾਂ ਕਿ ਨਕਸ਼ੇ ਉਤੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਦਰਸਾਈ ਗਈ ਪੋਜੀਸ਼ਨ ਸਹੀ ਹੈ।" + drag_map_marker: "ਪੇਂਡੂ ਖੇਤਰਾਂ ਵਿੱਚ ਕੰਮ ਕਰਨ ਵਾਲੇ ਬਹੁਤ ਸਾਰੇ ਉਤਪਾਦਕਾਂ ਦੇ ਕਾਰਨ, ਨਕਸ਼ਿਆਂ ਦੀ ਸਟੀਕਤਾ ਵਿੱਚ ਹਮੇਸ਼ਾ ਸੁਧਾਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ। ਪਿੰਨ ਨੂੰ ਦਬਾਉਣ ਜਾਂ ਟੈਪ ਕਰਕੇ ਪਿੰਨ ਨੂੰ ਹਿਲਾਉਣ ਲਈ ਉਪਰ ਦਿੱਤੇ ਮੈਪ ਨਾਲ ਇੰਟਰੈਕਟ ਕਰਕੇ ਬਿਹਤਰ ਢੰਗ ਨਾਲ ਇਹ ਸਮਝਣ ਵਿੱਚ ਸਾਡੀ ਮਦਦ ਕਰੋ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਸਥਿਤ ਹੋ ਅਤੇ ਫਿਰ ਤੁਹਾਡੇ ਗਿਆਨ ਦੇ ਆਧਾਰ ਤੇ ਜ਼ਿਆਦਾ ਸਟੀਕ ਹੋਣ ਵਾਲੇ ਸਥਾਨ ਤੇ ਖਿੱਚਣਾ।" + contact: + title: "ਸੰਪਰਕ" + who_is_managing_enterprise: "%{enterprise} ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਕੌਣ ਜ਼ਿੰਮੇਵਾਰ ਹੈ?" + contact_field: "ਪ੍ਰਾਇਮਰੀ ਸੰਪਰਕ" + contact_field_placeholder: "ਸੰਪਰਕ ਨਾਮ" + contact_field_required: "ਤੁਹਾਨੂੰ ਇੱਕ ਪ੍ਰਾਇਮਰੀ ਸੰਪਰਕ ਦਰਜ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।" + phone_field: "ਫੋਨ ਨੰਬਰ" + whatsapp_phone_field: "WhatsApp ਫੋਨ ਨੰਬਰ" + whatsapp_phone_tooltip: "ਇਹ ਨੰਬਰ ਤੁਹਾਡੇ ਪਬਲਿਕ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ WhatsApp ਲਿੰਕ ਦੇ ਰੂਪ ਵਿੱਚ ਖੋਲ੍ਹਣ ਲਈ ਵਿਖਾਇਆ ਜਾਵੇਗਾ।" + phone_field_placeholder: "ਜਿਵੇਂ ਕਿ (03) 1234 5678" + whatsapp_phone_field_placeholder: "ਜਿਵੇਂ ਕਿ +61 4 1234 5678" + type: + title: "ਕਿਸਮ" + headline: "%{enterprise} ਨੂੰ ਜੋੜਨ ਲਈ ਆਖਰੀ ਕਦਮ!" + question: "ਕੀ ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਹੋ?" + yes_producer: "ਹਾਂ, ਮੈਂ ਇੱਕ ਉਤਪਾਦਕ ਹਾਂ" + no_producer: "ਨਹੀਂ, ਮੈਂ ਉਤਪਾਦਕ ਨਹੀਂ ਹਾਂ" + producer_field_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਚੁਣੋ। ਕੀ ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਹੋ?" + yes_producer_help: "ਉਤਪਾਦਕ ਖਾਣ ਅਤੇ/ਜਾਂ ਪੀਣ ਲਈ ਸੁਆਦੀ ਚੀਜ਼ਾਂ ਬਣਾਉਂਦੇ ਹਨ। ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਹੋ ਜੇਕਰ ਤੁਸੀਂ ਇਸਨੂੰ ਉਗਾਉਂਦੇ ਹੋ, ਪਾਲਦੇ ਹੋ, ਬਰਿਊ ਕਰਦੇ ਹੋ, ਬੇਕ ਕਰਦੇ ਹੋ, ਖਮੀਰਦੇ ਹੋ, ਦੁੱਧ ਕੱਢਦੇ ਹੋ ਜਾਂ ਇਸਨੂੰ ਢਾਲਦੇ ਹੋ।" + no_producer_help: "ਜੇ ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਨਹੀਂ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਸ਼ਾਇਦ ਉਹ ਵਿਅਕਤੀ ਹੋ ਜੋ ਭੋਜਨ ਵੇਚਦੇ ਅਤੇ ਵੰਡਦੇ ਹੋ। ਤੁਸੀਂ ਇੱਕ ਹੱਬ, ਸਹਿਕਾਰੀ ਸਮਿਤੀ, ਖਰੀਦ ਸਮੂਹ, ਖੁਦਰਾ ਵਿਕਰੇਤਾ, ਥੋਕ ਵਿਕਰੇਤਾ ਜਾਂ ਕੋਈ ਹੋਰ ਹੋ ਸਕਦੇ ਹੋ।" + create_profile: "ਪ੍ਰੋਫਾਈਲ ਬਣਾਓ" + about: + title: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + headline: "ਬਹੁਤ ਵਧੀਆ!" + message: "ਆਓ ਹੁਣ ਇਸ ਬਾਰੇ ਵੇਰਵੇ ਨੂੰ ਬਾਹਰ ਕੱਢੀਏ" + success: "ਸਫਲਤਾ! %{enterprise} ਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ" + registration_exit_message: "ਜੇ ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਪੜਾਅ ਤੇ ਇਸ ਵਿਜ਼ਰਡ ਤੋਂ ਬਾਹਰ ਹੋ ਜਾਂਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਐਡਮਿਨ ਇੰਟਰਫੇਸ ਤੇ ਜਾ ਕੇ ਆਪਣੀ ਪ੍ਰੋਫਾਈਲ ਬਣਾਉਣਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ।" + enterprise_description: "ਸੰਖੇਪ ਵਰਣਨ" + enterprise_description_placeholder: "ਤੁਹਾਡੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਵਰਣਨ ਕਰਨ ਵਾਲਾ ਇੱਕ ਛੋਟਾ ਵਾਕ" + enterprise_long_desc: "ਲੰਬਾ ਵਰਣਨ" + enterprise_long_desc_placeholder: "ਇਹ ਤੁਹਾਡੇ ਲਈ ਤੁਹਾਡੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਕਹਾਣੀ ਦੱਸਣ ਦਾ ਮੌਕਾ ਹੈ - ਤੁਹਾਨੂੰ ਕਿਹੜੀ ਚੀਜ਼ ਵੱਖਰੀ ਅਤੇ ਸ਼ਾਨਦਾਰ ਬਣਾਉਂਦੀ ਹੈ? ਅਸੀਂ ਤੁਹਾਡੇ ਵਰਣਨ ਨੂੰ 600 ਅੱਖਰਾਂ ਜਾਂ 150 ਸ਼ਬਦਾਂ ਤੋਂ ਘੱਟ ਰੱਖਣ ਦਾ ਸੁਝਾਅ ਦੇਵਾਂਗੇ।" + enterprise_abn: "ABN" + enterprise_abn_placeholder: "ਜਿਵੇਂ - 99 123 456 789" + enterprise_acn: "ACN" + enterprise_acn_placeholder: "ਜਿਵੇਂ - 99 123 456 789" + enterprise_tax_required: "ਤੁਹਾਨੂੰ ਕਿਸੇ ਇੱਕ ਦੀ ਚੋਣ ਕਰਨੀ ਪਵੇਗੀ।" + images: + title: "ਫੋਟੋ" + headline: "ਧੰਨਵਾਦ!" + description: "ਆਓ ਤੁਹਾਡੀ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਸ਼ਾਨਦਾਰ ਬਣਾਉਣ ਲਈ ਕੁਝ ਸੁੰਦਰ ਫੋਟੋਆਂ ਅਪਲੋਡ ਕਰੀਏ! :)" + uploading: "ਅਪਲੋਡ ਹੋ ਰਿਹਾ ਹੈ..." + continue: "ਜਾਰੀ ਰੱਖੋ" + back: "ਵਾਪਸ" + logo: + select_logo: "ਕਦਮ 1. ਲੋਗੋ ਦੀ ਫੋਟੋ ਦੀ ਚੋਣ ਕਰੋ" + logo_tip: "ਟਿਪ: ਵਰਗ ਫੋਟੋ ਵਧੀਆ ਕੰਮ ਕਰਨਗੇ, ਤਰਜੀਹੀ ਤੌਰ 'ਤੇ ਘੱਟੋ-ਘੱਟ 300×300px" + logo_label: "ਇੱਕ ਲੋਗੋ ਫੋਟੋ ਚੁਣੋ" + logo_drag: "ਆਪਣੇ ਲੋਗੋ ਨੂੰ ਇੱਥੇ ਡ੍ਰੈਗ ਅਤੇ ਡਰੌਪ ਕਰੋ" + review_logo: "ਕਦਮ 2. ਆਪਣੇ ਲੋਗੋ ਦੀ ਸਮੀਖਿਆ ਕਰੋ" + review_logo_tip: "ਟਿਪ: ਵਧੀਆ ਨਤੀਜਿਆਂ ਲਈ, ਤੁਹਾਡੇ ਲੋਗੋ ਨੂੰ ਉਪਲਬਧ ਥਾਂ ਭਰਨੀ ਚਾਹੀਦੀ ਹੈ" + logo_placeholder: "ਤੁਹਾਡਾ ਲੋਗੋ ਅੱਪਲੋਡ ਹੋਣ ਤੋਂ ਬਾਅਦ ਸਮੀਖਿਆ ਲਈ ਇੱਥੇ ਦਿਖਾਈ ਦੇਵੇਗਾ" + promo: + select_promo_image: "ਕਦਮ 3. ਪ੍ਰੋਮੋ ਫੋਟੋ ਚੁਣੋ" + promo_image_tip: "ਟਿਪ: ਇੱਕ ਬੈਨਰ ਵਜੋਂ ਵਿਖਾਇਆ ਗਿਆ, ਤਰਜੀਹੀ ਕੀਤਾ ਸਾਈਜ਼ 1200×260px ਹੈ" + promo_image_label: "ਇੱਕ ਪ੍ਰੋਮੋ ਫੋਟੋ ਚੁਣੋ" + promo_image_drag: "ਆਪਣੇ ਪ੍ਰੋਮੋ ਨੂੰ ਇੱਥੇ ਡ੍ਰੈਗ ਕਰੋ ਅਤੇ ਡਰੌਪ ਕਰੋ" + review_promo_image: "ਕਦਮ 4. ਆਪਣੇ ਪ੍ਰੋਮੋ ਬੈਨਰ ਦੀ ਸਮੀਖਿਆ ਕਰੋ" + review_promo_image_tip: "ਟਿਪ: ਸਭ ਤੋਂ ਵਧੀਆ ਨਤੀਜਿਆਂ ਲਈ, ਤੁਹਾਡੀ ਪ੍ਰੋਮੋ ਫੋਟੋ ਨੂੰ ਉਪਲਬਧ ਥਾਂ ਭਰਨੀ ਚਾਹੀਦੀ ਹੈ" + promo_image_placeholder: "ਤੁਹਾਡਾ ਲੋਗੋ ਅੱਪਲੋਡ ਹੋਣ ਤੋਂ ਬਾਅਦ ਸਮੀਖਿਆ ਲਈ ਇੱਥੇ ਦਿਖਾਈ ਦੇਵੇਗਾ" + social: + title: "ਸਮਾਜਿਕ" + enterprise_final_step: "ਆਖਰੀ ਕਦਮ!" + enterprise_social_text: "ਲੋਕ %{enterprise} ਨੂੰ ਔਨਲਾਈਨ ਕਿਵੇਂ ਲੱਭ ਸਕਦੇ ਹਨ?" + website: "ਵੈਬਸਾਈਟ" + website_placeholder: "ਜਿਵੇਂ ਕਿ - openfoodnetwork.org.au" + facebook: "ਫੇਸਬੁੱਕ" + facebook_placeholder: "ਜਿਵੇਂ- www.facebook.com/PageNameHere" + linkedin: "ਲਿੰਕਡਇਨ" + linkedin_placeholder: "ਜਿਵੇਂ ਕਿ - www.linkedin.com/YourNameHere" + twitter: "ਟਵਿੱਟਰ" + twitter_placeholder: "ਜਿਵੇਂ ਕਿ - @twitter_handle" + instagram: "ਇੰਸਟਾਗ੍ਰਾਮ" + instagram_placeholder: "ਜਿਵੇਂ ਕਿ - @instagram_handle" + limit_reached: + headline: "ਓਹ ਨਹੀਂ!" + message: "ਤੁਸੀਂ ਇਸਦੀ ਹੱਦ ਤੱਕ ਪਹੁੰਚ ਗਏ ਹੋ!" + text: "ਤੁਸੀਂ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਰੱਖਣ ਦੀ ਸੰਖਿਆ ਦੀ ਸੀਮਾ ਤੱਕ ਪਹੁੰਚ ਗਏ ਹੋ ਜਿਨ੍ਹਾਂ ਦੀ ਤੁਹਾਨੂੰ ਇਜਾਜ਼ਤ ਹੈ" + finished: + headline: "ਸਮਾਪਤ!" + thanks: "%{enterprise} ਲਈ ਵੇਰਵੇ ਭਰਨ ਲਈ ਧੰਨਵਾਦ।" + login: "ਤੁਸੀਂ ਓਪਨ ਫੂਡ ਨੈੱਟਵਰਕ ਵਿੱਚ ਲੌਗਇਨ ਕਰਕੇ ਅਤੇ ਐਡਮਿਨ ਤੇ ਜਾ ਕੇ ਕਿਸੇ ਵੀ ਪੜਾਅ ਉਤੇ ਆਪਣੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਬਦਲ ਜਾਂ ਅੱਪਡੇਟ ਕਰ ਸਕਦੇ ਹੋ।" + action: "ਐਂਟਰਪ੍ਰਾਈਜ ਡੈਸ਼ਬੋਰਡ ਤੇ ਜਾਓ" + back: "ਵਾਪਸ" + continue: "ਜਾਰੀ ਰੱਖੋ" + action_or: "ਜਾਂ" + enterprise_limit: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੀਮਾ + shipping_method_destroy_error: "ਉਸ ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਹ ਇੱਕ ਆਰਡਰ ਦੁਆਰਾ ਹਵਾਲਾ ਦਿੱਤਾ ਗਿਆ ਹੈ: %{number}।" + fees: "ਫ਼ੀਸ" + fee_name: "ਫ਼ੀਸ ਦਾ ਨਾਮ" + fee_owner: "ਫ਼ੀਸ ਦਾ ਮਾਲਕ" + item_cost: "ਆਈਟਮ ਦੀ ਕੀਮਤ" + bulk: "ਥੋਕ" + shop_variant_quantity_min: "ਨਿਊਨਤਮ" + shop_variant_quantity_max: "ਅਧਿਕਤਮ" + contact: "ਸੰਪਰਕ" + follow: "ਫਾਲੋ ਕਰੋ" + shop_for_products_html: "%{enterprise} ਉਤਪਾਦਾਂ ਦੀ ਇੱਥੇ ਖਰੀਦਦਾਰੀ ਕਰੋ:" + change_shop: "ਸ਼ਾਪ ਨੂੰ ਇਸ ਵਿੱਚ ਬਦਲੋ:" + shop_at: "ਹੁਣੇ ਖਰੀਦਦਾਰੀ ਕਰੋ:" + admin_fee: "ਐਡਮਿਨ ਫੀਸ" + sales_fee: "ਵਿਕਰੀ ਫੀਸ" + packing_fee: "ਪੈਕਿੰਗ ਫੀਸ" + transport_fee: "ਟਰਾਂਸਪੋਰਟ ਫੀਸ" + fundraising_fee: "ਫੰਡਰੇਜ਼ਿੰਗ ਫੀਸ" + price_graph: "ਕੀਮਤ ਗ੍ਰਾਫ਼" + included_tax: "ਸ਼ਾਮਲ ਟੈਕਸ" + tax: "ਟੈਕਸ" + tax_amount_included: "%{amount} (ਸ਼ਾਮਲ)" + remove_tax: "ਟੈਕਸ ਹਟਾਓ" + balance: "ਬਾਕੀ ਰਕਮ" + transaction: "ਲੈਣ-ਦੇਣ" + transaction_date: "ਮਿਤੀ" + payment_state: "ਭੁਗਤਾਨ ਸਥਿਤੀ" + shipping_state: "ਸ਼ਿਪਿੰਗ ਸਥਿਤੀ" + value: "ਵਲਯੂ" + balance_due: "ਬਕਾਇਆ ਰਕਮ" + credit: "ਕ੍ਰੈਡਿਟ" + Paid: "ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ" + Ready: "ਤਿਆਰ" + not_visible: ਦਿਖਾਈ ਨਹੀਂ ਦੇ ਰਿਹਾ + you_have_no_orders_yet: "ਤੁਹਾਡੇ ਕੋਲ ਅਜੇ ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਹਨ" + show_only_complete_orders: "ਸਿਰਫ਼ ਪੂਰੇ ਆਰਡਰ ਵਿਖਾਓ" + successfully_created: '%{resource} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਬਣਾਇਆ ਗਿਆ ਹੈ!' + successfully_removed: '%{resource} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ!' + successfully_updated: '%{resource} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ!''' + running_balance: "ਚਲ ਰਹੀ ਬਕਾਇਆ ਰਕਮ" + outstanding_balance: "ਬਕਾਇਆ ਰਕਮ" + admin_enterprise_relationships: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਅਨੁਮਤੀਆਂ" + admin_enterprise_relationships_everything: "ਸਭ ਕੁਝ" + admin_enterprise_relationships_permits: "ਪਰਮਿਟ" + admin_enterprise_relationships_seach_placeholder: "ਖੋਜੋ" + admin_enterprise_relationships_button_create: "ਬਣਾਓ" + admin_enterprise_relationships_to: "ਨੂੰ" + admin_enterprise_groups: "ਐਂਟਰਪਰਾਈਜ਼ ਸਮੂਹ" + admin_enterprise_groups_name: "ਨਾਮ" + admin_enterprise_groups_owner: "ਮਾਲਕ'" + admin_enterprise_groups_on_front_page: "ਪਹਿਲੇ ਪੰਨੇ ਤੇ?" + admin_enterprise_groups_enterprise: "ਐਂਟਰਪ੍ਰਾਈਜ਼" + admin_enterprise_groups_data_powertip: "ਇਸ ਸਮੂਹ ਲਈ ਜ਼ਿੰਮੇਵਾਰ ਪ੍ਰਾਇਮਰੀ ਉਪਭੋਗਤਾ।" + admin_enterprise_groups_data_powertip_logo: "ਇਹ ਗਰੁੱਪ ਦਾ ਲੋਗੋ ਹੈ" + admin_enterprise_groups_data_powertip_promo_image: "ਇਹ ਫੋਟੋ ਗਰੁੱਪ ਪ੍ਰੋਫਾਈਲ ਦੇ ਸਿਖਰ ਤੇ ਪ੍ਰਦਰਸ਼ਿਤ ਹੁੰਦੀ ਹੈ" + admin_enterprise_groups_contact_phone_placeholder: "ਜਿਵੇਂ ਕਿ 98 7654 3210" + admin_enterprise_groups_contact_address1_placeholder: "ਜਿਵੇਂ ਕਿ 123 ਹਾਈ ਸਟਰੀਟ" + admin_enterprise_groups_contact_city: "ਉਪਨਗਰ" + admin_enterprise_groups_contact_city_placeholder: "ਜਿਵੇਂ ਕਿ ਨੌਰਥਕੋਟ" + admin_enterprise_groups_contact_zipcode: "ਪਿੰਨ ਕੋਡ" + admin_enterprise_groups_contact_zipcode_placeholder: "ਜਿਵੇਂ ਕਿ 3070" + admin_enterprise_groups_contact_state_id: "ਸਥਿਤੀ" + admin_enterprise_groups_contact_country_id: "ਦੇਸ਼" + admin_enterprise_groups_web_twitter: "ਜਿਵੇਂ - @the_prof" + admin_enterprise_groups_web_website_placeholder: "ਜਿਵੇਂ ਕਿ www.tuffles.com" + admin_order_cycles: "ਐਡਮਿਨ ਆਰਡਰ ਸਾਈਕਲ" + open: "ਖੁਲਾ ਹੈ" + close: "ਬੰਦ" + create: "ਬਣਾਓ" + search: "ਖੋਜੋ" + supplier: "ਸਪਲਾਇਰ" + product_name: "ਉਤਪਾਦ ਦਾ ਨਾਂ" + product_description: "ਉਤਪਾਦ ਵਰਣਨ" + permalink: "ਪਰਮਾਲਿੰਕ" + shipping_categories: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀਆਂ" + units: "ਯੂਨਿਟ ਸਾਈਜ਼" + coordinator: "ਕੋਆਰਡੀਨੇਟਰ" + distributor: "ਵਿਤਰਕ" + enterprise_fees: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ" + process_my_order: "ਮੇਰਾ ਆਰਡਰ ਸੰਸਾਧਿਤ ਕਰੋ" + delivery_instructions: ਡਿਲਿਵਰੀ ਨਿਰਦੇਸ਼ + delivery_method: ਡਿਲਿਵਰੀ ਦਾ ਢੰਗ + fee_type: "ਫੀਸ ਦੀ ਕਿਸਮ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + display: "ਡਿਸਪਲੇ" + tags: "ਟੈਗ" + calculator: "ਕੈਲਕੁਲੇਟਰ" + calculator_values: "ਕੈਲਕੁਲੇਟਰ ਵੈਲਯੂ" + calculator_settings_warning: "ਜੇਕਰ ਤੁਸੀਂ ਕੈਲਕੁਲੇਟਰ ਦੀ ਕਿਸਮ ਬਦਲ ਰਹੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਕੈਲਕੁਲੇਟਰ ਸੈਟਿੰਗਾਂ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਹਿਲਾਂ ਸੇਵ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ" + calculator_preferred_unit_error: "ਕਿਲੋ ਜਾਂ ਪੌਂਡ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + calculator_preferred_value_error: "ਅਵੈਧ ਇਨਪੁਟ। ਕਿਰਪਾ ਕਰਕੇ ਸਿਰਫ਼ ਨੰਬਰਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ। ਉਦਾਹਰਨ ਲਈ: 10, 5.5, -20" + flat_percent_per_item: "ਫਲੈਟ ਪ੍ਰਤੀਸ਼ਤ (ਪ੍ਰਤੀ ਆਈਟਮ)" + flat_rate_per_item: "ਫਲੈਟ ਪ੍ਰਤੀਸ਼ਤ (ਪ੍ਰਤੀ ਆਈਟਮ)" + flat_rate_per_order: "ਫਲੈਟ ਰੇਟ (ਪ੍ਰਤੀ ਆਰਡਰ)" + flexible_rate: "ਬਦਲਾਵ ਯੋਗ ਦਰ" + price_sack: "ਬੋਰੀ ਦੀ ਕੀਮਤ" + new_order_cycles: "ਨਵਾਂ ਆਰਡਰ ਸਾਈਕਲ" + new_order_cycle: "ਨਵਾਂ ਆਰਡਰ ਸਾਈਕਲ" + new_order_cycle_tooltip: "ਇੱਕ ਨਿਸ਼ਚਿਤ ਸਮੇਂ ਲਈ ਸ਼ਾਪ ਖੋਲ੍ਹੋ" + select_a_coordinator_for_your_order_cycle: "ਆਪਣੇ ਆਰਡਰ ਚੱਕਰ ਲਈ ਕੋਆਰਡੀਨੇਟਰ ਦੀ ਚੋਣ ਕਰੋ" + notify_producers: 'ਉਤਪਾਦਕਾਂ ਨੂੰ ਸੂਚਿਤ ਕਰੋ''' + edit_order_cycle: "ਆਰਡਰ ਸਾਈਕਲ ਦਾ ਸੰਪਾਦਨ ਕਰੋ" + roles: "ਭੂਮਿਕਾਵਾਂ" + update: "ਅੱਪਡੇਟ" + delete: ਹਟਾਓ + add_producer_property: "ਉਤਪਾਦਕ ਪ੍ਰਾਪਰਟੀ ਜੋੜੋ" + in_progress: "ਪ੍ਰਗਤੀ ਵਿੱਚ" + started_at: "ਇਸ ਸਮੇਂ ਸ਼ੁਰੂ ਹੋਇਆ" + queued: "ਕਤਾਰ ਵਿੱਚ" + scheduled_for: "ਇਸ ਲਈ ਸ਼ੈਡਿਊਲ ਕੀਤਾ ਗਿਆ" + customers: "ਗਾਹਕ" + please_select_hub: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਹੱਬ ਚੁਣੋ" + loading_customers: "ਗਾਹਕਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + no_customers_found: "ਕੋਈ ਗਾਹਕ ਨਹੀਂ ਲੱਭੇ" + go: "ਜਾਓ" + hub: "ਹੱਬ" + product: "ਉਤਪਾਦ" + price: "ਕੀਮਤ" + review: "ਸਮੀਖਿਆ" + save_changes: "ਬਦਲਾਵ ਸੇਵ ਕਰੋ" + order_saved: "ਆਰਡਰ ਸੇਵ ਕੀਤਾ ਗਿਆ" + no_products: ਕੋਈ ਉਤਪਾਦ ਨਹੀਂ + spree_admin_overview_enterprises_header: "ਮੇਰਾ ਐਂਟਰਪ੍ਰਾਈਜ਼" + spree_admin_overview_enterprises_footer: "ਮੇਰੇ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ" + spree_admin_enterprises_hubs_name: "ਨਾਮ" + spree_admin_enterprises_create_new: "ਨਵਾਂ ਬਣਾਓ" + spree_admin_enterprises_shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + spree_admin_enterprises_fees: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫ਼ੀਸ" + spree_admin_enterprises_none_create_a_new_enterprise: "ਇੱਕ ਨਵਾਂ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਬਣਾਓ" + spree_admin_enterprises_none_text: "ਤੁਹਾਡੇ ਕੋਲ ਅਜੇ ਕੋਈ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨਹੀਂ ਹੈ" + spree_admin_enterprises_tabs_hubs: "ਹੱਬ" + spree_admin_enterprises_producers_manage_products: "ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ" + spree_admin_enterprises_create_new_product: "ਇੱਕ ਨਵਾਂ ਉਤਪਾਦ ਬਣਾਓ" + spree_admin_single_enterprise_alert_mail_confirmation: "ਕਿਰਪਾ ਕਰਕੇ ਈਮੇਲ ਪਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + spree_admin_single_enterprise_alert_mail_sent: "ਅਸੀਂ ਇਸ ਨੂੰ ਇੱਕ ਈਮੇਲ ਭੇਜੀ ਹੈ" + spree_admin_overview_action_required: "ਕਾਰਵਾਈ ਲੋੜੀਂਦੀ ਹੈ" + spree_admin_overview_check_your_inbox: "ਕਿਰਪਾ ਕਰਕੇ ਹੋਰ ਹਦਾਇਤਾਂ ਲਈ ਆਪਣੇ ਇਨਬਾਕਸ ਦੀ ਜਾਂਚ ਕਰੋ। ਧੰਨਵਾਦ!" + spree_admin_unit_value: ਯੂਨਿਟ ਵੈਲਯੂ + spree_admin_unit_description: ਯੂਨਿਟ ਵਰਣਨ + spree_admin_variant_unit: ਵੇਰੀਐਂਟ ਯੂਨਿਟ + spree_admin_variant_unit_scale: ਵੇਰੀਐਂਟ ਯੂਨਿਟ ਸਕੇਲ + spree_admin_supplier: ਸਪਲਾਇਰ + spree_admin_product_category: ਉਤਪਾਦ ਸ਼੍ਰੇਣੀ + spree_admin_variant_unit_name: ਵੇਰੀਐਂਟ ਯੂਨਿਟ ਦਾ ਨਾਮ + unit_name: "ਯੂਨਿਟ ਦਾ ਨਾਮ" + change_package: "ਪੈਕੇਜ ਬਦਲੋ" + spree_admin_single_enterprise_hint: "ਸੰਕੇਤ: ਲੋਕਾਂ ਨੂੰ ਤੁਹਾਨੂੰ ਲੱਭਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਲਈ, ਇਸ ਦੇ ਹੇਠਾਂ ਆਪਣੀ ਦਿੱਖ ਨੂੰ ਚਾਲੂ ਕਰੋ" + spree_admin_eg_pickup_from_school: "ਜਿਵੇਂ ਕਿ 'ਪ੍ਰਾਇਮਰੀ ਸਕੂਲ ਤੋਂ ਪਿਕ-ਅੱਪ'" + spree_admin_eg_collect_your_order: "ਜਿਵੇਂ ਕਿ 'ਕਿਰਪਾ ਕਰਕੇ 123 ਇਮੇਜਿਨਰੀ ਸੇਂਟ, ਨੌਰਥਕੋਟ, 3070' ਤੋਂ ਆਪਣਾ ਆਰਡਰ ਲਵੋ" + spree_classification_primary_taxon_error: "ਟੈਕਸਨ %{taxon}, %{product} ਦਾ ਪ੍ਰਾਇਮਰੀ ਟੈਕਸਨ ਹੈ ਅਤੇ ਇਸ ਨੂੰ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ" + spree_order_availability_error: "ਵਿਤਰਕ ਜਾਂ ਆਰਡਰ ਸਾਈਕਲ ਤੁਹਾਡੇ ਕਾਰਟ ਵਿੱਚ ਉਤਪਾਦਾਂ ਦੀ ਸਪਲਾਈ ਨਹੀਂ ਕਰ ਸਕਦਾ" + spree_order_populator_error: "ਉਹ ਵਿਤਰਕ ਜਾਂ ਆਰਡਰ ਸਾਈਕਲ ਤੁਹਾਡੇ ਕਾਰਟ ਵਿੱਚ ਦਿੱਤੇ ਸਾਰੇ ਉਤਪਾਦਾਂ ਦੀ ਸਪਲਾਈ ਨਹੀਂ ਕਰ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਹੋਰ ਚੁਣੋ।" + spree_order_cycle_error: "ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਆਰਡਰ ਲਈ ਇੱਕ ਆਰਡਰ ਸਾਈਕਲ ਚੁਣੋ।" + spree_order_populator_availability_error: "ਉਹ ਉਤਪਾਦ ਚੁਣੇ ਹੋਏ ਵਿਤਰਕ ਜਾਂ ਆਰਡਰ ਸਾਈਕਲ ਤੋਂ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।" + spree_distributors_error: "ਘੱਟੋ-ਘੱਟ ਇੱਕ ਹੱਬ ਚੁਣਿਆ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ" + spree_user_enterprise_limit_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})." + spree_variant_product_error: ਘੱਟੋ-ਘੱਟ ਇੱਕ ਵੇਰੀਐਂਟ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + your_profil_live: "ਤੁਹਾਡਾ ਪ੍ਰੋਫਾਈਲ ਲਾਈਵ" + see: "ਵੇਖੋ" + live: "ਲਾਈਵ" + manage: "ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + resend: "ਦੁਬਾਰਾ ਭੇਜੋ" + add_and_manage_products: "ਉਤਪਾਦਾਂ ਨੂੰ ਜੋੜੋ ਅਤੇ ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + add_and_manage_order_cycles: "ਆਰਡਰ ਸਾਈਕਲ ਜੋੜੋ ਅਤੇ ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + manage_order_cycles: "ਆਰਡਰ ਸਾਈਕਲ ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + manage_products: "ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ" + edit_profile_details: "ਪ੍ਰੋਫਾਈਲ ਵੇਰਵਿਆਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ" + edit_profile_details_etc: "ਆਪਣਾ ਪ੍ਰੋਫ਼ਾਈਲ ਵਰਣਨ, ਫੋਟੋ, ਆਦਿ ਬਦਲੋ।" + order_cycle: "ਆਰਡਰ ਸਾਈਕਲ" + enterprise_relationships: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਅਨੁਮਤੀਆਂ" + first_name_begins_with: "ਪਹਿਲਾ ਨਾਮ ਇਸ ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ" + last_name_begins_with: "ਆਖਰੀ ਨਾਮ ਇਸ ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ" + shipping_method: "ਸ਼ਿਪਿੰਗ ਦਾ ਢੰਗ" + new_order: "ਨਵਾਂ ਆਰਡਰ" + enterprise_tos_link: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦਾ ਲਿੰਕ" + enterprise_tos_message: "ਅਸੀਂ ਉਹਨਾਂ ਲੋਕਾਂ ਨਾਲ ਕੰਮ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹਾਂ ਜੋ ਸਾਡੇ ਉਦੇਸ਼ਾਂ ਅਤੇ ਕਦਰਾਂ-ਕੀਮਤਾਂ ਨੂੰ ਸਾਂਝਾ ਕਰਦੇ ਹਨ। ਇਸ ਤਰ੍ਹਾਂ ਅਸੀਂ ਨਵੇਂ ਐਂਟਰਪ੍ਰਾਈਜ਼ਾਂ ਨੂੰ ਸਾਡੇ ਨਾਲ ਸਹਿਮਤ ਹੋਣ ਲਈ ਕਹਿੰਦੇ ਹਾਂ" + enterprise_tos_agree: "ਮੈਂ ਉਪਰੋਕਤ ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਨਾਲ ਸਹਿਮਤ ਹਾਂ" + tax_settings: "ਟੈਕਸ ਸੈਟਿੰਗਾਂ" + products_require_tax_category: "ਉਤਪਾਦਾਂ ਨੂੰ ਟੈਕਸ ਸ਼੍ਰੇਣੀ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ" + admin_shared_address_1: "ਪਤਾ" + admin_shared_address_2: "ਪਤਾ (ਜਾਰੀ)" + admin_share_city: "ਸ਼ਹਿਰ" + admin_share_zipcode: "ਪਿੰਨ ਕੋਡ" + admin_share_country: "ਦੇਸ਼" + admin_share_state: "ਸਥਿਤੀ" + hub_sidebar_hubs: "ਹੱਬ" + hub_sidebar_none_available: "ਕੋਈ ਵੀ ਉਪਲਬਧ ਨਹੀਂ" + hub_sidebar_manage: "ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + hub_sidebar_at_least: "ਘੱਟੋ-ਘੱਟ ਇੱਕ ਹੱਬ ਚੁਣਿਆ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ" + hub_sidebar_blue: "ਨੀਲਾ" + hub_sidebar_red: "ਲਾਲ" + order_cycles_closed_for_hub: "ਤੁਹਾਡੇ ਵੱਲੋਂ ਚੁਣਿਆ ਹੱਬ ਆਰਡਰਾਂ ਲਈ ਅਸਥਾਈ ਤੌਰ ਤੇ ਬੰਦ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + report_customers_distributor: "ਵਿਤਰਕ" + report_customers_hub: "ਹੱਬ" + report_customers_supplier: "ਸਪਲਾਇਰ" + report_customers_cycle: "ਆਰਡਰ ਸਾਈਕਲ" + report_customers_type: "ਰਿਪੋਰਟ ਦੀ ਕਿਸਮ" + report_customers_csv: "CSV ਦੇ ਤੌਰ ਤੇ ਡਾਊਨਲੋਡ ਕਰੋ" + report_customers: ਗਾਹਕ + report_producers: "ਉਤਪਾਦਕ" + report_type: "ਰਿਪੋਰਟ ਦੀ ਕਿਸਮ" + report_hubs: "ਹੱਬ" + report_payment: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + report_distributor: "ਵਿਤਰਕ" + report_payment_by: 'ਕਿਸਮ ਦੇ ਅਨੁਸਾਰ ਭੁਗਤਾਨ''' + report_itemised_payment: 'ਆਈਟਮ ਦੇ ਅਨੁਸਾਰ ਭੁਗਤਾਨ ਦਾ ਕੁੱਲ''' + report_payment_totals: 'ਭੁਗਤਾਨ ਦਾ ਕੁੱਲ''' + report_all: 'ਸਾਰੇ''' + report_order_cycle: "ਆਰਡਰ ਸਾਈਕਲ" + report_hide_columns: ਲੁਕਾਉਣ ਲਈ ਕਾਲਮ + report_columns: ਕਾਲਮ + report_enterprises: "ਐਂਟਰਪ੍ਰਾਈਜ਼" + report_enterprise_fee: "ਫੀਸ ਦੇ ਨਾਂ" + report_users: "ਉਪਭੋਗਤਾ" + report_tax_rates: ਟੈਕਸ ਦੀਆਂ ਦਰਾਂ + report_tax_types: ਟੈਕਸ ਦੀਆਂ ਕਿਸਮਾਂ + report_filters: ਰਿਪੋਰਟ ਫਿਲਟਰ + report_print: ਪ੍ਰਿੰਟ ਰਿਪੋਰਟ + report_render_options: ਰੈਂਡਰਿੰਗ ਵਿਕਲਪ + report_header_ofn_uid: OFN UID + report_header_order_cycle: ਆਰਡਰ ਸਾਈਕਲ + report_header_user: ਉਪਭੋਗਤਾ + report_header_email: ਈਮੇਲ + report_header_status: ਸਥਿਤੀ + report_header_comments: ਟਿੱਪਣੀਆਂ + report_header_first_name: ਪਹਿਲਾ ਨਾਂ + report_header_last_name: ਆਖਰੀ ਨਾਂ + report_header_suburb: ਉਪਨਗਰ + report_header_phone: ਫੋਨ + report_header_address: ਪਤਾ + report_header_billing_address: ਬਿਲਿੰਗ ਪਤਾ + report_header_relationship: ਸਬੰਧ + report_header_hub: ਹੱਬ + report_header_hub_address: ਹੱਬ ਦਾ ਪਤਾ + report_header_to_hub: ਹੱਬ ਨੂੰ + report_header_hub_code: ਹੱਬ ਕੋਡ + report_header_hub_id: ਹੱਬ ਆਈਡੀ + report_header_hub_business_number: "\"ਹੱਬ ਦਾ ਬਿਜ਼ਨਸ ਨੰਬਰ\"" + report_header_hub_legal_name: "ਹੱਬ ਦਾ ਕਾਨੂੰਨੀ ਨਾਂ" + report_header_hub_contact_name: "ਹੱਬ ਦਾ ਸੰਪਰਕ ਨਾਂ" + report_header_hub_email: "ਹੱਬ ਦਾ ਪਬਲਿਕ ਈਮੇਲ" + report_header_hub_owner_email: ਹੱਬ ਦੇ ਮਾਲਕ ਦਾ ਈਮੇਲ + report_header_hub_phone: "ਹੱਬ ਦਾ ਫੋਨ ਨੰਬਰ" + report_header_hub_address_line1: "ਹੱਬ ਪਤਾ ਲਾਈਨ 1" + report_header_hub_address_line2: "ਹੱਬ ਪਤਾ ਲਾਈਨ 2" + report_header_hub_address_city: "ਹੱਬ ਉਪਨਗਰ" + report_header_hub_address_zipcode: "ਹੱਬ ਪਿਨਕੋਡ" + report_header_hub_address_state_name: "ਹੱਬ ਰਾਜ" + report_header_code: ਕੋਡ + report_header_paid: ਭੁਗਤਾਨ ਕੀਤਾ? + report_header_delivery: ਡਿਲਿਵਰੀ? + report_header_shipping: ਸ਼ਿਪਿੰਗ + report_header_shipping_method: ਸ਼ਿਪਿੰਗ ਦਾ ਤਰੀਕਾ + report_header_shipping_instructions: ਸ਼ਿਪਿੰਗ ਨਿਰਦੇਸ਼ + report_header_ship_street: ਸ਼ਿਪ ਸਟ੍ਰੀਟ + report_header_ship_street_2: ਸ਼ਿਪ ਸਟ੍ਰੀਟ 2 + report_header_ship_city: ਸ਼ਿਪ ਸ਼ਹਿਰ + report_header_ship_postcode: ਸ਼ਿਪ ਪਿਨਕੋਡ + report_header_ship_state: ਸ਼ਿਪ ਰਾਜ + report_header_billing_street: ਬਿਲਿੰਗ ਸਟ੍ਰੀਟ + report_header_billing_street_2: ਬਿਲਿੰਗ ਸਟ੍ਰੀਟ 2 + report_header_billing_street_3: ਬਿਲਿੰਗ ਸਟ੍ਰੀਟ 3 + report_header_billing_street_4: ਬਿਲਿੰਗ ਸਟ੍ਰੀਟ 4 + report_header_billing_city: ਬਿਲਿੰਗ ਸ਼ਹਿਰ + report_header_billing_postcode: ਬਿਲਿੰਗ ਪਿਨਕੋਡ + report_header_billing_state: ਬਿਲਿੰਗ ਰਾਜ + report_header_incoming_transport: ਆਉਣ ਵਾਲੀ ਟ੍ਰਾੰਸਪੋਰਟ + report_header_special_instructions: ਵਿਸ਼ੇਸ਼ ਹਦਾਇਤਾਂ + report_header_order_number: ਆਰਡਰ ਨੰਬਰ + report_header_date: ਮਿਤੀ + report_header_confirmation_date: ਪੁਸ਼ਟੀਕਰਨ ਮਿਤੀ + report_header_tags: ਟੈਗ + report_header_items: ਵਸਤੂਆਂ + report_header_items_total: "ਕੁੱਲ ਆਈਟਮਾਂ %{currency_symbol}" + report_header_taxable_items_total: "ਕੁੱਲ ਟੈਕਸਯੋਗ ਆਈਟਮਾਂ (%{currency_symbol})" + report_header_sales_tax: "ਵਿਕਰੀ ਟੈਕਸ (%{currency_symbol})" + report_header_delivery_charge: "ਡਿਲਿਵਰੀ ਚਾਰਜ (%{currency_symbol})" + report_header_tax: "ਟੈਕਸ" + report_header_tax_on_delivery: "ਲਿਵਰੀ ਤੇ ਟੈਕਸ (%{currency_symbol})" + report_header_tax_on_fees: "ਫੀਸਾਂ ਤੇ ਟੈਕਸ (%{currency_symbol})" + report_header_tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + report_header_tax_rate_name: "ਟੈਕਸ ਦੀ ਦਰ ਦਾ ਨਾਂ" + report_header_tax_rate: "ਟੈਕਸ ਦੀ ਦਰ" + report_header_total_tax: "ਕੁੱਲ ਟੈਕਸ (%{currency_symbol})" + report_header_total_excl_tax: "ਟੈਕਸ ਹਟਾ ਕੇ ਕੁੱਲ (%{currency_symbol})" + report_header_total_incl_tax: "ਕੁੱਲ ਟੈਕਸ ਸਮੇਤ (%{currency_symbol})" + report_header_total_orders: "ਆਰਡਰਾਂ ਦੀ ਕੁੱਲ ਸੰਖਿਆ" + report_header_enterprise: ਐਂਟਰਪ੍ਰਾਈਜ਼ + report_header_enterprise_fee_name: ਨਾਮ + report_header_enterprise_fee_type: ਕਿਸਮ + report_header_enterprise_fee_owner: ਮਾਲਕ' + report_header_customer: ਗਾਹਕ + report_header_customer_first_name: ਪਹਿਲਾ ਨਾਂ + report_header_customer_last_name: ਆਖਰੀ ਨਾਂ + report_header_customer_code: ਗਾਹਕ ਕੋਡ + report_header_product: ਉਤਪਾਦ + report_header_product_properties: ਉਤਪਾਦ ਦੀ ਪਰੌਪਰਟੀਆਂ + report_header_product_tax_category: ਉਤਪਾਦ ਟੈਕਸ ਸ਼੍ਰੇਣੀ + report_header_quantity: ਮਾਤਰਾ + report_header_max_quantity: ਅਧਿਕਤਮ ਮਾਤਰਾ + report_header_variant: ਵੇਰੀਐਂਟ + report_header_variant_value: ਵੇਰੀਐਂਟ ਵੈਲਯੂ + report_header_variant_unit: '"ਵੇਰੀਐਂਟ ਯੂਨਿਟ"' + report_header_total_available: ਕੁੱਲ ਉਪਲਬਧ + report_header_unallocated: ਆਵੰਟਿਤ ਨਹੀਂ ਕੀਤੀ ਗਈ + report_header_max_quantity_excess: ਅਧਿਕਤਮ ਮਾਤਰਾ + report_header_taxons: ਟੈਕਸੋਨਸ + report_header_supplier: ਸਪਲਾਇਰ + report_header_producer: ਉਤਪਾਦਕ + report_header_producer_suburb: ਉਤਪਾਦਕ + report_header_producer_tax_status: ਉਤਪਾਦਕ ਟੈਕਸ ਸਥਿਤੀ + report_header_producer_charges_sales_tax?: ਜੀਐੱਸਟੀ/ਵੈਟ ਰਜਿਸਟਰਡ + report_header_unit: ਯੂਨਿਟ + report_header_group_buy_unit_quantity: ਸਮੂਹ ਖਰੀਦ ਯੂਨਿਟ ਦੀ ਮਾਤਰਾ + report_header_cost: ਲਾਗਤ + report_header_shipping_cost: ਸ਼ਿਪਿੰਗ ਦੀ ਲਾਗਤ + report_header_curr_cost_per_unit: ਪ੍ਰਤੀ ਯੂਨਿਟ ਕਰੰਸੀ ਦੀ ਲਾਗਤ + report_header_total_shipping_cost: ਕੁੱਲ ਸ਼ਿਪਿੰਗ ਲਾਗਤ + report_header_payment_method: ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ + report_header_sells: ਵੇਚਦਾ ਹੈ + report_header_visible: ਦਿਸਦਾ ਹੈ + report_header_price: ਕੀਮਤ + report_header_unit_size: ਯੂਨਿਟ ਸਾਈਜ਼ + report_header_distributor: ਵਿਤਰਕ + report_header_distributor_address: ਵਿਤਰਕ ਦਾ ਪਤਾ + report_header_distributor_city: ਵਿਤਰਕ ਦਾ ਸ਼ਹਿਰ + report_header_distributor_postcode: ਵਿਤਰਕ ਪਿਨਕੋਡ + report_header_distributor_tax_status: ਵਿਤਰਕ ਟੈਕਸ ਸਥਿਤੀ + report_header_delivery_address: ਡਿਲੀਵਰੀ ਦਾ ਪਤਾ + report_header_delivery_postcode: ਡਿਲੀਵਰੀ ਪਿਨਕੋਡ + report_header_bulk_unit_size: ਥੋਕ ਯੂਨਿਟ ਦਾ ਸਾਈਜ਼ + report_header_weight: ਭਾਰ + report_header_final_weight_volume: ਅੰਤਮ (ਭਾਰ/ਮਾਤਰਾ) + report_header_height: ਉਚਾਈ + report_header_width: ਚੌੜਾਈ + report_header_depth: ਡੂੰਘਾਈ + report_header_sum_total: ਕੁੱਲ ਜੋੜ + report_header_date_of_order: ਆਰਡਰ ਦੀ ਮਿਤੀ + report_header_amount_owing: ਬਕਾਇਆ ਰਕਮ + report_header_amount_paid: ਭੁਗਤਾਨ ਕੀਤੀ ਰਕਮ + report_header_units_required: ਲੋੜੀਂਦੀਆਂ ਯੂਨਿਟਾਂ + report_header_remainder: ਬਾਕੀ + report_header_order_date: ਆਰਡਰ ਦੀ ਮਿਤੀ + report_header_order_id: ਆਰਡਰ ਆਈਡੀ + report_header_item_name: ਆਈਟਮ ਦਾ ਨਾਂ + report_header_temp_controlled_items: ਤਾਪਮਾਨ ਨਿਯੰਤਰਿਤ ਆਈਟਮਾਂ? + report_header_customer_name: ਗਾਹਕ ਦਾ ਨਾਂ + report_header_customer_email: ਗਾਹਕ ਈਮੇਲ + report_header_customer_phone: ਫੋਨ + report_header_customer_city: ਗਾਹਕ ਸ਼ਹਿਰ + report_header_payment_state: ਭੁਗਤਾਨ ਸਥਿਤੀ + report_header_payment_type: ਭੁਗਤਾਨ ਸਥਿਤੀ + report_header_item_price: "ਆਈਟਮ (%{currency})" + report_header_item_fees_price: "ਆਈਟਮ + ਫੀਸ (%{currency})" + report_header_admin_handling_fees: "ਐਡਮਿਨ ਅਤੇ ਹੈਂਡਲਿੰਗ (%{currency})" + report_header_ship_price: "ਸ਼ਿਪ (%{currency})" + report_header_pay_fee_price: "ਫੀਸ ਦਾ ਭੁਗਤਾਨ ਕਰੋ (%{currency})" + report_header_total_price: "ਕੁੱਲ (%{currency})" + report_header_product_total_price: "ਕੁੱਲ ਉਤਪਾਦ (%{currency})" + report_header_shipping_total_price: "ਕੁੱਲ ਸ਼ਿਪਿੰਗ (%{currency})" + report_header_outstanding_balance_price: "ਬੱਚਿਆ ਬਕਾਇਆ (%{currency})" + report_header_eft_price: "ਈਐਫਟੀ (%{currency})" + report_header_paypal_price: "ਪੇਪਾਲ (%{currency})" + report_header_sku: SKU + report_header_amount: ਰਕਮ + report_header_balance: ਬਾਕੀ ਰਕਮ + report_header_total_cost: "ਕੁੱਲ ਲਾਗਤ" + report_header_total_ordered: ਕੁੱਲ ਕੀਤਾ ਗਿਆ ਆਰਡਰ + report_header_total_max: ਕੁੱਲ ਅਧਿਕਤਮ + report_header_total_units: ਕੁੱਲ ਯੂਨਿਟਾਂ + report_header_sum_max_total: "ਕੁੱਲ ਅਧਿਕਤਮ ਜੋੜ" + report_header_total_excl_vat: "ਟੈਕਸ ਹਟਾ ਕੇ ਕੁੱਲ (%{currency_symbol})" + report_header_total_incl_vat: "ਕੁੱਲ ਟੈਕਸ ਸਮੇਤ (%{currency_symbol})" + report_header_temp_controlled: ਤਾਪਮਾਨ ਦਵਾਰਾ ਨਿਯੰਤ੍ਰਿਤ? + report_header_is_producer: ਉਤਪਾਦਕ? + report_header_not_confirmed: ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋਈ + report_header_gst_on_income: ਆਮਦਨੀ ਤੇ ਜੀਐਸਟੀ + report_header_gst_free_income: ਜੀਐਸਟੀ ਮੁਕਤ ਆਮਦਨ + report_header_total_untaxable_produce: ਕੁੱਲ ਟੈਕਸ ਰਹਿਤ ਉਤਪਾਦ (ਕੋਈ ਟੈਕਸ ਨਹੀਂ) + report_header_total_taxable_produce: ਕੁੱਲ ਟੈਕਸਯੋਗ ਉਤਪਾਦ (ਟੈਕਸ ਸਮੇਤ) + report_header_total_untaxable_fees: ਕੁੱਲ ਟੈਕਸ ਰਹਿਤ ਫੀਸਾਂ (ਕੋਈ ਟੈਕਸ ਨਹੀਂ) + report_header_total_taxable_fees: ਕੁੱਲ ਟੈਕਸਯੋਗ ਫੀਸਾਂ (ਟੈਕਸ ਸਮੇਤ) + report_header_delivery_shipping_cost: ਡਿਲੀਵਰੀ ਸ਼ਿਪਿੰਗ ਲਾਗਤ (ਟੈਕਸ ਸਮੇਤ) + report_header_transaction_fee: ਲੈਣ-ਦੇਣ ਫੀਸ (ਕੋਈ ਟੈਕਸ ਨਹੀਂ) + report_header_total_untaxable_admin: ਕੁੱਲ ਟੈਕਸ ਰਹਿਤ ਐਡਮਿਨ ਐਡਜਸਟਮੈਂਟਸ (ਕੋਈ ਟੈਕਸ ਨਹੀਂ) + report_header_total_taxable_admin: ਕੁੱਲ ਟੈਕਸਯੋਗ ਐਡਮਿਨ ਐਡਜਸਟਮੈਂਟਸ (ਟੈਕਸ ਸਮੇਤ) + report_line_cost_of_produce: ਉਤਪਾਦ ਦੀ ਲਾਗਤ + report_line_line_items: ਲਾਈਨ ਆਈਟਮਾਂ + report_header_last_completed_order_date: ਆਖ਼ਰੀ ਮੁਕੰਮਲ ਆਰਡਰ ਦੀ ਮਿਤੀ + report_xero_configuration: ਜ਼ੀਰੋ ਕੌਂਫਿਗਰੇਸ਼ਨ + initial_invoice_number: "ਸ਼ੁਰੂਆਤੀ ਇਨਵੌਇਸ ਨੰਬਰ" + invoice_date: "ਇਨਵੌਇਸ ਮਿਤੀ" + due_date: "ਅਦਾਇਗੀ ਮਿਤੀ" + account_code: "ਖਾਤਾ ਕੋਡ" + equals: "ਦੇ ਬਰਾਬਰ" + contains: "ਸ਼ਾਮਲ ਹੈ" + discount: "ਛੂਟ" + filter_products: "ਉਤਪਾਦ ਫਿਲਟਰ ਕਰੋ" + delete_product_variant: "ਆਖਰੀ ਵੇਰੀਐਂਟ ਨੂੰ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ!" + progress: "ਪ੍ਰਗਤੀ" + saving: "ਸੇਵ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ.." + success: "ਸਫਲਤਾ" + failure: "ਅਸਫਲਤਾ" + unsaved_changes_confirmation: "ਸੇਵ ਨਾ ਕੀਤੀਆਂ ਬਦਲਾਵ ਗੁਮ ਹੋ ਜਾਣਗੀਆਂ। ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ?" + one_product_unsaved: "ਇੱਕ ਉਤਪਾਦ ਵਿੱਚ ਬਦਲਾਵ ਸੇਵ ਨਹੀਂ ਹਨ।" + products_unsaved: "%{n} ਉਤਪਾਦਾਂ ਵਿੱਚ ਬਦਲਾਵ ਸੇਵ ਨਹੀਂ ਹਨ।" + is_already_manager: "ਪਹਿਲਾਂ ਹੀ ਮੈਨੇਜਰ ਹੈ!" + no_change_to_save: "ਸੇਵ ਕਰਨ ਲਈ ਕੋਈ ਬਦਲਾਵ ਨਹੀਂ ਹਨ" + user_invited: "%{email} ਨੂੰ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਸੱਦਾ ਦਿੱਤਾ ਗਿਆ ਹੈ" + add_manager: "ਮੌਜੂਦਾ ਉਪਭੋਗਤਾ ਸ਼ਾਮਲ ਕਰੋ" + users: "ਉਪਭੋਗਤਾ" + about: "ਦੇ ਬਾਰੇ ਵਿੱਚ" + images: "ਫੋਟੋ" + web: "ਵੇਬ" + primary_details: "ਪ੍ਰਾਥਮਿਕ ਵੇਰਵੇ" + social: "ਸਮਾਜਿਕ" + shipping: "ਸ਼ਿਪਿੰਗ" + shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + payment_methods: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + payment_method_fee: "ਲੈਣ-ਦੇਣ ਫੀਸ" + payment_processing_failed: "ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਿਆ, ਕਿਰਪਾ ਕਰਕੇ ਤੁਹਾਡੇ ਦੁਆਰਾ ਦਰਜ ਕੀਤੇ ਵੇਰਵਿਆਂ ਦੀ ਜਾਂਚ ਕਰੋ" + payment_method_not_supported: "ਉਹ ਭੁਗਤਾਨ ਦਾ ਢੰਗ ਅਸਮਰਥਿਤ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਹੋਰ ਚੁਣੋ।" + payment_updated: "ਭੁਗਤਾਨ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ" + cannot_perform_operation: "ਭੁਗਤਾਨ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ" + action_required: "ਕਾਰਵਾਈ ਲੋੜੀਂਦੀ ਹੈ" + tag_rules: "ਟੈਗ ਨਿਯਮ" + enterprise_fee_whole_order: ਪੂਰਾ ਆਰਡਰ + enterprise_fee_by_name: "%{name} ਫੀਸ %{role} %{enterprise_name} ਦੁਆਰਾ" + validation_msg_relationship_already_established: "^ਉਹ ਸੰਬੰਧ ਪਹਿਲਾਂ ਹੀ ਸਥਾਪਿਤ ਹੈ।" + validation_msg_at_least_one_hub: "^ਘੱਟੋ-ਘੱਟ ਇੱਕ ਹੱਬ ਚੁਣਿਆ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ" + validation_msg_tax_category_cant_be_blank: "^ਟੈਕਸ ਸ਼੍ਰੇਣੀ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦੀ" + validation_msg_is_associated_with_an_exising_customer: "ਮੌਜੂਦਾ ਗਾਹਕ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ" + content_configuration_pricing_table: "(TODO: ਕੀਮਤ ਸਾਰਣੀ)" + content_configuration_case_studies: "(ਟੋਡੋ: ਕੇਸ ਸਟੱਡੀਜ਼)" + content_configuration_detail: "(TODO: ਵੇਰਵੇ)" + enterprise_name_error: "ਪਹਿਲਾਂ ਹੀ ਲੀਤਾ ਜਾ ਚੁੱਕਾ ਹੈ। ਜੇਕਰ ਇਹ ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਹੈ ਅਤੇ ਤੁਸੀਂ ਮਲਕੀਅਤ ਦਾ ਦਾਅਵਾ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ, ਜਾਂ ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨਾਲ ਵਪਾਰ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ %{email} ਤੇ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦੇ ਮੌਜੂਦਾ ਮੈਨੇਜਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।" + enterprise_owner_error: "^%{email} is not permitted to own any more enterprises (limit is %{enterprise_limit})." + enterprise_role_uniqueness_error: "^ਉਹ ਭੂਮਿਕਾ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।" + enterprise_terms_and_conditions_type_error: "ਸਿਰਫ ਪੀਡੀਐਫ ਦੀ ਇਜਾਜ਼ਤ ਹੈ" + inventory_item_visibility_error: ਸੱਚਾ ਜਾਂ ਝੂਠ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ + product_importer_file_error: "ਗਲਤੀ: ਕੋਈ ਫ਼ਾਈਲ ਅੱਪਲੋਡ ਨਹੀਂ ਕੀਤੀ ਗਈ" + product_importer_spreadsheet_error: "ਫਾਇਲ ਸੰਸਾਧਿਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ: ਅਵੈਧ ਫਾਇਲ ਕਿਸਮ" + product_importer_products_save_error: ਕਿਸੇ ਵੀ ਉਤਪਾਦ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਸੇਵ ਨਹੀਂ ਕੀਤਾ ਗਿਆ + product_import_file_not_found_notice: 'ਫ਼ਾਈਲ ਨਹੀਂ ਮਿਲੀ ਜਾਂ ਖੋਲ੍ਹੀ ਨਹੀਂ ਜਾ ਸਕੀ' + product_import_no_data_in_spreadsheet_notice: 'ਸਪ੍ਰੈਡਸ਼ੀਟ ਵਿੱਚ ਕੋਈ ਡਾਟਾ ਨਹੀਂ ਮਿਲਿਆ' + order_choosing_hub_notice: ਤੁਹਾਡਾ ਹੱਬ ਚੁਣਿਆ ਗਿਆ ਹੈ। + order_cycle_selecting_notice: ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ ਚੁਣਿਆ ਗਿਆ ਹੈ। + adjustments_tax_rate_error: "^ਕਿਰਪਾ ਕਰਕੇ ਜਾਂਚ ਕਰੋ ਕਿ ਇਸ ਸਮਾਯੋਜਨ ਲਈ ਟੈਕਸ ਦੀ ਦਰ ਸਹੀ ਹੈ।" + active_distributors_not_ready_for_checkout_message_singular: >- + ਹੱਬ %{distributor_names} ਇੱਕ ਸਰਗਰਮ ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਸੂਚੀਬੱਧ ਹੈ, ਪਰ ਇਸ ਵਿੱਚ ਵੈਧ + ਸ਼ਿਪਿੰਗ ਅਤੇ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਨਹੀਂ ਹਨ। ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਹਨਾਂ ਨੂੰ ਸੇਟ ਨਹੀਂ ਕਰਦੇ, ਗਾਹਕ + ਇਸ ਹੱਬ ਉਤੇ ਖਰੀਦਦਾਰੀ ਕਰਨ ਦੇ ਯੋਗ ਨਹੀਂ ਹੋਣਗੇ। + active_distributors_not_ready_for_checkout_message_plural: >- + ਹੱਬ %{distributor_names} ਇੱਕ ਸਰਗਰਮ ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਸੂਚੀਬੱਧ ਹਨ, ਪਰ ਇਸ ਵਿੱਚ ਵੈਧ + ਸ਼ਿਪਿੰਗ ਅਤੇ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਨਹੀਂ ਹਨ। ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਹਨਾਂ ਨੂੰ ਸੇਟ ਨਹੀਂ ਕਰਦੇ, ਗਾਹਕ + ਇਸ ਹੱਬ ਉਤੇ ਖਰੀਦਦਾਰੀ ਕਰਨ ਦੇ ਯੋਗ ਨਹੀਂ ਹੋਣਗੇ। + enterprise_fees_update_notice: ਤੁਹਾਡੀਆਂ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਫੀਸਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। + enterprise_register_package_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਪੈਕੇਜ ਚੁਣੋ" + enterprise_register_error: "%{enterprise} ਲਈ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਪੂਰੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ" + enterprise_register_success_notice: "ਵਧਾਈਆਂ! %{enterprise} ਲਈ ਰਜਿਸਟ੍ਰੇਸ਼ਨ ਪੂਰਾ ਹੋ ਗਿਆ ਹੈ!" + enterprise_bulk_update_success_notice: "ਐਂਟਰਪ੍ਰਾਈਜ਼ਾਂ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ" + enterprise_bulk_update_error: 'ਅੱਪਡੇਟ ਅਸਫਲ''' + enterprise_shop_show_error: "ਜਿਸ ਸ਼ਾਪ ਨੂੰ ਤੁਸੀਂ ਲੱਭ ਰਹੇ ਹੋ ਉਹ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਜਾਂ OFN ਤੇ ਅਕਿਰਿਆਸ਼ੀਲ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਹੋਰ ਸ਼ਾਪਾਂ ਦੀ ਜਾਂਚ ਕਰੋ।" + order_cycles_bulk_update_notice: 'ਆਰਡਰ ਸਾਈਕਲ ਅੱਪਡੇਟ ਕੀਤੇ ਗਏ।''' + order_cycles_no_permission_to_coordinate_error: "ਤੁਹਾਡੇ ਕਿਸੇ ਵੀ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਆਰਡਰ ਸਾਈਕਲ ਦਾ ਤਾਲਮੇਲ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ" + order_cycles_no_permission_to_create_error: "ਤੁਹਾਡੇ ਕੋਲ ਉਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੁਆਰਾ ਤਾਲਮੇਲ ਕੀਤਾ ਆਰਡਰ ਸਾਈਕਲ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ" + order_cycle_closed: "ਤੁਹਾਡੇ ਵੱਲੋਂ ਚੁਣਿਆ ਗਿਆ ਆਰਡਰ ਸਾਈਕਲ ਹੁਣੇ ਹੀ ਬੰਦ ਹੋਇਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ!" + back_to_orders_list: "ਆਰਡਰ ਸੂਚੀ ਤੇ ਵਾਪਸ" + no_orders_found: "ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਮਿਲਿਆ" + order_information: "ਆਰਡਰ ਜਾਣਕਾਰੀ" + new_payment: "ਨਵਾਂ ਭੁਗਤਾਨ" + create_or_update_invoice: "ਇਨਵੌਇਸ ਬਣਾਓ ਜਾਂ ਅੱਪਡੇਟ ਕਰੋ" + date_completed: "ਮੁਕੰਮਲ ਹੋਣ ਦੀ ਮਿਤੀ" + amount: "ਰਕਮ" + invoice_number: "ਇਨਵੌਇਸ ਨੰਬਰ" + invoice_file: "ਫਾਇਲ" + state_names: + ready: ਤਿਆਰ + pending: ਲੰਬਿਤ + shipped: ਭੇਜਿਆ ਗਿਆ + js: + saving: 'ਸੇਵ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...''' + changes_saved: 'ਬਦਲਾਵਾਂ ਨੂੰ ਸੇਵ ਕੀਤਾ ਗਿਆ।' + authorising: "ਅਧਿਕਾਰਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..." + save_changes_first: ਪਹਿਲਾਂ ਤਬਦੀਲੀਆਂ ਸੇਵ ਕਰੋ। + all_changes_saved: ਸਾਰੇ ਬਦਲਾਵ ਸੇਵ ਕੀਤੇ ਗਏ + unsaved_changes: ਤੁਹਾਡੇ ਕੋਲ ਸੇਵ ਨਾ ਕੀਤੀਆਂ ਤਬਦੀਲੀਆਂ ਹਨ + all_changes_saved_successfully: ਸਾਰੇ ਬਦਲਾਵ ਸਫਲਤਾਪੂਰਵਕ ਸੇਵ ਕੀਤੇ ਗਏ + oh_no: "ਓ ਨਹੀਂ! ਮੈਂ ਤੁਹਾਡੇ ਬਦਲਾਵਾਂ ਨੂੰ ਸੇਵ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ ਰਿਹਾ।" + unauthorized: "ਤੁਸੀਂ ਇਸ ਪੰਨੇ ਤੱਕ ਪਹੁੰਚਣ ਲਈ ਅਣਅਧਿਕਾਰਤ ਹੋ।" + error: ਗਲਤੀ + unavailable: ਅਣਉਪਲਬਧ + profile: ਪ੍ਰੋਫਾਈਲ + hub: ਹੱਬ + shop: ਸ਼ਾਪ + choose: ਚੁਣੋ + resolve_errors: ਕਿਰਪਾ ਕਰਕੇ ਹੇਠਾਂ ਦਿੱਤੀਆਂ ਗਲਤੀਆਂ ਨੂੰ ਹੱਲ ਕਰੋ + more_items: "+ %{count} ਹੋਰ" + default_card_updated: ਡਿਫੌਲਟ ਕਾਰਡ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ + default_card_voids_auth: ਤੁਹਾਡੇ ਡਿਫੌਲਟ ਕਾਰਡ ਨੂੰ ਬਦਲਣ ਨਾਲ ਇਸ ਨੂੰ ਚਾਰਜ ਕਰਨ ਲਈ ਸ਼ਾਪਾਂ ਦੇ ਮੌਜੂਦਾ ਅਧਿਕਾਰ ਹਟਾ ਦਿੱਤੇ ਜਾਣਗੇ। ਤੁਸੀਂ ਡਿਫੌਲਟ ਕਾਰਡ ਨੂੰ ਅਪਡੇਟ ਕਰਨ ਤੋਂ ਬਾਅਦ ਸ਼ਾਪਾਂ ਨੂੰ ਮੁੜ-ਅਧਿਕਾਰਤ ਕਰ ਸਕਦੇ ਹੋ। ਕੀ ਤੁਸੀਂ ਡਿਫੌਲਟ ਕਾਰਡ ਬਦਲਣਾ ਚਾਹੁੰਦੇ ਹੋ? + cart: + add_to_cart_failed: > + ਇਸ ਉਤਪਾਦ ਨੂੰ ਕਾਰਟ ਵਿੱਚ ਜੋੜਨ ਵਿੱਚ ਇੱਕ ਸਮੱਸਿਆ ਆਈ ਸੀ। ਸ਼ਾਇਦ ਇਹ ਅਣਉਪਲਬਧ ਹੋ ਗਿਆ + ਹੈ ਜਾਂ ਸ਼ਾਪ ਬੰਦ ਹੋ ਰਹੀ ਹੈ। + admin: + unit_price_tooltip: "\"ਯੂਨਿਟ ਦੀ ਕੀਮਤ ਤੁਹਾਡੇ ਗਾਹਕਾਂ ਨੂੰ ਵੱਖ-ਵੱਖ ਉਤਪਾਦਾਂ ਅਤੇ ਪੈਕੇਜਿੰਗ ਆਕਾਰਾਂ ਵਿਚਕਾਰ ਕੀਮਤਾਂ ਦੀ ਆਸਾਨੀ ਨਾਲ ਤੁਲਨਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇ ਕੇ ਪਾਰਦਰਸ਼ਤਾ ਵਧਾਉਂਦੀ ਹੈ। ਧਿਆਨ ਦਿਓ, ਕਿ ਸ਼ਾਪਫ੍ਰੰਟ ਵਿੱਚ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੀ ਗਈ ਅੰਤਿਮ ਯੂਨਿਟ ਕੀਮਤ ਵੱਖਰੀ ਹੋ ਸਕਦੀ ਹੈ ਕਿਉਂਕਿ ਇਸ ਵਿੱਚ ਟੈਕਸ ਅਤੇ ਫੀਸਾਂ ਸ਼ਾਮਲ ਹਨ।\"" + enterprise_limit_reached: "ਤੁਸੀਂ ਪ੍ਰਤੀ ਖਾਤਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ਾਂ ਦੀ ਮਿਆਰੀ ਸੀਮਾ ਤੱਕ ਪਹੁੰਚ ਗਏ ਹੋ। ਜੇਕਰ ਤੁਹਾਨੂੰ ਇਸਨੂੰ ਵਧਾਉਣ ਦੀ ਲੋੜ ਹੈ ਤਾਂ %{contact_email} ਨੂੰ ਲਿਖੋ।" + deleting_item_will_cancel_order: "ਇਸ ਕਾਰਵਾਈ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਇੱਕ ਜਾਂ ਇੱਕ ਤੋਂ ਵੱਧ ਖਾਲੀ ਆਰਡਰ ਹੋਣਗੇ, ਜੋ ਕਿ ਰੱਦ ਕਰ ਦਿੱਤੇ ਜਾਣਗੇ। ਕੀ ਤੁਸੀਂ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + modals: + got_it: "ਮਿਲ ਗਿਆ" + confirm: "ਪੁਸ਼ਟੀ ਕਰੋ" + close: "ਬੰਦ" + continue: "ਜਾਰੀ ਰੱਖੋ" + cancel: "ਰੱਦ ਕਰੋ" + invite: "ਸੱਦਾ ਦਿਓ" + invite_title: "ਇੱਕ ਗੈਰ-ਰਜਿਸਟਰਡ ਉਪਭੋਗਤਾ ਨੂੰ ਸੱਦਾ ਦਿਓ" + tag_rule_help: + title: ਟੈਗ ਨਿਯਮ + overview: ਸੰਖੇਪ ਜਾਣਕਾਰੀ + overview_text: > + ਟੈਗ ਨਿਯਮ ਇਹ ਵਰਣਨ ਕਰਨ ਦਾ ਇੱਕ ਤਰੀਕਾ ਪ੍ਰਦਾਨ ਕਰਦੇ ਹਨ ਕਿ ਕਿਹੜੀਆਂ ਆਈਟਮਾਂ ਵਿਖਾਈ + ਦੇ ਰਹੀਆਂ ਹਨ ਜਾਂ ਕਿਹੜੇ ਗਾਹਕਾਂ ਨੂੰ ਵਿਖਾਈ ਦੇ ਰਹੀਆਂ ਹਨ। ਆਈਟਮਾਂ ਸ਼ਿਪਿੰਗ ਦੇ + ਢੰਗਾਂ, ਭੁਗਤਾਨ ਦੇ ਢੰਗ, ਉਤਪਾਦ ਅਤੇ ਆਰਡਰ ਸਾਈਕਲ ਹੋ ਸਕਦੀਆਂ ਹਨ। + by_default_rules: "'ਡਿਫੌਲਟ ਦੇ ਤੌਰ ਤੇ...' ਨਿਯਮ" + by_default_rules_text: > + ਡਿਫੌਲਟ ਨਿਯਮ ਤੁਹਾਨੂੰ ਆਈਟਮਾਂ ਨੂੰ ਲੁਕਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੇ ਹਨ ਤਾਂ ਜੋ ਉਹ ਡਿਫੌਲਟ + ਰੂਪ ਵਿੱਚ ਵਿਖਾਈ ਨਾ ਦੇਣ। ਇਸ ਵਿਵਹਾਰ ਨੂੰ ਫਿਰ ਖਾਸ ਟੈਗਾਂ ਵਾਲੇ ਗਾਹਕਾਂ ਲਈ ਗੈਰ-ਡਿਫੌਲਟ + ਨਿਯਮਾਂ ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। + customer_tagged_rules: "'ਟੈਗ ਕੀਤੇ ਗਏ ਗਾਹਕ...' ਨਿਯਮ" + customer_tagged_rules_text: > + ਕਿਸੇ ਖਾਸ ਗਾਹਕ ਟੈਗ ਨਾਲ ਸੰਬੰਧਿਤ ਨਿਯਮ ਬਣਾ ਕੇ, ਤੁਸੀਂ ਖਾਸ ਟੈਗ ਵਾਲੇ ਗਾਹਕਾਂ + ਲਈ ਡਿਫੌਲਟ ਵਿਵਹਾਰ (ਭਾਵੇਂ ਇਹ ਚੀਜ਼ਾਂ ਵਿਖਾਉਣ ਲਈ ਜਾਂ ਲੁਕਾਉਣ ਲਈ ਹੋਣ) ਨੂੰ ਓਵਰਰਾਈਡ + ਕਰ ਸਕਦੇ ਹੋ। + terms_and_conditions_info: + title: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਅੱਪਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ" + message_1: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਤੁਹਾਡੇ, ਵਿਕਰੇਤਾ ਅਤੇ ਖਰੀਦਦਾਰ ਵਿਚਕਾਰ ਇਕਰਾਰਨਾਮੇ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਇੱਥੇ ਇੱਕ ਫਾਈਲ ਅਪਲੋਡ ਕਰਦੇ ਹੋ ਤਾਂ ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਚੈਕਆਉਟ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਪਵੇਗਾ। ਖਰੀਦਦਾਰ ਲਈ ਇਹ ਚੈਕਆਉਟ ਤੇ ਇੱਕ ਚੈਕਬਾਕਸ ਦੇ ਰੂਪ ਵਿੱਚ ਵਿਖਾਈ ਦੇਵੇਗਾ ਜੋ ਕਿ ਚੈਕਆਉਟ ਦੇ ਨਾਲ ਅੱਗੇ ਵਧਣ ਲਈ ਜ਼ਰੂਰੀ ਹੋਵੇਗਾ। ਅਸੀਂ ਤੁਹਾਨੂੰ ਰਾਸ਼ਟਰੀ ਕਾਨੂੰਨ ਦੇ ਨਾਲ ਇਕਸਾਰਤਾ ਵਿੱਚ ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਨੂੰ ਅਪਲੋਡ ਕਰਨ ਦੀ ਜ਼ੋਰਦਾਰ ਦੀ ਸਿਫਾਰਸ਼ ਕਰਦੇ ਹਾਂ।" + message_2: "ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਸਿਰਫ਼ ਇੱਕ ਵਾਰ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨ ਦੀ ਲੋੜ ਹੋਵੇਗੀ। ਹਾਲਾਂਕਿ ਜੇਕਰ ਤੁਸੀਂ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਨੂੰ ਬਦਲਦੇ ਹੋ ਤਾਂ ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਚੈਕਆਉਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਉਹਨਾਂ ਨੂੰ ਦੁਬਾਰਾ ਸਵੀਕਾਰ ਕਰਨ ਦੀ ਲੋੜ ਹੋਵੇਗੀ।" + terms_and_conditions_warning: + title: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਅੱਪਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ" + message_1: "ਤੁਹਾਡੇ ਸਾਰੇ ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਚੈਕਆਉਟ ਵੇਲੇ ਇੱਕ ਵਾਰ ਉਹਨਾਂ ਨਾਲ ਸਹਿਮਤ ਹੋਣਾ ਪਵੇਗਾ। ਜੇਕਰ ਤੁਸੀਂ ਫਾਈਲ ਨੂੰ ਅਪਡੇਟ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੇ ਸਾਰੇ ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਚੈਕਆਉਟ ਵੇਲੇ ਉਹਨਾਂ ਨਾਲ ਦੁਬਾਰਾ ਸਹਿਮਤ ਹੋਣਾ ਪਵੇਗਾ।" + message_2: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਵਾਲੇ ਖਰੀਦਦਾਰਾਂ ਲਈ, ਤੁਹਾਨੂੰ ਉਹਨਾਂ ਨੂੰ ਹੁਣੇ ਵਾਸਤੇ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ (ਜਾਂ ਉਹਨਾਂ ਵਿੱਚ ਤਬਦੀਲੀਆਂ) ਵਾਲੀ ਇੱਕ ਈਮੇਲ ਭੇਜਣ ਦੀ ਲੋੜ ਹੈ, ਨਹੀਂ ਤਾਂ ਉਹਨਾਂ ਨੂੰ ਇਹਨਾਂ ਨਵੇਂ ਨਿਯਮਾਂ ਅਤੇ ਸ਼ਰਤਾਂ ਬਾਰੇ ਕੋਈ ਵੀ ਸੂਚਿਤ ਨਹੀਂ ਕਰੇਗਾ।" + business_address_info: + message: "ਕੰਪਨੀ ਦਾ ਕਾਨੂੰਨੀ ਨਾਂ, ਕਨੂੰਨੀ ਪਤਾ ਅਤੇ ਕਨੂੰਨੀ ਫੋਨ ਨੰਬਰ ਉਹਨਾਂ ਕਾਰੋਬਾਰਾਂ ਲਈ ਵਰਤੇ ਜਾਂਦੇ ਹਨ ਜੋ ਉਹਨਾਂ ਦੀ ਜਨਤਕ ਵਪਾਰਕ ਜਾਣਕਾਰੀ ਵਿੱਚ ਵੱਖ-ਵੱਖ ਵੇਰਵਿਆਂ ਦੇ ਨਾਲ ਰਜਿਸਟਰਡ ਇੱਕ ਕਾਨੂੰਨੀ ਸੰਸਥਾ ਤੋਂ ਇਨਵੋਇਸ ਬਣਾਉਂਦੇ ਹਨ। ਇਹ ਵੇਰਵੇ ਸਿਰਫ਼ ਇਨਵੌਇਸਾਂ ਲਈ ਵਰਤੇ ਜਾਣਗੇ। ਜੇਕਰ ਇਹ ਵੇਰਵੇ ਖਾਲੀ ਹਨ ਤਾਂ ਤੁਹਾਡਾ ਜਨਤਕ ਨਾਮ, ਪਤਾ, ਅਤੇ ਫ਼ੋਨ ਨੰਬਰ ਇਨਵੋਇਸ ਉਤੇ ਵਰਤੇ ਜਾਣਗੇ।" + panels: + save: ਸੇਵ ਕਰੋ + saved: '"ਸੇਵ ਕੀਤਾ ਗਿਆ"' + saving: ਬਚਤ ਕਰ ਰਹੇ ਹਾਂ + enterprise_package: + hub_profile: ਹੱਬ ਪ੍ਰੋਫਾਈਲ + hub_profile_cost: "ਲਾਗਤ: ਹਮੇਸ਼ਾ ਮੁਫ਼ਤ" + hub_profile_text1: > + ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਤੇ ਲੋਕ ਤੁਹਾਨੂੰ ਲੱਭ ਅਤੇ ਸੰਪਰਕ ਕਰ ਸਕਦੇ ਹਨ। ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ + ਮੈਪ ਤੇ ਵਿਖਾਈ ਦੇਵੇਗਾ, ਅਤੇ ਸੂਚੀਆਂ ਵਿੱਚ ਖੋਜਣ ਯੋਗ ਹੋਵੇਗਾ। + hub_profile_text2: > + ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਹੋਣਾ, ਅਤੇ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਰਾਹੀਂ ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਪ੍ਰਣਾਲੀ + ਦੇ ਅੰਦਰ ਕਨੈਕਸ਼ਨ ਬਣਾਉਣਾ ਹਮੇਸ਼ਾ ਮੁਫ਼ਤ ਹੋਵੇਗਾ। + hub_shop: ਹੱਬ ਸ਼ਾਪ + hub_shop_text1: > + ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਪ੍ਰਣਾਲੀ ਦੀ ਰੀੜ੍ਹ ਦੀ ਹੱਡੀ ਹੈ। ਤੁਸੀਂ + ਦੂਜਿਆਂ ਉਦਯੋਗਾਂ ਦੀ ਪੈਦਾਵਾਰ ਇਕੱਠੇ ਕਰਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ + ਆਪਣੀ ਸ਼ਾਪ ਰਾਹੀਂ ਵੇਚ ਸਕਦੇ ਹੋ। + hub_shop_text2: > + ਹੱਬ ਕਈ ਰੂਪ ਲੈ ਸਕਦੇ ਹਨ, ਭਾਵੇਂ ਉਹ ਭੋਜਨ ਸਹਿਕਾਰਤਾ, ਇੱਕ ਖਰੀਦ ਸਮੂਹ, ਇੱਕ ਸ਼ਾਕਾਹਾਰੀ-ਬਾਕਸ + ਪ੍ਰੋਗਰਾਮ, ਜਾਂ ਇੱਕ ਸਥਾਨਕ ਕਰਿਆਨੇ ਦੀ ਦੁਕਾਨ ਹੋਵੇ। + hub_shop_text3: > + ਜੇਕਰ ਤੁਸੀਂ ਵੀ ਆਪਣੇ ਖੁਦ ਦੇ ਉਤਪਾਦ ਵੇਚਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਉਤਪਾਦਕ + ਬਣਨ ਲਈ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਨੂੰ ਬਦਲਣ ਦੀ ਲੋੜ ਹੋਵੇਗੀ। + choose_package: ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਪੈਕੇਜ ਚੁਣੋ + choose_package_text1: > + ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਉਦੋਂ ਤੱਕ ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਰਗਰਮ ਨਹੀਂ ਹੋਵੇਗਾ ਜਦੋਂ ਤੱਕ ਖੱਬੇ + ਪਾਸੇ ਦੇ ਵਿਕਲਪਾਂ ਵਿੱਚੋਂ ਇੱਕ ਪੈਕੇਜ ਨਹੀਂ ਚੁਣਿਆ ਜਾਂਦਾ। + choose_package_text2: > + ਹਰੇਕ ਪੈਕੇਜ ਬਾਰੇ ਵਧੇਰੇ ਵਿਸਤ੍ਰਿਤ ਜਾਣਕਾਰੀ ਵੇਖਣ ਲਈ ਇੱਕ ਵਿਕਲਪ ਉਤੇ ਕਲਿੱਕ ਕਰੋ, + ਅਤੇ ਜਦੋਂ ਤੁਸੀਂ ਪੂਰਾ ਕਰ ਲੈਂਦੇ ਹੋ ਤਾਂ ਲਾਲ ਸੇਵ ਬਟਨ ਨੂੰ ਦਬਾਓ! + profile_only: ਸਿਰਫ਼ ਪ੍ਰੋਫਾਈਲ + profile_only_cost: "ਲਾਗਤ: ਹਮੇਸ਼ਾ ਮੁਫ਼ਤ" + profile_only_text1: > + ਇੱਕ ਪ੍ਰੋਫਾਈਲ ਤੁਹਾਨੂੰ ਦੂਜਿਆਂ ਲਈ ਦ੍ਰਿਸ਼ਮਾਨ ਅਤੇ ਸੰਪਰਕਯੋਗ ਬਣਾਉਂਦਾ ਹੈ ਅਤੇ + ਤੁਹਾਡੀ ਕਹਾਣੀ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦਾ ਇੱਕ ਤਰੀਕਾ ਹੈ। + profile_only_text2: >+ + ਜੇਕਰ ਤੁਸੀਂ ਭੋਜਨ ਪੈਦਾ ਕਰਨ ਉਤੇ ਧਿਆਨ ਕੇਂਦਰਿਤ ਕਰਨਾ ਪਸੰਦ ਕਰਦੇ ਹੋ, ਅਤੇ ਇਸਨੂੰ + ਕਿਸੇ ਵੇਚਣ ਦਾ ਕੰਮ ਕੀਏ ਹੋਰ ਉਤੇ ਛੱਡਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ + ਤੇ ਸ਼ਾਪ ਦੀ ਲੋੜ ਨਹੀਂ ਪਵੇਗੀ। + + profile_only_text3: >+ + ਆਪਣੇ ਉਤਪਾਦਾਂ ਨੂੰ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ, ਹੱਬ ਨੂੰ ਉਹਨਾਂ ਦੇ ਸਟੋਰਾਂ + ਵਿੱਚ ਤੁਹਾਡੇ ਉਤਪਾਦਾਂ ਨੂੰ ਸਟਾਕ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੇ ਹੋਏ। + + producer_shop: ਉਤਪਾਦਕ ਸ਼ਾਪ + producer_shop_text1: >+ + ਆਪਣੇ ਖੁਦ ਦੇ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਸ਼ਾਪਫਰੰਟ ਰਾਹੀਂ ਗਾਹਕਾਂ ਨੂੰ ਆਪਣੇ ਉਤਪਾਦ ਸਿੱਧੇ + ਤੌਰ ਤੇ ਵੇਚੋ। + + producer_shop_text2: >+ + ਇੱਕ ਉਤਪਾਦਕ ਸ਼ਾਪ ਸਿਰਫ਼ ਤੁਹਾਡੇ ਉਤਪਾਦਾਂ ਲਈ ਹੈ, ਜੇਕਰ ਤੁਸੀਂ ਸਾਈਟ ਤੋਂ ਉਗਾਈ/ਉਤਪਾਦਿਤ + ਉਤਪਾਦ ਵੇਚਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ 'ਉਤਪਾਦਕ ਹੱਬ' ਨੂੰ ਚੁਣੋ। + + producer_hub: ਉਤਪਾਦਕ ਹੱਬ + producer_hub_text1: > + ਤੁਹਾਡਾ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਤੁਹਾਡੇ ਸਥਾਨਕ ਭੋਜਨ ਪ੍ਰਣਾਲੀ ਦੀ ਰੀੜ੍ਹ ਦੀ ਹੱਡੀ ਹੈ। ਤੁਸੀਂ + ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਆਪਣੇ ਸ਼ੌਪਫਰੰਟ ਰਾਹੀਂ ਆਪਣੀ ਖੁਦ ਦੀ ਪੈਦਾਵਾਰ ਦੇ ਨਾਲ-ਨਾਲ + ਦੂਜੇ ਉਦਯੋਗਾਂ ਤੋਂ ਇਕੱਠੀ ਕੀਤੀ ਉਪਜ ਵੀ ਵੇਚ ਸਕਦੇ ਹੋ। + producer_hub_text2: >+ + ਉਤਪਾਦਕ ਹੱਬ ਬਹੁਤ ਸਾਰੇ ਰੂਪ ਲੈ ਸਕਦੇ ਹਨ, ਭਾਵੇਂ ਉਹ CSA ਹੋਣ, ਸ਼ਾਕਾਹਾਰੀ-ਬਾਕਸ + ਪ੍ਰੋਗਰਾਮ, ਜਾਂ ਛੱਤ ਵਾਲੇ ਬਗੀਚੇ ਦੇ ਨਾਲ ਭੋਜਨ ਸਹਿਕਾਰਤਾ ਹੋਵੇ। + + producer_hub_text3: > + ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਦਾ ਉਦੇਸ਼ ਵੱਧ ਤੋਂ ਵੱਧ ਹੱਬ ਮਾਡਲਾਂ ਦਾ ਸਮਰਥਨ ਕਰਨਾ ਹੈ, ਇਸ + ਲਈ ਤੁਹਾਡੀ ਸਥਿਤੀ ਦਾ ਕੋਈ ਫਰਕ ਨਹੀਂ ਪੈਂਦਾ, ਅਸੀਂ ਤੁਹਾਡੇ ਸੰਗਠਨ ਜਾਂ ਸਥਾਨਕ ਭੋਜਨ + ਕਾਰੋਬਾਰ ਨੂੰ ਚਲਾਉਣ ਲਈ ਲੋੜੀਂਦੇ ਸਾਧਨ ਪ੍ਰਦਾਨ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹਾਂ। + get_listing: ਇੱਕ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰੋ + always_free: ਹਮੇਸ਼ਾ ਮੁਫ਼ਤ + sell_produce_others: ਦੂਜਿਆਂ ਦੇ ਉਤਪਾਦ ਵੇਚੋ + sell_own_produce: ਆਪਣੀ ਖੁਦ ਦੀ ਪੈਦਾਵਾਰ ਵੇਚੋ + sell_both: ਆਪਣੇ ਅਤੇ ਦੂਜਿਆਂ ਦੀ ਪੈਦਾਵਾਰ ਵੇਚੋ + enterprise_producer: + producer: ਉਤਪਾਦਕ + producer_text1: > + ਉਤਪਾਦਕ ਖਾਣ ਜਾਂ ਪੀਣ ਲਈ ਸੁਆਦੀ ਚੀਜ਼ਾਂ ਬਣਾਉਂਦੇ ਹਨ। ਤੁਸੀਂ ਇੱਕ ਉਤਪਾਦਕ ਹੋ ਜੇਕਰ + ਤੁਸੀਂ ਇਸਨੂੰ ਉਗਾਉਂਦੇ ਹੋ, ਇਸਨੂੰ ਪਾਲਦੇ ਹੋ, ਇਸਨੂੰ ਬਰਿਊ ਕਰਦੇ ਹੋ, ਇਸਨੂੰ ਬੇਕ + ਕਰਦੇ ਹੋ, ਇਸਨੂੰ ਖਮੀਰਦੇ ਹੋ, ਦੁੱਧ ਕੱਢਦੇ ਹੋ ਜਾਂ ਇਸਨੂੰ ਢਾਲਦੇ ਹੋ। + producer_text2: > + ਉਤਪਾਦਕ ਹੋਰ ਫੰਕਸ਼ਨ ਵੀ ਕਰ ਸਕਦੇ ਹਨ, ਜਿਵੇਂ ਕਿ ਦੂਜੇ ਉਦਯੋਗਾਂ ਤੋਂ ਭੋਜਨ ਇਕੱਠਾ + ਕਰਨਾ ਅਤੇ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਉਤੇ ਸ਼ਾਪ ਰਾਹੀਂ ਇਸ ਨੂੰ ਵੇਚਣਾ। + non_producer: ਗੈਰ-ਉਤਪਾਦਕ + non_producer_text1: > + ਗੈਰ-ਉਤਪਾਦਕ ਖੁਦ ਕੋਈ ਭੋਜਨ ਪੈਦਾ ਨਹੀਂ ਕਰਦੇ ਹਨ, ਮਤਲਬ ਕਿ ਉਹ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ + ਰਾਹੀਂ ਵਿਕਰੀ ਲਈ ਆਪਣੇ ਖੁਦ ਦੇ ਉਤਪਾਦ ਬਣਾ ਨਹੀਂ ਸਕਦੇ ਹਨ। + non_producer_text2: > + ਇਸ ਦੀ ਬਜਾਏ, ਗੈਰ-ਉਤਪਾਦਕ ਉਤਪਾਦਕਾਂ ਨੂੰ ਅੰਤਮ ਖਾਣ ਵਾਲੇ ਨਾਲ ਜੋੜਨ ਵਿੱਚ ਮੁਹਾਰਤ + ਰੱਖਦੇ ਹਨ, ਭਾਵੇਂ ਇਹ ਭੋਜਨ ਨੂੰ ਇਕੱਠਾ ਕਰਨ, ਗਰੇਡਿੰਗ, ਪੈਕਿੰਗ, ਵੇਚਣ ਜਾਂ ਡਿਲੀਵਰ + ਕਰਕੇ ਹੋਵੇ। + producer_desc: ਭੋਜਨ ਦੇ ਉਤਪਾਦਕ + producer_example: ਜਿਵੇਂ ਕਿ ਉਗਾਉਣ ਵਾਲੇ, ਬੇਕਰ, ਬਰੂਅਰ, ਮੇਕਰ + non_producer_desc: ਬਾਕੀ ਸਾਰੇ ਭੋਜਨ ਐਂਟਰਪ੍ਰਾਈਜ਼ + non_producer_example: ਜਿਵੇਂ ਕਿ ਕਰਿਆਨੇ ਦੀਆਂ ਦੁਕਾਨਾਂ, ਭੋਜਨ ਸਹਿਕਾਰਤਾ, ਖਰੀਦਣ ਵਾਲੇ ਸਮੂਹ + enterprise_status: + status_title: "%{name} ਸੇਟਅੱਪ ਹੋ ਗਿਆ ਹੈ ਅਤੇ ਸਾਹਮਣੇ ਆਉਣ ਲਈ ਤਿਆਰ ਹੈ!" + severity: ਤੀਬਰਤਾ + description: ਵਰਣਨ + resolve: ਸੰਕਲਪ + exchange_products: + load_more_variants: "ਹੋਰ ਵੇਰੀਐਂਟਸ ਲੋਡ ਕਰੋ" + load_all_variants: "ਸਾਰੇ ਵੇਰੀਐਂਟਸ ਲੋਡ ਕਰੋ" + select_all_variants: "ਸਾਰੇ %{total_number_of_variants} ਵੇਰੀਐਂਟ ਚੁਣੋ" + variants_loaded: "%{num_of_variants_loaded} of %{total_number_of_variants} ਵੇਰੀਐਂਟਸ ਲੋਡ ਕੀਤੇ ਗਏ" + loading_variants: "ਵੇਰੀਐਂਟ ਲੋਡ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ" + no_variants: "ਇਸ ਉਤਪਾਦ ਲਈ ਕੋਈ ਵੇਰੀਐਂਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ (ਇਨਵੇਂਟਰੀ ਸੈਟਿੰਗਾਂ ਰਾਹੀਂ ਲੁਕਿਆ ਹੋਇਆ)।" + some_variants_hidden: "(ਕੁਝ ਵੇਰੀਐਂਟ ਇਨਵੇਂਟਰੀ ਸੈਟਿੰਗਾਂ ਰਾਹੀਂ ਲੁਕੇ ਹੋਏ ਹੋ ਸਕਦੇ ਹਨ)" + tag_rules: + shipping_method_tagged_top: "ਸ਼ਿਪਿੰਗ ਢੰਗ ਟੈਗ ਕੀਤੇ ਗਏ" + shipping_method_tagged_bottom: "ਹਨ:" + payment_method_tagged_top: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਟੈਗ ਕੀਤੇ ਗਏ" + payment_method_tagged_bottom: "ਹਨ:" + order_cycle_tagged_top: "ਟੈਗ ਕੀਤੇ ਆਰਡਰ ਸਾਈਕਲ" + order_cycle_tagged_bottom: "ਹਨ:" + inventory_tagged_top: "ਟੈਗ ਕੀਤੇ ਗਏ ਇਨਵੇਂਟਰੀ ਵੇਰੀਐਂਟ" + inventory_tagged_bottom: "ਹਨ:" + new_tag_rule_dialog: + select_rule_type: "ਇੱਕ ਨਿਯਮ ਕਿਸਮ ਚੁਣੋ:" + add_rule: "ਨਿਯਮ ਜੋੜੋ" + enterprise_fees: + inherit_from_product: "ਉਤਪਾਦ ਤੋਂ ਅਪਣਾਓ" + orders: + index: + per_page: "ਪ੍ਰਤੀ ਪੇਜ %{results}" + view_file: "ਫਾਇਲ ਵੇਖੋ" + compiling_invoices: "ਇਨਵੌਇਸ ਇਕੱਠੇ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ" + bulk_invoice_created: "ਥੋਕ ਇਨਵੌਇਸ ਬਣਾਇਆ ਗਿਆ" + bulk_invoice_failed: "ਥੋਕ ਇਨਵੌਇਸ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ" + please_wait: "ਇਸ ਮੋਡਲ ਨੂੰ ਬੰਦ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕਿਰਪਾ ਕਰਕੇ ਪੀਡੀਐਫ ਦੇ ਤਿਆਰ ਹੋਣ ਤੱਕ ਉਡੀਕ ਕਰੋ।" + order_state: + address: "ਪਤਾ" + adjustments: "ਸਮਾਯੋਜਨ" + awaiting_return: "ਵਾਪਸੀ ਦੀ ਉਡੀਕ" + canceled: "ਰੱਦ ਕੀਤਾ ਗਿਆ" + cart: "ਕਾਰਟ" + complete: "ਪੂਰਾ" + confirm: "ਪੁਸ਼ਟੀ ਕਰੋ" + delivery: "ਡਿਲਿਵਰੀ" + paused: "ਵਿਰਾਮ ਕੀਤਾ" + payment: "ਭੁਗਤਾਨ" + pending: "ਲੰਬਿਤ" + resumed: "ਮੁੜ ਸ਼ੁਰੂ ਕੀਤਾ" + returned: "ਵਾਪਸ ਪਰਤਿਆ" + confirmation: "ਪੁਸ਼ਟੀ" + shipment_states: + backorder: "ਬੈਕ ਆਰਡਰ" + partial: "ਅੰਸ਼ਕ" + pending: "ਲੰਬਿਤ" + ready: "ਤਿਆਰ" + shipped: "ਭੇਜਿਆ ਗਿਆ" + canceled: "ਰੱਦ ਕੀਤਾ ਗਿਆ" + payment_states: + balance_due: "ਬਕਾਇਆ ਰਕਮ" + completed: "ਮੁਕੰਮਲ ਕੀਤਾ" + checkout: "ਚੈਕਆਊਟ" + credit_owed: "ਬਕਾਇਆ ਕਰਜ਼ਾ" + failed: "ਅਸਫ਼ਲ" + paid: "ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ" + pending: "ਲੰਬਿਤ" + requires_authorization: "ਅਧਿਕਾਰ ਦੀ ਲੋੜ ਹੈ" + processing: "ਸੰਸਾਧਨ" + void: "ਖਾਲੀ" + invalid: "ਅਵੈਧ" + quantity_unavailable: "ਨਾਕਾਫ਼ੀ ਸਟਾਕ ਉਪਲਬਧ ਹੈ। ਲਾਈਨ ਆਈਟਮ ਸੇਵ ਨਹੀਂ ਕੀਤੀ ਗਈ!" + quantity_unchanged: "ਪਿਛਲੀ ਰਕਮ ਤੋਂ ਮਾਤਰਾ ਵਿੱਚ ਕੋਈ ਬਦਲਾਅ ਨਹੀਂ ਕੀਤਾ ਗਿਆ।" + cancel_the_order_html: "ਇਹ ਮੌਜੂਦਾ ਆਰਡਰ ਨੂੰ ਰੱਦ ਕਰ ਦੇਵੇਗਾ।
ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + cancel_the_order_send_cancelation_email: "ਗਾਹਕ ਨੂੰ ਇੱਕ ਰੱਦ ਕਰਨ ਵਾਲੀ ਈਮੇਲ ਭੇਜੋ" + restock_item: "ਆਈਟਮਾਂ ਨੂੰ ਮੁੜ ਸਟਾਕ ਕਰੋ: ਇਸ ਆਈਟਮ ਨੂੰ ਸਟਾਕ ਵਿੱਚ ਵਾਪਸ ਕਰੋ" + restock_items: "ਆਈਟਮਾਂ ਨੂੰ ਮੁੜ ਸਟਾਕ ਕਰੋ: ਸਾਰੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਸਟਾਕ ਵਿੱਚ ਵਾਪਸ ਕਰੋ" + resend_user_email_confirmation: + resend: "ਦੁਬਾਰਾ ਭੇਜੋ" + sending: "ਦੁਬਾਰਾ ਭੇਜੋ" + done: "ਮੁੜ ਭੇਜਣਾ ਸਫਲ ਗਿਆ ✓" + failed: "ਮੁੜ ਭੇਜਣਾ ਅਸਫਲ ਗਿਆ ✓" + order_cycles: + schedules: + adding_a_new_schedule: "ਇੱਕ ਨਵਾਂ ਸ਼ਡਿਊਲ ਜੋੜ ਰਹੇ ਹਾਂ" + updating_a_schedule: "ਇੱਕ ਸ਼ਡਿਊਲ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਰਹੇ ਹਾਂ" + create_schedule: "ਸ਼ਡਿਊਲ ਬਣਾਓ" + update_schedule: "ਸ਼ਡਿਊਲ ਅਪਡੇਟ ਕਰੋ" + delete_schedule: "ਸ਼ਡਿਊਲ ਹਟਾਓ" + schedule_name_placeholder: "ਸ਼ਡਿਊਲ ਦਾ ਨਾਂ" + created_schedule: "ਸ਼ਡਿਊਲ ਬਣਾਇਆ ਗਿਆ" + updated_schedule: "ਸ਼ਡਿਊਲ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ" + deleted_schedule: "ਹਟਾਇਆ ਗਿਆ ਸ਼ਡਿਊਲ" + name_required_error: "ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਸ਼ਡਿਊਲ ਲਈ ਇੱਕ ਨਾਂ ਦਰਜ ਕਰੋ" + no_order_cycles_error: "ਕਿਰਪਾ ਕਰਕੇ ਘੱਟੋ-ਘੱਟ ਇੱਕ ਆਰਡਰ ਸਾਈਕਲ ਚੁਣੋ (ਡਰੈਗ ਐਂਡ ਡ੍ਰੌਪ)" + available: "ਉਪਲੱਬਧ" + selected: "ਚੁਣਿਆ ਗਿਆ" + customers: + index: + add_customer: "ਗਾਹਕ ਜੋੜੋ" + add_a_new_customer_for: "%{shop_name} ਲਈ ਨਵਾਂ ਗਾਹਕ ਜੋੜੋ" + customer_placeholder: "customer@example.org" + valid_email_error: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਵੈਧ ਈਮੇਲ ਪਤਾ ਭਰੋ" + subscriptions: + error_saving: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਸੇਵ ਕਰਨ ਵਿੱਚ ਗਲਤੀ" + new: + please_select_a_shop: "ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਸ਼ਾਪ ਚੁਣੋ" + enterprises: + form: + images: + removed_logo_successfully: "ਲੋਗੋ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਇਆ ਗਿਆ" + immediate_logo_removal_warning: "ਤੁਹਾਡੇ ਵੱਲੋਂ ਪੁਸ਼ਟੀ ਕਰਨ ਤੋਂ ਬਾਅਦ ਲੋਗੋ ਤੁਰੰਤ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।" + removed_promo_image_successfully: "ਪ੍ਰੋਮੋ ਦੀ ਫੋਟੋ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਇਆ ਗਿਆ" + immediate_promo_image_removal_warning: "ਤੁਹਾਡੇ ਵੱਲੋਂ ਪੁਸ਼ਟੀ ਕਰਨ ਤੋਂ ਬਾਅਦ ਪ੍ਰੋਮੋ ਫੋਟੋ ਨੂੰ ਤੁਰੰਤ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।" + immediate_terms_and_conditions_removal_warning: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਦੀ ਫਾਈਲ ਤੁਹਾਡੇ ਪੁਸ਼ਟੀ ਕਰਨ ਤੋਂ ਤੁਰੰਤ ਬਾਅਦ ਹਟਾ ਦਿੱਤੀ ਜਾਵੇਗੀ।" + removed_terms_and_conditions_successfully: "ਨਿਯਮ ਅਤੇ ਸ਼ਰਤਾਂ ਦੀ ਫਾਈਲ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਈ ਗਈ" + insufficient_stock: "ਨਾਕਾਫ਼ੀ ਸਟਾਕ ਉਪਲਬਧ ਹੈ, ਸਿਰਫ਼ %{on_hand} ਬਾਕੀ" + out_of_stock: + reduced_stock_available: ਘੱਟ ਸਟਾਕ ਉਪਲਬਧ ਹੈ + out_of_stock_text: > + ਜਦੋਂ ਤੁਸੀਂ ਖਰੀਦਦਾਰੀ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤੁਹਾਡੇ ਕਾਰਟ ਵਿੱਚ ਇੱਕ ਜਾਂ ਇੱਕ ਤੋਂ ਵੱਧ + ਉਤਪਾਦਾਂ ਦੇ ਸਟਾਕ ਦੇ ਪੱਧਰ ਘਟ ਗਏ ਹਨ। ਇੱਥੇ ਕੀ ਬਦਲਿਆ ਹੈ. ਉਹ ਵਿਖਾਇਆ ਗਿਆ ਹੈ: + now_out_of_stock: ਹੁਣ ਸਟਾਕ ਤੋਂ ਬਾਹਰ ਹੈ। + only_n_remainging: "ਹੁਣ ਸਿਰਫ਼ %{num} ਬਾਕੀ ਹਨ।" + shopfront: + variant: + add_to_cart: "ਜੋੜੋ" + in_cart: "ਕਾਰਟ ਵਿੱਚ" + quantity_in_cart: "%{quantity} ਕਾਰਟ ਵਿੱਚ" + remaining_in_stock: "ਸਿਰਫ %{quantity} ਬਾਕੀ" + bulk_buy_modal: + min_quantity: "ਨਿਊਨਤਮ ਮਾਤਰਾ" + max_quantity: "ਅਧਿਕਤਮ ਮਾਤਰਾ" + price_breakdown: "ਕੀਮਤ ਦਾ ਬ੍ਰੇਕਡਾਊਨ" + unit_price_tooltip: "ਇਹ ਇਸ ਉਤਪਾਦ ਦੀ ਯੂਨਿਟ ਕੀਮਤ ਹੈ। ਇਹ ਤੁਹਾਨੂੰ ਪੈਕੇਜਿੰਗ ਸਾਈਜ਼ ਅਤੇ ਵਜ਼ਨ ਤੋਂ ਸੁਤੰਤਰ ਉਤਪਾਦਾਂ ਦੀ ਕੀਮਤ ਦੀ ਤੁਲਨਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।" + variants: + on_demand: + 'yes': "ਡਿਮਾਂਡ ਤੇ" + variant_overrides: + on_demand: + use_producer_settings: "ਉਤਪਾਦਕ ਸਟਾਕ ਸੈਟਿੰਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ" + 'yes': "ਹਾਂ" + 'no': "ਨਹੀਂ" + inventory_products: "ਇਨਵੇਂਟਰੀ ਉਤਪਾਦ" + hidden_products: "ਲੁਕੇ ਹੋਏ ਉਤਪਾਦ" + new_products: "ਨਵੇਂ ਉਤਪਾਦ" + reset_stock_levels: ਸਟਾਕ ਪੱਧਰਾਂ ਨੂੰ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਕਰੋ + changes_to: ਇਸ ਵਿੱਚ ਬਦਲਦੇ ਹਨ + one_override: ਇੱਕ ਓਵਰਰਾਈਡ + overrides: ਓਵਰਰਾਈਡ ਕਰਦਾ ਹੈ + remain_unsaved: ਬਿਨਾ ਸੇਵ ਦੇ ਰਹਿ ਜਾਂਦੇ ਹਨ। + no_changes_to_save: ਸੇਵ ਕਰਨ ਲਈ ਕੋਈ ਵੀ ਬਦਲਾਵ ਨਹੀਂ।' + no_authorisation: "ਮੈਂ ਉਹਨਾਂ ਬਦਲਾਵਾਂ ਨੂੰ ਸੇਵ ਕਰਨ ਲਈ ਅਧਿਕਾਰ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਿਆ, ਇਸਲਈ ਉਹ ਬਿਨਾ ਸੇਵ ਕੀਤੇ ਰਹਿ ਗਏ ਹਨ।" + some_trouble: "ਮੈਨੂੰ ਸੇਵ ਕਰਨ ਵਿੱਚ ਕੁਝ ਮੁਸ਼ਕਲ ਆਈ: %{errors}" + changing_on_hand_stock: ਹੱਥ ਵਿਚ ਸਟਾਕ ਦੇ ਪੱਧਰਾਂ ਨੂੰ ਬਦਲ ਰਹੇ ਹਾਂ... + stock_reset: ਸਟਾਕ ਡਿਫੌਲਟ ਤੇ ਰੀਸੈਟ ਕੀਤੇ ਗਏ ਹਨ। + tag_rules: + show_hide_variants: 'ਮੇਰੇ ਸ਼ੌਪਫਰੰਟ ਵਿੱਚ ਵਾਰੀਏਂਟ ਵਿ ਓ ਜਾਂ ਲੁਕਾਓ''' + show_hide_shipping: 'ਚੈਕਆਉਟ ਵੇਲੇ ਸ਼ਿਪਿੰਗ ਢੰਗ ਵਿਖਾਓ ਜਾਂ ਲੁਕਾਓ''' + show_hide_payment: 'ਚੈਕਆਉਟ ਵੇਲੇ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਵਿਖਾਓ ਜਾਂ ਲੁਕਾਓ''' + show_hide_order_cycles: 'ਮੇਰੇ ਸ਼ੌਪਫਰੰਟ ਵਿੱਚ ਆਰਡਰ ਸਾਈਕਲ ਵਿਖਾਓ ਜਾਂ ਲੁਕਾਓ''' + visible: ਵਿਖਾਈ ਦੇ ਰਿਹਾ ਹੈ + not_visible: ਵਿਖਾਈ ਨਹੀਂ ਦੇ ਰਿਹਾ ਹੈ + services: + unsaved_changes_message: ਸੇਵ ਨਾ ਕਿੱਤੇ ਗਏ ਬਦਲਾਵ ਮੌਜੂਦ ਹਨ, ਹੁਣੇ ਸੇਵ ਕਰੋ ਜਾਂ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰੋ? + save: ਸੇਵ ਕਰੋ + ignore: ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰੋ + add_to_order_cycle: "ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਜੋੜੋ" + manage_products: "ਉਤਪਾਦਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ" + edit_profile: "ਪ੍ਰੋਫਾਈਲ ਸੰਪਾਦਿਤ ਕਰੋ" + add_products_to_inventory: "ਇਨਵੇਂਟਰੀ ਵਿੱਚ ਉਤਪਾਦ ਸ਼ਾਮਲ ਕਰੋ" + resources: + could_not_delete_customer: 'ਗਾਹਕ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ''' + product_import: + confirmation: | + ਇਹ ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਉਹਨਾਂ ਸਾਰੇ ਉਤਪਾਦਾਂ ਦੇ ਸਟਾਕ ਪੱਧਰ ਨੂੰ ਜ਼ੀਰੋ ਤੇ ਸੇਟ ਕਰ ਦੇਵੇਗਾ ਜੋ ਅੱਪਲੋਡ ਕੀਤੀ ਫਾਈਲ ਵਿੱਚ ਮੌਜੂਦ ਨਹੀਂ ਹਨ। + order_cycles: + create_failure: "ਆਰਡਰ ਸਾਈਕਲ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ" + update_success: 'ਤੁਹਾਡਾ ਆਰਡਰ ਸਾਈਕਲ ਅੱਪਡੇਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।''' + update_failure: "ਆਰਡਰ ਸਾਈਕਲ ਅੱਪਡੇਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲ" + no_distributors: ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਕੋਈ ਵਿਤਰਕ ਨਹੀਂ ਹਨ। ਇਹ ਆਰਡਰ ਸਾਈਕਲ ਗਾਹਕਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਵਿਖਾਈ ਨਹੀਂ ਦੇਵੇਗਾ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇੱਕ ਨੂੰ ਜੋੜ ਨਹੀਂ ਲੈਂਦੇ। ਕੀ ਤੁਸੀਂ ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਨੂੰ ਸੇਵ ਕਰਨਾ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਗੇ?' + enterprises: + producer: "ਉਤਪਾਦਕ" + non_producer: "ਗੈਰ-ਉਤਪਾਦਕ" + customers: + select_shop: 'ਕਿਰਪਾ ਕਰਕੇ ਪਹਿਲਾਂ ਇੱਕ ਸ਼ਾਪ ਚੁਣੋ' + could_not_create: ਮਾਫ ਕਰਨਾ! ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ + subscriptions: + closes: ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ + closed: ਬੰਦ ਕੀਤਾ ਹੋਇਆ + close_date_not_set: ਬੰਦ ਕਰਨ ਦੀ ਮਿਤੀ ਸੇਟ ਨਹੀਂ ਕੀਤੀ ਗਈ + spree: + users: + order: "ਆਰਡਰ" + registration: + welcome_to_ofn: "ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਵਿੱਚ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!" + signup_or_login: "ਸਾਈਨ ਅੱਪ (ਜਾਂ ਲੌਗਇਨ ਕਰਕੇ) ਸ਼ੁਰੂ ਕਰੋ" + have_an_account: "ਪਹਿਲਾਂ ਹੀ ਕੋਈ ਖਾਤਾ ਹੈ?" + action_login: "ਹੁਣੇ ਲਾਗਇਨ ਕਰੋ।" + stripe_elements: + unknown_error_from_stripe: | + ਸਾਡੇ ਭੁਗਤਾਨ ਗੇਟਵੇ ਵਿੱਚ ਤੁਹਾਡੇ ਕਾਰਡ ਨੂੰ ਸੇਟ ਕਰਨ ਵਿੱਚ ਇੱਕ ਸਮੱਸਿਆ ਆਈ ਸੀ। ਕਿਰਪਾ ਕਰਕੇ ਪੇਜ ਨੂੰ ਤਾਜ਼ਾ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ, ਜੇਕਰ ਇਹ ਦੂਜੀ ਵਾਰ ਅਸਫਲ ਹੁੰਦਾ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਮਦਦ ਲਈ ਸਾਡੇ ਨਾਲ ਸੰਪਰਕ ਕਰੋ। + producers: + signup: + start_free_profile: "ਇੱਕ ਮੁਫ਼ਤ ਪ੍ਰੋਫਾਈਲ ਨਾਲ ਸ਼ੁਰੂ ਕਰੋ, ਅਤੇ ਜਦੋਂ ਤੁਸੀਂ ਤਿਆਰ ਹੋਵੋ ਤਾਂ ਵਿਸਤਾਰ ਕਰੋ!" + order_management: + reports: + bulk_coop: + filters: + bulk_coop_allocation: "Bulk Co-op Allocation" + bulk_coop_customer_payments: "ਬਲਕ ਕੋ-ਆਪ ਗਾਹਕ ਭੁਗਤਾਨ" + bulk_coop_packing_sheets: "ਬਲਕ ਕੋ-ਆਪ ਪੈਕਿੰਗ ਸ਼ੀਟਾਂ" + bulk_coop_supplier_report: "ਬਲਕ ਕੋ-ਆਪ ਸਪਲਾਇਰ ਰਿਪੋਰਟ" + enterprise_fee_summaries: + filters: + date_range: "ਮਿਤੀ ਰੇਂਜ" + report_format_csv: "CSV ਦੇ ਤੌਰ ਤੇ ਡਾਊਨਲੋਡ ਕਰੋ" + generate_report: "ਰਿਪੋਰਟ ਤਿਆਰ ਕਰੋ" + report: + none: "ਕੋਈ ਨਹੀਂ" + select_and_search: "ਫਿਲਟਰ ਚੁਣੋ ਅਤੇ ਆਪਣੇ ਡੇਟਾ ਤੱਕ ਪਹੁੰਚਣ ਲਈ ਰਿਪੋਰਟ ਤਿਆਰ ਕਰੋ ਉਤੇ ਕਲਿੱਕ ਕਰੋ।" + enterprise_fee_summary: + date_end_before_start_error: "ਸ਼ੁਰੂ ਹੋਣ ਤੋਂ ਬਾਅਦ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + parameter_not_allowed_error: "ਤੁਸੀਂ ਇਸ ਰਿਪੋਰਟ ਲਈ ਇੱਕ ਜਾਂ ਇੱਕ ਤੋਂ ਵੱਧ ਚੁਣੇ ਗਏ ਫਿਲਟਰਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ।" + fee_calculated_on_transfer_through_all: "ਸਾਰੇ" + fee_calculated_on_transfer_through_entire_orders: "%{distributor} ਰਾਹੀਂ ਪੂਰੇ ਆਰਡਰ" + tax_category_various: "ਕਈ ਤਰ੍ਹਾਂ ਦੇ" + fee_type: + payment_method: "ਭੁਗਤਾਨ ਲੈਣ-ਦੇਣ" + shipping_method: "ਸ਼ਿਪਮੈਂਟ" + fee_placements: + supplier: "ਅੰਦਰ ਆਉਣ ਵਾਲੇ" + distributor: "ਬਾਹਰ ਜਾਣ ਵਾਲੇ" + coordinator: "ਕੋਆਰਡੀਨੇਟਰ" + tax_category_name: + shipping_instance_rate: "ਪਲੇਟਫਾਰਮ ਰੇਟ" + formats: + csv: + header: + fee_type: "ਫੀਸ ਦੀ ਕਿਸਮ" + enterprise_name: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੇ ਮਾਲਕ" + fee_name: "ਫ਼ੀਸ ਦਾ ਨਾਮ" + customer_name: "ਗਾਹਕ" + fee_placement: "ਫੀਸ ਪਲੇਸਮੈਂਟ" + fee_calculated_on_transfer_through_name: "ਇਸਦੇ ਰਾਹੀਂ ਟਰਾਂਸਫਰ ਉਤੇ ਫੀਸ ਦੀ ਗਣਨਾ" + tax_category_name: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + total_amount: "$$ ਯੋਗ" + html: + header: + fee_type: "ਫੀਸ ਦੀ ਕਿਸਮ" + enterprise_name: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੇ ਮਾਲਕ" + fee_name: "ਫ਼ੀਸ ਦਾ ਨਾਮ" + customer_name: "ਗਾਹਕ" + fee_placement: "ਫੀਸ ਪਲੇਸਮੈਂਟ" + fee_calculated_on_transfer_through_name: "ਇਸਦੇ ਰਾਹੀਂ ਟਰਾਂਸਫਰ ਉਤੇ ਫੀਸ ਦੀ ਗਣਨਾ" + tax_category_name: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + total_amount: "$$ ਯੋਗ" + invalid_filter_parameters: "ਇਸ ਰਿਪੋਰਟ ਲਈ ਤੁਹਾਡੇ ਦੁਆਰਾ ਚੁਣੇ ਗਏ ਫਿਲਟਰ ਅਵੈਧ ਹਨ।" + report: + none: "ਕੋਈ ਨਹੀਂ" + order: "ਆਰਡਰ" + order_details: "ਆਰਡਰ ਵੇਰਵੇ" + customer_details: "ਗਾਹਕ ਦੇ ਵੇਰਵੇ" + adjustments: "ਸਮਾਯੋਜਨ" + payments: "ਭੁਗਤਾਨ" + return_authorizations: "ਵਾਪਸ ਕਰਨ ਦੇ ਅਧਿਕਾਰ" + credit_owed: "ਬਕਾਇਆ ਕਰਜ਼ਾ" + new_adjustment: "ਨਵਾਂ ਸਮਾਯੋਜਨ" + payment: "ਭੁਗਤਾਨ" + payment_method: "ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ" + shipment: "ਸ਼ਿਪਮੈਂਟ" + shipment_inc_vat: "ਵੈਟ ਸਮੇਤ ਸ਼ਿਪਮੈਂਟ" + shipping_tax_rate: "ਸ਼ਿਪਿੰਗ ਟੈਕਸ ਦਰ" + category: "ਸ਼੍ਰੇਣੀ" + import_date: "ਇਮਪੋਰਟ ਮਿਤੀ" + delivery: "ਡਿਲਿਵਰੀ" + temperature_controlled: "ਤਾਪਮਾਨ ਨਿਯੰਤਰਿਤ" + new_product: "ਨਵਾਂ ਉਤਪਾਦ" + administration: "ਪ੍ਰਸ਼ਾਸਨ" + logged_in_as: "ਇਸ ਦੇ ਤੌਰ ਤੇ ਲੌਗ ਇਨ ਕੀਤਾ ਹੈ" + account: "ਖਾਤਾ" + logout: "ਲਾਗਆਊਟ" + date_range: "ਮਿਤੀ ਰੇਂਜ" + status: "ਸਥਿਤੀ" + new: "ਨਵਾਂ" + start: "ਸ਼ੁਰੂ" + end: "ਅੰਤ" + stop: "ਰੂਕੋ" + first: "ਪਹਿਲਾ" + previous: "ਪਿਛਲਾ" + last: "ਆਖਰੀ" + webhook_endpoints: + create: + success: ਵੇਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ ਸਫਲਤਾਪੂਰਵਕ ਬਣਾਇਆ ਗਿਆ + error: ਵੇਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ ਰਿਹਾ + destroy: + success: ਵੇਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਇਆ ਗਿਆ + error: ਵੇਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ ਨੂੰ ਮਿਟਾਉਣਾ ਅਸਫਲ ਰਿਹਾ + spree: + order_updated: "ਆਰਡਰ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ" + add_country: "ਦੇਸ਼ ਜੋੜੋ" + add_state: "ਰਾਜ ਜੋੜੋ\"" + adjustment: "ਸਮਾਯੋਜਨ" + all: "ਸਾਰੇ" + associated_adjustment_closed: "ਸਬੰਧਿਤ ਸਮਾਯੋਜਨ ਬੰਦ" + back_to_adjustments_list: "ਸਮਾਯੋਜਨ ਤੇ ਵਾਪਸ" + back_to_users_list: "ਉਪਭੋਗਤਾਵਾਂ ਤੇ ਵਾਪਸ" + back_to_zones_list: "ਜ਼ੋਨਾਂ ਤੇ ਵਾਪਸ" + card_code: "ਕਾਰਡ ਕੋਡ" + card_number: "ਕਾਰਡ ਨੰਬਰ" + category: "ਸ਼੍ਰੇਣੀ" + created_successfully: "ਸਫ਼ਲਤਾਪੂਰਵਕ ਬਣਾਇਆ ਗਿਆ" + credit: "ਕ੍ਰੈਡਿਟ" + editing_tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ ਦਾ ਸੰਪਾਦਨ" + editing_tax_rate: "ਟੈਕਸ ਦਰ ਦਾ ਸੰਪਾਦਨ" + editing_zone: "ਸੰਪਾਦਨ ਜ਼ੋਨ" + expiration: "ਮਿਆਦ ਸਮਾਪਤੀ" + invalid_payment_provider: "ਅਵੈਧ ਭੁਗਤਾਨ ਪ੍ਰਦਾਤਾ" + items_cannot_be_shipped: "ਆਈਟਮਾਂ ਭੇਜੀਆਂ ਨਹੀਂ ਜਾ ਸਕਦੀਆਂ" + gateway_config_unavailable: "ਗੇਟਵੇ ਕੌਂਫਿਗਰੇਸ਼ਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" + gateway_error: "ਭੁਗਤਾਨ ਅਸਫਲ" + more: "ਹੋਰ" + new_adjustment: "ਨਵਾਂ ਸਮਾਯੋਜਨ" + new_tax_category: "ਨਵੀਂ ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + new_taxon: "ਨਵਾਂ ਟੈਕਸੋਨ" + new_user: "ਨਵਾਂ ਉਪਭੋਗਤਾ" + no_pending_payments: "ਕੋਈ ਬਕਾਇਆ ਭੁਗਤਾਨ ਨਹੀਂ ਹੈ" + none: "ਕੋਈ ਨਹੀਂ" + not_found: "ਨਹੀਂ ਲਭਿਆ" + notice_messages: + variant_deleted: "ਵੇਰੀਐਂਟ ਹਟਾਇਆ ਗਿਆ" + payment_method_not_supported: "ਭੁਗਤਾਨ ਦਾ ਢੰਗ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ" + resend_authorization_email: "ਪ੍ਰਮਾਣਿਕਤਾ ਈਮੇਲ ਮੁੜ ਭੇਜੋ" + rma_credit: "RMA ਕ੍ਰੈਡਿਟ" + refund: "ਰਿਫੰਡ" + server_error: "ਸਰਵਰ ਤਰੁੱਟੀ" + shipping_method_names: + UPS Ground: "ਯੂਪੀਐਸ ਗਰਾਊਂਡ" + pick_up: "ਖੇਤ ਤੋਂ ਪਿਕ-ਅੱਪ" + delivery: "ਦਸਤਖਤ ਕੀਤੇ, ਸੀਲ ਕੀਤੇ, ਡਿਲੀਵਰ ਕੀਤੇ ਗਏ" + start_date: "ਸ਼ੁਰੂ ਕਰਨ ਦੀ ਮਿਤੀ" + successfully_removed: "ਸਫਲਤਾਪੂਰਵਕ ਹਟਾਇਆ ਗਿਆ" + taxonomy_edit: "ਟੈਕਸੋਨੌਮੀ ਸੰਪਾਦਨ" + tree: "ਟ੍ਰੀ" + updating: "ਅੱਪਡੇਟ ਹੋ ਰਿਹਾ ਹੈ" + your_order_is_empty_add_product: "ਤੁਹਾਡਾ ਆਰਡਰ ਖਾਲੀ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਉਪਰ ਇੱਕ ਉਤਪਾਦ ਦੀ ਖੋਜ ਕਰੋ ਅਤੇ ਜੋੜੋ" + add_product: "ਉਤਪਾਦ ਜੋੜੋ" + name_or_sku: "ਨਾਮ ਜਾਂ SKU (ਉਤਪਾਦ ਦੇ ਨਾਮ ਦੇ ਘੱਟੋ-ਘੱਟ ਪਹਿਲੇ 4 ਅੱਖਰ ਦਾਖਲ ਕਰੋ)" + resend: "ਦੁਬਾਰਾ ਭੇਜੋ" + back_to_orders_list: "ਆਰਡਰ ਸੂਚੀ ਤੇ ਵਾਪਸ" + back_to_payments_list: "ਭੁਗਤਾਨ ਸੂਚੀ ਤੇ ਵਾਪਸ ਜਾਓ" + return_authorizations: "ਵਾਪਸ ਕਰਨ ਦੇ ਅਧਿਕਾਰ" + cannot_create_returns: "ਰਿਟਰਨ ਨਹੀਂ ਬਣਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਸ ਆਰਡਰ ਵਿੱਚ ਕੋਈ ਵੀ ਸ਼ਿਪ ਕੀਤੀ ਯੂਨਿਟ ਨਹੀਂ ਹੈ।" + select_stock: "ਸਟਾਕ ਚੁਣੋ" + location: "ਲੋਕੇਸ਼ਨ" + count_on_hand: "ਹੱਥ ਤੇ ਗਿਣੋ" + quantity: "ਮਾਤਰਾ" + on_demand: "ਡਿਮਾਂਡ ਤੇ" + on_hand: "ਹੱਥ ਵਿਚ" + package_from: "ਇਸ ਤੋਂ ਪੈਕੇਜ" + item_description: "ਆਈਟਮ ਦਾ ਵੇਰਵਾ" + price: "ਕੀਮਤ" + total: "ਕੁੱਲ" + edit: "ਸੰਪਾਦਿਤ ਕਰੋ" + split: "ਵੰਡ" + delete: "ਹਟਾਓ" + cannot_set_shipping_method_without_address: "ਜਦ ਤੱਕ ਗਾਹਕ ਵੇਰਵੇ ਪ੍ਰਦਾਨ ਨਹੀਂ ਕੀਤੇ ਜਾਂਦੇ ਉਦੋਂ ਤੱਕ ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ ਨੂੰ ਸੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।" + no_tracking_present: "ਕੋਈ ਟਰੈਕਿੰਗ ਵੇਰਵੇ ਪ੍ਰਦਾਨ ਨਹੀਂ ਕੀਤੇ ਗਏ।" + tracking: "ਟਰੈਕਿੰਗ" + tracking_number: "ਟਰੈਕਿੰਗ ਨੰਬਰ" + order_total: "ਆਰਡਰ ਦਾ ਕੁੱਲ" + customer_details: "ਗਾਹਕ ਦੇ ਵੇਰਵੇ" + customer_details_updated: "ਗਾਹਕ ਵੇਰਵੇ ਅੱਪਡੇਟ ਕੀਤੇ ਗਏ" + customer_search: "ਗਾਹਕ ਖੋਜ" + choose_a_customer: "ਇੱਕ ਗਾਹਕ ਚੁਣੋ" + account: "ਖਾਤਾ" + billing_address: "ਬਿਲਿੰਗ ਪਤਾ" + shipping_address: "ਸ਼ਿਪਿੰਗ ਪਤਾ" + first_name: "ਪਹਿਲਾ ਨਾਂ" + last_name: "ਆਖਰੀ ਨਾਂ" + street_address: "ਗਲੀ ਦਾ ਪਤਾ" + street_address_2: "ਗਲੀ ਦਾ ਪਤਾ (ਜਾਰੀ)" + city: "ਸ਼ਹਿਰ" + zip: "ਪਿਨ" + country: "ਦੇਸ਼" + state: "ਸਥਿਤੀ" + phone: "ਫੋਨ" + update: "ਅੱਪਡੇਟ" + use_billing_address: "ਬਿਲਿੰਗ ਪਤਾ ਵਰਤੋ" + adjustments: "ਸਮਾਯੋਜਨ" + continue: "ਜਾਰੀ ਰੱਖੋ" + fill_in_customer_info: "ਕਿਰਪਾ ਕਰਕੇ ਗਾਹਕ ਦੀ ਜਾਣਕਾਰੀ ਭਰੋ" + credit_card: "ਕਰੇਡਿਟ ਕਾਰਡ" + new_payment: "ਨਵਾਂ ਭੁਗਤਾਨ" + capture: "ਕੈਪਚਰ" + capture_and_complete_order: "ਕੈਪਚਰ ਕਰੋ ਅਤੇ ਆਰਡਰ ਪੂਰਾ ਕਰੋ" + void: "ਖਾਲੀ" + login: "ਲਾਗਇਨ" + password: "ਪਾਸਵਰਡ" + signature: "ਦਸਤਖਤ" + solution: "ਹੱਲ" + landing_page: "ਲੈਂਡਿੰਗ ਪੇਜ" + server: "ਸਰਵਰ" + test_mode: "ਟੈਸਟ ਮੋਡ" + logourl: "ਲੋਗੋurl" + are_you_sure_delete: "ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਇਸ ਰਿਕਾਰਡ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + confirm_delete: "ਹਟਾਉਣ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + configurations: "ਕੌਂਫਿਗਰੇਸ਼ਨ" + general_settings: "ਆਮ ਸੈਟਿੰਗਾਂ" + site_name: "ਸਾਈਟ ਦਾ ਨਾਂ" + site_url: "ਸਾਈਟ URL" + default_seo_title: "ਡਿਫੌਲਟ SEO ਸਿਰਲੇਖ" + default_meta_description: "ਡਿਫੌਲਟ ਮੈਟਾ ਵਰਣਨ" + default_meta_keywords: "ਡਿਫੌਲਟ ਮੈਟਾ ਕੀਵਰਡਸ" + currency_decimal_mark: "ਕਰੰਸੀ ਦਸ਼ਮਲਵ ਚਿੰਨ੍ਹ" + currency_settings: "ਕਰੰਸੀ ਸੈਟਿੰਗ" + currency_symbol_position: ਲਗਾਓ ਡਾਲਰ ਦੀ ਰਕਮ ਤੋਂ ਪਹਿਲਾਂ ਜਾਂ ਬਾਅਦ ਵਿੱਚ ਮੁਦਰਾ ਚਿੰਨ੍ਹ? + currency_thousands_separator: "ਕਰੰਸੀ ਹਜ਼ਾਰ ਵੱਖ ਕਰਨ ਵਾਲਾ" + hide_cents: "ਸੇਂਟ ਲੁਕਾਓ" + display_currency: "ਕਰੰਸੀ ਡਿਸਪਲੇ ਕਰੋ" + choose_currency: "ਕਰੰਸੀ ਚੁਣੋ" + mail_method_settings: "ਮੇਲ ਵਿਧੀ ਸੈਟਿੰਗਾਂ" + mail_settings_notice_html: "ਇੱਥੇ ਕੀਤੀਆਂ ਤਬਦੀਲੀਆਂ ਅਸਥਾਈ ਹੋਣਗੀਆਂਸਿਰਫ ਡੀਬਗਿੰਗ ਲਈ, ਅਤੇ ਭਵਿੱਖ ਵਿੱਚ ਵਾਪਸ ਕੀਤੀਆਂ ਜਾ ਸਕਦੀਆਂ ਹਨ।
ਸਥਾਈ ਤਬਦੀਲੀਆਂ ਇੰਸਟੈਂਸ ਦੇ ਭੇਦ ਨੂੰ ਅੱਪਡੇਟ ਕਰਕੇ ਅਤੇ ofn-install ਦਾ ਉਪਯੋਗ ਕਰ ਕੇ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ OFN ਦੀ ਗਲੋਬਲ ਟੀਮ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।" + general: "ਜਨਰਲ" + enable_mail_delivery: "ਮੇਲ ਡਿਲਿਵਰੀ ਨੂੰ ਸਮਰੱਥ ਬਣਾਓ" + send_mails_as: "ਇਸ ਦੇ ਤੌਰ ਤੇ ਮੇਲ ਭੇਜੋ" + smtp_send_all_emails_as_from_following_address: "ਸਾਰੇ ਮੇਲ ਹੇਠਾਂ ਦਿੱਤੇ ਪਤੇ ਤੋਂ ਭੇਜੋ।" + send_copy_of_all_mails_to: "ਸਾਰੀਆਂ ਮੇਲਾਂ ਦੀ ਕਾਪੀ ਇਥੇ ਭੇਜੋ" + smtp_send_copy_to_this_addresses: "ਇਸ ਪਤੇ ਤੇ ਸਾਰੀਆਂ ਬਾਹਰ ਜਾਣ ਵਾਲਿਆਂ ਮੇਲਾਂ ਦੀ ਕਾਪੀ ਭੇਜਦਾ ਹੈ। ਇੱਕ ਤੋਂ ਜ਼ਿਆਦਾ ਪਤਿਆਂ ਲਈ, ਕਾੱਮੇ ਲਾ ਕੇ ਵੱਖ ਕਰੋ।" + tax_categories: "ਟੈਕਸ ਸ਼੍ਰੇਣੀਆਂ" + listing_tax_categories: "ਟੈਕਸ ਸ਼੍ਰੇਣੀਆਂ ਦੀ ਲਿਸਟਿੰਗ" + back_to_tax_categories_list: "ਟੈਕਸ ਸ਼੍ਰੇਣੀਆਂ ਦੀ ਸੂਚੀ ਵਿੱਚ ਵਾਪਸ" + tax rate: "ਟੈਕਸ ਦੀਆਂ ਦਰਾਂ" + new_tax_rate: "ਨਵੀਂ ਟੈਕਸ ਦਰ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + tax_rates: "ਟੈਕਸ ਦੀਆਂ ਦਰਾਂ" + rate: "ਦਰ" + tax_rate_amount_explanation: "ਟੈਕਸ ਦੀਆਂ ਦਰਾਂ ਗਣਨਾ ਵਿੱਚ ਸਹਾਇਤਾ ਲਈ ਇੱਕ ਦਸ਼ਮਲਵ ਰਕਮ ਹਨ, (ਜਿਵੇਂ ਕਿ ਜੇਕਰ ਟੈਕਸ ਦਰ 5% ਹੈ ਤਾਂ 0.05 ਦਰਜ ਕਰੋ)" + included_in_price: "ਕੀਮਤ ਵਿੱਚ ਸ਼ਾਮਲ" + show_rate_in_label: "ਲੇਬਲ ਵਿੱਚ ਦਰ ਦਿਖਾਓ" + back_to_tax_rates_list: "ਟੈਕਸ ਦਰਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + tax_settings: "ਟੈਕਸ ਸੈਟਿੰਗਾਂ" + zones: "ਜ਼ੋਨਾਂ" + new_zone: "ਨਵਾਂ ਜ਼ੋਨ" + default_tax: "ਡਿਫੌਲਟ ਟੈਕਸ" + default_tax_zone: "ਡਿਫੌਲਟ ਟੈਕਸ ਜ਼ੋਨ" + country_based: "ਦੇਸ਼ ਅਧਾਰਤ" + state_based: "ਰਾਜ ਅਧਾਰਤ" + countries: "ਦੇਸ਼" + listing_countries: "ਸੂਚੀਬੱਧ ਦੇਸ਼" + iso_name: "ISO ਨਾਂ" + states_required: "ਰਾਜਾਂ ਦੀ ਲੋੜ ਹੈ" + editing_country: "ਦੇਸ਼ ਦਾ ਸੰਪਾਦਨ" + back_to_countries_list: "ਦੇਸ਼ਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + states: "ਰਾਜ" + abbreviation: "ਸੰਖਿਪਤ ਰੂਪ" + new_state: "ਨਵਾਂ ਰਾਜ" + payment_methods: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + taxonomies: "ਟੈਕਸੋਨੌਮੀਜ਼" + new_taxonomy: "ਨਵਾਂ ਟੈਕਸੋਨੌਮੀ" + back_to_taxonomies_list: "\"ਟੈਕਸਨੋਮੀਜ਼ ਸੂਚੀ ਤੇ ਵਾਪਸ" + shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + shipping_method: "ਸ਼ਿਪਿੰਗ ਦਾ ਤਰੀਕਾ" + shipment: "ਸ਼ਿਪਮੈਂਟ" + payment: "ਭੁਗਤਾਨ" + status: "ਸਥਿਤੀ" + shipping_categories: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀਆਂ" + new_shipping_category: "ਨਵੀਂ ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ" + back_to_shipping_categories: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀਆਂ ਤੇ ਵਾਪਸ" + editing_shipping_category: "ਸ਼ਿਪਿੰਗ ਸ਼੍ਰੇਣੀ ਦਾ ਸੰਪਾਦਨ" + name: "ਨਾਮ" + description: "ਵਰਣਨ" + type: "ਕਿਸਮ" + default: "ਡਿਫੌਲਟ" + calculator: "ਕੈਲਕੁਲੇਟਰ" + zone: "ਜ਼ੋਨ" + display: "ਡਿਸਪਲੇ" + environment: "ਵਾਤਾਵਰਣ" + active: "ਸਕ੍ਰਿਅ" + nore: "ਹੋਰ" + no_results: "ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ" + create: "ਬਣਾਓ" + loading: "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + flat_percent: "ਫਲੈਟ ਪ੍ਰਤੀਸ਼ਤ" + per_kg: "ਪ੍ਰਤੀ ਕਿਲੋ" + amount: "ਰਕਮ" + currency: "ਕਰੰਸੀ" + first_item: "ਪਹਿਲੀ ਆਈਟਮ ਦੀ ਕੀਮਤ" + additional_item: "ਵਧੀਕ ਆਈਟਮ ਦੀ ਕੀਮਤ" + max_items: "ਅਧਿਕਤਮ ਆਈਟਮਾਂ" + minimal_amount: "ਨਿਊਨਤਮ ਰਕਮ" + normal_amount: "ਆਮ ਰਕਮ" + discount_amount: "ਛੂਟ ਰਕਮ" + no_images_found: "ਕੋਈ ਫੋਟੋ ਨਹੀਂ ਮਿਲੇ" + new_image: "ਨਈ ਫੋਟੋ" + filename: "ਫਾਈਲ ਦਾ ਨਾਂ" + alt_text: "ਵੈਕਲਪਿਕ ਟੈਕਸਟ" + thumbnail: "ਥੰਬਨੇਲ" + back_to_images_list: "ਫੋਟੋ ਸੂਚੀ ਤੇ ਵਾਪਸ" + email: ਈਮੇਲ + account_updated: "ਖਾਤਾ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ!" + email_updated: "ਨਵੇਂ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਹੋਣ ਤੋਂ ਬਾਅਦ ਖਾਤਾ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਵੇਗਾ।" + show_api_key_view_toggled: "ਦਿਖਾਓ ਕਿ API ਕੁੰਜੀ ਦ੍ਰਿਸ਼ ਬਦਲਿਆ ਗਿਆ ਹੈ!" + my_account: "ਮੇਰਾ ਖਾਤਾ" + date: "ਮਿਤੀ" + time: "ਸਮਾਂ" + inventory_error_flash_for_insufficient_quantity: "ਤੁਹਾਡੀ ਕਾਰਟ ਵਿੱਚ ਇੱਕ ਆਈਟਮ ਅਣਉਪਲਬਧ ਹੋ ਗਈ ਹੈ।" + inventory: ਇਨਵੇਂਟਰੀ + zipcode: ਪਿੰਨ ਕੋਡ + weight: ਵਜ਼ਨ (ਪ੍ਰਤੀ ਕਿਲੋ ਜਾਂ ਪੌਂਡ) + error_user_destroy_with_orders: "ਉਪਭੋਗਤਾ ਜਿਹਨਾਂ ਦੇ ਆਰਡਰ ਪੂਰੇ ਹੋ ਗਏ ਨੇ ਉਹਨਾਂ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ" + cannot_create_payment_without_payment_methods: "ਤੁਸੀਂ ਬਿਨਾਂ ਕਿਸੇ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਦੇ ਪਰਿਭਾਸ਼ਿਤ ਕੀਤੇ ਆਰਡਰ ਲਈ ਭੁਗਤਾਨ ਨਹੀਂ ਬਣਾ ਸਕਦੇ ਹੋ।" + please_define_payment_methods: "ਕਿਰਪਾ ਕਰਕੇ ਪਹਿਲਾਂ ਕੁਝ ਭੁਗਤਾਨ ਦੇ ਢੰਗਾਂ ਨੂੰ ਪਰਿਭਾਸ਼ਿਤ ਕਰੋ।" + options: "ਵਿਕਲਪ" + has_no_shipped_units: "ਕੋਈ ਵੀ ਭੇਜੇ ਗਏ ਯੂਨਿਟ ਨਹੀਂ ਹਨ" + successfully_created: '%{resource} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਬਣਾਇਆ ਗਿਆ ਹੈ!' + successfully_updated: '%{resource} ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ!''' + payment_method: "ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ" + payment_processing_failed: "ਭੁਗਤਾਨ ਸੰਸਾਧਿਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਿਆ, ਕਿਰਪਾ ਕਰਕੇ ਤੁਹਾਡੇ ਦੁਆਰਾ ਦਰਜ ਕੀਤੇ ਵੇਰਵਿਆਂ ਦੀ ਜਾਂਚ ਕਰੋ" + not_available: "ਉਪਲਬਧ ਨਹੀਂ" + sku: "SKU" + there_are_no_items_for_this_order: "ਇਸ ਆਰਡਰ ਲਈ ਕੋਈ ਆਈਟਮਾਂ ਨਹੀਂ ਹਨ।" + order_populator: + out_of_stock: '%{item} ਸਟਾਕ ਵਿੱਚ ਖਤਮ ਹੋ ਗਿਆ ਹੈ।''' + actions: + update: "ਅੱਪਡੇਟ" + cancel: "ਰੱਦ ਕਰੋ" + shared: + error_messages: + errors_prohibited_this_record_from_being_saved: + one: "1 ਗਲਤੀ ਨੇ ਇਸ ਰਿਕਾਰਡ ਨੂੰ ਸੇਵ ਕੀਤੇ ਜਾਣ ਤੋਂ ਰੋਕ ਦਿੱਤਾ:" + few: "%{count} ਗਲਤੀਆਂ ਨੇ ਇਸ ਰਿਕਾਰਡ ਨੂੰ ਸੇਵ ਕੀਤੇ ਜਾਣ ਤੋਂ ਰੋਕ ਦਿੱਤਾ:" + many: "%{count} ਗਲਤੀਆਂ ਨੇ ਇਸ ਰਿਕਾਰਡ ਨੂੰ ਸੇਵ ਕੀਤੇ ਜਾਣ ਤੋਂ ਰੋਕ ਦਿੱਤਾ:" + other: "%{count} ਗਲਤੀਆਂ ਨੇ ਇਸ ਰਿਕਾਰਡ ਨੂੰ ਸੇਵ ਕੀਤੇ ਜਾਣ ਤੋਂ ਰੋਕ ਦਿੱਤਾ:" + there_were_problems_with_the_following_fields: "ਹੇਠ ਦਿੱਤੇ ਖੇਤਰਾਂ ਵਿੱਚ ਸਮੱਸਿਆਵਾਂ ਸਨ" + payments_list: + date_time: "ਮਿਤੀ/ਸਮਾਂ" + amount: "ਰਕਮ" + payment_method: "ਭੁਗਤਾਨ ਦਾ ਤਰੀਕਾ" + payment_state: "ਭੁਗਤਾਨ ਸਥਿਤੀ" + errors: + messages: + included_price_validation: "ਉਦੋਂ ਤੱਕ ਚੁਣਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇੱਕ ਡਿਫਾਲਟ ਟੈਕਸ ਜ਼ੋਨ ਸੇਟ ਨਹੀਂ ਕੀਤਾ ਹੋਵੇ" + blank: "ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ" + invalid_instagram_url: "ਸਿਰਫ਼ ਉਪਭੋਗਤਾ ਦਾ ਨਾਂ ਹੀ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ, ਜਿਵੇਂ ਕਿ. the_prof" + layouts: + admin: + login_nav: + header: + store: ਸਟੋਰ + validation: + must_be_int: "ਇੱਕ ਪੂਰਨ ਅੰਕ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ" + admin: + mail_methods: + send_testmail: "ਟੈਸਟ ਈਮੇਲ ਭੇਜੋ" + testmail: + delivery_success: "ਟੈਸਟ ਈਮੇਲ ਭੇਜਿਆ ਗਿਆ।" + error: "ਟੈਸਟ ਈਮੇਲ ਭੇਜਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਵਿੱਚ ਇੱਕ ਗਲਤੀ ਹੋਈ।" + unit_price_tooltip: "\"ਯੂਨਿਟ ਦੀ ਕੀਮਤ ਤੁਹਾਡੇ ਗਾਹਕਾਂ ਨੂੰ ਵੱਖ-ਵੱਖ ਉਤਪਾਦਾਂ ਅਤੇ ਪੈਕੇਜਿੰਗ ਆਕਾਰਾਂ ਵਿਚਕਾਰ ਕੀਮਤਾਂ ਦੀ ਆਸਾਨੀ ਨਾਲ ਤੁਲਨਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇ ਕੇ ਪਾਰਦਰਸ਼ਤਾ ਵਧਾਉਂਦੀ ਹੈ। ਧਿਆਨ ਦਿਓ, ਕਿ ਸ਼ਾਪਫ੍ਰੰਟ ਵਿੱਚ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੀ ਗਈ ਅੰਤਿਮ ਯੂਨਿਟ ਕੀਮਤ ਵੱਖਰੀ ਹੋ ਸਕਦੀ ਹੈ ਕਿਉਂਕਿ ਇਸ ਵਿੱਚ ਟੈਕਸ ਅਤੇ ਫੀਸਾਂ ਸ਼ਾਮਲ ਹਨ।\"" + subscriptions: + number: "ਨੰਬਰ" + tab: + dashboard: "ਡੈਸ਼ਬੋਰਡ" + orders: "ਆਰਡਰ" + bulk_order_management: "ਥੋਕ ਆਰਡਰ ਪ੍ਰਬੰਧਨ" + subscriptions: "ਸਬਸਕ੍ਰਿਪਸ਼ਨ" + products: "ਉਤਪਾਦ" + option_types: "ਵਿਕਲਪ ਦੀਆਂ ਕਿਸਮਾਂ" + properties: "ਪ੍ਰਾਪਰਟੀਜ਼" + variant_overrides: "ਇਨਵੇਂਟਰੀ" + reports: "ਰਿਪੋਰਟਾਂ" + configuration: "ਕੌਂਫਿਗਰੇਸ਼ਨ" + users: "ਉਪਭੋਗਤਾ" + roles: "ਭੂਮਿਕਾਵਾਂ" + order_cycles: "ਆਰਡਰ ਸਾਈਕਲ" + enterprises: "ਐਂਟਰਪ੍ਰਾਈਜ਼" + enterprise_relationships: "ਇਜਾਜ਼ਤਾਂ" + customers: "ਗਾਹਕ" + groups: "ਸਮੂਹ" + oidc_settings: "OIDC ਸੈਟਿੰਗਾਂ" + product_properties: + index: + inherits_properties_checkbox_hint: "%{supplier} ਤੋਂ ਪ੍ਰੋਪੇਰਟੀਜ਼ ਅਪਣਾਓ? (ਜਦੋਂ ਤੱਕ ਕਿ ਉਪਰ ਓਵਰਰਾਈਡ ਨਾ ਕੀਤਾ ਗਿਆ ਹੋਵੇ)" + add_product_properties: "ਉਤਪਾਦ ਪਰੌਪਰਟੀਆਂ ਜੋੜੋ" + properties: + index: + properties: "ਪ੍ਰਾਪਰਟੀਜ਼" + new_property: "ਨਵੀ ਪ੍ਰਾਪਰਟੀ" + name: "ਨਾਮ" + presentation: "ਪ੍ਰਸਤੁਤੀ" + new: + new_property: "ਨਵੀ ਪ੍ਰਾਪਰਟੀ" + edit: + editing_property: "ਪ੍ਰਾਪਰਟੀ ਦਾ ਸੰਪਾਦਨ" + back_to_properties_list: "ਪਰੌਪਰਟੀਆਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + form: + name: "ਨਾਮ" + presentation: "ਪ੍ਰਸਤੁਤੀ" + return_authorizations: + index: + new_return_authorization: "ਨਵਾਂ ਵਾਪਸੀ ਅਧਿਕਾਰ" + return_authorizations: "ਵਾਪਸ ਕਰਨ ਦੇ ਅਧਿਕਾਰ" + back_to_orders_list: "ਆਰਡਰ ਸੂਚੀ ਤੇ ਵਾਪਸ" + rma_number: "RMA ਨੰਬਰ" + status: "ਸਥਿਤੀ" + amount: "ਰਕਮ" + cannot_create_returns: "ਰਿਟਰਨ ਨਹੀਂ ਬਣਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਸ ਆਰਡਰ ਵਿੱਚ ਕੋਈ ਵੀ ਸ਼ਿਪ ਕੀਤੀ ਯੂਨਿਟ ਨਹੀਂ ਹੈ।" + continue: "ਜਾਰੀ ਰੱਖੋ" + new: + new_return_authorization: "ਨਵਾਂ ਵਾਪਸੀ ਅਧਿਕਾਰ" + back_to_return_authorizations_list: "ਵਾਪਸੀ ਅਧਿਕਾਰ ਸੂਚੀ ਤੇ ਵਾਪਸ" + continue: "ਜਾਰੀ ਰੱਖੋ" + edit: + receive: "ਪ੍ਰਾਪਤ ਕਰੋ" + are_you_sure: "ਕੀ ਤੁਹਾਨੂੰ ਯਕੀਨ ਹੈ?" + return_authorization: "ਵਾਪਸੀ ਅਧਿਕਾਰ" + form: + product: "ਉਤਪਾਦ" + quantity_shipped: "ਭੇਜੀ ਗਈ ਮਾਤਰ" + quantity_returned: "ਵਾਪਸ ਕੀਤੀ ਗਈ ਮਾਤਰਾ " + return_quantity: "ਵਾਪਸੀ ਦੀ ਮਾਤਰਾ" + amount: "ਰਕਮ" + rma_value: "ਵੈਲਯੂ" + reason: "ਕਾਰਨ" + stock_location: "ਸਟਾਕ ਲੋਕੇਸ਼ਨ" + states: + authorized: "ਅਧਿਕਾਰਤ" + received: "ਪ੍ਰਾਪਤ ਹੋਇਆ" + canceled: "ਰੱਦ ਕੀਤਾ ਗਿਆ" + line_items: + index: + results_found: "%{number} ਨਤੀਜੇ ਮਿਲੇ।" + viewing: "%{start} ਨੂੰ %{end} ਤੱਕ ਵੇਖਨਾ।" + orders: + add_product: + cannot_add_item_to_canceled_order: "ਰੱਦ ਕੀਤੇ ਆਰਡਰ ਵਿੱਚ ਆਈਟਮ ਨੂੰ ਜੋੜਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ" + include_out_of_stock_variants: "ਉਪਲਬਧ ਸਟਾਕ ਦੇ ਨਾਲ ਵੇਰੀਐਂਟ ਸ਼ਾਮਲ ਕਰੋ" + index: + listing_orders: "ਲਿਸਟਿੰਗ ਆਰਡਰ" + new_order: "ਨਵਾਂ ਆਰਡਰ" + capture: "ਕੈਪਚਰ" + ship: "ਸ਼ਿਪ" + edit: "ਸੰਪਾਦਿਤ ਕਰੋ" + order_not_updated: "ਆਰਡਰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ" + note: "ਨੋਟ" + first: "ਪਹਿਲਾ" + last: "ਆਖਰੀ" + previous: "ਪਿਛਲਾ" + next: "ਅਗਲਾ" + loading: "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + no_orders_found: "ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਮਿਲਿਆ" + results_found: "%{number} ਨਤੀਜੇ ਮਿਲੇ।" + viewing: "%{start} ਨੂੰ %{end} ਤੱਕ ਵੇਖਨਾ।" + print_invoices: "ਇਨਵੌਇਸ ਪ੍ਰਿੰਟ ਕਰੋ" + cancel_orders: "ਆਰਡਰ ਰੱਦ ਕਰੋ" + resend_confirmation: "ਪੁਸ਼ਟੀ ਮੁੜ ਭੇਜੋ" + resend_confirmation_confirm_html: "ਇਹ ਗਾਹਕ ਨੂੰ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਦੁਬਾਰਾ ਭੇਜੇਗਾ।
ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + send_invoice: "ਇਨਵੌਇਸ ਭੇਜੋ" + send_invoice_confirm_html: "ਇਹ ਸਾਰੇ ਚੁਣੇ ਹੋਏ ਪੂਰੇ ਆਰਡਰਾਂ ਲਈ ਗਾਹਕ ਇਨਵੌਇਸ ਈਮੇਲ ਕਰੇਗਾ।
ਕੀ ਤੁਸੀਂ ਵਾਕਈ ਅੱਗੇ ਵਧਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + selected: + zero: "ਕੋਈ ਆਰਡਰ ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ" + one: "1 ਆਰਡਰ ਚੁਣਿਆ ਗਿਆ" + other: "%{count} ਆਰਡਰ ਚੁਣੇ ਗਏ" + sortable_header: + payment_state: "ਭੁਗਤਾਨ ਸਥਿਤੀ" + shipment_state: "ਸ਼ਿਪਮੈਂਟ ਦੀ ਸਥਿਤੀ" + completed_at: "ਇਸ ਸਮੇਂ ਪੂਰਾ ਹੋਇਆ" + number: "ਨੰਬਰ" + state: "ਸਥਿਤੀ" + email: "ਗਾਹਕ ਦਾ ਈਮੇਲ" + invoice: + issued_on: "ਇਸ ਮਿਤੀ ਨੂੰ ਜਾਰੀ ਕੀਤਾ ਗਿਆ" + tax_invoice: "ਟੈਕਸ ਇਨਵੌਇਸ" + code: "ਕੋਡ" + from: "ਤੋਂ" + to: "ਇਹਨਾਂ ਨੂੰ ਬਿੱਲ ਕਰੋ" + shipping: "ਸ਼ਿਪਿੰਗ" + order_number: "ਆਰਡਰ ਨੰਬਰ" + invoice_number: "ਇਨਵੌਇਸ ਨੰਬਰ" + payments_list: + date_time: "ਮਿਤੀ/ਸਮਾਂ" + payment_method: "ਭੁਗਤਾਨੇ ਦੇ ਢੰਗ" + payment_state: "ਭੁਗਤਾਨ ਦੀ ਸਥਿਤੀ" + amount: "ਰਕਮ" + note: + note_label: "ਨੋਟ:" + no_note_present: "ਕੋਈ ਨੋਟ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ।" + form: + distribution_fields: + title: "ਵਿਤਰਣ" + distributor: "ਵਿਤਰਕ:" + order_cycle: "ਆਰਡਰ ਸਾਈਕਲ:" + line_item_adjustments: "ਲਾਈਨ ਆਈਟਮ ਸਮਾਯੋਜਨ" + order_adjustments: "ਆਰਡਰ ਸਮਾਯੋਜਨ" + order_total: "ਆਰਡਰ ਦਾ ਕੁੱਲ" + overview: + enterprises_header: + ofn_with_tip: ਐਂਟਰਪ੍ਰਾਈਜ਼ ਉਤਪਾਦਕ ਅਤੇ/ਜਾਂ ਹੱਬ ਹਨ ਅਤੇ ਓਪਨ ਫੂਡ ਨੈਟਵਰਕ ਦੇ ਅੰਦਰ ਸੰਗਠਨ ਦੀ ਮੂਲ ਇਕਾਈ ਹਨ। + enterprise_row: + has_no_enterprise_fees: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਦੀ ਕੋਈ ਫੀਸ ਨਹੀਂ ਹੈ" + has_no_payment_methods: "ਭੁਗਤਾਨ ਦੇ ਕੋਈ ਢੰਗ ਨਹੀਂ ਹਨ" + has_no_shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਕੋਈ ਢੰਗ ਨਹੀਂ ਹਨ" + products: + active_products: + zero: "ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਕਿਰਿਆਸ਼ੀਲ ਉਤਪਾਦ ਨਹੀਂ ਹਨ।" + one: "ਤੁਹਾਡੇ ਕੋਲ ਇੱਕ ਕਿਰਿਆਸ਼ੀਲ ਉਤਪਾਦ ਹੈ" + few: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਉਤਪਾਦ ਹਨ" + many: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਉਤਪਾਦ ਹਨ" + other: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਉਤਪਾਦ ਹਨ" + order_cycles: + order_cycles: "ਆਰਡਰ ਸਾਈਕਲ" + order_cycles_tip: "ਆਰਡਰ ਸਾਈਕਲ ਇਹ ਨਿਰਧਾਰਤ ਕਰਦੇ ਹਨ ਕਿ ਤੁਹਾਡੇ ਉਤਪਾਦ ਗਾਹਕਾਂ ਲਈ ਕਦੋਂ ਅਤੇ ਕਿੱਥੇ ਉਪਲਬਧ ਹੋਣਗੇ।" + you_have_active: + zero: "ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਨਹੀਂ ਹਨ।" + one: "ਤੁਹਾਡੇ ਕੋਲ ਇੱਕ ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਹੈ।" + few: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਹਨ।" + many: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਹਨ।" + other: "ਤੁਹਾਡੇ ਕੋਲ %{count} ਕਿਰਿਆਸ਼ੀਲ ਆਰਡਰ ਸਾਈਕਲ ਹਨ।" + manage_order_cycles: "ਆਰਡਰ ਸਾਈਕਲ ਪ੍ਰਬੰਧਿਤ ਕਰੋ" + version: + view_all_releases: ਸਾਰੀਆਂ ਰਿਲੀਜ਼ਾਂ ਵੇਖੋ + shipping_methods: + index: + shipping_methods: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗ" + new_shipping_method: "ਨਵੀਂ ਸ਼ਿਪਿੰਗ ਦਾ ਢੰਗ" + name: "ਨਾਮ" + products_distributor: "ਵਿਤਰਕ" + zone: "ਜ਼ੋਨ" + calculator: "ਕੈਲਕੁਲੇਟਰ" + display: "ਡਿਸਪਲੇ" + both: "ਚੈਕਆਉਟ ਅਤੇ ਬੈਕ ਆਫਿਸ ਦੋਨੋ" + back_end: "ਸਿਰਫ ਬੈਕ ਆਫਿਸ" + no_shipping_methods_found: "ਸ਼ਿਪਿੰਗ ਦੇ ਕੋਈ ਢੰਗ ਨਹੀਂ ਮਿਲੇ" + new: + new_shipping_method: "ਨਵੀਂ ਸ਼ਿਪਿੰਗ ਦਾ ਢੰਗ" + back_to_shipping_methods_list: "ਸ਼ਿੱਪਿੰਗ ਦੇ ਢੰਗਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + edit: + editing_shipping_method: "ਸ਼ਿਪਿੰਗ ਦੇ ਢੰਗਾਂ ਦਾ ਸੰਪਾਦਨ" + new: "ਨਵਾਂ" + back_to_shipping_methods_list: "ਸ਼ਿੱਪਿੰਗ ਦੇ ਢੰਗਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + form: + categories: "ਸ਼੍ਰੇਣੀਆਂ" + tax_category: "ਟੈਕਸ ਸ਼੍ਰੇਣੀ" + zones: "ਜ਼ੋਨਾਂ" + both: "ਚੈਕਆਉਟ ਅਤੇ ਬੈਕ ਆਫਿਸ ਦੋਨੋ" + back_end: "ਸਿਰਫ ਬੈਕ ਆਫਿਸ" + deactivation_warning: "ਡਿਸਪਲੇ" + payment_methods: + index: + payment_methods: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ" + new_payment_method: "ਭੁਗਤਾਨ ਦਾ ਨਵਾਂ ਢੰਗ" + name: "ਨਾਮ" + products_distributor: "ਵਿਤਰਕ" + provider: "ਪ੍ਰਦਾਤਾ" + environment: "ਵਾਤਾਵਰਣ" + display: "ਡਿਸਪਲੇ" + active: "ਸਕ੍ਰਿਅ" + both: "ਦੋਨੋ" + back_end: "ਸਿਰਫ ਬੈਕ ਆਫਿਸ" + active_yes: "ਹਾਂ" + active_no: "ਨਹੀਂ" + no_payment_methods_found: "ਭੁਗਤਾਨ ਦਾ ਕੋਈ ਢੰਗ ਨਹੀਂ ਲੱਭੀਆ" + new: + new_payment_method: "ਭੁਗਤਾਨ ਦਾ ਨਵਾਂ ਢੰਗ" + back_to_payment_methods_list: "ਭੁਗਤਾਨ ਦੇ ਢੰਗਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + edit: + new: "ਨਵਾਂ" + editing_payment_method: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਦਾ ਸੰਪਾਦਨ" + back_to_payment_methods_list: "ਭੁਗਤਾਨ ਦੇ ਢੰਗਾਂ ਦੀ ਸੂਚੀ ਤੇ ਵਾਪਸ" + stripe_connect: + enterprise_select_placeholder: ਚੁਣੋ... + loading_account_information_msg: ਸਟਰਿੱਪ ਤੋਂ ਖਾਤਾ ਜਾਣਕਾਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ... + stripe_disabled_msg: ਸਟ੍ਰਾਈਪ ਤੋਂ ਖਾਤਾ ਜਾਣਕਾਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ... + request_failed_msg: ਮਾਫ਼ ਕਰਨਾ। ਸਟ੍ਰਾਈਪ ਨਾਲ ਖਾਤੇ ਦੇ ਵੇਰਵਿਆਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੇ ਸਮੇਂ ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ... + account_missing_msg: ਇਸ ਐਂਟਰਪ੍ਰਾਈਜ਼ ਲਈ ਕੋਈ ਸਟ੍ਰਾਈਪ ਖਾਤਾ ਨਹੀਂ ਹੈ। + connect_one: ਇੱਕ ਨੂੰ ਕਨੈਕਟ ਕਰੋ + access_revoked_msg: ਇਸ ਸਟ੍ਰਾਈਪ ਖਾਤੇ ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਖਾਤੇ ਨੂੰ ਦੁਬਾਰਾ ਕਨੇਕਟ ਕਰੋ। + status: ਸਥਿਤੀ + connected: ਕਨੇਕਟ ਕੀਤਾ ਗਿਆ + account_id: ਖਾਤਾ ਆਈ.ਡੀ + business_name: ਕਾਰੋਬਾਰ ਦਾ ਨਾਮ + charges_enabled: ਚਾਰਜ ਸਮਰੱਥ ਕੀਤੇ ਗਏ + form: + name: "ਨਾਮ" + description: "ਵਰਣਨ" + environment: "ਵਾਤਾਵਰਣ" + display: "ਡਿਸਪਲੇ" + active: "ਸਕ੍ਰਿਅ" + active_yes: "ਹਾਂ" + active_no: "ਨਹੀਂ" + both: "ਚੈਕਆਉਟ ਅਤੇ ਬੈਕ ਆਫਿਸ ਦੋਨੋ" + back_end: "ਸਿਰਫ ਬੈਕ ਆਫਿਸ" + tags: "ਟੈਗ" + deactivation_warning: "ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਨੂੰ ਡੀ-ਐਕਟੀਵੇਟ ਕਰਨ ਨਾਲ ਭੁਗਤਾਨ ਦਾ ਢੰਗ ਤੁਹਾਡੀ ਸੂਚੀ ਵਿੱਚੋਂ ਗਾਇਬ ਹੋ ਸਕਦਾ ਹੈ। ਵਿਕਲਪਕ ਤੌਰ ਤੇ, ਤੁਸੀਂ 'ਸਿਰਫ਼ ਬੈਕ ਆਫਿਸ' ਤੇ 'ਡਿਸਪਲੇ' ਵਿਕਲਪ ਨੂੰ ਸੇਟ ਕਰਕੇ ਚੈਕਆਉਟ ਪੇਜ ਤੋਂ ਭੁਗਤਾਨ ਦੇ ਢੰਗ ਨੂੰ ਲੁਕਾ ਸਕਦੇ ਹੋ।" + providers: + provider: "ਪ੍ਰਦਾਤਾ" + check: "ਨਕਦ/ਈਐਫਟੀ/ਆਦਿ (ਭੁਗਤਾਨ ਜਿਸ ਲਈ ਸਵੈਚਲਿਤ ਪ੍ਰਮਾਣਿਕਤਾ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ)" + pin: "ਪਿੰਨ ਭੁਗਤਾਨ" + paypalexpress: "ਪੇਪਾਲ ਐਕਸਪ੍ਰੈਸ" + stripeconnect: "ਸਟ੍ਰਾਈਪ" + stripesca: "ਸਟ੍ਰਾਈਪ SCA" + payments: + source_forms: + stripe: + error_saving_payment: ਭੁਗਤਾਨ ਸੇਵ ਕਰਨ ਵਿੱਚ ਗੜਬੜ + submitting_payment: ਭੁਗਤਾਨ ਜਮ੍ਹਾਂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ... + paypal: + no_payment_via_admin_backend: ਪੇਪਾਲ ਭੁਗਤਾਨਾਂ ਨੂੰ ਬੈਕ ਆਫਿਸ ਵਿੱਚ ਕੈਪਚਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ + products: + image_upload_error: "ਕਿਰਪਾ ਕਰਕੇ ਫੋਟੋ ਨੂੰ JPG, PNG, GIF, SVG ਜਾਂ WEBP ਫਾਰਮੈਟ ਵਿੱਚ ਅੱਪਲੋਡ ਕਰੋ।" + image_not_processable: "ਅਟੈਚ ਕੀਤੀ ਫੋਟੋ ਇੱਕ ਵੈਧ ਫੋਟੋ ਨਹੀਂ ਹੈ।" + new: + title: "ਨਵਾਂ ਉਤਪਾਦ" + new_product: "ਨਵਾਂ ਉਤਪਾਦ" + supplier: "ਸਪਲਾਇਰ" + supplier_select_placeholder: "ਇੱਕ ਸਪਲਾਇਰ ਚੁਣੋ" + product_name: "\"ਉਤਪਾਦ ਦਾ ਨਾਂ\"" + units: "ਯੂਨਿਟ ਸਾਈਜ਼" + value: "ਵਲਯੂ" + unit_name: "ਯੂਨਿਟ ਦਾ ਨਾਮ" + price: "ਕੀਮਤ" + unit_price: "ਯੂਨਿਟ ਦੀ ਕੀਮਤ" + unit_price_legend: "ਆਈਟਮ ਦੀ ਕੀਮਤ ਦੇ ਅਧਾਰ ਤੇ ਗਣਨਾ ਕੀਤੀ ਗਈ" + on_hand: "ਹੱਥ ਵਿਚ" + on_demand: "ਡਿਮਾਂਡ ਤੇ" + product_description: "ਉਤਪਾਦ ਵਰਣਨ" + image: "ਤਸਵੀਰ" + unit_name_placeholder: 'ਜਿਵੇਂ ਕਿ ਗੁੱਛੇ''' + index: + header: + title: ਥੋਕ ਸੰਪਾਦਿਤ ਉਤਪਾਦ + indicators: + title: ਉਤਪਾਦ ਲੋਡ ਹੋ ਰਹੇ ਹਨ... + no_products: "ਅਜੇ ਤੱਕ ਕੋਈ ਉਤਪਾਦ ਨਹੀਂ। ਤੁਸੀਂ ਕੁਝ ਕਿਉਂ ਨਹੀਂ ਜੋੜਦੇ?" + no_results: "ਮਾਫ਼ ਕਰਨਾ, ਕੋਈ ਨਤੀਜਾ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ" + products_head: + name: ਨਾਮ + unit: ਯੂਨਿਟ + display_as: ਇਸ ਤਰ੍ਹਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ + category: ਸ਼੍ਰੇਣੀ + tax_category: ਟੈਕਸ ਸ਼੍ਰੇਣੀ + inherits_properties?: ਪ੍ਰਾਪਰਟੀਜ਼ ਅਪਣਾਉਂਦੇ ਹਨ? + av_on: "ਏਵੀ. ਆਨ" + import_date: "ਇਮਪੋਰਟ ਮਿਤੀ" + products_variant: + variant_has_n_overrides: "ਇਸ ਵੇਰੀਐਂਟ ਵਿੱਚ %{n} ਓਵਰਰਾਈਡ ਹਨ" + new_variant: "ਨਵਾਂ ਵੇਰੀਐਂਟ" + product_name: '"ਉਤਪਾਦ ਦਾ ਨਾਂ"' + primary_taxon_form: + product_category: ਉਤਪਾਦ ਸ਼੍ਰੇਣੀ + group_buy_form: + group_buy: "ਸਮੂਹ ਖਰੀਦ?" + bulk_unit_size: ਥੋਕ ਯੂਨਿਟ ਦਾ ਸਾਈਜ਼ + display_as: + display_as: ਇਸ ਤਰ੍ਹਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ + reports: + table: + select_and_search: "ਫਿਲਟਰ ਚੁਣੋ ਅਤੇ ਆਪਣੇ ਡੇਟਾ ਤੱਕ ਪਹੁੰਚਣ ਲਈ %{option} ਤੇ ਕਲਿੱਕ ਕਰੋ।" + customer_names_message: + customer_names_tip: "ਜੇਕਰ ਤੁਹਾਡੇ ਦੁਆਰਾ ਸਪਲਾਈ ਕੀਤੇ ਗਏ ਆਰਡਰਾਂ ਲਈ ਗਾਹਕ ਦੇ ਨਾਂ ਲੁਕੇ ਹੋਏ ਹਨ, ਤਾਂ ਤੁਸੀਂ ਵਿਤਰਕ ਨਾਲ ਸੰਪਰਕ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਪੁੱਛ ਸਕਦੇ ਹੋ ਕਿ ਕੀ ਉਹ ਆਪਣੇ ਸਪਲਾਇਰਾਂ ਨੂੰ ਗਾਹਕਾਂ ਦੇ ਨਾਮ ਵੇਖਣ ਦੀ ਆਗਿਆ ਦੇਣ ਲਈ ਆਪਣੀਆਂ ਸ਼ਾਪਾਂ ਦੀਆਂ ਤਰਜੀਹਾਂ ਨੂੰ ਅਪਡੇਟ ਕਰ ਸਕਦੇ ਹਨ।" + products_and_inventory: + all_products: + message: "ਨੋਟ ਕਰੋ ਕਿ ਰਿਪੋਰਟ ਕੀਤੇ ਗਏ ਸਟਾਕ ਦੇ ਪੱਧਰ ਕੇਵਲ ਸਪਲਾਇਰ ਉਤਪਾਦ ਸੂਚੀਆਂ ਤੋਂ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੇ ਸਟਾਕ ਦੀ ਮਾਤਰਾ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਨਵੇਂਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋ ਤਾਂ ਇਸ ਵੈਲਯੂ ਨੂੰ ਇਸ ਰਿਪੋਰਟ ਵਿੱਚ ਨਜ਼ਰਅੰਦਾਜ਼ ਕੀਤਾ ਜਾਵੇਗਾ।" + users: + index: + listing_users: "ਲਿਸਟਿੰਗ ਉਪਭੋਗਤਾ" + new_user: "ਨਵਾਂ ਉਪਭੋਗਤਾ" + user: "ਉਪਭੋਗਤਾ" + enterprise_limit: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੀਮਾ" + search: "ਖੋਜੋ" + email: "ਈਮੇਲ" + edit: + editing_user: "ਉਪਭੋਗਤਾ ਦਾ ਸੰਪਾਦਨ" + back_to_users_list: "ਉਪਭੋਗਤਾ ਸੂਚੀ ਤੇ ਵਾਪਸ" + general_settings: "ਆਮ ਸੈਟਿੰਗਾਂ" + form: + disabled: "ਅਸਮਰੱਥ ਕੀਤਾ ਗਿਆ" + email: "ਈਮੇਲ" + roles: "ਭੂਮਿਕਾਵਾਂ" + enterprise_limit: "ਐਂਟਰਪ੍ਰਾਈਜ਼ ਸੀਮਾ" + confirm_password: "ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + password: "ਪਾਸਵਰਡ" + locale: "ਭਾਸ਼ਾ" + email_confirmation: + confirmation_pending: "ਈਮੇਲ ਪੁਸ਼ਟੀਕਰਨ ਲੰਬਿਤ ਹੈ। ਅਸੀਂ %{address} ਤੇ ਪੁਸ਼ਟੀਕਰਨ ਈਮੇਲ ਭੇਜਿਆ ਹੈ।" + variants: + index: + sku: "SKU" + price: "ਕੀਮਤ" + options: "ਵਿਕਲਪ" + no_results: "ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ" + option_types: "ਵਿਕਲਪ ਦੀਆਂ ਕਿਸਮਾਂ" + option_values: "ਵੈਲਯੂ" + and: "ਅਤੇ" + new_variant: "ਨਵਾਂ ਵੇਰੀਐਂਟ" + show_active: "ਕਿਰਿਆਸ਼ੀਲ ਵਿਖਾਓ" + show_deleted: "ਹਟਾਏ ਹੋਏ ਵਿਖਾਓ" + new: + new_variant: "ਨਵਾਂ ਵੇਰੀਐਂਟ" + form: + sku: "SKU" + price: "ਕੀਮਤ" + unit_price: "ਯੂਨਿਟ ਦੀ ਕੀਮਤ" + display_as: "ਇਸ ਤਰ੍ਹਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ" + display_name: "ਡਿਸਪਲੇ ਕੀਤੇ ਜਾਣ ਵਾਲਾ ਨਾਂ" + display_as_placeholder: 'ਜਿਵੇਂ ਕਿ 2 ਕਿਲੋ' + display_name_placeholder: 'ਜਿਵੇਂ ਕਿ ਟਮਾਟਰ''' + autocomplete: + out_of_stock: "ਸਟਾਕ ਵਿੱਚ ਨਹੀਂ ਹੈਂ" + producer_name: "ਉਤਪਾਦਕ" + unit: "ਯੂਨਿਟ" + shared: + configuration_menu: + terms_of_service: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ" + sortable_header: + name: "ਨਾਮ" + number: "ਨੰਬਰ" + completed_at: "ਇਸ ਸਮੇਂ ਪੂਰਾ ਹੋਇਆ" + state: "ਸਥਿਤੀ" + payment_state: "ਭੁਗਤਾਨ ਸਥਿਤੀ" + shipment_state: "ਸ਼ਿਪਮੈਂਟ ਦੀ ਸਥਿਤੀ" + email: "ਈਮੇਲ" + total: "ਕੁੱਲ" + billing_address_name: "ਨਾਮ" + taxons: + form: + name: ਨਾਂ + permalink: ਪਰਮਾਲਿੰਕ + description: ਵਰਣਨ + general_settings: + edit: + legal_settings: "ਕਾਨੂੰਨੀ ਸੈਟਿੰਗਾਂ" + cookies_consent_banner_toggle: "ਕੂਕੀਜ਼ ਸਹਿਮਤੀ ਬੈਨਰ ਡਿਸਪਲੇ ਕਰੋ" + privacy_policy_url: "ਗੋਪਨੀਯਤਾ ਨੀਤੀ URL" + enterprises_require_tos: "ਐਂਟਰਪ੍ਰਾਈਜ਼ਾਂ ਨੂੰ ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਸਵੀਕਾਰ ਕਰਨੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ" + shoppers_require_tos: "ਖਰੀਦਦਾਰਾਂ ਨੂੰ ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ" + cookies_policy_matomo_section: "ਕੂਕੀਜ਼ ਨੀਤੀ ਦੇ ਪੇਜ ਉਤੇ ਮਾਟੋਮੋ ਸੈਕਸ਼ਨ ਦਿਸਪਲੇ ਕਰੋ" + footer_tos_url: "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦਾ URL" + checkout: + payment: + stripe: + choose_one: ਇੱਕ ਚੁਣੋ + enter_new_card: ਨਵੇਂ ਕਾਰਡ ਲਈ ਵੇਰਵੇ ਦਰਜ ਕਰੋ + used_saved_card: "ਸੇਵ ਕੀਤੇ ਗਏ ਕਾਰਡ ਦਾ ਉਪਯੋਗ ਕਰੋ" + or_enter_new_card: "ਜਾਂ, ਇੱਕ ਨਵੇਂ ਕਾਰਡ ਲਈ ਵੇਰਵੇ ਦਰਜ ਕਰੋ:" + remember_this_card: ਇਹ ਕਾਰਡ ਯਾਦ ਰੱਖੀਏ? + stripe_sca: + choose_one: ਇੱਕ ਚੁਣੋ + enter_new_card: ਨਵੇਂ ਕਾਰਡ ਲਈ ਵੇਰਵੇ ਦਰਜ ਕਰੋ + used_saved_card: "ਸੇਵ ਕੀਤੇ ਗਏ ਕਾਰਡ ਦਾ ਉਪਯੋਗ ਕਰੋ" + or_enter_new_card: "ਜਾਂ, ਇੱਕ ਨਵੇਂ ਕਾਰਡ ਲਈ ਵੇਰਵੇ ਦਰਜ ਕਰੋ:" + remember_this_card: ਇਹ ਕਾਰਡ ਯਾਦ ਰੱਖੀਏ? + date_picker: + flatpickr_date_format: "ਸਾਲ-ਮਹੀਨਾ-ਦਿਨ" + flatpickr_datetime_format: "ਸਾਲ-ਮਹੀਨਾ-ਦਿਨ ਘੰਟੇ: ਮਿੰਟ" + today: "ਅੱਜ" + now: "ਹੁਣੇ" + close: "ਬੰਦ" + orders: + error_flash_for_unavailable_items: "ਤੁਹਾਡੇ ਕਾਰਟ ਵਿੱਚ ਇੱਕ ਆਈਟਮ ਅਣਉਪਲਬਧ ਹੋ ਗਈ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਚੁਣੀਆਂ ਗਈਆਂ ਮਾਤਰਾਵਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ।" + edit: + login_to_view_order: "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣਾ ਆਰਡਰ ਵੇਖਣ ਲਈ ਲਾਗਇਨ ਕਰੋ।" + bought: + item: "ਇਸ ਆਰਡਰ ਸਾਈਕਲ ਵਿੱਚ ਪਹਿਲਾਂ ਹੀ ਆਰਡਰ ਕੀਤਾ ਗਿਆ ਹੈ" + line_item: + insufficient_stock: "ਨਾਕਾਫ਼ੀ ਸਟਾਕ ਉਪਲਬਧ ਹੈ, ਸਿਰਫ਼ %{on_hand} ਬਾਕੀ" + out_of_stock: "ਸਟਾਕ ਵਿੱਚ ਨਹੀਂ ਹੈਂ" + unavailable_item: "ਇਸ ਵੇਲੇ ਅਣਉਪਲਬਧ" + shipment_states: + backorder: ਬੈਕ ਆਰਡਰ + partial: ਅੰਸ਼ਕ + pending: ਲੰਬਿਤ + ready: ਤਿਆਰ + shipped: ਭੇਜਿਆ ਗਿਆ + canceled: ਰੱਦ ਕੀਤਾ ਗਿਆ + payment_states: + balance_due: ਬਕਾਇਆ ਰਕਮ + completed: ਮੁਕੰਮਲ ਕੀਤਾ + checkout: ਚੈਕਆਊਟ + credit_owed: ਬਕਾਇਆ ਕਰਜ਼ਾ + failed: ਅਸਫ਼ਲ + paid: ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ + pending: ਲੰਬਿਤ + processing: ਸੰਸਾਧਨ + requires_authorization: "ਅਧਿਕਾਰ ਦੀ ਲੋੜ ਹੈ" + void: ਖਾਲੀ + invalid: ਅਵੈਧ + authorise: ਅਧਿਕਾਰਤ + order_mailer: + cancel_email: + customer_greeting: "ਪਿਆਰੇ %{name}," + instructions_html: "%{distributor} ਦੇ ਨਾਲ ਤੁਹਾਡਾ ਆਰਡਰ ਰੱਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਰਿਕਾਰਡਾਂ ਲਈ ਇਸ ਕੈਂਸਲੇਸ਼ਨ ਜਾਣਕਾਰੀ ਨੂੰ ਬਣਾਏ ਰੱਖੋ।" + dont_cancel: "ਜੇਕਰ ਤੁਸੀਂ ਆਪਣਾ ਮਨ ਬਦਲ ਲਿਆ ਹੈ ਜਾਂ ਇਸ ਆਰਡਰ ਨੂੰ ਰੱਦ ਨਹੀਂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ %{email} ਨਾਲ ਸੰਪਰਕ ਕਰੋ" + order_summary_canceled_html: "ਆਰਡਰ ਸੰਖੇਪ #%{number} [ਰੱਦ]" + details: "Here are the details of what you ordered:" + unpaid_order: "ਤੁਹਾਡੇ ਆਰਡਰ ਦਾ ਭੁਗਤਾਨ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਸੀ ਇਸਲਈ ਕੋਈ ਰਿਫੰਡ ਨਹੀਂ ਕੀਤਾ ਗਿਆ" + paid_order: "ਤੁਹਾਡੇ ਆਰਡਰ ਦਾ ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ ਸੀ ਇਸਲਈ %{distributor} ਨੇ ਪੂਰੀ ਰਕਮ ਵਾਪਸ ਕਰ ਦਿੱਤੀ ਹੈ" + credit_order: "ਤੁਹਾਡੇ ਆਰਡਰ ਦਾ ਭੁਗਤਾਨ ਕੀਤਾ ਗਿਆ ਸੀ ਇਸਲਈ ਤੁਹਾਡੇ ਖਾਤੇ ਵਿੱਚ ਪੈਸੇ ਜਮਾ ਕਰ ਦਿੱਤੇ ਗਏ ਹਨ" + subject: "ਆਰਡਰ ਰੱਦ" + cancel_email_for_shop: + greeting: "ਪਿਆਰੇ %{name}," + subject: "ਆਰਡਰ ਰੱਦ" + intro: "ਇੱਕ ਗਾਹਕ ਨੇ ਆਪਣਾ ਆਰਡਰ #%{number} ਰੱਦ ਕਰ ਦਿੱਤਾ ਹੈ।" + view_cancelled_order: "ਰੱਦ ਕੀਤੇ ਆਰਡਰ ਵੇਖੋ" + confirm_email: + subject: "ਆਰਡਰ ਦੀ ਪੁਸ਼ਟੀ" + invoice_email: + hi: "ਸਤਿ ਸ੍ਰੀ ਅਕਾਲ %{name}" + invoice_attached_text: ਕਿਰਪਾ ਕਰਕੇ ਤੁਹਾਡੇ ਹਾਲੀਆ ਆਰਡਰ ਲਈ ਨੱਥੀ ਇਨਵੌਇਸ ਲੱਭੋ + user_mailer: + reset_password_instructions: + request_sent_text: |+ + ਤੁਹਾਡੇ ਪਾਸਵਰਡ ਨੂੰ ਰੀਸੈਟ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਗਈ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਇਹ ਬੇਨਤੀ ਨਹੀਂ ਕੀਤੀ ਹੈ, ਤਾਂ ਇਸ ਈਮੇਲ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰੋ। + + link_text: >+ + ਜੇਕਰ ਤੁਸੀਂ ਇਹ ਬੇਨਤੀ ਕੀਤੀ ਹੈ ਤਾਂ ਹੇਠਾਂ ਦਿੱਤੇ ਲਿੰਕ ਉਤੇ ਕਲਿੱਕ ਕਰੋ: + + issue_text: | + ਜੇਕਰ ਉਪਰੋਕਤ URL ਕੰਮ ਨਹੀਂ ਕਰਦਾ ਹੈ ਤਾਂ ਇਸਨੂੰ ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਕਾਪੀ ਅਤੇ ਪੇਸਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ। ਜੇਕਰ ਸਮੱਸਿਆਵਾਂ ਜਾਰੀ ਰਹਿੰਦੀਆਂ ਹਨ ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਸਾਡੇ ਨਾਲ ਬੇਝਿਜਕ ਹੋ ਕੇ ਸੰਪਰਕ ਕਰੋ। + subject: "ਪਾਸਵਰਡ ਰੀਸੈਟ ਕਰਨ ਦੀਆਂ ਹਦਾਇਤਾਂ" + confirmation_instructions: + subject: "ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ OFN ਖਾਤੇ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ" + payment_mailer: + authorize_payment: + subject: "ਕਿਰਪਾ ਕਰਕੇ OFN ਤੇ %{distributor} ਨੂੰ ਆਪਣਾ ਭੁਗਤਾਨ ਅਧਿਕਾਰਤ ਕਰੋ" + instructions: "%{distributor} ਨੂੰ %{amount} ਦੇ ਤੁਹਾਡੇ ਭੁਗਤਾਨ ਲਈ ਵਾਧੂ ਪ੍ਰਮਾਣਿਕਤਾ ਦੀ ਲੋੜ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਭੁਗਤਾਨ ਨੂੰ ਅਧਿਕਾਰਤ ਕਰਨ ਲਈ ਹੇਠਾਂ ਦਿੱਤੇ URL ਉਤੇ ਜਾਓ:" + authorization_required: + subject: "ਭੁਗਤਾਨ ਲਈ ਗਾਹਕ ਤੋਂ ਅਧਿਕਾਰ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ" + message: "ਆਰਡਰ %{order_number} ਦੇ ਭੁਗਤਾਨ ਲਈ ਗਾਹਕ ਤੋਂ ਵਾਧੂ ਅਧਿਕਾਰ ਦੀ ਲੋੜ ਹੈ। ਗਾਹਕ ਨੂੰ ਈਮੇਲ ਰਾਹੀਂ ਸੂਚਿਤ ਕੀਤਾ ਗਿਆ ਹੈ ਅਤੇ ਜਦੋਂ ਤੱਕ ਇਹ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ ਜਾਂਦਾ, ਭੁਗਤਾਨ ਬਕਾਏ ਵਜੋਂ ਵਿਖਾਈ ਦੇਵੇਗਾ।" + shipment_mailer: + shipped_email: + dear_customer: "ਪਿਆਰੇ ਗਾਹਕ," + instructions: "%{distributor} ਤੋਂ ਤੁਹਾਡਾ ਆਰਡਰ ਭੇਜ ਦਿੱਤਾ ਗਿਆ ਹੈ" + shipment_summary: "ਸ਼ਿਪਮੈਂਟ ਸੰਖੇਪ" + subject: "ਸ਼ਿਪਮੈਂਟ ਬਾਰੇ ਸੂਚਨਾ" + thanks: "ਤੁਹਾਡੇ ਕਾਰੋਬਾਰ ਦੇਣ ਲਈ ਧੰਨਵਾਦ।" + track_information: "ਟਰੈਕਿੰਗ ਜਾਣਕਾਰੀ: %{tracking}" + track_link: "ਟਰੈਕਿੰਗ ਲਿੰਕ: %{url}" + picked_up_instructions: "%{distributor} ਤੋਂ ਤੁਹਾਡਾ ਆਰਡਰ ਪਿਕ-ਅੱਪ ਕਰ ਲਿਆ ਗਿਆ ਹੈ" + picked_up_subject: "ਪਿਕ ਅੱਪ ਨੋਟੀਫਿਕੇਸ਼ਨ" + test_mailer: + test_email: + greeting: "ਵਧਾਈਆਂ!" + message: "ਜੇਕਰ ਤੁਹਾਨੂੰ ਇਹ ਈਮੇਲ ਪ੍ਰਾਪਤ ਹੋਈ ਹੈ, ਤਾਂ ਤੁਹਾਡੀ ਈਮੇਲ ਸੈਟਿੰਗਾਂ ਸਹੀ ਹਨ।" + subject: "ਟੈਸਟ ਮੇਲ" + order_state: + address: ਪਤਾ + adjustments: ਸਮਾਯੋਜਨ + awaiting_return: ਵਾਪਸੀ ਦੀ ਉਡੀਕ + canceled: ਰੱਦ ਕੀਤਾ ਗਿਆ + cart: ਕਾਰਟ + confirmation: "ਪੁਸ਼ਟੀ" + complete: ਪੂਰਾ + confirm: ਪੁਸ਼ਟੀ ਕਰੋ + delivery: ਡਿਲਿਵਰੀ + paused: ਵਿਰਾਮ ਕੀਤਾ + payment: ਭੁਗਤਾਨ + pending: ਲੰਬਿਤ + resumed: ਮੁੜ ਸ਼ੁਰੂ ਕੀਤਾ + returned: ਵਾਪਸ ਪਰਤਿਆ + subscription_state: + active: ਸਕ੍ਰਿਅ + pending: ਲੰਬਿਤ + ended: ਖਤਮ ਹੋ ਗਿਆ + paused: ਵਿਰਾਮ ਕੀਤਾ + canceled: ਰੱਦ ਕੀਤਾ ਗਿਆ + paypal: + already_refunded: "ਇਹ ਭੁਗਤਾਨ ਵਾਪਸ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ ਅਤੇ ਇਸ ਤੇ ਕੋਈ ਹੋਰ ਕਾਰਵਾਈ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।" + no_payment_via_admin_backend: "ਤੁਸੀਂ ਇਸ ਸਮੇਂ ਐਡਮਿਨ ਬੈਕਐਂਡ ਦੁਆਰਾ ਪੇਪਾਲ ਖਾਤਿਆਂ ਤੋਂ ਚਾਰਜ ਨਹੀਂ ਲੈ ਸਕਦੇ ਹੋ।" + transaction: "ਪੇਪਾਲ ਲੈਣ-ਦੇਣ" + payer_id: "ਭੁਗਤਾਨਕਰਤਾ ਆਈਡੀ" + transaction_id: "\"ਲੈਣ-ਦੇਣ ਆਈਡੀ\"" + token: "ਟੋਕਨ" + refund: "ਰਿਫੰਡ" + refund_amount: "ਰਕਮ" + original_amount: "ਅਸਲ ਰਕਮ: %{amount}" + refund_successful: "ਪੇਪਾਲ ਰਿਫੰਡ ਸਫਲ" + refund_unsuccessful: "ਪੇਪਾਲ ਰਿਫੰਡ ਅਸਫਲ" + actions: + refund: "ਰਿਫੰਡ" + flash: + cancel: "ਪੇਪਾਲ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰਨਾ ਚਾਹੁੰਦੇ? ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ।" + connection_failed: "ਪੇਪਾਲ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।" + generic_error: "ਪੇਪਾਲ ਅਸਫਲ ਰਿਹਾ। %{reasons}" + users: + api_keys: + regenerate_key: "ਕੁੰਜੀ ਨੂੰ ਮੁੜ ਤਿਆਰ ਕਰੋ" + title: API ਕੁੰਜੀ + webhook_endpoints: + title: ਵੈਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ + description: ਸਿਸਟਮ ਵਿੱਚ ਇਵੈਂਟਸ ਵੈਬਹੁੱਕ ਨੂੰ ਬਾਹਰੀ ਸਿਸਟਮਾਂ ਲਈ ਟਰਿੱਗਰ ਕਰ ਸਕਦੇ ਹਨ। + event_types: + order_cycle_opened: ਆਰਡਰ ਸਾਈਕਲ ਖੋਲ੍ਹਿਆ ਗਿਆ + event_type: + header: ਇਵੇੰਟ ਦੀ ਕਿਸਮ + url: + header: ਐਂਡਪੁਆਇੰਟ URL + create_placeholder: ਰਿਮੋਟ ਵੈਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ ਦਾ URL ਦਾਖਲ ਕਰੋ + developer_settings: + title: ਡਿਵੈਲਪਰ ਸੈਟਿੰਗਜ਼ + form: + account_settings: ਖਾਤਾ ਸੈਟਿੰਗਜ਼ + show: + tabs: + developer_settings: ਡਿਵੈਲਪਰ ਸੈਟਿੰਗਜ਼ + orders: ਆਰਡਰ + cards: ਕ੍ਰੈਡਿਟ ਕਾਰਡ + transactions: ਲੈਣ-ਦੇਣ + settings: ਖਾਤਾ ਸੈਟਿੰਗਜ਼ + unconfirmed_email: "ਇਸ ਲਈ ਬਕਾਇਆ ਈਮੇਲ ਪੁਸ਼ਟੀਕਰਨ: %{unconfirmed_email}। ਨਵੀਂ ਈਮੇਲ ਦੀ ਪੁਸ਼ਟੀ ਹੋਣ ਤੇ ਤੁਹਾਡਾ ਈਮੇਲ ਪਤਾ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾਵੇਗਾ।" + orders: + open_orders: ਖੁੱਲ੍ਹੇ ਆਰਡਰ + past_orders: ਪਿਛਲੇ ਆਰਡਰ + transactions: + transaction_history: ਲੈਣ-ਦੇਣ ਦਾ ਇਤਿਹਾਸ + authorisation_required: ਅਧਿਕਾਰ ਦੀ ਲੋੜ ਹੈ + authorise: ਅਧਿਕਾਰਤ ਕਰੋ + open_orders: + order: ਆਰਡਰ + shop: ਸ਼ਾਪ + changes_allowed_until: ਇਸ ਤੱਕ ਬਦਲਾਵ ਦੀ ਇਜਾਜ਼ਤ ਹੈ + items: ਵਸਤੂਆਂ + total: ਕੁੱਲ + edit: ਸੰਪਾਦਿਤ ਕਰੋ + cancel: ਰੱਦ ਕਰੋ + closed: ਬੰਦ ਹੈ + until: ਤੱਕ + past_orders: + order: ਆਰਡਰ + shop: ਸ਼ਾਪ + completed_at: ਇਸ ਸਮੇਂ ਪੂਰਾ ਹੋਇਆ + items: ਵਸਤੂਆਂ + total: ਕੁੱਲ + paid?: ਭੁਗਤਾਨ ਕੀਤਾ? + status: ਸਥਿਤੀ + completed: ਪੂਰਾ ਹੋਇਆ + cancelled: ਰੱਦ ਕੀਤਾ ਗਿਆ + saved_cards: + default?: ਡਿਫੌਲਟ? + delete?: ਹਟਾਓ? + cards: + authorised_shops: ਅਧਿਕਾਰਤ ਸ਼ਾਪਾਂ + authorised_shops_agreement: ਇਹ ਉਹਨਾਂ ਸ਼ਾਪਾਂ ਦੀ ਸੂਚੀ ਹੈ ਜਿਹਨਾਂ ਨੂੰ ਤੁਹਾਡੇ ਕੋਲ ਕਿਸੇ ਵੀ ਗਾਹਕੀ (ਜਿਵੇਂ ਕਿ ਆਰਡਰ ਦੁਹਰਾਉਣ) ਲਈ ਤੁਹਾਡੇ ਡਿਫੌਲਟ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਤੋਂ ਚਾਰਜ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਹੈ। ਤੁਹਾਡੇ ਕਾਰਡ ਦੇ ਵੇਰਵੇ ਸੁਰੱਖਿਅਤ ਰੱਖੇ ਜਾਣਗੇ ਅਤੇ ਦੁਕਾਨ ਮਾਲਕਾਂ ਨਾਲ ਸਾਂਝੇ ਨਹੀਂ ਕੀਤੇ ਜਾਣਗੇ। ਤੁਹਾਡੇ ਤੋਂ ਚਾਰਜ ਲਏ ਜਾਣ ਤੇ ਤੁਹਾਨੂੰ ਹਮੇਸ਼ਾ ਸੂਚਿਤ ਕੀਤਾ ਜਾਵੇਗਾ। ਕਿਸੇ ਸ਼ਾਪ ਲਈ ਬਕਸੇ ਤੇ ਨਿਸ਼ਾਨ ਲਗਾ ਕੇ, ਤੁਸੀਂ ਉਸ ਸ਼ਾਪ ਨੂੰ ਉਸ ਵਿੱਤੀ ਸੰਸਥਾ ਨੂੰ ਨਿਰਦੇਸ਼ ਭੇਜਣ ਲਈ ਅਧਿਕਾਰਤ ਕਰਨ ਲਈ ਸਹਿਮਤ ਹੋ ਰਹੇ ਹੋ ਜਿਸ ਨੇ ਉਸ ਸ਼ਾਪ ਨਾਲ ਤੁਹਾਡੇ ਦੁਆਰਾ ਬਣਾਈ ਗਈ ਕਿਸੇ ਵੀ ਗਾਹਕੀ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੇ ਅਨੁਸਾਰ ਭੁਗਤਾਨ ਲੈਣ ਲਈ ਤੁਹਾਡਾ ਕਾਰਡ ਜਾਰੀ ਕੀਤਾ ਹੈ। + saved_cards_popover: ਇਹ ਉਹਨਾਂ ਕਾਰਡਾਂ ਦੀ ਸੂਚੀ ਹੈ ਜੋ ਤੁਸੀਂ ਬਾਅਦ ਵਿੱਚ ਵਰਤੋਂ ਲਈ ਸੇਵ ਕਰਨ ਦੀ ਚੋਣ ਕੀਤੀ ਹੈ। ਜਦੋਂ ਤੁਸੀਂ ਆਰਡਰ ਚੈਕਆਊਟ ਕਰਦੇ ਹੋ ਤਾਂ ਤੁਹਾਡਾ 'ਡਿਫੌਲਟ' ਆਪਣੇ ਆਪ ਚੁਣਿਆ ਜਾਵੇਗਾ, ਅਤੇ ਤੁਹਾਡੇ ਦੁਆਰਾ ਅਜਿਹਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਗਈ ਕਿਸੇ ਵੀ ਸ਼ਾਪ ਦੁਆਰਾ ਚਾਰਜ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ (ਸੱਜੇ ਵੇਖੋ)। + authorised_shops: + shop_name: "ਸ਼ਾਪ ਦਾ ਨਾਂ" + allow_charges?: "ਡਿਫਾਲਟ ਕਾਰਡ ਨੂੰ ਚਾਰਜ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ?" + no_default_saved_cards_tooltip: ਚਾਰਜ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਲਈ ਤੁਹਾਨੂੰ ਇੱਕ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੂੰ ਡਿਫੌਲਟ ਵਜੋਂ ਮਾਰਕ ਕਰਨ ਦੀ ਲੋੜ ਹੈ। + localized_number: + invalid_format: ਇੱਕ ਅਵੈਧ ਫਾਰਮੈਟ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਨੰਬਰ ਦਾਖਲ ਕਰੋ। + api: + invalid_api_key: "ਅਵੈਧ API ਕੁੰਜੀ (%{key}) ਨਿਰਧਾਰਤ ਕੀਤੀ ਗਈ।" + unauthorized: "ਤੁਸੀਂ ਉਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ।" + invalid_resource: "ਅਵੈਧ ਸਰੋਤ। ਕਿਰਪਾ ਕਰਕੇ ਗਲਤੀਆਂ ਠੀਕ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + resource_not_found: "ਜਿਸ ਸਰੋਤ ਨੂੰ ਤੁਸੀਂ ਲੱਭ ਰਹੇ ਸੀ, ਉਹ ਲੱਭਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।" + access: "API ਐਕਸੈਸ" + key: "ਕੁੰਜੀ" + clear_key: "ਕੁੰਜੀ ਸਾਫ਼ ਕਰੋ" + regenerate_key: "ਕੁੰਜੀ ਨੂੰ ਮੁੜ ਤਿਆਰ ਕਰੋ" + no_key: "ਕੋਈ ਕੁੰਜੀ ਨਹੀਂ" + generate_key: "API ਕੁੰਜੀ ਬਣਾਓ" + key_generated: "ਕੁੰਜੀ ਤਿਆਰ ਕੀਤੀ ਗਈ" + key_cleared: "ਕੁੰਜੀ ਹਟਾਈ ਗਈ" + shipment: + cannot_ready: "ਸ਼ਿਪਮੈਂਟ ਤਿਆਰ ਨਹੀਂ ਹੋ ਸਕਦੀ।" + invalid_taxonomy_id: "ਅਵੈਧ ਟੈਕਸੋਨੌਮੀ ਆਈਡੀ।" + toggle_api_key_view: "ਉਪਭੋਗਤਾ ਲਈ API ਕੁੰਜੀ ਦ੍ਰਿਸ਼ ਵਿਖਾਓ" + unit: ਯੂਨਿਟ + per_unit: ਪ੍ਰਤੀ ਯੂਨਿਟ + datetime: + distance_in_words: + half_a_minute: ਅੱਧਾ ਮਿੰਟ + components: + multiple_checked_select: + filter_placeholder: "ਫਿਲਟਰ ਵਿਕਲਪ" + search_input: + placeholder: ਖੋਜੋ + selector_with_filter: + selected_items: "%{count} ਚੁਣੇ ਗਏ" + search_placeholder: ਖੋਜੋ + pagination: + next: ਅਗਲਾ + previous: ਪਿਛਲਾ diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 67308657bc..da83204552 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -213,7 +213,6 @@ pl: cancel_order: "Anuluj zamówienie" confirm_send_invoice: "Rachunek do tego zamówienia zostanie wysłany do klienta. Kontynuować?" confirm_resend_order_confirmation: "Na pewno ponownie wysłać potwierdzającą wiadomość e-mail?" - must_have_valid_business_number: "%{enterprise_name} musi mieć ważny NIP, aby można było wysyłać faktury." invoice: "Faktura" cancelled: "Anulowane" more: "Więcej" @@ -334,6 +333,9 @@ pl: actions: edit: Edytuj clone: Kopiuj + delete: Usuń + image: + edit: Edytuj begins_at: Początek o begins_on: Początek się w dniu customer: Klient @@ -518,6 +520,8 @@ pl: producers: label: Producenci search: Szukaj + edit_image: + close: Wstecz product_import: title: Import produktów file_not_found: Nie znaleziono pliku lub nie można go otworzyć @@ -557,6 +561,9 @@ pl: product_categories: Kategorie produktów tax_categories: Kategorie podatków shipping_categories: Kategorie dostaw + dfc_import_form: + enterprise: "Podmiot" + import: "Import" import: review: Przejrzyj import: Import @@ -1354,6 +1361,7 @@ pl: invoice_tax_total: "Suma podatku VAT:" tax_invoice: "FAKTURA PODATKOWA" tax_total: "Całkowity podatek (%{rate}):" + invoice_shipping_category_pickup: "Odbiór" total_excl_tax: "Razem (bez podatku):" total_incl_tax: "Razem (z podatkiem):" abn: "NIP:" @@ -3460,6 +3468,10 @@ pl: email: "E-mail" total: "Razem" billing_address_name: "Nazwa" + taxons: + form: + name: Nazwa + description: Opis general_settings: edit: legal_settings: "Ustawienia prawne" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index d9ef8e2d4b..373e6e7e20 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -243,7 +243,6 @@ pt: cancel_order: "Cancelar Encomenda" confirm_send_invoice: "Vai ser enviada uma factura desta encomenda ao consumidor. Tem a certeza que deseja continuar?" confirm_resend_order_confirmation: "Tem a certeza que deseja enviar novamente o email de confirmação de encomenda? " - must_have_valid_business_number: "%{enterprise_name}tem de ter um NIPC/NIF válido antes de se poder enviar facturas." invoice: "Factura" active: "Ativo" cancelled: "Cancelada" @@ -375,6 +374,9 @@ pt: actions: edit: Editar clone: Clonar + delete: Apagar + image: + edit: Editar begins_at: Começa às begins_on: Começa em customer: Consumidor/a @@ -578,6 +580,10 @@ pt: categories: label: Categorias search: Procurar + table: + new_variant: Nova variante + edit_image: + close: Voltar product_import: title: Importação de Produtos file_not_found: O ficheiro não foi encontrado ou não pôde ser aberto @@ -617,6 +623,9 @@ pt: product_categories: Categorias de Produtos tax_categories: Categorias de Impostos shipping_categories: Categorias de Envio + dfc_import_form: + enterprise: "Organização" + import: "Importar" import: review: Rever import: Importar @@ -674,6 +683,7 @@ pt: view_products: Ir para a Página de Produtos view_inventory: Ir para a Página de Inventário product_headings: + distributor: Distribuidor producer: Produtor/a sku: SKU name: Nome @@ -951,6 +961,8 @@ pt: rate: Taxa customers: Consumidor active: Activo? + connected_apps: + loading: "A carregar" actions: edit_profile: Definições properties: Propriedades @@ -1475,6 +1487,8 @@ pt: invoice_tax_total: "Total IVA:" tax_invoice: "FACTURA FISCAL" tax_total: "Total de Impostos (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Levantamento" total_excl_tax: "Total (excl. imposto):" total_incl_tax: "Total (incl. imposto):" abn: "NIPC:" @@ -3447,6 +3461,10 @@ pt: email: "Email" total: "Total" billing_address_name: "Nome" + taxons: + form: + name: Nome + description: Descrição general_settings: edit: legal_settings: "Configurações Legais" diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index edd6fc288f..15b76c6656 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -270,7 +270,6 @@ pt_BR: cancel_order: "Cancelar pedido" confirm_send_invoice: "Uma fatura desse pedido será enviada ao cliente. Tem certeza que deseja continuar?" confirm_resend_order_confirmation: "Tem certeza que deseja enviar novamente o pedido de confirmação do e-mail?" - must_have_valid_business_number: "%{enterprise_name} deve ter um CNPJ válido antes que as faturas possam ser enviadas." invoice: "Fatura" active: "Ativo" cancelled: "Cancelado" @@ -403,6 +402,9 @@ pt_BR: actions: edit: Editar clone: Cópia + delete: Deletar + image: + edit: Editar adjustments: skipped_changing_canceled_order: "Não é possível modificar um pedido cancelado" begins_at: Começa em @@ -621,6 +623,8 @@ pt_BR: producers: label: Produtores search: Buscar + edit_image: + close: Voltar product_import: title: Importação de produtos file_not_found: O arquivo não foi encontrado ou não pôde ser aberto @@ -660,6 +664,9 @@ pt_BR: product_categories: Categorias de Produtos tax_categories: Categorias fiscais shipping_categories: Categorias de Remessa + dfc_import_form: + enterprise: "Iniciativas" + import: "Importar" import: review: Reveja import: Importar @@ -717,6 +724,7 @@ pt_BR: view_products: Ir para a página de produtos view_inventory: Ir para a página de inventário product_headings: + distributor: Distribuidor producer: Produtor sku: SKU name: Nome @@ -992,6 +1000,8 @@ pt_BR: vouchers: customers: Consumidor active: Ativo? + connected_apps: + loading: "Carregando" actions: edit_profile: Configurações properties: Propriedades @@ -1551,6 +1561,8 @@ pt_BR: invoice_tax_total: "Total de Imposto sobre Bens e Serviços" tax_invoice: "FATURA" tax_total: "Total de taxas (%{rate}):" + invoice_shipping_category_delivery: "Entrega" + invoice_shipping_category_pickup: "Retirada" total_excl_tax: "Total (sem imposto):" total_incl_tax: "Total (com imposto):" abn: "CNPJ:" @@ -3234,8 +3246,6 @@ pt_BR: start_date: "Data de início" successfully_removed: "Removido com Sucesso" taxonomy_edit: "Editar Taxonomia" - taxonomy_tree_error: "Erro na árvore taxonômica" - taxonomy_tree_instruction: "Guia para a árvore taxonômica" tree: "Árvore" updating: "Atualizando" your_order_is_empty_add_product: "Seu pedido está vazio, pesquise e adicione um produto acima" @@ -3781,6 +3791,11 @@ pt_BR: email: "E-mail" total: "Total" billing_address_name: "Nome" + taxons: + form: + name: Nome + permalink: Permalink + description: Descrição general_settings: edit: legal_settings: "Configurações de Legislação" diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 03516a796a..f94ca2223a 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -378,7 +378,6 @@ ru: cancel_order: "Отменить Заказ" confirm_send_invoice: "Счет на этот заказ будет отправлен клиенту. Вы уверены что хотите продолжить?" confirm_resend_order_confirmation: "Вы уверены, что хотите повторно отправить письмо с подтверждением заказа?" - must_have_valid_business_number: " %{enterprise_name} должен быть указан ИНН перед отправкой счетов." invoice: "Счёт" invoices: "Счета" file: "Файл" @@ -521,6 +520,9 @@ ru: actions: edit: Изменить clone: Клонировать + delete: Удалить + image: + edit: Изменить adjustments: skipped_changing_canceled_order: "Вы не можете изменить отмененный заказ." begins_at: Начинается С @@ -750,6 +752,10 @@ ru: no_products: no_products_found: Товары не найдены import_products: Импорт нескольких товаров + table: + new_variant: Новый вариант + edit_image: + close: Назад product_import: title: Импорт Товара file_not_found: Файл не найден или не может быть открыт @@ -791,6 +797,9 @@ ru: product_categories: Категории Товаров tax_categories: Налоговые Категории shipping_categories: Категории Доставки + dfc_import_form: + enterprise: "Предприятие" + import: "Импорт" import: review: Просмотр import: Импорт @@ -848,6 +857,7 @@ ru: view_products: Перейти на Страницу Товаров view_inventory: Перейти на Страницу Элементов товарной номенклатуры product_headings: + distributor: Дистрибьютор producer: Производитель sku: SKU name: Название @@ -1178,6 +1188,8 @@ ru: create_custom_tab: "Создать пользовательскую вкладку на витрине магазина" custom_tab_title: "Название пользовательской вкладки" custom_tab_content: "Контент для пользовательской вкладки" + connected_apps: + loading: "Загружается" actions: edit_profile: Настройки properties: Свойства @@ -1546,7 +1558,6 @@ ru: index: title: "Настройки OIDC" connect: "Подключить свою учетную запись" - already_connected: "Ваша учетная запись уже связана с этой учетной записью DFC:" les_communs_link: "Les Communs Open ID сервер" link_your_account: "Сначала вам необходимо связать свою учетную запись с поставщиком авторизации, используемым DFC (Les Communs Open ID Connect)." link_account_button: "Свяжите свою учетную запись Les Communs OIDC" @@ -1868,6 +1879,8 @@ ru: invoice_tax_total: "Всего налогов:" tax_invoice: "СЧЁТ" tax_total: "Всего налога (%{rate}):" + invoice_shipping_category_delivery: "Доставка" + invoice_shipping_category_pickup: "Самовывоз" total_excl_tax: "Всего (Искл. налог):" total_incl_tax: "Всего (Вкл. налог):" abn: "ИНН" @@ -3659,8 +3672,6 @@ ru: start_date: "Дата начала" successfully_removed: "Успешно удалено" taxonomy_edit: "Изменить таксономию" - taxonomy_tree_error: "Ошибка дерева таксономии" - taxonomy_tree_instruction: "Инструкция по дереву таксономии" tree: "Дерево" updating: "Обновление" your_order_is_empty_add_product: "Ваша корзина пуста, пожалуйста, найдите и добавьте товар выше" @@ -4238,6 +4249,11 @@ ru: email: "Email" total: "Всего" billing_address_name: "Название" + taxons: + form: + name: Название + permalink: Постоянная ссылка + description: Описание general_settings: edit: legal_settings: "Правовые настройки" diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 72468ce05d..a8992032d1 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -7,6 +7,8 @@ sv: attributes: spree/order/ship_address: phone: "Telefonnummer" + firstname: "Förnamn" + lastname: "Efternamn" spree/user: password: "Lösenord" enterprise_fee: @@ -22,6 +24,9 @@ sv: price: "Pris" supplier: "Leverantör" variant_unit: "Enhet för variant" + spree/credit_card: + month: "Månad" + year: "År" variant_override: count_on_hand: "På lager" errors: @@ -78,12 +83,11 @@ sv: cancel_order: "Ångra beställning" confirm_send_invoice: "En faktura på denna beställning kommer att skickas till kunden. Är du säker på att du vill fortsätta? " confirm_resend_order_confirmation: "Är du säker på att du vill skicka beställningsbekräftelsen en gång till? " - must_have_valid_business_number: "%{enterprise_name} måste ha ett giltigt organisationsnummer innan fakturor kan skickas." invoice: "Faktura" cancelled: "Avbokad" say_no: "Nej" say_yes: "Ja" - bill_address: Fakturerirngsadress + bill_address: Faktureringsadress ship_address: Leveransadress sort_order_cycles_on_shopfront_by: "Sortera butikens beställningsomgångar efter" required_fields: Obligatoriska fält är märkta med en asterisk @@ -172,6 +176,8 @@ sv: inherits_properties: "Ärva egenskaper?" actions: edit: Redigera + image: + edit: Redigera customer: Kund date: Datum email: epost @@ -319,6 +325,8 @@ sv: producers: label: 'Producenter ' search: Sök + edit_image: + close: Backa product_import: file_not_found: Filen hittades inte eller kunde inte öppnas no_data: Ingen data hittades i kalkylbladet @@ -327,7 +335,10 @@ sv: could_not_process: "kunde inte hantera filen: ogiltig filtyp" blank: kan inte vara blank none_saved: sparade inte några produkter + dfc_import_form: + enterprise: "Företag" product_headings: + distributor: Distributör producer: Producent sku: SKU name: Namn @@ -834,6 +845,12 @@ sv: label: Land shipping_info: title: 'Leveransadress ' + step2: + form: + card_month: + label: Månad + card_year: + label: År step3: delivery_details: title: Leveransdetaljer @@ -896,6 +913,8 @@ sv: invoice_tax_total: "Summa moms" tax_invoice: "SKATTEFAKTURA" tax_total: "Summa skatter (%{rate}):" + invoice_shipping_category_delivery: "Leverans" + invoice_shipping_category_pickup: "Upphämtning" total_excl_tax: "Summa (Exkl skatt):" total_incl_tax: "Summa (Inkl skatt):" abn: "ABN:" @@ -2129,6 +2148,8 @@ sv: account: "Konto" billing_address: "Fakturerirngsadress" shipping_address: "Leveransadress" + first_name: "Förnamn" + last_name: "Efternamn" city: "Ort" country: "Land" state: "Region" @@ -2324,6 +2345,10 @@ sv: email: "E-post" total: "Summa" billing_address_name: "Namn" + taxons: + form: + name: Namn + description: Beskrivning general_settings: edit: enterprises_require_tos: "Företag måste acceptera servicevillkoren" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 655a828874..ce3b8ba33e 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -247,7 +247,6 @@ tr: cancel_order: "Siparişi İptal Et" confirm_send_invoice: "Bu siparişin faturası müşteriye gönderilecek. Devam etmek istiyor musunuz?" confirm_resend_order_confirmation: "Sipariş onayı e-postasını tekrar göndermek istediğinizden emin misiniz?" - must_have_valid_business_number: "Faturaların gönderilebilmesi için %{enterprise_name} adına geçerli bir VKN/TCKN olmalı." invoice: "Fatura" active: "AKTİF" cancelled: "İptal edildi" @@ -383,6 +382,9 @@ tr: actions: edit: Düzenle clone: Kopyala + delete: Sil + image: + edit: Düzenle adjustments: skipped_changing_canceled_order: "İptal edilmiş bir siparişi değiştiremezsiniz." begins_at: Başlangıç @@ -603,6 +605,10 @@ tr: categories: label: Kategoriler search: Ara + table: + new_variant: Yeni Çeşit + edit_image: + close: Geri product_import: title: Ürün Aktarımı file_not_found: Dosya bulunamadı veya açılamadı @@ -642,6 +648,9 @@ tr: product_categories: Ürün Kategorileri tax_categories: Vergi Kategorileri shipping_categories: Teslimat Kategorileri + dfc_import_form: + enterprise: "İşletme" + import: "Aktar" import: review: İncele import: İçe Aktar @@ -699,6 +708,7 @@ tr: view_products: Ürünler Sayfasına Git view_inventory: Stok Sayfasına Git product_headings: + distributor: Dağıtımcı producer: ÜRETİCİ sku: Stok Kodu name: Ad @@ -979,6 +989,8 @@ tr: rate: oran customers: Müşteri active: AKTİF? + connected_apps: + loading: "Yükleniyor" actions: edit_profile: Ayarlar properties: ÖZELLİKLER @@ -1559,6 +1571,8 @@ tr: invoice_tax_total: "Toplam KDV:" tax_invoice: "VERGİ FATURASI" tax_total: "Toplam vergi (%{rate}):" + invoice_shipping_category_delivery: "Eve Teslim" + invoice_shipping_category_pickup: "Teslimat Noktası" total_excl_tax: "Toplam (vergi hariç):" total_incl_tax: "Toplam (Vergi dahil):" abn: "VKN/TCKN:" @@ -3230,8 +3244,6 @@ tr: start_date: "Başlangıç tarihi" successfully_removed: "Başarıyla Kaldırıldı" taxonomy_edit: "Kategori düzenleme" - taxonomy_tree_error: "Kategori ağacı hatası" - taxonomy_tree_instruction: "Kategori ağacı talimatı" tree: "Ağaç" updating: "Güncelleniyor" your_order_is_empty_add_product: "Sepetiniz boş, lütfen bir ürün arayın ve ekleyin" @@ -3782,6 +3794,11 @@ tr: email: "E-posta" total: "Toplam" billing_address_name: "Ad" + taxons: + form: + name: Ad + permalink: Permalink + description: Açıklama general_settings: edit: legal_settings: "Yasal Ayarlar" diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 18656e5710..85e80e55dd 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -329,7 +329,6 @@ uk: cancel_order: "Відмінити замовлення" confirm_send_invoice: "Рахунок-фактура на це замовлення буде надіслано клієнту. Ви впевнені, що хочете продовжити?" confirm_resend_order_confirmation: "Ви впевнені, що хочете повторно надіслати електронний лист із підтвердженням замовлення?" - must_have_valid_business_number: "%{enterprise_name} має мати дійсний номер ABN, перш ніж можна буде надсилати рахунки-фактури." invoice: "Рахунок-фактура" active: "Активний" cancelled: "Скасовано" @@ -465,6 +464,9 @@ uk: actions: edit: Редагувати clone: Клонувати + delete: Видалити + image: + edit: Редагувати adjustments: skipped_changing_canceled_order: "Ви не можете змінити скасоване замовлення." begins_at: Починається о @@ -688,6 +690,10 @@ uk: categories: label: Категорії search: Пошук + table: + new_variant: Новий варіант + edit_image: + close: Назад product_import: title: Імпорт продукту file_not_found: Файл не знайдено або не вдалося відкрити @@ -727,6 +733,9 @@ uk: product_categories: Категорії продуктів tax_categories: Податкові категорії shipping_categories: Категорії доставки + dfc_import_form: + enterprise: "Підприємство" + import: "Імпорт" import: review: Огляд import: Імпорт @@ -784,6 +793,7 @@ uk: view_products: Перейдіть на сторінку продуктів view_inventory: Перейдіть на сторінку інвентаризації product_headings: + distributor: Дистрибютор producer: Виробник sku: Артикул name: Ім'я @@ -1092,6 +1102,8 @@ uk: rate: Оцінка customers: Замовник active: Активний? + connected_apps: + loading: "Завантаження" actions: edit_profile: Налаштування properties: Властивості @@ -1712,6 +1724,8 @@ uk: invoice_tax_total: "Всього ПДВ:" tax_invoice: "ПОДАТКОВА НАКЛАДНА" tax_total: "Загальний податок (%{rate}):" + invoice_shipping_category_delivery: "Доставка" + invoice_shipping_category_pickup: "Самовивіз" total_excl_tax: "Усього (без податку):" total_incl_tax: "Всього (вкл. податок):" abn: "ABN:" @@ -3456,8 +3470,6 @@ uk: start_date: "Дата початку" successfully_removed: "Успішно видалено" taxonomy_edit: "Редагування таксономії" - taxonomy_tree_error: "Помилка дерева таксономії" - taxonomy_tree_instruction: "Інструкція з таксономічного дерева" tree: "Дерево" updating: "Оновлення" your_order_is_empty_add_product: "Ваше замовлення порожнє, знайдіть і додайте продукт вище" @@ -4018,6 +4030,11 @@ uk: email: "Email" total: "Всього" billing_address_name: "Ім'я" + taxons: + form: + name: Ім'я + permalink: Постійне посилання + description: Опис general_settings: edit: legal_settings: "Юридичні налаштування" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index b8fef0838c..8d0a9f6705 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -67,6 +67,8 @@ Openfoodnetwork::Application.routes.draw do post '/product_import/save_data', to: 'product_import#save_data', as: 'product_import_save_async' post '/product_import/reset_absent', to: 'product_import#reset_absent_products', as: 'product_import_reset_async' + resources :dfc_product_imports, only: [:index] + constraints FeatureToggleConstraint.new(:admin_style_v3) do resources :products, to: 'products_v3#index', only: :index do patch :bulk_update, on: :collection @@ -113,7 +115,7 @@ Openfoodnetwork::Application.routes.draw do put :unpause, on: :member end - resources :oidc_settings, only: :index + resources :oidc_settings, only: [:index, :destroy] resources :subscription_line_items, only: [], format: :json do post :build, on: :collection diff --git a/config/routes/spree.rb b/config/routes/spree.rb index 2568731c9a..f59e4e7dca 100644 --- a/config/routes/spree.rb +++ b/config/routes/spree.rb @@ -50,7 +50,6 @@ Spree::Core::Engine.routes.draw do resources :users - constraints FeatureToggleConstraint.new(:admin_style_v3, negate: true) do # Show old bulk products screen resources :products, :index do @@ -111,7 +110,7 @@ Spree::Core::Engine.routes.draw do resources :adjustments resources :invoices, only: [:index] resource :invoices, only: [] do - post :generate, to: :generate + post :generate end resources :payments do diff --git a/db/migrate/20231002000136871_add_foreign_key_to_spree_stock_items_spree_stock_locations.rb b/db/bad_migrations/20231002000136871_add_foreign_key_to_spree_stock_items_spree_stock_locations.rb similarity index 100% rename from db/migrate/20231002000136871_add_foreign_key_to_spree_stock_items_spree_stock_locations.rb rename to db/bad_migrations/20231002000136871_add_foreign_key_to_spree_stock_items_spree_stock_locations.rb diff --git a/db/migrate/20231002000136872_add_foreign_key_to_spree_stock_items_spree_variants.rb b/db/bad_migrations/20231002000136872_add_foreign_key_to_spree_stock_items_spree_variants.rb similarity index 100% rename from db/migrate/20231002000136872_add_foreign_key_to_spree_stock_items_spree_variants.rb rename to db/bad_migrations/20231002000136872_add_foreign_key_to_spree_stock_items_spree_variants.rb diff --git a/db/migrate/20231002000136876_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_methods.rb b/db/bad_migrations/20231002000136876_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_methods.rb similarity index 100% rename from db/migrate/20231002000136876_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_methods.rb rename to db/bad_migrations/20231002000136876_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_methods.rb diff --git a/db/migrate/20231002000136877_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_categories.rb b/db/bad_migrations/20231002000136877_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_categories.rb similarity index 100% rename from db/migrate/20231002000136877_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_categories.rb rename to db/bad_migrations/20231002000136877_add_foreign_key_to_spree_shipping_method_categories_spree_shipping_categories.rb diff --git a/db/migrate/20231002000136879_add_foreign_key_to_report_rendering_options_spree_users.rb b/db/bad_migrations/20231002000136879_add_foreign_key_to_report_rendering_options_spree_users.rb similarity index 100% rename from db/migrate/20231002000136879_add_foreign_key_to_report_rendering_options_spree_users.rb rename to db/bad_migrations/20231002000136879_add_foreign_key_to_report_rendering_options_spree_users.rb diff --git a/db/migrate/20231002000136926_add_foreign_key_to_tag_rules_enterprises.rb b/db/bad_migrations/20231002000136926_add_foreign_key_to_tag_rules_enterprises.rb similarity index 100% rename from db/migrate/20231002000136926_add_foreign_key_to_tag_rules_enterprises.rb rename to db/bad_migrations/20231002000136926_add_foreign_key_to_tag_rules_enterprises.rb diff --git a/db/migrate/20231002000136952_add_foreign_key_to_spree_stock_movements_spree_stock_items.rb b/db/bad_migrations/20231002000136952_add_foreign_key_to_spree_stock_movements_spree_stock_items.rb similarity index 100% rename from db/migrate/20231002000136952_add_foreign_key_to_spree_stock_movements_spree_stock_items.rb rename to db/bad_migrations/20231002000136952_add_foreign_key_to_spree_stock_movements_spree_stock_items.rb diff --git a/db/migrate/20231002000136955_add_foreign_key_to_spree_stock_locations_spree_states.rb b/db/bad_migrations/20231002000136955_add_foreign_key_to_spree_stock_locations_spree_states.rb similarity index 100% rename from db/migrate/20231002000136955_add_foreign_key_to_spree_stock_locations_spree_states.rb rename to db/bad_migrations/20231002000136955_add_foreign_key_to_spree_stock_locations_spree_states.rb diff --git a/db/migrate/20231002000136959_add_foreign_key_to_spree_stock_locations_spree_countries.rb b/db/bad_migrations/20231002000136959_add_foreign_key_to_spree_stock_locations_spree_countries.rb similarity index 100% rename from db/migrate/20231002000136959_add_foreign_key_to_spree_stock_locations_spree_countries.rb rename to db/bad_migrations/20231002000136959_add_foreign_key_to_spree_stock_locations_spree_countries.rb diff --git a/db/migrate/20231002000136976_add_foreign_key_to_spree_shipments_spree_stock_locations.rb b/db/bad_migrations/20231002000136976_add_foreign_key_to_spree_shipments_spree_stock_locations.rb similarity index 100% rename from db/migrate/20231002000136976_add_foreign_key_to_spree_shipments_spree_stock_locations.rb rename to db/bad_migrations/20231002000136976_add_foreign_key_to_spree_shipments_spree_stock_locations.rb diff --git a/db/migrate/20231002000137115_add_foreign_key_to_inventory_items_enterprises.rb b/db/bad_migrations/20231002000137115_add_foreign_key_to_inventory_items_enterprises.rb similarity index 100% rename from db/migrate/20231002000137115_add_foreign_key_to_inventory_items_enterprises.rb rename to db/bad_migrations/20231002000137115_add_foreign_key_to_inventory_items_enterprises.rb diff --git a/db/migrate/20231002000137116_add_foreign_key_to_inventory_items_spree_variants.rb b/db/bad_migrations/20231002000137116_add_foreign_key_to_inventory_items_spree_variants.rb similarity index 100% rename from db/migrate/20231002000137116_add_foreign_key_to_inventory_items_spree_variants.rb rename to db/bad_migrations/20231002000137116_add_foreign_key_to_inventory_items_spree_variants.rb diff --git a/db/migrate/20231003000823494_add_foreign_key_to_spree_orders_spree_users_created_by_id.rb b/db/bad_migrations/20231003000823494_add_foreign_key_to_spree_orders_spree_users_created_by_id.rb similarity index 100% rename from db/migrate/20231003000823494_add_foreign_key_to_spree_orders_spree_users_created_by_id.rb rename to db/bad_migrations/20231003000823494_add_foreign_key_to_spree_orders_spree_users_created_by_id.rb diff --git a/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb new file mode 100644 index 0000000000..22ae971b70 --- /dev/null +++ b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb @@ -0,0 +1,5 @@ +class AddDfcNameToSpreeTaxons < ActiveRecord::Migration[7.0] + def change + add_column :spree_taxons, :dfc_id, :string + end +end diff --git a/db/migrate/20231103061213_add_terms_of_service_accepted_at_to_spree_users.rb b/db/migrate/20231103061213_add_terms_of_service_accepted_at_to_spree_users.rb new file mode 100644 index 0000000000..a65835075b --- /dev/null +++ b/db/migrate/20231103061213_add_terms_of_service_accepted_at_to_spree_users.rb @@ -0,0 +1,16 @@ +class AddTermsOfServiceAcceptedAtToSpreeUsers < ActiveRecord::Migration[7.0] + def up + add_column :spree_users, :terms_of_service_accepted_at, :datetime + + if Spree::Config.enterprises_require_tos == true + # There isn't really a way to know which user have access to admin pages, so we update + # everyone. It's technically wrong to say shoppers have accepted ToS, but they will be + # required to accept the terms if they sign up for an enterprise. + Spree::User.update_all(terms_of_service_accepted_at: Time.zone.now) + end + end + + def down + remove_column :spree_users, :terms_of_service_accepted_at + end +end diff --git a/db/migrate/20231213054115_create_connected_apps.rb b/db/migrate/20231213054115_create_connected_apps.rb new file mode 100644 index 0000000000..73fcdbf9d6 --- /dev/null +++ b/db/migrate/20231213054115_create_connected_apps.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateConnectedApps < ActiveRecord::Migration[7.0] + def change + create_table :connected_apps do |t| + t.belongs_to :enterprise, foreign_key: true + t.json :data + + t.timestamps + end + end +end diff --git a/db/migrate/20240105043228_create_semantic_links.rb b/db/migrate/20240105043228_create_semantic_links.rb new file mode 100644 index 0000000000..89ac707783 --- /dev/null +++ b/db/migrate/20240105043228_create_semantic_links.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateSemanticLinks < ActiveRecord::Migration[7.0] + def change + create_table :semantic_links do |t| + t.references :variant, null: false, foreign_key: { to_table: :spree_variants } + t.string :semantic_id, null: false + + t.timestamps + end + end +end diff --git a/db/migrate/20240115022359_rename_offending_migrations.rb b/db/migrate/20240115022359_rename_offending_migrations.rb new file mode 100644 index 0000000000..21d9c73327 --- /dev/null +++ b/db/migrate/20240115022359_rename_offending_migrations.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class RenameOffendingMigrations < ActiveRecord::Migration[7.0] + MIGRATION_IDS = { + '20231002000136871': "20231002000136", + '20231002000136872': "20231002000137", + '20231002000136876': "20231002000138", + '20231002000136877': "20231002000139", + '20231002000136879': "20231002000140", + '20231002000136926': "20231002000141", + '20231002000136952': "20231002000142", + '20231002000136955': "20231002000143", + '20231002000136959': "20231002000144", + '20231002000136976': "20231002000145", + '20231002000137115': "20231002000146", + '20231002000137116': "20231002000147", + '20231003000823494': "20231003000823", + }.freeze + + def up + MIGRATION_IDS.each do |bad_id, good_id| + execute <<~SQL.squish + UPDATE schema_migrations + SET version='#{good_id}' + WHERE version='#{bad_id}' + SQL + end + end + + def down + MIGRATION_IDS.each do |bad_id, good_id| + execute <<~SQL.squish + UPDATE schema_migrations + SET version='#{bad_id}' + WHERE version='#{good_id}' + SQL + end + end +end diff --git a/db/migrate/20240213042618_create_oidc_accounts.rb b/db/migrate/20240213042618_create_oidc_accounts.rb new file mode 100644 index 0000000000..7a77e8b716 --- /dev/null +++ b/db/migrate/20240213042618_create_oidc_accounts.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateOidcAccounts < ActiveRecord::Migration[7.0] + def change + create_table :oidc_accounts do |t| + # We may allow multiple OIDC accounts per user in the future but for now + # we assume only one and therefore make this unique. + t.belongs_to :user, null: false, foreign_key: { to_table: :spree_users }, + index: { unique: true } + t.string :provider + t.string :uid, null: false, index: { unique: true } + t.string :token + t.string :refresh_token + + t.timestamps + end + end +end diff --git a/db/migrate/20240213044159_copy_oidc_data_to_oidc_accounts.rb b/db/migrate/20240213044159_copy_oidc_data_to_oidc_accounts.rb new file mode 100644 index 0000000000..72b1d88d14 --- /dev/null +++ b/db/migrate/20240213044159_copy_oidc_data_to_oidc_accounts.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CopyOidcDataToOidcAccounts < ActiveRecord::Migration[7.0] + def up + execute <<~SQL.squish + INSERT INTO oidc_accounts (user_id, provider, uid, created_at, updated_at) + SELECT id, provider, uid, updated_at, updated_at + FROM spree_users WHERE provider IS NOT NULL + SQL + end + + def down + execute "DELETE FROM oidc_accounts" + end +end diff --git a/db/schema.rb b/db/schema.rb index 7e782b8747..5c9ef39bf5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 20231003000823494) do +ActiveRecord::Schema[7.0].define(version: 2024_02_13_044159) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql" @@ -63,6 +63,14 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do t.index ["user_id", "action_name", "column_name"], name: "index_column_prefs_on_user_id_and_action_name_and_column_name", unique: true end + create_table "connected_apps", force: :cascade do |t| + t.bigint "enterprise_id" + t.json "data" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["enterprise_id"], name: "index_connected_apps_on_enterprise_id" + end + create_table "coordinator_fees", id: :serial, force: :cascade do |t| t.integer "order_cycle_id", null: false t.integer "enterprise_fee_id", null: false @@ -300,6 +308,18 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do t.index ["order_id"], name: "index_invoices_on_order_id" end + create_table "oidc_accounts", force: :cascade do |t| + t.bigint "user_id", null: false + t.string "provider" + t.string "uid", null: false + t.string "token" + t.string "refresh_token" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["uid"], name: "index_oidc_accounts_on_uid", unique: true + t.index ["user_id"], name: "index_oidc_accounts_on_user_id", unique: true + end + create_table "order_cycle_schedules", id: :serial, force: :cascade do |t| t.integer "order_cycle_id", null: false t.integer "schedule_id", null: false @@ -380,6 +400,14 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do t.datetime "updated_at", precision: nil, null: false end + create_table "semantic_links", force: :cascade do |t| + t.bigint "variant_id", null: false + t.string "semantic_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["variant_id"], name: "index_semantic_links_on_variant_id" + end + create_table "sessions", id: :serial, force: :cascade do |t| t.string "session_id", limit: 255, null: false t.text "data" @@ -873,6 +901,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do t.string "meta_title", limit: 255 t.string "meta_description", limit: 255 t.string "meta_keywords", limit: 255 + t.string "dfc_id" t.index ["parent_id"], name: "index_taxons_on_parent_id" t.index ["permalink"], name: "index_taxons_on_permalink" t.index ["taxonomy_id"], name: "index_taxons_on_taxonomy_id" @@ -924,6 +953,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do t.boolean "show_api_key_view", default: false, null: false t.string "provider" t.string "uid" + t.datetime "terms_of_service_accepted_at" t.index ["confirmation_token"], name: "index_spree_users_on_confirmation_token", unique: true t.index ["email"], name: "email_idx_unique", unique: true t.index ["persistence_token"], name: "index_users_on_persistence_token" @@ -1101,6 +1131,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk" add_foreign_key "adjustment_metadata", "spree_adjustments", column: "adjustment_id", name: "adjustment_metadata_adjustment_id_fk", on_delete: :cascade + add_foreign_key "connected_apps", "enterprises" add_foreign_key "coordinator_fees", "enterprise_fees", name: "coordinator_fees_enterprise_fee_id_fk" add_foreign_key "coordinator_fees", "order_cycles", name: "coordinator_fees_order_cycle_id_fk" add_foreign_key "custom_tabs", "enterprises", on_delete: :cascade @@ -1135,6 +1166,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do add_foreign_key "inventory_items", "enterprises" add_foreign_key "inventory_items", "spree_variants", column: "variant_id" add_foreign_key "invoices", "spree_orders", column: "order_id" + add_foreign_key "oidc_accounts", "spree_users", column: "user_id" add_foreign_key "order_cycle_schedules", "order_cycles", name: "oc_schedules_order_cycle_id_fk" add_foreign_key "order_cycle_schedules", "schedules", name: "oc_schedules_schedule_id_fk" add_foreign_key "order_cycles", "enterprises", column: "coordinator_id", name: "order_cycles_coordinator_id_fk" @@ -1144,6 +1176,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do add_foreign_key "proxy_orders", "spree_orders", column: "order_id", name: "order_id_fk" add_foreign_key "proxy_orders", "subscriptions", name: "proxy_orders_subscription_id_fk" add_foreign_key "report_rendering_options", "spree_users", column: "user_id" + add_foreign_key "semantic_links", "spree_variants", column: "variant_id" add_foreign_key "spree_addresses", "spree_countries", column: "country_id", name: "spree_addresses_country_id_fk" add_foreign_key "spree_addresses", "spree_states", column: "state_id", name: "spree_addresses_state_id_fk" add_foreign_key "spree_inventory_units", "spree_orders", column: "order_id", name: "spree_inventory_units_order_id_fk", on_delete: :cascade diff --git a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb index 3db7525d58..16ebe0411e 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb @@ -8,6 +8,7 @@ module DfcProvider protect_from_forgery with: :null_session rescue_from ActiveRecord::RecordNotFound, with: :not_found + rescue_from CanCan::AccessDenied, with: :unauthorized before_action :check_authorization @@ -16,7 +17,7 @@ module DfcProvider private def check_authorization - head :unauthorized if current_user.nil? + unauthorized if current_user.nil? end def check_enterprise @@ -50,5 +51,17 @@ module DfcProvider def not_found head :not_found end + + def unauthorized + head :unauthorized + end + + def current_ability + @current_ability ||= Spree::Ability.new(current_user) + end + + def import + DfcIo.import(request.body) + end end end diff --git a/engines/dfc_provider/app/controllers/dfc_provider/enterprise_groups/affiliated_by_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/enterprise_groups/affiliated_by_controller.rb new file mode 100644 index 0000000000..acb5ad6898 --- /dev/null +++ b/engines/dfc_provider/app/controllers/dfc_provider/enterprise_groups/affiliated_by_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module DfcProvider + module EnterpriseGroups + class AffiliatedByController < DfcProvider::ApplicationController + def create + group = EnterpriseGroup.find(params[:enterprise_group_id]) + + authorize! :update, group + + enterprise_uri = RDF::URI.new(params[:@id]) + + return head :bad_request unless enterprise_uri.valid? + + enterprise_id = ofn_id_from_uri(enterprise_uri) + enterprise = Enterprise.find(enterprise_id) + + group.enterprises << enterprise + + head :created + end + + def destroy + group = EnterpriseGroup.find(params[:enterprise_group_id]) + + authorize! :update, group + + group.enterprises.delete(params[:id]) + end + + private + + def ofn_id_from_uri(uri) + # enterprise uri follow this format http://test.host/api/dfc/enterprises/{ofn_enterprise_id} + uri.path.split("/").last + end + end + end +end diff --git a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb new file mode 100644 index 0000000000..437897c524 --- /dev/null +++ b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module DfcProvider + class OffersController < DfcProvider::ApplicationController + before_action :check_enterprise + + def show + subject = OfferBuilder.build(variant) + render json: DfcIo.export(subject) + end + + def update + offer = import + + return head :bad_request unless offer + + OfferBuilder.apply(offer, variant) + + variant.save! + end + + private + + def variant + @variant ||= current_enterprise.supplied_variants.find(params[:id]) + end + end +end diff --git a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb index 21b4c4b59d..57d25c1929 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb @@ -14,17 +14,14 @@ module DfcProvider return head :bad_request unless supplied_product - variant = SuppliedProductBuilder.import_variant(supplied_product) + variant = SuppliedProductBuilder.import_variant( + supplied_product, + current_enterprise, + ) product = variant.product - if product.new_record? - product.supplier = current_enterprise - product.save! - end - - if variant.new_record? - variant.save! - end + product.save! if product.new_record? + variant.save! if variant.new_record? supplied_product = SuppliedProductBuilder.supplied_product(variant) render json: DfcIo.export(supplied_product) @@ -48,10 +45,6 @@ module DfcProvider private - def import - DfcIo.import(request.body) - end - def variant @variant ||= current_enterprise.supplied_variants.find(params[:id]) end diff --git a/engines/dfc_provider/app/services/address_builder.rb b/engines/dfc_provider/app/services/address_builder.rb index 7b89d2a073..1c7b3618d3 100644 --- a/engines/dfc_provider/app/services/address_builder.rb +++ b/engines/dfc_provider/app/services/address_builder.rb @@ -2,12 +2,13 @@ class AddressBuilder < DfcBuilder def self.address(address) - DataFoodConsortium::Connector::Address.new( + DfcProvider::Address.new( urls.address_url(address), street: address.address1, postalCode: address.zipcode, city: address.city, - country: address.country.name + country: address.country.name, + region: address.state.name ) end end diff --git a/engines/dfc_provider/app/services/authorization_control.rb b/engines/dfc_provider/app/services/authorization_control.rb index e2643b43fd..254c6cee67 100644 --- a/engines/dfc_provider/app/services/authorization_control.rb +++ b/engines/dfc_provider/app/services/authorization_control.rb @@ -57,6 +57,6 @@ class AuthorizationControl def find_ofn_user(payload) return if payload["email"].blank? - Spree::User.find_by(uid: payload["email"]) + OidcAccount.find_by(uid: payload["email"])&.user end end diff --git a/engines/dfc_provider/app/services/dfc_builder.rb b/engines/dfc_provider/app/services/dfc_builder.rb index b3b3373172..5411d8ab28 100644 --- a/engines/dfc_provider/app/services/dfc_builder.rb +++ b/engines/dfc_provider/app/services/dfc_builder.rb @@ -12,22 +12,7 @@ class DfcBuilder id, product:, sku: variant.sku, stockLimitation: stock_limitation(variant), - offers: [offer(variant)], - ) - end - - def self.offer(variant) - # We don't have an endpoint for offers yet and this URL is only a - # placeholder for now. The offer is actually affected by order cycle and - # customer tags. We'll solve that at a later stage. - enterprise_url = urls.enterprise_url(id: variant.product.supplier_id) - id = "#{enterprise_url}/offers/#{variant.id}" - offered_to = [] - - DataFoodConsortium::Connector::Offer.new( - id, offeredTo: offered_to, - price: variant.price.to_f, - stockLimitation: stock_limitation(variant), + offers: [OfferBuilder.build(variant)], ) end @@ -37,12 +22,6 @@ class DfcBuilder variant.on_demand ? nil : variant.total_on_hand end - # OFN product categories (taxons) are currently not standardised. - # This is just a dummy value for demos. - def self.product_type - DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE - end - def self.urls DfcProvider::Engine.routes.url_helpers end diff --git a/engines/dfc_provider/app/services/dfc_product_type_factory.rb b/engines/dfc_provider/app/services/dfc_product_type_factory.rb new file mode 100644 index 0000000000..f41bfe2fe1 --- /dev/null +++ b/engines/dfc_provider/app/services/dfc_product_type_factory.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'singleton' + +class DfcProductTypeFactory + include Singleton + + def self.for(dfc_id) + instance.for(dfc_id) + end + + def initialize + @product_types = {} + + populate_product_types + end + + def for(dfc_id) + @product_types[dfc_id] + end + + private + + def populate_product_types + DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type| + record_type(DfcLoader.connector.PRODUCT_TYPES, product_type.to_s) + end + end + + def record_type(product_type_object, product_type) + current_product_type = product_type_object.public_send(product_type.to_s) + + id = current_product_type.semanticId + @product_types[id] = current_product_type + + # Narrower product types are defined as class method on the current product type object + narrowers = current_product_type.methods(false).sort + + # Leaf node + return if narrowers.empty? + + narrowers.each do |narrower| + # recursive call + record_type(current_product_type, narrower) + end + end +end diff --git a/engines/dfc_provider/app/services/dfc_request.rb b/engines/dfc_provider/app/services/dfc_request.rb new file mode 100644 index 0000000000..8de0108d99 --- /dev/null +++ b/engines/dfc_provider/app/services/dfc_request.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "private_address_check" +require "private_address_check/tcpsocket_ext" + +# Request a JSON document from a DFC API with authentication. +# +# All DFC API interactions are authenticated via OIDC tokens. If the user's +# access token is expired, we try to get a new one with the user's refresh +# token. +class DfcRequest + def initialize(user) + @user = user + end + + def get(url) + response = request(url) + + return response.body if response.status == 200 + + return "" if @user.oidc_account.updated_at > 15.minutes.ago + + refresh_access_token! + + response = request(url) + response.body + end + + private + + def request(url) + connection = Faraday.new( + request: { timeout: 30 }, + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => "Bearer #{@user.oidc_account.token}", + } + ) + + only_public_connections do + connection.get(url) + end + end + + def only_public_connections(&) + return yield if Rails.env.development? + + PrivateAddressCheck.only_public_connections(&) + end + + def refresh_access_token! + strategy = OmniAuth::Strategies::OpenIDConnect.new( + Rails.application, + Devise.omniauth_configs[:openid_connect].options + # Don't try to call `Devise.omniauth(:openid_connect)` first. + # It results in an empty config hash and we lose our config. + ) + client = strategy.client + client.token_endpoint = strategy.config.token_endpoint + client.refresh_token = @user.oidc_account.refresh_token + + token = client.access_token! + + @user.oidc_account.update!( + token: token.access_token, + refresh_token: token.refresh_token + ) + end +end diff --git a/engines/dfc_provider/app/services/enterprise_builder.rb b/engines/dfc_provider/app/services/enterprise_builder.rb index f5edfa4f5a..b2087c2601 100644 --- a/engines/dfc_provider/app/services/enterprise_builder.rb +++ b/engines/dfc_provider/app/services/enterprise_builder.rb @@ -1,7 +1,10 @@ # frozen_string_literal: true class EnterpriseBuilder < DfcBuilder - def self.enterprise(enterprise) + def self.enterprise(enterprise) # rubocop:disable Metrics/AbcSize + # The ABC size of this method should shrink when our custom attributes are + # in the DFC standard. + variants = enterprise.supplied_variants.to_a catalog_items = variants.map(&method(:catalog_item)) supplied_products = catalog_items.map(&:product) @@ -26,7 +29,8 @@ class EnterpriseBuilder < DfcBuilder # But that would require a new endpoint for a single string. add_ofn_property(e, "ofn:contact_name", enterprise.contact_name) - add_ofn_property(e, "ofn:logo_url", enterprise.logo.url) + add_ofn_property(e, "ofn:logo_url", enterprise.logo_url(:small)) + add_ofn_property(e, "ofn:promo_image_url", enterprise.promo_image_url(:large)) end end diff --git a/engines/dfc_provider/app/services/offer_builder.rb b/engines/dfc_provider/app/services/offer_builder.rb new file mode 100644 index 0000000000..b499c1d945 --- /dev/null +++ b/engines/dfc_provider/app/services/offer_builder.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class OfferBuilder < DfcBuilder + def self.build(variant) + id = urls.enterprise_offer_url( + enterprise_id: variant.product.supplier_id, + id: variant.id, + ) + + DataFoodConsortium::Connector::Offer.new( + id, + price: variant.price.to_f, + stockLimitation: stock_limitation(variant), + ) + end + + def self.apply(offer, variant) + variant.on_hand = offer.stockLimitation + variant.price = offer.price + end +end diff --git a/engines/dfc_provider/app/services/social_media_builder.rb b/engines/dfc_provider/app/services/social_media_builder.rb index bb13edc4f9..3c855b3d5b 100644 --- a/engines/dfc_provider/app/services/social_media_builder.rb +++ b/engines/dfc_provider/app/services/social_media_builder.rb @@ -12,10 +12,16 @@ class SocialMediaBuilder < DfcBuilder def self.social_media(enterprise, name) return nil unless name.in?(NAMES) - url = enterprise.attributes[name] + url = enterprise.public_send(name) return nil if url.blank? + if name == "instagram" + url = "https://www.instagram.com/#{url}/" + end + + url = "https://#{url}" unless url.starts_with?(%r{https?://}) + DataFoodConsortium::Connector::SocialMedia.new( urls.enterprise_social_media_url(enterprise.id, name), name:, url:, diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 23be177ee1..31c343b524 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -6,23 +6,27 @@ class SuppliedProductBuilder < DfcBuilder enterprise_id: variant.product.supplier_id, id: variant.id, ) + product_uri = urls.enterprise_url( + variant.product.supplier_id, + spree_product_id: variant.product_id + ) DfcProvider::SuppliedProduct.new( id, - name: variant.name_to_display, + name: variant.product_and_full_name, description: variant.description, - productType: product_type, + productType: product_type(variant), quantity: QuantitativeValueBuilder.quantity(variant), + spree_product_uri: product_uri, spree_product_id: variant.product.id, image_url: variant.product&.image&.url(:product) ) end - def self.import_variant(supplied_product) - product_id = supplied_product.spree_product_id + def self.import_variant(supplied_product, supplier) + product = referenced_spree_product(supplied_product, supplier) - if product_id.present? - product = Spree::Product.find(product_id) + if product Spree::Variant.new( product:, price: 0, @@ -31,8 +35,29 @@ class SuppliedProductBuilder < DfcBuilder end else product = import_product(supplied_product) + product.supplier = supplier product.ensure_standard_variant product.variants.first + end.tap do |variant| + link = supplied_product.semanticId + variant.semantic_links.new(semantic_id: link) if link.present? + end + end + + def self.referenced_spree_product(supplied_product, supplier) + uri = supplied_product.spree_product_uri + id = supplied_product.spree_product_id + + if uri.present? + route = Rails.application.routes.recognize_path(uri) + params = Rack::Utils.parse_nested_query(URI.parse(uri).query) + + # Check that the given URI points to us: + return unless uri == urls.enterprise_url(route.merge(params)) + + supplier.supplied_products.find_by(id: params["spree_product_id"]) + elsif id.present? + supplier.supplied_products.find_by(id:) end end @@ -41,7 +66,7 @@ class SuppliedProductBuilder < DfcBuilder name: supplied_product.name, description: supplied_product.description, price: 0, # will be in DFC Offer - primary_taxon: Spree::Taxon.first, # dummy value until we have a mapping + primary_taxon: taxon(supplied_product) ).tap do |product| QuantitativeValueBuilder.apply(supplied_product.quantity, product) end @@ -49,11 +74,28 @@ class SuppliedProductBuilder < DfcBuilder def self.apply(supplied_product, variant) variant.product.assign_attributes( - name: supplied_product.name, description: supplied_product.description, + primary_taxon: taxon(supplied_product) ) + variant.display_name = supplied_product.name QuantitativeValueBuilder.apply(supplied_product.quantity, variant.product) variant.unit_value = variant.product.unit_value end + + def self.product_type(variant) + taxon_dfc_id = variant.product.primary_taxon&.dfc_id + + DfcProductTypeFactory.for(taxon_dfc_id) + end + + def self.taxon(supplied_product) + dfc_id = supplied_product.productType&.semanticId + + # Every product needs a primary taxon to be valid. So if we don't have + # one or can't find it we just take a random one. + Spree::Taxon.find_by(dfc_id:) || Spree::Taxon.first + end + + private_class_method :product_type, :taxon end diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index 7390cc39a4..5bde8f23ba 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -4,9 +4,12 @@ DfcProvider::Engine.routes.draw do resources :addresses, only: [:show] resources :enterprises, only: [:show] do resources :catalog_items, only: [:index, :show, :update] + resources :offers, only: [:show, :update] resources :supplied_products, only: [:create, :show, :update] resources :social_medias, only: [:show] end - resources :enterprise_groups, only: [:index, :show] + resources :enterprise_groups, only: [:index, :show] do + resources :affiliated_by, only: [:create, :destroy], module: 'enterprise_groups' + end resources :persons, only: [:show] end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb b/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb deleted file mode 100644 index 7c1d22e6e3..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -# Load the original library first: -require "datafoodconsortium/connector" - -# Then our tools for monky-patching: -require_relative "importer" -require_relative "context" -require_relative "skos_parser_element" -require_relative "skos_concept" -require_relative "skos_parser" - -module DataFoodConsortium - module Connector - class Connector - def import(json_string_or_io) - Importer.new.import(json_string_or_io) - end - - # Monkey patch private method until fixed upstream: - # https://github.com/datafoodconsortium/connector-ruby/issues/19 - def loadThesaurus(data) # rubocop:disable Naming/MethodName - # The root element may be an array or the ontology. - data = data[0] if data.is_a?(Array) - @parser.parse(data["@graph"]) - end - end - end -end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/context.rb b/engines/dfc_provider/lib/data_food_consortium/connector/context.rb deleted file mode 100644 index 428d953cce..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/context.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -# Preload the DFC context. -# -# Similar to: https://github.com/ruby-rdf/json-ld-preloaded/ -module DataFoodConsortium - module Connector - class Context < JSON::LD::Context - VERSION_1_8 = JSON.parse <<~JSON - { - "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - "skos" : "http://www.w3.org/2004/02/skos/core#", - "dfc": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_FullModel.owl#", - "dc": "http://purl.org/dc/elements/1.1/#", - "dfc-b": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_BusinessOntology.owl#", - "dfc-p": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_ProductGlossary.owl#", - "dfc-t": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_TechnicalOntology.owl#", - "dfc-m": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", - "dfc-pt": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", - "dfc-f": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#", - "ontosec": "http://www.semanticweb.org/ontologies/2008/11/OntologySecurity.owl#", - "dfc-p:hasUnit":{ "@type":"@id" }, - "dfc-b:hasUnit":{ "@type":"@id" }, - "dfc-b:hasQuantity":{ "@type":"@id" }, - "dfc-p:hasType":{ "@type":"@id" }, - "dfc-b:hasType":{ "@type":"@id" }, - "dfc-b:references":{ "@type":"@id" }, - "dfc-b:referencedBy":{ "@type":"@id" }, - "dfc-b:offeres":{ "@type":"@id" }, - "dfc-b:supplies":{ "@type":"@id" }, - "dfc-b:defines":{ "@type":"@id" }, - "dfc-b:affiliates":{ "@type":"@id" }, - "dfc-b:hasCertification":{ "@type":"@id" }, - "dfc-b:manages":{ "@type":"@id" }, - "dfc-b:offeredThrough":{ "@type":"@id" }, - "dfc-b:hasBrand":{ "@type":"@id" }, - "dfc-b:hasGeographicalOrigin":{ "@type":"@id" }, - "dfc-b:hasClaim":{ "@type":"@id" }, - "dfc-b:hasAllergenDimension":{ "@type":"@id" }, - "dfc-b:hasNutrientDimension":{ "@type":"@id" }, - "dfc-b:hasPhysicalDimension":{ "@type":"@id" }, - "dfc:owner":{ "@type":"@id" }, - "dfc-t:hostedBy":{ "@type":"@id" }, - "dfc-t:hasPivot":{ "@type":"@id" }, - "dfc-t:represent":{ "@type":"@id" } - } - JSON - - add_preloaded("http://www.datafoodconsortium.org/") { parse(VERSION_1_8) } - alias_preloaded( - "http://static.datafoodconsortium.org/ontologies/context.json", - "http://www.datafoodconsortium.org/" - ) - end - end -end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb deleted file mode 100644 index 4e7d2b289a..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb +++ /dev/null @@ -1,146 +0,0 @@ -# frozen_string_literal: true - -require_relative "skos_parser" - -module DataFoodConsortium - module Connector - class Importer - TYPES = [ - DataFoodConsortium::Connector::CatalogItem, - DataFoodConsortium::Connector::Enterprise, - DataFoodConsortium::Connector::Offer, - DataFoodConsortium::Connector::Person, - DataFoodConsortium::Connector::QuantitativeValue, - DataFoodConsortium::Connector::SuppliedProduct, - ].freeze - - def self.type_map - unless @type_map - @type_map = {} - TYPES.each(&method(:register_type)) - end - - @type_map - end - - def self.register_type(clazz) - # Methods with variable arguments have a negative arity of -n-1 - # where n is the number of required arguments. - number_of_required_args = -1 * (clazz.instance_method(:initialize).arity + 1) - args = Array.new(number_of_required_args) - type_uri = clazz.new(*args).semanticType - type_map[type_uri] = clazz - end - - def self.prefixed_name(uri) - # When we skip backwards compatibility, we can just do this: - # - # key = RDF::URI.new(uri).pname(prefixes: Context::VERSION_1_8) - # - # But for now we do it manually. - uri.gsub( - "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_BusinessOntology.owl#", - "dfc-b:" - ).gsub( - # legacy URI - "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", - "dfc-b:" - ) - end - - def import(json_string_or_io) - @subjects = {} - - graph = parse_rdf(json_string_or_io) - build_subjects(graph) - apply_statements(graph) - - if @subjects.size > 1 - @subjects.values - else - @subjects.values.first - end - end - - private - - # The `io` parameter can be a String or an IO instance. - def parse_rdf(io) - io = StringIO.new(io) if io.is_a?(String) - RDF::Graph.new << JSON::LD::API.toRdf(io) - end - - def build_subjects(graph) - graph.query({ predicate: RDF.type }).each do |statement| - @subjects[statement.subject] = build_subject(statement) - end - end - - def build_subject(type_statement) - # Not all subjects have an id, some are anonymous. - id = type_statement.subject.try(:value) - type = type_statement.object.value - key = self.class.prefixed_name(type) - clazz = self.class.type_map[key] - - clazz.new(*[id].compact) - end - - def apply_statements(statements) - statements.each do |statement| - apply_statement(statement) - end - end - - def apply_statement(statement) - subject = subject_of(statement) - property_uri = statement.predicate.value - value = resolve_object(statement.object) - - property_id = self.class.prefixed_name(property_uri) - - return unless subject.hasSemanticProperty?(property_id) - - property = subject.semanticProperty(property_id) - - if property.value.is_a?(Enumerable) - property.value << value - else - setter = guess_setter_name(statement.predicate) - subject.try(setter, value) if setter - end - end - - def subject_of(statement) - @subjects[statement.subject] - end - - def resolve_object(object) - @subjects[object] || skos_concept(object) || object.object - end - - def skos_concept(object) - return unless object.uri? - - id = object.value.sub( - "http://static.datafoodconsortium.org/data/measures.rdf#", "dfc-m:" - ).sub( - "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", - "dfc-m:" - ) - SKOSParser.concepts[id] - end - - def guess_setter_name(predicate) - name = - # Some predicates are named like `hasQuantity` - # but the attribute name would be `quantity`. - predicate.fragment&.sub(/^has/, "")&.camelize(:lower) || - # And sometimes the URI looks like `ofn:spree_product_id`. - predicate.to_s.split(":").last - - "#{name}=" - end - end - end -end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb deleted file mode 100644 index ad8bbe266d..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# Patch: Improve parsing of SKOS Concept. Will be fixed upstream -require_relative 'skos_helper' - -module DataFoodConsortium - module Connector - class SKOSConcept - include DataFoodConsortium::Connector::SKOSHelper - end - end -end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb deleted file mode 100644 index e8b814678f..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -# Patch: Improve parsing of SKOS Concept. Will be fixed upstream -module DataFoodConsortium - module Connector - module SKOSHelper - def addAttribute(name, value) # rubocop:disable Naming/MethodName - instance_variable_set("@#{name}", value) - define_singleton_method(name) do - instance_variable_get("@#{name}") - end - end - - def hasAttribute(name) # rubocop:disable Naming/MethodName - methods.include?(:"#{name}") - end - end - end -end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb deleted file mode 100644 index dda1422ac2..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb +++ /dev/null @@ -1,144 +0,0 @@ -# frozen_string_literal: true - -# patches : -# - Maikel: Overriding the current implementation to store all parsed concepts for -# lookup later. Otherwise the importer can't associate these. -# This is just a workaround and needs to be solved upstream. - -# - Gaetan: Improve parsing of SKOS Concept. Will be fixed upstream - -require_relative 'skos_helper' - -module DataFoodConsortium - module Connector - class SKOSInstance - include DataFoodConsortium::Connector::SKOSHelper - - # Return a list of singelton methods, ie the list of Concept available - def topConcepts # rubocop:disable Naming/MethodName - methods(false).sort - end - end - end -end - -# rubocop:disable Naming/VariableName -module DataFoodConsortium - module Connector - class SKOSParser - CONCEPT_SCHEMES = ["Facet", "productTypes"].freeze - - def initialize - @results = DataFoodConsortium::Connector::SKOSInstance.new - @skosConcepts = {} - @rootElements = [] - @broaders = {} - # Flag used to tell the parser to use SkosConcept object when parsing data from - # Concept Scheme. - # defined in CONCEPT_SCHEMES - @useSkosConcept = false - end - - def parse(data) # rubocop:disable Metrics/CyclomaticComplexity - init - - data.each do |element| - current = DataFoodConsortium::Connector::SKOSParserElement.new(element) - - setSkosConceptFlag(current) - - next unless current.isConcept? || current.isCollection? - - if !@skosConcepts.key?(current.id) - concept = createSKOSConcept(current) - @skosConcepts[current.id] = concept - end - - if current.hasBroader - current.broader.each do |broader_id| - if !@broaders.key?(broader_id) - @broaders[broader_id] = [] - end - - @broaders[broader_id].push(current.id) - end - # No broader, save the concept to the root - else - @rootElements.push(current.id) - end - end - - @rootElements.each do |root_element_id| - setResults(@results, root_element_id) - end - - @results - end - - # Maikel's patch - def self.concepts - @concepts ||= {} - end - - protected - - def createSKOSConcept(element) # rubocop:disable Naming/MethodName - skosConcept = DataFoodConsortium::Connector::SKOSConcept.new( - element.id, - broaders: element.broader, - narrowers: element.narrower, - prefLabels: element.label - ) - skosConcept.semanticType = element.type - # Maikel's patch - self.class.concepts[element.id] = skosConcept - skosConcept - end - - private - - def init - @results = DataFoodConsortium::Connector::SKOSInstance.new - @skosConcepts = {} - @rootElements = [] - @broaders = {} - @useSkosConcept = false - end - - def setResults(parent, id) # rubocop:disable Naming/MethodName - name = getValueWithoutPrefix(id) - - if !parent.hasAttribute(name) - if @useSkosConcept && @skosConcepts[id] - parent.addAttribute(name, @skosConcepts[id]) - else - parent.addAttribute(name, DataFoodConsortium::Connector::SKOSInstance.new) - end - end - - # Leaf concepts, stop the process - if !@broaders.key?(id) - parent.instance_variable_set("@#{name}", @skosConcepts[id]) - return - end - - @broaders[id].each do |narrower| - parentSkosInstance = parent.instance_variable_get("@#{name}") - - setResults(parentSkosInstance, narrower) # recursive call - end - end - - def setSkosConceptFlag(current) # rubocop:disable Naming/MethodName - @useSkosConcept = true if current.isConceptScheme? && matchingConceptSchemes(current) - end - - def matchingConceptSchemes(current) # rubocop:disable Naming/MethodName - regex = /#{CONCEPT_SCHEMES.join('|')}/ - - current.id =~ regex - end - end - end -end -# rubocop:enable Naming/VariableName diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb deleted file mode 100644 index c8c684b8e4..0000000000 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -# Patch: Improve parsing of SKOS Concept. Will be fixed upstream -module DataFoodConsortium - module Connector - class SKOSParserElement - attr_reader :narrower, :label - - def initialize(element) # rubocop:disable Metrics/CyclomaticComplexity - @broader = [] - @narrower = [] - @label = {} - - if element - @id = element["@id"] - - @type = if element["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] - extractId(element["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]) - elsif element["@type"] - extractId(element["@type"]) - else - "undefined" - end - - element["http://www.w3.org/2004/02/skos/core#broader"]&.each do |broader| - @broader.push(broader["@id"]) - end - - element["http://www.w3.org/2004/02/skos/core#narrower"]&.each do |narrower| - @narrower.push(narrower["@id"]) - end - - element["http://www.w3.org/2004/02/skos/core#prefLabel"]&.each do |label| - @label[label["@language"].to_sym] = label["@value"] - end - else - @id = "" - @type = "" - end - end - - def isConceptScheme? # rubocop:disable Naming/MethodName - @type == "http://www.w3.org/2004/02/skos/core#ConceptScheme" - end - end - end -end diff --git a/engines/dfc_provider/lib/dfc_provider.rb b/engines/dfc_provider/lib/dfc_provider.rb index 4676736439..526cceb9c3 100644 --- a/engines/dfc_provider/lib/dfc_provider.rb +++ b/engines/dfc_provider/lib/dfc_provider.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true -# Load our monkey-patches of the DFC Connector: -require "data_food_consortium/connector/connector" +# Load the DFC Connector: +require "datafoodconsortium/connector" # Our Rails engine require "dfc_provider/engine" # Custom data types require "dfc_provider/supplied_product" +require "dfc_provider/address" module DfcProvider DataFoodConsortium::Connector::Importer.register_type(SuppliedProduct) diff --git a/engines/dfc_provider/lib/dfc_provider/address.rb b/engines/dfc_provider/lib/dfc_provider/address.rb new file mode 100644 index 0000000000..02ecc5ca3b --- /dev/null +++ b/engines/dfc_provider/lib/dfc_provider/address.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# Temporary solution to add `region` to Address, to be removed once the DFC connector supports it + +module DfcProvider + class Address < DataFoodConsortium::Connector::Address + # @return [String] + attr_accessor :region + + def initialize(semantic_id, region: "", **properties) + super(semantic_id, **properties) + @region = region + + registerSemanticProperty("dfc-b:region", &method("region")).valueSetter = method("region=") + end + end +end diff --git a/engines/dfc_provider/lib/dfc_provider/supplied_product.rb b/engines/dfc_provider/lib/dfc_provider/supplied_product.rb index 2e9e3a22c2..f2ebc9f61f 100644 --- a/engines/dfc_provider/lib/dfc_provider/supplied_product.rb +++ b/engines/dfc_provider/lib/dfc_provider/supplied_product.rb @@ -2,21 +2,27 @@ module DfcProvider class SuppliedProduct < DataFoodConsortium::Connector::SuppliedProduct - attr_accessor :spree_product_id, :image + attr_accessor :spree_product_id, :spree_product_uri, :image - def initialize(semantic_id, spree_product_id: nil, image_url: nil, **properties) + def initialize( + semantic_id, spree_product_id: nil, spree_product_uri: nil, image_url: nil, **properties + ) super(semantic_id, **properties) self.spree_product_id = spree_product_id + self.spree_product_uri = spree_product_uri self.image = image_url - registerSemanticProperty("ofn:spree_product_id") do - self.spree_product_id - end + # This is now replaced by spree_product_uri, keeping it for backward compatibility + register_ofn_property("spree_product_id") + register_ofn_property("spree_product_uri") # Temporary solution, will be replaced by "dfc_b:image" in future version of the DFC connector - registerSemanticProperty("ofn:image") do - image - end + register_ofn_property("image") + end + + def register_ofn_property(name) + registerSemanticProperty("ofn:#{name}", &method(name)) + .valueSetter = method("#{name}=") end end end diff --git a/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json b/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json index cc081912bb..d3e69dbb67 100644 --- a/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json +++ b/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json @@ -93,7 +93,7 @@ "dfc-b:hasUnit": "dfc-m:Piece", "dfc-b:value": 17 }, - "dfc-b:hasType": "dfc-pt:non-local-vegetable", + "dfc-b:hasType": "dfc-pt:drink", "dfc-b:lifetime": "", "dfc-b:name": "Pesto novo", "dfc-b:totalTheoreticalStock": 0, diff --git a/engines/dfc_provider/spec/lib/data_food_consortium/connector/connector_spec.rb b/engines/dfc_provider/spec/lib/data_food_consortium/connector/connector_spec.rb deleted file mode 100644 index f0bda34bf9..0000000000 --- a/engines/dfc_provider/spec/lib/data_food_consortium/connector/connector_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../spec_helper" -require "data_food_consortium/connector/connector" - -describe DataFoodConsortium::Connector::Connector, vcr: true do - subject(:connector) { described_class.instance } - let(:product) do - DataFoodConsortium::Connector::SuppliedProduct.new( - "https://example.net/tomato", - name: "Tomato", - description: "Awesome tomato" - ) - end - - it "exports" do - json = connector.export(product) - expect(json).to match '"dfc-b:name":"Tomato"' - end - - it "imports" do - json = connector.export(product) - result = connector.import(json) - expect(result).to be_a product.class - expect(result.semanticType).to eq product.semanticType - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - end - - it "imports from IO like Rails supplies it" do - json = connector.export(product) - io = StringIO.new(json) - result = connector.import(io) - - expect(result).to be_a product.class - expect(result.semanticType).to eq product.semanticType - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - end -end diff --git a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb b/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb deleted file mode 100644 index 7da3fdd88b..0000000000 --- a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb +++ /dev/null @@ -1,191 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../spec_helper" -require "data_food_consortium/connector/connector" - -describe DataFoodConsortium::Connector::Importer do - let(:connector) { DataFoodConsortium::Connector::Connector.instance } - let(:enterprise) do - DataFoodConsortium::Connector::Enterprise.new( - "https://example.net/foo-food-inc", - suppliedProducts: [product, second_product], - ) - end - let(:catalog_item) do - DataFoodConsortium::Connector::CatalogItem.new( - "https://example.net/tomatoItem", - product:, - ) - end - let(:product) do - DataFoodConsortium::Connector::SuppliedProduct.new( - "https://example.net/tomato", - name: "Tomato", - description: "Awesome tomato", - totalTheoreticalStock: 3, - ) - end - let(:product_data) do - <<~JSON - { - "@context":"http://static.datafoodconsortium.org/ontologies/context.json", - "@id":"https://example.net/tomato", - "@type":"dfc-b:SuppliedProduct", - "dfc-b:name":"Tomato", - "dfc-b:description":"Awesome tomato", - "dfc-b:alcoholPercentage":0.0, - "dfc-b:lifetime":"", - "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 - } - JSON - end - let(:product_data_with_context) do - <<~JSON - { - "@context": { - "dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", - "dfc-m": "http://static.datafoodconsortium.org/data/measures.rdf#", - "dfc-pt": "http://static.datafoodconsortium.org/data/productTypes.rdf#" - }, - "@id":"https://example.net/tomato", - "@type":"dfc-b:SuppliedProduct", - "dfc-b:name":"Tomato", - "dfc-b:description":"Awesome tomato", - "dfc-b:alcoholPercentage":0.0, - "dfc-b:lifetime":"", - "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 - } - JSON - end - let(:product_data_with_context_v1_8) do - <<~JSON - { - "@context": { - "dfc-b": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_BusinessOntology.owl#", - "dfc-m": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", - "dfc-pt": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#" - }, - "@id":"https://example.net/tomato", - "@type":"dfc-b:SuppliedProduct", - "dfc-b:name":"Tomato", - "dfc-b:description":"Awesome tomato", - "dfc-b:alcoholPercentage":0.0, - "dfc-b:lifetime":"", - "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 - } - JSON - end - let(:second_product) do - DataFoodConsortium::Connector::SuppliedProduct.new( - "https://example.net/ocra", - name: "Ocra", - ) - end - let(:quantity) do - DataFoodConsortium::Connector::QuantitativeValue.new( - unit: piece, - value: 5, - ) - end - let(:piece) do - unless connector.MEASURES.respond_to?(:UNIT) - connector.loadMeasures(read_file("measures")) - end - connector.MEASURES.PIECE - end - - it "imports a single object with simple properties" do - result = import(product) - - expect(result).to be_a product.class - expect(result.semanticType).to eq product.semanticType - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - expect(result.description).to eq "Awesome tomato" - expect(result.totalTheoreticalStock).to eq 3 - end - - it "imports an object with referenced context" do - result = connector.import(product_data) - - expect(result).to be_a DataFoodConsortium::Connector::SuppliedProduct - expect(result.semanticType).to eq "dfc-b:SuppliedProduct" - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - expect(result.description).to eq "Awesome tomato" - expect(result.totalTheoreticalStock).to eq 3 - end - - it "imports an object with included context" do - result = connector.import(product_data_with_context) - - expect(result).to be_a DataFoodConsortium::Connector::SuppliedProduct - expect(result.semanticType).to eq "dfc-b:SuppliedProduct" - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - expect(result.description).to eq "Awesome tomato" - expect(result.totalTheoreticalStock).to eq 3 - end - - it "imports an object with DFC v1.8 context" do - result = connector.import(product_data_with_context_v1_8) - - expect(result).to be_a DataFoodConsortium::Connector::SuppliedProduct - expect(result.semanticType).to eq "dfc-b:SuppliedProduct" - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - expect(result.description).to eq "Awesome tomato" - expect(result.totalTheoreticalStock).to eq 3 - end - - it "imports a graph with multiple objects" do - result = import(catalog_item, product) - - expect(result).to be_a Array - expect(result.size).to eq 2 - - item, tomato = result - - expect(item).to be_a catalog_item.class - expect(item.semanticType).to eq catalog_item.semanticType - expect(item.semanticId).to eq "https://example.net/tomatoItem" - expect(tomato.name).to eq "Tomato" - expect(tomato.description).to eq "Awesome tomato" - expect(tomato.totalTheoreticalStock).to eq 3 - end - - it "imports a graph including anonymous objects" do - product.quantity = quantity - - tomato, items = import(product) - - expect(tomato.name).to eq "Tomato" - expect(tomato.quantity).to eq items - expect(items.value).to eq 5 - expect(items.unit).to eq piece - end - - it "imports properties with lists" do - result = import(enterprise, product, second_product) - - expect(result.size).to eq 3 - - enterprise, tomato, ocra = result - - expect(enterprise.suppliedProducts).to eq [tomato, ocra] - end - - def import(*args) - json = connector.export(*args) - connector.import(json) - end - - def read_file(name) - JSON.parse( - Rails.root.join("engines/dfc_provider/vendor/#{name}.json").read - ) - end -end diff --git a/engines/dfc_provider/spec/requests/catalog_items_spec.rb b/engines/dfc_provider/spec/requests/catalog_items_spec.rb index c082630044..f047812e00 100644 --- a/engines/dfc_provider/spec/requests/catalog_items_spec.rb +++ b/engines/dfc_provider/spec/requests/catalog_items_spec.rb @@ -17,6 +17,14 @@ describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", :base_product, id: 90_000, supplier: enterprise, name: "Apple", description: "Red", variants: [variant], + primary_taxon: non_local_vegetable + ) + } + let(:non_local_vegetable) { + build( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR") } diff --git a/engines/dfc_provider/spec/requests/enterprise_groups/affiliated_by_spec.rb b/engines/dfc_provider/spec/requests/enterprise_groups/affiliated_by_spec.rb new file mode 100644 index 0000000000..4230fe2b29 --- /dev/null +++ b/engines/dfc_provider/spec/requests/enterprise_groups/affiliated_by_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require_relative "../../swagger_helper" + +describe "EnterpriseGroups::AffiliatedBy", type: :request, swagger_doc: "dfc.yaml", + rswag_autodoc: true do + let(:user) { create(:oidc_user, id: 12_345) } + let(:group) { + create( + :enterprise_group, + id: 60_000, owner: user, name: "Sustainable Farmers", address:, + enterprises: [enterprise], + ) + } + let(:address) { create(:address, id: 40_000, address1: "8 Acres Drive") } + let(:enterprise) { create(:enterprise, id: 10_000) } + let!(:enterprise2) { create(:enterprise, id: 10_001) } + + before { login_as user } + + path "/api/dfc/enterprise_groups/{enterprise_group_id}/affiliated_by" do + post "Add enterprise to group" do + consumes "application/json" + + parameter name: :enterprise_group_id, in: :path, type: :string + parameter name: :enterprise_id, in: :body, schema: { + example: { + '@id': "http://test.host/api/dfc/enterprises/10001" + } + } + + let(:enterprise_group_id) { group.id } + let(:enterprise_id) do |example| + example.metadata[:operation][:parameters].second[:schema][:example] + end + + response "201", "created" do + run_test! do + expect(group.enterprises.reload).to include(enterprise2) + end + end + + response "400", "bad request" do + describe "with missing request body" do + around do |example| + # Rswag expects all required parameters to be supplied with `let` + # but we want to send a request without the request body parameter. + parameters = example.metadata[:operation][:parameters] + example.metadata[:operation][:parameters] = [parameters.first] + example.run + example.metadata[:operation][:parameters] = parameters + end + + run_test! + end + + describe "with non valid enterprise uri" do + let(:enterprise_id) { { '@id': "http://test.host/%api/dfc/enterprises/10001" } } + + run_test! + end + end + + response "401", "unauthorized" do + let(:non_group_owner) { create(:oidc_user, id: 12_346) } + + before { login_as non_group_owner } + + run_test! + end + end + end + + path "/api/dfc/enterprise_groups/{enterprise_group_id}/affiliated_by/{id}" do + delete "Remove enterprise from group" do + parameter name: :enterprise_group_id, in: :path, type: :string + parameter name: :id, in: :path, type: :string + + let(:enterprise_group_id) { group.id } + let(:id) { enterprise2.id } + + response "204", "no content" do + before do + group.enterprises << enterprise2 + end + + it "removes enterperise from group" do |example| + expect { + submit_request(example.metadata) + group.reload + }.to change { group.enterprises.count }.by(-1) + end + end + + response "401", "unauthorized" do + let(:non_group_owner) { create(:oidc_user, id: 12_346) } + + before { login_as non_group_owner } + + run_test! + end + end + end +end diff --git a/engines/dfc_provider/spec/requests/enterprises_spec.rb b/engines/dfc_provider/spec/requests/enterprises_spec.rb index e043610dd8..587971a2ec 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -6,7 +6,7 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: let!(:user) { create(:oidc_user) } let!(:enterprise) do create( - :distributor_enterprise, :with_logo_image, + :distributor_enterprise, :with_logo_image, :with_promo_image, id: 10_000, owner: user, abn: "123 456", name: "Fred's Farm", description: "This is an awesome enterprise", contact_name: "Fred Farmer", @@ -29,6 +29,14 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: :product_with_image, id: 90_000, supplier: enterprise, name: "Apple", description: "Round", variants: [variant], + primary_taxon: non_local_vegetable + ) + } + let(:non_local_vegetable) { + build( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "APP") } @@ -72,6 +80,9 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: ).gsub!( %r{active_storage/[0-9A-Za-z/=-]*/logo.png}, "active_storage/url/logo.png", + ).gsub!( + %r{active_storage/[0-9A-Za-z/=-]*/promo.png}, + "active_storage/url/promo.png", ) end end @@ -82,7 +93,7 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: let(:other_enterprise) { create(:distributor_enterprise) } run_test! do - expect(response.body).to_not include "Apple" + expect(response.body).not_to include "Apple" end end end diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb new file mode 100644 index 0000000000..826df7be76 --- /dev/null +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require_relative "../swagger_helper" + +describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do + let!(:user) { create(:oidc_user) } + let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) } + let!(:product) { + create( + :product, + id: 90_000, + supplier: enterprise, name: "Pesto", description: "Basil Pesto", + variants: [variant], + ) + } + let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + + before { login_as user } + + path "/api/dfc/enterprises/{enterprise_id}/offers/{id}" do + parameter name: :enterprise_id, in: :path, type: :string + parameter name: :id, in: :path, type: :string + + let(:enterprise_id) { enterprise.id } + + get "Show Offer" do + produces "application/json" + + response "200", "success" do + let(:id) { variant.id } + + run_test! + end + end + + put "Update Offer" do + consumes "application/json" + + parameter name: :offer, in: :body, schema: { + example: { + '@context': "https://www.datafoodconsortium.org", + '@id': "http://test.host/api/dfc/enterprises/10000/offers/10001", + '@type': "dfc-b:Offer", + 'dfc-b:hasPrice': 9.99, + 'dfc-b:stockLimitation': 7 + } + } + + let(:id) { variant.id } + let(:offer) { offer_example } + let(:offer_example) { |example| + example.metadata[:operation][:parameters].first[:schema][:example] + } + + response "204", "success" do + context "with missing stockLimitation" do + let(:offer) { + offer_example.dup.tap do |o| + o.delete(:'dfc-b:stockLimitation') + end + } + + it "sets the variant to on demand" do |example| + pending "DFC Connector needs to support unset values." + + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.on_demand }.to(true) + .and change { variant.on_hand }.by(0) + end + end + + it "updates a variant" do |example| + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.price }.to(9.99) + .and change { variant.on_hand }.to(7) + end + end + end + end +end diff --git a/engines/dfc_provider/spec/requests/persons_spec.rb b/engines/dfc_provider/spec/requests/persons_spec.rb index 8e03a7667e..3af17b76e9 100644 --- a/engines/dfc_provider/spec/requests/persons_spec.rb +++ b/engines/dfc_provider/spec/requests/persons_spec.rb @@ -26,7 +26,7 @@ describe "Persons", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true let(:id) { other_user.id } run_test! do - expect(response.body).to_not include "dfc-b:Person" + expect(response.body).not_to include "dfc-b:Person" end end end diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 3e59aa2bad..6f22460e03 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -11,9 +11,25 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto id: 90_000, supplier: enterprise, name: "Pesto", description: "Basil Pesto", variants: [variant], + primary_taxon: taxon ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + let(:taxon) { + build( + :taxon, + name: "Processed Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#processed-vegetable" + ) + } + + let!(:non_local_vegetable) { + create( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" + ) + } before { login_as user } @@ -28,14 +44,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto parameter name: :supplied_product, in: :body, schema: { example: { - '@context': { - 'dfc-b': "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", - 'dfc-m': "http://static.datafoodconsortium.org/data/measures.rdf#", - 'dfc-pt': "http://static.datafoodconsortium.org/data/productTypes.rdf#", - 'dfc-b:hasUnit': { - '@type': "@id" - }, - }, + '@context': "https://www.datafoodconsortium.org", '@id': "http://test.host/api/dfc/enterprises/6201/supplied_products/0", '@type': "dfc-b:SuppliedProduct", 'dfc-b:name': "Apple", @@ -79,6 +88,11 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto end it "creates a product and variant" do |example| + # Despite requiring a tax catogory... + # https://github.com/openfoodfoundation/openfoodnetwork/issues/11212 + create(:tax_category, is_default: true) + Spree::Config.products_require_tax_category = true + expect { submit_request(example.metadata) } .to change { enterprise.supplied_products.count }.by(1) @@ -100,6 +114,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto product = Spree::Product.find(product_id) expect(product.name).to eq "Apple" expect(product.variants).to eq [variant] + expect(product.primary_taxon).to eq(non_local_vegetable) # Creates a variant for existing product supplied_product[:'ofn:spree_product_id'] = product_id @@ -127,6 +142,36 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto '"ofn:spree_product_id":90000' ) end + + context "when supplying spree_product_uri matching the host" do + it "creates a variant for the existing product" do |example| + supplied_product[:'ofn:spree_product_uri'] = + "http://test.host/api/dfc/enterprises/10000?spree_product_id=90000" + supplied_product[:'dfc-b:hasQuantity'][:'dfc-b:value'] = 6 + + expect { + submit_request(example.metadata) + product.variants.reload + } + .to change { product.variants.count }.by(1) + + # Creates a variant for existing product + variant_id = json_response["@id"].split("/").last.to_i + new_variant = Spree::Variant.find(variant_id) + expect(product.variants).to include(new_variant) + expect(new_variant.unit_value).to eq 6 + + # Insert static value to keep documentation deterministic: + response.body.gsub!( + "supplied_products/#{variant_id}", + "supplied_products/10001" + ) + .gsub!( + %r{active_storage/[0-9A-Za-z/=-]*/logo-white.png}, + "active_storage/url/logo-white.png", + ) + end + end end end end @@ -146,6 +191,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto run_test! do expect(response.body).to include variant.name expect(json_response["ofn:spree_product_id"]).to eq 90_000 + expect(json_response["dfc-b:hasType"]).to eq("dfc-pt:processed-vegetable") expect(json_response["ofn:image"]).to include("logo-white.png") # Insert static value to keep documentation deterministic: @@ -165,6 +211,14 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto end put "Update SuppliedProduct" do + let!(:drink_taxon) { + create( + :taxon, + name: "Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + ) + } + consumes "application/json" parameter name: :supplied_product, in: :body, schema: { @@ -188,8 +242,9 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto submit_request(example.metadata) variant.reload }.to change { variant.description }.to("DFC-Pesto updated") - .and change { variant.name }.to("Pesto novo") + .and change { variant.display_name }.to("Pesto novo") .and change { variant.unit_value }.to(17) + .and change { variant.product.primary_taxon }.to(drink_taxon) end end end diff --git a/engines/dfc_provider/spec/services/address_builder_spec.rb b/engines/dfc_provider/spec/services/address_builder_spec.rb index 660c7781ae..134e08e998 100644 --- a/engines/dfc_provider/spec/services/address_builder_spec.rb +++ b/engines/dfc_provider/spec/services/address_builder_spec.rb @@ -8,6 +8,7 @@ describe AddressBuilder do build( :address, id: 1, address1: "Paradise 15", zipcode: "0001", city: "Goosnargh", + state: build(:state, name: "Victoria") ) } @@ -33,5 +34,9 @@ describe AddressBuilder do it "assigns a country" do expect(result.country).to eq "Australia" end + + it "assigns a region" do + expect(result.region).to eq "Victoria" + end end end diff --git a/engines/dfc_provider/spec/services/authorization_control_spec.rb b/engines/dfc_provider/spec/services/authorization_control_spec.rb index ed1f4cfe97..97cd646df3 100644 --- a/engines/dfc_provider/spec/services/authorization_control_spec.rb +++ b/engines/dfc_provider/spec/services/authorization_control_spec.rb @@ -16,9 +16,8 @@ describe AuthorizationControl do end it "ignores blank email" do - create(:user, uid: nil) - create(:user, uid: "") - token = allow_token_for(email: nil) + OidcAccount.where(user:).update_all(uid: "") + token = allow_token_for(email: "") expect(auth(oidc_token: token).user).to eq nil end diff --git a/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb new file mode 100644 index 0000000000..be4fa4342b --- /dev/null +++ b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +describe DfcProductTypeFactory do + describe ".for" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + } + + it "assigns a top level product type" do + drink = DfcLoader.connector.PRODUCT_TYPES.DRINK + + expect(described_class.for(dfc_id).semanticId).to eq drink.semanticId + end + + context "with second level product type" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + } + + it "assigns a second level product type" do + soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + + expect(described_class.for(dfc_id).semanticId).to eq soft_drink.semanticId + end + end + + context "with leaf level product type" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade" + } + + it "assigns a leaf level product type" do + lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE + + expect(described_class.for(dfc_id).semanticId).to eq lemonade.semanticId + end + end + + context "with non existing product type" do + let(:dfc_id) { "other" } + + it "returns nil" do + expect(described_class.for(dfc_id)).to be_nil + end + end + end +end diff --git a/engines/dfc_provider/spec/services/dfc_request_spec.rb b/engines/dfc_provider/spec/services/dfc_request_spec.rb new file mode 100644 index 0000000000..a4f0421fad --- /dev/null +++ b/engines/dfc_provider/spec/services/dfc_request_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +describe DfcRequest do + subject(:api) { DfcRequest.new(user) } + + let(:user) { build(:oidc_user) } + let(:account) { user.oidc_account } + + it "gets a DFC document" do + stub_request(:get, "http://example.net/api"). + to_return(status: 200, body: '{"@context":"/"}') + + expect(api.get("http://example.net/api")).to eq '{"@context":"/"}' + end + + it "refreshes the access token on fail", vcr: true do + # Live VCR recordings require the following secret ENV variables: + # - OPENID_APP_ID + # - OPENID_APP_SECRET + # - OPENID_REFRESH_TOKEN + # You can set them in the .env.test.local file. + + stub_request(:get, "http://example.net/api"). + to_return(status: 401) + + # A refresh is only attempted if the token is stale. + account.refresh_token = ENV.fetch("OPENID_REFRESH_TOKEN") + account.updated_at = 1.day.ago + + expect { + api.get("http://example.net/api") + }.to change { + account.token + }.and change { + account.refresh_token + } + end + + it "doesn't try to refresh the token when it's still fresh" do + stub_request(:get, "http://example.net/api"). + to_return(status: 401) + + user.oidc_account.updated_at = 1.minute.ago + + expect(api.get("http://example.net/api")).to eq "" + + # Trying to reach the OIDC server via network request to refresh the token + # would raise errors because we didn't setup Webmock or VCR. + # The absence of errors makes this test pass. + end +end diff --git a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb index c1d4bf94a8..8e24ed4fd1 100644 --- a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb +++ b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb @@ -41,7 +41,7 @@ describe EnterpriseBuilder do expect(variant).to be_persisted expect(result.suppliedProducts.count).to eq 1 - expect(result.suppliedProducts[0].name).to eq "Apple" + expect(result.suppliedProducts[0].name).to eq "Apple - 1g" end it "assigns an address" do diff --git a/engines/dfc_provider/spec/services/offer_builder_spec.rb b/engines/dfc_provider/spec/services/offer_builder_spec.rb index 3d0bf3256a..15ef9c4dda 100644 --- a/engines/dfc_provider/spec/services/offer_builder_spec.rb +++ b/engines/dfc_provider/spec/services/offer_builder_spec.rb @@ -2,7 +2,7 @@ require_relative "../spec_helper" -describe DfcBuilder do +describe OfferBuilder do let(:variant) { build(:variant) } describe ".offer" do @@ -11,7 +11,7 @@ describe DfcBuilder do variant.save! variant.on_hand = 5 - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq 5 end @@ -22,7 +22,7 @@ describe DfcBuilder do variant.on_hand = 5 variant.on_demand = true - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq nil end diff --git a/engines/dfc_provider/spec/services/social_media_builder_spec.rb b/engines/dfc_provider/spec/services/social_media_builder_spec.rb new file mode 100644 index 0000000000..633ce732d8 --- /dev/null +++ b/engines/dfc_provider/spec/services/social_media_builder_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +describe SocialMediaBuilder do + let(:enterprise) do + create( + :enterprise, + id: 10_000, + + # These formats are requested by our UI: + facebook: "www.facebook.com/user", + instagram: "handle", + linkedin: "www.linkedin.com/company/name", + ) + end + + describe ".social_media" do + it "links to Facebook" do + result = SocialMediaBuilder.social_media(enterprise, "facebook") + expect(result.url).to eq "https://www.facebook.com/user" + end + + it "links to Instagram" do + result = SocialMediaBuilder.social_media(enterprise, "instagram") + expect(result.url).to eq "https://www.instagram.com/handle/" + end + + it "links to Linkedin" do + result = SocialMediaBuilder.social_media(enterprise, "linkedin") + expect(result.url).to eq "https://www.linkedin.com/company/name" + end + end +end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 464cc73036..08db382860 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -7,7 +7,22 @@ describe SuppliedProductBuilder do subject(:builder) { described_class } let(:variant) { - build(:variant, id: 5).tap { |v| v.product.supplier_id = 7 } + build(:variant, id: 5, product: spree_product) + } + let(:spree_product) { + create(:product, id: 6, supplier:).tap do |p| + p.primary_taxon = taxon + end + } + let(:supplier) { + build(:supplier_enterprise, id: 7) + } + let(:taxon) { + build( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) } describe ".supplied_product" do @@ -38,14 +53,17 @@ describe SuppliedProductBuilder do variant.display_name = "Granny Smith" product = builder.supplied_product(variant) - expect(product.name).to eq "Granny Smith" + expect(product.name).to eq "Apple - Granny Smith" end - it "assigns a product type" do - product = builder.supplied_product(variant) - vegetable = DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE + context "product_type mapping" do + subject(:product) { builder.supplied_product(variant) } - expect(product.productType).to eq vegetable + it "assigns a product type" do + soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + + expect(product.productType).to eq soft_drink + end end it "assigns an image_url type" do @@ -58,5 +76,245 @@ describe SuppliedProductBuilder do expect(product.image).to eq variant.product.image.url(:product) end + + it "assigns the product uri" do + product = builder.supplied_product(variant) + + expect(product.spree_product_uri).to eq( + "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + ) + end + end + + describe ".import_product" do + let(:supplied_product) do + DataFoodConsortium::Connector::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } + let!(:taxon) { + create( + :taxon, + name: "Non local vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" + ) + } + + it "creates a new Spree::Product" do + product = builder.import_product(supplied_product) + + expect(product).to be_a(Spree::Product) + expect(product.name).to eq("Tomato") + expect(product.description).to eq("Awesome tomato") + expect(product.variant_unit).to eq("weight") + end + + describe "taxon" do + it "assigns the taxon matching the DFC product type" do + product = builder.import_product(supplied_product) + + expect(product.primary_taxon).to eq(taxon) + end + end + end + + describe ".import_variant" do + let(:imported_variant) { builder.import_variant(supplied_product, supplier) } + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } + + it "creates a new Spree::Product and variant" do + create(:taxon) + + expect(imported_variant).to be_a(Spree::Variant) + expect(imported_variant).to be_valid + expect(imported_variant.id).to be_nil + expect(imported_variant.semantic_links.size).to eq 1 + + link = imported_variant.semantic_links[0] + expect(link.semantic_id).to eq "https://example.net/tomato" + + imported_product = imported_variant.product + expect(imported_product).to be_a(Spree::Product) + expect(imported_product).to be_valid + expect(imported_product.id).to be_nil + expect(imported_product.name).to eq("Tomato") + expect(imported_product.description).to eq("Awesome tomato") + expect(imported_product.variant_unit).to eq("weight") + end + + context "with spree_product_id supplied" do + let(:imported_variant) { builder.import_variant(supplied_product, supplier) } + + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Better Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_id: variant.product.id + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK } + let!(:new_taxon) { + create( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) + } + + it "update an existing Spree::Product" do + imported_product = imported_variant.product + expect(imported_product.id).to eq(spree_product.id) + expect(imported_product.description).to eq("Better Awesome tomato") + expect(imported_product.primary_taxon).to eq(new_taxon) + end + + it "adds a new variant" do + expect(imported_variant.id).to be_nil + expect(imported_variant.product).to eq(spree_product) + expect(imported_variant.display_name).to eq("Tomato") + expect(imported_variant.unit_value).to eq(2000) + end + end + + context "with spree_product_uri supplied" do + let(:imported_variant) { builder.import_variant(supplied_product, supplier) } + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK } + let!(:new_taxon) { + create( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) + } + + context "when spree_product_uri match the server host" do + let(:supplied_product) do + variant.save! # referenced in spree_product_id + + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Better Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_uri: "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + ) + end + + it "update an existing Spree::Product" do + imported_product = imported_variant.product + expect(imported_product.id).to eq(spree_product.id) + expect(imported_product.description).to eq("Better Awesome tomato") + expect(imported_product.primary_taxon).to eq(new_taxon) + end + + it "adds a new variant" do + expect(imported_variant.id).to be_nil + expect(imported_variant.product).to eq(spree_product) + expect(imported_variant.display_name).to eq("Tomato") + expect(imported_variant.unit_value).to eq(2000) + end + end + + context "when spree_product_uri doesn't match the server host" do + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_uri: "http://another.host/api/dfc/enterprises/10/supplied_products/50" + ) + end + + it "creates a new Spree::Product and variant" do + expect(imported_variant).to be_a(Spree::Variant) + expect(imported_variant.id).to be_nil + + imported_product = imported_variant.product + expect(imported_product).to be_a(Spree::Product) + expect(imported_product.id).to be_nil + expect(imported_product.name).to eq("Tomato") + expect(imported_product.description).to eq("Awesome tomato") + expect(imported_product.variant_unit).to eq("weight") + end + end + end + end + + describe ".referenced_spree_product" do + let(:result) { builder.referenced_spree_product(supplied_product, supplier) } + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + ) + end + + it "returns nil when no reference is given" do + expect(result).to eq nil + end + + it "returns a product referenced by URI" do + variant.save! + supplied_product.spree_product_uri = + "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + expect(result).to eq spree_product + end + + it "doesn't return a product of another enterprise" do + variant.save! + create(:product, id: 8, supplier: create(:enterprise)) + + supplied_product.spree_product_uri = + "http://test.host/api/dfc/enterprises/7?spree_product_id=8" + expect(result).to eq nil + end + + it "doesn't return a foreign product referenced by URI" do + variant.save! + supplied_product.spree_product_uri = + "http://another.host/api/dfc/enterprises/7?spree_product_id=6" + expect(result).to eq nil + end + + it "returns a product referenced by id" do + variant.save! + supplied_product.spree_product_id = "6" + expect(result).to eq spree_product + end end end diff --git a/engines/dfc_provider/spec/swagger_helper.rb b/engines/dfc_provider/spec/swagger_helper.rb index 3a549d38ca..ebc26a578d 100644 --- a/engines/dfc_provider/spec/swagger_helper.rb +++ b/engines/dfc_provider/spec/swagger_helper.rb @@ -5,7 +5,7 @@ require_relative "spec_helper" RSpec.configure do |config| # Override swagger docs to generate only this file: - config.swagger_docs = { + config.openapi_specs = { 'dfc.yaml' => { openapi: '3.0.1', info: { diff --git a/engines/dfc_provider/vendor/facets.json b/engines/dfc_provider/vendor/facets.json index ca3dd5c38a..30bae6d5e2 100644 --- a/engines/dfc_provider/vendor/facets.json +++ b/engines/dfc_provider/vendor/facets.json @@ -104,7 +104,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PartOrigin" } ], @@ -127,10 +127,10 @@ "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Pig" }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Quail" - }, { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Roster" }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Sheep" + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Rooster" } ], "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { "@language" : "en", @@ -218,7 +218,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -245,7 +245,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BeeHoney", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee" } ], @@ -261,7 +261,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BeePollen", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee" } ], @@ -277,7 +277,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BeePropolis", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee" } ], @@ -293,7 +293,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BeeVenom", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee" } ], @@ -309,7 +309,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BeeWax", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bee" } ], @@ -424,7 +424,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bulb", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -440,7 +440,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bull", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -459,7 +459,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#BullBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Bull" } ], @@ -530,6 +530,22 @@ "http://www.w3.org/2004/02/skos/core#topConceptOf" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#ChickenBody", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#broader" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#c_99fce7e0" + } ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "fr", + "@value" : "Partie du poulet" + }, { + "@language" : "en", + "@value" : "Chicken's body" + } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Claim", "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], @@ -555,6 +571,19 @@ "http://www.w3.org/2004/02/skos/core#topConceptOf" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#ContainerInformation", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "en", + "@value" : "Container Information" + } ], + "http://www.w3.org/2004/02/skos/core#topConceptOf" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#ContainsNutrientOrSubstance", "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], @@ -599,7 +628,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Cow", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -620,7 +649,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#CowBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Cow" } ], @@ -636,7 +665,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#CowMilk", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Cow" } ], @@ -830,7 +859,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Ewe", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -851,7 +880,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#EweBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Ewe" } ], @@ -867,7 +896,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#EweMilk", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Ewe" } ], @@ -958,7 +987,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Flower", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -1027,7 +1056,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Fruit", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -1094,7 +1123,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Goat", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -1115,7 +1144,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#GoatBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Goat" } ], @@ -1131,7 +1160,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#GoatMilk", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Goat" } ], @@ -1231,7 +1260,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Hen", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -1252,7 +1281,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#HenBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Hen" } ], @@ -1268,7 +1297,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#HenEgg", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Hen" } ], @@ -1565,7 +1594,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Leaf", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -1880,7 +1909,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#MultiOrigin", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PartOrigin" } ], @@ -2409,9 +2438,22 @@ "@language" : "fr", "@value" : "Statut géographique protégé" } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Package", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#broader" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#ContainerInformation" + } ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "en", + "@value" : "Package" + } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PartOrigin", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://purl.org/dc/elements/1.1/description" : [ { "@language" : "en", "@value" : "Part of natural \"living\" origin concerned" @@ -2490,7 +2532,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Pig", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -2509,7 +2551,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PigBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Pig" } ], @@ -2541,7 +2583,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PartOrigin" } ], @@ -2640,7 +2682,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Quail", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -2659,7 +2701,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#QuailEgg", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Quail" } ], @@ -2738,9 +2780,44 @@ "@language" : "fr", "@value" : "Réduit en nutriment" } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Rooster", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#broader" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" + } ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#narrower" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#RoosterBody" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "fr", + "@value" : "Coq" + }, { + "@language" : "en", + "@value" : "Rooster" + } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#RoosterBody", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#broader" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Rooster" + } ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "fr", + "@value" : "Partie de coq" + }, { + "@language" : "en", + "@value" : "Rooster's body" + } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Root", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -2754,41 +2831,6 @@ "@language" : "en", "@value" : "Root" } ] - }, { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Roster", - "@type" : [ "skos:Concept" ], - "http://www.w3.org/2004/02/skos/core#broader" : [ { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" - } ], - "http://www.w3.org/2004/02/skos/core#inScheme" : [ { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" - } ], - "http://www.w3.org/2004/02/skos/core#narrower" : [ { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#RosterBody" - } ], - "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { - "@language" : "fr", - "@value" : "Coq" - }, { - "@language" : "en", - "@value" : "Rooster" - } ] - }, { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#RosterBody", - "@type" : [ "skos:Concept" ], - "http://www.w3.org/2004/02/skos/core#broader" : [ { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Roster" - } ], - "http://www.w3.org/2004/02/skos/core#inScheme" : [ { - "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" - } ], - "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { - "@language" : "fr", - "@value" : "Partie de coq" - }, { - "@language" : "en", - "@value" : "Rooster's body" - } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#SaturatedFatFree", "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], @@ -2833,7 +2875,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Seed", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -2849,7 +2891,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Sheep", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" } ], @@ -2868,7 +2910,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#SheepBody", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Sheep" } ], @@ -3037,7 +3079,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Stem", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -3151,7 +3193,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#Tuber", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -3250,7 +3292,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#UnknownPartOrigin", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PartOrigin" } ], @@ -3356,7 +3398,7 @@ } ] }, { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#WholePlant", - "@type" : [ "skos:Concept" ], + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], "http://www.w3.org/2004/02/skos/core#broader" : [ { "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#PlantPartOrigin" } ], @@ -3370,6 +3412,22 @@ "@language" : "en", "@value" : "Whole plant" } ] + }, { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#c_99fce7e0", + "@type" : [ "http://www.w3.org/2004/02/skos/core#Concept" ], + "http://www.w3.org/2004/02/skos/core#broader" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#AnimalPartOrigin" + } ], + "http://www.w3.org/2004/02/skos/core#inScheme" : [ { + "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf#DFC_ProductGlossary_Facet" + } ], + "http://www.w3.org/2004/02/skos/core#prefLabel" : [ { + "@language" : "en", + "@value" : "Chicken" + }, { + "@language" : "fr", + "@value" : "Poulet" + } ] } ], "@id" : "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/facets.rdf" } ] \ No newline at end of file diff --git a/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb b/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb index cfecbc7de9..0763a32b1a 100644 --- a/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb +++ b/engines/order_management/app/services/order_management/order/stripe_sca_payment_authorize.rb @@ -12,7 +12,7 @@ module OrderManagement def initialize(order, payment: nil, off_session: false, notify_hub: false) @order = order - @payment = payment || OrderPaymentFinder.new(order).last_pending_payment + @payment = payment || Orders::FindPaymentService.new(order).last_pending_payment @off_session = off_session @notify_hub = notify_hub end diff --git a/engines/order_management/app/services/order_management/order/updater.rb b/engines/order_management/app/services/order_management/order/updater.rb index 6c311dade7..97b77ace28 100644 --- a/engines/order_management/app/services/order_management/order/updater.rb +++ b/engines/order_management/app/services/order_management/order/updater.rb @@ -38,13 +38,6 @@ module OrderManagement update_pending_payment end - def update_pending_payment - return unless order.state.in? ["payment", "confirmation"] - return unless order.pending_payments.any? - - order.pending_payments.first.update_attribute :amount, order.total - end - # Updates the following Order total values: # # - payment_total - total value of all finalized Payments (excludes non-finalized Payments) @@ -239,6 +232,16 @@ module OrderManagement def requires_authorization? payments.requires_authorization.any? && payments.completed.empty? end + + def update_pending_payment + # We only want to update complete order pending payment when it's a cash payment. We assume + # that if the payment was a credit card it would alread have been processed, so we don't + # bother checking the payment type + return unless order.state.in? ["payment", "confirmation", "complete"] + return unless order.pending_payments.any? + + order.pending_payments.first.update_attribute :amount, order.total + end end end end diff --git a/engines/order_management/app/services/order_management/subscriptions/form.rb b/engines/order_management/app/services/order_management/subscriptions/form.rb index 8ea469d8b3..74aa78d72f 100644 --- a/engines/order_management/app/services/order_management/subscriptions/form.rb +++ b/engines/order_management/app/services/order_management/subscriptions/form.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'order_management/subscriptions/proxy_order_syncer' - module OrderManagement module Subscriptions class Form @@ -17,7 +15,7 @@ module OrderManagement @options = options @estimator = OrderManagement::Subscriptions::Estimator.new(subscription) @validator = OrderManagement::Subscriptions::Validator.new(subscription) - @order_syncer = OrderSyncer.new(subscription) + @order_syncer = Orders::SyncService.new(subscription) end def save diff --git a/engines/order_management/app/services/order_management/subscriptions/payment_setup.rb b/engines/order_management/app/services/order_management/subscriptions/payment_setup.rb index fea4da84e5..15cab96ee2 100644 --- a/engines/order_management/app/services/order_management/subscriptions/payment_setup.rb +++ b/engines/order_management/app/services/order_management/subscriptions/payment_setup.rb @@ -18,7 +18,7 @@ module OrderManagement private def create_payment - payment = OrderPaymentFinder.new(@order).last_pending_payment + payment = Orders::FindPaymentService.new(@order).last_pending_payment return payment if payment.present? @order.payments.create( diff --git a/engines/order_management/app/services/order_management/subscriptions/proxy_order_syncer.rb b/engines/order_management/app/services/order_management/subscriptions/proxy_order_syncer.rb index 9c50e175d5..e906a0ed16 100644 --- a/engines/order_management/app/services/order_management/subscriptions/proxy_order_syncer.rb +++ b/engines/order_management/app/services/order_management/subscriptions/proxy_order_syncer.rb @@ -14,7 +14,7 @@ module OrderManagement when ActiveRecord::Relation @subscriptions = subscriptions.not_ended.not_canceled else - raise "ProxyOrderSyncer must be initialized with " \ + raise "ProxyOrders::SyncService must be initialized with " \ "an instance of Subscription or ActiveRecord::Relation" end end diff --git a/engines/order_management/app/services/order_management/subscriptions/stripe_payment_setup.rb b/engines/order_management/app/services/order_management/subscriptions/stripe_payment_setup.rb index df79b45ddd..e3d01bdfc1 100644 --- a/engines/order_management/app/services/order_management/subscriptions/stripe_payment_setup.rb +++ b/engines/order_management/app/services/order_management/subscriptions/stripe_payment_setup.rb @@ -5,7 +5,7 @@ module OrderManagement class StripePaymentSetup def initialize(order) @order = order - @payment = OrderPaymentFinder.new(@order).last_pending_payment + @payment = Orders::FindPaymentService.new(@order).last_pending_payment end def call! diff --git a/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb b/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb index 9dd15e8a8b..afe866f15b 100644 --- a/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb +++ b/engines/order_management/spec/services/order_management/order/stripe_sca_payment_authorize_spec.rb @@ -74,9 +74,9 @@ module OrderManagement payment_authorize.call! expect(order.errors.size).to eq 0 - expect(PaymentMailer).to_not have_received(:authorize_payment) - expect(PaymentMailer).to_not have_received(:authorization_required) - expect(mail_mock).to_not have_received(:deliver_now) + expect(PaymentMailer).not_to have_received(:authorize_payment) + expect(PaymentMailer).not_to have_received(:authorization_required) + expect(mail_mock).not_to have_received(:deliver_now) end context "when the processing is off-session (via backoffice/subscription)" do diff --git a/engines/order_management/spec/services/order_management/order/updater_spec.rb b/engines/order_management/spec/services/order_management/order/updater_spec.rb index 921c4cc18a..6c2542b9e0 100644 --- a/engines/order_management/spec/services/order_management/order/updater_spec.rb +++ b/engines/order_management/spec/services/order_management/order/updater_spec.rb @@ -8,7 +8,7 @@ module OrderManagement let(:order) { create(:order) } let(:updater) { OrderManagement::Order::Updater.new(order) } - context "updating order totals" do + describe "#update_totals" do before do 2.times { create(:line_item, order:, price: 10) } end @@ -19,11 +19,23 @@ module OrderManagement updater.update_totals expect(order.payment_total).to eq(10) end + end + + describe "#update_item_total" do + before do + 2.times { create(:line_item, order:, price: 10) } + end it "updates item total" do updater.update_item_total expect(order.item_total).to eq(20) end + end + + describe "#update_adjustment_total" do + before do + 2.times { create(:line_item, order:, price: 10) } + end it "updates adjustment totals" do allow(order).to receive_message_chain(:all_adjustments, :additional, :eligible, @@ -40,7 +52,7 @@ module OrderManagement end end - context "updating shipment state" do + describe "#update_shipment_state" do let(:shipment) { build(:shipment) } before do @@ -85,19 +97,111 @@ module OrderManagement order.state_changed('shipment') end - context "completed order" do + describe "#update" do + it "updates totals once" do + expect(updater).to receive(:update_totals).once + updater.update + end + + it "updates all adjustments" do + expect(updater).to receive(:update_all_adjustments) + updater.update + end + + context "completed order" do + before { allow(order).to receive(:completed?) { true } } + + it "updates payment state" do + expect(updater).to receive(:update_payment_state) + updater.update + end + + it "updates shipment state" do + expect(updater).to receive(:update_shipment_state) + updater.update + end + + context "whith pending payments" do + let(:order) { create(:completed_order_with_totals) } + + it "updates pending payments" do + payment = create(:payment, order:, amount: order.total) + + # update order so the order total will change + update_order_quantity(order) + order.payments.reload + + expect { updater.update }.to change { payment.reload.amount }.from(50).to(60) + end + end + end + + context "incompleted order" do + before { allow(order).to receive_messages completed?: false } + + it "doesnt update payment state" do + expect(updater).not_to receive(:update_payment_state) + updater.update + end + + it "doesnt update shipment state" do + expect(updater).not_to receive(:update_shipment_state) + updater.update + end + + it "doesnt update the order shipment" do + shipment = build(:shipment) + allow(order).to receive_messages shipments: [shipment] + + expect(shipment).not_to receive(:update!).with(order) + expect(updater).not_to receive(:update_shipments).with(order) + updater.update + end + + context "with pending payments" do + let!(:payment) { create(:payment, order:, amount: order.total) } + + context "with order in payment state" do + let(:order) { create(:order_with_totals, state: "payment") } + + it "updates pending payments" do + # update order so the order total will change + update_order_quantity(order) + order.payments.reload + + expect { updater.update }.to change { payment.reload.amount }.from(10).to(20) + end + end + + context "with order in confirmation state" do + let(:order) { create(:order_with_totals, state: "confirmation") } + + it "updates pending payments" do + # update order so the order total will change + update_order_quantity(order) + order.payments.reload + + expect { updater.update }.to change { payment.reload.amount }.from(10).to(20) + end + end + + context "with order in cart" do + let(:order) { create(:order_with_totals) } + + it "doesn't update pending payments" do + # update order so the order total will change + update_order_quantity(order) + + expect { updater.update }.not_to change { payment.reload.amount } + end + end + end + end + end + + describe "#update_shipments" do before { allow(order).to receive(:completed?) { true } } - it "updates payment state" do - expect(updater).to receive(:update_payment_state) - updater.update - end - - it "updates shipment state" do - expect(updater).to receive(:update_shipment_state) - updater.update - end - it "updates the order shipment" do shipment = build(:shipment) allow(order).to receive_messages shipments: [shipment] @@ -107,39 +211,6 @@ module OrderManagement end end - context "incompleted order" do - before { allow(order).to receive_messages completed?: false } - - it "doesnt update payment state" do - expect(updater).not_to receive(:update_payment_state) - updater.update - end - - it "doesnt update shipment state" do - expect(updater).not_to receive(:update_shipment_state) - updater.update - end - - it "doesnt update the order shipment" do - shipment = build(:shipment) - allow(order).to receive_messages shipments: [shipment] - - expect(shipment).not_to receive(:update!).with(order) - expect(updater).not_to receive(:update_shipments).with(order) - updater.update - end - end - - it "updates totals once" do - expect(updater).to receive(:update_totals).once - updater.update - end - - it "updates all adjustments" do - expect(updater).to receive(:update_all_adjustments) - updater.update - end - describe "#update_payment_state" do context "when the order has no valid payments" do it "is failed" do @@ -166,7 +237,7 @@ module OrderManagement it "returns paid" do updater.update_payment_state - expect(order.payment_state).to_not eq("requires_authorization") + expect(order.payment_state).not_to eq("requires_authorization") end end @@ -277,9 +348,28 @@ module OrderManagement end end end + + context "when unused payments records exist which require authorization, " \ + "but the order is fully paid" do + let!(:cash_payment) { + build(:payment, state: "completed", amount: order.new_outstanding_balance) + } + let!(:stripe_payment) { build(:payment, state: "requires_authorization") } + before do + order.payments << cash_payment + order.payments << stripe_payment + end + + it "cancels unused payments requiring authorization" do + expect(stripe_payment).to receive(:void_transaction!) + expect(cash_payment).not_to receive(:void_transaction!) + + order.updater.update_payment_state + end + end end - context '#shipping_address_from_distributor' do + describe '#shipping_address_from_distributor' do let(:distributor) { build(:distributor_enterprise) } let(:shipment) { create(:shipment_with, :shipping_method, shipping_method:) @@ -313,69 +403,52 @@ module OrderManagement end end - describe "updating order totals" do - describe "#update_totals_and_states" do - it "deals with legacy taxes" do - expect(updater).to receive(:handle_legacy_taxes) + describe "#update_totals_and_states" do + it "deals with legacy taxes" do + expect(updater).to receive(:handle_legacy_taxes) - updater.update_totals_and_states + updater.update_totals_and_states + end + end + + describe "#handle_legacy_taxes" do + context "when the order is incomplete" do + it "doesn't touch taxes" do + allow(order).to receive(:completed?) { false } + + expect(order).not_to receive(:create_tax_charge!) + updater.__send__(:handle_legacy_taxes) end end - describe "#handle_legacy_taxes" do - context "when the order is incomplete" do - it "doesn't touch taxes" do - allow(order).to receive(:completed?) { false } + context "when the order is complete" do + before { allow(order).to receive(:completed?) { true } } + + context "and the order has legacy taxes" do + let!(:legacy_tax_adjustment) { + create(:adjustment, order:, adjustable: order, included: false, + originator_type: "Spree::TaxRate") + } + + it "re-applies order taxes" do + expect(order).to receive(:create_tax_charge!) - expect(order).to_not receive(:create_tax_charge!) updater.__send__(:handle_legacy_taxes) end end - context "when the order is complete" do - before { allow(order).to receive(:completed?) { true } } + context "and the order has no legacy taxes" do + it "leaves taxes untouched" do + expect(order).not_to receive(:create_tax_charge!) - context "and the order has legacy taxes" do - let!(:legacy_tax_adjustment) { - create(:adjustment, order:, adjustable: order, included: false, - originator_type: "Spree::TaxRate") - } - - it "re-applies order taxes" do - expect(order).to receive(:create_tax_charge!) - - updater.__send__(:handle_legacy_taxes) - end - end - - context "and the order has no legacy taxes" do - it "leaves taxes untouched" do - expect(order).to_not receive(:create_tax_charge!) - - updater.__send__(:handle_legacy_taxes) - end + updater.__send__(:handle_legacy_taxes) end end end end - context "when unused payments records exist which require authorization, " \ - "but the order is fully paid" do - let!(:cash_payment) { - build(:payment, state: "completed", amount: order.new_outstanding_balance) - } - let!(:stripe_payment) { build(:payment, state: "requires_authorization") } - before do - order.payments << cash_payment - order.payments << stripe_payment - end - - it "cancels unused payments requiring authorization" do - expect(stripe_payment).to receive(:void_transaction!) - expect(cash_payment).to_not receive(:void_transaction!) - - order.updater.update_payment_state - end + def update_order_quantity(order) + order.line_items.first.update_attribute(:quantity, 2) end end end diff --git a/engines/order_management/spec/services/order_management/stock/package_spec.rb b/engines/order_management/spec/services/order_management/stock/package_spec.rb index 5f36592a67..64a9fd46d3 100644 --- a/engines/order_management/spec/services/order_management/stock/package_spec.rb +++ b/engines/order_management/spec/services/order_management/stock/package_spec.rb @@ -168,7 +168,7 @@ module OrderManagement end it "does not return soft-deleted shipping methods" do - expect(package.shipping_methods).to_not include shipping_method3 + expect(package.shipping_methods).not_to include shipping_method3 end it "returns an empty array if distributor is nil" do diff --git a/engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb index 210c9e602f..035021eb49 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb @@ -22,7 +22,7 @@ module OrderManagement end it "creates a new payment on the order" do - expect{ payment_setup.call! }.to change(Spree::Payment, :count).by(1) + expect{ payment_setup.call! }.to change { Spree::Payment.count }.by(1) expect(order.payments.first.amount).to eq 5 end end @@ -34,7 +34,7 @@ module OrderManagement before { allow(order).to receive(:new_outstanding_balance) { 5 } } it "updates the payment total to reflect the outstanding balance" do - expect{ payment_setup.call! }.to change(payment, :amount).from(10).to(5) + expect{ payment_setup.call! }.to change { payment.amount }.from(10).to(5) end end @@ -42,7 +42,7 @@ module OrderManagement before { allow(order).to receive(:new_outstanding_balance) { 10 } } it "does nothing" do - expect{ payment_setup.call! }.to_not change(payment, :amount).from(10) + expect{ payment_setup.call! }.not_to change { payment.amount }.from(10) end end end diff --git a/engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb index e9307cb82a..fcf06793df 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb @@ -10,8 +10,8 @@ module OrderManagement it "raises an error when initialized with an object that is not a Subscription or an ActiveRecord::Relation" do - expect{ ProxyOrderSyncer.new(subscription) }.to_not raise_error - expect{ ProxyOrderSyncer.new(Subscription.where(id: subscription.id)) }.to_not raise_error + expect{ ProxyOrderSyncer.new(subscription) }.not_to raise_error + expect{ ProxyOrderSyncer.new(Subscription.where(id: subscription.id)) }.not_to raise_error expect{ ProxyOrderSyncer.new("something") }.to raise_error RuntimeError end end @@ -70,8 +70,8 @@ module OrderManagement let!(:oc) { closed_oc } it "does not create a new proxy order for that oc" do - expect{ subscription.save! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ subscription.save! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -79,8 +79,8 @@ module OrderManagement let!(:oc) { open_oc_closes_before_begins_at_oc } it "does not create a new proxy order for that oc" do - expect{ subscription.save! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ subscription.save! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -90,7 +90,7 @@ module OrderManagement it "creates a new proxy order for that oc" do syncer.sync! - expect{ subscription.save! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ subscription.save! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -99,8 +99,8 @@ module OrderManagement let!(:oc) { upcoming_closes_before_begins_at_oc } it "does not create a new proxy order for that oc" do - expect{ subscription.save! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ subscription.save! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -110,7 +110,7 @@ module OrderManagement it "creates a new proxy order for that oc" do syncer.sync! - expect{ subscription.save! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ subscription.save! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -121,7 +121,7 @@ module OrderManagement it "creates a new proxy order for that oc" do syncer.sync! - expect{ subscription.save! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ subscription.save! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -130,8 +130,8 @@ module OrderManagement let!(:oc) { upcoming_closes_after_ends_at_oc } it "does not create a new proxy order for that oc" do - expect{ subscription.save! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ subscription.save! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end end @@ -149,7 +149,7 @@ module OrderManagement context "the oc is closed (ie. closed before opens_at)" do let(:oc) { closed_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -157,7 +157,7 @@ module OrderManagement context "and the schedule includes an open oc that closes before begins_at" do let(:oc) { open_oc_closes_before_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -165,7 +165,7 @@ module OrderManagement context "and the oc is open and closes between begins_at and ends_at" do let(:oc) { open_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -173,7 +173,7 @@ module OrderManagement context "and the oc is upcoming and closes before begins_at" do let(:oc) { upcoming_closes_before_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -181,7 +181,7 @@ module OrderManagement context "and the oc is upcoming and closes on begins_at" do let(:oc) { upcoming_closes_on_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -189,7 +189,7 @@ module OrderManagement context "and the oc is upcoming and closes on ends_at" do let(:oc) { upcoming_closes_on_ends_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -197,7 +197,7 @@ module OrderManagement context "and the oc is upcoming and closes after ends_at" do let(:oc) { upcoming_closes_after_ends_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -208,8 +208,8 @@ module OrderManagement let(:oc) { closed_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end @@ -217,8 +217,8 @@ module OrderManagement let(:oc) { open_oc_closes_before_begins_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end @@ -238,7 +238,7 @@ module OrderManagement let(:oc) { open_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -247,7 +247,7 @@ module OrderManagement let(:oc) { upcoming_closes_on_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -256,7 +256,7 @@ module OrderManagement let(:oc) { upcoming_closes_on_ends_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -266,8 +266,8 @@ module OrderManagement let(:oc) { upcoming_closes_before_begins_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end @@ -275,8 +275,8 @@ module OrderManagement let(:oc) { upcoming_closes_after_ends_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end end @@ -297,7 +297,7 @@ module OrderManagement context "the oc is closed (ie. closed before opens_at)" do let(:oc) { closed_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -305,7 +305,7 @@ module OrderManagement context "and the schedule includes an open oc that closes before begins_at" do let(:oc) { open_oc_closes_before_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -313,7 +313,7 @@ module OrderManagement context "and the oc is open and closes between begins_at and ends_at" do let(:oc) { open_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -321,7 +321,7 @@ module OrderManagement context "and the oc is upcoming and closes before begins_at" do let(:oc) { upcoming_closes_before_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -329,7 +329,7 @@ module OrderManagement context "and the oc is upcoming and closes on begins_at" do let(:oc) { upcoming_closes_on_begins_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -337,7 +337,7 @@ module OrderManagement context "and the oc is upcoming and closes on ends_at" do let(:oc) { upcoming_closes_on_ends_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -345,7 +345,7 @@ module OrderManagement context "and the oc is upcoming and closes after ends_at" do let(:oc) { upcoming_closes_after_ends_at_oc } it "keeps the proxy order" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(1) + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(1) expect(proxy_orders).to include proxy_order end end @@ -356,8 +356,8 @@ module OrderManagement context "the oc is closed (ie. closed before opens_at)" do let(:oc) { closed_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end @@ -365,40 +365,40 @@ module OrderManagement context "and the oc is open and closes between begins_at and ends_at" do let(:oc) { open_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end context "and the oc is upcoming and closes before begins_at" do let(:oc) { upcoming_closes_before_begins_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end context "and the oc is upcoming and closes on begins_at" do let(:oc) { upcoming_closes_on_begins_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end context "and the oc is upcoming and closes on ends_at" do let(:oc) { upcoming_closes_on_ends_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end context "and the oc is upcoming and closes after ends_at" do let(:oc) { upcoming_closes_after_ends_at_oc } it "removes the proxy order" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(1).to(0) - expect(proxy_orders).to_not include proxy_order + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(1).to(0) + expect(proxy_orders).not_to include proxy_order end end end @@ -421,8 +421,8 @@ module OrderManagement let!(:oc) { closed_oc } it "does not create a new proxy order for that oc" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -430,8 +430,8 @@ module OrderManagement let(:oc) { open_oc_closes_before_begins_at_oc } it "does not create a new proxy order for that oc" do - expect{ subscription.save! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ subscription.save! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -439,7 +439,7 @@ module OrderManagement let!(:oc) { open_oc } it "creates a new proxy order for that oc" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -448,8 +448,8 @@ module OrderManagement let!(:oc) { upcoming_closes_before_begins_at_oc } it "does not create a new proxy order for that oc" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end @@ -457,7 +457,7 @@ module OrderManagement let!(:oc) { upcoming_closes_on_begins_at_oc } it "creates a new proxy order for that oc" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -466,7 +466,7 @@ module OrderManagement let!(:oc) { upcoming_closes_on_ends_at_oc } it "creates a new proxy order for that oc" do - expect{ syncer.sync! }.to change(ProxyOrder, :count).from(0).to(1) + expect{ syncer.sync! }.to change { ProxyOrder.count }.from(0).to(1) expect(subscription.reload.proxy_orders.map(&:order_cycle)).to include oc end end @@ -475,8 +475,8 @@ module OrderManagement let!(:oc) { upcoming_closes_after_ends_at_oc } it "does not create a new proxy order for that oc" do - expect{ syncer.sync! }.to_not change(ProxyOrder, :count).from(0) - expect(order_cycles).to_not include oc + expect{ syncer.sync! }.not_to change { ProxyOrder.count }.from(0) + expect(order_cycles).not_to include oc end end end diff --git a/engines/order_management/spec/services/order_management/subscriptions/stripe_payment_setup_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/stripe_payment_setup_spec.rb index 8e9b063f3c..9c4e97157c 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/stripe_payment_setup_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/stripe_payment_setup_spec.rb @@ -28,7 +28,7 @@ module OrderManagement let(:payment_method) { create(:payment_method) } it "returns the pending payment with no change" do - expect(payment).to_not receive(:update) + expect(payment).not_to receive(:update) expect(payment_setup.call!).to eq payment end end @@ -38,7 +38,7 @@ module OrderManagement context "and the card is already set (the payment source is a credit card)" do it "returns the pending payment with no change" do - expect(payment).to_not receive(:update) + expect(payment).not_to receive(:update) expect(payment_setup.call!).to eq payment end end @@ -54,7 +54,7 @@ module OrderManagement it "adds an error to the order and does not update the payment" do payment_setup.call! - expect(payment).to_not receive(:update) + expect(payment).not_to receive(:update) expect(payment_setup.call!).to eq payment expect(order.errors[:base].first).to eq "There are no authorised " \ "credit cards available to charge" @@ -80,7 +80,7 @@ module OrderManagement it "adds an error to the order and does not update the payment" do payment_setup.call! - expect(payment).to_not receive(:update) + expect(payment).not_to receive(:update) expect(payment_setup.call!).to eq payment expect(order.errors[:base].first).to eq "There are no authorised " \ "credit cards available to charge" diff --git a/engines/order_management/spec/services/order_management/subscriptions/summary_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/summary_spec.rb index dcfd214008..6aeb292bf2 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/summary_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/summary_spec.rb @@ -121,7 +121,7 @@ module OrderManagement it "returns orders specified by unrecorded_ids" do expect(orders).to include order1 - expect(orders).to_not include order2 + expect(orders).not_to include order2 end end @@ -130,7 +130,7 @@ module OrderManagement it "returns orders specified by the relevant issue hash" do expect(orders).to include order2 - expect(orders).to_not include order1 + expect(orders).not_to include order1 end end end diff --git a/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb index 6df7740d4e..55e4d28944 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb @@ -74,7 +74,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:shipping_method]).to_not be_empty + expect(validator.errors[:shipping_method]).not_to be_empty end end @@ -89,7 +89,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:shipping_method]).to_not be_empty + expect(validator.errors[:shipping_method]).not_to be_empty end end @@ -115,7 +115,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -130,7 +130,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -164,7 +164,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -192,7 +192,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:begins_at]).to_not be_empty + expect(validator.errors[:begins_at]).not_to be_empty end end @@ -213,7 +213,7 @@ module OrderManagement let(:ends_at) { Time.zone.today } it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:ends_at]).to_not be_empty + expect(validator.errors[:ends_at]).not_to be_empty end end @@ -221,7 +221,7 @@ module OrderManagement let(:ends_at) { Time.zone.today - 1.day } it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:ends_at]).to_not be_empty + expect(validator.errors[:ends_at]).not_to be_empty end end @@ -249,8 +249,8 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:bill_address]).to_not be_empty - expect(validator.errors[:ship_address]).to_not be_empty + expect(validator.errors[:bill_address]).not_to be_empty + expect(validator.errors[:ship_address]).not_to be_empty end end @@ -276,7 +276,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:customer]).to_not be_empty + expect(validator.errors[:customer]).not_to be_empty end end @@ -288,7 +288,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:customer]).to_not be_empty + expect(validator.errors[:customer]).not_to be_empty end end @@ -313,7 +313,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:schedule]).to_not be_empty + expect(validator.errors[:schedule]).not_to be_empty end end @@ -325,7 +325,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:schedule]).to_not be_empty + expect(validator.errors[:schedule]).not_to be_empty end end @@ -371,7 +371,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -383,7 +383,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -395,7 +395,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).to_not be_empty + expect(validator.errors[:payment_method]).not_to be_empty end end @@ -428,7 +428,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).to_not be_empty + expect(validator.errors[:subscription_line_items]).not_to be_empty end end @@ -441,7 +441,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).to_not be_empty + expect(validator.errors[:subscription_line_items]).not_to be_empty end end @@ -468,7 +468,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).to_not be_empty + expect(validator.errors[:subscription_line_items]).not_to be_empty end end @@ -480,7 +480,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).to_not be_empty + expect(validator.errors[:subscription_line_items]).not_to be_empty end end end @@ -538,7 +538,7 @@ module OrderManagement it "adds an error and returns false" do expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).to_not be_empty + expect(validator.errors[:subscription_line_items]).not_to be_empty end end diff --git a/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb index 0e00df50df..dc51f14f8e 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb @@ -66,7 +66,7 @@ module OrderManagement } it "is not eligible" do - expect(described_class.eligible_variants(shop)).to_not include(variant) + expect(described_class.eligible_variants(shop)).not_to include(variant) end end @@ -106,7 +106,7 @@ module OrderManagement context "if the variant is unrelated" do it "is not eligible" do - expect(described_class.eligible_variants(shop)).to_not include(variant) + expect(described_class.eligible_variants(shop)).not_to include(variant) end end end @@ -154,7 +154,7 @@ module OrderManagement context "if the variant is unrelated" do it "is false" do - expect(described_class).to_not be_in_open_and_upcoming_order_cycles(shop, + expect(described_class).not_to be_in_open_and_upcoming_order_cycles(shop, schedule, variant) end diff --git a/engines/web/app/views/web/angular_templates/cookies_banner.html.haml b/engines/web/app/views/web/angular_templates/cookies_banner.html.haml index 2343051cd7..04b4b4ba20 100644 --- a/engines/web/app/views/web/angular_templates/cookies_banner.html.haml +++ b/engines/web/app/views/web/angular_templates/cookies_banner.html.haml @@ -11,5 +11,5 @@ {{ 'legal.cookies_banner.cookies_policy_link' | t}} .large-3.columns - %button{ng: { controller:"CookiesBannerCtrl", click: "acceptCookies()" }} + %button{ "ng-controller": "CookiesBannerCtrl", "ng-click": "acceptCookies()" } {{ 'legal.cookies_banner.cookies_accept_button' | t}} diff --git a/engines/web/app/views/web/angular_templates/cookies_policy.html.haml b/engines/web/app/views/web/angular_templates/cookies_policy.html.haml index c53fa93bed..ab52054b24 100644 --- a/engines/web/app/views/web/angular_templates/cookies_policy.html.haml +++ b/engines/web/app/views/web/angular_templates/cookies_policy.html.haml @@ -12,7 +12,7 @@ %p {{ 'legal.cookies_policy.essential_cookies_desc' | t }} -%table{ng: { controller:"CookiesPolicyModalCtrl"}} +%table{ "ng-controller": "CookiesPolicyModalCtrl" } = render_cookie_entry( "_ofn_session_id", "legal.cookies_policy.cookie_session_desc" ) = render_cookie_entry( "cookies_consent", "legal.cookies_policy.cookie_consent_desc" ) = render_cookie_entry( "remember_spree_user_token", "legal.cookies_policy.cookie_remember_me_desc" ) @@ -52,7 +52,7 @@ %p {{ 'legal.cookies_policy.statistics_cookies_matomo_desc_html' | t }} - %table{ng: { controller:"CookiesPolicyModalCtrl"}} + %table{ "ng-controller": "CookiesPolicyModalCtrl" } = render_cookie_entry( "_pk_ref, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_basics_desc" ) = render_cookie_entry( "_pk_hsr, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_heatmap_desc" ) = render_cookie_entry( "piwik_ignore, _pk_cvar, _pk_id and _pk_ses", "legal.cookies_policy.cookie_matomo_ignore_desc" ) diff --git a/engines/web/spec/helpers/cookies_policy_helper_spec.rb b/engines/web/spec/helpers/cookies_policy_helper_spec.rb index 6807786a36..02ff9910a0 100644 --- a/engines/web/spec/helpers/cookies_policy_helper_spec.rb +++ b/engines/web/spec/helpers/cookies_policy_helper_spec.rb @@ -24,13 +24,13 @@ module Web end scenario "is not equal to the matomo URL" do - expect(helper.matomo_iframe_src).to_not eq Spree::Config.matomo_url + expect(helper.matomo_iframe_src).not_to eq Spree::Config.matomo_url end end scenario "is not nil, when matomo url is nil" do Spree::Config.matomo_url = nil - expect(helper.matomo_iframe_src).to_not eq nil + expect(helper.matomo_iframe_src).not_to eq nil end end diff --git a/lib/active_merchant/billing/gateways/stripe_payment_intents_decorator.rb b/lib/active_merchant/billing/gateways/stripe_payment_intents_decorator.rb deleted file mode 100644 index 711589f0cc..0000000000 --- a/lib/active_merchant/billing/gateways/stripe_payment_intents_decorator.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -ActiveMerchant::Billing::StripePaymentIntentsGateway.class_eval do - CREATE_INTENT_ATTRIBUTES = - %i[description statement_descriptor receipt_email save_payment_method].freeze - - def create_intent(money, payment_method, options = {}) - post = {} - add_amount(post, money, options, true) - add_capture_method(post, options) - add_confirmation_method(post, options) - add_customer(post, options) - add_payment_method_token(post, payment_method, options) - add_metadata(post, options) - add_return_url(post, options) - add_connected_account(post, options) - add_shipping_address(post, options) - setup_future_usage(post, options) - - CREATE_INTENT_ATTRIBUTES.each do |attribute| - add_whitelisted_attribute(post, options, attribute) - end - - commit(:post, 'payment_intents', post, options) - end - - def capture(money, intent_id, options = {}) - post = {} - post[:amount_to_capture] = money - add_connected_account(post, options) - commit(:post, "payment_intents/#{intent_id}/capture", post, options) - end - - def refund(money, intent_id, options = {}) - intent = commit(:get, "payment_intents/#{intent_id}", nil, options) - charge_id = intent.params.dig('charges', 'data')[0]['id'] - super(money, charge_id, options) - end - - # Note: Not all payment methods are currently supported by the - # {Payment Methods API}[https://stripe.com/docs/payments/payment-methods] - # Current implementation will create - # a PaymentMethod object if the method is a token or credit card - # All other types will default to legacy Stripe store - def store(payment_method, options = {}) - params = {} - post = {} - - # If customer option is provided, create a payment method and attach to customer id - # Otherwise, create a customer, then attach - # if payment_method.is_a?(StripePaymentToken) || - # payment_method.is_a?(ActiveMerchant::Billing::CreditCard) - add_payment_method_token(params, payment_method, options) - if options[:customer] - customer_id = options[:customer] - else - post[:validate] = options[:validate] unless options[:validate].nil? - post[:description] = options[:description] if options[:description] - post[:email] = options[:email] if options[:email] - customer = commit(:post, 'customers', post, options) - customer_id = customer.params['id'] - - # return the stripe response if expected customer id is not present - return customer if customer_id.nil? - end - commit(:post, - "payment_methods/#{params[:payment_method]}/attach", - { customer: customer_id }, options) - # else - # super(payment, options) - # end - end - - private - - def add_connected_account(post, options = {}) - return unless transfer_data = options[:transfer_data] - - post[:transfer_data] = {} - if transfer_data[:destination] - post[:transfer_data][:destination] = transfer_data[:destination] - end - post[:transfer_data][:amount] = transfer_data[:amount] if transfer_data[:amount] - post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of] - post[:transfer_group] = options[:transfer_group] if options[:transfer_group] - post[:application_fee_amount] = options[:application_fee] if options[:application_fee] - post - end -end diff --git a/lib/haml_up.rb b/lib/haml_up.rb new file mode 100644 index 0000000000..2ecc619960 --- /dev/null +++ b/lib/haml_up.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +# Upgrade HAML attribute syntax to prepare for HAML 6. +# +# HAML 6 stopped supporting nested hash attributes other than `data` and `aria`. +# We used to be able to write: +# +# %div{ ng: { class: "upper", bind: "model" } } +# +# This needs to be written in a flat structure now: +# +# %div{ "ng-class" => "upper", "ng-bind" => "model" } +# +require "fileutils" +require "haml" + +class HamlUp + def upgrade_file(filename) + template = File.read(filename) + rewrite_template(template) + File.write(filename, template) + end + + def rewrite_template(template) + haml_attributes(template).compact.each do |attributes| + rewrite_attributes(template, attributes) + end + end + + def rewrite_attributes(template, original) + attributes = parse_attributes(original) + + if attributes.nil? # parser failed + puts "Warning: failed to parse:\n" # rubocop:disable Rails/Output + puts original # rubocop:disable Rails/Output + return + end + + parse_deprecated_hashes(attributes) + + to_transform = attributes.select { |_k, v| v.is_a? Hash } + + return if to_transform.empty? + + to_transform.each do |key, hash| + add_full_keys(attributes, key, hash) + attributes.delete(key) + end + + replace_attributes(template, original, attributes) + end + + def haml_attributes(template) + options = Haml::Options.new + parsed_tree = Haml::Parser.new(options).call(template) + elements = flatten_tree(parsed_tree) + elements.map { |e| e.value[:dynamic_attributes]&.old } + end + + def flatten_tree(parent) + parent.children.flat_map do |child| + [child, *flatten_tree(child)] + end + end + + def parse_attributes(string) + Haml::AttributeParser.parse(string) + end + + def parse_deprecated_hashes(hash) + hash.each do |key, value| + next if ["aria", "data"].include?(key) + + parsed = parse_attributes(value) + next unless parsed.is_a? Hash + + parse_deprecated_hashes(parsed) + hash[key] = parsed + end + end + + def add_full_keys(attributes, key, hash) + hash.each do |subkey, value| + full_key = "#{key}-#{subkey}" + if value.is_a? Hash + add_full_keys(attributes, full_key, value) + else + attributes[full_key] = value + end + end + end + + def replace_attributes(template, original, attributes) + parsed_lines = original.split("\n") + lines_as_regex = parsed_lines.map(&Regexp.method(:escape)) + pattern = lines_as_regex.join("\n\s*") + + template.gsub!(/#{pattern}/, stringify(attributes)) + end + + def stringify(hash) + entries = hash.map do |key, value| + value = stringify(value) if value.is_a? Hash + + # We prefer the Ruby 1.9 hash syntax with symbols followed by a colon + # like this: + # + # %button{ disabled: true, "ng-class": "primary-button" } + # + # Symbols start with `:` which we slice off. It gets appended below. + key = key.to_sym.inspect.slice(1..-1) + + "#{key}: #{value}" + end + + "{ #{entries.join(', ')} }" + end +end diff --git a/lib/open_food_network/feature_toggle.rb b/lib/open_food_network/feature_toggle.rb index f1a075ef8f..6ee7a1fe5e 100644 --- a/lib/open_food_network/feature_toggle.rb +++ b/lib/open_food_network/feature_toggle.rb @@ -32,31 +32,24 @@ module OpenFoodNetwork "api_v1" => <<~DESC, Enable the new API at /api/v1 DESC - "background_reports" => <<~DESC, - Generate reports in a background process to limit memory consumption. - DESC "match_shipping_categories" => <<~DESC, During checkout, show only shipping methods that support all shipping categories. Activating this feature for an enterprise owner will activate it for all shops of this enterprise. DESC - "vouchers" => <<~DESC, - Add voucher functionality. Voucher can be managed via Enterprise settings. - This is activated per enterprise. Enter actors as Enterprise;1234. - DESC "invoices" => <<~DESC, Preserve the state of generated invoices and enable multiple invoice numbers instead of only one live-updating invoice. DESC + "connected_apps" => <<~DESC, + Enterprise data can be shared with another app. + The first example is the Australian Discover Regenerative Portal. + DESC }.freeze # Features you would like to be enabled to start with. - # - # Copy features here that were activated in a migration so that new - # instances, development and test environments have the feature active. ACTIVE_BY_DEFAULT = { - "background_reports" => <<~DESC, - Generate reports in a background process to limit memory consumption. - DESC + # Copy features here that were activated in a migration so that new + # instances, development and test environments have the feature active. }.freeze def self.setup! diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index 7568ff970e..a20a6292e2 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -105,9 +105,9 @@ module OpenFoodNetwork where("spree_products.id IN (?)", product_ids).pluck(:id).uniq end - ids = managed_permitted_ids | hubs_permitted_ids | hubs_permitting_ids \ - | producers_permitted_ids | producers_permitting_ids | managed_active_ids \ - | hubs_active_ids | producers_active_ids + ids = managed_permitted_ids | hubs_permitted_ids | hubs_permitting_ids | + producers_permitted_ids | producers_permitting_ids | + managed_active_ids | hubs_active_ids | producers_active_ids Enterprise.where(id: ids) end diff --git a/lib/reporting/line_items.rb b/lib/reporting/line_items.rb index a949e02dce..233c965c2a 100644 --- a/lib/reporting/line_items.rb +++ b/lib/reporting/line_items.rb @@ -7,7 +7,7 @@ module Reporting @order_permissions = order_permissions @params = params complete_not_canceled_visible_orders = - CompleteVisibleOrders.new(order_permissions).query.not_state(:canceled) + CompleteVisibleOrdersQuery.new(order_permissions).call.not_state(:canceled) @orders_relation = orders_relation || complete_not_canceled_visible_orders end @@ -36,7 +36,7 @@ module Reporting without_editable_line_items = line_items - editable_line_items(line_items) without_editable_line_items.each do |line_item| - OrderDataMasker.new(line_item.order).call + Orders::MaskDataService.new(line_item.order).call end line_items diff --git a/lib/reporting/queries/query_builder.rb b/lib/reporting/queries/query_builder.rb index 0f20f77c19..02e908ab2d 100644 --- a/lib/reporting/queries/query_builder.rb +++ b/lib/reporting/queries/query_builder.rb @@ -11,7 +11,7 @@ module Reporting def initialize(model, grouping_fields = proc { [] }) @grouping_fields = instance_exec(&grouping_fields) - super model.arel_table + super(model.arel_table) end def selecting(lambda) @@ -68,7 +68,9 @@ module Reporting options_text = variant_table[:unit_presentation] unit_to_display = coalesce(nullify_empty_strings(display_as), options_text) + # rubocop:disable Rails/OutputSafety combined_description = sql_concat(display_name, raw("' ('"), unit_to_display, raw("')'")) + # rubocop:enable Rails/OutputSafety Case.new. when(nullify_empty_strings(display_name).eq(nil)).then(unit_to_display). @@ -79,7 +81,8 @@ module Reporting private def default_mask_rule - line_item_table[:order_id].in(raw("#{managed_orders_alias.name}.id")). + id = raw("#{managed_orders_alias.name}.id") # rubocop:disable Rails/OutputSafety + line_item_table[:order_id].in(id). or(distributor_alias[:show_customer_names_to_suppliers].eq(true)) end diff --git a/lib/reporting/queries/query_interface.rb b/lib/reporting/queries/query_interface.rb index 54d818cc9c..c6e7b8a041 100644 --- a/lib/reporting/queries/query_interface.rb +++ b/lib/reporting/queries/query_interface.rb @@ -86,7 +86,7 @@ module Reporting end def empty_string - raw("''") + raw("''") # rubocop:disable Rails/OutputSafety end def sql_concat(*args) diff --git a/lib/reporting/readme.md b/lib/reporting/readme.md new file mode 100644 index 0000000000..c7f823b1f5 --- /dev/null +++ b/lib/reporting/readme.md @@ -0,0 +1,9 @@ +# Reports framework + +A framework that handles querying and rendering tabular data. + +TODO: add more details on how each part works. + +## Rules +Rules are used for grouping, ordering, and summary rows. Options are documented at [`Reporting::ReportTemplate#rules`]( +https://github.com/openfoodfoundation/openfoodnetwork/blob/master/lib/reporting/report_template.rb#L68-L95). diff --git a/lib/reporting/reports/bulk_coop/base.rb b/lib/reporting/reports/bulk_coop/base.rb index bf48a6a9d8..5bb079d923 100644 --- a/lib/reporting/reports/bulk_coop/base.rb +++ b/lib/reporting/reports/bulk_coop/base.rb @@ -35,7 +35,7 @@ module Reporting @report_line_items ||= Reporting::LineItems.new( order_permissions, @params, - CompleteVisibleOrders.new(order_permissions).query + CompleteVisibleOrdersQuery.new(order_permissions).call ) end diff --git a/lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_order.rb b/lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_order.rb index a502d34d96..e035a2d469 100644 --- a/lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_order.rb +++ b/lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_order.rb @@ -158,7 +158,8 @@ module Reporting end def enterprise_fees_sum(order) - enterprise_fees(order).sum(:amount) + amount = enterprise_fees(order).sum(:amount) + apply_voucher_on_amount(order, amount) end def enterprise_fees(order) @@ -181,7 +182,8 @@ module Reporting query = order.all_adjustments.tax query = query.inclusive if included == true query = query.additional if added == true - query.where(adjustable: enterprise_fees(order)).sum(:amount) + amount = query.where(adjustable: enterprise_fees(order)).sum(:amount) + apply_voucher_on_amount(order, amount) end def distributor(query_result_row) @@ -221,23 +223,25 @@ module Reporting end def total_excl_tax(query_result_row) + order = order(query_result_row) amount = Spree::Adjustment.enterprise_fee - .where(order: order(query_result_row)) + .where(order:) .where(originator_id: enterprise_fee_id(query_result_row)) .pick("sum(amount)") || 0 - amount - tax(query_result_row, all: true, included: true) + apply_voucher_on_amount(order, amount) - tax(query_result_row, all: true, included: true) end def tax(query_result_row, all: false, included: nil) - order_id = order(query_result_row).id + order = order(query_result_row) adjustment_ids = enterprise_fee_adjustment_ids(query_result_row) query = Spree::Adjustment.tax query = query.where(included: true) unless included.nil? query = query.where(originator_id: tax_rate_id(query_result_row)) unless all == true - query.where(order_id:) + tax_amount = query.where(order:) .where(adjustable_type: 'Spree::Adjustment') .where(adjustable_id: adjustment_ids) .pick("sum(amount)") || 0 + apply_voucher_on_amount(order, tax_amount) end def total_incl_tax(query_result_row) @@ -299,6 +303,12 @@ module Reporting def keys(query_result_row) query_result_row.first end + + def apply_voucher_on_amount(order, amount) + rate = order.applied_voucher_rate + result = amount + (amount * rate) + BigDecimal(result.to_s).round(2, BigDecimal::ROUND_HALF_UP) + end end end end diff --git a/lib/reporting/reports/order_cycle_management/base.rb b/lib/reporting/reports/order_cycle_management/base.rb index e20e13ab25..d62cd3de33 100644 --- a/lib/reporting/reports/order_cycle_management/base.rb +++ b/lib/reporting/reports/order_cycle_management/base.rb @@ -29,7 +29,7 @@ module Reporting def orders search_result = search.result.order(:completed_at) - orders = OutstandingBalance.new(search_result).query.select('spree_orders.*') + orders = OutstandingBalanceQuery.new(search_result).call.select('spree_orders.*') filter(orders) end diff --git a/lib/reporting/reports/orders_and_distributors/base.rb b/lib/reporting/reports/orders_and_distributors/base.rb index 9225096680..2f54235d5e 100644 --- a/lib/reporting/reports/orders_and_distributors/base.rb +++ b/lib/reporting/reports/orders_and_distributors/base.rb @@ -43,7 +43,7 @@ module Reporting editable_orders_ids = permissions.editable_orders.select(&:id).map(&:id) orders .filter { |order| order.in?(editable_orders_ids) } - .each { |order| OrderDataMasker.new(order).call } + .each { |order| Orders::MaskDataService.new(order).call } # Get Line Items orders.map(&:line_items).flatten end diff --git a/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb b/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb index caf65c8165..8a538dfe23 100644 --- a/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb +++ b/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb @@ -29,6 +29,8 @@ module Reporting admin_handling_fees: proc { |_line_items| "" }, ship_price: proc { |_line_items| "" }, pay_fee_price: proc { |_line_items| "" }, + voucher_label: proc { |_line_items| "" }, + voucher_amount: proc { |_line_items| "" }, total_price: proc { |_line_items| "" }, paid: proc { |line_items| line_items.all? { |li| li.order.paid? } }, @@ -105,7 +107,7 @@ module Reporting def default_params super.merge( { - fields_to_hide: [:final_weight_volume] + fields_to_hide: %i[final_weight_volume voucher_label voucher_amount] } ) end @@ -129,6 +131,8 @@ module Reporting admin_handling_fees: order.admin_and_handling_total, ship_price: order.ship_total, pay_fee_price: order.payment_fee, + voucher_label: voucher_label(order), + voucher_amount: voucher_amount(order), total_price: order.total, paid: order.paid?, comments: order.special_instructions, @@ -163,6 +167,23 @@ module Reporting user = line_items.first.order.user user&.customer_of(distributor) end + + def voucher_label(order) + return '' unless voucher_applicable?(order) + + voucher = order.voucher_adjustments.take.originator + voucher.code.to_s + end + + def voucher_amount(order) + return '' unless voucher_applicable?(order) + + order.pre_discount_total - order.total + end + + def voucher_applicable?(order) + order.voucher_adjustments.present? + end end end end diff --git a/lib/reporting/reports/revenues_by_hub/base.rb b/lib/reporting/reports/revenues_by_hub/base.rb index 5fb5de67c3..e539c77fed 100644 --- a/lib/reporting/reports/revenues_by_hub/base.rb +++ b/lib/reporting/reports/revenues_by_hub/base.rb @@ -19,7 +19,7 @@ module Reporting } end - def columns # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity + def columns # rubocop:disable Metrics/AbcSize { hub: proc { |orders| distributor(orders).name }, hub_id: proc { |orders| distributor(orders).id }, @@ -35,16 +35,16 @@ module Reporting hub_address_zipcode: proc { |orders| distributor(orders).address&.zipcode }, hub_address_state_name: proc { |orders| distributor(orders).address&.state_name }, total_orders: proc { |orders| orders.count }, - total_excl_tax: proc { |orders| - orders.sum { |order| order.total - order.total_tax } - }, - total_tax: proc { |orders| orders.sum(&:total_tax) }, - total_incl_tax: proc { |orders| orders.sum(&:total) } + total_excl_tax: :total_excl_tax, + total_tax: :total_tax, + total_incl_tax: :total_incl_tax } end def query_result - search.result.group_by(&:distributor).values + result = search.result.group_by(&:distributor).values + build_tax_data(result) + result end private @@ -52,6 +52,46 @@ module Reporting def distributor(orders) orders.first.distributor end + + def build_tax_data(grouped_orders) + @tax_data = {} + + grouped_orders.each do |orders| + voucher_adjustments = calculate_voucher_adjustments(orders) + + total_incl_tax = orders.sum(&:total) + total_tax = orders.sum(&:total_tax) + voucher_adjustments + total_excl_tax = total_incl_tax - total_tax + + @tax_data[distributor(orders).id] = { + total_incl_tax:, total_tax:, total_excl_tax: + } + end + end + + def calculate_voucher_adjustments(orders) + result = 0.0 + + orders.each do |order| + adjustment_service = VoucherAdjustmentsService.new(order) + result += adjustment_service.voucher_included_tax + + adjustment_service.voucher_excluded_tax + end + + result + end + + def total_incl_tax(orders) + @tax_data[distributor(orders).id][:total_incl_tax] + end + + def total_tax(orders) + @tax_data[distributor(orders).id][:total_tax] + end + + def total_excl_tax(orders) + @tax_data[distributor(orders).id][:total_excl_tax] + end end end end diff --git a/lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb b/lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb index 0271b38d59..a2c00ea082 100644 --- a/lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb +++ b/lib/reporting/reports/sales_tax/sales_tax_totals_by_producer.rb @@ -24,9 +24,9 @@ module Reporting # [tax_rate, supplier_id, distributor_id and order_cycle_id] report_line_items.list .flat_map do |line_item| - line_item.tax_rates.map do |tax_rate| + line_item.adjustments.eligible.tax.map do |tax_rate| { - tax_rate_id: tax_rate.id, + tax_rate_id: tax_rate.originator_id, line_item: } end diff --git a/lib/reporting/reports/sales_tax/tax_rates.rb b/lib/reporting/reports/sales_tax/tax_rates.rb index 83bd3f2eb0..8c82d44b66 100644 --- a/lib/reporting/reports/sales_tax/tax_rates.rb +++ b/lib/reporting/reports/sales_tax/tax_rates.rb @@ -11,7 +11,7 @@ module Reporting total_excl_vat: proc { |order| order.total - order.total_tax } } add_key_for_each_rate(result, proc { |rate| - proc { |order| OrderTaxAdjustmentsFetcher.new(order).totals.fetch(rate, 0) } + proc { |order| Orders::FetchTaxAdjustmentsService.new(order).totals.fetch(rate, 0) } }) other = { total_tax: proc { |order| order.total_tax }, diff --git a/lib/spree/core/environment_extension.rb b/lib/spree/core/environment_extension.rb index 674ed9dbf2..56d54517ea 100644 --- a/lib/spree/core/environment_extension.rb +++ b/lib/spree/core/environment_extension.rb @@ -8,7 +8,7 @@ module Spree def add_class(name) instance_variable_set "@#{name}", Set.new - create_method( "#{name}=".to_sym ) { |val| + create_method( :"#{name}=" ) { |val| instance_variable_set( "@" + name, val) } diff --git a/lib/spree/money.rb b/lib/spree/money.rb index 968f349b00..4a11d33497 100644 --- a/lib/spree/money.rb +++ b/lib/spree/money.rb @@ -9,7 +9,7 @@ module Spree delegate :cents, to: :money def initialize(amount, options = {}) - @money = ::Monetize.parse([amount, (options[:currency] || Spree::Config[:currency])].join) + @money = ::Monetize.parse([amount, options[:currency] || Spree::Config[:currency]].join) if options.key?(:symbol_position) options[:format] = position_to_format(options.delete(:symbol_position)) @@ -29,7 +29,7 @@ module Spree def to_html(options = { html_wrap: true }) "#{@money.format(@options.merge(options))}" - .html_safe + .html_safe # rubocop:disable Rails/OutputSafety end def format(options = {}) diff --git a/lib/stripe/profile_storer.rb b/lib/stripe/profile_storer.rb index 688bb4f9e8..ba3cc7b0aa 100644 --- a/lib/stripe/profile_storer.rb +++ b/lib/stripe/profile_storer.rb @@ -12,7 +12,9 @@ module Stripe end def create_customer_from_token - token = @payment.source.gateway_payment_profile_id + token = ActiveMerchant::Billing::StripeGateway::StripePaymentToken.new( + { 'id' => @payment.source.gateway_payment_profile_id } + ) response = @provider.store(token, options) if response.success? diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake index da55b3580e..1cb18ff0db 100644 --- a/lib/tasks/data.rake +++ b/lib/tasks/data.rake @@ -7,7 +7,7 @@ namespace :ofn do input = request_months # For each order cycle which was modified within the past 3 months - OrderCycle.where('updated_at > ?', Date.current - input.months).each do |order_cycle| + OrderCycle.where('updated_at > ?', Date.current - input.months).find_each do |order_cycle| # Cycle through the incoming exchanges order_cycle.exchanges.incoming.each do |exchange| next if exchange.sender == exchange.receiver diff --git a/lib/tasks/reset.rake b/lib/tasks/reset.rake new file mode 100644 index 0000000000..35e5819aa6 --- /dev/null +++ b/lib/tasks/reset.rake @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'sidekiq/api' + +namespace :ofn do + task reset: :environment do + Rake::Task["ofn:reset_sidekiq"].invoke + Rake::Task["db:reset"].invoke + end + + task reset_sidekiq: :environment do + # Clear retry set + Sidekiq::RetrySet.new.clear + + # Clear scheduled jobs + Sidekiq::ScheduledSet.new.clear + + # Clear 'Dead' jobs statistics + Sidekiq::DeadSet.new.clear + + # Clear 'Processed' and 'Failed' jobs statistics + Sidekiq::Stats.new.reset + + # Clear all queues + Sidekiq::Queue.all.map(&:clear) + end +end diff --git a/lib/tasks/sample_data/order_factory.rb b/lib/tasks/sample_data/order_factory.rb index 39cfdb2996..ef670aa5e0 100644 --- a/lib/tasks/sample_data/order_factory.rb +++ b/lib/tasks/sample_data/order_factory.rb @@ -51,7 +51,7 @@ module SampleData def create_complete_order order = create_cart_order - OrderWorkflow.new(order).complete + Orders::WorkflowService.new(order).complete order end diff --git a/lib/tasks/sample_data/user_factory.rb b/lib/tasks/sample_data/user_factory.rb index 445c3fd402..bb5268e2b3 100644 --- a/lib/tasks/sample_data/user_factory.rb +++ b/lib/tasks/sample_data/user_factory.rb @@ -30,7 +30,7 @@ module SampleData def create_user(name) email = "#{name.downcase.tr(' ', '.')}@example.org" - password = Spree::User.friendly_token + password = "ofn123" log "- #{email}" user = Spree::User.create_with( password:, diff --git a/lib/tasks/subscriptions/debug.rake b/lib/tasks/subscriptions/debug.rake index 3b2e29d90a..750bfb4d73 100644 --- a/lib/tasks/subscriptions/debug.rake +++ b/lib/tasks/subscriptions/debug.rake @@ -12,7 +12,7 @@ namespace :ofn do puts "Order Cycle #{order_cycle.name}" order_cycle.schedules.each do |schedule| puts "Schedule #{schedule.name}" - Subscription.where(schedule_id: schedule.id).each do |subscription| + Subscription.where(schedule_id: schedule.id).find_each do |subscription| puts puts "Subscription #{subscription.id}" puts subscription.shop.name @@ -23,7 +23,7 @@ namespace :ofn do puts "Canceled at #{subscription.canceled_at} and paused at #{subscription.paused_at}" ProxyOrder.where(order_cycle_id:, - subscription_id: subscription.id).each do |proxy_order| + subscription_id: subscription.id).find_each do |proxy_order| puts puts "Proxy Order #{proxy_order.id}" puts "Canceled at #{proxy_order.canceled_at}" @@ -42,7 +42,7 @@ namespace :ofn do puts "Source #{payment.source.to_json}" end Spree::LogEntry.where(source_type: "Spree::Payment", - source_id: payment.id).each do |log_entry| + source_id: payment.id).find_each do |log_entry| puts "Log Entries found" puts log_entry.details end diff --git a/package.json b/package.json index fba50143e5..d9bbb8076b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ }, "license": "AGPL-3.0", "scripts": { - "prepare": "husky install", "pretty-quick": "pretty-quick" }, "jest": { @@ -19,36 +18,37 @@ ] }, "dependencies": { - "@floating-ui/dom": "^1.5.3", - "@hotwired/turbo": "^7.3.0", + "@floating-ui/dom": "^1.6.3", + "@hotwired/turbo": "^8.0.4", "@rails/webpacker": "5.4.4", - "cable_ready": "5.0.2", + "cable_ready": "5.0.1", "debounced": "^0.0.5", "flatpickr": "^4.6.9", "foundation-sites": "^5.5.3", + "hotkeys-js": "^3.13.7", "jquery-ui": "1.13.2", - "js-big-decimal": "^2.0.4", - "moment": "^2.29.1", + "js-big-decimal": "^2.0.7", + "moment": "^2.30.1", "mrujs": "^1.0.0", "select2": "^4.0.13", "shortcut-buttons-flatpickr": "^0.4.0", "stimulus": "^3.2.2", "stimulus-flatpickr": "^1.4.0", + "stimulus-rails-nested-form": "https://github.com/openfoodfoundation/stimulus-rails-nested-form.git#dist", "stimulus_reflex": "3.5.0-rc3", - "tom-select": "^2.2.3", - "trix": "^2.0.8", + "tom-select": "^2.3.1", + "trix": "^2.0.10", "webpack": "~4" }, "devDependencies": { - "husky": "^8.0.0", - "jasmine-core": "~5.1.1", + "jasmine-core": "~5.1.2", "jest": "^27.4.7", - "karma": "~6.4.2", + "karma": "~6.4.3", "karma-chrome-launcher": "~3.2.0", "karma-coffee-preprocessor": "~1.0.1", "karma-jasmine": "~0.3.8", - "prettier": "2.8.8", - "pretty-quick": "^3.1.3", + "prettier": "3.2.4", + "pretty-quick": "4.0.0", "webpack-dev-server": "~3" } } diff --git a/script/haml-up.rb b/script/haml-up.rb new file mode 100755 index 0000000000..033e09f348 --- /dev/null +++ b/script/haml-up.rb @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Upgrade HAML attribute syntax to prepare for HAML 6. +# +# HAML 6 stopped supporting nested hash attributes other than `data` and `aria`. +# We used to be able to write: +# +# %div{ ng: { class: "upper", bind: "model" } } +# +# This needs to be written in a flat structure now: +# +# %div{ "ng-class" => "upper", "ng-bind" => "model" } +# +# This script rewrites HAML files automatically. It may be used like: +# +# git ls-files '*.haml' | while read f; do ./haml-up.rb "$f"; done +# +require "haml_up" + +puts ARGV[0] +HamlUp.new.upgrade_file(ARGV[0]) diff --git a/script/nodenv-install.sh b/script/nodenv-install.sh index 37081b6d7d..b041111df5 100755 --- a/script/nodenv-install.sh +++ b/script/nodenv-install.sh @@ -5,6 +5,14 @@ # If our node-build version is outdated and it can't build the version we want # then we try upgrading node-build and installing again. +# Fail if a single command fails. +set -e + +if ! command -v nodenv > /dev/null; then + printf "Please install https://github.com/nodenv/nodenv.\n" + exit 1 +fi + if nodenv install --skip-existing; then echo "Correct Node version is installed." else diff --git a/script/release/update_locales b/script/release/update_locales index e872df46fc..702140f0f5 100755 --- a/script/release/update_locales +++ b/script/release/update_locales @@ -6,10 +6,10 @@ # Exit on error or uncommitted changes # TODO: check that master matches upstream/master set -e -#if [ ! -z "$(git status --porcelain)" ]; then -# echo "Aborted: git working directory is not clean." -# exit 1 -#fi +if [ ! -z "$(git status --porcelain -uno)" ]; then + echo "Aborted: git working directory is not clean." + exit 1 +fi echo "\n*** Checking out latest master... ***\n" git checkout master diff --git a/script/rubocop-diff.sh b/script/rubocop-diff.sh new file mode 100755 index 0000000000..32dbef1ca8 --- /dev/null +++ b/script/rubocop-diff.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# While you are developing, you can call this script to check all +# changed files. And then you can also tell Git to check before every +# commit by adding this line to your `.git/hooks/pre-commit` file: +# +# ./script/rubocop-diff.sh --cached || exit 1 +# + +rubocop="`dirname $0`/../bin/rubocop" +cached="$1" # may be empty + +if git diff $cached --diff-filter=ACMR HEAD --quiet; then + # nothing changed + exit 0 +fi + +exec git diff $cached --name-only --relative --diff-filter=ACMR HEAD |\ + xargs \ + $rubocop --force-exclusion \ + --fail-level A \ + --format simple \ + --parallel --cache true diff --git a/script/test-stripe-live b/script/test-stripe-live index 530bd8c5ad..b80cfacf26 100755 --- a/script/test-stripe-live +++ b/script/test-stripe-live @@ -1,6 +1,12 @@ #!/usr/bin/env sh # # Test Stripe API integration and record new cassettes. +# Requires account details in .env.test.local. You can copy from Bitwarden, or [set up a new Stripe account](https://github.com/openfoodfoundation/openfoodnetwork/wiki/Setting-up-Stripe-on-an-OFN-instance) + +set -e # Exit if any command fails git rm spec/fixtures/vcr_cassettes/Stripe-v* -r ./bin/rspec --tag stripe_version + +git add spec/fixtures/vcr_cassettes/Stripe-v* +git commit -m "Update Stripe API recordings for new version" diff --git a/spec/base_spec_helper.rb b/spec/base_spec_helper.rb index 49c2631095..c875249ab7 100644 --- a/spec/base_spec_helper.rb +++ b/spec/base_spec_helper.rb @@ -50,6 +50,14 @@ end FactoryBot.use_parent_strategy = false FactoryBot::SyntaxRunner.include FileHelper +# raise I18n exception handler +I18n.exception_handler = proc do |exception, *_| + raise exception.to_exception +end + +# Disable timestamp check for test environment +InvisibleCaptcha.timestamp_enabled = false + RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{Rails.root.join('spec/fixtures')}" @@ -152,11 +160,21 @@ RSpec.configure do |config| # config.around(:each, :stripe_version) do |example| stripe_version = "Stripe-v#{Stripe::VERSION}" + cassette_library_dir, default_cassette_options = nil, nil + VCR.configure do |vcr_config| - vcr_config.cassette_library_dir = "spec/fixtures/vcr_cassettes/#{stripe_version}" + cassette_library_dir = vcr_config.cassette_library_dir + default_cassette_options = vcr_config.default_cassette_options + vcr_config.cassette_library_dir += "/#{stripe_version}" vcr_config.default_cassette_options = { record: :none } if ENV["CI"] end + example.run + + VCR.configure do |vcr_config| + vcr_config.cassette_library_dir = cassette_library_dir + vcr_config.default_cassette_options = default_cassette_options + end end # Geocoding @@ -216,7 +234,6 @@ RSpec.configure do |config| config.include PreferencesHelper config.include OpenFoodNetwork::FiltersHelper config.include OpenFoodNetwork::EnterpriseGroupsHelper - config.include OpenFoodNetwork::ProductsHelper config.include OpenFoodNetwork::DistributionHelper config.include OpenFoodNetwork::HtmlHelper config.include ActionView::Helpers::DateHelper diff --git a/spec/controllers/admin/bulk_line_items_controller_spec.rb b/spec/controllers/admin/bulk_line_items_controller_spec.rb index 9fab630c8e..15e540c97b 100644 --- a/spec/controllers/admin/bulk_line_items_controller_spec.rb +++ b/spec/controllers/admin/bulk_line_items_controller_spec.rb @@ -390,7 +390,7 @@ describe Admin::BulkLineItemsController, type: :controller do order.shipments.map(&:refresh_rates) order.select_shipping_method(shipping_method.id) - OrderWorkflow.new(order).advance_to_payment + Orders::WorkflowService.new(order).advance_to_payment order.finalize! order.recreate_all_fees! order.create_tax_charge! diff --git a/spec/controllers/admin/customers_controller_spec.rb b/spec/controllers/admin/customers_controller_spec.rb index 527b1f0c36..f7397e58ab 100644 --- a/spec/controllers/admin/customers_controller_spec.rb +++ b/spec/controllers/admin/customers_controller_spec.rb @@ -44,12 +44,12 @@ module Admin get :index, params: end - it 'calls CustomersWithBalance' do - customers_with_balance = instance_double(CustomersWithBalance) - allow(CustomersWithBalance) + it 'calls CustomersWithBalanceQuery' do + customers_with_balance = instance_double(CustomersWithBalanceQuery) + allow(CustomersWithBalanceQuery) .to receive(:new).with(customers) { customers_with_balance } - expect(customers_with_balance).to receive(:query) { Customer.none } + expect(customers_with_balance).to receive(:call) { Customer.none } get :index, params: end @@ -182,7 +182,7 @@ module Admin customer: { email: 'new.email@gmail.com' } expect(response).to redirect_to unauthorized_path expect(assigns(:customer)).to eq nil - expect(customer.email).to_not eq 'new.email@gmail.com' + expect(customer.email).not_to eq 'new.email@gmail.com' end end end @@ -204,7 +204,7 @@ module Admin end it "allows me to create the customer" do - expect { create_customer enterprise }.to change(Customer, :count).by(1) + expect { create_customer enterprise }.to change { Customer.count }.by(1) expect(Customer.reorder(:id).last.created_manually).to eq true end end @@ -215,7 +215,7 @@ module Admin end it "prevents me from creating the customer" do - expect { create_customer enterprise }.to change(Customer, :count).by(0) + expect { create_customer enterprise }.to change { Customer.count }.by(0) end end @@ -225,7 +225,7 @@ module Admin end it "allows admins to create the customer" do - expect { create_customer enterprise }.to change(Customer, :count).by(1) + expect { create_customer enterprise }.to change { Customer.count }.by(1) end end end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index d2045aa9f1..9c033da706 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -168,7 +168,7 @@ describe Admin::EnterprisesController, type: :controller do spree_post :update, update_params distributor.reload - expect(distributor.users).to_not include user + expect(distributor.users).not_to include user end it "updates the contact for notifications" do @@ -190,7 +190,7 @@ describe Admin::EnterprisesController, type: :controller do } expect { spree_post :update, params }. - to_not change { distributor.contact } + not_to change { distributor.contact } end it "updates enterprise preferences" do @@ -223,7 +223,7 @@ describe Admin::EnterprisesController, type: :controller do expect(Spree::Property.count).to be 1 expect(ProducerProperty.count).to be 0 property_names = producer.reload.properties.map(&:name) - expect(property_names).to_not include 'a different name' + expect(property_names).not_to include 'a different name' end end @@ -721,7 +721,7 @@ describe Admin::EnterprisesController, type: :controller do it "scopes @collection to enterprises editable by the user" do get :index, format: :json expect(assigns(:collection)).to include enterprise1, enterprise2 - expect(assigns(:collection)).to_not include enterprise3 + expect(assigns(:collection)).not_to include enterprise3 end end end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index 864f81c112..fd154b7118 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -37,7 +37,7 @@ module Admin context "where ransack conditions are specified" do it "loads order cycles closed within past month, and orders w/o a close_at date" do get :index, as: :json - expect(assigns(:collection)).to_not include oc1, oc2 + expect(assigns(:collection)).not_to include oc1, oc2 expect(assigns(:collection)).to include oc3, oc4 end end @@ -47,7 +47,7 @@ module Admin it "loads order cycles closed after specified date, and orders w/o a close_at date" do get :index, as: :json, params: { q: } - expect(assigns(:collection)).to_not include oc1 + expect(assigns(:collection)).not_to include oc1 expect(assigns(:collection)).to include oc2, oc3, oc4 end @@ -56,7 +56,7 @@ module Admin it "loads order cycles that meet all conditions" do get :index, format: :json, params: { q: } - expect(assigns(:collection)).to_not include oc1, oc2, oc4 + expect(assigns(:collection)).not_to include oc1, oc2, oc4 expect(assigns(:collection)).to include oc3 end end @@ -132,9 +132,9 @@ module Admin end it do - query_counter = QueryCounter.new - get :show, params: { id: order_cycle.id }, as: :json - expect(query_counter.queries).to eq( + expect { + get :show, params: { id: order_cycle.id }, as: :json + }.to query_database( { select: { enterprise_fees: 3, @@ -151,7 +151,6 @@ module Admin update: { spree_users: 1 } } ) - query_counter.stop end end end @@ -161,12 +160,12 @@ module Admin let(:shop) { create(:distributor_enterprise) } context "as a manager of a shop" do - let(:form_mock) { instance_double(OrderCycleForm) } + let(:form_mock) { instance_double(OrderCycles::FormService) } let(:params) { { as: :json, order_cycle: {} } } before do controller_login_as_enterprise_user([shop]) - allow(OrderCycleForm).to receive(:new) { form_mock } + allow(OrderCycles::FormService).to receive(:new) { form_mock } end context "when creation is successful" do @@ -204,10 +203,10 @@ module Admin describe "update" do let(:order_cycle) { create(:simple_order_cycle) } let(:coordinator) { order_cycle.coordinator } - let(:form_mock) { instance_double(OrderCycleForm) } + let(:form_mock) { instance_double(OrderCycles::FormService) } before do - allow(OrderCycleForm).to receive(:new) { form_mock } + allow(OrderCycles::FormService).to receive(:new) { form_mock } end context "as a manager of the coordinator" do @@ -276,7 +275,7 @@ module Admin end it "can update preference product_selection_from_coordinator_inventory_only" do - expect(OrderCycleForm).to receive(:new). + expect(OrderCycles::FormService).to receive(:new). with(order_cycle, { "preferred_product_selection_from_coordinator_inventory_only" => true }, anything) { form_mock } @@ -289,7 +288,7 @@ module Admin end it "can update preference automatic_notifications" do - expect(OrderCycleForm).to receive(:new). + expect(OrderCycles::FormService).to receive(:new). with(order_cycle, { "automatic_notifications" => true }, anything) { form_mock } @@ -325,7 +324,7 @@ module Admin format: :json, id: order_cycle.id, order_cycle: allowed.merge(restricted) } } - let(:form_mock) { instance_double(OrderCycleForm, save: true) } + let(:form_mock) { instance_double(OrderCycles::FormService, save: true) } before { allow(controller).to receive(:spree_current_user) { user } } @@ -334,7 +333,7 @@ module Admin let(:expected) { [order_cycle, allowed.merge(restricted), user] } it "allows me to update exchange information for exchanges, name and dates" do - expect(OrderCycleForm).to receive(:new).with(*expected) { form_mock } + expect(OrderCycles::FormService).to receive(:new).with(*expected) { form_mock } spree_put :update, params end end @@ -344,7 +343,7 @@ module Admin let(:expected) { [order_cycle, allowed, user] } it "allows me to update exchange information for exchanges, but not name or dates" do - expect(OrderCycleForm).to receive(:new).with(*expected) { form_mock } + expect(OrderCycles::FormService).to receive(:new).with(*expected) { form_mock } spree_put :update, params end end @@ -377,7 +376,7 @@ module Admin it "does nothing when no data is supplied" do expect do spree_put :bulk_update, format: :json - end.to change(oc, :orders_open_at).by(0) + end.to change { oc.orders_open_at }.by(0) json_response = JSON.parse(response.body) expect(json_response['errors']) .to eq 'Hm, something went wrong. No order cycle data found.' @@ -416,9 +415,9 @@ module Admin } } } oc.reload - expect(oc.name).to_not eq "Updated Order Cycle" - expect(oc.orders_open_at.to_date).to_not eq Date.current - 21.days - expect(oc.orders_close_at.to_date).to_not eq Date.current + 21.days + expect(oc.name).not_to eq "Updated Order Cycle" + expect(oc.orders_open_at.to_date).not_to eq Date.current - 21.days + expect(oc.orders_close_at.to_date).not_to eq Date.current + 21.days end end end @@ -506,8 +505,8 @@ module Admin get :destroy, params: { id: cloned.id } expect(OrderCycle.find_by(id: cloned.id)).to be nil - expect(OrderCycle.find_by(id: oc.id)).to_not be nil - expect(EnterpriseFee.find_by(id: enterprise_fee1.id)).to_not be nil + expect(OrderCycle.find_by(id: oc.id)).not_to be nil + expect(EnterpriseFee.find_by(id: enterprise_fee1.id)).not_to be nil expect(response).to redirect_to admin_order_cycles_path end end diff --git a/spec/controllers/admin/proxy_orders_controller_spec.rb b/spec/controllers/admin/proxy_orders_controller_spec.rb index a19f94a14a..6e505530b6 100644 --- a/spec/controllers/admin/proxy_orders_controller_spec.rb +++ b/spec/controllers/admin/proxy_orders_controller_spec.rb @@ -83,7 +83,7 @@ describe Admin::ProxyOrdersController, type: :controller do before do # Processing order to completion allow(Spree::OrderMailer).to receive(:cancel_email) { double(:email, deliver_later: true) } - OrderWorkflow.new(order).complete! + Orders::WorkflowService.new(order).complete! proxy_order.reload proxy_order.cancel allow(controller).to receive(:spree_current_user) { user } diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 32a44c1b88..318682a8e8 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -162,7 +162,7 @@ describe Admin::ReportsController, type: :controller do report_types = assigns(:reports).keys expect(report_types).to include :orders_and_fulfillment, :products_and_inventory, :packing # and others - expect(report_types).to_not include :sales_tax + expect(report_types).not_to include :sales_tax end end @@ -365,7 +365,7 @@ describe Admin::ReportsController, type: :controller do spree_get :show, report_type: :sales_tax, report_subtype: report_type expect(response).to have_http_status(:ok) expect(resulting_orders_prelim).to include(orderA1, orderB1) - expect(resulting_orders_prelim).to_not include(orderA2, orderB2) + expect(resulting_orders_prelim).not_to include(orderA2, orderB2) end end end diff --git a/spec/controllers/admin/schedules_controller_spec.rb b/spec/controllers/admin/schedules_controller_spec.rb index ee7967287b..38b1f12e35 100644 --- a/spec/controllers/admin/schedules_controller_spec.rb +++ b/spec/controllers/admin/schedules_controller_spec.rb @@ -115,7 +115,7 @@ describe Admin::SchedulesController, type: :controller do uncoordinated_order_cycle, uncoordinated_order_cycle3 # coordinated_order_cycle is removed, uncoordinated_order_cycle2 is NOT added - expect(coordinated_schedule.reload.order_cycles).to_not include coordinated_order_cycle, + expect(coordinated_schedule.reload.order_cycles).not_to include coordinated_order_cycle, uncoordinated_order_cycle2 end @@ -145,7 +145,7 @@ describe Admin::SchedulesController, type: :controller do schedule: { name: "my awesome schedule" } expect(response).to redirect_to unauthorized_path expect(assigns(:schedule)).to eq nil - expect(coordinated_schedule.name).to_not eq "my awesome schedule" + expect(coordinated_schedule.name).not_to eq "my awesome schedule" end end end @@ -173,7 +173,7 @@ describe Admin::SchedulesController, type: :controller do context "where no order cycles ids are provided" do it "does not allow me to create the schedule" do - expect { create_schedule params }.to_not change(Schedule, :count) + expect { create_schedule params }.not_to change { Schedule.count } end end @@ -184,10 +184,10 @@ describe Admin::SchedulesController, type: :controller do end it "allows me to create the schedule, adding only order cycles that I manage" do - expect { create_schedule params }.to change(Schedule, :count).by(1) + expect { create_schedule params }.to change { Schedule.count }.by(1) schedule = Schedule.last expect(schedule.order_cycles).to include coordinated_order_cycle - expect(schedule.order_cycles).to_not include uncoordinated_order_cycle + expect(schedule.order_cycles).not_to include uncoordinated_order_cycle end it "sync proxy orders" do @@ -205,7 +205,7 @@ describe Admin::SchedulesController, type: :controller do end it "prevents me from creating the schedule" do - expect { create_schedule params }.to_not change(Schedule, :count) + expect { create_schedule params }.not_to change { Schedule.count } end end end @@ -218,7 +218,7 @@ describe Admin::SchedulesController, type: :controller do end it "allows me to create a schedule" do - expect { create_schedule params }.to change(Schedule, :count).by(1) + expect { create_schedule params }.to change { Schedule.count }.by(1) schedule = Schedule.last expect(schedule.order_cycles).to include coordinated_order_cycle, uncoordinated_order_cycle @@ -249,7 +249,7 @@ describe Admin::SchedulesController, type: :controller do context "when no dependent subscriptions are present" do it "allows me to destroy the schedule" do - expect { spree_delete :destroy, params }.to change(Schedule, :count).by(-1) + expect { spree_delete :destroy, params }.to change { Schedule.count }.by(-1) end end @@ -257,7 +257,7 @@ describe Admin::SchedulesController, type: :controller do let!(:subscription) { create(:subscription, schedule: coordinated_schedule) } it "returns an error message and prevents me from deleting the schedule" do - expect { spree_delete :destroy, params }.to_not change(Schedule, :count) + expect { spree_delete :destroy, params }.not_to change { Schedule.count } json_response = JSON.parse(response.body) expect(json_response["errors"]) .to include 'This schedule cannot be deleted ' \ @@ -270,7 +270,7 @@ describe Admin::SchedulesController, type: :controller do before { params.merge!(id: uncoordinated_schedule.id) } it "prevents me from destroying the schedule" do - expect { spree_delete :destroy, params }.to_not change(Schedule, :count) + expect { spree_delete :destroy, params }.not_to change { Schedule.count } end end end diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index 5f807d5110..c00d8ae113 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -81,7 +81,7 @@ describe Admin::SubscriptionsController, type: :controller do expect(json_response.count).to be 1 ids = json_response.map{ |so| so['id'] } expect(ids).to include subscription2.id - expect(ids).to_not include subscription.id + expect(ids).not_to include subscription.id end end end @@ -134,7 +134,7 @@ describe Admin::SubscriptionsController, type: :controller do context 'when I submit insufficient params' do it 'returns errors' do - expect{ spree_post :create, params }.to_not change{ Subscription.count } + expect{ spree_post :create, params }.not_to change{ Subscription.count } json_response = JSON.parse(response.body) expect(json_response['errors'].keys).to include 'schedule', 'customer', 'payment_method', 'shipping_method', 'begins_at' @@ -168,7 +168,7 @@ describe Admin::SubscriptionsController, type: :controller do end it 'returns errors' do - expect{ spree_post :create, params }.to_not change{ Subscription.count } + expect{ spree_post :create, params }.not_to change{ Subscription.count } json_response = JSON.parse(response.body) expect(json_response['errors'].keys).to include 'schedule', 'customer', 'payment_method', 'shipping_method', 'ends_at' @@ -197,7 +197,7 @@ describe Admin::SubscriptionsController, type: :controller do context 'where the specified variants are not available from the shop' do it 'returns an error' do - expect{ spree_post :create, params }.to_not change{ Subscription.count } + expect{ spree_post :create, params }.not_to change{ Subscription.count } json_response = JSON.parse(response.body) expect(json_response['errors']['subscription_line_items']) .to eq ["#{variant.product.name} - #{variant.full_name} " \ @@ -343,7 +343,7 @@ describe Admin::SubscriptionsController, type: :controller do end it 'returns errors' do - expect{ spree_post :update, params }.to_not change{ Subscription.count } + expect{ spree_post :update, params }.not_to change{ Subscription.count } json_response = JSON.parse(response.body) expect(json_response['errors'].keys).to include 'payment_method', 'shipping_method' subscription.reload @@ -387,7 +387,7 @@ describe Admin::SubscriptionsController, type: :controller do context 'where the specified variants are not available from the shop' do it 'returns an error' do expect{ spree_post :update, params } - .to_not change{ subscription.subscription_line_items.count } + .not_to change{ subscription.subscription_line_items.count } json_response = JSON.parse(response.body) expect(json_response['errors']['subscription_line_items']) .to eq ["#{product2.name} - #{variant2.full_name} " \ @@ -478,7 +478,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the cancelled subscription as json, and does not cancel the open order' do spree_put :cancel, params json_response = JSON.parse(response.body) - expect(json_response['canceled_at']).to_not be nil + expect(json_response['canceled_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now expect(order.reload.state).to eq 'complete' @@ -498,7 +498,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the cancelled subscription as json, and cancels the open order' do spree_put :cancel, params json_response = JSON.parse(response.body) - expect(json_response['canceled_at']).to_not be nil + expect(json_response['canceled_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now expect(order.reload.state).to eq 'canceled' @@ -512,7 +512,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the cancelled subscription as json' do spree_put :cancel, params json_response = JSON.parse(response.body) - expect(json_response['canceled_at']).to_not be nil + expect(json_response['canceled_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.canceled_at).to be_within(5.seconds).of Time.zone.now end @@ -582,7 +582,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the paused subscription as json, and does not cancel the open order' do spree_put :pause, params json_response = JSON.parse(response.body) - expect(json_response['paused_at']).to_not be nil + expect(json_response['paused_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now expect(order.reload.state).to eq 'complete' @@ -602,7 +602,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the paused subscription as json, and cancels the open order' do spree_put :pause, params json_response = JSON.parse(response.body) - expect(json_response['paused_at']).to_not be nil + expect(json_response['paused_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now expect(order.reload.state).to eq 'canceled' @@ -616,7 +616,7 @@ describe Admin::SubscriptionsController, type: :controller do it 'renders the paused subscription as json' do spree_put :pause, params json_response = JSON.parse(response.body) - expect(json_response['paused_at']).to_not be nil + expect(json_response['paused_at']).not_to be nil expect(json_response['id']).to eq subscription.id expect(subscription.reload.paused_at).to be_within(5.seconds).of Time.zone.now end @@ -708,7 +708,7 @@ describe Admin::SubscriptionsController, type: :controller do expect(json_response['id']).to eq subscription.id expect(subscription.reload.paused_at).to be nil expect(order.reload.state).to eq 'canceled' - expect(proxy_order.reload.canceled_at).to_not be nil + expect(proxy_order.reload.canceled_at).not_to be nil end end end @@ -771,7 +771,7 @@ describe Admin::SubscriptionsController, type: :controller do it "only loads Stripe and Cash payment methods" do controller.send(:load_form_data) expect(assigns(:payment_methods)).to include payment_method, stripe - expect(assigns(:payment_methods)).to_not include paypal + expect(assigns(:payment_methods)).not_to include paypal end end end diff --git a/spec/controllers/admin/terms_of_service_files_controller_spec.rb b/spec/controllers/admin/terms_of_service_files_controller_spec.rb index 2a81cc898b..de6b0ba174 100644 --- a/spec/controllers/admin/terms_of_service_files_controller_spec.rb +++ b/spec/controllers/admin/terms_of_service_files_controller_spec.rb @@ -12,12 +12,12 @@ describe Admin::TermsOfServiceFilesController, type: :controller do it "does not allow deletion" do post :destroy - expect(TermsOfServiceFile).to_not receive(:current) + expect(TermsOfServiceFile).not_to receive(:current) end it "does not allow creation" do post :create - expect(TermsOfServiceFile).to_not receive(:create!) + expect(TermsOfServiceFile).not_to receive(:create!) end end diff --git a/spec/controllers/admin/variant_overrides_controller_spec.rb b/spec/controllers/admin/variant_overrides_controller_spec.rb index f6040d8ba7..4224452fb1 100644 --- a/spec/controllers/admin/variant_overrides_controller_spec.rb +++ b/spec/controllers/admin/variant_overrides_controller_spec.rb @@ -97,7 +97,7 @@ describe Admin::VariantOverridesController, type: :controller do it "allows to update other variant overrides" do put :bulk_update, as: format, params: { variant_overrides: variant_override_params } - expect(response).to_not redirect_to unauthorized_path + expect(response).not_to redirect_to unauthorized_path variant_override.reload expect(variant_override.price).to eq 123.45 end @@ -193,7 +193,7 @@ describe Admin::VariantOverridesController, type: :controller do it "does not reset count_on_hand for variant_overrides not in params" do expect { put :bulk_reset, params: - }.to_not change{ variant_override3.reload.count_on_hand } + }.not_to change{ variant_override3.reload.count_on_hand } end end end diff --git a/spec/controllers/api/v0/logos_controller_spec.rb b/spec/controllers/api/v0/logos_controller_spec.rb index 9aa9769d1d..3d8c800ba0 100644 --- a/spec/controllers/api/v0/logos_controller_spec.rb +++ b/spec/controllers/api/v0/logos_controller_spec.rb @@ -18,7 +18,7 @@ module Api } describe "removing logo" do - let(:image) { Rack::Test::UploadedFile.new(black_logo_file, "image/png") } + let(:image) { black_logo_file } let(:enterprise) { create(:enterprise, owner: enterprise_owner, logo: image) } @@ -35,7 +35,7 @@ module Api expect(response.status).to eq 200 expect(json_response["id"]).to eq enterprise.id enterprise.reload - expect(enterprise.logo).to_not be_attached + expect(enterprise.logo).not_to be_attached end context "when logo does not exist" do diff --git a/spec/controllers/api/v0/order_cycles_controller_spec.rb b/spec/controllers/api/v0/order_cycles_controller_spec.rb index dfc6010ec1..7a8aaa0a24 100644 --- a/spec/controllers/api/v0/order_cycles_controller_spec.rb +++ b/spec/controllers/api/v0/order_cycles_controller_spec.rb @@ -57,7 +57,7 @@ module Api q: { ransack_param => "Kangaroo" } expect(product_ids).to include product1.id - expect(product_ids).to_not include product2.id + expect(product_ids).not_to include product2.id end context "with variant overrides" do @@ -84,7 +84,7 @@ module Api it "does not return products where the variant overrides are out of stock" do api_get :products, id: order_cycle.id, distributor: distributor.id - expect(product_ids).to_not include product2.id + expect(product_ids).not_to include product2.id end end @@ -99,7 +99,7 @@ module Api expect(response.status).to eq 200 expect(product_ids).to eq [product1.id, product2.id] - expect(product_ids).to_not include product3.id + expect(product_ids).not_to include product3.id end context "with supplier properties" do @@ -118,7 +118,7 @@ module Api expect(response.status).to eq 200 expect(product_ids).to match_array [product1.id, product2.id] - expect(product_ids).to_not include product3.id + expect(product_ids).not_to include product3.id end end end @@ -129,7 +129,7 @@ module Api q: { primary_taxon_id_in_any: [taxon2.id] } expect(product_ids).to include product2.id, product3.id - expect(product_ids).to_not include product1.id, product4.id + expect(product_ids).not_to include product1.id, product4.id end end @@ -176,7 +176,7 @@ module Api api_get :products, id: order_cycle.id, distributor: distributor.id - expect(product_ids).to_not include product1.id + expect(product_ids).not_to include product1.id end it "does not return variants hidden for this specific customer" do @@ -185,7 +185,7 @@ module Api api_get :products, id: order_cycle.id, distributor: distributor.id - expect(product_ids).to_not include product2.id + expect(product_ids).not_to include product2.id end it "returns hidden variants made visible for this specific customer" do @@ -197,7 +197,7 @@ module Api api_get :products, id: order_cycle.id, distributor: distributor.id - expect(product_ids).to_not include product1.id + expect(product_ids).not_to include product1.id expect(product_ids).to include product3.id end end diff --git a/spec/controllers/api/v0/product_images_controller_spec.rb b/spec/controllers/api/v0/product_images_controller_spec.rb index 464e9025cc..2c0a380bee 100644 --- a/spec/controllers/api/v0/product_images_controller_spec.rb +++ b/spec/controllers/api/v0/product_images_controller_spec.rb @@ -8,9 +8,8 @@ describe Api::V0::ProductImagesController, type: :controller do render_views describe "uploading an image" do - let(:image) { Rack::Test::UploadedFile.new(black_logo_file, 'image/png') } - let(:pdf) { Rack::Test::UploadedFile.new(pdf_path, 'application/pdf') } - let(:pdf_path) { Rails.public_path.join('Terms-of-service.pdf') } + let(:image) { black_logo_file } + let(:pdf) { terms_pdf_file } let(:product_without_image) { create(:product) } let(:product_with_image) { create(:product_with_image) } let(:current_api_user) { create(:admin_user) } diff --git a/spec/controllers/api/v0/products_controller_spec.rb b/spec/controllers/api/v0/products_controller_spec.rb index c31e323896..af6dbfe032 100644 --- a/spec/controllers/api/v0/products_controller_spec.rb +++ b/spec/controllers/api/v0/products_controller_spec.rb @@ -25,15 +25,17 @@ describe Api::V0::ProductsController, type: :controller do end context "as a normal user" do + let(:attachment) { fixture_file_upload("thinking-cat.jpg") } + before do allow(current_api_user) .to receive(:has_spree_role?).with("admin").and_return(false) end it "gets a single product" do - product.create_image!(attachment: image("thinking-cat.jpg")) + product.create_image!(attachment:) product.variants.create!(unit_value: "1", unit_description: "thing", price: 1) - product.variants.first.images.create!(attachment: image("thinking-cat.jpg")) + product.variants.first.images.create!(attachment:) product.set_property("spree", "rocks") api_get :show, id: product.to_param @@ -50,7 +52,20 @@ describe Api::V0::ProductsController, type: :controller do expect(response.status).to eq(404) end - include_examples "modifying product actions are restricted" + it "cannot create a new product if not an admin" do + api_post :create, product: { name: "Brand new product!" } + assert_unauthorized! + end + + it "cannot update a product" do + api_put :update, id: product.to_param, product: { name: "I hacked your store!" } + assert_unauthorized! + end + + it "cannot delete a product" do + api_delete :destroy, id: product.to_param + assert_unauthorized! + end end context "as an enterprise user" do diff --git a/spec/controllers/api/v0/promo_images_controller_spec.rb b/spec/controllers/api/v0/promo_images_controller_spec.rb index dff570aba3..76e7288ae1 100644 --- a/spec/controllers/api/v0/promo_images_controller_spec.rb +++ b/spec/controllers/api/v0/promo_images_controller_spec.rb @@ -18,7 +18,7 @@ module Api } describe "removing promo image" do - let(:image) { Rack::Test::UploadedFile.new(black_logo_file, "image/png") } + let(:image) { black_logo_file } let(:enterprise) { create(:enterprise, owner: enterprise_owner, promo_image: image) } @@ -35,7 +35,7 @@ module Api expect(response.status).to eq 200 expect(json_response["id"]).to eq enterprise.id enterprise.reload - expect(enterprise.promo_image).to_not be_attached + expect(enterprise.promo_image).not_to be_attached end context "when promo image does not exist" do diff --git a/spec/controllers/api/v0/reports_controller_spec.rb b/spec/controllers/api/v0/reports_controller_spec.rb index d5403990c0..e50c55cbdc 100644 --- a/spec/controllers/api/v0/reports_controller_spec.rb +++ b/spec/controllers/api/v0/reports_controller_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" describe Api::V0::ReportsController, type: :controller do - let(:enterprise_user) { create(:user, enterprises: create(:enterprise)) } + let(:enterprise_user) { create(:user, enterprises: [create(:enterprise)]) } let(:params) { { report_type: 'packing', diff --git a/spec/controllers/api/v0/shipments_controller_spec.rb b/spec/controllers/api/v0/shipments_controller_spec.rb index 98584c9121..add3d0c3ba 100644 --- a/spec/controllers/api/v0/shipments_controller_spec.rb +++ b/spec/controllers/api/v0/shipments_controller_spec.rb @@ -91,6 +91,18 @@ describe Api::V0::ShipmentsController, type: :controller do expect_error_response end + + it "applies any enterprise fees that are present" do + order_cycle = create(:simple_order_cycle, + coordinator: order.distributor, + coordinator_fees: [create(:enterprise_fee, amount: 20)], + distributors: [order.distributor], + variants: [variant]) + order.update!(order_cycle_id: order_cycle.id) + spree_post :create, params + + expect(order.line_item_adjustments.where(originator_type: "EnterpriseFee")).to be_present + end end it "can make a shipment ready" do @@ -182,14 +194,14 @@ describe Api::V0::ShipmentsController, type: :controller do expect { api_put :add, params.merge(variant_id: existing_variant.to_param) expect(response.status).to eq(422) - }.to_not change { existing_variant.reload.on_hand } + }.not_to change { existing_variant.reload.on_hand } end it "doesn't adjust stock when removing a variant" do expect { api_put :remove, params.merge(variant_id: existing_variant.to_param) expect(response.status).to eq(422) - }.to_not change { existing_variant.reload.on_hand } + }.not_to change { existing_variant.reload.on_hand } end end @@ -355,13 +367,17 @@ describe Api::V0::ShipmentsController, type: :controller do instance_double(Spree::Order, number: "123", distributor: variant.product.supplier) } let(:contents) { instance_double(Spree::OrderContents) } + let(:fee_order_shipment) { + instance_double(Spree::Shipment) + } before do allow(Spree::Order).to receive(:find_by!) { fee_order } - allow(controller).to receive(:find_and_update_shipment) {} allow(controller).to receive(:refuse_changing_cancelled_orders) {} allow(fee_order).to receive(:contents) { contents } - allow(contents).to receive(:add) {} + allow(contents).to receive_messages(add: {}, remove: {}) + allow(fee_order).to receive_message_chain(:shipments, :find_by!) { fee_order_shipment } + allow(fee_order_shipment).to receive_messages(update: nil, reload: nil, persisted?: nil) allow(fee_order).to receive(:recreate_all_fees!) end @@ -370,6 +386,12 @@ describe Api::V0::ShipmentsController, type: :controller do spree_put :add, params expect(fee_order).to have_received(:recreate_all_fees!) end + + it "recalculates fees for the line item when qty is decreased" do + params[:order_id] = fee_order.number + spree_put :remove, params + expect(fee_order).to have_received(:recreate_all_fees!) + end end end diff --git a/spec/controllers/api/v0/terms_and_conditions_controller_spec.rb b/spec/controllers/api/v0/terms_and_conditions_controller_spec.rb index 89d0b6fc08..53596f7fd3 100644 --- a/spec/controllers/api/v0/terms_and_conditions_controller_spec.rb +++ b/spec/controllers/api/v0/terms_and_conditions_controller_spec.rb @@ -12,10 +12,7 @@ module Api let(:enterprise_manager) { create(:user, enterprises: [enterprise]) } describe "removing terms and conditions file" do - let(:terms_file_path) { Rails.public_path.join('Terms-of-service.pdf') } - let(:terms_and_conditions_file) { - Rack::Test::UploadedFile.new(terms_file_path, "application/pdf") - } + let(:terms_and_conditions_file) { terms_pdf_file } let(:enterprise) { create(:enterprise, owner: enterprise_owner) } before do @@ -32,7 +29,7 @@ module Api expect(response.status).to eq 200 expect(json_response["id"]).to eq enterprise.id enterprise.reload - expect(enterprise.terms_and_conditions).to_not be_attached + expect(enterprise.terms_and_conditions).not_to be_attached end context "when terms and conditions file does not exist" do diff --git a/spec/controllers/api/v0/variants_controller_spec.rb b/spec/controllers/api/v0/variants_controller_spec.rb index f67fefd7f0..9272b0b522 100644 --- a/spec/controllers/api/v0/variants_controller_spec.rb +++ b/spec/controllers/api/v0/variants_controller_spec.rb @@ -172,7 +172,7 @@ describe Api::V0::VariantsController, type: :controller do variant = product.variants.first spree_delete :destroy, id: variant.to_param - expect(variant.reload).to_not be_deleted + expect(variant.reload).not_to be_deleted expect(assigns(:variant).errors[:product]).to include "must have at least one variant" end end diff --git a/spec/controllers/base_controller_spec.rb b/spec/controllers/base_controller_spec.rb index 17725eb773..5158dd1c3c 100644 --- a/spec/controllers/base_controller_spec.rb +++ b/spec/controllers/base_controller_spec.rb @@ -17,7 +17,7 @@ describe BaseController, type: :controller do it "doesn't change anything without a user" do expect { get :index - }.to_not change { Spree::Order.count } + }.not_to change { Spree::Order.count } end it "creates a new order" do @@ -36,7 +36,7 @@ describe BaseController, type: :controller do expect { get :index - }.to_not change { Spree::Order.count } + }.not_to change { Spree::Order.count } expect(session[:order_id]).to eq last_cart.id end @@ -63,7 +63,7 @@ describe BaseController, type: :controller do expect { get :index - }.to_not change { Spree::Order.count } + }.not_to change { Spree::Order.count } expect(current_cart.line_items.count).to eq 0 end @@ -89,13 +89,13 @@ describe BaseController, type: :controller do get :index }.to change { Spree::Order.count }.by(1) - expect(session[:order_id]).to_not eq just_completed_order.id - expect(session[:order_id]).to_not eq last_cart.id + expect(session[:order_id]).not_to eq just_completed_order.id + expect(session[:order_id]).not_to eq last_cart.id expect(controller.current_order.line_items.count).to eq 0 end it "doesn't load variant overrides without line items" do - expect(VariantOverride).to_not receive(:indexed) + expect(VariantOverride).not_to receive(:indexed) controller.current_order(true) end end diff --git a/spec/controllers/cart_controller_spec.rb b/spec/controllers/cart_controller_spec.rb index b39bdb3d14..70ef4fd4e5 100644 --- a/spec/controllers/cart_controller_spec.rb +++ b/spec/controllers/cart_controller_spec.rb @@ -111,7 +111,7 @@ describe CartController, type: :controller do expect do spree_post :populate, variants: { variant.id => 1 }, variant_attributes: { variant.id => { max_quantity: "3" } } - end.to change(Spree::LineItem, :count).by(1) + end.to change { Spree::LineItem.count }.by(1) end end end diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index 355343d0d5..4f948cdefa 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -148,7 +148,7 @@ describe CheckoutController, type: :controller do it "doesn't update default bill address on user" do expect { put :update, params: params.merge(order: { save_bill_address: "0" }) - }.to_not change { + }.not_to change { order.user.reload.bill_address } end @@ -163,7 +163,7 @@ describe CheckoutController, type: :controller do it "doesn't update default ship address on user" do expect { put :update, params: params.merge(order: { save_ship_address: "0" }) - }.to_not change { + }.not_to change { order.user.reload.ship_address } end @@ -196,7 +196,7 @@ describe CheckoutController, type: :controller do end it "doesn't recalculate the voucher adjustment" do - expect(service).to_not receive(:update) + expect(service).not_to receive(:update) put(:update, params:) @@ -256,7 +256,7 @@ describe CheckoutController, type: :controller do order.bill_address = address order.ship_address = address order.select_shipping_method shipping_method.id - OrderWorkflow.new(order).advance_to_payment + Orders::WorkflowService.new(order).advance_to_payment end context "with incomplete data" do @@ -387,7 +387,7 @@ describe CheckoutController, type: :controller do order.bill_address = address order.ship_address = address order.select_shipping_method shipping_method.id - OrderWorkflow.new(order).advance_to_payment + Orders::WorkflowService.new(order).advance_to_payment order.payments << build(:payment, amount: order.total, payment_method:) order.next @@ -459,7 +459,7 @@ describe CheckoutController, type: :controller do allow(order).to receive(:distributor).and_return(distributor) order.update(order_cycle:) - allow(OrderCycleDistributedVariants).to receive(:new).and_return( + allow(OrderCycles::DistributedVariantsService).to receive(:new).and_return( order_cycle_distributed_variants ) end diff --git a/spec/controllers/concerns/raising_parameters_spec.rb b/spec/controllers/concerns/raising_parameters_spec.rb index cf330221b7..73008f20cf 100644 --- a/spec/controllers/concerns/raising_parameters_spec.rb +++ b/spec/controllers/concerns/raising_parameters_spec.rb @@ -26,7 +26,7 @@ describe RaisingParameters do it "raises no error when all parameters are permitted" do expect { params.require(:data).permit(:id, :admin) - }.to_not raise_error + }.not_to raise_error end it "doesn't change standard parameter objects" do @@ -34,7 +34,7 @@ describe RaisingParameters do expect { original_params.permit(:one) - }.to_not raise_error + }.not_to raise_error end end end diff --git a/spec/controllers/payment_gateways/stripe_controller_spec.rb b/spec/controllers/payment_gateways/stripe_controller_spec.rb index 72ceef6bfe..9b2e3233d9 100644 --- a/spec/controllers/payment_gateways/stripe_controller_spec.rb +++ b/spec/controllers/payment_gateways/stripe_controller_spec.rb @@ -11,7 +11,9 @@ module PaymentGateways let!(:order) { create(:order_with_totals, distributor:, order_cycle:) } let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first } - let(:order_cycle_distributed_variants) { instance_double(OrderCycleDistributedVariants) } + let(:order_cycle_distributed_variants) { + instance_double(OrderCycles::DistributedVariantsService) + } before do exchange.variants << order.line_items.first.variant @@ -305,11 +307,11 @@ completed due to stock issues." context "with an invalid last payment" do let(:payment_intent) { "valid" } - let(:finder) { instance_double(OrderPaymentFinder, last_payment: payment) } + let(:finder) { instance_double(Orders::FindPaymentService, last_payment: payment) } before do allow(payment).to receive(:response_code).and_return("invalid") - allow(OrderPaymentFinder).to receive(:new).with(order).and_return(finder) + allow(Orders::FindPaymentService).to receive(:new).with(order).and_return(finder) allow(Stripe::PaymentIntentValidator) .to receive_message_chain(:new, :call).and_return(payment_intent) stub_payment_intent_get_request(payment_intent_id: "valid") diff --git a/spec/controllers/spree/admin/adjustments_controller_spec.rb b/spec/controllers/spree/admin/adjustments_controller_spec.rb index d44c27a08b..a6ae890071 100644 --- a/spec/controllers/spree/admin/adjustments_controller_spec.rb +++ b/spec/controllers/spree/admin/adjustments_controller_spec.rb @@ -27,7 +27,7 @@ module Spree spree_get :index, order_id: order.number expect(assigns(:collection)).to include adjustment1, adjustment2 - expect(assigns(:collection)).to_not include adjustment3 + expect(assigns(:collection)).not_to include adjustment3 end it "displays admin adjustments" do @@ -39,7 +39,7 @@ module Spree it "does not display enterprise fee adjustments" do spree_get :index, order_id: order.number - expect(assigns(:collection)).to_not include adjustment4 + expect(assigns(:collection)).not_to include adjustment4 end end @@ -237,7 +237,7 @@ module Spree expect { spree_post :create, order_id: order.number, adjustment: { label: "Testing", amount: "110" } - }.to_not change { [Adjustment.count, order.reload.total] } + }.not_to change { [Adjustment.count, order.reload.total] } expect(response).to redirect_to spree.admin_order_adjustments_path(order) end @@ -246,7 +246,7 @@ module Spree expect { spree_put :update, order_id: order.number, id: adjustment.id, adjustment: { label: "Testing", amount: "110" } - }.to_not change { [adjustment.reload.amount, order.reload.total] } + }.not_to change { [adjustment.reload.amount, order.reload.total] } expect(response).to redirect_to spree.admin_order_adjustments_path(order) end diff --git a/spec/controllers/spree/admin/general_settings_controller_spec.rb b/spec/controllers/spree/admin/general_settings_controller_spec.rb index 4db32d6183..3fff115d01 100644 --- a/spec/controllers/spree/admin/general_settings_controller_spec.rb +++ b/spec/controllers/spree/admin/general_settings_controller_spec.rb @@ -13,7 +13,7 @@ describe Spree::Admin::GeneralSettingsController, type: :controller do end it "updates available units" do - expect(Spree::Config.available_units).to_not include("lb") + expect(Spree::Config.available_units).not_to include("lb") settings_params = { available_units: { lb: "1" } } spree_put :update, settings_params expect(Spree::Config.available_units).to include("lb") diff --git a/spec/controllers/spree/admin/orders/invoices_spec.rb b/spec/controllers/spree/admin/orders/invoices_spec.rb index e889534882..2a7b3b00c1 100644 --- a/spec/controllers/spree/admin/orders/invoices_spec.rb +++ b/spec/controllers/spree/admin/orders/invoices_spec.rb @@ -40,10 +40,10 @@ describe Spree::Admin::OrdersController, type: :controller do it "should allow me to send order invoices" do expect do spree_get :invoice, params - end.to_not change{ Spree::OrderMailer.deliveries.count } + end.not_to change{ Spree::OrderMailer.deliveries.count } expect(response).to redirect_to spree.edit_admin_order_path(order) expect(flash[:error]) - .to eq "#{distributor.name} must have a valid ABN before invoices can be sent." + .to eq "#{distributor.name} must have a valid ABN before invoices can be used." end end diff --git a/spec/controllers/spree/admin/orders/payments/payments_controller_refunds_spec.rb b/spec/controllers/spree/admin/orders/payments/payments_controller_refunds_spec.rb index 3dd36c5273..a6ed0be235 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_refunds_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_refunds_spec.rb @@ -49,13 +49,13 @@ describe Spree::Admin::PaymentsController, type: :controller do it "voids the payment" do order.reload - expect(order.payment_total).to_not eq 0 + expect(order.payment_total).not_to eq 0 expect(order.outstanding_balance.to_f).to eq 0 spree_put :fire, params expect(payment.reload.state).to eq 'void' order.reload expect(order.payment_total).to eq 0 - expect(order.outstanding_balance.to_f).to_not eq 0 + expect(order.outstanding_balance.to_f).not_to eq 0 end end @@ -69,12 +69,12 @@ describe Spree::Admin::PaymentsController, type: :controller do it "does not void the payment" do order.reload - expect(order.payment_total).to_not eq 0 + expect(order.payment_total).not_to eq 0 expect(order.outstanding_balance.to_f).to eq 0 spree_put :fire, params expect(payment.reload.state).to eq 'completed' order.reload - expect(order.payment_total).to_not eq 0 + expect(order.payment_total).not_to eq 0 expect(order.outstanding_balance.to_f).to eq 0 expect(flash[:error]).to eq "Bup-bow!" end @@ -92,13 +92,13 @@ describe Spree::Admin::PaymentsController, type: :controller do it "can still void the payment" do order.reload - expect(order.payment_total).to_not eq 0 + expect(order.payment_total).not_to eq 0 expect(order.outstanding_balance.to_f).to eq 0 spree_put :fire, params expect(payment.reload.state).to eq 'void' order.reload expect(order.payment_total).to eq 0 - expect(order.outstanding_balance.to_f).to_not eq 0 + expect(order.outstanding_balance.to_f).not_to eq 0 end end end @@ -115,13 +115,13 @@ describe Spree::Admin::PaymentsController, type: :controller do it "voids the payment" do order.reload - expect(order.payment_total).to_not eq 0 + expect(order.payment_total).not_to eq 0 expect(order.outstanding_balance.to_f).to eq 0 spree_put :fire, params expect(payment.reload.state).to eq 'void' order.reload expect(order.payment_total).to eq 0 - expect(order.outstanding_balance.to_f).to_not eq 0 + expect(order.outstanding_balance.to_f).not_to eq 0 end end end diff --git a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb index a7a06816d5..6478629e48 100644 --- a/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb +++ b/spec/controllers/spree/admin/orders/payments/payments_controller_spec.rb @@ -258,7 +258,7 @@ describe Spree::Admin::PaymentsController, type: :controller do it 'does not process the event' do spree_put :fire, params - expect(payment).to_not receive(:unrecognized_event) + expect(payment).not_to receive(:unrecognized_event) expect(flash[:error]).to eq('Could not update the payment') end end diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index 0e27a9dc3c..0dc568430d 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -24,7 +24,7 @@ describe Spree::Admin::OrdersController, type: :controller do spree_get :edit, id: order - expect(response.body).to_not match adjustment.label + expect(response.body).not_to match adjustment.label end end end @@ -204,7 +204,7 @@ describe Spree::Admin::OrdersController, type: :controller do order.reload expect(order.all_adjustments.tax.count).to eq 2 - expect(order.all_adjustments.tax).to_not include legacy_tax_adjustment + expect(order.all_adjustments.tax).not_to include legacy_tax_adjustment expect(order.additional_tax_total).to eq 0.5 end end diff --git a/spec/controllers/spree/admin/payment_methods_controller_spec.rb b/spec/controllers/spree/admin/payment_methods_controller_spec.rb index 9588da846a..37569b28ec 100644 --- a/spec/controllers/spree/admin/payment_methods_controller_spec.rb +++ b/spec/controllers/spree/admin/payment_methods_controller_spec.rb @@ -93,7 +93,7 @@ module Spree spree_post :create, payment_method: { name: "Test Method", type: "Spree::Gateway::PayPalExpress", distributor_ids: [enterprise.id] } - }.to change(Spree::PaymentMethod, :count).by(1) + }.to change { Spree::PaymentMethod.count }.by(1) expect(response).to be_redirect expect(response).to redirect_to spree @@ -105,7 +105,7 @@ module Spree spree_post :create, payment_method: { name: "Invalid Payment Method", type: "Spree::InvalidType", distributor_ids: [enterprise.id] } - }.to change(Spree::PaymentMethod, :count).by(0) + }.to change { Spree::PaymentMethod.count }.by(0) expect(response).to be_redirect expect(response).to redirect_to spree.new_admin_payment_method_path diff --git a/spec/controllers/spree/admin/products_controller_spec.rb b/spec/controllers/spree/admin/products_controller_spec.rb index bc02ae7217..2fcb53ae2a 100644 --- a/spec/controllers/spree/admin/products_controller_spec.rb +++ b/spec/controllers/spree/admin/products_controller_spec.rb @@ -190,7 +190,7 @@ describe Spree::Admin::ProductsController, type: :controller do spree_put :update, id: product, product: { supplier_id: new_producer.id } expect(product.reload.supplier.id).to eq new_producer.id - expect(order_cycle.reload.distributed_variants).to_not include product.variants.first + expect(order_cycle.reload.distributed_variants).not_to include product.variants.first end end @@ -227,7 +227,7 @@ describe Spree::Admin::ProductsController, type: :controller do expect(Spree::Property.count).to be 1 expect(Spree::ProductProperty.count).to be 0 property_names = product.reload.properties.map(&:name) - expect(property_names).to_not include 'a different name' + expect(property_names).not_to include 'a different name' end end diff --git a/spec/controllers/spree/admin/search_controller_spec.rb b/spec/controllers/spree/admin/search_controller_spec.rb index e34e553453..53313e5521 100644 --- a/spec/controllers/spree/admin/search_controller_spec.rb +++ b/spec/controllers/spree/admin/search_controller_spec.rb @@ -18,7 +18,7 @@ describe Spree::Admin::SearchController, type: :controller do it "returns a list of users that I share management of enteprises with" do expect(assigns(:users)).to include owner, manager - expect(assigns(:users)).to_not include random + expect(assigns(:users)).not_to include random end end diff --git a/spec/controllers/spree/admin/tax_rates_controller_spec.rb b/spec/controllers/spree/admin/tax_rates_controller_spec.rb index 27d0c10464..3ce91d9ebf 100644 --- a/spec/controllers/spree/admin/tax_rates_controller_spec.rb +++ b/spec/controllers/spree/admin/tax_rates_controller_spec.rb @@ -27,7 +27,7 @@ module Spree it "updates the record" do expect { spree_put :update, id: tax_rate.id, tax_rate: params - }.to_not change{ Spree::TaxRate.with_deleted.count } + }.not_to change{ Spree::TaxRate.with_deleted.count } expect(response).to redirect_to spree.admin_tax_rates_url expect(tax_rate.reload.name).to eq "Updated Rate" diff --git a/spec/controllers/spree/api_keys_controller_spec.rb b/spec/controllers/spree/api_keys_controller_spec.rb index c39b46e821..99faf43964 100644 --- a/spec/controllers/spree/api_keys_controller_spec.rb +++ b/spec/controllers/spree/api_keys_controller_spec.rb @@ -26,7 +26,7 @@ describe Spree::ApiKeysController, type: :controller, performance: true do expect { spree_post :create, id: other_user.id other_user.reload - }.to_not change { + }.not_to change { other_user.spree_api_key } end diff --git a/spec/controllers/spree/credit_cards_controller_spec.rb b/spec/controllers/spree/credit_cards_controller_spec.rb index e41beb724d..41e87d975c 100644 --- a/spec/controllers/spree/credit_cards_controller_spec.rb +++ b/spec/controllers/spree/credit_cards_controller_spec.rb @@ -5,10 +5,8 @@ require 'spec_helper' describe Spree::CreditCardsController, type: :controller do describe "using VCR", :vcr, :stripe_version do let(:user) { create(:user) } - let(:secret) { ENV.fetch('STRIPE_SECRET_TEST_API_KEY', nil) } before do - Stripe.api_key = secret allow(controller).to receive(:spree_current_user) { user } end @@ -96,7 +94,7 @@ describe Spree::CreditCardsController, type: :controller do { status: 402, body: JSON.generate(error: { message: "Bup-bow..." }) } } it "doesn't save the card locally, and renders a flash error" do - expect{ spree_post :new_from_token, params }.to_not change(Spree::CreditCard, :count) + expect{ spree_post :new_from_token, params }.not_to change { Spree::CreditCard.count } json_response = JSON.parse(response.body) flash_message = "There was a problem with your payment information: %s" % 'Bup-bow...' @@ -174,7 +172,7 @@ describe Spree::CreditCardsController, type: :controller do let(:params) { { id: 123 } } it "redirects to /account with a flash error, does not request deletion with Stripe" do - expect(controller).to_not receive(:destroy_at_stripe) + expect(controller).not_to receive(:destroy_at_stripe) spree_delete :destroy, params expect(flash[:error]).to eq 'Sorry, the card could not be removed' expect(response.status).to eq 200 @@ -207,7 +205,7 @@ describe Spree::CreditCardsController, type: :controller do end it "doesn't delete the card" do - expect{ spree_delete :destroy, params }.to_not change(Spree::CreditCard, :count) + expect{ spree_delete :destroy, params }.not_to change { Spree::CreditCard.count } expect(flash[:error]).to eq 'Sorry, the card could not be removed' expect(response.status).to eq 422 end @@ -220,7 +218,7 @@ describe Spree::CreditCardsController, type: :controller do end it "deletes the card and redirects to account_path" do - expect{ spree_delete :destroy, params }.to change(Spree::CreditCard, :count).by(-1) + expect{ spree_delete :destroy, params }.to change { Spree::CreditCard.count }.by(-1) expect(flash[:success]) .to eq "Your card has been removed (number: %s)" % "x-#{card.last_digits}" expect(response.status).to eq 200 diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 5acb0f5aae..78ed96e16e 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -150,7 +150,7 @@ describe Spree::OrdersController, type: :controller do spree_registration_path = '/signup' ofn_registration_path = '/register' get :edit - expect(response.body).to_not match spree_registration_path + expect(response.body).not_to match spree_registration_path expect(response.body).to match ofn_registration_path end end diff --git a/spec/controllers/spree/users_controller_spec.rb b/spec/controllers/spree/users_controller_spec.rb index 3837cf8b71..ee8de1d00f 100644 --- a/spec/controllers/spree/users_controller_spec.rb +++ b/spec/controllers/spree/users_controller_spec.rb @@ -7,7 +7,7 @@ describe Spree::UsersController, type: :controller do include AuthenticationHelper - describe "show" do + describe "#show" do let!(:u1) { create(:user) } let!(:u2) { create(:user) } let!(:distributor1) { create(:distributor_enterprise) } @@ -23,7 +23,7 @@ describe Spree::UsersController, type: :controller do let(:orders) { assigns(:orders) } let(:shops) { Enterprise.where(id: orders.pluck(:distributor_id)) } - let(:outstanding_balance) { instance_double(OutstandingBalance) } + let(:outstanding_balance_query) { instance_double(OutstandingBalanceQuery) } before do allow(controller).to receive(:spree_current_user) { u1 } @@ -33,7 +33,7 @@ describe Spree::UsersController, type: :controller do get :show expect(orders).to include d1o1, d1o2 - expect(orders).to_not include d1_order_for_u2, d1o3, d2o1 + expect(orders).not_to include d1_order_for_u2, d1o3, d2o1 expect(shops).to include distributor1 # Doesn't return orders for irrelevant distributors" do @@ -47,15 +47,15 @@ describe Spree::UsersController, type: :controller do expect(orders).not_to include d1o3 end - it 'calls OutstandingBalance' do - allow(OutstandingBalance).to receive(:new).and_return(outstanding_balance) - expect(outstanding_balance).to receive(:query) { Spree::Order.none } + it 'calls OutstandingBalanceQuery' do + allow(OutstandingBalanceQuery).to receive(:new).and_return(outstanding_balance_query) + expect(outstanding_balance_query).to receive(:call) { Spree::Order.none } spree_get :show end end - describe "registered_email" do + describe "#registered_email" do routes { Openfoodnetwork::Application.routes } let!(:user) { create(:user) } @@ -71,16 +71,16 @@ describe Spree::UsersController, type: :controller do end end - context '#load_object' do - it 'should redirect to signup path if user is not found' do + describe '#load_object' do + it 'redirects to signup path if user is not found' do allow(controller).to receive_messages(spree_current_user: nil) put :update, params: { user: { email: 'foobar@example.com' } } expect(response).to redirect_to('/login') end end - context '#create' do - it 'should create a new user' do + describe '#create' do + it 'creates a new user' do post :create, params: { user: { email: 'foobar@example.com', password: 'foobar123', password_confirmation: 'foobar123', locale: 'es' } } diff --git a/spec/controllers/user_confirmations_controller_spec.rb b/spec/controllers/user_confirmations_controller_spec.rb index 40be9eebb1..dd4897c4e1 100644 --- a/spec/controllers/user_confirmations_controller_spec.rb +++ b/spec/controllers/user_confirmations_controller_spec.rb @@ -55,7 +55,7 @@ describe UserConfirmationsController, type: :controller do unconfirmed_user.save! spree_get :show, confirmation_token: unconfirmed_user.confirmation_token expect(response).to be_redirect - expect(response.body).to include spree.edit_spree_user_password_path + expect(response.headers["Location"]).to include spree.edit_spree_user_password_path end end end diff --git a/spec/controllers/webhook_endpoints_controller_spec.rb b/spec/controllers/webhook_endpoints_controller_spec.rb index c36720edb8..e628710f72 100644 --- a/spec/controllers/webhook_endpoints_controller_spec.rb +++ b/spec/controllers/webhook_endpoints_controller_spec.rb @@ -24,7 +24,7 @@ describe WebhookEndpointsController, type: :controller do it "shows error if parameters not specified" do expect { spree_post :create, { url: "" } - }.to_not change { + }.not_to change { user.webhook_endpoints.count } diff --git a/spec/factories/enterprise_factory.rb b/spec/factories/enterprise_factory.rb index 762d80c667..e29445941d 100644 --- a/spec/factories/enterprise_factory.rb +++ b/spec/factories/enterprise_factory.rb @@ -29,7 +29,7 @@ FactoryBot.define do end trait :with_promo_image do - logo { Rack::Test::UploadedFile.new('spec/fixtures/files/promo.png', 'image/png') } + promo_image { Rack::Test::UploadedFile.new('spec/fixtures/files/promo.png', 'image/png') } end factory :supplier_enterprise, parent: :enterprise do diff --git a/spec/factories/order_cycle_factory.rb b/spec/factories/order_cycle_factory.rb index 8518bb68c1..18c1fd7181 100644 --- a/spec/factories/order_cycle_factory.rb +++ b/spec/factories/order_cycle_factory.rb @@ -35,7 +35,7 @@ FactoryBot.define do viewable_id: product.id, viewable_type: 'Spree::Product', alt: "position 1", - attachment: Rack::Test::UploadedFile.new(white_logo_path), + attachment: white_logo_file, position: 1 ) diff --git a/spec/factories/order_factory.rb b/spec/factories/order_factory.rb index 096753389b..b9726a3614 100644 --- a/spec/factories/order_factory.rb +++ b/spec/factories/order_factory.rb @@ -28,7 +28,7 @@ FactoryBot.define do after(:create) do |order, evaluator| order.select_shipping_method evaluator.shipping_method.id - OrderWorkflow.new(order).advance_to_payment + Orders::WorkflowService.new(order).advance_to_payment end factory :order_ready_for_confirmation do diff --git a/spec/factories/tax_category_factory.rb b/spec/factories/tax_category_factory.rb index 0cb0f3af4a..5503091437 100644 --- a/spec/factories/tax_category_factory.rb +++ b/spec/factories/tax_category_factory.rb @@ -2,7 +2,7 @@ FactoryBot.define do factory :tax_category, class: Spree::TaxCategory do - name { "TaxCategory - #{rand(999_999)}" } + sequence(:name) { |n| "TaxCategory - #{n}" } description { generate(:random_string) } end end diff --git a/spec/factories/user_factory.rb b/spec/factories/user_factory.rb index 8a5d7b4c3d..80f2ecf432 100644 --- a/spec/factories/user_factory.rb +++ b/spec/factories/user_factory.rb @@ -6,10 +6,6 @@ FactoryBot.define do end factory :user, class: Spree::User do - transient do - enterprises { [] } - end - email { generate(:random_email) } login { email } password { 'secret' } @@ -22,6 +18,7 @@ FactoryBot.define do confirmation_sent_at { '1970-01-01 00:00:00' } confirmed_at { '1970-01-01 00:00:01' } + terms_of_service_accepted_at { 1.hour.ago } before(:create) do |user, evaluator| if evaluator.confirmation_sent_at @@ -33,24 +30,18 @@ FactoryBot.define do end end - after(:create) do |user, proxy| - user.spree_roles.clear # Remove admin role - - user.enterprises << proxy.enterprises + factory :enterprise_user do + enterprises { [build(:enterprise)] } end factory :admin_user do spree_roles { [Spree::Role.find_or_create_by!(name: 'admin')] } - - after(:create) do |user| - user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin') - end end factory :oidc_user do - after(:create) do |user| - user.update uid: user.email - end + oidc_account { + OidcAccount.new(provider: "openid_connect", uid: email) + } end end end diff --git a/spec/factories/zone_factory.rb b/spec/factories/zone_factory.rb index 4d4ed09542..95c5ae8ddc 100644 --- a/spec/factories/zone_factory.rb +++ b/spec/factories/zone_factory.rb @@ -2,7 +2,7 @@ FactoryBot.define do factory :zone, class: Spree::Zone do - name { generate(:random_string) } + sequence(:name) { |n| "#{generate(:random_string)}#{n}" } description { generate(:random_string) } end diff --git a/spec/fixtures/files/omniauth.auth.json b/spec/fixtures/files/omniauth.auth.json new file mode 100644 index 0000000000..d89f690f04 --- /dev/null +++ b/spec/fixtures/files/omniauth.auth.json @@ -0,0 +1,48 @@ +{ + "provider": "openid_connect", + "uid": "ofn@example.com", + "info": { + "name": "OFN Developer", + "email": "ofn@example.com", + "email_verified": false, + "nickname": "ofn@example.com", + "first_name": "OFN", + "last_name": "Developer", + "gender": null, + "image": null, + "phone": null, + "urls": { + "website": null + } + }, + "credentials": { + "id_token": "ey...id_token...zg", + "token": "ey...token...9g", + "refresh_token": "ey...refresh_token...bk", + "expires_in": 1800, + "scope": "openid profile email" + }, + "extra": { + "raw_info": { + "sub": "97da8027-a7a9-44c8-9cfd-ad639cec8630", + "email_verified": false, + "name": "OFN Developer", + "preferred_username": "ofn@example.com", + "given_name": "OFN", + "family_name": "Developer", + "email": "ofn@example.com", + "exp": 1707458565, + "iat": 1707456765, + "auth_time": 1707456763, + "jti": "00643994-5914-4699-96b0-2b4a308fca65", + "iss": "https://login.lescommuns.org/auth/realms/data-food-consortium", + "aud": "coopcircuits", + "typ": "ID", + "azp": "coopcircuits", + "nonce": "215831991b35c70d43fb2102ee78be55", + "session_state": "8b5725a1-e83a-4f78-a54b-36c5a2983dd4", + "at_hash": "RT8oVVJdFDiaytyDxHJLyQ", + "sid": "8b5725a1-e83a-4f78-a54b-36c5a2983dd4" + } + } +} diff --git a/spec/support/fixtures/thinking-cat.jpg b/spec/fixtures/files/thinking-cat.jpg similarity index 100% rename from spec/support/fixtures/thinking-cat.jpg rename to spec/fixtures/files/thinking-cat.jpg diff --git a/spec/fixtures/vcr_cassettes/ConnectAppJob/stores_connection_data_on_the_app.yml b/spec/fixtures/vcr_cassettes/ConnectAppJob/stores_connection_data_on_the_app.yml new file mode 100644 index 0000000000..42a832a80b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/ConnectAppJob/stores_connection_data_on_the_app.yml @@ -0,0 +1,54 @@ +--- +http_interactions: +- request: + method: post + uri: https://n8n.openfoodnetwork.org.uk/webhook/regen/connect-enterprise + body: + encoding: UTF-8 + string: '{"id":"27bc9d0a-d95c-4a36-9b16-01fdd8a82b51","at":"2023-12-21 14:54:28 + +1100","event":"connect-app","data":{"@id":"http://test.host/api/dfc/enterprises/3","access_token":"12345"}}' + headers: + User-Agent: + - openfoodnetwork_webhook/1.0 + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Thu, 21 Dec 2023 03:54:33 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '141' + Connection: + - keep-alive + Etag: + - W/"8d-Lz10bce6zwT2C429xIkj52OBWyk" + Vary: + - Accept-Encoding + Strict-Transport-Security: + - max-age=63072000 + X-Xss-Protection: + - 1; mode=block + X-Download-Options: + - noopen + X-Content-Type-Options: + - nosniff + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - same-origin + body: + encoding: UTF-8 + string: '{"link":"https://example.net/update","destroy":"https://n8n.openfoodnetwork.org.uk/webhook/remove-enterprise?id=recjBXXXXXXXXXXXX&key=12345"}' + recorded_at: Thu, 21 Dec 2023 03:54:33 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Connected_Apps/can_be_enabled_and_disabled.yml b/spec/fixtures/vcr_cassettes/Connected_Apps/can_be_enabled_and_disabled.yml new file mode 100644 index 0000000000..b968963b7b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Connected_Apps/can_be_enabled_and_disabled.yml @@ -0,0 +1,54 @@ +--- +http_interactions: +- request: + method: post + uri: https://n8n.openfoodnetwork.org.uk/webhook/regen/connect-enterprise + body: + encoding: UTF-8 + string: '{"id":"4da377c8-0c8f-4aaa-8f85-f2a218a13d6e","at":"2023-12-14 12:52:53 + +1100","event":"connect-app","data":{"enterprise_id":45,"access_token":"2c5f828a1da2f5a87798e6d3ee44daee6729b2963db6d264"}}' + headers: + User-Agent: + - openfoodnetwork_webhook/1.0 + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Thu, 14 Dec 2023 01:52:55 GMT + Content-Type: + - text/html; charset=utf-8 + Content-Length: + - '35' + Connection: + - keep-alive + Etag: + - W/"23-GW39X6dSljjgz4GPY7ICa+eNupE" + Vary: + - Accept-Encoding + Strict-Transport-Security: + - max-age=63072000 + X-Xss-Protection: + - 1; mode=block + X-Download-Options: + - noopen + X-Content-Type-Options: + - nosniff + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - same-origin + body: + encoding: UTF-8 + string: '{"link":"https://example.net/edit"}' + recorded_at: Thu, 14 Dec 2023 01:52:55 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/DfcRequest/refreshes_the_access_token_on_fail.yml b/spec/fixtures/vcr_cassettes/DfcRequest/refreshes_the_access_token_on_fail.yml new file mode 100644 index 0000000000..9a14071f73 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/DfcRequest/refreshes_the_access_token_on_fail.yml @@ -0,0 +1,105 @@ +--- +http_interactions: +- request: + method: get + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/.well-known/openid-configuration + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - SWD 2.0.3 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 15 Mar 2024 05:44:06 GMT + Content-Type: + - application/json;charset=UTF-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - Accept-Encoding + Set-Cookie: + - AUTH_SESSION_ID=1710481447.162.5206.870756|6055218c9898cae39f8ffd531999e49a; + Path=/; Secure; HttpOnly + Cache-Control: + - no-cache, must-revalidate, no-transform, no-store + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: ASCII-8BIT + string: '{"issuer":"https://login.lescommuns.org/auth/realms/data-food-consortium","authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth","token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","end_session_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/logout","frontchannel_logout_session_supported":true,"frontchannel_logout_supported":true,"jwks_uri":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/certs","check_session_iframe":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/login-status-iframe.html","grant_types_supported":["authorization_code","implicit","refresh_token","password","client_credentials","urn:openid:params:grant-type:ciba","urn:ietf:params:oauth:grant-type:device_code"],"acr_values_supported":["0","1"],"response_types_supported":["code","none","id_token","token","id_token + token","code id_token","code token","code id_token token"],"subject_types_supported":["public","pairwise"],"id_token_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"id_token_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"id_token_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"userinfo_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"userinfo_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"request_object_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512","none"],"request_object_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"request_object_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"response_modes_supported":["query","fragment","form_post","query.jwt","fragment.jwt","form_post.jwt","jwt"],"registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","token_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"token_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"introspection_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"introspection_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"authorization_encryption_alg_values_supported":["RSA-OAEP","RSA-OAEP-256","RSA1_5"],"authorization_encryption_enc_values_supported":["A256GCM","A192GCM","A128GCM","A128CBC-HS256","A192CBC-HS384","A256CBC-HS512"],"claims_supported":["aud","sub","iss","auth_time","name","given_name","family_name","preferred_username","email","acr"],"claim_types_supported":["normal"],"claims_parameter_supported":true,"scopes_supported":["openid","microprofile-jwt","phone","roles","profile","email","address","web-origins","acr","offline_access"],"request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"code_challenge_methods_supported":["plain","S256"],"tls_client_certificate_bound_access_tokens":true,"revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","revocation_endpoint_auth_methods_supported":["private_key_jwt","client_secret_basic","client_secret_post","tls_client_auth","client_secret_jwt"],"revocation_endpoint_auth_signing_alg_values_supported":["PS384","ES384","RS384","HS256","HS512","ES256","RS256","HS384","ES512","PS256","PS512","RS512"],"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","backchannel_token_delivery_modes_supported":["poll","ping"],"backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth","backchannel_authentication_request_signing_alg_values_supported":["PS384","ES384","RS384","ES256","RS256","ES512","PS256","PS512","RS512"],"require_pushed_authorization_requests":false,"pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","mtls_endpoint_aliases":{"token_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token","revocation_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/revoke","introspection_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token/introspect","device_authorization_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/auth/device","registration_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/clients-registrations/openid-connect","userinfo_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/userinfo","pushed_authorization_request_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/par/request","backchannel_authentication_endpoint":"https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/ext/ciba/auth"},"authorization_response_iss_parameter_supported":true}' + recorded_at: Fri, 15 Mar 2024 05:44:05 GMT +- request: + method: post + uri: https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/token + body: + encoding: UTF-8 + string: grant_type=refresh_token&refresh_token= + headers: + User-Agent: + - Rack::OAuth2 (2.2.1) + Authorization: + - "" + Content-Type: + - application/x-www-form-urlencoded + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 15 Mar 2024 05:44:07 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Vary: + - Accept-Encoding + Set-Cookie: + - AUTH_SESSION_ID=1710481448.492.2309.531618|6055218c9898cae39f8ffd531999e49a; + Path=/; Secure; HttpOnly + Cache-Control: + - no-store + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + body: + encoding: ASCII-8BIT + string: '{"access_token":"","expires_in":1800,"refresh_expires_in":28510621,"refresh_token":"","token_type":"Bearer","id_token":"","not-before-policy":0,"session_state":"989db9a7-584c-4eeb-bff5-db77b53e8def","scope":"openid + profile email"}' + recorded_at: Fri, 15 Mar 2024 05:44:07 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml similarity index 73% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml index cf47db91bd..71c0fb9179 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_CreditCardsController/using_VCR/_new_from_token/when_the_request_to_store_the_customer/card_with_Stripe_is_successful/saves_the_card_locally.yml @@ -8,18 +8,15 @@ http_interactions: string: card[number]=4242424242424242&card[exp_month]=9&card[exp_year]=2024&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -32,11 +29,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:36 GMT + - Mon, 18 Mar 2024 00:14:57 GMT Content-Type: - application/json Content-Length: - - '801' + - '850' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -56,12 +53,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Ftokens; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 6fbc60ba-1a5a-4a98-8e2a-46ca51ca880b + - e6514b0a-784c-4cea-8450-5b4a1af22768 Original-Request: - - req_YJweaF8coHn4N5 + - req_hbqb0vQQUNrdXU Request-Id: - - req_YJweaF8coHn4N5 + - req_hbqb0vQQUNrdXU Stripe-Should-Retry: - 'false' Stripe-Version: @@ -76,10 +75,10 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "tok_1OJPWaKuuB1fWySnYLICe68T", + "id": "tok_1OvTqvKuuB1fWySnEX98PvTy", "object": "token", "card": { - "id": "card_1OJPWaKuuB1fWySnG0BPSQzC", + "id": "card_1OvTqvKuuB1fWySnrmlTcJKr", "object": "card", "address_city": null, "address_country": null, @@ -100,38 +99,38 @@ http_interactions: "last4": "4242", "metadata": {}, "name": null, + "networks": { + "preferred": null + }, "tokenization_method": null, "wallet": null }, - "client_ip": "124.170.116.197", - "created": 1701647796, + "client_ip": "124.188.129.192", + "created": 1710720897, "livemode": false, "type": "card", "used": false } - recorded_at: Sun, 03 Dec 2023 23:56:36 GMT + recorded_at: Mon, 18 Mar 2024 00:14:57 GMT - request: method: post uri: https://api.stripe.com/v1/customers body: encoding: UTF-8 - string: email=ma.rogahn%40feeney.co.uk&source=tok_1OJPWaKuuB1fWySnYLICe68T + string: email=tristan%40dibbertdaniel.co.uk&source=tok_1OvTqvKuuB1fWySnEX98PvTy headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_YJweaF8coHn4N5","request_duration_ms":632}}' + - '{"last_request_metrics":{"request_id":"req_hbqb0vQQUNrdXU","request_duration_ms":538}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -144,11 +143,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:37 GMT + - Mon, 18 Mar 2024 00:14:58 GMT Content-Type: - application/json Content-Length: - - '661' + - '666' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -168,12 +167,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - b57ba34f-6559-4559-9bb1-5d2be42a6cef + - 2db286e4-5eae-4cc4-897d-7d3591f55a95 Original-Request: - - req_rLZMjFmxNUfXYV + - req_S37wLPFxPMlINX Request-Id: - - req_rLZMjFmxNUfXYV + - req_S37wLPFxPMlINX Stripe-Should-Retry: - 'false' Stripe-Version: @@ -188,18 +189,18 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "cus_P7eqsUYTGZ2DWM", + "id": "cus_PkzquAAy75gQda", "object": "customer", "address": null, "balance": 0, - "created": 1701647796, + "created": 1710720898, "currency": null, - "default_source": "card_1OJPWaKuuB1fWySnG0BPSQzC", + "default_source": "card_1OvTqvKuuB1fWySnrmlTcJKr", "delinquent": false, "description": null, "discount": null, - "email": "ma.rogahn@feeney.co.uk", - "invoice_prefix": "CD478CDB", + "email": "tristan@dibbertdaniel.co.uk", + "invoice_prefix": "13C28000", "invoice_settings": { "custom_fields": null, "default_payment_method": null, @@ -216,29 +217,26 @@ http_interactions: "tax_exempt": "none", "test_clock": null } - recorded_at: Sun, 03 Dec 2023 23:56:37 GMT + recorded_at: Mon, 18 Mar 2024 00:14:58 GMT - request: method: get - uri: https://api.stripe.com/v1/customers/cus_P7eqsUYTGZ2DWM/sources?limit=1&object=card + uri: https://api.stripe.com/v1/customers/cus_PkzquAAy75gQda/sources?limit=1&object=card body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_rLZMjFmxNUfXYV","request_duration_ms":879}}' + - '{"last_request_metrics":{"request_id":"req_S37wLPFxPMlINX","request_duration_ms":937}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -251,7 +249,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:37 GMT + - Mon, 18 Mar 2024 00:14:59 GMT Content-Type: - application/json Content-Length: @@ -276,8 +274,10 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Request-Id: - - req_csbcri928ZfdcH + - req_g9XAFj7rXoyJdL Stripe-Version: - '2023-10-16' Vary: @@ -293,7 +293,7 @@ http_interactions: "object": "list", "data": [ { - "id": "card_1OJPWaKuuB1fWySnG0BPSQzC", + "id": "card_1OvTqvKuuB1fWySnrmlTcJKr", "object": "card", "address_city": null, "address_country": null, @@ -305,7 +305,7 @@ http_interactions: "address_zip_check": null, "brand": "Visa", "country": "US", - "customer": "cus_P7eqsUYTGZ2DWM", + "customer": "cus_PkzquAAy75gQda", "cvc_check": "pass", "dynamic_last4": null, "exp_month": 9, @@ -320,7 +320,7 @@ http_interactions: } ], "has_more": false, - "url": "/v1/customers/cus_P7eqsUYTGZ2DWM/sources" + "url": "/v1/customers/cus_PkzquAAy75gQda/sources" } - recorded_at: Sun, 03 Dec 2023 23:56:37 GMT + recorded_at: Mon, 18 Mar 2024 00:14:59 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_credit/refunds_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_credit/refunds_the_payment.yml new file mode 100644 index 0000000000..319d76a1d2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_credit/refunds_the_payment.yml @@ -0,0 +1,838 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=carrot.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_kYJ3h29vQqkIEB","request_duration_ms":422}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:16 GMT + Content-Type: + - application/json + Content-Length: + - '3046' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2147d3e8-fd96-4217-9772-5cf9cb8b8f4c + Original-Request: + - req_Jmj6S8kHu0Oyay + Request-Id: + - req_Jmj6S8kHu0Oyay + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTt94EhW9TXYs3", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710721035, + "default_currency": "aud", + "details_submitted": false, + "email": "carrot.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTt94EhW9TXYs3/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:17:16 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_card_mastercard&payment_method_types[0]=card&capture_method=automatic&confirm=true + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Jmj6S8kHu0Oyay","request_duration_ms":1832}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:18 GMT + Content-Type: + - application/json + Content-Length: + - '1396' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b238a3b1-3a75-4bf0-92a6-9f001ffb90e9 + Original-Request: + - req_Eg0MPkARpYy2HA + Request-Id: + - req_Eg0MPkARpYy2HA + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721037, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTtB4EhW9TXYs30K2JbDsH", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtA4EhW9TXYs3yQzqJZ6R", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:18 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtB4EhW9TXYs30P7DQrvf + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:18 GMT + Content-Type: + - application/json + Content-Length: + - '5160' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_6mchEb8KrwJnDy + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_3OvTtB4EhW9TXYs30K2JbDsH", + "object": "charge", + "amount": 1000, + "amount_captured": 1000, + "amount_refunded": 0, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTtB4EhW9TXYs30OAdKdmx", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721037, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 48, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "payment_method": "pm_1OvTtA4EhW9TXYs3yQzqJZ6R", + "payment_method_details": { + "card": { + "amount_authorized": 1000, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 1000, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUdDk0RWhXOVRYWXMzKI6Q3q8GMgZgMLg51FE6LBa7DhmVzOB7aEaZOIKCLNkc77zPMd7CB-qAQorzTueRz9-lWLN8nu1aowIS", + "refunded": false, + "refunds": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_3OvTtB4EhW9TXYs30K2JbDsH/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_3OvTtB4EhW9TXYs30P7DQrvf" + }, + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721037, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTtB4EhW9TXYs30K2JbDsH", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtA4EhW9TXYs3yQzqJZ6R", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:18 GMT +- request: + method: post + uri: https://api.stripe.com/v1/charges/ch_3OvTtB4EhW9TXYs30K2JbDsH/refunds + body: + encoding: UTF-8 + string: amount=1000&expand[0]=charge + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:20 GMT + Content-Type: + - application/json + Content-Length: + - '4536' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcharges%2F%3Acharge%2Frefunds; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - '05483c67-05ff-49b5-be76-7fac44ee4e85' + Original-Request: + - req_Xf3bGarHmTjryy + Request-Id: + - req_Xf3bGarHmTjryy + Stripe-Account: + - acct_1OvTt94EhW9TXYs3 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "re_3OvTtB4EhW9TXYs304KdB74m", + "object": "refund", + "amount": 1000, + "balance_transaction": "txn_3OvTtB4EhW9TXYs30u93CfDo", + "charge": { + "id": "ch_3OvTtB4EhW9TXYs30K2JbDsH", + "object": "charge", + "amount": 1000, + "amount_captured": 1000, + "amount_refunded": 1000, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTtB4EhW9TXYs30OAdKdmx", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721037, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 48, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "payment_method": "pm_1OvTtA4EhW9TXYs3yQzqJZ6R", + "payment_method_details": { + "card": { + "amount_authorized": 1000, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 1000, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUdDk0RWhXOVRYWXMzKJCQ3q8GMgbaIirMk_Y6LBace1yVWNol5c5enrMWzkoofA6DGu56yIdc3OsN9L51Yush79KvBr6W9ayG", + "refunded": true, + "refunds": { + "object": "list", + "data": [ + { + "id": "re_3OvTtB4EhW9TXYs304KdB74m", + "object": "refund", + "amount": 1000, + "balance_transaction": "txn_3OvTtB4EhW9TXYs30u93CfDo", + "charge": "ch_3OvTtB4EhW9TXYs30K2JbDsH", + "created": 1710721039, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges/ch_3OvTtB4EhW9TXYs30K2JbDsH/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + }, + "created": 1710721039, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTtB4EhW9TXYs30P7DQrvf", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + recorded_at: Mon, 18 Mar 2024 00:17:20 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_error_message/when_payment_intent_state_is_not_in_requires_capture_state/does_not_succeed_if_payment_intent_state_is_not_requires_capture.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_error_message/when_payment_intent_state_is_not_in_requires_capture_state/does_not_succeed_if_payment_intent_state_is_not_requires_capture.yml new file mode 100644 index 0000000000..48da20231c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_error_message/when_payment_intent_state_is_not_in_requires_capture_state/does_not_succeed_if_payment_intent_state_is_not_requires_capture.yml @@ -0,0 +1,378 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Eg0MPkARpYy2HA","request_duration_ms":1493}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:21 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_k0V0K2ZLOcE0Mw + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTtEKuuB1fWySnkwpbbLl4", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721040, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:21 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_1OvTtEKuuB1fWySnkwpbbLl4&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_k0V0K2ZLOcE0Mw","request_duration_ms":371}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:21 GMT + Content-Type: + - application/json + Content-Length: + - '1344' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c4a2ebba-6599-4440-bb2e-b058ef5e66f5 + Original-Request: + - req_xmnXHMTrvfFTUl + Request-Id: + - req_xmnXHMTrvfFTUl + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtFKuuB1fWySn1ZEhZXxY", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721041, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtEKuuB1fWySnkwpbbLl4", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:21 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtFKuuB1fWySn1ZEhZXxY + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_xmnXHMTrvfFTUl","request_duration_ms":466}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:21 GMT + Content-Type: + - application/json + Content-Length: + - '1344' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_B79MaIEhVEEPAQ + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtFKuuB1fWySn1ZEhZXxY", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721041, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtEKuuB1fWySnkwpbbLl4", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:21 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/completes_the_purchase.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/completes_the_purchase.yml new file mode 100644 index 0000000000..6c5b802852 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/completes_the_purchase.yml @@ -0,0 +1,765 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_LAXaOxGMoXOMBE","request_duration_ms":747}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:57 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_8etFHaQHXszxZG + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721017, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:57 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_1OvTsrKuuB1fWySn1NlQqx0m&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_8etFHaQHXszxZG","request_duration_ms":317}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:57 GMT + Content-Type: + - application/json + Content-Length: + - '1344' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3397c0b1-661d-4b21-a457-4ca4ad93c7bf + Original-Request: + - req_V65kJy2UAQwxCI + Request-Id: + - req_V65kJy2UAQwxCI + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsrKuuB1fWySn09EU0Roi", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721017, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:57 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsrKuuB1fWySn09EU0Roi/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_V65kJy2UAQwxCI","request_duration_ms":479}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:58 GMT + Content-Type: + - application/json + Content-Length: + - '1367' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 69b2c079-80ad-43b6-96ae-cf8ee6caa50f + Original-Request: + - req_pL44RyfX6Im79F + Request-Id: + - req_pL44RyfX6Im79F + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsrKuuB1fWySn09EU0Roi", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 1000, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721017, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsrKuuB1fWySn0ADqhOVF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:58 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsrKuuB1fWySn09EU0Roi + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_pL44RyfX6Im79F","request_duration_ms":921}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:59 GMT + Content-Type: + - application/json + Content-Length: + - '1367' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_Je3JZYGqfsoY6v + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsrKuuB1fWySn09EU0Roi", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 1000, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721017, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsrKuuB1fWySn0ADqhOVF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:59 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsrKuuB1fWySn09EU0Roi/capture + body: + encoding: UTF-8 + string: amount_to_capture=1000 + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:01 GMT + Content-Type: + - application/json + Content-Length: + - '5163' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e8c8ab11-efa8-4ae7-a8c2-d0b5afe4171c + Original-Request: + - req_0Cn1MwTlgORHBK + Request-Id: + - req_0Cn1MwTlgORHBK + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsrKuuB1fWySn09EU0Roi", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_3OvTsrKuuB1fWySn0ADqhOVF", + "object": "charge", + "amount": 1000, + "amount_captured": 1000, + "amount_refunded": 0, + "amount_updates": [], + "application": null, + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTsrKuuB1fWySn0fRay9LN", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721018, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 60, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTsrKuuB1fWySn09EU0Roi", + "payment_method": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "payment_method_details": { + "card": { + "amount_authorized": 1000, + "brand": "mastercard", + "capture_before": 1711325818, + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 1000, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xRmlxRXNLdXVCMWZXeVNuKPyP3q8GMgZR9qeMufA6LBYL69eHefKPSJr7NcncC3gdmlCpLJj1stOhBI2VfYSfCoS1QyNenvmjqZVa", + "refunded": false, + "refunds": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_3OvTsrKuuB1fWySn0ADqhOVF/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_3OvTsrKuuB1fWySn09EU0Roi" + }, + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721017, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsrKuuB1fWySn0ADqhOVF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsrKuuB1fWySn1NlQqx0m", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:01 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/provides_an_error_message_to_help_developer_debug.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/provides_an_error_message_to_help_developer_debug.yml new file mode 100644 index 0000000000..2077d1ec88 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_purchase/provides_an_error_message_to_help_developer_debug.yml @@ -0,0 +1,384 @@ +--- +http_interactions: +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Je3JZYGqfsoY6v","request_duration_ms":286}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:01 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_qOImK0gC8EtBKg + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsvKuuB1fWySnmhjmvAfj", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721021, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:01 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_1OvTsvKuuB1fWySnmhjmvAfj&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_qOImK0gC8EtBKg","request_duration_ms":456}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:01 GMT + Content-Type: + - application/json + Content-Length: + - '1344' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 95d1b1ea-2763-4117-a007-53bf7ac839f4 + Original-Request: + - req_pMu6L70PG5BmHc + Request-Id: + - req_pMu6L70PG5BmHc + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsvKuuB1fWySn14C8n9le", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721021, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsvKuuB1fWySnmhjmvAfj", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:01 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsvKuuB1fWySn14C8n9le/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_pMu6L70PG5BmHc","request_duration_ms":509}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:02 GMT + Content-Type: + - application/json + Content-Length: + - '1367' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - fdeff711-2503-49ea-bf8e-c780d6fb38da + Original-Request: + - req_tpFKmpYlEHPZTy + Request-Id: + - req_tpFKmpYlEHPZTy + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsvKuuB1fWySn14C8n9le", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 1000, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721021, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsvKuuB1fWySn1tc7LulR", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsvKuuB1fWySnmhjmvAfj", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:03 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_confirmed_payment/refunds_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_confirmed_payment/refunds_the_payment.yml new file mode 100644 index 0000000000..893fe4bfa5 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_confirmed_payment/refunds_the_payment.yml @@ -0,0 +1,1087 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=carrot.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_tpFKmpYlEHPZTy","request_duration_ms":1026}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:05 GMT + Content-Type: + - application/json + Content-Length: + - '3046' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 985f0eb8-05ab-45a5-8208-da288cbbac45 + Original-Request: + - req_znMjsKJzcmNgAS + Request-Id: + - req_znMjsKJzcmNgAS + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTsx4DXqtLEoR8", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710721024, + "default_currency": "aud", + "details_submitted": false, + "email": "carrot.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTsx4DXqtLEoR8/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:17:05 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_znMjsKJzcmNgAS","request_duration_ms":1766}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:06 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_wyLJZvPiCiFdnm + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTt0KuuB1fWySnUCztcYD7", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721026, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:06 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_card_mastercard&payment_method_types[0]=card&capture_method=automatic&confirm=true + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_wyLJZvPiCiFdnm","request_duration_ms":448}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:07 GMT + Content-Type: + - application/json + Content-Length: + - '1396' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e612060a-27e0-4041-b981-631957bf8016 + Original-Request: + - req_EYLl0zFhfPcJj3 + Request-Id: + - req_EYLl0zFhfPcJj3 + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt04DXqtLEoR80GthblMq", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721026, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt04DXqtLEoR81erYM7xD", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:07 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTt04DXqtLEoR80GthblMq + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_EYLl0zFhfPcJj3","request_duration_ms":1533}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:08 GMT + Content-Type: + - application/json + Content-Length: + - '1396' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_n21ZueS6MuFNLw + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt04DXqtLEoR80GthblMq", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721026, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt04DXqtLEoR81erYM7xD", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:08 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTt04DXqtLEoR80GthblMq + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:08 GMT + Content-Type: + - application/json + Content-Length: + - '5160' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_cBHWzojWArVSj4 + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt04DXqtLEoR80GthblMq", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 1000, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "object": "charge", + "amount": 1000, + "amount_captured": 1000, + "amount_refunded": 0, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTt04DXqtLEoR80PxY1ex2", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721027, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 40, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTt04DXqtLEoR80GthblMq", + "payment_method": "pm_1OvTt04DXqtLEoR81erYM7xD", + "payment_method_details": { + "card": { + "amount_authorized": 1000, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 1000, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUc3g0RFhxdExFb1I4KISQ3q8GMgYbTnjasls6LBbzE7IU_NM3iee_T-sMt21-a4qQf9r_dmHCzVwTEiSA0cE1B4p_hGYPye3Q", + "refunded": false, + "refunds": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_3OvTt04DXqtLEoR80d2uLuNK/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_3OvTt04DXqtLEoR80GthblMq" + }, + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721026, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt04DXqtLEoR81erYM7xD", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:08 GMT +- request: + method: post + uri: https://api.stripe.com/v1/charges/ch_3OvTt04DXqtLEoR80d2uLuNK/refunds + body: + encoding: UTF-8 + string: amount=1000&expand[0]=charge + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:10 GMT + Content-Type: + - application/json + Content-Length: + - '4536' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcharges%2F%3Acharge%2Frefunds; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 333302cd-636e-49b6-838c-569de3326974 + Original-Request: + - req_ApI8tFa43Mgg37 + Request-Id: + - req_ApI8tFa43Mgg37 + Stripe-Account: + - acct_1OvTsx4DXqtLEoR8 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "re_3OvTt04DXqtLEoR8015urEeE", + "object": "refund", + "amount": 1000, + "balance_transaction": "txn_3OvTt04DXqtLEoR80z4jFrdN", + "charge": { + "id": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "object": "charge", + "amount": 1000, + "amount_captured": 1000, + "amount_refunded": 1000, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTt04DXqtLEoR80PxY1ex2", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721027, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 40, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTt04DXqtLEoR80GthblMq", + "payment_method": "pm_1OvTt04DXqtLEoR81erYM7xD", + "payment_method_details": { + "card": { + "amount_authorized": 1000, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 1000, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUc3g0RFhxdExFb1I4KIaQ3q8GMgazDzHjCFg6LBZloYn7cGSHX1hBFAqrxBRr29LSSltqjrbTOFC2Z-eJv485WFoQ65n0mgAh", + "refunded": true, + "refunds": { + "object": "list", + "data": [ + { + "id": "re_3OvTt04DXqtLEoR8015urEeE", + "object": "refund", + "amount": 1000, + "balance_transaction": "txn_3OvTt04DXqtLEoR80z4jFrdN", + "charge": "ch_3OvTt04DXqtLEoR80d2uLuNK", + "created": 1710721029, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTt04DXqtLEoR80GthblMq", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges/ch_3OvTt04DXqtLEoR80d2uLuNK/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + }, + "created": 1710721029, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTt04DXqtLEoR80GthblMq", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + recorded_at: Mon, 18 Mar 2024 00:17:10 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_voidable_payment/void_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_voidable_payment/void_the_payment.yml new file mode 100644 index 0000000000..1fe86c7e5c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Spree_Gateway_StripeSCA/_void/with_a_voidable_payment/void_the_payment.yml @@ -0,0 +1,736 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=carrot.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_n21ZueS6MuFNLw","request_duration_ms":396}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:12 GMT + Content-Type: + - application/json + Content-Length: + - '3046' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d8e003ae-f7b1-4c5a-8ecb-af13cddb4da9 + Original-Request: + - req_jBAjzFqvG3cbm1 + Request-Id: + - req_jBAjzFqvG3cbm1 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTt4QMCDDol8mg", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710721031, + "default_currency": "aud", + "details_submitted": false, + "email": "carrot.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTt4QMCDDol8mg/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:17:12 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jBAjzFqvG3cbm1","request_duration_ms":1728}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:13 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_a5fwaAIdzPpbV7 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTt6KuuB1fWySnlNssK55r", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721032, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:13 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=1000¤cy=aud&payment_method=pm_card_mastercard&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_a5fwaAIdzPpbV7","request_duration_ms":461}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:13 GMT + Content-Type: + - application/json + Content-Length: + - '1377' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a501026b-0adb-41c9-87e5-28788481cc26 + Original-Request: + - req_d2BOLiwt5JKLAU + Request-Id: + - req_d2BOLiwt5JKLAU + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt7QMCDDol8mg0B4AiMn3", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721033, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt7QMCDDol8mgfgbwjUd9", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:13 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTt7QMCDDol8mg0B4AiMn3 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_d2BOLiwt5JKLAU","request_duration_ms":483}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:13 GMT + Content-Type: + - application/json + Content-Length: + - '1377' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_kYJ3h29vQqkIEB + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt7QMCDDol8mg0B4AiMn3", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721033, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt7QMCDDol8mgfgbwjUd9", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:14 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTt7QMCDDol8mg0B4AiMn3/cancel + body: + encoding: US-ASCII + string: '' + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:14 GMT + Content-Type: + - application/json + Content-Length: + - '1541' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcancel; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6593aa6b-f115-4786-9e43-b253222a656a + Original-Request: + - req_XuJWYnH8jq6uQR + Request-Id: + - req_XuJWYnH8jq6uQR + Stripe-Account: + - acct_1OvTt4QMCDDol8mg + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTt7QMCDDol8mg0B4AiMn3", + "object": "payment_intent", + "amount": 1000, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": 1710721034, + "cancellation_reason": null, + "capture_method": "manual", + "charges": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges?payment_intent=pi_3OvTt7QMCDDol8mg0B4AiMn3" + }, + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721033, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTt7QMCDDol8mgfgbwjUd9", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "canceled", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:14 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml new file mode 100644 index 0000000000..f45daa7ad8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml @@ -0,0 +1,91 @@ +--- +http_interactions: +- request: + method: post + uri: https://connect.stripe.com/oauth/deauthorize + body: + encoding: UTF-8 + string: stripe_user_id=&client_id=bogus_client_id + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_B79MaIEhVEEPAQ","request_duration_ms":311}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 401 + message: Unauthorized + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:22 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '96' + Connection: + - keep-alive + Cache-Control: + - max-age=0, no-cache, no-store, must-revalidate + Content-Security-Policy: + - report-uri /csp-report?p=%2Foauth%2Fdeauthorize;block-all-mixed-content;default-src + 'none' 'report-sample';base-uri 'none';form-action 'none';style-src 'unsafe-inline';frame-ancestors + 'self';connect-src 'self';img-src 'self' https://b.stripecdn.com + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Request-Id: + - req_GHrVmVa7FGmpaS + Set-Cookie: + - __Host-session=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; + secure; SameSite=None + - __stripe_orig_props=%7B%22referrer%22%3A%22%22%2C%22landing%22%3A%22https%3A%2F%2Fconnect.stripe.com%2Foauth%2Fdeauthorize%22%7D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:22 GMT; secure; + HttpOnly; SameSite=Lax + - cid=2582bbce-6937-4242-8cf6-c4343f511344; domain=stripe.com; path=/; expires=Sun, + 16 Jun 2024 00:17:22 GMT; secure; SameSite=Lax + - machine_identifier=u08LMPqUrD5B1kp1zW091JTX1MYNXe2GXreB9n879fzGcPyBLxvc1GSNLjOs5sUCJgc%3D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:22 GMT; secure; + HttpOnly; SameSite=Lax + - private_machine_identifier=0RNxLTzWt20%2BdTYyGlYw33oFTzBCS0DcoCPxJ0x4mBQXzB3ZPxI2XLgrMIOu85fwhMI%3D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:22 GMT; secure; + HttpOnly; SameSite=None + - site-auth=; domain=stripe.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 + 00:00:00 GMT; secure + - stripe.csrf=aSus_H8-dkvYjBCLXdh8ntg0xuAW1tkywbt_IwbHZRnT37RbQnHIDIMXzyB345bI1i97FekAm_9ddOCbwUDqrjw-AYTZVJz9GFAUrBpzKH_O3PHD6OAxKYSYiUWLtYNMqKvDFBVQ3g%3D%3D; + domain=stripe.com; path=/; secure; HttpOnly; SameSite=None + Stripe-Kill-Route: + - "[]" + Www-Authenticate: + - Bearer realm="Stripe" + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "error": "invalid_client", + "error_description": "No such application: 'bogus_client_id'" + } + recorded_at: Mon, 18 Mar 2024 00:17:22 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml new file mode 100644 index 0000000000..b52145f516 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml @@ -0,0 +1,294 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=jumping.jack%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_B79MaIEhVEEPAQ","request_duration_ms":311}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:24 GMT + Content-Type: + - application/json + Content-Length: + - '3043' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 788464b4-12cd-4010-9f2c-1f9f6084c894 + Original-Request: + - req_DESgTgTGnGMKSG + Request-Id: + - req_DESgTgTGnGMKSG + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTtGQMp0lyFQ94", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710721043, + "default_currency": "aud", + "details_submitted": false, + "email": "jumping.jack@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTtGQMp0lyFQ94/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:17:24 GMT +- request: + method: post + uri: https://connect.stripe.com/oauth/deauthorize + body: + encoding: UTF-8 + string: stripe_user_id=acct_1OvTtGQMp0lyFQ94&client_id= + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_DESgTgTGnGMKSG","request_duration_ms":1866}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:24 GMT + Content-Type: + - application/json + Content-Length: + - '47' + Connection: + - keep-alive + Cache-Control: + - max-age=0, no-cache, no-store, must-revalidate + Content-Security-Policy: + - report-uri /csp-report?p=%2Foauth%2Fdeauthorize;block-all-mixed-content;default-src + 'none' 'report-sample';base-uri 'none';form-action 'none';style-src 'unsafe-inline';frame-ancestors + 'self';connect-src 'self';img-src 'self' https://b.stripecdn.com + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Request-Id: + - req_NRuOqrZYZsOvY4 + Set-Cookie: + - __Host-session=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; + secure; SameSite=None + - __stripe_orig_props=%7B%22referrer%22%3A%22%22%2C%22landing%22%3A%22https%3A%2F%2Fconnect.stripe.com%2Foauth%2Fdeauthorize%22%7D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:24 GMT; secure; + HttpOnly; SameSite=Lax + - cid=5c415000-74b6-4cc1-a552-9585aa5c3405; domain=stripe.com; path=/; expires=Sun, + 16 Jun 2024 00:17:24 GMT; secure; SameSite=Lax + - machine_identifier=dFbFbEgmqfRTYoQrHRZPV8Viaa3TdXOcyaHzp9%2FbuTwpTJv4Y4UqtYkSdos6I4upkkw%3D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:24 GMT; secure; + HttpOnly; SameSite=Lax + - private_machine_identifier=xE%2BK2%2BlUylTzVVomPbZiN8i0mlFHXzYekKDtQ%2BEsmyvOLfBVPJwXM6vpHxPtl2Vh94g%3D; + domain=stripe.com; path=/; expires=Tue, 18 Mar 2025 00:17:24 GMT; secure; + HttpOnly; SameSite=None + - site-auth=; domain=stripe.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 + 00:00:00 GMT; secure + - stripe.csrf=GqBzZzi63YUV_sTHahtU-rcSjGe9iPImil-9-M3yqjCAc3SZ_sCrydfEB9rbU2glYEHo_MElt8muUgAy2eKuBzw-AYTZVJyqhoBLAXavr-PDpNbPXZ_YEue4llqvZbqHDPxWXPFAvA%3D%3D; + domain=stripe.com; path=/; secure; HttpOnly; SameSite=None + Stripe-Kill-Route: + - "[]" + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "stripe_user_id": "acct_1OvTtGQMp0lyFQ94" + } + recorded_at: Mon, 18 Mar 2024 00:17:24 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_Stripe_payment_has_been_captured/returns_true.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_Stripe_payment_has_been_captured/returns_true.yml new file mode 100644 index 0000000000..fdf43f639a --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_Stripe_payment_has_been_captured/returns_true.yml @@ -0,0 +1,645 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jIiMyuL3cGyXiu","request_duration_ms":1328}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:32 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 83da0cfb-bd95-44fc-afb8-01634ecd8d3a + Original-Request: + - req_25HytWVowVqHlu + Request-Id: + - req_25HytWVowVqHlu + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTtQKuuB1fWySn9aNF8uS7", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721052, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:32 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=aud&payment_method=pm_1OvTtQKuuB1fWySn9aNF8uS7&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_25HytWVowVqHlu","request_duration_ms":598}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:33 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 335e7ba2-193c-4e2a-a7ec-f0b1a4315e1f + Original-Request: + - req_KxWXDLCJ5MaOtW + Request-Id: + - req_KxWXDLCJ5MaOtW + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtRKuuB1fWySn2GrEKUaE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721053, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtQKuuB1fWySn9aNF8uS7", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:33 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtRKuuB1fWySn2GrEKUaE/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_KxWXDLCJ5MaOtW","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:34 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6b124504-22a9-46c7-a601-f68ab1e8c335 + Original-Request: + - req_LZ5CHZyseYpGK2 + Request-Id: + - req_LZ5CHZyseYpGK2 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtRKuuB1fWySn2GrEKUaE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721053, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTtRKuuB1fWySn2luCuMhP", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtQKuuB1fWySn9aNF8uS7", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:34 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtRKuuB1fWySn2GrEKUaE/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_LZ5CHZyseYpGK2","request_duration_ms":920}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:35 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - de418712-e7ef-42be-9659-f8af5a2f4b8a + Original-Request: + - req_lyqI9elLYo4CtZ + Request-Id: + - req_lyqI9elLYo4CtZ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtRKuuB1fWySn2GrEKUaE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721053, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTtRKuuB1fWySn2luCuMhP", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtQKuuB1fWySn9aNF8uS7", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:35 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtRKuuB1fWySn2GrEKUaE + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_lyqI9elLYo4CtZ","request_duration_ms":1227}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:35 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_QLgFMvG0k7taRw + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtRKuuB1fWySn2GrEKUaE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721053, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTtRKuuB1fWySn2luCuMhP", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtQKuuB1fWySn9aNF8uS7", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:35 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_false.yml similarity index 74% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/returns_payment_intent_id_and_does_not_raise.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_false.yml index eea14e86b8..1be5dacc57 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/returns_payment_intent_id_and_does_not_raise.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_captured_/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_false.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_csbcri928ZfdcH","request_duration_ms":383}}' + - '{"last_request_metrics":{"request_id":"req_AUnct1pFCKDHcy","request_duration_ms":472}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:38 GMT + - Mon, 18 Mar 2024 00:17:29 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - f5709309-e423-4316-a53a-efff308a5142 + - e7502dab-d61a-41df-b97b-32475f4c5071 Original-Request: - - req_92pt1iiudNKQXC + - req_24mN6lAsSAN3gG Request-Id: - - req_92pt1iiudNKQXC + - req_24mN6lAsSAN3gG Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWcKuuB1fWySn3lTkJZ7P", + "id": "pm_1OvTtMKuuB1fWySnT962jz7H", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "6E6tgVjx6U65iHFV", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647798, + "created": 1710721049, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:38 GMT + recorded_at: Mon, 18 Mar 2024 00:17:29 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWcKuuB1fWySn3lTkJZ7P&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=aud&payment_method=pm_1OvTtMKuuB1fWySnT962jz7H&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_92pt1iiudNKQXC","request_duration_ms":495}}' + - '{"last_request_metrics":{"request_id":"req_24mN6lAsSAN3gG","request_duration_ms":498}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:38 GMT + - Mon, 18 Mar 2024 00:17:29 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 66769690-c72a-4110-a0f7-d46d5e8acf9b + - 86ac3170-9499-46c4-8658-1f907cade657 Original-Request: - - req_rlc68o8Yad1N0J + - req_VOLcnzKz3tfBOI Request-Id: - - req_rlc68o8Yad1N0J + - req_VOLcnzKz3tfBOI Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWcKuuB1fWySn1TvbLKfQ", + "id": "pi_3OvTtNKuuB1fWySn1GSigJJY", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,10 +216,10 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWcKuuB1fWySn1TvbLKfQ_secret_SD3LOsHFK4C8czYeqCnIA018P", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647798, - "currency": "eur", + "created": 1710721049, + "currency": "aud", "customer": null, "description": null, "invoice": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWcKuuB1fWySn3lTkJZ7P", + "payment_method": "pm_1OvTtMKuuB1fWySnT962jz7H", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:38 GMT + recorded_at: Mon, 18 Mar 2024 00:17:29 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWcKuuB1fWySn1TvbLKfQ/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtNKuuB1fWySn1GSigJJY/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_rlc68o8Yad1N0J","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_VOLcnzKz3tfBOI","request_duration_ms":476}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,7 +286,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:39 GMT + - Mon, 18 Mar 2024 00:17:30 GMT Content-Type: - application/json Content-Length: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - a714cacb-6c93-42b8-8a13-1a4ce00db5de + - e682276e-9ca1-453d-be77-5d1911ace78d Original-Request: - - req_S4T4unZ2XulfdV + - req_BJlSZz2HxFEmzf Request-Id: - - req_S4T4unZ2XulfdV + - req_BJlSZz2HxFEmzf Stripe-Should-Retry: - 'false' Stripe-Version: @@ -335,7 +333,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWcKuuB1fWySn1TvbLKfQ", + "id": "pi_3OvTtNKuuB1fWySn1GSigJJY", "object": "payment_intent", "amount": 100, "amount_capturable": 100, @@ -349,20 +347,20 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWcKuuB1fWySn1TvbLKfQ_secret_SD3LOsHFK4C8czYeqCnIA018P", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647798, - "currency": "eur", + "created": 1710721049, + "currency": "aud", "customer": null, "description": null, "invoice": null, "last_payment_error": null, - "latest_charge": "ch_3OJPWcKuuB1fWySn1iNGVTRZ", + "latest_charge": "ch_3OvTtNKuuB1fWySn1wBO9TUB", "livemode": false, "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWcKuuB1fWySn3lTkJZ7P", + "payment_method": "pm_1OvTtMKuuB1fWySnT962jz7H", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -387,29 +385,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:39 GMT + recorded_at: Mon, 18 Mar 2024 00:17:30 GMT - request: - method: get - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWcKuuB1fWySn1TvbLKfQ + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtNKuuB1fWySn1GSigJJY/capture body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_S4T4unZ2XulfdV","request_duration_ms":920}}' + - '{"last_request_metrics":{"request_id":"req_BJlSZz2HxFEmzf","request_duration_ms":1039}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -422,11 +417,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:40 GMT + - Mon, 18 Mar 2024 00:17:31 GMT Content-Type: - application/json Content-Length: - - '1365' + - '1358' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -443,12 +438,20 @@ http_interactions: Cache-Control: - no-cache, no-store Content-Security-Policy: - - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b2e910a7-a77a-4101-aeac-d1cddd8a00cb + Original-Request: + - req_jIiMyuL3cGyXiu Request-Id: - - req_icnFZXbIUY8Jy4 + - req_jIiMyuL3cGyXiu + Stripe-Should-Retry: + - 'false' Stripe-Version: - '2023-10-16' Vary: @@ -461,34 +464,34 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWcKuuB1fWySn1TvbLKfQ", + "id": "pi_3OvTtNKuuB1fWySn1GSigJJY", "object": "payment_intent", "amount": 100, - "amount_capturable": 100, + "amount_capturable": 0, "amount_details": { "tip": {} }, - "amount_received": 0, + "amount_received": 100, "application": null, "application_fee_amount": null, "automatic_payment_methods": null, "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWcKuuB1fWySn1TvbLKfQ_secret_SD3LOsHFK4C8czYeqCnIA018P", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647798, - "currency": "eur", + "created": 1710721049, + "currency": "aud", "customer": null, "description": null, "invoice": null, "last_payment_error": null, - "latest_charge": "ch_3OJPWcKuuB1fWySn1iNGVTRZ", + "latest_charge": "ch_3OvTtNKuuB1fWySn1wBO9TUB", "livemode": false, "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWcKuuB1fWySn3lTkJZ7P", + "payment_method": "pm_1OvTtMKuuB1fWySnT962jz7H", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -509,9 +512,9 @@ http_interactions: "source": null, "statement_descriptor": null, "statement_descriptor_suffix": null, - "status": "requires_capture", + "status": "succeeded", "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:40 GMT + recorded_at: Mon, 18 Mar 2024 00:17:32 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/and_the_last_action_on_the_Stripe_payment_failed/returns_failed_response.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/and_the_last_action_on_the_Stripe_payment_failed/returns_failed_response.yml new file mode 100644 index 0000000000..6793cd95b2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/and_the_last_action_on_the_Stripe_payment_failed/returns_failed_response.yml @@ -0,0 +1,258 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_fbp76TyEf0Myfo","request_duration_ms":331}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:28 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b721012c-96d9-4601-8673-b65dec902779 + Original-Request: + - req_opUjOcKKxkh5vb + Request-Id: + - req_opUjOcKKxkh5vb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTtLKuuB1fWySnEmIUiiVy", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721048, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:28 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=aud&payment_method=pm_1OvTtLKuuB1fWySnEmIUiiVy&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_opUjOcKKxkh5vb","request_duration_ms":465}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:28 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c4686184-37c6-458f-b749-7792a8a7b005 + Original-Request: + - req_AUnct1pFCKDHcy + Request-Id: + - req_AUnct1pFCKDHcy + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtMKuuB1fWySn0bOj3ofn", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721048, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtLKuuB1fWySnEmIUiiVy", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:28 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/fetches_the_status_with_Stripe_PaymentIntentValidator.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/fetches_the_status_with_Stripe_PaymentIntentValidator.yml new file mode 100644 index 0000000000..16e82d0fe5 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_has_a_payment_intent/fetches_the_status_with_Stripe_PaymentIntentValidator.yml @@ -0,0 +1,383 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_USv3mvc5dCZdDZ","request_duration_ms":502}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:26 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 071b0f81-d3a1-45de-b83c-4d5064292bdf + Original-Request: + - req_p9wvMPEXGsUpLv + Request-Id: + - req_p9wvMPEXGsUpLv + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTtKKuuB1fWySngKDk5AoV", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721046, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:26 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=aud&payment_method=pm_1OvTtKKuuB1fWySngKDk5AoV&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_p9wvMPEXGsUpLv","request_duration_ms":597}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:27 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - de05649f-ee50-4da1-8d5d-23014b4a46bf + Original-Request: + - req_1uqsFUSRNJGcgX + Request-Id: + - req_1uqsFUSRNJGcgX + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtLKuuB1fWySn2FHWVRuE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721047, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtKKuuB1fWySngKDk5AoV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:27 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTtLKuuB1fWySn2FHWVRuE + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1uqsFUSRNJGcgX","request_duration_ms":531}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:27 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_fbp76TyEf0Myfo + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtLKuuB1fWySn2FHWVRuE", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721047, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtKKuuB1fWySngKDk5AoV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:27 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_nil.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_nil.yml new file mode 100644 index 0000000000..0d519648c1 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/StripePaymentStatus/_stripe_status/when_the_payment_is_not_a_Stripe_payment_or_does_not_have_a_payment_intent/returns_nil.yml @@ -0,0 +1,258 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_NRuOqrZYZsOvY4","request_duration_ms":506}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:25 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d5936b5b-28ae-4f15-813e-7eb13cf7ae1e + Original-Request: + - req_4pxrlQQVZcEEnb + Request-Id: + - req_4pxrlQQVZcEEnb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTtJKuuB1fWySnX8xe4FWX", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721045, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:17:25 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=aud&payment_method=pm_1OvTtJKuuB1fWySnX8xe4FWX&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_4pxrlQQVZcEEnb","request_duration_ms":437}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:17:25 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 23d57b84-7c4f-478e-875a-ad784ec21b08 + Original-Request: + - req_USv3mvc5dCZdDZ + Request-Id: + - req_USv3mvc5dCZdDZ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTtJKuuB1fWySn0UQ7dfp2", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721045, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTtJKuuB1fWySnX8xe4FWX", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:17:26 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_card_without_a_customer_one_time_usage_card_/clones_the_payment_method_only.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_card_without_a_customer_one_time_usage_card_/clones_the_payment_method_only.yml new file mode 100644 index 0000000000..5622322d2e --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_card_without_a_customer_one_time_usage_card_/clones_the_payment_method_only.yml @@ -0,0 +1,664 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=8&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_g9XAFj7rXoyJdL","request_duration_ms":284}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:14:59 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3d3ad09e-41e2-4ca5-9295-8da644a90250 + Original-Request: + - req_bBC0qNNWFQPFwx + Request-Id: + - req_bBC0qNNWFQPFwx + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTqxKuuB1fWySnCSkG86bK", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720899, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:14:59 GMT +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=apple.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_bBC0qNNWFQPFwx","request_duration_ms":457}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:01 GMT + Content-Type: + - application/json + Content-Length: + - '3045' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - def56cc1-bfb8-4161-a275-cd846983f9ad + Original-Request: + - req_IFyLI7pm6FNaWz + Request-Id: + - req_IFyLI7pm6FNaWz + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTqy4JnzBWpcWU", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710720900, + "default_currency": "aud", + "details_submitted": false, + "email": "apple.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTqy4JnzBWpcWU/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:15:01 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTqxKuuB1fWySnCSkG86bK + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_IFyLI7pm6FNaWz","request_duration_ms":2141}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:02 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_dSevEFHysAQ7yf + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTqxKuuB1fWySnCSkG86bK", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720899, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:02 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers?email=apple.customer@example.com&limit=100 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_dSevEFHysAQ7yf","request_duration_ms":271}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTqy4JnzBWpcWU + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:02 GMT + Content-Type: + - application/json + Content-Length: + - '83' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_IQWVmgSPe44Ei9 + Stripe-Account: + - acct_1OvTqy4JnzBWpcWU + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "object": "list", + "data": [], + "has_more": false, + "url": "/v1/customers" + } + recorded_at: Mon, 18 Mar 2024 00:15:02 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: payment_method=pm_1OvTqxKuuB1fWySnCSkG86bK + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_IQWVmgSPe44Ei9","request_duration_ms":341}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTqy4JnzBWpcWU + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:02 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 45dffaff-6a12-4ca7-838b-6cb570fd6c20 + Original-Request: + - req_s5SCuqf1YmkWEQ + Request-Id: + - req_s5SCuqf1YmkWEQ + Stripe-Account: + - acct_1OvTqy4JnzBWpcWU + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr04JnzBWpcWUVcpJovNu", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720902, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:03 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_valid_customer_and_payment_method/clones_both_the_payment_method_and_the_customer.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_valid_customer_and_payment_method/clones_both_the_payment_method_and_the_customer.yml new file mode 100644 index 0000000000..5d6f1e9dee --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardCloner/_find_or_clone/when_called_with_a_valid_customer_and_payment_method/clones_both_the_payment_method_and_the_customer.yml @@ -0,0 +1,1268 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=8&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_s5SCuqf1YmkWEQ","request_duration_ms":412}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:03 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 9cc8da25-5ccf-4448-bbc9-265f53be0858 + Original-Request: + - req_yGPPYYDoFt6kNx + Request-Id: + - req_yGPPYYDoFt6kNx + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr1KuuB1fWySnMXLPMlil", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720903, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:03 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: name=Apple+Customer&email=apple.customer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_yGPPYYDoFt6kNx","request_duration_ms":456}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:03 GMT + Content-Type: + - application/json + Content-Length: + - '650' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1173847d-05e2-4685-ae60-45d8d9f801f3 + Original-Request: + - req_lfyMVzX1xo2mso + Request-Id: + - req_lfyMVzX1xo2mso + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_PkzqRCn2t6YtTS", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710720903, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "apple.customer@example.com", + "invoice_prefix": "E1D3A9D5", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": "Apple Customer", + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:15:04 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr1KuuB1fWySnMXLPMlil/attach + body: + encoding: UTF-8 + string: customer=cus_PkzqRCn2t6YtTS + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_lfyMVzX1xo2mso","request_duration_ms":484}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:04 GMT + Content-Type: + - application/json + Content-Length: + - '970' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - edadf19d-d102-442d-ba3b-d3609c1594bc + Original-Request: + - req_VOKM1bRJ9ZgzoW + Request-Id: + - req_VOKM1bRJ9ZgzoW + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr1KuuB1fWySnMXLPMlil", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720903, + "customer": "cus_PkzqRCn2t6YtTS", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:04 GMT +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=apple.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_VOKM1bRJ9ZgzoW","request_duration_ms":740}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:06 GMT + Content-Type: + - application/json + Content-Length: + - '3045' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 4109397a-178a-43b5-bdcd-978f02771a87 + Original-Request: + - req_sEcpXDU4jBEkkq + Request-Id: + - req_sEcpXDU4jBEkkq + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTr24C6BmtoRvb", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710720905, + "default_currency": "aud", + "details_submitted": false, + "email": "apple.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTr24C6BmtoRvb/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:15:06 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr1KuuB1fWySnMXLPMlil + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_sEcpXDU4jBEkkq","request_duration_ms":1655}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:06 GMT + Content-Type: + - application/json + Content-Length: + - '970' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_aM4luej1FH080B + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr1KuuB1fWySnMXLPMlil", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720903, + "customer": "cus_PkzqRCn2t6YtTS", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:06 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers?email=apple.customer@example.com&limit=100 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_aM4luej1FH080B","request_duration_ms":284}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:06 GMT + Content-Type: + - application/json + Content-Length: + - '83' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_cwF2fSEHkH8Ojd + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "object": "list", + "data": [], + "has_more": false, + "url": "/v1/customers" + } + recorded_at: Mon, 18 Mar 2024 00:15:06 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: customer=cus_PkzqRCn2t6YtTS&payment_method=pm_1OvTr1KuuB1fWySnMXLPMlil + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_cwF2fSEHkH8Ojd","request_duration_ms":302}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:07 GMT + Content-Type: + - application/json + Content-Length: + - '954' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1ce67b02-edfd-4b6f-844e-cec1abf59265 + Original-Request: + - req_1jCvDBU6dIkYVR + Request-Id: + - req_1jCvDBU6dIkYVR + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr54C6BmtoRvbFVBgSIi4", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720907, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:07 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: email=apple.customer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1jCvDBU6dIkYVR","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:07 GMT + Content-Type: + - application/json + Content-Length: + - '638' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 4a503310-e9cb-4cb2-b102-c06ee984e9c6 + Original-Request: + - req_Obb9BYdZKNUJeI + Request-Id: + - req_Obb9BYdZKNUJeI + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_Pkzq62KbE37Fmo", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710720907, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "apple.customer@example.com", + "invoice_prefix": "47842A70", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:15:07 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr54C6BmtoRvbFVBgSIi4/attach + body: + encoding: UTF-8 + string: customer=cus_Pkzq62KbE37Fmo + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Obb9BYdZKNUJeI","request_duration_ms":409}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:08 GMT + Content-Type: + - application/json + Content-Length: + - '970' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 0ac93fe8-91ec-4b90-9e2e-b57e461918cd + Original-Request: + - req_jdf4c6lfaxD0Yc + Request-Id: + - req_jdf4c6lfaxD0Yc + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr54C6BmtoRvbFVBgSIi4", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720907, + "customer": "cus_Pkzq62KbE37Fmo", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:08 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr54C6BmtoRvbFVBgSIi4 + body: + encoding: UTF-8 + string: metadata[ofn-clone]=true + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jdf4c6lfaxD0Yc","request_duration_ms":382}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:08 GMT + Content-Type: + - application/json + Content-Length: + - '997' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 80b154ea-c0df-403d-af14-55e6c3c9cdcd + Original-Request: + - req_sVjwduoedlBMUP + Request-Id: + - req_sVjwduoedlBMUP + Stripe-Account: + - acct_1OvTr24C6BmtoRvb + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr54C6BmtoRvbFVBgSIi4", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720907, + "customer": "cus_Pkzq62KbE37Fmo", + "livemode": false, + "metadata": { + "ofn-clone": "true" + }, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:08 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000000..044da8c5a8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_does_not_exist/raises_an_error.yml @@ -0,0 +1,210 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=8&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1B4QZ4hAlW2eCO","request_duration_ms":650}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:12 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - bebef18d-452d-4b28-92a3-8ef784513ccd + Original-Request: + - req_7jejsG3JSQBIRE + Request-Id: + - req_7jejsG3JSQBIRE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrAKuuB1fWySnW3Z0QqtX", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720912, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:12 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/non_existing_customer_id + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_7jejsG3JSQBIRE","request_duration_ms":391}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 404 + message: Not Found + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:13 GMT + Content-Type: + - application/json + Content-Length: + - '339' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers%2F%3Acustomer; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_pao2UbJSanD9Dp + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: | + { + "error": { + "code": "resource_missing", + "doc_url": "https://stripe.com/docs/error-codes/resource-missing", + "message": "No such customer: 'non_existing_customer_id'", + "param": "id", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_pao2UbJSanD9Dp?t=1710720913", + "type": "invalid_request_error" + } + } + recorded_at: Mon, 18 Mar 2024 00:15:13 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_deleted/deletes_the_credit_card_clone.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_deleted/deletes_the_credit_card_clone.yml new file mode 100644 index 0000000000..0a66200f40 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_deleted/deletes_the_credit_card_clone.yml @@ -0,0 +1,360 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=8&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_ypX8qLRDGVuEPU","request_duration_ms":392}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:11 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b347bd9a-8d7d-4358-bce5-470a2f044a61 + Original-Request: + - req_aOo8om3yszVYt6 + Request-Id: + - req_aOo8om3yszVYt6 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr9KuuB1fWySnTjrL4o4W", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720911, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:11 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: name=Apple+Customer&email=applecustomer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_aOo8om3yszVYt6","request_duration_ms":434}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:11 GMT + Content-Type: + - application/json + Content-Length: + - '649' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - f75a7cba-7c9a-4bcb-9f61-4bcc759477e9 + Original-Request: + - req_VDXR9y2kuyd0no + Request-Id: + - req_VDXR9y2kuyd0no + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_Pkzq2qR31aEubD", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710720911, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "applecustomer@example.com", + "invoice_prefix": "D630F7A7", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": "Apple Customer", + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:15:11 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr9KuuB1fWySnTjrL4o4W/attach + body: + encoding: UTF-8 + string: customer=cus_Pkzq2qR31aEubD + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_VDXR9y2kuyd0no","request_duration_ms":415}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:12 GMT + Content-Type: + - application/json + Content-Length: + - '970' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3f63c95b-0b6e-4fcc-b0ad-6802e874777c + Original-Request: + - req_1B4QZ4hAlW2eCO + Request-Id: + - req_1B4QZ4hAlW2eCO + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr9KuuB1fWySnTjrL4o4W", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720911, + "customer": "cus_Pkzq2qR31aEubD", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:12 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_not_deleted/deletes_the_credit_card_clone_and_the_customer.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_not_deleted/deletes_the_credit_card_clone_and_the_customer.yml new file mode 100644 index 0000000000..f2e2d36b94 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_CreditCardRemover/_remove/Stripe_customer_exists/and_is_not_deleted/deletes_the_credit_card_clone_and_the_customer.yml @@ -0,0 +1,538 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=8&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_sVjwduoedlBMUP","request_duration_ms":420}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:09 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b8096734-0645-4c1a-9aa2-6a86ae39a8e3 + Original-Request: + - req_oQTeF6nvN6SdGN + Request-Id: + - req_oQTeF6nvN6SdGN + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr6KuuB1fWySnBoqO3RSP", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720908, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:09 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: name=Apple+Customer&email=applecustomer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_oQTeF6nvN6SdGN","request_duration_ms":443}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:09 GMT + Content-Type: + - application/json + Content-Length: + - '649' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a6578083-e4a0-4d37-8b95-391dd7ef8d3a + Original-Request: + - req_ZiZGFQNamgcRXg + Request-Id: + - req_ZiZGFQNamgcRXg + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_PkzquQLM8Ru9YM", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710720909, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "applecustomer@example.com", + "invoice_prefix": "3A772772", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": "Apple Customer", + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:15:09 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTr6KuuB1fWySnBoqO3RSP/attach + body: + encoding: UTF-8 + string: customer=cus_PkzquQLM8Ru9YM + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_ZiZGFQNamgcRXg","request_duration_ms":426}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:10 GMT + Content-Type: + - application/json + Content-Length: + - '970' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 04a3aa69-7ca7-454c-9749-386fa532cae4 + Original-Request: + - req_vYJTWA0yQoqyY0 + Request-Id: + - req_vYJTWA0yQoqyY0 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTr6KuuB1fWySnBoqO3RSP", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 8, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720908, + "customer": "cus_PkzquQLM8Ru9YM", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:10 GMT +- request: + method: get + uri: https://api.stripe.com/v1/customers/cus_PkzquQLM8Ru9YM + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_vYJTWA0yQoqyY0","request_duration_ms":718}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:10 GMT + Content-Type: + - application/json + Content-Length: + - '649' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers%2F%3Acustomer; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_4yQh3JIMoftz7J + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_PkzquQLM8Ru9YM", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710720909, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "applecustomer@example.com", + "invoice_prefix": "3A772772", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": "Apple Customer", + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:15:10 GMT +- request: + method: delete + uri: https://api.stripe.com/v1/customers/cus_PkzquQLM8Ru9YM + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_4yQh3JIMoftz7J","request_duration_ms":299}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:10 GMT + Content-Type: + - application/json + Content-Length: + - '75' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers%2F%3Acustomer; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_ypX8qLRDGVuEPU + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_PkzquQLM8Ru9YM", + "object": "customer", + "deleted": true + } + recorded_at: Mon, 18 Mar 2024 00:15:10 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 7c3c2c1f93..765201ef99 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Exceeding_velocity_limit_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000006975&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000006975&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_D5ptSBHj23IjUE","request_duration_ms":408}}' + - '{"last_request_metrics":{"request_id":"req_FTty1oLKmkciRM","request_duration_ms":370}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:54 GMT + - Mon, 18 Mar 2024 00:16:51 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 759821e8-8859-4b39-b85a-70771a71b950 + - 31c1564d-41bb-4b36-9c8a-d9d446859137 Original-Request: - - req_l2zKCNooZyf2th + - req_3j0FV5avjWFI2E Request-Id: - - req_l2zKCNooZyf2th + - req_3j0FV5avjWFI2E Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWsKuuB1fWySnVwO5eWL5", + "id": "pm_1OvTskKuuB1fWySnR2olGPyY", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "WoxwxVPUPcg0EjXW", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647814, + "created": 1710721010, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:54 GMT + recorded_at: Mon, 18 Mar 2024 00:16:51 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWsKuuB1fWySnVwO5eWL5&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTskKuuB1fWySnR2olGPyY&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_l2zKCNooZyf2th","request_duration_ms":498}}' + - '{"last_request_metrics":{"request_id":"req_3j0FV5avjWFI2E","request_duration_ms":448}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:55 GMT + - Mon, 18 Mar 2024 00:16:51 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - fc40bcc2-459f-4393-9535-2f06d242c34f + - 7db2a2c2-fc69-438d-beb8-cdd8572e8ebb Original-Request: - - req_0rzygoslpXi9qk + - req_CyTKXekG7OQLuV Request-Id: - - req_0rzygoslpXi9qk + - req_CyTKXekG7OQLuV Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWtKuuB1fWySn221ckSMT", + "id": "pi_3OvTslKuuB1fWySn2Ai8AtTP", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWtKuuB1fWySn221ckSMT_secret_9bvyw60lulsujNmdfcgcalrrv", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647815, + "created": 1710721011, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWsKuuB1fWySnVwO5eWL5", + "payment_method": "pm_1OvTskKuuB1fWySnR2olGPyY", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:55 GMT + recorded_at: Mon, 18 Mar 2024 00:16:51 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWtKuuB1fWySn221ckSMT/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTslKuuB1fWySn2Ai8AtTP/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_0rzygoslpXi9qk","request_duration_ms":510}}' + - '{"last_request_metrics":{"request_id":"req_CyTKXekG7OQLuV","request_duration_ms":459}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:56 GMT + - Mon, 18 Mar 2024 00:16:52 GMT Content-Type: - application/json Content-Length: - - '4872' + - '4942' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 5a553b05-552f-4f61-a928-87c6698ae7db + - e62ca50b-59b3-4950-b5fb-3cdfbd334494 Original-Request: - - req_xZctRZh3XbCjGO + - req_cEPErBWpyI3fXe Request-Id: - - req_xZctRZh3XbCjGO + - req_cEPErBWpyI3fXe Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWtKuuB1fWySn2QgmILjv", + "charge": "ch_3OvTslKuuB1fWySn2Xs3w4BH", "code": "card_declined", "decline_code": "card_velocity_exceeded", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined for making repeated attempts too frequently or exceeding its amount limit.", "payment_intent": { - "id": "pi_3OJPWtKuuB1fWySn221ckSMT", + "id": "pi_3OvTslKuuB1fWySn2Ai8AtTP", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWtKuuB1fWySn221ckSMT_secret_9bvyw60lulsujNmdfcgcalrrv", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647815, + "created": 1710721011, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWtKuuB1fWySn2QgmILjv", + "charge": "ch_3OvTslKuuB1fWySn2Xs3w4BH", "code": "card_declined", "decline_code": "card_velocity_exceeded", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined for making repeated attempts too frequently or exceeding its amount limit.", "payment_method": { - "id": "pm_1OJPWsKuuB1fWySnVwO5eWL5", + "id": "pm_1OvTskKuuB1fWySnR2olGPyY", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "WoxwxVPUPcg0EjXW", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647814, + "created": 1710721010, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWtKuuB1fWySn2QgmILjv", + "latest_charge": "ch_3OvTslKuuB1fWySn2Xs3w4BH", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWsKuuB1fWySnVwO5eWL5", + "id": "pm_1OvTskKuuB1fWySnR2olGPyY", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "WoxwxVPUPcg0EjXW", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647814, + "created": 1710721010, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_xZctRZh3XbCjGO?t=1701647815", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_cEPErBWpyI3fXe?t=1710721011", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:56 GMT + recorded_at: Mon, 18 Mar 2024 00:16:52 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index d2ffd45fbf..0907f0d2d4 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Expired_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000000069&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000000069&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_HHLwPDme4T8e5a","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_oeGLyCRcs5bHid","request_duration_ms":440}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:48 GMT + - Mon, 18 Mar 2024 00:16:45 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 0073c743-14b8-42fe-ac66-ae2123debeac + - 32433daf-3960-4548-8b3a-70fae08fb979 Original-Request: - - req_eGrCNHCK1BRVF5 + - req_WsemZhDyoAK6mW Request-Id: - - req_eGrCNHCK1BRVF5 + - req_WsemZhDyoAK6mW Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWmKuuB1fWySn9ULltFdF", + "id": "pm_1OvTsfKuuB1fWySnNQaJbSj1", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "qpQikrTL7IyNA2rE", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647808, + "created": 1710721005, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:49 GMT + recorded_at: Mon, 18 Mar 2024 00:16:45 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWmKuuB1fWySn9ULltFdF&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsfKuuB1fWySnNQaJbSj1&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_eGrCNHCK1BRVF5","request_duration_ms":501}}' + - '{"last_request_metrics":{"request_id":"req_WsemZhDyoAK6mW","request_duration_ms":434}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:49 GMT + - Mon, 18 Mar 2024 00:16:45 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 7b6176b7-f745-4e59-b31d-9405ca740208 + - 291419df-5c1f-47eb-b442-c612fb4a1f72 Original-Request: - - req_XP5lZ4B7xbSO70 + - req_at7IEHLp7T2iKL Request-Id: - - req_XP5lZ4B7xbSO70 + - req_at7IEHLp7T2iKL Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWnKuuB1fWySn28qr8gJg", + "id": "pi_3OvTsfKuuB1fWySn05I3MyFj", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWnKuuB1fWySn28qr8gJg_secret_aB02g7vByFUhXmSQel4L9ZDqh", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647809, + "created": 1710721005, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWmKuuB1fWySn9ULltFdF", + "payment_method": "pm_1OvTsfKuuB1fWySnNQaJbSj1", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:49 GMT + recorded_at: Mon, 18 Mar 2024 00:16:45 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWnKuuB1fWySn28qr8gJg/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsfKuuB1fWySn05I3MyFj/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_XP5lZ4B7xbSO70","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_at7IEHLp7T2iKL","request_duration_ms":470}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:50 GMT + - Mon, 18 Mar 2024 00:16:46 GMT Content-Type: - application/json Content-Length: - - '4678' + - '4748' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 5ca06732-4e65-465e-9e09-003e7fe918f5 + - 2ad5f071-6576-41ae-8a63-fc36f4899486 Original-Request: - - req_xOOV8TewpisAeY + - req_ZFRdRHyPlL3PnQ Request-Id: - - req_xOOV8TewpisAeY + - req_ZFRdRHyPlL3PnQ Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWnKuuB1fWySn2wrWuncf", + "charge": "ch_3OvTsfKuuB1fWySn0xB2TuIx", "code": "expired_card", "doc_url": "https://stripe.com/docs/error-codes/expired-card", "message": "Your card has expired.", "param": "exp_month", "payment_intent": { - "id": "pi_3OJPWnKuuB1fWySn28qr8gJg", + "id": "pi_3OvTsfKuuB1fWySn05I3MyFj", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWnKuuB1fWySn28qr8gJg_secret_aB02g7vByFUhXmSQel4L9ZDqh", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647809, + "created": 1710721005, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWnKuuB1fWySn2wrWuncf", + "charge": "ch_3OvTsfKuuB1fWySn0xB2TuIx", "code": "expired_card", "doc_url": "https://stripe.com/docs/error-codes/expired-card", "message": "Your card has expired.", "param": "exp_month", "payment_method": { - "id": "pm_1OJPWmKuuB1fWySn9ULltFdF", + "id": "pm_1OvTsfKuuB1fWySnNQaJbSj1", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "qpQikrTL7IyNA2rE", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647808, + "created": 1710721005, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWnKuuB1fWySn2wrWuncf", + "latest_charge": "ch_3OvTsfKuuB1fWySn0xB2TuIx", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWmKuuB1fWySn9ULltFdF", + "id": "pm_1OvTsfKuuB1fWySnNQaJbSj1", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "qpQikrTL7IyNA2rE", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647808, + "created": 1710721005, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_xOOV8TewpisAeY?t=1701647809", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_ZFRdRHyPlL3PnQ?t=1710721005", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:50 GMT + recorded_at: Mon, 18 Mar 2024 00:16:46 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 7bce25457a..094fdad69f 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Generic_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000000002&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000000002&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_icnFZXbIUY8Jy4","request_duration_ms":404}}' + - '{"last_request_metrics":{"request_id":"req_rMYTwbu1M5H7eN","request_duration_ms":337}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:40 GMT + - Mon, 18 Mar 2024 00:16:37 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - a427cec7-6af5-4c46-8206-b1f13808b3db + - e50d37ee-b9e0-4565-9be8-9647eed9aebc Original-Request: - - req_24dumky1SqM65d + - req_dK376gX1VvnQFd Request-Id: - - req_24dumky1SqM65d + - req_dK376gX1VvnQFd Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWeKuuB1fWySnuUuHd6sG", + "id": "pm_1OvTsWKuuB1fWySnOOB4JvEa", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "IKC2ubfpSLuZKsVs", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647800, + "created": 1710720997, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:41 GMT + recorded_at: Mon, 18 Mar 2024 00:16:37 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWeKuuB1fWySnuUuHd6sG&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsWKuuB1fWySnOOB4JvEa&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_24dumky1SqM65d","request_duration_ms":443}}' + - '{"last_request_metrics":{"request_id":"req_dK376gX1VvnQFd","request_duration_ms":594}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:41 GMT + - Mon, 18 Mar 2024 00:16:37 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - e844f2be-8ace-428c-99b5-585076567522 + - 75db1a1e-be42-4ff1-a69d-c1c227f3c804 Original-Request: - - req_X1NZeaz9hVS7Nb + - req_0nflBr539AASIu Request-Id: - - req_X1NZeaz9hVS7Nb + - req_0nflBr539AASIu Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWfKuuB1fWySn0A53Bdfm", + "id": "pi_3OvTsXKuuB1fWySn09hnqIH7", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWfKuuB1fWySn0A53Bdfm_secret_vWHSbJEkeqimHi5honsNHxS9s", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647801, + "created": 1710720997, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWeKuuB1fWySnuUuHd6sG", + "payment_method": "pm_1OvTsWKuuB1fWySnOOB4JvEa", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:41 GMT + recorded_at: Mon, 18 Mar 2024 00:16:37 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWfKuuB1fWySn0A53Bdfm/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsXKuuB1fWySn09hnqIH7/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_X1NZeaz9hVS7Nb","request_duration_ms":459}}' + - '{"last_request_metrics":{"request_id":"req_0nflBr539AASIu","request_duration_ms":409}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:42 GMT + - Mon, 18 Mar 2024 00:16:38 GMT Content-Type: - application/json Content-Length: - - '4710' + - '4780' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 61d971c4-c392-4648-8e0e-e9548bdfe468 + - a131704a-eb0f-4e75-8373-71ddb456afbd Original-Request: - - req_ED3cDOhUtvpFNQ + - req_eu8GEL3fuhSGTN Request-Id: - - req_ED3cDOhUtvpFNQ + - req_eu8GEL3fuhSGTN Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWfKuuB1fWySn0DbK8mhB", + "charge": "ch_3OvTsXKuuB1fWySn0Uu8H5ZX", "code": "card_declined", "decline_code": "generic_decline", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_intent": { - "id": "pi_3OJPWfKuuB1fWySn0A53Bdfm", + "id": "pi_3OvTsXKuuB1fWySn09hnqIH7", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWfKuuB1fWySn0A53Bdfm_secret_vWHSbJEkeqimHi5honsNHxS9s", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647801, + "created": 1710720997, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWfKuuB1fWySn0DbK8mhB", + "charge": "ch_3OvTsXKuuB1fWySn0Uu8H5ZX", "code": "card_declined", "decline_code": "generic_decline", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_method": { - "id": "pm_1OJPWeKuuB1fWySnuUuHd6sG", + "id": "pm_1OvTsWKuuB1fWySnOOB4JvEa", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "IKC2ubfpSLuZKsVs", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647800, + "created": 1710720997, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWfKuuB1fWySn0DbK8mhB", + "latest_charge": "ch_3OvTsXKuuB1fWySn0Uu8H5ZX", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWeKuuB1fWySnuUuHd6sG", + "id": "pm_1OvTsWKuuB1fWySnOOB4JvEa", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "IKC2ubfpSLuZKsVs", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647800, + "created": 1710720997, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_ED3cDOhUtvpFNQ?t=1701647801", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_eu8GEL3fuhSGTN?t=1710720997", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:42 GMT + recorded_at: Mon, 18 Mar 2024 00:16:38 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 669b36648b..f7873bf076 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Incorrect_CVC_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000000127&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000000127&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_XP5lZ4B7xbSO70","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_at7IEHLp7T2iKL","request_duration_ms":470}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:51 GMT + - Mon, 18 Mar 2024 00:16:47 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 426f2ff0-7405-4198-82ea-08f07c00d3cc + - 66946a2e-513f-482d-816a-b9884c637e12 Original-Request: - - req_KxNkKxV4U80Ipq + - req_qSMF0S3thwU9h1 Request-Id: - - req_KxNkKxV4U80Ipq + - req_qSMF0S3thwU9h1 Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWoKuuB1fWySncv0r9OTQ", + "id": "pm_1OvTshKuuB1fWySnl6MBim02", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "eWmxEL5j3bNdPnK5", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647811, + "created": 1710721007, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:51 GMT + recorded_at: Mon, 18 Mar 2024 00:16:47 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWoKuuB1fWySncv0r9OTQ&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTshKuuB1fWySnl6MBim02&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_KxNkKxV4U80Ipq","request_duration_ms":478}}' + - '{"last_request_metrics":{"request_id":"req_qSMF0S3thwU9h1","request_duration_ms":429}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:51 GMT + - Mon, 18 Mar 2024 00:16:47 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - ae7b43f2-5fdf-4ccb-b9a1-97fd6c7fa309 + - 9a7b18c9-ba6f-45ac-9c12-953fe7516667 Original-Request: - - req_E0hGITkzcwFnV4 + - req_F2NBjS4FcqBzcc Request-Id: - - req_E0hGITkzcwFnV4 + - req_F2NBjS4FcqBzcc Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWpKuuB1fWySn1V997EV3", + "id": "pi_3OvTshKuuB1fWySn1ONkqFK3", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWpKuuB1fWySn1V997EV3_secret_cKVEGknbpsn0bowp24n5avXMM", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647811, + "created": 1710721007, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWoKuuB1fWySncv0r9OTQ", + "payment_method": "pm_1OvTshKuuB1fWySnl6MBim02", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:51 GMT + recorded_at: Mon, 18 Mar 2024 00:16:47 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWpKuuB1fWySn1V997EV3/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTshKuuB1fWySn1ONkqFK3/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_E0hGITkzcwFnV4","request_duration_ms":405}}' + - '{"last_request_metrics":{"request_id":"req_F2NBjS4FcqBzcc","request_duration_ms":387}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:52 GMT + - Mon, 18 Mar 2024 00:16:48 GMT Content-Type: - application/json Content-Length: - - '4704' + - '4774' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 9c7c140c-3715-4f37-bff8-6bc63dabc6e7 + - 94ed595b-4319-4ad8-9faf-5133c7c79d4b Original-Request: - - req_b63XBr7naFwld2 + - req_XSGtmMjAhMsd9c Request-Id: - - req_b63XBr7naFwld2 + - req_XSGtmMjAhMsd9c Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWpKuuB1fWySn1tzregij", + "charge": "ch_3OvTshKuuB1fWySn1J2Vm4DC", "code": "incorrect_cvc", "doc_url": "https://stripe.com/docs/error-codes/incorrect-cvc", "message": "Your card's security code is incorrect.", "param": "cvc", "payment_intent": { - "id": "pi_3OJPWpKuuB1fWySn1V997EV3", + "id": "pi_3OvTshKuuB1fWySn1ONkqFK3", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWpKuuB1fWySn1V997EV3_secret_cKVEGknbpsn0bowp24n5avXMM", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647811, + "created": 1710721007, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWpKuuB1fWySn1tzregij", + "charge": "ch_3OvTshKuuB1fWySn1J2Vm4DC", "code": "incorrect_cvc", "doc_url": "https://stripe.com/docs/error-codes/incorrect-cvc", "message": "Your card's security code is incorrect.", "param": "cvc", "payment_method": { - "id": "pm_1OJPWoKuuB1fWySncv0r9OTQ", + "id": "pm_1OvTshKuuB1fWySnl6MBim02", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "fail" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "eWmxEL5j3bNdPnK5", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647811, + "created": 1710721007, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWpKuuB1fWySn1tzregij", + "latest_charge": "ch_3OvTshKuuB1fWySn1J2Vm4DC", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWoKuuB1fWySncv0r9OTQ", + "id": "pm_1OvTshKuuB1fWySnl6MBim02", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "fail" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "eWmxEL5j3bNdPnK5", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647811, + "created": 1710721007, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_b63XBr7naFwld2?t=1701647811", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_XSGtmMjAhMsd9c?t=1710721007", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:52 GMT + recorded_at: Mon, 18 Mar 2024 00:16:48 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 74a315a43f..29f24e442c 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Insufficient_funds_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000009995&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000009995&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_X1NZeaz9hVS7Nb","request_duration_ms":459}}' + - '{"last_request_metrics":{"request_id":"req_0nflBr539AASIu","request_duration_ms":409}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:42 GMT + - Mon, 18 Mar 2024 00:16:39 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - fbb7b09f-7a30-471f-b04b-342181d45513 + - 35b61012-436a-41cc-80c2-b7db23f7c35b Original-Request: - - req_AwBK2psHCIDTcT + - req_zVIOjYxp5zHWU5 Request-Id: - - req_AwBK2psHCIDTcT + - req_zVIOjYxp5zHWU5 Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWgKuuB1fWySnB2gKX2wK", + "id": "pm_1OvTsZKuuB1fWySn38adKrtd", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "O0I0muUGQBJy3p73", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647802, + "created": 1710720999, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:43 GMT + recorded_at: Mon, 18 Mar 2024 00:16:39 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWgKuuB1fWySnB2gKX2wK&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsZKuuB1fWySn38adKrtd&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_AwBK2psHCIDTcT","request_duration_ms":499}}' + - '{"last_request_metrics":{"request_id":"req_zVIOjYxp5zHWU5","request_duration_ms":449}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:43 GMT + - Mon, 18 Mar 2024 00:16:39 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 61cbfa78-eeaa-40da-9c0e-5b0c9eafbe4f + - 67ff2a64-1d6d-4967-9c09-f04c315e2268 Original-Request: - - req_fWWgVAg2XZk58v + - req_DnNTN3UyDvUcFb Request-Id: - - req_fWWgVAg2XZk58v + - req_DnNTN3UyDvUcFb Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWhKuuB1fWySn0DyDJcpY", + "id": "pi_3OvTsZKuuB1fWySn183Uk8Ct", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWhKuuB1fWySn0DyDJcpY_secret_4qxjRsmGHMZ8Uv9LbIcuWWqFb", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647803, + "created": 1710720999, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWgKuuB1fWySnB2gKX2wK", + "payment_method": "pm_1OvTsZKuuB1fWySn38adKrtd", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:43 GMT + recorded_at: Mon, 18 Mar 2024 00:16:39 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWhKuuB1fWySn0DyDJcpY/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsZKuuB1fWySn183Uk8Ct/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_fWWgVAg2XZk58v","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_DnNTN3UyDvUcFb","request_duration_ms":448}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:44 GMT + - Mon, 18 Mar 2024 00:16:40 GMT Content-Type: - application/json Content-Length: - - '4736' + - '4806' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - e2b49f02-fa7c-4745-a9e8-6dc19d3ad1be + - 98e9be9e-def1-47fc-a75e-3b05a2c4e556 Original-Request: - - req_BbOwq5uFABNaEy + - req_rHDaznC22p7wem Request-Id: - - req_BbOwq5uFABNaEy + - req_rHDaznC22p7wem Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWhKuuB1fWySn0nMfC9IB", + "charge": "ch_3OvTsZKuuB1fWySn1H38yAXr", "code": "card_declined", "decline_code": "insufficient_funds", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card has insufficient funds.", "payment_intent": { - "id": "pi_3OJPWhKuuB1fWySn0DyDJcpY", + "id": "pi_3OvTsZKuuB1fWySn183Uk8Ct", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWhKuuB1fWySn0DyDJcpY_secret_4qxjRsmGHMZ8Uv9LbIcuWWqFb", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647803, + "created": 1710720999, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWhKuuB1fWySn0nMfC9IB", + "charge": "ch_3OvTsZKuuB1fWySn1H38yAXr", "code": "card_declined", "decline_code": "insufficient_funds", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card has insufficient funds.", "payment_method": { - "id": "pm_1OJPWgKuuB1fWySnB2gKX2wK", + "id": "pm_1OvTsZKuuB1fWySn38adKrtd", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "O0I0muUGQBJy3p73", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647802, + "created": 1710720999, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWhKuuB1fWySn0nMfC9IB", + "latest_charge": "ch_3OvTsZKuuB1fWySn1H38yAXr", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWgKuuB1fWySnB2gKX2wK", + "id": "pm_1OvTsZKuuB1fWySn38adKrtd", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "O0I0muUGQBJy3p73", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647802, + "created": 1710720999, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_BbOwq5uFABNaEy?t=1701647803", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_rHDaznC22p7wem?t=1710720999", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:44 GMT + recorded_at: Mon, 18 Mar 2024 00:16:40 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 245017910c..4b16b9a2a0 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Lost_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000009987&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000009987&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_fWWgVAg2XZk58v","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_DnNTN3UyDvUcFb","request_duration_ms":448}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:44 GMT + - Mon, 18 Mar 2024 00:16:41 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 3d0e34d1-0d23-4416-b718-6b019b539792 + - 19f5c01a-d28b-454b-930b-6da099348ef3 Original-Request: - - req_lzKqd5Wp0tAgzD + - req_tI7E1R4GUPLRyh Request-Id: - - req_lzKqd5Wp0tAgzD + - req_tI7E1R4GUPLRyh Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWiKuuB1fWySnlD5zueaP", + "id": "pm_1OvTsbKuuB1fWySnL76GiUf9", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "hMDekBwrnWL1oLxe", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647804, + "created": 1710721001, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:45 GMT + recorded_at: Mon, 18 Mar 2024 00:16:41 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWiKuuB1fWySnlD5zueaP&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsbKuuB1fWySnL76GiUf9&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_lzKqd5Wp0tAgzD","request_duration_ms":502}}' + - '{"last_request_metrics":{"request_id":"req_tI7E1R4GUPLRyh","request_duration_ms":437}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:45 GMT + - Mon, 18 Mar 2024 00:16:41 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 479bace4-4814-44cf-89c1-70e9e3d8b2b3 + - f4e1bd20-47fa-4f21-818a-bc1fa31fbf44 Original-Request: - - req_Xin0QLkjWsO6xs + - req_5zdcYe3sjZduBs Request-Id: - - req_Xin0QLkjWsO6xs + - req_5zdcYe3sjZduBs Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWjKuuB1fWySn1wC7ojD9", + "id": "pi_3OvTsbKuuB1fWySn1HGsGt81", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWjKuuB1fWySn1wC7ojD9_secret_upou0iFRqslQbItb3lhC3dF5W", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647805, + "created": 1710721001, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWiKuuB1fWySnlD5zueaP", + "payment_method": "pm_1OvTsbKuuB1fWySnL76GiUf9", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:45 GMT + recorded_at: Mon, 18 Mar 2024 00:16:41 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWjKuuB1fWySn1wC7ojD9/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsbKuuB1fWySn1HGsGt81/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_Xin0QLkjWsO6xs","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_5zdcYe3sjZduBs","request_duration_ms":471}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:46 GMT + - Mon, 18 Mar 2024 00:16:42 GMT Content-Type: - application/json Content-Length: - - '4698' + - '4768' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 5fc94e97-c13c-4bab-a5b6-27322a9ae4d1 + - 5c714dab-23c7-4dee-815e-b6f4194d1bf0 Original-Request: - - req_vGk79GKbs2i5E9 + - req_8XTD2Wch8mKJiH Request-Id: - - req_vGk79GKbs2i5E9 + - req_8XTD2Wch8mKJiH Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWjKuuB1fWySn1SZZ0Q41", + "charge": "ch_3OvTsbKuuB1fWySn1RQHwDgC", "code": "card_declined", "decline_code": "lost_card", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_intent": { - "id": "pi_3OJPWjKuuB1fWySn1wC7ojD9", + "id": "pi_3OvTsbKuuB1fWySn1HGsGt81", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWjKuuB1fWySn1wC7ojD9_secret_upou0iFRqslQbItb3lhC3dF5W", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647805, + "created": 1710721001, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWjKuuB1fWySn1SZZ0Q41", + "charge": "ch_3OvTsbKuuB1fWySn1RQHwDgC", "code": "card_declined", "decline_code": "lost_card", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_method": { - "id": "pm_1OJPWiKuuB1fWySnlD5zueaP", + "id": "pm_1OvTsbKuuB1fWySnL76GiUf9", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "hMDekBwrnWL1oLxe", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647804, + "created": 1710721001, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWjKuuB1fWySn1SZZ0Q41", + "latest_charge": "ch_3OvTsbKuuB1fWySn1RQHwDgC", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWiKuuB1fWySnlD5zueaP", + "id": "pm_1OvTsbKuuB1fWySnL76GiUf9", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "hMDekBwrnWL1oLxe", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647804, + "created": 1710721001, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_vGk79GKbs2i5E9?t=1701647805", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_8XTD2Wch8mKJiH?t=1710721001", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:46 GMT + recorded_at: Mon, 18 Mar 2024 00:16:42 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index 3fe13f3707..55bf038115 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Processing_error_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000000119&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000000119&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_E0hGITkzcwFnV4","request_duration_ms":405}}' + - '{"last_request_metrics":{"request_id":"req_F2NBjS4FcqBzcc","request_duration_ms":387}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:53 GMT + - Mon, 18 Mar 2024 00:16:49 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 68a35a03-943c-4000-8f10-3c1ae418add8 + - f49249aa-d923-40d5-9c5f-5b6c83002c8a Original-Request: - - req_KINDdOrlJDMpyn + - req_F59ww0jNzASmIE Request-Id: - - req_KINDdOrlJDMpyn + - req_F59ww0jNzASmIE Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWqKuuB1fWySnK6hwOzS0", + "id": "pm_1OvTsiKuuB1fWySnNdsdfxyR", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "9HWWxe4EyniQy61z", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647812, + "created": 1710721009, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:53 GMT + recorded_at: Mon, 18 Mar 2024 00:16:49 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWqKuuB1fWySnK6hwOzS0&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsiKuuB1fWySnNdsdfxyR&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_KINDdOrlJDMpyn","request_duration_ms":478}}' + - '{"last_request_metrics":{"request_id":"req_F59ww0jNzASmIE","request_duration_ms":470}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:53 GMT + - Mon, 18 Mar 2024 00:16:49 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - b07ecd9e-1dfa-4268-bd9d-200406c4a415 + - 5772c367-06eb-4924-9626-347c3c6bf678 Original-Request: - - req_D5ptSBHj23IjUE + - req_FTty1oLKmkciRM Request-Id: - - req_D5ptSBHj23IjUE + - req_FTty1oLKmkciRM Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWrKuuB1fWySn0UpMWk65", + "id": "pi_3OvTsjKuuB1fWySn0kEgb4cI", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWrKuuB1fWySn0UpMWk65_secret_IcYlIeNZUK3Kus0a5haL8zeHg", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647813, + "created": 1710721009, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWqKuuB1fWySnK6hwOzS0", + "payment_method": "pm_1OvTsiKuuB1fWySnNdsdfxyR", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:53 GMT + recorded_at: Mon, 18 Mar 2024 00:16:49 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWrKuuB1fWySn0UpMWk65/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsjKuuB1fWySn0kEgb4cI/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_D5ptSBHj23IjUE","request_duration_ms":408}}' + - '{"last_request_metrics":{"request_id":"req_FTty1oLKmkciRM","request_duration_ms":370}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:54 GMT + - Mon, 18 Mar 2024 00:16:50 GMT Content-Type: - application/json Content-Length: - - '4738' + - '4808' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - d63884cc-27d6-4c85-ba98-0b10e55bc795 + - ee11ff7c-b8c5-4170-b8bc-52f2bf3ea7ba Original-Request: - - req_bB3bAySTJJWh7k + - req_ChQ33Qh1ZKiZAY Request-Id: - - req_bB3bAySTJJWh7k + - req_ChQ33Qh1ZKiZAY Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,12 +334,12 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWrKuuB1fWySn0ELr018N", + "charge": "ch_3OvTsjKuuB1fWySn0uxfNIGe", "code": "processing_error", "doc_url": "https://stripe.com/docs/error-codes/processing-error", "message": "An error occurred while processing your card. Try again in a little bit.", "payment_intent": { - "id": "pi_3OJPWrKuuB1fWySn0UpMWk65", + "id": "pi_3OvTsjKuuB1fWySn0kEgb4cI", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -356,20 +354,20 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWrKuuB1fWySn0UpMWk65_secret_IcYlIeNZUK3Kus0a5haL8zeHg", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647813, + "created": 1710721009, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWrKuuB1fWySn0ELr018N", + "charge": "ch_3OvTsjKuuB1fWySn0uxfNIGe", "code": "processing_error", "doc_url": "https://stripe.com/docs/error-codes/processing-error", "message": "An error occurred while processing your card. Try again in a little bit.", "payment_method": { - "id": "pm_1OJPWqKuuB1fWySnK6hwOzS0", + "id": "pm_1OvTsiKuuB1fWySnNdsdfxyR", "object": "payment_method", "billing_details": { "address": { @@ -392,8 +390,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "9HWWxe4EyniQy61z", "funding": "credit", "generated_from": null, @@ -409,7 +408,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647812, + "created": 1710721009, "customer": null, "livemode": false, "metadata": { @@ -418,7 +417,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWrKuuB1fWySn0ELr018N", + "latest_charge": "ch_3OvTsjKuuB1fWySn0uxfNIGe", "livemode": false, "metadata": { }, @@ -450,7 +449,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWqKuuB1fWySnK6hwOzS0", + "id": "pm_1OvTsiKuuB1fWySnNdsdfxyR", "object": "payment_method", "billing_details": { "address": { @@ -473,8 +472,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "9HWWxe4EyniQy61z", "funding": "credit", "generated_from": null, @@ -490,16 +490,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647812, + "created": 1710721009, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_bB3bAySTJJWh7k?t=1701647813", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_ChQ33Qh1ZKiZAY?t=1710721009", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:54 GMT + recorded_at: Mon, 18 Mar 2024 00:16:50 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml similarity index 79% rename from spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml rename to spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml index ef21aa782c..cc3ab50c6a 100644 --- a/spec/fixtures/vcr_cassettes/Stripe-v10.2.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_invalid/invalid_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Stolen_card_decline/raises_Stripe_error_with_payment_intent_last_payment_error_as_message.yml @@ -5,23 +5,20 @@ http_interactions: uri: https://api.stripe.com/v1/payment_methods body: encoding: UTF-8 - string: type=card&card[number]=4000000000009979&card[exp_month]=12&card[exp_year]=2024&card[cvc]=314 + string: type=card&card[number]=4000000000009979&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_Xin0QLkjWsO6xs","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_5zdcYe3sjZduBs","request_duration_ms":471}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -34,11 +31,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:46 GMT + - Mon, 18 Mar 2024 00:16:43 GMT Content-Type: - application/json Content-Length: - - '931' + - '960' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -58,12 +55,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - ba68a83f-3960-4e9f-91c2-88b05bd9d415 + - 79e61cbc-801b-4498-824d-65edcc42e41b Original-Request: - - req_CJf7vjTDu1OURG + - req_Fo4qtgYiOPMmoN Request-Id: - - req_CJf7vjTDu1OURG + - req_Fo4qtgYiOPMmoN Stripe-Should-Retry: - 'false' Stripe-Version: @@ -78,7 +77,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pm_1OJPWkKuuB1fWySnMF43raHg", + "id": "pm_1OvTsdKuuB1fWySnbf8DElDr", "object": "payment_method", "billing_details": { "address": { @@ -101,8 +100,9 @@ http_interactions: "cvc_check": "unchecked" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "1pjhEFFOW1eCi1AB", "funding": "credit", "generated_from": null, @@ -118,35 +118,32 @@ http_interactions: }, "wallet": null }, - "created": 1701647806, + "created": 1710721003, "customer": null, "livemode": false, "metadata": {}, "type": "card" } - recorded_at: Sun, 03 Dec 2023 23:56:47 GMT + recorded_at: Mon, 18 Mar 2024 00:16:43 GMT - request: method: post uri: https://api.stripe.com/v1/payment_intents body: encoding: UTF-8 - string: amount=100¤cy=eur&payment_method=pm_1OJPWkKuuB1fWySnMF43raHg&payment_method_types[0]=card&capture_method=manual + string: amount=100¤cy=eur&payment_method=pm_1OvTsdKuuB1fWySnbf8DElDr&payment_method_types[0]=card&capture_method=manual headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_CJf7vjTDu1OURG","request_duration_ms":499}}' + - '{"last_request_metrics":{"request_id":"req_Fo4qtgYiOPMmoN","request_duration_ms":467}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -159,7 +156,7 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:47 GMT + - Mon, 18 Mar 2024 00:16:43 GMT Content-Type: - application/json Content-Length: @@ -183,12 +180,14 @@ http_interactions: - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 3d08f88b-4172-4370-9a15-bfd73bd80ff8 + - 9995bc21-6de2-4484-9d3e-d5515641d2b9 Original-Request: - - req_HHLwPDme4T8e5a + - req_oeGLyCRcs5bHid Request-Id: - - req_HHLwPDme4T8e5a + - req_oeGLyCRcs5bHid Stripe-Should-Retry: - 'false' Stripe-Version: @@ -203,7 +202,7 @@ http_interactions: encoding: UTF-8 string: |- { - "id": "pi_3OJPWlKuuB1fWySn0kDMpXrl", + "id": "pi_3OvTsdKuuB1fWySn0MgByDPp", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -217,9 +216,9 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWlKuuB1fWySn0kDMpXrl_secret_MGpYMNEzjObvLhQmAzfSHKO7s", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647807, + "created": 1710721003, "currency": "eur", "customer": null, "description": null, @@ -230,7 +229,7 @@ http_interactions: "metadata": {}, "next_action": null, "on_behalf_of": null, - "payment_method": "pm_1OJPWkKuuB1fWySnMF43raHg", + "payment_method": "pm_1OvTsdKuuB1fWySnbf8DElDr", "payment_method_configuration_details": null, "payment_method_options": { "card": { @@ -255,29 +254,26 @@ http_interactions: "transfer_data": null, "transfer_group": null } - recorded_at: Sun, 03 Dec 2023 23:56:47 GMT + recorded_at: Mon, 18 Mar 2024 00:16:43 GMT - request: method: post - uri: https://api.stripe.com/v1/payment_intents/pi_3OJPWlKuuB1fWySn0kDMpXrl/confirm + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsdKuuB1fWySn0MgByDPp/confirm body: encoding: US-ASCII string: '' headers: User-Agent: - - Stripe/v1 RubyBindings/10.2.0 + - Stripe/v1 RubyBindings/10.12.0 Authorization: - - Bearer + - Bearer Content-Type: - application/x-www-form-urlencoded X-Stripe-Client-Telemetry: - - '{"last_request_metrics":{"request_id":"req_HHLwPDme4T8e5a","request_duration_ms":508}}' + - '{"last_request_metrics":{"request_id":"req_oeGLyCRcs5bHid","request_duration_ms":440}}' Stripe-Version: - '2023-10-16' X-Stripe-Client-User-Agent: - - '{"bindings_version":"10.2.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux - version 6.1.0-13-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14) - 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian - 6.1.55-1 (2023-09-29)","hostname":"blackbox"}' + - "" Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -290,11 +286,11 @@ http_interactions: Server: - nginx Date: - - Sun, 03 Dec 2023 23:56:48 GMT + - Mon, 18 Mar 2024 00:16:44 GMT Content-Type: - application/json Content-Length: - - '4702' + - '4772' Connection: - keep-alive Access-Control-Allow-Credentials: @@ -315,12 +311,14 @@ http_interactions: block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report Idempotency-Key: - - 0ab61f5a-6e62-4afc-a225-2d567b39cdf1 + - 6651e15d-ff33-43f3-a076-c1e70e5e6105 Original-Request: - - req_mDCYshSZmrPA56 + - req_XSMdJvSz6syccz Request-Id: - - req_mDCYshSZmrPA56 + - req_XSMdJvSz6syccz Stripe-Should-Retry: - 'false' Stripe-Version: @@ -336,13 +334,13 @@ http_interactions: string: | { "error": { - "charge": "ch_3OJPWlKuuB1fWySn058jBdVv", + "charge": "ch_3OvTsdKuuB1fWySn0LHjFCev", "code": "card_declined", "decline_code": "stolen_card", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_intent": { - "id": "pi_3OJPWlKuuB1fWySn0kDMpXrl", + "id": "pi_3OvTsdKuuB1fWySn0MgByDPp", "object": "payment_intent", "amount": 100, "amount_capturable": 0, @@ -357,21 +355,21 @@ http_interactions: "canceled_at": null, "cancellation_reason": null, "capture_method": "manual", - "client_secret": "pi_3OJPWlKuuB1fWySn0kDMpXrl_secret_MGpYMNEzjObvLhQmAzfSHKO7s", + "client_secret": "", "confirmation_method": "automatic", - "created": 1701647807, + "created": 1710721003, "currency": "eur", "customer": null, "description": null, "invoice": null, "last_payment_error": { - "charge": "ch_3OJPWlKuuB1fWySn058jBdVv", + "charge": "ch_3OvTsdKuuB1fWySn0LHjFCev", "code": "card_declined", "decline_code": "stolen_card", "doc_url": "https://stripe.com/docs/error-codes/card-declined", "message": "Your card was declined.", "payment_method": { - "id": "pm_1OJPWkKuuB1fWySnMF43raHg", + "id": "pm_1OvTsdKuuB1fWySnbf8DElDr", "object": "payment_method", "billing_details": { "address": { @@ -394,8 +392,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "1pjhEFFOW1eCi1AB", "funding": "credit", "generated_from": null, @@ -411,7 +410,7 @@ http_interactions: }, "wallet": null }, - "created": 1701647806, + "created": 1710721003, "customer": null, "livemode": false, "metadata": { @@ -420,7 +419,7 @@ http_interactions: }, "type": "card_error" }, - "latest_charge": "ch_3OJPWlKuuB1fWySn058jBdVv", + "latest_charge": "ch_3OvTsdKuuB1fWySn0LHjFCev", "livemode": false, "metadata": { }, @@ -452,7 +451,7 @@ http_interactions: "transfer_group": null }, "payment_method": { - "id": "pm_1OJPWkKuuB1fWySnMF43raHg", + "id": "pm_1OvTsdKuuB1fWySnbf8DElDr", "object": "payment_method", "billing_details": { "address": { @@ -475,8 +474,9 @@ http_interactions: "cvc_check": "pass" }, "country": "US", + "display_brand": "visa", "exp_month": 12, - "exp_year": 2024, + "exp_year": 2025, "fingerprint": "1pjhEFFOW1eCi1AB", "funding": "credit", "generated_from": null, @@ -492,16 +492,16 @@ http_interactions: }, "wallet": null }, - "created": 1701647806, + "created": 1710721003, "customer": null, "livemode": false, "metadata": { }, "type": "card" }, - "request_log_url": "https://dashboard.stripe.com/test/logs/req_mDCYshSZmrPA56?t=1701647807", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_XSMdJvSz6syccz?t=1710721003", "type": "card_error" } } - recorded_at: Sun, 03 Dec 2023 23:56:48 GMT + recorded_at: Mon, 18 Mar 2024 00:16:44 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/captures_the_payment.yml new file mode 100644 index 0000000000..3693409f8f --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=378282246310005&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_gda2k7gbAL9VMv","request_duration_ms":927}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:49 GMT + Content-Type: + - application/json + Content-Length: + - '973' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d020a87f-0d88-455e-99ee-d420375d9c29 + Original-Request: + - req_GrzKVJverAWs1N + Request-Id: + - req_GrzKVJverAWs1N + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "amex", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "american_express", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "zYCOiuhqkk4w2g2M", + "funding": "credit", + "generated_from": null, + "last4": "0005", + "networks": { + "available": [ + "amex" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720949, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:49 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrkKuuB1fWySn7zhPa7J5&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_GrzKVJverAWs1N","request_duration_ms":441}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:49 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b7d93f26-46fc-4680-a24b-0ab9710f1bc3 + Original-Request: + - req_8pkLZgQ4FyDILv + Request-Id: + - req_8pkLZgQ4FyDILv + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrlKuuB1fWySn0UWvAFQv", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720949, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:49 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrlKuuB1fWySn0UWvAFQv/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_8pkLZgQ4FyDILv","request_duration_ms":445}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:50 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b080eed9-3fce-484e-badb-a58d5eddcd25 + Original-Request: + - req_xyLG0KtSD7SWDN + Request-Id: + - req_xyLG0KtSD7SWDN + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrlKuuB1fWySn0UWvAFQv", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720949, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrlKuuB1fWySn0JYcsknx", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:50 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrlKuuB1fWySn0UWvAFQv + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_xyLG0KtSD7SWDN","request_duration_ms":974}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:50 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_ZCwfGMV65edTDC + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrlKuuB1fWySn0UWvAFQv", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720949, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrlKuuB1fWySn0JYcsknx", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:50 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrlKuuB1fWySn0UWvAFQv/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_ZCwfGMV65edTDC","request_duration_ms":290}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:51 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 06a0a71a-a5d4-4516-82c6-4df5c3e01b67 + Original-Request: + - req_KMjbvDPh0hR90a + Request-Id: + - req_KMjbvDPh0hR90a + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrlKuuB1fWySn0UWvAFQv", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720949, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrlKuuB1fWySn0JYcsknx", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:51 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrlKuuB1fWySn0UWvAFQv + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_KMjbvDPh0hR90a","request_duration_ms":985}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:52 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_8vqX4xpyNVZMxF + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrlKuuB1fWySn0UWvAFQv", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720949, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrlKuuB1fWySn0JYcsknx", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrkKuuB1fWySn7zhPa7J5", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:52 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..7a585189e1 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_American_Express/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=378282246310005&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_b7rfB7ZShjT8E0","request_duration_ms":307}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:47 GMT + Content-Type: + - application/json + Content-Length: + - '973' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a2a8eab9-4a71-402e-98e5-a611a641e252 + Original-Request: + - req_OJgRWYVT250hSM + Request-Id: + - req_OJgRWYVT250hSM + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrjKuuB1fWySnJEnMHZYn", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "amex", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "american_express", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "zYCOiuhqkk4w2g2M", + "funding": "credit", + "generated_from": null, + "last4": "0005", + "networks": { + "available": [ + "amex" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720947, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:47 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrjKuuB1fWySnJEnMHZYn&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_OJgRWYVT250hSM","request_duration_ms":437}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:47 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 15ecd09b-2259-4c0a-a149-04db606d157f + Original-Request: + - req_BrR0DKbyEthfeW + Request-Id: + - req_BrR0DKbyEthfeW + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrjKuuB1fWySn0F1EPBzd", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720947, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrjKuuB1fWySnJEnMHZYn", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:47 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrjKuuB1fWySn0F1EPBzd/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_BrR0DKbyEthfeW","request_duration_ms":361}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:48 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 993b2154-6e15-4060-90fc-86ffb7287451 + Original-Request: + - req_gda2k7gbAL9VMv + Request-Id: + - req_gda2k7gbAL9VMv + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrjKuuB1fWySn0F1EPBzd", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720947, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrjKuuB1fWySn0nhLuf6P", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrjKuuB1fWySnJEnMHZYn", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:48 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/captures_the_payment.yml new file mode 100644 index 0000000000..03b44d3035 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6555900000604105&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Sm42Wtb00Gaohq","request_duration_ms":931}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:16 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3bb6dc8d-41c9-42ec-a498-fc95301be844 + Original-Request: + - req_JsMyoeCnvU76V4 + Request-Id: + - req_JsMyoeCnvU76V4 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "7NZ8adObS8Rw8HOq", + "funding": "credit", + "generated_from": null, + "last4": "4105", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720976, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:16 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsCKuuB1fWySnc9M8OPUu&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_JsMyoeCnvU76V4","request_duration_ms":429}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:16 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d1441f97-ee80-456d-aa83-4cff438bff32 + Original-Request: + - req_kQ4mIbaiPq4RIN + Request-Id: + - req_kQ4mIbaiPq4RIN + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsCKuuB1fWySn2n8pFsQO", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720976, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:17 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsCKuuB1fWySn2n8pFsQO/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_kQ4mIbaiPq4RIN","request_duration_ms":496}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:18 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 9c39a296-fb99-4c74-871d-d500ede04f14 + Original-Request: + - req_SB4DzXMFi50gTk + Request-Id: + - req_SB4DzXMFi50gTk + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsCKuuB1fWySn2n8pFsQO", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720976, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsCKuuB1fWySn2YJt7xAr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:18 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsCKuuB1fWySn2n8pFsQO + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_SB4DzXMFi50gTk","request_duration_ms":1023}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:18 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_gOcDrrD6YXTUl1 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsCKuuB1fWySn2n8pFsQO", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720976, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsCKuuB1fWySn2YJt7xAr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:18 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsCKuuB1fWySn2n8pFsQO/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_gOcDrrD6YXTUl1","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:19 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 0b229f0e-8878-41c6-84e7-91ade34bba6f + Original-Request: + - req_vTmo6BUCETnxfE + Request-Id: + - req_vTmo6BUCETnxfE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsCKuuB1fWySn2n8pFsQO", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720976, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsCKuuB1fWySn2YJt7xAr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:19 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsCKuuB1fWySn2n8pFsQO + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_vTmo6BUCETnxfE","request_duration_ms":1023}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:19 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_0NP9abiv73dbAY + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsCKuuB1fWySn2n8pFsQO", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720976, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsCKuuB1fWySn2YJt7xAr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsCKuuB1fWySnc9M8OPUu", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:19 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..468cd094b9 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_BCcard_and_DinaCard/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6555900000604105&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_lAQZo297GkbSla","request_duration_ms":274}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:14 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2c676138-f27a-4784-8ccb-ecedf837d08f + Original-Request: + - req_3On9wtXeBvLsBh + Request-Id: + - req_3On9wtXeBvLsBh + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsAKuuB1fWySn1ITYdALk", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "7NZ8adObS8Rw8HOq", + "funding": "credit", + "generated_from": null, + "last4": "4105", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720974, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:14 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsAKuuB1fWySn1ITYdALk&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_3On9wtXeBvLsBh","request_duration_ms":430}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:15 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c67ef120-22fb-4da3-a19e-5ea869102628 + Original-Request: + - req_fV52kLYGOr4KUT + Request-Id: + - req_fV52kLYGOr4KUT + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsAKuuB1fWySn0SMsGIat", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720974, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsAKuuB1fWySn1ITYdALk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:15 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsAKuuB1fWySn0SMsGIat/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_fV52kLYGOr4KUT","request_duration_ms":395}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:15 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6e87773a-bc50-4fb1-ac55-3f07068563c2 + Original-Request: + - req_Sm42Wtb00Gaohq + Request-Id: + - req_Sm42Wtb00Gaohq + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsAKuuB1fWySn0SMsGIat", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720974, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsAKuuB1fWySn09czHrmY", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsAKuuB1fWySn1ITYdALk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:16 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/captures_the_payment.yml new file mode 100644 index 0000000000..2098f6be2a --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=3056930009020004&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_iZ6RNIQNWljp8W","request_duration_ms":1021}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:05 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7a7f6d9e-b596-44f0-8b09-e3f3655c7b9e + Original-Request: + - req_xoXtZBtUNqxjtT + Request-Id: + - req_xoXtZBtUNqxjtT + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "diners", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "diners_club", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "8CvV2XFCUY7eGw6O", + "funding": "credit", + "generated_from": null, + "last4": "0004", + "networks": { + "available": [ + "diners" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720965, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:05 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTs1KuuB1fWySne1dgVDRG&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_xoXtZBtUNqxjtT","request_duration_ms":529}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:06 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6b53d4b9-9ed9-470d-a694-a5d740489913 + Original-Request: + - req_GPY1Ljma4kYOq0 + Request-Id: + - req_GPY1Ljma4kYOq0 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs1KuuB1fWySn1S9ruLf8", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720965, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:06 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs1KuuB1fWySn1S9ruLf8/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_GPY1Ljma4kYOq0","request_duration_ms":509}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:07 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6ece7036-540b-4427-98de-f95e3ed5fc17 + Original-Request: + - req_p4FuOn88owd471 + Request-Id: + - req_p4FuOn88owd471 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs1KuuB1fWySn1S9ruLf8", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720965, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs1KuuB1fWySn1ZspPK6I", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:07 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs1KuuB1fWySn1S9ruLf8 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_p4FuOn88owd471","request_duration_ms":943}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:07 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_D656CCebXucvGt + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs1KuuB1fWySn1S9ruLf8", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720965, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs1KuuB1fWySn1ZspPK6I", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:07 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs1KuuB1fWySn1S9ruLf8/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_D656CCebXucvGt","request_duration_ms":291}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:08 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 103d880a-8c89-4419-9895-2d0390cf771f + Original-Request: + - req_b62GR8S8sa9B4w + Request-Id: + - req_b62GR8S8sa9B4w + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs1KuuB1fWySn1S9ruLf8", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720965, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs1KuuB1fWySn1ZspPK6I", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:08 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs1KuuB1fWySn1S9ruLf8 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_b62GR8S8sa9B4w","request_duration_ms":943}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:08 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_yDW6lOmTkBf8DQ + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs1KuuB1fWySn1S9ruLf8", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720965, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs1KuuB1fWySn1ZspPK6I", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs1KuuB1fWySne1dgVDRG", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:08 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..799234ba48 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=3056930009020004&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_XSjJwBtpszuVl9","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:03 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - fbc6e5a6-8901-4d34-822a-5e50d3edbff0 + Original-Request: + - req_lW3GU1K1MMOjCh + Request-Id: + - req_lW3GU1K1MMOjCh + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrzKuuB1fWySn7pSJbRQ4", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "diners", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "diners_club", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "8CvV2XFCUY7eGw6O", + "funding": "credit", + "generated_from": null, + "last4": "0004", + "networks": { + "available": [ + "diners" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720963, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:03 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrzKuuB1fWySn7pSJbRQ4&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_lW3GU1K1MMOjCh","request_duration_ms":498}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:04 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ab368c58-df2c-4386-9f1b-65f25cf00bf7 + Original-Request: + - req_NevU5UX8s1SAiu + Request-Id: + - req_NevU5UX8s1SAiu + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrzKuuB1fWySn2XCJcxNT", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720963, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrzKuuB1fWySn7pSJbRQ4", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:04 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrzKuuB1fWySn2XCJcxNT/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_NevU5UX8s1SAiu","request_duration_ms":409}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:05 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a1635647-2bc2-4123-9b0b-e282c8479104 + Original-Request: + - req_iZ6RNIQNWljp8W + Request-Id: + - req_iZ6RNIQNWljp8W + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrzKuuB1fWySn2XCJcxNT", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720963, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrzKuuB1fWySn2LJIRjRn", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrzKuuB1fWySn7pSJbRQ4", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:05 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/captures_the_payment.yml new file mode 100644 index 0000000000..7e964fbc18 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=36227206271667&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_rPbaP8bXqEKqt9","request_duration_ms":1022}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:11 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e6080b62-ac48-4a10-a376-bdde2a9c85ba + Original-Request: + - req_n53jKGE64zf5NT + Request-Id: + - req_n53jKGE64zf5NT + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "diners", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "diners_club", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "gDlx6y9moRYkO83e", + "funding": "credit", + "generated_from": null, + "last4": "1667", + "networks": { + "available": [ + "diners" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720971, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:11 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTs6KuuB1fWySnf31Y2Oa3&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_n53jKGE64zf5NT","request_duration_ms":523}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:11 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 77b3d3b8-33a6-42ba-8f40-7dd34a03b882 + Original-Request: + - req_tmX1i9smrgNiZU + Request-Id: + - req_tmX1i9smrgNiZU + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs7KuuB1fWySn1Bf0ysYc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720971, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:11 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs7KuuB1fWySn1Bf0ysYc/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_tmX1i9smrgNiZU","request_duration_ms":377}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:12 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 87614ff8-8a68-4b58-9705-56676a4150cc + Original-Request: + - req_SqQrA0CjnEHSBo + Request-Id: + - req_SqQrA0CjnEHSBo + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs7KuuB1fWySn1Bf0ysYc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720971, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs7KuuB1fWySn1ewt57Re", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:12 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs7KuuB1fWySn1Bf0ysYc + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_SqQrA0CjnEHSBo","request_duration_ms":901}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:12 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_kPlmXnsAfVwRKt + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs7KuuB1fWySn1Bf0ysYc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720971, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs7KuuB1fWySn1ewt57Re", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:12 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs7KuuB1fWySn1Bf0ysYc/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_kPlmXnsAfVwRKt","request_duration_ms":308}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:13 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2060ac76-afdf-4b71-bc60-1e337e9a0084 + Original-Request: + - req_39Kex9rIQw5rCU + Request-Id: + - req_39Kex9rIQw5rCU + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs7KuuB1fWySn1Bf0ysYc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720971, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs7KuuB1fWySn1ewt57Re", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:13 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs7KuuB1fWySn1Bf0ysYc + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_39Kex9rIQw5rCU","request_duration_ms":1069}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:14 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_lAQZo297GkbSla + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs7KuuB1fWySn1Bf0ysYc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720971, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs7KuuB1fWySn1ewt57Re", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs6KuuB1fWySnf31Y2Oa3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:14 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..2fe1836900 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Diners_Club_14-digit_card_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=36227206271667&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_yDW6lOmTkBf8DQ","request_duration_ms":378}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:09 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ce11a9ba-0c54-4f99-977d-8fae0762ab5a + Original-Request: + - req_A6YkOfPMDfoUp9 + Request-Id: + - req_A6YkOfPMDfoUp9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTs5KuuB1fWySnnAw0e1Tl", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "diners", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "diners_club", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "gDlx6y9moRYkO83e", + "funding": "credit", + "generated_from": null, + "last4": "1667", + "networks": { + "available": [ + "diners" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": false + }, + "wallet": null + }, + "created": 1710720969, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:09 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTs5KuuB1fWySnnAw0e1Tl&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_A6YkOfPMDfoUp9","request_duration_ms":418}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:09 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - bf779317-4c18-439c-a2a1-369df209da80 + Original-Request: + - req_gIPaPraplh7sxm + Request-Id: + - req_gIPaPraplh7sxm + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs5KuuB1fWySn0ZYyikLq", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720969, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs5KuuB1fWySnnAw0e1Tl", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:09 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTs5KuuB1fWySn0ZYyikLq/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_gIPaPraplh7sxm","request_duration_ms":386}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:10 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d0447ef5-7e66-43bc-bf68-150c606cfa9e + Original-Request: + - req_rPbaP8bXqEKqt9 + Request-Id: + - req_rPbaP8bXqEKqt9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTs5KuuB1fWySn0ZYyikLq", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720969, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTs5KuuB1fWySn0LnnOkx8", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTs5KuuB1fWySnnAw0e1Tl", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:10 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/captures_the_payment.yml new file mode 100644 index 0000000000..b209b8aeec --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6011111111111117&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Nt2XjVoIEMYBME","request_duration_ms":958}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:54 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 905957b2-9209-44a9-bd5e-3747b731d81e + Original-Request: + - req_pj0DkbmO03PC11 + Request-Id: + - req_pj0DkbmO03PC11 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "SJztPPlfyEUr9hdK", + "funding": "credit", + "generated_from": null, + "last4": "1117", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720954, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:54 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrqKuuB1fWySnWrZDtIlV&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_pj0DkbmO03PC11","request_duration_ms":512}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:55 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d4a88a66-46b0-433f-8df4-efcf19582a01 + Original-Request: + - req_z3kDjxX8LS4Too + Request-Id: + - req_z3kDjxX8LS4Too + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrrKuuB1fWySn20Ymh718", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720955, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:55 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrrKuuB1fWySn20Ymh718/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_z3kDjxX8LS4Too","request_duration_ms":398}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:56 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c18408a7-353a-4afb-b3d8-606e900a7332 + Original-Request: + - req_ILwI8dnjB0DxNE + Request-Id: + - req_ILwI8dnjB0DxNE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrrKuuB1fWySn20Ymh718", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720955, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrrKuuB1fWySn2txObMp7", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:56 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrrKuuB1fWySn20Ymh718 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_ILwI8dnjB0DxNE","request_duration_ms":1034}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:56 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_5BDEZhMGqpPKH9 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrrKuuB1fWySn20Ymh718", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720955, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrrKuuB1fWySn2txObMp7", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:56 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrrKuuB1fWySn20Ymh718/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_5BDEZhMGqpPKH9","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:57 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 216e1964-ea93-4ba4-bf6b-7c870a8a25e4 + Original-Request: + - req_SOi11n45i7xIot + Request-Id: + - req_SOi11n45i7xIot + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrrKuuB1fWySn20Ymh718", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720955, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrrKuuB1fWySn2txObMp7", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:57 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrrKuuB1fWySn20Ymh718 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_SOi11n45i7xIot","request_duration_ms":913}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:57 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_6r91uA6K8iIwbh + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrrKuuB1fWySn20Ymh718", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720955, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrrKuuB1fWySn2txObMp7", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrqKuuB1fWySnWrZDtIlV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:57 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..3120c33092 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6011111111111117&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_8vqX4xpyNVZMxF","request_duration_ms":0}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:52 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d8d3eeb0-c81a-4424-b33d-03d956326085 + Original-Request: + - req_H4sjt740Jzqc5G + Request-Id: + - req_H4sjt740Jzqc5G + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTroKuuB1fWySnhXq3B35O", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "SJztPPlfyEUr9hdK", + "funding": "credit", + "generated_from": null, + "last4": "1117", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720952, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:52 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTroKuuB1fWySnhXq3B35O&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_H4sjt740Jzqc5G","request_duration_ms":488}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:53 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 88504cd8-3fb7-4bf3-83e8-beb3a0c64615 + Original-Request: + - req_gy3shbiiJSMlbv + Request-Id: + - req_gy3shbiiJSMlbv + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrpKuuB1fWySn10lsL7cb", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720953, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTroKuuB1fWySnhXq3B35O", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:53 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrpKuuB1fWySn10lsL7cb/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_gy3shbiiJSMlbv","request_duration_ms":369}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:54 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 5e6af337-de3a-46c3-bdff-35c4bcc4447b + Original-Request: + - req_Nt2XjVoIEMYBME + Request-Id: + - req_Nt2XjVoIEMYBME + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrpKuuB1fWySn10lsL7cb", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720953, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrpKuuB1fWySn1XB2PFtm", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTroKuuB1fWySnhXq3B35O", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:54 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/captures_the_payment.yml new file mode 100644 index 0000000000..5fb23e2a8b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6011981111111113&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_yatKJVrxlvFge6","request_duration_ms":848}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:00 GMT + Content-Type: + - application/json + Content-Length: + - '971' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c5c59b48-7183-43fb-bf52-52091ac19f27 + Original-Request: + - req_1t2hLcURtLlQVi + Request-Id: + - req_1t2hLcURtLlQVi + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "Y3EGIoTEEuDsD8eJ", + "funding": "debit", + "generated_from": null, + "last4": "1113", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720960, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:00 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrwKuuB1fWySnri3tHSXe&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1t2hLcURtLlQVi","request_duration_ms":473}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:00 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 5a5a7c3a-42c0-477c-87ec-87dfc941cc4f + Original-Request: + - req_mKWLwcQF6Mkusv + Request-Id: + - req_mKWLwcQF6Mkusv + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrwKuuB1fWySn0WdD9T2H", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720960, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:00 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrwKuuB1fWySn0WdD9T2H/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_mKWLwcQF6Mkusv","request_duration_ms":434}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:01 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a6b53504-f38f-4c1a-b9e1-b705bb1de29e + Original-Request: + - req_d1VtMvxkl601Eh + Request-Id: + - req_d1VtMvxkl601Eh + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrwKuuB1fWySn0WdD9T2H", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720960, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrwKuuB1fWySn0WvxcN5y", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:01 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrwKuuB1fWySn0WdD9T2H + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_d1VtMvxkl601Eh","request_duration_ms":824}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:01 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_NtN8lL51529VsL + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrwKuuB1fWySn0WdD9T2H", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720960, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrwKuuB1fWySn0WvxcN5y", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:01 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrwKuuB1fWySn0WdD9T2H/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_NtN8lL51529VsL","request_duration_ms":312}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:02 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e263e140-d3d3-47c2-a628-2eb3d68e97df + Original-Request: + - req_WHOqLbBAGy8rcu + Request-Id: + - req_WHOqLbBAGy8rcu + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrwKuuB1fWySn0WdD9T2H", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720960, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrwKuuB1fWySn0WvxcN5y", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:02 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrwKuuB1fWySn0WdD9T2H + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_WHOqLbBAGy8rcu","request_duration_ms":983}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:03 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_XSjJwBtpszuVl9 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrwKuuB1fWySn0WdD9T2H", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720960, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrwKuuB1fWySn0WvxcN5y", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrwKuuB1fWySnri3tHSXe", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:03 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..e96af06683 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Discover_debit_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6011981111111113&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_6r91uA6K8iIwbh","request_duration_ms":0}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:58 GMT + Content-Type: + - application/json + Content-Length: + - '971' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2ffd1fef-2e1f-4415-8fe5-66f37ab8870a + Original-Request: + - req_yMHJ8WbQRxhmmZ + Request-Id: + - req_yMHJ8WbQRxhmmZ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTruKuuB1fWySnPMzFtGoa", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "discover", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "discover", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "Y3EGIoTEEuDsD8eJ", + "funding": "debit", + "generated_from": null, + "last4": "1113", + "networks": { + "available": [ + "discover" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720958, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:58 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTruKuuB1fWySnPMzFtGoa&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_yMHJ8WbQRxhmmZ","request_duration_ms":462}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:58 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2841ca57-eab0-4011-8c84-3571cc0ce235 + Original-Request: + - req_w8fDrhxDE86O7z + Request-Id: + - req_w8fDrhxDE86O7z + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTruKuuB1fWySn0TUGd08Z", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720958, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTruKuuB1fWySnPMzFtGoa", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:58 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTruKuuB1fWySn0TUGd08Z/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_w8fDrhxDE86O7z","request_duration_ms":481}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:59 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d904944b-fe8d-47f2-99ca-1e473cf4a005 + Original-Request: + - req_yatKJVrxlvFge6 + Request-Id: + - req_yatKJVrxlvFge6 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTruKuuB1fWySn0TUGd08Z", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720958, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTruKuuB1fWySn0Qa2PFIH", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTruKuuB1fWySnPMzFtGoa", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:59 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/captures_the_payment.yml new file mode 100644 index 0000000000..29d084742f --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=3566002020360505&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_XsEWpqUYZGkMTy","request_duration_ms":869}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:22 GMT + Content-Type: + - application/json + Content-Length: + - '957' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ed07381f-d500-4167-bbf9-b8e275e96e3a + Original-Request: + - req_NtdVOImSJGfmJB + Request-Id: + - req_NtdVOImSJGfmJB + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "jcb", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "JP", + "display_brand": "jcb", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "8f2gcynh7EdGyDKt", + "funding": "credit", + "generated_from": null, + "last4": "0505", + "networks": { + "available": [ + "jcb" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720981, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:22 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsHKuuB1fWySn9Zdn9YCf&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_NtdVOImSJGfmJB","request_duration_ms":471}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:22 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - cd5f7b81-5d9b-41f2-bff6-1f6ec88d70ff + Original-Request: + - req_f4mH2wKBJw5xy9 + Request-Id: + - req_f4mH2wKBJw5xy9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsIKuuB1fWySn2h6v6jLa", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720982, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:22 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsIKuuB1fWySn2h6v6jLa/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_f4mH2wKBJw5xy9","request_duration_ms":407}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:23 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 68dc2b67-4646-45ce-8a55-9642cc829079 + Original-Request: + - req_eHvL6Nuuzjgs9N + Request-Id: + - req_eHvL6Nuuzjgs9N + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsIKuuB1fWySn2h6v6jLa", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720982, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsIKuuB1fWySn2Ay6cTCd", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:23 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsIKuuB1fWySn2h6v6jLa + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_eHvL6Nuuzjgs9N","request_duration_ms":934}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:23 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_74MpOtu2vrbWNG + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsIKuuB1fWySn2h6v6jLa", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720982, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsIKuuB1fWySn2Ay6cTCd", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:23 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsIKuuB1fWySn2h6v6jLa/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_74MpOtu2vrbWNG","request_duration_ms":394}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:24 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7d2c347c-75e4-46e1-9bfc-038469b40a55 + Original-Request: + - req_2k51weDL40CUJt + Request-Id: + - req_2k51weDL40CUJt + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsIKuuB1fWySn2h6v6jLa", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720982, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsIKuuB1fWySn2Ay6cTCd", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:25 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsIKuuB1fWySn2h6v6jLa + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_2k51weDL40CUJt","request_duration_ms":1125}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:25 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_4eRWQMiyznFClQ + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsIKuuB1fWySn2h6v6jLa", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720982, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsIKuuB1fWySn2Ay6cTCd", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsHKuuB1fWySn9Zdn9YCf", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:25 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..2eae020c36 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_JCB/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=3566002020360505&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_0NP9abiv73dbAY","request_duration_ms":307}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:20 GMT + Content-Type: + - application/json + Content-Length: + - '957' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d9452e53-fd70-4d8b-89af-c4e3bd092d18 + Original-Request: + - req_2SV1wqboTa5qCm + Request-Id: + - req_2SV1wqboTa5qCm + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsFKuuB1fWySnaCdYCzVI", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "jcb", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "JP", + "display_brand": "jcb", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "8f2gcynh7EdGyDKt", + "funding": "credit", + "generated_from": null, + "last4": "0505", + "networks": { + "available": [ + "jcb" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720980, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:20 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsFKuuB1fWySnaCdYCzVI&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_2SV1wqboTa5qCm","request_duration_ms":501}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:20 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e89f6844-8be9-4fae-8579-19303ce17602 + Original-Request: + - req_1cs9SEfKW2eq3k + Request-Id: + - req_1cs9SEfKW2eq3k + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsGKuuB1fWySn06qMPeZF", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720980, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsFKuuB1fWySnaCdYCzVI", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:20 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsGKuuB1fWySn06qMPeZF/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1cs9SEfKW2eq3k","request_duration_ms":407}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:21 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 8a2b4589-bc09-4366-95b2-81018e43d1fe + Original-Request: + - req_XsEWpqUYZGkMTy + Request-Id: + - req_XsEWpqUYZGkMTy + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsGKuuB1fWySn06qMPeZF", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720980, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsGKuuB1fWySn0lhXW14p", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsFKuuB1fWySnaCdYCzVI", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:21 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/captures_the_payment.yml new file mode 100644 index 0000000000..387f1f77e7 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5555555555554444&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_y1K1o8i4n95p6j","request_duration_ms":1063}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:26 GMT + Content-Type: + - application/json + Content-Length: + - '978' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ced58fca-771b-490f-bcb9-fded957023c0 + Original-Request: + - req_SE6rzY46VZ3kOI + Request-Id: + - req_SE6rzY46VZ3kOI + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720926, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:26 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrOKuuB1fWySnIMLVsnqc&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_SE6rzY46VZ3kOI","request_duration_ms":411}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:27 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b723bac9-e029-4886-9317-562f242936a4 + Original-Request: + - req_ie2CNfGK0R6fF7 + Request-Id: + - req_ie2CNfGK0R6fF7 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrPKuuB1fWySn1dwfaQr0", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720927, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:27 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrPKuuB1fWySn1dwfaQr0/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_ie2CNfGK0R6fF7","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:28 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - e50b4e3d-5fec-427a-83e8-dd42b6debf1d + Original-Request: + - req_6pJA1xe5gmzgBw + Request-Id: + - req_6pJA1xe5gmzgBw + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrPKuuB1fWySn1dwfaQr0", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720927, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrPKuuB1fWySn1oFX0t4d", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:28 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrPKuuB1fWySn1dwfaQr0 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_6pJA1xe5gmzgBw","request_duration_ms":1123}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:28 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_RVlKj5lmk3NRuX + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrPKuuB1fWySn1dwfaQr0", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720927, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrPKuuB1fWySn1oFX0t4d", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:28 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrPKuuB1fWySn1dwfaQr0/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_RVlKj5lmk3NRuX","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:29 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b804d2cc-5c91-4c4f-b26b-cb0b47467943 + Original-Request: + - req_N2lThYADiH2V5P + Request-Id: + - req_N2lThYADiH2V5P + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrPKuuB1fWySn1dwfaQr0", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720927, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrPKuuB1fWySn1oFX0t4d", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:29 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrPKuuB1fWySn1dwfaQr0 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_N2lThYADiH2V5P","request_duration_ms":998}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:29 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_4hAPqGLT8Xc0hy + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrPKuuB1fWySn1dwfaQr0", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720927, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrPKuuB1fWySn1oFX0t4d", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrOKuuB1fWySnIMLVsnqc", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:30 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..99530b2678 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5555555555554444&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_7WiaUKb3OVSyj9","request_duration_ms":307}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:24 GMT + Content-Type: + - application/json + Content-Length: + - '978' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2b1b87e4-2425-40ee-afe7-39d1f100843c + Original-Request: + - req_FU5dQmIIwAt5UB + Request-Id: + - req_FU5dQmIIwAt5UB + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrMKuuB1fWySnWocJpQow", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720924, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:24 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrMKuuB1fWySnWocJpQow&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_FU5dQmIIwAt5UB","request_duration_ms":499}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:25 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1cd682ce-61d8-4a00-a9bf-4e2800fa326b + Original-Request: + - req_YxV8Bf1rs27Y11 + Request-Id: + - req_YxV8Bf1rs27Y11 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrNKuuB1fWySn0qriyTFb", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720925, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrMKuuB1fWySnWocJpQow", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:25 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrNKuuB1fWySn0qriyTFb/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_YxV8Bf1rs27Y11","request_duration_ms":367}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:26 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 499d9299-3962-478a-92da-07af2b0e1f5b + Original-Request: + - req_y1K1o8i4n95p6j + Request-Id: + - req_y1K1o8i4n95p6j + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrNKuuB1fWySn0qriyTFb", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720925, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrNKuuB1fWySn0FIhUqIF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrMKuuB1fWySnWocJpQow", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:26 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/captures_the_payment.yml new file mode 100644 index 0000000000..6f9eca8224 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=2223003122003222&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_F6SV3n8p827ldm","request_duration_ms":1021}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:32 GMT + Content-Type: + - application/json + Content-Length: + - '978' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1cba080e-8010-4551-ad03-13c288e4bd11 + Original-Request: + - req_5Qkx6WtOtfvt74 + Request-Id: + - req_5Qkx6WtOtfvt74 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "0gTPwvyIV7E6CAld", + "funding": "credit", + "generated_from": null, + "last4": "3222", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720932, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:32 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrUKuuB1fWySng3Hl6jn3&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_5Qkx6WtOtfvt74","request_duration_ms":387}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:32 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6e580196-a75b-44fa-8815-83caac56e2aa + Original-Request: + - req_SR9Ey547AhrYaq + Request-Id: + - req_SR9Ey547AhrYaq + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrUKuuB1fWySn0OJBRMzt", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720932, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:32 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrUKuuB1fWySn0OJBRMzt/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_SR9Ey547AhrYaq","request_duration_ms":432}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:33 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 5ee9e03f-e7ba-4404-8e0d-5cc5da530ecc + Original-Request: + - req_jyUToa4RdO5AN4 + Request-Id: + - req_jyUToa4RdO5AN4 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrUKuuB1fWySn0OJBRMzt", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720932, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrUKuuB1fWySn0Rrth1wF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:33 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrUKuuB1fWySn0OJBRMzt + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jyUToa4RdO5AN4","request_duration_ms":918}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:34 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_jM63eYpKejss6m + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrUKuuB1fWySn0OJBRMzt", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720932, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrUKuuB1fWySn0Rrth1wF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:34 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrUKuuB1fWySn0OJBRMzt/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jM63eYpKejss6m","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:35 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 95dc4daf-26e2-4ed1-b505-12ffe2be3437 + Original-Request: + - req_H9X6oSdMCQg8PN + Request-Id: + - req_H9X6oSdMCQg8PN + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrUKuuB1fWySn0OJBRMzt", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720932, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrUKuuB1fWySn0Rrth1wF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:35 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrUKuuB1fWySn0OJBRMzt + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_H9X6oSdMCQg8PN","request_duration_ms":1023}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:35 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_BMIQ8UnaBeKHJc + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrUKuuB1fWySn0OJBRMzt", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720932, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrUKuuB1fWySn0Rrth1wF", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrUKuuB1fWySng3Hl6jn3", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:35 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..450df03c50 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_2-series_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=2223003122003222&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_4hAPqGLT8Xc0hy","request_duration_ms":297}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:30 GMT + Content-Type: + - application/json + Content-Length: + - '978' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 40f0f090-9dc0-4ffa-a2ac-b7ed01d25633 + Original-Request: + - req_A1T5hkkTfwVdbQ + Request-Id: + - req_A1T5hkkTfwVdbQ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrSKuuB1fWySnhjlVrL9a", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "0gTPwvyIV7E6CAld", + "funding": "credit", + "generated_from": null, + "last4": "3222", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720930, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:30 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrSKuuB1fWySnhjlVrL9a&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_A1T5hkkTfwVdbQ","request_duration_ms":539}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:30 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 59bfcd3f-e926-4dd3-9257-f7074b298e24 + Original-Request: + - req_BH5m2BIHfjWhrF + Request-Id: + - req_BH5m2BIHfjWhrF + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrSKuuB1fWySn21KZfYYg", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720930, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrSKuuB1fWySnhjlVrL9a", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:30 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrSKuuB1fWySn21KZfYYg/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_BH5m2BIHfjWhrF","request_duration_ms":407}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:31 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 9f94a41e-730d-458c-9988-726003d79e0f + Original-Request: + - req_F6SV3n8p827ldm + Request-Id: + - req_F6SV3n8p827ldm + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrSKuuB1fWySn21KZfYYg", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720930, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrSKuuB1fWySn2tFA4riM", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrSKuuB1fWySnhjlVrL9a", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:31 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/captures_the_payment.yml new file mode 100644 index 0000000000..eddf2604b6 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5200828282828210&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_cArA6vQ0QX9xBg","request_duration_ms":1023}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:37 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 6d9172b8-6e72-4018-a012-37408e15d687 + Original-Request: + - req_O92V0lbqUdmmWK + Request-Id: + - req_O92V0lbqUdmmWK + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrZKuuB1fWySnu99drOTk", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "DpQ8VoC0Z3P9xrbi", + "funding": "debit", + "generated_from": null, + "last4": "8210", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720937, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:38 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrZKuuB1fWySnu99drOTk&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_O92V0lbqUdmmWK","request_duration_ms":526}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:38 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b931f807-f426-4232-8162-80717198db54 + Original-Request: + - req_xYkL2JI0yIi6IW + Request-Id: + - req_xYkL2JI0yIi6IW + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTraKuuB1fWySn0yoy26V4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720938, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrZKuuB1fWySnu99drOTk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:38 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTraKuuB1fWySn0yoy26V4/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_xYkL2JI0yIi6IW","request_duration_ms":409}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:39 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c51fd5d9-0d17-49d3-98f8-7b968a1778c1 + Original-Request: + - req_KfZYdqvzUvqZji + Request-Id: + - req_KfZYdqvzUvqZji + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTraKuuB1fWySn0yoy26V4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720938, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTraKuuB1fWySn0EVAvMbS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrZKuuB1fWySnu99drOTk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:39 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTraKuuB1fWySn0yoy26V4 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_KfZYdqvzUvqZji","request_duration_ms":1022}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:39 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_nO5OefF8xqH0cl + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTraKuuB1fWySn0yoy26V4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720938, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTraKuuB1fWySn0EVAvMbS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrZKuuB1fWySnu99drOTk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:39 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTraKuuB1fWySn0yoy26V4/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_nO5OefF8xqH0cl","request_duration_ms":411}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:40 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 152262b0-1437-47c5-af47-c71f6528bca4 + Original-Request: + - req_51MsGhRNg8nEpk + Request-Id: + - req_51MsGhRNg8nEpk + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTraKuuB1fWySn0yoy26V4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720938, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTraKuuB1fWySn0EVAvMbS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrZKuuB1fWySnu99drOTk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:40 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTraKuuB1fWySn0yoy26V4 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_51MsGhRNg8nEpk","request_duration_ms":1091}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:41 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_qYAVAOt5tolkRn + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTraKuuB1fWySn0yoy26V4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720938, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTraKuuB1fWySn0EVAvMbS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrZKuuB1fWySnu99drOTk", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:41 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..de240ff81a --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_debit_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5200828282828210&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_BMIQ8UnaBeKHJc","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:35 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7ab630b8-1ac8-4020-86a5-a587923c26d8 + Original-Request: + - req_Gv6XA9ilksWfbE + Request-Id: + - req_Gv6XA9ilksWfbE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrXKuuB1fWySniJsmZueZ", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "DpQ8VoC0Z3P9xrbi", + "funding": "debit", + "generated_from": null, + "last4": "8210", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720935, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:35 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrXKuuB1fWySniJsmZueZ&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Gv6XA9ilksWfbE","request_duration_ms":456}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:36 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 4ce0e164-5eb4-435b-b585-275e0d4b84a1 + Original-Request: + - req_HYZbpogYU5LRGY + Request-Id: + - req_HYZbpogYU5LRGY + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrYKuuB1fWySn1cKDcQWx", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720936, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrXKuuB1fWySniJsmZueZ", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:36 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrYKuuB1fWySn1cKDcQWx/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_HYZbpogYU5LRGY","request_duration_ms":449}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:37 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - cd50b15c-15ac-4f54-9730-0c8b8a2ee2e3 + Original-Request: + - req_cArA6vQ0QX9xBg + Request-Id: + - req_cArA6vQ0QX9xBg + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrYKuuB1fWySn1cKDcQWx", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720936, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrYKuuB1fWySn1hK8i87T", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrXKuuB1fWySniJsmZueZ", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:37 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/captures_the_payment.yml new file mode 100644 index 0000000000..ecd7f8d361 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5105105105105100&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_z8BN0i7c97soJU","request_duration_ms":1022}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:43 GMT + Content-Type: + - application/json + Content-Length: + - '979' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1f6eb8c3-bad3-451c-ae1f-edc439e77e56 + Original-Request: + - req_kYgXlX6iCM6uNQ + Request-Id: + - req_kYgXlX6iCM6uNQ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "B9ykFJ6imaeWU8aO", + "funding": "prepaid", + "generated_from": null, + "last4": "5100", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720943, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:43 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrfKuuB1fWySnIUhRVp2j&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_kYgXlX6iCM6uNQ","request_duration_ms":528}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:44 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c613f9d8-ab73-4f88-a22e-783f4ece702e + Original-Request: + - req_rVdCaan3PkQrwo + Request-Id: + - req_rVdCaan3PkQrwo + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrgKuuB1fWySn02SeVhW9", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720944, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:44 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrgKuuB1fWySn02SeVhW9/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_rVdCaan3PkQrwo","request_duration_ms":401}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:45 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - d4160081-eca6-4ef4-99d0-5097b556496d + Original-Request: + - req_rOpCYqlEXLeean + Request-Id: + - req_rOpCYqlEXLeean + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrgKuuB1fWySn02SeVhW9", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720944, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrgKuuB1fWySn0bQZYfUr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:45 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrgKuuB1fWySn02SeVhW9 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_rOpCYqlEXLeean","request_duration_ms":1029}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:45 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_Wv0iqO5pTMnI8u + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrgKuuB1fWySn02SeVhW9", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720944, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrgKuuB1fWySn0bQZYfUr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:45 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrgKuuB1fWySn02SeVhW9/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Wv0iqO5pTMnI8u","request_duration_ms":409}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:46 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 12c304f1-c5d3-4f5d-8fa4-dbaba0db827b + Original-Request: + - req_T6XJK9yFeA0sGw + Request-Id: + - req_T6XJK9yFeA0sGw + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrgKuuB1fWySn02SeVhW9", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720944, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrgKuuB1fWySn0bQZYfUr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:46 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrgKuuB1fWySn02SeVhW9 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_T6XJK9yFeA0sGw","request_duration_ms":919}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:46 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_b7rfB7ZShjT8E0 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrgKuuB1fWySn02SeVhW9", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720944, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrgKuuB1fWySn0bQZYfUr", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrfKuuB1fWySnIUhRVp2j", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:46 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..ee64617400 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Mastercard_prepaid_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=5105105105105100&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_qYAVAOt5tolkRn","request_duration_ms":337}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:41 GMT + Content-Type: + - application/json + Content-Length: + - '979' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c553eb0e-f7a2-4fcc-81e5-fa727d4948ce + Original-Request: + - req_NVE3ICLZo5lI4O + Request-Id: + - req_NVE3ICLZo5lI4O + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrdKuuB1fWySnTpxZ4mch", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "B9ykFJ6imaeWU8aO", + "funding": "prepaid", + "generated_from": null, + "last4": "5100", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720941, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:41 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrdKuuB1fWySnTpxZ4mch&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_NVE3ICLZo5lI4O","request_duration_ms":429}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:42 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ec9a598a-d016-4100-be44-44eef068357a + Original-Request: + - req_hQNN9biDMsF081 + Request-Id: + - req_hQNN9biDMsF081 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrdKuuB1fWySn0m88VIt4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720941, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrdKuuB1fWySnTpxZ4mch", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:42 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrdKuuB1fWySn0m88VIt4/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_hQNN9biDMsF081","request_duration_ms":477}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:43 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - f58acce0-e939-46e5-8217-b8a53a93b631 + Original-Request: + - req_z8BN0i7c97soJU + Request-Id: + - req_z8BN0i7c97soJU + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrdKuuB1fWySn0m88VIt4", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720941, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrdKuuB1fWySn02qfpzDs", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrdKuuB1fWySnTpxZ4mch", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:43 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/captures_the_payment.yml new file mode 100644 index 0000000000..fb3486c7ab --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6200000000000005&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_eUBysKV03XEYxT","request_duration_ms":1015}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:27 GMT + Content-Type: + - application/json + Content-Length: + - '973' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ad5f47e5-6f33-4f99-9e26-ad99435153d8 + Original-Request: + - req_5vGOTNUa54PiMA + Request-Id: + - req_5vGOTNUa54PiMA + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "unionpay", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "CN", + "display_brand": "union_pay", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "Aq45rzUxvT6SiF1W", + "funding": "credit", + "generated_from": null, + "last4": "0005", + "networks": { + "available": [ + "unionpay" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720987, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:27 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsNKuuB1fWySno7bqhpsP&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_5vGOTNUa54PiMA","request_duration_ms":533}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:28 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7560ef01-4e32-43b3-abb6-260085e3c31d + Original-Request: + - req_1Hb6sMc2vThowU + Request-Id: + - req_1Hb6sMc2vThowU + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsOKuuB1fWySn0XQgsbZs", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720988, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:28 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsOKuuB1fWySn0XQgsbZs/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_1Hb6sMc2vThowU","request_duration_ms":407}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:29 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 8f5cfd58-43d7-44ce-8e72-ed5781ae61fd + Original-Request: + - req_6Ostq0AowQZf4U + Request-Id: + - req_6Ostq0AowQZf4U + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsOKuuB1fWySn0XQgsbZs", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720988, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsOKuuB1fWySn0Yjm5Cxs", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:29 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsOKuuB1fWySn0XQgsbZs + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_6Ostq0AowQZf4U","request_duration_ms":943}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:29 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_WJ7FqNzhfD4r3w + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsOKuuB1fWySn0XQgsbZs", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720988, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsOKuuB1fWySn0Yjm5Cxs", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:29 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsOKuuB1fWySn0XQgsbZs/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_WJ7FqNzhfD4r3w","request_duration_ms":388}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:30 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b0dd54f6-793b-4e09-b886-85229fbfb2b0 + Original-Request: + - req_vh8eEroZBMIR7E + Request-Id: + - req_vh8eEroZBMIR7E + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsOKuuB1fWySn0XQgsbZs", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720988, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsOKuuB1fWySn0Yjm5Cxs", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:30 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsOKuuB1fWySn0XQgsbZs + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_vh8eEroZBMIR7E","request_duration_ms":1221}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:31 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_wpFH10pcTSBRrz + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsOKuuB1fWySn0XQgsbZs", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720988, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsOKuuB1fWySn0Yjm5Cxs", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsNKuuB1fWySno7bqhpsP", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:31 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..baee8d727b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6200000000000005&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_4eRWQMiyznFClQ","request_duration_ms":411}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:25 GMT + Content-Type: + - application/json + Content-Length: + - '973' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - da85eb81-2b37-435c-abc4-9a8d9815b0cb + Original-Request: + - req_pALh6hN0ULK9jh + Request-Id: + - req_pALh6hN0ULK9jh + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsLKuuB1fWySnpMKbegER", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "unionpay", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "CN", + "display_brand": "union_pay", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "Aq45rzUxvT6SiF1W", + "funding": "credit", + "generated_from": null, + "last4": "0005", + "networks": { + "available": [ + "unionpay" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720985, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:25 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsLKuuB1fWySnpMKbegER&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_pALh6hN0ULK9jh","request_duration_ms":458}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:26 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2e82521d-8bbb-4c20-8395-2c4892cc1a79 + Original-Request: + - req_5qJ1fAR54XPl36 + Request-Id: + - req_5qJ1fAR54XPl36 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsMKuuB1fWySn0Ufm2pIh", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720986, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsLKuuB1fWySnpMKbegER", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:26 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsMKuuB1fWySn0Ufm2pIh/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_5qJ1fAR54XPl36","request_duration_ms":448}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:27 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 86eab2a5-e6d7-4669-a7eb-03f88ab9930a + Original-Request: + - req_eUBysKV03XEYxT + Request-Id: + - req_eUBysKV03XEYxT + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsMKuuB1fWySn0Ufm2pIh", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720986, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsMKuuB1fWySn0d0qYlWV", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsLKuuB1fWySnpMKbegER", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:27 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/captures_the_payment.yml new file mode 100644 index 0000000000..ae89ac5c41 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6205500000000000004&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_C1ZGBOTnpRE8B3","request_duration_ms":1051}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:33 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 710671f6-dffe-4bd9-9a06-f8f62796f6cd + Original-Request: + - req_gsnCkR0MvWyxK7 + Request-Id: + - req_gsnCkR0MvWyxK7 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "unionpay", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "union_pay", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "F9o1vzLUnyEJBPXi", + "funding": "debit", + "generated_from": null, + "last4": "0004", + "networks": { + "available": [ + "unionpay" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720993, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:33 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsTKuuB1fWySnwlzmB90u&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_gsnCkR0MvWyxK7","request_duration_ms":518}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:34 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 314b047e-698c-4988-8c31-538ca168661c + Original-Request: + - req_58h3yprJqoo9Ws + Request-Id: + - req_58h3yprJqoo9Ws + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsUKuuB1fWySn2jx7jK6U", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720994, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:34 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsUKuuB1fWySn2jx7jK6U/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_58h3yprJqoo9Ws","request_duration_ms":408}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:35 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 0dc74051-0bb0-4998-8e75-2666e0a4bf72 + Original-Request: + - req_3geYw0tnTsr9TH + Request-Id: + - req_3geYw0tnTsr9TH + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsUKuuB1fWySn2jx7jK6U", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720994, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsUKuuB1fWySn28v0baTh", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:35 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsUKuuB1fWySn2jx7jK6U + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_3geYw0tnTsr9TH","request_duration_ms":1020}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:35 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_jrK6wdykIG5KTK + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsUKuuB1fWySn2jx7jK6U", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720994, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsUKuuB1fWySn28v0baTh", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:35 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsUKuuB1fWySn2jx7jK6U/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jrK6wdykIG5KTK","request_duration_ms":289}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:36 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 33fc0972-11c0-4cbd-8858-75cfc375d1d4 + Original-Request: + - req_KdMYJ4N4VJbwhE + Request-Id: + - req_KdMYJ4N4VJbwhE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsUKuuB1fWySn2jx7jK6U", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720994, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsUKuuB1fWySn28v0baTh", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:36 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsUKuuB1fWySn2jx7jK6U + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_KdMYJ4N4VJbwhE","request_duration_ms":908}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:36 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_rMYTwbu1M5H7eN + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsUKuuB1fWySn2jx7jK6U", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720994, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsUKuuB1fWySn28v0baTh", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsTKuuB1fWySnwlzmB90u", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:36 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..beb275e285 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_UnionPay_19-digit_card_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=6205500000000000004&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_wpFH10pcTSBRrz","request_duration_ms":310}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:31 GMT + Content-Type: + - application/json + Content-Length: + - '972' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7c1de68c-4dff-4f66-a53d-23d3fd85d652 + Original-Request: + - req_MdBf01aEdewhO9 + Request-Id: + - req_MdBf01aEdewhO9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsRKuuB1fWySndh5jdI4P", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "unionpay", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "union_pay", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "F9o1vzLUnyEJBPXi", + "funding": "debit", + "generated_from": null, + "last4": "0004", + "networks": { + "available": [ + "unionpay" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720991, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:31 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTsRKuuB1fWySndh5jdI4P&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_MdBf01aEdewhO9","request_duration_ms":433}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:32 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 9bfc5e5a-8118-4a88-b281-a3c6f4db085b + Original-Request: + - req_sidZ3gsvRwFm8p + Request-Id: + - req_sidZ3gsvRwFm8p + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsRKuuB1fWySn2tWEtp5t", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720991, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsRKuuB1fWySndh5jdI4P", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:32 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTsRKuuB1fWySn2tWEtp5t/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_sidZ3gsvRwFm8p","request_duration_ms":449}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:33 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 4f7df98c-e671-4fcd-89f2-882bb2dd639f + Original-Request: + - req_C1ZGBOTnpRE8B3 + Request-Id: + - req_C1ZGBOTnpRE8B3 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTsRKuuB1fWySn2tWEtp5t", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720991, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTsRKuuB1fWySn2A9nRyGl", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTsRKuuB1fWySndh5jdI4P", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:16:33 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/captures_the_payment.yml new file mode 100644 index 0000000000..1dd0fc7de9 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_dtS949YMjjoHya","request_duration_ms":930}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:15 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 0ea43829-0202-45de-88eb-9a748d74649f + Original-Request: + - req_dTkrpJyibE6wg7 + Request-Id: + - req_dTkrpJyibE6wg7 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720915, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:15 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrDKuuB1fWySnRwZg9BlO&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_dTkrpJyibE6wg7","request_duration_ms":455}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:16 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ffaf625d-1712-49fc-8b01-9c75f0a3b575 + Original-Request: + - req_davbqaDMQ97Pg2 + Request-Id: + - req_davbqaDMQ97Pg2 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrDKuuB1fWySn091mKkgk", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720915, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:16 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrDKuuB1fWySn091mKkgk/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_davbqaDMQ97Pg2","request_duration_ms":407}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:17 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3f0f5e97-4448-48b0-8223-194b2783a4df + Original-Request: + - req_oHMk0SMK4wGsOZ + Request-Id: + - req_oHMk0SMK4wGsOZ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrDKuuB1fWySn091mKkgk", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720915, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrDKuuB1fWySn0KaM6WCS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:17 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrDKuuB1fWySn091mKkgk + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_oHMk0SMK4wGsOZ","request_duration_ms":920}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:17 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_5YxBuXnlaembay + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrDKuuB1fWySn091mKkgk", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720915, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrDKuuB1fWySn0KaM6WCS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:17 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrDKuuB1fWySn091mKkgk/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_5YxBuXnlaembay","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:18 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1f432206-beb4-4806-a191-b4d3842d8c3e + Original-Request: + - req_YiXPVJtRKb7HN9 + Request-Id: + - req_YiXPVJtRKb7HN9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrDKuuB1fWySn091mKkgk", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720915, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrDKuuB1fWySn0KaM6WCS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:18 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrDKuuB1fWySn091mKkgk + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_YiXPVJtRKb7HN9","request_duration_ms":1125}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:18 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_Ogqtdp3mqHyWXS + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrDKuuB1fWySn091mKkgk", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720915, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrDKuuB1fWySn0KaM6WCS", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrDKuuB1fWySnRwZg9BlO", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:18 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..1737335093 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_7jejsG3JSQBIRE","request_duration_ms":391}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:13 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7dbb85ac-2d3f-4f24-a1bd-4b64236450b9 + Original-Request: + - req_MUoxPGHkVEFb0n + Request-Id: + - req_MUoxPGHkVEFb0n + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrBKuuB1fWySnSi5zVbTF", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720913, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:13 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrBKuuB1fWySnSi5zVbTF&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_MUoxPGHkVEFb0n","request_duration_ms":504}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:14 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 1d06fdb1-8042-4249-b298-6e23aac036e8 + Original-Request: + - req_bSxxsIwoq3ebxB + Request-Id: + - req_bSxxsIwoq3ebxB + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrCKuuB1fWySn2xWagSLc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720914, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrBKuuB1fWySnSi5zVbTF", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:14 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrCKuuB1fWySn2xWagSLc/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_bSxxsIwoq3ebxB","request_duration_ms":399}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:15 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 984212ad-6a8e-4107-a070-2ee5e4cec7fc + Original-Request: + - req_dtS949YMjjoHya + Request-Id: + - req_dtS949YMjjoHya + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrCKuuB1fWySn2xWagSLc", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720914, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrCKuuB1fWySn2HY9WrJD", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrBKuuB1fWySnSi5zVbTF", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:15 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/captures_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/captures_the_payment.yml new file mode 100644 index 0000000000..d62e9509cd --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/captures_the_payment.yml @@ -0,0 +1,770 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4000056655665556&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_65bdb7xckNrfv9","request_duration_ms":1055}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:21 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2f2b1c16-751a-4363-a9dd-083f04061395 + Original-Request: + - req_hxl0E0yRBSqFwP + Request-Id: + - req_hxl0E0yRBSqFwP + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrJKuuB1fWySn717F2dAV", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "QOaaYMtlTSm6xJM8", + "funding": "debit", + "generated_from": null, + "last4": "5556", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720921, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:21 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrJKuuB1fWySn717F2dAV&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_hxl0E0yRBSqFwP","request_duration_ms":530}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:21 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - ce6a3fb4-8b72-466c-8894-ea91c3944bf3 + Original-Request: + - req_aWFEGpu95D9tCJ + Request-Id: + - req_aWFEGpu95D9tCJ + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrJKuuB1fWySn1slHNzIA", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720921, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrJKuuB1fWySn717F2dAV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:21 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrJKuuB1fWySn1slHNzIA/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_aWFEGpu95D9tCJ","request_duration_ms":405}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:22 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 9cbd8a9f-9a48-4ad6-b000-5c6d8b5cec7c + Original-Request: + - req_EsILKUysv5Pc78 + Request-Id: + - req_EsILKUysv5Pc78 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrJKuuB1fWySn1slHNzIA", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720921, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrJKuuB1fWySn1WEgdABW", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrJKuuB1fWySn717F2dAV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:22 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrJKuuB1fWySn1slHNzIA + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_EsILKUysv5Pc78","request_duration_ms":1023}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:23 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_VVHj9NlENtzYEp + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrJKuuB1fWySn1slHNzIA", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720921, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrJKuuB1fWySn1WEgdABW", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrJKuuB1fWySn717F2dAV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:23 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrJKuuB1fWySn1slHNzIA/capture + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_VVHj9NlENtzYEp","request_duration_ms":275}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:24 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fcapture; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - bcaa1f12-45f2-4e7d-9759-f8830f30545c + Original-Request: + - req_S62m5ncks84tAm + Request-Id: + - req_S62m5ncks84tAm + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrJKuuB1fWySn1slHNzIA", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720921, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrJKuuB1fWySn1WEgdABW", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrJKuuB1fWySn717F2dAV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:24 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrJKuuB1fWySn1slHNzIA + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_S62m5ncks84tAm","request_duration_ms":952}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:24 GMT + Content-Type: + - application/json + Content-Length: + - '1358' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_7WiaUKb3OVSyj9 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrJKuuB1fWySn1slHNzIA", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 100, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720921, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrJKuuB1fWySn1WEgdABW", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrJKuuB1fWySn717F2dAV", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:24 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/returns_payment_intent_id_and_does_not_raise.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/returns_payment_intent_id_and_does_not_raise.yml new file mode 100644 index 0000000000..e827e61906 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_PaymentIntentValidator/_call/when_payment_intent_is_valid/valid_non-3D_credit_cards_are_correctly_handled/behaves_like_payments_intents/from_Visa_debit_/returns_payment_intent_id_and_does_not_raise.yml @@ -0,0 +1,389 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4000056655665556&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Ogqtdp3mqHyWXS","request_duration_ms":306}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:19 GMT + Content-Type: + - application/json + Content-Length: + - '959' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - '0938e7ba-3eb3-4b14-bb65-e14cf2a840c2' + Original-Request: + - req_Mjhsfd0XyWDLXd + Request-Id: + - req_Mjhsfd0XyWDLXd + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTrHKuuB1fWySnpGKE35pW", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "QOaaYMtlTSm6xJM8", + "funding": "debit", + "generated_from": null, + "last4": "5556", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710720919, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:15:19 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=100¤cy=eur&payment_method=pm_1OvTrHKuuB1fWySnpGKE35pW&payment_method_types[0]=card&capture_method=manual + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_Mjhsfd0XyWDLXd","request_duration_ms":442}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:19 GMT + Content-Type: + - application/json + Content-Length: + - '1343' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - b6bc186f-adbb-4310-8bcb-d10fa5c85939 + Original-Request: + - req_o8NUtAqs1AeIGV + Request-Id: + - req_o8NUtAqs1AeIGV + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrHKuuB1fWySn2RFIvGyx", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720919, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrHKuuB1fWySnpGKE35pW", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_confirmation", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:19 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTrHKuuB1fWySn2RFIvGyx/confirm + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_o8NUtAqs1AeIGV","request_duration_ms":406}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:15:20 GMT + Content-Type: + - application/json + Content-Length: + - '1365' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent%2Fconfirm; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 723b109e-9c51-4388-a489-ac568058dc1a + Original-Request: + - req_65bdb7xckNrfv9 + Request-Id: + - req_65bdb7xckNrfv9 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTrHKuuB1fWySn2RFIvGyx", + "object": "payment_intent", + "amount": 100, + "amount_capturable": 100, + "amount_details": { + "tip": {} + }, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "manual", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710720919, + "currency": "eur", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTrHKuuB1fWySn2YfTt57Z", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTrHKuuB1fWySnpGKE35pW", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_capture", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:15:20 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_called_from_Stripe_SCA/fetches_the_customer_id_and_the_card_id_from_the_correct_response_fields.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_called_from_Stripe_SCA/fetches_the_customer_id_and_the_card_id_from_the_correct_response_fields.yml new file mode 100644 index 0000000000..04c27a50a8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_called_from_Stripe_SCA/fetches_the_customer_id_and_the_card_id_from_the_correct_response_fields.yml @@ -0,0 +1,372 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_CyTKXekG7OQLuV","request_duration_ms":459}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:53 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 7fa698ed-1a56-4f04-b932-9c2d9843fb38 + Original-Request: + - req_mpPtHOpP9NsWW8 + Request-Id: + - req_mpPtHOpP9NsWW8 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsmKuuB1fWySnc3AVREoG", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721012, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:53 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: expand[0]=sources&email=alvaro_metz%40streich.co.uk + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:53 GMT + Content-Type: + - application/json + Content-Length: + - '822' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - a0a6726b-c54a-473f-9e19-424da37ac207 + Original-Request: + - req_7hK3AvMaZOoQIi + Request-Id: + - req_7hK3AvMaZOoQIi + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_Pkzsx4eT5IoBhL", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710721013, + "currency": null, + "default_currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "alvaro_metz@streich.co.uk", + "invoice_prefix": "24C0B07A", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "sources": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_Pkzsx4eT5IoBhL/sources" + }, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:16:53 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTsmKuuB1fWySnc3AVREoG/attach + body: + encoding: UTF-8 + string: customer=cus_Pkzsx4eT5IoBhL + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:54 GMT + Content-Type: + - application/json + Content-Length: + - '971' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 37f4e127-e85b-40c8-87b3-37e0f03636d6 + Original-Request: + - req_DTlxu8LY6tOPWM + Request-Id: + - req_DTlxu8LY6tOPWM + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsmKuuB1fWySnc3AVREoG", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721012, + "customer": "cus_Pkzsx4eT5IoBhL", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:54 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_request_fails/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_request_fails/raises_an_error.yml new file mode 100644 index 0000000000..bf4b65936c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/Stripe_ProfileStorer/create_customer_from_token/when_request_fails/raises_an_error.yml @@ -0,0 +1,563 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods + body: + encoding: UTF-8 + string: type=card&card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2025&card[cvc]=314 + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_mpPtHOpP9NsWW8","request_duration_ms":517}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:54 GMT + Content-Type: + - application/json + Content-Length: + - '960' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - f5feb700-deab-47e6-a3bb-1f7876e0c19d + Original-Request: + - req_jO2hgjzzNwuqXx + Request-Id: + - req_jO2hgjzzNwuqXx + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsoKuuB1fWySnCzCb8eht", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721014, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:54 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: name=Apple+Customer&email=applecustomer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_jO2hgjzzNwuqXx","request_duration_ms":419}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:55 GMT + Content-Type: + - application/json + Content-Length: + - '649' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - '0906982f-87ad-4dbe-b4fd-767448e9af6e' + Original-Request: + - req_kMpp9oFdLMAdlu + Request-Id: + - req_kMpp9oFdLMAdlu + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_Pkzs7IZP3pd5Zf", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710721015, + "currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "applecustomer@example.com", + "invoice_prefix": "07F225F9", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": "Apple Customer", + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:16:55 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTsoKuuB1fWySnCzCb8eht/attach + body: + encoding: UTF-8 + string: customer=cus_Pkzs7IZP3pd5Zf + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_kMpp9oFdLMAdlu","request_duration_ms":358}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:55 GMT + Content-Type: + - application/json + Content-Length: + - '971' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 49f9efd1-275c-4002-b6ff-41b74bbade31 + Original-Request: + - req_LAXaOxGMoXOMBE + Request-Id: + - req_LAXaOxGMoXOMBE + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTsoKuuB1fWySnCzCb8eht", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "visa", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "display_brand": "visa", + "exp_month": 12, + "exp_year": 2025, + "fingerprint": "6E6tgVjx6U65iHFV", + "funding": "credit", + "generated_from": null, + "last4": "4242", + "networks": { + "available": [ + "visa" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721014, + "customer": "cus_Pkzs7IZP3pd5Zf", + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:16:56 GMT +- request: + method: post + uri: https://api.stripe.com/v1/customers + body: + encoding: UTF-8 + string: expand[0]=sources&email=cristy_bins%40schoenauer.ca + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:56 GMT + Content-Type: + - application/json + Content-Length: + - '822' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcustomers; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 39039f16-97fc-438a-87ab-26f0c1c14e85 + Original-Request: + - req_4sTXiju5KGSaXj + Request-Id: + - req_4sTXiju5KGSaXj + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "cus_PkzsgzTLJgS1ty", + "object": "customer", + "address": null, + "balance": 0, + "created": 1710721016, + "currency": null, + "default_currency": null, + "default_source": null, + "delinquent": false, + "description": null, + "discount": null, + "email": "cristy_bins@schoenauer.ca", + "invoice_prefix": "90D76186", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null, + "rendering_options": null + }, + "livemode": false, + "metadata": {}, + "name": null, + "next_invoice_sequence": 1, + "phone": null, + "preferred_locales": [], + "shipping": null, + "sources": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/customers/cus_PkzsgzTLJgS1ty/sources" + }, + "tax_exempt": "none", + "test_clock": null + } + recorded_at: Mon, 18 Mar 2024 00:16:56 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_methods/pm_1OvTsoKuuB1fWySnCzCb8eht/attach + body: + encoding: UTF-8 + string: customer=cus_PkzsgzTLJgS1ty + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 400 + message: Bad Request + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:16:57 GMT + Content-Type: + - application/json + Content-Length: + - '245' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method%2Fattach; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 3256a36a-15d6-4aa6-9b24-3b16d12a8052 + Original-Request: + - req_i442hC44iepq49 + Request-Id: + - req_i442hC44iepq49 + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: | + { + "error": { + "message": "The payment method you provided has already been attached to a customer.", + "request_log_url": "https://dashboard.stripe.com/test/logs/req_i442hC44iepq49?t=1710721016", + "type": "invalid_request_error" + } + } + recorded_at: Mon, 18 Mar 2024 00:16:57 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/_As_an_hub_manager_I_want_to_make_Stripe_payments_/with_a_payment_using_a_StripeSCA_payment_method/that_is_completed/allows_to_refund_the_payment.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/_As_an_hub_manager_I_want_to_make_Stripe_payments_/with_a_payment_using_a_StripeSCA_payment_method/that_is_completed/allows_to_refund_the_payment.yml new file mode 100644 index 0000000000..46a49f88b8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.12.0/_As_an_hub_manager_I_want_to_make_Stripe_payments_/with_a_payment_using_a_StripeSCA_payment_method/that_is_completed/allows_to_refund_the_payment.yml @@ -0,0 +1,1083 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=lettuce.producer%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:06 GMT + Content-Type: + - application/json + Content-Length: + - '3047' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - c24c84fc-1a5d-43b9-ba61-3cc4953c0a70 + Original-Request: + - req_n54Ppkea7rp67x + Request-Id: + - req_n54Ppkea7rp67x + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OvTyn4K4RMDjS4m", + "object": "account", + "business_profile": { + "annual_revenue": null, + "estimated_worker_count": null, + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1710721386, + "default_currency": "aud", + "details_submitted": false, + "email": "lettuce.producer@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OvTyn4K4RMDjS4m/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "invoices": { + "default_account_tax_ids": null + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Mon, 18 Mar 2024 00:23:06 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_methods/pm_card_mastercard + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_n54Ppkea7rp67x","request_duration_ms":1766}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:07 GMT + Content-Type: + - application/json + Content-Length: + - '977' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_methods%2F%3Apayment_method; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_UHdxBdF4f19v09 + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pm_1OvTypKuuB1fWySn0d5PeiEU", + "object": "payment_method", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "card": { + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "unchecked" + }, + "country": "US", + "display_brand": "mastercard", + "exp_month": 3, + "exp_year": 2025, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "generated_from": null, + "last4": "4444", + "networks": { + "available": [ + "mastercard" + ], + "preferred": null + }, + "three_d_secure_usage": { + "supported": true + }, + "wallet": null + }, + "created": 1710721387, + "customer": null, + "livemode": false, + "metadata": {}, + "type": "card" + } + recorded_at: Mon, 18 Mar 2024 00:23:07 GMT +- request: + method: post + uri: https://api.stripe.com/v1/payment_intents + body: + encoding: UTF-8 + string: amount=2600¤cy=aud&payment_method=pm_card_mastercard&payment_method_types[0]=card&capture_method=automatic&confirm=true + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_UHdxBdF4f19v09","request_duration_ms":433}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:08 GMT + Content-Type: + - application/json + Content-Length: + - '1396' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 86d0fb70-d451-4969-9d3f-d8beb36dd080 + Original-Request: + - req_BGFuTj3WuvBHH4 + Request-Id: + - req_BGFuTj3WuvBHH4 + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "object": "payment_intent", + "amount": 2600, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 2600, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721387, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTyp4K4RMDjS4m4clZuyBB", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:23:08 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTyp4K4RMDjS4m1NqnFF25 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.12.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - "" + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:11 GMT + Content-Type: + - application/json + Content-Length: + - '1396' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_n6H77sljF3kMec + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "object": "payment_intent", + "amount": 2600, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 2600, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721387, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTyp4K4RMDjS4m4clZuyBB", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:23:11 GMT +- request: + method: get + uri: https://api.stripe.com/v1/payment_intents/pi_3OvTyp4K4RMDjS4m1NqnFF25 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:12 GMT + Content-Type: + - application/json + Content-Length: + - '5160' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fpayment_intents%2F%3Aintent; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Request-Id: + - req_jzDttzyCPbqzsW + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "object": "payment_intent", + "amount": 2600, + "amount_capturable": 0, + "amount_details": { + "tip": {} + }, + "amount_received": 2600, + "application": "", + "application_fee_amount": null, + "automatic_payment_methods": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [ + { + "id": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "object": "charge", + "amount": 2600, + "amount_captured": 2600, + "amount_refunded": 0, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTyp4K4RMDjS4m13eitOcT", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721387, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 23, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "payment_method": "pm_1OvTyp4K4RMDjS4m4clZuyBB", + "payment_method_details": { + "card": { + "amount_authorized": 2600, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 2600, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUeW40SzRSTURqUzRtKO-S3q8GMgYHrQRhcO46LBZa3OT0mlUOjx0tFVDoM-QN3uHeJaHtIY-iSfNNDolfxU_76v50Kn8ZBMy0", + "refunded": false, + "refunds": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges/ch_3OvTyp4K4RMDjS4m1wuevOiC/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges?payment_intent=pi_3OvTyp4K4RMDjS4m1NqnFF25" + }, + "client_secret": "", + "confirmation_method": "automatic", + "created": 1710721387, + "currency": "aud", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "latest_charge": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": "pm_1OvTyp4K4RMDjS4m4clZuyBB", + "payment_method_configuration_details": null, + "payment_method_options": { + "card": { + "installments": null, + "mandate_options": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "processing": null, + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + } + recorded_at: Mon, 18 Mar 2024 00:23:12 GMT +- request: + method: post + uri: https://api.stripe.com/v1/charges/ch_3OvTyp4K4RMDjS4m1wuevOiC/refunds + body: + encoding: UTF-8 + string: amount=2600&expand[0]=charge + headers: + Content-Type: + - application/x-www-form-urlencoded + Authorization: + - Basic c2tfdGVzdF94RmdKUU9sWHBNQUZzb3p0endGQlRGaFAwMEhHN0J1Q0ptOg== + User-Agent: + - Stripe/v1 ActiveMerchantBindings/1.133.0 + Stripe-Version: + - '2020-08-27' + X-Stripe-Client-User-Agent: + - "" + X-Stripe-Client-User-Metadata: + - '{"ip":null}' + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Connection: + - close + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Mon, 18 Mar 2024 00:23:13 GMT + Content-Type: + - application/json + Content-Length: + - '4536' + Connection: + - close + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Fcharges%2F%3Acharge%2Frefunds; + block-all-mixed-content; default-src 'none'; base-uri 'none'; form-action + 'none'; frame-ancestors 'none'; img-src 'self'; script-src 'self' 'report-sample'; + style-src 'self' + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Idempotency-Key: + - 2ca5b545-7c74-4543-bb6c-2f3b71d75044 + Original-Request: + - req_3hmvj4355uuaoS + Request-Id: + - req_3hmvj4355uuaoS + Stripe-Account: + - acct_1OvTyn4K4RMDjS4m + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2020-08-27' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "re_3OvTyp4K4RMDjS4m1x8ErKMf", + "object": "refund", + "amount": 2600, + "balance_transaction": "txn_3OvTyp4K4RMDjS4m1BdcDvrJ", + "charge": { + "id": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "object": "charge", + "amount": 2600, + "amount_captured": 2600, + "amount_refunded": 2600, + "application": "", + "application_fee": null, + "application_fee_amount": null, + "balance_transaction": "txn_3OvTyp4K4RMDjS4m13eitOcT", + "billing_details": { + "address": { + "city": null, + "country": null, + "line1": null, + "line2": null, + "postal_code": null, + "state": null + }, + "email": null, + "name": null, + "phone": null + }, + "calculated_statement_descriptor": "OFNOFNOFN", + "captured": true, + "created": 1710721387, + "currency": "aud", + "customer": null, + "description": null, + "destination": null, + "dispute": null, + "disputed": false, + "failure_balance_transaction": null, + "failure_code": null, + "failure_message": null, + "fraud_details": {}, + "invoice": null, + "livemode": false, + "metadata": {}, + "on_behalf_of": null, + "order": null, + "outcome": { + "network_status": "approved_by_network", + "reason": null, + "risk_level": "normal", + "risk_score": 23, + "seller_message": "Payment complete.", + "type": "authorized" + }, + "paid": true, + "payment_intent": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "payment_method": "pm_1OvTyp4K4RMDjS4m4clZuyBB", + "payment_method_details": { + "card": { + "amount_authorized": 2600, + "brand": "mastercard", + "checks": { + "address_line1_check": null, + "address_postal_code_check": null, + "cvc_check": "pass" + }, + "country": "US", + "exp_month": 3, + "exp_year": 2025, + "extended_authorization": { + "status": "disabled" + }, + "fingerprint": "BL35fEFVcTTS5wpE", + "funding": "credit", + "incremental_authorization": { + "status": "unavailable" + }, + "installments": null, + "last4": "4444", + "mandate": null, + "multicapture": { + "status": "unavailable" + }, + "network": "mastercard", + "network_token": { + "used": false + }, + "overcapture": { + "maximum_amount_capturable": 2600, + "status": "unavailable" + }, + "three_d_secure": null, + "wallet": null + }, + "type": "card" + }, + "radar_options": {}, + "receipt_email": null, + "receipt_number": null, + "receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xT3ZUeW40SzRSTURqUzRtKPGS3q8GMgbJATLnHKk6LBbZ-xLSlGY0PCIODJO7zHlijb68jSzdZw-U57pYDuYMQOXFGOQlYpq-n1qr", + "refunded": true, + "refunds": { + "object": "list", + "data": [ + { + "id": "re_3OvTyp4K4RMDjS4m1x8ErKMf", + "object": "refund", + "amount": 2600, + "balance_transaction": "txn_3OvTyp4K4RMDjS4m1BdcDvrJ", + "charge": "ch_3OvTyp4K4RMDjS4m1wuevOiC", + "created": 1710721392, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + ], + "has_more": false, + "total_count": 1, + "url": "/v1/charges/ch_3OvTyp4K4RMDjS4m1wuevOiC/refunds" + }, + "review": null, + "shipping": null, + "source": null, + "source_transfer": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "succeeded", + "transfer_data": null, + "transfer_group": null + }, + "created": 1710721392, + "currency": "aud", + "destination_details": { + "card": { + "reference_status": "pending", + "reference_type": "acquirer_reference_number", + "type": "refund" + }, + "type": "card" + }, + "metadata": {}, + "payment_intent": "pi_3OvTyp4K4RMDjS4m1NqnFF25", + "reason": null, + "receipt_number": null, + "source_transfer_reversal": null, + "status": "succeeded", + "transfer_reversal": null + } + recorded_at: Mon, 18 Mar 2024 00:23:13 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/helpers/admin/enterprises_helper_spec.rb b/spec/helpers/admin/enterprises_helper_spec.rb new file mode 100644 index 0000000000..e7683a12b3 --- /dev/null +++ b/spec/helpers/admin/enterprises_helper_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Admin::EnterprisesHelper, type: :helper do + let(:user) { build(:user) } + + before do + # Enable helper to use `#can?` method. + # We could extract this when other helper specs need it. + allow_any_instance_of(CanCan::ControllerAdditions).to receive(:current_ability) do + Spree::Ability.new(user) + end + allow(helper).to receive(:spree_current_user) { user } + end + + describe "#enterprise_side_menu_items" do + let(:enterprise) { build(:enterprise) } + let(:menu_items) { helper.enterprise_side_menu_items(enterprise) } + let(:visible_items) { menu_items.select { |i| i[:show] } } + + it "lists default items" do + expect(visible_items.pluck(:name)).to eq %w[ + primary_details address contact social about business_details images + vouchers enterprise_permissions inventory_settings tag_rules + shop_preferences users white_label + ] + end + + it "lists enabled features when allowed", feature: :connected_apps do + user.enterprises << enterprise + expect(visible_items.pluck(:name)).to include "connected_apps" + end + end +end diff --git a/spec/helpers/bulk_form_builder_spec.rb b/spec/helpers/bulk_form_builder_spec.rb index 00ab150c2c..2c1bbc23c7 100644 --- a/spec/helpers/bulk_form_builder_spec.rb +++ b/spec/helpers/bulk_form_builder_spec.rb @@ -9,7 +9,7 @@ describe BulkFormBuilder do let(:product) { create(:product) } let(:form) { BulkFormBuilder.new(:product, product, self, {}) } - it { expect(form.text_field(:name)).to_not include "changed" } + it { expect(form.text_field(:name)).not_to include "changed" } context "attribute has been changed" do before { product.assign_attributes name: "updated name" } @@ -19,7 +19,7 @@ describe BulkFormBuilder do context "and saved" do before { product.save } - it { expect(form.text_field(:name)).to_not include "changed" } + it { expect(form.text_field(:name)).not_to include "changed" } end end end diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index 75fbb5189c..1c355a5577 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -78,6 +78,6 @@ describe InjectionHelper, type: :helper do gateway_customer_profile_id: nil) injected_cards = helper.inject_saved_credit_cards expect(injected_cards).to match "1234" - expect(injected_cards).to_not match "4321" + expect(injected_cards).not_to match "4321" end end diff --git a/spec/helpers/shop_helper_spec.rb b/spec/helpers/shop_helper_spec.rb index 3050f0b131..1c0bd4df7b 100644 --- a/spec/helpers/shop_helper_spec.rb +++ b/spec/helpers/shop_helper_spec.rb @@ -24,7 +24,7 @@ describe ShopHelper, type: :helper do end it "should not return the groups tab" do - expect(helper.shop_tabs).to_not include(name: "groups", show: true, title: "Groups") + expect(helper.shop_tabs).not_to include(name: "groups", show: true, title: "Groups") end end diff --git a/spec/helpers/tax_helper_spec.rb b/spec/helpers/tax_helper_spec.rb index 1e2cf79915..ed87864390 100644 --- a/spec/helpers/tax_helper_spec.rb +++ b/spec/helpers/tax_helper_spec.rb @@ -49,22 +49,80 @@ describe TaxHelper, type: :helper do end describe "#display_line_items_taxes" do - it "displays included tax" do - expect( - helper.display_line_items_taxes(line_item) - ).to eq Spree::Money.new(line_item.included_tax, currency: line_item.currency) + let(:enterprise_fee) { create(:enterprise_fee, tax_category: tax_rate.tax_category) } + + context "with included tax" do + it "displays included tax" do + expect( + helper.display_line_items_taxes(line_item) + ).to eq Spree::Money.new(line_item.included_tax, currency: line_item.currency) + end + + context "with enterprise fee incuring tax" do + let(:fee_adjustment) { + create( :adjustment, originator: enterprise_fee, adjustable: line_item, state: "closed") + } + let!(:fee_tax_adjustment) { + create( + :adjustment, + originator: tax_rate, + adjustable: fee_adjustment, + amount: 10, + state: "closed", + included: true + ) + } + + it "includes enterprise fee tax" do + expected_tax = line_item.included_tax + fee_tax_adjustment.amount + expect( + helper.display_line_items_taxes(line_item) + ).to eq Spree::Money.new(expected_tax, currency: line_item.currency) + end + end end - it "displays additional tax" do - expect( - helper.display_line_items_taxes(line_item2) - ).to eq Spree::Money.new(line_item2.added_tax, currency: line_item2.currency) + context "with additional tax (tax exluded from price)" do + it "displays additional tax" do + expect( + helper.display_line_items_taxes(line_item2) + ).to eq Spree::Money.new(line_item2.added_tax, currency: line_item2.currency) + end + + context "with enterprise fee incuring tax" do + let(:fee_adjustment) { + create( :adjustment, originator: enterprise_fee, adjustable: line_item2, state: "closed") + } + let(:fee_tax_adjustment) { + create( + :adjustment, + originator: tax_rate, + adjustable: fee_adjustment, + amount: 10, + state: "closed", + included: false + ) + } + + it "includes enterprise fee tax" do + expected_tax = line_item2.added_tax + fee_tax_adjustment.amount + expect( + helper.display_line_items_taxes(line_item2) + ).to eq Spree::Money.new(expected_tax, currency: line_item2.currency) + end + end end it "displays formatted 0.00 amount when amount is zero" do expect( helper.display_line_items_taxes(line_item3) - ).to eq Spree::Money.new(0.00,) + ).to eq Spree::Money.new(0.00) + end + + it "optionally displays nothing when amount is zero" do + expect( + helper.display_line_items_taxes(line_item3, display_zero: false) + ).to be_nil end end diff --git a/spec/javascripts/stimulus/bulk_form_controller_test.js b/spec/javascripts/stimulus/bulk_form_controller_test.js index b0a3386ce9..82d83c7271 100644 --- a/spec/javascripts/stimulus/bulk_form_controller_test.js +++ b/spec/javascripts/stimulus/bulk_form_controller_test.js @@ -36,6 +36,10 @@ describe("BulkFormController", () => {
+
@@ -47,7 +51,7 @@ describe("BulkFormController", () => { }); describe("marking changed fields", () => { - it("onInput", () => { + it("input: onInput", () => { input1a.value = 'updated1a'; input1a.dispatchEvent(new Event("input")); // Expect only first field to show changed @@ -59,7 +63,23 @@ describe("BulkFormController", () => { input1a.value = 'initial1a'; input1a.dispatchEvent(new Event("input")); expect(input1a.classList).not.toContain('changed'); + }); + it("select: onInput", () => { + // Select a different option (it's the only way in Jest..) + select1.options[0].selected = true; + select1.options[1].selected = false; + select1.dispatchEvent(new Event("input")); + // Expect select to show changed + expect(input1a.classList).not.toContain('changed'); + expect(input1b.classList).not.toContain('changed'); + expect(select1.classList).toContain('changed'); + + // Change back to original value + select1.options[0].selected = false; + select1.options[1].selected = true; + select1.dispatchEvent(new Event("input")); + expect(select1.classList).not.toContain('changed'); }); it("multiple fields", () => { @@ -181,6 +201,48 @@ describe("BulkFormController", () => { }); }); + describe("Adding new fields", () => { + beforeEach(() => { + document.body.innerHTML = ` +
+ + +
+
+ +
+ + + `; + }); + + describe("registerElements", () => { + beforeEach(() => { + // Add new field after controller has initialised + input1a.insertAdjacentHTML("afterend", template.innerHTML); + + // Trigger bulk-form#registerElements + form.dispatchEvent(new Event("custom-event")); + }); + + it("onInput", () => { + input1b.value = 'updated1b'; + input1b.dispatchEvent(new Event("input")); + // Expect only updated field to show changed + expect(input1b.classList).toContain('changed'); + expect(input2.classList).not.toContain('changed'); + + // Change back to original value + input1b.value = 'initial1b'; + input1b.dispatchEvent(new Event("input")); + expect(input1b.classList).not.toContain('changed'); + }); + }) + }); + // unable to test disconnect at this stage // describe("disconnect()", () => { // it("resets other elements", () => { diff --git a/spec/javascripts/stimulus/dropdown_controller_test.js b/spec/javascripts/stimulus/dropdown_controller_test.js index 2f1bceaf4d..9e8c60c5ed 100644 --- a/spec/javascripts/stimulus/dropdown_controller_test.js +++ b/spec/javascripts/stimulus/dropdown_controller_test.js @@ -13,13 +13,20 @@ describe("Dropdown controller", () => { describe("Controller", () => { beforeEach(() => { - document.body.innerHTML = `
- - - - + document.body.innerHTML = `
+
`; }); @@ -27,48 +34,15 @@ describe("Dropdown controller", () => { document.body.innerHTML = ""; }); - it("hide menu by default", () => { - const menu = document.getElementById("menu"); - expect(menu.classList.contains("hidden")).toBe(true); - }); - - it("show menu when toggle and add/remove class on arrow", () => { - const dropdown = document.getElementById("dropdown"); - const arrow = document.getElementById("arrow"); - const menu = document.getElementById("menu"); - expect(menu.classList.contains("hidden")).toBe(true); - expect(arrow.classList.contains("expandedClass")).toBe(false); - expect(arrow.classList.contains("expandedClass2")).toBe(false); - expect(arrow.classList.contains("collapsedClass")).toBe(true); - - dropdown.click(); - - expect(menu.classList.contains("hidden")).toBe(false); - expect(arrow.classList.contains("expandedClass")).toBe(true); - expect(arrow.classList.contains("expandedCLass2")).toBe(true); - expect(arrow.classList.contains("collapsedClass")).toBe(false); - }); - it ("hide menu when click outside", () => { const dropdown = document.getElementById("dropdown"); const menu = document.getElementById("menu"); - dropdown.click(); - expect(menu.classList.contains("hidden")).toBe(false); - + //open the details + dropdown.toggleAttribute('open') + //click elsewhere document.body.click(); - expect(menu.classList.contains("hidden")).toBe(true); - }); - - it ("do not display menu when disabled", () => { - const dropdown = document.getElementById("dropdown"); - const container = document.getElementById("container"); - const menu = document.getElementById("menu"); - container.classList.add("disabled"); - - dropdown.click(); - - expect(menu.classList.contains("hidden")).toBe(true); + expect(dropdown.open).toBe(false); }); }); }); diff --git a/spec/javascripts/stimulus/flash_controller_test.js b/spec/javascripts/stimulus/flash_controller_test.js new file mode 100644 index 0000000000..665c07aa47 --- /dev/null +++ b/spec/javascripts/stimulus/flash_controller_test.js @@ -0,0 +1,34 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import flash_controller from "../../../app/webpacker/controllers/flash_controller"; + +describe("FlashController", () => { + beforeAll(() => { + const application = Application.start(); + application.register("flash", flash_controller); + }); + + beforeEach(() => { + document.body.innerHTML = ` +
+ `; + + }); + + describe("autoClose", () => { + jest.useFakeTimers(); + + it("is cleared after about 5 seconds", () => { + let element = document.getElementById("element"); + expect(element).not.toBe(null); + + jest.advanceTimersByTime(5500); + + element = document.getElementById("element"); + expect(element).toBe(null); + }); + }); +}); diff --git a/spec/javascripts/stimulus/help_modal_controller_test.js b/spec/javascripts/stimulus/help_modal_controller_test.js index 1120ede695..944cdea04c 100644 --- a/spec/javascripts/stimulus/help_modal_controller_test.js +++ b/spec/javascripts/stimulus/help_modal_controller_test.js @@ -34,7 +34,7 @@ describe("HelpModalController", () => { data-action="click->help-modal#close">