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.
- data/.gitignore +1 -0
- data/.travis.yml +2 -2
- data/CHANGELOG +6 -1
- data/CONTRIBUTORS +40 -1
- data/Capfile +5 -0
- data/Gemfile +25 -6
- data/Gemfile.lock +128 -86
- data/README.md +23 -16
- data/{vendor → app}/assets/images/delete.png +0 -0
- data/app/assets/javascripts/admin/fields.js.coffee +62 -0
- data/app/assets/javascripts/application.js.erb +8 -1
- data/app/assets/javascripts/crm.js +44 -46
- data/app/assets/javascripts/crm_chosen.js.coffee +10 -7
- data/app/assets/javascripts/datepicker.js.coffee +15 -0
- data/app/assets/javascripts/format_buttons.js.coffee +48 -0
- data/app/assets/javascripts/{admin/field_groups.js.coffee → groups.js.coffee} +0 -0
- data/app/assets/javascripts/lists.js.coffee +1 -1
- data/app/assets/javascripts/search.js.coffee +56 -38
- data/app/assets/stylesheets/admin/{field_groups.css.scss → fields.css.scss} +4 -0
- data/app/assets/stylesheets/advanced_search.css.scss +79 -0
- data/app/assets/stylesheets/application.css.erb +4 -0
- data/app/assets/stylesheets/base.scss +3 -3
- data/app/assets/stylesheets/common.scss +72 -25
- data/app/assets/stylesheets/fields.scss +14 -0
- data/app/assets/stylesheets/format_buttons.css.scss +30 -0
- data/app/assets/stylesheets/groups.css.scss +3 -0
- data/app/assets/stylesheets/header.scss +7 -6
- data/{vendor/plugins/.gitkeep → app/assets/stylesheets/index_headers.css.scss} +0 -0
- data/app/assets/stylesheets/print.css.scss +12 -3
- data/app/assets/stylesheets/rails.scss +12 -3
- data/app/controllers/admin/application_controller.rb +1 -1
- data/app/controllers/admin/field_groups_controller.rb +2 -0
- data/app/controllers/admin/fields_controller.rb +41 -17
- data/app/controllers/admin/groups_controller.rb +70 -0
- data/app/controllers/admin/tags_controller.rb +1 -0
- data/app/controllers/admin/users_controller.rb +8 -7
- data/app/controllers/application_controller.rb +34 -5
- data/app/controllers/comments_controller.rb +2 -3
- data/app/controllers/entities/accounts_controller.rb +30 -37
- data/app/controllers/entities/campaigns_controller.rb +56 -29
- data/app/controllers/entities/contacts_controller.rb +24 -26
- data/app/controllers/entities/leads_controller.rb +38 -37
- data/app/controllers/entities/opportunities_controller.rb +28 -34
- data/app/controllers/entities_controller.rb +71 -43
- data/app/controllers/home_controller.rb +29 -27
- data/app/controllers/tasks_controller.rb +18 -17
- data/app/controllers/users_controller.rb +12 -8
- data/app/helpers/accounts_helper.rb +58 -16
- data/app/helpers/addresses_helper.rb +4 -1
- data/app/helpers/admin/fields_helper.rb +3 -4
- data/app/helpers/application_helper.rb +147 -100
- data/app/helpers/campaigns_helper.rb +2 -9
- data/app/helpers/fields_helper.rb +0 -1
- data/app/helpers/groups_helper.rb +2 -0
- data/app/helpers/home_helper.rb +11 -0
- data/app/helpers/leads_helper.rb +2 -9
- data/app/helpers/opportunities_helper.rb +2 -9
- data/app/helpers/{crm_tags_helper.rb → tags_helper.rb} +9 -8
- data/app/helpers/tasks_helper.rb +5 -21
- data/app/helpers/users_helper.rb +14 -7
- data/app/helpers/versions_helper.rb +46 -0
- data/app/inputs/date_time_input.rb +8 -25
- data/app/inputs/datepair_input.rb +77 -0
- data/app/inputs/datetimepair_input.rb +37 -0
- data/app/mailers/user_mailer.rb +10 -0
- data/app/models/entities/account.rb +18 -11
- data/app/models/entities/account_contact.rb +4 -3
- data/app/models/entities/account_opportunity.rb +0 -1
- data/app/models/entities/campaign.rb +6 -7
- data/app/models/entities/contact.rb +53 -25
- data/app/models/entities/contact_opportunity.rb +0 -1
- data/app/models/entities/lead.rb +24 -13
- data/app/models/entities/opportunity.rb +34 -16
- data/app/models/fields/custom_field.rb +29 -13
- data/app/models/fields/custom_field_date_pair.rb +58 -0
- data/app/models/fields/custom_field_datetime_pair.rb +28 -0
- data/app/models/fields/custom_field_pair.rb +54 -0
- data/app/models/fields/field.rb +60 -43
- data/app/models/fields/field_group.rb +0 -9
- data/app/models/observers/entity_observer.rb +29 -0
- data/app/models/observers/opportunity_observer.rb +3 -0
- data/app/models/polymorphic/address.rb +14 -0
- data/app/models/polymorphic/avatar.rb +13 -1
- data/app/models/polymorphic/email.rb +1 -1
- data/app/models/polymorphic/task.rb +14 -13
- data/app/models/setting.rb +14 -21
- data/app/models/users/ability.rb +25 -4
- data/app/models/users/group.rb +13 -0
- data/app/models/users/permission.rb +3 -1
- data/app/models/users/preference.rb +1 -2
- data/app/models/users/user.rb +25 -3
- data/app/views/accounts/_account.html.haml +5 -39
- data/app/views/accounts/_edit.html.haml +3 -2
- data/app/views/accounts/_index_brief.html.haml +32 -0
- data/app/views/accounts/_index_long.html.haml +42 -0
- data/app/views/accounts/_new.html.haml +3 -2
- data/app/views/accounts/_sidebar_index.html.haml +3 -4
- data/app/views/accounts/_sidebar_show.html.haml +2 -2
- data/app/views/accounts/_title_bar.html.haml +11 -0
- data/app/views/accounts/_top_section.html.haml +1 -1
- data/app/views/accounts/create.js.rjs +1 -2
- data/app/views/accounts/destroy.js.rjs +1 -1
- data/app/views/accounts/edit.js.rjs +0 -2
- data/app/views/accounts/index.html.haml +6 -13
- data/app/views/accounts/index.js.rjs +3 -1
- data/app/views/accounts/index.xls.builder +82 -0
- data/app/views/accounts/new.js.rjs +0 -2
- data/app/views/accounts/show.html.haml +17 -15
- data/app/views/accounts/show.js.erb +2 -0
- data/app/views/admin/custom_fields/_base_field.html.haml +21 -0
- data/app/views/admin/custom_fields/_check_boxes_field.html.haml +6 -0
- data/app/views/admin/custom_fields/_date_field.html.haml +14 -0
- data/app/views/admin/custom_fields/_date_pair_field.html.haml +21 -0
- data/app/views/admin/custom_fields/_datetime_field.html.haml +14 -0
- data/app/views/admin/custom_fields/_datetime_pair_field.html.haml +21 -0
- data/app/views/admin/custom_fields/_radio_field.html.haml +6 -0
- data/app/views/admin/custom_fields/_select_field.html.haml +25 -0
- data/app/views/admin/custom_fields/_string_field.html.haml +2 -0
- data/app/views/admin/field_groups/_field_group.html.haml +7 -5
- data/app/views/admin/field_groups/_top_section.html.haml +4 -3
- data/app/views/admin/field_groups/create.js.rjs +1 -2
- data/app/views/admin/fields/_field.html.haml +5 -3
- data/app/views/admin/fields/_form.html.haml +35 -0
- data/app/views/admin/fields/_subform.html.haml +6 -0
- data/app/views/admin/fields/create.js.erb +19 -0
- data/app/views/admin/fields/destroy.js.erb +9 -0
- data/app/views/admin/fields/edit.js.erb +1 -0
- data/app/views/admin/fields/index.html.haml +11 -8
- data/app/views/admin/fields/update.js.erb +13 -0
- data/app/views/admin/groups/_edit.html.haml +12 -0
- data/app/views/admin/groups/_form.html.haml +13 -0
- data/app/views/admin/groups/_group.html.haml +15 -0
- data/app/views/admin/groups/_new.html.haml +12 -0
- data/app/views/admin/groups/create.js.rjs +11 -0
- data/app/views/admin/{fields → groups}/destroy.js.rjs +3 -3
- data/app/views/admin/groups/edit.js.rjs +21 -0
- data/app/views/admin/groups/index.html.haml +18 -0
- data/app/views/admin/groups/index.js.rjs +2 -0
- data/app/views/admin/groups/new.js.rjs +9 -0
- data/app/views/admin/groups/show.html.haml +11 -0
- data/app/views/admin/groups/update.js.rjs +10 -0
- data/app/views/admin/users/_profile.html.haml +13 -1
- data/app/views/admin/users/_user.html.haml +7 -2
- data/app/views/admin/users/index.js.rjs +1 -0
- data/app/views/application/index.atom.builder +1 -1
- data/app/views/application/index.rss.builder +1 -1
- data/app/views/application/show.atom.builder +32 -0
- data/app/views/application/show.rss.builder +29 -0
- data/app/views/campaigns/_campaign.html.haml +5 -21
- data/app/views/campaigns/_edit.html.haml +3 -2
- data/app/views/campaigns/_index_brief.html.haml +15 -0
- data/app/views/campaigns/_index_long.html.haml +20 -0
- data/app/views/campaigns/_new.html.haml +4 -2
- data/app/views/campaigns/_sidebar_index.html.haml +3 -3
- data/app/views/campaigns/_title_bar.html.haml +11 -0
- data/app/views/campaigns/_top_section.html.haml +2 -2
- data/app/views/campaigns/create.js.rjs +1 -4
- data/app/views/campaigns/destroy.js.rjs +1 -1
- data/app/views/campaigns/edit.js.rjs +0 -4
- data/app/views/campaigns/index.html.haml +6 -13
- data/app/views/campaigns/index.js.rjs +3 -1
- data/app/views/campaigns/index.xls.builder +75 -0
- data/app/views/campaigns/new.js.rjs +0 -4
- data/app/views/campaigns/show.html.haml +17 -15
- data/app/views/campaigns/show.js.erb +2 -0
- data/app/views/campaigns/update.js.rjs +0 -2
- data/app/views/comments/_comment.html.haml +6 -5
- data/app/views/comments/_edit.html.haml +5 -5
- data/app/views/comments/_new.html.haml +6 -5
- data/app/views/comments/create.js.rjs +1 -1
- data/app/views/contacts/_contact.html.haml +5 -52
- data/app/views/contacts/_contacts.html.haml +1 -0
- data/app/views/contacts/_edit.html.haml +3 -2
- data/app/views/contacts/_index_brief.html.haml +25 -0
- data/app/views/contacts/_index_full.html.haml +57 -0
- data/app/views/contacts/_index_long.html.haml +43 -0
- data/app/views/contacts/_new.html.haml +4 -2
- data/app/views/contacts/_section_extra.html.haml +26 -0
- data/app/views/contacts/_section_general.html.haml +22 -0
- data/app/views/contacts/_sidebar_index.html.haml +0 -3
- data/app/views/contacts/_sidebar_show.html.haml +2 -10
- data/app/views/contacts/_title_bar.html.haml +11 -0
- data/app/views/contacts/_top_section.html.haml +1 -1
- data/app/views/contacts/create.js.rjs +1 -2
- data/app/views/contacts/destroy.js.rjs +1 -1
- data/app/views/contacts/edit.js.rjs +0 -2
- data/app/views/contacts/index.html.haml +6 -14
- data/app/views/contacts/index.js.rjs +3 -1
- data/app/views/contacts/index.xls.builder +102 -0
- data/app/views/contacts/new.js.rjs +0 -2
- data/app/views/contacts/show.html.haml +15 -15
- data/app/views/contacts/show.js.erb +2 -0
- data/app/views/entities/_basic_search.html.haml +22 -0
- data/app/views/entities/_permissions.html.haml +41 -0
- data/app/views/entities/_search.html.haml +15 -0
- data/app/views/entities/_section_custom_fields.html.haml +11 -0
- data/app/views/entities/_title_bar.html.haml +12 -0
- data/app/views/entities/attach.js.rjs +1 -1
- data/app/views/entities/opportunities.js.rjs +1 -1
- data/app/views/entities/versions.js.erb +3 -0
- data/app/views/fields/_edit_custom_field_group.html.haml +8 -0
- data/app/views/fields/_group.html.haml +11 -20
- data/app/views/fields/_group_table.html.haml +13 -0
- data/app/views/fields/_group_view.html.haml +7 -0
- data/app/views/fields/_groups.html.haml +1 -4
- data/app/views/fields/_sidebar_show.html.haml +5 -4
- data/app/views/home/_account.html.haml +29 -0
- data/app/views/home/_activity.html.haml +16 -6
- data/app/views/home/_opportunity.html.haml +41 -0
- data/app/views/home/_options.html.haml +1 -1
- data/app/views/home/_task.html.haml +46 -0
- data/app/views/home/index.html.haml +32 -1
- data/app/views/home/index.js.rjs +1 -0
- data/app/views/layouts/_header.html.haml +3 -3
- data/app/views/layouts/_sidebar.html.haml +1 -3
- data/app/views/layouts/_tabbed.html.haml +2 -3
- data/app/views/layouts/_tabless.html.haml +1 -1
- data/app/views/layouts/admin/_header.html.haml +1 -1
- data/app/views/layouts/application.html.haml +7 -8
- data/app/views/layouts/header.xls.builder +8 -0
- data/app/views/leads/_contact.html.haml +1 -2
- data/app/views/leads/_convert.html.haml +1 -2
- data/app/views/leads/_convert_permissions.html.haml +21 -11
- data/app/views/leads/_edit.html.haml +3 -2
- data/app/views/leads/_index_brief.html.haml +41 -0
- data/app/views/leads/_index_long.html.haml +62 -0
- data/app/views/leads/_lead.html.haml +5 -62
- data/app/views/leads/_new.html.haml +4 -2
- data/app/views/leads/_opportunity.html.haml +1 -1
- data/app/views/leads/_sidebar_index.html.haml +3 -3
- data/app/views/leads/_sidebar_show.html.haml +3 -3
- data/app/views/leads/_status.html.haml +1 -1
- data/app/views/leads/_title_bar.html.haml +16 -0
- data/app/views/leads/convert.js.rjs +0 -1
- data/app/views/leads/create.js.rjs +1 -2
- data/app/views/leads/destroy.js.rjs +1 -1
- data/app/views/leads/edit.js.rjs +0 -2
- data/app/views/leads/index.html.haml +6 -13
- data/app/views/leads/index.js.rjs +3 -1
- data/app/views/leads/index.xls.builder +100 -0
- data/app/views/leads/new.js.rjs +0 -2
- data/app/views/leads/promote.js.rjs +0 -1
- data/app/views/leads/show.html.haml +13 -18
- data/app/views/leads/show.js.erb +2 -0
- data/app/views/{shared/_lists.html.haml → lists/_sidebar.html.haml} +0 -0
- data/app/views/lists/create.js.rjs +1 -1
- data/app/views/opportunities/_edit.html.haml +3 -2
- data/app/views/opportunities/_index_brief.html.haml +29 -0
- data/app/views/opportunities/_index_long.html.haml +59 -0
- data/app/views/opportunities/_new.html.haml +4 -2
- data/app/views/opportunities/_opportunity.html.haml +5 -57
- data/app/views/opportunities/_sidebar_index.html.haml +3 -3
- data/app/views/opportunities/_sidebar_show.html.haml +2 -2
- data/app/views/opportunities/_title_bar.html.haml +11 -0
- data/app/views/opportunities/_top_section.html.haml +2 -2
- data/app/views/opportunities/create.js.rjs +4 -6
- data/app/views/opportunities/destroy.js.rjs +1 -1
- data/app/views/opportunities/edit.js.rjs +0 -3
- data/app/views/opportunities/index.html.haml +6 -14
- data/app/views/opportunities/index.js.rjs +3 -1
- data/app/views/opportunities/index.xls.builder +69 -0
- data/app/views/opportunities/new.js.rjs +1 -5
- data/app/views/opportunities/show.html.haml +14 -14
- data/app/views/opportunities/show.js.erb +2 -0
- data/app/views/opportunities/update.js.rjs +0 -1
- data/app/views/passwords/new.html.haml +1 -1
- data/app/views/shared/_add_comment.html.haml +8 -0
- data/app/views/shared/_address.html.haml +1 -1
- data/app/views/shared/_address_show.html.haml +1 -1
- data/app/views/shared/_edit_comment.html.haml +2 -2
- data/app/views/shared/_export.html.haml +2 -1
- data/app/views/shared/_paginate_with_per_page.html.haml +14 -0
- data/app/views/shared/_recently.html.haml +1 -1
- data/app/views/shared/_search.html.haml +1 -1
- data/app/views/shared/_tags.html.haml +1 -2
- data/app/views/shared/_tasks.html.haml +2 -2
- data/app/views/tasks/_completed.html.haml +3 -3
- data/app/views/tasks/_new.html.haml +1 -1
- data/app/views/tasks/_pending.html.haml +2 -3
- data/app/views/tasks/_sidebar_index.html.haml +2 -2
- data/app/views/tasks/_top_section.html.haml +5 -3
- data/app/views/tasks/create.js.rjs +3 -4
- data/app/views/tasks/edit.js.rjs +2 -4
- data/app/views/tasks/index.html.haml +1 -1
- data/app/views/tasks/index.xls.builder +47 -0
- data/app/views/tasks/new.js.rjs +0 -2
- data/app/views/tasks/update.js.rjs +0 -1
- data/app/views/user_mailer/assigned_entity_notification.html.haml +3 -0
- data/app/views/users/_user.html.haml +2 -2
- data/app/views/users/_user_report.html.haml +6 -0
- data/app/views/users/opportunities_overview.html.haml +16 -0
- data/app/views/users/show.html.haml +2 -3
- data/app/views/users/update.js.rjs +1 -1
- data/app/views/versions/_version.html.haml +4 -19
- data/app/views/versions/_version_item.html.haml +8 -0
- data/app/views/versions/_versions.html.haml +9 -9
- data/config/application.rb +10 -2
- data/config/deploy.example.rb +47 -0
- data/config/environments/development.rb +1 -0
- data/config/environments/production.rb +1 -3
- data/config/environments/staging.rb +1 -1
- data/config/initializers/action_mailer.rb +10 -8
- data/config/initializers/custom_fields.rb +21 -0
- data/config/initializers/gravatar.rb +7 -0
- data/config/initializers/paper_trail.rb +1 -0
- data/config/initializers/ransack.rb +15 -0
- data/config/initializers/simple_form.rb +1 -2
- data/config/initializers/views.rb +42 -0
- data/config/locales/cz_fat_free_crm.yml +0 -10
- data/config/locales/de_fat_free_crm.yml +23 -21
- data/config/locales/en-GB_fat_free_crm.yml +241 -25
- data/config/locales/en-US_fat_free_crm.yml +139 -57
- data/config/locales/en-US_ransack.yml +92 -0
- data/config/locales/es_fat_free_crm.yml +0 -6
- data/config/locales/fr-CA_fat_free_crm.yml +0 -6
- data/config/locales/fr_fat_free_crm.yml +3 -14
- data/config/locales/it_fat_free_crm.yml +0 -11
- data/config/locales/ja_fat_free_crm.yml +0 -10
- data/config/locales/pl_fat_free_crm.yml +0 -6
- data/config/locales/pt-BR_fat_free_crm.yml +0 -6
- data/config/locales/ru_fat_free_crm.yml +0 -9
- data/config/locales/sv-SE_fat_free_crm.yml +0 -10
- data/config/locales/th_fat_free_crm.yml +0 -6
- data/config/locales/zh-CN.yml +4 -0
- data/config/locales/zh-CN_fat_free_crm.yml +78 -47
- data/config/routes.rb +13 -0
- data/config/settings.default.yml +20 -20
- data/db/demo/addresses.yml +2 -2
- data/db/migrate/20100928030598_create_sessions.rb +5 -0
- data/db/migrate/20100928030627_acts_as_taggable_on_migration.rb +6 -6
- data/db/migrate/20111201030535_add_field_groups_klass_name.rb +2 -1
- data/db/migrate/20120406082136_create_groups.rb +19 -0
- data/db/migrate/20120510025219_add_not_null_constraints_for_timestamp_columns.rb +21 -0
- data/db/migrate/20120528102124_increase_length_of_version_events.rb +9 -0
- data/db/migrate/20120801032706_add_pair_id_to_fields.rb +5 -0
- data/db/migrate/20121003063155_add_settings_to_custom_fields.rb +5 -0
- data/db/migrate/20121221033947_fix_country_mapping.rb +32 -0
- data/db/schema.rb +25 -6
- data/fat_free_crm.gemspec +16 -13
- data/lib/fat_free_crm.rb +2 -0
- data/lib/fat_free_crm/comment_extensions.rb +23 -0
- data/lib/fat_free_crm/core_ext/array.rb +8 -2
- data/lib/fat_free_crm/fields.rb +24 -2
- data/lib/fat_free_crm/gem_dependencies.rb +7 -1
- data/lib/fat_free_crm/permissions.rb +47 -26
- data/lib/fat_free_crm/version.rb +1 -1
- data/lib/fat_free_crm/view_factory.rb +95 -0
- data/lib/plugins/country_select/lib/country_select.rb +393 -374
- data/lib/tasks/{plugins.rake → db/migrate.rake} +1 -1
- data/lib/tasks/{schema_upgrade.rake → db/schema.rake} +2 -1
- data/lib/tasks/{mail_processing.rake → ffcrm/comment_replies.rake} +2 -21
- data/lib/tasks/ffcrm/config.rake +33 -0
- data/lib/tasks/{demo.rake → ffcrm/demo.rake} +3 -2
- data/lib/tasks/ffcrm/dropbox.rake +42 -0
- data/lib/tasks/ffcrm/settings.rake +46 -0
- data/lib/tasks/{fat_free_crm.rake → ffcrm/setup.rake} +1 -39
- data/lib/tasks/ffcrm/update_data.rake +256 -0
- data/{acceptance → spec/acceptance}/acceptance_helper.rb +5 -4
- data/{acceptance → spec/acceptance}/accounts_spec.rb +36 -10
- data/spec/acceptance/admin/groups_spec.rb +26 -0
- data/spec/acceptance/admin/users_spec.rb +34 -0
- data/spec/acceptance/campaigns_spec.rb +101 -0
- data/spec/acceptance/contacts_spec.rb +101 -0
- data/spec/acceptance/dashboard_spec.rb +62 -0
- data/spec/acceptance/leads_spec.rb +109 -0
- data/spec/acceptance/opportunities_overview_spec.rb +82 -0
- data/spec/acceptance/opportunities_spec.rb +103 -0
- data/{acceptance → spec/acceptance}/support/browser.rb +0 -0
- data/{acceptance → spec/acceptance}/support/headless.rb +0 -0
- data/{acceptance → spec/acceptance}/support/helpers.rb +8 -1
- data/{acceptance → spec/acceptance}/support/maintain_sessions.rb +0 -0
- data/spec/acceptance/support/paths.rb +42 -0
- data/spec/acceptance/support/selector_helpers.rb +31 -0
- data/spec/acceptance/tasks_spec.rb +85 -0
- data/spec/controllers/admin/groups_controller_spec.rb +5 -0
- data/spec/controllers/admin/users_controller_spec.rb +2 -3
- data/spec/controllers/applications_controller_spec.rb +40 -0
- data/spec/controllers/comments_controller_spec.rb +5 -6
- data/spec/controllers/emails_controller_spec.rb +1 -2
- data/spec/controllers/entities/accounts_controller_spec.rb +65 -81
- data/spec/controllers/entities/campaigns_controller_spec.rb +69 -85
- data/spec/controllers/entities/contacts_controller_spec.rb +78 -95
- data/spec/controllers/entities/leads_controller_spec.rb +105 -127
- data/spec/controllers/entities/opportunities_controller_spec.rb +88 -103
- data/spec/controllers/entities_controller_spec.rb +39 -0
- data/spec/controllers/home_controller_spec.rb +101 -11
- data/spec/controllers/tasks_controller_spec.rb +27 -32
- data/spec/controllers/users_controller_spec.rb +95 -28
- data/spec/factories/account_factories.rb +2 -2
- data/spec/factories/opportunity_factories.rb +12 -4
- data/spec/factories/task_factories.rb +4 -0
- data/spec/factories/user_factories.rb +9 -2
- data/spec/helpers/application_helper_spec.rb +15 -1
- data/spec/helpers/groups_helper_spec.rb +15 -0
- data/spec/helpers/users_helper_spec.rb +26 -4
- data/spec/lib/comment_extensions_spec.rb +29 -0
- data/spec/lib/core_ext/{string.rb → string_spec.rb} +0 -1
- data/spec/lib/fields_spec.rb +104 -0
- data/spec/lib/permissions_spec.rb +144 -0
- data/spec/lib/view_factory_spec.rb +68 -0
- data/spec/mailers/subscription_mailer_spec.rb +18 -4
- data/spec/mailers/user_mailer_spec.rb +134 -0
- data/spec/models/entities/account_spec.rb +44 -4
- data/spec/models/entities/campaign_spec.rb +3 -4
- data/spec/models/entities/contact_spec.rb +45 -5
- data/spec/models/entities/lead_spec.rb +3 -4
- data/spec/models/entities/opportunity_spec.rb +105 -9
- data/spec/models/fields/custom_field_date_pair_spec.rb +106 -0
- data/spec/models/fields/custom_field_pair_spec.rb +94 -0
- data/spec/models/fields/custom_field_spec.rb +43 -23
- data/spec/models/fields/field_spec.rb +3 -5
- data/spec/models/observers/entity_observer_spec.rb +65 -0
- data/spec/models/polymorphic/task_spec.rb +74 -118
- data/spec/models/polymorphic/version_spec.rb +18 -18
- data/spec/models/users/group_spec.rb +5 -0
- data/spec/models/users/permission_spec.rb +18 -1
- data/spec/models/users/preference_spec.rb +5 -1
- data/spec/models/users/user_spec.rb +55 -8
- data/spec/shared/models.rb +54 -0
- data/spec/spec_helper.rb +82 -87
- data/spec/support/auth_macros.rb +2 -3
- data/spec/views/accounts/_edit.haml_spec.rb +1 -2
- data/spec/views/accounts/_new.haml_spec.rb +1 -2
- data/spec/views/accounts/create.rjs_spec.rb +1 -3
- data/spec/views/accounts/edit.rjs_spec.rb +3 -4
- data/spec/views/accounts/index.haml_spec.rb +9 -6
- data/spec/views/accounts/new.rjs_spec.rb +2 -9
- data/spec/views/accounts/show.haml_spec.rb +1 -2
- data/spec/views/accounts/update.rjs_spec.rb +2 -3
- data/spec/views/admin/users/_create.haml_spec.rb +1 -2
- data/spec/views/admin/users/create.rjs_spec.rb +1 -2
- data/spec/views/campaigns/_edit.haml_spec.rb +2 -3
- data/spec/views/campaigns/_new.haml_spec.rb +2 -3
- data/spec/views/campaigns/create.rjs_spec.rb +0 -3
- data/spec/views/campaigns/destroy.rjs_spec.rb +1 -2
- data/spec/views/campaigns/edit.rjs_spec.rb +3 -6
- data/spec/views/campaigns/index.haml_spec.rb +6 -3
- data/spec/views/campaigns/new.rjs_spec.rb +2 -13
- data/spec/views/campaigns/show.haml_spec.rb +4 -6
- data/spec/views/campaigns/update.rjs_spec.rb +2 -7
- data/spec/views/contacts/_edit.haml_spec.rb +6 -7
- data/spec/views/contacts/_new.haml_spec.rb +2 -3
- data/spec/views/contacts/create.rjs_spec.rb +1 -3
- data/spec/views/contacts/edit.rjs_spec.rb +3 -4
- data/spec/views/contacts/index.haml_spec.rb +6 -3
- data/spec/views/contacts/new.rjs_spec.rb +2 -11
- data/spec/views/contacts/show.haml_spec.rb +1 -2
- data/spec/views/contacts/update.rjs_spec.rb +2 -3
- data/spec/views/home/index.haml_spec.rb +6 -2
- data/spec/views/home/index.rjs_spec.rb +1 -2
- data/spec/views/home/options.rjs_spec.rb +1 -2
- data/spec/views/leads/_convert.haml_spec.rb +1 -2
- data/spec/views/leads/_edit.haml_spec.rb +2 -3
- data/spec/views/leads/_new.haml_spec.rb +2 -3
- data/spec/views/leads/_sidebar_show.haml_spec.rb +1 -2
- data/spec/views/leads/convert.rjs_spec.rb +3 -5
- data/spec/views/leads/edit.rjs_spec.rb +4 -5
- data/spec/views/leads/index.haml_spec.rb +6 -3
- data/spec/views/leads/new.rjs_spec.rb +2 -11
- data/spec/views/leads/promote.rjs_spec.rb +3 -5
- data/spec/views/leads/show.haml_spec.rb +1 -2
- data/spec/views/leads/update.rjs_spec.rb +2 -3
- data/spec/views/opportunities/_edit.haml_spec.rb +5 -6
- data/spec/views/opportunities/_new.haml_spec.rb +2 -4
- data/spec/views/opportunities/create.rjs_spec.rb +0 -2
- data/spec/views/opportunities/edit.rjs_spec.rb +3 -4
- data/spec/views/opportunities/index.haml_spec.rb +7 -4
- data/spec/views/opportunities/new.rjs_spec.rb +2 -12
- data/spec/views/opportunities/show.haml_spec.rb +1 -2
- data/spec/views/opportunities/update.rjs_spec.rb +2 -3
- data/spec/views/tasks/_edit.haml_spec.rb +1 -3
- data/spec/views/tasks/complete.rjs_spec.rb +2 -3
- data/spec/views/tasks/create.rjs_spec.rb +2 -2
- data/spec/views/tasks/edit.rjs_spec.rb +1 -2
- data/spec/views/tasks/new.rjs_spec.rb +1 -18
- data/spec/views/users/avatar.rjs_spec.rb +1 -2
- data/spec/views/users/change_password.rjs_spec.rb +1 -2
- data/spec/views/users/edit.rjs_spec.rb +1 -2
- data/spec/views/users/password.rjs_spec.rb +1 -1
- data/spec/views/users/update.rjs_spec.rb +1 -2
- data/spec/views/users/upload_avatar.rjs_spec.rb +6 -7
- data/vendor/assets/images/brief.png +0 -0
- data/vendor/assets/images/chosen-sprite.png +0 -0
- data/vendor/assets/images/full.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_0_eeeeee_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_100_ffffff_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_25_3875d7_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_55_ffffff_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_65_3875d7_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
- data/vendor/assets/images/jquery-ui/{ui-icons_ef8c08_256x240.png → ui-icons_0073ea_256x240.png} +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_466bb1_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/{ui-icons_ffd27a_256x240.png → ui-icons_ff0084_256x240.png} +0 -0
- data/vendor/assets/images/long.png +0 -0
- data/vendor/assets/javascripts/chosen-jquery.js +3 -0
- data/vendor/assets/javascripts/chosen-prototype.js +3 -0
- data/vendor/assets/javascripts/chosen.jquery.coffee +574 -0
- data/vendor/assets/javascripts/chosen.proto.coffee +580 -0
- data/vendor/assets/javascripts/jquery.disable.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-addon.js +1882 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-af.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ca.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-cz.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-de.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-el.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-es.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-et.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fi.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fr-CA.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-fr.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-gl.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-he.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-hu.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-id.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-it.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ja.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ko.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-lt.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-nl.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-no.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-pl.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-pt.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ro.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-ru.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-sk.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-sv.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-tr.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-vi.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-zh-CN.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery-ui-timepicker-zh-TW.js +20 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-cz.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-de.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-en-GB.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-es.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-fr.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-it.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-ja.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-pl.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-pt-BR.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-ru.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-sv-SE.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-th.js +23 -0
- data/vendor/assets/javascripts/jquery_ui_datepicker/jquery.ui.datepicker-zh-CN.js +23 -0
- data/vendor/assets/javascripts/lib/abstract-chosen.coffee +110 -0
- data/vendor/assets/javascripts/lib/select-parser.coffee +51 -0
- data/vendor/assets/stylesheets/chosen.css.sass +361 -0
- data/vendor/assets/stylesheets/{jquery-ui.custom.css.erb → jquery-ui.custom.css} +70 -70
- data/zeus.json +21 -0
- metadata +304 -140
- data/acceptance/support/database_cleaner.rb +0 -16
- data/acceptance/support/paths.rb +0 -9
- data/app/views/accounts/_options.html.haml +0 -20
- data/app/views/accounts/_permissions.html.haml +0 -24
- data/app/views/accounts/options.js.rjs +0 -10
- data/app/views/admin/fields/_edit.html.haml +0 -9
- data/app/views/admin/fields/_new.html.haml +0 -10
- data/app/views/admin/fields/_options.html.haml +0 -12
- data/app/views/admin/fields/_top_section.html.haml +0 -54
- data/app/views/admin/fields/create.js.rjs +0 -16
- data/app/views/admin/fields/edit.js.rjs +0 -35
- data/app/views/admin/fields/index.js.rjs +0 -6
- data/app/views/admin/fields/new.js.rjs +0 -8
- data/app/views/admin/fields/options.js.rjs +0 -10
- data/app/views/admin/fields/update.js.rjs +0 -17
- data/app/views/campaigns/_options.html.haml +0 -20
- data/app/views/campaigns/_permissions.html.haml +0 -24
- data/app/views/campaigns/options.js.rjs +0 -10
- data/app/views/contacts/_options.html.haml +0 -21
- data/app/views/contacts/_permissions.html.haml +0 -24
- data/app/views/contacts/options.js.rjs +0 -10
- data/app/views/entities/_advanced_search.html.haml +0 -12
- data/app/views/entities/_condition_fields.html.haml +0 -14
- data/app/views/entities/_grouping_fields.html.haml +0 -12
- data/app/views/entities/advanced_search.js.rjs +0 -9
- data/app/views/entities/versions.js.rjs +0 -3
- data/app/views/leads/_options.html.haml +0 -21
- data/app/views/leads/_permissions.html.haml +0 -28
- data/app/views/leads/options.js.rjs +0 -10
- data/app/views/opportunities/_options.html.haml +0 -20
- data/app/views/opportunities/_permissions.html.haml +0 -24
- data/app/views/opportunities/options.js.rjs +0 -10
- data/app/views/shared/_outline.html.haml +0 -9
- data/app/views/shared/_per_page.html.haml +0 -10
- data/app/views/shared/_sort_by.html.haml +0 -10
- data/config/initializers/cancan.rb +0 -151
- data/config/initializers/squeel.rb +0 -5
- data/public/blank_iframe.html +0 -2
- data/spec/views/accounts/options.rjs_spec.rb +0 -59
- data/spec/views/campaigns/options.rjs_spec.rb +0 -60
- data/spec/views/contacts/options.rjs_spec.rb +0 -61
- data/spec/views/leads/options.rjs_spec.rb +0 -61
- data/spec/views/opportunities/options.rjs_spec.rb +0 -60
- data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
- data/vendor/assets/javascripts/calendar_date_select/calendar_date_select.js +0 -446
- data/vendor/assets/javascripts/calendar_date_select/format_american.js +0 -35
- data/vendor/assets/javascripts/calendar_date_select/format_danish.js +0 -31
- data/vendor/assets/javascripts/calendar_date_select/format_db.js +0 -27
- data/vendor/assets/javascripts/calendar_date_select/format_euro_24hr.js +0 -7
- data/vendor/assets/javascripts/calendar_date_select/format_euro_24hr_ymd.js +0 -7
- data/vendor/assets/javascripts/calendar_date_select/format_finnish.js +0 -32
- data/vendor/assets/javascripts/calendar_date_select/format_french.js +0 -24
- data/vendor/assets/javascripts/calendar_date_select/format_hyphen_ampm.js +0 -37
- data/vendor/assets/javascripts/calendar_date_select/format_iso_date.js +0 -29
- data/vendor/assets/javascripts/calendar_date_select/format_italian.js +0 -24
- data/vendor/assets/javascripts/calendar_date_select/locale/ar.js +0 -10
- data/vendor/assets/javascripts/calendar_date_select/locale/da.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/de.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/es.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/fi.js +0 -10
- data/vendor/assets/javascripts/calendar_date_select/locale/fr.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/it.js +0 -9
- data/vendor/assets/javascripts/calendar_date_select/locale/ja.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/nl.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/pl.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/pt.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/ru.js +0 -10
- data/vendor/assets/javascripts/calendar_date_select/locale/sl.js +0 -11
- data/vendor/assets/javascripts/calendar_date_select/locale/sv.js +0 -9
- data/vendor/assets/stylesheets/calendar_date_select/blue.css +0 -130
- data/vendor/assets/stylesheets/calendar_date_select/default.css +0 -135
- data/vendor/assets/stylesheets/calendar_date_select/green.css +0 -142
- data/vendor/assets/stylesheets/calendar_date_select/plain.css +0 -128
- data/vendor/assets/stylesheets/calendar_date_select/red.css +0 -135
- data/vendor/assets/stylesheets/calendar_date_select/silver.css +0 -133
- 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
|
-
|
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
|
-
|
@@ -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
|
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
|
-
|
64
|
+
has_ransackable_associations %w(account opportunities tags activities emails addresses comments tasks)
|
65
|
+
ransack_can_autocomplete
|
64
66
|
|
65
|
-
|
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
|
-
|
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
|
-
|
77
|
-
}
|
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
|
-
|
85
|
+
t[:first_name].matches("%#{query}%").or(t[:last_name].matches("%#{query}%"))
|
80
86
|
end
|
81
|
-
|
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
|
-
|
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.
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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.
|
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
|
-
|
data/app/models/entities/lead.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
120
|
+
def update_with_lead_counters(attributes)
|
112
121
|
if self.campaign_id == attributes[:campaign_id] # Same campaign (if any).
|
113
|
-
|
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
|
-
|
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]
|
127
|
-
opportunity = Opportunity.create_for(self, account, params[:opportunity]
|
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,
|
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,
|
59
|
-
scope :lost,
|
60
|
-
scope :
|
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
|
-
|
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
|
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]
|
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.
|
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]
|
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
|
-
|
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
|
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.
|
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 => [[
|
67
|
-
:one => {
|
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
|
-
|
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][:
|
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
|
-
|