From 10bbb34689021bf982aeae44ff905200c29541c9 Mon Sep 17 00:00:00 2001 From: Damian Legawiec Date: Sat, 7 Aug 2021 00:01:52 +0000 Subject: [PATCH] GitBook: [master] 65 pages modified --- README.md | 10 + SUMMARY.md | 91 ++++ advanced/deface_overrides_tutorial.md | 260 +++++++++ advanced/existing_app_tutorial.md | 135 +++++ advanced/extend_product_attributes.md | 98 ++++ advanced/testing.md | 93 ++++ contributing/developing_spree.md | 172 ++++++ contributing/extensions_tutorial.md | 257 +++++++++ contributing/index.md | 39 ++ contributing/upgrading_extensions.md | 96 ++++ customization/api_v1.md | 179 +++++++ customization/api_v2.md | 16 + customization/authentication.md | 209 ++++++++ customization/checkout.md | 270 ++++++++++ customization/dependencies.md | 88 +++ customization/emails.md | 22 + customization/extensions.md | 46 ++ customization/i18n.md | 136 +++++ customization/images.md | 54 ++ customization/logic.md | 127 +++++ customization/permissions.md | 111 ++++ customization/storefront.md | 459 ++++++++++++++++ deployment/aws.md | 36 ++ deployment/heroku.md | 58 ++ getting-started/installation.md | 36 ++ getting-started/understanding_spree.md | 67 +++ internals/addresses.md | 54 ++ internals/adjustments.md | 149 ++++++ internals/calculators.md | 250 +++++++++ internals/inventory.md | 50 ++ internals/orders.md | 138 +++++ internals/payments.md | 186 +++++++ internals/preferences.md | 434 +++++++++++++++ internals/products.md | 162 ++++++ internals/promotions.md | 164 ++++++ internals/shipments.md | 504 ++++++++++++++++++ internals/stores.md | 65 +++ internals/taxation.md | 132 +++++ security/api.md | 14 + security/authentication_and_authorization.md | 33 ++ security/index.md | 44 ++ security/pci_compliance.md | 24 + upgrades/index.md | 42 ++ upgrades/upgrades/README.md | 2 + .../upgrades/four-dot-oh-to-four-dot-one.md | 121 +++++ .../upgrades/four-dot-one-to-four-dot-two.md | 126 +++++ .../fout-dot-two-to-four-dot-three.md | 97 ++++ .../upgrades/one-dot-oh-to-one-dot-one.md | 71 +++ .../upgrades/one-dot-one-to-one-dot-two.md | 59 ++ .../upgrades/one-dot-three-to-two-dot-oh.md | 68 +++ .../upgrades/one-dot-two-to-one-dot-three.md | 90 ++++ .../upgrades/point-seventy-to-one-dot-oh.md | 82 +++ .../upgrades/point-sixty-to-point-seventy.md | 91 ++++ .../three-dot-five-to-three-dot-six.md | 54 ++ .../three-dot-four-to-three-dot-five.md | 66 +++ .../upgrades/three-dot-oh-to-three-dot-one.md | 86 +++ .../three-dot-one-to-three-dot-two.md | 62 +++ .../three-dot-seven-to-four-dot-oh.md | 256 +++++++++ .../three-dot-six-to-three-dot-seven.md | 71 +++ .../three-dot-three-to-three-dot-four.md | 50 ++ .../three-dot-two-to-three-dot-three.md | 100 ++++ .../upgrades/two-dot-oh-to-two-dot-one.md | 52 ++ .../upgrades/two-dot-one-to-two-dot-two.md | 62 +++ .../upgrades/two-dot-three-to-two-dot-four.md | 47 ++ .../upgrades/two-dot-two-to-two-dot-three.md | 51 ++ 65 files changed, 7374 insertions(+) create mode 100644 README.md create mode 100644 SUMMARY.md create mode 100644 advanced/deface_overrides_tutorial.md create mode 100644 advanced/existing_app_tutorial.md create mode 100644 advanced/extend_product_attributes.md create mode 100644 advanced/testing.md create mode 100644 contributing/developing_spree.md create mode 100644 contributing/extensions_tutorial.md create mode 100644 contributing/index.md create mode 100644 contributing/upgrading_extensions.md create mode 100644 customization/api_v1.md create mode 100644 customization/api_v2.md create mode 100644 customization/authentication.md create mode 100644 customization/checkout.md create mode 100644 customization/dependencies.md create mode 100644 customization/emails.md create mode 100644 customization/extensions.md create mode 100644 customization/i18n.md create mode 100644 customization/images.md create mode 100644 customization/logic.md create mode 100644 customization/permissions.md create mode 100644 customization/storefront.md create mode 100644 deployment/aws.md create mode 100644 deployment/heroku.md create mode 100644 getting-started/installation.md create mode 100644 getting-started/understanding_spree.md create mode 100644 internals/addresses.md create mode 100644 internals/adjustments.md create mode 100644 internals/calculators.md create mode 100644 internals/inventory.md create mode 100644 internals/orders.md create mode 100644 internals/payments.md create mode 100644 internals/preferences.md create mode 100644 internals/products.md create mode 100644 internals/promotions.md create mode 100644 internals/shipments.md create mode 100644 internals/stores.md create mode 100644 internals/taxation.md create mode 100644 security/api.md create mode 100644 security/authentication_and_authorization.md create mode 100644 security/index.md create mode 100644 security/pci_compliance.md create mode 100644 upgrades/index.md create mode 100644 upgrades/upgrades/README.md create mode 100644 upgrades/upgrades/four-dot-oh-to-four-dot-one.md create mode 100644 upgrades/upgrades/four-dot-one-to-four-dot-two.md create mode 100644 upgrades/upgrades/fout-dot-two-to-four-dot-three.md create mode 100644 upgrades/upgrades/one-dot-oh-to-one-dot-one.md create mode 100644 upgrades/upgrades/one-dot-one-to-one-dot-two.md create mode 100644 upgrades/upgrades/one-dot-three-to-two-dot-oh.md create mode 100644 upgrades/upgrades/one-dot-two-to-one-dot-three.md create mode 100644 upgrades/upgrades/point-seventy-to-one-dot-oh.md create mode 100644 upgrades/upgrades/point-sixty-to-point-seventy.md create mode 100644 upgrades/upgrades/three-dot-five-to-three-dot-six.md create mode 100644 upgrades/upgrades/three-dot-four-to-three-dot-five.md create mode 100644 upgrades/upgrades/three-dot-oh-to-three-dot-one.md create mode 100644 upgrades/upgrades/three-dot-one-to-three-dot-two.md create mode 100644 upgrades/upgrades/three-dot-seven-to-four-dot-oh.md create mode 100644 upgrades/upgrades/three-dot-six-to-three-dot-seven.md create mode 100644 upgrades/upgrades/three-dot-three-to-three-dot-four.md create mode 100644 upgrades/upgrades/three-dot-two-to-three-dot-three.md create mode 100644 upgrades/upgrades/two-dot-oh-to-two-dot-one.md create mode 100644 upgrades/upgrades/two-dot-one-to-two-dot-two.md create mode 100644 upgrades/upgrades/two-dot-three-to-two-dot-four.md create mode 100644 upgrades/upgrades/two-dot-two-to-two-dot-three.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..503b053 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +--- +title: Installation +section: getting_started +order: 0 +--- + +# Developer's Documentation + +## + diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..f3d3491 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,91 @@ +# Table of contents + +* [Developer's Documentation](README.md) + +## Getting Started + +* [Installation](getting-started/installation.md) +* [Understanding how Spree works](getting-started/understanding_spree.md) + +## Internals + +* [Stores](internals/stores.md) +* [Products](internals/products.md) +* [Orders](internals/orders.md) +* [Payments](internals/payments.md) +* [Shipments](internals/shipments.md) +* [Promotions](internals/promotions.md) +* [Inventory](internals/inventory.md) +* [Taxation](internals/taxation.md) +* [Addresses](internals/addresses.md) +* [Adjustments](internals/adjustments.md) +* [Calculators](internals/calculators.md) +* [Preferences](internals/preferences.md) + +## Customization + +* [Dependencies](customization/dependencies.md) +* [Business Logic](customization/logic.md) +* [Extensions](customization/extensions.md) +* [API v1](customization/api_v1.md) +* [API v2](customization/api_v2.md) +* [Checkout](customization/checkout.md) +* [Authentication](customization/authentication.md) +* [Permissions](customization/permissions.md) +* [Images](customization/images.md) +* [Internationalization](customization/i18n.md) +* [Storefront](customization/storefront.md) +* [Emails](customization/emails.md) + +## Deployment + +* [AWS](deployment/aws.md) +* [Heroku](deployment/heroku.md) + +## Security + +* [General guidelines](security/index.md) +* [API](security/api.md) +* [PCI Compliance](security/pci_compliance.md) +* [Authentication & authorization](security/authentication_and_authorization.md) + +## Advanced + +* [Adding Spree to existing Rails application](advanced/existing_app_tutorial.md) +* [Deface](advanced/deface_overrides_tutorial.md) +* [Adding new attributes to Product model](advanced/extend_product_attributes.md) +* [Testing Spree applications](advanced/testing.md) + +## Upgrades + +* [How to perform upgrades](upgrades/index.md) +* [Upgrade guides](upgrades/upgrades/README.md) + * [4.2 to 4.3](upgrades/upgrades/fout-dot-two-to-four-dot-three.md) + * [4.0 to 4.1](upgrades/upgrades/four-dot-oh-to-four-dot-one.md) + * [4.1 to 4.2](upgrades/upgrades/four-dot-one-to-four-dot-two.md) + * [one-dot-oh-to-one-dot-one](upgrades/upgrades/one-dot-oh-to-one-dot-one.md) + * [one-dot-one-to-one-dot-two](upgrades/upgrades/one-dot-one-to-one-dot-two.md) + * [one-dot-three-to-two-dot-oh](upgrades/upgrades/one-dot-three-to-two-dot-oh.md) + * [one-dot-two-to-one-dot-three](upgrades/upgrades/one-dot-two-to-one-dot-three.md) + * [point-seventy-to-one-dot-oh](upgrades/upgrades/point-seventy-to-one-dot-oh.md) + * [point-sixty-to-point-seventy](upgrades/upgrades/point-sixty-to-point-seventy.md) + * [three-dot-five-to-three-dot-six](upgrades/upgrades/three-dot-five-to-three-dot-six.md) + * [three-dot-four-to-three-dot-five](upgrades/upgrades/three-dot-four-to-three-dot-five.md) + * [three-dot-oh-to-three-dot-one](upgrades/upgrades/three-dot-oh-to-three-dot-one.md) + * [three-dot-one-to-three-dot-two](upgrades/upgrades/three-dot-one-to-three-dot-two.md) + * [three-dot-seven-to-four-dot-oh](upgrades/upgrades/three-dot-seven-to-four-dot-oh.md) + * [three-dot-six-to-three-dot-seven](upgrades/upgrades/three-dot-six-to-three-dot-seven.md) + * [three-dot-three-to-three-dot-four](upgrades/upgrades/three-dot-three-to-three-dot-four.md) + * [three-dot-two-to-three-dot-three](upgrades/upgrades/three-dot-two-to-three-dot-three.md) + * [two-dot-oh-to-two-dot-one](upgrades/upgrades/two-dot-oh-to-two-dot-one.md) + * [two-dot-one-to-two-dot-two](upgrades/upgrades/two-dot-one-to-two-dot-two.md) + * [two-dot-three-to-two-dot-four](upgrades/upgrades/two-dot-three-to-two-dot-four.md) + * [two-dot-two-to-two-dot-three](upgrades/upgrades/two-dot-two-to-two-dot-three.md) + +## Contributing + +* [General guidelines](contributing/index.md) +* [Developing Spree](contributing/developing_spree.md) +* [Creating new extensions](contributing/extensions_tutorial.md) +* [Upgrading extensions](contributing/upgrading_extensions.md) + diff --git a/advanced/deface_overrides_tutorial.md b/advanced/deface_overrides_tutorial.md new file mode 100644 index 0000000..58ff8f6 --- /dev/null +++ b/advanced/deface_overrides_tutorial.md @@ -0,0 +1,260 @@ +--- +title: Deface Overrides +section: advanced +--- + +# Deface + + Using Deface is not recommended. Please refer to \[Template Customization section\]\(/developer/customization/view.html\) for more information. + +## Introduction + +This tutorial is a continuation of the previous one, [Extensions](/developer/advanced/extensions_tutorial.html), and begins where we left off in the last one. We have created a simple extension for promoting on-sale products on a "sales homepage". + +In this tutorial we are going to learn about [Deface](https://github.com/spree/deface) and how we can use it to improve our extension. As part of improving our extension, we will be updating the existing Spree admin interface so that we are able to set the `sale_price` for products. + +## What is Deface? + +Deface is a standalone Rails library that enables you to customize Erb templates without needing to directly edit the underlying view file. Deface allows you to use standard CSS3 style selectors to target any element \(including Ruby blocks\), and perform an action against all the matching elements. + +## Improving Our Extension Using Deface + +### The Goal + +Our goal is to add a field to the product edit admin page that allows the `sale_price` to be added or updated. We could do this by overriding the view Spree provides, but there are potential problems with this technique. If Spree updates the view in a new release we won't get the updated view as we are already overriding it. We would need to update our view with the new content from Spree and then add our customizations back in to stay fully up to date. + +Let's do this instead using Deface, which we just learned about. Using Deface will allow us to keep our view customizations in one spot, `app/overrides`, and make sure we are always using the latest implementation of the view provided by Spree. + +### The Implementation + +We want to override the product edit admin page, so the view we want to modify in this case is the product form partial. This file's path will be `spree/admin/products/_form.html.erb`. + +First, let's create the overrides directory with the following command: + +```bash +mkdir app/overrides +``` + +So we want to override `spree/admin/products/_form.html.erb`. Here is the part of the file we are going to add content to \(you can also view the [full file](https://github.com/spree/spree/blob/master/backend/app/views/spree/admin/products/_form.html.erb)\): + +```text +
+ <%= f.field_container :price do %> + <%= f.label :price, raw(Spree.t(:master_price) + required_span_tag) %> + <%= f.text_field :price, value: number_to_currency(@product.price, unit: '')%> + <%= f.error_message_on :price %> + <% end %> +
+``` + +We want our override to insert another field container after the price field container. We can do this by creating a new file `app/overrides/add_sale_price_to_product_edit.rb` and adding the following content: + +```ruby +Deface::Override.new(virtual_path: 'spree/admin/products/_form', + name: 'add_sale_price_to_product_edit', + insert_after: "erb[loud]:contains('text_field :price')", + text: " + <%%= f.field_container :sale_price do %> + <%%= f.label :sale_price, raw(Spree.t(:sale_price) + content_tag(:span, ' *')) %> + <%%= f.text_field :sale_price, value: + number_to_currency(@product.sale_price, unit: '') %> + <%%= f.error_message_on :sale_price %> + <%% end %> + ") +``` + +We also need to delegate `sale_price` to the master variant in order to get the updated product edit form working. + +We can do this by creating a new file `app/models/spree/product_decorator.rb` and adding the following content to it: + +```ruby +module Spree + Product.class_eval do + delegate :sale_price, :sale_price=, to: :master + end +end +``` + +Now, when we head to `http://localhost:3000/admin/products` and edit a product, we should be able to set a sale price for the product and be able to view it on our sale page, `http://localhost:3000/sale`. Note that you will likely need to restart our example Spree application \(created in the [Getting Started tutorial](/developer/getting_started/installation.html)\). + +### Available actions + +Deface applies an **action** to element\(s\) matching the supplied CSS selector. These actions are passed when defining a new override are supplied as the key while the CSS selector for the target element\(s\) is the value, for example: + +```ruby +remove: "p.junk" + +insert_after: "div#wow p.header" + +insert_bottom: "ul#giant-list" +``` + +Deface currently supports the following actions: + +* :remove - Removes all elements that match the supplied selector +* :replace - Replaces all elements that match the supplied selector, with the content supplied +* :replace\_contents - Replaces the contents of all elements that match the supplied selector +* :surround - Surrounds all elements that match the supplied selector, expects replacement markup to contain <%%= render\_original %> placeholder +* :surround\_contents - Surrounds the contents of all elements that match the supplied selector, expects replacement markup to contain <%%= render\_original %> placeholder +* :insert\_after - Inserts after all elements that match the supplied selector +* :insert\_before - Inserts before all elements that match the supplied selector +* :insert\_top - Inserts inside all elements that match the supplied selector, as the first child +* :insert\_bottom - Inserts inside all elements that match the supplied selector, as the last child +* :set\_attributes - Sets attributes on all elements that match the supplied selector, replacing existing attribute value if present or adding if not. Expects :attributes option to be passed. +* :add\_to\_attributes - Appends value to attributes on all elements that match the supplied selector, adds attribute if not present. Expects :attributes option to be passed. +* :remove\_from\_attributes - Removes value from attributes on all elements that match the supplied selector. Expects :attributes option to be passed. + + Not all actions are applicable to all elements. For example, \`:insert\_top\` and \`:insert\_bottom\` expects a parent element with children. + +### Supplying content + +Deface supports three options for supplying content to be used by an override: + +* :text - String containing markup +* :partial - Relative path to a partial +* :template - Relative path to a template + + As Deface operates on the Erb source the content supplied to an override can include Erb, it is not limited to just HTML. You also have access to all variables accessible in the original Erb context. + +### Targeting elements + +While Deface allows you to use a large subset of CSS3 style selectors \(as provided by Nokogiri\), the majority of Spree's views have been updated to include a custom HTML attribute \(data-hook\), which is designed to provide consistent targets for your overrides to use. + +As Spree views are changed over coming versions, the original HTML elements maybe edited or be removed. We will endeavour to ensure that data-hook / id combination will remain consistent within any single view file \(where possible\), thus making your overrides more robust and upgrade proof. + +For example, spree/products/show.html.erb looks as follows: + +```text +
+ <%% body_id = 'product-details' %> +
+
+
+
+ <%%= render 'image' %> +
+ +
+ <%%= render 'thumbnails', product: product %> +
+
+ +
+ <%%= render 'properties' %> +
+ +
+
+ +
+
+ +
+ +

<%%= accurate_title %>

+ +
+ <%%= product_description(product) rescue Spree.t(:product_has_no_description) %> +
+ +
+ <%%= render 'cart_form' %> +
+
+ + <%%= render 'taxons' %> +
+
+
+``` + +As you can see from the example above the `data-hook` can be present in a number of ways: + +* On elements with **no** `id` attribute the `data-hook` attribute + + contains a value similar to what would be included in the `id` + + attribute. + +* On elements with an `id` attribute the `data-hook` attribute does + + **not** normally contain a value. + +* Occasionally on elements with an `id` attribute the `data-hook` will + + contain a value different from the elements id. This is generally to + + support migration from the old 0.60.x style of hooks, where the old + + hook names were converted into `data-hook` versions. + +The suggested way to target an element is to use the `data-hook` attribute wherever possible. Here are a few examples based on **products/show.html.erb** above: + +```ruby +replace: "[data-hook='product_show']" + +insert_top: "#thumbnails[data-hook]" + +remove: "[data-hook='cart_form']" +``` + +You can also use a combination of both styles of selectors in a single override to ensure maximum protection against changes: + +```ruby + insert_top: "[data-hook='thumbnails'], #thumbnails[data-hook]" +``` + +### Targeting ruby blocks + +Deface evaluates all the selectors passed against the original erb view contents \(and importantly not against the finished / generated HTML\). In order for Deface to make ruby blocks contained in a view parseable they are converted into a pseudo markup as follows. + +Given the following Erb file: + +```text +<%% if products.empty? %> + <%%= Spree.t(:no_products_found) %> +<%% elsif params.key?(:keywords) %> +

<%%= Spree.t(:products) %>

+<%% end %> +``` + +Would be seen by Deface as: + +```markup + +``` + +So you can target ruby code blocks with the same standard CSS3 style selectors, for example: + +```ruby +replace: "erb[loud]:contains('t(:products)')" + +insert_before: "erb[silent]:contains('elsif')" +``` + +### View upgrade protection + +To ensure upgrading between versions of Spree is as painless as possible, Deface supports an `:original` option that can contain a string of the original content that's being replaced. When Deface is applying the override it will ensure that the current source matches the value supplied and will output to the Rails application log if they are different. + +These warnings are a good indicator that you need to review the source and ensure your replacement is adequately replacing all the functionality provided by Spree. This will help reduce unexpected issues after upgrades. + +Once you've reviewed the new source you can update the `:original` value to new source to clear the warning. + + Deface removes all whitespace from both the actual and \`:original\` source values before comparing, to reduce false warnings caused by basic whitespace differences. + +### Organizing Overrides + +The suggested method for organizing your overrides is to create a separate file for each override inside the **app/overrides** directory, naming each file the same as the **:name** specified within. + +### More information on Deface + +For more information and sample overrides please refer to its [README](https://github.com/spree/deface) file on GitHub. + diff --git a/advanced/existing_app_tutorial.md b/advanced/existing_app_tutorial.md new file mode 100644 index 0000000..3953801 --- /dev/null +++ b/advanced/existing_app_tutorial.md @@ -0,0 +1,135 @@ +--- +title: Add Spree to an existing Ruby on Rails application +section: advanced +order: 1 +--- + +# Adding Spree to existing Rails application + +## Prerequisites + +Before starting this tutorial, please check which Rails version are you using by running: + +```bash +bundle exec rails -v +``` + +in your project root directory. + +## Installation + +1. Add Spree gems to your `Gemfile` + + **Rails 5.2, 6.0 and 6.1** + + ```ruby + gem 'spree', '~> 4.3' + gem 'spree_auth_devise', '~> 4.3' + gem 'spree_gateway', '~> 3.9' + gem 'spree_i18n', '~> 5.0' + gem 'sassc', github: 'sass/sassc-ruby', branch: 'master' # only needed for MacOS and Ruby 3.0 + ``` + + **Rails 5.1** + + ```ruby + gem 'spree', '~> 3.5.0' + gem 'spree_auth_devise', '~> 3.3' + gem 'spree_gateway', '~> 3.3' + ``` + + **Rails 5.0** + + ```ruby + gem 'spree', '~> 3.2.0' + gem 'spree_auth_devise', '~> 3.2.0' + gem 'spree_gateway', '~> 3.2.0' + ``` + +2. Install gems + + ```bash + bundle install + ``` + + **Note**: if you run into `Bundler could not find compatible versions for gem "sprockets":` error message, please run + + ```bash + bundle update + ``` + +3. Use the install generators to set up Spree + + ```text + bundle exec rails g spree:install --user_class=Spree::User + bundle exec rails g spree:auth:install + bundle exec rails g spree_gateway:install + ``` + +### Installation options + +By default, the installation generator \(`rails g spree:install`\) will run migrations as well as adding seed and sample data. This can be disabled using + +```text +rails g spree:install --migrate=false --sample=false --seed=false +``` + +You can always perform any of these steps later by using these commands. + +```text +bundle exec rake railties:install:migrations +bundle exec rails db:migrate +bundle exec rails db:seed +bundle exec rake spree_sample:load +``` + +### Headless installation \(API-mode\) + +To use Spree in [API-only mode](https://guides.spreecommerce.org/api/overview/) you need to replace `spree` with `spree_api` in your project Gemfile. This will skip Storefront and Admin Panel. If you would want to include the Admin Panel please add `spree_backend` to your Gemfile. + +### Mounting the Spree engine + +When `rails g spree:install` is run inside an application, it will install Spree, mounting the `Spree::Core::Engine` component by inserting this line automatically into `config/routes.rb`: + +```ruby +mount Spree::Core::Engine, at: '/' +``` + +By default, all Spree routes will be available at the root of your domain. For example, if your domain is `http://localhost:3000`, Spree’s `/products` URL will be available at `http://localhost:3000/products`. + +You can customize this simply by changing the `:at` specification in `config/routes.rb` to be something else. For example, if you would like Spree to be mounted at `/shop`, you can write this: + +```ruby +mount Spree::Core::Engine, at: `/shop` +``` + +The different parts of Spree \(API, Admin\) will be mounted there as well, eg. `http://localhost:3000/shop/admin`. + +### Use your existing authentication + +[Spree Auth Devise](https://github.com/spree/spree_auth_devise) is the default authentication that comes with Spree but you can swap it for your own, please [follow this guide](/developer/customization/authentication.html) + +## Hello, Spree Commerce + +You now have a functional Spree application after running only a few commands! + +To see your application in action, open a browser window and navigate to [http://localhost:3000](http://localhost:3000). You should see the Spree default home page: + +To stop the web server, hit Ctrl-C in the terminal window where it's running. In development mode, Spree does not generally require you to stop the server; changes you make in files will be automatically picked up by the server. + +### Logging Into the Admin Panel + +The next thing you'll probably want to do is to log into the admin interface. Use your browser window to navigate to [http://localhost:3000/admin](http://localhost:3000/admin). You can login with the username `spree@example.com` and password `spree123`. + +Upon successful authentication, you should see the admin screen: + +Feel free to explore some of the Admin Panel features that Spree has to offer and to verify that your installation is working properly. + +## Next steps + +If you've followed the steps described in this tutorial, you should now have a fully functional Spree application up and running. + +We recommend you should continue to [Customization section](/developer/customization/storefront.html) to learn how to modify and extend your Spree application. + +To learn more about Spree internals please refer [Core section](/developer/internals/orders.html). + diff --git a/advanced/extend_product_attributes.md b/advanced/extend_product_attributes.md new file mode 100644 index 0000000..d57ac3c --- /dev/null +++ b/advanced/extend_product_attributes.md @@ -0,0 +1,98 @@ +--- +title: Extend Product Attributes +section: tutorial +--- + +# Adding new attributes to Product model + +## Overview + +If you need to add more attributes to your product model you can easily extend spree. In this example you are going to extend your product with an `short_descripton` and make it manageable through admin. + +Note: Replace `RailsappName` and `railsapp_name` with your actualy Rails App Name \(sic!\) + +## Extend your Product + +### Extend Product Attributes + +Create a migration that extends your spree products table. `rails g migration AddFieldsToSpreeProducts short_description:text` should create this migration + +```ruby +class AddFieldsToSpreeProducts < ActiveRecord::Migration[5.2] + def change + add_column :spree_products, :short_description, :text + end +end +``` + +This file extends your actual Product Serializer to make the attributes available in your _APP_. + +```ruby +module RailsappName + module Spree + module ProductDecorator + def self.prepended(base) + base.attributes :short_description + end + end + end +end + +::Spree::V2::Storefront::Product.prepend RailsappName::Spree::ProductDecorator if ::Spree::V2::Storefront::Product.included_modules.exclude?(RailsappName::Spree::ProductDecorator) +``` + +File Location: `app/models/railsapp_name/spree/product_serializer_decorator.rb` \(Does not exist by default\) + +### Make it available in your API + +This file extends your actual Product Serializer to make the attributes available in your _API_. + +```ruby +module RailsappName + module Spree + module ProductSerializerDecorator + def self.prepended(base) + base.attributes :short_description + end + end + end +end + +::Spree::V2::Storefront::ProductSerializer.prepend RailsappName::Spree::ProductSerializerDecorator if ::Spree::V2::Storefront::ProductSerializer.included_modules.exclude?(RailsappName::Spree::ProductSerializerDecorator) +``` + +File Location: `app/serializers/railsapp_name/spree/product_serializer_decorator.rb` \(Does not exist by default\) + +### Add Fields to your admin interface + +_Create Template for Admin Form_ + +```ruby +
+ <%= f.field_container :short_description, class: ['form-group'] do %> + <%= f.label :short_description %> + <%= f.error_message_on :short_description %> + <%= f.text_area :short_description, class: 'form-control', placeholder: 'Am besten drei Bullet Points' %> + <% end %> +
+``` + +File Location: `app/views/spree/admin/products/_product_custom_fields.html` \(Does not exist by default\) + +_Inject your Template to Admin Panel_ + +```ruby +Deface::Override.new(:virtual_path => "spree/admin/products/_form", + :name => "product_custom_fields_admin_product_form_right", + :insert_after => "[data-hook='admin_product_form_description']", + :partial => "spree/admin/products/product_custom_fields", + :original => "eb9ecf7015fa51bb0adf7dafd7e6fdf1d652025d", + :disabled => false) +``` + +File Location: `app/overrides/add_custom_fields_product_admin_tabs.rb` \(Does not exist by default\) + +## Gotchas, Known Issues, and Further Considerations + +This extends your product just with simple fields and makes them available on your master product. Please be advised for more complex extensions it might make sense to create new tabs in your admin panel or manage this through a relationship. + diff --git a/advanced/testing.md b/advanced/testing.md new file mode 100644 index 0000000..fee893d --- /dev/null +++ b/advanced/testing.md @@ -0,0 +1,93 @@ +--- +title: Testing Spree Applications +section: advanced +--- + +# Testing Spree applications + +## Overview + +The Spree project currently uses [RSpec](http://rspec.info) for all of its tests. Each of the gems that makes up Spree has a test suite that can be run to verify the code base. + +The Spree test code is an evolving story. We started out with RSpec, then switched to Shoulda and now we're back to RSpec. RSpec has evolved considerably since we first tried it. When looking to improve the test coverage of Spree we took another look at RSpec and it was the clear winner in terms of strength of community and documentation. + +## Testing Spree Components + +Spree consists of several different gems \(see the [Understanding Spree](/developer/getting_started/understanding_spree.html#spree-modules) for more details.\) Each of these gems has its own test suite which can be found in the `spec` directory. Since these gems are also Rails engines, they can't really be tested in complete isolation - they need to be tested within the context of a Rails application. + +You can easily build such an application by using the Rake task designed for this purpose, running it inside the component you want to test: + +```bash +bundle exec rake test_app +``` + +This will build the appropriate test application inside of your `spec` directory. It will also add the gem under test to your `Gemfile` along with the `spree_core` gem \(since all of the gems depend on this.\) + +This rake task will regenerate the application \(after deleting the existing one\) each time you run it. It will also run the migrations for you automatically so that your test database is ready to go. There is no need to run `rake db:migrate` or `rake db:test:prepare` after running `test_app`. + +### Running the Specs + +Once your test application has been built, you can then run the specs in the standard RSpec manner: + +```bash +bundle exec rspec spec +``` + +We also set up a build script that mimics what our build server performs. You can run it from the root of the Spree project like this: + +```bash +$ bin/build.sh +``` + +If you wish to run spec for a single file then you can do so like this: + +```bash +bundle exec rspec spec/models/spree/state_spec.rb +``` + +If you wish to test a particular line number of the spec file then you can do so like this: + +```bash +bundle exec rspec spec/models/spree/state_spec.rb:7 +``` + +### Using Factories + +Spree uses [factory\_bot](https://github.com/thoughtbot/factory_bot) to create valid records for testing purpose. All of the factories are also packaged in the gem. So if you are writing an extension or if you just want to play with Spree models then you can use these factories as illustrated below or add it directly to `rails_helper`. + +```bash +rails console +require 'spree/testing_support/factories' +``` + +The `spree_core` gem has a good number of factories which can be used for testing. If you are writing an extension or just testing Spree you can make use of these factories. + +## Testing Your Spree Application + +Currently, Spree does not come with any tests that you can install into your application. What we would advise doing instead is either copying the tests from the components of Spree and modifying them as you need them or writing your own test suite. + +### Unit Testing + +Spree itself is well unit-tested. However, when you install a Spree store for the first time, your new app doesn't have any tests of its own. When you start modifying parts of Spree code in your own app, you'll want to add unit tests that cover the extension or modification you made. + +### Integration Testing + +In the early days, Rails developers preferred fixtures and seed data. As apps grew, fixtures and seed data went out of vogue in favor of Factories. Factories can have their own problems, but at this point are widely considered superior to a large fixture/seed data setup. This [blog post](https://semaphoreci.com/blog/2014/01/14/rails-testing-antipatterns-fixtures-and-factories.html) discusses some background consideration. + +Below are some examples for how to create a test suite using Factories \(with FactoryBot\). As discussed above, you can copy all of the Spree Factories from the Spree core, or you can write your own Factories. + +We recommend a fully integration suite covering your checkout. You can also write integration tests for the Admin area, but many people put less attention into this because it is not user-facing. As with the unit tests, the most important thing to test is the modifications you make that make your Spree store different from the default Spree install. + +#### Testing as Someone Logged In + +If you're using spree\_auth\_devise, your app already comes with the Warden gem, which can be used to log-in a user through your test suite + +```ruby +let(:user) { FactoryBot.create(:user) } +before(:each) do + login_as(user, scope: :spree_user) +end +``` + +This lets your Spree app behave as if this user is logged in. + diff --git a/contributing/developing_spree.md b/contributing/developing_spree.md new file mode 100644 index 0000000..d1b6d4f --- /dev/null +++ b/contributing/developing_spree.md @@ -0,0 +1,172 @@ +--- +title: Developing Spree +section: contributing +order: 0 +--- + +# Developing Spree + +## Overview + +This guide covers all the necessary steps to contributing to Spree source code. We're happy you're here! + +## Fork Spree repo + +Go to [Spree GitHub repository](https://github.com/spree/spree) and click **Fork** button. This will create a copy of Spree repository on your GitHub account. See [Github Documentation](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) for more information on forking. + +## Setup locally + +1. Clone the your fork repository + + ```text + git clone git://github.com/your_github_username/spree.git + cd spree + ``` + +2. Install the gem dependencies + + ```text + bundle install + ``` + +### Fix Bundle errors on MacOS + +If `bundle install` fails that means you're missing some required system libraries. + +Firstly, ensure you hve [homebrew installed](https://brew.sh/). You will need to install some packages needed to run Spree and Rails applications in general: + +```text +brew install openssl mysql postgresql sqlite imagemagick +``` + +## Create Sandbox application + +Spree is meant to be run within the context of Rails application and the source code is essentially a collection of gems. You can easily create a sandbox application inside of your cloned source directory for testing purposes. + +This will setup a Rails application with Spree and some essential extensions and gems pre-installed with some data seeded Sandbox application is not meant to be run on production! + +```text +bundle exec rake sandbox +``` + +For **headless sandbox** please run: + +```text +SPREE_HEADLESS=true bundle exec rake sandbox +``` + +By default Sandbox uses **SQLite** database. But you can switch to **PostgreSQL**: + +```text +DB=postgres bundle exec rake sandbox +``` + +or **MySQL**: + +```text +DB=mysql bundle exec rake sandbox +``` + +You can also combine those options: + +```text +SPREE_HEADLESS=true DB=postgres bundle exec rake sandbox +``` + +Start the server + +```text +cd sandbox +bundle exec rails s +``` + +### Performance in development mode + +You may notice that your Spree store runs slower in development environment. This can be because in development each asset \(css and javascript\) is loaded separately. You can disable it by adding the following line to `config/environments/development.rb`. + +```ruby +config.assets.debug = false +``` + +### Caching + +Also in development caching is disabled by default. To turn on caching run: + +```bash +bundle exec rails dev:cache +``` + +You will need to restart rails server after this change. + +## Making changes + +Create a new branch for your changes. Do not push changes to the main branch. Branch name should be human readable and informative, eg. + +* bug fixes: `fix/order-recalculation-total-bug` +* features: `feature/my-new-amazing-feature` + +## Running Tests + +We use [CircleCI](https://circleci.com/) to run the tests for Spree. + +You can see the build statuses at [https://circleci.com/gh/spree/spree](https://circleci.com/gh/spree/spree). + +Each gem contains its own series of tests, and for each directory, you need to do a quick one-time creation of a test application and then you can use it to run the tests. For example, to run the tests for the core project. + +```text +cd core +bundle exec rake test_app +bundle exec rspec spec +``` + +If you would like to run specs against a particular database you may specify the dummy app's database, which defaults to sqlite3. + +```text +DB=postgres bundle exec rake test_app +``` + +If you want to run specs for only a single spec file + +```text +cd core +bundle exec rspec spec/models/spree/state_spec.rb +``` + +If you want to run a particular line of spec + +```text +cd core +bundle exec rspec spec/models/spree/state_spec.rb:7 +``` + +### Running integration tests on MacOS + +We use chromedriver to run integration tests. To install it please use this command: + +```bash +brew cask install chromedriver +``` + +## Submitting Changes + +Please keep your commit history meaningful and clear. [This guide](https://about.gitlab.com/blog/2018/06/07/keeping-git-commit-history-clean/) covers it quite well and we recommend reading it, not only for Spree project but for any IT project overall. + +1. Push your changes to a topic branch in your fork of the repository. + + ```text + git push -u origin fix/order-recalculation-total-bug + ``` + +2. Create a Pull request - [please follow this guide](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) + + If your changes references Github issues, please mark which issue you're fixing by adding `Fixes #` in the commit name or PR title/description. This will automatically mark that issue as closed when your PR will be merged. + +3. Wait for CI to pass +4. If CI passed wait for Spree Core team code review + + We're aiming to review and leave feedback as soon as possible. We'll leave you a meaningul feedback if needed. + +## That's a wrap! + +Thank you for participating in Open Source and improving Spree - you're awesome! + diff --git a/contributing/extensions_tutorial.md b/contributing/extensions_tutorial.md new file mode 100644 index 0000000..6fd3761 --- /dev/null +++ b/contributing/extensions_tutorial.md @@ -0,0 +1,257 @@ +--- +title: Creating an Extension +section: contributing +order: 1 +--- + +# Creating new extensions + +## Getting Started + +Let's build a simple [extension](/developer/customization/extensions.html). Suppose we want the ability to mark certain products as being on sale. We'd like to be able to set a sale price on a product and show products that are on sale on a separate products page. This is a great example of how an extension can be used to build on the solid Spree foundation. + +Before we start, let's make sure we have spree command installed by running: + +```ruby +gem install spree +``` + +So let's start by generating the extension. Run the following command from a directory of your choice outside of our Spree application: + +```bash +spree extension simple_sales +``` + +This creates a `spree_simple_sales` directory with several additional files and directories. After generating the extension make sure you change to its directory: + +```bash +cd spree_simple_sales +``` + +## Adding a Sale Price to Variants + +The first thing we need to do is create a migration that adds a sale\_price column to [variants](/developer/products.html#variants). + +We can do this with the following command: + +```bash +bundle exec rails g migration add_sale_price_to_spree_variants sale_price:decimal +``` + +Because we are dealing with prices, we need to now edit the generated migration to ensure the correct precision and scale. Edit the file `db/migrate/XXXXXXXXXXX_add_sale_price_to_spree_variants.rb` so that it contains the following: + +```ruby +class AddSalePriceToSpreeVariants < SpreeExtension::Migration + def change + add_column :spree_variants, :sale_price, :decimal, precision: 8, scale: 2 + end +end +``` + + We're not inheriting directly from ActiveRecord::Migration, instead we're using \[SpreeExtension::Migration\]\(https://github.com/spree-contrib/spree\_extension/blob/master/lib/spree\_extension/migration.rb\) to support multiple Rails versions. + +## Adding Our Extension to the Spree Application + +Before we continue development of our extension, let's add it to the Spree application we created in the [last tutorial](/developer/getting_started_tutorial.html). This will allow us to see how the extension works with an actual Spree store while we develop it. + +Within the `my_store` application directory, add the following line to the bottom of our `Gemfile`: + +```ruby +gem 'spree_simple_sales', path: '../spree_simple_sales' +``` + +You may have to adjust the path somewhat depending on where you created the extension. You want this to be the path relative to the location of the `my_store` application. + +Once you have added the gem, it's time to bundle: + +```bash +bundle install +``` + +Finally, let's run the `spree_simple_sales` install generator to copy over the migration we just created \(answer **yes** if prompted to run migrations\): + +```bash +# context: Your Spree store's app root (i.e. Rails.root); not the extension's root path. +bundle exec rails g spree_simple_sales:install +``` + +## Adding a Controller Action to HomeController + +Now we need to extend `Spree::HomeController` and add an action that selects "on sale" products. + + Note for the sake of this example that \`Spree::HomeController\` is only included in spree\_frontend so you need to make it a dependency on your extensions \*.gemspec file. + +Make sure you are in the `spree_simple_sales` root directory and run the following command to create the directory structure for our controller decorator: + +```bash +mkdir -p app/controllers/spree_simple_sales/spree +``` + +Next, create a new file in the directory we just created called `home_controller_decorator.rb` and add the following content to it: + +```ruby +module SpreeSimpleSales + module Spree + module HomeControllerDecorator + def sale + @products = ::Spree::Product.joins(:variants_including_master).where('spree_variants.sale_price is not null').distinct + end + end + end +end + +Spree::HomeController.prepend SpreeSimpleSales::Spree::HomeControllerDecorator +``` + +This will select just the products that have a variant with a `sale_price` set. + +We also need to add a route to this action in our `config/routes.rb` file. Let's do this now. Update the routes file to contain the following: + +```ruby +Spree::Core::Engine.routes.draw do + get "/sale" => "home#sale" +end +``` + +## Viewing On Sale Products + +### Setting the Sale Price for a Variant + +Now that our variants have the attribute `sale_price` available to them, let's update the sample data so we have at least one product that is on sale in our application. We will need to do this in the rails console for the time being, as we have no admin interface to set sale prices for variants. So, in order to do this, first open up the rails console: + +```bash +bundle exec rails c +``` + +Now, follow the steps I take in selecting a product and updating its master variant to have a sale price. Note, you may not be editing the exact same product as I am, but this is not important. We just need one "on sale" product to display on the sales page. + +```text +> product = Spree::Product.first +=> # + +> variant = product.master +=> #, position: nil, lock_version: 0, on_demand: false, cost_currency: nil, sale_price: nil> + +> variant.sale_price = 8.00 +=> 8.0 + +> variant.save +=> true +``` + +## Creating a View + +Now we have at least one product in our database that is on sale. Let's create a view to display these products. + +First, create the required views directory with the following command: + +```bash +mkdir -p app/views/spree/home +``` + +Next, create the file `app/views/spree/home/sale.html.erb` and add the following content to it: + +```text +
+ <%= render 'spree/shared/products', products: @products %> +
+``` + +If you navigate to `http://localhost:3000/sale` you should now see the product\(s\) listed that we set a `sale_price` on earlier in the tutorial. However, if you look at the price, you'll notice that it's not actually displaying the correct price. This is easy enough to fix and we will cover that in the next section. + +## Decorating Variants + +Let's fix our extension so that it uses the `sale_price` when it is present. + +First, create the required directory structure for our new decorator: + +```bash +mkdir -p app/models/spree_simple_sales/spree +``` + +Next, create the file `app/models/spree_simple_sales/spree/variant_decorator.rb` and add the following content to it: + +```ruby +module SpreeSimpleSales + module Spree + module VariantDecorator + def price_in(currency) + return super unless sale_price.present? + ::Spree::Price.new(variant_id: self.id, amount: self.sale_price, currency: currency) + end + end + end +end + +Spree::Variant.prepend SpreeSimpleSales::Spree::VariantDecorator +``` + +If there is a `sale_price` present on the product's master variant, we return that price. Otherwise, we call the original implementation of `price_in` \(using `return super`\). + +## Testing Our Decorator + +It's always a good idea to test your code. We should be extra careful to write tests for our Variant decorator since we are modifying core Spree functionality. Let's write a couple of simple unit tests for `variant_decorator.rb` + +### Generating the Test App + +An extension is not a full Rails application, so we need something to test our extension against. By running the Spree `test_app` rake task, we can generate a barebones Spree application within our `spec` directory to run our tests against. + +We can do this with the following command from the root directory of our extension: + +```bash +bundle exec rake test_app +``` + +After this command completes, you should be able to run `rspec` and see the following output: + +```bash +No examples found. + +Finished in 0.00005 seconds +0 examples, 0 failures +``` + +Great! We're ready to start adding some tests. Let's replicate the extension's directory structure in our spec directory by running the following command + +```bash +mkdir -p spec/models/spree +``` + +Now, let's create a new file in this directory called `variant_decorator_spec.rb` and add the following tests to it: + +```ruby +require 'spec_helper' + +describe Spree::Variant do + describe "#price_in" do + it "returns the sale price if it is present" do + variant = create(:variant, sale_price: 8.00) + expected = Spree::Price.new(variant_id: variant.id, currency: "USD", amount: variant.sale_price) + + result = variant.price_in("USD") + + expect(result.variant_id).to eq(expected.variant_id) + expect(result.amount.to_f).to eq(expected.amount.to_f) + expect(result.currency).to eq(expected.currency) + end + + it "returns the normal price if it is not on sale" do + variant = create(:variant, price: 15.00) + expected = Spree::Price.new(variant_id: variant.id, currency: "USD", amount: variant.price) + + result = variant.price_in("USD") + + expect(result.variant_id).to eq(expected.variant_id) + expect(result.amount.to_f).to eq(expected.amount.to_f) + expect(result.currency).to eq(expected.currency) + end + end +end +``` + +These specs test that the `price_in` method we overrode in our `VariantDecorator` returns the correct price both when the sale price is present and when it is not. + +## Summary + +In this tutorial you learned how to both install extensions and create your own. A lot of core Spree development concepts were covered and you gained exposure to some of the Spree internals. + diff --git a/contributing/index.md b/contributing/index.md new file mode 100644 index 0000000..4918160 --- /dev/null +++ b/contributing/index.md @@ -0,0 +1,39 @@ +--- +title: Contributing +section: contributing +--- + +# General guidelines + +Spree is an open, community powered project that anyone can contribute to. There are several ways you can help us improve Spree: + +* submitting an issue describing a bug or a feature request +* fixing a bug or creating a new feature +* creating a Spree extension +* upgrading old extensions to recent Spree versions +* helping others on Spree slack + +## Submitting issues + +You can [submit an issue](https://github.com/spree/spree/issues/new/choose) on our [Github Issues tracker](https://github.com/spree/spree/issues) + +## Fixing a bug or creating a new feature + +Before diving into the Spree codebase we recommend you spend some time to review the [Internals section](/developer/internals/) which will help you understand how Spree works under the hood. + +After you're done with that please follow to [Developing Spree guide](developing_spree.md) + +To find issues you can tackle on we recommend visiting [Spree Contribute page](https://github.com/spree/spree/contribute) which lists issues tagged with [Good First Issue label](https://github.com/spree/spree/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22). Those bugs or features are great starting points into Open Source development. + +## Creating a Spree extension + +Extensions provide additional features and integrations for your Spree store. You can create your own and share with the rest of the community. Before doing so please check our [Extension directory](/extensions) if your desired extension does not exist already. If not [follow Extensions tutorial](extensions_tutorial.md) to learn how to create one. + +## Upgrading old extensions to recent Spree versions + +If you found an extension that does not work with the current Spree version you can read the [Updating Extensions for Spree guide](upgrading_extensions.md) to bring it up to speed. + +## Helping others on Spree slack + +Last but not least, you're invited to [join our slack](http://slack.spreecommerce.org) to help and receive guidance from fellow Spree developers including Spree Core Team. + diff --git a/contributing/upgrading_extensions.md b/contributing/upgrading_extensions.md new file mode 100644 index 0000000..bddd075 --- /dev/null +++ b/contributing/upgrading_extensions.md @@ -0,0 +1,96 @@ +--- +title: Updating Extensions for Spree 4 +section: contributing +order: 2 +--- + +# Upgrading extensions + +## Overview + +This guide covers the process of migrating an old Spree 2/3 extension to Spree 4. + +## Zeitwerk compatibility + +Zeitwerk is the [new default code autoloader in Rails 6](https://weblog.rubyonrails.org/2019/2/22/zeitwerk-integration-in-rails-6-beta-2/). + +This doesn't work well with the old approach to decorators \(files that name ends with decorator.rb, eg. `app/models/spree/order_decorator.rb`\) using [class\_eval](https://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval/). + +To fix this we need to convert all `class_eval` decorators to modules and use [Module.prepend](https://medium.com/@leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073). Also we need to name them properly according to [Zeitwerk naming rules](https://github.com/fxn/zeitwerk#file-structure) + +Example of an old decorator: + +`app/models/spree/order_decorator.rb` + +```ruby +Spree::Order.class_eval do + has_many :new_custom_model + + def some_method + # ... + end +end +``` + +the same decorator in the new notation: + +`app/models/your_extension_name/order_decorator.rb` + +```ruby +module YourExtensionName::OrderDecorator + def self.prepended(base) + base.has_many :new_custom_model + end + + def some_method + # ... + end +end + +::Spree::Order.prepend(YourExtensionName::OrderDecorator) +``` + +## Travis CI configuration + +You can always find up-to-date Travis CI config here: [https://github.com/spree/spree/blob/master/cmd/lib/spree\_cmd/templates/extension/travis.yml](https://github.com/spree/spree/blob/master/cmd/lib/spree_cmd/templates/extension/travis.yml) For the rationale of the changes please look at the blame view: [https://github.com/spree/spree/blame/master/cmd/lib/spree\_cmd/templates/extension/travis.yml](https://github.com/spree/spree/blame/master/cmd/lib/spree_cmd/templates/extension/travis.yml) + +## Appraisals config + +You can always find up-to-date Appraisals config here: [https://github.com/spree/spree/blob/master/cmd/lib/spree\_cmd/templates/extension/Appraisals](https://github.com/spree/spree/blob/master/cmd/lib/spree_cmd/templates/extension/Appraisals) + +For the rationale of the changes please look at the blame view: [https://github.com/spree/spree/blame/master/cmd/lib/spree\_cmd/templates/extension/Appraisals](https://github.com/spree/spree/blame/master/cmd/lib/spree_cmd/templates/extension/Appraisals) + +After each change please remember to re-generate gemfiles by running: + +```bash +bundle exec appraisal generate --travis +``` + +## Fixing Deface Overrides + +Some Extensions still use Deface overrides to add some UI features, mainly in the admin panel. Deface isn't recommended. If you can use other methods. Eg. If your extension adds a link to the Admin Panel menu you can do it this way [https://guides.spreecommerce.org/developer/customization/view.html\#adding-new-links-to-the-admin-panel-menu](https://guides.spreecommerce.org/developer/customization/view.html#adding-new-links-to-the-admin-panel-menu) + +If you're stuck on Deface please remember to prepare versioned overrides for both Spree 3.x and 4.x, eg. [https://github.com/spree-contrib/spree\_static\_content/commit/e4b9e4900024235158d0ec1a48a100b4732348ef](https://github.com/spree-contrib/spree_static_content/commit/e4b9e4900024235158d0ec1a48a100b4732348ef) + +Spree 4 uses Bootstrap 4 and many partials and HTML structure changed compared to Spree 3.x. + +Also - **remember to add deface gem to gemspec** as deface itself was removed as a dependency of Spree. eg. [https://github.com/spree/spree\_auth\_devise/commit/d729689ca87d8586e541ffcc865ef1e0a5a79fe4](https://github.com/spree/spree_auth_devise/commit/d729689ca87d8586e541ffcc865ef1e0a5a79fe4) + +## Migrate to Spree Dev Tools + +Replace all development dependencies with: + +```ruby +s.add_development_dependency 'spree_dev_tools' +``` + +Replace `spec_helper.rb` contents with: + +[https://github.com/spree/spree/blob/777a284b4c70e69d32a05ffa61bbe3905d8f1297/cmd/lib/spree\_cmd/templates/extension/spec/spec\_helper.rb](https://github.com/spree/spree/blob/777a284b4c70e69d32a05ffa61bbe3905d8f1297/cmd/lib/spree_cmd/templates/extension/spec/spec_helper.rb) + +Example migrations: + +* [https://github.com/spree/spree\_gateway/pull/357](https://github.com/spree/spree_gateway/pull/357) +* [https://github.com/spree-contrib/spree-product-assembly/pull/200](https://github.com/spree-contrib/spree-product-assembly/pull/200) +* [https://github.com/spree/spree\_auth\_devise/pull/487](https://github.com/spree/spree_auth_devise/pull/487) + diff --git a/customization/api_v1.md b/customization/api_v1.md new file mode 100644 index 0000000..4fb5703 --- /dev/null +++ b/customization/api_v1.md @@ -0,0 +1,179 @@ +--- +title: API v1 +section: customization +order: 7 +--- + +# API v1 + +## Introduction + +In this tutorial we are going to learn how we can customize the [**REST API**](api) provided by Spree, adding a new endpoint \(or you can override an existing in core\). We will use `spree_simple_sales` extension created in [Extensions tutorial](/developer/advanced/extensions_tutorial.html). If you haven't seen before, please check them! + +## Adding Custom Endpoints + +Similarly to adding a controller action of [Extensions tutorial](/developer/advanced/extensions_tutorial.html), you can create a new controller class with an action that emits a json response from a [Rabl](https://github.com/nesquena/rabl) view. + +### Creating the controller and action + +Let's create a new custom endpoint to `api/v1/sales`. For this, make sure you are in the `spree_simple_sales` root directory and run the following command to create the directory structure for our new controller api: + +```bash +mkdir -p app/controllers/spree/api/v1 +``` + +Next, we will create the new controller `Spree::Api::V1:SalesController`, that inherit from `Spree::Api::BaseController` class. + +In the directory we just created add a new file called `sales_controller.rb` with the the following content: + +```ruby +module Spree + module Api + module V1 + class SalesController < Spree::Api::BaseController + def index + @products = Spree::Product.joins(:variants_including_master).where('spree_variants.sale_price is not null').distinct + + expires_in 15.minutes, public: true + + headers['Surrogate-Control'] = "max-age=#{15.minutes}" + respond_with(@products) + end + end + end + end +end +``` + + Note that distinct of \`Spree::HomeController\` from the previous tutorial, we are extending from \`Spree::Api\` module now + +The difference from `Spree::HomeController.home` action is the 3 last extra lines, that perform: + +* `expires_in`: Define the time that the endpoint expires +* `headers[]`: In addition to `expires_in`, returns a header to client with that time expiration +* `respond_with`: Normalize the response before parser to json. + +We also need to add a route to this endpoint/action in our `config/routes.rb` file. Let's do this now. Update the routes file to contain the following: + +```ruby +Spree::Core::Engine.add_routes do + # The route added in previous tutorial + get "/sale" => "home#sale" + + namespace :api, defaults: { format: 'json' } do + namespace :v1 do + # Our new route goes here! + resources :sales, only: [:index] + end + end +end +``` + + The \`only:\` symbol defines which actions of the controller are allowed to be endpoints. Whether you don't define this, Spree will try execute a \`SalesController.show\(\)\` action method, that in this case, not exists! + +### Creating a View + +Now, let's create a view to return the data defined in action for client. Spree uses [Rabl](https://github.com/nesquena/rabl) gem for field customizations, inheritance of specifications from the other `.rabl` files and many other cool features. This gem do something similar to [grape-entity](https://github.com/ruby-grape/grape-entity) presenters. + +First, create the required views api directory with the following command: + +```bash +# The view needs be [controller]/[action].[api_version].rabl +mkdir -p app/views/spree/api/v1/sales +``` + +Next, create the file `app/views/spree/api/v1/sales/index.v1.rabl` and add the following content to it: + +```ruby +collection @products +attributes *product_attributes << :sale_price +``` + +### Testing Our endpoint + +Like described in [Testing Our Decorator](/developer/advanced/extensions_tutorial.html#testing-our-decorator) it's always a good idea to test your code, including your api new/changed endpoints. Let's write a integration test that simulate your api of simple unit tests for `sales_controller.rb` + +#### Creating and running the test + +1. Verify if the `Gemfile` of our extensions contains the gems below, into `:test` the group: + +```ruby +group :test do + gem 'rails-controller-testing' + gem 'rspec-rails', '~> 3.8.0' + gem 'rspec-activemodel-mocks' +end +``` + +> **PS:** The `rspec-activemodel-mocks` is need to use `stub_*` methods \(e.g `stub_model` called by `stub_authentication!`\) + +1. `bundle install` +2. Copy the file [spree/controller\_hacks.rb](https://github.com/spree/spree/blob/master/api/spec/support/controller_hacks.rb) to `spec/support` folder. That is required to use `api_*` methods to simulate api requests \(e.g `api_get :action`, `api_post :action`...\) +3. Replicate the extension's controller directory structure in our spec directory by running the following command + +```bash +mkdir -p spec/controllers/spree/api/v1 +``` + +Now, let's create a new file in this directory called `sales_controller_spec.rb` and add the following test to it: + +```ruby +require 'spec_helper' + +module Spree + describe Api::V1::SalesController, type: :controller do + render_views + + # 8.00 it's a example value. Use any other value that your wish! + let!(:product) { create(:product, sale_price: 8.00) } + let!(:other_product) { create(:product, stores: Spree::Store.all) } + let!(:user) { create(:user) } + + before do + # Mock API autentication using a "spree_api_key" + stub_authentication! + end + + it 'retrieves a list of products in sale' do + api_get :index + expect(json_response.size).to eq(1) + expect(json_response.first["sale_price"].to_f).to eq(product.sale_price) + end + end +end +``` + +Open your terminal and execute `rspec` command to run all tests: + +```bash +rspec +``` + +You should see the output below in your terminal: + +```bash +3 examples found. + +Finished in 0.00005 seconds +3 examples, 0 failures +``` + +#### Get the endpoint result + +In your terminal, execute the `rails console`: + +```bash +rails console +``` + +Fetch the api key of any user of your database \(e.g admin user\): + +```ruby +user = Spree::user_class.first +api_key = user.spree_api_key # Copy the api_key value +``` + +Now, when we head to `http://localhost:3000/api/v1/sales`, passing the header `X-Spree-Token: [YOUR_COPIED_API_KEY]`, \(or add a `?token=[YOUR_COPIED_API_KEY]` to the url\) into your browser or any REST client, we should see the json result with all products with a sale price. + + Note that you will likely need to restart our example Spree application \(created in the \[Getting Started\]\(/developer/getting\_started/installation.html\) tutorial\). + diff --git a/customization/api_v2.md b/customization/api_v2.md new file mode 100644 index 0000000..54509c4 --- /dev/null +++ b/customization/api_v2.md @@ -0,0 +1,16 @@ +--- +title: API v2 +section: customization +order: 6 +--- + +# API v2 + +## Introduction + +## Adding custom endpoints + +## Modyfying existing endpoints + +Replacing + diff --git a/customization/authentication.md b/customization/authentication.md new file mode 100644 index 0000000..7be9c75 --- /dev/null +++ b/customization/authentication.md @@ -0,0 +1,209 @@ +--- +title: Authentication +section: customization +order: 8 +--- + +# Authentication + +## Overview + +This guide covers using a custom authentication setup with Spree, such as one provided by your own application. This is ideal in situations where you want to handle the sign-in or sign-up flow of your application uniquely, outside the realms of what would be possible with Spree. After reading this guide, you will be familiar with: + +* Setting up Spree to work with your custom authentication + +### The User Model + +This guide assumes that you have a pre-existing model inside your application that represents the users of your application already. This model could be provided by gems such as [Devise](https://github.com/plataformatec/devise). This guide also assumes that the application that this `User` model exists in is already a Spree application. + +This model **does not** need to be called `User`, but for the purposes of this guide the model we will be referring to **will** be called `User`. If your model is called something else, do some mental substitution wherever you see `User`. + +#### Initial Setup + +To begin using your custom `User` class, you must first edit Spree's initializer located at `config/initializers/spree.rb` by changing this line: + +```ruby +Spree.user_class = 'Spree::User' +``` + +To this: + +```ruby +Spree.user_class = 'User' +``` + +Next, you need to run the custom user generator for Spree which will create two files. The first is a migration that will add the necessary Spree fields to your users table, and the second is an extension \(that lives at `lib/spree/authentication_helpers.rb`\) to the `Spree::Core::AuthenticationHelpers` module inside of Spree. + +Run this generator with this command: + +```bash +bundle exec rails g spree:custom_user User +``` + +This will tell the generator that you want to use the `User` class as the class that represents users in Spree. Run the new migration by running this: + +```bash +bundle exec rails db:migrate +``` + +Next you will need to define some methods to tell Spree where to find your application's authentication routes. + +### Authentication Helpers + +There are some authentication helpers of Spree's that you will need to possibly override. The file at `lib/spree/authentication_helpers.rb` contains the following code to help you do that: + +```ruby +module Spree + module AuthenticationHelpers + def self.included(receiver) + receiver.send :helper_method, :spree_login_path + receiver.send :helper_method, :spree_signup_path + receiver.send :helper_method, :spree_logout_path + receiver.send :helper_method, :spree_current_user + end + + def spree_current_user + current_user + end + + def spree_login_path + main_app.login_path + end + + def spree_signup_path + main_app.signup_path + end + + def spree_logout_path + main_app.logout_path + end + end +end +``` + +In your `ApplicationController` add those lines: + +```ruby +include Spree::AuthenticationHelpers +include Spree::Core::ControllerHelpers::Auth +include Spree::Core::ControllerHelpers::Common +include Spree::Core::ControllerHelpers::Order +include Spree::Core::ControllerHelpers::Store +include Spree::Core::ControllerHelpers::Currency +include Spree::Core::ControllerHelpers::Locale + +helper 'spree/base' +helper 'spree/locale', 'spree/currency', 'spree/store' +``` + +Please note that including `Spree::Core::ControllerHelpers::Common` will replace your application layout with [Spree layout](https://github.com/spree/spree/blob/master/frontend/app/views/spree/layouts/spree_application.html.erb). For applications not wanting to use Spree layout omit the `Spree::Core::ControllerHelpers::Common` module. + +Each of the methods defined in this module return values that are the most common in Rails applications today, but you may need to customize them. In order, they are: + +* `spree_current_user` Used to tell Spree what the current user + + of a request is. + +* `spree_login_path` The location of the login/sign in form in + + your application. + +* `spree_signup_path` The location of the sign up form in your + + application. + +* `spree_logout_path` The location of the logout feature of your + + application. + + URLs inside the \`spree\_login\_path\`, \`spree\_signup\_path\` and \`spree\_logout\_path\` methods \*\*must\*\* have \`main\_app\` prefixed if they are inside your application. This is because Spree will otherwise attempt to route to a \`login\_path\`, \`signup\_path\` or \`logout\_path\` inside of itself, which does not exist. By prefixing with \`main\_app\`, you tell it to look at the application's routes. + +You will need to define the `login_path`, `signup_path` and `logout_path` routes yourself, by using code like this inside your application's `config/routes.rb` if you're using Devise: + +```ruby +devise_for :users +devise_scope :user do + get '/login', to: "devise/sessions#new" + get '/signup', to: "devise/registrations#new" + delete '/logout', to: "devise/sessions#destroy" +end +``` + +Of course, this code will be different if you're not using Devise. Simply **do not** use the `devise_scope` method and change the controllers and actions for these routes. + +You can also customize the `spree_login_path`, `spree_signup_path` and `spree_logout_path` methods inside `lib/spree/authentication_helpers.rb` to use the routing helper methods already provided by the authentication setup you have, if you wish. + + Any modifications made to \`lib/spree/authentication\_helpers.rb\` while the server is running will require a restart, as with any other modification to other files in \`lib\`. + +## The User Model + +In your User Model you have to add: + +```ruby +include Spree::UserMethods +include Spree::UserAddress +include Spree::UserPaymentSource +``` + +The first of these methods are the ones added for the `has_and_belongs_to_many` association called `spree_roles`. This association will retrieve all the roles that a user has for Spree. + +The second of these is the `spree_orders` association. This will return all orders associated with the user in Spree. There's also a `last_incomplete_spree_order` method which will return the last incomplete spree order for the user. This is used internal to Spree to persist order data across a user's login sessions. + +The third and fourth associations are for address information for a user. When a user places an order, the address information for that order will be linked to that user so that it is available for subsequent orders. + +The next method is one called `has_spree_role?` which can be used to check if a user has a specific role. This method is used internally to Spree to check if the user is authorized to perform specific actions, such as accessing the admin section. Admin users of your system should be assigned the Spree admin role, like this: + +```ruby +user = User.find_by(email: 'master@example.com') +user.spree_roles << Spree::Role.where(name: 'admin').first_or_create +``` + +To test that this has worked, use the `has_spree_role?` method, like this: + +```ruby +user.has_spree_role?('admin') +``` + +If this returns `true`, then the user has admin permissions within Spree. + +Finally, if you are using the API component of Spree, there are more methods added. The first is the `spree_api_key` getter and setter methods, used for the API key that is used with Spree. The next two methods are `generate_spree_api_key!` and `clear_spree_api_key` which will generate and clear the Spree API key respectively. + +## Login link + + This is only applicable for Spree 4.0 and older. Spree 4.1 and newer releases handle this out of the box. + +To make the login link appear on Spree pages, you will need to modify `spree/shared/_nav_bar.html.erb` file which you can copy over from Spree codebase to your project \(detailed in [View Customization section](/developer/customization/view.html)\). + +You will need to add this code: + +```text +<%% if try_spree_current_user %> +
  • + <%%= link_to Spree.t(:logout), spree_logout_path, method: :delete %> +
  • +<%% else %> +
  • + <%%= link_to Spree.t(:login), spree_login_path %> +
  • +
  • + <%%= link_to Spree.t(:sign_up), spree_signup_path %> +
  • +<%% end %> +``` + +This will then use the URL helpers you have defined in `lib/spree/authentication_helpers.rb` to define three links, one to allow users to logout, one to allow them to login, and one to allow them to signup. These links will be visible on all customer-facing pages of Spree. + +## Gemfile + +The `spree_auth_devise` gem is not needed when using an existing application authentication unless the goal is to have two separate authentication methods. + +## Signup promotion + +In Spree, there is a promotion that acts on the user signup which will not work correctly automatically when you're not using the standard authentication method with Spree. To fix this, you will need to trigger this event after a user has successfully signed up in your application by setting a session variable after successful signup in whatever controller deals with user signup: + +```ruby +session[:spree_user_signup] = true +``` + +This line will cause the Spree event notifiers to be notified of this event and to apply any promotions to an order that are triggered once a user signs up. + diff --git a/customization/checkout.md b/customization/checkout.md new file mode 100644 index 0000000..c65efd4 --- /dev/null +++ b/customization/checkout.md @@ -0,0 +1,270 @@ +--- +title: Checkout Flow +section: customization +order: 5 +--- + +# Checkout + +## Overview + +The Spree checkout process has been designed for maximum flexibility. It's been redesigned several times now, each iteration has benefited from the feedback of real world deployment experience. It is relatively simple to customize the checkout process to suit your needs. Secure transmission of customer information is possible via SSL and credit card information is never stored in the database. + +The customization of the flow of the checkout can be done by using Spree's `checkout_flow` DSL, described in the [Checkout Flow DSL](checkout.md#the-checkout-flow-dsl) section below. + +## Default Checkout Steps + +The Spree checkout process consists of the following steps. With the exception of the Registration step, each of these steps corresponds to a state of the `Spree::Order` object: + +* Registration \(Optional - only if using spree\_auth\_devise extension, can be toggled through the `Spree::Auth::Config[:registration_step]` configuration setting\) +* Address Information +* Delivery Options \(Shipping Method\) +* Payment +* Confirmation + +The following sections will provide a walk-though of a checkout from a user's perspective, and offer some information on how to configure the default behavior of the various steps. + +### Registration + +Prior to beginning the checkout process, the customer will be prompted to create a new account or to login to their existing account. By default, there is also a "guest checkout" option which allows users to specify only their email address if they do not wish to create an account. + +Technically, the registration step is not an actual state in the `Spree::Order` state machine. The `spree_auth_devise` gem \(an extension that comes with Spree by default\) adds the `check_registration` before filter to the all actions of `Spree::CheckoutController` \(except for obvious reasons the `registration` and `update_registration` actions\), which redirects to a registration page unless one of the following is true: + +* `Spree::Auth::Config[:registration_step]` preference is not `true` +* user is already logged in +* the current order has an email address associated with it + +The method is defined like this: + +```ruby +def check_registration + return unless Spree::Auth::Config[:registration_step] + return if spree_current_user or current_order.email + store_location + redirect_to spree.checkout_registration_path +end +``` + +The configuration of the guest checkout option is done via [Preferences](/developer/internals/preferences.html). Spree will allow guest checkout by default. Use the `allow_guest_checkout` preference to change the default setting. + +### Address Information + +This step allows the customer to add both their billing and shipping information. Customers can click the "use billing address" option to use the same address for both. Selecting this option will have the effect of hiding the shipping address fields using JavaScript. If users have disabled JavaScript, the section will not disappear but it will copy over the address information once submitted. If you would like to automatically copy the address information via JavaScript on the client side, that is an exercise left to the developer. We have found the server side approach to be simpler and easier to maintain. + +The address fields include a select box for choosing state/province. The list of states will be populated via JavaScript and will contain all of the states listed in the database for the currently selected country. If there are no states configured for a particular country, or if the user has JavaScript disabled, the select box will be replaced by a text field instead. + + The default "seed" data for Spree only includes the U.S. states. It's easy enough to add states or provinces for other countries but beyond the scope of the Spree project to maintain such a list. + + The state field can be disabled entirely by using the \`Spree::Config\[:address\_requires\_state\]\` preference. You can also allow for an "alternate phone" field by using the \`Spree::Config\[:alternative\_shipping\_phone\]\` and \`Spree::Config\[:alternative\_shipping\]\` fields. + +The list of countries that appear in the country select box can also be configured. Spree will list all countries by default, but you can configure exactly which countries you would like to appear. The list can be limited to a specific set of countries by configuring the `Spree::Config[:checkout_zone]` preference and setting its value to the name of a [Zone](/developer/internals/addresses.html#zones) containing the countries you wish to use. Spree assumes that the list of billing and shipping countries will be the same. You can always change this logic via an extension if this does not suit your needs. + +### Delivery Options + +During this step, the user may choose a delivery method. Spree assumes the list of shipping methods to be dependent on the shipping address. This is one of the reasons why it is difficult to support single page checkout for customers who have disabled JavaScript. + +### Payment + +This step is where the customer provides payment information. This step is intentionally placed last in order to minimize security issues with credit card information. Credit card information is never stored in the database so it would be impossible to have a subsequent step and still be able to submit the information to the payment gateway. Spree submits the information to the gateway before saving the model so that the sensitive information can be discarded before saving the checkout information. + +Spree stores only the last four digits of the credit card number along with the expiration information. The full credit card number and verification code are never stored in the Spree database. + +Several gateways such as ActiveMerchant and Beanstream provide a secure method for storing a "payment profile" in your database. This approach typically involves the use of a "token" which can be used for subsequent purchases but only with your merchant account. If you are using a secure payment profile it would then be possible to show a final "confirmation" step after payment information is entered. + +If you do not want to use a gateway with payment profiles then you will need to customize the checkout process so that your final step submits the credit card information. You can then perform an authorization before the order is saved. This is perfectly secure because the credit card information is not ever saved. It's transmitted to the gateway and then discarded like normal. + + Spree discards the credit card number after this step is processed. If you do not have a gateway with payment profiles enabled then your card information will be lost before it's time to authorize the card. + +For more information about payments, please see the [Payments guide](/developer/internals/payments.html). + +### Confirmation + +This is the final opportunity for the customer to review their order before submitting it to be processed. Users have the opportunity to return to any step in the process using either the back button or by clicking on the appropriate step in the "progress breadcrumb." + +This step is disabled by default \(except for payment methods that support payment profiles\), but can be enabled by overriding the `confirmation_required?` method in `Spree::Order`. + +## Checkout Architecture + +The following is a detailed summary of the checkout architecture. A complete understanding of this architecture will allow you to be able to customize the checkout process to handle just about any scenario you can think of. Feel free to skip this section and come back to it later if you require a deeper understanding of the design in order to customize your checkout. + +### Checkout Routes + +Three custom routes in spree\_core handle all of the routing for a checkout: + +```ruby +put '/checkout/update/:state', to: 'checkout#update', as: :update_checkout +get '/checkout/:state', to: 'checkout#edit', as: :checkout_state +get '/checkout', to: 'checkout#edit', as: :checkout +``` + +The '/checkout' route maps to the `edit` action of the `Spree::CheckoutController`. A request to this route will redirect to the current state of the current order. If the current order was in the "address" state, then a request to '/checkout' would redirect to '/checkout/address'. + +The '/checkout/:state' route is used for the previously mentioned route, and also maps to the `edit` action of `Spree::CheckoutController`. + +The '/checkout/update/:state' route maps to the `Spree::CheckoutController#update` action and is used in the checkout form to update order data during the checkout process. + +### Spree::CheckoutController + +The `Spree::CheckoutController` drives the state of an order during checkout. Since there is no "checkout" model, the `Spree::CheckoutController` is not a typical RESTful controller. The spree\_core and spree\_auth\_devise gems expose a few different actions for the `Spree::CheckoutController`. + +The `edit` action renders the checkout/edit.html.erb template, which then renders a partial with the current state, such as `app/views/spree/checkout/address.html.erb`. This partial shows state-specific fields for the user to fill in. If you choose to customize the checkout flow to add a new state, you will need to create a new partial for this state. + +The `update` action performs the following: + +* Updates the `current_order` with the parameters passed in from the current + + step. + +* Transitions the order state machine using the `next` event after successfully + + updating the order. + +* Executes callbacks based on the new state after successfully transitioning. +* Redirects to the next checkout step if the `current_order.state` is anything + + other than `complete`, else redirect to the `order_path` for `current_order` + + For security reasons, the \`Spree::CheckoutController\` will not update the order once the checkout process is complete. It is therefore impossible for an order to be tampered with \(ex. changing the quantity\) after checkout. + +### Filters + +The `spree_core` and the default authentication gem \(`spree_auth_devise`\) gems define several `before_actions` for the `Spree::CheckoutController`: + +* `load_order`: Assigns the `@order` instance variable and sets the `@order.state` to the `params[:state]` value. This filter also runs the "before" callbacks for the current state. +* `check_authorization`: Verifies that the `current_user` has access to `current_order`. +* `check_registration`: Checks the registration status of `current_user` and redirects to the registration step if necessary. + +### The Order Model and State Machine + +The `Spree::Order` state machine is the foundation of the checkout process. Spree makes use of the [state\_machines](https://github.com/state-machines/state_machines) gem in the `Spree::Order` model as well as in several other places \(such as `Spree::Shipment` and `Spree::InventoryUnit`.\) + +The default checkout flow for the `Spree::Order` model is defined in `app/models/spree/order/checkout.rb` of spree\_core. + +An `Spree::Order` object has an initial state of 'cart'. From there any number of events transition the `Spree::Order` to different states. Spree does not have a separate model or database table for the shopping cart. What the user considers a "shopping cart" is actually an in-progress `Spree::Order`. An order is considered in-progress, or incomplete when its `completed_at` attribute is `nil`. Incomplete orders can be easily filtered during reporting and it's also simple enough to write a quick script to periodically purge incomplete orders from the system. The end result is a simplified data model along with the ability for store owners to search and report on incomplete/abandoned orders. + + For more information on the state machines gem please see the \[README\]\(https://github.com/state-machines/state\_machines\) + +## Checkout Customization + +It is possible to override the default checkout workflow to meet your store's needs. + +### Customizing an Existing Step + +Spree allows you to customize the individual steps of the checkout process. There are a few distinct scenarios that we'll cover here. + +* Adding logic either before or after a particular step. +* Customizing the view for a particular step. + +### Adding Logic Before or After a Particular Step + +The [state\_machines](https://github.com/state-machines/state_machines) gem allows you to implement callbacks before or after transitioning to a particular step. These callbacks work similarly to [Active Record Callbacks](http://guides.rubyonrails.org/active_record_callbacks.html) in that you can specify a method or block of code to be executed prior to or after a transition. If the method executed in a before\_transition returns false, then the transition will not execute. + +So, for example, if you wanted to verify that the user provides a valid zip code before transitioning to the delivery step, you would first implement a `valid_zip_code?` method, and then tell the state machine to run this method before that transition, placing this code in a file called `app/models/spree/order_decorator.rb`: + +```ruby +Spree::Order.state_machine.before_transition to: :delivery, do: :valid_zip_code? +``` + +This callback would prevent transitioning to the `delivery` step if `valid_zip_code?` returns false. + +### Customizing the View for a Particular Step + +Each of the default checkout steps has its own partial defined in the spree frontend `app/views/spree/checkout` directory. Changing the view for an existing step is as simple as overriding the relevant partial in your site extension. + +## The Checkout Flow DSL + +Spree comes with a new checkout DSL that allows you succinctly define the different steps of your checkout. This new DSL allows you to customize _just_ the checkout flow, while maintaining the unrelated admin states, such as "canceled" and "resumed", that an order can transition to. Ultimately, it provides a shorter syntax compared with overriding the entire state machine for the `Spree::Order` class. + +The default checkout flow for Spree is defined like this, adequately demonstrating the abilities of this new system: + +```ruby +checkout_flow do + go_to_state :address + go_to_state :delivery + go_to_state :payment, if: ->(order) { + order.update_totals + order.payment_required? + } + go_to_state :confirm, if: ->(order) { order.confirmation_required? } + go_to_state :complete + remove_transition from: :delivery, to: :confirm +``` + +we can pass a block on each checkout step definition and work some logic to figure if the step is required dynamically. e.g. the confirm step might only be necessary for payment gateways that support payment profiles. + +These conditional states present a situation where an order could transition from delivery to one of payment, confirm or complete. In the default checkout, we never want to transition from delivery to confirm, and therefore have removed it using the `remove_transition` method of the Checkout DSL. The resulting transitions between states look like the image below: + +These two helper methods are provided on `Spree::Order` instances for your convenience: + +* `checkout_steps`: returns a list of all the potential states of the checkout. +* `has_step?`: Used to check if the current order fulfills the requirements for a specific state. + +If you want a list of all the currently available states for the checkout, use the `checkout_steps` method, which will return the steps in an array. + +### Modifying the checkout flow + +To add or remove steps to the checkout flow, you can use the `insert_checkout_step` and `remove_checkout_step` helpers respectively. + +The `insert_checkout_step` takes a `before` or `after` option to determine where to insert the step: + +```ruby +insert_checkout_step :new_step, before: :address +# or +insert_checkout_step :new_step, after: :address +``` + +The `remove_checkout_step` will remove just one checkout step at a time: + +```ruby +remove_checkout_step :address +remove_checkout_step :delivery +``` + +What will happen here is that when a user goes to checkout, they will be asked to \(potentially\) fill in their payment details and then \(potentially\) confirm the order. This is the default behavior of the payment and the confirm steps within the checkout. If they are not required to provide payment or confirmation for this order then checking out this order will result in its immediate completion. + +To completely re-define the flow of the checkout, use the `checkout_flow` helper: + +```ruby +checkout_flow do + go_to_state :payment + go_to_state :complete +end +``` + +### The Checkout View + +After creating a checkout step, you'll need to create a partial for the checkout controller to load for your custom step. If your additonal checkout step is `new_step` you'll need to a `spree/checkout/_new_step.html.erb` partial. + +### The Checkout "Breadcrumb" + +The Spree code automatically creates a progress "breadcrumb" based on the available checkout states. The states listed in the breadcrumb come from the `Spree::Order#checkout_steps` method. If you add a new state you'll want to add a translation for that state in the relevant translation file located in the `config/locales` directory of your extension or application: + +```ruby +en: + order_state: + new_step: New Step +``` + + The default use of the breadcrumb is entirely optional. It does not need to correspond to checkout states, nor does every state need to be represented. Feel free to customize this behavior to meet your exact requirements. + +## Payment Profiles + +The default checkout process in Spree assumes a gateway that allows for some form of third party support for payment profiles. An example of such a service would be [Authorize.net CIM](https://www.authorize.net/our-features/secure-customer-data.html) Such a service allows for a secure and PCI compliant means of storing the users credit card information. This allows merchants to issue refunds to the credit card or to make changes to an existing order without having to leave Spree and use the gateway provider's website. More importantly, it allows us to have a final "confirmation" step before the order is processed since the number is stored securely on the payment step and can still be used to perform the standard authorization/capture via the secure token provided by the gateway. + +Spree provides a wrapper around the standard active merchant API in order to provide a common abstraction for dealing with payment profiles. All `Gateway` classes now have a `payment_profiles_supported?` method which indicates whether or not payment profiles are supported. If you are adding Spree support to a `Gateway` you should also implement the `create_profile` method. The following is an example of the implementation of `create_profile` used in the `AuthorizeNetCim` class: + +```ruby +# Create a new CIM customer profile ready to accept a payment +def create_profile(payment) + if payment.source.gateway_customer_profile_id.nil? + profile_hash = create_customer_profile(payment) + payment.source.update({ + gateway_customer_profile_id: profile_hash[:customer_profile_id], + gateway_payment_profile_id: profile_hash[:customer_payment_profile_id]) + }) + end +end +``` + + Most gateways do not yet support payment profiles but the default checkout process of Spree assumes that you have selected a gateway that supports this feature. This allows users to enter credit card information during the checkout without having to store it in the database. Spree has never stored credit card information in the database but prior to the use of profiles, the only safe way to handle this was to post the credit card information in the final step. It should be possible to customize the checkout so that the credit card information is entered on the final step and then you can authorize the card before Spree automatically discards the sensitive data before saving. + diff --git a/customization/dependencies.md b/customization/dependencies.md new file mode 100644 index 0000000..1f7fb8b --- /dev/null +++ b/customization/dependencies.md @@ -0,0 +1,88 @@ +--- +title: Dependency system +section: customization +order: 3 +--- + +# Dependencies + +## Overview + +With Dependencies you can easily replace parts of Spree internals with your custom classes. You can replace [Services](https://github.com/spree/spree/tree/master/core/app/services/spree), Abilities and [Serializers](https://github.com/spree/spree/tree/master/api/app/serializers/spree/v2). More will come in the future. + +## Controller level customization + +To replace [serializers](https://github.com/jsonapi-serializer/jsonapi-serializer) or Services in a specific API endpoint you can create a simple decorator: + +Create a `app/controllers/my_store/spree/cart_controller_decorator.rb` + +```ruby + module MyStore + module Spree + module CartControllerDecorator + def resource_serializer + MyNewAwesomeCartSerializer + end + + def add_item_service + MyNewAwesomeAddItemToCart + end + end + end + end + Spree::Api::V2::Storefront::CartController.prepend MyStore::Spree::CartControllerDecorator +``` + +This will change the serializer in this API endpoint to `MyNewAwesomeCartSerializer` and also it will swap the default `add_item_service` to `MyNewAwesomeAddItemToCart`. + +Different API endpoints can have different dependency injection points. You can review their [source code](https://github.com/spree/spree/tree/master/api/app/controllers/spree/api/v2) to see what you can configure. + +## API level customization + +Storefront and Platform APIs have separate Dependencies injection points so you can easily customize one without touching the other. + +In your Spree initializer \(`config/initializers/spree.rb`\) please add: + +```ruby +Spree::Api::Dependencies.storefront_cart_serializer = 'MyNewAwesomeCartSerializer' +Spree::Api::Dependencies.storefront_cart_add_item_service = 'MyNewAwesomeAddItemToCart' +``` + +This will swap the default Cart serializer and Add Item to Cart service for your custom ones within all Storefront API endpoints that uses those classes. + + Values set in the initializer has to be strings, eg. \`'MyNewAwesomeAddItemToCart'\` + +## Application \(global\) customization + +You can also inject classes globally to the entire Spree stack. Be careful about this though as this touches every aspect of the application \(both APIs, Admin Panel and default Rails frontend if you're using it\). + +```ruby +Spree::Dependencies.cart_add_item_service = 'MyNewAwesomeAddItemToCart' +``` + +or + +```ruby +Spree.dependencies do |dependencies| + dependencies.cart_add_item_service = 'MyNewAwesomeAddItemToCart' +end +``` + +You can mix and match both global and API level customizations: + +```ruby +Spree::Dependencies.cart_add_item_service = 'MyNewAwesomeAddItemToCart' +Spree::Api::Dependencies.storefront_cart_add_item_service = 'AnotherAddItemToCart' +``` + +The second line will have precedence over the first one, and the Storefront API will use `AnotherAddItemToCart` and the rest of the application will use `MyNewAwesomeAddItemToCart` + + Values set in the initializer has to be strings, eg. \`'MyNewAwesomeAddItemToCart'\` + +## Default values + +Default values can be easily checked looking at the source code of Dependencies classes: + +* [Application \(global\) dependencies](https://github.com/spree/spree/blob/master/core/app/models/spree/app_dependencies.rb) +* [API level dependencies](https://github.com/spree/spree/blob/master/api/app/models/spree/api_dependencies.rb) + diff --git a/customization/emails.md b/customization/emails.md new file mode 100644 index 0000000..440dee1 --- /dev/null +++ b/customization/emails.md @@ -0,0 +1,22 @@ +--- +title: Emails +section: customization +order: 10 +--- + +# Emails + +## Overview + +Spree uses [postmark templates](https://github.com/wildbit/postmark-templates), as a base for all transactional emails. + +## Email previews + +Spree offers emails preview generator for development purposes. To generate them, use command: + +`bundle exec rails g spree:mailers_preview` + +After that, start rails server locally and go to: `localhost:3000/rails/mailers` + +\(it requires seeded development database in order to work properly\) + diff --git a/customization/extensions.md b/customization/extensions.md new file mode 100644 index 0000000..aa725e1 --- /dev/null +++ b/customization/extensions.md @@ -0,0 +1,46 @@ +--- +title: Extensions +section: customization +order: 9 +--- + +# Extensions + +## What is a Spree Extension? + +Extensions provide a convenient mechanism for Spree developers to share reusable code with one another. Even if you do not plan on sharing your extensions with the community, they can still be a useful way to reuse code within your organization. Extensions are also a convenient mechanism for organizing and isolating discrete chunks of functionality. + +## Installing an Extension + +We are going to be adding the [Spree I18n](https://github.com/spree-contrib/spree_i18n) extension to our store. SpreeI18n is a extension containing community contributed translations of Spree & ability to supply different attribute values per language such as product names and descriptions. Extensions can also add models, controllers, and views to create new functionality. + +There are three steps we need to take to install spree\_i18n. + +First, we need to add the gem to the bottom of our `Gemfile`: + +```ruby +gem 'spree_i18n', github: 'spree-contrib/spree_i18n', branch: 'master' +``` + +Now, let's install the gem via Bundler with the following command: + +```bash +bundle install +``` + +Finally, let's copy over the required migrations and assets from the extension with the following command: + +```bash +bundle exec rails g spree_i18n:install +``` + +Answer **yes** when prompted to run migrations. + +## Finding more Extensions + +[Go to Extension Directory](/extensions/) to find more. You can also search and browse GitHub there are many smaller, 3rd party extensions out there. + +## Creating your own Extension + +Please refer [Creating an Extension Tutorial](/developer/contributing/extensions_tutorial.html) + diff --git a/customization/i18n.md b/customization/i18n.md new file mode 100644 index 0000000..53415e3 --- /dev/null +++ b/customization/i18n.md @@ -0,0 +1,136 @@ +--- +title: Internationalization +section: customization +order: 8 +--- + +# Internationalization + +## Overview + +This guide covers how Spree uses Rails' internationalization features, and how you can leverage and extend these features in your Spree contributions and extensions. + +## How Spree i18n works + +Spree uses the standard Rails approach to internationalization so we suggest take some time to review the [official Rails i18n guide](http://guides.rubyonrails.org/i18n.html) to help you get started. + +## The spree\_i18n project + +Spree now stores all of the translation information in a separate GitHub project known as [Spree I18n](https://github.com/spree/spree_i18n). This is a stand alone project with a large number of volunteer committers who maintain the locale files. This is basically the same approach followed by the Rails project which keeps their localizations in [rails-i18n](https://github.com/svenfuchs/rails-i18n). + +The project is actually a Spree extension. This extension contains translations files. To translate models \(provide translations for Products, Taxons, etc\) you will need to install also [Spree Globalize](https://github.com/spree-contrib/spree_globalize). + +### Translation Files + +Each language is stored in a YAML file located in `config/locales`. Each YAML file contains one top level key which is the language code for the translations contained within that file. The following is a snippet showing the basic layout of a locale file: + +```yaml +pt-BR: + spree: + say_no: "Não" + say_yes: "Sim" +``` + + All translations for Spree are "namespaced" within the \`spree\` key so that they don't conflict with translations from other parts of the parent application. + +### Localization Files + +Spree maintains its localization information in a YAML file using a naming convention similar to that of the Rails project. Each of the localization filenames contains a prefix representing the language code of the locale. For example, the Russian translation is contained in `config/locales/ru.yml`. + + Spree has over 43 locale files and counting. See the \[GitHub Repository\]\(https://github.com/spree/spree\_i18n/tree/master/config/locales\) for a complete list. + +### Required Files + +Each locale that you wish to support will require both a Rails and Spree translation. The required Spree translation files are available automatically when you install the `spree_i18n` gem. + +You don't need to copy any files from `spree_i18n` or `rails-i18n` for their translations to be available within your application. They are made available automatically, because both `spree_i18n` and `rails-i18n` are railties. + +## Translating Views + +When reviewing the source of any view in Spree you'll notice that all text is rendered by passing a string to a helper method similar to: + +```text +<%= Spree.t(:price) %> +``` + +The _Spree.t\(\)_ helper method looks up the currently configured locale and retrieves the translated value from the relevant locale YAML file. Assuming a default locale, this translation would be fetched from the en translations collated from the application, `spree_i18n` and `rails-i18n`. Its relative key within those translation files would need to be this: + +```yaml +en: + spree: + price: Price +``` + +## Model Translations + +Like mentioned before to translate models you will need to install [Spree Globalize extension](https://github.com/spree-contrib/spree_globalize) which uses [Globalize](https://github.com/globalize/globalize) library under the hood. + +This gem will allow you to translate: + +* Products +* Promotions +* Option Types +* Taxonomies +* Taxons +* Properties +* Shipping Methods + +## The Default Locale + +Since Spree is basically a Rails application it has the same default locale as any Rails application. The default locale is `en` which use the English language. We can verify this in the rails console + +```ruby +>> I18n.locale +=> :en +``` + +You can also see in the console how the default locale values are translated into English + +```ruby +>> Spree.t(:action) +=> Action +``` + +## Setting the Default Locale + +The default locale for Rails, and therefore Spree, is `en`. This can be changed by setting `config.i18n.default_locale` in `config/application.rb`. This setting is ignored unless the relevant translation file are within `#{Rails.root}/config/locales` or the `spree_i18n` gem. + +## Setting the Default Currency + +We're parsing the prices through the [Money gem](https://github.com/RubyMoney/money) which will display prices consistently across all I18n locales. To change the currency for your site, go to Admin, then Configuration, then General Settings. Changing the currency will only change the currency symbol across all prices of your store. + +There are configuration options for currency: + +* `Spree::Config[:currency]`: 3-letter currency code representing the current currency. + +This options should be set in `config/initializers/spree.rb` file in your application. + +## Multi-currency support + +To add multiple currency support to your application please install [Multi Currency extension](https://github.com/spree-contrib/spree_multi_currency). + +## Localizing Seed Data + +Spree use [Carmen](https://github.com/jim/carmen) to seed the Country and State data. You can localize the seed data by adding Carmen configuration to your `seeds.rb`. See example below: + +```ruby +# add Carmen counfiguration with the following 2 lines +require 'carmen' +Carmen.i18n_backend.locale = :ja + +Spree::Core::Engine.load_seed if defined?(Spree::Core) +Spree::Auth::Engine.load_seed if defined?(Spree::Auth) +``` + +## Creating and Modifying Locales + +Please submit Pull Requests or issues directly to [Spree I18n](https://github.com/spree/spree_i18n) for missing translations. + +## Localizing Extensions + +Spree extensions can contain their own `config/locales` directory where developers can include YAML files for each language they wish to support. + +We strongly urge all extension developers to ensure all customer facing text is rendered via the `Spree.t()` helper method even if they only include a single default language locale file \(as other users can simply include the required YAML file and translations in their site extension\). + + Since Spree extensions are equivalent to Rails Engines they can provide localization information automatically \(just like a standalone Rails application.\) + diff --git a/customization/images.md b/customization/images.md new file mode 100644 index 0000000..df58da5 --- /dev/null +++ b/customization/images.md @@ -0,0 +1,54 @@ +--- +title: Images +section: customization +order: 2 +--- + +# Images + +## Overview + +This guide explains how to change Product Images dimensions and different storage options for [ActiveStorage](https://edgeguides.rubyonrails.org/active_storage_overview.html) which is the default attachment storage system in Spree. + +### Image dimensions + +To change the default image dimensions or add new ones you need to create a decorator file `app/models/my_store/spree/image_decorator.rb`: + +```ruby +module MyStore + module Spree + module ImageDecorator + module ClassMethods + def styles + { + mini: '48x48>', + small: '100x100>', + product: '240x240>', + large: '600x600>', + } + end + end + + def self.prepended(base) + base.inheritance_column = nil + base.singleton_class.prepend ClassMethods + end + end + end +end + +::Spree::Image.prepend ::MyStore::Spree::ImageDecorator +``` + +You can also create image variations on the fly in your templates, eg. + +```text +<%= image_tag(main_app.url_for(@product.images.first.attachment.variant(resize: '150x150'))) %> +``` + +### Using Amazon S3 as storage system + +Please refer to the official [Active Storage documentation](https://guides.rubyonrails.org/active_storage_overview.html#amazon-s3-service) + +You can also use [Microsoft Azure Storage](https://guides.rubyonrails.org/active_storage_overview.html#microsoft-azure-storage-service) or [Google Cloud Storage](https://guides.rubyonrails.org/active_storage_overview.html#google-cloud-storage-service) + diff --git a/customization/logic.md b/customization/logic.md new file mode 100644 index 0000000..7b9bd55 --- /dev/null +++ b/customization/logic.md @@ -0,0 +1,127 @@ +--- +title: Business Logic +section: customization +order: 4 +--- + +# Business Logic + +## Overview + +It is highly recommended to use [Dependencies](dependencies.md) and [Extensions](/developer/customization/extensions.html) first rather than to apply patches to Spree Core. Still if you don't find those to be efficient you can pretty much overwrite any part of Spree following this guide. + +## Extending Classes + +All of Spree's business logic \(models, controllers, helpers, etc\) can easily be extended or overridden to meet your exact requirements using standard Ruby idioms. + +Standard practice for including such changes in your application or extension is to create a file within the relevant **app/models/spree** or **app/controllers/spree** directory with the original class name with **\_decorator** appended. + +## Extending Models + +Adding a custom method to the [Product](https://github.com/spree/spree/blob/master/core/app/models/spree/product.rb) model: `app/models/my_store/spree/product_decorator.rb` + +```ruby +module MyStore + module Spree + module ProductDecorator + def self.prepended(base) + base.before_validation :strip_whitespaces + end + + def some_method + ... + end + + protected + + def strip_whitespaces + ... + end + end + end +end + +::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator) +``` + +#### Adding new associations to existing models + +Assume you want to add a new model called `Video` associated to `Spree::Product`. Let's start with creating a database migration: + +```text +bundle exec rails g migration CreateVideos url:string product:references +bundle exec rails db:migrate +``` + +Add new model to `app/models/videos.rb`: + +```ruby +class Video < ApplicationRecord + belongs_to :product, class_name: 'Spree::Product' +end +``` + +Finally add the association in `ProductDecorator` in `app/models/my_store/spree/product_decorator.rb`: + +```ruby +module MyStore + module Spree + module ProductDecorator + def self.prepended(base) + base.has_many :videos, class_name: 'Video', foreign_key: 'product_id', dependent: :destroy + end + end + end +end + +::Spree::Product.prepend MyStore::Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(MyStore::Spree::ProductDecorator) +``` + +## Extending Controllers + +Adding a custom action to the [ProductsController](https://github.com/spree/spree/blob/master/frontend/app/controllers/spree/products_controller.rb): `app/controllers/my_store/spree/products_controller_decorator.rb` + +```ruby +module MyStore + module Spree + module ProductsControllerDecorator + def some_action + ... + end + end + end +end + +::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator) +``` + +The exact same format can be used to redefine an existing method. + +### Accessing Product Data + +If you extend the Products controller with a new method, you may very well want to access product data in that method. You can do so by using the `:load_data before_action`. + +```ruby +module MyStore + module Spree + module ProductsControllerDecorator + def self.prepended(base) + base.before_action :load_data, only: :some_action + end + + def some_action + ... + end + end + end +end + +::Spree::ProductsController.prepend MyStore::Spree::ProductsControllerDecorator if ::Spree::ProductsController.included_modules.exclude?(MyStore::Spree::ProductsControllerDecorator) +``` + +`:load_data` will use `params[:id]` to lookup the product by its permalink. + +## Replacing Models or Controllers + +If your customizations are so large that you overwrite majority of a given Model or Controller we recommend to drop the `_decorator` pattern and overwrite the Model or Controller completely in your project. This will make future Spree upgrades easier. + diff --git a/customization/permissions.md b/customization/permissions.md new file mode 100644 index 0000000..6dbbbf8 --- /dev/null +++ b/customization/permissions.md @@ -0,0 +1,111 @@ +--- +title: Permissions +section: customization +order: 5 +--- + +# Permissions + +## Overview + +## User and Roles + +Spree by default comes with `admin` and `user` roles. You can create more roles in the Admin Panel UI or rails console / seed, eg.: + +```ruby +Spree::Role.find_or_create_by(name: 'customer_service') +``` + +Same with assigning a role, you can do it in the Admin Panel or from the console: + +```ruby +Spree.user_class.find_by(email: 'john@example.com').spree_roles << Spree::Role.find_or_create_by(name: 'customer_service') +``` + +## Ability class + +For authorization Spree uses [CanCanCan](https://github.com/CanCanCommunity/cancancan). The main ability class by default is [Spree::Ability](https://github.com/spree/spree/blob/master/core/app/models/spree/ability.rb). + +## Adding custom permissions + +Let's assume you would like to add a new Role `customer_service` with some limited access to Admin Panel \(only Orders section\). + +Create a new file called `app/models/my_project/customer_service_ability.rb` + +```ruby +module MyProject + class CustomerServiceAbility + include CanCan::Ability + + def initialize(user) + if user.respond_to?(:has_spree_role?) && user.has_spree_role?('customer_service') + can :manage, ::Spree::Order + end + end + end +end +``` + +Please familiarize yourself with [CanCanCan](https://github.com/CanCanCommunity/cancancan) syntax to understand `can/cannot` methods more. + +Now we need to inform Spree to use this ability, create another file `app/models/my_project/spree/ability_decorator.rb` with contents: + +```ruby +module MyProject + module Spree + module AbilityDecorator + def abilities_to_register + [CustomerServiceAbility] + end + end + end +end + +::Spree::Ability.prepend(MyProject::Spree::AbilityDecorator) +``` + +## Replacing all permissions + +As we've mentioned earlier, Spree uses main Ability class. But you can change that to use your own custom Ability class via [Dependencies](/developer/customization/dependencies.html) in Spree initializer \(`config/initializers/spree.rb`\), eg. + +```ruby +Spree::Dependencies.ability_class = 'MyProject::Ability' +``` + +Now, let's define that new class in `app/models/my_project/ability.rb` + +```ruby +module MyProject + class Ability < ::Spree::Ability + def initialize(user) + alias_cancan_delete_action + + user ||= Spree.user_class.new + + if user.respond_to?(:has_spree_role?) && user.has_spree_role?('admin') + apply_admin_permissions(user) + # add some more permissions here for the admin role + else + if user.respond_to?(:has_spree_role?) && user.has_spree_role?(:customer_service) + apply_customer_service_permissions(user) + end + + apply_user_permissions(user) + end + + protect_admin_role + end + + protected + + def apply_customer_service_permissions(user) + can :manage, ::Spree::Order + end + end +end +``` + +Now your can restart your rails server and observe new permissions being implemented in the application. + +Leter permission changes should be automatically picked up in development and should not require application restarts. + diff --git a/customization/storefront.md b/customization/storefront.md new file mode 100644 index 0000000..7f0c117 --- /dev/null +++ b/customization/storefront.md @@ -0,0 +1,459 @@ +--- +title: Storefront +section: customization +order: 0 +--- + +# Storefront + +## Styling with SASS variables + +Spree 4 under the hood uses [Bootstrap 4](https://getbootstrap.com/docs/4.6/getting-started/theming/) for easy theming with some additional Spree-specific [SASS variables](https://sass-lang.com/documentation/variables). + +To make those changes live you need to update SCSS variable files located in your project at `app/assets/stylesheets/spree/frontend/variables/variables.scss`. + +### Header + +_**$header-background**_ - header background color variable with 2 examples: white and blue one. By default, this is set with a _**$primary-background**_ value but you can replace it with any other value in the variables.scss file. + +_**$header-font-color**_- Header font color. By default set with _**$font-color**_ value but you can replace it with any other value in the variables.scss file. + +### Footer + +_**$footer-background**_ - a variable that overrides $primary-background and allows you to change the footer color. See a white and a blue example below. + +_**$footer-font-color**_ - a variable that overrides _**$font-color**_ and allows you to change the footer font color. See black and blue font examples below. + +### Meganav menu + +_**$meganav-background**_ - a variable that allows you to change the mega nav menu background color. By default the meganav menu is set to a _**$primary-background**_ value but you can replace it with any other value in the variables.scss file. + +_**$meganav-font-color**_ - a font color variable in the mega nav menu. By default the mega nav font color is set to a $font-color value but you can replace it with any other value in the variables.scss file. + +### Background + +_**$primary-background**_ - the main background color across the whole site. There are two examples below, of the white and the black backgrounds. Please note that you can also use an image as a background. + +_**$secondary-background**_ - the second background color present across the whole site with examples attached below. + +_**$font-color**_ - this variable affects all fonts on the $primary-background. Please see two examples below. + +_**$secondary-font-color**_ - this affects all fonts on $secondary-background. By default, it is set with a $font-color value but you can replace it with any other value in the variables.scss file. + +### Border color + +_**$global-border-style**_ - this affects the border and separator color throughout the whole site + +### Fonts + +_**$font-family**_ - this sets the font family used across your site. By default, it is in Sans Serif but you can replace it with any other value in the variables.scss file. Check out [these font families](https://websitesetup.org/web-safe-fonts-html-css/) you might use. + +### Input fields + +_**$input-background**_ - this allows you to set a color for all input field backgrounds across the site. See two examples of a white and a yellow backround below. + +_**$second-global-border**_ - this allows you to set a color for all input field borders across the whole site. See an example below with red input field borders. + +### Primary color + +#### Home page + +_**$primary-color**_ variable changes + +* The color of the **SHOP NOW** button on the main hero image +* The color of the **Summer 2019** text and **READ MORE** button +* The color of the **NEW COLLECTION** and **SUMMER SALE** headers inside the categories section + +#### Search results + +_**$primary-color**_ variable changes + +* The color of the **No results** icon + +#### Mega Menu + +_**$primary-color**_ variable changes + +* The color of **NEW COLLECTION** and **SUMMER SALE** headers inside the banners + +#### PDP + +_**$primary-color**_ variable changes + +* The color of the **IN STOCK** text +* The color of the **ADD TO CART** button + +#### Cart Page + +_**$primary-color**_ variable changes + +* The color of the **Trash** delete icon for removing items from the cart +* The color of the **CHECKOUT** button + +#### Cart pop-up + +_**$primary-color**_ variable changes + +* The color of the **CHECKOUT** and **VIEW CART** buttons + +#### Cart - empty + +_**$primary-color**_ variable changes + +* The color of the **CONTINUE SHOPPING** button +* The color of the **Empty cart** icon + +#### Checkout - Registration Step + +_**$primary-color**_ variable changes + +* The color of the **LOG IN, SIGN UP** and **CONTINUE AS A GUEST** buttons + +#### Checkout - Address step + +_**$primary-color**_ variable changes + +* The color of the **SAVE AND CONTINUE** button \(this element remains the same across the whole checkout process\) +* The color of the **Edit** icon + +#### Checkout - Payment step + +_**$primary-color**_ variable changes + +* The color of the **APPLY** button + +#### Checkout - Confirm step + +_**$primary-color**_ variable changes + +* The color of the **PLACE ORDER** button + +#### Sign-in page + +_**$primary-color**_ variable changes + +The color of the **LOG IN** and **SIGN UP** buttons + +#### Sign up page + +_**$primary-color**_ variable changes + +* The color of the **SIGN UP** and **LOG IN** buttons + +#### My account page + +_**$primary-color**_ variable changes + +* The color of the **Edit** and **Trash** icons + +#### Edit account page + +_**$primary-color**_ variable changes + +* The color of the **UPDATE** button + +#### Pop-ups + +_**$primary-color**_ variable changes + +* The color of the **CANCEL** and **OK** buttons + +### Secondary color + +#### PLP + +_**$secondary-color**_ variable changes + +* The color of the chosen **color** border variant +* The color of the chosen **size** border variant +* The color of the chosen **length** border variant +* The color of the chosen **price** border variant + +#### PDP + +_**$secondary-color**_ variable changes + +* The color of the chosen **color** border variant +* The color of the chosen **size** border variant +* The color of the chosen **length** border variant +* The color of the chosen **image** border + +#### Pop-ups + +_**$secondary-color**_ variable changes + +* The color of the **Add to bag successfully** icon + +Log-in and Sign-in page + +_**$secondary-color**_ variable changes + +* The color of the **Remember me** checkbox +* The color of the **input: focus** + +#### Checkout + +_**$secondary-color**_ variable changes + +* The color of **individual steps** \(box, name step, and guideline\) - this element remains the same across the whole checkout process + +#### Checkout - Address step + +_**$secondary-color**_ variable changes + +* The color of the **Use shipping address** checkbox + +#### Checkout - Delivery step + +_**$secondary-color**_ variable changes + +* The color of delivery type radio buttons + +#### Checkout - Payment step + +_**$secondary-color**_ variable changes + +* The color of payment type radio buttons +* The color of payment card radio buttons + +#### Order confirmation page + +_**$secondary-color**_ variable changes + +* The color of the **successful checkmark** icon + +### Grid breakpoints + +[Grid breakpoint variable](https://github.com/spree/spree/blob/master/frontend/app/assets/stylesheets/spree/frontend/variables/bootstrap-overrides.scss) allows you to slightly change element sizes on various devices. These changes are mostly to images and their scale ratio. Feel free to learn more from the [Bootstrap manual](https://getbootstrap.com/docs/4.0/layout/grid/), though we don’t recommend changing these values unless you really need to. + +### Rounding for components + +_**$enable-rounded**_ - Enable rounding for components. + +Possible values: **true** or **false** + +**“True” example** + +**“False” example** + +### Shadows for components + +_**$enable-shadows**_ - Enable shadow for components + +Possible values: **true** or **false** + +### Gradient for components + +_**$enable-gradients**_ - Enable gradient for components + +_**$enable-gradients**_ - Enable gradient for components + +## Templates + +### Importing + +You can import all templates from spree frontend into your application using this command \(in your application root directory\): + +```bash +rails g spree:frontend:copy_storefront +``` + +All of those views will be added to your `app/views` directory under `spree` folder. You can modify them as you wish. + +## Assets + +### Spree's Asset Pipeline + +Spree applications include an `app/assets` directory. We've taken this one step further by subdividing each top level asset directory \(images, JavaScript files, stylesheets\) into `frontend` and `backend` directories. This is designed to keep assets from the frontend and backend from conflicting with each other. + +A typical assets directory for a Spree application will look like: + +```text +app +|-- assets + |-- images + | |-- spree + | |-- frontend + | |-- backend + |-- javascripts + | |-- spree + | |-- frontend + | | |-- all.js + | |-- backend + | |-- all.js + |-- stylesheets + | |-- spree + | |-- frontend + | | |-- all.css + | |-- backend + | |-- all.css +``` + +Spree also generates four top level manifests \(all.css & all.js, see above\) that require all the core extension's and site specific stylesheets / JavaScript files. + +### How core extensions \(engines\) manage assets + +All core engines have been updated to provide four asset manifests that are responsible for bundling up all the JavaScript files and stylesheets required for that engine. + +For example, Spree provides the following manifests: + +```text +vendor +|-- assets + |-- javascripts + | |-- spree + | |-- frontend + | | |-- all.js + | |-- backend + | |-- all.js + |-- stylesheets + | |-- spree + | |-- frontend + | | |-- all.css + | |-- backend + | |-- all.css +``` + +These manifests are included by default by the relevant all.css or all.js in the host Spree application. For example, `vendor/assets/javascripts/spree/backend/all.js` includes: + +```javascript +//= require spree/backend + +//= require_tree . +``` + +External JavaScript libraries, stylesheets and images have also been relocated into vendor/assets. + +### Managing your application's assets + +Assets that customize your Spree store should go inside the appropriate directories inside `vendor/assets/images/spree`, `vendor/assets/javascripts/spree`, or `vendor/assets/stylesheets/spree`. This is done so that these assets do not interfere with other parts of your application. + +### Overriding Spree's assets + +Overriding or replacing any of Spree's internal assets is even easier than before. It's recommended to attempt to replace as little as possible in a given JavaScript or stylesheet file to help ease future upgrade work required. + +The methods listed below will work for both applications, extensions and themes with one noticeable difference: Extension & theme asset files will not be automatically included \(see above for instructions on how to include asset files from your extensions / themes\). + +### Overriding individual CSS styles + +Say for example you want to replace the following CSS snippet: + +```css +/* spree/app/assets/stylesheets/spree/frontend/screen.css */ + +div#footer { + clear: both; +} +``` + +You can now just create a new stylesheet inside `your_app/vendor/assets/stylesheets/spree/frontend/` and include the following CSS: + +```css +/* vendor/assets/stylesheets/spree/frontend/foo.css */ + +div#footer { + clear: none; + border: 1px solid red; +} +``` + +The `frontend/all.css` manifest will automatically include `foo.css` and it will actually include both definitions with the one from `foo.css` being included last, hence it will be the rule applied. + +### Overriding entire CSS files + +To replace an entire stylesheet as provided by Spree you simply need to create a file with the same name and save it to the corresponding path within your application's or extension's `vendor/assets/stylesheets` directory. + +For example, to replace `spree/frontend/all.css` you would save the replacement to `your_app/vendor/assets/stylesheets/spree/frontend/all.css`. + + This same method can also be used to override stylesheets provided by third-party extensions. + +### Overriding individual JavaScript functions + +A similar approach can be used for JavaScript functions. For example, if you wanted to override the `show_variant_images` method: + +```javascript + // spree/app/assets/javascripts/spree/frontend/product.js + +var show_variant_images = function(variant_id) { + $('li.vtmb').hide(); + $('li.vtmb-' + variant_id).show(); + var currentThumb = $('#' + + $("#main-image").data('selectedThumbId')); + + // if currently selected thumb does not belong to current variant, + // nor to common images, + // hide it and select the first available thumb instead. + + if(!currentThumb.hasClass('vtmb-' + variant_id) && + !currentThumb.hasClass('tmb-all')) { + var thumb = $($('ul.thumbnails li:visible').eq(0)); + var newImg = thumb.find('a').attr('href'); + $('ul.thumbnails li').removeClass('selected'); + thumb.addClass('selected'); + $('#main-image img').attr('src', newImg); + $("#main-image").data('selectedThumb', newImg); + $("#main-image").data('selectedThumbId', thumb.attr('id')); + } +} +``` + +Again, just create a new JavaScript file inside `your_app/vendor/assets/javascripts/spree/frontend` and include the new method definition: + +```javascript + // your_app/vendor/assets/javascripts/spree/frontend/foo.js + +var show_variant_images = function(variant_id) { + alert('hello world'); +} +``` + +The resulting `frontend/all.js` would include both methods, with the latter being the one executed on request. + +### Overriding entire JavaScript files + +To replace an entire JavaScript file as provided by Spree you simply need to create a file with the same name and save it to the corresponding path within your application's or extension's `app/assets/javascripts` directory. + +For example, to replace `spree/frontend/all.js` you would save the replacement to `your_app/vendor/assets/javascripts/spree/frontend/all.js`. + + This same method can be used to override JavaScript files provided by third-party extensions. + + + +## SEO recommendations + +### Sitemap + +We highly recommend adding a sitemap to your site. It might affect how Google bot crawls your store pages. There is an official extension called [Spree Sitemap](https://github.com/spree-contrib/spree_sitemap) for that exact purpose. + +1. Per region, language or currency +2. Click the **Edit** button \(indicated with a pencil icon\) for the right store +3. Enter a title, keywords, and description values for the store homepage +4. Click the **Update** button at the bottom of the page + +To set the title, meta keywords, and description for each store **category page \(PLP\)**, in the admin panel: + +1. Go to **Products > Taxonomies** +2. Go into the Categories list by pressing the **Edit** button \(pencil icon\) +3. Pick the category you’d like to edit by right-clicking \(control + click on a Mac\) a child in the tree to access the menu for adding, deleting or sorting a child. +4. Click the **Edit** link for that category +5. Replace the default values for title, meta keywords, and description with your own +6. Click the **Update** button at the bottom of the page + +You’ll have to edit every category and subcategory to your liking in a similar fashion. + +To set the title, meta keywords and description for each **product page \(PDP\)**, in the admin panel: + +1. Go to **Products > Products** +2. In the product list pick the right one by pressing the **Edit** button \(pencil icon\) +3. While in the Details tab, scroll down and input your values for the title, meta keywords, and description +4. Click the **Update** button at the bottom of the page + +### Social sharing and search preview + +The new Spree UX has the following social sharing features implemented: + +* Facebook sharing with [Open Graph tags](https://ogp.me/) to enable an attractive page preview +* Google visibility with structured data using [Schema.org](http://schema.org/) with [JSON-DL](https://json-ld.org/) + +Feel free to [test the Open Graph tags implementation](https://developers.facebook.com/tools/debug/) and the also [test the Schema.org implementation](https://search.google.com/structured-data/testing-tool/u/0/) for your store. + diff --git a/deployment/aws.md b/deployment/aws.md new file mode 100644 index 0000000..771b760 --- /dev/null +++ b/deployment/aws.md @@ -0,0 +1,36 @@ +--- +title: Amazon Web Services (AWS) +section: deployment +order: 0 +--- + +# AWS + +Amazon Web Services offers reliable, scalable, and inexpensive cloud computing services. Free to join, pay only for what you use. + +AWS is also one of the most popular choices for hosting a Spree application. There are several services you can use to host Spree on AWS, here we're briefly touch upon those options. + +## AWS Elastic Beanstalk + +The easiest way to run Spree or any other Ruby on Rails application on AWS is through AWS Elastic Beanstalk which is comparable to Heroku PaaS \(Platform as a Service\). + +This is the recommended approach if you're just starting up. Please follow [Beanstalk Deployment guide](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ruby-rails-tutorial.html) for more details. + +## AWS ECS + +Another option is [Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) to host and deploy your Docker containers. + +This is more advanced than Beanstalk and will require DevOps knowledge. For building Docker images we recommend you use [Spree Starter Dockerfile](https://github.com/spree/spree_starter/blob/main/Dockerfile.production). + +## AWS EC2 + +[EC2](https://aws.amazon.com/ec2/) is the most basic offering, running a single or multiple instances of servers. This is the bare bones variant, you need to setup the deployment all by yourself using tools like [Capistrano](https://capistranorb.com/). + +## Recommended AWS services + +* [AWS S3](https://aws.amazon.com/s3/) - object storage service to store and read your uploaded files such as Product images etc. We **do not recommend** keeping your uploads on the same instance as the application. +* [AWS RDS](https://aws.amazon.com/rds/) - Amazon Relational Database Service \(Amazon RDS\) makes it easy to set up, operate, and scale a relational database in the cloud. Spree works great with multiple databases: [Amazon Aurora \(both MySQL and PostgreSQL variants\)](https://aws.amazon.com/rds/aurora/), [RDS PostgreSQL](https://aws.amazon.com/rds/postgresql/), [RDS MySQL](https://aws.amazon.com/rds/mysql/) and [RDS MariaDB](https://aws.amazon.com/rds/mariadb/) +* [AWS ElastiCache Redis](https://aws.amazon.com/elasticache/redis/?nc=sn&loc=2&dn=1) - we recommend setting up a Redis database for [Active Job background queue](https://guides.rubyonrails.org/active_job_basics.html), which we use for sending out transactional emails +* [AWS ElastiCache Memcached](https://aws.amazon.com/elasticache/memcached/?nc=sn&loc=2&dn=1) - we recommend using [Memcached as a cache storage](https://guides.rubyonrails.org/caching_with_rails.html) to increase performance and scalability of your Spree application +* [AWS CloudFront](https://aws.amazon.com/cloudfront/) - fast content delivery network \(CDN\) to speed up your asset \(images/stylesheets/javascripts\) delivery. This will greatly enhance your application responsiveness. + diff --git a/deployment/heroku.md b/deployment/heroku.md new file mode 100644 index 0000000..9311960 --- /dev/null +++ b/deployment/heroku.md @@ -0,0 +1,58 @@ +--- +title: Heroku +section: deployment +order: 1 +--- + +# Heroku + +## Overview + +Heroku is a Platform as a Service that makes deploying and hosting Spree applications super easy. + +You should just follow [Heroku Rails 6 guide](https://devcenter.heroku.com/articles/getting-started-with-rails6). + +We recommend you start and stick to Heroku if you do not have DevOps-skilled team members. [Spree Starter](https://github.com/spree/spree_starter) is pre-configured to work with Heroku out of the box. + +## Dynos + +[Heroku Dynos](https://www.heroku.com/dynos) are lightweight, isolated environments that provide compute and run yuor application. + +There's 2 type of dynos: + +* Web - for running the web interface of yuour Store \(Storefront, API, Admin Panel\) +* Worker - for running background jobs via [Active Job](https://guides.rubyonrails.org/active_job_basics.html) such as email send out, report generation, etc + +### Recommended sizing + +| Dynos | Staging environment | Production environment | +| :--- | :--- | :--- | +| **web** | 1 x Standard-2x | 1 x Standard-2x \(small traffic\) or 1 x Performance-M \(medium traffic\) | +| **worker** | 1 x Standard-1x | 1 x Standard-1x | + +## Add-Ons + +[Heroku Add-Ons](https://elements.heroku.com/addons) are tools and services for developing, extending, and operating your app. + +### Recommended Add-Ons and plans + +| Plan | Staging environment | Production environment | +| :--- | :--- | :--- | +| Bucketeer | Hobbyist | Micro | +| Edge | Hobby | Micro | +| Heroku Postgres | Hobby Basic | Standard-0 | +| Heroku Scheduler | N/A | N/A | +| Memcached Cloud | Free | 100 MB | +| Papertrail | Choklad | Fixa | +| Redis Cloud | Free | 100 MB | +| Scout APM | Free | Free \(small traffic\) or Eldora \(medium traffic\) | +| Sendgrid | Free | Bronze | +| Sentry | Free | Small | + +## Other resources + +* [https://devcenter.heroku.com/categories/reference](https://devcenter.heroku.com/categories/reference) +* [https://devcenter.heroku.com/articles/getting-started-with-rails6](https://devcenter.heroku.com/articles/getting-started-with-rails6) +* [https://devcenter.heroku.com/categories/monitoring-metrics](https://devcenter.heroku.com/categories/monitoring-metrics) +* [https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server](https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server) + diff --git a/getting-started/installation.md b/getting-started/installation.md new file mode 100644 index 0000000..a81cb6b --- /dev/null +++ b/getting-started/installation.md @@ -0,0 +1,36 @@ +# Installation + +## Prerequisites + +Before proceeding make sure you have [Docker Desktop](https://docs.docker.com/get-docker/) installed on your system. This is fairly straightforward, but differs depending on which operating system you use. + +If you would like to add Spree to your existing Ruby on Rails application, please [follow this guide instead](/developer/advanced/existing_app_tutorial.html). + +### Windows + +Windows users will need to [install Linux subsystem](https://docs.microsoft.com/en-us/windows/wsl/install-win10) to proceed. + +## Installation + +1. Download [Spree Starter](https://github.com/spree/spree_starter/archive/main.zip) +2. Unzip it +3. Rename `spree_starter-main` directory as you please +4. Run `bin/setup` in said directory +5. Wait for the commands to execute \(it can take around 2-3 minutes\) + +## Hello, Spree Commerce + +You now have a functional Spree application after running only a few commands! + +To see your application in action, open a browser window and navigate to [http://localhost:3000](http://localhost:3000). You should see the Spree default home page: + +To stop the web server, hit Ctrl-C in the terminal window where it's running. In development mode, Spree does not generally require you to stop the server; changes you make in files will be automatically picked up by the server. + +### Logging Into the Admin Panel + +The next thing you'll probably want to do is to log into the admin interface. Use your browser window to navigate to [http://localhost:3000/admin](http://localhost:3000/admin). You can login with the username `spree@example.com` and password `spree123`. + +Upon successful authentication, you should see the admin screen: + +Feel free to explore some of the Admin Panel features that Spree has to offer and to verify that your installation is working properly. + diff --git a/getting-started/understanding_spree.md b/getting-started/understanding_spree.md new file mode 100644 index 0000000..8d039ca --- /dev/null +++ b/getting-started/understanding_spree.md @@ -0,0 +1,67 @@ +--- +title: Understanding Spree +section: getting_started +order: 1 +--- + +# Understanding how Spree works + +So you're probably wondering how all that magic works? Let's dive in. + +## Rails Engine + +Spree is a [Ruby on Rails Engine](https://guides.rubyonrails.org/engines.html), which means it's an application that provides functionality to their host applications \(that is your store application\). + +Spree is a collection of Models, Views and Controllers that your application gains access when you install Spree. You can easily combine Spree with any Ruby on Rails application meaning you can add e-commerce capabilities to your existing RoR applications. + +## Spree namespace + +All Spree models, controllers and other classes are namespaced by the `Spree` keyword, eg. `Spree::Product`. This means that those files are also located in `spree` sub-directories eg. [app/models/spree/product.rb](https://github.com/spree/spree/blob/master/core/app/models/spree/product.rb). + +## Spree modules + +Spree is divided into several modules / gems which you can opt-out if you would like. Installing Spree via Spree Starter gives you access to all of Spree features such as Stoprefront, API and Admin Panel. Not all of the modules are required, eg. headless installations will not require Storefront at all. + +| Spree module | Description | Required? | +| :--- | :--- | :--- | +| **api** | REST API for your Store | yes | +| **backend** | Admin Panel UI | no | +| **core** | Models, Services and libraries | yes | +| **frontend** | Storefront UI | no | +| **sample** | Sample seed data | no | + +-- + +There are many other Spree-gems providing additional functionality to your Store called [Extensions](/extensions). + +To change which Spree gems you would like to install you will need to modify your project `Gemfile`. + +### Full-stack Spree application + +```ruby +gem 'spree' +``` + +### Headless installation with API and Admin Panel + +```ruby +gem 'spree_api' +gem 'spree_backend' +``` + +After changing the Gemfile please run + +```bash +bundle install +``` + +or if using [Spree Starter](https://github.com/spree/spree_starter): + +```bash +bin/bundle-install +``` + +## Next steps + +We recommend you go over [Internals section](../internals/stores.md) to learn more how Spree works under the hood. This knowledge will be very helpful when you'll decide you want to [customize your Spree store](/developer/customization/). + diff --git a/internals/addresses.md b/internals/addresses.md new file mode 100644 index 0000000..f9a8679 --- /dev/null +++ b/internals/addresses.md @@ -0,0 +1,54 @@ +--- +title: Addresses +section: internals +--- + +# Addresses + +## Address + +The `Address` model in the `spree` gem is used to track address information, mainly for orders. Address information can also be tied to the `Spree::User` objects which come from the [Spree Auth Devise](https://github.com/spree/spree_auth_devise) extension. + +Addresses have the following attributes: + +* `firstname`: The first name for the person at this address. +* `lastname`: The last name for the person at this address. +* `address1`: The address's first line. +* `address2`: The address's second line. +* `city`: The city where the address is. +* `zipcode`: The postal code. +* `phone`: The phone number. +* `state_name`: The name for the state. +* `alternative_phone`: The alternative phone number. +* `company`: A company name. + +Addresses can also link to countries and states. An address must always link to a `Spree::Country` object. It can optionally link to a `Spree::State` object, but only in the cases where the related country has no states listed. In that case, the state information is still required, and is kept within the `state_name` field on the address record. An easy way to get the state information for the address is to call `state_text` on that object. + +## Users + +As of Spree 4.0 `Spree::User` can have multiple addresses and can manage them on the Checkout and Account page. Previously this feature was part of the [Spree Address Book](https://github.com/spree-contrib/spree_address_book) extension. Each `Spree::Address` object has a `user_id` column which links to the User. + +Additionally in the `Spree::User` there is `ship_address_id` and `bill_address_id` columns which indicates which addresses are the default ones for this User. + +## Checkout + +When a signed in User completes the Checkout Spree clones the selected Shipping and Billing Addresses and associates them with the Order. We do so that even if User modifies those addresses in the future their current state is preserved. Cloned addresses aren't associated to the User anymore. Originals will still be visible in their Address Books for future use. + +## Zones + +When an order's address is linked to a country or a state, that can ultimately affect different features of the order, including shipping availability and taxation. The way these effects work is through zones. + +A zone is comprised of many different "zone members", which can either be a set of countries or a set of states. + +Every order has a "tax zone", which indicates if a user should or shouldn't be taxed when placing an order. For more information, please see the [Taxation](/developer/internals/taxation.html) guide. + +In addition to tax zones, orders also have shipping methods. These are provided to the user based on their address information, and once selected lock in how an order is going to be shipped to that user. For more information, please see the [Shipments](/developer/internals/shipments.html) guide. + +## Countries + +Countries within Spree are used as a container for states. Countries can be zone members, and also link to an address. The difference between one country and another on an address record can determine which tax rates and shipping methods are used for the order. + +## States + +States within Spree are used to scope address data slightly more than country. States are useful for tax purposes, as different states in a country may impose different tax rates on different products. In addition to this, different states may cause different tax rates and shipping methods to be used for an order, similar to how countries affect it also. + diff --git a/internals/adjustments.md b/internals/adjustments.md new file mode 100644 index 0000000..ab38c04 --- /dev/null +++ b/internals/adjustments.md @@ -0,0 +1,149 @@ +--- +title: Adjustments +section: internals +order: 6 +--- + +# Adjustments + +## Overview + +An `Adjustment` object tracks an adjustment to the price of an [Order](/developer/internals/orders.html), an order's [Line Item](/developer/internals/orders.html#line-items), or an order's [Shipments](/developer/internals/shipments.html) within a Spree Commerce storefront. + +Adjustments can be either positive or negative. Adjustments with a positive value are sometimes referred to as "charges" while adjustments with a negative value are sometimes referred to as "credits." These are just terms of convenience since there is only one `Spree::Adjustment` model in a storefront which handles this by allowing either positive or negative values. + +Adjustments can either be considered included or additional. An "included" adjustment is an adjustment to the price of an item which is included in that price of an item. A good example of this is a GST/VAT tax. An "additional" adjustment is an adjustment to the price of the item on top of the original item price. A good example of that would be how sales tax is handled in countries like the United States. + +Adjustments have the following attributes: + +* `amount` The dollar amount of the adjustment. +* `label`: The label for the adjustment to indicate what the adjustment is for. +* `eligible`: Indicates if the adjustment is eligible for the thing it's adjusting. +* `mandatory`: Indicates if this adjustment is mandatory; i.e that this adjustment _must_ be applied regardless of its eligibility rules. +* `state`: Can either be `open` or `closed`. Once an adjustment is closed, it will not be automatically updated. +* `included`: Whether or not this adjustment affects the final price of the item it is applied to. Used only for tax adjustments which may themselves be included in the price. + +Along with these attributes, an adjustment links to three polymorphic objects: + +* A source +* An adjustable + +The _source_ is the source of the adjustment. Typically a `Spree::TaxRate` object or a `Spree::PromotionAction` object. + +The _adjustable_ is the object being adjusted, which is either an order, line item or shipment. + +Adjustments can come from one of two locations within Spree's core: + +* Tax Rates +* Promotions + +An adjustment's `label` attribute can be used as a good indicator of where the adjustment is coming from. + +## Adjustment Scopes + +There are some helper methods to return the different types of adjustments: + +```ruby +scope :shipping, -> { where(adjustable_type: 'Spree::Shipment') } +scope :is_included, -> { where(included: true) } +scope :additional, -> { where(included: false) } +``` + +* `open`: All open adjustments. +* `tax`: All adjustments which have a source that is a `Spree::TaxRate` object +* `price`: All adjustments which adjust a `Spree::LineItem` object. +* `shipping`: All adjustments which adjust a `Spree::Shipment` object. +* `promotion`: All adjustments where the source is a `Spree::PromotionAction` object. +* `optional`: All adjustments which are not `mandatory`. +* `return_authorization`: All adjustments where the source is a `Spree::ReturnAuthorization`. +* `eligible`: Adjustments which have been determined to be `eligible` for their adjustable. Useful for determining which adjustments are applying to the adjustable. +* `charge`: Adjustments which _increase_ the price of their adjustable. +* `credit`: Adjustments which _decrease_ the price of their adjustable. +* `included`: Adjustments which are included in the object's price. Typically tax adjustments. +* `additional`: Adjustments which modify the object's price. The default for all adjustments. + +These scopes can be called on either the `Spree::Adjustment` class itself, or on an `adjustments` association. For example, calling any one of these three is valid: + +```ruby +Spree::Adjustment.eligible +order.adjustments.eligible +line_item.adjustments.eligible +shipment.adjustments.eligible +``` + +## Adjustment Associations + +As of Spree 2.2, you are able to retrieve the specific adjustments of an Order, a Line Item or a Shipment. + +An order itself, much like line items and shipments, can have its own individual modifications. For instance, an order with over $100 of line items may have 10% off. To retrieve these adjustments on the order, call the `adjustments` association: + +```ruby +order.adjustments +``` + +If you want to retrieve all the adjustments for all the line items, shipments and the order itself, call the `all_adjustments` method: + +```ruby +order.all_adjustments +``` + +If you want to grab just the line item adjustments, call `line_item_adjustments`: + +```ruby +order.line_item_adjustments +``` + +Similarly, if you want to grab the adjustments applied to shipments, call `shipment_adjustments`: + +```ruby +order.shipment_adjustments +``` + +## Extending Adjustments + +### Creating a New Adjuster + +To create a new adjuster for Spree, create a new ruby object that inherits from `Spree::Adjustable::Adjuster::Base` and implements an `update` method: + +```ruby +module Spree + module Adjustable + module Adjuster + class MyAdjuster < Spree::Adjustable::Adjuster::Base + def update + ... + #your ruby magic + ... + update_totals(some_total, my_other_total) + end + + private + + # Note to persist your totals you need to update @totals + # This is shown in a separate method for readability + def update_totals(some_total, my_other_total) + # if you want to keep track of your total, + # you will need the column defined + @totals[:total_you_want_to_track] += some_total + @totals[:taxable_adjustment_total] += some_total + @totals[:non_taxable_adjustment_total] += my_other_total + end + end + end + end +end +``` + +Next you need to add the class to spree `Rails.application.config.spree.adjusters` so it is included whenever adjustments are updated \(Promotion and Tax are included by default\): + +```ruby +# NOTE: it is advisable that that Tax be implemented last so Tax is calculated correctly +app.config.spree.adjusters = [ + Spree::Adjustable::Adjuster::MyAdjuster, + Spree::Adjustable::Adjuster::Promotion, + Spree::Adjustable::Adjuster::Tax + ] +``` + +That's it! Your custom adjuster is ready to go. + diff --git a/internals/calculators.md b/internals/calculators.md new file mode 100644 index 0000000..f70ca37 --- /dev/null +++ b/internals/calculators.md @@ -0,0 +1,250 @@ +--- +title: Calculators +section: internals +order: 7 +--- + +# Calculators + +## Overview + +Spree makes extensive use of the `Spree::Calculator` model and there are several subclasses provided to deal with various types of calculations \(flat rate, percentage discount, sales tax, VAT, etc.\) All calculators extend the `Spree::Calculator` class and must provide the following methods: + +```ruby +def self.description + # Human readable description of the calculator +end + +def compute(object=nil) + # Returns the value after performing the required calculation +end +``` + +Calculators link to a `calculable` object, which are typically one of `Spree::ShippingMethod`, `Spree::TaxRate`, or `Spree::Promotion::Actions::CreateAdjustment`. These three classes use the `Spree::Core::CalculatedAdjustment` module described below to provide an easy way to calculate adjustments for their objects. + +## Available Calculators + +The following are descriptions of the currently available calculators in Spree. If you would like to add your own, please see the [Creating a New Calculator](calculators.md#creating-a-new-calculator) section. + +### Default Tax + +For information about this calculator, please read the [Taxation](/developer/internals/taxation.html) guide. + +### Flat Percent Per Item Total + +This calculator has one preference: `flat_percent` and can be set like this: + +```ruby +calculator.preferred_flat_percent = 10 +``` + +This calculator takes an order and calculates an amount using this calculation: + +```ruby +[item total] x [flat percentage] +``` + +For example, if an order had an item total of $31 and the calculator was configured to have a flat percent amount of 10, the discount would be$3.10, because $31 x 10% =$3.10. + +### Flat Rate + +This calculator can be used to provide a flat rate discount. + +This calculator has two preferences: `amount` and `currency`. These can be set like this: + +```ruby +calculator.preferred_amount = 10 +calculator.currency = "USD" +``` + +The currency for this calculator is used to check to see if a shipping method is available for an order. If an order's currency does not match the shipping method's currency, then that shipping method will not be displayed on the frontend. + +This calculator can take any object and will return simply the preferred amount. + +### Flexi Rate + +This calculator is typically used for promotional discounts when you want a specific discount for the first product, and then subsequent discounts for other products, up to a certain amount. + +This calculator takes three preferences: + +* `first_item`: The discounted price of the first item\(s\). +* `additional_item`: The discounted price of subsequent items. +* `max_items`: The maximum number of items this discount applies to. + +The calculator computes based on this: + +```text +[first item discount] + (([items_count*] - 1) x [additional item discount]) +``` + +* up to the `max_items` + +Thus, if you have ten items in your shopping cart, your `first_item` preference is set to $10, your `additional_items` preference is set to $5, and your `max_items` preference is set to 4, the total discount would be$25: + +* $10 for the first item +* $5 for each of the 3 subsequent items:$5 \* 3 = $15 +* $0 for the remaining 6 items + +### Per Item + +The Per Item calculator computes a value for every item within an order. This is useful for providing a discount for a specific product, without it affecting others. + +This calculator takes two preferences: + +* `amount`: The amount per item to calculate. +* `currency`: The currency for this calculator. + +This calculator depends on its `calculable` responding to a `promotion` method, which should return a `Spree::Promotion` \(or similar\) object. This object should then return a list of rules, which should respond to a `products` method. This is used to return a result of matching products. + +The list of matching products is compared against the line items for the order being calculated. If any of the matching products are included in the order, they are eligible for this calculator. The calculation is this: + +\[matching product quantity\] x \[amount\] + +Every matching product within an order will add to the calculator's total. For example, assuming the calculator has an `amount` of 5 and there's an order with the following line items: + +* Product A: $15.00 x 2 \(within matching products\) +* Product B: $10.00 x 1 \(within matching products\) +* Product C: $20.00 x 4 \(excluded from matching products\) + +The calculation would be: + +```text += (2 x 5) + (1 x 5) += 10 + 5 +``` + +meaning the calculator will compute an amount of 15. + +### Percent Per Item + +The Percent Per Item calculator works in a near-identical fashion to the [Per Item Calculator](calculators.md#per-item), with the exception that rather than providing a flat-rate per item, it is a percentage. + +Assuming a calculator amount of 10% and an order such as this: + +* Product A: $15.00 x 2 \(within matching products\) +* Product B: $10.00 x 1 \(within matching products\) +* Product C: $20.00 x 4 \(excluded from matching products\) + +The calculation would be: + +```text += ($15 x 2 x 10%) + ($10 x 10%) += ($30 x 10%) + ($10 x 10%) += $3 + $1 +``` + +The calculator will calculate a discount of $4. + +### Price Sack + +The Price Sack calculator is useful for when you want to provide a discount for an order which is over a certain price. The calculator has four preferences: + +* `minimal_amount`: The minimum amount for the line items total to trigger the calculator. +* `discount_amount`: The amount to discount from the order if the line items total is equal to or greater than the `minimal_amount`. +* `normal_amount`: The amount to discount from the order if the line items total is less than the `minimal_amount`. +* `currency`: The currency for this calculator. Defaults to the currency you have set for your application with `Spree::Config[:currency]` + +Suppose you have a Price Sack calculator with a `minimal_amount` preference of $50, a `normal_amount` preference of $2, and a `discount_amount` of$5. An order with a line items total of $60 would result in a discount of$5 for the whole order. An order of $20 would result in a discount of$2. + +## Creating a New Calculator + +To create a new calculator for Spree, you need to do two things. The first is to inherit from the `Spree::Calculator` class and define `description` and `compute` methods on that class: + +```ruby +class CustomCalculator < Spree::Calculator + def self.description + # Human readable description of the calculator + end + + def compute(object=nil) + # Returns the value after performing the required calculation + end +end +``` + +If you are creating a new calculator for shipping methods, please be aware that you need to inherit from `Spree::ShippingCalculator` instead, and define a `compute_package` method: + +```ruby +class CustomCalculator < Spree::ShippingCalculator + def self.description + # Human readable description of the calculator + end + + def compute_package(package) + # Returns the value after performing the required calculation + end +end +``` + +The second thing is to register this calculator as a tax, shipping, or promotion adjustment calculator by calling code like this at the end of `config/initializers/spree.rb` inside your application \(`config` variable defined for brevity\): + +```ruby +config = Rails.application.config +config.spree.calculators.tax_rates << CustomCalculator +config.spree.calculators.shipping_methods << CustomCalculator +config.spree.calculators.promotion_actions_create_adjustments << CustomCalculator +``` + +For example if your calculator is placed in `app/models/spree/calculator/shipping/my_own_calculator.rb` you should call: + +```ruby +config = Rails.application.config +config.spree.calculators.shipping_methods << Spree::Calculator::Shipping::MyOwnCalculator +``` + +### Determining Availability + +By default, all shipping method calculators are available at all times. If you wish to make this dependent on something from the order, you can re-define the `available?` method inside your calculator: + +```ruby +class CustomCalculator < Spree::Calculator + def available?(object) + object.currency == "USD" + end +end +``` + +## Calculated Adjustments + +If you wish to use Spree's calculator functionality for your own application, you can include the `Spree::Core::CalculatedAdjustments` module into a model of your choosing. + +```ruby +class Plan < ActiveRecord::Base + include Spree::Core::CalculatedAdjustments +end +``` + +To have calculators available for this class, you will need to register them: + +```ruby +config.spree.calculators.plans << CustomCalculator +``` + +Then you can access these calculators by calling this method: + +```ruby +Plan.calculators +``` + +Using this method, you can then display the calculators as you please. Each object for this new class will need to have a calculator associated so that adjustments can be calculated on them. + +This module provides a `has_one` association to a `calculator` object, as well as some convenience helpers for creating and updating adjustments for objects. Assuming that an object has a calculator associated with it first, creating an adjustment is simple: + +```ruby +plan.create_adjustment("#{plan.name}", , ) +``` + +To update this adjustment: + +```ruby +plan.update_adjustment(, ) +``` + +To work out what the calculator would compute an amount to be: + +```ruby +plan.compute_amount() +``` + +`create_adjustment`, `update_adjustment` and `compute_amount` will call `compute` on the `Calculator` object. This `calculable` amount is whatever object your `CustomCalculator` class supports. + diff --git a/internals/inventory.md b/internals/inventory.md new file mode 100644 index 0000000..6a01504 --- /dev/null +++ b/internals/inventory.md @@ -0,0 +1,50 @@ +--- +title: Inventory +section: internals +order: 5 +--- + +# Inventory + +## Overview + +Spree uses a hybrid approach for tracking inventory: On-hand inventory is stored as a count on a variant `StockItem`. This gives good performance for stores with large inventories. Back-ordered, sold, or shipped products are stored as individual `InventoryUnit` objects so they can have relevant information attached to them. + +What if you don't need to track inventory? We have come up with a design that basically shields users of simple stores from much of this complexity. Simply set `Spree::Config[:track_inventory_levels]` to `false` and you never have to worry about it. + +New products created in the system can be given a starting "on hand" inventory level. You can subsequently set new inventory levels and the correct things will happen, e.g. adding new on-hand inventory to an out-of-stock product that has some backorders will first fill the backorders then update the product with the remaining inventory count. + +## Stock Management + +### Stock Locations + +Stock Locations are the locations where your inventory is shipped from. Each `StockLocation` has many `stock_items` and `stock_movements`. + +Stock Locations are created in the admin interface \(Configuration → Stock Locations\). Note that a `StockItem` will be added to the newly created `StockLocation` for each variant in your application. + +### Stock Items + +Stock Items represent the inventory at a stock location for a specific variant. Stock item count on hand can be increased or decreased by creating stock movements. + + Stock items are created automatically for each stock location you have. You don't need to manage these manually. + +### Stock Movements + +Stock movements allow you to manage the inventory of a stock item for a stock location. Stock movements are created in the admin interface by first navigating to the product you want to manage. Then, follow the "Stock Management" link in the sidebar. + +As shown in the image above, you can increase or decrease the count on hand available for a variant at a stock location. To increase the count on hand, make a stock movement with a positive quantity. To decrease the count on hand, make a stock movement with a negative quantity. + +### Stock Transfers + +Stock transfers allow you to move inventory in bulk from one stock location to another stock location. Transfers are created in the admin interface by first navigating to the Configuration page. Then, follow the "Stock Transfers" link. + +As shown in the image above, you can move stock from one location to a different location. This is done by selecting a source location, a destination location, and one or more variants. You are also able to set the quantity for each variant individually. + +If you check "Receive Stock" while creating a new transfer, your stock transfer will only have a destination stock location. + +## Return Authorizations + +After an order is shipped, administrators can approve the return of some part \(maybe all\) of an order via the "Return Authorizations" tab in the single order console. To create a new return authorization, you should indicate which part of the order is being returned, what the reason for the return is, and what the resulting credit should be. The sale price of the product is shown for reference, but you can choose any value you like. + +After the authorization is created, you can return later to its edit page and click on the 'Received' button to register the return of goods. This will create a credit adjustment on the order, which you can apply \(i.e. refund\) to the order's credit card via the payments screen. Spree will log the events in the order's history. + diff --git a/internals/orders.md b/internals/orders.md new file mode 100644 index 0000000..106b710 --- /dev/null +++ b/internals/orders.md @@ -0,0 +1,138 @@ +--- +title: Orders +section: internals +order: 1 +--- + +# Orders + +## Overview + +The `Order` model is one of the key models in Spree. It provides a central place around which to collect information about a customer order - including line items, adjustments, payments, addresses, return authorizations, and shipments. + +Orders have the following attributes: + +* `number`: The unique identifier for this order. It begins with the letter R and ends in a 9-digit number. This number is shown to the users, and can be used to find the order by calling `Spree::Order.find_by(number: number)`. +* `item_total`: The sum of all the line items for this order. +* `adjustment_total`: The sum of all adjustments on this order. +* `total`: The result of the sum of the `item_total` and the `adjustment_total`. +* `payment_total`: The total value of all finalized payments. +* `shipment_total`: The total value of all shipments' costs. +* `additional_tax_total`: The sum of all shipments' and line items' `additional_tax`. +* `included_tax_total`: The sum of all shipments' and line items' `included_tax`. +* `promo_total`: The sum of all shipments', line items' and promotions' `promo_total`. +* `state`: The current state of the order. To read more about the states an order goes through, read [The Order State Machine](orders.md#the-order-state-machine) section of this guide. +* `email`: The email address for the user who placed this order. Stored in case this order is for a guest user. +* `user_id`: The ID for the corresponding user record for this order. Stored only if the order is placed by a signed-in user. +* `completed_at`: The timestamp of when the order was completed. +* `bill_address_id`: The ID for the related `Address` object with billing address information. +* `ship_address_id`: The ID for the related `Address` object with shipping address information. +* `shipping_method_id`: The ID for the related `ShippingMethod` object. +* `created_by_id`: The ID of object that created this order. +* `shipment_state`: The current shipment state of the order. It takes into account all shipments. Described below in [Order Shipment states section](orders.md#order-shipment-states). +* `payment_state`: The current payment state of the order. It takes into account all payments. Described below in [Order Payment states section](orders.md#order-payment-states). +* `special_instructions`: Any special instructions for the store to do with this order. Will only appear if `Spree::Config[:shipping_instructions]` is set to `true`. +* `currency`: The currency for this order. Determined by the `Store#default_currency` currency in which this order was created +* `last_ip_address`: The last IP address used to update this order in the frontend. +* `channel`: The channel specified when importing orders from other stores. e.g. amazon. +* `item_count`: The total value of line items' quantity. +* `approver_id`: The ID of user that approved this order. +* `confirmation_delivered`: Boolean value indicating that confirmation email was delivered. +* `token`: The token stored corresponding to token stored in cookies. In older Spree versions this attribute was called `guest_token` +* `canceler_id`: The ID of user that canceled this order. +* `store_id`: The ID of `Store` in which this order was created. + +Some methods you may find useful: + +* `outstanding_balance`: The outstanding balance for the order, calculated by taking the `total` and subtracting `payment_total`. +* `display_item_total`: A "pretty" version of `item_total`. If `item_total` was `10.0`, `display_item_total` would be `$10.00`. +* `display_adjustment_total`: Same as above, except for `adjustment_total`. +* `display_total`: Same as above, except for `total`. +* `display_outstanding_balance`: Same as above, except for `outstanding_balance`. + +## The Order State Machine + +Orders flow through a state machine, beginning at a `cart` state and ending up at a `complete` state. The intermediary states can be configured using the [Checkout Flow API](/developer/customization/checkout.html). + +The default states are as follows: + +* `cart` - initial state +* `address` - Cart moved to Checkout +* `delivery` - buyer has added Shipping and Billing addresses +* `payment` - buyer selected delivery option +* `confirm` - buyer added payment option \(if required\) +* `complete` - order was placed + +The `payment` state will only be triggered if `payment_required?` returns `true`. + +The `confirm` state will only be triggered if `confirmation_required?` returns `true`. + +The `complete` state can only be reached in one of two ways: + +1. No payment is required on the order. +2. Payment is required on the order, and at least the order total has been received as payment. + +Assuming that an order meets the criteria for the next state, you will be able to transition it to the next state by calling `next` on that object. If this returns `false`, then the order does _not_ meet the criteria. To work out why it cannot transition, check the result of an `errors` method call. + +### Order Shipment states + +Alongside the global Order state there's also `shipment_state` column which indicates the state of all shipments. Order can have multiple shipments. + +* `shipped` - all Shipments are in the `shipped` state +* `partial` - at least one Shipment has a state of `shipped` and there is another Shipment with a state other than `shipped` or there are [InventoryUnits](/developer/internals/inventory.html) associated with the order that have a state of `sold` but are not associated with a Shipment +* `ready` - all Shipments are in the `ready` state +* `backorder` - there is backordered inventory associated with an order +* `pending` - all Shipments are in the `pending` state + +For more on this please go to [Shipment States page](developer/2_internals/developer/core/shipments.html#overview). + +### Order Payment states + +Alongside the global Order state there's also `payment_state` column which indicates the state of all payments. Order can have multiple payments. + +* `paid` - `payment_total` is equal to `total` +* `balance_due` - `payment_total` is less than `total` +* `credit_owed` - `payment_total` is greater than `total` +* `failed` - most recent payment is in the `failed` state +* `void` - order is canceled and `payment_total` isequal to `zero` + +For more on this please go to [Payment States page](/developer/internals/payments.html#overview). + +### Order Shipment states + +## Line Items + +Line items are used to keep track of items within the context of an order. These records provide a link between orders, and [Variants](developer/2_internals/products.html#variants). + +When a variant is added to an order, the price of that item is tracked along with the line item to preserve that data. If the variant's price were to change, then the line item would still have a record of the price at the time of ordering. + +## Addresses + +An order can link to two `Address` objects. The shipping address indicates where the order's product\(s\) should be shipped to. This address is used to determine which shipping methods are available for an order. + +The billing address indicates where the user who's paying for the order is located. This can alter the tax rate for the order, which in turn can change how much the final order total can be. + +For more information about addresses, please read the [Addresses](/developer/internals/addresses.html) guide. + +## Adjustments + +Adjustments are used to affect an order's final cost, either by decreasing it \([Promotions](/developer/internals/promotions.html)\) or by increasing it \([Shipping](/developer/internals/shipments.html), [Taxes](/developer/internals/taxation.html)\). + +For more information about adjustments, please see the [Adjustments](/developer/internals/adjustments.html) guide. + +## Payments + +Payment records are used to track payment information about an order. For more information, please read the [Payments](/developer/internals/payments.html) guide. + +## Return Authorizations + +An order can have many `ReturnAuthorization` objects. These records keeps track of which items have been authorized for return and how the user will be compensated -- either via exchanging the item\(s\) or a reimbursement. + +## Updating an Order + +If you change any aspect of an `Order` object within code and you wish to update the order's totals -- including associated adjustments and shipments -- call the `update_with_updater!` method on that object, which calls out to the `OrderUpdater` class. + +For example, if you create or modify an existing payment for the order which would change the order's `payment_state` to a different value, calling `update_with_updater!` will cause the `payment_state` to be recalculated for that order. + +Another example is if a `LineItem` within the order had its price changed. Calling `update_with_updater!` will cause the totals for the order to be updated, the adjustments for the order to be recalculated, and then a final total to be established. + diff --git a/internals/payments.md b/internals/payments.md new file mode 100644 index 0000000..bc0aef6 --- /dev/null +++ b/internals/payments.md @@ -0,0 +1,186 @@ +--- +title: Payments +section: internals +order: 3 +--- + +# Payments + +## Overview + +Spree has a highly flexible payments model which allows multiple payment methods to be available during checkout. The logic for processing payments is decoupled from orders, making it easy to define custom payment methods with their own processing logic. + +Payment methods typically represent a payment gateway. Gateways will process card payments, and may also include non-gateway methods of payment such as Check, which is provided in Spree by default. + +The `Payment` model in Spree tracks payments against [Orders](/developer/internals/orders.html). Payments relate to a `source` which indicates how the payment was made, and a `PaymentMethod`, indicating the processor used for this payment. + +When a payment is created, it is given a unique, 8-character identifier. This is used when sending the payment details to the payment processor. Without this identifier, some payment gateways mistakenly reported duplicate payments. + +A payment can go through many different states, as illustrated below. + +An explanation of the different states: + +* `checkout`: Checkout has not been completed +* `processing`: The payment is being processed \(temporary – intended to prevent double submission\) +* `pending`: The payment has been processed but is not yet complete \(ex. authorized but not captured\) +* `failed`: The payment was rejected \(ex. credit card was declined\) +* `void`: The payment should not be counted against the order +* `completed`: The payment is completed. Only payments in this state count against the order total + +The state transition for these is handled by the processing code within Spree; however, you are able to call the event methods yourself to reach these states. The event methods are: + +* `started_processing` +* `failure` +* `pend` +* `complete` +* `void` + +## Payment Methods + +Payment methods represent the different options a customer has for making a payment. Most sites will accept credit card payments through a payment gateway, but there are other options. Spree also comes with built-in support for a Check payment, which can be used to represent any offline payment. There are also third-party extensions that provide support for some other interesting options such as [Spree Braintree Vzero](https://github.com/spree-contrib/spree_braintree_vzero) for Braintree & PayPal payment methods. + +A `PaymentMethod` can have the following attributes: + +* `type`: The subclass of `Spree::PaymentMethod` this payment method represents. Uses rails single table inheritance feature. +* `name`: The visible name for this payment method +* `description`: The description for this payment method +* `active`: Whether or not this payment method is active. Set it `false` to hide it in frontend. +* `display_on`: Determines where the payment method can be visible. Values can be `front` for frontend, `back` for backend or `both` for both. + +### Payment Method Visibility + +The appearance of the payment methods on the frontend and backend depend on several criteria used by the `PaymentMethod.available` method. The code is this: + +```ruby +def self.available(display_on = 'both') + all.select do |p| + p.active && + (p.display_on == display_on.to_s || p.display_on.blank?) + end +end +``` + +If a payment method meets these criteria, then it will be available. + +### Auto-Capturing + +By default, a payment method's `auto_capture?` method depends on the `Spree::Config[:auto_capture]` preference. If you have set this preference to `true`, but don't want a payment method to be auto-capturable like other payment methods in your system, you can override the `auto_capture?` method in your `PaymentMethod` subclass: + +```ruby +class FancyPaymentMethod < Spree::PaymentMethod + def auto_capture? + false + end +end +``` + +The result of this method determines if a payment will be automatically captured \(true\) or only authorized \(false\) during the processing of the payment. + +## Payment Processing + +Payment processing in Spree supports many different gateways, but also attempts to comply with the API provided by the [active\_merchant](https://github.com/shopify/active_merchant) gem where possible. + +### Gateway Options + +For every gateway action, a list of gateway options are passed through. + +* `email` and `customer`: The email address related to the order +* `ip`: The last IP address for the order +* `order_id`: The Order's `number` attribute, plus the `identifier` for each payment, generated when the payment is first created +* `shipping`: The total shipping cost for the order, in cents +* `tax`: The total tax cost for the order, in cents +* `subtotal`: The item total for the order, in cents +* `currency`: The 3-character currency code for the order +* `discount`: The promotional discount applied to the order +* `billing_address`: A hash containing billing address information +* `shipping_address`: A hash containing shipping address information + +The billing address and shipping address data is as follows: + +* `name`: The combined `first_name` and `last_name` from the address +* `address1`: The first line of the address information +* `address2`: The second line of address information +* `city`: The city of the address +* `state`: An abbreviated version of the state name or, failing that, the state name itself, from the related `State` object. If that fails, the `state_name` attribute from the address. +* `country`: The ISO name for the country. For example, United States of America is "US", Australia is "AU". +* `phone`: The phone number associated with the address + +### Credit Card Data + +Spree stores only the type, expiration date, name and last four digits for the card on your server. This data can then be used to present to the user so that they can verify that the correct card is being used. All credit card data sent through forms is sent through immediately to the gateways, and is not stored for any period of time. + +### Processing Walkthrough + +When an order is completed in spree, each `Payment` object associated with the order has the `process!` method called on it \(unless `payment_required?` for the order returns `false`\), in order to attempt to automatically fulfill the payment required for the order. If the payment method requires a source, and the payment has a source associated with it, then Spree will attempt to process the payment. Otherwise, the payment will need to be processed manually. + +If the `PaymentMethod` object is configured to auto-capture payments, then the `Payment#purchase!` method will be called, which will call `PaymentMethod#purchase` like this: + +```ruby +payment_method.purchase(, , ) +``` + +If the payment is _not_ configured to auto-capture payments, the `Payment#authorize!` method will be called, with the same arguments as the `purchase` method above: + +```ruby +payment_method.authorize(, , ) +``` + +How the payment is actually put through depends on the `PaymentMethod` sub-class' implementation of the `purchase` and `authorize` methods. + +The returned object from both the `purchase` and `authorize` methods on the payment method objects must be an `ActiveMerchant::Billing::Response` object. This response object is then stored \(in YAML\) in the `spree_log_entries` table. Log entries can be retrieved with a call to the `log_entries` association on any `Payment` object. + +If the `purchase!` route is taken and is successful, the payment is marked as `completed`. If it fails, it is marked as `failed`. If the `authorize` method is successful, the payment is transitioned to the `pending` state so that it can be manually captured later by calling the `capture!` method. If it is unsuccessful, it is also transitioned to the `failed` state. + + Once a payment has been saved, it also updates the order. This may trigger the \`payment\_state\` to change, which would reflect the current payment state of the order. The possible states are: \* \`balance\_due\`: Indicates that payment is required for this order \* \`failed\`: Indicates that the last payment for the order failed \* \`credit\_owed\`: This order has been paid for in excess of its total \* \`paid\`: This order has been paid for in full. + + You may want to keep tabs on the number of orders with a \`payment\_state\` of \`failed\`. A sudden increase in the number of such orders could indicate a problem with your credit card gateway and most likely indicates a serious problem affecting customer satisfaction. You should check the latest \`log\_entries\` for the most recent payments in the store if this is happening. + +### Log Entries + +Responses from payment gateways within Spree are typically `ActiveMerchant::Billing::Response` objects. When Spree handles a response from a payment gateway, it will serialize the object as YAML and store it in the database as a log entry for a payment. These responses can be useful for debugging why a payment has failed. + +You can get a list of these log entries by calling the `log_entries` on any `Spree::Payment` object. To get the `Active::Merchant::Billing::Response` out of these `Spree::LogEntry` objects, call the `details` method. + +## Supported Gateways + +Access to a number of payment gateways is handled with the usage of the [Spree Gateway](https://github.com/spree/spree_gateway) extension. This extension currently supports the following gateways: + +* Authorize.net +* Apple Pay \(via Stripe\) +* BanWire +* Bambora \(previously Beanstream\) +* Braintree +* CyberSource +* ePay +* eWay +* maxipago +* MasterCard Payment Gateway Service \(formerly MiGS\) +* Moneris +* PayJunction +* Payflow +* Paymill +* Pin Payments +* QuickPay +* sage Pay +* SecurePay +* Spreedly +* Stripe \(with Stripe Elements\) +* USAePay +* Worldpay \(previously Cardsave\) + +With the `spree_gateway` gem included in your application's `Gemfile`, these gateways will be selectable in the admin backend for payment methods. + + These are just some of the gateways which are supported by the Active Merchant gem. You can see a \[list of all the Active Merchant gateways on that project's GitHub page\]\(https://github.com/activemerchant/active\_merchant\#supported-payment-gateways\). + +In order to implement a new gateway in the spree\_gateway project, please refer to the other gateways within `app/models/spree/gateway` inside that project. + +## Adding your custom gateway + +In order to make your custom gateway show up on backend list of available payment methods you need to add it to spree config list of payment methods first. That can be achieved by adding the following code in your spree.rb for example: + +```ruby +Rails.application.config.spree.payment_methods << YourCustomGateway +``` + +[Spree Braintree Vzero](https://github.com/spree-contrib/spree_braintree_vzero) is a good example of a standalone custom gateways. + diff --git a/internals/preferences.md b/internals/preferences.md new file mode 100644 index 0000000..3e64d2f --- /dev/null +++ b/internals/preferences.md @@ -0,0 +1,434 @@ +--- +title: Preferences +section: internals +--- + +# Preferences + +## Overview + +Spree Preferences support general application configuration and preferences per model instance. Spree comes with preferences for your store like `logo` and `currency`. Additional preferences can be added by your application or included extensions. + +To implement preferences for a model, simply add a new column called `preferences`. This is an example migration for the `spree_products` table: + +```ruby +class AddPreferencesColumnToSpreeProducts < ActiveRecord::Migration[4.2] + def change + add_column :spree_products, :preferences, :text + end +end +``` + +This will work because `Spree::Product` is a subclass of `Spree::Base`. If found, the `preferences` attribute gets serialized into a `Hash` and merged with the default values. + +As another example, you might want to add preferences for users to manage their notification settings. Just make sure your `User` model inherits from `Spree::Base` then add the `preferences` column. You'll then be able to define preferences for `User`s without adding extra columns to the database table. + +> If you're using `spree_auth_devise`, note that the provided `Spree::User` doesn't inherit from `Spree::Base`. + +Extensions may add to the Spree General Settings or create their own namespaced preferences. + +The first several sections of this guide describe preferences in a very general way. If you're just interested in making modifications to the existing preferences, you can skip ahead to the [Configuring Spree Preferences section](preferences.md#configuring-spree-preferences). If you would like a more in-depth understanding of the underlying concepts used by the preference system, please read on. + +### Motivation + +Preferences for models within an application are very common. Although the rule of thumb is to keep the number of preferences available to a minimum, sometimes it's necessary if you want users to have optional preferences like disabling e-mail notifications. + +Both use cases are handled by Spree Preferences. They are easy to define, provide quick cached reads, persist across restarts and do not require additional columns to be added to your models' tables. + +## General Settings + +Spree comes with many application-wide preferences. They are defined in `core/app/models/spree/app_configuration.rb` and made available to your code through `Spree::Config`, e.g., `Spree::Config.logo`. + +A limited set of the general settings are available in the admin interface of your store \(`/admin/general_settings`\). + +You can add additional preferences under the `spree/app_configuration` namespace or create your own subclass of `Preferences::Configuration`. + +```ruby +# These will be saved with key: spree/app_configuration/hot_salsa +Spree::AppConfiguration.class_eval do + preference :hot_salsa, :boolean + preference :dark_chocolate, :boolean, default: true + preference :color, :string + preference :favorite_number + preference :language, :string, default: 'English' +end + +# Spree::Config is an instance of Spree::AppConfiguration +Spree::Config.hot_salsa = false + +# Create your own class +# These will be saved with key: kona/store_configuration/hot_coffee +Kona::StoreConfiguration < Preferences::Configuration + preference :hot_coffee, :boolean + preference :color, :string, default: 'black' +end + +KONA::STORE_CONFIG = Kona::StoreConfiguration.new +puts KONA::STORE_CONFIG.hot_coffee +``` + +## Defining Preferences + +You can define preferences for a model within the model itself: + +```ruby +class User < ActiveRecord::Base + preference :hot_salsa, :boolean + preference :dark_chocolate, :boolean, default: true + preference :color, :string + preference :favorite_number, :integer + preference :language, :string, default: "English" +end +``` + +In the above model, five preferences have been defined: + +* `hot_salsa` +* `dark_chocolate` +* `color` +* `favorite_number` +* `language` + +For each preference, a data type is provided. The types available are: + +* `boolean` +* `string` +* `password` +* `integer` +* `text` +* `array` +* `hash` + +An optional default value may be defined which will be used unless a value has been set for that specific instance. + +## Accessing Preferences + +Once preferences have been defined for a model, they can be accessed either using the shortcut methods that are generated for each preference or the generic methods that are not specific to a particular preference. + +### Shortcut Methods + +There are several shortcut methods that are generated. They are shown below. + +Query methods: + +```ruby +user.prefers_hot_salsa? # => false +user.prefers_dark_chocolate? # => false +``` + +Reader methods: + +```ruby +user.preferred_color # => nil +user.preferred_language # => "English" +``` + +Writer methods: + +```ruby +user.prefers_hot_salsa = false # => false +user.preferred_language = "English" # => "English" +``` + +Check if a preference is available: + +```ruby +user.has_preference? :hot_salsa +``` + +### Generic Methods + +Each shortcut method is essentially a wrapper for the various generic methods shown below: + +Query method: + +```ruby +user.prefers?(:hot_salsa) # => false +user.prefers?(:dark_chocolate) # => false +``` + +Reader methods: + +```ruby +user.preferred(:color) # => nil +user.preferred(:language) # => "English" +``` + +```ruby +user.get_preference :color +user.get_preference :language +``` + +Writer method: + +```ruby +user.set_preference(:hot_salsa, false) # => false +user.set_preference(:language, "English") # => "English" +``` + +### Accessing All Preferences + +You can get a hash of all stored preferences by accessing the `preferences` helper: + +```ruby +user.preferences # => {"language"=>"English", "color"=>nil} +``` + +This hash will contain the value for every preference that has been defined for the model instance, whether the value is the default or one that has been previously stored. + +### Default and Type + +You can access the default value for a preference: + +```ruby +user.preferred_color_default # => 'blue' +``` + +Types are used to generate forms or display the preference. You can also get the type defined for a preference: + +```ruby +user.preferred_color_type # => :string +``` + +## Configuring Spree Preferences + +Up until now we've been discussing the general preference system that was adapted to Spree. This has given you a general idea of what types of preference features are theoretically supported. Now, let's start to look specifically at how Spree is using these preferences for configuration. + +### Reading the Current Preferences + +At the heart of Spree preferences lies the `Spree::Config` constant. This object provides general access to the configuration settings anywhere in the application. + +These settings can be accessed from initializers, models, controllers, views, etc. + +The `Spree::Config` constant returns an instance of `Spree::AppConfiguration` which is where the default values for all of the general Spree preferences are defined. + +You can access these preferences directly in code. To see this in action, just fire up `rails console` and try the following: + +```ruby +>> Spree::Config.admin_interface_logo +=> "logo/spree_50.png" +>> Spree::Config.admin_products_per_page +=> 10 +``` + +The above examples show the default configuration values for these preferences. The defaults themselves are coded within the `Spree::AppConfiguration` class. + +```ruby +class Spree::AppConfiguration < Configuration + #... snip ... + preference :allow_guest_checkout, :boolean, default: true + #... snip ... +end +``` + +If you are using the default preferences without any modifications, then nothing will be stored in the database. If you set a value for the preference it will save it to `spree_preferences` or in our `preferences` column. It will use a memory cached version to maintain performance. + +### Overriding the Default Preferences + +The default Spree preferences in `Spree::AppConfiguration` can be changed using the `set` method of the `Spree::Config` module. For example to set the number of products shown on the products listing in the admin interface we could do the following: + +```ruby +>> Spree::Config.admin_products_per_page = 20 +=> 20 +>> Spree::Config.admin_products_per_page +=> 20 +``` + +Here we are changing a preference to something other than the default as specified in `Spree::AppConfiguration`. In this case the preference system will persist the new value in the `spree_preferences` table. + +### Configuration Through the Spree Initializer + +During the Spree installation process, an initializer file is created within your application's source code. The initializer is found under `config/initializers/spree.rb`: + +```ruby +Spree.config do |config| + # Example: + # Uncomment to override the default logo location. + # config.logo = 'logo/my_store.png' +end +``` + +The `Spree.config` block acts as a shortcut to setting `Spree::Config` multiple times. If you have multiple default preferences you would like to override within your code you may override them here. Using the initializer for setting the defaults is a nice shortcut, and helps keep your preferences organized in a standard location. + +For example if you would like to change the logo location and if you want to tax using the shipping address you can accomplish this by doing the following: + +```ruby +Spree.config do |config| + config.logo = 'logo/my_store.png' + config.tax_using_ship_address = true +end +``` + +Initializing preferences in `config/initializers/spree.rb` will overwrite any changes that were made through the admin user interface when you restart. + +### Configuration Through the Admin Interface + +The Spree admin interface has several different screens where various settings can be configured. For instance, the `admin/general_settings` URL in your Spree application can be used to configure the values for the site name and the site URL. This is basically equivalent to calling `Spree::Config.set(currency: "CDN", currency_thousands_separator: " ")` directly in your Ruby code. + +## Site-Wide Preferences + +You can define preferences that are site-wide and don't apply to a specific instance of a model by creating a configuration file that inherits from `Spree::Preferences::Configuration`. + +```ruby +class Spree::MyApplicationConfiguration < Spree::Preferences::Configuration + preference :theme, :string, default: "Default" + preference :show_splash_page, :boolean + preference :number_of_articles, :integer +end +``` + +In the above configuration file, three preferences have been defined: + +* theme +* show\_splash\_page +* number\_of\_articles + +It is recommended to create the configuration file in the `lib/` directory. + +Extensions can also define site-wide preferences. For more information on using preferences like this with extensions, check out the [Extensions Tutorial](developer/2_internals/developer/contributing/extensions_tutorial.html). + +### Configuring Site-Wide Preferences + +The recommended way to configure site-wide preferences is through an initializer. Let's take a look at configuring the preferences defined in the previous configuration example. + +```ruby +module Spree + MyApp::Config = Spree::MyApplicationConfiguration.new +end + +MyApp::Config[:theme] = "blue_theme" +MyApp::Config[:show_spash_page] = true +MyApp::Config[:number_of_articles] = 5 +``` + +The `MyApp` name used here is an example and should be replaced with your actual application's name, found in `config/application.rb`. + +The above example will configure the preferences we defined earlier. Take note of the second line. In order to set and get preferences using `MyApp::Config`, we must first instantiate the configuration object. + +## Spree Configuration Options + +This section lists all of the configuration options for the current version of Spree. + +`address_requires_state` + +Will determine if the state field should appear on the checkout page. Defaults to `true`. + +`admin_interface_logo` + +The path to the logo to display on the admin interface. Can be different from `Spree::Config[:logo]`. Defaults to `logo/spree_50.png`. + +`admin_products_per_page` + +How many products to display on the products listing in the admin interface. Defaults to 30. + +`allow_checkout_on_gateway_error` + +Continues the checkout process even if the payment gateway error failed. Defaults to `false`. + +`alternative_shipping_phone` + +Determines if an alternative phone number should be present for the shipping address on the checkout page. Defaults to `false`. + +`always_put_site_name_in_title` + +Determines if the site name \(`current_store.site_name`\) should be placed into the title. Defaults to `true`. + +`attachment_default_url` + +Tells `Paperclip` the form of the URL to use for attachments which are missing. + +`attachment_path` + +Tells `Paperclip` the path at which to store images. + +`attachment_styles` + +A JSON hash of different styles that are supported by attachments. Defaults to: + +```javascript +{ + "mini":"48x48>", + "small":"100x100>", + "product":"240x240>", + "large":"600x600>" +} +``` + +`attachment_default_style` + +A key from the list of styles from `Spree::Config[:attachment_styles]` that is the default style for images. Defaults to the the `product` style. + +`auto_capture` + +Depending on whether or not Spree is configured to "auto capture" the credit card, either a purchase or an authorize operation will be performed on the card \(via the current credit card gateway\). Defaults to `false`. + +`checkout_zone` + +Limits the checkout to countries from a specific zone, by name. Defaults to `nil`. + +`company` + +Determines whether or not a field for "Company" displays on the checkout pages for shipping and billing addresses. Defaults to `false`. + +`currency` + +The three-letter currency code for the currency that prices will be displayed in. Defaults to "USD". + +`default_country_id` + +The default country's id. Defaults to 214, as this is the id for the United States within the seed data. + +`layout` + +The path to the layout of your application, relative to the `app/views` directory. Defaults to `spree/layouts/spree_application`. To make Spree use your application's layout rather than Spree's default, use this: + +```ruby +Spree.config do |config| + config.layout = "application" +end +``` + +`max_level_in_taxons_menu` + +The number of levels to descend when viewing a taxon menu. Defaults to `1`. + +`admin_orders_per_page` + +The number of orders to display on the orders listing in the admin backend. Defaults to `30`. + +`prices_inc_tax` + +Determines if prices are labelled as including tax or not. Defaults to `false`. + +`shipment_inc_vat` + +Determines if shipments should include VAT calculations. Defaults to `false`. + +`shipping_instructions` + +Determines if shipping instructions are requested or not when checking out. Defaults to `false`. + +`show_descendents` + +Determines if taxon descendants are shown when showing taxons. Defaults to `true`. + +`show_only_complete_orders_by_default` + +Determines if, on the admin listing screen, only completed orders should be shown. Defaults to `true`. + +`show_variant_full_price` + +Determines if the variant's full price or price difference from a product should be displayed on the product's show page. Defaults to `false`. + +`show_store_selector` + +Display the Store selector in the main nav bar of Storefront and allow users to change Store and Currency. Defaults to `false`. + +`tax_using_ship_address` + +Determines if tax information should be based on shipping address, rather than the billing address. Defaults to `true`. + +`track_inventory_levels` + +Determines if inventory levels should be tracked when products are purchased at checkout. This option causes new `InventoryUnit` objects to be created when a product is bought. Defaults to `true`. + diff --git a/internals/products.md b/internals/products.md new file mode 100644 index 0000000..c1fe975 --- /dev/null +++ b/internals/products.md @@ -0,0 +1,162 @@ +--- +title: Products +section: internals +order: 2 +--- + +# Products + +## Overview + +`Product` records track unique products within your store. These differ from [Variants](products.md#variants), which track the unique variations of a product. For instance, a product that's a T-shirt would have variants denoting its different colors. Together, Products and Variants describe what is for sale. + +Products have the following attributes: + +* `name`: short name for the product +* `description`: The most elegant, poetic turn of phrase for describing your product's benefits and features to your site visitors +* `slug`: An SEO slug based on the product name that is placed into the URL for the product +* `available_on`: The first date the product becomes available for sale online in your shop. If you don't set the `available_on` attribute, the product will not appear among your store's products for sale. +* `discontinue_on`: Date when the product will become unavailable for sale online in your shop +* `deleted_at`: The date the product is marked as deleted +* `meta_title`: Optional title used for search engines instead of `name` +* `meta_description`: A description targeted at search engines for search engine optimization \(SEO\) +* `meta_keywords`: Several words and short phrases separated by commas, also targeted at search engines + +To understand how variants come to be, you must first understand option types and option values. + +## Option Types and Option Values + +Option types denote the different options for a variant. A typical option type would be a size, with that option type's values being something such as "Small", "Medium" and "Large". Another typical option type could be a color, such as "Red", "Green", or "Blue". + +A product can be assigned many option types, but must be assigned at least one if you wish to create variants for that product. + +## Variants + +`Variant` records track the individual variants of a `Product`. Variants are of two types: master variants and normal variants. + +Variant records can track some individual properties regarding a variant, such as height, width, depth, and cost price. These properties are unique to each variant, and so are different from [Product Properties](products.md#product-properties), which apply to all variants of that product. + +### Master Variants + +Every single product has a master variant, which tracks basic information such as a count on hand, a price and a SKU. Whenever a product is created, a master variant for that product will be created too. + +Master variants are automatically created along with a product and exist for the sole purpose of having a consistent API when associating variants and [line items](developer/2_internals/orders.html#line-items). If there were no master variant, then line items would need to track a polymorphic association which would either be a product or a variant. + +By having a master variant, the code within Spree to track is simplified. + +### Normal Variants + +Variants which are not the master variant are unique based on [option type and option value](products.md#option_type) combinations. For instance, you may be selling a product which is a Baseball Jersey, which comes in the sizes "Small", "Medium" and "Large", as well as in the colors of "Red", "Green" and "Blue". For this combination of sizes and colors, you would be able to create 9 unique variants: + +* Small, Red +* Small, Green +* Small, Blue +* Medium, Red +* Medium, Green +* Medium, Blue +* Large, Red +* Large, Green +* Large, Blue + +### Default Variant + +To simplify things you can call `product.default_variant` to get the default Variant. If product has multiple Variants it will return the first non-master Variant based on their sort position set in the Admin Panel. If there's no non-master Variants it will return the Master Variant. + +## Images + +Images link to a product through its master variant. The sub-variants for the product may also have their own unique images to differentiate them in the frontend. + +Spree automatically handles creation and storage of several size versions of each image \(via Active Storage\). See [Images Customization](/developer/customization/images.html) section. + +## Product Properties + +Product properties track individual attributes for a product which don't apply to all products. These are typically additional information about the item. For instance, a T-Shirt may have properties representing information about the kind of material used, as well as the type of fit the shirt is. + +A `Property` should not be confused with an [`OptionType`](products.md#option_type), which is used when defining [Variants](products.md#variants) for a product. + +You can retrieve the value for a property on a `Product` object by calling the `property` method on it and passing through that property's name: + +```bash +product.property("material") +=> "100% Cotton" +``` + +You can set a property on a product by calling the `set_property` method: + +```ruby +product.set_property("material", "100% cotton") +``` + +If this property doesn't already exist, a new `Property` instance with this name will be created. + +## Multi-Currency Support + +`Price` objects track a price for a particular currency and variant combination. For instance, a [Variant](products.md#variants) may be available for $15 \(15 USD\) and €7 \(7 Euro\). + +Spree behind the scenes uses [Ruby Money gem](https://github.com/RubyMoney/money) with some [additional](https://github.com/spree/spree/blob/master/core/app/models/concerns/spree/display_money.rb) [tweaks](https://github.com/spree/spree/blob/master/core/lib/spree/money.rb). + +This presence or lack of a price for a variant in a particular currency will determine if that variant is visible in the frontend if this currency is selected. If no variants of a product have a particular price value for the store current currency, that product will not be visible in the frontend. Frontend currency is determined by `default_currency` value in `Spree::Store` model. + +You may see what price a product would be in the application default currency \(`Spree::Config[:currency]`\) by calling the `price` method on that instance: + +```bash +product.price +=> "15.99" +``` + +To get the price with the currency symbol please use `display_price` method: + +```bash +product.display_price +=> "$15.99" +``` + +To find a list of currencies that this product is available in, call `prices` to get a list of related `Price` objects: + +```bash +product.prices +=> [# [#1345, + "USPS First-Class Mail International Large Envelope"=>376, + "USPS USPS GXG Envelopes"=>4295, + "USPS Express Mail International Flat Rate Envelope"=>2895, + "USPS First-Class Mail International Package"=>396, + "USPS Priority Mail International Medium Flat Rate Box"=>4345, + "USPS Priority Mail International"=>2800, + "USPS Priority Mail International Large Flat Rate Box"=>5595, + "USPS Global Express Guaranteed Non-Document Non-Rectangular"=>4295, + "USPS Global Express Guaranteed Non-Document Rectangular"=>4295, + "USPS Global Express Guaranteed (GXG)"=>4295, + "USPS Express Mail International"=>2895, + "USPS Priority Mail International Small Flat Rate Box"=>1345 +} +``` + +From all of the viable shipping services in this hash, the `compute` method selects the one that matches the description of the calculator. At this point, an optional flat handling fee \(set via preferences\) can be added: + +```ruby +rate = rates[self.description].to_f + (Spree::ActiveShipping::Config[:handling_fee].to_f || 0.0) +``` + +Finally, don't forget to register the calculator you added. In extensions, this is accomplished with the `activate` method: + +```ruby +def activate + Calculator::Usps::FirstClassMailInternationalParcels.register +end +``` + +## Filtering Shipping Methods On Criteria Other Than the Zone + +Ordinarily, it is the zone of the shipping address that determines which shipping methods are displayed to a customer at checkout. Here is how the availability of a shipping method is determined: + +```ruby +class Spree::Stock::Estimator + def shipping_methods(package) + shipping_methods = package.shipping_methods + shipping_methods.delete_if { |ship_method| !ship_method.calculator.available?(package.contents) } + shipping_methods.delete_if { |ship_method| !ship_method.include?(order.ship_address) } + shipping_methods.delete_if { |ship_method| !(ship_method.calculator.preferences[:currency].nil? || ship_method.calculator.preferences[:currency] == currency) } + shipping_methods + end +end +``` + +Unless overridden, the calculator's `available?` method returns `true` by default. It is, therefore, the zone of the destination address that filters out the shipping methods in most cases. However, in some circumstances it may be necessary to filter out additional shipping methods. + +Consider the case of the USPS First Class domestic shipping service, which is not offered if the weight of the package is greater than 13oz. Even though the USPS API does not return the option for First Class in this instance, First Class will appear as an option in the checkout view with an unfortunate value of 0, since it has been set as a Shipping Method. + +To ensure that First Class shipping is not available for orders that weigh more than 13oz, the calculator's `available?` method must be overridden as follows: + +```ruby +class Calculator::Usps::FirstClassMailParcels < Calculator::Usps::Base + def self.description + "USPS First-Class Mail Parcel" + end + + def available?(order) + multiplier = 1.3 + weight = order.line_items.inject(0) do |weight, line_item| + weight + (line_item.variant.weight ? (line_item.quantity * line_item.variant.weight * multiplier) : 0) + end + #if weight in ounces > 13, then First Class Mail is not available for the order + weight > 13 ? false : true + end +end +``` + +## Split Shipments + +### Introduction + +Split shipments are a feature for more complex Spree applications that require sophisticated shipping and warehouse logic. This includes detailed inventory management and allows for shipping from multiple locations. + +### Creating Proposed Shipments + +This section steps through the basics of what is involved in determining shipments for an order. There are a lot of pieces that make up this process. They are explained in detail in the [Components of Split Shipments](shipments.md#components-of-split-shipments) section of this guide. + +The process of determining shipments for an order is triggered by calling `create_proposed_shipments` on an `Order` object while transitioning to the `delivery` state during checkout. This process will first delete any existing shipments for an order and then determine the possible shipments available for that order. + +`create_proposed_shipments` will initially call `Spree::Stock::Coordinator.new(@order).packages`. This will return an array of packages. In order to determine which items belong in which package when they are being built, Spree uses an object called a `Splitter`, described in more detail [below](shipments.md#the-packer). + +After obtaining the array of available packages, they are converted to shipments on the order object. Shipping rates are determined and inventory units are created during this process as well. + +At this point, the checkout process can continue to the delivery step. + +## Components of Split Shipments + +This section describes the four main components that power split shipments: [The Coordinator](shipments.md#the-coordinator), [The Packer](shipments.md#the-packer), [The Prioritizer](shipments.md#the-prioritizer), and [The Estimator](shipments.md#the-estimator). + +### The Coordinator + +The `Spree::Stock::Coordinator` is the starting point for determining shipments when calling `create_proposed_shipments` on an order. Its job is to go through each `StockLocation` available and determine what can be shipped from that location. + +The `Spree::Stock::Coordinator` will ultimately return an array of packages which can then be easily converted into shipments for an order by calling `to_shipment` on them. + +### The Packer + +A `Spree::Stock::Packer` object is an important part of the `create_proposed_shipments` process. Its job is to determine possible packages for a given StockLocation and order. It uses rules defined in classes known as `Splitters` to determine what packages can be shipped from a `StockLocation`. + +For example, we may have two splitters for a stock location. One splitter has a rule that any order weighing more than 50lbs should be shipped in a separate package from items weighing less. Our other splitter is a catch-all for any item weighing less than 50lbs. So, given one item in an order weighing 60lbs and two items weighing less, the Packer would use the rules defined in our splitters to come up with two separate packages: one containing the single 60lb item, the other containing our other two items. + +#### Default Splitters + +Spree comes with two default splitters which are run in sequence. This means that the first splitter takes the packages array from the order, and each subsequent splitter takes the output of the splitter that came before it. + +Let's take a look at what the default splitters do: + +* **Shipping Category Splitter**: Splits an order into packages based on items' shipping categories. This means that each package will only have items that all belong to the same shipping category. +* **Weight Splitter**: Splits an order into packages based on a weight threshold. This means that each package has a mass weight. If a new item is added to the order and it causes a package to go over the weight threshold, a new package will be created so that all packages weigh less than the threshold. You can set the weight threshold by changing `Spree::Stock::Splitter::Weight.threshold` \(defaults to `150`\) in an initializer. + +#### Custom Splitters + +Note that splitters can be customized, and creating your own can be done with relative ease. By inheriting from `Spree::Stock::Splitter::Base`, you can create your own splitter. + +For an example of a simple splitter, take a look at Spree's [weight based splitter](https://github.com/spree/spree/blob/235e470b242225d7c75c7c4c4c033ee3d739bb36/core/app/models/spree/stock/splitter/weight.rb). This splitter pulls items with a weight greater than 150 into their own shipment. + +After creating your splitter, you need to add it to the array of splitters Spree uses. To do this, add the following to your application's spree initializer `spree.rb` file: + +```ruby +Rails.application.config.spree.stock_splitters << Spree::Stock::Splitter::CustomSplitter +``` + +You can also completely override the splitters used in Spree, rearrange them, etc. To do this, add the following to your `spree.rb` file: + +```ruby +Rails.application.config.spree.stock_splitters = [ + Spree::Stock::Splitter::CustomSplitter, + Spree::Stock::Splitter::ShippingCategory +] +``` + +Or if you don't want to split packages just set the option above to an empty array. e.g. a store with the following configuration in spree.rb won't have any package splitted. + +```ruby +Rails.application.config.spree.stock_splitters = [] +``` + +If you want to add different splitters for each `StockLocation`, you need to decorate the `Spree::Stock::Coordinator` class and override the `splitters` method. + +### The Prioritizer + +A `Spree::Stock::Prioritizer` object will decide which `StockLocation` should ship which package from an order. The prioritizer will attempt to come up with the best shipping situation available to the user. + +By default, the prioritizer will first select packages where the items are on hand. Then it will try to find packages where items are backordered. During this process, the `Spree::Stock::Adjuster` is also used to ensure each package has the correct number of items. + +The prioritizer is also a customization point. If you want to customize which packages should take priority for the order during this process, you can override the `sort_packages` method in `Spree::Stock::Prioritizer`. + +#### Customizing the Adjuster + +The `Adjuster` visits each package in an order and ensures the correct number of items are in each package. To customize this functionality, you need to do two things: + +1. Subclass the [Spree::Stock::Adjuster](https://github.com/spree/spree/blob/master/core/app/models/spree/stock/adjuster.rb) class and override the the `adjust` method to get the desired functionality. +2. Decorate the `Spree::Stock::Coordinator` and override the `prioritize_packages` method, passing in your custom adjuster class to the `Prioritizer` initializer. For example, if our adjuster was called `Spree::Stock::CustomAdjuster`, we would do the following in `app/my_store/spree/stock/coordinator_decorator.rb`: + +```ruby +module MyStore + module Spree + module Stock + module CoordinatorDecorator do + def prioritize_packages(packages) + prioritizer = Prioritizer.new(order, packages, ::Spree::Stock::CustomAdjuster) + prioritizer.prioritized_packages + end + end + end + end +end + +::Spree::Stock::Coordinator.prepend(MyStore::Spree::Stock::CoordinatorDecorator) +``` + +### The Estimator + +The `Spree::Stock::Estimator` loops through the packages created by the packer in order to calculate and attach shipping rates to them. This information is then returned to the user so they can select shipments for their order and complete the checkout process. + diff --git a/internals/stores.md b/internals/stores.md new file mode 100644 index 0000000..7f96d0e --- /dev/null +++ b/internals/stores.md @@ -0,0 +1,65 @@ +--- +title: Stores +section: internals +order: 0 +--- + +# Stores + +## Overview + +The `Store` model is the center of the Spree ecosystem. Each Spree installation can have multiple Stores. Each Store operates on a different domain or subdomain, eg. + +* Store A, `us.example.com` +* Store B, `eu.example.com` +* Store C, `another-brand.com` + +## `current_store` method + +All Spree controllers or any other controllers that includes [Spree::Core::ControllerHelpers::Store](https://github.com/spree/spree/blob/master/core/lib/spree/core/controller_helpers/store.rb) have access to the `current_store` method which returns the currently in use `Spree::Store` object. All parts of Spree \(API v1, API v2, Storefront, Admin Panel\) include this mechanism. This method is also available in views. + +Under the hood `current_store` calls [Store.current](https://github.com/spree/spree/blob/master/core/app/models/spree/store.rb#L36). + +## Default Store + +If the system cannot find any Store that matches the current URL it will fallback to the Default Store. + +You can set the default Store in Admin Panel -> Configurations -> Store. + +To get the default store in code you can call `Spree::Store.default`. + +## Localization and Currency + +Each Store can have different multiple locales and currencies. This configuration is stored in Store model attributes: + +* `default_currency`- this is the default currency this is the default locale/language which will be pre-selected when visiting the store the first time, eg. `USD` +* `supported_currencies` - if there are more than one supported currency visitor will be able to choose which currency they would like to browse your store, eg. `USD,CAD` +* `default_locale` - this is the default locale/language which will be pre-selected when visiting the store the first time, eg. `en` +* `supported_locales`, if there are more than one supported locale visitor will be able to choose which locale they would like to browse your store, eg `en,fr`. Locales are available upon installing [Spree I18n](https://github.com/spree-contrib/spree_i18n) + +## Checkout configuration + +Each Store can be configured to ship to only selected countries. This is achieved via `checkout_zone_id` attribute which holds the ID of the selected [Zone record](/user/configuration/configuring_geography.html). + +Available Shipping Methods on the Checkout are determined based on the [Zone and Shipping Methods configuration](/developer/internals/shipments.html). + +This will also have an effect on what [Shipping / Billing Addresses](/developer/internals/addresses.html) user can add / select during Checkout. Only Addresses from Countries or States available in the selected Zone can be used and will be visible in the User's Address Book. + +## Associated models + +### Orders + +When a user starts a checkout in a selected Store [Order](/developer/internals/orders.html) is associated with that Store. That means that items added to the Cart will be visible only in a selected Store. If a user switches to another Store they will have a separate `Order` record for that Store. Order is created when a User adds the first item to the Cart. This means you can host multiple brands on one single Spree instance. + +### Payment Methods + +Each [Payment Method](/developer/internals/payments.html#payment-methods) can be associated with multiple Stores, eg. you would like to have Stripe and PayPal in Store A, but only Stripe in Store B, and Braintree in Store C. + +### Products + +You can assign which Products + + + + + diff --git a/internals/taxation.md b/internals/taxation.md new file mode 100644 index 0000000..3859c77 --- /dev/null +++ b/internals/taxation.md @@ -0,0 +1,132 @@ +--- +title: Taxation +section: internals +order: 6 +--- + +# Taxation + +## Overview + +Spree represents taxes for an order by using `tax_categories` and `tax_rates`. + +Products within Spree can be linked to Tax Categories, which are then used to influence the taxation rate for the products when they are purchased. One Tax Category can be set to being the default for the entire system, which means that if a product doesn't have a related tax category, then this default tax category would be used. + +A `tax_category` can have many `tax_rates`, which indicate the rate at which the products belonging to a specific tax category will be taxed at. A tax rate links a tax rate to a particular zone \(see [Addresses](addresses.md) for more information about zones\). When an order is placed in a specific zone, any of the products for that order which have a tax zone that matches the order's tax zone will be taxed. + +The standard sales tax policies commonly found in the USA can be modeled as well as Value Added Tax \(VAT\) which is commonly used in Europe. These are not the only types of tax rules that you can model in Spree. Once you obtain a sufficient understanding of the basic concepts you should be able to model the tax rules of your country or jurisdiction. + +Taxation within the United States can get exceptionally complex, with different states, countries and even cities having different taxation rates. If you are shipping interstate within the United States, we would strongly advise you to use the [Spree Avatax](https://github.com/spree-contrib/spree_avatax_official) or [Spree TaxJar](https://github.com/spree-contrib/spree_taxjar) extension so that you get correct tax rates. + +## Tax Categories + +The Spree default is to treat everything as exempt from tax. In order for a product to be considered taxable, it must belong to a tax category. The tax category is a special concept that is specific to taxation. The tax category is normally never seen by the user so you could call it something generic like "Taxable Goods." If you wish to tax certain products at different rates, however, then you will want to choose something more descriptive \(ex. "Clothing."\). + + It can be somewhat tedious to set the tax category for every product. We're currently exploring ways to make this simpler. If you are importing inventory from another source you will likely be writing your own custom Ruby program that automates this process. + +## Tax Rates + +A tax rate is essentially a percentage amount charged based on the sales price. Tax rates also contain other important information. + +* Whether product prices are inclusive of this tax +* The zone in which the order address must fall within +* The tax category that a product must belong to in order to be considered taxable. + +Spree will calculate tax based on the best matching zone for the order. It's also possible to have more than one applicable tax rate for a single zone. In order for a tax rate to apply to a particular product, that product must have a tax category that matches the tax category of the tax rate. + +## Basic Examples + +Let's say you need to charge 5% tax for all items that ship to New York and 6% on only clothing items that ship to Pennsylvania. This will mean you need to construct two different zones: one zone containing just the state of New York and another zone consisting of the single state of Pennsylvania. + +Here's another hypothetical scenario. You would like to charge 10% tax on all electronic items and 5% tax on everything else. This tax should apply to all countries in the European Union \(EU\). In this case you would construct just a single zone consisting of all the countries in the EU. The fact that you want to charge two different rates depending on the type of good does not mean you need two zones. + + Please see the \[Addresses guide\]\(/developer/internals/addresses.html\) for more information on constructing a zone. + +## Default Tax Zone + +Spree also has the concept of a default tax zone. When a user is adding items to their cart we do not yet know where the order will be shipped to, and so it's assumed that the cart is within the default tax zone until later. In some cases we may want to estimate the tax for the order by assuming the order falls within a particular tax zone. + +Why might we want to do this? The primary use case for this is for countries where there is already tax included in the price. In the EU, for example, most products have a Value Added Tax \(VAT\) included in the price. There are cases where it may be desirable to show the portion of the product price that includes tax. In order to calculate this tax amount we need to know the zone \(and corresponding Tax Rate\) that was assumed in the price. + +We may also reduce the order total by the tax amount if the order is being shipped outside the tax jurisdiction. Again, this requires us to know the zone assumed in making the original tax calculation so that the tax amount can be backed out. + +## Shipping vs. Billing Address + +Most tax jurisdictions base the tax on the shipping address of where the order is being shipped to. So in these cases the shipping address is used when determining the tax zone. Spree does, however, allow you to use the billing address to determine the zone. + +To determine tax based on billing address instead of shipping address you will need to set the `Spree::Config[:tax_using_ship_address]` preference to `false`. + + \`Zone.match\` is a method used to determine the most applicable zone for taxation. In the case of multiple matches, the closer match will be used, with State zone matches having priority over Country zone matches. + +## Calculators + +In order to charge tax in Spree you also need a `Spree::Calculator`. In most cases you should be able to use Spree's `DefaultTax` calculator. It is suitable for both sales tax and price-inclusive tax scenarios. For more information, please read the [Calculators guide](calculators.md). + + The \`DefaultTax\` calculator uses the item total \(exclusive of shipping\) when computing sales tax. + +## Tax Types + +There are two basic types of tax that a store owner might need to contend with. In the United States \(and some other countries\) store owners sometimes need to charge what is known as "sales tax." In the European Union \(EU\) and other countries stores owners need to deal with "tax inclusive" pricing which is often called Value Added Tax \(VAT\). + + Most taxes can be considered one of these two types. For instance, in Australia customers pay a Goods and Services Tax \(GST\). This is basically equivalent to VAT in Europe. + +In some cases you may need to charge one type of tax for orders falling within one zone and another type of tax for orders falling within a different zone. There are even some rare situations where you may need to charge both types of tax in the same zone. Spree supports all of these scenarios. + +### Sales Tax + +Sales tax is the default tax type for any tax rate in Spree. + +Let's take an example of a sales tax situation for the United States. Imagine that we have a zone that covers all of North America and that the zone is used for a tax rate which applies a 5% tax on products with the tax category of "Clothing". + +If the customer purchases a single clothing item for $17.99 and they live in the United States \(which is within the North America zone we defined\) they are eligible to pay sales tax. + +The sales tax calculation is $17.99 x 5% for a total tax of $0.8995, which is rounded up to two decimal places, to $0.90. This tax amount is then applied to the order as an adjustment. + + See the \[Adjustments Guide\]\(/developer/internals/adjustments.html\) if you need more information on adjustments. + +If the quantity of the item is changed to 2, then the tax amount doubles: \($17.99 x 2\) x 0.05 is $1.799, which is again rounded up to two decimal places, applying a tax adjustment of $1.80. + +Let's now assume that we have another product that's a coffee mug, which doesn't have the "Clothing" tax category applied to it. Let's also assume this product costs $13.99, and there's no default tax category set up for the system. Under these circumstances, the coffee mug will not be taxed when it's added to the order. + +Finally, if the taxable address \(either the shipping or billing, depending on the `Spree::Config[:tax_using_ship_address]` setting\) is changed for the order to outside this taxable zone, then the tax adjustment on the order will be removed. If the address is changed back, the tax rate will be applied once more. + +### Tax Included + +Many jurisdictions have what is commonly referred to as a Value Added Tax \(VAT.\) In these cases the tax is typically applied to the price. This means that prices for items are "inclusive of tax" and no additional tax needs to be applied during checkout. + +In the case of tax inclusive pricing the store owner can enter all prices inclusive of tax if their home country is the default zone. If there is no default zone set, any taxes that are included in the price will be added to the net prices on the fly depending on the current order's tax zone. + +If the current order's tax zone is outside the default zone, prices will be shown and used with only the included taxes for that zone applied. If there is no VAT for that zone \(for example when the current order's shipping address is outside the EU\), the net price will be shown and used. + + Keep in mind that each order records the price a customer paid \(including the tax\) as part of the line item record. This means you don't have to worry about changing prices or tax rates affecting older orders. + +When tax is included in the price there is no order adjustment needed \(unlike the sales tax case\). Stores are, however, typically interested in showing the amount of tax the user paid. These totals are for informational purposes only and do not affect the order total. + +Let's start by looking at an example where there is a 5% included on all products and it's included in the price. We'll further assume that this tax should only apply to orders within the United Kingdom \(UK\). + +In the case where the order address is within the UK and we purchase a single clothing item for £17.99 we see an order total of £17.99. The tax rate adjustment applied is £17.99 x 5%, which is £0.8995, and that is rounded up to two decimal places, becoming £0.90. + +Now let's increase the quantity on the item from 1 to 2. The order total changes to £35.98 with a tax total of £1.799, which is again rounded up to now being £1.80. + +Next we'll add a different clothing item costing £19.99 to our order. Since both items are clothing and taxed at the same rate, they can be reduced to a single total, which means there's a single adjustment still applied to the order, calculated like this: \(£17.99 + £19.99\) x 0.05 = £1.899, rounded up to two decimal places: £1.90. + +Now let's assume an additional tax rate of 10% on a "Consumer Electronics" tax category. When we add a product with this tax category to our order with a price of £16.99, there will be a second adjustment added to the order, with a calculated total of £16.99 x 10%, which is £1.699. Rounded up, it's £1.70. + +Finally, if the order's address is changed to being outside this tax zone, then there will be two negative adjustments applied to remove these tax rates from the order. + +### Additional Examples + +#### Differing VATs for different product categories depending on the customer's zone + +As of January 1st, 2015, digital products sold within the EU must have the VAT of the receiving country applied. Physical products must have the seller's VAT applied. In order to set this up, please proceed as follows: + +1. Create zones for all EU countries and a zone for all EU countries except your home zone. +2. Mark your home zone as the default zone so you can conveniently enter gross prices. +3. Create a tax category "Digital products", and a tax category "Physical products". +4. Add tax rates that are "included in tax" for the tax category "Physical goods" for all EU countries. +5. Add two tax rates for the tax category "Physical products": 1. One for your home country, with your home country's VAT 2. One for the rest of the EU, also with your home country's VAT + +If you change the tax zone of the current order \(by changing the relevant address\), prices will now be shown and used including the correct VAT for the current order. + + All of the examples in this guide are meant to be used for illustrative purposes. They are not meant to be used as definitive interpretations of tax law. You should consult your accountant or attorney for guidance on how much tax to collect and under what circumstances. + diff --git a/security/api.md b/security/api.md new file mode 100644 index 0000000..356b1d4 --- /dev/null +++ b/security/api.md @@ -0,0 +1,14 @@ +--- +title: API +section: security +order: 3 +--- + +# API + +The REST API behaves slightly differently than a standard user. First, an admin has to create the access key before any user can query the REST API. This includes generating the key for the admin him/herself. This is not the case if `Spree::Api::Config[:requires_authentication]` is set to `false`. + +In cases where `Spree::Api::Config[:requires_authentication]` is set to `false`, read-only requests in the API will be possible for all users. For actions that modify data within Spree, a user will need to have an API key and then their user record would need to have permission to perform those actions. + +It is up to you to communicate that key. As an added measure, this authentication has to occur on every request made through the REST API as no session or cookies are created or stored for the REST API. + diff --git a/security/authentication_and_authorization.md b/security/authentication_and_authorization.md new file mode 100644 index 0000000..147331d --- /dev/null +++ b/security/authentication_and_authorization.md @@ -0,0 +1,33 @@ +--- +title: Authentication and Authorization +section: security +order: 2 +--- + +# Authentication & authorization + +If you install spree\_auth\_devise when setting up your app, we use a third party authentication library for Ruby known as [Devise](https://github.com/plataformatec/devise). This library provides a host of useful functionality that is in turn available to Spree, including the following features: + +* Authentication +* Strong password encryption \(with the ability to specify your own algorithms\) +* "Remember Me" cookies +* "Forgot my password" emails +* Token-based/oAuth 2.0 access \(for REST API\) + +## Devise Configuration + + A default Spree install comes with the \[Spree Auth Devise\]\(https://github.com/spree/spree\_auth\_devise\) gem, which provides authentication for Spree using Devise. This section of the guide covers the default setup. If you're using your own authentication, please consult the manual for that authentication engine. + +We have configured Devise to handle only what is needed to authenticate with a Spree site. The following details cover the default configurations: + +* Passwords are stored in the database encrypted with the salt. +* User authentication is done through the database query. +* User registration is enabled and the user's login is available immediately \(no validation emails\). +* There is a remember me and password recovery tool built in and enabled through Devise. + +These configurations represent a reasonable starting point for a typical e-commerce site. Devise can be configured extensively to allow for a different feature set but that is currently beyond the scope of this document. Developers are encouraged to visit the [Devise wiki](https://github.com/plataformatec/devise/wiki) for more details. + +## Authorization + +Please refer to [Permissions Customization guide](/developer/customization/permissions.html) for more information. + diff --git a/security/index.md b/security/index.md new file mode 100644 index 0000000..11219c2 --- /dev/null +++ b/security/index.md @@ -0,0 +1,44 @@ +--- +title: Security +order: 0 +--- + +# General guidelines + +## Overview + +Proper application design, intelligent programming, and secure infrastructure are all essential in creating a secure e-commerce store using any software \(Spree included\). The Spree team has done its best to provide you with the tools to create a secure and profitable web presence, but it is up to you to take these tools and put them in good practice. We highly recommend reading and understanding the [Rails Security Guide](http://guides.rubyonrails.org/security.html). + +## Supported Versions + +This is a list of all Spree versions currently being supported by the Spree team. + +| Version | Supported? | Release date | EOL date | +| :--- | :--- | :--- | :--- | +| 4.2 | **Yes** | 23.02.2021 | 23.02.2023 | +| 4.1 | **Yes** | 02.03.2020 | 02.03.2022 | +| 4.0 | **Yes** | 09.10.2019 | 09.10.2021 | +| 3.7 LTS | **Yes** | 04.02.2019 | 04.02.2022 | +| 3.6 | No | 12.06.2018 | 12.06.2020 | +| 3.5 | No | 12.06.2018 | 12.06.2020 | +| 3.4 | No | 12.10.2017 | 12.12.2019 | +| 3.3 | No | 22.08.2017 | 24.01.2019 | +| 3.2 | No | 17.03.2017 | 24.01.2019 | +| 3.1 | No | 15.06.2016 | 24.01.2019 | + +LTS means [Long-term support releases](https://en.wikipedia.org/wiki/Long-term_support), usually last release in the major release cycle. + +If you're using an older version [please upgrade](/developer/upgrades/upgrade_guides.html). Having trouble upgrading? [Contact us for support](https://spreecommerce.org/contact/). + +## Reporting Security Issues + +Please do not announce potential security vulnerabilities in public. We have a dedicated email address [security@spreecommerce.org](mailto:security@spreecommerce.org). We will work quickly to determine the severity of the issue and provide a fix for the appropriate versions. We will credit you with the discovery of this patch by naming you in a blog post. + +If you would like to provide a patch yourself for the security issue **do not open a pull request for it**. Instead, create a commit on your fork of Spree and run this command: + +```bash +git format-patch HEAD~1..HEAD --stdout > patch.txt +``` + +This command will generate a file called `patch.txt` with your changes. Please email a description of the patch along with the patch itself to our [dedicated email address](mailto:hi@spreecommerce.org). + diff --git a/security/pci_compliance.md b/security/pci_compliance.md new file mode 100644 index 0000000..8a57c8e --- /dev/null +++ b/security/pci_compliance.md @@ -0,0 +1,24 @@ +--- +title: PCI Compliance +section: security +order: 1 +--- + +# PCI Compliance + +## Overview + +All store owners wishing to process credit card transactions should be familiar with [PCI Compliance](http://en.wikipedia.org/wiki/Pci_compliance). [Spree Gateway Stripe itnegration](https://github.com/spree/spree_gateway) and [Spree Braintree vzero](https://github.com/spree-contrib/spree_braintree_vzero) are PCI-compliant. + +## Transmit Exactly Once + +Spree uses extreme caution in its handling of credit cards. In production mode, credit card data is transmitted to Spree via SSL. The data is immediately relayed to your chosen payment gateway and then discarded. The credit card data is never stored in the database \(not even temporarily\) and it exists in memory on the server for only a fraction of a second before it is discarded. + +Spree does store the last four digits of the credit card and the expiration month and date. You could easily customize Spree further if you wanted and opt out of storing even that little bit of information. + +## 3-D Secure and Strong Customer Authenthication support + +[Spree Gateway Stripe itnegration](https://github.com/spree/spree_gateway) supports [Strong Customer Authentication \(SCA\)](https://stripe.com/en-pl/guides/strong-customer-authentication) out of the box. Remember to use S**tripe Elements** gateway with **Payment Intents** option enabled. + +[Spree Braintree vzero](https://github.com/spree-contrib/spree_braintree_vzero) extension supports [3D Secure 2.0](https://developers.braintreepayments.com/guides/3d-secure/overview). + diff --git a/upgrades/index.md b/upgrades/index.md new file mode 100644 index 0000000..732147f --- /dev/null +++ b/upgrades/index.md @@ -0,0 +1,42 @@ +--- +title: Upgrading Spree +section: upgrades +order: 0 +--- + +# How to perform upgrades + +## Overview + +Keeping your Spree installation up-to-date is essential to keep it safe and secure. Each Spree version brings new features, enhancements and bugfixes so it's greatly beneficial for you and your clients to upgrade frequently. + +The upgrade process is fairly easy and well described. Of course it all boils down to the level of customizations and the way you've customized your Spree applications. + +We strongly advise upgrading Spree incrementally, rather than in one big go. + +## Support + +If you're stuck and would want get some professional help, you can [contact us directly](https://spreecommerce.org/contact/) and request a quote for our constulting services. + +## Release types + +Spree development follows a shifted version of [Semantic Versioning](https://semver.org/): + +**Patch Z, eg. 4.2.1** + +Only bug fixes, no API changes, no new features. Except as necessary for security fixes. + +**Minor Y, eg. 4.2.0** + +New features, may contain API changes \(Serve as major versions of Semver\). Breaking changes are paired with deprecation notices in the previous minor or major release. + +**Major X, eg. 4.0.0** + +New features, will likely contain API changes. The difference between Spree minor and major releases is the magnitude of breaking changes, and usually reserved for special occasions. + +## Security + +Our Security policy is [described here](/developer/security/security.html). + +When a release series is no longer supported, it's your own responsibility to deal with bugs and security issues. We may provide backports of some critical security fixes. + diff --git a/upgrades/upgrades/README.md b/upgrades/upgrades/README.md new file mode 100644 index 0000000..58ddd90 --- /dev/null +++ b/upgrades/upgrades/README.md @@ -0,0 +1,2 @@ +# Upgrade guides + diff --git a/upgrades/upgrades/four-dot-oh-to-four-dot-one.md b/upgrades/upgrades/four-dot-oh-to-four-dot-one.md new file mode 100644 index 0000000..b46aade --- /dev/null +++ b/upgrades/upgrades/four-dot-oh-to-four-dot-one.md @@ -0,0 +1,121 @@ +--- +title: Upgrading Spree 4.0 to 4.1 +section: upgrades +order: 1 +--- + +# 4.0 to 4.1 + +This guide covers upgrading a **4.0 Spree application** to **Spree 4.1**. + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + +**If you're on an older version than 4.0 please follow previous upgrade guides and perform those upgrades incrementally**, eg. + +1. [upgrade 3.3 to 3.4](/developer/upgrades/three-dot-three-to-three-dot-four.html) +2. [upgrade 3.4 to 3.5](/developer/upgrades/three-dot-four-to-three-dot-five.html) +3. [upgrade 3.5 to 3.6](/developer/upgrades/three-dot-five-to-three-dot-six.html) +4. [upgrade 3.6 to 3.7](/developer/upgrades/three-dot-six-to-three-dot-seven.html) +5. [upgrade 3.7 to 4.0](/developer/upgrades/three-dot-seven-to-four-dot-oh.html) + +This is the safest and recommended method. + +## Update Gemfile + +```ruby +gem 'spree', '~> 4.1' +gem 'spree_gateway', '~> 3.9' +``` + +## Run `bundle update` + +## Install missing migrations + +```bash +rails spree:install:migrations +``` + +## Run migrations + +```bash +rails db:migrate +``` + +## Decide what to do next + +You have two options: + +1. Migrate to the new Storefront UI +2. Stay at the current UI + +## Migrate to the new Storefront UI + +Spree 4.1 comes with a completely new mobile-first ultra-fast Storefront powered by Turbolinks. + +To replace your current frontend with the new Spree UI follow these steps: + +1. Update Spree Auth Devise to 4.1 in your `Gemfile` + + ```ruby + gem 'spree_auth_devise', '~> 4.1' + ``` + +2. In your project root directory run: + + ```bash + rails g spree:frontend:copy_storefront + ``` + + **WARNING** this will overwrite your current project templates, it's required for the new UI, so if you'll be asked by the generator what to do please choose **A** to proceed + +3. Next, you will need to copy over two files: + * [spree\_storefront.rb](https://raw.githubusercontent.com/spree/spree/master/core/lib/generators/spree/install/templates/config/initializers/spree_storefront.rb) to `config/initializers/spree_storefront.rb` + * [spree\_storefront.yml](https://raw.githubusercontent.com/spree/spree/master/core/lib/generators/spree/install/templates/config/spree_storefront.yml) to `config/spree_storefront.yml` +4. If you overwrote any `spree_frontend` [controllers](https://github.com/spree/spree/tree/master/frontend/app/controllers) you will need to either remove your local copies or move your custom logic to [decorators](https://guides.spreecommerce.org/developer/customization/logic.html#extending-controllers) +5. Same goes for [helpers](https://github.com/spree/spree/tree/master/frontend/app/helpers/spree) +6. You will also need to remove this line: + + ```javascript + //= require spree/frontend/spree_auth + ``` + + from `vendor/assets/javascripts/spree/frontend.all.js` file + +## Stay at the current UI + +If you wish to not move to the new Storefront UI it's still an option. Just proceed with the steps described below. + +1. Keep Spree Auth Devise at the version you're currently using + + If you're using Spree Auth Devise gem you need to lock it at 4.0.0 in your `Gemfile`: + + ```ruby + gem 'spree_auth_devise', '~> 4.0.0' + ``` + +2. Copy over all views from Spree 4.0 + + Copy over views from: [https://github.com/spree/spree/tree/4-0-stable/frontend/app/views](https://github.com/spree/spree/tree/4-0-stable/frontend/app/views) to your application views directory: `app/views` + + **WARNING** remember to not overwrite your customizations! + +3. Copy over all Stylesheets from Spree 4.0 + + Copy over stylesheets from: [https://github.com/spree/spree/tree/4-0-stable/frontend/app/assets/stylesheets](https://github.com/spree/spree/tree/4-0-stable/frontend/app/assets/stylesheets) to `app/stylesheets` + + **WARNING** remember to not overwrite your customizations! + +4. Copy over all JavaScript from Spree 4.0 + + Copy over stylesheets from: [https://github.com/spree/spree/tree/4-0-stable/frontend/app/assets/javascripts](https://github.com/spree/spree/tree/4-0-stable/frontend/app/assets/javascripts) to `app/javascripts` + + **WARNING** remember to not overwrite your customizations! + +## Read the release notes + +For information about changes contained within this release, please read the [4.1.0 Release Notes](https://guides.spreecommerce.org/release_notes/spree_4_1_0.html). + +## More info + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + diff --git a/upgrades/upgrades/four-dot-one-to-four-dot-two.md b/upgrades/upgrades/four-dot-one-to-four-dot-two.md new file mode 100644 index 0000000..8d82973 --- /dev/null +++ b/upgrades/upgrades/four-dot-one-to-four-dot-two.md @@ -0,0 +1,126 @@ +--- +title: Upgrading Spree 4.1 to 4.2 +section: upgrades +order: 0 +--- + +# 4.1 to 4.2 + +This guide covers upgrading a **4.1 Spree application** to **Spree 4.2**. + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + +**If you're on an older version than 4.1 please follow previous upgrade guides and perform those upgrades incrementally**, eg. + +1. [upgrade 3.7 to 4.0](/developer/upgrades/three-dot-seven-to-four-dot-oh.html) +2. [upgrade 4.0 to 4.1](/developer/upgrades/four-dot-oh-to-four-dot-one.html) + +This is the safest and recommended method. + +## Update Gemfile + +```ruby +gem 'spree', '~> 4.2' +gem 'spree_auth_devise', '~> 4.3' +gem 'spree_gateway', '~> 3.9' +gem 'spree_i18n', '~> 5.0' +``` + +## Remove SpreeMultiCurrency \(optional\) + +If you used that gem in the past you need to remove. Multi Currency is now incorporated into Spree core and you cannot use that gem anymore. + +1. Remove `spree_multi_currency` from your `Gemfile` +2. Remove these preferences from your Spree initializer \(`config/initializers/spree.rb`\): + * `allow_currency_change` + * `show_currency_selector` + * `supported_currencies` +3. Remove `//= require spree/frontend/spree_multi_currency` from `vendor/assets/javascripts/spree/frontend/all.js` +4. Remove `//= require spree/backend/spree_multi_currency` from `vendor/assets/javascripts/spree/backend/all.js` + +## Add `deface` gem \(optional\) + +If you used [Deface overrides](/developer/advanced/deface_overrides_tutorial.html) you will need to include `deface` in your `Gemfile` as it was removed from Spree / Spree Auth Devise / Spree Gateway dependencies. + +Simply add it to your `Gemfile`: + +```ruby +gem 'deface' +``` + +## Update gems + +```bash +bundle update +``` + +## Fix RMA migration + +Please find a `add_stock_location_to_rma` migration in your `db/migrate` directory and change: + +```ruby +class AddStockLocationToRma < ActiveRecord::Migration[4.2] +``` + +to + +```ruby +class AddStockLocationToRMA < ActiveRecord::Migration[4.2] +``` + +## Install missing migrations + +```bash +rails spree:install:migrations +rails active_storage:update +``` + +## Run migrations + +```bash +rails db:migrate +``` + +## Upgrade all of your Spree extensions to the newest versions + +To avoid errors and compatibility issues, please update all of your Spree extension gems to the newest versions which usually includes fixes for the new Spree release, eg. + +```bash +bundle update spree_related_products +``` + +## Other things to remember + +### Replace `fast_json` with `jsonapi-serializer` + +Please follow [this guide to migrate your custom serializers](https://github.com/jsonapi-serializer/jsonapi-serializer#migrating-from-netflixfast_jsonapi). + +### Migrate select2 3.5 to 4.x + +Only if you've added new Admin Panel pages with Select2 dropdown - [this guide will help](https://select2.org/upgrading/migrating-from-35) + +### Make sure you've got up to date Spree templates \(Storefront\) + +If you're using Spree default Storefront \(`spree_frontend` gem\) make sure to update your templates, especially: + +* [app/views/spree/shared/\_head.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_head.html.erb) +* [app/views/spree/shared/\_locale\_and\_currency.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_locale_and_currency.html.erb) +* [app/views/spree/shared/\_link\_to\_account.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_link_to_account.html.erb) +* [app/views/spree/shared/\_internationalization\_options.html.erb](https://github.com/spree/spree/blob/master/frontend/app/views/spree/shared/_internationalization_options.html.erb) +* [app/views/spree/shared/\_locale\_dropdown.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_locale_dropdown.html.erb) +* [app/views/spree/shared/\_currency\_dropdown.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_currency_dropdown.html.erb) +* [app/views/spree/shared/\_mobile\_navigation.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_mobile_navigation.html.erb) +* [app/views/spree/shared/\_mobile\_internationalization\_options.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_mobile_internationalization_options.html.erb) +* [app/views/spree/shared/\_nav\_bar.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_nav_bar.html.erb) +* [app/views/spree/shared/\_line\_item.html.erb](https://github.com/spree/spree/blob/4-2-stable/frontend/app/views/spree/shared/_line_item.html.erb) + +Or simply run `bundle exec rails g spree:frontend:copy_storefront` + +## Read the release notes + +For information about changes contained within this release, please read the [4.2.0 Release Notes](https://guides.spreecommerce.org/release_notes/spree_4_2_0.html). + +## More info + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + diff --git a/upgrades/upgrades/fout-dot-two-to-four-dot-three.md b/upgrades/upgrades/fout-dot-two-to-four-dot-three.md new file mode 100644 index 0000000..0ce0519 --- /dev/null +++ b/upgrades/upgrades/fout-dot-two-to-four-dot-three.md @@ -0,0 +1,97 @@ +--- +title: Upgrading Spree 4.2 to 4.3 +section: upgrades +order: 0 +hidden: true +--- + +# 4.2 to 4.3 + +This guide covers upgrading a **4.2 Spree application** to **Spree 4.3**. + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + +**If you're on an older version than 4.1 please follow previous upgrade guides and perform those upgrades incrementally**, eg. + +1. [upgrade 3.7 to 4.0](/developer/upgrades/three-dot-seven-to-four-dot-oh.html) +2. [upgrade 4.0 to 4.1](/developer/upgrades/four-dot-oh-to-four-dot-one.html) +3. [upgrade 4.1 to 4.2](/developer/upgrades/four-dot-one-to-four-dot-two.html) + +This is the safest and recommended method. + +## Update Gemfile + +```ruby +gem 'spree', '~> 4.3' +``` + +## Remove SpreeMultiDomain \(optional\) + +If you used that gem in the past you need to remove it. + +Multi Store is now incorporated into Spree core and you cannot use that gem anymore. + +1. Remove `spree_multi_domain` from your `Gemfile` +2. Remove `//= require spree/frontend/spree_multi_domain` from `vendor/assets/javascripts/spree/frontend/all.js` +3. Remove `//= require spree/backend/spree_multi_domain` from `vendor/assets/javascripts/spree/backend/all.js` + +## Add `spree_frontend` gem \(optional\) + +`spree` gem now does not include the `spree_frontend` gem anymore. If you use the default Spree Storefront you need to add it to your `Gemfile`. + +```ruby +gem 'spree_frontend' +``` + +## Add `spree_backend` gem \(optional\) + +`spree` gem now does not include the `spree_backend` gem anymore. If you use the default Spree Admin Panel you need to add it to your `Gemfile`. + +```ruby +gem 'spree_backend' +``` + +## Add `spree_emails` gem \(optional\) + +Transactional emails once part of `spree_core` were extracted into their own gem called `spree_emails`. If you would like to still use this feature you'll need to include this new gem in your `Gemfile`. + +```ruby +gem 'spree_emails' +``` + +## Update gems + +```bash +bundle update +``` + +## Install missing migrations + +```bash +rails spree:install:migrations +``` + +## Run migrations + +```bash +rails db:migrate +``` + +## Upgrade all of your Spree extensions to the newest versions + +To avoid errors and compatibility issues, please update all of your Spree extension gems to the newest versions which usually includes fixes for the new Spree release, eg. + +```bash +bundle update spree_related_products +``` + +## Other things to remember + +## Read the release notes + +For information about changes contained within this release, please read the [4.3.0 Release Notes](https://guides.spreecommerce.org/release_notes/spree_4_3_0.html). + +## More info + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + diff --git a/upgrades/upgrades/one-dot-oh-to-one-dot-one.md b/upgrades/upgrades/one-dot-oh-to-one-dot-one.md new file mode 100644 index 0000000..4b04a43 --- /dev/null +++ b/upgrades/upgrades/one-dot-oh-to-one-dot-one.md @@ -0,0 +1,71 @@ +--- +title: Upgrading Spree from 1.0.x to 1.1.x +section: upgrades +hidden: true +order: 16 +--- + +# one-dot-oh-to-one-dot-one + +## Overview + +This guide covers upgrading a 1.0.x Spree store, to a 1.1.x store. This guide has been written from the perspective of a blank Spree 1.0.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 1.1.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 1-1-stable branch. + +## Upgrade Rails + +Spree 1.1 depends on any Rails 3.2 release afer Rails 3.2.9. Ensure that you have that dependency specified in your Gemfile: + +```ruby +gem 'rails', '~> 3.2.9'``` + +Along with this, you may have to also update your assets group in the Gemfile: + +```ruby +group :assets do + gem 'sass-rails', '~> 3.2.5' + gem 'coffee-rails', '~> 3.2.1' + gem 'uglifier', '>= 1.0.3' +end + +gem 'jquery-rails', '2.1.4' +``` + +For more information, please refer to the [Upgrading Ruby on Rails guide](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-3-1-to-rails-3-2). + +## Upgrade Spree + +For best results, use the 1-1-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '1-1-stable'``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Remove references to spree_api assets + +Spree API no longer provides any asset files, so references to these must be removed from: + +* app/assets/stylesheets/store/all.css +* app/assets/stylesheets/admin/all.css +* app/assets/javascripts/store/all.js +* app/assets/javascripts/admin/all.js + +## Read the release notes + +For information about what has changed in this release, please read the [1.1.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_1_1_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/one-dot-one-to-one-dot-two.md b/upgrades/upgrades/one-dot-one-to-one-dot-two.md new file mode 100644 index 0000000..03df6c2 --- /dev/null +++ b/upgrades/upgrades/one-dot-one-to-one-dot-two.md @@ -0,0 +1,59 @@ +--- +title: Upgrading Spree from 1.1.x to 1.2.x +section: upgrades +order: 15 +hidden: true +--- + +# one-dot-one-to-one-dot-two + +## Overview + +This guide covers upgrading a 1.1.x Spree store, to a 1.2.x store. This guide has been written from the perspective of a blank Spree 1.1.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 1.2.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 1-2-stable branch. + +## Upgrade Spree + +For best results, use the 1-2-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '1-2-stable'``` + +Run `bundle update spree`. + +## Authentication dependency + +In this release, the `spree_auth` component was moved out of the main set of +gems into an extension, called `spree_auth_devise`. If you want to continue using Spree's authentication, then you will need to specify this extension as a dependency in your `Gemfile`: + +```ruby +gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '1-2-stable'``` + +Run `bundle install` to install this extension. + +### Rename current_user to current_spree_user + +To ensure that Spree does not conflict with any authentication provided by the application, Spree has renamed its `current_user` variable to `current_spree_user`. You should make this change wherever necessary within your application. + +Similar to this, any references to `@user` are now `@spree_user`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +This may copy over additional migrations from spree_auth_devise and run them as well. + +## Read the release notes + +For information about changes contained with this release, please read the [1.2.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_1_2_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/one-dot-three-to-two-dot-oh.md b/upgrades/upgrades/one-dot-three-to-two-dot-oh.md new file mode 100644 index 0000000..db72260 --- /dev/null +++ b/upgrades/upgrades/one-dot-three-to-two-dot-oh.md @@ -0,0 +1,68 @@ +--- +title: Upgrading Spree from 1.3.x to 2.0.x +section: upgrades +order: 13 +hidden: true +--- + +# one-dot-three-to-two-dot-oh + +## Overview + +This guide covers upgrading a 1.3.x Spree store, to a 2.0.x store. This guide has been written from the perspective of a blank Spree 1.3.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 2.0.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 2-0-stable branch. + +Given that this is a major release, you may want to read through the [2.0.0 release notes](http://guides.spreecommerce.org/release_notes/spree_2_0_0.html) to see what has changed before proceeding with this upgrade. + +## Upgrade Spree + +For best results, use the 2-0-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '2-0-stable'``` + +Run `bundle update spree`. + +## Bump jquery-rails + +This version of Spree bumps the dependency for jquery-rails to this: + +```ruby +gem 'jquery-rails', '3.0.0'``` + +Ensure that you have a line such as this in your Gemfile to allow that dependency. + +## Remove middleware + +The two pieces of middleware previously inserted into `config/application.rb` have now been deprecated. Remove these two lines: + +```ruby +config.middleware.use "Spree::Core::Middleware::RedirectLegacyProductUrl" +config.middleware.use "Spree::Core::Middleware::SeoAssist"``` + +## Rename assets + +In Spree 2, assets have been renamed. + +In `store/all.css` and `store/all.js`, you will need to rename the references from `spree_core` to `spree_frontend`. Similarly to this, in `admin/all.css` and `admin/all.js`, you will need to rename the references from `spree_core` to `spree_backend`. + +Additionally, remove references to `spree_promo` from these files. That component of Spree has now been merged with the Core component. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Read the release notes + +For information about changes contained with this release, please read the [2.0.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_2_0_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/one-dot-two-to-one-dot-three.md b/upgrades/upgrades/one-dot-two-to-one-dot-three.md new file mode 100644 index 0000000..57c7642 --- /dev/null +++ b/upgrades/upgrades/one-dot-two-to-one-dot-three.md @@ -0,0 +1,90 @@ +--- +title: Upgrading Spree from 1.2.x to 1.3.x +section: upgrades +order: 14 +hidden: true +--- + +# one-dot-two-to-one-dot-three + +## Overview + +This guide covers upgrading a 1.2.x Spree store, to a 1.3.x store. This guide has been written from the perspective of a blank Spree 1.2.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 1.3.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 1-3-stable branch. + +## Upgrade Spree + +For best results, use the 1-3-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '1-3-stable'``` + +Run `bundle update spree`. + +## Bump jquery-rails + +This version of Spree bumps the dependency for jquery-rails to this: + +```ruby +gem 'jquery-rails', '2.2.0'``` + +Ensure that you have a line such as this in your Gemfile to allow that dependency. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Replace money usages + +In older versions of Spree, we had a helper method called `money` which +occasionally formatted money amounts incorrectly. Specifically, if the `I18n.locale` was changed, currencies started to display in that amount, rather than the proper amount. An item that was once $100, would suddenly become 100¥ if the locale was switched to Japanese, for instance. + +In Spree 1.3, money handling +has been reworked by a major contribution by the [Free Running +Technologies](http://www.freerunningtech.com/) team. See [#2197](https://github.com/spree/spree/pull/2197) for details. + +Prices are now stored in a separate table, called `spree_prices`. This table tracks the variant, the price amount, and the currency. This allows for variants to have different prices in different currencies. + +Along with this, we introduced the `Spree::Money` class which is used to display amounts correctly. Where previously Spree would have done this: + +```erb +<%%= money adjustment.amount %>``` + +We now use this: + +```erb +<%%= adjustment.display_amount.to_html %>``` + +Alternatively, you can use `Spree::Money.new(amount)` to get a `Spree::Money` representation. Calling `to_html` on that object will format it neatly for HTML views, and calling `to_s` will format it nicely everywhere else. + +### Variant.active scope + +Along with these changes, the `Spree::Variant.active` scope now takes an argument for the currency. Whatever currency is specified will return variants in that currency. Previously it may have been enough to just do this: + +```ruby +@product.variants.active``` + +But now you must specify a currency: + +```ruby +@product.variants.active("USD")``` + +Or you can rely on the current currency within views: + +```ruby +@product.variants.active(current_currency)``` + +## Read the release notes + +For information about changes contained with this release, please read the [1.3.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_1_3_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/point-seventy-to-one-dot-oh.md b/upgrades/upgrades/point-seventy-to-one-dot-oh.md new file mode 100644 index 0000000..f2c5803 --- /dev/null +++ b/upgrades/upgrades/point-seventy-to-one-dot-oh.md @@ -0,0 +1,82 @@ +--- +title: Upgrading Spree from 0.70.x to 1.0.x +section: upgrades +order: 17 +hidden: true +--- + +# point-seventy-to-one-dot-oh + +## Overview + +This guide covers upgrading a 0.70.x Spree store, to a 1.0.x store. This guide has been written from the perspective of a blank Spree 0.70.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 1.0.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 1-0-stable branch. + +Worth noting here is that Spree 1.0 was the first release to properly use the features of Rails engines. This means that Spree needs to be mounted manually within the `config/routes.rb` file of the application, and that the classes such as `Product` and `Variant` from Spree are now namespaced within a module, so that they are now `Spree::Product` and `Spree::Variant`. Tables are similarly namespaced \(i.e. `spree_products` and `spree_variants`\). + +Along with this, migrations must be copied over to the application using the `rake railties:install:migrations` command, rather than a `rails g spree:site` command as before. + +## Upgrade Rails + +Spree 1.0 depends on any Rails 3.1 release afer Rails 3.1.10. Ensure that you have that dependency specified in your Gemfile: + +```ruby +gem 'rails', '~> 3.1.10' + +## Upgrade Spree + +For best results, use the 1-0-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '1-0-stable'``` + +Run `bundle update spree`. + +## Rename middleware classes + +In `config/application.rb`, there are two pieces of middleware: + +```ruby +config.middleware.use "RedirectLegacyProductUrl" +config.middleware.use "SeoAssist"``` + +These classes are now namespaced within Spree: + +```ruby +config.middleware.use "Spree::Core::Middleware::RedirectLegacyProductUrl" +config.middleware.use "Spree::Core::Middleware::SeoAssist"``` + + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Mount the Spree engine + +Within `config/routes.rb`, you must now mount the Spree engine: + +```ruby +mount Spree::Core::Engine, at: '/'``` + +This is the standard way of adding engines to Rails applications. + +## Remove spree_dash assets + +Spree's dash component was removed as a dependency of Spree, and so references +to its assets must be removed also. Remove references to spree_dash from: + +* app/assets/stylesheets/store/all.css +* app/assets/javascripts/store/all.js +* app/assets/stylesheets/admin/all.css +* app/assets/javascripts/admin/all.js + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/point-sixty-to-point-seventy.md b/upgrades/upgrades/point-sixty-to-point-seventy.md new file mode 100644 index 0000000..0c3ab90 --- /dev/null +++ b/upgrades/upgrades/point-sixty-to-point-seventy.md @@ -0,0 +1,91 @@ +--- +title: Upgrading Spree from 0.60.x to 0.70.x +section: upgrades +order: 18 +hidden: true +--- + +# point-sixty-to-point-seventy + +## Overview + +This guide covers upgrading a 0.60.x Spree store, to a 0.70.x store. This guide has been written from the perspective of a blank Spree 0.60.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 0.70.x store once this upgrade is complete. + +## Upgrade Rails + +Spree 0.60.x depends on Rails 3.0.12, whereas Spree 0.70.x depends on any Rails version from 3.1.1 up to 3.1.4. The first step in upgrading Spree is to upgrade the Rails version in the `Gemfile`: + +```ruby +gem 'rails', '3.1.12'``` + +For more information, please read the [Upgrading Ruby on Rails Guide](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-3-0-to-rails-3-1). + +## Upgrade Spree + +For best results, use the 0-70-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '0-70-stable'``` + +Run `bundle update rails` and `bundle update spree` and verify that was successful. + +## Remove debug_rjs configuration + +In `config/environments/development.rb`, remove this line: + +```ruby +config.action_view.debug_rjs = true``` + +## Remove lib/spree_site.rb + +This file is no longer used in 0.70.x versions of Spree. + +## Set up new data + +To migrate the data across, use these commands: + +```bash +rails g spree:site +rake db:migrate``` + +## The Asset Pipline + +With the upgrade to Rails 3.1 comes the [asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html). You need to add these gems to your Gemfile in order to support Spree's assets being served: + +```ruby +group :assets do + gem 'sass-rails', '~> 3.1.5' + gem 'coffee-rails', '~> 3.1.1' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + # gem 'therubyracer' + + gem 'uglifier', '>= 1.0.3' +end + +gem 'jquery-rails', '2.2.1'``` + +Along with these gems, you will need to enable assets within the class definition inside `config/application.rb`: + +```ruby +module YourStore + class Application < Rails::Application + + # ... + + # Enable the asset pipeline + config.assets.enabled = true + + # Version of your assets, change this if you want to expire all your assets + config.assets.version = '1.0' + + end +end``` + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/three-dot-five-to-three-dot-six.md b/upgrades/upgrades/three-dot-five-to-three-dot-six.md new file mode 100644 index 0000000..441a09f --- /dev/null +++ b/upgrades/upgrades/three-dot-five-to-three-dot-six.md @@ -0,0 +1,54 @@ +--- +title: Upgrading Spree from 3.5 to 3.6 +section: upgrades +order: 4 +--- + +# three-dot-five-to-three-dot-six + +This guide covers upgrading a 3.5 Spree application, to a 3.6 application. + +### Update Gemfile + +```ruby +gem 'spree', '~> 3.6.1' +gem 'spree_auth_devise', '~> 3.3' +gem 'spree_gateway', '~> 3.3' +``` + +### Update your Rails version to 5.2 + +Please follow the [official Rails guide](http://guides.rubyonrails.org/5_2_release_notes.html#upgrading-to-rails-5-2) to upgrade your store. + +### Run `bundle update` + +### Migrate to ActiveStorage \(optional\) + +Please follow the [official paperclip guide](https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md) if you want to use ActiveStorage instead of paperclip. + +You cann still use paperclip for attachment management by setting `SPREE_USE_PAPERCLIP` environment variable to `true`, but keep in mind that paperclip is DEPRECATED and we will remove paperclip support in Spree 4.0. + +### Install missing migrations + +```bash +rails spree:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +### Run migrations + +```bash +rails db:migrate +``` + +You're good to go! + +## Read the release notes + +For information about changes contained within this release, please read the [3.6.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_6_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your application and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/three-dot-four-to-three-dot-five.md b/upgrades/upgrades/three-dot-four-to-three-dot-five.md new file mode 100644 index 0000000..3a47282 --- /dev/null +++ b/upgrades/upgrades/three-dot-four-to-three-dot-five.md @@ -0,0 +1,66 @@ +--- +title: Upgrading Spree from 3.4 to 3.5 +section: upgrades +order: 5 +--- + +# three-dot-four-to-three-dot-five + +This guide covers upgrading a 3.4 Spree store, to a 3.5 store. + +### Update Gemfile + +```ruby +gem 'spree', '~> 3.5.0' +gem 'spree_auth_devise', '~> 3.3' +gem 'spree_gateway', '~> 3.3' +``` + +### Run `bundle update` + +### Install missing migrations + +```bash +rails spree:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +### Run migrations + +```bash +rails db:migrate +``` + +### Install Spree Analytics Trackers extension + +If you were previously using Analytics Trackers feature you need to install it as an extension as it was [extracted from the core](https://github.com/spree/spree/pull/8408). + +1. Add [Spree Analytics Trackers](https://github.com/spree-contrib/spree_analytics_trackers) to your `Gemfile`: + +```ruby +gem 'spree_analytics_trackers', github: 'spree-contrib/spree_analytics_trackers' +``` + +1. Install the gem using Bundler: + +```bash +bundle install +``` + +1. Copy and run migrations: + +```bash +bundle exec rails g spree_analytics_trackers:install +``` + +You're good to go! + +## Read the release notes + +For information about changes contained within this release, please read the [3.5.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_5_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/three-dot-oh-to-three-dot-one.md b/upgrades/upgrades/three-dot-oh-to-three-dot-one.md new file mode 100644 index 0000000..7783adf --- /dev/null +++ b/upgrades/upgrades/three-dot-oh-to-three-dot-one.md @@ -0,0 +1,86 @@ +--- +title: Upgrading Spree from 3.0 to 3.1 +section: upgrades +order: 9 +--- + +# three-dot-oh-to-three-dot-one + +This guide covers upgrading a 3.0.x Spree store, to a 3.1.x store. This guide has been written from the perspective of a blank Spree 3.0.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 3.1.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 3-1-stable branch. + +## Upgrade Rails + +For this Spree release, you will need to upgrade your Rails version to at least 4.2.6. + +```ruby +gem 'rails', '~> 4.2.6' +``` + +## Upgrade Spree + +For best results, use the spree gem in version 3.1.x: + +```ruby +gem 'spree', '~> 3.1.0.rc1' +``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree \(and any other engine\) and run them using these commands: + +```text +rake railties:install:migrations +rake db:migrate +``` + +## Spree Auth Devise & Spree Gateway + +If you are using Spree Gateway and/or Spree Auth Devise you should also upgrade them: + +```ruby +gem 'spree_auth_devise', '~> 3.1.0.rc1' +gem 'spree_gateway', '~> 3.1.0.rc1' +``` + +For Spree Auth Devise run installer: + +```text +rails g spree:auth:install +``` + +\(you don't have to override config/initializers/devise.rb\) + +## Additional information + +### Make sure to v1 namespace custom rabl templates & overrides. + +If your rabl templates reference others with extend you'll need to add the v1 namespace. + +For example: + +```ruby +extends 'spree/api/zones/show' +``` + +Becomes: + +```ruby +extends 'spree/api/v1/zones/show' +``` + +### Remove Spree::Config.check\_for\_spree\_alerts + +If you were disabling the alert checks you'll now want to remove this preference as it's no longer used. + +## Read the release notes + +For information about changes contained within this release, please read the [3.1.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_1_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/three-dot-one-to-three-dot-two.md b/upgrades/upgrades/three-dot-one-to-three-dot-two.md new file mode 100644 index 0000000..b2b8d1c --- /dev/null +++ b/upgrades/upgrades/three-dot-one-to-three-dot-two.md @@ -0,0 +1,62 @@ +--- +title: Upgrading Spree from 3.1 to 3.2 +section: upgrades +order: 8 +--- + +# three-dot-one-to-three-dot-two + +This guide covers upgrading a 3.1.x Spree store, to a 3.2.x store. + +### Update your Rails version to 5.0 + +Please follow the [official Rails guide](http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-4-2-to-rails-5-0) to upgrade your store. + +### Update Gemfile + +```ruby +gem 'spree', '~> 3.2.0' +gem 'spree_auth_devise', '~> 3.2.0' +gem 'spree_gateway', '~> 3.2.0' +``` + +### Update your extensions + +We're changing how extensions dependencies work. Previously you had to match extension branch to Spree branch. Starting from now `master` branch of all `spree-contrib` extensions should work with Spree >= `3.1` and < `4.0`. Please change your extensions in Gemfile eg.: + +from: + +```ruby +gem 'spree_braintree_vzero', github: 'spree-contrib/spree_braintree_vzero', branch: '3-1-stable' +``` + +to: + +```ruby +gem 'spree_braintree_vzero', github: 'spree-contrib/spree_braintree_vzero' +``` + +### Run `bundle update` + +### Install missing migrations + +```bash +rails spree:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +### Run migrations + +```bash +rails db:migrate +``` + +## Read the release notes + +For information about changes contained within this release, please read the [3.2.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_2_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/three-dot-seven-to-four-dot-oh.md b/upgrades/upgrades/three-dot-seven-to-four-dot-oh.md new file mode 100644 index 0000000..44a6490 --- /dev/null +++ b/upgrades/upgrades/three-dot-seven-to-four-dot-oh.md @@ -0,0 +1,256 @@ +--- +title: Upgrading Spree 3.7 to 4.0 +section: upgrades +order: 2 +--- + +# three-dot-seven-to-four-dot-oh + +This guide covers upgrading a **3.7 Spree application** to **Spree 4.0**. + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + +**If you're on an older version than 3.7 please follow previous upgrade guides and perform those upgrades incrementally**, eg. + +1. [upgrade 3.2 to 3.3](/developer/upgrades/three-dot-two-to-three-dot-three.html) +2. [upgrade 3.3 to 3.4](/developer/upgrades/three-dot-three-to-three-dot-four.html) +3. [upgrade 3.4 to 3.5](/developer/upgrades/three-dot-four-to-three-dot-five.html) +4. [upgrade 3.5 to 3.6](/developer/upgrades/three-dot-five-to-three-dot-six.html) +5. [upgrade 3.6 to 3.7](/developer/upgrades/three-dot-six-to-three-dot-seven.html) + +This is the safest and recommended method. + +## Update your Ruby version to 2.5.0 at least + +Spree 4.0 requires Ruby 2.5.0 at least so you need to bump the ruby version in your project's `Gemfile` and `.ruby-version` files. + +## Migrate from Paperclip to ActiveStorage + +In Spree 3.6 we deprecated [Paperclip support in favour of ActiveStorage](/release_notes/3_6_0.html#active-storage-support). Paperclip gem itself isn't maintained anymore and it is recommended to move to ActiveStorage as it is the defualt Rails storage engine since Rails 5.2 release. + +In Spree 4.0 we've removed Paperclip support in favour of ActiveStorage. + +Please remove also any occurances of `Rails.application.config.use_paperclip` and `Configuration::Paperclip` from your codebase. + +Please follow the [official Paperclip to ActiveStorage migration guide](https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md). + +## Replace OrderContents with services in your codebase + +`OrderContents` was deprecated in Spree 3.7 and removed in 4.0. We've replaced it with [service objects](/release_notes/3_7_0.html#service-oriented-architecture). + +You need to replace any instances of `OrderContents` usage with coresponding services in your codebase. + +### `OrderContents#update_cart` + +before: + +```ruby +order.contents.update_cart(line_items_attributes) +``` + +after: + +```ruby +Spree::Cart::Update.call(order: order, params: line_items_attributes) +``` + +### `OrderContents#add` + +before: + +```ruby +order.contents.add(variant, quantity, shipment: shipment) +``` + +after: + +```ruby +Spree::Cart::AddItem.call( + order: order, + variant: variant, + quantity: quantity, + options: { + shipment: @shipment + } +) +``` + +### `OrderContents#remove` + +before: + +```ruby +order.contents.remove(variant, quantity, shipment: shipment) +``` + +after: + +```ruby +Spree::Cart::RemoveItem.call( + order: order, + variant: variant, + quantity: quantity, + options: { + shipment: shipment + } +) +``` + +## Replace `add_store_credit_payments` with `Checkout::AddStoreCredit` + +Similar to `OrderContents` method `add_store_credit_payments` was replaced with `Checkout::AddStoreCredit` service. + +before: + +```ruby +order.add_store_credit_payments +``` + +after: + +```ruby +Spree::Checkout::AddStoreCredit.call(order: order) +``` + +## Replace `remove_store_credit_payments` with `Checkout::RemoveStoreCredit` + +Similar to `OrderContents` method `remove_store_credit_payments` was replaced with `Checkout::RemeoveStoreCredit` service. + +before: + +```ruby +order.remove_store_credit_payments +``` + +after: + +```ruby +Spree::Checkout::RemoveStoreCredit.call(order: order) +``` + +## Remove `spree_address_book` extension + +If you're using [Address Book](https://github.com/spree-contrib/spree_address_book) extension you need to remove as this feature was [merged into core Spree](/release_notes/4_0_0.html#address-book-support). + +1. Remove this line from `Gemfile` + + ```ruby + gem 'spree_address_book', github: 'spree-contrib/spree_address_book' + ``` + +2. Remove this line from `vendor/assets/javascripts/spree/frontend/all.js` + + ```text + //= require spree/frontend/spree_address_book + ``` + +3. Remove this line from `vendor/assets/stylesheets/spree/frontend/all.css` + + ```text + //= require spree/frontend/spree_address_book + ``` + +## Replace `class_eval` with `Module.prepend` \(only for Rails 6\) + +Rails 6.0 ships with [new code autoloader called Zeitwerk](https://medium.com/@fxn/zeitwerk-a-new-code-loader-for-ruby-ae7895977e73) which has some [strict rules in terms of file naming and contents](https://github.com/fxn/zeitwerk#file-structure). If you used `class_eval` to extend and modify Spree classes you will need to rewrite those with `Module.prepend`. Eg. + +Old decorator syntax - `app/models/spree/order_decorator.rb` + +```ruby +Spree::Order.class_eval do + has_many :new_custom_model + + def some_method + # ... + end +end +``` + +New decorator syntax - `app/models/my_store/spree/order_decorator.rb` + +```ruby +module MyStore + module Spree + module OrderDecorator + def self.prepended(base) + base.has_many :new_custom_model + end + + def some_method + # ... + end + end + end +end + +::Spree::Order.prepend(MyStore::Spree::OrderDecorator) +``` + +When migrating class method to the new [autoloader](https://medium.com/@fxn/zeitwerk-a-new-code-loader-for-ruby-ae7895977e73) things are a little different because you will have to prepend to the Singleton class as shown in this example: + +```ruby +module Spree::BaseDecorator + def spree_base_scopes + # custom implementation + end +end + +Spree::Base.singleton_class.send :prepend, Spree::BaseDecorator +``` + +Please also consider other options for [Logic Customization](/developer/customization/logic.html). + +We recommend also reading through [Ruby modules: Include vs Prepend vs Extend](https://medium.com/@leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073) + +## Update Bootstrap 3 to 4 or stay at Bootstrap 3 + +Spree 4 uses Bootstrap 4 for both Storefront and Admin Panel. You have two options: + +### Stay at Bootstrap 3 + +As we know this is a big portion of work you can still use Bootstrap 3 for your Storefront. + +1. Copy all remaining views by running `bundle exec spree:frontend:copy_views` +2. Add `bootstrap-sass` gem to your `Gemfile` + + ```ruby + gem 'bootstrap-sass', '~> 3.4.1' + ``` + +### Move to Bootstrap 4 + +[Follow the official Bootstrap 3 to 4 migration guide](https://getbootstrap.com/docs/4.0/migration/) + +## Update Gemfile + +```ruby +gem 'spree', '~> 4.0' +gem 'spree_auth_devise', '~> 4.0' +gem 'spree_gateway', '~> 3.6' +``` + +## Run `bundle update` + +## Install missing migrations + +```bash +rails spree:install:migrations +rails spree_api:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +## Run migrations + +```bash +rails db:migrate +``` + +## Read the release notes + +For information about changes contained within this release, please read the [4.0.0 Release Notes](https://guides.spreecommerce.org/release_notes/spree_4_0_0.html). + +## More info + +If you have any questions or suggestions feel free to reach out through [Spree slack channels](http://slack.spreecommerce.org/) + diff --git a/upgrades/upgrades/three-dot-six-to-three-dot-seven.md b/upgrades/upgrades/three-dot-six-to-three-dot-seven.md new file mode 100644 index 0000000..4bd460a --- /dev/null +++ b/upgrades/upgrades/three-dot-six-to-three-dot-seven.md @@ -0,0 +1,71 @@ +--- +title: Upgrading Spree from 3.6 to 3.7 +section: upgrades +order: 3 +--- + +# three-dot-six-to-three-dot-seven + +This guide covers upgrading a 3.6 Spree application, to version 3.7. + +## Update Gemfile + +```ruby +gem 'spree', '~> 3.7.0' +gem 'spree_auth_devise', '~> 3.5' +gem 'spree_gateway', '~> 3.4' +``` + +## Run `bundle update` + +## Install missing migrations + +```bash +rails spree:install:migrations +rails spree_api:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +## Run migrations + +```bash +rails db:migrate +``` + +## Migrate Taxon icons to Spree Assets + +We renamed `TaxonIcon` to `TaxonImage` to clarify usage of this model. If you were using `TaxonIcon` please run this to migrate your icons to images: + +```bash +rails db:migrate_taxon_icons_to_images +``` + +## Ensure all Orders associated to Store + +Orders needs to be associated to Stores. To ensure all existing `Order` are associated with `Store` please run this: + +```bash +rails db:associate_orders_with_store +``` + +This will associate all Orders without Store to the default Store. This can take some time depending on your volume of data. + +## Ensure all Orders have currency present + +To enhance multi currency capabilities we've made `currency` presence obligatory in `Order` model. To ensure all existing `Orders` have `currency` present please run this command: + +```bash +rails db:ensure_order_currency_presence +``` + +This will set `currency` in Orders without currency set to `Spree::Config[:default_currency]` value. This can take some time depending on your volume of data. + +## Replace `guest_token` with `token` in your codebase + +`Order#guest_token` was renamed to `Order#token` in order to unify the experience for guest checkouts and orders placed by signed in users. + +## Read the release notes + +For information about changes contained within this release, please read the [3.7.0 Release Notes](https://guides.spreecommerce.org/release_notes/spree_3_7_0.html). + diff --git a/upgrades/upgrades/three-dot-three-to-three-dot-four.md b/upgrades/upgrades/three-dot-three-to-three-dot-four.md new file mode 100644 index 0000000..d0f403b --- /dev/null +++ b/upgrades/upgrades/three-dot-three-to-three-dot-four.md @@ -0,0 +1,50 @@ +--- +title: Upgrading Spree from 3.3 to 3.4 +section: upgrades +order: 6 +--- + +# three-dot-three-to-three-dot-four + +This guide covers upgrading a 3.3 Spree store, to a 3.4 store. + +### Update Gemfile + +```ruby +gem 'spree', '~> 3.4.0' +gem 'spree_auth_devise', '~> 3.3' +gem 'spree_gateway', '~> 3.3' +``` + +### Run `bundle update` + +### Install missing migrations + +```bash +rails spree:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +### Run migrations + +```bash +rails db:migrate +``` + +### Migrate Spree::Taxon icons to Spree Assets + +We changed `Spree::Taxon` icon to use `Spree::Asset` to unify attachment usage across all Spree models. If you were using icon images in `Spree::Taxon` please run this to migrate your icons: + +```bash +rails db:migrate_taxon_icons +``` + +## Read the release notes + +For information about changes contained within this release, please read the [3.4.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_4_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/three-dot-two-to-three-dot-three.md b/upgrades/upgrades/three-dot-two-to-three-dot-three.md new file mode 100644 index 0000000..0d3d679 --- /dev/null +++ b/upgrades/upgrades/three-dot-two-to-three-dot-three.md @@ -0,0 +1,100 @@ +--- +title: Upgrading Spree from 3.2 to 3.3 +section: upgrades +order: 7 +--- + +# three-dot-two-to-three-dot-three + +This guide covers upgrading a 3.2.x Spree store, to a 3.3.x store. + +### Update your Rails version to 5.1 + +Please follow the [official Rails guide](http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-5-0-to-rails-5-1) to upgrade your store. + +### Update Gemfile + +```ruby +gem 'spree', '~> 3.3.0' +gem 'spree_auth_devise', '~> 3.3' +gem 'spree_gateway', '~> 3.3' +``` + +### Update your extensions + +We're changing how extensions dependencies work. Previously you had to match extension branch to Spree branch. Starting from Spree 3.2 release date `master` branch of all `spree-contrib` extensions should work with Spree >= `3.1` and < `4.0`. Please change your extensions in Gemfile eg.: + +from: + +```ruby +gem 'spree_braintree_vzero', github: 'spree-contrib/spree_braintree_vzero', branch: '3-1-stable' +``` + +to: + +```ruby +gem 'spree_braintree_vzero', github: 'spree-contrib/spree_braintree_vzero' +``` + +### Run `bundle update` + +### Install missing migrations + +```bash +rails spree:install:migrations +rails spree_auth:install:migrations +rails spree_gateway:install:migrations +``` + +### Run migrations + +```bash +rails db:migrate +``` + +### Include `UserMethods` in your `User` class + +With this release we're not including this automatically. You need to do it manually if you're not using `spree_auth_devise`. + +You need to include `Spree::UserMethods` in your user class, eg. + +```ruby +class User + include UserAddress + include UserMethods + include UserPaymentSource +end +``` + +### Update `aws-sdk` gem to `>= 2.0` + +Spree 3.3 comes with paperclip 5.1 support so if you're using Amazon S3 storage you need to change in your Gemfile, from: + +```ruby +gem 'aws-sdk', '< 2.0' +``` + +to: + +```ruby +gem 'aws-sdk', '>= 2.0' +``` + +and run `bundle update aws-sdk` + +In your paperclip configuration you also need to specify `s3_region` attribute eg. [https://github.com/spree/spree/blame/master/guides/content/developer/customization/s3\_storage.md\#L27](https://github.com/spree/spree/blame/master/guides/content/developer/customization/s3_storage.md#L27) + +Seel also [RubyThursday episode](https://rubythursday.com/episodes/ruby-snack-27-upgrade-paperclip-and-aws-sdk-in-prep-for-rails-5) walkthrough of upgrading paperclip in your project. + +### Add jquery.validate to your project if you've used it directly from Spree + +If your application.js file includes line `//= require jquery.validate/jquery.validate.min` you will need to add it this file manually to your project because this library was [removed from Spree in favour of native HTML5 validation](https://github.com/spree/spree/pull/8173). + +## Read the release notes + +For information about changes contained within this release, please read the [3.3.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_3_3_0.html). + +## Verify that everything is OK + +Run you test suite, click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. + diff --git a/upgrades/upgrades/two-dot-oh-to-two-dot-one.md b/upgrades/upgrades/two-dot-oh-to-two-dot-one.md new file mode 100644 index 0000000..3747b2d --- /dev/null +++ b/upgrades/upgrades/two-dot-oh-to-two-dot-one.md @@ -0,0 +1,52 @@ +--- +title: Upgrading Spree from 2.0.x to 2.1.x +section: upgrades +order: 12 +hidden: true +--- + +# two-dot-oh-to-two-dot-one + +## Overview + +This guide covers upgrading a 2.0.x Spree store, to a 2.1.x store. This guide has been written from the perspective of a blank Spree 2.0.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 2.1.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 2-1-stable branch. + +This is the first Spree release which supports Rails 4 exclusively. Spree releases after this point will continue to support Rails 4 only. + +## Upgrade Rails + +For this Spree release, you will need to upgrade your Rails version to at least 4.0.0. + +It is recommended to read through the [Upgrading Ruby on Rails guide](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-%20from-rails-3-2-to-rails-4-0) to learn what needs to be done for your application to migrate to Rails 4. + +```ruby +gem 'rails', '~> 4.0.0'``` + +## Upgrade Spree + +For best results, use the 2-1-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '2-1-stable'``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Read the release notes + +For information about changes contained with this release, please read the [2.1.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_2_1_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/two-dot-one-to-two-dot-two.md b/upgrades/upgrades/two-dot-one-to-two-dot-two.md new file mode 100644 index 0000000..1a063e1 --- /dev/null +++ b/upgrades/upgrades/two-dot-one-to-two-dot-two.md @@ -0,0 +1,62 @@ +--- +title: Upgrading Spree from 2.1.x to 2.2.x +section: upgrades +order: 11 +hidden: true +--- + +# two-dot-one-to-two-dot-two + +## Overview + +This guide covers upgrading a 2.1.x Spree store, to a 2.2.x store. This guide has been written from the perspective of a blank Spree 2.1.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 2.2.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 2-2-stable branch. + +## Upgrade Rails + +For this Spree release, you will need to upgrade your Rails version to at least 4.0.6. + +```ruby +gem 'rails', '~> 4.0.6' +``` + +## Upgrade Spree + +For best results, use the 2-2-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '2-2-stable'``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Read the release notes + +For information about changes contained with this release, please read the [2.2.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_2_2_0.html). + +### Rename assets + +As mentioned in the release notes, asset paths have changed. Change the references on the left, to the ones on the right: + +* `admin/spree_backend` => `spree/backend` +* `store/spree_frontend` => `spree/frontend` + +This applies across the board on Spree, and may need to be done in your store's extensions. + +### Paperclip settings have been removed from master + +Please consult [this section](http://guides.spreecommerce.org/release_notes/spree_2_2_0.html#paperclip-settings-have-been-removed) of the release notes if you were using custom Paperclip settings. This will direct you what to do in that particular case. + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/two-dot-three-to-two-dot-four.md b/upgrades/upgrades/two-dot-three-to-two-dot-four.md new file mode 100644 index 0000000..fb429f5 --- /dev/null +++ b/upgrades/upgrades/two-dot-three-to-two-dot-four.md @@ -0,0 +1,47 @@ +--- +title: Upgrading Spree from 2.3.x to 2.4.x +section: upgrades +hidden: true +order: 9 +--- + +# two-dot-three-to-two-dot-four + +This guide covers upgrading a 2.3.x Spree store, to a 2.4.x store. This guide has been written from the perspective of a blank Spree 2.3.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 2.4.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 2-4-stable branch. + +## Upgrade Rails + +For this Spree release, you will need to upgrade your Rails version to at least 4.1.8. + +```ruby +gem 'rails', '~> 4.1.8' +``` + +## Upgrade Spree + +For best results, use the 2-4-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '2-4-stable'``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Read the release notes + +For information about changes contained within this release, please read the [2.4.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_2_4_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` + diff --git a/upgrades/upgrades/two-dot-two-to-two-dot-three.md b/upgrades/upgrades/two-dot-two-to-two-dot-three.md new file mode 100644 index 0000000..054fc4e --- /dev/null +++ b/upgrades/upgrades/two-dot-two-to-two-dot-three.md @@ -0,0 +1,51 @@ +--- +title: Upgrading Spree from 2.2.x to 2.3.x +section: upgrades +hidden: true +order: 10 +--- + +# two-dot-two-to-two-dot-three + +## Overview + +This guide covers upgrading a 2.2.x Spree store, to a 2.3.x store. This guide has been written from the perspective of a blank Spree 2.2.x store with no extensions. + +If you have extensions that your store depends on, you will need to manually verify that each of those extensions work within your 2.3.x store once this upgrade is complete. Typically, extensions that are compatible with this version of Spree will have a 2-3-stable branch. + +This is the first Spree release which supports Rails 4.1. + +## Upgrade Rails + +For this Spree release, you will need to upgrade your Rails version to at least 4.1.2. + +```ruby +gem 'rails', '~> 4.1.2' +``` + +## Upgrade Spree + +For best results, use the 2-3-stable branch from GitHub: + +```ruby +gem 'spree', github: 'spree/spree', branch: '2-3-stable'``` + +Run `bundle update spree`. + +## Copy and run migrations + +Copy over the migrations from Spree (and any other engine) and run them using +these commands: + + rake railties:install:migrations + rake db:migrate + +## Read the release notes + +For information about changes contained with this release, please read the [2.3.0 Release Notes](http://guides.spreecommerce.org/release_notes/spree_2_3_0.html). + +## Verify that everything is OK + +Click around in your store and make sure it's performing as normal. Fix any deprecation warnings you see. +``` +