caboose-cms 0.8.78 → 0.8.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,8 +20,7 @@ class Caboose::CorePlugin < Caboose::CaboosePlugin
20
20
  item['children'] << { 'id' => 'smtp' , 'text' => 'SMTP (Mail)' , 'href' => '/admin/smtp' , 'modal' => false } if user.is_allowed('smtp' , 'view')
21
21
  item['children'] << { 'id' => 'social' , 'text' => 'Social Media' , 'href' => '/admin/social' , 'modal' => false } if user.is_allowed('social' , 'view')
22
22
  item['children'] << { 'id' => 'store' , 'text' => 'Store' , 'href' => '/admin/store' , 'modal' => false } if user.is_allowed('store' , 'view') if site.use_store == true
23
- item['children'] << { 'id' => 'users' , 'text' => 'Users' , 'href' => '/admin/users' , 'modal' => false } if user.is_allowed('users' , 'view')
24
- item['children'] << { 'id' => 'usersubscriptions' , 'text' => 'User Subscriptions' , 'href' => '/admin/user-subscriptions' , 'modal' => false } if user.is_allowed('usersubscriptions' , 'view')
23
+ item['children'] << { 'id' => 'users' , 'text' => 'Users' , 'href' => '/admin/users' , 'modal' => false } if user.is_allowed('users' , 'view')
25
24
  item['children'] << { 'id' => 'subscriptions' , 'text' => 'Subscriptions' , 'href' => '/admin/subscriptions' , 'modal' => false } if user.is_allowed('subscriptions' , 'view')
26
25
  item['children'] << { 'id' => 'variables' , 'text' => 'Variables' , 'href' => '/admin/settings' , 'modal' => false } if user.is_allowed('settings' , 'view')
27
26
  nav << item if item['children'].count > 0
@@ -4,6 +4,7 @@ module Caboose
4
4
 
5
5
  belongs_to :variant
6
6
  belongs_to :invoice
7
+ belongs_to :subscription
7
8
  belongs_to :invoice_package, :class_name => 'InvoicePackage'
8
9
  belongs_to :parent, :class_name => 'LineItem', :foreign_key => 'parent_id'
9
10
  has_many :children, :class_name => 'LineItem', :foreign_key => 'parent_id'
@@ -25,7 +26,7 @@ module Caboose
25
26
  :gift_message ,
26
27
  :gift_wrap ,
27
28
  :hide_prices ,
28
- :user_subscription_id ,
29
+ :subscription_id ,
29
30
  :date_starts ,
30
31
  :date_ends
31
32
 
@@ -87,7 +87,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
87
87
  :amount_discounted ,
88
88
  :auth_code ,
89
89
  :date_shipped ,
90
- :decremented
90
+ :decremented ,
91
+ :user_subscription_id
91
92
  ],
92
93
  #Caboose::PageCache => [:block],
93
94
  #Caboose::RetargetingConfig => [:fb_pixels_function],
@@ -341,7 +342,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
341
342
  [ :gift_message , :text ],
342
343
  [ :gift_wrap , :boolean , { :default => false }],
343
344
  [ :hide_prices , :boolean , { :default => false }],
344
- [ :user_subscription_id , :integer ],
345
+ [ :subscription_id , :integer ],
345
346
  [ :date_starts , :date ],
346
347
  [ :date_ends , :date ]
347
348
  ],
@@ -436,6 +437,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
436
437
  [ :shipping_address_id , :integer ],
437
438
  [ :billing_address_id , :integer ],
438
439
  [ :notes , :text ],
440
+ [ :customer_notes , :text ],
439
441
  [ :status , :string ],
440
442
  [ :payment_terms , :string ],
441
443
  [ :date_created , :datetime ],
@@ -828,19 +830,12 @@ class Caboose::Schema < Caboose::Utilities::Schema
828
830
  [ :default_taxable , :boolean ],
829
831
  [ :allow_instore_pickup , :boolean , { :default => false }]
830
832
  ],
831
- Caboose::Subscription => [
832
- [ :site_id , :integer ],
833
- [ :name , :string ],
834
- [ :description , :text ],
835
- [ :variant_id , :integer ],
836
- [ :interval , :string ],
837
- [ :prorate , :boolean , { :default => false }],
838
- [ :prorate_method , :string ],
839
- [ :prorate_flat_amount , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
840
- [ :prorate_function , :text ],
841
- [ :start_on_day , :boolean , { :default => false }],
842
- [ :start_day , :integer ],
843
- [ :start_month , :integer ]
833
+ Caboose::Subscription => [
834
+ [ :variant_id , :integer ],
835
+ [ :user_id , :integer ],
836
+ [ :date_started , :date ],
837
+ [ :date_started_full, :date ],
838
+ [ :status , :string ]
844
839
  ],
845
840
  Caboose::User => [
846
841
  [ :site_id , :integer ],
@@ -874,59 +869,61 @@ class Caboose::Schema < Caboose::Utilities::Schema
874
869
  [ :card_brand , :string ],
875
870
  [ :card_exp_month , :integer ],
876
871
  [ :card_exp_year , :integer ]
877
- ],
878
- Caboose::UserSubscription => [
879
- [ :subscription_id , :integer ],
880
- [ :user_id , :integer ],
881
- [ :date_started , :date ],
882
- [ :date_started_full, :date ],
883
- [ :status , :string ]
884
- ],
872
+ ],
885
873
  Caboose::Variant => [
886
- [ :product_id , :integer ],
887
- [ :alternate_id , :string ],
888
- [ :sku , :string ],
889
- [ :barcode , :string ],
890
- [ :cost , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
891
- [ :price , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
892
- [ :sale_price , :decimal , { :precision => 8, :scale => 2 }],
893
- [ :date_sale_starts , :datetime ],
894
- [ :date_sale_ends , :datetime ],
895
- [ :date_sale_end , :datetime ],
896
- [ :clearance , :boolean , { :default => false }],
897
- [ :clearance_price , :decimal , { :precision => 8, :scale => 2 }],
898
- [ :available , :boolean , { :default => true }],
899
- [ :quantity_in_stock , :integer , { :default => 0 }],
900
- [ :ignore_quantity , :boolean , { :default => false }],
901
- [ :allow_backorder , :boolean , { :default => false }],
902
- [ :weight , :decimal ],
903
- [ :length , :decimal ],
904
- [ :width , :decimal ],
905
- [ :height , :decimal ],
906
- [ :volume , :decimal ],
907
- [ :cylinder , :boolean , { :default => false }],
908
- [ :option1 , :string ],
909
- [ :option2 , :string ],
910
- [ :option3 , :string ],
911
- [ :option1_media_id , :integer ],
912
- [ :option2_media_id , :integer ],
913
- [ :option3_media_id , :integer ],
914
- [ :requires_shipping , :boolean , { :default => true }],
915
- [ :taxable , :boolean , { :default => true }],
916
- [ :shipping_unit_value , :numeric ],
917
- [ :flat_rate_shipping , :boolean , { :default => false }],
918
- [ :flat_rate_shipping_package_id , :integer ],
919
- [ :flat_rate_shipping_method_id , :integer ],
920
- [ :flat_rate_shipping_single , :decimal , { :precision => 8, :scale => 2, :default => 0.0 }],
921
- [ :flat_rate_shipping_combined , :decimal , { :precision => 8, :scale => 2, :default => 0.0 }],
922
- [ :status , :string ],
923
- [ :option1_sort_order , :integer , { :default => 0 }],
924
- [ :option2_sort_order , :integer , { :default => 0 }],
925
- [ :option3_sort_order , :integer , { :default => 0 }],
926
- [ :sort_order , :integer , { :default => 0 }],
927
- [ :downloadable , :boolean , { :default => false }],
928
- [ :download_path , :string ],
929
- [ :is_bundle , :boolean , { :default => false }]
874
+ [ :product_id , :integer ],
875
+ [ :alternate_id , :string ],
876
+ [ :sku , :string ],
877
+ [ :barcode , :string ],
878
+ [ :cost , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
879
+ [ :price , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
880
+ [ :sale_price , :decimal , { :precision => 8, :scale => 2 }],
881
+ [ :date_sale_starts , :datetime ],
882
+ [ :date_sale_ends , :datetime ],
883
+ [ :date_sale_end , :datetime ],
884
+ [ :clearance , :boolean , { :default => false }],
885
+ [ :clearance_price , :decimal , { :precision => 8, :scale => 2 }],
886
+ [ :available , :boolean , { :default => true }],
887
+ [ :quantity_in_stock , :integer , { :default => 0 }],
888
+ [ :ignore_quantity , :boolean , { :default => false }],
889
+ [ :allow_backorder , :boolean , { :default => false }],
890
+ [ :weight , :decimal ],
891
+ [ :length , :decimal ],
892
+ [ :width , :decimal ],
893
+ [ :height , :decimal ],
894
+ [ :volume , :decimal ],
895
+ [ :cylinder , :boolean , { :default => false }],
896
+ [ :option1 , :string ],
897
+ [ :option2 , :string ],
898
+ [ :option3 , :string ],
899
+ [ :option1_media_id , :integer ],
900
+ [ :option2_media_id , :integer ],
901
+ [ :option3_media_id , :integer ],
902
+ [ :requires_shipping , :boolean , { :default => true }],
903
+ [ :taxable , :boolean , { :default => true }],
904
+ [ :shipping_unit_value , :numeric ],
905
+ [ :flat_rate_shipping , :boolean , { :default => false }],
906
+ [ :flat_rate_shipping_package_id , :integer ],
907
+ [ :flat_rate_shipping_method_id , :integer ],
908
+ [ :flat_rate_shipping_single , :decimal , { :precision => 8, :scale => 2, :default => 0.0 }],
909
+ [ :flat_rate_shipping_combined , :decimal , { :precision => 8, :scale => 2, :default => 0.0 }],
910
+ [ :status , :string ],
911
+ [ :option1_sort_order , :integer , { :default => 0 }],
912
+ [ :option2_sort_order , :integer , { :default => 0 }],
913
+ [ :option3_sort_order , :integer , { :default => 0 }],
914
+ [ :sort_order , :integer , { :default => 0 }],
915
+ [ :downloadable , :boolean , { :default => false }],
916
+ [ :download_path , :string ],
917
+ [ :is_bundle , :boolean , { :default => false }],
918
+ [ :is_subscription , :boolean , { :default => false }],
919
+ [ :subscription_interval , :string ],
920
+ [ :subscription_prorate , :boolean , { :default => false }],
921
+ [ :subscription_prorate_method , :string ],
922
+ [ :subscription_prorate_flat_amount , :decimal , { :precision => 8, :scale => 2, :default => 0.00 }],
923
+ [ :subscription_prorate_function , :text ],
924
+ [ :subscription_start_on_day , :boolean , { :default => false }],
925
+ [ :subscription_start_day , :integer ],
926
+ [ :subscription_start_month , :integer ]
930
927
  ],
931
928
  Caboose::VariantChild => [
932
929
  [ :parent_id , :integer ],
@@ -4,29 +4,139 @@ module Caboose
4
4
  self.table_name = 'store_subscriptions'
5
5
  self.primary_key = 'id'
6
6
 
7
- belongs_to :site
8
7
  belongs_to :variant
9
- has_many :user_subscriptions
10
- attr_accessible :id ,
11
- :site_id ,
12
- :name ,
13
- :description ,
14
- :variant_id ,
15
- :interval ,
16
- :prorate ,
17
- :prorate_method ,
18
- :prorate_flat_amount ,
19
- :prorate_function ,
20
- :start_on_day ,
21
- :start_day ,
22
- :start_month
8
+ belongs_to :user
9
+ has_many :line_items
10
+ attr_accessible :id ,
11
+ :variant_id ,
12
+ :user_id ,
13
+ :date_started ,
14
+ :date_started_full ,
15
+ :status
16
+
17
+ STATUS_ACTIVE = 'active'
18
+ STATUS_INACTIVE = 'inactive'
23
19
 
24
- INTERVAL_MONTHLY = 'monthly'
25
- INTERVAL_YEARLY = 'yearly'
20
+ def calculate_date_started_full
21
+
22
+ v = self.variant
23
+ if !v.subscription_start_on_day
24
+ self.date_started_full = self.date_started
25
+ self.save
26
+ end
27
+
28
+ d = nil
29
+ if v.subscription_interval == Variant::SUBSCRIPTION_INTERVAL_YEARLY
30
+ d = Date.new(self.date_started.year, v.subscription_start_month, v.subscription_start_day)
31
+ d = d + 1.year if d < self.date_started
32
+ elsif v.subscription_interval == Variant::SUBSCRIPTION_INTERVAL_MONTHLY
33
+ d = Date.new(self.date_started.year, v.subscription_start_month, v.subscription_start_day)
34
+ d = d + 1.month if d < self.date_started
35
+ end
36
+ self.date_started_full = d
37
+ self.save
38
+
39
+ end
26
40
 
27
- PRORATE_METHOD_FLAT = 'flat'
28
- PRORATE_METHOD_PERCENTAGE = 'percentage'
29
- PRORATE_METHOD_CUSTOM = 'custom'
41
+ def custom_prorate
42
+ return eval(self.variant.subscription_prorate_function)
43
+ end
44
+
45
+ # Verify invoices exist for the entire subscription period up until today
46
+ def create_invoices
47
+
48
+ self.calculate_date_started_full if self.date_started_full.nil?
49
+
50
+ v = self.variant
51
+ interval = case v.subscription_interval
52
+ when Variant::SUBSCRIPTION_INTERVAL_MONTHLY then 1.month
53
+ when Variant::SUBSCRIPTION_INTERVAL_YEARLY then 1.year
54
+ end
55
+ sc = v.product.site.store_config
56
+ unit_price = v.clearance && v.clearance_price ? v.clearance_price : (v.on_sale? ? v.sale_price : v.price)
57
+
58
+ # Special case if the subscription starts on specific day
59
+ if v.subscription_start_on_day && (Date.today > self.date_started_full)
60
+ li = self.line_items.where("date_starts = ? date_ends = ?", self.date_started, self.date_started_full - 1.day).first
61
+ if li.nil?
62
+ prorated_unit_price = unit_price + 0.00
63
+ if v.subscription_prorate
64
+ prorated_unit_price = case v.subscription_prorate_method
65
+ when Variant::SUBSCRIPTION_PRORATE_METHOD_FLAT then v.subscription_prorate_flat_amount
66
+ when Variant::SUBSCRIPTION_PRORATE_METHOD_PERCENTAGE then unit_price * ((self.date_started_full - self.date_started).to_f / ((self.date_started_full + interval) - self.date_started_full).to_f)
67
+ when Variant::SUBSCRIPTION_PRORATE_METHOD_CUSTOM then self.custom_prorate
68
+ end
69
+ end
70
+ invoice = Caboose::Invoice.create(
71
+ :site_id => v.product.site_id,
72
+ :status => Caboose::Invoice::STATUS_PENDING,
73
+ :financial_status => Caboose::Invoice::STATUS_PENDING,
74
+ :date_created => DateTime.now,
75
+ :payment_terms => sc.default_payment_terms,
76
+ :invoice_number => sc.next_invoice_number
77
+ )
78
+ LineItem.create(
79
+ :invoice_id => invoice.id,
80
+ :variant_id => v.id,
81
+ :quantity => 1,
82
+ :unit_price => prorated_unit_price,
83
+ :subtotal => prorated_unit_price,
84
+ :status => 'pending',
85
+ :subscription_id => self.id,
86
+ :date_starts => d,
87
+ :date_ends => d + interval - 1.day
88
+ )
89
+ invoice.calculate
90
+ invoice.save
91
+ end
92
+ end
93
+
94
+ d2 = self.date_started_full + 1.day - 1.day
95
+ while d2 <= Date.today do
96
+ d2 = d2 + interval
97
+ end
98
+ d = self.date_started + 1.day - 1.day
99
+ while d <= d2 do
100
+ # See if an invoice has already been created for today
101
+ li = self.line_items.where("date_starts = ? AND date_ends = ?", d, d + interval - 1.day).first
102
+ if li.nil?
103
+ invoice = Caboose::Invoice.create(
104
+ :site_id => v.product.site_id,
105
+ :customer_id => self.user_id,
106
+ :status => Caboose::Invoice::STATUS_PENDING,
107
+ :financial_status => Caboose::Invoice::STATUS_PENDING,
108
+ :date_created => DateTime.now,
109
+ :payment_terms => sc.default_payment_terms,
110
+ :invoice_number => sc.next_invoice_number
111
+ )
112
+ LineItem.create(
113
+ :invoice_id => invoice.id,
114
+ :variant_id => v.id,
115
+ :quantity => 1,
116
+ :unit_price => unit_price,
117
+ :subtotal => unit_price,
118
+ :status => 'pending',
119
+ :subscription_id => self.id,
120
+ :date_starts => d,
121
+ :date_ends => d + interval - 1.day
122
+ )
123
+ invoice.calculate
124
+ invoice.save
125
+ end
126
+ d = d + interval
127
+ end
128
+ return true
129
+ end
130
+
131
+ #===========================================================================
132
+ # Static methods
133
+ #===========================================================================
134
+
135
+ def Subscription.create_invoices
136
+ Subscription.where(:status => Subscription::STATUS_ACTIVE).all.each do |s|
137
+ s.create_invoices
138
+ end
139
+ end
30
140
 
31
141
  end
32
142
  end
@@ -65,7 +65,27 @@ module Caboose
65
65
  :sort_order,
66
66
  :downloadable,
67
67
  :download_path,
68
- :is_bundle
68
+ :is_bundle,
69
+ :is_subscription,
70
+ :subscription_interval ,
71
+ :subscription_prorate ,
72
+ :subscription_prorate_method ,
73
+ :subscription_prorate_flat_amount ,
74
+ :subscription_prorate_function ,
75
+ :subscription_start_on_day ,
76
+ :subscription_start_day ,
77
+ :subscription_start_month
78
+
79
+ STATUS_ACTIVE = 'Active'
80
+ STATUS_INACTIVE = 'Inactive'
81
+ STATUS_DELETED = 'Deleted'
82
+
83
+ SUBSCRIPTION_INTERVAL_MONTHLY = 'monthly'
84
+ SUBSCRIPTION_INTERVAL_YEARLY = 'yearly'
85
+
86
+ SUBSCRIPTION_PRORATE_METHOD_FLAT = 'flat'
87
+ SUBSCRIPTION_PRORATE_METHOD_PERCENTAGE = 'percentage'
88
+ SUBSCRIPTION_PRORATE_METHOD_CUSTOM = 'custom'
69
89
 
70
90
  after_initialize do |v|
71
91
  v.price = 0.00 if v.price.nil?
@@ -1,13 +1,20 @@
1
1
 
2
- <input type='hidden' name='invoice_id' id='invoice_id' value='<%= @invoice.id %>' />
3
-
4
- <h1>Edit Invoice #<%= @invoice.id %></h1>
2
+ <% if @edituser %>
3
+ <%= render :partial => 'caboose/users/admin_header' %>
4
+ <% else %>
5
+ <h1>Edit Invoice #<%= @invoice.id %></h1>
6
+ <% end %>
5
7
 
8
+ <input type='hidden' name='invoice_id' id='invoice_id' value='<%= @invoice.id %>' />
6
9
  <div id='overview_table'></div>
7
10
  <div id='invoice_table'></div>
8
11
  <div id='message'></div>
9
12
  <div id='controls'></div>
10
13
 
14
+ <% if @edituser %>
15
+ <%= render :partial => 'caboose/users/admin_footer' %>
16
+ <% end %>
17
+
11
18
  <!--
12
19
  <p>
13
20
  <input type='button' value='< Back' onclick="window.location='/admin/invoices';" />
@@ -46,6 +53,7 @@ $(document).ready(function() {
46
53
  <% content_for :caboose_css do %>
47
54
  <style type='text/css'>
48
55
 
56
+ table.subscription_dates td,
49
57
  table.shipping_address td,
50
58
  table.billing_address td {
51
59
  padding: 0 !important;
@@ -1,4 +1,9 @@
1
- <h1>Invoices</h1>
1
+
2
+ <% if @edituser %>
3
+ <%= render :partial => 'caboose/users/admin_header' %>
4
+ <% else %>
5
+ <h1>Invoices</h1>
6
+ <% end %>
2
7
 
3
8
  <style type='text/css'>
4
9
  #search_form_wrapper { margin-bottom: 10px; position: absolute; top:0; right: 0; }
@@ -14,16 +19,20 @@
14
19
  <form action='/admin/invoices' method='get' id='search_form' style='display: none;'>
15
20
  <p><input type='text' name='id' placeholder='Invoice ID' value="<%= @pager.params['id'] %>" style='width: 100px;' /></p>
16
21
  <p><input type='text' name='invoice_number' placeholder='Invoice Number' value="<%= @pager.params['invoice_number'] %>" style='width: 100px;' /></p>
17
- <p><select name='customer_id'>
18
- <optgroup label='Status' style='width: 100px'>
19
- <option value=''>-- All customers --</option>
20
- <% @customers.each do |c| %>
21
- <option value='<%= c.id %>'<%= @pager.params['customer_id'] == c.id ? " selected='true'" : '' %>>
22
- <%= c.first_name.blank? && c.last_name.blank? ? c.username : "#{c.last_name}, #{c.first_name}" %>
23
- </option>
24
- <% end %>
25
- </optgroup>
26
- </select></p>
22
+ <% if @edituser.nil? %>
23
+ <p><select name='customer_id'>
24
+ <optgroup label='Status' style='width: 100px'>
25
+ <option value=''>-- All customers --</option>
26
+ <% @customers.each do |c| %>
27
+ <option value='<%= c.id %>'<%= @pager.params['customer_id'] == c.id ? " selected='true'" : '' %>>
28
+ <%= c.first_name.blank? && c.last_name.blank? ? c.username : "#{c.last_name}, #{c.first_name}" %>
29
+ </option>
30
+ <% end %>
31
+ </optgroup>
32
+ </select></p>
33
+ <% end %>
34
+ <p><input type='text' name='total_gte' placeholder='Total Min' value="<%= @pager.params['total_gte'] %>" style='width: 100px;' /></p>
35
+ <p><input type='text' name='total_lte' placeholder='Total Max' value="<%= @pager.params['total_lte'] %>" style='width: 100px;' /></p>
27
36
  <p><select name='status'>
28
37
  <optgroup label='Status'>
29
38
  <option value=''>-- All statuses --</option>
@@ -35,7 +44,7 @@
35
44
  </select></p>
36
45
  <p>
37
46
  <input type='submit' value='Search' />
38
- <input type='button' value='Clear' onclick="window.location='/admin/invoices';" />
47
+ <input type='button' value='Clear' onclick="window.location='/admin<%= @edituser ? "/users/#{@edituser.id}" : '' %>/invoices';" />
39
48
  </p>
40
49
  </form>
41
50
 
@@ -53,7 +62,7 @@
53
62
  %>
54
63
  </tr>
55
64
  <% @invoices.each do |invoice| %>
56
- <tr onclick="window.location='/admin/invoices/<%= invoice.id %>';">
65
+ <tr onclick="window.location='/admin<%= @edituser ? "/users/#{@edituser.id}" : '' %>/invoices/<%= invoice.id %>';">
57
66
  <td><%= raw invoice.id %></td>
58
67
  <td><%= raw invoice.invoice_number %></td>
59
68
  <td>
@@ -78,6 +87,10 @@
78
87
  <p>There are no invoices right now.</p>
79
88
  <% end %>
80
89
 
90
+ <% if @edituser %>
91
+ <%= render :partial => 'caboose/users/admin_footer' %>
92
+ <% end %>
93
+
81
94
  <% content_for :caboose_js do %>
82
95
  <script type='text/javascript'>
83
96
  var modal = false;