diff --git a/admin/settings/grades.php b/admin/settings/grades.php
index 2d6e86874a71b..c99121cc5f04c 100644
--- a/admin/settings/grades.php
+++ b/admin/settings/grades.php
@@ -246,6 +246,11 @@
new lang_string('gradepenalty_supportedplugins', 'grades'),
new lang_string('gradepenalty_supportedplugins_help', 'grades'), [], $options));
+ // Option to apply penalty to overridden grades.
+ $temp->add(new admin_setting_configcheckbox('gradepenalty_overriddengrade',
+ new lang_string('gradepenalty_overriddengrade', 'grades'),
+ new lang_string('gradepenalty_overriddengrade_help', 'grades'), 0));
+
$ADMIN->add('gradepenalty', $temp);
}
diff --git a/grade/report/grader/lang/en/gradereport_grader.php b/grade/report/grader/lang/en/gradereport_grader.php
index bf27242c63cd8..75d80e9c884b4 100644
--- a/grade/report/grader/lang/en/gradereport_grader.php
+++ b/grade/report/grader/lang/en/gradereport_grader.php
@@ -23,6 +23,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['applypenaltytext'] = 'Deduct {$a}';
+$string['applypenaltytooltip'] = 'Apply a deduction to the grade. This value is retrieved from the current overridden grade.';
$string['aria:dropdowncolumns'] = 'Collapsed columns found';
$string['clearsearch'] = 'Clear searched users';
$string['collapsedcolumns'] = 'Collapsed columns {$a}';
diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php
index 356d7eb8d144a..a82ca0c15e0f8 100644
--- a/grade/report/grader/lib.php
+++ b/grade/report/grader/lib.php
@@ -241,18 +241,21 @@ public function process_data($data) {
continue;
}
- // If the grade item uses a custom scale
- if (!empty($oldvalue->grade_item->scaleid)) {
+ // Detect if there is any mark deduction.
+ if (!isset($data->deduction[$userid][$itemid])) {
+ // If the grade item uses a custom scale.
+ if (!empty($oldvalue->grade_item->scaleid)) {
- if ((int)$oldvalue->finalgrade === (int)$postedvalue) {
- continue;
- }
- } else {
- // The grade item uses a numeric scale
+ if ((int)$oldvalue->finalgrade === (int)$postedvalue) {
+ continue;
+ }
+ } else {
+ // The grade item uses a numeric scale.
- // Format the finalgrade from the DB so that it matches the grade from the client
- if ($postedvalue === format_float($oldvalue->finalgrade, $oldvalue->grade_item->get_decimals())) {
- continue;
+ // Format the finalgrade from the DB so that it matches the grade from the client.
+ if ($postedvalue === format_float($oldvalue->finalgrade, $oldvalue->grade_item->get_decimals())) {
+ continue;
+ }
}
}
@@ -328,6 +331,13 @@ public function process_data($data) {
$gradeitem->update_final_grade($userid, $finalgrade, 'gradebook', false,
FORMAT_MOODLE, null, null, true);
+
+ // Apply penalty.
+ if (isset($data->deduction[$userid][$itemid])) {
+ $deductedmark = $data->deduction[$userid][$itemid];
+ $gradeitem->update_final_grade($userid, $finalgrade - $deductedmark, 'gradepenalty', false,
+ FORMAT_MOODLE, null, null, true);
+ }
}
}
}
@@ -1146,6 +1156,14 @@ public function get_right_rows(bool $displayaverages): array {
if ($context->statusicons) {
$context->extraclasses .= ' statusicons';
}
+
+ // If option to reapply deduction is enabled, add the option to the context.
+ if (get_config('core', 'gradepenalty_overriddengrade')) {
+ $context->deductedmark = format_float($grade->deductedmark, $decimalpoints);
+ $context->reapplydeduction = $grade->deductedmark > 0;
+ $context->deductionid = 'deduction_' . $userid . '_' . $item->id;
+ $context->deductionname = 'deduction[' . $userid . '][' . $item->id .']';
+ }
} else {
$context->extraclasses = 'gradevalue' . $hidden . $gradepass;
$context->text = format_float($gradeval, $decimalpoints);
diff --git a/grade/report/grader/templates/cell.mustache b/grade/report/grader/templates/cell.mustache
index 52b8910a844b2..2115462b64fa1 100644
--- a/grade/report/grader/templates/cell.mustache
+++ b/grade/report/grader/templates/cell.mustache
@@ -41,7 +41,19 @@
{{>core_grades/grades/grader/scale}}
{{/scale}}
{{^scale}}
- {{>core_grades/grades/grader/input}}
+
+
+ {{>core_grades/grades/grader/input}}
+
+ {{#reapplydeduction}}
+
+
+
+
+ {{/reapplydeduction}}
+
{{/scale}}
{{/iseditable}}
{{^iseditable}}
diff --git a/grade/report/grader/tests/behat/behat_gradereport_grader.php b/grade/report/grader/tests/behat/behat_gradereport_grader.php
index 46acb8c3fc1f7..24132ed6e5a02 100644
--- a/grade/report/grader/tests/behat/behat_gradereport_grader.php
+++ b/grade/report/grader/tests/behat/behat_gradereport_grader.php
@@ -144,4 +144,14 @@ public static function get_partial_named_selectors(): array {
),
];
}
+
+ /**
+ * Enable penalty for overridden grade.
+ *
+ * @Given I enable penalty for overridden grade
+ */
+ public function i_enable_penalty_for_overridden_grade(): void {
+ set_config('gradepenalty_enabled', 1);
+ set_config('gradepenalty_overriddengrade', 1);
+ }
}
diff --git a/grade/report/grader/tests/behat/grade_override_with_deduction.feature b/grade/report/grader/tests/behat/grade_override_with_deduction.feature
new file mode 100644
index 0000000000000..55f7fd0cbcbf3
--- /dev/null
+++ b/grade/report/grader/tests/behat/grade_override_with_deduction.feature
@@ -0,0 +1,48 @@
+@gradereport @gradereport_grader @gradereport_grader_deduction
+Feature: As a teacher, I want to override a grade with a deduction and check the gradebook.
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And I enable penalty for overridden grade
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ | student1 | Student | 1 | student1@example.com |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ And the following "grade items" exist:
+ | itemname | grademin | grademax | course |
+ | Manual grade 01 | 0 | 100 | C1 |
+ | Manual grade 02 | 0 | 100 | C1 |
+ And the following "grade grades" exist:
+ | gradeitem | user | grade | deductedmark |
+ | Manual grade 01 | student1 | 60 | 10 |
+ | Manual grade 02 | student1 | 80 | 20 |
+ When I log in as "teacher1"
+
+ @javascript
+ Scenario: Override a grade with a deduction and check the gradebook
+ And I am on "Course 1" course homepage
+ And I navigate to "View > Grader report" in the course gradebook
+ And the following should exist in the "user-grades" table:
+ | -1- | -2- | -3- | -4- | -5- |
+ | Student 1 | student1@example.com | 60 | 80 | 140 |
+ And I turn editing mode on
+ And I set the field "Student 1 Manual grade 01 grade" to "80"
+ And I click on "Deduct 10.00" "checkbox"
+ And I click on "Save changes" "button"
+ And I turn editing mode off
+ And the following should exist in the "user-grades" table:
+ | -1- | -2- | -3- | -4- | -5- |
+ | Student 1 | student1@example.com | 70 | 80 | 150 |
+ And I turn editing mode on
+ And I set the field "Student 1 Manual grade 02 grade" to "100"
+ And I click on "Save changes" "button"
+ And I turn editing mode off
+ And the following should exist in the "user-grades" table:
+ | -1- | -2- | -3- | -4- | -5- |
+ | Student 1 | student1@example.com | 70 | 100 | 170 |
diff --git a/lang/en/grades.php b/lang/en/grades.php
index a886c4a7d36d5..7bed40640f2de 100644
--- a/lang/en/grades.php
+++ b/lang/en/grades.php
@@ -334,6 +334,8 @@
$string['gradepenalty_enabled'] = 'Grade penalty';
$string['gradepenalty_enabled_help'] = 'If enabled, the penalty will be applied to the grades of supported modules.';
$string['gradepenalty_indicator_info'] = 'Late penalty applied -{$a} marks';
+$string['gradepenalty_overriddengrade'] = 'Apply penalty to overridden grades';
+$string['gradepenalty_overriddengrade_help'] = 'If enabled, the penalty will be applied to overridden grades.';
$string['gradepenalty_supportedplugins'] = 'Supported modules';
$string['gradepenalty_supportedplugins_help'] = 'Enable the grade penalty for the selected modules.';
$string['gradepointdefault'] = 'Grade point default';