├── .gitignore ├── test ├── config │ └── timezone.csv ├── files │ ├── src_sla │ │ ├── sla_types.yml │ │ ├── sla_statuses.yml │ │ ├── sla_calendars.yml │ │ └── slas.yml │ └── src_redmine │ │ ├── custom_fields_projects.yml │ │ ├── custom_fields_trackers.yml │ │ ├── custom_field_enumerations.yml │ │ └── email_addresses.yml ├── unit │ ├── sla_log_test.rb │ ├── sla_holiday_test.rb │ ├── sla_level_term_test.rb │ ├── sla_schedule_test.rb │ ├── sla_view_journal_test.rb │ ├── sla_calendar_holiday_test.rb │ ├── sla_project_tracker_test.rb │ ├── sla_cache_test.rb │ ├── sla_cache_spent_test.rb │ ├── sla_type_test.rb │ ├── sla_status_test.rb │ ├── sla_calendar_test.rb │ └── sla_test.rb ├── application_system_test_case.rb ├── assertion_helpers.rb ├── helpers │ └── sla_caches_helper.rb └── integration │ └── routing │ ├── slas_test.rb │ ├── sla_types_test.rb │ └── sla_statuses_test.rb ├── app ├── views │ ├── sla_issues_helper │ │ └── index.html.erb │ ├── sla_settings_plugin │ │ ├── index.html.erb │ │ ├── _sla_settings_plugin_cache.html.erb │ │ ├── _sla_settings_plugin_display.html.erb │ │ ├── _sla_settings_plugin_logs.html.erb │ │ ├── _sla_settings_plugin_zone.html.erb │ │ └── _sla_settings_plugin.html.erb │ ├── slas │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── show.html.erb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── _form.html.erb │ │ ├── _action_menu.html.erb │ │ ├── context_menu.html.erb │ │ └── index.html.erb │ ├── sla_types │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── show.html.erb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── _form.html.erb │ │ ├── context_menu.html.erb │ │ ├── _action_menu.html.erb │ │ └── index.html.erb │ ├── sla_calendars │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── _sla_schedule_fields.erb │ │ ├── context_menu.html.erb │ │ ├── _action_menu.html.erb │ │ ├── index.html.erb │ │ ├── _form.html.erb │ │ └── show.html.erb │ ├── sla_holidays │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── show.html.erb │ │ ├── context_menu.html.erb │ │ ├── _action_menu.html.erb │ │ ├── _form.html.erb │ │ └── index.html.erb │ ├── sla_statuses │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── show.html.erb │ │ ├── _action_menu.html.erb │ │ ├── context_menu.html.erb │ │ ├── index.html.erb │ │ └── _form.html.erb │ ├── sla_levels │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ ├── context_menu.html.erb │ │ ├── index.html.erb │ │ ├── _action_menu.html.erb │ │ └── _form.html.erb │ ├── sla_project_trackers │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ └── context_menu.html.erb │ ├── sla_calendar_holidays │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── show.html.erb │ │ ├── context_menu.html.erb │ │ ├── index.html.erb │ │ └── _form.html.erb │ ├── sla_level_terms │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── show.html.erb │ │ ├── _action_menu.html.erb │ │ ├── index.html.erb │ │ ├── context_menu.html.erb │ │ └── _form.html.erb │ ├── sla_schedules │ │ ├── show.api.rsb │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── index.api.rsb │ │ ├── context_menu.html.erb │ │ ├── index.html.erb │ │ ├── show.html.erb │ │ └── _form.html.erb │ ├── sla_cache_spents │ │ ├── show.api.rsb │ │ ├── index.api.rsb │ │ └── context_menu.html.erb │ └── sla_caches │ │ ├── index.api.rsb │ │ ├── show.api.rsb │ │ └── context_menu.html.erb ├── helpers │ ├── sla_custom_fields_helper.rb │ ├── sla_statuses_helper.rb │ ├── sla_schedules_helper.rb │ ├── sla_cache_spents_helper.rb │ ├── sla_level_terms_helper.rb │ ├── sla_calendar_holidays_helper.rb │ ├── slas_helper.rb │ ├── sla_types_helper.rb │ ├── queries │ │ ├── slas_queries_helper.rb │ │ ├── sla_types_queries_helper.rb │ │ ├── sla_holidays_queries_helper.rb │ │ ├── sla_cache_spents_queries_helper.rb │ │ ├── sla_calendars_queries_helper.rb │ │ ├── sla_statuses_queries_helper.rb │ │ ├── sla_project_trackers_queries_helper.rb │ │ ├── sla_schedules_queries_helper.rb │ │ ├── sla_levels_queries_helper.rb │ │ ├── sla_calendar_holidays_queries_helper.rb │ │ ├── sla_caches_queries_helper.rb │ │ └── sla_level_terms_queries_helper.rb │ ├── sla_levels_helper.rb │ ├── sla_holidays_helper.rb │ ├── sla_caches_helper.rb │ └── sla_calendars_helper.rb └── models │ ├── sla_log.rb │ ├── sla_view_journal.rb │ ├── sla_custom_field.rb │ ├── sla_priority_scf.rb │ └── sla_holiday.rb ├── assets ├── images │ ├── SOURCE.MD │ ├── sla_16.png │ ├── sla_24.png │ ├── sla_32.png │ ├── sla_cache_16.png │ ├── sla_cache_24.png │ ├── sla_cache_32.png │ ├── sla_level_16.png │ ├── sla_level_24.png │ ├── sla_level_32.png │ ├── sla_type_16.png │ ├── sla_type_24.png │ ├── sla_type_32.png │ ├── sla_holiday_16.png │ ├── sla_holiday_24.png │ ├── sla_holiday_32.png │ ├── sla_status_16.png │ ├── sla_status_24.png │ ├── sla_status_32.png │ ├── sla_calendar_16.png │ ├── sla_calendar_24.png │ ├── sla_calendar_32.png │ ├── sla_level_term_16.png │ ├── sla_level_term_24.png │ ├── sla_level_term_32.png │ ├── sla_schedule_16.png │ ├── sla_schedule_24.png │ ├── sla_schedule_32.png │ ├── sla_cache_spent_16.png │ ├── sla_cache_spent_24.png │ ├── sla_cache_spent_32.png │ ├── sla_cache_refresh_16.png │ ├── sla_project_tracker_16.png │ ├── sla_project_tracker_24.png │ ├── sla_project_tracker_32.png │ ├── sla_calendar_holiday_16.png │ ├── sla_calendar_holiday_24.png │ ├── sla_calendar_holiday_32.png │ └── sla_cache_spent_refresh_16.png └── javascripts │ └── redmine_sla.js ├── doc ├── images │ ├── mcd │ │ └── redmine_sla_mcd.png │ ├── redmine_sla_issue_patch.png │ ├── example_01 │ │ ├── redmine_sla_doc_example_01_01.png │ │ ├── redmine_sla_doc_example_01_02.png │ │ ├── redmine_sla_doc_example_01_03.png │ │ ├── redmine_sla_doc_example_01_04.png │ │ ├── redmine_sla_doc_example_01_05.png │ │ ├── redmine_sla_doc_example_01_06.png │ │ ├── redmine_sla_doc_example_01_07.png │ │ ├── redmine_sla_doc_example_01_08.png │ │ ├── redmine_sla_doc_example_01_09.png │ │ ├── redmine_sla_doc_example_01_10.png │ │ ├── redmine_sla_doc_example_01_11.png │ │ └── redmine_sla_doc_example_01_12.png │ ├── example_02 │ │ ├── redmine_sla_doc_example_02_01.png │ │ ├── redmine_sla_doc_example_02_02.png │ │ ├── redmine_sla_doc_example_02_03.png │ │ ├── redmine_sla_doc_example_02_04.png │ │ ├── redmine_sla_doc_example_02_05.png │ │ ├── redmine_sla_doc_example_02_06.png │ │ ├── redmine_sla_doc_example_02_07.png │ │ ├── redmine_sla_doc_example_02_08.png │ │ ├── redmine_sla_doc_example_02_09.png │ │ ├── redmine_sla_doc_example_02_10.png │ │ ├── redmine_sla_doc_example_02_11.png │ │ └── redmine_sla_doc_example_02_12.png │ └── example_03 │ │ ├── redmine_sla_doc_example_03_01.png │ │ ├── redmine_sla_doc_example_03_02.png │ │ ├── redmine_sla_doc_example_03_03.png │ │ ├── redmine_sla_doc_example_03_04.png │ │ ├── redmine_sla_doc_example_03_05.png │ │ ├── redmine_sla_doc_example_03_06.png │ │ ├── redmine_sla_doc_example_03_07.png │ │ ├── redmine_sla_doc_example_03_08.png │ │ ├── redmine_sla_doc_example_03_09.png │ │ ├── redmine_sla_doc_example_03_10.png │ │ ├── redmine_sla_doc_example_03_11.png │ │ └── redmine_sla_doc_example_03_12.png ├── TASKS.md ├── API │ ├── API_sla_project_trackers.md │ ├── API_sla_types.md │ ├── API_sla_statuses.md │ ├── API_sla_holidays.md │ ├── API_slas.md │ ├── API_sla_levels.md │ ├── API_sla_level_terms.md │ └── API_sla_schedules.md ├── SETTINGS.md ├── MCD.md ├── API.md └── USECASE.md ├── config └── initializers │ └── inflections.rb ├── CHANGELOG.md ├── Gemfile ├── db ├── migrate │ ├── 202111112021001_create_slas.rb │ ├── 202111112021007_create_sla_types.rb │ ├── 202111112021004_create_sla_holidays.rb │ ├── 202111112021003_create_sla_calendars.rb │ ├── 202111112021011_create_sla_get_date.rb │ ├── 202111112021013_create_sla_view_journal_statuses.rb │ ├── 202111112021017_create_sla_view_roll_statuses.rb │ ├── 202111112021006_create_sla_schedules.rb │ ├── 202111112021005_create_sla_calendar_holidays.rb │ ├── 202111112021012_create_sla_statuses.rb │ └── 202111112021010_create_sla_project_trackers.rb ├── schema.rb ├── sql_functions │ └── sla_get_date.sql └── sql_views │ └── sla_view_roll_statuses.sql └── lib ├── redmine_sla ├── version.rb ├── patches │ └── issue_custom_field_patch.rb └── hooks │ ├── views_layouts_hook.rb │ └── views_issues_hook.rb └── redmine_sla.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /test/fixtures/* -------------------------------------------------------------------------------- /test/config/timezone.csv: -------------------------------------------------------------------------------- 1 | "Europe/Paris" -------------------------------------------------------------------------------- /app/views/sla_issues_helper/index.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/index.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/slas/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla do 2 | api.id @sla.id 3 | api.name @sla.name 4 | end 5 | -------------------------------------------------------------------------------- /assets/images/SOURCE.MD: -------------------------------------------------------------------------------- 1 | Icons created by Freepik - Flaticon : -------------------------------------------------------------------------------- /assets/images/sla_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_16.png -------------------------------------------------------------------------------- /assets/images/sla_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_24.png -------------------------------------------------------------------------------- /assets/images/sla_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_32.png -------------------------------------------------------------------------------- /test/files/src_sla/sla_types.yml: -------------------------------------------------------------------------------- 1 | GTI: 2 | id: 1 3 | name: GTI 4 | GTR: 5 | id: 2 6 | name: GTR 7 | -------------------------------------------------------------------------------- /app/views/sla_types/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_type do 2 | api.id @sla_type.id 3 | api.name @sla_type.name 4 | end 5 | -------------------------------------------------------------------------------- /assets/images/sla_cache_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_16.png -------------------------------------------------------------------------------- /assets/images/sla_cache_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_24.png -------------------------------------------------------------------------------- /assets/images/sla_cache_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_32.png -------------------------------------------------------------------------------- /assets/images/sla_level_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_16.png -------------------------------------------------------------------------------- /assets/images/sla_level_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_24.png -------------------------------------------------------------------------------- /assets/images/sla_level_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_32.png -------------------------------------------------------------------------------- /assets/images/sla_type_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_type_16.png -------------------------------------------------------------------------------- /assets/images/sla_type_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_type_24.png -------------------------------------------------------------------------------- /assets/images/sla_type_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_type_32.png -------------------------------------------------------------------------------- /assets/images/sla_holiday_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_holiday_16.png -------------------------------------------------------------------------------- /assets/images/sla_holiday_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_holiday_24.png -------------------------------------------------------------------------------- /assets/images/sla_holiday_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_holiday_32.png -------------------------------------------------------------------------------- /assets/images/sla_status_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_status_16.png -------------------------------------------------------------------------------- /assets/images/sla_status_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_status_24.png -------------------------------------------------------------------------------- /assets/images/sla_status_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_status_32.png -------------------------------------------------------------------------------- /test/files/src_redmine/custom_fields_projects.yml: -------------------------------------------------------------------------------- 1 | --- 2 | cfp_01_cf_04_project: 3 | custom_field_id: 1 4 | project_id: 4 -------------------------------------------------------------------------------- /assets/images/sla_calendar_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_16.png -------------------------------------------------------------------------------- /assets/images/sla_calendar_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_24.png -------------------------------------------------------------------------------- /assets/images/sla_calendar_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_32.png -------------------------------------------------------------------------------- /assets/images/sla_level_term_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_term_16.png -------------------------------------------------------------------------------- /assets/images/sla_level_term_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_term_24.png -------------------------------------------------------------------------------- /assets/images/sla_level_term_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_level_term_32.png -------------------------------------------------------------------------------- /assets/images/sla_schedule_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_schedule_16.png -------------------------------------------------------------------------------- /assets/images/sla_schedule_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_schedule_24.png -------------------------------------------------------------------------------- /assets/images/sla_schedule_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_schedule_32.png -------------------------------------------------------------------------------- /doc/images/mcd/redmine_sla_mcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/mcd/redmine_sla_mcd.png -------------------------------------------------------------------------------- /app/views/sla_calendars/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_calendar do 2 | api.id @sla_calendar.id 3 | api.name @sla_calendar.name 4 | end 5 | -------------------------------------------------------------------------------- /assets/images/sla_cache_spent_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_spent_16.png -------------------------------------------------------------------------------- /assets/images/sla_cache_spent_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_spent_24.png -------------------------------------------------------------------------------- /assets/images/sla_cache_spent_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_spent_32.png -------------------------------------------------------------------------------- /assets/images/sla_cache_refresh_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_refresh_16.png -------------------------------------------------------------------------------- /assets/images/sla_project_tracker_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_project_tracker_16.png -------------------------------------------------------------------------------- /assets/images/sla_project_tracker_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_project_tracker_24.png -------------------------------------------------------------------------------- /assets/images/sla_project_tracker_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_project_tracker_32.png -------------------------------------------------------------------------------- /doc/images/redmine_sla_issue_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/redmine_sla_issue_patch.png -------------------------------------------------------------------------------- /assets/images/sla_calendar_holiday_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_holiday_16.png -------------------------------------------------------------------------------- /assets/images/sla_calendar_holiday_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_holiday_24.png -------------------------------------------------------------------------------- /assets/images/sla_calendar_holiday_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_calendar_holiday_32.png -------------------------------------------------------------------------------- /assets/images/sla_cache_spent_refresh_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/assets/images/sla_cache_spent_refresh_16.png -------------------------------------------------------------------------------- /app/views/sla_holidays/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_holiday do 2 | api.id @sla_holiday.id 3 | api.name @sla_holiday.name 4 | api.date @sla_holiday.date 5 | end 6 | -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_01.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_02.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_03.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_04.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_05.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_06.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_07.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_08.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_09.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_10.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_11.png -------------------------------------------------------------------------------- /doc/images/example_01/redmine_sla_doc_example_01_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_01/redmine_sla_doc_example_01_12.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_01.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_02.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_03.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_04.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_05.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_06.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_07.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_08.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_09.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_10.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_11.png -------------------------------------------------------------------------------- /doc/images/example_02/redmine_sla_doc_example_02_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_02/redmine_sla_doc_example_02_12.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_01.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_02.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_03.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_04.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_05.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_06.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_07.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_08.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_09.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_10.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_11.png -------------------------------------------------------------------------------- /doc/images/example_03/redmine_sla_doc_example_03_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likehopper/redmine_sla/HEAD/doc/images/example_03/redmine_sla_doc_example_03_12.png -------------------------------------------------------------------------------- /app/views/sla_statuses/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_status do 2 | api.id @sla_status.id 3 | api.sla_type @sla_status.sla_type 4 | api.status @sla_status.status 5 | end 6 | -------------------------------------------------------------------------------- /app/views/sla_levels/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_level do 2 | api.id @sla_level.id 3 | api.name @sla_level.name 4 | api.sla @sla_level.sla 5 | api.sla_calendar @sla_level.sla_calendar 6 | api.custom_field @sla_level.custom_field 7 | end 8 | -------------------------------------------------------------------------------- /app/views/sla_project_trackers/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_project_tracker do 2 | api.id @sla_project_tracker.id 3 | api.project @sla_project_tracker.project 4 | api.tracker @sla_project_tracker.tracker 5 | api.sla @sla_project_tracker.sla 6 | end -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/_sla_settings_plugin_cache.html.erb: -------------------------------------------------------------------------------- 1 |

2 | 3 | <%= text_field_tag 'settings[sla_cache_ttl]', @settings["sla_cache_ttl"] %> 4 |

-------------------------------------------------------------------------------- /app/views/slas/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :slas, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla| 3 | api.sla do 4 | api.id sla.id 5 | api.name sla.name 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /test/files/src_redmine/custom_fields_trackers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | cft_01_cf_01_tracker: 3 | custom_field_id: 1 4 | tracker_id: 1 5 | cft_01_cf_02_tracker: 6 | custom_field_id: 1 7 | tracker_id: 2 8 | cft_01_cf_03_tracker: 9 | custom_field_id: 1 10 | tracker_id: 3 -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_calendar_holiday do 2 | api.id @sla_calendar_holiday.id 3 | api.sla_calendar @sla_calendar_holiday.sla_calendar 4 | api.sla_holiday @sla_calendar_holiday.sla_holiday 5 | api.match @sla_calendar_holiday.match 6 | end 7 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_level_term do 2 | api.id @sla_level_term.id 3 | api.sla_level @sla_level_term.sla_level 4 | api.sla_type @sla_level_term.sla_type 5 | api.sla_priority_id @sla_level_term.sla_priority_id 6 | api.term @sla_level_term.term 7 | end 8 | -------------------------------------------------------------------------------- /app/views/sla_types/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_types, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_type| 3 | api.sla_type do 4 | api.id sla_type.id 5 | api.name sla_type.name 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/_sla_settings_plugin_display.html.erb: -------------------------------------------------------------------------------- 1 |

2 | 3 | <%= select_tag 'settings[sla_display]', options_for_select(['none','bar','pie - flat','pie - modern'], 4 | @settings["sla_display"]) %> 5 |

-------------------------------------------------------------------------------- /app/views/sla_schedules/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_schedule do 2 | api.id @sla_schedule.id 3 | api.sla_calendar @sla_schedule.sla_calendar 4 | api.dow @sla_schedule.dow 5 | api.start_time @sla_schedule.start_time 6 | api.end_time @sla_schedule.end_time 7 | api.match @sla_schedule.match 8 | end 9 | -------------------------------------------------------------------------------- /app/views/sla_calendars/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_calendars, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_calendar| 3 | api.sla_calendar do 4 | api.id sla_calendar.id 5 | api.name sla_calendar.name 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/_sla_settings_plugin_logs.html.erb: -------------------------------------------------------------------------------- 1 |

2 | 3 | <%= select_tag 'settings[sla_log_level]', options_for_select(Sla.sla_log_levels.map {|k, v| [l(k), v]}, 4 | @settings["sla_log_level"]) %> 5 |

-------------------------------------------------------------------------------- /test/files/src_sla/sla_statuses.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # GTI only on New 4 | # 5 | one: 6 | id: 1 7 | sla_type_id: 1 8 | status_id: 1 9 | # 10 | # GTR on New and Assigned 11 | # 12 | two: 13 | id: 2 14 | sla_type_id: 2 15 | status_id: 1 16 | three: 17 | id: 3 18 | sla_type_id: 2 19 | status_id: 2 -------------------------------------------------------------------------------- /app/views/sla_holidays/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_holidays, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_holiday| 3 | api.sla_holiday do 4 | api.id sla_holiday.id 5 | api.name sla_holiday.name 6 | api.date sla_holiday.date 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /doc/TASKS.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | ## build_fixture_sla.rake 4 | Build fixture base on CSV file 5 | Usage: `rake redmine:plugins:redmine_sla:build_fixture RAILS_ENV=development --trace` 6 | 7 | ## update_sla.rake 8 | Update SLA manualy or by crontab 9 | Usage: `rake redmine:plugins:redmine_sla:update_sla RAILS_ENV=development --trace` 10 | -------------------------------------------------------------------------------- /app/views/sla_statuses/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_statuses, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_status| 3 | api.sla_status do 4 | api.id sla_status.id 5 | api.sla_type_id sla_status.sla_type_id 6 | api.status_id sla_status.status_id 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /assets/javascripts/redmine_sla.js: -------------------------------------------------------------------------------- 1 | function remove_fields(link) { 2 | $(link).prev("input[type=hidden]").val("1"); 3 | $(link).closest(".fields").hide(); 4 | } 5 | 6 | function add_fields(link, association, content) { 7 | var new_id = new Date().getTime(); 8 | var regexp = new RegExp("new_" + association, "g") 9 | $(link).before(content.replace(regexp, new_id)); 10 | } -------------------------------------------------------------------------------- /app/views/slas/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 | <%= render :partial => 'action_menu' %> 4 |
5 | <%= title l('sla_label.sla.show') %> 6 |
7 |

<%= @sla.name %>

8 |
9 | <% content_for :sidebar do %> 10 | <% render partial: 'slas/sidebar' %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/config/initializers/inflections.rb 2 | # Purpose: 3 | # Define custom inflection rules for the Redmine SLA plugin. 4 | # In particular, ensure that the singular/plural forms of "sla_cache" 5 | # are handled correctly by ActiveRecord (e.g. model ↔ table names). 6 | 7 | ActiveSupport::Inflector.inflections(:en) do |inflect| 8 | inflect.irregular 'sla_cache', 'sla_caches' 9 | end -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/_sla_settings_plugin_zone.html.erb: -------------------------------------------------------------------------------- 1 |

2 | 3 | <% 4 | # select_tag 'settings[sla_time_zone]', options_for_select(ActiveSupport::TimeZone::MAPPING.map {|k,v| [k,v]}, 5 | %> 6 | <%= select_tag 'settings[sla_time_zone]', options_for_select(ActiveSupport::TimeZone::MAPPING.map {|k,v| [v,v]}, 7 | @settings["sla_time_zone"]) %> 8 |

-------------------------------------------------------------------------------- /app/views/sla_levels/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_levels, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_level| 3 | api.sla_level do 4 | api.id sla_level.id 5 | api.name sla_level.name 6 | api.sla_id sla_level.sla_id 7 | api.sla_calendar_id sla_level.sla_calendar_id 8 | api.custom_field_id sla_level.custom_field_id 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2.0.0 (Stable - Compatible Redmine 6) 🚀 4 | 5 | * **Mise à jour de Compatibilité :** Support officiel ajouté pour **Redmine 6.x**. 6 | 7 | --- 8 | 9 | ## 1.0.0 (Stable - Compatible Redmine 5) 10 | 11 | * **Changement Majeur :** Passage en version stable majeure. 12 | * Mise à jour de la compatibilité pour supporter Redmine 5. 13 | 14 | --- 15 | 16 | ## 0.0.9 17 | 18 | - First public release (développement) -------------------------------------------------------------------------------- /app/views/slas/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla.plural'), slas_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla: @sla } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/slas/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla.plural'), slas_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla: @sla } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_types/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 | <%= render :partial => 'action_menu' %> 4 |
5 | <%= title l('sla_label.sla_type.show') %> 6 |
7 | 8 |

n°<%= @sla_type.id %> : <%= @sla_type.name %>

9 | 10 |
11 | <% content_for :sidebar do %> 12 | <% render partial: 'slas/sidebar' %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/sla_cache_spents/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_cache_spent do 2 | api.id @sla_cache_spent.id 3 | api.issue_id @sla_cache_spent.issue_id 4 | api.project_id @sla_cache_spent.project_id 5 | api.sla_cache @sla_cache_spent.sla_cache 6 | api.sla_type @sla_cache_spent.sla_type 7 | api.created_on @sla_cache_spent.created_on 8 | api.updated_on @sla_cache_spent.updated_on 9 | api.spent @sla_cache_spent.spent 10 | end -------------------------------------------------------------------------------- /app/views/sla_project_trackers/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_project_trackers, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_project_tracker| 3 | api.sla_project_tracker do 4 | api.id sla_project_tracker.id 5 | api.project_id sla_project_tracker.project_id 6 | api.tracker_id sla_project_tracker.tracker_id 7 | api.sla_id sla_project_tracker.sla_id 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /app/views/sla_project_trackers/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l('sla_label.sla_project_tracker.new')) %> 2 | <%= title l('sla_label.sla_project_tracker.new') %> 3 | <%= labelled_form_for([@sla_project_tracker],url: _sla_project_trackers_path(@project, back_url: @back), html: { multipart: false, id: 'sla-project-tracker-form', class: 'tabular', remote: request.xhr? }) do |f| %> 4 | <%= render partial: 'form', locals: { sla_project_tracker: @sla_project_tracker, f: f } %> 5 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_calendar_holidays, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_calendar_holiday| 3 | api.sla_calendar_holiday do 4 | api.id sla_calendar_holiday.id 5 | api.sla_calendar_id sla_calendar_holiday.sla_calendar_id 6 | api.sla_holiday_id sla_calendar_holiday.sla_holiday_id 7 | api.match sla_calendar_holiday.match 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /app/views/sla_level_terms/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_level_terms, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_level_term| 3 | api.sla_level_term do 4 | api.id sla_level_term.id 5 | api.sla_level_id sla_level_term.sla_level_id 6 | api.sla_type_id sla_level_term.sla_type_id 7 | api.sla_priority_id sla_level_term.sla_priority_id 8 | api.term sla_level_term.term 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /app/views/sla_types/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_type.plural'), sla_types_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_type.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_type: @sla_type } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_types/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_type.plural'), sla_types_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_type.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_type: @sla_type } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_levels/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_level.plural'), sla_levels_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_level.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_level: @sla_level } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_levels/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_level.plural'), sla_levels_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_level.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_level: @sla_level } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_statuses/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_status.plural'), sla_statuses_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_status.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_status: @sla_status } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_statuses/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_status.plural'), sla_statuses_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_status.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_status: @sla_status } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_holidays/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_holiday.plural'), sla_holidays_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_holiday.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_holiday: @sla_holiday } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_holidays/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_holiday.plural'), sla_holidays_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_holiday.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_holiday: @sla_holiday } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /test/files/src_sla/sla_calendars.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | name: TMA - HO 4 | two: 5 | id: 2 6 | name: Managed Services std - HO - Request 7 | three: 8 | id: 3 9 | name: Managed Services std - HO - Incident 10 | four: 11 | id: 4 12 | name: Managed Services std - HNO - Incident 13 | five: 14 | id: 5 15 | name: Managed Services spe - HO - Request 16 | six: 17 | id: 6 18 | name: Managed Services spe - HO - Incident 19 | seven: 20 | id: 7 21 | name: Managed Services spe - HNO - Incident -------------------------------------------------------------------------------- /app/views/sla_schedules/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_schedule.plural'), sla_schedules_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_schedule.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_schedule: @sla_schedule } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_calendars/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar.plural'), sla_calendars_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l("sla_label.sla_calendar.edit") %> 8 |
9 | <%= render partial: 'form', locals: { sla_calendar: @sla_calendar } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_calendars/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar.plural'), sla_calendars_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_calendar: @sla_calendar } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_schedules/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_schedule.plural'), sla_schedules_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_schedule.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_schedule: @sla_schedule } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /doc/API/API_sla_project_trackers.md: -------------------------------------------------------------------------------- 1 | ## Sla Project Trackers 2 | 3 | ### Listing Sla Project Trackers 4 | 5 | `GET /projects/[project-identifier]/slas/index.[format]` 6 | 7 | Returns a paginated list of Sla Project Trackers. By default, it returns all Sla Project Trackers. 8 | 9 | Optional filters: 10 | - sla_id 11 | - tracker_id 12 | 13 | Examples: 14 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/projects/[project-identifier]/slas/index.json"` 15 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_level_term.plural'), sla_level_terms_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_level_term.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_level_term: @sla_level_term } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_level_terms/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_level_term.plural'), sla_level_terms_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_level_term.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_level_term: @sla_level_term } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_schedules/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_schedules, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_schedule| 3 | api.sla_schedule do 4 | api.id sla_schedule.id 5 | api.sla_calendar_id sla_schedule.sla_calendar_id 6 | api.dow sla_schedule.dow 7 | api.start_time sla_schedule.start_time.strftime('%H:%M') 8 | api.end_time sla_schedule.end_time.strftime('%H:%M') 9 | api.match sla_schedule.match 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /app/views/sla_project_trackers/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l('sla_label.sla_project_tracker.edit')) %> 2 | <%= title l('sla_label.sla_project_tracker.edit') %> 3 | <% Rails.logger.debug "Arguments: back_url = #{back_url}" %> 4 | <%= labelled_form_for([@sla_project_tracker], 5 | url: _sla_project_tracker_path(@project, back_url: @back), 6 | html: { multipart: false, id: 'sla-project-tracker-form', class: 'tabular', remote: request.xhr? }) do |f| %> 7 | <%= render partial: 'form', locals: { sla_project_tracker: @sla_project_tracker, f: f } %> 8 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_holidays/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= render :partial => 'action_menu' %> 5 |
6 |
7 | <%= title l('sla_label.sla_holiday.show') %> 8 |
9 | 10 |

n°<%= @sla_holiday.id %> : <%= @sla_holiday.name %>

11 | 12 |

<%= @sla_holiday.date %>

13 | 14 |
15 | <% content_for :sidebar do %> 16 | <% render partial: 'slas/sidebar' %> 17 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/new.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar_holiday.plural'), sla_calendar_holidays_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar_holiday.new') %> 8 |
9 | <%= render partial: 'form', locals: { sla_calendar_holiday: @sla_calendar_holiday } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar_holiday.plural'), sla_calendar_holidays_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar_holiday.edit') %> 8 |
9 | <%= render partial: 'form', locals: { sla_calendar_holiday: @sla_calendar_holiday } %> 10 |
11 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_caches/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_caches, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_cache| 3 | api.sla_cache do 4 | api.id sla_cache.id 5 | api.project_id sla_cache.project_id 6 | api.issue_id sla_cache.issue_id 7 | api.sla_level_id sla_cache.sla_level_id 8 | api.start_date sla_cache.start_date 9 | render_api_includes(sla_cache, api) 10 | api.created_on sla_cache.created_on 11 | api.updated_on sla_cache.updated_on 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/Gemfile 4 | # Purpose: 5 | # Declare Ruby gem dependencies required by the Redmine SLA plugin. 6 | # These gems must be installed in the Redmine environment in order for 7 | # the plugin features to operate correctly (nested forms, time parsing). 8 | 9 | # Provides nested form fields used in SLA configuration screens 10 | gem "nested_form" 11 | 12 | # Natural language time parsing (e.g. "5 minutes", "2 hours") 13 | gem "chronic" 14 | 15 | # Duration parsing and formatting used for SLA time representation 16 | gem "chronic_duration" -------------------------------------------------------------------------------- /app/views/sla_statuses/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= render :partial => 'action_menu' %> 5 |
6 |
7 | <%= title l('sla_label.sla_status.show') %> 8 |
9 | 10 |

n°<%= @sla_status.status_id %> : <%= @sla_status.status.name %>

11 | 12 |

n°<%= @sla_status.sla_type_id %> : <%= @sla_status.sla_type.name %>

13 | 14 |
15 | <% content_for :sidebar do %> 16 | <% render partial: 'slas/sidebar' %> 17 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_cache_spents/index.api.rsb: -------------------------------------------------------------------------------- 1 | api.array :sla_cache_spents, api_meta(:total_count => @entity_count, :offset => @offset, :limit => @limit) do 2 | @entities.each do |sla_cache_spent| 3 | api.sla_cache_spent do 4 | api.id sla_cache_spent.id 5 | api.issue_id sla_cache_spent.issue_id 6 | api.project_id sla_cache_spent.project_id 7 | api.sla_cache_id sla_cache_spent.sla_cache_id 8 | api.sla_type_id sla_cache_spent.sla_type_id 9 | api.spent sla_cache_spent.spent 10 | api.created_on sla_cache_spent.created_on 11 | api.updated_on sla_cache_spent.updated_on 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /app/views/sla_caches/show.api.rsb: -------------------------------------------------------------------------------- 1 | api.sla_cache do 2 | api.id @sla_cache.id 3 | api.project_id @sla_cache.project_id 4 | api.issue_id @sla_cache.issue_id 5 | api.tracker_id @sla_cache.tracker_id 6 | api.sla_level @sla_cache.sla_level 7 | api.start_date @sla_cache.start_date 8 | api.created_on @sla_cache.created_on 9 | api.updated_on @sla_cache.updated_on 10 | api.array :sla_cache_spents do 11 | @sla_cache_spents.each do |sla_cache_spent| 12 | api.sla_cache_spent :id => sla_cache_spent.id do 13 | api.sla_type sla_cache_spent.sla_type 14 | api.spent sla_cache_spent.spent 15 | end 16 | end 17 | end if include_in_api_response?('sla_cache_spents') 18 | end -------------------------------------------------------------------------------- /app/views/sla_statuses/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l('sla_label.sla_status.plural'), sla_statuses_path, class: 'icon icon-cancel' %> 3 | <%= link_to l('sla_label.sla_status.edit'), edit_sla_status_path(@sla_status), 4 | title: l('sla_label.sla_status.edit'), class: 'icon icon-edit', 5 | :accesskey => accesskey(:edit) if @sla_status.editable? %> 6 | <%= link_to l('sla_label.sla_status.delete'), sla_status_path(@sla_status), 7 | data: {confirm: l(:text_are_you_sure)}, method: :delete, 8 | title: l('sla_label.sla_status.delete'), class: 'icon icon-del', 9 | :accesskey => accesskey(:delete) if @sla_status.deletable? %> 10 |
-------------------------------------------------------------------------------- /db/migrate/202111112021001_create_slas.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021001_create_slas.rb 2 | # Purpose: 3 | # Define the base "slas" table, which stores the main SLA entities used 4 | # by the plugin (name and unique constraint). Other SLA-related tables 5 | # reference this one. 6 | 7 | class CreateSlas < ActiveRecord::Migration[5.2] 8 | 9 | def change 10 | create_table :slas do |t| 11 | # SLA display name, required and unique across all SLAs 12 | t.text :name, null: false, index: { name: 'slas_name_ukey', unique: true } 13 | end 14 | 15 | # Migration log message shown when the table is created 16 | say "Created table slas" 17 | end 18 | 19 | end -------------------------------------------------------------------------------- /app/views/slas/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla], html: { multipart: false, id: 'sla-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla.safe_attribute?('name') %> 5 |

6 | <%= f.text_field :name, :required => true %> 7 |

8 | <% end %> 9 |
10 |

11 | <% if f.object.new_record? %> 12 | <%= submit_tag l('sla_label.sla.new'), title: l(:button_create), class: "button-positive" %> 13 | <% else %> 14 | <%= submit_tag l('sla_label.sla.save'), title: l(:button_update), class: "button-positive" %> 15 | <% end %> 16 |

17 | <% end %> 18 | -------------------------------------------------------------------------------- /app/views/sla_types/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_type], html: { multipart: false, id: 'sla-type-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_type.safe_attribute?('name') %> 5 |

6 | <%= f.text_field :name, :required => true %> 7 |

8 | <% end %> 9 |
10 |

11 | <% if f.object.new_record? %> 12 | <%= submit_tag l('sla_label.sla_type.new'), title: l(:button_create), class: "button-positive" %> 13 | <% else %> 14 | <%= submit_tag l('sla_label.sla_type.save'), title: l(:button_update), class: "button-positive" %> 15 | <% end %> 16 |

17 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_calendars/_sla_schedule_fields.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= f.label :dow %> 4 | <%= f.select :dow, I18n.t('date.day_names').map.with_index.to_h %> 5 | <%= f.label :start_time %> 6 | <%= f.time_field :start_time, value: (f.object.start_time.strftime("%H:%M") unless f.object.start_time.blank?), min: 'hh:mm', max: 'hh:mm' %> 7 | <%= f.label :end_time %> 8 | <%= f.time_field :end_time, value: (f.object.end_time.strftime("%H:%M") unless f.object.end_time.blank?), min: 'hh:mm', max: 'hh:mm' %> 9 | <%= f.label :match %> 10 | <%= f.check_box :match %> 11 |   12 | <%= f.hidden_field :_destroy %> 13 | <%= link_to_remove_fields "Remove", f %> 14 |
15 |
-------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar_holiday.plural'), sla_calendar_holidays_path, class: 'icon icon-cancel' %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar_holiday.show') %> 8 |
9 | 10 |

n°<%= @sla_calendar_holiday.sla_calendar.id %> : <%= @sla_calendar_holiday.sla_calendar.name %>

11 |

n°<%= @sla_calendar_holiday.sla_holiday.id %> : <%= @sla_calendar_holiday.sla_holiday.name %>

12 | 13 |
14 | <% content_for :sidebar do %> 15 | <% render partial: 'slas/sidebar' %> 16 | <% end %> -------------------------------------------------------------------------------- /db/migrate/202111112021007_create_sla_types.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021007_create_sla_types.rb 2 | # Purpose: 3 | # Create the `sla_types` table, which stores the different SLA categories/types. 4 | # These types can be used to group SLAs (e.g. "Response time", "Resolution time") 5 | # and may be referenced by other SLA-related entities. 6 | 7 | class CreateSlaTypes < ActiveRecord::Migration[5.2] 8 | 9 | def change 10 | create_table :sla_types do |t| 11 | 12 | # SLA type name, must be unique across all SLA types 13 | t.text :name, null: false, 14 | index: { name: 'sla_types_name_ukey', unique: true } 15 | end 16 | 17 | # Migration log output 18 | say "Created table sla_types" 19 | end 20 | 21 | end -------------------------------------------------------------------------------- /test/files/src_sla/slas.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sla_tma_bug: 3 | id: 1 4 | name: SLA TMA tracker_bug_request 5 | sla_tma_ask: 6 | id: 2 7 | name: SLA TMA tracker_support_request 8 | sla_info_std_ask: 9 | id: 3 10 | name: SLA Managed Services Standard tracker_change_request 11 | sla_info_std_bug: 12 | id: 4 13 | name: SLA Managed Services Standard tracker_production_incident 14 | sla_info_spe_ask: 15 | id: 5 16 | name: SLA Managed Services Spécific tracker_change_request 17 | sla_info_spe_bug: 18 | id: 6 19 | name: SLA Managed Services Spécific tracker_production_incident 20 | sla_tma_bug_scf: 21 | id: 7 22 | name: SLA TMA tracker_bug_request with Sla Custom Field 23 | sla_tma_ask_scf: 24 | id: 8 25 | name: SLA TMA tracker_support_request Sla Custom Field 26 | -------------------------------------------------------------------------------- /app/views/slas/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) %> 5 | <%= link_to l('sla_label.sla.index'), slas_path, 6 | class: 'icon icon-sla' if @sla.visible? %> 7 | <%= link_to l('sla_label.sla.edit'), edit_sla_path(@sla), 8 | title: l('sla_label.sla.edit'), class: 'icon icon-edit', 9 | :accesskey => accesskey(:edit) if @sla.editable? %> 10 | <%= link_to l('sla_label.sla.delete'), sla_path(@sla), 11 | data: {confirm: l(:text_are_you_sure)}, method: :delete, 12 | title: l('sla_label.sla.delete'), class: 'icon icon-del', 13 | :accesskey => accesskey(:delete) if @sla.deletable? %> 14 |
15 | -------------------------------------------------------------------------------- /app/views/slas/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= render :partial => 'action_menu' %> 5 |
6 |
7 | <%= title l('sla_label.sla_level_term.show') %> 8 |
9 | 10 |

n°<%= @sla_level_term.sla_level.id %> : <%= @sla_level_term.sla_level.name %>

11 |

n°<%= @sla_level_term.sla_type.id %> : <%= @sla_level_term.sla_type.name %>

12 | 13 |

<%= l(:field_sla_priority_id) %> : <%= @sla_level_term.sla_priority_id %>

14 |

<%= @sla_level_term.term %>

15 | 16 |
17 | <% content_for :sidebar do %> 18 | <% render partial: 'slas/sidebar' %> 19 | <% end %> -------------------------------------------------------------------------------- /doc/SETTINGS.md: -------------------------------------------------------------------------------- 1 | # Plugin settings 2 | 3 | The plugin settings are accessible on `http://[redmine-host]/settings/plugin/redmine_sla`. 4 | 5 | 6 | ## SLA Error logs 7 | 8 | Allows you to define the plugin's log generation level : 9 | - 0 (none) 10 | - 1 (error) 11 | - 2 (info) 12 | - 3 (debug) 13 | 14 | Default: 1 (Errors) 15 | 16 | Not use for the moment 17 | 18 | 19 | ## Time zone 20 | 21 | Globally change the timezone used in the calculation of SLAs. 22 | 23 | Default: Etc/UTC 24 | 25 | 26 | ## Display 27 | 28 | Allows you to choose between displaying the as a bar or as a pie chart. 29 | 30 | Default: bar 31 | 32 | 33 | ## SLA Cache management 34 | 35 | Allows you to define the cache refresh rate in minutes. 36 | 37 | Default: 1 (minute) 38 | 39 | But not use for the moment ! -------------------------------------------------------------------------------- /app/views/sla_cache_spents/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) -%> 5 | <% if User.current.admin? -%> 6 | <%= link_to l('sla_label.sla_level_term.edit'), sla_terms_sla_level_path(@sla_level_term.sla_level), 7 | title: l('sla_label.sla_level_term.edit'), 8 | class: 'icon icon-sla_level_term' -%> 9 | <%= link_to l('sla_label.sla_level_term.delete'), sla_level_term_path(@sla_level_term), 10 | data: {:confirm => l(:text_are_you_sure)}, :method => :delete, 11 | title: l('sla_label.sla_level_term.delete'), class: 'icon icon-del', 12 | :accesskey => accesskey(:delete) -%> 13 | <% end -%> 14 |
15 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= title l('sla_label.sla_level_term.index') %> 5 |
6 | <%= form_tag(sla_level_terms_path, method: :get, id: 'query_form') do %> 7 | <%= render partial: 'queries/query_form' %> 8 | <% end %> 9 | <% if @query.valid? %> 10 | <% if @entities.empty? %> 11 |

<%= l(:label_no_data) %>

12 | <% else %> 13 | <%= render_query_totals(@query) %> 14 | <%= render partial: 'list', locals: { entities: @entities }%> 15 | <%= pagination_links_full @entity_pages, @entity_count %> 16 | <% end %> 17 | <% end %> 18 |
19 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_types/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_settings_plugin/_sla_settings_plugin.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_plugin_settings)) %> 2 |

<%= l(:sla_label_plugin_settings) %>

3 | <% 4 | sla_tabs = [ 5 | { 6 | :name => 'general', 7 | :partial => 'sla_settings_plugin/sla_settings_plugin_logs', 8 | :label => 'sla_label.sla_log.error_logs' 9 | },{ 10 | :name => 'sla_zone', 11 | :partial => 'sla_settings_plugin/sla_settings_plugin_zone', 12 | :label => :field_time_zone 13 | },{ 14 | :name => 'sla_display', 15 | :partial => 'sla_settings_plugin/sla_settings_plugin_display', 16 | :label => :label_display 17 | },{ 18 | :name => 'sla_cache', 19 | :partial => 'sla_settings_plugin/sla_settings_plugin_cache', 20 | :label => 'sla_label.sla_cache.manage' 21 | } 22 | ] 23 | %> 24 | <%= render_tabs sla_tabs %> -------------------------------------------------------------------------------- /app/views/sla_levels/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_statuses/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_types/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) %> 5 | <%= link_to l('sla_label.sla_type.index'), sla_types_path, 6 | class: 'icon icon-sla_type' if @sla_type.visible? %> 7 | <%= link_to l('sla_label.sla_type.edit'), edit_sla_type_path(@sla_type), 8 | title: l('sla_label.sla_type.edit'), class: 'icon icon-edit', 9 | :accesskey => accesskey(:edit) if @sla_type.editable? %> 10 | <%= link_to l('sla_label.sla_type.delete'), sla_type_path(@sla_type), 11 | data: {confirm: l(:text_are_you_sure)}, method: :delete, 12 | title: l('sla_label.sla_type.delete'), class: 'icon icon-del', 13 | :accesskey => accesskey(:delete) if @sla_type.deletable? %> 14 |
15 | -------------------------------------------------------------------------------- /app/views/sla_holidays/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_calendars/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_schedules/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/sla_custom_fields_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaCustomFieldsHelper 20 | 21 | end -------------------------------------------------------------------------------- /db/migrate/202111112021004_create_sla_holidays.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021004_create_sla_holidays.rb 2 | # Purpose: 3 | # Create the `sla_holidays` table, which stores individual holiday definitions 4 | # used by SLA calendars. A holiday has a date and a name, and may later be 5 | # linked to SLA calendars to determine whether SLA time is active or paused 6 | # on that specific day. 7 | 8 | class CreateSlaHolidays < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | create_table :sla_holidays do |t| 12 | 13 | # Holiday date, must be unique across all entries 14 | t.date :date, null: false, 15 | index: { name: 'sla_holidays_date_ukey', unique: true } 16 | 17 | # Human-readable holiday name (e.g. "Christmas", "New Year") 18 | t.text :name, null: false 19 | end 20 | 21 | # Migration log output 22 | say "Created table sla_holidays" 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /app/views/sla_holidays/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) %> 5 | <% if User.current.admin? -%> 6 | <%= link_to l('sla_label.sla_holiday.index'), sla_holidays_path, 7 | title: l('sla_label.sla_holiday.index'), class: 'icon icon-sla_holiday' %> 8 | <%= link_to l('sla_label.sla_holiday.edit'), edit_sla_holiday_path(@sla_holiday), 9 | title: l('sla_label.sla_holiday.edit'), class: 'icon icon-edit', 10 | :accesskey => accesskey(:edit) %> 11 | <%= link_to l('sla_label.sla_holiday.delete'), sla_holiday_path(@sla_holiday), 12 | data: {:confirm => l(:text_are_you_sure)}, :method => :delete, 13 | title: l('sla_label.sla_holiday.delete'), class: 'icon icon-del', 14 | :accesskey => accesskey(:delete) %> 15 | <% end -%> 16 |
17 | -------------------------------------------------------------------------------- /app/views/sla_holidays/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_holiday], html: { multipart: false, id: 'sla-holiday-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_holiday.safe_attribute?('name') %> 5 |

6 | <%= f.text_field :name, :required => true %> 7 |

8 | <% end %> 9 | <% if sla_holiday.safe_attribute?('date') %> 10 |

11 | <%= f.date_field :date, :required => true %> 12 |

13 | <% end %> 14 |
15 | <% if !request.xhr? %> 16 |

17 | <% if f.object.new_record? %> 18 | <%= submit_tag l('sla_label.sla_holiday.new'), title: l(:button_create), class: "button-positive" %> 19 | <% else %> 20 | <%= submit_tag l('sla_label.sla_holiday.save'), title: l(:button_update), class: "button-positive" %> 21 | <% end %> 22 |

23 | <% end %> 24 | <% end %> -------------------------------------------------------------------------------- /app/views/sla_calendars/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) %> 5 | <% if User.current.admin? -%> 6 | <%= link_to l('sla_label.sla_calendar.index'), sla_calendars_path, 7 | title: l('sla_label.sla_calendar.index'), class: 'icon icon-sla_calendar' %> 8 | <%= link_to l('sla_label.sla_calendar.edit'), edit_sla_calendar_path(@sla_calendar), 9 | title: l('sla_label.sla_calendar.edit'), class: 'icon icon-edit', 10 | :accesskey => accesskey(:edit) %> 11 | <%= link_to l('sla_label.sla_calendar.delete'), sla_calendar_path(@sla_calendar), 12 | data: {:confirm => l(:text_are_you_sure)}, :method => :delete, 13 | title: l('sla_label.sla_calendar.delete'), class: 'icon icon-del', 14 | :accesskey => accesskey(:delete) %> 15 | <% end -%> 16 |
17 | -------------------------------------------------------------------------------- /app/views/slas/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla.new'), new_sla_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla.index') %> 8 |
9 | <%= form_tag(slas_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_types/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_type.new'), new_sla_type_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_type.index') %> 8 |
9 | <%= form_tag(sla_types_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_levels/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_level.new'), new_sla_level_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_level.index') %> 8 |
9 | <%= form_tag(sla_levels_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_statuses/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_status.new'), new_sla_status_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_status.index') %> 8 |
9 | <%= form_tag(sla_statuses_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /db/migrate/202111112021003_create_sla_calendars.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021003_create_sla_calendars.rb 2 | # Purpose: 3 | # Create the `sla_calendars` table, which stores SLA calendar definitions. 4 | # An SLA calendar defines the weekly time windows where SLA time can elapse. 5 | # Each calendar can be associated with multiple SLA schedules and levels. 6 | 7 | class CreateSlaCalendars < ActiveRecord::Migration[5.2] 8 | 9 | def change 10 | create_table :sla_calendars do |t| 11 | 12 | # Calendar name, must be unique across all calendars 13 | t.text :name, null: false, index: { name: 'sla_calendars_name_ukey', unique: true } 14 | 15 | # Standard timestamp fields used across the plugin 16 | t.datetime :created_on, null: false, default: -> { 'CURRENT_TIMESTAMP' } 17 | t.datetime :updated_on, null: false, default: -> { 'CURRENT_TIMESTAMP' } 18 | end 19 | 20 | # Rails migration log output 21 | say "Created table sla_calendars" 22 | end 23 | 24 | end -------------------------------------------------------------------------------- /app/views/sla_holidays/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_holiday.new'), new_sla_holiday_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_holiday.index') %> 8 |
9 | <%= form_tag(sla_holidays_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_calendars/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar.new'), new_sla_calendar_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar.index') %> 8 |
9 | <%= form_tag(sla_calendars_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_schedules/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_schedule.new'), new_sla_schedule_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_schedule.index') %> 8 |
9 | <%= form_tag(sla_schedules_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_caches/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/unit/sla_log_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaLogTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/unit/sla_holiday_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaHolidayTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/unit/sla_level_term_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaLevelTermTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/unit/sla_schedule_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaScheduleTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/unit/sla_view_journal_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaViewJournalTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_calendar_holiday.new'), new_sla_calendar_holiday_path, class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar_holiday.index') %> 8 |
9 | <%= form_tag(sla_calendar_holidays_path, method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /test/unit/sla_calendar_holiday_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaCalendarHolidayTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/unit/sla_project_tracker_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaProjectTrackerTest < ActiveSupport::TestCase 22 | 23 | test "the truth" do 24 | assert true 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /app/views/sla_project_trackers/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) %> 2 |
3 |
4 | <%= link_to l('sla_label.sla_project_tracker.new'), _new_sla_project_tracker_path(@project), class: 'icon icon-add' if User.current.allowed_to?(:manage_sla, nil, global: true) %> 5 |
6 |
7 | <%= title l('sla_label.sla_project_tracker.index') %> 8 |
9 | <%= form_tag(_sla_project_trackers_path(@project), method: :get, id: 'query_form') do %> 10 | <%= render partial: 'queries/query_form' %> 11 | <% end %> 12 | <% if @query.valid? %> 13 | <% if @entities.empty? %> 14 |

<%= l(:label_no_data) %>

15 | <% else %> 16 | <%= render_query_totals(@query) %> 17 | <%= render partial: 'list', locals: { entities: @entities }%> 18 | <%= pagination_links_full @entity_pages, @entity_count %> 19 | <% end %> 20 | <% end %> 21 |
22 | <%= render partial: 'slas/sidebar' -%> -------------------------------------------------------------------------------- /test/unit/sla_cache_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaCacheTest < ActiveSupport::TestCase 22 | 23 | test "#SlaCacheTest Count" do 24 | assert_not SlaCache.count(:all).zero? 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /app/helpers/sla_statuses_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaStatusesHelper 20 | 21 | # Generates a link to a sla_status with id 22 | def link_to_sla_status_id(sla_status) 23 | link_to sla_status.id, sla_status_url(sla_status, {:only_path => true}) 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /app/helpers/sla_schedules_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaSchedulesHelper 20 | 21 | # Generates a link to a sla_schedule 22 | def link_to_sla_schedule_id(sla_schedule) 23 | link_to sla_schedule.id, sla_schedule_url(sla_schedule, {:only_path => true}) 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /test/unit/sla_cache_spent_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaCacheSpentTest < ActiveSupport::TestCase 22 | 23 | test "#SlaCacheSpentTest Count" do 24 | assert_not SlaCacheSpent.count(:all).zero? 25 | end 26 | 27 | end -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 20 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/application_system_test_case') 21 | require File.expand_path(File.dirname(__FILE__) + '/test_helper') -------------------------------------------------------------------------------- /app/helpers/sla_cache_spents_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaCacheSpentsHelper 20 | 21 | def _sla_cache_spents_path(project, issue, *args) 22 | if project 23 | project_sla_cache_spents_path(project, *args) 24 | else 25 | sla_cache_spents_path(*args) 26 | end 27 | end 28 | 29 | end -------------------------------------------------------------------------------- /app/helpers/sla_level_terms_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaLevelTermsHelper 20 | 21 | # Generates a link to a sla_level_term with id 22 | def link_to_sla_level_term_id(sla_level_term) 23 | link_to sla_level_term.id, sla_level_term_url(sla_level_term, {:only_path => true}) 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /app/views/sla_levels/_action_menu.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to l(:button_back), :back, 3 | :title => l(:button_back), class: 'icon icon-cancel', 4 | :accesskey => accesskey(:previous) %> 5 | <% if User.current.admin? -%> 6 | <%= link_to l('sla_label.sla_level_term.edit'), sla_terms_sla_level_path(@sla_level), 7 | title: l('sla_label.sla_level_term.edit'), 8 | class: 'icon icon-sla_level_term' -%> 9 | <%= link_to l('sla_label.sla_level.index'), sla_levels_path, 10 | title: l('sla_label.sla_level.index'), class: 'icon icon-sla_level' %> 11 | <%= link_to l('sla_label.sla_level.edit'), edit_sla_level_path(@sla_level), 12 | title: l('sla_label.sla_level.edit'), class: 'icon icon-edit', 13 | :accesskey => accesskey(:edit) -%> 14 | <%= link_to l('sla_label.sla_level.delete'), sla_level_path(@sla_level), 15 | data: {:confirm => l(:text_are_you_sure)}, :method => :delete, 16 | title: l('sla_label.sla_level.delete'), class: 'icon icon-del', 17 | :accesskey => accesskey(:delete) -%> 18 | <% end -%> 19 |
20 | -------------------------------------------------------------------------------- /db/migrate/202111112021011_create_sla_get_date.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021011_create_sla_get_date.rb 2 | # Purpose: 3 | # Deploy the PostgreSQL function `sla_get_date`, which normalizes timestamps 4 | # according to the configured SLA timezone and truncates them to the minute. 5 | # The function is stored in `db/sql_functions/sla_get_date.sql` and loaded 6 | # using a reversible migration so the function can be cleanly removed on rollback. 7 | 8 | class CreateSlaGetDate < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | 12 | reversible do |dir| 13 | 14 | dir.up do 15 | # Execute the SQL function definition from the SQL file 16 | execute File.read( 17 | File.expand_path('../../sql_functions/sla_get_date.sql', __FILE__) 18 | ) 19 | say "Created function sla_get_date" 20 | end 21 | 22 | dir.down do 23 | # Remove the function if the migration is rolled back 24 | execute "DROP FUNCTION IF EXISTS public.sla_get_date CASCADE ;" 25 | say "Dropped function sla_get_date" 26 | end 27 | 28 | end 29 | 30 | end 31 | 32 | end -------------------------------------------------------------------------------- /test/assertion_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module AssertionHelpers 20 | 21 | def assert_select_not(selector, attributes = {}, message = nil) 22 | attribute_selector = attributes.map { |key, value| "[#{key}=\"#{value}\"]" }.join 23 | assert_select "#{selector}#{attribute_selector}", 0, message 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/unit/sla_type_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaTypeTest < ActiveSupport::TestCase 22 | 23 | test "should not save SlaType without name" do 24 | sla_type = SlaType.new 25 | assert_not sla_type.save, "Saved the SlaType without name" 26 | end 27 | 28 | end -------------------------------------------------------------------------------- /app/models/sla_log.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | class SlaLog < ActiveRecord::Base 20 | 21 | belongs_to :project 22 | belongs_to :issue 23 | belongs_to :sla_leveldeveloper 24 | 25 | after_initialize :set_default_sla_log_level, :if => :new_record? 26 | 27 | def set_default_sla_log_level 28 | self.role ||= :none 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /app/helpers/sla_calendar_holidays_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaCalendarHolidaysHelper 20 | 21 | # Generates a link to a sla_calendar_holidaywith id 22 | def link_to_sla_calendar_holiday_id(sla_calendar_holiday) 23 | link_to sla_calendar_holiday.id, sla_calendar_holiday_url(sla_calendar_holiday, {:only_path => true}) 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /app/views/sla_project_trackers/context_menu.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /db/migrate/202111112021013_create_sla_view_journal_statuses.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021013_create_sla_view_journal_statuses.rb 2 | # Purpose: 3 | # Deploy the SQL view `sla_view_journal_statuses`, which normalizes issue 4 | # status changes extracted from Redmine journals. The view definition is 5 | # stored in `db/sql_views/sla_view_journal_statuses.sql` and loaded using 6 | # a reversible migration so it can be dropped cleanly on rollback. 7 | 8 | class CreateSlaViewJournalStatuses < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | 12 | reversible do |dir| 13 | 14 | dir.up do 15 | # Create the SQL view from the dedicated SQL file 16 | execute File.read( 17 | File.expand_path('../../sql_views/sla_view_journal_statuses.sql', __FILE__) 18 | ) 19 | say "Created view sla_view_journal_statuses" 20 | end 21 | 22 | dir.down do 23 | # Drop the view when rolling back the migration 24 | execute "DROP VIEW IF EXISTS public.sla_view_journal_statuses CASCADE ;" 25 | say "Dropped view sla_view_journal_statuses" 26 | end 27 | 28 | end 29 | 30 | end 31 | 32 | end -------------------------------------------------------------------------------- /db/migrate/202111112021017_create_sla_view_roll_statuses.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021017_create_sla_view_roll_statuses.rb 2 | # Purpose: 3 | # Deploy the SQL view `sla_view_roll_statuses`, which rebuilds issue status 4 | # intervals (from/to status + dates) from the normalized journal data. 5 | # The view definition is stored in `db/sql_views/sla_view_roll_statuses.sql` 6 | # and is loaded in a reversible way so it can be cleanly dropped on rollback. 7 | 8 | class CreateSlaViewRollStatuses < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | 12 | reversible do |dir| 13 | 14 | dir.up do 15 | # Create the SQL view from the dedicated SQL file 16 | execute File.read( 17 | File.expand_path('../../sql_views/sla_view_roll_statuses.sql', __FILE__) 18 | ) 19 | say "Created view sla_view_roll_statuses" 20 | end 21 | 22 | dir.down do 23 | # Drop the view when rolling back the migration 24 | execute "DROP VIEW IF EXISTS public.sla_view_roll_statuses CASCADE ;" 25 | say "Dropped view sla_view_roll_statuses" 26 | end 27 | 28 | end 29 | 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /app/helpers/slas_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlasHelper 20 | 21 | # Generates a link to a sla with id 22 | def link_to_sla_id(sla) 23 | link_to sla.id, sla_url(sla, {:only_path => true}) 24 | end 25 | 26 | # Generates a link to a sla with name 27 | def link_to_sla_name(sla) 28 | link_to sla.name, sla_url(sla, {:only_path => true}) 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /test/unit/sla_status_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaStatusTest < ActiveSupport::TestCase 22 | 23 | test "should not save SlaStatus without status_id, sla_type_id" do 24 | sla_status = SlaStatus.new 25 | assert_not sla_status.save, "Saved the SlaStatus without status_id, sla_type_id" 26 | end 27 | 28 | 29 | end -------------------------------------------------------------------------------- /test/helpers/sla_caches_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | # require File.expand_path('../../../application_system_test_case', __FILE__) 20 | 21 | # class SlasSystemTest < ApplicationSystemTestCase 22 | module SlaCachesHelperTest 23 | 24 | def renew_issue(issue_id) 25 | issue = Issue.find(issue_id) 26 | SlaType.all.each { |sla_type| 27 | issue.get_sla_spent(sla_type.id) 28 | } 29 | end 30 | end -------------------------------------------------------------------------------- /app/models/sla_view_journal.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | class SlaViewJournal < ActiveRecord::Base 20 | 21 | belongs_to :issue 22 | belongs_to :to_status_, :class_name => 'issues_statuses', :foreign_key => 'to_status_id' 23 | belongs_to :from_status_, :class_name => 'issues_statuses', :foreign_key => 'from_status_id' 24 | 25 | after_initialize :readonly! 26 | 27 | def readonly? 28 | true 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /app/helpers/sla_types_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaTypesHelper 20 | 21 | # Generates a link to a sla_type with id 22 | def link_to_sla_type_id(sla_type) 23 | link_to sla_type.id, sla_type_url(sla_type, {:only_path => true}) 24 | end 25 | 26 | # Generates a link to a sla_type with name 27 | def link_to_sla_type_name(sla_type) 28 | link_to sla_type.name, sla_type_url(sla_type, {:only_path => true}) 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /app/helpers/queries/slas_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlasQueriesHelper 21 | include ApplicationHelper 22 | include SlasHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(Sla) 26 | case column.name 27 | when :id 28 | link_to_sla_id(item) 29 | else 30 | super 31 | end 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/sla_levels_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaLevelsHelper 20 | 21 | # Generates a link to a sla_level with id 22 | def link_to_sla_level_id(sla_level) 23 | link_to sla_level.id, sla_level_url(sla_level, {:only_path => true}) 24 | end 25 | 26 | # Generates a link to a sla_level with name 27 | def link_to_sla_level_name(sla_level) 28 | link_to sla_level.name, sla_level_url(sla_level, {:only_path => true}) 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /db/migrate/202111112021006_create_sla_schedules.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021006_create_sla_schedules.rb 2 | # Purpose: 3 | # Create the `sla_schedules` table, which defines daily schedules for SLA 4 | # calendars. A schedule specifies on which weekday (0–6) and during which 5 | # time range the SLA is active. These schedules are later matched against 6 | # issue timestamps to determine whether SLA time should accumulate. 7 | 8 | class CreateSlaSchedules < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | create_table :sla_schedules do |t| 12 | 13 | # Reference to the parent SLA calendar 14 | t.integer :sla_calendar_id, null: false 15 | 16 | # Day of the week: 0 = Sunday, 1 = Monday, ... 6 = Saturday 17 | t.integer :dow, null: false 18 | 19 | # Start and end times for the active SLA window during the specified weekday 20 | t.time :start_time, null: false 21 | t.time :end_time, null: false 22 | 23 | # Whether this schedule should be considered a match (true) or not (false) 24 | # Used to handle exceptional schedules or overrides 25 | t.boolean :match, null: false, default: true 26 | end 27 | 28 | # Migration log output 29 | say "Created table sla_schedules" 30 | end 31 | 32 | end -------------------------------------------------------------------------------- /lib/redmine_sla/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/lib/redmine_sla/version.rb 4 | # Purpose: 5 | # Declare the plugin version constant for Redmine SLA. 6 | # This value is used by Redmine to display plugin information 7 | # and to check compatibility or migration versions. 8 | 9 | # Redmine SLA - Redmine Plugin 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 24 | 25 | module RedmineSla 26 | # Current plugin version. 27 | # This is the value displayed in Redmine's plugin administration screen. 28 | Version = '2.0.0'.freeze 29 | end -------------------------------------------------------------------------------- /app/helpers/queries/sla_types_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaTypesQueriesHelper 21 | include ApplicationHelper 22 | include SlaTypesHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(SlaType) 26 | case column.name 27 | when :id 28 | link_to_sla_type_id(item) 29 | else 30 | super 31 | end 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_holidays_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaHolidaysQueriesHelper 21 | include ApplicationHelper 22 | include SlaHolidaysHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(SlaHoliday) 26 | case column.name 27 | when :id 28 | link_to_sla_holiday_id(item) 29 | else 30 | super 31 | end 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/sla_holidays_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaHolidaysHelper 20 | 21 | # Generates a link to a sla_holiday with name 22 | def link_to_sla_holiday_id(sla_holiday) 23 | link_to sla_holiday.id, sla_holiday_url(sla_holiday, {:only_path => true}) 24 | end 25 | 26 | # Generates a link to a sla_holiday with name 27 | def link_to_sla_holiday_name(sla_holiday) 28 | link_to sla_holiday.name, sla_holiday_url(sla_holiday, {:only_path => true}) 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /app/views/sla_calendars/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= javascript_include_tag :redmine_sla, :plugin => 'redmine_sla' %> 2 | <%= form_with model: @sla_calendar, id: 'sla-calendar-form', local: true do |sla_calendar_form| %> 3 | <%= error_messages_for @sla_calendar %> 4 | <%= error_messages_for @sla_schedule %> 5 |
6 | <%= l('sla_label.sla_calendar.singular') %> 7 |
8 | <%= sla_calendar_form.label :name, :for => "sla_calendar_name"%> * 9 | <%= sla_calendar_form.text_field :name, :id => "sla_calendar_name", :required => true %> 10 |
11 |
12 | <%= l('sla_label.sla_schedule.plural') %> 13 | <%= sla_calendar_form.fields_for :sla_schedules do |sla_schedules_form| %> 14 | <%= render "sla_schedule_fields", f: sla_schedules_form %> 15 | <% end %> 16 | <%= link_to_add_fields "Add Schedule", sla_calendar_form, :sla_schedules %> 17 |
18 |
19 | <% if sla_calendar_form.object.new_record? %> 20 | <%= submit_tag l('sla_label.sla_calendar.new'), title: l(:button_create), class: "button-positive" %> 21 | <% else %> 22 | <%= submit_tag l('sla_label.sla_calendar.save'), title: l(:button_update), class: "button-positive" %> 23 | <% end %> 24 | <% end %> -------------------------------------------------------------------------------- /app/helpers/queries/sla_cache_spents_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaCacheSpentsQueriesHelper 21 | include ApplicationHelper 22 | include SlaTypesHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(SlaCacheSpent) 26 | case column.name 27 | when :id 28 | item.id 29 | when :sla_type 30 | link_to_sla_type_name(item.sla_type) 31 | else 32 | super 33 | end 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_calendars_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaCalendarsQueriesHelper 21 | include ApplicationHelper 22 | include SlaCalendarsHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(SlaCalendar) 26 | case column.name 27 | when :id 28 | link_to_sla_calendar_id(item) 29 | when :name 30 | link_to_sla_calendar_name(item) 31 | else 32 | super 33 | end 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_statuses_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaStatusesQueriesHelper 21 | include ApplicationHelper 22 | include SlaTypesHelper 23 | 24 | def column_value(column, item, value) 25 | if item.is_a?(SlaStatus) 26 | case column.name 27 | when :id 28 | link_to_sla_status_id(item) 29 | when :sla_type 30 | link_to_sla_type_name(item.sla_type) 31 | else 32 | super 33 | end 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/views/sla_calendars/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) -%> 2 |
3 |
4 | <%= render :partial => 'action_menu' %> 5 |
6 |
7 | <%= title l('sla_label.sla_calendar.show') -%> 8 |
9 | 10 |

n°<%= @sla_calendar.id -%> : <%= @sla_calendar.name -%>

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% SlaSchedule.where(sla_calendar: @sla_calendar).each do |sla_schedule| -%> 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% end -%> 27 |
<%= l("sla_dictionary.dow") -%><%= l("sla_dictionary.start_time") -%><%= l("sla_dictionary.end_time") -%><%= l("sla_dictionary.match") -%>
<%= I18n.t('date.day_names')[sla_schedule.dow] -%><%= sla_schedule.start_time.strftime('%H:%M') -%><%= sla_schedule.end_time.strftime('%H:%M') -%><%= sla_schedule.match ? "X" : "" -%>
28 | 29 |
30 | <% content_for :sidebar do -%> 31 | <% render partial: 'slas/sidebar' -%> 32 | <% end -%> -------------------------------------------------------------------------------- /db/migrate/202111112021005_create_sla_calendar_holidays.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021005_create_sla_calendar_holidays.rb 2 | # Purpose: 3 | # Create the join table `sla_calendar_holidays`, which associates holidays 4 | # with SLA calendars. This defines which holidays apply to which calendars, 5 | # and whether a holiday is considered as matching (SLA active) or non-matching 6 | # (SLA paused) in calendar calculations. 7 | 8 | class CreateSlaCalendarHolidays < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | create_table :sla_calendar_holidays do |t| 12 | 13 | # Link to the SLA calendar 14 | t.integer :sla_calendar_id, null: false 15 | 16 | # Link to the holiday definition 17 | t.integer :sla_holiday_id, null: false 18 | 19 | # Whether the holiday is considered as a matching day 20 | # (true = SLA counts time on this holiday, false = SLA stops) 21 | t.boolean :match, null: false, default: false 22 | end 23 | 24 | # Index to ensure that a holiday is not assigned twice to the same calendar 25 | add_index :sla_calendar_holidays, 26 | [:sla_calendar_id, :sla_holiday_id], 27 | unique: true, 28 | name: 'sla_calendar_holiday_ukey' 29 | 30 | # Migration log entry 31 | say "Created table sla_calendar_holidays with unique index sla_calendar_holiday_ukey" 32 | end 33 | 34 | end -------------------------------------------------------------------------------- /app/views/sla_calendar_holidays/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_calendar_holiday], html: { multipart: false, id: 'sla-calendar-holiday-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_calendar_holiday.safe_attribute?('sla_calendar_id') %> 5 |

6 | <%= f.select :sla_calendar_id, SlaCalendar.all.collect { |p| [ p.name, p.id ] }, :required => true, :include_blank => (@sla_calendar_holiday.new_record??true:false) %> 7 |

8 | <% end %> 9 | <% if sla_calendar_holiday.safe_attribute?('sla_holiday_id') %> 10 |

11 | <%= f.select :sla_holiday_id, SlaHoliday.all.collect { |p| [ p.name, p.id ] }, :required => true, :include_blank => (@sla_calendar_holiday.new_record??true:false) %> 12 |

13 | <% end %> 14 | <% if sla_calendar_holiday.safe_attribute?('match') %> 15 |

16 | <%= f.check_box :match, :required => true %> 17 |

18 | <% end %> 19 |
20 | <% if !request.xhr? %> 21 |

22 | <% if f.object.new_record? %> 23 | <%= submit_tag l("sla_label.sla_calendar_holiday.new"), title: l(:button_create), class: "button-positive" %> 24 | <% else %> 25 | <%= submit_tag l("sla_label.sla_calendar_holiday.save"), title: l(:button_update), class: "button-positive" %> 26 | <% end %> 27 |

28 | <% end %> 29 | <% end %> 30 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_project_trackers_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaProjectTrackersQueriesHelper 21 | include ApplicationHelper 22 | include SlaProjectTrackersHelper 23 | include SlasHelper 24 | 25 | def column_value(column, item, value) 26 | if item.is_a?(SlaProjectTracker) 27 | case column.name 28 | when :id 29 | link_to_sla_project_tracker_id(item) 30 | when :sla 31 | link_to_sla_name(item.sla) 32 | else 33 | super 34 | end 35 | end 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_schedules_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaSchedulesQueriesHelper 21 | include ApplicationHelper 22 | include SlaSchedulesHelper 23 | include SlaCalendarsHelper 24 | 25 | def column_value(column, item, value) 26 | if item.is_a?(SlaSchedule) 27 | case column.name 28 | when :id 29 | link_to_sla_schedule_id(item) 30 | when :sla_calendar 31 | link_to_sla_calendar_name(item.sla_calendar) 32 | else 33 | super 34 | end 35 | end 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /app/views/sla_schedules/show.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title(l(:sla_label_global_settings)) -%> 2 |
3 |
4 | <% if User.current.admin? -%> 5 | <%= link_to l('sla_label.sla_schedule.plural'), sla_schedules_path, class: 'icon icon-cancel' -%> 6 | <% else -%> 7 | <%= link_to l(:button_back), :back, :class => 'button', :title => l(:button_back) -%> 8 | <% end -%> 9 |
10 |
11 | <%= title l('sla_label.sla_schedule.show') -%> 12 |
13 | 14 |

n°<%= @sla_schedule.id -%>

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
<%= l("sla_dictionary.dow") -%><%= l("sla_dictionary.start_time") -%><%= l("sla_dictionary.end_time") -%><%= l("sla_dictionary.match") -%>
<%= I18n.t('date.day_names')[@sla_schedule.dow] -%><%= @sla_schedule.start_time.strftime('%H:%M') -%><%= @sla_schedule.end_time.strftime('%H:%M') -%><%= @sla_schedule.match ? "X" : "" -%>
30 | 31 |
32 | <% content_for :sidebar do -%> 33 | <% render partial: 'slas/sidebar' -%> 34 | <% end -%> -------------------------------------------------------------------------------- /app/helpers/queries/sla_levels_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaLevelsQueriesHelper 21 | include ApplicationHelper 22 | include SlaCalendarsHelper 23 | include SlasHelper 24 | 25 | def column_value(column, item, value) 26 | if item.is_a?(SlaLevel) 27 | case column.name 28 | when :id 29 | link_to_sla_level_id(item) 30 | when :sla 31 | link_to_sla_name(item.sla) 32 | when :sla_calendar 33 | link_to_sla_calendar_name(item.sla_calendar) 34 | else 35 | super 36 | end 37 | end 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /test/files/src_redmine/custom_field_enumerations.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Minor: 3 | id: 1 4 | custom_field_id: 1 5 | name: Minor 6 | active: true 7 | position: 0 8 | Major: 9 | id: 2 10 | custom_field_id: 1 11 | name: Major 12 | active: true 13 | position: 1 14 | Blocking: 15 | id: 3 16 | custom_field_id: 1 17 | name: Blocking 18 | active: true 19 | position: 2 20 | Immediate: 21 | id: 4 22 | custom_field_id: 1 23 | name: Immediate 24 | active: false 25 | position: 3 26 | Item1for2: 27 | id: 5 28 | custom_field_id: 2 29 | name: Item1 30 | active: true 31 | position: 0 32 | Item2for2: 33 | id: 6 34 | custom_field_id: 2 35 | name: Item2 36 | active: true 37 | position: 1 38 | Item3for2: 39 | id: 7 40 | custom_field_id: 2 41 | name: Item3 42 | active: true 43 | position: 2 44 | Item1for3: 45 | id: 8 46 | custom_field_id: 3 47 | name: Item1 48 | active: true 49 | position: 0 50 | Item2for3: 51 | id: 9 52 | custom_field_id: 3 53 | name: Item2 54 | active: true 55 | position: 1 56 | Item3for3: 57 | id: 10 58 | custom_field_id: 3 59 | name: Item3 60 | active: true 61 | position: 2 62 | Item1for5: 63 | id: 11 64 | custom_field_id: 5 65 | name: Item1 66 | active: true 67 | position: 0 68 | Item2for5: 69 | id: 12 70 | custom_field_id: 5 71 | name: Item2 72 | active: true 73 | position: 1 74 | Item3for5: 75 | id: 13 76 | custom_field_id: 5 77 | name: Item3 78 | active: true 79 | position: 2 -------------------------------------------------------------------------------- /test/files/src_redmine/email_addresses.yml: -------------------------------------------------------------------------------- 1 | --- 2 | email_address_admin: 3 | id: 1 4 | user_id: 1 5 | address: redmine.admin@localhost.localdomain 6 | is_default: true 7 | notify: true 8 | created_on: 2021-11-11 11:11:11 +02:00 9 | created_on: 2021-11-11 11:11:11 +02:00 10 | email_address_manager: 11 | id: 2 12 | user_id: 2 13 | address: redmine.manager@localhost.localdomain 14 | is_default: true 15 | notify: true 16 | created_on: 2021-11-11 11:11:11 +02:00 17 | created_on: 2021-11-11 11:11:11 +02:00 18 | email_address_developer: 19 | id: 3 20 | user_id: 3 21 | address: redmine.developer@localhost.localdomain 22 | is_default: true 23 | notify: true 24 | created_on: 2021-11-11 11:11:11 +02:00 25 | created_on: 2021-11-11 11:11:11 +02:00 26 | email_address_sysadmin: 27 | id: 4 28 | user_id: 4 29 | address: redmine.sysadmin@localhost.localdomain 30 | is_default: true 31 | notify: true 32 | created_on: 2021-11-11 11:11:11 +02:00 33 | created_on: 2021-11-11 11:11:11 +02:00 34 | email_address_reporter: 35 | id: 5 36 | user_id: 5 37 | address: redmine.reporter@localhost.localdomain 38 | is_default: true 39 | notify: true 40 | created_on: 2021-11-11 11:11:11 +02:00 41 | created_on: 2021-11-11 11:11:11 +02:00 42 | email_address_other: 43 | id: 6 44 | user_id: 6 45 | address: redmine.other@localhost.localdomain 46 | is_default: true 47 | notify: true 48 | created_on: 2021-11-11 11:11:11 +02:00 49 | created_on: 2021-11-11 11:11:11 +02:00 -------------------------------------------------------------------------------- /app/helpers/sla_caches_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaCachesHelper 20 | 21 | def _sla_caches_path(project, *args) 22 | if project 23 | project_sla_caches_path(project, *args) 24 | else 25 | sla_caches_path(*args) 26 | end 27 | end 28 | 29 | def render_api_includes(sla_cache, api) 30 | api.array :sla_cache_spents do 31 | sla_cache.sla_cache_spents.each do |sla_cache_spent| 32 | api.sla_cache_spent( 33 | :id => sla_cache_spent.id, 34 | :sla_type => sla_cache_spent.sla_type, 35 | :spent => sla_cache_spent.spent 36 | ) 37 | end 38 | end if include_in_api_response?('sla_cache_spents') 39 | end 40 | 41 | end -------------------------------------------------------------------------------- /app/views/sla_statuses/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_status], html: { multipart: false, id: 'sla-status-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_status.safe_attribute?('sla_type_id') %> 5 |

6 | <% if SlaType.all.blank? then %> 7 | <%= l('sla_label.sla_type.miss') %> 8 | <% else %> 9 | <%= f.select :sla_type_id, SlaType.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 10 | <% end %> 11 |

12 | <% end %> 13 | <% if sla_status.safe_attribute?('status_id') %> 14 |

15 | <% if IssueStatus.all.blank? then %> 16 | <%= l(:error_no_default_issue_status) %> 17 | <% else %> 18 | <%= f.select :status_id, IssueStatus.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 19 | <% end %> 20 |

21 | <% end %> 22 |
23 |

24 | <% if f.object.new_record? %> 25 | <%= submit_tag l('sla_label.sla_status.new'), title: l(:button_create), class: "button-positive" %> 26 | <% else %> 27 | <%= submit_tag l('sla_label.sla_status.save'), title: l(:button_update), class: "button-positive" %> 28 | <% end %> 29 |

30 | <% end %> 31 | -------------------------------------------------------------------------------- /test/unit/sla_calendar_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaCalendarTest < ActiveSupport::TestCase 22 | 23 | test "Just initialize" do 24 | sla_calendar = SlaCalendar.new 25 | assert_nil sla_calendar.name 26 | end 27 | 28 | test "should not save SlaCalendar without name" do 29 | sla_calendar = SlaCalendar.new 30 | assert_not sla_calendar.save, "Saved the SlaCalendar without name" 31 | end 32 | 33 | test "should save SlaCalendar with name" do 34 | sla_calendar = SlaCalendar.new 35 | sla_calendar.name = "SlaCalendar Test !" 36 | assert sla_calendar.save, "Saved the SlaCalendar with name" 37 | end 38 | 39 | end -------------------------------------------------------------------------------- /app/helpers/queries/sla_calendar_holidays_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc.SlaCalendarHolidayFranklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaCalendarHolidaysQueriesHelper 21 | include ApplicationHelper 22 | include SlaHolidaysHelper 23 | include SlaCalendarsHelper 24 | 25 | def column_value(column, item, value) 26 | if item.is_a?(SlaCalendarHoliday) 27 | case column.name 28 | when :id 29 | link_to_sla_calendar_holiday_id(item) 30 | when :sla_calendar 31 | link_to_sla_calendar_name(item.sla_calendar) 32 | when :sla_holiday 33 | link_to_sla_holiday_name(item.sla_holiday) 34 | else 35 | super 36 | end 37 | end 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /doc/MCD.md: -------------------------------------------------------------------------------- 1 | # Conceptual Data Model 2 | 3 | ## Overview 4 | 5 | During installation, thirteen tables are created : 6 | - slas : all SLA 7 | - sla_calendars : a calendar for SLA 8 | - sla_holidays : list of holidays 9 | - sla_calendar_holidays : allocation of holidays to calendars 10 | - sla_schedules : hourly covers 11 | - sla_types : SLA types (ex: response time guarantee or resolution time guarantee) 12 | - sla_levels : Name of a SLA level 13 | - sla_level_terms : Terms of SLA relative to SLA level, SLA type and priority_id 14 | - sla_project_trackers : SLA level to apply to a tracker of a project 15 | - sla_statuses : list of active statuses for a SLA level 16 | - sla_caches : caching of the SLA level of the issues (obtained with "sla_get_level") 17 | - sla_cache_spents : caching spent of issues (obtained with "sla_get_spent") 18 | - sla_logs : not yet used (even if the loglevel can be set in the plugin configuration) 19 | 20 | With three functions : 21 | - sla_get_date : adjusts a date according to the plugin's time zone 22 | - sla_get_level : identifies the start date of the SLA and the associated level 23 | - sla_get_spent : the heart of the plugin responsible for calculating the times spent 24 | 25 | And two views : 26 | - sla_view_journal_statuses : simplified the selection of journals for statuses 27 | - sla_view_roll_statuses : rebuilds issues statuses journal from its creation to "now" 28 | 29 | ## Drawing 30 |
31 | 32 | ![Redmine SLA - MCD](images/mcd/redmine_sla_mcd.png) 33 |
34 | 35 | ## Comments 36 | ... -------------------------------------------------------------------------------- /app/helpers/queries/sla_caches_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/app/helpers/queries/sla_caches_queries_helper.rb 4 | # Redmine - project management software 5 | # Copyright (C) 2006- Jean-Philippe Lang 6 | # 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License 9 | # as published by the Free Software Foundation; either version 2 10 | # of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | module Queries::SlaCachesQueriesHelper 22 | include ApplicationHelper 23 | include SlaLevelsHelper 24 | 25 | def column_value(column, item, value) 26 | if item.is_a?(SlaCache) 27 | case column.name 28 | when :id 29 | item.id 30 | when :sla_level 31 | link_to_sla_level_name(item.sla_level) 32 | when /^(?:issue\.|time_entry\.|sla_cache\.|sla_caches\.)?get_sla_respect/ 33 | sla_respect_icon_tag(item.send(column.name)) 34 | else 35 | super 36 | end 37 | end 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /test/unit/sla_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require File.expand_path('../../test_helper', __FILE__) 20 | 21 | class SlaTest < ActiveSupport::TestCase 22 | 23 | include Redmine::I18n 24 | 25 | def setup 26 | User.current = nil 27 | set_language_if_valid 'en' 28 | end 29 | 30 | def teardown 31 | User.current = nil 32 | end 33 | 34 | test "Just initialize" do 35 | sla = Sla.new 36 | assert_nil sla.name 37 | end 38 | 39 | test "should not save Sla without name" do 40 | sla = Sla.new 41 | assert_not sla.save, "Saved the Sla without name" 42 | end 43 | 44 | test "should save Sla with name" do 45 | sla = Sla.new 46 | sla.name = "Sla Test !" 47 | assert sla.save, "Saved the Sla with name" 48 | end 49 | 50 | end -------------------------------------------------------------------------------- /app/views/sla_levels/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_level], html: { multipart: false, id: 'sla-level-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_level.safe_attribute?('name') %> 5 |

6 | <%= f.text_field :name, :required => true %> 7 |

8 | <% end %> 9 | <% if sla_level.safe_attribute?('sla_id') %> 10 |

11 | <%= f.select :sla_id, Sla.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 12 |

13 | <% end %> 14 | <% if sla_level.safe_attribute?('sla_calendar_id') %> 15 |

16 | <%= f.select :sla_calendar_id, SlaCalendar.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 17 |

18 | <% end %> 19 | <% if sla_level.safe_attribute?('custom_field_id') %> 20 |

21 | <%= f.select :custom_field_id, SlaCustomField.all.collect { |p| [ p.name, p.id ] }, :include_blank => true, :required => false %> 22 |

23 | <% end %> 24 | 25 |
26 | 27 | <% if !request.xhr? %> 28 |

29 | <% if f.object.new_record? %> 30 | <%= submit_tag l('sla_label.sla_level.new'), title: l(:button_create), class: "button-positive" %> 31 | <% else %> 32 | <%= submit_tag l('sla_label.sla_level.save'), title: l(:button_update), class: "button-positive" %> 33 | <% end %> 34 |

35 | <% end %> 36 | <% end %> 37 | -------------------------------------------------------------------------------- /db/migrate/202111112021012_create_sla_statuses.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021012_create_sla_statuses.rb 2 | # Purpose: 3 | # Create the `sla_statuses` table, which links Redmine issue statuses to SLA 4 | # types. This mapping is used to determine, for each status, how it should 5 | # behave from an SLA perspective (for example: count time, pause, stop, etc., 6 | # depending on the associated SLA type). 7 | 8 | class CreateSlaStatuses < ActiveRecord::Migration[5.2] 9 | 10 | def change 11 | create_table :sla_statuses do |t| 12 | 13 | # Reference to the Redmine issue status (from `issue_statuses` table) 14 | t.belongs_to :status, 15 | references: :IssueStatuses, 16 | foreign_key: { 17 | name: 'sla_statuses_issue_statuses_fkey', 18 | on_delete: :cascade, 19 | to_table: :issue_statuses 20 | } 21 | 22 | # Reference to the SLA type used to categorize the behavior of this status 23 | t.belongs_to :sla_type, 24 | foreign_key: { 25 | name: 'sla_statuses_sla_types_fkey', 26 | on_delete: :cascade 27 | } 28 | end 29 | 30 | # Migration log message (kept as-is, even if the wording mentions "sla_level_terms") 31 | say "Created table sla_level_terms" 32 | 33 | # Ensure that a given (status_id, sla_type_id) pair is unique 34 | add_index :sla_statuses, 35 | [:status_id, :sla_type_id], 36 | unique: true, 37 | name: 'sla_statuses_ukey' 38 | say "Created index unique sla_statuses_ukey" 39 | 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /doc/API.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | Some of the SLA data is exposed through Redmine's REST API for the resources described below. The API supports both XML and JSON formats. 4 | 5 | > **_NOTE:_** for each example, we use the command `curl`. The `TRACKER` variable contains the Redmine URL and the `APIKEY` variable the API key available from each user's account (if the REST API is enabled in the configuration). 6 | 7 | | Resources | Status | Notes | Availability | 8 | |------------------------|--------|----------------|--------------| 9 | | [Slas](API/API_slas.md) | Stable | | 1.0 | 10 | | [Sla Types](API/API_sla_types.md) | Stable | | 1.0 | 11 | | [Sla Statuses](API/API_sla_statuses.md) | Stable | | 1.0 | 12 | | [Sla Schedules](API/API_sla_schedules.md) | Stable | | 1.0 | 13 | | [Sla Holidays](API/API_sla_holidays.md) | Stable | | 1.0 | 14 | | [Sla Calendar Holildays](API/API_sla_calendar_holidays.md) | Stable | | 1.0 | 15 | | [Sla Levels](API/API_sla_levels.md) | Stable | | 1.0 | 16 | | [Sla Level Terms](API/API_sla_level_terms.md) | Stable | | 1.0 | 17 | | [Sla Project Trackers](API/API_sla_project_trackers.md) | Stable | | 1.0 | 18 | | [Sla Caches](API/API_sla_caches.md) | Stable | | 1.0 | 19 | | [Sla Cache Spents](API/API_sla_cache_spents.md) | Stable | | 1.0 | 20 | -------------------------------------------------------------------------------- /doc/USECASE.md: -------------------------------------------------------------------------------- 1 | # Step-by-step use case 2 | 3 | These examples are present in the web interface after the launch of the functional tests : 4 | - TMA Support : Support request on hours worked (HO) with GTI only 5 | - TMA Bug : tracker bug on hours worked (HO) with GTI/GTR 6 | - "Managed Services Standard" and "Managed Services Spécific" : for change request on hours worked (HO) and for production incident also on non-working hours (HNO). 7 | 8 | 9 | ## Connctions 10 | 11 | Differents users are configured : 12 | - user `admin` with pass `admin` 13 | - user `manager` with pass `manager` 14 | - user `developer` with pass `developer` 15 | - user `reporter` with pass `reporter` 16 | 17 | 18 | ## Redmine base 19 | 20 | For each user an eponymous role has been defined 21 | - role `admin` : he can define the SLAs in the global configuration. 22 | - role `manager` : he sees the SLAs and manages them in the projects. 23 | - role `developer` : it only sees SLAs in issues. 24 | - role `reporter` : he doesn't see the SLAs nowhere. 25 | 26 | A few issue priorities are defined in enumerations : low, normal and high. Currently, only "normal" priority is used in tests. 27 | 28 | 29 | ## Simple case with only HO 30 | 31 | Typically, in TMA, commitments are in working hours. Outside of these hours, weekends and public holidays, past times are suspended. 32 | 33 | 34 | ## Complexe whith NHO 35 | 36 | In outsourcing, commitments for change requests are defined as in TMA. 37 | Commitments for incidents are not the same whether they occur during business hours or not. But, whatever the hour at which an incident occurs, the past times do not stop. 38 | 39 | -------------------------------------------------------------------------------- /doc/API/API_sla_types.md: -------------------------------------------------------------------------------- 1 | ## Sla Types 2 | 3 | ###  Listing Sla Types 4 | 5 | `GET /sla/types.[format]` 6 | 7 | Returns a paginated list of Sla Types. By default, it returns all Sla Types. 8 | 9 | Optional filters: 10 | - name 11 | 12 | Examples: 13 | 14 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/types.json"` 15 | 16 | 17 | 18 | ### Showing a Sla Type 19 | 20 | `GET /sla/types/[id].[format]` 21 | 22 | Examples: 23 | 24 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/types/17.json"` 25 | 26 | 27 | ### Creating a Sla Type 28 | 29 | `POST /sla/types.[format]` 30 | 31 | Examples: 32 | 33 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/types.json"` 34 | 35 | Use with : 36 | ``` 37 | DATA="$(cat <<-EOF 38 | { 39 | "sla_type": { 40 | "name": "Response time" 41 | } 42 | } 43 | EOF 44 | )" 45 | ``` 46 | 47 | ### Updating a Sla Type 48 | 49 | `PUT /sla/types/[id].[format]` 50 | 51 | Examples: 52 | 53 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/types/1.json"` 54 | 55 | Use with : 56 | ``` 57 | DATA="$(cat <<-EOF 58 | { 59 | "sla_type": { 60 | "name": "Response time" 61 | } 62 | } 63 | EOF 64 | )" 65 | ``` 66 | 67 | 68 | ### Deleting a Sla Type 69 | 70 | `DELETE /sla/types/[id].[format]` 71 | 72 | Examples: 73 | 74 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/types/17.json"` 75 | -------------------------------------------------------------------------------- /app/views/sla_level_terms/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_level_term], html: { multipart: false, id: 'sla-level-term-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 |
4 | <% if sla_level_term.safe_attribute?('sla_level_id') %> 5 |

6 | <%= f.select :sla_level_id, SlaLevel.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 7 |

8 | <% end %> 9 | <% if sla_level_term.safe_attribute?('sla_type_id') %> 10 |

11 | <%= f.select :sla_type_id, SlaType.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 12 |

13 | <% end %> 14 | <% if sla_level_term.safe_attribute?('sla_priority_id') %> 15 |

16 | <%= f.select :sla_priority_id, SlaPriority.all.collect { |p| [ p.name, p.id ] }, :include_blank => (f.object.new_record??true:false), :required => true %> 17 |

18 | <% end %> 19 | <% if sla_level_term.safe_attribute?('term') %> 20 |

21 | <%= f.text_field :term, :required => true %> 22 |

23 | <% end %> 24 |
25 | <% if !request.xhr? %> 26 |

27 | <% if f.object.new_record? %> 28 | <%= submit_tag l('sla_label.sla_level_term.new'), title: l(:button_create), class: "button-positive" %> 29 | <% else %> 30 | <%= submit_tag l('sla_label.sla_level_term.save'), title: l(:button_update), class: "button-positive" %> 31 | <% end %> 32 |

33 | <% end %> 34 | <% end %> 35 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/db/schema.rb 4 | # Redmine SLA - Redmine's Plugin 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | # 20 | # Purpose: 21 | # This file defines the database schema snapshot for the RedmineSLA plugin. 22 | # It is automatically maintained by Rails migrations and represents the 23 | # structural state of the plugin's database entities at a given point in time. 24 | # 25 | # Notes: 26 | # - Do NOT manually edit this file unless absolutely necessary. 27 | # - It is regenerated when running migrations or loading the schema. 28 | # - Only comments have been added here; the generated code below remains unchanged. 29 | 30 | ActiveRecord::Schema.define(version: 20211111111111) do 31 | 32 | # Database extensions that must be enabled for PostgreSQL, especially 'plpgsql' 33 | # which is required for stored functions written in PL/pgSQL. 34 | enable_extension "plpgsql" 35 | 36 | end -------------------------------------------------------------------------------- /lib/redmine_sla.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/lib/redmine_sla.rb 4 | # Purpose: 5 | # Main entry point for the Redmine SLA plugin. This file loads all required 6 | # hook listeners and provides a central namespace for the plugin’s modules. 7 | # 8 | # Redmine SLA - Redmine Plugin 9 | # 10 | # This program is free software; you can redistribute it and/or 11 | # modify it under the terms of the GNU General Public License 12 | # as published by the Free Software Foundation; either version 2 13 | # of the License, or (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program; if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | # Base directory for loading plugin files 25 | base_url = File.dirname(__FILE__) 26 | 27 | # Hooks 28 | # require base_url + '/redmine_sla/hooks/helpers_queries_hook' 29 | # (commented out, presumably unused) 30 | require base_url + '/redmine_sla/hooks/views_layouts_hook' 31 | require base_url + '/redmine_sla/hooks/views_issues_hook' 32 | 33 | module RedmineSla 34 | # This module acts as the main namespace for the plugin. 35 | # 36 | # QueriesHelper may be extended via Hooks::HelpersQueriesHook (if uncommented): 37 | # 38 | # QueriesHelper.send(:include, RedmineSla::Hooks::HelpersQueriesHook) 39 | end -------------------------------------------------------------------------------- /lib/redmine_sla/patches/issue_custom_field_patch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/lib/redmine_sla/patches/issue_custom_field_patch.rb 4 | # Purpose: 5 | # Extend Redmine's IssueCustomField behavior for SLA-related usage. 6 | # This patch overrides `to_s` so that SLA custom fields can be rendered 7 | # consistently (for example in SLA level lists or configuration screens). 8 | 9 | # Redmine SLA - Redmine Plugin 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 24 | 25 | require_dependency 'issue_custom_field' 26 | 27 | module RedmineSla 28 | module Patches 29 | 30 | # Patch module for Redmine's IssueCustomField 31 | module IssueCustomFieldPatch 32 | 33 | # Redefine the string representation to return the field name. 34 | # This is useful when displaying SLA-related custom fields in lists. 35 | def to_s 36 | self.name 37 | end 38 | 39 | module ClassMethods 40 | end 41 | 42 | module InstanceMethods 43 | end 44 | 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /db/sql_functions/sla_get_date.sql: -------------------------------------------------------------------------------- 1 | -- File: redmine_sla/db/sql_functions/sla_get_date.sql 2 | -- Remove the existing function to ensure a clean redeployment 3 | DROP FUNCTION IF EXISTS sla_get_date CASCADE ; 4 | 5 | -- Normalize a timestamp to the SLA timezone and truncate to the minute. 6 | -- This function is used everywhere a consistent datetime reference is required. 7 | CREATE FUNCTION sla_get_date(p_date TIMESTAMP WITHOUT TIME ZONE) 8 | RETURNS TIMESTAMP WITHOUT TIME ZONE 9 | LANGUAGE sql 10 | AS $BODY$ 11 | 12 | -- Convert the input timestamp into the SLA-specific configured timezone. 13 | -- Steps: 14 | -- 1. Retrieve the configured SLA timezone from plugin settings. 15 | -- The setting is stored as a serialized hash inside Redmine's settings table. 16 | -- We extract it by regex from the "plugin_redmine_sla" setting. 17 | -- 2. Fallback to 'Etc/UTC' if no timezone is configured. 18 | -- 3. Apply the timezone conversion. 19 | -- 4. Truncate the result to the minute (SLA granularity). 20 | -- 21 | -- Note: 22 | -- p_date is always interpreted as UTC first ("AT TIME ZONE 'Etc/UTC'") 23 | -- then converted to the configured timezone via TIMEZONE(). 24 | SELECT DATE_TRUNC( 25 | 'MINUTE', 26 | TIMEZONE( 27 | ( 28 | SELECT COALESCE( 29 | ( 30 | -- Extract SLA timezone from Redmine plugin settings (YAML-like blob) 31 | SELECT SUBSTRING(value FROM 'sla_time_zone: ([a-z,A-Z,/]*)') 32 | FROM settings 33 | WHERE name LIKE 'plugin_redmine_sla' 34 | ), 35 | -- Default timezone if none is configured 36 | 'Etc/UTC' 37 | ) 38 | ), 39 | -- Normalize input timestamp as UTC before conversion 40 | p_date AT TIME ZONE 'Etc/UTC' 41 | ) 42 | ) 43 | 44 | $BODY$ ; -------------------------------------------------------------------------------- /doc/API/API_sla_statuses.md: -------------------------------------------------------------------------------- 1 | ## Sla Statuses 2 | 3 | ### Listing Sla Statuses 4 | 5 | `GET /sla/statuses.[format]` 6 | 7 | Returns a paginated list of Sla Statuses. By default, it returns all Sla Statuses. 8 | 9 | Optional filters: 10 | - sla_type_id 11 | - status_id 12 | 13 | Examples: 14 | 15 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/statuses.json"` 16 | 17 | 18 | ### Showing a Sla Status 19 | 20 | `GET /sla/statuses/[id].[format]` 21 | 22 | Examples: 23 | 24 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/statuses/17.json"` 25 | 26 | 27 | ### Creating a Sla Status 28 | 29 | `POST /sla/statuses.[format]` 30 | 31 | Examples: 32 | 33 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/statuses.json"` 34 | 35 | Use with : 36 | ``` 37 | DATA="$(cat <<-EOF 38 | { 39 | "sla_status": { 40 | "sla_type_id": "1", 41 | "status_id": "1" 42 | } 43 | } 44 | EOF 45 | )" 46 | ``` 47 | 48 | ### Updating a Sla Status 49 | 50 | `PUT /sla/statuses/[id].[format]` 51 | 52 | Examples: 53 | 54 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/statuses/1.json"` 55 | 56 | Use with : 57 | ``` 58 | DATA="$(cat <<-EOF 59 | { 60 | "sla_status": { 61 | "sla_type_id": "1", 62 | "status_id": "1" 63 | } 64 | } 65 | EOF 66 | )" 67 | ``` 68 | 69 | 70 | ### Deleting a Sla Status 71 | 72 | `DELETE /sla/statuses/[id].[format]` 73 | 74 | Examples: 75 | 76 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/statuses/17.json"` 77 | -------------------------------------------------------------------------------- /app/helpers/queries/sla_level_terms_queries_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006- Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | module Queries::SlaLevelTermsQueriesHelper 21 | include ApplicationHelper 22 | include SlaLevelTermsHelper 23 | include SlaLevelsHelper 24 | include SlaTypesHelper 25 | 26 | def column_value(column, item, value) 27 | if item.is_a?(SlaLevelTerm) 28 | case column.name 29 | when :id 30 | link_to_sla_level_term_id(item) 31 | when :sla_level 32 | link_to_sla_level_name(item.sla_level) 33 | when :sla_type 34 | link_to_sla_type_name(item.sla_type) 35 | when :sla_priority_id 36 | ( item.sla_level.custom_field_id.nil? ? "[IssuePriority] " : "[CustomField] " ) + 37 | SlaPriority.create(item.sla_level.custom_field_id).find_by_priority_id(value).name 38 | else 39 | super 40 | end 41 | end 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /app/models/sla_custom_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006-2023 Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | class SlaCustomField < IssueCustomField 21 | 22 | include ActiveModel::Model 23 | 24 | def self.class 25 | Rails.logger.debug "==>> SlaCustomField self.class" 26 | "IssueCustomField" 27 | end 28 | 29 | # TODO : filter on issue with an other method self.find_by_issue ( issue.available_custom_fields.find { |field| field.id == custom_field_id } ) 30 | def self.find(custom_field_id) 31 | # TODO : LOG : ERROR : if nil !!! 32 | IssueCustomField.find_by(field_format: :enumeration, multiple: :false, is_required:true, id: custom_field_id) 33 | end 34 | 35 | # To only list IssueCustomFields of type "enumeration" with single value in SlaLevel#edit 36 | def self.all 37 | # TODO : LOG : NOTICE : if nil 38 | IssueCustomField.where(field_format: :enumeration, multiple: :false, is_required:true) 39 | end 40 | 41 | end -------------------------------------------------------------------------------- /doc/API/API_sla_holidays.md: -------------------------------------------------------------------------------- 1 | ## Sla Holidays 2 | 3 | ### Listing Sla Holidays 4 | 5 | `GET /sla/holidays.[format]` 6 | 7 | Returns a paginated list of Sla Holidays. By default, it returns all Sla Holidays. 8 | 9 | Optional filters: 10 | - name 11 | - date 12 | 13 | Examples: 14 | 15 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/holidays.json"` 16 | 17 | 18 | ### Showing a Sla Holiday 19 | 20 | `GET /sla/holidays/[id].[format]` 21 | 22 | Examples: 23 | 24 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/holidays/17.json"` 25 | 26 | 27 | ### Creating a Sla Holiday 28 | 29 | `POST /sla/holidays.[format]` 30 | 31 | Examples: 32 | 33 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/holidays.json"` 34 | 35 | Use with : 36 | ``` 37 | DATA="$(cat <<-EOF 38 | { 39 | "sla_holiday": { 40 | "name": "New Year's Day", 41 | "date": "2024-01-01" 42 | } 43 | } 44 | EOF 45 | )" 46 | ``` 47 | 48 | 49 | ### Updating a Sla Holiday 50 | 51 | `PUT /sla/holidays/[id].[format]` 52 | 53 | Examples: 54 | 55 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/holidays/1.json"` 56 | 57 | Use with : 58 | ``` 59 | DATA="$(cat <<-EOF 60 | { 61 | "sla_holiday": { 62 | "name": "New Year's Day", 63 | "date": "2024-01-01" 64 | } 65 | } 66 | EOF 67 | )" 68 | ``` 69 | 70 | 71 | ### Deleting a Sla Holiday 72 | 73 | `DELETE /sla/holidays/[id].[format]` 74 | 75 | Examples: 76 | 77 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/holidays/17.json"` 78 | -------------------------------------------------------------------------------- /doc/API/API_slas.md: -------------------------------------------------------------------------------- 1 | ## Slas 2 | 3 | ### Listing Slas 4 | 5 | `GET /sla/slas.[format]` 6 | 7 | Returns a paginated list of Slas. By default, it returns all Slas. 8 | 9 | Optional filters: 10 | - name 11 | 12 | Examples: 13 | 14 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas.json"` 15 | 16 | To improve the presentation, you can use this command: 17 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas.json" | python3 -m json.tool` 18 | 19 | 20 | 21 | ### Showing a Sla 22 | 23 | `GET /sla/slas/[id].[format]` 24 | 25 | Examples: 26 | 27 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas/17.json"` 28 | 29 | 30 | ### Creating a Sla 31 | 32 | `POST /sla/slas.[format]` 33 | 34 | Examples: 35 | 36 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas.json"` 37 | 38 | Use with : 39 | ``` 40 | DATA="$(cat <<-EOF 41 | { 42 | "sla": { 43 | "name": "Sla Support Tracker" 44 | } 45 | } 46 | EOF 47 | )" 48 | ``` 49 | 50 | ### Updating a Sla 51 | 52 | `PUT /sla/slas/[id].[format]` 53 | 54 | Examples: 55 | 56 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas/1.json"` 57 | 58 | Use with : 59 | ``` 60 | DATA="$(cat <<-EOF 61 | { 62 | "sla": { 63 | "name": "Sla Support Tracker" 64 | } 65 | } 66 | EOF 67 | )" 68 | ``` 69 | 70 | 71 | ### Deleting a Sla 72 | 73 | `DELETE /sla/slas/[id].[format]` 74 | 75 | Examples: 76 | 77 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/slas/17.json"` 78 | -------------------------------------------------------------------------------- /test/integration/routing/slas_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require_relative "../../test_helper" 20 | 21 | class RoutingSlasTest < Redmine::RoutingTest 22 | 23 | def test_slas 24 | should_route 'GET /sla/slas' => 'slas#index' 25 | !should_route 'GET /sla/slas.pdf' => 'slas#index', :format => 'pdf' 26 | !should_route 'GET /sla/slas.atom' => 'slas#index', :format => 'atom' 27 | 28 | should_route 'GET /sla/slas/1' => 'slas#show', :id => '1' 29 | !should_route 'GET /sla/slas/1.pdf' => 'slas#show', :id => '1', :format => 'pdf' 30 | !should_route 'GET /sla/slas/1.atom' => 'slas#show', :id => '1', :format => 'atom' 31 | 32 | should_route 'GET /sla/slas/new' => 'slas#new' 33 | should_route 'POST /sla/slas' => 'slas#create' 34 | 35 | should_route 'GET /sla/slas/1/edit' => 'slas#edit', :id => '1' 36 | should_route 'PUT /sla/slas/1' => 'slas#update', :id => '1' 37 | should_route 'DELETE /sla/slas/1' => 'slas#destroy', :id => '1' 38 | end 39 | 40 | end -------------------------------------------------------------------------------- /doc/API/API_sla_levels.md: -------------------------------------------------------------------------------- 1 | ## Sla Levels 2 | 3 | ### Listing Sla Levels 4 | 5 | `GET /sla/levels.[format]` 6 | 7 | Returns a paginated list of Sla Levels. By default, it returns all Sla Levels. 8 | 9 | Optional filters: 10 | - name 11 | - sla_id 12 | - sla_calendar_id 13 | 14 | Examples: 15 | 16 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/levels.json"` 17 | 18 | 19 | 20 | ### Showing a Sla Level 21 | 22 | `GET /sla/levels/[id].[format]` 23 | 24 | Examples: 25 | 26 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/levels/17.json"` 27 | 28 | 29 | ### Creating a Sla Level 30 | 31 | `POST /sla/levels.[format]` 32 | 33 | Examples: 34 | 35 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/levels.json"` 36 | 37 | Use with : 38 | ``` 39 | DATA="$(cat <<-EOF 40 | { 41 | "sla_level": { 42 | "name": "Level Bug Tracker", 43 | "sla_id": 1, 44 | "sla_calendar_id": 1 45 | } 46 | } 47 | EOF 48 | )" 49 | ``` 50 | 51 | ### Updating a Sla Level 52 | 53 | `PUT /sla/levels/[id].[format]` 54 | 55 | Examples: 56 | 57 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/levels/1.json"` 58 | 59 | Use with : 60 | ``` 61 | DATA="$(cat <<-EOF 62 | { 63 | "sla_level": { 64 | "name": "Level Bug Tracker", 65 | "sla_id": 1, 66 | "sla_calendar_id": 1 67 | } 68 | } 69 | EOF 70 | )" 71 | ``` 72 | 73 | 74 | ### Deleting a Sla Level 75 | 76 | `DELETE /sla/levels/[id].[format]` 77 | 78 | Examples: 79 | 80 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/levels/17.json"` 81 | -------------------------------------------------------------------------------- /lib/redmine_sla/hooks/views_layouts_hook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/lib/redmine_sla/views_layouts_hook.rb 4 | # Purpose: 5 | # Register a layout-level view hook that injects the plugin's stylesheet 6 | # into Redmine's global layout ( section), so SLA-specific styles are 7 | # available across all pages. 8 | 9 | # Redmine SLA - Redmine Plugin 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 24 | 25 | module RedmineSla 26 | module Hooks 27 | # Layout hook used to inject the plugin stylesheet in the global layout. 28 | # The hook is called when rendering the section of Redmine's layout. 29 | class ViewsLayoutsHook < Redmine::Hook::ViewListener 30 | 31 | # Called by Redmine in the section of the base layout. 32 | # Returns: 33 | # An HTML tag referencing the plugin stylesheet: 34 | # public/plugin_assets/redmine_sla/stylesheets/redmine_sla.css 35 | def view_layouts_base_html_head(context = {}) 36 | return stylesheet_link_tag(:redmine_sla, plugin: 'redmine_sla') 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /app/views/sla_schedules/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= labelled_form_for([@sla_schedule], html: { multipart: false, id: 'sla-schedule-form', class: 'tabular', remote: request.xhr? }) do |f| %> 2 | <%= error_messages_for f.object %> 3 | 4 |
5 | 6 | <% if sla_schedule.safe_attribute?('sla_calendar_id') %> 7 |

8 | <%= f.select :sla_calendar_id, SlaCalendar.all.collect { |p| [ p.name, p.id ] }, :required => true, :include_blank => (@sla_schedule.new_record??true:false) %> 9 |

10 | <% end %> 11 | 12 | <% if sla_schedule.safe_attribute?('dow') %> 13 |

14 | <%= f.select :dow, I18n.t('date.day_names').map.with_index.to_h, :required => true %> 15 |

16 | <% end %> 17 | 18 | <% if sla_schedule.safe_attribute?('start_time') %> 19 |

20 | <%= f.time_field :start_time, value: (f.object.start_time.strftime("%H:%M") unless f.object.start_time.blank?), min: 'hh:mm', max: 'hh:mm', :required => true %> 21 |

22 | <% end %> 23 | 24 | <% if sla_schedule.safe_attribute?('end_time') %> 25 |

26 | <%= f.time_field :end_time, value: (f.object.end_time.strftime("%H:%M") unless f.object.end_time.blank?), min: 'hh:mm', max: 'hh:mm', :required => true %> 27 |

28 | <% end %> 29 | 30 | <% if sla_schedule.safe_attribute?('match') %> 31 |

32 | <%= f.check_box :match, :required => true %> 33 |

34 | <% end %> 35 | 36 |
37 | 38 | <% if !request.xhr? %> 39 |

40 | <% if f.object.new_record? %> 41 | <%= submit_tag l('sla_label.sla_schedule.new'), title: l(:button_create), class: "button-positive" %> 42 | <% else %> 43 | <%= submit_tag l('sla_label.sla_schedule.save'), title: l(:button_update), class: "button-positive" %> 44 | <% end %> 45 |

46 | <% end %> 47 | 48 | <% end %> 49 | -------------------------------------------------------------------------------- /test/integration/routing/sla_types_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require_relative "../../test_helper" 20 | 21 | class RoutingSlaTypesTest < Redmine::RoutingTest 22 | 23 | def test_slas 24 | should_route 'GET /sla/types' => 'sla_types#index' 25 | !should_route 'GET /sla/types.pdf' => 'sla_types#index', :format => 'pdf' 26 | !should_route 'GET /sla/types.atom' => 'sla_types#index', :format => 'atom' 27 | 28 | should_route 'GET /sla/types/1' => 'sla_types#show', :id => '1' 29 | !should_route 'GET /sla/types/1.pdf' => 'sla_types#show', :id => '1', :format => 'pdf' 30 | !should_route 'GET /sla/types/1.atom' => 'sla_types#show', :id => '1', :format => 'atom' 31 | 32 | should_route 'GET /sla/types/new' => 'sla_types#new' 33 | should_route 'POST /sla/types' => 'sla_types#create' 34 | 35 | should_route 'GET /sla/types/1/edit' => 'sla_types#edit', :id => '1' 36 | should_route 'PUT /sla/types/1' => 'sla_types#update', :id => '1' 37 | should_route 'DELETE /sla/types/1' => 'sla_types#destroy', :id => '1' 38 | end 39 | 40 | end -------------------------------------------------------------------------------- /app/models/sla_priority_scf.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine - project management software 4 | # Copyright (C) 2006-2023 Jean-Philippe Lang 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | class SlaPriorityScf < SlaPriority 21 | 22 | include ActiveModel::Model 23 | 24 | def initialize(custom_field_id) 25 | @scf = SlaCustomField.find(custom_field_id) 26 | end 27 | 28 | # For display one SlaPriorityScf by issue in IssueHelper 29 | def find_by_issue(issue) 30 | self.find_by_priority_id(issue.custom_field_value(@scf.id)) 31 | end 32 | 33 | # For display one SlaPriorityScf by priority_id in IssueHelper 34 | def find_by_priority_id(priority_id) 35 | # TODO : LOG : on ActiveRecord::RecordNotFound si find et nil si find_by 36 | return nil if priority_id.nil? 37 | priority = @scf.enumerations.active.find(priority_id) 38 | self.create_value(priority.id,priority.name) 39 | end 40 | 41 | # For display all SlaPriority in SlaLevel views after self.create ( base on all values of the CustomField ) 42 | def all 43 | @scf.enumerations.active { |id,name| self.create_value(id,name) } 44 | end 45 | 46 | end -------------------------------------------------------------------------------- /app/helpers/sla_calendars_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | module SlaCalendarsHelper 20 | 21 | # Generates a link to a sla_calendar with id 22 | def link_to_sla_calendar_id(sla_calendar) 23 | link_to sla_calendar.id, sla_calendar_url(sla_calendar, {:only_path => true}) 24 | end 25 | 26 | # Generates a link to a sla_calendar with name 27 | def link_to_sla_calendar_name(sla_calendar) 28 | link_to sla_calendar.name, sla_calendar_url(sla_calendar, {:only_path => true}) 29 | end 30 | 31 | def link_to_remove_fields(name, f) 32 | f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)") 33 | end 34 | 35 | def link_to_add_fields(name, f, association) 36 | new_object = f.object.class.reflect_on_association(association).klass.new 37 | fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| 38 | render(association.to_s.singularize + "_fields", :f => builder) 39 | end 40 | link_to_function(name, "add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")") 41 | end 42 | 43 | end -------------------------------------------------------------------------------- /db/migrate/202111112021010_create_sla_project_trackers.rb: -------------------------------------------------------------------------------- 1 | # File: redmine_sla/db/migrate/202111112021010_create_sla_project_trackers.rb 2 | # Purpose: 3 | # Create the `sla_project_trackers` table, which links: 4 | # - a Redmine project, 5 | # - a Redmine tracker, 6 | # - a given SLA definition. 7 | # 8 | # This association determines which SLA applies to issues of a specific 9 | # tracker inside a specific project. Each (project, tracker) pair must be 10 | # unique, ensuring that only one SLA is attached to the combination. 11 | 12 | class CreateSlaProjectTrackers < ActiveRecord::Migration[5.2] 13 | 14 | def change 15 | create_table :sla_project_trackers do |t| 16 | 17 | # Link to the Redmine project 18 | t.belongs_to :project, 19 | foreign_key: { 20 | name: 'sla_project_trackers_projects_fkey', 21 | on_delete: :cascade 22 | } 23 | 24 | # Link to the Redmine tracker 25 | t.belongs_to :tracker, 26 | foreign_key: { 27 | name: 'sla_project_trackers_trackers_fkey', 28 | on_delete: :cascade 29 | } 30 | 31 | # Link to the SLA definition applied to this (project, tracker) pair 32 | t.belongs_to :sla, 33 | foreign_key: { 34 | name: 'sla_project_trackers_slas_fkey', 35 | on_delete: :cascade 36 | } 37 | end 38 | 39 | # Migration log message 40 | say "Created table sla_project_trackers" 41 | 42 | # Ensure that a project cannot define more than one SLA for the same tracker 43 | add_index :sla_project_trackers, 44 | [:project_id, :tracker_id], 45 | unique: true, 46 | name: 'sla_project_trackers_ukey' 47 | say "Created index unique sla_project_trackers_ukey" 48 | 49 | end 50 | 51 | end -------------------------------------------------------------------------------- /test/integration/routing/sla_statuses_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | require_relative "../../test_helper" 20 | 21 | class RoutingSlaStatusTest < Redmine::RoutingTest 22 | 23 | def test_slas 24 | should_route 'GET /sla/statuses' => 'sla_statuses#index' 25 | !should_route 'GET /sla/statuses.pdf' => 'sla_statuses#index', :format => 'pdf' 26 | !should_route 'GET /sla/statuses.atom' => 'sla_statuses#index', :format => 'atom' 27 | 28 | should_route 'GET /sla/statuses/1' => 'sla_statuses#show', :id => '1' 29 | !should_route 'GET /sla/statuses/1.pdf' => 'sla_statuses#show', :id => '1', :format => 'pdf' 30 | !should_route 'GET /sla/statuses/1.atom' => 'sla_statuses#show', :id => '1', :format => 'atom' 31 | 32 | should_route 'GET /sla/statuses/new' => 'sla_statuses#new' 33 | should_route 'POST /sla/statuses' => 'sla_statuses#create' 34 | 35 | should_route 'GET /sla/statuses/1/edit' => 'sla_statuses#edit', :id => '1' 36 | should_route 'PUT /sla/statuses/1' => 'sla_statuses#update', :id => '1' 37 | should_route 'DELETE /sla/statuses/1' => 'sla_statuses#destroy', :id => '1' 38 | end 39 | 40 | end -------------------------------------------------------------------------------- /db/sql_views/sla_view_roll_statuses.sql: -------------------------------------------------------------------------------- 1 | -- File: redmine_sla/db/sql_views/sla_view_roll_statuses.sql 2 | -- Purpose: 3 | -- Build continuous status intervals for each issue based on its journal 4 | -- status history (from sla_view_journal_statuses). This view reconstructs 5 | -- "from" and "to" statuses with their corresponding dates so that SLA 6 | -- computations can easily determine how long an issue stayed in each status. 7 | 8 | -- Issues journals rebuild, with database time zone 9 | CREATE OR REPLACE VIEW sla_view_roll_statuses AS 10 | ( 11 | -- First and subsequent status changes: 12 | -- Each row represents a transition from one status to another, 13 | -- with the time interval during which the "from" status was active. 14 | SELECT 15 | issue_id AS issue_id, 16 | journal_detail_old_value AS from_status_id, 17 | LAG(journals_created_on, 1, issue_created_on) OVER window_status AS from_status_date, 18 | journal_detail_value AS to_status_id, 19 | journals_created_on AS to_status_date 20 | FROM sla_view_journal_statuses 21 | WINDOW window_status AS ( 22 | PARTITION BY issue_id 23 | ORDER BY journals_created_on ASC 24 | ) 25 | ) UNION ( 26 | -- Ensure we also have the "last" status interval: 27 | -- This covers the period from the last recorded status change 28 | -- until the issue is closed, or until "now" if it is still open. 29 | SELECT 30 | issue_id AS issue_id, 31 | FIRST_VALUE(journal_detail_value) OVER window_status AS from_status_id, 32 | FIRST_VALUE(journals_created_on) OVER window_status AS from_status_date, 33 | FIRST_VALUE(journal_detail_value) OVER window_status AS to_status_id, 34 | COALESCE( 35 | issue_closed_on, 36 | sla_get_date(NOW()::TIMESTAMP WITHOUT TIME ZONE) 37 | ) AS to_status_date 38 | FROM sla_view_journal_statuses 39 | WINDOW window_status AS ( 40 | PARTITION BY issue_id 41 | ORDER BY journals_created_on DESC 42 | ) 43 | ); -------------------------------------------------------------------------------- /lib/redmine_sla/hooks/views_issues_hook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # File: redmine_sla/lib/redmine_sla/views_issues_hook.rb 4 | # Purpose: 5 | # Define Redmine view hooks used by the SLA plugin. This hook injects 6 | # SLA rendering logic into the issue details view, allowing SLA-related 7 | # information to be displayed directly in the issue UI. 8 | 9 | # Redmine SLA - Redmine Plugin 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 24 | 25 | module RedmineSla 26 | module Hooks 27 | 28 | # Listener hooking into Redmine's issue view rendering. 29 | # It allows the plugin to inject SLA information in the issue UI, 30 | # specifically at the bottom of the "details" section. 31 | class ViewsIssuesHook < Redmine::Hook::ViewListener 32 | 33 | # Injects the partial: 34 | # redmine_sla/app/views/sla_issues_helper/_show.html.erb 35 | # 36 | # into: 37 | # view_issues_show_details_bottom 38 | # 39 | # This is where SLA-related information (timing, levels, compliance...) 40 | # is displayed in the issue details screen. 41 | render_on :view_issues_show_details_bottom, 42 | partial: "sla_issues_helper/show" 43 | 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /doc/API/API_sla_level_terms.md: -------------------------------------------------------------------------------- 1 | ## Sla Level Terms 2 | 3 | ### Listing Sla Level Terms 4 | 5 | `GET /sla/level_terms.[format]` 6 | 7 | Returns a paginated list of Sla Level Terms. By default, it returns all Sla Level Terms. 8 | 9 | Optional filters: 10 | - sla_level_id 11 | - sla_type_id 12 | - priority_id 13 | - term 14 | 15 | Examples: 16 | 17 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/level_terms.json"` 18 | 19 | 20 | ### Showing a Sla Level Term 21 | 22 | `GET /sla/level_terms/[id].[format]` 23 | 24 | Examples: 25 | 26 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/level_terms/17.json"` 27 | 28 | 29 | ### Creating a Sla Level Term 30 | 31 | `POST /sla/level_terms.[format]` 32 | 33 | Examples: 34 | 35 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/level_terms.json"` 36 | 37 | Use with : 38 | ``` 39 | DATA="$(cat <<-EOF 40 | { 41 | "sla_level_term": { 42 | "sla_level_id": 1, 43 | "sla_type_id": 1, 44 | "priority_id": 1, 45 | "term": "240" 46 | } 47 | } 48 | EOF 49 | )" 50 | ``` 51 | 52 | ### Updating a Sla Level Term 53 | 54 | `PUT /sla/level_terms/[id].[format]` 55 | 56 | Examples: 57 | 58 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/level_terms/1.json"` 59 | 60 | Use with : 61 | ``` 62 | DATA="$(cat <<-EOF 63 | { 64 | "sla_level_term": { 65 | "sla_level_id": 1, 66 | "sla_type_id": 1, 67 | "priority_id": 1, 68 | "term": "240" 69 | } 70 | } 71 | EOF 72 | )" 73 | ``` 74 | 75 | 76 | ### Deleting a Sla Level Term 77 | 78 | `DELETE /sla/level_terms/[id].[format]` 79 | 80 | Examples: 81 | 82 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/level_terms/17.json"` 83 | -------------------------------------------------------------------------------- /doc/API/API_sla_schedules.md: -------------------------------------------------------------------------------- 1 | ## Sla Schedules 2 | 3 | ### Listing Sla Schedules 4 | 5 | `GET /sla/schedules.[format]` 6 | 7 | Returns a paginated list of Sla Statuses. By default, it returns all Sla Statuses. 8 | 9 | Optional filters: 10 | - sla_calendar_id 11 | - dow 12 | - match 13 | 14 | Examples: 15 | 16 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/schedules.json"` 17 | 18 | 19 | ### Showing a Sla Schedule 20 | 21 | `GET /sla/schedules/[id].[format]` 22 | 23 | Examples: 24 | 25 | `curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/schedules/17.json"` 26 | 27 | 28 | ### Creating a Sla Schedule 29 | 30 | `POST /sla/schedules.[format]` 31 | 32 | Examples: 33 | 34 | `curl -s -H "Content-Type: application/json" -X POST --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/schedules.json"` 35 | 36 | Use with : 37 | ``` 38 | DATA="$(cat <<-EOF 39 | { 40 | "sla_schedule": { 41 | "sla_calendar_id": "1", 42 | "dow": "1", 43 | "start_time": "09:30", 44 | "end_time": "12h29", 45 | "match": "true" 46 | } 47 | } 48 | EOF 49 | )" 50 | ``` 51 | 52 | ### Updating a Sla Schedule 53 | 54 | `PUT /sla/schedules/[id].[format]` 55 | 56 | Examples: 57 | 58 | `curl -s -H "Content-Type: application/json" -X PUT --data "$DATA" -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/schedules/1.json"` 59 | 60 | Use with : 61 | ``` 62 | DATA="$(cat <<-EOF 63 | { 64 | "sla_schedule": { 65 | "sla_calendar_id": "1", 66 | "dow": "1", 67 | "start_time": "09:30", 68 | "end_time": "12h29", 69 | "match": "true" 70 | } 71 | } 72 | EOF 73 | )" 74 | ``` 75 | 76 | 77 | ### Deleting a Sla Schedule 78 | 79 | `DELETE /sla/schedules/[id].[format]` 80 | 81 | Examples: 82 | 83 | `curl -s -H "Content-Type: application/json" -X DELETE -H "X-Redmine-API-Key: $APIKEY" "$TRACKER/sla/schedules/17.json"` 84 | -------------------------------------------------------------------------------- /app/models/sla_holiday.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Redmine SLA - Redmine's Plugin 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | class SlaHoliday < ActiveRecord::Base 20 | 21 | include Redmine::SafeAttributes 22 | 23 | scope :visible, ->(*args) { where(SlaHoliday.visible_condition(args.shift || User.current, *args)) } 24 | 25 | # TODO : permit group by year functionnal 26 | # TODO : permit order by name/date functionnal 27 | 28 | default_scope { } 29 | 30 | validates_presence_of :name 31 | validates_presence_of :date 32 | 33 | validates_uniqueness_of :date 34 | 35 | safe_attributes *%w[name date] 36 | 37 | # No selection limitations 38 | def self.visible_condition(user, options = {}) 39 | '1=1' 40 | end 41 | 42 | # For index and show 43 | def visible?(user=User.current) 44 | user.allowed_to?(:manage_sla, nil, global: true) 45 | end 46 | 47 | # For create and update 48 | def editable?(user=User.current) 49 | user.allowed_to?(:manage_sla, nil, global: true) 50 | end 51 | 52 | # For destroy 53 | def deletable?(user=User.current) 54 | user.allowed_to?(:manage_sla, nil, global: true) 55 | end 56 | 57 | # Print text for link objects 58 | def to_s 59 | name.to_s 60 | end 61 | 62 | end --------------------------------------------------------------------------------