fat_free_crm 0.11.3 → 0.11.4

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.

Potentially problematic release.


This version of fat_free_crm might be problematic. Click here for more details.

Files changed (637) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG +6 -1
  4. data/CONTRIBUTORS +40 -1
  5. data/Capfile +5 -0
  6. data/Gemfile +25 -6
  7. data/Gemfile.lock +128 -86
  8. data/README.md +23 -16
  9. data/{vendor → app}/assets/images/delete.png +0 -0
  10. data/app/assets/javascripts/admin/fields.js.coffee +62 -0
  11. data/app/assets/javascripts/application.js.erb +8 -1
  12. data/app/assets/javascripts/crm.js +44 -46
  13. data/app/assets/javascripts/crm_chosen.js.coffee +10 -7
  14. data/app/assets/javascripts/datepicker.js.coffee +15 -0
  15. data/app/assets/javascripts/format_buttons.js.coffee +48 -0
  16. data/app/assets/javascripts/{admin/field_groups.js.coffee → groups.js.coffee} +0 -0
  17. data/app/assets/javascripts/lists.js.coffee +1 -1
  18. data/app/assets/javascripts/search.js.coffee +56 -38
  19. data/app/assets/stylesheets/admin/{field_groups.css.scss → fields.css.scss} +4 -0
  20. data/app/assets/stylesheets/advanced_search.css.scss +79 -0
  21. data/app/assets/stylesheets/application.css.erb +4 -0
  22. data/app/assets/stylesheets/base.scss +3 -3
  23. data/app/assets/stylesheets/common.scss +72 -25
  24. data/app/assets/stylesheets/fields.scss +14 -0
  25. data/app/assets/stylesheets/format_buttons.css.scss +30 -0
  26. data/app/assets/stylesheets/groups.css.scss +3 -0
  27. data/app/assets/stylesheets/header.scss +7 -6
  28. data/{vendor/plugins/.gitkeep → app/assets/stylesheets/index_headers.css.scss} +0 -0
  29. data/app/assets/stylesheets/print.css.scss +12 -3
  30. data/app/assets/stylesheets/rails.scss +12 -3
  31. data/app/controllers/admin/application_controller.rb +1 -1
  32. data/app/controllers/admin/field_groups_controller.rb +2 -0
  33. data/app/controllers/admin/fields_controller.rb +41 -17
  34. data/app/controllers/admin/groups_controller.rb +70 -0
  35. data/app/controllers/admin/tags_controller.rb +1 -0
  36. data/app/controllers/admin/users_controller.rb +8 -7
  37. data/app/controllers/application_controller.rb +34 -5
  38. data/app/controllers/comments_controller.rb +2 -3
  39. data/app/controllers/entities/accounts_controller.rb +30 -37
  40. data/app/controllers/entities/campaigns_controller.rb +56 -29
  41. data/app/controllers/entities/contacts_controller.rb +24 -26
  42. data/app/controllers/entities/leads_controller.rb +38 -37
  43. data/app/controllers/entities/opportunities_controller.rb +28 -34
  44. data/app/controllers/entities_controller.rb +71 -43
  45. data/app/controllers/home_controller.rb +29 -27
  46. data/app/controllers/tasks_controller.rb +18 -17
  47. data/app/controllers/users_controller.rb +12 -8
  48. data/app/helpers/accounts_helper.rb +58 -16
  49. data/app/helpers/addresses_helper.rb +4 -1
  50. data/app/helpers/admin/fields_helper.rb +3 -4
  51. data/app/helpers/application_helper.rb +147 -100
  52. data/app/helpers/campaigns_helper.rb +2 -9
  53. data/app/helpers/fields_helper.rb +0 -1
  54. data/app/helpers/groups_helper.rb +2 -0
  55. data/app/helpers/home_helper.rb +11 -0
  56. data/app/helpers/leads_helper.rb +2 -9
  57. data/app/helpers/opportunities_helper.rb +2 -9
  58. data/app/helpers/{crm_tags_helper.rb → tags_helper.rb} +9 -8
  59. data/app/helpers/tasks_helper.rb +5 -21
  60. data/app/helpers/users_helper.rb +14 -7
  61. data/app/helpers/versions_helper.rb +46 -0
  62. data/app/inputs/date_time_input.rb +8 -25
  63. data/app/inputs/datepair_input.rb +77 -0
  64. data/app/inputs/datetimepair_input.rb +37 -0
  65. data/app/mailers/user_mailer.rb +10 -0
  66. data/app/models/entities/account.rb +18 -11
  67. data/app/models/entities/account_contact.rb +4 -3
  68. data/app/models/entities/account_opportunity.rb +0 -1
  69. data/app/models/entities/campaign.rb +6 -7
  70. data/app/models/entities/contact.rb +53 -25
  71. data/app/models/entities/contact_opportunity.rb +0 -1
  72. data/app/models/entities/lead.rb +24 -13
  73. data/app/models/entities/opportunity.rb +34 -16
  74. data/app/models/fields/custom_field.rb +29 -13
  75. data/app/models/fields/custom_field_date_pair.rb +58 -0
  76. data/app/models/fields/custom_field_datetime_pair.rb +28 -0
  77. data/app/models/fields/custom_field_pair.rb +54 -0
  78. data/app/models/fields/field.rb +60 -43
  79. data/app/models/fields/field_group.rb +0 -9
  80. data/app/models/observers/entity_observer.rb +29 -0
  81. data/app/models/observers/opportunity_observer.rb +3 -0
  82. data/app/models/polymorphic/address.rb +14 -0
  83. data/app/models/polymorphic/avatar.rb +13 -1
  84. data/app/models/polymorphic/email.rb +1 -1
  85. data/app/models/polymorphic/task.rb +14 -13
  86. data/app/models/setting.rb +14 -21
  87. data/app/models/users/ability.rb +25 -4
  88. data/app/models/users/group.rb +13 -0
  89. data/app/models/users/permission.rb +3 -1
  90. data/app/models/users/preference.rb +1 -2
  91. data/app/models/users/user.rb +25 -3
  92. data/app/views/accounts/_account.html.haml +5 -39
  93. data/app/views/accounts/_edit.html.haml +3 -2
  94. data/app/views/accounts/_index_brief.html.haml +32 -0
  95. data/app/views/accounts/_index_long.html.haml +42 -0
  96. data/app/views/accounts/_new.html.haml +3 -2
  97. data/app/views/accounts/_sidebar_index.html.haml +3 -4
  98. data/app/views/accounts/_sidebar_show.html.haml +2 -2
  99. data/app/views/accounts/_title_bar.html.haml +11 -0
  100. data/app/views/accounts/_top_section.html.haml +1 -1
  101. data/app/views/accounts/create.js.rjs +1 -2
  102. data/app/views/accounts/destroy.js.rjs +1 -1
  103. data/app/views/accounts/edit.js.rjs +0 -2
  104. data/app/views/accounts/index.html.haml +6 -13
  105. data/app/views/accounts/index.js.rjs +3 -1
  106. data/app/views/accounts/index.xls.builder +82 -0
  107. data/app/views/accounts/new.js.rjs +0 -2
  108. data/app/views/accounts/show.html.haml +17 -15
  109. data/app/views/accounts/show.js.erb +2 -0
  110. data/app/views/admin/custom_fields/_base_field.html.haml +21 -0
  111. data/app/views/admin/custom_fields/_check_boxes_field.html.haml +6 -0
  112. data/app/views/admin/custom_fields/_date_field.html.haml +14 -0
  113. data/app/views/admin/custom_fields/_date_pair_field.html.haml +21 -0
  114. data/app/views/admin/custom_fields/_datetime_field.html.haml +14 -0
  115. data/app/views/admin/custom_fields/_datetime_pair_field.html.haml +21 -0
  116. data/app/views/admin/custom_fields/_radio_field.html.haml +6 -0
  117. data/app/views/admin/custom_fields/_select_field.html.haml +25 -0
  118. data/app/views/admin/custom_fields/_string_field.html.haml +2 -0
  119. data/app/views/admin/field_groups/_field_group.html.haml +7 -5
  120. data/app/views/admin/field_groups/_top_section.html.haml +4 -3
  121. data/app/views/admin/field_groups/create.js.rjs +1 -2
  122. data/app/views/admin/fields/_field.html.haml +5 -3
  123. data/app/views/admin/fields/_form.html.haml +35 -0
  124. data/app/views/admin/fields/_subform.html.haml +6 -0
  125. data/app/views/admin/fields/create.js.erb +19 -0
  126. data/app/views/admin/fields/destroy.js.erb +9 -0
  127. data/app/views/admin/fields/edit.js.erb +1 -0
  128. data/app/views/admin/fields/index.html.haml +11 -8
  129. data/app/views/admin/fields/update.js.erb +13 -0
  130. data/app/views/admin/groups/_edit.html.haml +12 -0
  131. data/app/views/admin/groups/_form.html.haml +13 -0
  132. data/app/views/admin/groups/_group.html.haml +15 -0
  133. data/app/views/admin/groups/_new.html.haml +12 -0
  134. data/app/views/admin/groups/create.js.rjs +11 -0
  135. data/app/views/admin/{fields → groups}/destroy.js.rjs +3 -3
  136. data/app/views/admin/groups/edit.js.rjs +21 -0
  137. data/app/views/admin/groups/index.html.haml +18 -0
  138. data/app/views/admin/groups/index.js.rjs +2 -0
  139. data/app/views/admin/groups/new.js.rjs +9 -0
  140. data/app/views/admin/groups/show.html.haml +11 -0
  141. data/app/views/admin/groups/update.js.rjs +10 -0
  142. data/app/views/admin/users/_profile.html.haml +13 -1
  143. data/app/views/admin/users/_user.html.haml +7 -2
  144. data/app/views/admin/users/index.js.rjs +1 -0
  145. data/app/views/application/index.atom.builder +1 -1
  146. data/app/views/application/index.rss.builder +1 -1
  147. data/app/views/application/show.atom.builder +32 -0
  148. data/app/views/application/show.rss.builder +29 -0
  149. data/app/views/campaigns/_campaign.html.haml +5 -21
  150. data/app/views/campaigns/_edit.html.haml +3 -2
  151. data/app/views/campaigns/_index_brief.html.haml +15 -0
  152. data/app/views/campaigns/_index_long.html.haml +20 -0
  153. data/app/views/campaigns/_new.html.haml +4 -2
  154. data/app/views/campaigns/_sidebar_index.html.haml +3 -3
  155. data/app/views/campaigns/_title_bar.html.haml +11 -0
  156. data/app/views/campaigns/_top_section.html.haml +2 -2
  157. data/app/views/campaigns/create.js.rjs +1 -4
  158. data/app/views/campaigns/destroy.js.rjs +1 -1
  159. data/app/views/campaigns/edit.js.rjs +0 -4
  160. data/app/views/campaigns/index.html.haml +6 -13
  161. data/app/views/campaigns/index.js.rjs +3 -1
  162. data/app/views/campaigns/index.xls.builder +75 -0
  163. data/app/views/campaigns/new.js.rjs +0 -4
  164. data/app/views/campaigns/show.html.haml +17 -15
  165. data/app/views/campaigns/show.js.erb +2 -0
  166. data/app/views/campaigns/update.js.rjs +0 -2
  167. data/app/views/comments/_comment.html.haml +6 -5
  168. data/app/views/comments/_edit.html.haml +5 -5
  169. data/app/views/comments/_new.html.haml +6 -5
  170. data/app/views/comments/create.js.rjs +1 -1
  171. data/app/views/contacts/_contact.html.haml +5 -52
  172. data/app/views/contacts/_contacts.html.haml +1 -0
  173. data/app/views/contacts/_edit.html.haml +3 -2
  174. data/app/views/contacts/_index_brief.html.haml +25 -0
  175. data/app/views/contacts/_index_full.html.haml +57 -0
  176. data/app/views/contacts/_index_long.html.haml +43 -0
  177. data/app/views/contacts/_new.html.haml +4 -2
  178. data/app/views/contacts/_section_extra.html.haml +26 -0
  179. data/app/views/contacts/_section_general.html.haml +22 -0
  180. data/app/views/contacts/_sidebar_index.html.haml +0 -3
  181. data/app/views/contacts/_sidebar_show.html.haml +2 -10
  182. data/app/views/contacts/_title_bar.html.haml +11 -0
  183. data/app/views/contacts/_top_section.html.haml +1 -1
  184. data/app/views/contacts/create.js.rjs +1 -2
  185. data/app/views/contacts/destroy.js.rjs +1 -1
  186. data/app/views/contacts/edit.js.rjs +0 -2
  187. data/app/views/contacts/index.html.haml +6 -14
  188. data/app/views/contacts/index.js.rjs +3 -1
  189. data/app/views/contacts/index.xls.builder +102 -0
  190. data/app/views/contacts/new.js.rjs +0 -2
  191. data/app/views/contacts/show.html.haml +15 -15
  192. data/app/views/contacts/show.js.erb +2 -0
  193. data/app/views/entities/_basic_search.html.haml +22 -0
  194. data/app/views/entities/_permissions.html.haml +41 -0
  195. data/app/views/entities/_search.html.haml +15 -0
  196. data/app/views/entities/_section_custom_fields.html.haml +11 -0
  197. data/app/views/entities/_title_bar.html.haml +12 -0
  198. data/app/views/entities/attach.js.rjs +1 -1
  199. data/app/views/entities/opportunities.js.rjs +1 -1
  200. data/app/views/entities/versions.js.erb +3 -0
  201. data/app/views/fields/_edit_custom_field_group.html.haml +8 -0
  202. data/app/views/fields/_group.html.haml +11 -20
  203. data/app/views/fields/_group_table.html.haml +13 -0
  204. data/app/views/fields/_group_view.html.haml +7 -0
  205. data/app/views/fields/_groups.html.haml +1 -4
  206. data/app/views/fields/_sidebar_show.html.haml +5 -4
  207. data/app/views/home/_account.html.haml +29 -0
  208. data/app/views/home/_activity.html.haml +16 -6
  209. data/app/views/home/_opportunity.html.haml +41 -0
  210. data/app/views/home/_options.html.haml +1 -1
  211. data/app/views/home/_task.html.haml +46 -0
  212. data/app/views/home/index.html.haml +32 -1
  213. data/app/views/home/index.js.rjs +1 -0
  214. data/app/views/layouts/_header.html.haml +3 -3
  215. data/app/views/layouts/_sidebar.html.haml +1 -3
  216. data/app/views/layouts/_tabbed.html.haml +2 -3
  217. data/app/views/layouts/_tabless.html.haml +1 -1
  218. data/app/views/layouts/admin/_header.html.haml +1 -1
  219. data/app/views/layouts/application.html.haml +7 -8
  220. data/app/views/layouts/header.xls.builder +8 -0
  221. data/app/views/leads/_contact.html.haml +1 -2
  222. data/app/views/leads/_convert.html.haml +1 -2
  223. data/app/views/leads/_convert_permissions.html.haml +21 -11
  224. data/app/views/leads/_edit.html.haml +3 -2
  225. data/app/views/leads/_index_brief.html.haml +41 -0
  226. data/app/views/leads/_index_long.html.haml +62 -0
  227. data/app/views/leads/_lead.html.haml +5 -62
  228. data/app/views/leads/_new.html.haml +4 -2
  229. data/app/views/leads/_opportunity.html.haml +1 -1
  230. data/app/views/leads/_sidebar_index.html.haml +3 -3
  231. data/app/views/leads/_sidebar_show.html.haml +3 -3
  232. data/app/views/leads/_status.html.haml +1 -1
  233. data/app/views/leads/_title_bar.html.haml +16 -0
  234. data/app/views/leads/convert.js.rjs +0 -1
  235. data/app/views/leads/create.js.rjs +1 -2
  236. data/app/views/leads/destroy.js.rjs +1 -1
  237. data/app/views/leads/edit.js.rjs +0 -2
  238. data/app/views/leads/index.html.haml +6 -13
  239. data/app/views/leads/index.js.rjs +3 -1
  240. data/app/views/leads/index.xls.builder +100 -0
  241. data/app/views/leads/new.js.rjs +0 -2
  242. data/app/views/leads/promote.js.rjs +0 -1
  243. data/app/views/leads/show.html.haml +13 -18
  244. data/app/views/leads/show.js.erb +2 -0
  245. data/app/views/{shared/_lists.html.haml → lists/_sidebar.html.haml} +0 -0
  246. data/app/views/lists/create.js.rjs +1 -1
  247. data/app/views/opportunities/_edit.html.haml +3 -2
  248. data/app/views/opportunities/_index_brief.html.haml +29 -0
  249. data/app/views/opportunities/_index_long.html.haml +59 -0
  250. data/app/views/opportunities/_new.html.haml +4 -2
  251. data/app/views/opportunities/_opportunity.html.haml +5 -57
  252. data/app/views/opportunities/_sidebar_index.html.haml +3 -3
  253. data/app/views/opportunities/_sidebar_show.html.haml +2 -2
  254. data/app/views/opportunities/_title_bar.html.haml +11 -0
  255. data/app/views/opportunities/_top_section.html.haml +2 -2
  256. data/app/views/opportunities/create.js.rjs +4 -6
  257. data/app/views/opportunities/destroy.js.rjs +1 -1
  258. data/app/views/opportunities/edit.js.rjs +0 -3
  259. data/app/views/opportunities/index.html.haml +6 -14
  260. data/app/views/opportunities/index.js.rjs +3 -1
  261. data/app/views/opportunities/index.xls.builder +69 -0
  262. data/app/views/opportunities/new.js.rjs +1 -5
  263. data/app/views/opportunities/show.html.haml +14 -14
  264. data/app/views/opportunities/show.js.erb +2 -0
  265. data/app/views/opportunities/update.js.rjs +0 -1
  266. data/app/views/passwords/new.html.haml +1 -1
  267. data/app/views/shared/_add_comment.html.haml +8 -0
  268. data/app/views/shared/_address.html.haml +1 -1
  269. data/app/views/shared/_address_show.html.haml +1 -1
  270. data/app/views/shared/_edit_comment.html.haml +2 -2
  271. data/app/views/shared/_export.html.haml +2 -1
  272. data/app/views/shared/_paginate_with_per_page.html.haml +14 -0
  273. data/app/views/shared/_recently.html.haml +1 -1
  274. data/app/views/shared/_search.html.haml +1 -1
  275. data/app/views/shared/_tags.html.haml +1 -2
  276. data/app/views/shared/_tasks.html.haml +2 -2
  277. data/app/views/tasks/_completed.html.haml +3 -3
  278. data/app/views/tasks/_new.html.haml +1 -1
  279. data/app/views/tasks/_pending.html.haml +2 -3
  280. data/app/views/tasks/_sidebar_index.html.haml +2 -2
  281. data/app/views/tasks/_top_section.html.haml +5 -3
  282. data/app/views/tasks/create.js.rjs +3 -4
  283. data/app/views/tasks/edit.js.rjs +2 -4
  284. data/app/views/tasks/index.html.haml +1 -1
  285. data/app/views/tasks/index.xls.builder +47 -0
  286. data/app/views/tasks/new.js.rjs +0 -2
  287. data/app/views/tasks/update.js.rjs +0 -1
  288. data/app/views/user_mailer/assigned_entity_notification.html.haml +3 -0
  289. data/app/views/users/_user.html.haml +2 -2
  290. data/app/views/users/_user_report.html.haml +6 -0
  291. data/app/views/users/opportunities_overview.html.haml +16 -0
  292. data/app/views/users/show.html.haml +2 -3
  293. data/app/views/users/update.js.rjs +1 -1
  294. data/app/views/versions/_version.html.haml +4 -19
  295. data/app/views/versions/_version_item.html.haml +8 -0
  296. data/app/views/versions/_versions.html.haml +9 -9
  297. data/config/application.rb +10 -2
  298. data/config/deploy.example.rb +47 -0
  299. data/config/environments/development.rb +1 -0
  300. data/config/environments/production.rb +1 -3
  301. data/config/environments/staging.rb +1 -1
  302. data/config/initializers/action_mailer.rb +10 -8
  303. data/config/initializers/custom_fields.rb +21 -0
  304. data/config/initializers/gravatar.rb +7 -0
  305. data/config/initializers/paper_trail.rb +1 -0
  306. data/config/initializers/ransack.rb +15 -0
  307. data/config/initializers/simple_form.rb +1 -2
  308. data/config/initializers/views.rb +42 -0
  309. data/config/locales/cz_fat_free_crm.yml +0 -10
  310. data/config/locales/de_fat_free_crm.yml +23 -21
  311. data/config/locales/en-GB_fat_free_crm.yml +241 -25
  312. data/config/locales/en-US_fat_free_crm.yml +139 -57
  313. data/config/locales/en-US_ransack.yml +92 -0
  314. data/config/locales/es_fat_free_crm.yml +0 -6
  315. data/config/locales/fr-CA_fat_free_crm.yml +0 -6
  316. data/config/locales/fr_fat_free_crm.yml +3 -14
  317. data/config/locales/it_fat_free_crm.yml +0 -11
  318. data/config/locales/ja_fat_free_crm.yml +0 -10
  319. data/config/locales/pl_fat_free_crm.yml +0 -6
  320. data/config/locales/pt-BR_fat_free_crm.yml +0 -6
  321. data/config/locales/ru_fat_free_crm.yml +0 -9
  322. data/config/locales/sv-SE_fat_free_crm.yml +0 -10
  323. data/config/locales/th_fat_free_crm.yml +0 -6
  324. data/config/locales/zh-CN.yml +4 -0
  325. data/config/locales/zh-CN_fat_free_crm.yml +78 -47
  326. data/config/routes.rb +13 -0
  327. data/config/settings.default.yml +20 -20
  328. data/db/demo/addresses.yml +2 -2
  329. data/db/migrate/20100928030598_create_sessions.rb +5 -0
  330. data/db/migrate/20100928030627_acts_as_taggable_on_migration.rb +6 -6
  331. data/db/migrate/20111201030535_add_field_groups_klass_name.rb +2 -1
  332. data/db/migrate/20120406082136_create_groups.rb +19 -0
  333. data/db/migrate/20120510025219_add_not_null_constraints_for_timestamp_columns.rb +21 -0
  334. data/db/migrate/20120528102124_increase_length_of_version_events.rb +9 -0
  335. data/db/migrate/20120801032706_add_pair_id_to_fields.rb +5 -0
  336. data/db/migrate/20121003063155_add_settings_to_custom_fields.rb +5 -0
  337. data/db/migrate/20121221033947_fix_country_mapping.rb +32 -0
  338. data/db/schema.rb +25 -6
  339. data/fat_free_crm.gemspec +16 -13
  340. data/lib/fat_free_crm.rb +2 -0
  341. data/lib/fat_free_crm/comment_extensions.rb +23 -0
  342. data/lib/fat_free_crm/core_ext/array.rb +8 -2
  343. data/lib/fat_free_crm/fields.rb +24 -2
  344. data/lib/fat_free_crm/gem_dependencies.rb +7 -1
  345. data/lib/fat_free_crm/permissions.rb +47 -26
  346. data/lib/fat_free_crm/version.rb +1 -1
  347. data/lib/fat_free_crm/view_factory.rb +95 -0
  348. data/lib/plugins/country_select/lib/country_select.rb +393 -374
  349. data/lib/tasks/{plugins.rake → db/migrate.rake} +1 -1
  350. data/lib/tasks/{schema_upgrade.rake → db/schema.rake} +2 -1
  351. data/lib/tasks/{mail_processing.rake → ffcrm/comment_replies.rake} +2 -21
  352. data/lib/tasks/ffcrm/config.rake +33 -0
  353. data/lib/tasks/{demo.rake → ffcrm/demo.rake} +3 -2
  354. data/lib/tasks/ffcrm/dropbox.rake +42 -0
  355. data/lib/tasks/ffcrm/settings.rake +46 -0
  356. data/lib/tasks/{fat_free_crm.rake → ffcrm/setup.rake} +1 -39
  357. data/lib/tasks/ffcrm/update_data.rake +256 -0
  358. data/{acceptance → spec/acceptance}/acceptance_helper.rb +5 -4
  359. data/{acceptance → spec/acceptance}/accounts_spec.rb +36 -10
  360. data/spec/acceptance/admin/groups_spec.rb +26 -0
  361. data/spec/acceptance/admin/users_spec.rb +34 -0
  362. data/spec/acceptance/campaigns_spec.rb +101 -0
  363. data/spec/acceptance/contacts_spec.rb +101 -0
  364. data/spec/acceptance/dashboard_spec.rb +62 -0
  365. data/spec/acceptance/leads_spec.rb +109 -0
  366. data/spec/acceptance/opportunities_overview_spec.rb +82 -0
  367. data/spec/acceptance/opportunities_spec.rb +103 -0
  368. data/{acceptance → spec/acceptance}/support/browser.rb +0 -0
  369. data/{acceptance → spec/acceptance}/support/headless.rb +0 -0
  370. data/{acceptance → spec/acceptance}/support/helpers.rb +8 -1
  371. data/{acceptance → spec/acceptance}/support/maintain_sessions.rb +0 -0
  372. data/spec/acceptance/support/paths.rb +42 -0
  373. data/spec/acceptance/support/selector_helpers.rb +31 -0
  374. data/spec/acceptance/tasks_spec.rb +85 -0
  375. data/spec/controllers/admin/groups_controller_spec.rb +5 -0
  376. data/spec/controllers/admin/users_controller_spec.rb +2 -3
  377. data/spec/controllers/applications_controller_spec.rb +40 -0
  378. data/spec/controllers/comments_controller_spec.rb +5 -6
  379. data/spec/controllers/emails_controller_spec.rb +1 -2
  380. data/spec/controllers/entities/accounts_controller_spec.rb +65 -81
  381. data/spec/controllers/entities/campaigns_controller_spec.rb +69 -85
  382. data/spec/controllers/entities/contacts_controller_spec.rb +78 -95
  383. data/spec/controllers/entities/leads_controller_spec.rb +105 -127
  384. data/spec/controllers/entities/opportunities_controller_spec.rb +88 -103
  385. data/spec/controllers/entities_controller_spec.rb +39 -0
  386. data/spec/controllers/home_controller_spec.rb +101 -11
  387. data/spec/controllers/tasks_controller_spec.rb +27 -32
  388. data/spec/controllers/users_controller_spec.rb +95 -28
  389. data/spec/factories/account_factories.rb +2 -2
  390. data/spec/factories/opportunity_factories.rb +12 -4
  391. data/spec/factories/task_factories.rb +4 -0
  392. data/spec/factories/user_factories.rb +9 -2
  393. data/spec/helpers/application_helper_spec.rb +15 -1
  394. data/spec/helpers/groups_helper_spec.rb +15 -0
  395. data/spec/helpers/users_helper_spec.rb +26 -4
  396. data/spec/lib/comment_extensions_spec.rb +29 -0
  397. data/spec/lib/core_ext/{string.rb → string_spec.rb} +0 -1
  398. data/spec/lib/fields_spec.rb +104 -0
  399. data/spec/lib/permissions_spec.rb +144 -0
  400. data/spec/lib/view_factory_spec.rb +68 -0
  401. data/spec/mailers/subscription_mailer_spec.rb +18 -4
  402. data/spec/mailers/user_mailer_spec.rb +134 -0
  403. data/spec/models/entities/account_spec.rb +44 -4
  404. data/spec/models/entities/campaign_spec.rb +3 -4
  405. data/spec/models/entities/contact_spec.rb +45 -5
  406. data/spec/models/entities/lead_spec.rb +3 -4
  407. data/spec/models/entities/opportunity_spec.rb +105 -9
  408. data/spec/models/fields/custom_field_date_pair_spec.rb +106 -0
  409. data/spec/models/fields/custom_field_pair_spec.rb +94 -0
  410. data/spec/models/fields/custom_field_spec.rb +43 -23
  411. data/spec/models/fields/field_spec.rb +3 -5
  412. data/spec/models/observers/entity_observer_spec.rb +65 -0
  413. data/spec/models/polymorphic/task_spec.rb +74 -118
  414. data/spec/models/polymorphic/version_spec.rb +18 -18
  415. data/spec/models/users/group_spec.rb +5 -0
  416. data/spec/models/users/permission_spec.rb +18 -1
  417. data/spec/models/users/preference_spec.rb +5 -1
  418. data/spec/models/users/user_spec.rb +55 -8
  419. data/spec/shared/models.rb +54 -0
  420. data/spec/spec_helper.rb +82 -87
  421. data/spec/support/auth_macros.rb +2 -3
  422. data/spec/views/accounts/_edit.haml_spec.rb +1 -2
  423. data/spec/views/accounts/_new.haml_spec.rb +1 -2
  424. data/spec/views/accounts/create.rjs_spec.rb +1 -3
  425. data/spec/views/accounts/edit.rjs_spec.rb +3 -4
  426. data/spec/views/accounts/index.haml_spec.rb +9 -6
  427. data/spec/views/accounts/new.rjs_spec.rb +2 -9
  428. data/spec/views/accounts/show.haml_spec.rb +1 -2
  429. data/spec/views/accounts/update.rjs_spec.rb +2 -3
  430. data/spec/views/admin/users/_create.haml_spec.rb +1 -2
  431. data/spec/views/admin/users/create.rjs_spec.rb +1 -2
  432. data/spec/views/campaigns/_edit.haml_spec.rb +2 -3
  433. data/spec/views/campaigns/_new.haml_spec.rb +2 -3
  434. data/spec/views/campaigns/create.rjs_spec.rb +0 -3
  435. data/spec/views/campaigns/destroy.rjs_spec.rb +1 -2
  436. data/spec/views/campaigns/edit.rjs_spec.rb +3 -6
  437. data/spec/views/campaigns/index.haml_spec.rb +6 -3
  438. data/spec/views/campaigns/new.rjs_spec.rb +2 -13
  439. data/spec/views/campaigns/show.haml_spec.rb +4 -6
  440. data/spec/views/campaigns/update.rjs_spec.rb +2 -7
  441. data/spec/views/contacts/_edit.haml_spec.rb +6 -7
  442. data/spec/views/contacts/_new.haml_spec.rb +2 -3
  443. data/spec/views/contacts/create.rjs_spec.rb +1 -3
  444. data/spec/views/contacts/edit.rjs_spec.rb +3 -4
  445. data/spec/views/contacts/index.haml_spec.rb +6 -3
  446. data/spec/views/contacts/new.rjs_spec.rb +2 -11
  447. data/spec/views/contacts/show.haml_spec.rb +1 -2
  448. data/spec/views/contacts/update.rjs_spec.rb +2 -3
  449. data/spec/views/home/index.haml_spec.rb +6 -2
  450. data/spec/views/home/index.rjs_spec.rb +1 -2
  451. data/spec/views/home/options.rjs_spec.rb +1 -2
  452. data/spec/views/leads/_convert.haml_spec.rb +1 -2
  453. data/spec/views/leads/_edit.haml_spec.rb +2 -3
  454. data/spec/views/leads/_new.haml_spec.rb +2 -3
  455. data/spec/views/leads/_sidebar_show.haml_spec.rb +1 -2
  456. data/spec/views/leads/convert.rjs_spec.rb +3 -5
  457. data/spec/views/leads/edit.rjs_spec.rb +4 -5
  458. data/spec/views/leads/index.haml_spec.rb +6 -3
  459. data/spec/views/leads/new.rjs_spec.rb +2 -11
  460. data/spec/views/leads/promote.rjs_spec.rb +3 -5
  461. data/spec/views/leads/show.haml_spec.rb +1 -2
  462. data/spec/views/leads/update.rjs_spec.rb +2 -3
  463. data/spec/views/opportunities/_edit.haml_spec.rb +5 -6
  464. data/spec/views/opportunities/_new.haml_spec.rb +2 -4
  465. data/spec/views/opportunities/create.rjs_spec.rb +0 -2
  466. data/spec/views/opportunities/edit.rjs_spec.rb +3 -4
  467. data/spec/views/opportunities/index.haml_spec.rb +7 -4
  468. data/spec/views/opportunities/new.rjs_spec.rb +2 -12
  469. data/spec/views/opportunities/show.haml_spec.rb +1 -2
  470. data/spec/views/opportunities/update.rjs_spec.rb +2 -3
  471. data/spec/views/tasks/_edit.haml_spec.rb +1 -3
  472. data/spec/views/tasks/complete.rjs_spec.rb +2 -3
  473. data/spec/views/tasks/create.rjs_spec.rb +2 -2
  474. data/spec/views/tasks/edit.rjs_spec.rb +1 -2
  475. data/spec/views/tasks/new.rjs_spec.rb +1 -18
  476. data/spec/views/users/avatar.rjs_spec.rb +1 -2
  477. data/spec/views/users/change_password.rjs_spec.rb +1 -2
  478. data/spec/views/users/edit.rjs_spec.rb +1 -2
  479. data/spec/views/users/password.rjs_spec.rb +1 -1
  480. data/spec/views/users/update.rjs_spec.rb +1 -2
  481. data/spec/views/users/upload_avatar.rjs_spec.rb +6 -7
  482. data/vendor/assets/images/brief.png +0 -0
  483. data/vendor/assets/images/chosen-sprite.png +0 -0
  484. data/vendor/assets/images/full.png +0 -0
  485. data/vendor/assets/images/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  486. data/vendor/assets/images/jquery-ui/ui-bg_flat_0_eeeeee_40x100.png +0 -0
  487. data/vendor/assets/images/jquery-ui/ui-bg_flat_100_ffffff_40x100.png +0 -0
  488. data/vendor/assets/images/jquery-ui/ui-bg_flat_25_3875d7_40x100.png +0 -0
  489. data/vendor/assets/images/jquery-ui/ui-bg_flat_55_ffffff_40x100.png +0 -0
  490. data/vendor/assets/images/jquery-ui/ui-bg_flat_65_3875d7_40x100.png +0 -0
  491. data/vendor/assets/images/jquery-ui/ui-bg_flat_75_ffffff_40x100.png +0 -0
  492. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
  493. data/vendor/assets/images/jquery-ui/{ui-icons_ef8c08_256x240.png → ui-icons_0073ea_256x240.png} +0 -0
  494. data/vendor/assets/images/jquery-ui/ui-icons_466bb1_256x240.png +0 -0
  495. data/vendor/assets/images/jquery-ui/{ui-icons_ffd27a_256x240.png → ui-icons_ff0084_256x240.png} +0 -0
  496. data/vendor/assets/images/long.png +0 -0
  497. data/vendor/assets/javascripts/chosen-jquery.js +3 -0
  498. data/vendor/assets/javascripts/chosen-prototype.js +3 -0
  499. data/vendor/assets/javascripts/chosen.jquery.coffee +574 -0
  500. data/vendor/assets/javascripts/chosen.proto.coffee +580 -0
  501. data/vendor/assets/javascripts/jquery.disable.js +23 -0
  502. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-addon.js +1882 -0
  503. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-af.js +20 -0
  504. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ca.js +20 -0
  505. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-cz.js +20 -0
  506. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-de.js +20 -0
  507. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-el.js +20 -0
  508. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-es.js +20 -0
  509. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-et.js +20 -0
  510. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fi.js +20 -0
  511. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fr-CA.js +20 -0
  512. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fr.js +20 -0
  513. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-gl.js +20 -0
  514. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-he.js +20 -0
  515. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-hu.js +20 -0
  516. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-id.js +20 -0
  517. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-it.js +20 -0
  518. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ja.js +20 -0
  519. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ko.js +20 -0
  520. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-lt.js +20 -0
  521. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-nl.js +20 -0
  522. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-no.js +20 -0
  523. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-pl.js +20 -0
  524. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-pt.js +20 -0
  525. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ro.js +20 -0
  526. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ru.js +20 -0
  527. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-sk.js +20 -0
  528. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-sv.js +20 -0
  529. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-tr.js +20 -0
  530. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-vi.js +20 -0
  531. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-zh-CN.js +20 -0
  532. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-zh-TW.js +20 -0
  533. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-cz.js +23 -0
  534. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-de.js +23 -0
  535. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-en-GB.js +23 -0
  536. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-es.js +23 -0
  537. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-fr.js +23 -0
  538. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-it.js +23 -0
  539. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-ja.js +23 -0
  540. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-pl.js +23 -0
  541. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-pt-BR.js +23 -0
  542. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-ru.js +23 -0
  543. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-sv-SE.js +23 -0
  544. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-th.js +23 -0
  545. data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-zh-CN.js +23 -0
  546. data/vendor/assets/javascripts/lib/abstract-chosen.coffee +110 -0
  547. data/vendor/assets/javascripts/lib/select-parser.coffee +51 -0
  548. data/vendor/assets/stylesheets/chosen.css.sass +361 -0
  549. data/vendor/assets/stylesheets/{jquery-ui.custom.css.erb → jquery-ui.custom.css} +70 -70
  550. data/zeus.json +21 -0
  551. metadata +304 -140
  552. data/acceptance/support/database_cleaner.rb +0 -16
  553. data/acceptance/support/paths.rb +0 -9
  554. data/app/views/accounts/_options.html.haml +0 -20
  555. data/app/views/accounts/_permissions.html.haml +0 -24
  556. data/app/views/accounts/options.js.rjs +0 -10
  557. data/app/views/admin/fields/_edit.html.haml +0 -9
  558. data/app/views/admin/fields/_new.html.haml +0 -10
  559. data/app/views/admin/fields/_options.html.haml +0 -12
  560. data/app/views/admin/fields/_top_section.html.haml +0 -54
  561. data/app/views/admin/fields/create.js.rjs +0 -16
  562. data/app/views/admin/fields/edit.js.rjs +0 -35
  563. data/app/views/admin/fields/index.js.rjs +0 -6
  564. data/app/views/admin/fields/new.js.rjs +0 -8
  565. data/app/views/admin/fields/options.js.rjs +0 -10
  566. data/app/views/admin/fields/update.js.rjs +0 -17
  567. data/app/views/campaigns/_options.html.haml +0 -20
  568. data/app/views/campaigns/_permissions.html.haml +0 -24
  569. data/app/views/campaigns/options.js.rjs +0 -10
  570. data/app/views/contacts/_options.html.haml +0 -21
  571. data/app/views/contacts/_permissions.html.haml +0 -24
  572. data/app/views/contacts/options.js.rjs +0 -10
  573. data/app/views/entities/_advanced_search.html.haml +0 -12
  574. data/app/views/entities/_condition_fields.html.haml +0 -14
  575. data/app/views/entities/_grouping_fields.html.haml +0 -12
  576. data/app/views/entities/advanced_search.js.rjs +0 -9
  577. data/app/views/entities/versions.js.rjs +0 -3
  578. data/app/views/leads/_options.html.haml +0 -21
  579. data/app/views/leads/_permissions.html.haml +0 -28
  580. data/app/views/leads/options.js.rjs +0 -10
  581. data/app/views/opportunities/_options.html.haml +0 -20
  582. data/app/views/opportunities/_permissions.html.haml +0 -24
  583. data/app/views/opportunities/options.js.rjs +0 -10
  584. data/app/views/shared/_outline.html.haml +0 -9
  585. data/app/views/shared/_per_page.html.haml +0 -10
  586. data/app/views/shared/_sort_by.html.haml +0 -10
  587. data/config/initializers/cancan.rb +0 -151
  588. data/config/initializers/squeel.rb +0 -5
  589. data/public/blank_iframe.html +0 -2
  590. data/spec/views/accounts/options.rjs_spec.rb +0 -59
  591. data/spec/views/campaigns/options.rjs_spec.rb +0 -60
  592. data/spec/views/contacts/options.rjs_spec.rb +0 -61
  593. data/spec/views/leads/options.rjs_spec.rb +0 -61
  594. data/spec/views/opportunities/options.rjs_spec.rb +0 -60
  595. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  596. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  597. data/vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
  598. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  599. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  600. data/vendor/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
  601. data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  602. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  603. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  604. data/vendor/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
  605. data/vendor/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
  606. data/vendor/assets/javascripts/calendar_date_select/calendar_date_select.js +0 -446
  607. data/vendor/assets/javascripts/calendar_date_select/format_american.js +0 -35
  608. data/vendor/assets/javascripts/calendar_date_select/format_danish.js +0 -31
  609. data/vendor/assets/javascripts/calendar_date_select/format_db.js +0 -27
  610. data/vendor/assets/javascripts/calendar_date_select/format_euro_24hr.js +0 -7
  611. data/vendor/assets/javascripts/calendar_date_select/format_euro_24hr_ymd.js +0 -7
  612. data/vendor/assets/javascripts/calendar_date_select/format_finnish.js +0 -32
  613. data/vendor/assets/javascripts/calendar_date_select/format_french.js +0 -24
  614. data/vendor/assets/javascripts/calendar_date_select/format_hyphen_ampm.js +0 -37
  615. data/vendor/assets/javascripts/calendar_date_select/format_iso_date.js +0 -29
  616. data/vendor/assets/javascripts/calendar_date_select/format_italian.js +0 -24
  617. data/vendor/assets/javascripts/calendar_date_select/locale/ar.js +0 -10
  618. data/vendor/assets/javascripts/calendar_date_select/locale/da.js +0 -11
  619. data/vendor/assets/javascripts/calendar_date_select/locale/de.js +0 -11
  620. data/vendor/assets/javascripts/calendar_date_select/locale/es.js +0 -11
  621. data/vendor/assets/javascripts/calendar_date_select/locale/fi.js +0 -10
  622. data/vendor/assets/javascripts/calendar_date_select/locale/fr.js +0 -11
  623. data/vendor/assets/javascripts/calendar_date_select/locale/it.js +0 -9
  624. data/vendor/assets/javascripts/calendar_date_select/locale/ja.js +0 -11
  625. data/vendor/assets/javascripts/calendar_date_select/locale/nl.js +0 -11
  626. data/vendor/assets/javascripts/calendar_date_select/locale/pl.js +0 -11
  627. data/vendor/assets/javascripts/calendar_date_select/locale/pt.js +0 -11
  628. data/vendor/assets/javascripts/calendar_date_select/locale/ru.js +0 -10
  629. data/vendor/assets/javascripts/calendar_date_select/locale/sl.js +0 -11
  630. data/vendor/assets/javascripts/calendar_date_select/locale/sv.js +0 -9
  631. data/vendor/assets/stylesheets/calendar_date_select/blue.css +0 -130
  632. data/vendor/assets/stylesheets/calendar_date_select/default.css +0 -135
  633. data/vendor/assets/stylesheets/calendar_date_select/green.css +0 -142
  634. data/vendor/assets/stylesheets/calendar_date_select/plain.css +0 -128
  635. data/vendor/assets/stylesheets/calendar_date_select/red.css +0 -135
  636. data/vendor/assets/stylesheets/calendar_date_select/silver.css +0 -133
  637. data/vendor/plugins/.gitkeep~master +0 -0
@@ -30,8 +30,9 @@
30
30
  class AccountContact < ActiveRecord::Base
31
31
  belongs_to :account
32
32
  belongs_to :contact
33
- validates_presence_of :account_id, :contact_id
33
+
34
+ has_paper_trail :meta => { :related => :contact }, :ignore => [ :id, :created_at, :updated_at, :contact_id ]
35
+
36
+ validates :account_id, :presence => true
34
37
 
35
- # has_paper_trail
36
38
  end
37
-
@@ -34,4 +34,3 @@ class AccountOpportunity < ActiveRecord::Base
34
34
 
35
35
  has_paper_trail
36
36
  end
37
-
@@ -57,19 +57,20 @@ class Campaign < ActiveRecord::Base
57
57
  scope :created_by, lambda { |user| where('user_id = ?' , user.id) }
58
58
  scope :assigned_to, lambda { |user| where('assigned_to = ?', user.id) }
59
59
 
60
- scope :text_search, lambda { |query|
61
- query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
62
- where('upper(name) LIKE upper(?)', "%#{query}%")
63
- }
60
+ scope :text_search, lambda { |query| search('name_cont' => query).result }
64
61
 
65
62
  uses_user_permissions
66
63
  acts_as_commentable
64
+ uses_comment_extensions
67
65
  acts_as_taggable_on :tags
68
66
  has_paper_trail :ignore => [ :subscribed_users ]
69
67
  has_fields
70
68
  exportable
71
69
  sortable :by => [ "name ASC", "target_leads DESC", "target_revenue DESC", "leads_count DESC", "revenue DESC", "starts_on DESC", "ends_on DESC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
72
70
 
71
+ has_ransackable_associations %w(leads opportunities tags activities emails comments tasks)
72
+ ransack_can_autocomplete
73
+
73
74
  validates_presence_of :name, :message => :missing_campaign_name
74
75
  validates_uniqueness_of :name, :scope => [ :user_id, :deleted_at ]
75
76
  validate :start_and_end_dates
@@ -77,8 +78,7 @@ class Campaign < ActiveRecord::Base
77
78
 
78
79
  # Default values provided through class methods.
79
80
  #----------------------------------------------------------------------------
80
- def self.per_page ; 20 ; end
81
- def self.outline ; "long" ; end
81
+ def self.per_page ; 20 ; end
82
82
 
83
83
  # Attach given attachment to the campaign if it hasn't been attached already.
84
84
  #----------------------------------------------------------------------------
@@ -121,4 +121,3 @@ class Campaign < ActiveRecord::Base
121
121
  end
122
122
 
123
123
  end
124
-
@@ -58,33 +58,45 @@ class Contact < ActiveRecord::Base
58
58
  has_many :opportunities, :through => :contact_opportunities, :uniq => true, :order => "opportunities.id DESC"
59
59
  has_many :tasks, :as => :asset, :dependent => :destroy#, :order => 'created_at DESC'
60
60
  has_one :business_address, :dependent => :destroy, :as => :addressable, :class_name => "Address", :conditions => "address_type = 'Business'"
61
+ has_many :addresses, :dependent => :destroy, :as => :addressable, :class_name => "Address" # advanced search uses this
61
62
  has_many :emails, :as => :mediator
62
63
 
63
- serialize :subscribed_users, Set
64
+ has_ransackable_associations %w(account opportunities tags activities emails addresses comments tasks)
65
+ ransack_can_autocomplete
64
66
 
65
- accepts_nested_attributes_for :business_address, :allow_destroy => true
67
+ serialize :subscribed_users, Set
66
68
 
69
+ accepts_nested_attributes_for :business_address, :allow_destroy => true, :reject_if => proc {|attributes| Address.reject_address(attributes)}
70
+
67
71
  scope :created_by, lambda { |user| { :conditions => [ "user_id = ?", user.id ] } }
68
72
  scope :assigned_to, lambda { |user| { :conditions => ["assigned_to = ?", user.id ] } }
69
73
 
70
74
  scope :text_search, lambda { |query|
71
- query = query.gsub(/[^@\w\s\-\.'\p{L}]/u, '').strip
75
+ t = Contact.arel_table
72
76
  # We can't always be sure that names are entered in the right order, so we must
73
77
  # split the query into all possible first/last name permutations.
74
78
  name_query = if query.include?(" ")
75
- query.name_permutations.map{ |first, last|
76
- "(upper(first_name) LIKE upper('%#{first}%') AND upper(last_name) LIKE upper('%#{last}%'))"
77
- }.join(" OR ")
79
+ scope, *rest = query.name_permutations.map{ |first, last|
80
+ t[:first_name].matches("%#{first}%").and(t[:last_name].matches("%#{last}%"))
81
+ }
82
+ rest.map{|r| scope = scope.or(r)} if scope
83
+ scope
78
84
  else
79
- "upper(first_name) LIKE upper('%#{query}%') OR upper(last_name) LIKE upper('%#{query}%')"
85
+ t[:first_name].matches("%#{query}%").or(t[:last_name].matches("%#{query}%"))
80
86
  end
81
- where("#{name_query} OR upper(email) LIKE upper(:m) OR upper(alt_email) LIKE upper(:m) OR phone LIKE :m OR mobile LIKE :m", :m => "%#{query}%")
87
+
88
+ other = t[:email].matches("%#{query}%").or(t[:alt_email].matches("%#{query}%"))
89
+ other = other.or(t[:phone].matches("%#{query}%")).or(t[:mobile].matches("%#{query}%"))
90
+
91
+ where( name_query.nil? ? other : name_query.or(other) )
82
92
  }
83
93
 
84
94
  uses_user_permissions
85
95
  acts_as_commentable
96
+ uses_comment_extensions
86
97
  acts_as_taggable_on :tags
87
98
  has_paper_trail :ignore => [ :subscribed_users ]
99
+
88
100
  has_fields
89
101
  exportable
90
102
  sortable :by => [ "first_name ASC", "last_name ASC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
@@ -96,7 +108,6 @@ class Contact < ActiveRecord::Base
96
108
  # Default values provided through class methods.
97
109
  #----------------------------------------------------------------------------
98
110
  def self.per_page ; 20 ; end
99
- def self.outline ; "long" ; end
100
111
  def self.first_name_position ; "before" ; end
101
112
 
102
113
  #----------------------------------------------------------------------------
@@ -112,25 +123,19 @@ class Contact < ActiveRecord::Base
112
123
  # Backend handler for [Create New Contact] form (see contact/create).
113
124
  #----------------------------------------------------------------------------
114
125
  def save_with_account_and_permissions(params)
115
- account = Account.create_or_select_for(self, params[:account], params[:users])
116
- self.account_contact = AccountContact.new(:account => account, :contact => self) unless account.id.blank?
126
+ save_account(params)
117
127
  self.opportunities << Opportunity.find(params[:opportunity]) unless params[:opportunity].blank?
118
- self.save_with_permissions(params[:users])
128
+ self.save
119
129
  end
120
130
 
121
131
  # Backend handler for [Update Contact] form (see contact/update).
122
132
  #----------------------------------------------------------------------------
123
133
  def update_with_account_and_permissions(params)
124
- if params[:account][:id] == "" || params[:account][:name] == ""
125
- self.account = nil # Contact is not associated with the account anymore.
126
- else
127
- account = Account.create_or_select_for(self, params[:account], params[:users])
128
- if self.account != account and account.id.present?
129
- self.account_contact = AccountContact.new(:account => account, :contact => self)
130
- end
131
- end
132
- self.reload
133
- self.update_with_permissions(params[:contact], params[:users])
134
+ save_account(params)
135
+ # Must set access before user_ids, because user_ids= method depends on access value.
136
+ self.access = params[:contact][:access] if params[:contact][:access]
137
+ self.attributes = params[:contact]
138
+ self.save
134
139
  end
135
140
 
136
141
  # Attach given attachment to the contact if it hasn't been attached already.
@@ -163,8 +168,18 @@ class Contact < ActiveRecord::Base
163
168
  %w(first_name last_name title source email alt_email phone mobile blog linkedin facebook twitter skype do_not_call background_info).each do |name|
164
169
  attributes[name] = model.send(name.intern)
165
170
  end
166
-
171
+
167
172
  contact = Contact.new(attributes)
173
+
174
+ # Set custom fields.
175
+ if model.class.respond_to?(:fields)
176
+ model.class.fields.each do |field|
177
+ if contact.respond_to?(field.name)
178
+ contact.send "#{field.name}=", model.send(field.name)
179
+ end
180
+ end
181
+ end
182
+
168
183
  contact.business_address = Address.new(:street1 => model.business_address.street1, :street2 => model.business_address.street2, :city => model.business_address.city, :state => model.business_address.state, :zipcode => model.business_address.zipcode, :country => model.business_address.country, :full_address => model.business_address.full_address, :address_type => "Business") unless model.business_address.nil?
169
184
 
170
185
  # Save the contact only if the account and the opportunity have no errors.
@@ -173,7 +188,7 @@ class Contact < ActiveRecord::Base
173
188
  contact.account_contact = AccountContact.new(:account => account, :contact => contact) unless account.id.blank?
174
189
  contact.opportunities << opportunity unless opportunity.id.blank?
175
190
  if contact.access != "Lead" || model.nil?
176
- contact.save_with_permissions(params[:users])
191
+ contact.save
177
192
  else
178
193
  contact.save_with_model_permissions(model)
179
194
  end
@@ -187,6 +202,19 @@ class Contact < ActiveRecord::Base
187
202
  def users_for_shared_access
188
203
  errors.add(:access, :share_contact) if self[:access] == "Shared" && !self.permissions.any?
189
204
  end
205
+
206
+ # Handles the saving of related accounts
207
+ #----------------------------------------------------------------------------
208
+ def save_account(params)
209
+ if params[:account][:id] == "" || params[:account][:name] == ""
210
+ self.account = nil
211
+ else
212
+ account = Account.create_or_select_for(self, params[:account])
213
+ if self.account != account and account.id.present?
214
+ self.account_contact = AccountContact.new(:account => account, :contact => self)
215
+ end
216
+ end
217
+ self.reload unless self.new_record? # ensure the account association is updated
218
+ end
190
219
 
191
220
  end
192
-
@@ -35,4 +35,3 @@ class ContactOpportunity < ActiveRecord::Base
35
35
 
36
36
  # has_paper_trail
37
37
  end
38
-
@@ -55,6 +55,7 @@ class Lead < ActiveRecord::Base
55
55
  has_one :contact, :dependent => :nullify # On destroy keep the contact, but nullify its lead_id
56
56
  has_many :tasks, :as => :asset, :dependent => :destroy#, :order => 'created_at DESC'
57
57
  has_one :business_address, :dependent => :destroy, :as => :addressable, :class_name => "Address", :conditions => "address_type='Business'"
58
+ has_many :addresses, :dependent => :destroy, :as => :addressable, :class_name => "Address" # advanced search uses this
58
59
  has_many :emails, :as => :mediator
59
60
 
60
61
  serialize :subscribed_users, Set
@@ -69,19 +70,20 @@ class Lead < ActiveRecord::Base
69
70
  scope :created_by, lambda { |user| where('user_id = ?' , user.id) }
70
71
  scope :assigned_to, lambda { |user| where('assigned_to = ?' , user.id) }
71
72
 
72
- scope :text_search, lambda { |query|
73
- query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
74
- where('upper(first_name) LIKE upper(:s) OR upper(last_name) LIKE upper(:s) OR upper(company) LIKE upper(:m) OR upper(email) LIKE upper(:m)', :s => "#{query}%", :m => "%#{query}%")
75
- }
73
+ scope :text_search, lambda { |query| search('first_name_or_last_name_or_company_or_email_cont' => query).result }
76
74
 
77
75
  uses_user_permissions
78
76
  acts_as_commentable
77
+ uses_comment_extensions
79
78
  acts_as_taggable_on :tags
80
79
  has_paper_trail :ignore => [ :subscribed_users ]
81
80
  has_fields
82
81
  exportable
83
82
  sortable :by => [ "first_name ASC", "last_name ASC", "company ASC", "rating DESC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
84
83
 
84
+ has_ransackable_associations %w(contact campaign tasks tags activities emails addresses comments)
85
+ ransack_can_autocomplete
86
+
85
87
  validates_presence_of :first_name, :message => :missing_first_name
86
88
  validates_presence_of :last_name, :message => :missing_last_name if Setting.require_last_names
87
89
  validate :users_for_shared_access
@@ -91,29 +93,38 @@ class Lead < ActiveRecord::Base
91
93
 
92
94
  # Default values provided through class methods.
93
95
  #----------------------------------------------------------------------------
94
- def self.per_page ; 20 ; end
95
- def self.outline ; "long" ; end
96
+ def self.per_page ; 20 ; end
96
97
  def self.first_name_position ; "before" ; end
97
98
 
98
99
  # Save the lead along with its permissions.
99
100
  #----------------------------------------------------------------------------
100
101
  def save_with_permissions(params)
101
102
  self.campaign = Campaign.find(params[:campaign]) unless params[:campaign].blank?
102
- if self.access == "Campaign" && self.campaign # Copy campaign permissions.
103
+ if params[:lead][:access] == "Campaign" && self.campaign # Copy campaign permissions.
103
104
  save_with_model_permissions(Campaign.find(self.campaign_id))
104
105
  else
105
- super(params[:users]) # invoke :save_with_permissions in plugin.
106
+ self.attributes = params[:leads]
107
+ save
106
108
  end
107
109
  end
108
110
 
111
+ # Deprecated: see update_with_lead_counters
112
+ #----------------------------------------------------------------------------
113
+ def update_with_permissions(attributes, users = nil)
114
+ ActiveSupport::Deprecation.warn "lead.update_with_permissions is deprecated and may be removed from future releases, use user_ids and group_ids inside attributes instead and call lead.update_with_lead_counters"
115
+ update_with_lead_counters(attributes)
116
+ end
117
+
109
118
  # Update lead attributes taking care of campaign lead counters when necessary.
110
119
  #----------------------------------------------------------------------------
111
- def update_with_permissions(attributes, users)
120
+ def update_with_lead_counters(attributes)
112
121
  if self.campaign_id == attributes[:campaign_id] # Same campaign (if any).
113
- super(attributes, users) # See lib/fat_free_crm/permissions.rb
122
+ self.attributes = attributes
123
+ self.save
114
124
  else # Campaign has been changed -- update lead counters...
115
125
  decrement_leads_count # ..for the old campaign...
116
- lead = super(attributes, users) # Assign new campaign.
126
+ self.attributes = attributes # Assign new campaign.
127
+ lead = self.save
117
128
  increment_leads_count # ...and now for the new campaign.
118
129
  lead
119
130
  end
@@ -123,8 +134,8 @@ class Lead < ActiveRecord::Base
123
134
  # successful promotion Lead status gets set to :converted.
124
135
  #----------------------------------------------------------------------------
125
136
  def promote(params)
126
- account = Account.create_or_select_for(self, params[:account], params[:users])
127
- opportunity = Opportunity.create_for(self, account, params[:opportunity], params[:users])
137
+ account = Account.create_or_select_for(self, params[:account])
138
+ opportunity = Opportunity.create_for(self, account, params[:opportunity])
128
139
  contact = Contact.create_for(self, account, opportunity, params)
129
140
 
130
141
  [account, opportunity, contact]
@@ -53,31 +53,47 @@ class Opportunity < ActiveRecord::Base
53
53
  scope :state, lambda { |filters|
54
54
  where('stage IN (?)' + (filters.delete('other') ? ' OR stage IS NULL' : ''), filters)
55
55
  }
56
- scope :created_by, lambda { |user| where('user_id = ?', user.id) }
56
+ scope :created_by, lambda { |user| where('user_id = ?', user.id) }
57
57
  scope :assigned_to, lambda { |user| where('assigned_to = ?', user.id) }
58
- scope :won, where("opportunities.stage = 'won'")
59
- scope :lost, where("opportunities.stage = 'lost'")
60
- scope :pipeline, where("opportunities.stage IS NULL OR (opportunities.stage != 'won' AND opportunities.stage != 'lost')")
58
+ scope :won, where("opportunities.stage = 'won'")
59
+ scope :lost, where("opportunities.stage = 'lost'")
60
+ scope :not_lost, where("opportunities.stage <> 'lost'")
61
+ scope :pipeline, where("opportunities.stage IS NULL OR (opportunities.stage != 'won' AND opportunities.stage != 'lost')")
62
+ scope :unassigned, where("opportunities.assigned_to IS NULL")
61
63
 
62
64
  # Search by name OR id
63
65
  scope :text_search, lambda { |query|
64
- query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
65
66
  # postgresql does not like to compare string to integer field
66
67
  if query =~ /^\d+$/
68
+ query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
67
69
  where('upper(name) LIKE upper(:name) OR opportunities.id = :id', :name => "%#{query}%", :id => query)
68
70
  else
69
- where('upper(name) LIKE upper(:name)', :name => "%#{query}%")
71
+ search('name_cont' => query).result
70
72
  end
71
73
  }
72
74
 
75
+ scope :visible_on_dashboard, lambda { |user|
76
+ # Show opportunities which either belong to the user and are unassigned, or are assigned to the user and haven't been closed (won/lost)
77
+ where('(user_id = :user_id AND assigned_to IS NULL) OR assigned_to = :user_id', :user_id => user.id).where("opportunities.stage != 'won'").where("opportunities.stage != 'lost'")
78
+ }
79
+
80
+ scope :by_closes_on, order(:closes_on)
81
+ scope :by_amount, order('opportunities.amount DESC')
82
+
73
83
  uses_user_permissions
74
84
  acts_as_commentable
85
+ uses_comment_extensions
75
86
  acts_as_taggable_on :tags
76
87
  has_paper_trail :ignore => [ :subscribed_users ]
77
88
  has_fields
78
89
  exportable
79
90
  sortable :by => [ "name ASC", "amount DESC", "amount*probability DESC", "probability DESC", "closes_on ASC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
80
91
 
92
+ has_ransackable_associations %w(account contacts tags activities emails comments)
93
+ ransack_can_autocomplete
94
+
95
+ validates :stage, :inclusion => { :in => Setting.unroll(:opportunity_stage).map{|s| s.last.to_s } }
96
+
81
97
  validates_presence_of :name, :message => :missing_opportunity_name
82
98
  validates_numericality_of [ :probability, :amount, :discount ], :allow_nil => true
83
99
  validate :users_for_shared_access
@@ -92,8 +108,7 @@ class Opportunity < ActiveRecord::Base
92
108
 
93
109
  # Default values provided through class methods.
94
110
  #----------------------------------------------------------------------------
95
- def self.per_page ; 20 ; end
96
- def self.outline ; "long" ; end
111
+ def self.per_page ; 20 ; end
97
112
 
98
113
  #----------------------------------------------------------------------------
99
114
  def weighted_amount
@@ -105,12 +120,13 @@ class Opportunity < ActiveRecord::Base
105
120
  def save_with_account_and_permissions(params)
106
121
  # Quick sanitization, makes sure Account will not search for blank id.
107
122
  params[:account].delete(:id) if params[:account][:id].blank?
108
- account = Account.create_or_select_for(self, params[:account], params[:users])
123
+ account = Account.create_or_select_for(self, params[:account])
109
124
  self.account_opportunity = AccountOpportunity.new(:account => account, :opportunity => self) unless account.id.blank?
110
125
  self.account = account
111
- self.contacts << Contact.find(params[:contact]) unless params[:contact].blank?
112
126
  self.campaign = Campaign.find(params[:campaign]) unless params[:campaign].blank?
113
- self.save_with_permissions(params[:users])
127
+ result = self.save
128
+ self.contacts << Contact.find(params[:contact]) unless params[:contact].blank?
129
+ result
114
130
  end
115
131
 
116
132
  # Backend handler for [Update Opportunity] form (see opportunity/update).
@@ -119,13 +135,16 @@ class Opportunity < ActiveRecord::Base
119
135
  if params[:account] && (params[:account][:id] == "" || params[:account][:name] == "")
120
136
  self.account = nil # Opportunity is not associated with the account anymore.
121
137
  elsif params[:account]
122
- account = Account.create_or_select_for(self, params[:account], params[:users])
138
+ account = Account.create_or_select_for(self, params[:account])
123
139
  if self.account != account and account.id.present?
124
140
  self.account_opportunity = AccountOpportunity.new(:account => account, :opportunity => self)
125
141
  end
126
142
  end
127
143
  self.reload
128
- self.update_with_permissions(params[:opportunity], params[:users])
144
+ # Must set access before user_ids, because user_ids= method depends on access value.
145
+ self.access = params[:opportunity][:access] if params[:opportunity][:access]
146
+ self.attributes = params[:opportunity]
147
+ self.save
129
148
  end
130
149
 
131
150
  # Attach given attachment to the opportunity if it hasn't been attached already.
@@ -148,7 +167,7 @@ class Opportunity < ActiveRecord::Base
148
167
 
149
168
  # Class methods.
150
169
  #----------------------------------------------------------------------------
151
- def self.create_for(model, account, params, users)
170
+ def self.create_for(model, account, params)
152
171
  opportunity = Opportunity.new(params)
153
172
 
154
173
  # Save the opportunity if its name was specified and account has no errors.
@@ -156,7 +175,7 @@ class Opportunity < ActiveRecord::Base
156
175
  # Note: opportunity.account = account doesn't seem to work here.
157
176
  opportunity.account_opportunity = AccountOpportunity.new(:account => account, :opportunity => opportunity) unless account.id.blank?
158
177
  if opportunity.access != "Lead" || model.nil?
159
- opportunity.save_with_permissions(users)
178
+ opportunity.save
160
179
  else
161
180
  opportunity.save_with_model_permissions(model)
162
181
  end
@@ -186,4 +205,3 @@ class Opportunity < ActiveRecord::Base
186
205
  end
187
206
 
188
207
  end
189
-
@@ -63,8 +63,8 @@ class CustomField < Field
63
63
  before_create :add_column
64
64
 
65
65
  SAFE_DB_TRANSITIONS = {
66
- :any => [[:date, :time, :timestamp], [:integer, :float]],
67
- :one => {:string => :text}
66
+ :any => [['date', 'time', 'timestamp'], ['integer', 'float']],
67
+ :one => {'string' => 'text'}
68
68
  }
69
69
 
70
70
  def available_as
@@ -73,7 +73,16 @@ class CustomField < Field
73
73
  end
74
74
  end
75
75
 
76
- protected
76
+ # Extra validation that is called on this field when validation happens
77
+ # obj is reference to parent object
78
+ #------------------------------------------------------------------------------
79
+ def custom_validator(obj)
80
+ attr = name.to_sym
81
+ obj.errors.add(attr, ::I18n.t('activerecord.errors.models.custom_field.required', :field => label)) if required? and obj.send(attr).blank?
82
+ obj.errors.add(attr, ::I18n.t('activerecord.errors.models.custom_field.maxlength', :field => label)) if (maxlength.to_i > 0) and (obj.send(attr).to_s.length > maxlength.to_i)
83
+ end
84
+
85
+ protected
77
86
 
78
87
  # When changing a custom field's type, it may be necessary to
79
88
  # change the column type in the database. This method returns
@@ -82,14 +91,15 @@ protected
82
91
  # :null => no transition needed
83
92
  # :safe => transition is safe
84
93
  # :unsafe => transition is unsafe
94
+ #------------------------------------------------------------------------------
85
95
  def db_transition_safety(old_type, new_type = self.as)
86
- old_col, new_col = [old_type, new_type].map{|t| column_type(t) }
96
+ old_col, new_col = [old_type, new_type].map{|t| column_type(t).to_s }
87
97
  return :null if old_col == new_col # no transition needed
88
98
  return :safe if SAFE_DB_TRANSITIONS[:one].any? do |start, final|
89
- old_col == start && new_col == final # one-to-one
99
+ old_col == start.to_s && new_col == final.to_s # one-to-one
90
100
  end
91
101
  return :safe if SAFE_DB_TRANSITIONS[:any].any? do |col_set|
92
- [old_col, new_col].all?{|c| col_set.include?(c)} # any-to-any
102
+ [old_col, new_col].all?{|c| col_set.include?(c.to_s)} # any-to-any
93
103
  end
94
104
  :unsafe # Else, unsafe.
95
105
  end
@@ -102,10 +112,11 @@ protected
102
112
  klass.columns.map(&:name)
103
113
  end
104
114
 
115
+ # Generate column name for custom field.
116
+ # If column name is already taken, a numeric suffix is appended.
117
+ # Example column sequence: cf_custom, cf_custom_2, cf_custom_3, ...
118
+ #------------------------------------------------------------------------------
105
119
  def generate_column_name
106
- # Generate column name for custom field.
107
- # If column name is already taken, a numeric suffix is appended.
108
- # Example column sequence: cf_custom, cf_custom_2, cf_custom_3, ...
109
120
  suffix = nil
110
121
  field_name = 'cf_' + label.downcase.gsub(/[^a-z0-9]+/, '_')
111
122
  while (final_name = [field_name, suffix].compact.join('_')) &&
@@ -116,23 +127,28 @@ protected
116
127
  end
117
128
 
118
129
  # Returns options for ActiveRecord operations
130
+ #------------------------------------------------------------------------------
119
131
  def column_options
120
- Field.field_types[self.as][:options] || {}
132
+ Field.field_types[self.as][:column_options] || {}
121
133
  end
122
134
 
135
+ # Create a new column to hold the custom field data
136
+ #------------------------------------------------------------------------------
123
137
  def add_column
124
138
  self.name = generate_column_name if name.blank?
125
139
  connection.add_column(table_name, name, column_type, column_options)
126
140
  klass.reset_column_information
141
+ klass.serialize_custom_fields!
127
142
  end
128
143
 
144
+ # Change database column type only if safe to do so
145
+ # Note: columns will never be renamed or destroyed
146
+ #------------------------------------------------------------------------------
129
147
  def update_column
130
- # Change database column type if appropriate
131
- # (NOTE: Columns will never be renamed or destroyed)
132
148
  if self.errors.empty? && db_transition_safety(as_was) == :safe
133
149
  connection.change_column(table_name, name, column_type, column_options)
134
150
  klass.reset_column_information
151
+ klass.serialize_custom_fields!
135
152
  end
136
153
  end
137
154
  end
138
-