workarea-registries 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +9 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +58 -0
- data/app/assets/images/workarea/storefront/registries/icons/email.svg +4 -0
- data/app/assets/images/workarea/storefront/registries/icons/link.svg +1 -0
- data/app/assets/javascripts/workarea/storefront/registries/modules/add_to_registry_button.js +49 -0
- data/app/assets/javascripts/workarea/storefront/registries/modules/copy_registry_url_button.js +51 -0
- data/app/assets/javascripts/workarea/storefront/registries/modules/toggle_registry_address.js +58 -0
- data/app/assets/javascripts/workarea/storefront/registries/templates/add_to_registry_button.jst.ejs +3 -0
- data/app/controllers/workarea/admin/registries_controller.rb +28 -0
- data/app/controllers/workarea/storefront/cart_items_controller.decorator +14 -0
- data/app/controllers/workarea/storefront/registries_controller.rb +47 -0
- data/app/controllers/workarea/storefront/users/registries_controller.rb +94 -0
- data/app/controllers/workarea/storefront/users/registry_items_controller.rb +67 -0
- data/app/controllers/workarea/storefront/users/registry_shares_controller.rb +43 -0
- data/app/controllers/workarea/storefront/users/registry_thanks_controller.rb +41 -0
- data/app/helpers/workarea/registries_helper.rb +41 -0
- data/app/mailers/workarea/storefront/registries_mailer.rb +52 -0
- data/app/models/workarea/checkout/steps/addresses.decorator +28 -0
- data/app/models/workarea/order.decorator +7 -0
- data/app/models/workarea/order/item.decorator +11 -0
- data/app/models/workarea/registry.rb +129 -0
- data/app/models/workarea/registry/item.rb +39 -0
- data/app/models/workarea/registry/public_search.rb +23 -0
- data/app/models/workarea/registry/share.rb +46 -0
- data/app/models/workarea/registry/thank.rb +26 -0
- data/app/models/workarea/search/admin/registry.rb +29 -0
- data/app/queries/workarea/search/admin_orders.decorator +7 -0
- data/app/queries/workarea/search/admin_registries.rb +16 -0
- data/app/seeds/workarea/registry_content_seeds.rb +50 -0
- data/app/services/workarea/inventory_adjustment.decorator +43 -0
- data/app/view_models/workarea/admin/registry_view_model.rb +19 -0
- data/app/view_models/workarea/storefront/checkout/addresses_view_model.decorator +17 -0
- data/app/view_models/workarea/storefront/order_item_view_model.decorator +9 -0
- data/app/view_models/workarea/storefront/order_view_model.decorator +8 -0
- data/app/view_models/workarea/storefront/registry_email_view_model.rb +37 -0
- data/app/view_models/workarea/storefront/registry_item_view_model.rb +72 -0
- data/app/view_models/workarea/storefront/registry_share_view_model.rb +6 -0
- data/app/view_models/workarea/storefront/registry_thank_view_model.rb +6 -0
- data/app/view_models/workarea/storefront/registry_view_model.rb +74 -0
- data/app/view_models/workarea/storefront/user_view_model.decorator +12 -0
- data/app/views/workarea/admin/registries/_menu.html.haml +1 -0
- data/app/views/workarea/admin/registries/index.html.haml +70 -0
- data/app/views/workarea/storefront/carts/_registry_details.html.haml +3 -0
- data/app/views/workarea/storefront/checkouts/_registry_address.html.haml +13 -0
- data/app/views/workarea/storefront/checkouts/_registry_address_fields.html.haml +28 -0
- data/app/views/workarea/storefront/checkouts/_registry_details.html.haml +3 -0
- data/app/views/workarea/storefront/products/_add_to_registry_placeholder.html.haml +1 -0
- data/app/views/workarea/storefront/registries/_registries_link.html.haml +4 -0
- data/app/views/workarea/storefront/registries/index.html.haml +39 -0
- data/app/views/workarea/storefront/registries/search.html.haml +35 -0
- data/app/views/workarea/storefront/registries/show.html.haml +63 -0
- data/app/views/workarea/storefront/registries_mailer/creation.html.haml +13 -0
- data/app/views/workarea/storefront/registries_mailer/gift_receipt.html.haml +51 -0
- data/app/views/workarea/storefront/registries_mailer/share.html.haml +21 -0
- data/app/views/workarea/storefront/registries_mailer/thank.html.haml +19 -0
- data/app/views/workarea/storefront/users/_registry_count.json.jbuilder +3 -0
- data/app/views/workarea/storefront/users/accounts/_registries.html.haml +14 -0
- data/app/views/workarea/storefront/users/registries/_form.html.haml +56 -0
- data/app/views/workarea/storefront/users/registries/_radio_list.html.haml +8 -0
- data/app/views/workarea/storefront/users/registries/_summary.html.haml +12 -0
- data/app/views/workarea/storefront/users/registries/edit.html.haml +4 -0
- data/app/views/workarea/storefront/users/registries/index.html.haml +24 -0
- data/app/views/workarea/storefront/users/registries/new.html.haml +4 -0
- data/app/views/workarea/storefront/users/registries/show.html.haml +75 -0
- data/app/views/workarea/storefront/users/registry_items/new.html.haml +10 -0
- data/app/views/workarea/storefront/users/registry_items/templates/_purchased.html.haml +29 -0
- data/app/views/workarea/storefront/users/registry_items/templates/_unpurchased.html.haml +35 -0
- data/app/views/workarea/storefront/users/registry_shares/_share_buttons.html.haml +18 -0
- data/app/views/workarea/storefront/users/registry_shares/new.html.haml +49 -0
- data/app/views/workarea/storefront/users/registry_thanks/new.html.haml +47 -0
- data/app/workers/workarea/send_gift_receipt.rb +15 -0
- data/app/workers/workarea/send_registry_creation_email.rb +12 -0
- data/app/workers/workarea/update_registry_items.rb +24 -0
- data/bin/rails +20 -0
- data/config/initializers/appends.rb +54 -0
- data/config/initializers/fields.rb +19 -0
- data/config/locales/en.yml +133 -0
- data/config/routes.rb +26 -0
- data/lib/workarea/mailer_previews/storefront/registry_mailer_preview.rb +25 -0
- data/lib/workarea/registries.rb +11 -0
- data/lib/workarea/registries/engine.rb +17 -0
- data/lib/workarea/registries/version.rb +5 -0
- data/test/dummy/config/application.rb +27 -0
- data/test/factories/workarea/registry.rb +67 -0
- data/test/integration/workarea/storefront/cart_item_registry_integration_test.rb +104 -0
- data/test/integration/workarea/storefront/checkout_registry_integration_test.rb +156 -0
- data/test/integration/workarea/storefront/registry_privacy_integration_test.rb +48 -0
- data/test/integration/workarea/storefront/user_registries_integration_test.rb +50 -0
- data/test/integration/workarea/storefront/user_registry_items_integration_test.rb +106 -0
- data/test/models/workarea/registry/item_test.rb +22 -0
- data/test/models/workarea/registry/public_search_test.rb +48 -0
- data/test/models/workarea/registry_test.rb +113 -0
- data/test/system/workarea/admin/registry_system_test.rb +20 -0
- data/test/workers/update_registry_items_test.rb +45 -0
- data/workarea-registries.gemspec +20 -0
- metadata +156 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
class Users::RegistryItemsController < ApplicationController
|
4
|
+
before_action :require_login
|
5
|
+
before_action :find_registry, except: [:new]
|
6
|
+
before_action :find_item, except: [:new, :create, :update]
|
7
|
+
|
8
|
+
def new
|
9
|
+
@registries = RegistryViewModel.wrap(
|
10
|
+
Registry.for_user(current_user.id),
|
11
|
+
view_model_options
|
12
|
+
)
|
13
|
+
|
14
|
+
@product = ProductViewModel.wrap(
|
15
|
+
Catalog::Product.find(params[:product_id]),
|
16
|
+
view_model_options
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
if @registry.add_item(item_params)
|
22
|
+
flash[:success] = t('workarea.storefront.flash_messages.registry_item_created')
|
23
|
+
else
|
24
|
+
flash[:error] = t('workarea.storefront.flash_messages.registry_item_not_created')
|
25
|
+
end
|
26
|
+
|
27
|
+
redirect_to users_registry_path(@registry)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
if @registry.update_item(params[:id], item_params)
|
32
|
+
flash[:success] = t('workarea.storefront.flash_messages.registry_item_updated')
|
33
|
+
else
|
34
|
+
flash[:error] = t('workarea.storefront.flash_messages.registry_item_not_updated')
|
35
|
+
end
|
36
|
+
|
37
|
+
redirect_to users_registry_path(@registry)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
if @item.unpurchased?
|
42
|
+
@registry.remove_item(params[:id])
|
43
|
+
flash[:success] = t('workarea.storefront.flash_messages.registry_item_destroyed')
|
44
|
+
else
|
45
|
+
flash[:error] = t('workarea.storefront.flash_messages.registry_item_not_destroyed')
|
46
|
+
end
|
47
|
+
|
48
|
+
redirect_to users_registry_path(@registry)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def item_params
|
54
|
+
params.permit(:product_id, :sku, :quantity, :purchased_by)
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_registry
|
58
|
+
@registry =
|
59
|
+
Registry.for_user(current_user.id).find(params[:registry_id])
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_item
|
63
|
+
@item = @registry.items.find(params[:id])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
class Users::RegistrySharesController < Storefront::ApplicationController
|
4
|
+
before_action :find_registry_share
|
5
|
+
|
6
|
+
def new
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
if @registry_share.save
|
11
|
+
flash[:success] = t(
|
12
|
+
'workarea.storefront.flash_messages.registry_share_message_sent',
|
13
|
+
recipient: @registry_share.friendly_to
|
14
|
+
)
|
15
|
+
RegistriesMailer.share(@registry_share.id.to_s).deliver_later
|
16
|
+
redirect_to users_registry_path(@registry_share.registry_id)
|
17
|
+
else
|
18
|
+
render :new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_registry_share
|
25
|
+
model = Registry::Share.new(share_params)
|
26
|
+
@registry_share = RegistryShareViewModel.wrap(model, view_model_options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def share_params
|
30
|
+
params.permit(
|
31
|
+
:url,
|
32
|
+
:registry_id,
|
33
|
+
:registry_name,
|
34
|
+
:to_email,
|
35
|
+
:to_name,
|
36
|
+
:from_email,
|
37
|
+
:from_name,
|
38
|
+
:message
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
class Users::RegistryThanksController < Storefront::ApplicationController
|
4
|
+
before_action :find_registry_thank
|
5
|
+
|
6
|
+
def new
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
if @registry_thank.save
|
11
|
+
flash[:success] = t(
|
12
|
+
'workarea.storefront.flash_messages.registry_thank_message_sent',
|
13
|
+
recipient: @registry_thank.friendly_to
|
14
|
+
)
|
15
|
+
RegistriesMailer.thank(@registry_thank.id.to_s).deliver_later
|
16
|
+
redirect_to users_registry_path(@registry_thank.registry_id, state: 'unpurchased')
|
17
|
+
else
|
18
|
+
render :new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_registry_thank
|
25
|
+
model = Registry::Thank.new(thank_params)
|
26
|
+
@registry_thank = RegistryThankViewModel.wrap(model, view_model_options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def thank_params
|
30
|
+
params.permit(
|
31
|
+
:registry_id,
|
32
|
+
:to_email,
|
33
|
+
:to_name,
|
34
|
+
:from_email,
|
35
|
+
:from_name,
|
36
|
+
:message
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Workarea
|
2
|
+
module RegistriesHelper
|
3
|
+
def minimum_quantity(received)
|
4
|
+
received.positive? ? received : 1
|
5
|
+
end
|
6
|
+
|
7
|
+
def share_registry_url(registry, options = {})
|
8
|
+
registry_url(registry, options.merge(host: Workarea.config.host))
|
9
|
+
end
|
10
|
+
|
11
|
+
def facebook_query_string(url)
|
12
|
+
hash_without_blanks_to_query(u: url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def twitter_query_string(url, message = nil)
|
16
|
+
hash_without_blanks_to_query(url: url, text: message)
|
17
|
+
end
|
18
|
+
|
19
|
+
def share_query_string(url, message = nil, image = nil)
|
20
|
+
hash_without_blanks_to_query(
|
21
|
+
url: url,
|
22
|
+
description: message,
|
23
|
+
media: image
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def share_analytics_data(type, url)
|
28
|
+
{
|
29
|
+
event: 'share',
|
30
|
+
domEvent: 'click',
|
31
|
+
payload: { type: type, url: url }
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def hash_without_blanks_to_query(hash)
|
38
|
+
hash.delete_if { |_k, v| v.blank? }.to_query
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
class RegistriesMailer < ApplicationMailer
|
4
|
+
def creation(id)
|
5
|
+
@registry = RegistryViewModel.wrap(Registry.find(id))
|
6
|
+
@content = Content::Email.find_content('registry_creation')
|
7
|
+
|
8
|
+
mail(
|
9
|
+
to: @registry.email,
|
10
|
+
from: Workarea.config.email_from,
|
11
|
+
subject: t('workarea.storefront.email.registries.creation.subject')
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def share(id)
|
16
|
+
@share = RegistryShareViewModel.wrap(Registry::Share.find(id))
|
17
|
+
@content = Content::Email.find_content('registry_share')
|
18
|
+
|
19
|
+
mail(
|
20
|
+
to: @share.friendly_to,
|
21
|
+
from: Workarea.config.email_from,
|
22
|
+
reply_to: @share.from,
|
23
|
+
subject: t('workarea.storefront.email.registries.share.subject', from: @share.friendly_from)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def thank(id)
|
28
|
+
@thank = RegistryThankViewModel.wrap(Registry::Thank.find(id))
|
29
|
+
@content = Content::Email.find_content('registry_thank')
|
30
|
+
|
31
|
+
mail(
|
32
|
+
to: @thank.friendly_to,
|
33
|
+
from: Workarea.config.email_from,
|
34
|
+
reply_to: @thank.from,
|
35
|
+
subject: t('workarea.storefront.email.registries.thank.subject', from: @thank.friendly_from)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def gift_receipt(order_id)
|
40
|
+
order = Order.find(order_id)
|
41
|
+
@order = Storefront::OrderViewModel.wrap(order)
|
42
|
+
@content = Content::Email.find_content('registry_gift_receipt')
|
43
|
+
|
44
|
+
mail(
|
45
|
+
to: @order.registry_email,
|
46
|
+
from: Workarea.config.email_from,
|
47
|
+
subject: t('workarea.storefront.email.registries.gift_receipt.subject')
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Checkout::Steps::Addresses, with: :registries do
|
3
|
+
|
4
|
+
def update(params = {})
|
5
|
+
set_ship_to_registrant(params)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def set_shipping_address(params)
|
12
|
+
if params[:ship_to_registrant].present? && params[:ship_to_registrant] == 'true'
|
13
|
+
if order.requires_shipping? && params[:registry_shipping_address].present?
|
14
|
+
shipping.set_address(params[:registry_shipping_address])
|
15
|
+
set_default_shipping_option
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_ship_to_registrant(params)
|
23
|
+
if params[:ship_to_registrant].present?
|
24
|
+
order.ship_to_registrant = params[:ship_to_registrant]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Registry
|
3
|
+
include ApplicationDocument
|
4
|
+
|
5
|
+
field :user_id, type: String
|
6
|
+
field :email, type: String
|
7
|
+
field :name, type: String
|
8
|
+
field :description, type: String
|
9
|
+
field :privacy, type: String, default: 'public'
|
10
|
+
field :event_date, type: Date
|
11
|
+
|
12
|
+
embeds_many :items,
|
13
|
+
class_name: 'Workarea::Registry::Item',
|
14
|
+
cascade_callbacks: true,
|
15
|
+
inverse_of: :registry
|
16
|
+
|
17
|
+
embeds_one :address,
|
18
|
+
class_name: 'Workarea::Address',
|
19
|
+
as: :addressable
|
20
|
+
|
21
|
+
validates :name, presence: true
|
22
|
+
validates :user_id, presence: true
|
23
|
+
validates :email, presence: true
|
24
|
+
validates :privacy, presence: true,
|
25
|
+
inclusion: { in: %w(public shared private) }
|
26
|
+
|
27
|
+
scope :for_user, ->(user_id) { where(user_id: user_id.to_s) }
|
28
|
+
scope :by_activity, -> { order(updated_at: :desc) }
|
29
|
+
|
30
|
+
index({ user_id: 1 })
|
31
|
+
index('items.product_id' => 1)
|
32
|
+
index(name: 1)
|
33
|
+
index(email: 1)
|
34
|
+
index(id: 1)
|
35
|
+
|
36
|
+
def self.search(query)
|
37
|
+
Workarea::Registry::PublicSearch.new(
|
38
|
+
query
|
39
|
+
).results
|
40
|
+
end
|
41
|
+
|
42
|
+
%w(public shared private).each do |privacy_level|
|
43
|
+
define_method "#{privacy_level}?" do
|
44
|
+
privacy == privacy_level
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_privacy?
|
49
|
+
public? || privacy.blank?
|
50
|
+
end
|
51
|
+
|
52
|
+
def purchased_items
|
53
|
+
items.select(&:purchased_by?)
|
54
|
+
end
|
55
|
+
|
56
|
+
def unpurchased_items
|
57
|
+
items.reject(&:purchased_by?)
|
58
|
+
end
|
59
|
+
|
60
|
+
def available_items
|
61
|
+
items.reject(&:purchased_by?).select(&:available?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_item(params)
|
65
|
+
quantity = params.fetch(:quantity, 1).to_i
|
66
|
+
existing_item = find_existing_item(sku: params[:sku])
|
67
|
+
|
68
|
+
if existing_item.present?
|
69
|
+
update_item(
|
70
|
+
existing_item.id,
|
71
|
+
quantity: existing_item.quantity + quantity
|
72
|
+
)
|
73
|
+
else
|
74
|
+
items.build(params)
|
75
|
+
end
|
76
|
+
|
77
|
+
save
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_purchased_item(params)
|
81
|
+
existing_purchased_item = find_existing_purchased_item(params)
|
82
|
+
|
83
|
+
if existing_purchased_item
|
84
|
+
quantity = existing_purchased_item.quantity + params[:quantity]
|
85
|
+
existing_purchased_item.update(quantity: quantity)
|
86
|
+
else
|
87
|
+
items.build(params)
|
88
|
+
end
|
89
|
+
|
90
|
+
save
|
91
|
+
end
|
92
|
+
|
93
|
+
def update_item(id, params)
|
94
|
+
sku = params[:sku]
|
95
|
+
existing_item = find_existing_item(sku: sku) if sku.present?
|
96
|
+
|
97
|
+
if existing_item.present? && existing_item.id.to_s != id.to_s
|
98
|
+
item = items.find(id)
|
99
|
+
quantity = existing_item.quantity + (params[:quantity] || item.quantity)
|
100
|
+
|
101
|
+
existing_item.update(quantity: quantity) && item.delete
|
102
|
+
else
|
103
|
+
items.find(id).update(params)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def remove_item(id)
|
108
|
+
item = items.detect { |item| item.id.to_s == id.to_s }
|
109
|
+
item.nil? || item.destroy!
|
110
|
+
end
|
111
|
+
|
112
|
+
def mark_item_received(item)
|
113
|
+
existing_item = find_existing_item(sku: item.sku)
|
114
|
+
|
115
|
+
if existing_item
|
116
|
+
received = existing_item.received + item.quantity
|
117
|
+
existing_item.update(received: received)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_existing_item(params = {})
|
122
|
+
unpurchased_items.detect { |item| item.sku == params[:sku] }
|
123
|
+
end
|
124
|
+
|
125
|
+
def find_existing_purchased_item(params = {})
|
126
|
+
items.detect { |item| item.sku == params[:sku] && item.purchased_by == params[:purchased_by] }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Registry
|
3
|
+
class Item
|
4
|
+
include ApplicationDocument
|
5
|
+
|
6
|
+
field :product_id, type: String
|
7
|
+
field :sku, type: String
|
8
|
+
field :quantity, type: Integer, default: 1
|
9
|
+
field :received, type: Integer, default: 0
|
10
|
+
field :purchased_by, type: String
|
11
|
+
|
12
|
+
embedded_in :registry,
|
13
|
+
class_name: 'Workarea::Registry',
|
14
|
+
touch: true
|
15
|
+
|
16
|
+
validates :product_id, presence: true
|
17
|
+
validates :sku, presence: true
|
18
|
+
validates :quantity, presence: true, numericality: { greater_than: 0 }
|
19
|
+
|
20
|
+
index(product_id: 1)
|
21
|
+
|
22
|
+
def available?
|
23
|
+
quantity > received
|
24
|
+
end
|
25
|
+
|
26
|
+
def received?
|
27
|
+
received.positive?
|
28
|
+
end
|
29
|
+
|
30
|
+
def purchased?
|
31
|
+
purchased_by.present?
|
32
|
+
end
|
33
|
+
|
34
|
+
def unpurchased?
|
35
|
+
!purchased? && !received?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|