smartkiosk-server 0.9.0

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.
Files changed (254) hide show
  1. data/.gitignore +32 -0
  2. data/.rspec +1 -0
  3. data/Capfile +4 -0
  4. data/Gemfile +71 -0
  5. data/Gemfile.lock +439 -0
  6. data/Guardfile +25 -0
  7. data/LICENSE +287 -0
  8. data/README.md +13 -0
  9. data/Rakefile +5 -0
  10. data/app/acquirers/cards_mkb_acquirer.rb +87 -0
  11. data/app/acquirers/cash_acquirer.rb +22 -0
  12. data/app/admin/agents.rb +108 -0
  13. data/app/admin/collections.rb +62 -0
  14. data/app/admin/commissions.rb +87 -0
  15. data/app/admin/gateways.rb +183 -0
  16. data/app/admin/limits.rb +82 -0
  17. data/app/admin/payments.rb +246 -0
  18. data/app/admin/provider_groups.rb +60 -0
  19. data/app/admin/provider_profiles.rb +11 -0
  20. data/app/admin/provider_receipt_templates.rb +82 -0
  21. data/app/admin/providers.rb +201 -0
  22. data/app/admin/rebates.rb +83 -0
  23. data/app/admin/report_results.rb +66 -0
  24. data/app/admin/report_templates.rb +209 -0
  25. data/app/admin/reports.rb +158 -0
  26. data/app/admin/revisions.rb +120 -0
  27. data/app/admin/system_receipt_templates.rb +76 -0
  28. data/app/admin/terminal_builds.rb +39 -0
  29. data/app/admin/terminal_profiles.rb +123 -0
  30. data/app/admin/terminals.rb +388 -0
  31. data/app/admin/users.rb +117 -0
  32. data/app/admin/versions.rb +131 -0
  33. data/app/admin/welcome.rb +64 -0
  34. data/app/assets/fonts/PT_Sans-Web-Bold.eot +0 -0
  35. data/app/assets/fonts/PT_Sans-Web-Bold.svg +6386 -0
  36. data/app/assets/fonts/PT_Sans-Web-Bold.ttf +0 -0
  37. data/app/assets/fonts/PT_Sans-Web-Bold.woff +0 -0
  38. data/app/assets/fonts/PT_Sans-Web-Regular.eot +0 -0
  39. data/app/assets/fonts/PT_Sans-Web-Regular.svg +5650 -0
  40. data/app/assets/fonts/PT_Sans-Web-Regular.ttf +0 -0
  41. data/app/assets/fonts/PT_Sans-Web-Regular.woff +0 -0
  42. data/app/assets/images/chosen-sprite.png +0 -0
  43. data/app/assets/javascripts/active_admin/chosen.js.erb.coffee +12 -0
  44. data/app/assets/javascripts/active_admin/qtip.js.coffee +22 -0
  45. data/app/assets/javascripts/active_admin/sortable_forms.js.coffee +14 -0
  46. data/app/assets/javascripts/active_admin.js.coffee +9 -0
  47. data/app/assets/javascripts/application.js +15 -0
  48. data/app/assets/javascripts/report_templates.js.coffee +10 -0
  49. data/app/assets/javascripts/terminals.js.coffee +12 -0
  50. data/app/assets/stylesheets/active_admin/PIE.css.scss +18 -0
  51. data/app/assets/stylesheets/active_admin/filter_numeric_range.css.scss +14 -0
  52. data/app/assets/stylesheets/active_admin/selectable_check_boxes.css.scss +12 -0
  53. data/app/assets/stylesheets/active_admin/sortable_forms.css.scss +11 -0
  54. data/app/assets/stylesheets/active_admin.css.scss +705 -0
  55. data/app/assets/stylesheets/fonts.css.scss +14 -0
  56. data/app/assets/stylesheets/provider_groups.css.scss +12 -0
  57. data/app/assets/stylesheets/provider_receipt_templates.css.scss +12 -0
  58. data/app/assets/stylesheets/terminals.css.scss +9 -0
  59. data/app/controllers/application_controller.rb +12 -0
  60. data/app/controllers/collections_controller.rb +10 -0
  61. data/app/controllers/payments_controller.rb +73 -0
  62. data/app/controllers/system_receipt_templates_controller.rb +10 -0
  63. data/app/controllers/terminal_builds_controller.rb +5 -0
  64. data/app/controllers/terminal_orders_controller.rb +25 -0
  65. data/app/controllers/terminal_pings_controller.rb +45 -0
  66. data/app/controllers/welcome_controller.rb +5 -0
  67. data/app/helpers/active_admin/report_templates_helper.rb +56 -0
  68. data/app/helpers/active_admin/views_helper.rb +20 -0
  69. data/app/helpers/application_helper.rb +2 -0
  70. data/app/mailers/.gitkeep +0 -0
  71. data/app/models/.gitkeep +0 -0
  72. data/app/models/ability.rb +30 -0
  73. data/app/models/agent.rb +24 -0
  74. data/app/models/collection.rb +65 -0
  75. data/app/models/commission.rb +77 -0
  76. data/app/models/commission_section.rb +75 -0
  77. data/app/models/gateway.rb +99 -0
  78. data/app/models/gateway_attachment.rb +9 -0
  79. data/app/models/gateway_setting.rb +7 -0
  80. data/app/models/gateway_switch.rb +7 -0
  81. data/app/models/limit.rb +77 -0
  82. data/app/models/limit_section.rb +58 -0
  83. data/app/models/payment.rb +322 -0
  84. data/app/models/provider.rb +59 -0
  85. data/app/models/provider_field.rb +7 -0
  86. data/app/models/provider_gateway.rb +47 -0
  87. data/app/models/provider_group.rb +45 -0
  88. data/app/models/provider_profile.rb +9 -0
  89. data/app/models/provider_rebate.rb +69 -0
  90. data/app/models/provider_receipt_template.rb +85 -0
  91. data/app/models/rebate.rb +84 -0
  92. data/app/models/report.rb +71 -0
  93. data/app/models/report_result.rb +11 -0
  94. data/app/models/report_template.rb +56 -0
  95. data/app/models/revision.rb +104 -0
  96. data/app/models/role.rb +48 -0
  97. data/app/models/system_receipt_template.rb +13 -0
  98. data/app/models/terminal.rb +126 -0
  99. data/app/models/terminal_build.rb +62 -0
  100. data/app/models/terminal_order.rb +42 -0
  101. data/app/models/terminal_ping.rb +120 -0
  102. data/app/models/terminal_profile.rb +51 -0
  103. data/app/models/terminal_profile_promotion.rb +18 -0
  104. data/app/models/terminal_profile_provider.rb +11 -0
  105. data/app/models/terminal_profile_provider_group.rb +19 -0
  106. data/app/models/user.rb +38 -0
  107. data/app/models/user_role.rb +45 -0
  108. data/app/reports/agents_report.rb +81 -0
  109. data/app/reports/collections_report.rb +151 -0
  110. data/app/reports/payments_report.rb +189 -0
  111. data/app/reports/terminals_report.rb +123 -0
  112. data/app/uploaders/file_uploader.rb +53 -0
  113. data/app/uploaders/icon_uploader.rb +15 -0
  114. data/app/uploaders/zip_uploader.rb +53 -0
  115. data/app/views/admin/gateways/providers.html.arb +10 -0
  116. data/app/views/admin/revisions/payments.html.arb +28 -0
  117. data/app/views/admin/terminal_profiles/_tree.html.haml +35 -0
  118. data/app/views/admin/terminal_profiles/sort.html.haml +21 -0
  119. data/app/views/admin/terminals/pings.html.arb +55 -0
  120. data/app/views/admin/terminals/upgrade_build.html.arb +30 -0
  121. data/app/views/layouts/application.html.erb +1 -0
  122. data/app/views/welcome/index.html.erb +25 -0
  123. data/app/workers/pay_worker.rb +11 -0
  124. data/app/workers/report_worker.rb +7 -0
  125. data/app/workers/revise_worker.rb +7 -0
  126. data/config/acquiring.yml +13 -0
  127. data/config/application.rb +75 -0
  128. data/config/boot.rb +6 -0
  129. data/config/database.yml +17 -0
  130. data/config/deploy/roundlake-passenger.rb +3 -0
  131. data/config/deploy/roundlake-trinidad.rb +3 -0
  132. data/config/deploy.rb +37 -0
  133. data/config/environment.rb +5 -0
  134. data/config/environments/development.rb +39 -0
  135. data/config/environments/production.rb +67 -0
  136. data/config/environments/test.rb +37 -0
  137. data/config/initializers/active_admin.rb +31 -0
  138. data/config/initializers/backtrace_silencers.rb +7 -0
  139. data/config/initializers/devise.rb +232 -0
  140. data/config/initializers/inflections.rb +15 -0
  141. data/config/initializers/mime_types.rb +5 -0
  142. data/config/initializers/money.rb +6 -0
  143. data/config/initializers/redis.rb +4 -0
  144. data/config/initializers/secret_token.rb +7 -0
  145. data/config/initializers/session_store.rb +8 -0
  146. data/config/initializers/setup_rack.rb +1 -0
  147. data/config/initializers/wrap_parameters.rb +14 -0
  148. data/config/locales/active_admin.ru.yml +71 -0
  149. data/config/locales/activerecord.ru.yml +645 -0
  150. data/config/locales/devise.ru.yml +57 -0
  151. data/config/locales/ru.yml +33 -0
  152. data/config/locales/smartkiosk.gateways.ru.yml +241 -0
  153. data/config/locales/smartkiosk.hardware.ru.yml +38 -0
  154. data/config/locales/smartkiosk.reports.ru.yml +56 -0
  155. data/config/locales/smartkiosk.ru.yml +163 -0
  156. data/config/nginx.conf +9 -0
  157. data/config/redis.yml +9 -0
  158. data/config/routes.rb +48 -0
  159. data/config/schedule.rb +13 -0
  160. data/config/sidekiq.yml +5 -0
  161. data/config/trinidad.yml +6 -0
  162. data/config.ru +4 -0
  163. data/db/migrate/20120825083035_create_terminals.rb +47 -0
  164. data/db/migrate/20120825083305_create_payments.rb +51 -0
  165. data/db/migrate/20120827062618_devise_create_users.rb +50 -0
  166. data/db/migrate/20120827132621_create_admin_notes.rb +17 -0
  167. data/db/migrate/20120827132622_move_admin_notes_to_comments.rb +25 -0
  168. data/db/migrate/20120903193346_create_providers.rb +24 -0
  169. data/db/migrate/20120922075345_create_collections.rb +26 -0
  170. data/db/migrate/20120922080440_create_agents.rb +29 -0
  171. data/db/migrate/20120922081104_create_commissions.rb +10 -0
  172. data/db/migrate/20120927035841_create_gateways.rb +13 -0
  173. data/db/migrate/20120927040626_create_provider_gateways.rb +13 -0
  174. data/db/migrate/20120927091849_create_limits.rb +10 -0
  175. data/db/migrate/20120927164647_create_roles.rb +14 -0
  176. data/db/migrate/20121003131522_create_report_templates.rb +18 -0
  177. data/db/migrate/20121007135652_create_reports.rb +13 -0
  178. data/db/migrate/20121008073905_create_report_results.rb +10 -0
  179. data/db/migrate/20121017114538_create_gateway_settings.rb +12 -0
  180. data/db/migrate/20121017114556_create_gateway_attachments.rb +10 -0
  181. data/db/migrate/20121019132606_create_revisions.rb +19 -0
  182. data/db/migrate/20121023073501_create_gateway_switches.rb +10 -0
  183. data/db/migrate/20121112142743_create_terminal_orders.rb +13 -0
  184. data/db/migrate/20121118191553_create_versions.rb +19 -0
  185. data/db/migrate/20121125093414_create_system_receipt_templates.rb +9 -0
  186. data/db/migrate/20121127192257_create_terminal_builds.rb +10 -0
  187. data/db/migrate/20121212053441_create_rebates.rb +15 -0
  188. data/db/migrate/20121216143855_create_provider_receipt_templates.rb +9 -0
  189. data/db/migrate/20130101091100_create_terminal_profiles.rb +13 -0
  190. data/db/migrate/20130101091734_create_provider_rebates.rb +21 -0
  191. data/db/migrate/20130102164447_create_limit_sections.rb +14 -0
  192. data/db/migrate/20130102164503_create_commission_sections.rb +17 -0
  193. data/db/migrate/20130102171743_create_provider_profiles.rb +10 -0
  194. data/db/migrate/20130103152507_create_provider_fields.rb +17 -0
  195. data/db/migrate/20130103154526_create_provider_groups.rb +10 -0
  196. data/db/migrate/20130104063628_create_terminal_profile_providers.rb +11 -0
  197. data/db/migrate/20130104090957_create_terminal_profile_provider_groups.rb +11 -0
  198. data/db/migrate/20130108091644_create_terminal_profile_promotions.rb +10 -0
  199. data/db/schema.rb +489 -0
  200. data/db/seeds/receipt_templates/payment.txt +19 -0
  201. data/db/seeds/receipt_templates/system/balance.txt +13 -0
  202. data/db/seeds/receipt_templates/system/collection.txt +17 -0
  203. data/db/seeds/receipt_templates/system/touchtest.txt +15 -0
  204. data/db/seeds.rb +18 -0
  205. data/doc/README_FOR_APP +2 -0
  206. data/init.rb +1 -0
  207. data/lib/active_admin/cancan_integration.rb +81 -0
  208. data/lib/active_admin/form_builder_fix.rb +76 -0
  209. data/lib/active_admin/inputs/filter_date_range_input_fix.rb +24 -0
  210. data/lib/active_admin/inputs/filter_multiple_select_input.rb +18 -0
  211. data/lib/active_admin/inputs/filter_numeric_range_input.rb +31 -0
  212. data/lib/active_admin/resource_controller_fix.rb +23 -0
  213. data/lib/active_admin/views/pages/base_fix.rb +13 -0
  214. data/lib/assets/.gitkeep +0 -0
  215. data/lib/blueprints.rb +79 -0
  216. data/lib/date_expander.rb +38 -0
  217. data/lib/dav4rack/build_resource.rb +59 -0
  218. data/lib/formtastic/inputs/selectable_check_boxes.rb +39 -0
  219. data/lib/paper_trail/version_fix.rb +3 -0
  220. data/lib/report_builder.rb +88 -0
  221. data/lib/seeder.rb +103 -0
  222. data/lib/smartkiosk/server/version.rb +5 -0
  223. data/lib/smartkiosk/server.rb +35 -0
  224. data/lib/string_file.rb +12 -0
  225. data/lib/tasks/.gitkeep +0 -0
  226. data/lib/tasks/db.rake +27 -0
  227. data/lib/tasks/dump.rake +53 -0
  228. data/lib/tasks/matrioshka.rake +16 -0
  229. data/log/.gitkeep +0 -0
  230. data/log/gateways/.gitkeep +0 -0
  231. data/public/404.html +26 -0
  232. data/public/422.html +26 -0
  233. data/public/500.html +25 -0
  234. data/public/robots.txt +5 -0
  235. data/script/rails +6 -0
  236. data/smartkiosk-server.gemspec +23 -0
  237. data/spec/controllers/collections_controller_spec.rb +32 -0
  238. data/spec/controllers/payments_controller_spec.rb +123 -0
  239. data/spec/controllers/system_receipt_templates_controller_spec.rb +5 -0
  240. data/spec/controllers/terminal_builds_controller_spec.rb +5 -0
  241. data/spec/controllers/terminal_orders_controller_spec.rb +32 -0
  242. data/spec/controllers/terminal_pings_controller_spec.rb +31 -0
  243. data/spec/spec_helper.rb +66 -0
  244. data/vendor/assets/javascripts/.gitkeep +0 -0
  245. data/vendor/assets/javascripts/chosen.jquery.js +1026 -0
  246. data/vendor/assets/javascripts/datepicker-ru.jquery.js +21 -0
  247. data/vendor/assets/javascripts/nestedSortable.jquery.js +429 -0
  248. data/vendor/assets/javascripts/qtip.jquery.js +3403 -0
  249. data/vendor/assets/stylesheets/.gitkeep +0 -0
  250. data/vendor/assets/stylesheets/chosen.jquery.css +397 -0
  251. data/vendor/assets/stylesheets/modules/PIE.htc +96 -0
  252. data/vendor/assets/stylesheets/qtip.jquery.css.scss +604 -0
  253. data/vendor/plugins/.gitkeep +0 -0
  254. metadata +333 -0
@@ -0,0 +1,99 @@
1
+ class Gateway < ActiveRecord::Base
2
+
3
+ has_paper_trail
4
+
5
+ #
6
+ # RELATIONS
7
+ #
8
+ has_many :provider_gateways
9
+ has_many :providers, :through => :provider_gateways
10
+ has_many :payments, :order => 'id DESC'
11
+ has_many :gateway_settings
12
+ has_many :gateway_attachments
13
+ has_many :gateway_switches
14
+ has_many :rebates
15
+
16
+ accepts_nested_attributes_for :gateway_settings
17
+ accepts_nested_attributes_for :gateway_attachments
18
+ accepts_nested_attributes_for :gateway_switches
19
+
20
+ scope :enabled, where(:enabled => true)
21
+
22
+ #
23
+ # METHODS
24
+ #
25
+ def librarize(instantiate=true)
26
+ klass = ("Payzilla::Gateways::"+payzilla.camelize).constantize
27
+ return instantiate ? klass.new(self) : klass
28
+ end
29
+
30
+ def available_settings
31
+ librarize(false)::available_settings
32
+ end
33
+
34
+ def available_attachments
35
+ librarize(false)::available_attachments
36
+ end
37
+
38
+ def available_switches
39
+ librarize(false)::available_switches
40
+ end
41
+
42
+ def serialize_options
43
+ options = {}
44
+ sources = {
45
+ "setting" => available_settings,
46
+ "attachment" => available_attachments,
47
+ "switch" => available_switches
48
+ }
49
+
50
+ sources.each do |prefix, source|
51
+ source.each do |element|
52
+ keyword = "#{prefix}_#{element}"
53
+ options[keyword] = send keyword
54
+ end
55
+ end
56
+
57
+ options
58
+ end
59
+
60
+ def respond_to?(key, include_private=false)
61
+ return true if key.to_s.starts_with?('setting_') || key.to_s.starts_with?('attachment_') || key.to_s.starts_with?('switch_')
62
+ super(key, include_private)
63
+ end
64
+
65
+ def method_missing(name, *args, &block)
66
+ assign = name[-1] == '='
67
+ name = name[0, name.length-1] if assign
68
+
69
+ if name.to_s.starts_with?('setting_')
70
+ source = gateway_settings
71
+ klass = GatewaySetting
72
+ name = name.to_s.gsub('setting_', '')
73
+ elsif name.to_s.starts_with?('attachment_')
74
+ source = gateway_attachments
75
+ klass = GatewayAttachment
76
+ name = name.to_s.gsub('attachment_', '')
77
+ elsif name.to_s.starts_with?('switch_')
78
+ source = gateway_switches
79
+ klass = GatewaySwitch
80
+ name = name.to_s.gsub('switch_', '')
81
+ else
82
+ return super
83
+ end
84
+
85
+ if assign
86
+ data = source.select{|x| x.keyword == name}.first
87
+
88
+ if data.blank?
89
+ source << klass.new(:keyword => name, :gateway_id => id, :value => args[0])
90
+ else
91
+ data.value = args[0]
92
+ end
93
+ else
94
+ result = source.select{|x| x.keyword == name}.first.try(:value)
95
+ result = false if result.blank? && klass == GatewaySwitch
96
+ result
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,9 @@
1
+ class GatewayAttachment < ActiveRecord::Base
2
+
3
+ #
4
+ # RELATIONS
5
+ #
6
+ belongs_to :gateway
7
+
8
+ mount_uploader :value, FileUploader
9
+ end
@@ -0,0 +1,7 @@
1
+ class GatewaySetting < ActiveRecord::Base
2
+
3
+ #
4
+ # RELATIONS
5
+ #
6
+ belongs_to :gateway
7
+ end
@@ -0,0 +1,7 @@
1
+ class GatewaySwitch < ActiveRecord::Base
2
+
3
+ #
4
+ # RELATIONS
5
+ #
6
+ belongs_to :gateway
7
+ end
@@ -0,0 +1,77 @@
1
+ class Limit < ActiveRecord::Base
2
+
3
+ has_paper_trail
4
+
5
+ #
6
+ # RELATIONS
7
+ #
8
+ belongs_to :provider_profile
9
+ has_many :providers, :through => :provider_profile
10
+ has_many :limit_sections, :inverse_of => :limit
11
+
12
+ accepts_nested_attributes_for :limit_sections, :allow_destroy => true
13
+
14
+ default_scope includes(:limit_sections)
15
+
16
+ scope :actual, lambda{
17
+ where(arel_table[:start].lteq Date.today).
18
+ where(arel_table[:finish].gteq Date.today)
19
+ }
20
+
21
+ #
22
+ # VALIDATIONS
23
+ #
24
+ validates :provider_profile, :presence => true
25
+ validates :start, :presence => true
26
+ validates :finish, :presence => true
27
+
28
+ validate do
29
+ return if start.blank? || finish.blank?
30
+
31
+ if start > finish
32
+ errors.add(:start, :too_large)
33
+ return
34
+ end
35
+
36
+ # Conditions intersections
37
+ neighbors = self.class.where(:provider_profile_id => provider_profile_id)
38
+ neighbors = neighbors.where("id != ?", id) unless id.blank?
39
+
40
+ # Ranges intersections
41
+ neighbors = neighbors.to_a
42
+
43
+ neighbors.select! do |x|
44
+ x.start.between?(start, finish) ||
45
+ x.finish.between?(start, finish) ||
46
+ start.between?(x.start, x.finish) ||
47
+ finish.between?(x.start, x.finish)
48
+ end
49
+
50
+ if neighbors.count > 0
51
+ errors[:base] << I18n.t('activerecord.errors.models.limit.conditions_intersect')
52
+ end
53
+ end
54
+
55
+ #
56
+ # METHODS
57
+ #
58
+ def self.for(payment, filter_amount=true)
59
+ limit = payment.provider.limits.actual.first
60
+ return [] if limit.blank?
61
+
62
+ rates = limit.limit_sections.
63
+ by_terminal_profile_and_agent_ids(payment.terminal.terminal_profile_id, payment.terminal.agent_id).
64
+ by_payment_type(payment.payment_type)
65
+
66
+ rates.select!{|x|
67
+ x.min <= payment.paid_amount &&
68
+ x.max >= payment.paid_amount
69
+ } if filter_amount
70
+
71
+ rates.sort_by(&:weight).reverse
72
+ end
73
+
74
+ def title
75
+ "##{id}"
76
+ end
77
+ end
@@ -0,0 +1,58 @@
1
+ class LimitSection < ActiveRecord::Base
2
+
3
+ #
4
+ # RELATIONS
5
+ #
6
+ belongs_to :limit, :inverse_of => :limit_sections
7
+ belongs_to :agent
8
+ belongs_to :terminal_profile
9
+
10
+ scope :by_terminal_profile_and_agent_ids, lambda { |p, a|
11
+ conditions = [
12
+ '(terminal_profile_id IS NULL AND agent_id IS NULL)',
13
+ '(agent_id = ? AND terminal_profile_id IS NULL)',
14
+ 'terminal_profile_id = ?'
15
+ ]
16
+ where(
17
+ conditions.join(' OR '),
18
+ a,
19
+ p
20
+ )
21
+ }
22
+ scope :by_payment_type, lambda { |x|
23
+ where("payment_type IS NULL OR payment_type = ?", x)
24
+ }
25
+
26
+ #
27
+ # VALIDATIONS
28
+ #
29
+ validates :min, :presence => true
30
+ validates :max, :presence => true
31
+
32
+ validate do
33
+ return if min.blank? || max.blank?
34
+
35
+ if min > max
36
+ errors.add(:min, :too_large)
37
+ return
38
+ end
39
+
40
+ neighbors = limit.limit_sections.select{|x| x != self}
41
+
42
+ if neighbors.select{|x| x.payment_type.nil? != payment_type.nil?}.count > 0
43
+ errors[:base] << I18n.t('activerecord.errors.models.limit.payment_type_intersects')
44
+ end
45
+
46
+ neighbors.select!{|x| x.agent_id == agent_id && x.terminal_profile_id == terminal_profile_id}
47
+
48
+ if neighbors.count > 0
49
+ errors[:base] << I18n.t('activerecord.errors.models.limit.conditions_intersect')
50
+ end
51
+ end
52
+
53
+ def weight
54
+ return 3 unless terminal_profile_id.blank?
55
+ return 2 unless agent_id.blank?
56
+ return 1
57
+ end
58
+ end
@@ -0,0 +1,322 @@
1
+ class Payment < ActiveRecord::Base
2
+ include DateExpander
3
+ include Stateflow
4
+
5
+ SOURCE_INNER = 0
6
+ SOURCE_MANUAL = 1
7
+ SOURCE_IMPORT = 2
8
+
9
+ TYPE_CASH = 0
10
+ TYPE_INNER_CARD = 1
11
+ TYPE_FOREIGN_CARD = 2
12
+ TYPE_IBANK = 3
13
+ TYPE_MBANK = 4
14
+ TYPE_PURSE = 5
15
+ TYPE_ACCOUNT = 6
16
+
17
+ #
18
+ # STATEFLOW
19
+ #
20
+ stateflow do
21
+ state :new, :checked, :declined, :queue, :paid, :error, :manual
22
+
23
+ event :check do
24
+ transitions :from => :new, :to => [:checked, :declined], :decide => :check?
25
+ end
26
+
27
+ event :enqueue do
28
+ transitions :from => [:error, :checked], :to => :queue
29
+ end
30
+
31
+ event :pay do
32
+ transitions :from => [:checked, :queue], :to => [:paid, :error], :decide => :pay?
33
+ end
34
+
35
+ event :pay_offline do
36
+ transitions :from => :new, :to => [:paid, :error], :decide => :pay_offline?
37
+ end
38
+
39
+ event :pay_manually do
40
+ transitions :from => :error, :to => :manual
41
+ end
42
+
43
+ event :requeue do
44
+ transitions :from => :error, :to => :queue
45
+ end
46
+ end
47
+
48
+ def pay_manually!(user)
49
+ pay_manually
50
+ save!
51
+
52
+ Version.create!(
53
+ :item_type => self.class.to_s,
54
+ :item_id => self.id,
55
+ :event => "payment.paid_manually",
56
+ :whodunnit => user.id
57
+ )
58
+ end
59
+
60
+ def requeue!(user)
61
+ requeue
62
+ save!
63
+
64
+ PayWorker.perform_async(self.id)
65
+ Version.create!(
66
+ :item_type => self.class.to_s,
67
+ :item_id => self.id,
68
+ :event => "payment.requeued",
69
+ :whodunnit => user.id
70
+ )
71
+ end
72
+
73
+ def enqueue!(attributes={})
74
+ [ :paid_amount, :receipt_number, :card_track1, :card_track2 ].each do |key|
75
+ if attributes.include? key
76
+ write_attribute key, attributes[key]
77
+ end
78
+ end
79
+
80
+ enqueue
81
+ save!
82
+ PayWorker.perform_async(id)
83
+ end
84
+
85
+ #
86
+ # RELATIONS
87
+ #
88
+ belongs_to :agent
89
+ belongs_to :terminal
90
+ belongs_to :provider
91
+ belongs_to :gateway
92
+ belongs_to :user
93
+ belongs_to :corrected_payment, :class_name => 'Payment'
94
+ has_one :corrected_by, :foreign_key => 'corrected_payment_id', :class_name => 'Payment'
95
+
96
+ scope :queued, where(:state => 'queue')
97
+ scope :error, where(:state => 'error')
98
+ scope :recent, order('created_at DESC')
99
+
100
+ #
101
+ # VALIDATIONS
102
+ #
103
+ validates :gateway, :presence => true
104
+ validates :provider, :presence => true, :if => lambda{|x| x.source == SOURCE_INNER}
105
+ validates :terminal, :presence => true, :if => lambda{|x| x.source == SOURCE_INNER}
106
+ validates :agent, :presence => true, :if => lambda{|x| x.source == SOURCE_INNER}
107
+ validates :commission_amount, :presence => true, :if => lambda{|x| x.source == SOURCE_MANUAL}
108
+ validates :enrolled_amount, :presence => true, :if => lambda{|x| x.source == SOURCE_MANUAL}
109
+ validates :session_id, :uniqueness => {:scope => :terminal_id}, :unless => lambda{|x| x.terminal.blank?}
110
+ validates :user, :presence => true, :if => lambda{|x| x.source == SOURCE_MANUAL}
111
+ validates :payment_type, :presence => true
112
+
113
+ #
114
+ # MODIFICATIONS
115
+ #
116
+ before_validation do
117
+ self.meta = {} unless self.meta.is_a?(Hash)
118
+ self.agent = self.terminal.agent unless self.terminal.blank?
119
+ end
120
+
121
+ before_save do
122
+ if !self.paid_amount.nil? && (self.enrolled_amount.nil? || self.commission_amount.nil?)
123
+ self.commission_amount = Commission.for(self).first.try(:fee, paid_amount) || 0
124
+ self.enrolled_amount = self.paid_amount - self.commission_amount
125
+ end
126
+
127
+ if self.paid_amount.nil? && !self.enrolled_amount.nil? && !self.commission_amount.nil?
128
+ self.paid_amount = self.enrolled_amount + self.commission_amount
129
+ end
130
+
131
+ if !self.paid_amount.nil? && !self.commission_amount.nil? && self.rebate_amount.nil?
132
+ rebate = Rebate.for(self)
133
+ self.rebate_amount = rebate.try(:fee, paid_amount) || 0
134
+ end
135
+ end
136
+
137
+ after_create do
138
+ if source == SOURCE_MANUAL
139
+ Version.create!(
140
+ :item_type => self.class.to_s,
141
+ :item_id => self.id,
142
+ :event => "create",
143
+ :whodunnit => user.id
144
+ )
145
+ end
146
+ end
147
+
148
+ serialize :fields
149
+ serialize :raw_fields
150
+ serialize :meta
151
+
152
+ #
153
+ # METHODS
154
+ #
155
+ def self.acquirer(payment_type)
156
+ unless @acquiring_settings
157
+ @acquiring_settings = YAML::load File.read(Rails.root.join 'config/acquiring.yml')
158
+ @acquiring_settings.each do |x|
159
+ x['type'] = x['type'].split(',').map do |t|
160
+ Payment.const_get "TYPE_#{t.strip.upcase}"
161
+ end
162
+ x['class'] = x['class'].constantize
163
+ end
164
+ end
165
+
166
+ acquirer = @acquiring_settings.find{|x| x['type'].include?(payment_type)}
167
+ raise "unsupported payment type: #{payment_type}" unless acquirer
168
+
169
+ acquirer['class'].new(acquirer)
170
+ end
171
+
172
+ def self.plogger
173
+ return @plogger if @plogger
174
+
175
+ @plogger ||= Logger.new(Rails.root.join('log/payments.log'), 10, 1024000)
176
+
177
+ separator = ' | '
178
+
179
+ @plogger.formatter = proc { |severity, datetime, data, message|
180
+ data ||= {}
181
+
182
+ header = "~~ " + [
183
+ severity,
184
+ datetime.iso8601(12),
185
+ data[:progname],
186
+ data[:payment_id],
187
+ data[:payment_state],
188
+ data[:session_id],
189
+ data[:terminal_id],
190
+ data[:gateway_id],
191
+ message
192
+ ].join(separator) + "\n"
193
+ }
194
+
195
+ @plogger
196
+ end
197
+
198
+ def self.plog(severity, progname, message, data={})
199
+ data[:progname] = progname
200
+
201
+ unless block_given?
202
+ plogger.send(severity, data){ message }
203
+ else
204
+ begin
205
+ yield
206
+ rescue Exception => e
207
+ plogger.error(data){ e }
208
+ raise e
209
+ else
210
+ plogger.send(severity, data){ message }
211
+ end
212
+ end
213
+ end
214
+
215
+ def plog(severity, progname, message, &block)
216
+ data = {
217
+ :payment_id => self.id,
218
+ :payment_state => self.state,
219
+ :session_id => self.session_id,
220
+ :terminal_id => self.terminal_id,
221
+ :gateway_id => self.gateway_id
222
+ }
223
+
224
+ self.class.plog(severity, progname, message, data, &block)
225
+ end
226
+
227
+ def manual?
228
+ source == SOURCE_MANUAL
229
+ end
230
+
231
+ def complete?
232
+ %w(paid manual).include? state
233
+ end
234
+
235
+ def cash?
236
+ payment_type == TYPE_CASH
237
+ end
238
+
239
+ def cashless?
240
+ !cash?
241
+ end
242
+
243
+ def self.build!(terminal, provider, attributes)
244
+ payment = new(attributes)
245
+ provider_gateway = provider.provider_gateways.enabled.order(:priority).first
246
+
247
+ return false if provider_gateway.blank?
248
+
249
+ payment.terminal = terminal
250
+ payment.provider_gateway = provider_gateway
251
+ payment.raw_fields = payment.fields
252
+ payment.fields = provider_gateway.map(payment.account, payment.fields)
253
+
254
+ payment.save!
255
+ payment
256
+ end
257
+
258
+ def provider_gateway=(pg)
259
+ self.gateway = pg.gateway
260
+ self.provider = pg.provider
261
+ self.gateway_provider_id = pg.gateway_provider_id
262
+ end
263
+
264
+ def title
265
+ "##{id}: #{provider.try(:title)} (#{account || '--'})"
266
+ end
267
+
268
+ def human_fields=(value)
269
+ self.fields = value.gsub("\r", '').split("\n").map{|x| x.split('=')}
270
+ self.fields = Hash[*self.fields.select{|x| x.length > 1}.flatten]
271
+ end
272
+
273
+ def human_fields
274
+ return '' if self.fields.blank?
275
+ self.fields.collect{|k,v| "#{k}=#{v}"}.join("\n")
276
+ end
277
+
278
+ def check?
279
+ result = self.gateway.librarize.check(self)
280
+
281
+ if result[:success]
282
+ self.gateway_error = nil
283
+ self.gateway_payment_id = result[:gateway_payment_id] unless result[:gateway_payment_id].blank?
284
+ self.save!
285
+ return :checked
286
+ else
287
+ self.update_attribute(:gateway_error, result[:error])
288
+ return :declined
289
+ end
290
+ end
291
+
292
+ def pay?
293
+ acquirer = Payment.acquirer(self.payment_type)
294
+ authorization = acquirer.authorize(self)
295
+
296
+ if authorization.success?
297
+ result = self.gateway.librarize.pay(self)
298
+
299
+ if result[:success]
300
+ self.gateway_error = nil
301
+ self.gateway_payment_id = result[:gateway_payment_id] unless result[:gateway_payment_id].blank?
302
+ self.paid_at = DateTime.now
303
+ self.meta[:gateway] = self.gateway.serialize_options
304
+
305
+ self.save!
306
+ authorization.confirm
307
+ return :paid
308
+ else
309
+ self.update_attribute(:gateway_error, result[:error])
310
+ authorization.reverse
311
+ return :error
312
+ end
313
+ else
314
+ self.update_attribute(:gateway_error, authorization.error)
315
+ return :error
316
+ end
317
+ end
318
+
319
+ def approved?
320
+ !['new', 'declined'].include?(state)
321
+ end
322
+ end
@@ -0,0 +1,59 @@
1
+ class Provider < ActiveRecord::Base
2
+ include Redis::Objects::RMap
3
+
4
+ value :timestamp, :global => true, :marshal => true
5
+
6
+ has_rmap({:id => lambda{|x| x.to_s}}, :title)
7
+ has_paper_trail
8
+
9
+ mount_uploader :icon, IconUploader
10
+
11
+ after_save do
12
+ self.class.timestamp = updated_at
13
+ end
14
+
15
+ after_destroy do
16
+ self.class.timestamp = DateTime.now
17
+ end
18
+
19
+ #
20
+ # RELATIONS
21
+ #
22
+ belongs_to :provider_profile
23
+ belongs_to :provider_group
24
+ belongs_to :provider_receipt_template
25
+ has_many :provider_gateways
26
+ has_many :gateways, :through => :provider_gateways
27
+ has_many :payments, :order => 'id DESC'
28
+ has_many :commissions, :through => :provider_profile
29
+ has_many :limits, :through => :provider_profile
30
+ has_many :provider_fields, :dependent => :destroy, :order => :priority
31
+ has_many :terminal_profile_providers, :dependent => :destroy, :order => :priority
32
+ has_many :terminal_profile_promotions, :dependent => :destroy, :order => :priority
33
+
34
+ accepts_nested_attributes_for :provider_gateways, :allow_destroy => true
35
+ accepts_nested_attributes_for :provider_fields, :allow_destroy => true
36
+
37
+ scope :gateway_ids_eq, lambda{|x| includes(:gateways).where(:gateways => {:id => x})}
38
+ search_method :gateway_ids_eq
39
+
40
+ scope :after, lambda{|x|
41
+ x.blank? ? scoped
42
+ : where(arel_table[:updated_at].gt x)
43
+ }
44
+
45
+ #
46
+ # VALIDATIONS
47
+ #
48
+ validates :provider_profile, :presence => true
49
+ validates :provider_group, :presence => true
50
+ validates :keyword, :presence => true
51
+ validates :keyword, :uniqueness => true
52
+
53
+ #
54
+ # METHODS
55
+ #
56
+ def fields_dump
57
+ provider_fields.as_json(:only => [:keyword, :title, :kind, :mask, :values, :priority])
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ class ProviderField < ActiveRecord::Base
2
+ belongs_to :provider
3
+
4
+ validates :title, :presence => true
5
+ validates :kind, :presence => true
6
+ validates :values, :presence => true, :if => lambda{|x| x.kind == 'select'}
7
+ end
@@ -0,0 +1,47 @@
1
+ class ProviderGateway < ActiveRecord::Base
2
+ #
3
+ # RELATIONS
4
+ #
5
+ belongs_to :provider, :counter_cache => true
6
+ belongs_to :gateway
7
+
8
+ scope :enabled, includes(:gateway).where(:gateways => {:enabled => true})
9
+
10
+ #
11
+ # MODIFICATIONS
12
+ #
13
+ serialize :fields_mapping
14
+
15
+ #
16
+ # METHODS
17
+ #
18
+ def human_fields_mapping=(value)
19
+ self.fields_mapping = value.gsub("\r", '').split("\n").map{|x| x.split('=')}
20
+ self.fields_mapping = Hash[*self.fields_mapping.select{|x| x.length > 1}.flatten]
21
+ end
22
+
23
+ def human_fields_mapping
24
+ return '' if self.fields_mapping.blank?
25
+ self.fields_mapping.collect{|k,v| "#{k}=#{v}"}.join("\n")
26
+ end
27
+
28
+ def map(account, raw_fields)
29
+ fields = {}
30
+
31
+ if !account.blank? && !account_mapping.blank?
32
+ fields[account_mapping] = account
33
+ end
34
+
35
+ unless raw_fields.blank?
36
+ unless fields_mapping.blank?
37
+ fields_mapping.each do |k, v|
38
+ fields[v] = raw_fields[k] unless raw_fields[k].blank?
39
+ end
40
+ else
41
+ fields.merge!(raw_fields)
42
+ end
43
+ end
44
+
45
+ fields
46
+ end
47
+ end