{"action":"created","comment":{"url":"https://api.github.com/repos/spree/spree/pulls/comments/39340442","id":39340442,"diff_hunk":"@@ -550,7 +550,7 @@ class SubclassedOrder < Spree::Order\n     it \"does not attempt to process payments\" do\n       allow(order).to receive_message_chain(:line_items, :present?) { true }\n       allow(order).to receive(:ensure_line_items_are_in_stock) { true }\n-      allow(order).to receive(:ensure_line_item_variants_are_not_deleted) { true }\n+      allow(order).to receive(:ensure_line_item_variants_are_not_discontinued) { true }","path":"core/spec/models/spree/order/checkout_spec.rb","position":5,"original_position":5,"commit_id":"e0918417ccfa706813060dd969b817467fa3672a","original_commit_id":"e0918417ccfa706813060dd969b817467fa3672a","user":{"login":"houndci","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci","html_url":"https://github.com/houndci","followers_url":"https://api.github.com/users/houndci/followers","following_url":"https://api.github.com/users/houndci/following{/other_user}","gists_url":"https://api.github.com/users/houndci/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci/subscriptions","organizations_url":"https://api.github.com/users/houndci/orgs","repos_url":"https://api.github.com/users/houndci/repos","events_url":"https://api.github.com/users/houndci/events{/privacy}","received_events_url":"https://api.github.com/users/houndci/received_events","type":"User","site_admin":false},"body":"Line is too long. [87/80]","created_at":"2015-09-12T19:12:34Z","updated_at":"2015-09-12T19:12:34Z","html_url":"https://github.com/spree/spree/pull/6692#discussion_r39340442","pull_request_url":"https://api.github.com/repos/spree/spree/pulls/6692","_links":{"self":{"href":"https://api.github.com/repos/spree/spree/pulls/comments/39340442"},"html":{"href":"https://github.com/spree/spree/pull/6692#discussion_r39340442"},"pull_request":{"href":"https://api.github.com/repos/spree/spree/pulls/6692"}}},"pull_request":{"url":"https://api.github.com/repos/spree/spree/pulls/6692","id":43193402,"html_url":"https://github.com/spree/spree/pull/6692","diff_url":"https://github.com/spree/spree/pull/6692.diff","patch_url":"https://github.com/spree/spree/pull/6692.patch","issue_url":"https://api.github.com/repos/spree/spree/issues/6692","number":6692,"state":"open","locked":false,"title":"Add discontinue_on to Products and Variants","user":{"login":"jasonfb","id":59002,"avatar_url":"https://avatars.githubusercontent.com/u/59002?v=3","gravatar_id":"","url":"https://api.github.com/users/jasonfb","html_url":"https://github.com/jasonfb","followers_url":"https://api.github.com/users/jasonfb/followers","following_url":"https://api.github.com/users/jasonfb/following{/other_user}","gists_url":"https://api.github.com/users/jasonfb/gists{/gist_id}","starred_url":"https://api.github.com/users/jasonfb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasonfb/subscriptions","organizations_url":"https://api.github.com/users/jasonfb/orgs","repos_url":"https://api.github.com/users/jasonfb/repos","events_url":"https://api.github.com/users/jasonfb/events{/privacy}","received_events_url":"https://api.github.com/users/jasonfb/received_events","type":"User","site_admin":false},"body":"Note: this is 90% complete. Still putting finishing touches on it. I am pull requesting it to make sure CI tools pass all specs (yes, they pass locally for me) and for early feedback. (See \"TODOs\" below -- I will finish them this week and revise this pull request accordingly)\r\n\r\n----\r\n\r\nSpree's current implementation uses act_as_paranoid to implement a ActiveRecord-wide \"soft-delete\" of Products and Variants. Unfortunately, soft deleting means that the records are left in the database but behave as if they are really deleted.\r\n\r\nBecause associations from other objects (like line itmes to variant) won't normally see the deleted, core code is forced (unnaturally) to use scopes .with_deleted \r\n\r\nhttps://github.com/spree/spree/blob/master/core/app/models/spree/product.rb#L224\r\n\r\nNot only does this abstract magic get in the way of acts_as_paranoid's true intention, but  in a worse example, it becomes necessary to overload an AR belongs_to with a magic method.\r\n\r\nhttps://github.com/spree/spree/blob/master/core/app/models/spree/line_item.rb#L106\r\n\r\nThis hack above solves the problem that when looking at a past order with line items attached to deleted variants you can't load the variants because the objects appear to be missing in Active Record.\r\n\r\nThe design flaw here is that these objects really should not be considered \"deleted.\" The word is wrong, the concept is wrong, and the implementation bends over backwards to accomodate the flawed design. \r\n\r\nThe approach proposed solves the underlying flaw *and all the related bugs cuased by this flaw* in the following ways:\r\n\r\n- [x] Adds a field discontinue_on to Products and Variants. \r\n\r\n- [x] Migrate the timestamps deleted_at to discontinue_on (when possible), and un-delete the deleted variants (when there is not matching SKU) and products (when there is no matching slug)\r\n\r\n- [x] Redefine the available scope on the Products object as so. Notice that the not_discontinued scope is added to the end of whatever scope chain is here. This is necessary because we no longer have the default_scope weeding out discontinued products:\r\n\r\n  ```\r\n    add_search_scope :not_discontinued do\r\n      where(\"#{Product.quoted_table_name}.discontinue_on IS NULL or #{Product.quoted_table_name}.discontinue_on >= ?\", Time.zone.now)\r\n    end\r\n    search_scopes << :not_discontinued\r\n\r\n    # Can't use add_search_scope for this as it needs a default argument\r\n    def self.available(available_on = nil, currency = nil)\r\n      not_discontinued.joins(:master => :prices).where(\"#{Product.quoted_table_name}.available_on <= ?\", available_on || Time.now)\r\n    end\r\n    search_scopes << :available\r\n\r\n    def self.active(currency = nil)\r\n      not_discontinued.available(nil, currency)\r\n    end\r\n```\r\n\r\n- [x] Add a ```not_discontinued``` scope on the Variant object:\r\n\r\n ```\r\n    scope :not_discontinued, -> { where(\"#{Variant.quoted_table_name}.discontinue_on IS NULL OR #{Variant.quoted_table_name}.discontinue_on <= ?\",  Time.now) }\r\n```\r\n\r\n(the active class method has been changed to include this scope)\r\n\r\n- [x] Removes all references to unscope association chains from other objects to the Product & Variant objects in places where unscope is used explicitly to work-around the default scope problem. (This is a big win because it makes the associations cleaner and easier to work with.)\r\n\r\n- [x] Finally, although it is slightly counter-intuitive, I have actually left the deleted_at fields in place (although their data will be moved to discontinued_at field and their values will be reset to NULL in the db migration). You can (and should!) use deleting to remove human-error mistakes (real mistakes) before the items get sold in your store, or in the case when you have duplicate slugs (Products) or SKUs (Variants) in your database. In those special cases only, you should continue to use delete. In all other cases, use the new discontinue_on feature. \r\n\r\n- [x] You can only delete a Product or Variant if no orders have been placed for that product/variant. Once the variant is associated with a Line item, it can never be \"deleted,\" and instead you must use the new discontinue_on feature. Model-level checks (```before_destroy```) enforce this.\r\n\r\n- [x] Note: The DB migration should fix your database correctly unless you have created new Products & Variants with matching slugs/SKUs of deleted records. In this case, you must use the included ```rake db:fix_orphan_line_items``` task to clean up your records. Both the schema migration and the script are very pro-active in helping you fix your own database. \r\n\r\n- [x] Not only do we fix *all* product & variant ```undefined method XYZ for nil``` bugs (and all the bugs yet-to-be-created), but we also get a new feature: You can now schedule a product or variant to be discontinued in the future. Neato!\r\n\r\n- [ ] The Admin interface now shows a distinction between discontinued & deleted, as well as options for searching for discontinued products. \r\n- [ ] When I delete a Product or Variant that has associated line items, fail gracefully in admin\r\n- [ ] update Spree's release notes https://github.com/spree/spree/blob/master/guides/content/release_notes/3_1_0.md","created_at":"2015-08-24T16:23:41Z","updated_at":"2015-09-12T19:12:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f1bcf3266922f784e2fde4c4331419971a5d320e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/spree/spree/pulls/6692/commits","review_comments_url":"https://api.github.com/repos/spree/spree/pulls/6692/comments","review_comment_url":"https://api.github.com/repos/spree/spree/pulls/comments{/number}","comments_url":"https://api.github.com/repos/spree/spree/issues/6692/comments","statuses_url":"https://api.github.com/repos/spree/spree/statuses/e0918417ccfa706813060dd969b817467fa3672a","head":{"label":"jasonfb:add-discontinued-at-to-products-and-variants","ref":"add-discontinued-at-to-products-and-variants","sha":"e0918417ccfa706813060dd969b817467fa3672a","user":{"login":"jasonfb","id":59002,"avatar_url":"https://avatars.githubusercontent.com/u/59002?v=3","gravatar_id":"","url":"https://api.github.com/users/jasonfb","html_url":"https://github.com/jasonfb","followers_url":"https://api.github.com/users/jasonfb/followers","following_url":"https://api.github.com/users/jasonfb/following{/other_user}","gists_url":"https://api.github.com/users/jasonfb/gists{/gist_id}","starred_url":"https://api.github.com/users/jasonfb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasonfb/subscriptions","organizations_url":"https://api.github.com/users/jasonfb/orgs","repos_url":"https://api.github.com/users/jasonfb/repos","events_url":"https://api.github.com/users/jasonfb/events{/privacy}","received_events_url":"https://api.github.com/users/jasonfb/received_events","type":"User","site_admin":false},"repo":{"id":41037228,"name":"spree","full_name":"jasonfb/spree","owner":{"login":"jasonfb","id":59002,"avatar_url":"https://avatars.githubusercontent.com/u/59002?v=3","gravatar_id":"","url":"https://api.github.com/users/jasonfb","html_url":"https://github.com/jasonfb","followers_url":"https://api.github.com/users/jasonfb/followers","following_url":"https://api.github.com/users/jasonfb/following{/other_user}","gists_url":"https://api.github.com/users/jasonfb/gists{/gist_id}","starred_url":"https://api.github.com/users/jasonfb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasonfb/subscriptions","organizations_url":"https://api.github.com/users/jasonfb/orgs","repos_url":"https://api.github.com/users/jasonfb/repos","events_url":"https://api.github.com/users/jasonfb/events{/privacy}","received_events_url":"https://api.github.com/users/jasonfb/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jasonfb/spree","description":"Spree is a complete open source e-commerce solution for Ruby on Rails.","fork":true,"url":"https://api.github.com/repos/jasonfb/spree","forks_url":"https://api.github.com/repos/jasonfb/spree/forks","keys_url":"https://api.github.com/repos/jasonfb/spree/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jasonfb/spree/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jasonfb/spree/teams","hooks_url":"https://api.github.com/repos/jasonfb/spree/hooks","issue_events_url":"https://api.github.com/repos/jasonfb/spree/issues/events{/number}","events_url":"https://api.github.com/repos/jasonfb/spree/events","assignees_url":"https://api.github.com/repos/jasonfb/spree/assignees{/user}","branches_url":"https://api.github.com/repos/jasonfb/spree/branches{/branch}","tags_url":"https://api.github.com/repos/jasonfb/spree/tags","blobs_url":"https://api.github.com/repos/jasonfb/spree/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jasonfb/spree/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jasonfb/spree/git/refs{/sha}","trees_url":"https://api.github.com/repos/jasonfb/spree/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jasonfb/spree/statuses/{sha}","languages_url":"https://api.github.com/repos/jasonfb/spree/languages","stargazers_url":"https://api.github.com/repos/jasonfb/spree/stargazers","contributors_url":"https://api.github.com/repos/jasonfb/spree/contributors","subscribers_url":"https://api.github.com/repos/jasonfb/spree/subscribers","subscription_url":"https://api.github.com/repos/jasonfb/spree/subscription","commits_url":"https://api.github.com/repos/jasonfb/spree/commits{/sha}","git_commits_url":"https://api.github.com/repos/jasonfb/spree/git/commits{/sha}","comments_url":"https://api.github.com/repos/jasonfb/spree/comments{/number}","issue_comment_url":"https://api.github.com/repos/jasonfb/spree/issues/comments{/number}","contents_url":"https://api.github.com/repos/jasonfb/spree/contents/{+path}","compare_url":"https://api.github.com/repos/jasonfb/spree/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jasonfb/spree/merges","archive_url":"https://api.github.com/repos/jasonfb/spree/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jasonfb/spree/downloads","issues_url":"https://api.github.com/repos/jasonfb/spree/issues{/number}","pulls_url":"https://api.github.com/repos/jasonfb/spree/pulls{/number}","milestones_url":"https://api.github.com/repos/jasonfb/spree/milestones{/number}","notifications_url":"https://api.github.com/repos/jasonfb/spree/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jasonfb/spree/labels{/name}","releases_url":"https://api.github.com/repos/jasonfb/spree/releases{/id}","created_at":"2015-08-19T13:52:33Z","updated_at":"2015-08-19T13:52:40Z","pushed_at":"2015-09-12T19:12:17Z","git_url":"git://github.com/jasonfb/spree.git","ssh_url":"git@github.com:jasonfb/spree.git","clone_url":"https://github.com/jasonfb/spree.git","svn_url":"https://github.com/jasonfb/spree","homepage":"http://spreecommerce.com","size":75172,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"spree:master","ref":"master","sha":"20c89b98626a48670353b625098344768a9b58e5","user":{"login":"spree","id":56702,"avatar_url":"https://avatars.githubusercontent.com/u/56702?v=3","gravatar_id":"","url":"https://api.github.com/users/spree","html_url":"https://github.com/spree","followers_url":"https://api.github.com/users/spree/followers","following_url":"https://api.github.com/users/spree/following{/other_user}","gists_url":"https://api.github.com/users/spree/gists{/gist_id}","starred_url":"https://api.github.com/users/spree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spree/subscriptions","organizations_url":"https://api.github.com/users/spree/orgs","repos_url":"https://api.github.com/users/spree/repos","events_url":"https://api.github.com/users/spree/events{/privacy}","received_events_url":"https://api.github.com/users/spree/received_events","type":"Organization","site_admin":false},"repo":{"id":3314,"name":"spree","full_name":"spree/spree","owner":{"login":"spree","id":56702,"avatar_url":"https://avatars.githubusercontent.com/u/56702?v=3","gravatar_id":"","url":"https://api.github.com/users/spree","html_url":"https://github.com/spree","followers_url":"https://api.github.com/users/spree/followers","following_url":"https://api.github.com/users/spree/following{/other_user}","gists_url":"https://api.github.com/users/spree/gists{/gist_id}","starred_url":"https://api.github.com/users/spree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spree/subscriptions","organizations_url":"https://api.github.com/users/spree/orgs","repos_url":"https://api.github.com/users/spree/repos","events_url":"https://api.github.com/users/spree/events{/privacy}","received_events_url":"https://api.github.com/users/spree/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/spree/spree","description":"Spree is a complete open source e-commerce solution for Ruby on Rails.","fork":false,"url":"https://api.github.com/repos/spree/spree","forks_url":"https://api.github.com/repos/spree/spree/forks","keys_url":"https://api.github.com/repos/spree/spree/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spree/spree/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spree/spree/teams","hooks_url":"https://api.github.com/repos/spree/spree/hooks","issue_events_url":"https://api.github.com/repos/spree/spree/issues/events{/number}","events_url":"https://api.github.com/repos/spree/spree/events","assignees_url":"https://api.github.com/repos/spree/spree/assignees{/user}","branches_url":"https://api.github.com/repos/spree/spree/branches{/branch}","tags_url":"https://api.github.com/repos/spree/spree/tags","blobs_url":"https://api.github.com/repos/spree/spree/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spree/spree/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spree/spree/git/refs{/sha}","trees_url":"https://api.github.com/repos/spree/spree/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spree/spree/statuses/{sha}","languages_url":"https://api.github.com/repos/spree/spree/languages","stargazers_url":"https://api.github.com/repos/spree/spree/stargazers","contributors_url":"https://api.github.com/repos/spree/spree/contributors","subscribers_url":"https://api.github.com/repos/spree/spree/subscribers","subscription_url":"https://api.github.com/repos/spree/spree/subscription","commits_url":"https://api.github.com/repos/spree/spree/commits{/sha}","git_commits_url":"https://api.github.com/repos/spree/spree/git/commits{/sha}","comments_url":"https://api.github.com/repos/spree/spree/comments{/number}","issue_comment_url":"https://api.github.com/repos/spree/spree/issues/comments{/number}","contents_url":"https://api.github.com/repos/spree/spree/contents/{+path}","compare_url":"https://api.github.com/repos/spree/spree/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spree/spree/merges","archive_url":"https://api.github.com/repos/spree/spree/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spree/spree/downloads","issues_url":"https://api.github.com/repos/spree/spree/issues{/number}","pulls_url":"https://api.github.com/repos/spree/spree/pulls{/number}","milestones_url":"https://api.github.com/repos/spree/spree/milestones{/number}","notifications_url":"https://api.github.com/repos/spree/spree/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spree/spree/labels{/name}","releases_url":"https://api.github.com/repos/spree/spree/releases{/id}","created_at":"2008-03-10T14:45:35Z","updated_at":"2015-09-12T08:10:03Z","pushed_at":"2015-09-12T19:12:19Z","git_url":"git://github.com/spree/spree.git","ssh_url":"git@github.com:spree/spree.git","clone_url":"https://github.com/spree/spree.git","svn_url":"https://github.com/spree/spree","homepage":"http://spreecommerce.com","size":139041,"stargazers_count":6701,"watchers_count":6701,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3168,"mirror_url":null,"open_issues_count":178,"forks":3168,"open_issues":178,"watchers":6701,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/spree/spree/pulls/6692"},"html":{"href":"https://github.com/spree/spree/pull/6692"},"issue":{"href":"https://api.github.com/repos/spree/spree/issues/6692"},"comments":{"href":"https://api.github.com/repos/spree/spree/issues/6692/comments"},"review_comments":{"href":"https://api.github.com/repos/spree/spree/pulls/6692/comments"},"review_comment":{"href":"https://api.github.com/repos/spree/spree/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/spree/spree/pulls/6692/commits"},"statuses":{"href":"https://api.github.com/repos/spree/spree/statuses/e0918417ccfa706813060dd969b817467fa3672a"}}}}