caboose-cms 0.8.78 → 0.8.79

Sign up to get free protection for your applications and to get access to all the features.
@@ -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;